diff --git a/.github/workflows/lint-web.yml b/.github/workflows/lint-web.yml index 12a74e2dd..b58b73c4c 100644 --- a/.github/workflows/lint-web.yml +++ b/.github/workflows/lint-web.yml @@ -33,6 +33,9 @@ jobs: run: | npm i -g npm pnpm pnpm i --frozen-lockfile + - name: Build + working-directory: ./web + run: pnpm build - name: Lint working-directory: ./web run: pnpm run lint diff --git a/.gitmodules b/.gitmodules index a5df1c851..561704131 100644 --- a/.gitmodules +++ b/.gitmodules @@ -3,4 +3,4 @@ url = ../proto.git [submodule "web/src/shared/defguard-ui"] path = web/src/shared/defguard-ui - url = ../ui.git + url = git@github.com:DefGuard/ui.git diff --git a/Dockerfile b/Dockerfile index 8939f2b52..100ea408a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,11 +1,10 @@ FROM public.ecr.aws/docker/library/node:24 AS web WORKDIR /app -COPY web/package.json web/pnpm-lock.yaml web/.npmrc ./ +COPY web/package.json web/pnpm-lock.yaml ./ RUN npm i -g pnpm RUN pnpm install --ignore-scripts --frozen-lockfile COPY web/ . -RUN pnpm run generate-translation-types RUN pnpm build FROM public.ecr.aws/docker/library/rust:1 AS chef @@ -31,7 +30,6 @@ RUN cargo chef cook --release --recipe-path recipe.json # build project COPY --from=web /app/dist ./web/dist -COPY web/src/shared/images/svg ./web/src/shared/images/svg RUN apt-get update && apt-get -y install protobuf-compiler libprotobuf-dev COPY Cargo.toml Cargo.lock ./ # for vergen diff --git a/web/.biomeignore b/web/.biomeignore deleted file mode 100644 index 8260f01a7..000000000 --- a/web/.biomeignore +++ /dev/null @@ -1,2 +0,0 @@ -src/i18n/*.ts -src/i18n/*.tsx diff --git a/web/.browserslistrc b/web/.browserslistrc new file mode 100644 index 000000000..549f14c02 --- /dev/null +++ b/web/.browserslistrc @@ -0,0 +1 @@ +>0.3%, last 2 versions, since 2025, not op_mini all, not dead diff --git a/web/.editorconfig b/web/.editorconfig index 57516b7ac..73f410322 100644 --- a/web/.editorconfig +++ b/web/.editorconfig @@ -7,23 +7,5 @@ insert_final_newline = true indent_style = space indent_size = 2 -[*.{ts,tsx}] -indent_style = space -indent_size = 2 -rulers = 90 - -[*.{scss}] -indent_style = space -indent_size = 2 -rulers = 90 - -[*.{html}] -indent_style = space -indent_size = 2 +[*.{ts,tsx,js,jsx,scss,html,json,yaml}] rulers = 90 - -[*.{json,yaml}] -insert_final_newline = true -indent_style = space -indent_size = 2 -rulers = 80 diff --git a/web/.gitattributes b/web/.gitattributes new file mode 100644 index 000000000..ee7f45a36 --- /dev/null +++ b/web/.gitattributes @@ -0,0 +1,36 @@ +* text=auto eol=lf + +*.js text eol=lf +*.jsx text eol=lf +*.ts text eol=lf +*.tsx text eol=lf +*.css text eol=lf +*.scss text eol=lf +*.json text eol=lf +*.html text eol=lf +*.md text eol=lf +*.yml text eol=lf +*.yaml text eol=lf +*.lock text eol=lf + +*.woff binary +*.woff2 binary +*.ttf binary +*.otf binary +*.eot binary +*.png binary +*.jpg binary +*.jpeg binary +*.gif binary +*.webp binary +*.avif binary +*.ico binary +*.mp4 binary +*.webm binary +*.ogg binary +*.mp3 binary +*.wav binary +*.pdf binary +*.zip binary +*.tar binary +*.gz binary diff --git a/web/.gitignore b/web/.gitignore index 88e111584..175f4eee7 100644 --- a/web/.gitignore +++ b/web/.gitignore @@ -1,2 +1,29 @@ -*.local* -./*.env +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local +project.inlang/cache +.env.development + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? +.tanstack +.cfft* +blueprint-templates diff --git a/web/.npmrc b/web/.npmrc deleted file mode 100644 index 319e41e69..000000000 --- a/web/.npmrc +++ /dev/null @@ -1 +0,0 @@ -strict-peer-dependencies=false diff --git a/web/.nvmrc b/web/.nvmrc deleted file mode 100644 index 53a256a2e..000000000 --- a/web/.nvmrc +++ /dev/null @@ -1 +0,0 @@ -v24.7 diff --git a/web/.prettierignore b/web/.prettierignore index 74dbecd59..402ea0086 100644 --- a/web/.prettierignore +++ b/web/.prettierignore @@ -1,4 +1,3 @@ -/src/i18n/*.ts -/src/i18n/*.tsx /src/**/*.tsx /src/**/*.ts +/src/**/*.js diff --git a/web/.stylelintrc.json b/web/.stylelintrc.json new file mode 100644 index 000000000..6a4e21333 --- /dev/null +++ b/web/.stylelintrc.json @@ -0,0 +1,10 @@ +{ + "extends": ["stylelint-config-standard-scss"], + "plugins": ["stylelint-scss"], + "rules": { + "at-rule-no-unknown": null, + "scss/at-rule-no-unknown": true, + "custom-property-empty-line-before": null, + "value-keyword-case": null + } +} diff --git a/web/.typesafe-i18n.json b/web/.typesafe-i18n.json deleted file mode 100644 index 28148cbaf..000000000 --- a/web/.typesafe-i18n.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "adapter": "react", - "$schema": "https://unpkg.com/typesafe-i18n@5.26.2/schema/typesafe-i18n.json", - "baseLocale": "en" -} diff --git a/web/README.md b/web/README.md index c88e39e5b..4dcad1f91 100644 --- a/web/README.md +++ b/web/README.md @@ -1,73 +1,73 @@ -# Defguard frontend +# React + TypeScript + Vite -React.js based, web user interface for Defguard project. +This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. -## Run development server +Currently, two official plugins are available: -Install dependencies +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) (or [oxc](https://oxc.rs) when used in [rolldown-vite](https://vite.dev/guide/rolldown)) for Fast Refresh +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh -```bash - pnpm install -``` - -Start the dev server - -```bash - pnpm run dev -``` - -You can configure API proxy by changing `target` key value under `/api` path within `vite.config.ts` file. - -## Build docker image +## React Compiler -```bash -docker build . -``` - -## Linting - -For linting this project uses [prettier](https://www.npmjs.com/package/prettier), [eslint](https://www.npmjs.com/package/eslint) and [stylelint](https://stylelint.io/) +The React Compiler is currently not compatible with SWC. See [this issue](https://github.com/vitejs/vite-plugin-react/issues/428) for tracking the progress. -#### Available commands +## Expanding the ESLint configuration -Check linting in all supported files ( both prettier and eslint ) +If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules: -```bash -pnpm run lint -``` +```js +export default defineConfig([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + // Other configs... -Fix all autofixable problems with eslint + // Remove tseslint.configs.recommended and replace with this + tseslint.configs.recommendedTypeChecked, + // Alternatively, use this for stricter rules + tseslint.configs.strictTypeChecked, + // Optionally, add this for stylistic rules + tseslint.configs.stylisticTypeChecked, -```bash -pnpm run eslint-fix + // Other configs... + ], + languageOptions: { + parserOptions: { + project: ['./tsconfig.node.json', './tsconfig.app.json'], + tsconfigRootDir: import.meta.dirname, + }, + // other options... + }, + }, +]) ``` -Fix all autofixable probles with prettier - -```bash -pnpm run prettier-fix +You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules: + +```js +// eslint.config.js +import reactX from 'eslint-plugin-react-x' +import reactDom from 'eslint-plugin-react-dom' + +export default defineConfig([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + // Other configs... + // Enable lint rules for React + reactX.configs['recommended-typescript'], + // Enable lint rules for React DOM + reactDom.configs.recommended, + ], + languageOptions: { + parserOptions: { + project: ['./tsconfig.node.json', './tsconfig.app.json'], + tsconfigRootDir: import.meta.dirname, + }, + // other options... + }, + }, +]) ``` - -Fix all autofixable problems with pretter and eslint - -```bash -pnpm fix-lint -``` - -#### Adding new SVG components - -Move .svg files into `src/shared/images/svg` then use command: - -```bash -pnpm parse-svgs -``` - -This will generate new components within `src/shared/components/svg`, they can be used as a regular components. Also this command doesn't replace or modify already existing files. - -## Conventional Commits - -Using [commitlint](https://commitlint.js.org/#/) with this [config](https://github.com/conventional-changelog/commitlint/tree/master/%40commitlint/config-conventional). - -## Versioning - -Using [standard-version](https://github.com/conventional-changelog/standard-version) diff --git a/web/biome.json b/web/biome.json index 2bcbfd1d7..bfaf40f57 100644 --- a/web/biome.json +++ b/web/biome.json @@ -1,21 +1,29 @@ { - "$schema": "https://biomejs.dev/schemas/2.2.2/schema.json", - "vcs": { "enabled": false, "clientKind": "git", "useIgnoreFile": false }, + "$schema": "https://biomejs.dev/schemas/2.3.3/schema.json", + "vcs": { + "enabled": false, + "clientKind": "git", + "useIgnoreFile": false + }, "files": { "ignoreUnknown": false, - "includes": ["src/**", "!src/i18n", "!**/svg"] + "includes": [ + "src/**", + "!src/messages", + "!src/paraglide/**/*.js", + "!src/routeTree.gen.ts" + ] }, "formatter": { "enabled": true, "formatWithErrors": false, - "indentStyle": "space", - "indentWidth": 2, - "lineEnding": "lf", - "lineWidth": 90, "attributePosition": "auto", "bracketSameLine": false, "bracketSpacing": true, "expand": "auto", + "lineEnding": "lf", + "lineWidth": 90, + "indentStyle": "space", "useEditorconfig": true }, "linter": { @@ -23,47 +31,26 @@ "rules": { "recommended": true, "a11y": "off", - "complexity": { - "noBannedTypes": "error", - "noUselessTypeConstraint": "error" - }, "correctness": { - "noChildrenProp": "error", - "noPrecisionLoss": "error", - "noUnusedVariables": "error", - "useExhaustiveDependencies": "error", - "useHookAtTopLevel": "error", - "useJsxKeyInIterable": "error", "useUniqueElementIds": "off" }, - "security": { "noDangerouslySetInnerHtmlWithChildren": "error" }, "style": { - "noNamespace": "error", - "noNonNullAssertion": "error", - "useArrayLiterals": "error", - "useAsConstAssertion": "error", - "useBlockStatements": "off", - "useLiteralEnumMembers": "off" + "useLiteralEnumMembers": "off", + "useBlockStatements": "off" }, "suspicious": { - "noCommentText": "error", - "noDuplicateJsxProps": "error", - "noExplicitAny": "error", - "noExtraNonNullAssertion": "error", - "noMisleadingInstantiator": "error", - "noUnsafeDeclarationMerging": "error", "noArrayIndexKey": "off" } } }, "javascript": { "formatter": { + "quoteStyle": "single", "jsxQuoteStyle": "double", "quoteProperties": "asNeeded", "trailingCommas": "all", "semicolons": "always", "arrowParentheses": "always", - "quoteStyle": "single", "attributePosition": "auto", "bracketSameLine": false, "bracketSpacing": true @@ -71,14 +58,10 @@ }, "assist": { "enabled": true, - "actions": { "source": { "organizeImports": "on" } } - }, - "overrides": [ - { - "includes": ["src/shared/links.ts"], - "formatter": { - "enabled": false + "actions": { + "source": { + "organizeImports": "on" } } - ] + } } diff --git a/web/index.html b/web/index.html index 33c26718f..5917d0880 100644 --- a/web/index.html +++ b/web/index.html @@ -4,36 +4,34 @@ - - - - + + + Defguard - - - - + + + + - - - - - - - - - - - - + + + + + + + + + + + +
-
-
+
- \ No newline at end of file + diff --git a/web/messages/en/auth.json b/web/messages/en/auth.json new file mode 100644 index 000000000..e690a586c --- /dev/null +++ b/web/messages/en/auth.json @@ -0,0 +1,22 @@ +{ + "$schema": "https://inlang.com/schema/inlang-message-format", + "login_main_title": "Welcome to Defguard", + "login_main_subtitle": "Please enter your credentials to enter your account", + "login_main_forgot": "If you forgot your password, contact your organization's administrator.", + "login_main_attempts_info": "Too many login attempts. Please try again later.", + "login_error_invalid": "username or password is incorrect.", + "login_mfa_title": "Two-factor authentication", + "login_totp_subtitle": "Use code from your authentication app to proceed.", + "login_totp_label": "Authentication Code", + "login_mfa_alternative_recovery": "recovery codes", + "login_mfa_alternative_email": "email", + "login_mfa_alternative_passkey": "Passkey", + "login_mfa_alternative_totp": "Authenticator app instead", + "login_mfa_alternative_back": "Back to login", + "login_mfa_use_instead": "Use {method} instead", + "login_mfa_recovery_subtitle": "Enter one of active recovery codes bellow.", + "login_mfa_recovery_label": "Recovery Code", + "login_mfa_passkey_subtitle": "Please use one of your Passkeys to log in.", + "login_mfa_passkey_button": "Login with Passkey", + "login_mfa_email_subtitle": "Use code sent to your email to proceed." +} diff --git a/web/messages/en/common.json b/web/messages/en/common.json new file mode 100644 index 000000000..ee3a8ccbf --- /dev/null +++ b/web/messages/en/common.json @@ -0,0 +1,38 @@ +{ + "$schema": "https://inlang.com/schema/inlang-message-format", + "test_placeholder": "Placeholder text", + "test_placeholder_long": "Placeholder text - Nam viverra, urna a rhoncus scelerisque, dolor nunc commodo ante, non ullamcorper quam elit a nunc. Aenean elementum sed lorem eu facilisis.", + "test_placeholder_extreme": "Placeholder text - Lorem ipsum dolor sit amet, consectetur adipiscing elit. In euismod pretium convallis. Integer commodo nunc et orci vulputate, vel euismod nunc faucibus. Morbi non lacinia tellus. Quisque egestas hendrerit rutrum. Cras vel ex at metus aliquet convallis. Pellentesque volutpat velit orci, in suscipit lectus ultricies eget. Mauris rhoncus tempor hendrerit. Nam viverra, urna a rhoncus scelerisque, dolor nunc commodo ante, non ullamcorper quam elit a nunc. Aenean elementum sed lorem eu facilisis.", + "misc_or": "or", + "misc_and": "and", + "misc_for": "for", + "misc_clipboard_copy": "Copied to clipboard!", + "misc_secret": "Secret", + "controls_copy": "Copy", + "controls_download": "Download", + "controls_rename": "Rename", + "controls_close": "Close", + "controls_back": "Back", + "controls_delete": "Delete", + "controls_edit": "Edit", + "controls_logout": "Logout", + "controls_submit": "Submit", + "controls_cancel": "Cancel", + "controls_save": "Save", + "controls_save_changes": "Save changes", + "controls_sign_in": "Sign in", + "controls_enable": "Enable", + "controls_disable": "Disable", + "controls_next": "Next", + "controls_continue": "Continue", + "controls_verify_code": "Verify code", + "controls_complete": "Complete", + "controls_copy_clipboard": "Copy to clipboard", + "state_disabled": "Disabled", + "state_warning": "Warning", + "state_active": "Active", + "state_default": "Default", + "state_enabled": "Enabled", + "state_step": "Step {step}", + "col_created_at": "Created at" +} diff --git a/web/messages/en/components.json b/web/messages/en/components.json new file mode 100644 index 000000000..006f618aa --- /dev/null +++ b/web/messages/en/components.json @@ -0,0 +1,3 @@ +{ + "$schema": "https://inlang.com/schema/inlang-message-format" +} diff --git a/web/messages/en/form.json b/web/messages/en/form.json new file mode 100644 index 000000000..ee2356077 --- /dev/null +++ b/web/messages/en/form.json @@ -0,0 +1,41 @@ +{ + "$schema": "https://inlang.com/schema/inlang-message-format", + "form_error_name_reserved": "Name already taken", + "form_error_email": "Enter valid email", + "form_error_required": "Field is required", + "form_error_token": "Token is not valid", + "form_error_invalid": "Field is invalid", + "form_error_forbidden_char": "Field contains forbidden characters", + "form_error_username_taken": "Username is already in use", + "form_error_max_len": "Maximum length of {length} exceeded", + "form_error_min_len": "Minimal length of {length} required", + "form_error_code": "Incorrect code. Please check and try again.", + "password_form_check_title": "Your password must include:", + "password_form_check_number": "At least one number required", + "password_form_check_special": "At least one special character", + "password_form_check_lowercase": "At least one lowercase character", + "password_form_check_uppercase": "At least one uppercase character", + "password_form_check_minimum": "Minimum length of 8", + "password_form_check_repeat_match": "Password's doesn't match", + "password_form_special_error": "Password doesn't meet the requirements. Please check the list below.", + "form_label_auth_code": "Authentication Code", + "form_label_verification_code": "Verification Code", + "form_label_username": "Username", + "form_label_password": "Password", + "form_label_first_name": "First Name", + "form_label_last_name": "Last Name", + "form_label_phone": "Phone Number", + "form_label_email": "Email", + "form_label_user_groups": "User Groups", + "form_label_current_password": "Current Password", + "form_label_new_password": "New Password", + "form_label_confirm_new_password": "Confirm New Password", + "form_label_url": "URL", + "form_label_token": "Token", + "form_label_device_name": "Device name", + "form_label_key": "Key", + "form_label_name": "Name", + "form_label_public_key": "Public key", + "form_label_device_public_key": "Device public key (wireguard)", + "form_label_type": "Type" +} diff --git a/web/messages/en/modal.json b/web/messages/en/modal.json new file mode 100644 index 000000000..1cfc58038 --- /dev/null +++ b/web/messages/en/modal.json @@ -0,0 +1,71 @@ +{ + "$schema": "https://inlang.com/schema/inlang-message-format", + "modal_change_password_title": "Change password", + "modal_change_password_submit": "Change password", + "modal_delete_authorized_app_title": "Delete authorized app", + "modal_delete_authorized_app_content_1": "Are you sure you want to remove this application? If you do, you won't be able to use it to log in with defguard anymore.", + "modal_delete_authorized_app_content_2": "Please note: until you log out from this application, it will remain signed in with DefGuard.", + "modal_mfa_enable_email_title": "Email MFA setup", + "modal_mfa_enable_email_verification": "Email verification", + "modal_mfa_enable_email_content": "To setup your MFA enter the code that was sent to your account email: {email}", + "modal_mfa_enable_email_resend": "Resend email", + "modal_mfa_enable_totp_title": "Authentication App Setup", + "modal_mfa_enable_totp_step_1_title": "Scan QR code", + "modal_mfa_enable_totp_step_1_content": "To set up Multi-Factor Authentication (MFA), simply scan this QR code using an authenticator app (like Google Authenticator, Microsoft Authenticator, or any other you prefer).", + "modal_mfa_enable_totp_qr_problem": "Can't scan QR code? Enter code manually in the app.", + "modal_mfa_enable_totp_step_2_title": "Enter 6-digit code from authentication app", + "modal_mfa_add_passkey_title": "Add passkey", + "modal_mfa_add_passkey_content": "Passkeys can be used as your second authentication factor instead of a verification code. Learn how to set them up in the documentation.", + "modal_mfa_add_passkey_label": "Passkey name", + "modal_recovery_codes_download_title": "Download recovery codes", + "modal_recovery_codes_download_cta_download": "Download codes", + "modal_recovery_codes_download_confirm": "I have saved my codes", + "modal_recovery_codes_explain": "Recovery codes are your backup access. Store them securely (e.g. in a password manager like LastPass or Bitwarden) in case you lose your authenticator app.", + "modal_recovery_codes_error": "Confirm you have saved your recovery codes.", + "modal_add_user_device_title_add": "Add new device", + "modal_add_user_device_title_manual": "Manual setup", + "modal_add_user_device_title_save": "Save configuration files", + "modal_add_user_device_show_advanced": "Show advanced option", + "modal_add_user_device_hide_advanced": "Hide advanced option", + "modal_add_user_device_start_client_title": "Client Activation", + "modal_add_user_device_start_client_subtitle": "Use the defguard client to set up your device. Easily configure it with a single token or by scanning a QR code.", + "modal_add_user_device_client_title": "Client activation", + "modal_add_user_device_client_subtitle": "Select the activation type that matches the device where you installed the defguard app/client.", + "modal_add_user_device_client_desktop_title": "Desktop client", + "modal_add_user_device_client_desktop_automatic": "Automatic configuration", + "modal_add_user_device_client_desktop_automatic_explain_1": "Click the button below for automatic configuration.", + "modal_add_user_device_client_desktop_automatic_explain_2": "Before using this option make sure the Defguard desktop client is already installed.", + "modal_add_user_device_client_desktop_manual_title": "Automatic configuration", + "modal_add_user_device_client_desktop_manual_subtitle": "Activate your desktop client manually by entering the URL and token you see bellow.", + "modal_add_user_device_client_desktop_one_click": "One-Click Configuration", + "modal_add_user_device_client_desktop_download": "Get the desktop client", + "modal_add_user_device_client_fold_closed": "Show advanced configuration", + "modal_add_user_device_client_fold_open": "Hide advanced configuration", + "modal_add_user_device_client_mobile_title": "Mobile application", + "modal_add_user_device_client_mobile_subtitle": "Scan QR code bellow to activate Defguard mobile application.", + "modal_add_user_device_client_mobile_get_mobile": "Get mobile app by clicking one of the buttons below.", + "modal_add_user_device_client_close_info": "Once your Defguard client is configured, you can close this window.", + "modal_add_user_device_start_manual_title": "Manual WireGuard Setup", + "modal_add_user_device_start_manual_subtitle": "Download config file or QR code and set up the VPN manually for full control over network and client settings.", + "modal_add_user_device_manual_setup_title": "Create VPN device", + "modal_add_user_device_manual_setup_explain": "Configure WireGuardVPN on your device, please visit documentation if you don't know how to do it.", + "modal_add_user_device_manual_setup_choice": "Choose setup method", + "modal_add_user_device_manual_setup_choice_auto": "Generate key pair", + "modal_add_user_device_manual_setup_choice_manual": "Use your own keys", + "modal_add_user_device_manual_download_warn_title": "Important", + "modal_add_user_device_manual_download_warn_content": "Please download the configuration file now, as we do not store your private keys. After this window is closed, you will not be able to retrieve the full configuration file (only a blank template).", + "modal_add_user_device_manual_download_location_label": "Config file for location", + "modal_add_user_device_manual_download_explain": "Scan the QR code or import the configuration file into your device's WireGuard app.", + "modal_add_user_device_manual_download_actions_download": "Download config file", + "modal_add_user_device_manual_download_actions_download_one": "This location only", + "modal_add_user_device_manual_download_actions_download_all": "All locations", + "modal_edit_user_device_title": "Edit device", + "modal_user_device_config_title": "Device VPN configuration", + "modal_add_auth_key_title": "Add authentication key", + "modal_add_auth_key_submit": "Add key", + "modal_rename_auth_key_title": "Rename authentication key", + "modal_add_api_title": "Add API token", + "modal_add_api_token_copy_copy_label": "API Token", + "modal_add_api_token_copy_warning": "Please copy and save the API token below now. You won't be able to see it again.", + "modal_rename_api_title": "Rename API token" +} diff --git a/web/messages/en/profile.json b/web/messages/en/profile.json new file mode 100644 index 000000000..05cb4c112 --- /dev/null +++ b/web/messages/en/profile.json @@ -0,0 +1,52 @@ +{ + "$schema": "https://inlang.com/schema/inlang-message-format", + "profile_my_profile": "My Profile", + "profile_title": "{name} Profile", + "profile_tabs_details": "Details", + "profile_tabs_devices": "Devices", + "profile_tabs_auth_keys": "Authentication Keys", + "profile_tabs_api": "API Tokens", + "profile_general_card_title": "General", + "profile_auth_card_title": "Password and authentication", + "profile_auth_card_section_password": "Password settings", + "profile_auth_card_section_2fa": "Two-factor methods", + "profile_auth_card_2fa_controls_disable_all": "Disable all", + "profile_auth_card_2fa_totp": "Time-based One-Time Password", + "profile_auth_card_2fa_email": "Email", + "profile_auth_card_2fa_passkeys": "Passkeys", + "profile_auth_card_add_passkey": "Add Passkey", + "profile_auth_card_disable_passkeys": "Delete all Passkeys", + "profile_auth_card_delete_passkey": "Delete passkey", + "profile_auth_card_password_change": "Change password", + "profile_auth_card_make_default": "Make default", + "profile_auth_card_biometric_title": "Biometric authentication devices", + "profile_auth_card_devices_link": "Devices", + "profile_apps_no_data_title": "No authorized apps", + "profile_apps_no_data_subtitle": "Applications you allow to log in with Defguard will appear here.", + "profile_apps_card_title": "Authorized apps", + "profile_apps_card_subtitle": "These applications are allowed to log in with DefGuard. You can remove them from the list if you wish.", + "profile_devices_title": "All devices", + "profile_devices_add_new": "Add new device", + "profile_devices_col_name": "Device name", + "profile_devices_col_pub_ip": "Public IP", + "profile_devices_col_location": "Connected through", + "profile_devices_col_connected": "Connected date", + "profile_devices_col_location_name": "Location name", + "profile_devices_col_location_ip": "Assigned IP", + "profile_devices_col_location_connected_from": "Last connected from", + "profile_devices_col_location_connected": "Last connected", + "profile_devices_col_never_connected": "Never connected", + "profile_devices_menu_show_config": "Show configuration", + "profile_auth_keys_no_data_title": "You don't have any added keys.", + "profile_auth_keys_no_data_subtitle": "To add one, click the button below", + "profile_auth_keys_no_data_cta": "Add new key", + "profile_auth_keys_header_title": "Authentication keys", + "profile_auth_keys_table_col_name": "Key name", + "profile_auth_keys_table_menu_download_ssh": "Download SSH key", + "profile_auth_keys_table_menu_download_gpg": "Download GPG key", + "profile_api_title": "API tokens", + "profile_api_add": "Add new API token", + "profile_api_empty_title": "You don't have any API tokens.", + "profile_api_empty_subtitle": "To add one, click the button below.", + "profile_api_col_name": "Token name" +} diff --git a/web/messages/en/users.json b/web/messages/en/users.json new file mode 100644 index 000000000..b4b07875b --- /dev/null +++ b/web/messages/en/users.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://inlang.com/schema/inlang-message-format", + "users_title": "Users", + "users_header_title": "All users", + "users_search_placeholder": "Find user", + "users_add": "Add new user", + "users_empty_title": "No users here yet.", + "users_empty_subtitle": "Add more users by clicking the button below.", + "users_not_found_title": "Nothing is found.", + "users_not_found_subtitle": "We couldn't find any users matching your search. Try adjusting your filters or search terms.", + "users_col_name": "User name", + "users_col_login": "Login", + "users_col_phone": "Phone", + "users_col_groups": "Groups" +} diff --git a/web/nginx.conf b/web/nginx.conf deleted file mode 100644 index aad3a639a..000000000 --- a/web/nginx.conf +++ /dev/null @@ -1,38 +0,0 @@ -http { - server { - listen 80; - server_name prl; - root /web/; - access_log /var/log/nginx/prl.access.log; - error_log /var/log/nginx/prl.error.log; - - ignore_invalid_headers off; - large_client_header_buffers 4 16k; - - gzip on; - gzip_disable "msie6"; - - gzip_vary on; - gzip_proxied any; - gzip_buffers 16 8k; - gzip_http_version 1.1; - gzip_types application/atom+xml application/javascript application/json application/rss+xml - application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml - application/xml font/opentype image/svg+xml image/x-icon text/css text/plain text/x-component; - - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_pass_request_headers on; - - location / { - include /etc/nginx/mime.types; - try_files $uri $uri/ /index.html =404; - } - } -} - -events { - worker_connections 1024; ## Default: 1024 -} diff --git a/web/package.json b/web/package.json index 72f2c3acb..ee13d63e7 100644 --- a/web/package.json +++ b/web/package.json @@ -1,145 +1,70 @@ { "name": "web", + "private": true, + "version": "0.0.1", "type": "module", "scripts": { - "preview": "pnpm run build && pnpm run vite preview", - "dev": "concurrently \"pnpm run vite\" \"pnpm run typesafe-i18n\"", - "build": "pnpm run typecheck && vite build", - "serve": "vite preview", - "typecheck": "tsc --project ./tsconfig.app.json", - "generate-translation-types": "typesafe-i18n --no-watch", - "fix": "biome check ./src --write --assist-enabled=true && prettier src/**/*.scss -w --log-level silent", - "fix-unsafe": "biome check ./src --write --unsafe --assist-enabled=true && prettier src/**/*.scss -w --log-level silent", - "lint": "biome check ./src --error-on-warnings --formatter-enabled=true --enforce-assist=true --linter-enabled=true && pnpm run typecheck && prettier src/**/*.scss --check --log-level error", - "typesafe-i18n": "typesafe-i18n", - "vite": "vite", - "prettier": "prettier", - "biome": "biome" - }, - "browserslist": { - "production": [ - ">0.2%", - "not dead", - "not op_mini all" - ], - "development": [ - "last 1 chrome version", - "last 1 firefox version", - "last 1 safari version" - ] - }, - "pnpm": { - "peerDependencyRules": { - "ignoreMissing": [ - "react-native" - ] - }, - "ignoredBuiltDependencies": [ - "@swc/core", - "esbuild" - ], - "onlyBuiltDependencies": [ - "@swc/core" - ] + "dev": "vite", + "build": "vite build && tsc -b", + "preview": "vite preview", + "biome": "biome", + "lint": "biome check ./src/ && prettier src/**/*.scss --check --log-level error && stylelint \"src/**/*.scss\" -c ./.stylelintrc.json && tsc -b", + "fix": "biome check ./src/ --write --unsafe && prettier src/**/*.scss -w --log-level silent", + "tsc": "tsc" }, "dependencies": { + "@axa-ch/react-polymorphic-types": "^1.4.1", "@floating-ui/react": "^0.27.16", - "@github/webauthn-json": "^2.1.1", - "@hookform/resolvers": "^5.2.2", - "@react-hook/resize-observer": "^2.0.2", - "@react-rxjs/core": "^0.10.8", + "@inlang/paraglide-js": "^2.4.0", + "@shortercode/webzip": "1.1.1-0", "@stablelib/base64": "^2.0.1", "@stablelib/x25519": "^2.0.1", - "@tanstack/query-core": "^5.90.2", - "@tanstack/react-query": "^5.90.2", - "@tanstack/react-virtual": "3.13.12", - "@tanstack/virtual-core": "3.13.12", - "@use-gesture/react": "^10.3.1", - "axios": "^1.12.2", - "byte-size": "^9.0.1", - "classnames": "^2.5.1", + "@tanstack/react-devtools": "^0.8.0", + "@tanstack/react-form": "^1.23.8", + "@tanstack/react-query": "^5.90.6", + "@tanstack/react-query-devtools": "^5.90.2", + "@tanstack/react-router": "^1.134.9", + "@tanstack/react-router-devtools": "^1.134.9", + "@tanstack/react-table": "^8.21.3", + "@tanstack/react-virtual": "^3.13.12", + "@uidotdev/usehooks": "^2.4.1", + "axios": "^1.13.1", + "change-case": "^5.4.4", "clsx": "^2.1.1", - "date-fns": "^4.1.0", - "dayjs": "^1.11.18", - "deepmerge-ts": "^7.1.5", - "detect-browser": "^5.3.0", - "dice-coefficient": "^2.1.1", - "events": "^3.3.0", - "fast-deep-equal": "^3.1.3", - "file-saver": "^2.0.5", - "fuse.js": "^7.1.0", - "get-text-width": "^1.0.3", - "hex-rgb": "^5.0.0", - "html-react-parser": "^5.2.6", - "humanize-duration": "^3.33.1", - "ipaddr.js": "^2.2.0", - "itertools": "^2.5.0", - "js-base64": "^3.7.8", + "dayjs": "^1.11.19", "lodash-es": "^4.17.21", - "merge-refs": "^2.0.0", - "millify": "^6.1.0", - "motion": "^12.23.22", - "numbro": "^2.5.0", - "qrcode": "^1.5.4", + "motion": "^12.23.24", + "qrcode.react": "^4.2.0", "qs": "^6.14.0", - "radash": "^12.1.1", - "react": "^19.1.1", - "react-click-away-listener": "^2.4.0", - "react-datepicker": "^8.7.0", - "react-dom": "^19.1.1", - "react-hook-form": "^7.63.0", - "react-idle-timer": "^5.7.2", - "react-intersection-observer": "^9.16.0", - "react-is": "^19.1.1", - "react-loading-skeleton": "^3.5.0", + "react": "^19.2.0", + "react-dom": "^19.2.0", "react-markdown": "^10.1.0", - "react-qr-code": "^2.0.18", - "react-resize-detector": "^12.3.0", - "react-router": "^6.30.1", - "react-router-dom": "^6.30.1", - "react-tracked": "^2.0.1", - "react-virtualized-auto-sizer": "^1.0.26", - "recharts": "^3.2.1", - "rehype-external-links": "^3.0.0", - "rehype-raw": "^7.0.0", - "rehype-sanitize": "^6.0.0", "rxjs": "^7.8.2", - "scheduler": "^0.26.0", - "text-case": "^1.2.9", - "typesafe-i18n": "^5.26.2", - "use-breakpoint": "^4.0.6", - "zod": "^3.25.76", + "zod": "^4.1.12", "zustand": "^5.0.8" }, "devDependencies": { - "@babel/core": "^7.28.4", - "@biomejs/biome": "2.2.2", - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4", - "@hookform/devtools": "^4.4.0", - "@tanstack/react-query-devtools": "^5.90.2", - "@types/byte-size": "^8.1.2", - "@types/file-saver": "^2.0.7", - "@types/humanize-duration": "^3.27.4", + "@biomejs/biome": "2.3.3", + "@inlang/paraglide-js": "2.4.0", + "@tanstack/devtools-vite": "^0.3.11", + "@tanstack/router-plugin": "^1.134.9", "@types/lodash-es": "^4.17.12", - "@types/node": "^24.5.2", + "@types/node": "^24.10.0", "@types/qs": "^6.14.0", - "@types/react": "^19.1.13", - "@types/react-dom": "^19.1.9", - "@types/react-router-dom": "^5.3.3", - "@vitejs/plugin-react-swc": "^4.1.0", + "@types/react": "^19.2.2", + "@types/react-dom": "^19.2.2", + "@vitejs/plugin-react-swc": "^4.2.0", "autoprefixer": "^10.4.21", - "concurrently": "^9.2.1", - "dotenv": "^17.2.2", - "esbuild": "^0.25.10", - "globals": "^16.4.0", - "postcss": "^8.5.6", + "globals": "^16.5.0", "prettier": "^3.6.2", - "sass": "~1.70.0", - "standard-version": "^9.5.0", - "type-fest": "^4.41.0", - "typescript": "~5.9.2", - "vite": "^7.1.7", - "vite-plugin-package-version": "^1.1.0" + "sass": "^1.93.3", + "sharp": "^0.34.4", + "stylelint": "^16.25.0", + "stylelint-config-standard-scss": "^16.0.0", + "stylelint-scss": "^6.12.1", + "typescript": "~5.9.3", + "typescript-eslint": "^8.46.2", + "vite": "^7.1.12", + "vite-plugin-image-optimizer": "^2.0.3" } } diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index a8738b891..3a603e822 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -8,322 +8,204 @@ importers: .: dependencies: + '@axa-ch/react-polymorphic-types': + specifier: ^1.4.1 + version: 1.4.1(@types/react@19.2.2)(react@19.2.0) '@floating-ui/react': specifier: ^0.27.16 - version: 0.27.16(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@github/webauthn-json': - specifier: ^2.1.1 - version: 2.1.1 - '@hookform/resolvers': - specifier: ^5.2.2 - version: 5.2.2(react-hook-form@7.63.0(react@19.1.1)) - '@react-hook/resize-observer': - specifier: ^2.0.2 - version: 2.0.2(react@19.1.1) - '@react-rxjs/core': - specifier: ^0.10.8 - version: 0.10.8(react@19.1.1)(rxjs@7.8.2) + version: 0.27.16(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@inlang/paraglide-js': + specifier: ^2.4.0 + version: 2.4.0 + '@shortercode/webzip': + specifier: 1.1.1-0 + version: 1.1.1-0 '@stablelib/base64': specifier: ^2.0.1 version: 2.0.1 '@stablelib/x25519': specifier: ^2.0.1 version: 2.0.1 - '@tanstack/query-core': - specifier: ^5.90.2 - version: 5.90.2 + '@tanstack/react-devtools': + specifier: ^0.8.0 + version: 0.8.0(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(csstype@3.1.3)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(solid-js@1.9.9) + '@tanstack/react-form': + specifier: ^1.23.8 + version: 1.23.8(react-dom@19.2.0(react@19.2.0))(react@19.2.0) '@tanstack/react-query': + specifier: ^5.90.6 + version: 5.90.6(react@19.2.0) + '@tanstack/react-query-devtools': specifier: ^5.90.2 - version: 5.90.2(react@19.1.1) + version: 5.90.2(@tanstack/react-query@5.90.6(react@19.2.0))(react@19.2.0) + '@tanstack/react-router': + specifier: ^1.134.9 + version: 1.134.9(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@tanstack/react-router-devtools': + specifier: ^1.134.9 + version: 1.134.9(@tanstack/react-router@1.134.9(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(@tanstack/router-core@1.134.9)(@types/node@24.10.0)(csstype@3.1.3)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(sass@1.93.3)(solid-js@1.9.9)(tiny-invariant@1.3.3)(tsx@4.20.6) + '@tanstack/react-table': + specifier: ^8.21.3 + version: 8.21.3(react-dom@19.2.0(react@19.2.0))(react@19.2.0) '@tanstack/react-virtual': - specifier: 3.13.12 - version: 3.13.12(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@tanstack/virtual-core': - specifier: 3.13.12 - version: 3.13.12 - '@use-gesture/react': - specifier: ^10.3.1 - version: 10.3.1(react@19.1.1) + specifier: ^3.13.12 + version: 3.13.12(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@uidotdev/usehooks': + specifier: ^2.4.1 + version: 2.4.1(react-dom@19.2.0(react@19.2.0))(react@19.2.0) axios: - specifier: ^1.12.2 - version: 1.12.2 - byte-size: - specifier: ^9.0.1 - version: 9.0.1 - classnames: - specifier: ^2.5.1 - version: 2.5.1 + specifier: ^1.13.1 + version: 1.13.1 + change-case: + specifier: ^5.4.4 + version: 5.4.4 clsx: specifier: ^2.1.1 version: 2.1.1 - date-fns: - specifier: ^4.1.0 - version: 4.1.0 dayjs: - specifier: ^1.11.18 - version: 1.11.18 - deepmerge-ts: - specifier: ^7.1.5 - version: 7.1.5 - detect-browser: - specifier: ^5.3.0 - version: 5.3.0 - dice-coefficient: - specifier: ^2.1.1 - version: 2.1.1 - events: - specifier: ^3.3.0 - version: 3.3.0 - fast-deep-equal: - specifier: ^3.1.3 - version: 3.1.3 - file-saver: - specifier: ^2.0.5 - version: 2.0.5 - fuse.js: - specifier: ^7.1.0 - version: 7.1.0 - get-text-width: - specifier: ^1.0.3 - version: 1.0.3 - hex-rgb: - specifier: ^5.0.0 - version: 5.0.0 - html-react-parser: - specifier: ^5.2.6 - version: 5.2.6(@types/react@19.1.13)(react@19.1.1) - humanize-duration: - specifier: ^3.33.1 - version: 3.33.1 - ipaddr.js: - specifier: ^2.2.0 - version: 2.2.0 - itertools: - specifier: ^2.5.0 - version: 2.5.0 - js-base64: - specifier: ^3.7.8 - version: 3.7.8 + specifier: ^1.11.19 + version: 1.11.19 lodash-es: specifier: ^4.17.21 version: 4.17.21 - merge-refs: - specifier: ^2.0.0 - version: 2.0.0(@types/react@19.1.13) - millify: - specifier: ^6.1.0 - version: 6.1.0 motion: - specifier: ^12.23.22 - version: 12.23.22(@emotion/is-prop-valid@1.4.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - numbro: - specifier: ^2.5.0 - version: 2.5.0 - qrcode: - specifier: ^1.5.4 - version: 1.5.4 + specifier: ^12.23.24 + version: 12.23.24(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + qrcode.react: + specifier: ^4.2.0 + version: 4.2.0(react@19.2.0) qs: specifier: ^6.14.0 version: 6.14.0 - radash: - specifier: ^12.1.1 - version: 12.1.1 react: - specifier: ^19.1.1 - version: 19.1.1 - react-click-away-listener: - specifier: ^2.4.0 - version: 2.4.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - react-datepicker: - specifier: ^8.7.0 - version: 8.7.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + specifier: ^19.2.0 + version: 19.2.0 react-dom: - specifier: ^19.1.1 - version: 19.1.1(react@19.1.1) - react-hook-form: - specifier: ^7.63.0 - version: 7.63.0(react@19.1.1) - react-idle-timer: - specifier: ^5.7.2 - version: 5.7.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - react-intersection-observer: - specifier: ^9.16.0 - version: 9.16.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - react-is: - specifier: ^19.1.1 - version: 19.1.1 - react-loading-skeleton: - specifier: ^3.5.0 - version: 3.5.0(react@19.1.1) + specifier: ^19.2.0 + version: 19.2.0(react@19.2.0) react-markdown: specifier: ^10.1.0 - version: 10.1.0(@types/react@19.1.13)(react@19.1.1) - react-qr-code: - specifier: ^2.0.18 - version: 2.0.18(react@19.1.1) - react-resize-detector: - specifier: ^12.3.0 - version: 12.3.0(react@19.1.1) - react-router: - specifier: ^6.30.1 - version: 6.30.1(react@19.1.1) - react-router-dom: - specifier: ^6.30.1 - version: 6.30.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - react-tracked: - specifier: ^2.0.1 - version: 2.0.1(react@19.1.1)(scheduler@0.26.0) - react-virtualized-auto-sizer: - specifier: ^1.0.26 - version: 1.0.26(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - recharts: - specifier: ^3.2.1 - version: 3.2.1(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react-is@19.1.1)(react@19.1.1)(redux@5.0.1) - rehype-external-links: - specifier: ^3.0.0 - version: 3.0.0 - rehype-raw: - specifier: ^7.0.0 - version: 7.0.0 - rehype-sanitize: - specifier: ^6.0.0 - version: 6.0.0 + version: 10.1.0(@types/react@19.2.2)(react@19.2.0) rxjs: specifier: ^7.8.2 version: 7.8.2 - scheduler: - specifier: ^0.26.0 - version: 0.26.0 - text-case: - specifier: ^1.2.9 - version: 1.2.9 - typesafe-i18n: - specifier: ^5.26.2 - version: 5.26.2(typescript@5.9.2) - use-breakpoint: - specifier: ^4.0.6 - version: 4.0.6(react-dom@19.1.1(react@19.1.1))(react@19.1.1) zod: - specifier: ^3.25.76 - version: 3.25.76 + specifier: ^4.1.12 + version: 4.1.12 zustand: specifier: ^5.0.8 - version: 5.0.8(@types/react@19.1.13)(immer@10.1.3)(react@19.1.1)(use-sync-external-store@1.5.0(react@19.1.1)) + version: 5.0.8(@types/react@19.2.2)(react@19.2.0)(use-sync-external-store@1.6.0(react@19.2.0)) devDependencies: - '@babel/core': - specifier: ^7.28.4 - version: 7.28.4 '@biomejs/biome': - specifier: 2.2.2 - version: 2.2.2 - '@csstools/css-parser-algorithms': - specifier: ^3.0.5 - version: 3.0.5(@csstools/css-tokenizer@3.0.4) - '@csstools/css-tokenizer': - specifier: ^3.0.4 - version: 3.0.4 - '@hookform/devtools': - specifier: ^4.4.0 - version: 4.4.0(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - '@tanstack/react-query-devtools': - specifier: ^5.90.2 - version: 5.90.2(@tanstack/react-query@5.90.2(react@19.1.1))(react@19.1.1) - '@types/byte-size': - specifier: ^8.1.2 - version: 8.1.2 - '@types/file-saver': - specifier: ^2.0.7 - version: 2.0.7 - '@types/humanize-duration': - specifier: ^3.27.4 - version: 3.27.4 + specifier: 2.3.3 + version: 2.3.3 + '@tanstack/devtools-vite': + specifier: ^0.3.11 + version: 0.3.11(vite@7.1.12(@types/node@24.10.0)(sass@1.93.3)(tsx@4.20.6)) + '@tanstack/router-plugin': + specifier: ^1.134.9 + version: 1.134.9(@tanstack/react-router@1.134.9(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(vite@7.1.12(@types/node@24.10.0)(sass@1.93.3)(tsx@4.20.6)) '@types/lodash-es': specifier: ^4.17.12 version: 4.17.12 '@types/node': - specifier: ^24.5.2 - version: 24.5.2 + specifier: ^24.10.0 + version: 24.10.0 '@types/qs': specifier: ^6.14.0 version: 6.14.0 '@types/react': - specifier: ^19.1.13 - version: 19.1.13 + specifier: ^19.2.2 + version: 19.2.2 '@types/react-dom': - specifier: ^19.1.9 - version: 19.1.9(@types/react@19.1.13) - '@types/react-router-dom': - specifier: ^5.3.3 - version: 5.3.3 + specifier: ^19.2.2 + version: 19.2.2(@types/react@19.2.2) '@vitejs/plugin-react-swc': - specifier: ^4.1.0 - version: 4.1.0(vite@7.1.7(@types/node@24.5.2)(jiti@2.4.2)(sass@1.70.0)(terser@5.37.0)(yaml@2.6.1)) + specifier: ^4.2.0 + version: 4.2.0(vite@7.1.12(@types/node@24.10.0)(sass@1.93.3)(tsx@4.20.6)) autoprefixer: specifier: ^10.4.21 version: 10.4.21(postcss@8.5.6) - concurrently: - specifier: ^9.2.1 - version: 9.2.1 - dotenv: - specifier: ^17.2.2 - version: 17.2.2 - esbuild: - specifier: ^0.25.10 - version: 0.25.10 globals: - specifier: ^16.4.0 - version: 16.4.0 - postcss: - specifier: ^8.5.6 - version: 8.5.6 + specifier: ^16.5.0 + version: 16.5.0 prettier: specifier: ^3.6.2 version: 3.6.2 sass: - specifier: ~1.70.0 - version: 1.70.0 - standard-version: - specifier: ^9.5.0 - version: 9.5.0 - type-fest: - specifier: ^4.41.0 - version: 4.41.0 + specifier: ^1.93.3 + version: 1.93.3 + sharp: + specifier: ^0.34.4 + version: 0.34.4 + stylelint: + specifier: ^16.25.0 + version: 16.25.0(typescript@5.9.3) + stylelint-config-standard-scss: + specifier: ^16.0.0 + version: 16.0.0(postcss@8.5.6)(stylelint@16.25.0(typescript@5.9.3)) + stylelint-scss: + specifier: ^6.12.1 + version: 6.12.1(stylelint@16.25.0(typescript@5.9.3)) typescript: - specifier: ~5.9.2 - version: 5.9.2 + specifier: ~5.9.3 + version: 5.9.3 + typescript-eslint: + specifier: ^8.46.2 + version: 8.46.2(eslint@9.37.0)(typescript@5.9.3) vite: - specifier: ^7.1.7 - version: 7.1.7(@types/node@24.5.2)(jiti@2.4.2)(sass@1.70.0)(terser@5.37.0)(yaml@2.6.1) - vite-plugin-package-version: - specifier: ^1.1.0 - version: 1.1.0(vite@7.1.7(@types/node@24.5.2)(jiti@2.4.2)(sass@1.70.0)(terser@5.37.0)(yaml@2.6.1)) + specifier: ^7.1.12 + version: 7.1.12(@types/node@24.10.0)(sass@1.93.3)(tsx@4.20.6) + vite-plugin-image-optimizer: + specifier: ^2.0.3 + version: 2.0.3(sharp@0.34.4)(vite@7.1.12(@types/node@24.10.0)(sass@1.93.3)(tsx@4.20.6)) packages: + '@axa-ch/react-polymorphic-types@1.4.1': + resolution: {integrity: sha512-2lo5a9slPZbc9hVCC5Z7fXWimd+UWCtETi88uGxsZ6GG5s6sAnwxPDPZKZ8qR/riFSWROglxDGNBtK+8jKz2eQ==} + peerDependencies: + '@types/react': ^18.0.0 || ^19.0.0 + react: ^18.0.0 || ^19.0.0 + '@babel/code-frame@7.27.1': resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} engines: {node: '>=6.9.0'} - '@babel/compat-data@7.28.4': - resolution: {integrity: sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==} + '@babel/compat-data@7.28.5': + resolution: {integrity: sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.28.5': + resolution: {integrity: sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==} engines: {node: '>=6.9.0'} - '@babel/core@7.28.4': - resolution: {integrity: sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==} + '@babel/generator@7.28.5': + resolution: {integrity: sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==} engines: {node: '>=6.9.0'} - '@babel/generator@7.28.3': - resolution: {integrity: sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==} + '@babel/helper-annotate-as-pure@7.27.3': + resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==} engines: {node: '>=6.9.0'} '@babel/helper-compilation-targets@7.27.2': resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} engines: {node: '>=6.9.0'} + '@babel/helper-create-class-features-plugin@7.28.5': + resolution: {integrity: sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + '@babel/helper-globals@7.28.0': resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} engines: {node: '>=6.9.0'} + '@babel/helper-member-expression-to-functions@7.28.5': + resolution: {integrity: sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==} + engines: {node: '>=6.9.0'} + '@babel/helper-module-imports@7.27.1': resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} engines: {node: '>=6.9.0'} @@ -334,12 +216,30 @@ packages: peerDependencies: '@babel/core': ^7.0.0 + '@babel/helper-optimise-call-expression@7.27.1': + resolution: {integrity: sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-plugin-utils@7.27.1': + resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-replace-supers@7.27.1': + resolution: {integrity: sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-skip-transparent-expression-wrappers@7.27.1': + resolution: {integrity: sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==} + engines: {node: '>=6.9.0'} + '@babel/helper-string-parser@7.27.1': resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-identifier@7.27.1': - resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} + '@babel/helper-validator-identifier@7.28.5': + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} engines: {node: '>=6.9.0'} '@babel/helper-validator-option@7.27.1': @@ -350,80 +250,115 @@ packages: resolution: {integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==} engines: {node: '>=6.9.0'} - '@babel/parser@7.28.4': - resolution: {integrity: sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==} + '@babel/parser@7.28.5': + resolution: {integrity: sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==} engines: {node: '>=6.0.0'} hasBin: true - '@babel/runtime@7.28.4': - resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==} + '@babel/plugin-syntax-jsx@7.27.1': + resolution: {integrity: sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-typescript@7.27.1': + resolution: {integrity: sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-commonjs@7.27.1': + resolution: {integrity: sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-typescript@7.28.5': + resolution: {integrity: sha512-x2Qa+v/CuEoX7Dr31iAfr0IhInrVOWZU/2vJMJ00FOR/2nM0BcBEclpaf9sWCDc+v5e9dMrhSH8/atq/kX7+bA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/preset-typescript@7.28.5': + resolution: {integrity: sha512-+bQy5WOI2V6LJZpPVxY+yp66XdZ2yifu0Mc1aP5CQKgjn4QM5IN2i5fAZ4xKop47pr8rpVhiAeu+nDQa12C8+g==} engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 '@babel/template@7.27.2': resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} engines: {node: '>=6.9.0'} - '@babel/traverse@7.28.4': - resolution: {integrity: sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==} + '@babel/traverse@7.28.5': + resolution: {integrity: sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==} engines: {node: '>=6.9.0'} - '@babel/types@7.28.4': - resolution: {integrity: sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==} + '@babel/types@7.28.5': + resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} engines: {node: '>=6.9.0'} - '@biomejs/biome@2.2.2': - resolution: {integrity: sha512-j1omAiQWCkhuLgwpMKisNKnsM6W8Xtt1l0WZmqY/dFj8QPNkIoTvk4tSsi40FaAAkBE1PU0AFG2RWFBWenAn+w==} + '@biomejs/biome@2.3.3': + resolution: {integrity: sha512-zn/P1pRBCpDdhi+VNSMnpczOz9DnqzOA2c48K8xgxjDODvi5O8gs3a2H233rck/5HXpkFj6TmyoqVvxirZUnvg==} engines: {node: '>=14.21.3'} hasBin: true - '@biomejs/cli-darwin-arm64@2.2.2': - resolution: {integrity: sha512-6ePfbCeCPryWu0CXlzsWNZgVz/kBEvHiPyNpmViSt6A2eoDf4kXs3YnwQPzGjy8oBgQulrHcLnJL0nkCh80mlQ==} + '@biomejs/cli-darwin-arm64@2.3.3': + resolution: {integrity: sha512-5+JtW6RKmjqL9un0UtHV0ezOslAyYBzyl5ZhYiu7GHesX2x8NCDl6tXYrenv9m7e1RLbkO5E5Kh04kseMtz6lw==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [darwin] - '@biomejs/cli-darwin-x64@2.2.2': - resolution: {integrity: sha512-Tn4JmVO+rXsbRslml7FvKaNrlgUeJot++FkvYIhl1OkslVCofAtS35MPlBMhXgKWF9RNr9cwHanrPTUUXcYGag==} + '@biomejs/cli-darwin-x64@2.3.3': + resolution: {integrity: sha512-UPmKRalkHicvIpeccuKqq+/gA2HYV8FUnAEDJnqYBlGlycKqe6xrovWqvWTE4TTNpIFf4UQyuaDzLkN6Kz6tbA==} engines: {node: '>=14.21.3'} cpu: [x64] os: [darwin] - '@biomejs/cli-linux-arm64-musl@2.2.2': - resolution: {integrity: sha512-/MhYg+Bd6renn6i1ylGFL5snYUn/Ct7zoGVKhxnro3bwekiZYE8Kl39BSb0MeuqM+72sThkQv4TnNubU9njQRw==} + '@biomejs/cli-linux-arm64-musl@2.3.3': + resolution: {integrity: sha512-KhCDMV+V7Yu72v40ssGJTHuv/j0n7JQ6l0s/c+EMcX5zPYLMLr4XpmI+WXhp4Vfkz0T5Xnh5wbrTBI3f2UTpjQ==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [linux] - '@biomejs/cli-linux-arm64@2.2.2': - resolution: {integrity: sha512-JfrK3gdmWWTh2J5tq/rcWCOsImVyzUnOS2fkjhiYKCQ+v8PqM+du5cfB7G1kXas+7KQeKSWALv18iQqdtIMvzw==} + '@biomejs/cli-linux-arm64@2.3.3': + resolution: {integrity: sha512-zeiKwALNB/hax7+LLhCYqhqzlWdTfgE9BGkX2Z8S4VmCYnGFrf2fON/ec6KCos7mra5MDm6fYICsEWN2+HKZhw==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [linux] - '@biomejs/cli-linux-x64-musl@2.2.2': - resolution: {integrity: sha512-ZCLXcZvjZKSiRY/cFANKg+z6Fhsf9MHOzj+NrDQcM+LbqYRT97LyCLWy2AS+W2vP+i89RyRM+kbGpUzbRTYWig==} + '@biomejs/cli-linux-x64-musl@2.3.3': + resolution: {integrity: sha512-IyqQ+jYzU5MVy9CK5NV0U+NnUMPUAhYMrB/x4QgL/Dl1MqzBVc61bHeyhLnKM6DSEk73/TQYrk/8/QmVHudLdQ==} engines: {node: '>=14.21.3'} cpu: [x64] os: [linux] - '@biomejs/cli-linux-x64@2.2.2': - resolution: {integrity: sha512-Ogb+77edO5LEP/xbNicACOWVLt8mgC+E1wmpUakr+O4nKwLt9vXe74YNuT3T1dUBxC/SnrVmlzZFC7kQJEfquQ==} + '@biomejs/cli-linux-x64@2.3.3': + resolution: {integrity: sha512-05CjPLbvVVU8J6eaO6iSEoA0FXKy2l6ddL+1h/VpiosCmIp3HxRKLOa1hhC1n+D13Z8g9b1DtnglGtM5U3sTag==} engines: {node: '>=14.21.3'} cpu: [x64] os: [linux] - '@biomejs/cli-win32-arm64@2.2.2': - resolution: {integrity: sha512-wBe2wItayw1zvtXysmHJQoQqXlTzHSpQRyPpJKiNIR21HzH/CrZRDFic1C1jDdp+zAPtqhNExa0owKMbNwW9cQ==} + '@biomejs/cli-win32-arm64@2.3.3': + resolution: {integrity: sha512-NtlLs3pdFqFAQYZjlEHKOwJEn3GEaz7rtR2oCrzaLT2Xt3Cfd55/VvodQ5V+X+KepLa956QJagckJrNL+DmumQ==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [win32] - '@biomejs/cli-win32-x64@2.2.2': - resolution: {integrity: sha512-DAuHhHekGfiGb6lCcsT4UyxQmVwQiBCBUMwVra/dcOSs9q8OhfaZgey51MlekT3p8UwRqtXQfFuEJBhJNdLZwg==} + '@biomejs/cli-win32-x64@2.3.3': + resolution: {integrity: sha512-klJKPPQvUk9Rlp0Dd56gQw/+Wt6uUprHdHWtbDC93f3Iv+knA2tLWpcYoOZJgPV+9s+RBmYv0DGy4mUlr20esg==} engines: {node: '>=14.21.3'} cpu: [x64] os: [win32] + '@cacheable/memoize@2.0.3': + resolution: {integrity: sha512-hl9wfQgpiydhQEIv7fkjEzTGE+tcosCXLKFDO707wYJ/78FVOlowb36djex5GdbSyeHnG62pomYLMuV/OT8Pbw==} + + '@cacheable/memory@2.0.4': + resolution: {integrity: sha512-cCmJKCKlT1t7hNBI1+gFCwmKFd9I4pS3zqBeNGXTSODnpa0EeDmORHY8oEMTuozfdg3cgsVh8ojLaPYb6eC7Cg==} + + '@cacheable/utils@2.2.0': + resolution: {integrity: sha512-7xaQayO3msdVcxXLYcLU5wDqJBNdQcPPPHr6mdTEIQI7N7TbtSVVTpWOTfjyhg0L6AQwQdq7miKdWtTDBoBldQ==} + '@csstools/css-parser-algorithms@3.0.5': resolution: {integrity: sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==} engines: {node: '>=18'} @@ -434,216 +369,223 @@ packages: resolution: {integrity: sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==} engines: {node: '>=18'} - '@emotion/babel-plugin@11.13.5': - resolution: {integrity: sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==} - - '@emotion/cache@11.14.0': - resolution: {integrity: sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==} - - '@emotion/hash@0.9.2': - resolution: {integrity: sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==} - - '@emotion/is-prop-valid@1.4.0': - resolution: {integrity: sha512-QgD4fyscGcbbKwJmqNvUMSE02OsHUa+lAWKdEUIJKgqe5IwRSKd7+KhibEWdaKwgjLj0DRSHA9biAIqGBk05lw==} - - '@emotion/memoize@0.9.0': - resolution: {integrity: sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==} - - '@emotion/react@11.14.0': - resolution: {integrity: sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==} - peerDependencies: - '@types/react': '*' - react: '>=16.8.0' - peerDependenciesMeta: - '@types/react': - optional: true - - '@emotion/serialize@1.3.3': - resolution: {integrity: sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==} - - '@emotion/sheet@1.4.0': - resolution: {integrity: sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==} - - '@emotion/styled@11.14.1': - resolution: {integrity: sha512-qEEJt42DuToa3gurlH4Qqc1kVpNq8wO8cJtDzU46TjlzWjDlsVyevtYCRijVq3SrHsROS+gVQ8Fnea108GnKzw==} + '@csstools/media-query-list-parser@4.0.3': + resolution: {integrity: sha512-HAYH7d3TLRHDOUQK4mZKf9k9Ph/m8Akstg66ywKR4SFAigjs3yBiUeZtFxywiTm5moZMAp/5W/ZuFnNXXYLuuQ==} + engines: {node: '>=18'} peerDependencies: - '@emotion/react': ^11.0.0-rc.0 - '@types/react': '*' - react: '>=16.8.0' - peerDependenciesMeta: - '@types/react': - optional: true - - '@emotion/unitless@0.10.0': - resolution: {integrity: sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==} + '@csstools/css-parser-algorithms': ^3.0.5 + '@csstools/css-tokenizer': ^3.0.4 - '@emotion/use-insertion-effect-with-fallbacks@1.2.0': - resolution: {integrity: sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==} + '@csstools/selector-specificity@5.0.0': + resolution: {integrity: sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw==} + engines: {node: '>=18'} peerDependencies: - react: '>=16.8.0' + postcss-selector-parser: ^7.0.0 - '@emotion/utils@1.4.2': - resolution: {integrity: sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==} + '@dual-bundle/import-meta-resolve@4.2.1': + resolution: {integrity: sha512-id+7YRUgoUX6CgV0DtuhirQWodeeA7Lf4i2x71JS/vtA5pRb/hIGWlw+G6MeXvsM+MXrz0VAydTGElX1rAfgPg==} - '@emotion/weak-memoize@0.4.0': - resolution: {integrity: sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==} + '@emnapi/runtime@1.7.0': + resolution: {integrity: sha512-oAYoQnCYaQZKVS53Fq23ceWMRxq5EhQsE0x0RdQ55jT7wagMu5k+fS39v1fiSLrtrLQlXwVINenqhLMtTrV/1Q==} - '@esbuild/aix-ppc64@0.25.10': - resolution: {integrity: sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw==} + '@esbuild/aix-ppc64@0.25.12': + resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.25.10': - resolution: {integrity: sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg==} + '@esbuild/android-arm64@0.25.12': + resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.25.10': - resolution: {integrity: sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w==} + '@esbuild/android-arm@0.25.12': + resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.25.10': - resolution: {integrity: sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg==} + '@esbuild/android-x64@0.25.12': + resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.25.10': - resolution: {integrity: sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA==} + '@esbuild/darwin-arm64@0.25.12': + resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.25.10': - resolution: {integrity: sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg==} + '@esbuild/darwin-x64@0.25.12': + resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.25.10': - resolution: {integrity: sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg==} + '@esbuild/freebsd-arm64@0.25.12': + resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.25.10': - resolution: {integrity: sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA==} + '@esbuild/freebsd-x64@0.25.12': + resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.25.10': - resolution: {integrity: sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ==} + '@esbuild/linux-arm64@0.25.12': + resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.25.10': - resolution: {integrity: sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg==} + '@esbuild/linux-arm@0.25.12': + resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.25.10': - resolution: {integrity: sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ==} + '@esbuild/linux-ia32@0.25.12': + resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.25.10': - resolution: {integrity: sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg==} + '@esbuild/linux-loong64@0.25.12': + resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.25.10': - resolution: {integrity: sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA==} + '@esbuild/linux-mips64el@0.25.12': + resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.25.10': - resolution: {integrity: sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA==} + '@esbuild/linux-ppc64@0.25.12': + resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.25.10': - resolution: {integrity: sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA==} + '@esbuild/linux-riscv64@0.25.12': + resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.25.10': - resolution: {integrity: sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew==} + '@esbuild/linux-s390x@0.25.12': + resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.25.10': - resolution: {integrity: sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA==} + '@esbuild/linux-x64@0.25.12': + resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-arm64@0.25.10': - resolution: {integrity: sha512-AKQM3gfYfSW8XRk8DdMCzaLUFB15dTrZfnX8WXQoOUpUBQ+NaAFCP1kPS/ykbbGYz7rxn0WS48/81l9hFl3u4A==} + '@esbuild/netbsd-arm64@0.25.12': + resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-x64@0.25.10': - resolution: {integrity: sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig==} + '@esbuild/netbsd-x64@0.25.12': + resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.25.10': - resolution: {integrity: sha512-5Se0VM9Wtq797YFn+dLimf2Zx6McttsH2olUBsDml+lm0GOCRVebRWUvDtkY4BWYv/3NgzS8b/UM3jQNh5hYyw==} + '@esbuild/openbsd-arm64@0.25.12': + resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-x64@0.25.10': - resolution: {integrity: sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw==} + '@esbuild/openbsd-x64@0.25.12': + resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/openharmony-arm64@0.25.10': - resolution: {integrity: sha512-AVTSBhTX8Y/Fz6OmIVBip9tJzZEUcY8WLh7I59+upa5/GPhh2/aM6bvOMQySspnCCHvFi79kMtdJS1w0DXAeag==} + '@esbuild/openharmony-arm64@0.25.12': + resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] - '@esbuild/sunos-x64@0.25.10': - resolution: {integrity: sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ==} + '@esbuild/sunos-x64@0.25.12': + resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.25.10': - resolution: {integrity: sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw==} + '@esbuild/win32-arm64@0.25.12': + resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.25.10': - resolution: {integrity: sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw==} + '@esbuild/win32-ia32@0.25.12': + resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.25.10': - resolution: {integrity: sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw==} + '@esbuild/win32-x64@0.25.12': + resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} engines: {node: '>=18'} cpu: [x64] os: [win32] + '@eslint-community/eslint-utils@4.9.0': + resolution: {integrity: sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.2': + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/config-array@0.21.1': + resolution: {integrity: sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-helpers@0.4.2': + resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.16.0': + resolution: {integrity: sha512-nmC8/totwobIiFcGkDza3GIKfAw1+hLiYVrh3I1nIomQ8PEr5cxg34jnkmGawul/ep52wGRAcyeDCNtWKSOj4Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.17.0': + resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/eslintrc@3.3.1': + resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.37.0': + resolution: {integrity: sha512-jaS+NJ+hximswBG6pjNX0uEJZkrT0zwpVi3BA3vX22aFGjJjmgSTSmPpZCRKmoBL5VY/M6p0xsSJx7rk7sy5gg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.7': + resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.4.1': + resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@floating-ui/core@1.7.3': resolution: {integrity: sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==} @@ -665,25 +607,158 @@ packages: '@floating-ui/utils@0.2.10': resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==} - '@github/webauthn-json@2.1.1': - resolution: {integrity: sha512-XrftRn4z75SnaJOmZQbt7Mk+IIjqVHw+glDGOxuHwXkZBZh/MBoRS7MHjSZMDaLhT4RjN2VqiEU7EOYleuJWSQ==} - deprecated: 'Deprecated: Modern browsers support built-in WebAuthn JSON methods. Please use native browser methods instead. For more information, visit https://github.com/github/webauthn-json' - hasBin: true + '@humanfs/core@0.19.1': + resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} + engines: {node: '>=18.18.0'} - '@hookform/devtools@4.4.0': - resolution: {integrity: sha512-Mtlic+uigoYBPXlfvPBfiYYUZuyMrD3pTjDpVIhL6eCZTvQkHsKBSKeZCvXWUZr8fqrkzDg27N+ZuazLKq6Vmg==} - peerDependencies: - react: ^16.8.0 || ^17 || ^18 || ^19 - react-dom: ^16.8.0 || ^17 || ^18 || ^19 + '@humanfs/node@0.16.7': + resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==} + engines: {node: '>=18.18.0'} - '@hookform/resolvers@5.2.2': - resolution: {integrity: sha512-A/IxlMLShx3KjV/HeTcTfaMxdwy690+L/ZADoeaTltLx+CVuzkeVIPuybK3jrRfw7YZnmdKsVVHAlEPIAEUNlA==} - peerDependencies: - react-hook-form: ^7.55.0 + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} - '@hutson/parse-repository-url@3.0.2': - resolution: {integrity: sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q==} - engines: {node: '>=6.9.0'} + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} + + '@img/colour@1.0.0': + resolution: {integrity: sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==} + engines: {node: '>=18'} + + '@img/sharp-darwin-arm64@0.34.4': + resolution: {integrity: sha512-sitdlPzDVyvmINUdJle3TNHl+AG9QcwiAMsXmccqsCOMZNIdW2/7S26w0LyU8euiLVzFBL3dXPwVCq/ODnf2vA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [darwin] + + '@img/sharp-darwin-x64@0.34.4': + resolution: {integrity: sha512-rZheupWIoa3+SOdF/IcUe1ah4ZDpKBGWcsPX6MT0lYniH9micvIU7HQkYTfrx5Xi8u+YqwLtxC/3vl8TQN6rMg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-darwin-arm64@1.2.3': + resolution: {integrity: sha512-QzWAKo7kpHxbuHqUC28DZ9pIKpSi2ts2OJnoIGI26+HMgq92ZZ4vk8iJd4XsxN+tYfNJxzH6W62X5eTcsBymHw==} + cpu: [arm64] + os: [darwin] + + '@img/sharp-libvips-darwin-x64@1.2.3': + resolution: {integrity: sha512-Ju+g2xn1E2AKO6YBhxjj+ACcsPQRHT0bhpglxcEf+3uyPY+/gL8veniKoo96335ZaPo03bdDXMv0t+BBFAbmRA==} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-linux-arm64@1.2.3': + resolution: {integrity: sha512-I4RxkXU90cpufazhGPyVujYwfIm9Nk1QDEmiIsaPwdnm013F7RIceaCc87kAH+oUB1ezqEvC6ga4m7MSlqsJvQ==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linux-arm@1.2.3': + resolution: {integrity: sha512-x1uE93lyP6wEwGvgAIV0gP6zmaL/a0tGzJs/BIDDG0zeBhMnuUPm7ptxGhUbcGs4okDJrk4nxgrmxpib9g6HpA==} + cpu: [arm] + os: [linux] + + '@img/sharp-libvips-linux-ppc64@1.2.3': + resolution: {integrity: sha512-Y2T7IsQvJLMCBM+pmPbM3bKT/yYJvVtLJGfCs4Sp95SjvnFIjynbjzsa7dY1fRJX45FTSfDksbTp6AGWudiyCg==} + cpu: [ppc64] + os: [linux] + + '@img/sharp-libvips-linux-s390x@1.2.3': + resolution: {integrity: sha512-RgWrs/gVU7f+K7P+KeHFaBAJlNkD1nIZuVXdQv6S+fNA6syCcoboNjsV2Pou7zNlVdNQoQUpQTk8SWDHUA3y/w==} + cpu: [s390x] + os: [linux] + + '@img/sharp-libvips-linux-x64@1.2.3': + resolution: {integrity: sha512-3JU7LmR85K6bBiRzSUc/Ff9JBVIFVvq6bomKE0e63UXGeRw2HPVEjoJke1Yx+iU4rL7/7kUjES4dZ/81Qjhyxg==} + cpu: [x64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-arm64@1.2.3': + resolution: {integrity: sha512-F9q83RZ8yaCwENw1GieztSfj5msz7GGykG/BA+MOUefvER69K/ubgFHNeSyUu64amHIYKGDs4sRCMzXVj8sEyw==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-x64@1.2.3': + resolution: {integrity: sha512-U5PUY5jbc45ANM6tSJpsgqmBF/VsL6LnxJmIf11kB7J5DctHgqm0SkuXzVWtIY90GnJxKnC/JT251TDnk1fu/g==} + cpu: [x64] + os: [linux] + + '@img/sharp-linux-arm64@0.34.4': + resolution: {integrity: sha512-YXU1F/mN/Wu786tl72CyJjP/Ngl8mGHN1hST4BGl+hiW5jhCnV2uRVTNOcaYPs73NeT/H8Upm3y9582JVuZHrQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + + '@img/sharp-linux-arm@0.34.4': + resolution: {integrity: sha512-Xyam4mlqM0KkTHYVSuc6wXRmM7LGN0P12li03jAnZ3EJWZqj83+hi8Y9UxZUbxsgsK1qOEwg7O0Bc0LjqQVtxA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm] + os: [linux] + + '@img/sharp-linux-ppc64@0.34.4': + resolution: {integrity: sha512-F4PDtF4Cy8L8hXA2p3TO6s4aDt93v+LKmpcYFLAVdkkD3hSxZzee0rh6/+94FpAynsuMpLX5h+LRsSG3rIciUQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ppc64] + os: [linux] + + '@img/sharp-linux-s390x@0.34.4': + resolution: {integrity: sha512-qVrZKE9Bsnzy+myf7lFKvng6bQzhNUAYcVORq2P7bDlvmF6u2sCmK2KyEQEBdYk+u3T01pVsPrkj943T1aJAsw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [s390x] + os: [linux] + + '@img/sharp-linux-x64@0.34.4': + resolution: {integrity: sha512-ZfGtcp2xS51iG79c6Vhw9CWqQC8l2Ot8dygxoDoIQPTat/Ov3qAa8qpxSrtAEAJW+UjTXc4yxCjNfxm4h6Xm2A==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + + '@img/sharp-linuxmusl-arm64@0.34.4': + resolution: {integrity: sha512-8hDVvW9eu4yHWnjaOOR8kHVrew1iIX+MUgwxSuH2XyYeNRtLUe4VNioSqbNkB7ZYQJj9rUTT4PyRscyk2PXFKA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + + '@img/sharp-linuxmusl-x64@0.34.4': + resolution: {integrity: sha512-lU0aA5L8QTlfKjpDCEFOZsTYGn3AEiO6db8W5aQDxj0nQkVrZWmN3ZP9sYKWJdtq3PWPhUNlqehWyXpYDcI9Sg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + + '@img/sharp-wasm32@0.34.4': + resolution: {integrity: sha512-33QL6ZO/qpRyG7woB/HUALz28WnTMI2W1jgX3Nu2bypqLIKx/QKMILLJzJjI+SIbvXdG9fUnmrxR7vbi1sTBeA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [wasm32] + + '@img/sharp-win32-arm64@0.34.4': + resolution: {integrity: sha512-2Q250do/5WXTwxW3zjsEuMSv5sUU4Tq9VThWKlU2EYLm4MB7ZeMwF+SFJutldYODXF6jzc6YEOC+VfX0SZQPqA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [win32] + + '@img/sharp-win32-ia32@0.34.4': + resolution: {integrity: sha512-3ZeLue5V82dT92CNL6rsal6I2weKw1cYu+rGKm8fOCCtJTR2gYeUfY3FqUnIJsMUPIH68oS5jmZ0NiJ508YpEw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ia32] + os: [win32] + + '@img/sharp-win32-x64@0.34.4': + resolution: {integrity: sha512-xIyj4wpYs8J18sVN3mSQjwrw7fKUqRw+Z5rnHNCy5fYTxigBz81u5mOMPmFumwjcn8+ld1ppptMBCLic1nz6ig==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [win32] + + '@inlang/paraglide-js@2.4.0': + resolution: {integrity: sha512-T/m9uoev574/1JrhCnPcgK1xnAwkVMgaDev4LFthnmID8ubX2xjboSGO3IztwXWwO0aJoT1UJr89JCwjbwgnJQ==} + hasBin: true + + '@inlang/recommend-sherlock@0.2.1': + resolution: {integrity: sha512-ckv8HvHy/iTqaVAEKrr+gnl+p3XFNwe5D2+6w6wJk2ORV2XkcRkKOJ/XsTUJbPSiyi4PI+p+T3bqbmNx/rDUlg==} + + '@inlang/sdk@2.4.9': + resolution: {integrity: sha512-cvz/C1rF5WBxzHbEoiBoI6Sz6q6M+TdxfWkEGBYTD77opY8i8WN01prUWXEM87GPF4SZcyIySez9U0Ccm12oFQ==} + engines: {node: '>=18.0.0'} '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} @@ -695,168 +770,274 @@ packages: resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} - '@jridgewell/source-map@0.3.11': - resolution: {integrity: sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==} - '@jridgewell/sourcemap-codec@1.5.5': resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} - '@react-hook/latest@1.0.3': - resolution: {integrity: sha512-dy6duzl+JnAZcDbNTfmaP3xHiKtbXYOaz3G51MGVljh548Y8MWzTr+PHLOfvpypEVW9zwvl+VyKjbWKEVbV1Rg==} + '@keyv/bigmap@1.1.0': + resolution: {integrity: sha512-MX7XIUNwVRK+hjZcAbNJ0Z8DREo+Weu9vinBOjGU1thEi9F6vPhICzBbk4CCf3eEefKRz7n6TfZXwUFZTSgj8Q==} + engines: {node: '>= 18'} peerDependencies: - react: '>=16.8' + keyv: ^5.5.3 - '@react-hook/passive-layout-effect@1.2.1': - resolution: {integrity: sha512-IwEphTD75liO8g+6taS+4oqz+nnroocNfWVHWz7j+N+ZO2vYrc6PV1q7GQhuahL0IOR7JccFTsFKQ/mb6iZWAg==} - peerDependencies: - react: '>=16.8' + '@keyv/serialize@1.1.1': + resolution: {integrity: sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==} - '@react-hook/resize-observer@2.0.2': - resolution: {integrity: sha512-tzKKzxNpfE5TWmxuv+5Ae3IF58n0FQgQaWJmcbYkjXTRZATXxClnTprQ2uuYygYTpu1pqbBskpwMpj6jpT1djA==} - peerDependencies: - react: '>=18' + '@lix-js/sdk@0.4.7': + resolution: {integrity: sha512-pRbW+joG12L0ULfMiWYosIW0plmW4AsUdiPCp+Z8rAsElJ+wJ6in58zhD3UwUcd4BNcpldEGjg6PdA7e0RgsDQ==} + engines: {node: '>=18'} - '@react-rxjs/core@0.10.8': - resolution: {integrity: sha512-vCA7dDpJ7whvBJCerCqY5wvrPnIo4EvxYihQNuDy0u0OhN4kYafs2H755sMLeUXBwSihiskd9Z3v8SHpmcEdzQ==} - peerDependencies: - react: '>=16.8.0' - rxjs: '>=7' + '@lix-js/server-protocol-schema@0.1.1': + resolution: {integrity: sha512-jBeALB6prAbtr5q4vTuxnRZZv1M2rKe8iNqRQhFJ4Tv7150unEa0vKyz0hs8Gl3fUGsWaNJBh3J8++fpbrpRBQ==} - '@reduxjs/toolkit@2.9.0': - resolution: {integrity: sha512-fSfQlSRu9Z5yBkvsNhYF2rPS8cGXn/TZVrlwN1948QyZ8xMZ0JvP50S2acZNaf+o63u6aEeMjipFyksjIcWrog==} - peerDependencies: - react: ^16.9.0 || ^17.0.0 || ^18 || ^19 - react-redux: ^7.2.1 || ^8.1.3 || ^9.0.0 - peerDependenciesMeta: - react: - optional: true - react-redux: - optional: true + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} - '@remix-run/router@1.23.0': - resolution: {integrity: sha512-O3rHJzAQKamUz1fvE0Qaw0xSFqsA/yafi2iqeE0pvdFtCO1viYx8QL6f3Ln/aCCTLxs68SLf0KPM9eSeM8yBnA==} - engines: {node: '>=14.0.0'} + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@parcel/watcher-android-arm64@2.5.1': + resolution: {integrity: sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [android] + + '@parcel/watcher-darwin-arm64@2.5.1': + resolution: {integrity: sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [darwin] + + '@parcel/watcher-darwin-x64@2.5.1': + resolution: {integrity: sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [darwin] + + '@parcel/watcher-freebsd-x64@2.5.1': + resolution: {integrity: sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [freebsd] + + '@parcel/watcher-linux-arm-glibc@2.5.1': + resolution: {integrity: sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==} + engines: {node: '>= 10.0.0'} + cpu: [arm] + os: [linux] + + '@parcel/watcher-linux-arm-musl@2.5.1': + resolution: {integrity: sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==} + engines: {node: '>= 10.0.0'} + cpu: [arm] + os: [linux] + + '@parcel/watcher-linux-arm64-glibc@2.5.1': + resolution: {integrity: sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + + '@parcel/watcher-linux-arm64-musl@2.5.1': + resolution: {integrity: sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + + '@parcel/watcher-linux-x64-glibc@2.5.1': + resolution: {integrity: sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + + '@parcel/watcher-linux-x64-musl@2.5.1': + resolution: {integrity: sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + + '@parcel/watcher-win32-arm64@2.5.1': + resolution: {integrity: sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [win32] + + '@parcel/watcher-win32-ia32@2.5.1': + resolution: {integrity: sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==} + engines: {node: '>= 10.0.0'} + cpu: [ia32] + os: [win32] + + '@parcel/watcher-win32-x64@2.5.1': + resolution: {integrity: sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [win32] + + '@parcel/watcher@2.5.1': + resolution: {integrity: sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==} + engines: {node: '>= 10.0.0'} - '@rolldown/pluginutils@1.0.0-beta.35': - resolution: {integrity: sha512-slYrCpoxJUqzFDDNlvrOYRazQUNRvWPjXA17dAOISY3rDMxX6k8K4cj2H+hEYMHF81HO3uNd5rHVigAWRM5dSg==} + '@rolldown/pluginutils@1.0.0-beta.43': + resolution: {integrity: sha512-5Uxg7fQUCmfhax7FJke2+8B6cqgeUJUD9o2uXIKXhD+mG0mL6NObmVoi9wXEU1tY89mZKgAYA6fTbftx3q2ZPQ==} - '@rollup/rollup-android-arm-eabi@4.52.2': - resolution: {integrity: sha512-o3pcKzJgSGt4d74lSZ+OCnHwkKBeAbFDmbEm5gg70eA8VkyCuC/zV9TwBnmw6VjDlRdF4Pshfb+WE9E6XY1PoQ==} + '@rollup/rollup-android-arm-eabi@4.52.5': + resolution: {integrity: sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.52.2': - resolution: {integrity: sha512-cqFSWO5tX2vhC9hJTK8WAiPIm4Q8q/cU8j2HQA0L3E1uXvBYbOZMhE2oFL8n2pKB5sOCHY6bBuHaRwG7TkfJyw==} + '@rollup/rollup-android-arm64@4.52.5': + resolution: {integrity: sha512-mQGfsIEFcu21mvqkEKKu2dYmtuSZOBMmAl5CFlPGLY94Vlcm+zWApK7F/eocsNzp8tKmbeBP8yXyAbx0XHsFNA==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.52.2': - resolution: {integrity: sha512-vngduywkkv8Fkh3wIZf5nFPXzWsNsVu1kvtLETWxTFf/5opZmflgVSeLgdHR56RQh71xhPhWoOkEBvbehwTlVA==} + '@rollup/rollup-darwin-arm64@4.52.5': + resolution: {integrity: sha512-takF3CR71mCAGA+v794QUZ0b6ZSrgJkArC+gUiG6LB6TQty9T0Mqh3m2ImRBOxS2IeYBo4lKWIieSvnEk2OQWA==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.52.2': - resolution: {integrity: sha512-h11KikYrUCYTrDj6h939hhMNlqU2fo/X4NB0OZcys3fya49o1hmFaczAiJWVAFgrM1NCP6RrO7lQKeVYSKBPSQ==} + '@rollup/rollup-darwin-x64@4.52.5': + resolution: {integrity: sha512-W901Pla8Ya95WpxDn//VF9K9u2JbocwV/v75TE0YIHNTbhqUTv9w4VuQ9MaWlNOkkEfFwkdNhXgcLqPSmHy0fA==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.52.2': - resolution: {integrity: sha512-/eg4CI61ZUkLXxMHyVlmlGrSQZ34xqWlZNW43IAU4RmdzWEx0mQJ2mN/Cx4IHLVZFL6UBGAh+/GXhgvGb+nVxw==} + '@rollup/rollup-freebsd-arm64@4.52.5': + resolution: {integrity: sha512-QofO7i7JycsYOWxe0GFqhLmF6l1TqBswJMvICnRUjqCx8b47MTo46W8AoeQwiokAx3zVryVnxtBMcGcnX12LvA==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.52.2': - resolution: {integrity: sha512-QOWgFH5X9+p+S1NAfOqc0z8qEpJIoUHf7OWjNUGOeW18Mx22lAUOiA9b6r2/vpzLdfxi/f+VWsYjUOMCcYh0Ng==} + '@rollup/rollup-freebsd-x64@4.52.5': + resolution: {integrity: sha512-jr21b/99ew8ujZubPo9skbrItHEIE50WdV86cdSoRkKtmWa+DDr6fu2c/xyRT0F/WazZpam6kk7IHBerSL7LDQ==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.52.2': - resolution: {integrity: sha512-kDWSPafToDd8LcBYd1t5jw7bD5Ojcu12S3uT372e5HKPzQt532vW+rGFFOaiR0opxePyUkHrwz8iWYEyH1IIQA==} + '@rollup/rollup-linux-arm-gnueabihf@4.52.5': + resolution: {integrity: sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.52.2': - resolution: {integrity: sha512-gKm7Mk9wCv6/rkzwCiUC4KnevYhlf8ztBrDRT9g/u//1fZLapSRc+eDZj2Eu2wpJ+0RzUKgtNijnVIB4ZxyL+w==} + '@rollup/rollup-linux-arm-musleabihf@4.52.5': + resolution: {integrity: sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.52.2': - resolution: {integrity: sha512-66lA8vnj5mB/rtDNwPgrrKUOtCLVQypkyDa2gMfOefXK6rcZAxKLO9Fy3GkW8VkPnENv9hBkNOFfGLf6rNKGUg==} + '@rollup/rollup-linux-arm64-gnu@4.52.5': + resolution: {integrity: sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.52.2': - resolution: {integrity: sha512-s+OPucLNdJHvuZHuIz2WwncJ+SfWHFEmlC5nKMUgAelUeBUnlB4wt7rXWiyG4Zn07uY2Dd+SGyVa9oyLkVGOjA==} + '@rollup/rollup-linux-arm64-musl@4.52.5': + resolution: {integrity: sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loong64-gnu@4.52.2': - resolution: {integrity: sha512-8wTRM3+gVMDLLDdaT6tKmOE3lJyRy9NpJUS/ZRWmLCmOPIJhVyXwjBo+XbrrwtV33Em1/eCTd5TuGJm4+DmYjw==} + '@rollup/rollup-linux-loong64-gnu@4.52.5': + resolution: {integrity: sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA==} cpu: [loong64] os: [linux] - '@rollup/rollup-linux-ppc64-gnu@4.52.2': - resolution: {integrity: sha512-6yqEfgJ1anIeuP2P/zhtfBlDpXUb80t8DpbYwXQ3bQd95JMvUaqiX+fKqYqUwZXqdJDd8xdilNtsHM2N0cFm6A==} + '@rollup/rollup-linux-ppc64-gnu@4.52.5': + resolution: {integrity: sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.52.2': - resolution: {integrity: sha512-sshYUiYVSEI2B6dp4jMncwxbrUqRdNApF2c3bhtLAU0qA8Lrri0p0NauOsTWh3yCCCDyBOjESHMExonp7Nzc0w==} + '@rollup/rollup-linux-riscv64-gnu@4.52.5': + resolution: {integrity: sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-riscv64-musl@4.52.2': - resolution: {integrity: sha512-duBLgd+3pqC4MMwBrKkFxaZerUxZcYApQVC5SdbF5/e/589GwVvlRUnyqMFbM8iUSb1BaoX/3fRL7hB9m2Pj8Q==} + '@rollup/rollup-linux-riscv64-musl@4.52.5': + resolution: {integrity: sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.52.2': - resolution: {integrity: sha512-tzhYJJidDUVGMgVyE+PmxENPHlvvqm1KILjjZhB8/xHYqAGeizh3GBGf9u6WdJpZrz1aCpIIHG0LgJgH9rVjHQ==} + '@rollup/rollup-linux-s390x-gnu@4.52.5': + resolution: {integrity: sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.52.2': - resolution: {integrity: sha512-opH8GSUuVcCSSyHHcl5hELrmnk4waZoVpgn/4FDao9iyE4WpQhyWJ5ryl5M3ocp4qkRuHfyXnGqg8M9oKCEKRA==} + '@rollup/rollup-linux-x64-gnu@4.52.5': + resolution: {integrity: sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.52.2': - resolution: {integrity: sha512-LSeBHnGli1pPKVJ79ZVJgeZWWZXkEe/5o8kcn23M8eMKCUANejchJbF/JqzM4RRjOJfNRhKJk8FuqL1GKjF5oQ==} + '@rollup/rollup-linux-x64-musl@4.52.5': + resolution: {integrity: sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg==} cpu: [x64] os: [linux] - '@rollup/rollup-openharmony-arm64@4.52.2': - resolution: {integrity: sha512-uPj7MQ6/s+/GOpolavm6BPo+6CbhbKYyZHUDvZ/SmJM7pfDBgdGisFX3bY/CBDMg2ZO4utfhlApkSfZ92yXw7Q==} + '@rollup/rollup-openharmony-arm64@4.52.5': + resolution: {integrity: sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw==} cpu: [arm64] os: [openharmony] - '@rollup/rollup-win32-arm64-msvc@4.52.2': - resolution: {integrity: sha512-Z9MUCrSgIaUeeHAiNkm3cQyst2UhzjPraR3gYYfOjAuZI7tcFRTOD+4cHLPoS/3qinchth+V56vtqz1Tv+6KPA==} + '@rollup/rollup-win32-arm64-msvc@4.52.5': + resolution: {integrity: sha512-w0cDWVR6MlTstla1cIfOGyl8+qb93FlAVutcor14Gf5Md5ap5ySfQ7R9S/NjNaMLSFdUnKGEasmVnu3lCMqB7w==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.52.2': - resolution: {integrity: sha512-+GnYBmpjldD3XQd+HMejo+0gJGwYIOfFeoBQv32xF/RUIvccUz20/V6Otdv+57NE70D5pa8W/jVGDoGq0oON4A==} + '@rollup/rollup-win32-ia32-msvc@4.52.5': + resolution: {integrity: sha512-Aufdpzp7DpOTULJCuvzqcItSGDH73pF3ko/f+ckJhxQyHtp67rHw3HMNxoIdDMUITJESNE6a8uh4Lo4SLouOUg==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-gnu@4.52.2': - resolution: {integrity: sha512-ApXFKluSB6kDQkAqZOKXBjiaqdF1BlKi+/eqnYe9Ee7U2K3pUDKsIyr8EYm/QDHTJIM+4X+lI0gJc3TTRhd+dA==} + '@rollup/rollup-win32-x64-gnu@4.52.5': + resolution: {integrity: sha512-UGBUGPFp1vkj6p8wCRraqNhqwX/4kNQPS57BCFc8wYh0g94iVIW33wJtQAx3G7vrjjNtRaxiMUylM0ktp/TRSQ==} cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.52.2': - resolution: {integrity: sha512-ARz+Bs8kY6FtitYM96PqPEVvPXqEZmPZsSkXvyX19YzDqkCaIlhCieLLMI5hxO9SRZ2XtCtm8wxhy0iJ2jxNfw==} + '@rollup/rollup-win32-x64-msvc@4.52.5': + resolution: {integrity: sha512-TAcgQh2sSkykPRWLrdyy2AiceMckNf5loITqXxFI5VuQjS5tSuw3WlwdN8qv8vzjLAUTvYaH/mVjSFpbkFbpTg==} cpu: [x64] os: [win32] - '@rx-state/core@0.1.4': - resolution: {integrity: sha512-Z+3hjU2xh1HisLxt+W5hlYX/eGSDaXXP+ns82gq/PLZpkXLu0uwcNUh9RLY3Clq4zT+hSsA3vcpIGt6+UAb8rQ==} + '@shortercode/webzip@1.1.1-0': + resolution: {integrity: sha512-c+9LbU7MTqWDXS73Pf7vEm5AyFFfbuuHiMgqmApwkwD5BrlELKaSqKjofG4Gqd3c/NgF8fO0iR5NbPdE26jF2Q==} + + '@sinclair/typebox@0.31.28': + resolution: {integrity: sha512-/s55Jujywdw/Jpan+vsy6JZs1z2ZTGxTmbZTPiuSL2wz9mfzA2gN1zzaqmvfi4pq+uOt7Du85fkiwv5ymW84aQ==} + + '@solid-primitives/event-listener@2.4.3': + resolution: {integrity: sha512-h4VqkYFv6Gf+L7SQj+Y6puigL/5DIi7x5q07VZET7AWcS+9/G3WfIE9WheniHWJs51OEkRB43w6lDys5YeFceg==} + peerDependencies: + solid-js: ^1.6.12 + + '@solid-primitives/keyboard@1.3.3': + resolution: {integrity: sha512-9dQHTTgLBqyAI7aavtO+HnpTVJgWQA1ghBSrmLtMu1SMxLPDuLfuNr+Tk5udb4AL4Ojg7h9JrKOGEEDqsJXWJA==} + peerDependencies: + solid-js: ^1.6.12 + + '@solid-primitives/resize-observer@2.1.3': + resolution: {integrity: sha512-zBLje5E06TgOg93S7rGPldmhDnouNGhvfZVKOp+oG2XU8snA+GoCSSCz1M+jpNAg5Ek2EakU5UVQqL152WmdXQ==} + peerDependencies: + solid-js: ^1.6.12 + + '@solid-primitives/rootless@1.5.2': + resolution: {integrity: sha512-9HULb0QAzL2r47CCad0M+NKFtQ+LrGGNHZfteX/ThdGvKIg2o2GYhBooZubTCd/RTu2l2+Nw4s+dEfiDGvdrrQ==} peerDependencies: - rxjs: '>=7' + solid-js: ^1.6.12 + + '@solid-primitives/static-store@0.1.2': + resolution: {integrity: sha512-ReK+5O38lJ7fT+L6mUFvUr6igFwHBESZF+2Ug842s7fvlVeBdIVEdTCErygff6w7uR6+jrr7J8jQo+cYrEq4Iw==} + peerDependencies: + solid-js: ^1.6.12 + + '@solid-primitives/utils@6.3.2': + resolution: {integrity: sha512-hZ/M/qr25QOCcwDPOHtGjxTD8w2mNyVAYvcfgwzBHq2RwNqHNdDNsMZYap20+ruRwW4A3Cdkczyoz0TSxLCAPQ==} + peerDependencies: + solid-js: ^1.6.12 + + '@sqlite.org/sqlite-wasm@3.48.0-build4': + resolution: {integrity: sha512-hI6twvUkzOmyGZhQMza1gpfqErZxXRw6JEsiVjUbo7tFanVD+8Oil0Ih3l2nGzHdxPI41zFmfUQG7GHqhciKZQ==} + hasBin: true '@stablelib/base64@2.0.1': resolution: {integrity: sha512-P2z89A7N1ETt6RxgpVdDT2xlg8cnm3n6td0lY9gyK7EiWK3wdq388yFX/hLknkCC0we05OZAD1rfxlQJUbl5VQ==} @@ -882,74 +1063,68 @@ packages: '@stablelib/x25519@2.0.1': resolution: {integrity: sha512-qi04HS2puHaBf50kM/kes5QcZFGsx8yF0YmCjLCOa/LPmnBaKEKX9ZR82OnnCwMn72YH13R/bBZgr/UP0aPFfA==} - '@standard-schema/spec@1.0.0': - resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} - - '@standard-schema/utils@0.3.0': - resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==} - - '@swc/core-darwin-arm64@1.13.19': - resolution: {integrity: sha512-NxDyte9tCJSJ8+R62WDtqwg8eI57lubD52sHyGOfezpJBOPr36bUSGGLyO3Vod9zTGlOu2CpkuzA/2iVw92u1g==} + '@swc/core-darwin-arm64@1.14.0': + resolution: {integrity: sha512-uHPC8rlCt04nvYNczWzKVdgnRhxCa3ndKTBBbBpResOZsRmiwRAvByIGh599j+Oo6Z5eyTPrgY+XfJzVmXnN7Q==} engines: {node: '>=10'} cpu: [arm64] os: [darwin] - '@swc/core-darwin-x64@1.13.19': - resolution: {integrity: sha512-+w5DYrJndSygFFRDcuPYmx5BljD6oYnAohZ15K1L6SfORHp/BTSIbgSFRKPoyhjuIkDiq3W0um8RoMTOBAcQjQ==} + '@swc/core-darwin-x64@1.14.0': + resolution: {integrity: sha512-2SHrlpl68vtePRknv9shvM9YKKg7B9T13tcTg9aFCwR318QTYo+FzsKGmQSv9ox/Ua0Q2/5y2BNjieffJoo4nA==} engines: {node: '>=10'} cpu: [x64] os: [darwin] - '@swc/core-linux-arm-gnueabihf@1.13.19': - resolution: {integrity: sha512-7LlfgpdwwYq2q7himNkAAFo4q6jysMLFNoBH6GRP7WL29NcSsl5mPMJjmYZymK+sYq/9MTVieDTQvChzYDsapw==} + '@swc/core-linux-arm-gnueabihf@1.14.0': + resolution: {integrity: sha512-SMH8zn01dxt809svetnxpeg/jWdpi6dqHKO3Eb11u4OzU2PK7I5uKS6gf2hx5LlTbcJMFKULZiVwjlQLe8eqtg==} engines: {node: '>=10'} cpu: [arm] os: [linux] - '@swc/core-linux-arm64-gnu@1.13.19': - resolution: {integrity: sha512-ml3I6Lm2marAQ3UC/TS9t/yILBh/eDSVHAdPpikp652xouWAVW1znUeV6bBSxe1sSZIenv+p55ubKAWq/u84sQ==} + '@swc/core-linux-arm64-gnu@1.14.0': + resolution: {integrity: sha512-q2JRu2D8LVqGeHkmpVCljVNltG0tB4o4eYg+dElFwCS8l2Mnt9qurMCxIeo9mgoqz0ax+k7jWtIRHktnVCbjvQ==} engines: {node: '>=10'} cpu: [arm64] os: [linux] - '@swc/core-linux-arm64-musl@1.13.19': - resolution: {integrity: sha512-M/otFc3/rWWkbF6VgbOXVzUKVoE7MFcphTaStxJp4bwb7oP5slYlxMZN51Dk/OTOfvCDo9pTAFDKNyixbkXMDQ==} + '@swc/core-linux-arm64-musl@1.14.0': + resolution: {integrity: sha512-uofpVoPCEUjYIv454ZEZ3sLgMD17nIwlz2z7bsn7rl301Kt/01umFA7MscUovFfAK2IRGck6XB+uulMu6aFhKQ==} engines: {node: '>=10'} cpu: [arm64] os: [linux] - '@swc/core-linux-x64-gnu@1.13.19': - resolution: {integrity: sha512-NoMUKaOJEdouU4tKF88ggdDHFiRRING+gYLxDqnTfm+sUXaizB5OGBRzvSVDYSXQb1SuUuChnXFPFzwTWbt3ZQ==} + '@swc/core-linux-x64-gnu@1.14.0': + resolution: {integrity: sha512-quTTx1Olm05fBfv66DEBuOsOgqdypnZ/1Bh3yGXWY7ANLFeeRpCDZpljD9BSjdsNdPOlwJmEUZXMHtGm3v1TZQ==} engines: {node: '>=10'} cpu: [x64] os: [linux] - '@swc/core-linux-x64-musl@1.13.19': - resolution: {integrity: sha512-r6krlZwyu8SBaw24QuS1lau2I9q8M+eJV6ITz0rpb6P1Bx0elf9ii5Bhh8ddmIqXXH8kOGSjC/dwcdHbZqAhgw==} + '@swc/core-linux-x64-musl@1.14.0': + resolution: {integrity: sha512-caaNAu+aIqT8seLtCf08i8C3/UC5ttQujUjejhMcuS1/LoCKtNiUs4VekJd2UGt+pyuuSrQ6dKl8CbCfWvWeXw==} engines: {node: '>=10'} cpu: [x64] os: [linux] - '@swc/core-win32-arm64-msvc@1.13.19': - resolution: {integrity: sha512-awcZSIuxyVn0Dw28VjMvgk1qiDJ6CeQwHkZNUjg2UxVlq23zE01NMMp+zkoGFypmLG9gaGmJSzuoqvk/WCQ5tw==} + '@swc/core-win32-arm64-msvc@1.14.0': + resolution: {integrity: sha512-EeW3jFlT3YNckJ6V/JnTfGcX7UHGyh6/AiCPopZ1HNaGiXVCKHPpVQZicmtyr/UpqxCXLrTgjHOvyMke7YN26A==} engines: {node: '>=10'} cpu: [arm64] os: [win32] - '@swc/core-win32-ia32-msvc@1.13.19': - resolution: {integrity: sha512-H5d+KO7ISoLNgYvTbOcCQjJZNM3R7yaYlrMAF13lUr6GSiOUX+92xtM31B+HvzAWI7HtvVe74d29aC1b1TpXFA==} + '@swc/core-win32-ia32-msvc@1.14.0': + resolution: {integrity: sha512-dPai3KUIcihV5hfoO4QNQF5HAaw8+2bT7dvi8E5zLtecW2SfL3mUZipzampXq5FHll0RSCLzlrXnSx+dBRZIIQ==} engines: {node: '>=10'} cpu: [ia32] os: [win32] - '@swc/core-win32-x64-msvc@1.13.19': - resolution: {integrity: sha512-qNoyCpXvv2O3JqXKanRIeoMn03Fho/As+N4Fhe7u0FsYh4VYqGQah4DGDzEP/yjl4Gx1IElhqLGDhCCGMwWaDw==} + '@swc/core-win32-x64-msvc@1.14.0': + resolution: {integrity: sha512-nm+JajGrTqUA6sEHdghDlHMNfH1WKSiuvljhdmBACW4ta4LC3gKurX2qZuiBARvPkephW9V/i5S8QPY1PzFEqg==} engines: {node: '>=10'} cpu: [x64] os: [win32] - '@swc/core@1.13.19': - resolution: {integrity: sha512-V1r4wFdjaZIUIZZrV2Mb/prEeu03xvSm6oatPxsvnXKF9lNh5Jtk9QvUdiVfD9rrvi7bXrAVhg9Wpbmv/2Fl1g==} + '@swc/core@1.14.0': + resolution: {integrity: sha512-oExhY90bes5pDTVrei0xlMVosTxwd/NMafIpqsC4dMbRYZ5KB981l/CX8tMnGsagTplj/RcG9BeRYmV6/J5m3w==} engines: {node: '>=10'} peerDependencies: '@swc/helpers': '>=0.5.17' @@ -963,61 +1138,183 @@ packages: '@swc/types@0.1.25': resolution: {integrity: sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g==} - '@tanstack/query-core@5.90.2': - resolution: {integrity: sha512-k/TcR3YalnzibscALLwxeiLUub6jN5EDLwKDiO7q5f4ICEoptJ+n9+7vcEFy5/x/i6Q+Lb/tXrsKCggf5uQJXQ==} + '@tanstack/devtools-client@0.0.4': + resolution: {integrity: sha512-LefnH9KE9uRDEWifc3QDcooskA8ikfs41bybDTgpYQpyTUspZnaEdUdya9Hry0KYxZ8nos0S3nNbsP79KHqr6Q==} + engines: {node: '>=18'} - '@tanstack/query-devtools@5.90.1': - resolution: {integrity: sha512-GtINOPjPUH0OegJExZ70UahT9ykmAhmtNVcmtdnOZbxLwT7R5OmRztR5Ahe3/Cu7LArEmR6/588tAycuaWb1xQ==} + '@tanstack/devtools-event-bus@0.3.3': + resolution: {integrity: sha512-lWl88uLAz7ZhwNdLH6A3tBOSEuBCrvnY9Fzr5JPdzJRFdM5ZFdyNWz1Bf5l/F3GU57VodrN0KCFi9OA26H5Kpg==} + engines: {node: '>=18'} - '@tanstack/react-query-devtools@5.90.2': - resolution: {integrity: sha512-vAXJzZuBXtCQtrY3F/yUNJCV4obT/A/n81kb3+YqLbro5Z2+phdAbceO+deU3ywPw8B42oyJlp4FhO0SoivDFQ==} + '@tanstack/devtools-event-client@0.3.4': + resolution: {integrity: sha512-eq+PpuutUyubXu+ycC1GIiVwBs86NF/8yYJJAKSpPcJLWl6R/761F1H4F/9ziX6zKezltFUH1ah3Cz8Ah+KJrw==} + engines: {node: '>=18'} + + '@tanstack/devtools-ui@0.4.4': + resolution: {integrity: sha512-5xHXFyX3nom0UaNfiOM92o6ziaHjGo3mcSGe2HD5Xs8dWRZNpdZ0Smd0B9ddEhy0oB+gXyMzZgUJb9DmrZV0Mg==} + engines: {node: '>=18'} peerDependencies: - '@tanstack/react-query': ^5.90.2 - react: ^18 || ^19 + solid-js: '>=1.9.7' - '@tanstack/react-query@5.90.2': - resolution: {integrity: sha512-CLABiR+h5PYfOWr/z+vWFt5VsOA2ekQeRQBFSKlcoW6Ndx/f8rfyVmq4LbgOM4GG2qtxAxjLYLOpCNTYm4uKzw==} + '@tanstack/devtools-vite@0.3.11': + resolution: {integrity: sha512-t5jaWJNgkXOQTxuNrwkz71cN86zPZnLJY2Rz0IaMDgjb0ib1EKHeRgdqHMR/2YL96yhCHHDCHroBQXsw5Da4dg==} + engines: {node: '>=18'} peerDependencies: - react: ^18 || ^19 + vite: ^6.0.0 || ^7.0.0 - '@tanstack/react-virtual@3.13.12': - resolution: {integrity: sha512-Gd13QdxPSukP8ZrkbgS2RwoZseTTbQPLnQEn7HY/rqtM+8Zt95f7xKC7N0EsKs7aoz0WzZ+fditZux+F8EzYxA==} + '@tanstack/devtools@0.8.0': + resolution: {integrity: sha512-j6tBkMpAyTkiGH4ELiHzFSpV0f9fCYtAMEqTEiXEOVVfB57C36KR8LyIHtdeGzK6eQ5sKEdgLbHG9EU31L3Jng==} + engines: {node: '>=18'} peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + solid-js: '>=1.9.7' - '@tanstack/virtual-core@3.13.12': - resolution: {integrity: sha512-1YBOJfRHV4sXUmWsFSf5rQor4Ss82G8dQWLRbnk3GA4jeP8hQt1hxXh0tmflpC0dz3VgEv/1+qwPyLeWkQuPFA==} + '@tanstack/form-core@1.24.4': + resolution: {integrity: sha512-+eIR7DiDamit1zvTVgaHxuIRA02YFgJaXMUGxsLRJoBpUjGl/g/nhUocQoNkRyfXqOlh8OCMTanjwDprWSRq6w==} - '@types/byte-size@8.1.2': - resolution: {integrity: sha512-jGyVzYu6avI8yuqQCNTZd65tzI8HZrLjKX9sdMqZrGWVlNChu0rf6p368oVEDCYJe5BMx2Ov04tD1wqtgTwGSA==} + '@tanstack/history@1.133.28': + resolution: {integrity: sha512-B7+x7eP2FFvi3fgd3rNH9o/Eixt+pp0zCIdGhnQbAJjFrlwIKGjGnwyJjhWJ5fMQlGks/E2LdDTqEV4W9Plx7g==} + engines: {node: '>=12'} - '@types/d3-array@3.2.2': - resolution: {integrity: sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==} + '@tanstack/pacer@0.15.4': + resolution: {integrity: sha512-vGY+CWsFZeac3dELgB6UZ4c7OacwsLb8hvL2gLS6hTgy8Fl0Bm/aLokHaeDIP+q9F9HUZTnp360z9uv78eg8pg==} + engines: {node: '>=18'} - '@types/d3-color@3.1.3': - resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==} + '@tanstack/query-core@5.90.6': + resolution: {integrity: sha512-AnZSLF26R8uX+tqb/ivdrwbVdGemdEDm1Q19qM6pry6eOZ6bEYiY7mWhzXT1YDIPTNEVcZ5kYP9nWjoxDLiIVw==} - '@types/d3-ease@3.0.2': - resolution: {integrity: sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==} + '@tanstack/query-devtools@5.90.1': + resolution: {integrity: sha512-GtINOPjPUH0OegJExZ70UahT9ykmAhmtNVcmtdnOZbxLwT7R5OmRztR5Ahe3/Cu7LArEmR6/588tAycuaWb1xQ==} - '@types/d3-interpolate@3.0.4': - resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==} + '@tanstack/react-devtools@0.8.0': + resolution: {integrity: sha512-0TsFICBPr68us3iWHFXCIBSEilTo8j1OdIJLW48LNQNjC/Puno82uqX4qFuaZWfZv6K37QnS6UeRxzWJItMFSA==} + engines: {node: '>=18'} + peerDependencies: + '@types/react': '>=16.8' + '@types/react-dom': '>=16.8' + react: '>=16.8' + react-dom: '>=16.8' + + '@tanstack/react-form@1.23.8': + resolution: {integrity: sha512-ivfkiOHAI3aIWkCY4FnPWVAL6SkQWGWNVjtwIZpaoJE4ulukZWZ1KB8TQKs8f4STl+egjTsMHrWJuf2fv3Xh1w==} + peerDependencies: + '@tanstack/react-start': ^1.130.10 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@tanstack/react-start': + optional: true + + '@tanstack/react-query-devtools@5.90.2': + resolution: {integrity: sha512-vAXJzZuBXtCQtrY3F/yUNJCV4obT/A/n81kb3+YqLbro5Z2+phdAbceO+deU3ywPw8B42oyJlp4FhO0SoivDFQ==} + peerDependencies: + '@tanstack/react-query': ^5.90.2 + react: ^18 || ^19 + + '@tanstack/react-query@5.90.6': + resolution: {integrity: sha512-gB1sljYjcobZKxjPbKSa31FUTyr+ROaBdoH+wSSs9Dk+yDCmMs+TkTV3PybRRVLC7ax7q0erJ9LvRWnMktnRAw==} + peerDependencies: + react: ^18 || ^19 + + '@tanstack/react-router-devtools@1.134.9': + resolution: {integrity: sha512-HwAOPK002wlnNgoHdYwchDqU9STl2ehQZOrTXoVOPgkUz5MMAHx/qcC/QZaJSU4znme4A081v8g7R8dI1aVNrw==} + engines: {node: '>=12'} + peerDependencies: + '@tanstack/react-router': ^1.134.9 + react: '>=18.0.0 || >=19.0.0' + react-dom: '>=18.0.0 || >=19.0.0' + + '@tanstack/react-router@1.134.9': + resolution: {integrity: sha512-JIxFamShs3gRIkOxpgz/3bglbSKZHMrzKASwNFg+sQPVXVPOLtN35D5PuEDAFTPPht9Wv48WWUNYE03ZytnNug==} + engines: {node: '>=12'} + peerDependencies: + react: '>=18.0.0 || >=19.0.0' + react-dom: '>=18.0.0 || >=19.0.0' + + '@tanstack/react-store@0.7.7': + resolution: {integrity: sha512-qqT0ufegFRDGSof9D/VqaZgjNgp4tRPHZIJq2+QIHkMUtHjaJ0lYrrXjeIUJvjnTbgPfSD1XgOMEt0lmANn6Zg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + '@tanstack/react-store@0.8.0': + resolution: {integrity: sha512-1vG9beLIuB7q69skxK9r5xiLN3ztzIPfSQSs0GfeqWGO2tGIyInZx0x1COhpx97RKaONSoAb8C3dxacWksm1ow==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + '@tanstack/react-table@8.21.3': + resolution: {integrity: sha512-5nNMTSETP4ykGegmVkhjcS8tTLW6Vl4axfEGQN3v0zdHYbK4UfoqfPChclTrJ4EoK9QynqAu9oUf8VEmrpZ5Ww==} + engines: {node: '>=12'} + peerDependencies: + react: '>=16.8' + react-dom: '>=16.8' + + '@tanstack/react-virtual@3.13.12': + resolution: {integrity: sha512-Gd13QdxPSukP8ZrkbgS2RwoZseTTbQPLnQEn7HY/rqtM+8Zt95f7xKC7N0EsKs7aoz0WzZ+fditZux+F8EzYxA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + '@tanstack/router-core@1.134.9': + resolution: {integrity: sha512-9Vr8tYC59I70DYGVRknRf4vjQMjSfHvmc+iTM8vcpwERBh3Vgkv90f8ol85KHKqjorSsCqMeYFhFt8AM4A4CSw==} + engines: {node: '>=12'} + + '@tanstack/router-devtools-core@1.134.9': + resolution: {integrity: sha512-VIc8ipL2QER+nWZedKinb09q7D7N9oPBpkhxgYfXqPBATAQAdv5oBxUQ2SmHz3v1vbENQZj/HlPpm2CYmsFkRg==} + engines: {node: '>=12'} + peerDependencies: + '@tanstack/router-core': ^1.134.9 + csstype: ^3.0.10 + solid-js: '>=1.9.5' + tiny-invariant: ^1.3.3 + peerDependenciesMeta: + csstype: + optional: true + + '@tanstack/router-generator@1.134.9': + resolution: {integrity: sha512-yBPX/xCWE/sdEEtCKOtPBl4cQo+G5Tt7UTB0li49CW8qhmD2eFKTQY1enRb68SwFNH5uwToBXFmJkSG1zPaA5Q==} + engines: {node: '>=12'} + + '@tanstack/router-plugin@1.134.9': + resolution: {integrity: sha512-iD85GvRADpVhRXkVGRwJqprhIXPLNH+O210UjFDQ8RC2Vn92IwKY6sx8fCgwjHtcYgnTdu3p8eIYJ8CfrLazxA==} + engines: {node: '>=12'} + peerDependencies: + '@rsbuild/core': '>=1.0.2' + '@tanstack/react-router': ^1.134.9 + vite: '>=5.0.0 || >=6.0.0 || >=7.0.0' + vite-plugin-solid: ^2.11.10 + webpack: '>=5.92.0' + peerDependenciesMeta: + '@rsbuild/core': + optional: true + '@tanstack/react-router': + optional: true + vite: + optional: true + vite-plugin-solid: + optional: true + webpack: + optional: true + + '@tanstack/router-utils@1.133.19': + resolution: {integrity: sha512-WEp5D2gPxvlLDRXwD/fV7RXjYtqaqJNXKB/L6OyZEbT+9BG/Ib2d7oG9GSUZNNMGPGYAlhBUOi3xutySsk6rxA==} + engines: {node: '>=12'} - '@types/d3-path@3.1.1': - resolution: {integrity: sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==} + '@tanstack/store@0.7.7': + resolution: {integrity: sha512-xa6pTan1bcaqYDS9BDpSiS63qa6EoDkPN9RsRaxHuDdVDNntzq3xNwR5YKTU/V3SkSyC9T4YVOPh2zRQN0nhIQ==} - '@types/d3-scale@4.0.9': - resolution: {integrity: sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==} + '@tanstack/store@0.8.0': + resolution: {integrity: sha512-Om+BO0YfMZe//X2z0uLF2j+75nQga6TpTJgLJQBiq85aOyZNIhkCgleNcud2KQg4k4v9Y9l+Uhru3qWMPGTOzQ==} - '@types/d3-shape@3.1.7': - resolution: {integrity: sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==} + '@tanstack/table-core@8.21.3': + resolution: {integrity: sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg==} + engines: {node: '>=12'} - '@types/d3-time@3.0.4': - resolution: {integrity: sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==} + '@tanstack/virtual-core@3.13.12': + resolution: {integrity: sha512-1YBOJfRHV4sXUmWsFSf5rQor4Ss82G8dQWLRbnk3GA4jeP8hQt1hxXh0tmflpC0dz3VgEv/1+qwPyLeWkQuPFA==} - '@types/d3-timer@3.0.2': - resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==} + '@tanstack/virtual-file-routes@1.133.19': + resolution: {integrity: sha512-IKwZENsK7owmW1Lm5FhuHegY/SyQ8KqtL/7mTSnzoKJgfzhrrf9qwKB1rmkKkt+svUuy/Zw3uVEpZtUzQruWtA==} + engines: {node: '>=12'} '@types/debug@4.1.12': resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} @@ -1028,17 +1325,11 @@ packages: '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} - '@types/file-saver@2.0.7': - resolution: {integrity: sha512-dNKVfHd/jk0SkR/exKGj2ggkB45MAkzvWCaqLUUgkyjITkGNzH8H+yUwr+BLJUBjZOe9w8X3wgmXhZDRg1ED6A==} - '@types/hast@3.0.4': resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} - '@types/history@4.7.11': - resolution: {integrity: sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==} - - '@types/humanize-duration@3.27.4': - resolution: {integrity: sha512-yaf7kan2Sq0goxpbcwTQ+8E9RP6HutFBPv74T/IA/ojcHKhuKVlk2YFYyHhWZeLvZPzzLE3aatuQB4h0iqyyUA==} + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} '@types/lodash-es@4.17.12': resolution: {integrity: sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==} @@ -1049,37 +1340,22 @@ packages: '@types/mdast@4.0.4': resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} - '@types/minimist@1.2.5': - resolution: {integrity: sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==} - '@types/ms@2.1.0': resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} - '@types/node@24.5.2': - resolution: {integrity: sha512-FYxk1I7wPv3K2XBaoyH2cTnocQEu8AOZ60hPbsyukMPLv5/5qr7V1i8PLHdl6Zf87I+xZXFvPCXYjiTFq+YSDQ==} - - '@types/normalize-package-data@2.4.4': - resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} - - '@types/parse-json@4.0.2': - resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} + '@types/node@24.10.0': + resolution: {integrity: sha512-qzQZRBqkFsYyaSWXuEHc2WR9c0a0CXwiE5FWUvn7ZM+vdy1uZLfCunD38UzhuB7YN/J11ndbDBcTmOdxJo9Q7A==} '@types/qs@6.14.0': resolution: {integrity: sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==} - '@types/react-dom@19.1.9': - resolution: {integrity: sha512-qXRuZaOsAdXKFyOhRBg6Lqqc0yay13vN7KrIg4L7N4aaHN68ma9OK3NE1BoDFgFOTfM7zg+3/8+2n8rLUH3OKQ==} + '@types/react-dom@19.2.2': + resolution: {integrity: sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw==} peerDependencies: - '@types/react': ^19.0.0 - - '@types/react-router-dom@5.3.3': - resolution: {integrity: sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==} + '@types/react': ^19.2.0 - '@types/react-router@5.1.20': - resolution: {integrity: sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q==} - - '@types/react@19.1.13': - resolution: {integrity: sha512-hHkbU/eoO3EG5/MZkuFSKmYqPbSVk5byPFa3e7y/8TybHiLMACgI8seVYlicwk7H5K/rI2px9xrQp/C+AUDTiQ==} + '@types/react@19.2.2': + resolution: {integrity: sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==} '@types/unist@2.0.11': resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} @@ -1087,60 +1363,134 @@ packages: '@types/unist@3.0.3': resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} - '@types/use-sync-external-store@0.0.6': - resolution: {integrity: sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==} + '@typescript-eslint/eslint-plugin@8.46.2': + resolution: {integrity: sha512-ZGBMToy857/NIPaaCucIUQgqueOiq7HeAKkhlvqVV4lm089zUFW6ikRySx2v+cAhKeUCPuWVHeimyk6Dw1iY3w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.46.2 + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' - '@ungap/structured-clone@1.3.0': - resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + '@typescript-eslint/parser@8.46.2': + resolution: {integrity: sha512-BnOroVl1SgrPLywqxyqdJ4l3S2MsKVLDVxZvjI1Eoe8ev2r3kGDo+PcMihNmDE+6/KjkTubSJnmqGZZjQSBq/g==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/project-service@8.46.2': + resolution: {integrity: sha512-PULOLZ9iqwI7hXcmL4fVfIsBi6AN9YxRc0frbvmg8f+4hQAjQ5GYNKK0DIArNo+rOKmR/iBYwkpBmnIwin4wBg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/scope-manager@8.46.2': + resolution: {integrity: sha512-LF4b/NmGvdWEHD2H4MsHD8ny6JpiVNDzrSZr3CsckEgCbAGZbYM4Cqxvi9L+WqDMT+51Ozy7lt2M+d0JLEuBqA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.46.2': + resolution: {integrity: sha512-a7QH6fw4S57+F5y2FIxxSDyi5M4UfGF+Jl1bCGd7+L4KsaUY80GsiF/t0UoRFDHAguKlBaACWJRmdrc6Xfkkag==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' - '@use-gesture/core@10.3.1': - resolution: {integrity: sha512-WcINiDt8WjqBdUXye25anHiNxPc0VOrlT8F6LLkU6cycrOGUDyY/yyFmsg3k8i5OLvv25llc0QC45GhR/C8llw==} + '@typescript-eslint/type-utils@8.46.2': + resolution: {integrity: sha512-HbPM4LbaAAt/DjxXaG9yiS9brOOz6fabal4uvUmaUYe6l3K1phQDMQKBRUrr06BQkxkvIZVVHttqiybM9nJsLA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/types@8.46.2': + resolution: {integrity: sha512-lNCWCbq7rpg7qDsQrd3D6NyWYu+gkTENkG5IKYhUIcxSb59SQC/hEQ+MrG4sTgBVghTonNWq42bA/d4yYumldQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.46.2': + resolution: {integrity: sha512-f7rW7LJ2b7Uh2EiQ+7sza6RDZnajbNbemn54Ob6fRwQbgcIn+GWfyuHDHRYgRoZu1P4AayVScrRW+YfbTvPQoQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' - '@use-gesture/react@10.3.1': - resolution: {integrity: sha512-Yy19y6O2GJq8f7CHf7L0nxL8bf4PZCPaVOCgJrusOeFHY1LvHgYXnmnXg6N5iwAnbgbZCDjo60SiM6IPJi9C5g==} + '@typescript-eslint/utils@8.46.2': + resolution: {integrity: sha512-sExxzucx0Tud5tE0XqR0lT0psBQvEpnpiul9XbGUB1QwpWJJAps1O/Z7hJxLGiZLBKMCutjTzDgmd1muEhBnVg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - react: '>= 16.8.0' + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/visitor-keys@8.46.2': + resolution: {integrity: sha512-tUFMXI4gxzzMXt4xpGJEsBsTox0XbNQ1y94EwlD/CuZwFcQP79xfQqMhau9HsRc/J0cAPA/HZt1dZPtGn9V/7w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@vitejs/plugin-react-swc@4.1.0': - resolution: {integrity: sha512-Ff690TUck0Anlh7wdIcnsVMhofeEVgm44Y4OYdeeEEPSKyZHzDI9gfVBvySEhDfXtBp8tLCbfsVKPWEMEjq8/g==} + '@uidotdev/usehooks@2.4.1': + resolution: {integrity: sha512-1I+RwWyS+kdv3Mv0Vmc+p0dPYH0DTRAo04HLyXReYBL9AeseDWUJyi4THuksBJcu9F0Pih69Ak150VDnqbVnXg==} + engines: {node: '>=16'} + peerDependencies: + react: '>=18.0.0' + react-dom: '>=18.0.0' + + '@ungap/structured-clone@1.3.0': + resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + + '@vitejs/plugin-react-swc@4.2.0': + resolution: {integrity: sha512-/tesahXD1qpkGC6FzMoFOJj0RyZdw9xLELOL+6jbElwmWfwOnIVy+IfpY+o9JfD9PKaR/Eyb6DNrvbXpuvA+8Q==} engines: {node: ^20.19.0 || >=22.12.0} peerDependencies: vite: ^4 || ^5 || ^6 || ^7 - JSONStream@1.3.5: - resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==} - hasBin: true + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 acorn@8.15.0: resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} engines: {node: '>=0.4.0'} hasBin: true - add-stream@1.0.0: - resolution: {integrity: sha512-qQLMr+8o0WC4FZGQTcJiKBVC59JylcPSrTtk6usvmIDFUOCKegapy1VHQwRbFMOFyb/inzUVqHs+eMYKDM1YeQ==} + ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + + ajv@8.17.1: + resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} + + ansi-colors@4.1.3: + resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} + engines: {node: '>=6'} ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} - ansi-styles@3.2.1: - resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} - engines: {node: '>=4'} - ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} + ansis@4.2.0: + resolution: {integrity: sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==} + engines: {node: '>=14'} + anymatch@3.1.3: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} - array-ify@1.0.0: - resolution: {integrity: sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==} + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - arrify@1.0.1: - resolution: {integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==} - engines: {node: '>=0.10.0'} + array-timsort@1.0.3: + resolution: {integrity: sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==} + + array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + + ast-types@0.16.1: + resolution: {integrity: sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==} + engines: {node: '>=4'} + + astral-regex@2.0.0: + resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} + engines: {node: '>=8'} asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} @@ -1152,12 +1502,11 @@ packages: peerDependencies: postcss: ^8.1.0 - axios@1.12.2: - resolution: {integrity: sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==} + axios@1.13.1: + resolution: {integrity: sha512-hU4EGxxt+j7TQijx1oYdAjw4xuIp1wRQSsbMFwSthCWeBQur1eF+qJ5iQ5sN3Tw8YRzQNKb8jszgBdMDVqwJcw==} - babel-plugin-macros@3.1.0: - resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==} - engines: {node: '>=10', npm: '>=6'} + babel-dead-code-elimination@1.0.10: + resolution: {integrity: sha512-DV5bdJZTzZ0zn0DC24v3jD7Mnidh6xhKa4GfKCbq3sfW8kaWhDdZjP3i81geA8T33tdYqWKw4D3fVv0CwEgKVA==} bail@2.0.2: resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} @@ -1165,12 +1514,12 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - baseline-browser-mapping@2.8.7: - resolution: {integrity: sha512-bxxN2M3a4d1CRoQC//IqsR5XrLh0IJ8TCv2x6Y9N0nckNz/rTjZB3//GGscZziZOxmjP55rzxg/ze7usFI9FqQ==} - hasBin: true + balanced-match@2.0.0: + resolution: {integrity: sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==} - bignumber.js@9.3.1: - resolution: {integrity: sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==} + baseline-browser-mapping@2.8.23: + resolution: {integrity: sha512-616V5YX4bepJFzNyOfce5Fa8fDJMfoxzOIzDCZwaGL8MKVpFrXqfNUoIpRn9YMI5pXf/VKgzjB4htFMsFKKdiQ==} + hasBin: true binary-extensions@2.3.0: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} @@ -1179,26 +1528,20 @@ packages: brace-expansion@1.1.12: resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} + brace-expansion@2.0.2: + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - browserslist@4.26.2: - resolution: {integrity: sha512-ECFzp6uFOSB+dcZ5BK/IBaGWssbSYBHvuMeMt3MMFyhI0Z8SqGgEkBLARgpRH3hutIgPVsALcMwbDrJqPxQ65A==} + browserslist@4.27.0: + resolution: {integrity: sha512-AXVQwdhot1eqLihwasPElhX2tAZiBjWdJ9i/Zcj2S6QYIjkx62OKSfnobkriB81C3l4w0rVy3Nt4jaTBltYEpw==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true - buffer-from@1.1.2: - resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - - byte-size@9.0.1: - resolution: {integrity: sha512-YLe9x3rabBrcI0cueCdLS2l5ONUKywcRpTs02B8KP9/Cimhj7o3ZccGrPnRvcbyHMbb7W79/3MUJl7iGgTXKEw==} - engines: {node: '>=12.17'} - peerDependencies: - '@75lb/nature': latest - peerDependenciesMeta: - '@75lb/nature': - optional: true + cacheable@2.1.1: + resolution: {integrity: sha512-LmF4AXiSNdiRbI2UjH8pAp9NIXxeQsTotpEaegPiDcnN0YPygDJDV3l/Urc0mL72JWdATEorKqIHEx55nDlONg==} call-bind-apply-helpers@1.0.2: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} @@ -1212,28 +1555,23 @@ packages: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} - camelcase-keys@6.2.2: - resolution: {integrity: sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==} - engines: {node: '>=8'} - - camelcase@5.3.1: - resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} - engines: {node: '>=6'} - - caniuse-lite@1.0.30001745: - resolution: {integrity: sha512-ywt6i8FzvdgrrrGbr1jZVObnVv6adj+0if2/omv9cmR2oiZs30zL4DIyaptKcbOrBdOIc74QTMoJvSE2QHh5UQ==} + caniuse-lite@1.0.30001753: + resolution: {integrity: sha512-Bj5H35MD/ebaOV4iDLqPEtiliTN29qkGtEHCwawWn4cYm+bPJM2NsaP30vtZcnERClMzp52J4+aw2UNbK4o+zw==} ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} - chalk@2.4.2: - resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} - engines: {node: '>=4'} - chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} + chalk@5.6.2: + resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + + change-case@5.4.4: + resolution: {integrity: sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==} + character-entities-html4@2.1.0: resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} @@ -1250,36 +1588,24 @@ packages: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} - classnames@2.5.1: - resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} - - cliui@6.0.0: - resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} - - cliui@7.0.4: - resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} - - cliui@8.0.1: - resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} - engines: {node: '>=12'} + chokidar@4.0.3: + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} + engines: {node: '>= 14.16.0'} clsx@2.1.1: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} engines: {node: '>=6'} - color-convert@1.9.3: - resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} - color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} - color-name@1.1.3: - resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} - color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + colord@2.9.3: + resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==} + combined-stream@1.0.8: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} @@ -1287,166 +1613,61 @@ packages: comma-separated-tokens@2.0.3: resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} - commander@2.20.3: - resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + commander@11.1.0: + resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} + engines: {node: '>=16'} - compare-func@2.0.0: - resolution: {integrity: sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==} + comment-json@4.4.1: + resolution: {integrity: sha512-r1To31BQD5060QdkC+Iheai7gHwoSZobzunqkf2/kQ6xIAfJyrKNAFUwdKvkK7Qgu7pVTKQEa7ok7Ed3ycAJgg==} + engines: {node: '>= 6'} concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - concat-stream@2.0.0: - resolution: {integrity: sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==} - engines: {'0': node >= 6.0} - - concurrently@9.2.1: - resolution: {integrity: sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng==} - engines: {node: '>=18'} - hasBin: true - - conventional-changelog-angular@5.0.13: - resolution: {integrity: sha512-i/gipMxs7s8L/QeuavPF2hLnJgH6pEZAttySB6aiQLWcX3puWDL3ACVmvBhJGxnAy52Qc15ua26BufY6KpmrVA==} - engines: {node: '>=10'} - - conventional-changelog-atom@2.0.8: - resolution: {integrity: sha512-xo6v46icsFTK3bb7dY/8m2qvc8sZemRgdqLb/bjpBsH2UyOS8rKNTgcb5025Hri6IpANPApbXMg15QLb1LJpBw==} - engines: {node: '>=10'} - - conventional-changelog-codemirror@2.0.8: - resolution: {integrity: sha512-z5DAsn3uj1Vfp7po3gpt2Boc+Bdwmw2++ZHa5Ak9k0UKsYAO5mH1UBTN0qSCuJZREIhX6WU4E1p3IW2oRCNzQw==} - engines: {node: '>=10'} - - conventional-changelog-config-spec@2.1.0: - resolution: {integrity: sha512-IpVePh16EbbB02V+UA+HQnnPIohgXvJRxHcS5+Uwk4AT5LjzCZJm5sp/yqs5C6KZJ1jMsV4paEV13BN1pvDuxQ==} - - conventional-changelog-conventionalcommits@4.6.3: - resolution: {integrity: sha512-LTTQV4fwOM4oLPad317V/QNQ1FY4Hju5qeBIM1uTHbrnCE+Eg4CdRZ3gO2pUeR+tzWdp80M2j3qFFEDWVqOV4g==} - engines: {node: '>=10'} - - conventional-changelog-core@4.2.4: - resolution: {integrity: sha512-gDVS+zVJHE2v4SLc6B0sLsPiloR0ygU7HaDW14aNJE1v4SlqJPILPl/aJC7YdtRE4CybBf8gDwObBvKha8Xlyg==} - engines: {node: '>=10'} - - conventional-changelog-ember@2.0.9: - resolution: {integrity: sha512-ulzIReoZEvZCBDhcNYfDIsLTHzYHc7awh+eI44ZtV5cx6LVxLlVtEmcO+2/kGIHGtw+qVabJYjdI5cJOQgXh1A==} - engines: {node: '>=10'} - - conventional-changelog-eslint@3.0.9: - resolution: {integrity: sha512-6NpUCMgU8qmWmyAMSZO5NrRd7rTgErjrm4VASam2u5jrZS0n38V7Y9CzTtLT2qwz5xEChDR4BduoWIr8TfwvXA==} - engines: {node: '>=10'} - - conventional-changelog-express@2.0.6: - resolution: {integrity: sha512-SDez2f3iVJw6V563O3pRtNwXtQaSmEfTCaTBPCqn0oG0mfkq0rX4hHBq5P7De2MncoRixrALj3u3oQsNK+Q0pQ==} - engines: {node: '>=10'} - - conventional-changelog-jquery@3.0.11: - resolution: {integrity: sha512-x8AWz5/Td55F7+o/9LQ6cQIPwrCjfJQ5Zmfqi8thwUEKHstEn4kTIofXub7plf1xvFA2TqhZlq7fy5OmV6BOMw==} - engines: {node: '>=10'} - - conventional-changelog-jshint@2.0.9: - resolution: {integrity: sha512-wMLdaIzq6TNnMHMy31hql02OEQ8nCQfExw1SE0hYL5KvU+JCTuPaDO+7JiogGT2gJAxiUGATdtYYfh+nT+6riA==} - engines: {node: '>=10'} - - conventional-changelog-preset-loader@2.3.4: - resolution: {integrity: sha512-GEKRWkrSAZeTq5+YjUZOYxdHq+ci4dNwHvpaBC3+ENalzFWuCWa9EZXSuZBpkr72sMdKB+1fyDV4takK1Lf58g==} - engines: {node: '>=10'} - - conventional-changelog-writer@5.0.1: - resolution: {integrity: sha512-5WsuKUfxW7suLblAbFnxAcrvf6r+0b7GvNaWUwUIk0bXMnENP/PEieGKVUQrjPqwPT4o3EPAASBXiY6iHooLOQ==} - engines: {node: '>=10'} - hasBin: true - - conventional-changelog@3.1.25: - resolution: {integrity: sha512-ryhi3fd1mKf3fSjbLXOfK2D06YwKNic1nC9mWqybBHdObPd8KJ2vjaXZfYj1U23t+V8T8n0d7gwnc9XbIdFbyQ==} - engines: {node: '>=10'} - - conventional-commits-filter@2.0.7: - resolution: {integrity: sha512-ASS9SamOP4TbCClsRHxIHXRfcGCnIoQqkvAzCSbZzTFLfcTqJVugB0agRgsEELsqaeWgsXv513eS116wnlSSPA==} - engines: {node: '>=10'} - - conventional-commits-parser@3.2.4: - resolution: {integrity: sha512-nK7sAtfi+QXbxHCYfhpZsfRtaitZLIA6889kFIouLvz6repszQDgxBu7wf2WbU+Dco7sAnNCJYERCwt54WPC2Q==} - engines: {node: '>=10'} - hasBin: true - - conventional-recommended-bump@6.1.0: - resolution: {integrity: sha512-uiApbSiNGM/kkdL9GTOLAqC4hbptObFo4wW2QRyHsKciGAfQuLU1ShZ1BIVI/+K2BE/W1AWYQMCXAsv4dyKPaw==} - engines: {node: '>=10'} - hasBin: true - - convert-source-map@1.9.0: - resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} + consola@3.4.0: + resolution: {integrity: sha512-EiPU8G6dQG0GFHNR8ljnZFki/8a+cQwEQ+7wpxdChl02Q8HXlwEZWD5lqAF8vC2sEC3Tehr8hy7vErz88LHyUA==} + engines: {node: ^14.18.0 || >=16.10.0} convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + cookie-es@2.0.0: + resolution: {integrity: sha512-RAj4E421UYRgqokKUmotqAwuplYw15qtdXfY+hGzgCJ/MBjCVZcSoHK/kH9kocfjRjcDME7IiDWR/1WX1TM2Pg==} + core-util-is@1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} - cosmiconfig@7.1.0: - resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} - engines: {node: '>=10'} - - csstype@3.1.3: - resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} - - d3-array@3.2.4: - resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==} - engines: {node: '>=12'} - - d3-color@3.1.0: - resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==} - engines: {node: '>=12'} - - d3-ease@3.0.1: - resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==} - engines: {node: '>=12'} - - d3-format@3.1.0: - resolution: {integrity: sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==} - engines: {node: '>=12'} - - d3-interpolate@3.0.1: - resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==} - engines: {node: '>=12'} - - d3-path@3.1.0: - resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==} - engines: {node: '>=12'} - - d3-scale@4.0.2: - resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==} - engines: {node: '>=12'} - - d3-shape@3.2.0: - resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==} - engines: {node: '>=12'} - - d3-time-format@4.1.0: - resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==} - engines: {node: '>=12'} + cosmiconfig@9.0.0: + resolution: {integrity: sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true - d3-time@3.1.0: - resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==} - engines: {node: '>=12'} + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} - d3-timer@3.0.1: - resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==} - engines: {node: '>=12'} + css-functions-list@3.2.3: + resolution: {integrity: sha512-IQOkD3hbR5KrN93MtcYuad6YPuTSUhntLHDuLEbFWE+ff2/XSZNdZG+LcbbIW5AXKg/WFIfYItIzVoHngHXZzA==} + engines: {node: '>=12 || >=16'} - dargs@7.0.0: - resolution: {integrity: sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==} - engines: {node: '>=8'} + css-tree@3.1.0: + resolution: {integrity: sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} - date-fns@4.1.0: - resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==} + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true - dateformat@3.0.3: - resolution: {integrity: sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==} + csstype@3.1.3: + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} - dayjs@1.11.18: - resolution: {integrity: sha512-zFBQ7WFRvVRhKcWoUh+ZA1g2HVgUbsZm9sbddh8EC5iv93sui8DVVz1Npvz+r6meo9VKfa8NyLWBsQK1VvIKPA==} + dayjs@1.11.19: + resolution: {integrity: sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==} debug@4.4.3: resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} @@ -1457,23 +1678,22 @@ packages: supports-color: optional: true - decamelize-keys@1.1.1: - resolution: {integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==} - engines: {node: '>=0.10.0'} - - decamelize@1.2.0: - resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} - engines: {node: '>=0.10.0'} - - decimal.js-light@2.5.1: - resolution: {integrity: sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==} + decode-formdata@0.9.0: + resolution: {integrity: sha512-q5uwOjR3Um5YD+ZWPOF/1sGHVW9A5rCrRwITQChRXlmPkxDFBqCm4jNTIVdGHNH9OnR+V9MoZVgRhsFb+ARbUw==} decode-named-character-reference@1.2.0: resolution: {integrity: sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==} - deepmerge-ts@7.1.5: - resolution: {integrity: sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==} - engines: {node: '>=16.0.0'} + dedent@1.5.1: + resolution: {integrity: sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==} + peerDependencies: + babel-plugin-macros: ^3.1.0 + peerDependenciesMeta: + babel-plugin-macros: + optional: true + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} delayed-stream@1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} @@ -1483,69 +1703,42 @@ packages: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} - detect-browser@5.3.0: - resolution: {integrity: sha512-53rsFbGdwMwlF7qvCt0ypLM5V5/Mbl0szB7GPN8y9NCcbknYOeVVXdrXEq+90IwAfrrzt6Hd+u2E2ntakICU8w==} + detect-libc@1.0.3: + resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==} + engines: {node: '>=0.10'} + hasBin: true - detect-indent@6.1.0: - resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} - detect-newline@3.1.0: - resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} - engines: {node: '>=8'} + devalue@5.4.2: + resolution: {integrity: sha512-MwPZTKEPK2k8Qgfmqrd48ZKVvzSQjgW0lXLxiIBA8dQjtf/6mw6pggHNLcyDKyf+fI6eXxlQwPsfaCMTU5U+Bw==} devlop@1.1.0: resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} - dice-coefficient@2.1.1: - resolution: {integrity: sha512-vPTcHmOQAuGvU6eyBtj7QCBwDJh2I7QpbBU51lbgfv7592KjBl6dm0baRBSh9ekt2X91MNAz7OpJrXCIUtDzlw==} - hasBin: true - - dijkstrajs@1.0.3: - resolution: {integrity: sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==} - - dom-serializer@2.0.0: - resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} - - domelementtype@2.3.0: - resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} - - domhandler@5.0.3: - resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} - engines: {node: '>= 4'} - - domutils@3.2.2: - resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} + diff@8.0.2: + resolution: {integrity: sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg==} + engines: {node: '>=0.3.1'} - dot-prop@5.3.0: - resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==} + dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} - dotenv@17.2.2: - resolution: {integrity: sha512-Sf2LSQP+bOlhKWWyhFsn0UsfdK/kCWRv1iuA2gXAwt3dyNabr6QSj00I2V10pidqz69soatm9ZwZvpQMTIOd5Q==} - engines: {node: '>=12'} - - dotgitignore@2.1.0: - resolution: {integrity: sha512-sCm11ak2oY6DglEPpCB8TixLjWAxd3kJTs6UIcSasNYxXdFPV+YKlye92c8H4kKFqV5qYMIh7d+cYecEg0dIkA==} - engines: {node: '>=6'} - dunder-proto@1.0.1: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} - electron-to-chromium@1.5.224: - resolution: {integrity: sha512-kWAoUu/bwzvnhpdZSIc6KUyvkI1rbRXMT0Eq8pKReyOyaPZcctMli+EgvcN1PAvwVc7Tdo4Fxi2PsLNDU05mdg==} + electron-to-chromium@1.5.244: + resolution: {integrity: sha512-OszpBN7xZX4vWMPJwB9illkN/znA8M36GQqQxi6MNy9axWxhOfJyZZJtSLQCpEFLHP2xK33BiWx9aIuIEXVCcw==} emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - entities@4.5.0: - resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} - engines: {node: '>=0.12'} - - entities@6.0.1: - resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} - engines: {node: '>=0.12'} + env-paths@2.2.1: + resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} + engines: {node: '>=6'} error-ex@1.3.4: resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} @@ -1566,11 +1759,8 @@ packages: resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} engines: {node: '>= 0.4'} - es-toolkit@1.39.10: - resolution: {integrity: sha512-E0iGnTtbDhkeczB0T+mxmoVlT4YNweEKBLq7oaU4p11mecdsZpNWOglI4895Vh4usbQ+LsJiuLuI2L0Vdmfm2w==} - - esbuild@0.25.10: - resolution: {integrity: sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ==} + esbuild@0.25.12: + resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} engines: {node: '>=18'} hasBin: true @@ -1578,23 +1768,59 @@ packages: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} - escape-string-regexp@1.0.5: - resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} - engines: {node: '>=0.8.0'} - escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} + eslint-scope@8.4.0: + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@4.2.1: + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint@9.37.0: + resolution: {integrity: sha512-XyLmROnACWqSxiGYArdef1fItQd47weqB7iwtfr9JHwRrqIXZdcFMvvEcL9xHCmL0SNsOvF0c42lWyM1U5dgig==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@10.4.0: + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + + esquery@1.6.0: + resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + estree-util-is-identifier-name@3.0.0: resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==} - eventemitter3@5.0.1: - resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} - - events@3.3.0: - resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} - engines: {node: '>=0.8.x'} + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} extend@3.0.2: resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} @@ -1602,6 +1828,26 @@ packages: fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fast-uri@3.1.0: + resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} + + fastest-levenshtein@1.0.16: + resolution: {integrity: sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==} + engines: {node: '>= 4.9.1'} + + fastq@1.19.1: + resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} + fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} engines: {node: '>=12.0.0'} @@ -1611,36 +1857,31 @@ packages: picomatch: optional: true - figures@3.2.0: - resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} - engines: {node: '>=8'} + file-entry-cache@10.1.4: + resolution: {integrity: sha512-5XRUFc0WTtUbjfGzEwXc42tiGxQHBmtbUG1h9L2apu4SulCGN3Hqm//9D6FAolf8MYNL7f/YlJl9vy08pj5JuA==} - file-saver@2.0.5: - resolution: {integrity: sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==} + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} - find-root@1.1.0: - resolution: {integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==} - - find-up@2.1.0: - resolution: {integrity: sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==} - engines: {node: '>=4'} - - find-up@3.0.0: - resolution: {integrity: sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==} - engines: {node: '>=6'} - - find-up@4.1.0: - resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} - engines: {node: '>=8'} - find-up@5.0.0: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flat-cache@6.1.18: + resolution: {integrity: sha512-JUPnFgHMuAVmLmoH9/zoZ6RHOt5n9NlUw/sDXsTbROJ2SFoS2DS4s+swAV6UTeTbGH/CAsZIE6M8TaG/3jVxgQ==} + + flatted@3.3.3: + resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + follow-redirects@1.15.11: resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} engines: {node: '>=4.0'} @@ -1657,8 +1898,8 @@ packages: fraction.js@4.3.7: resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} - framer-motion@12.23.22: - resolution: {integrity: sha512-ZgGvdxXCw55ZYvhoZChTlG6pUuehecgvEAJz0BHoC5pQKW1EC5xf1Mul1ej5+ai+pVY0pylyFfdl45qnM1/GsA==} + framer-motion@12.23.24: + resolution: {integrity: sha512-HMi5HRoRCTou+3fb3h9oTLyJGBxHfW+HnNE25tAXOvVx/IvwMHK0cx7IR4a2ZU6sh3IX1Z+4ts32PcYBOqka8w==} peerDependencies: '@emotion/is-prop-valid': '*' react: ^18.0.0 || ^19.0.0 @@ -1679,78 +1920,63 @@ packages: function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - fuse.js@7.1.0: - resolution: {integrity: sha512-trLf4SzuuUxfusZADLINj+dE8clK1frKdmqiJNb1Es75fmI5oY6X2mxLVUciLLjxqw/xr72Dhy+lER6dGd02FQ==} - engines: {node: '>=10'} - gensync@1.0.0-beta.2: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} - get-caller-file@2.0.5: - resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} - engines: {node: 6.* || 8.* || >= 10.*} - get-intrinsic@1.3.0: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} - get-pkg-repo@4.2.1: - resolution: {integrity: sha512-2+QbHjFRfGB74v/pYWjd5OhU3TDIC2Gv/YKUTk/tCvAz0pkn/Mz6P3uByuBimLOcPvN2jYdScl3xGFSrx0jEcA==} - engines: {node: '>=6.9.0'} - hasBin: true - get-proto@1.0.1: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} - get-text-width@1.0.3: - resolution: {integrity: sha512-kv1MaexPcR/qaZ4kN8sUDjG5pRp5ptHvxcDGDBTeGld1cmo7MnlCMH22jevyvs/VV7Ran203o7qAOq2+kWw9cA==} + get-tsconfig@4.13.0: + resolution: {integrity: sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==} - git-raw-commits@2.0.11: - resolution: {integrity: sha512-VnctFhw+xfj8Va1xtfEqCUD2XDrbAPSJx+hSrE5K7fGdjZruW7XV+QOrN7LF/RJyvspRiD2I0asWsxFp0ya26A==} - engines: {node: '>=10'} - hasBin: true + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} - git-remote-origin-url@2.0.0: - resolution: {integrity: sha512-eU+GGrZgccNJcsDH5LkXR3PB9M958hxc7sbA8DFJjrv9j4L2P/eZfKhM+QD6wyzpiv+b1BpK0XrYCxkovtjSLw==} - engines: {node: '>=4'} + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} - git-semver-tags@4.1.1: - resolution: {integrity: sha512-OWyMt5zBe7xFs8vglMmhM9lRQzCWL3WjHtxNNfJTMngGym7pC1kh8sP6jevfydJ6LP3ZvGxfb6ABYgPUM0mtsA==} - engines: {node: '>=10'} - hasBin: true + global-modules@2.0.0: + resolution: {integrity: sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==} + engines: {node: '>=6'} - gitconfiglocal@1.0.0: - resolution: {integrity: sha512-spLUXeTAVHxDtKsJc8FkFVgFtMdEN9qPGpL23VfSHx4fP4+Ds097IXLvymbnDH8FnmxX5Nr9bPw3A+AQ6mWEaQ==} + global-prefix@3.0.0: + resolution: {integrity: sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==} + engines: {node: '>=6'} - glob-parent@5.1.2: - resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} - engines: {node: '>= 6'} + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} - globals@16.4.0: - resolution: {integrity: sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==} + globals@16.5.0: + resolution: {integrity: sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==} engines: {node: '>=18'} - gopd@1.2.0: - resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} - engines: {node: '>= 0.4'} + globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} - graceful-fs@4.2.11: - resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + globjoin@0.1.4: + resolution: {integrity: sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==} - handlebars@4.7.8: - resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==} - engines: {node: '>=0.4.7'} - hasBin: true + goober@2.1.18: + resolution: {integrity: sha512-2vFqsaDVIT9Gz7N6kAL++pLpp41l3PfDuusHcjnGLfR6+huZkl6ziX+zgVC3ZxpqWhzH6pyDdGrCeDhMIvwaxw==} + peerDependencies: + csstype: ^3.0.10 - hard-rejection@2.1.0: - resolution: {integrity: sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==} - engines: {node: '>=6'} + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} - has-flag@3.0.0: - resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} - engines: {node: '>=4'} + graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} @@ -1768,105 +1994,50 @@ packages: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} - hast-util-from-parse5@8.0.3: - resolution: {integrity: sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==} - - hast-util-is-element@3.0.0: - resolution: {integrity: sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==} - - hast-util-parse-selector@4.0.0: - resolution: {integrity: sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==} - - hast-util-raw@9.1.0: - resolution: {integrity: sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==} - - hast-util-sanitize@5.0.2: - resolution: {integrity: sha512-3yTWghByc50aGS7JlGhk61SPenfE/p1oaFeNwkOOyrscaOkMGrcW9+Cy/QAIOBpZxP1yqDIzFMR0+Np0i0+usg==} - hast-util-to-jsx-runtime@2.3.6: resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==} - hast-util-to-parse5@8.0.0: - resolution: {integrity: sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw==} - hast-util-whitespace@3.0.0: resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} - hastscript@9.0.1: - resolution: {integrity: sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==} - - hex-rgb@5.0.0: - resolution: {integrity: sha512-NQO+lgVUCtHxZ792FodgW0zflK+ozS9X9dwGp9XvvmPlH7pyxd588cn24TD3rmPm/N0AIRXF10Otah8yKqGw4w==} - engines: {node: '>=12'} - - hoist-non-react-statics@3.3.2: - resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} - - hosted-git-info@2.8.9: - resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} + hookified@1.12.2: + resolution: {integrity: sha512-aokUX1VdTpI0DUsndvW+OiwmBpKCu/NgRsSSkuSY0zq8PY6Q6a+lmOfAFDXAAOtBqJELvcWY9L1EVtzjbQcMdg==} - hosted-git-info@4.1.0: - resolution: {integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==} - engines: {node: '>=10'} - - html-dom-parser@5.1.1: - resolution: {integrity: sha512-+o4Y4Z0CLuyemeccvGN4bAO20aauB2N9tFEAep5x4OW34kV4PTarBHm6RL02afYt2BMKcr0D2Agep8S3nJPIBg==} - - html-react-parser@5.2.6: - resolution: {integrity: sha512-qcpPWLaSvqXi+TndiHbCa+z8qt0tVzjMwFGFBAa41ggC+ZA5BHaMIeMJla9g3VSp4SmiZb9qyQbmbpHYpIfPOg==} - peerDependencies: - '@types/react': 0.14 || 15 || 16 || 17 || 18 || 19 - react: 0.14 || 15 || 16 || 17 || 18 || 19 - peerDependenciesMeta: - '@types/react': - optional: true + html-tags@3.3.1: + resolution: {integrity: sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==} + engines: {node: '>=8'} html-url-attributes@3.0.1: resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==} - html-void-elements@3.0.0: - resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} - - htmlparser2@10.0.0: - resolution: {integrity: sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==} + human-id@4.1.2: + resolution: {integrity: sha512-v/J+4Z/1eIJovEBdlV5TYj1IR+ZiohcYGRY+qN/oC9dAfKzVT023N/Bgw37hrKCoVRBvk3bqyzpr2PP5YeTMSg==} + hasBin: true - humanize-duration@3.33.1: - resolution: {integrity: sha512-hwzSCymnRdFx9YdRkQQ0OYequXiVAV6ZGQA2uzocwB0F4309Ke6pO8dg0P8LHhRQJyVjGteRTAA/zNfEcpXn8A==} + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} - immer@10.1.3: - resolution: {integrity: sha512-tmjF/k8QDKydUlm3mZU+tjM6zeq9/fFpPqH9SzWmBnVVKsPBg/V66qsMwb3/Bo90cgUN+ghdVBess+hPsxUyRw==} + ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} - immutable@4.3.7: - resolution: {integrity: sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==} + immutable@5.1.4: + resolution: {integrity: sha512-p6u1bG3YSnINT5RQmx/yRZBpenIl30kVxkTLDyHLIMk0gict704Q9n+thfDI7lTRm9vXdDYutVzXhzcThxTnXA==} import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} - engines: {node: '>=6'} - - indent-string@4.0.0: - resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} - engines: {node: '>=8'} + engines: {node: '>=6'} - inherits@2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} ini@1.3.8: resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} - inline-style-parser@0.2.4: - resolution: {integrity: sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==} - - internmap@2.0.3: - resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} - engines: {node: '>=12'} - - ipaddr.js@2.2.0: - resolution: {integrity: sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==} - engines: {node: '>= 10'} - - is-absolute-url@4.0.1: - resolution: {integrity: sha512-/51/TKE88Lmm7Gc4/8btclNXWS+g50wXhYJq8HWIBAGUBnoAdRu1aXeh364t/O7wXDAcTJDP8PNuNKWUDWie+A==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + inline-style-parser@0.2.6: + resolution: {integrity: sha512-gtGXVaBdl5mAes3rPcMedEBm12ibjt1kDMFfheul1wUAOVEJW60voNdMVzVkfLN06O7ZaD/rxhfKgtlgtTbMjg==} is-alphabetical@2.0.1: resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==} @@ -1881,10 +2052,6 @@ packages: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} engines: {node: '>=8'} - is-core-module@2.16.1: - resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} - engines: {node: '>= 0.4'} - is-decimal@2.0.1: resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} @@ -1907,88 +2074,85 @@ packages: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} - is-obj@2.0.0: - resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} - engines: {node: '>=8'} - - is-plain-obj@1.1.0: - resolution: {integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==} - engines: {node: '>=0.10.0'} - is-plain-obj@4.1.0: resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} engines: {node: '>=12'} - is-text-path@1.0.1: - resolution: {integrity: sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w==} + is-plain-object@5.0.0: + resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} engines: {node: '>=0.10.0'} - isarray@1.0.0: - resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} - - itertools@2.5.0: - resolution: {integrity: sha512-4ghJEXkRGkw4veNQhfO0cLY8+zePMXbe9wGt3ckSVFtrQVyyoKCUESaG2HsjuEfidVtuIEj1Dt1BlmTL3GUWFg==} + isbot@5.1.31: + resolution: {integrity: sha512-DPgQshehErHAqSCKDb3rNW03pa2wS/v5evvUqtxt6TTnHRqAG8FdzcSSJs9656pK6Y+NT7K9R4acEYXLHYfpUQ==} + engines: {node: '>=18'} - jiti@2.4.2: - resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==} - hasBin: true + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - js-base64@3.7.8: - resolution: {integrity: sha512-hNngCeKxIUQiEUN3GPJOkz4wF/YvdUdbNL9hsBcMQTkKzboD7T/q3OYOuuPZLUE6dBxSGpwhk5mwuDud7JVAow==} + js-sha256@0.11.1: + resolution: {integrity: sha512-o6WSo/LUvY2uC4j7mO50a2ms7E/EAdbP0swigLV+nzHKTTaYnaLIWJ02VdXrsJX0vGedDESQnLsOekr94ryfjg==} js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + jsesc@3.1.0: resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} engines: {node: '>=6'} hasBin: true - json-parse-better-errors@1.0.2: - resolution: {integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==} + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} - json-stringify-safe@5.0.1: - resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} json5@2.2.3: resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} engines: {node: '>=6'} hasBin: true - jsonparse@1.3.1: - resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} - engines: {'0': node >= 0.2.0} + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + keyv@5.5.3: + resolution: {integrity: sha512-h0Un1ieD+HUrzBH6dJXhod3ifSghk5Hw/2Y4/KHBziPlZecrFyE9YOTPU6eOs0V9pYl8gOs86fkr/KN8lUX39A==} kind-of@6.0.3: resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} engines: {node: '>=0.10.0'} - lines-and-columns@1.2.4: - resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + known-css-properties@0.36.0: + resolution: {integrity: sha512-A+9jP+IUmuQsNdsLdcg6Yt7voiMF/D4K83ew0OpJtpu+l34ef7LaohWV0Rc6KNvzw6ZDizkqfyB5JznZnzuKQA==} - little-state-machine@4.8.1: - resolution: {integrity: sha512-liPHqaWMQ7rzZryQUDnbZ1Gclnnai3dIyaJ0nAgwZRXMzqbYrydrlCI0NDojRUbE5VYh5vu6hygEUZiH77nQkQ==} - peerDependencies: - react: ^16.8.0 || ^17 || ^18 || ^19 + known-css-properties@0.37.0: + resolution: {integrity: sha512-JCDrsP4Z1Sb9JwG0aJ8Eo2r7k4Ou5MwmThS/6lcIe1ICyb7UBJKGRIUUdqc2ASdE/42lgz6zFUnzAIhtXnBVrQ==} - load-json-file@4.0.0: - resolution: {integrity: sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==} - engines: {node: '>=4'} + kysely@0.27.6: + resolution: {integrity: sha512-FIyV/64EkKhJmjgC0g2hygpBv5RNWVPyNCqSAD7eTCv6eFWNIi4PN1UvdSJGicN/o35bnevgis4Y0UDC0qi8jQ==} + engines: {node: '>=14.0.0'} - locate-path@2.0.0: - resolution: {integrity: sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==} - engines: {node: '>=4'} + launch-editor@2.12.0: + resolution: {integrity: sha512-giOHXoOtifjdHqUamwKq6c49GzBdLjvxrd2D+Q4V6uOHopJv7p9VJxikDsQ/CBXZbEITgUqSVHXLTG3VhPP1Dg==} - locate-path@3.0.0: - resolution: {integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==} - engines: {node: '>=6'} + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} - locate-path@5.0.0: - resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} - engines: {node: '>=8'} + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} @@ -1997,38 +2161,25 @@ packages: lodash-es@4.17.21: resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} - lodash.ismatch@4.4.0: - resolution: {integrity: sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g==} + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} - lodash@4.17.21: - resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + lodash.truncate@4.4.2: + resolution: {integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==} longest-streak@3.1.0: resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} - loose-envify@1.4.0: - resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} - hasBin: true - lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} - lru-cache@6.0.0: - resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} - engines: {node: '>=10'} - - map-obj@1.0.1: - resolution: {integrity: sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==} - engines: {node: '>=0.10.0'} - - map-obj@4.3.0: - resolution: {integrity: sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==} - engines: {node: '>=8'} - math-intrinsics@1.1.0: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} + mathml-tag-names@2.1.3: + resolution: {integrity: sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==} + mdast-util-from-markdown@2.0.2: resolution: {integrity: sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==} @@ -2053,17 +2204,19 @@ packages: mdast-util-to-string@4.0.0: resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} - meow@8.1.2: - resolution: {integrity: sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==} - engines: {node: '>=10'} + mdn-data@2.12.2: + resolution: {integrity: sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==} - merge-refs@2.0.0: - resolution: {integrity: sha512-3+B21mYK2IqUWnd2EivABLT7ueDhb0b8/dGK8LoFQPrU61YITeCMn14F7y7qZafWNZhUEKb24cJdiT5Wxs3prg==} - peerDependencies: - '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - peerDependenciesMeta: - '@types/react': - optional: true + mdn-data@2.25.0: + resolution: {integrity: sha512-T2LPsjgUE/tgMmRXREVmwsux89DwWfNjiynOeXuLd2mX6jphGQ2YE3Ukz7LQ2VOFKiVZU/Ee1GqzHiipZCjymw==} + + meow@13.2.0: + resolution: {integrity: sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==} + engines: {node: '>=18'} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} micromark-core-commonmark@2.0.3: resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} @@ -2128,9 +2281,9 @@ packages: micromark@4.0.2: resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==} - millify@6.1.0: - resolution: {integrity: sha512-H/E3J6t+DQs/F2YgfDhxUVZz/dF8JXPPKTLHL/yHCcLZLtCXJDUaqvhJXQwqOVBvbyNn4T0WjLpIHd7PAw7fBA==} - hasBin: true + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} mime-db@1.52.0: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} @@ -2140,32 +2293,21 @@ packages: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} - min-indent@1.0.1: - resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} - engines: {node: '>=4'} - minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} - minimist-options@4.1.0: - resolution: {integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==} - engines: {node: '>= 6'} - - minimist@1.2.8: - resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - - modify-values@1.0.1: - resolution: {integrity: sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==} - engines: {node: '>=0.10.0'} + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} - motion-dom@12.23.21: - resolution: {integrity: sha512-5xDXx/AbhrfgsQmSE7YESMn4Dpo6x5/DTZ4Iyy4xqDvVHWvFVoV+V2Ri2S/ksx+D40wrZ7gPYiMWshkdoqNgNQ==} + motion-dom@12.23.23: + resolution: {integrity: sha512-n5yolOs0TQQBRUFImrRfs/+6X4p3Q4n1dUEqt/H58Vx7OW6RF+foWEgmTVDhIWJIMXOuNNL0apKH2S16en9eiA==} motion-utils@12.23.6: resolution: {integrity: sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==} - motion@12.23.22: - resolution: {integrity: sha512-iSq6X9vLHbeYwmHvhK//+U74ROaPnZmBuy60XZzqNl0QtZkWfoZyMDHYnpKuWFv0sNMqHgED8aCXk94LCoQPGg==} + motion@12.23.24: + resolution: {integrity: sha512-Rc5E7oe2YZ72N//S3QXGzbnXgqNrTESv8KKxABR20q2FLch9gHLo0JLyYo2hZ238bZ9Gx6cWhj9VO0IgwbMjCw==} peerDependencies: '@emotion/is-prop-valid': '*' react: ^18.0.0 || ^19.0.0 @@ -2181,26 +2323,19 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - n-gram@2.0.2: - resolution: {integrity: sha512-S24aGsn+HLBxUGVAUFOwGpKs7LBcG4RudKU//eWzt/mQ97/NMKQxDWHyHx63UNWk/OOdihgmzoETn1tf5nQDzQ==} - nanoid@3.3.11: resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - neo-async@2.6.2: - resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - node-releases@2.0.21: - resolution: {integrity: sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw==} + node-addon-api@7.1.1: + resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} - normalize-package-data@2.5.0: - resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} - - normalize-package-data@3.0.3: - resolution: {integrity: sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==} - engines: {node: '>=10'} + node-releases@2.0.27: + resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} @@ -2210,53 +2345,22 @@ packages: resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} engines: {node: '>=0.10.0'} - numbro@2.5.0: - resolution: {integrity: sha512-xDcctDimhzko/e+y+Q2/8i3qNC9Svw1QgOkSkQoO0kIPI473tR9QRbo2KP88Ty9p8WbPy+3OpTaAIzehtuHq+A==} - - object-assign@4.1.1: - resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} - engines: {node: '>=0.10.0'} - object-inspect@1.13.4: resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} engines: {node: '>= 0.4'} - p-limit@1.3.0: - resolution: {integrity: sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==} - engines: {node: '>=4'} - - p-limit@2.3.0: - resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} - engines: {node: '>=6'} + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} p-limit@3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} - p-locate@2.0.0: - resolution: {integrity: sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==} - engines: {node: '>=4'} - - p-locate@3.0.0: - resolution: {integrity: sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==} - engines: {node: '>=6'} - - p-locate@4.1.0: - resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} - engines: {node: '>=8'} - p-locate@5.0.0: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} - p-try@1.0.0: - resolution: {integrity: sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==} - engines: {node: '>=4'} - - p-try@2.2.0: - resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} - engines: {node: '>=6'} - parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -2264,36 +2368,25 @@ packages: parse-entities@4.0.2: resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==} - parse-json@4.0.0: - resolution: {integrity: sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==} - engines: {node: '>=4'} - parse-json@5.2.0: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} - parse5@7.3.0: - resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} - - path-exists@3.0.0: - resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==} - engines: {node: '>=4'} - path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} - path-parse@1.0.7: - resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - - path-type@3.0.0: - resolution: {integrity: sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==} - engines: {node: '>=4'} + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -2305,17 +2398,27 @@ packages: resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} engines: {node: '>=12'} - pify@2.3.0: - resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} - engines: {node: '>=0.10.0'} + postcss-media-query-parser@0.2.3: + resolution: {integrity: sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==} - pify@3.0.0: - resolution: {integrity: sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==} - engines: {node: '>=4'} + postcss-resolve-nested-selector@0.1.6: + resolution: {integrity: sha512-0sglIs9Wmkzbr8lQwEyIzlDOOC9bGmfVKcJTaxv3vMmd3uo4o4DerC3En0bnmgceeql9BfC8hRkp7cg0fjdVqw==} - pngjs@5.0.0: - resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==} - engines: {node: '>=10.13.0'} + postcss-safe-parser@7.0.1: + resolution: {integrity: sha512-0AioNCJZ2DPYz5ABT6bddIqlhgwhpHZ/l65YAYo0BCIn0xiDpsnTHz0gnoTGk0OXZW0JRs+cDwL8u/teRdz+8A==} + engines: {node: '>=18.0'} + peerDependencies: + postcss: ^8.4.31 + + postcss-scss@4.0.9: + resolution: {integrity: sha512-AjKOeiwAitL/MXxQW2DliT28EKukvvbEWx3LBmJIRN8KfBGZbRTxNYW0kSqi1COiTZ57nZ9NW06S6ux//N1c9A==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.4.29 + + postcss-selector-parser@7.1.0: + resolution: {integrity: sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==} + engines: {node: '>=4'} postcss-value-parser@4.2.0: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} @@ -2324,105 +2427,45 @@ packages: resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + prettier@3.6.2: resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==} engines: {node: '>=14'} hasBin: true - process-nextick-args@2.0.1: - resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} - - prop-types@15.8.1: - resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} - - property-information@6.5.0: - resolution: {integrity: sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==} - property-information@7.1.0: resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} - proxy-compare@3.0.1: - resolution: {integrity: sha512-V9plBAt3qjMlS1+nC8771KNf6oJ12gExvaxnNzN/9yVRLdTv/lc+oJlnSzrdYDAvBfTStPCoiaCOTmTs0adv7Q==} - proxy-from-env@1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} - q@1.5.1: - resolution: {integrity: sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==} - engines: {node: '>=0.6.0', teleport: '>=0.2.0'} - deprecated: |- - You or someone you depend on is using Q, the JavaScript Promise library that gave JavaScript developers strong feelings about promises. They can almost certainly migrate to the native JavaScript promise now. Thank you literally everyone for joining me in this bet against the odds. Be excellent to each other. - - (For a CapTP with native promises, see @endo/eventual-send and @endo/captp) + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} - qr.js@0.0.0: - resolution: {integrity: sha512-c4iYnWb+k2E+vYpRimHqSu575b1/wKl4XFeJGpFmrJQz5I88v9aY2czh7s0w36srfCM1sXgC/xpoJz5dJfq+OQ==} + qified@0.5.1: + resolution: {integrity: sha512-+BtFN3dCP+IaFA6IYNOu/f/uK1B8xD2QWyOeCse0rjtAebBmkzgd2d1OAXi3ikAzJMIBSdzZDNZ3wZKEUDQs5w==} + engines: {node: '>=20'} - qrcode@1.5.4: - resolution: {integrity: sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==} - engines: {node: '>=10.13.0'} - hasBin: true + qrcode.react@4.2.0: + resolution: {integrity: sha512-QpgqWi8rD9DsS9EP3z7BT+5lY5SFhsqGjpgW5DY/i3mK4M9DTBNz3ErMi8BWYEfI3L0d8GIbGmcdFAS1uIRGjA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 qs@6.14.0: resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} engines: {node: '>=0.6'} - quick-lru@4.0.1: - resolution: {integrity: sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==} - engines: {node: '>=8'} - - radash@12.1.1: - resolution: {integrity: sha512-h36JMxKRqrAxVD8201FrCpyeNuUY9Y5zZwujr20fFO77tpUtGa6EZzfKw/3WaiBX95fq7+MpsuMLNdSnORAwSA==} - engines: {node: '>=14.18.0'} - - react-click-away-listener@2.4.0: - resolution: {integrity: sha512-jDkXY8Q9qM8e197K7c7AoVhhk2meQO5POyjRJrKN2vUQUvIef49h/paM3JA6q+lf+JygDy9ENOBOsZalARUIeg==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - - react-datepicker@8.7.0: - resolution: {integrity: sha512-r5OJbiLWc3YiVNy69Kau07/aVgVGsFVMA6+nlqCV7vyQ8q0FUOnJ+wAI4CgVxHejG3i5djAEiebrF8/Eip4rIw==} - peerDependencies: - react: ^16.9.0 || ^17 || ^18 || ^19 || ^19.0.0-rc - react-dom: ^16.9.0 || ^17 || ^18 || ^19 || ^19.0.0-rc - - react-dom@19.1.1: - resolution: {integrity: sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==} - peerDependencies: - react: ^19.1.1 - - react-hook-form@7.63.0: - resolution: {integrity: sha512-ZwueDMvUeucovM2VjkCf7zIHcs1aAlDimZu2Hvel5C5907gUzMpm4xCrQXtRzCvsBqFjonB4m3x4LzCFI1ZKWA==} - engines: {node: '>=18.0.0'} - peerDependencies: - react: ^16.8.0 || ^17 || ^18 || ^19 - - react-idle-timer@5.7.2: - resolution: {integrity: sha512-+BaPfc7XEUU5JFkwZCx6fO1bLVK+RBlFH+iY4X34urvIzZiZINP6v2orePx3E6pAztJGE7t4DzvL7if2SL/0GQ==} - peerDependencies: - react: '>=16' - react-dom: '>=16' - - react-intersection-observer@9.16.0: - resolution: {integrity: sha512-w9nJSEp+DrW9KmQmeWHQyfaP6b03v+TdXynaoA964Wxt7mdR3An11z4NNCQgL4gKSK7y1ver2Fq+JKH6CWEzUA==} - peerDependencies: - react: ^17.0.0 || ^18.0.0 || ^19.0.0 - react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0 - peerDependenciesMeta: - react-dom: - optional: true - - react-is@16.13.1: - resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - react-is@19.1.1: - resolution: {integrity: sha512-tr41fA15Vn8p4X9ntI+yCyeGSf1TlYaY5vlTZfQmeLBrFo3psOPX6HhTDnFNL9uj3EhP0KAQ80cugCl4b4BERA==} - - react-loading-skeleton@3.5.0: - resolution: {integrity: sha512-gxxSyLbrEAdXTKgfbpBEFZCO/P153DnqSCQau2+o6lNy1jgMRr2MmRmOzMmyrwSaSYLRB8g7b0waYPmUjz7IhQ==} + react-dom@19.2.0: + resolution: {integrity: sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==} peerDependencies: - react: '>=16.8.0' + react: ^19.2.0 react-markdown@10.1.0: resolution: {integrity: sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==} @@ -2430,120 +2473,21 @@ packages: '@types/react': '>=18' react: '>=18' - react-property@2.0.2: - resolution: {integrity: sha512-+PbtI3VuDV0l6CleQMsx2gtK0JZbZKbpdu5ynr+lbsuvtmgbNcS3VM0tuY2QjFNOcWxvXeHjDpy42RO+4U2rug==} - - react-qr-code@2.0.18: - resolution: {integrity: sha512-v1Jqz7urLMhkO6jkgJuBYhnqvXagzceg3qJUWayuCK/c6LTIonpWbwxR1f1APGd4xrW/QcQEovNrAojbUz65Tg==} - peerDependencies: - react: '*' - - react-redux@9.2.0: - resolution: {integrity: sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==} - peerDependencies: - '@types/react': ^18.2.25 || ^19 - react: ^18.0 || ^19 - redux: ^5.0.0 - peerDependenciesMeta: - '@types/react': - optional: true - redux: - optional: true - - react-resize-detector@12.3.0: - resolution: {integrity: sha512-mIDOVrTHKGnKe6qEUWi8dFdfHM5CPyTOpqoHctdMQf89Ljm/0qqDIzkP3vTRZZJi9/raaMiRxDEOqO4you5x+A==} - peerDependencies: - react: ^18.0.0 || ^19.0.0 - - react-router-dom@6.30.1: - resolution: {integrity: sha512-llKsgOkZdbPU1Eg3zK8lCn+sjD9wMRZZPuzmdWWX5SUs8OFkN5HnFVC0u5KMeMaC9aoancFI/KoLuKPqN+hxHw==} - engines: {node: '>=14.0.0'} - peerDependencies: - react: '>=16.8' - react-dom: '>=16.8' - - react-router@6.30.1: - resolution: {integrity: sha512-X1m21aEmxGXqENEPG3T6u0Th7g0aS4ZmoNynhbs+Cn+q+QGTLt+d5IQ2bHAXKzKcxGJjxACpVbnYQSCRcfxHlQ==} - engines: {node: '>=14.0.0'} - peerDependencies: - react: '>=16.8' - - react-simple-animate@3.5.3: - resolution: {integrity: sha512-Ob+SmB5J1tXDEZyOe2Hf950K4M8VaWBBmQ3cS2BUnTORqHjhK0iKG8fB+bo47ZL15t8d3g/Y0roiqH05UBjG7A==} - peerDependencies: - react-dom: ^16.8.0 || ^17 || ^18 || ^19 - - react-tracked@2.0.1: - resolution: {integrity: sha512-qjbmtkO2IcW+rB2cFskRWDTjKs/w9poxvNnduacjQA04LWxOoLy9J8WfIEq1ahifQ/tVJQECrQPBm+UEzKRDtg==} - peerDependencies: - react: '>=18.0.0' - scheduler: '>=0.19.0' - - react-virtualized-auto-sizer@1.0.26: - resolution: {integrity: sha512-CblNyiNVw2o+hsa5/49NH2ogGxZ+t+3aweRvNSq7TVjDIlwk7ir4lencEg5HxHeSzwNarSkNkiu0qJSOXtxm5A==} - peerDependencies: - react: ^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0 || ^19.0.0 - react-dom: ^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0 || ^19.0.0 - - react@19.1.1: - resolution: {integrity: sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==} + react@19.2.0: + resolution: {integrity: sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==} engines: {node: '>=0.10.0'} - read-pkg-up@3.0.0: - resolution: {integrity: sha512-YFzFrVvpC6frF1sz8psoHDBGF7fLPc+llq/8NB43oagqWkx8ar5zYtsTORtOjw9W2RHLpWP+zTWwBvf1bCmcSw==} - engines: {node: '>=4'} - - read-pkg-up@7.0.1: - resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==} - engines: {node: '>=8'} - - read-pkg@3.0.0: - resolution: {integrity: sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==} - engines: {node: '>=4'} - - read-pkg@5.2.0: - resolution: {integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==} - engines: {node: '>=8'} - - readable-stream@2.3.8: - resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} - - readable-stream@3.6.2: - resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} - engines: {node: '>= 6'} - readdirp@3.6.0: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} - recharts@3.2.1: - resolution: {integrity: sha512-0JKwHRiFZdmLq/6nmilxEZl3pqb4T+aKkOkOi/ZISRZwfBhVMgInxzlYU9D4KnCH3KINScLy68m/OvMXoYGZUw==} - engines: {node: '>=18'} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react-is: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - - redent@3.0.0: - resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} - engines: {node: '>=8'} - - redux-thunk@3.1.0: - resolution: {integrity: sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==} - peerDependencies: - redux: ^5.0.0 - - redux@5.0.1: - resolution: {integrity: sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==} - - rehype-external-links@3.0.0: - resolution: {integrity: sha512-yp+e5N9V3C6bwBeAC4n796kc86M4gJCdlVhiMTxIrJG5UHDMh+PJANf9heqORJbt1nrCbDwIlAZKjANIaVBbvw==} + readdirp@4.1.2: + resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} + engines: {node: '>= 14.18.0'} - rehype-raw@7.0.0: - resolution: {integrity: sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==} - - rehype-sanitize@6.0.0: - resolution: {integrity: sha512-CsnhKNsyI8Tub6L4sm5ZFsme4puGfc6pYylvXo1AeqaGbjOYyzNv3qZPwvs0oMJ39eryyeOdmxwUIo94IpEhqg==} + recast@0.23.11: + resolution: {integrity: sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA==} + engines: {node: '>= 4'} remark-parse@11.0.0: resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==} @@ -2551,62 +2495,74 @@ packages: remark-rehype@11.1.2: resolution: {integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==} - require-directory@2.1.1: - resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} - require-main-filename@2.0.0: - resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} - - reselect@5.1.1: - resolution: {integrity: sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==} - resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} - resolve@1.22.10: - resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} - engines: {node: '>= 0.4'} - hasBin: true + resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} - rollup@4.52.2: - resolution: {integrity: sha512-I25/2QgoROE1vYV+NQ1En9T9UFB9Cmfm2CJ83zZOlaDpvz29wGQSZXWKw7MiNXau7wYgB/T9fVIdIuEQ+KbiiA==} + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rollup@4.52.5: + resolution: {integrity: sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + rxjs@7.8.2: resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} - safe-buffer@5.1.2: - resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} - - safe-buffer@5.2.1: - resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - - sass@1.70.0: - resolution: {integrity: sha512-uUxNQ3zAHeAx5nRFskBnrWzDUJrrvpCPD5FNAoRvTi0WwremlheES3tg+56PaVtCs5QDRX5CBLxxKMDJMEa1WQ==} + sass@1.93.3: + resolution: {integrity: sha512-elOcIZRTM76dvxNAjqYrucTSI0teAF/L2Lv0s6f6b7FOwcwIuA357bIE871580AjHJuSvLIRUosgV+lIWx6Rgg==} engines: {node: '>=14.0.0'} hasBin: true - scheduler@0.26.0: - resolution: {integrity: sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==} - - semver@5.7.2: - resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} - hasBin: true + scheduler@0.27.0: + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true - semver@7.7.2: - resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} + semver@7.7.3: + resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} engines: {node: '>=10'} hasBin: true - set-blocking@2.0.0: - resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} + seroval-plugins@1.3.3: + resolution: {integrity: sha512-16OL3NnUBw8JG1jBLUoZJsLnQq0n5Ua6aHalhJK4fMQkz1lqR7Osz1sA30trBtd9VUDc2NgkuRCn8+/pBwqZ+w==} + engines: {node: '>=10'} + peerDependencies: + seroval: ^1.0 + + seroval@1.3.2: + resolution: {integrity: sha512-RbcPH1n5cfwKrru7v7+zrZvjLurgHhGyso3HTyGtRivGWgYjbOmGuivCQaORNELjNONoK35nj28EoWul9sb1zQ==} + engines: {node: '>=10'} + + sharp@0.34.4: + resolution: {integrity: sha512-FUH39xp3SBPnxWvd5iib1X8XY7J0K0X7d93sie9CJg2PO8/7gmg89Nve6OjItK53/MlAushNNxteBYfM6DEuoA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} shell-quote@1.8.3: resolution: {integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==} @@ -2621,195 +2577,136 @@ packages: engines: {node: '>= 0.4'} side-channel-weakmap@1.0.2: - resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} - engines: {node: '>= 0.4'} - - side-channel@1.1.0: - resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} - engines: {node: '>= 0.4'} - - source-map-js@1.2.1: - resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} - engines: {node: '>=0.10.0'} - - source-map-support@0.5.21: - resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} - - source-map@0.5.7: - resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==} - engines: {node: '>=0.10.0'} - - source-map@0.6.1: - resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} - engines: {node: '>=0.10.0'} - - space-separated-tokens@2.0.2: - resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} - - spdx-correct@3.2.0: - resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} - - spdx-exceptions@2.5.0: - resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} - - spdx-expression-parse@3.0.1: - resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} - - spdx-license-ids@3.0.22: - resolution: {integrity: sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==} - - split2@3.2.2: - resolution: {integrity: sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==} - - split@1.0.1: - resolution: {integrity: sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==} - - standard-version@9.5.0: - resolution: {integrity: sha512-3zWJ/mmZQsOaO+fOlsa0+QK90pwhNd042qEcw6hKFNoLFs7peGyvPffpEBbK/DSGPbyOvli0mUIFv5A4qTjh2Q==} - engines: {node: '>=10'} - hasBin: true - - string-width@4.2.3: - resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} - engines: {node: '>=8'} - - string_decoder@1.1.1: - resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} - - string_decoder@1.3.0: - resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} - - stringify-entities@4.0.4: - resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} - - stringify-package@1.0.1: - resolution: {integrity: sha512-sa4DUQsYciMP1xhKWGuFM04fB0LG/9DlluZoSVywUMRNvzid6XucHK0/90xGxRoHrAaROrcHK1aPKaijCtSrhg==} - deprecated: This module is not used anymore, and has been replaced by @npmcli/package-json - - strip-ansi@6.0.1: - resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} - engines: {node: '>=8'} - - strip-bom@3.0.0: - resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} - engines: {node: '>=4'} - - strip-indent@3.0.0: - resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} - engines: {node: '>=8'} - - style-to-js@1.1.17: - resolution: {integrity: sha512-xQcBGDxJb6jjFCTzvQtfiPn6YvvP2O8U1MDIPNfJQlWMYfktPy+iGsHE7cssjs7y84d9fQaK4UF3RIJaAHSoYA==} - - style-to-object@1.0.9: - resolution: {integrity: sha512-G4qppLgKu/k6FwRpHiGiKPaPTFcG3g4wNVX/Qsfu+RqQM30E7Tyu/TEgxcL9PNLF5pdRLwQdE3YKKf+KF2Dzlw==} - - stylis@4.2.0: - resolution: {integrity: sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==} - - supports-color@5.5.0: - resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} - engines: {node: '>=4'} - - supports-color@7.2.0: - resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} - engines: {node: '>=8'} - - supports-color@8.1.1: - resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} - engines: {node: '>=10'} - - supports-preserve-symlinks-flag@1.0.0: - resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} engines: {node: '>= 0.4'} - tabbable@6.2.0: - resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==} + side-channel@1.1.0: + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + engines: {node: '>= 0.4'} - terser@5.37.0: - resolution: {integrity: sha512-B8wRRkmre4ERucLM/uXx4MOV5cbnOlVAqUst+1+iLKPI0dOgFO28f84ptoQt9HEI537PMzfYa/d+GEPKTRXmYA==} - engines: {node: '>=10'} - hasBin: true + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} - text-camel-case@1.2.9: - resolution: {integrity: sha512-wKYs9SgRxYizJE1mneR7BbLNlGw2IYzJAS8XwkWIry0CTbO1gvvPkFsx5Z1/hr+VqUaBqx9q3yKd30HpZLdMsQ==} + slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} - text-capital-case@1.2.9: - resolution: {integrity: sha512-X5zV8U8pxtq2xS2t46lgAWqZdDbgWMKq03MQSNwY2CJdQCsdTNh144E2Q/q9wBxWzSBUXn+jRc9kF+Gs8/pGhA==} + slice-ansi@4.0.0: + resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==} + engines: {node: '>=10'} - text-case@1.2.9: - resolution: {integrity: sha512-zZVdA8rMcjx9zhekdUuOPZShc25UTV7W8/ddKbgbPtfCEvIiToPtWiSd2lXLSuiGMovNhJ4+Tw49xll9o9ts+Q==} + solid-js@1.9.9: + resolution: {integrity: sha512-A0ZBPJQldAeGCTW0YRYJmt7RCeh5rbFfPZ2aOttgYnctHE7HgKeHCBB/PVc2P7eOfmNXqMFFFoYYdm3S4dcbkA==} - text-constant-case@1.2.9: - resolution: {integrity: sha512-Vosm6nC7Gag+JFakJHwqS9AXRNgl07j5KZ7srU9cYuKRzYwrxzeJ4RpEogRBNHw7CfmOm0j5FGEznblWtu7pIw==} + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} - text-dot-case@1.2.9: - resolution: {integrity: sha512-N83hsnvGdSO9q9AfNSB9Cy1LFDNN2MCx53LcxtaPoDWPUTk47fv0JlvIY1tgY0wyzCiThF03kVj3jworvAOScA==} + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} - text-extensions@1.9.0: - resolution: {integrity: sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==} - engines: {node: '>=0.10'} + source-map@0.7.6: + resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} + engines: {node: '>= 12'} - text-header-case@1.2.9: - resolution: {integrity: sha512-TqryEKcYisQAfWLbtT3xPnZlMZ/mySO1uS+LUg+B0eNuqgETrSzVpXIUj5E6Zf/EyJHgpZf4VndbAXtOMJuT4w==} + space-separated-tokens@2.0.2: + resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} - text-is-lower-case@1.2.9: - resolution: {integrity: sha512-cEurrWSnYVYqL8FSwl5cK4mdfqF7qNDCcKJgXI3NnfTesiB8umxAhdlQoErrRYI1xEvYr2WN0MI333EehUhQjg==} + sqlite-wasm-kysely@0.3.0: + resolution: {integrity: sha512-TzjBNv7KwRw6E3pdKdlRyZiTmUIE0UttT/Sl56MVwVARl/u5gp978KepazCJZewFUnlWHz9i3NQd4kOtP/Afdg==} + peerDependencies: + kysely: '*' - text-is-upper-case@1.2.9: - resolution: {integrity: sha512-HxsWr3VCsXXiLlhD0c+Ey+mS2lOTCiSJbkepjaXNHl2bp33KiscQaiG0qLwQmmpZQm4SJCg2s9FkndxS0RNDLQ==} + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} - text-kebab-case@1.2.9: - resolution: {integrity: sha512-nOUyNR5Ej2B9D/wyyXfwUEv26+pQuOb1pEX+ojE37mCIWo8QeOxw5y6nxuqDmG7NrEPzbO6265UMV+EICH13Cw==} + stringify-entities@4.0.4: + resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} - text-lower-case-first@1.2.9: - resolution: {integrity: sha512-iiphHTV7PVH0MljrEQUA9iBE7jfDpXoi4RQju3WzZU3BRVbS6540cNZgxR19hWa0z6z/7cJTH0Ls9LPBaiUfKg==} + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} - text-lower-case@1.2.9: - resolution: {integrity: sha512-53AOnDrhPpiAUQkgY1SHleKUXp/u7GsqRX13NcCREZscmtjLLJ099uxMRjkK7q2KwHkFYVPl9ytkQlTkTQLS0w==} + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} - text-no-case@1.2.9: - resolution: {integrity: sha512-IcCt328KaapimSrytP4ThfC8URmHZb2DgOqCL9BYvGjpxY2lDiqCkIQk9sClZtwcELs2gTnq83a7jNc573FTLA==} + style-to-js@1.1.19: + resolution: {integrity: sha512-Ev+SgeqiNGT1ufsXyVC5RrJRXdrkRJ1Gol9Qw7Pb72YCKJXrBvP0ckZhBeVSrw2m06DJpei2528uIpjMb4TsoQ==} - text-param-case@1.2.9: - resolution: {integrity: sha512-nR/Ju9amY3aQS1en2CUCgqN/ZiZIVdDyjlJ3xX5J92ChBevGuA4o9K10fh3JGMkbzK97Vcb+bWQJ4Q+Svz+GyQ==} + style-to-object@1.0.12: + resolution: {integrity: sha512-ddJqYnoT4t97QvN2C95bCgt+m7AAgXjVnkk/jxAfmp7EAB8nnqqZYEbMd3em7/vEomDb2LAQKAy1RFfv41mdNw==} - text-pascal-case@1.2.9: - resolution: {integrity: sha512-o6ZxMGjWDTUW54pcghpXes+C2PqbYRMdU5mHrIhueb6z6nq1NueiIOeCUdrSjN/3wXfhCmnFjK7/d9aRGZNqSg==} + stylelint-config-recommended-scss@16.0.2: + resolution: {integrity: sha512-aUTHhPPWCvFyWaxtckJlCPaXTDFsp4pKO8evXNCsW9OwsaUWyMd6jvcUhSmfGWPrTddvzNqK4rS/UuSLcbVGdQ==} + engines: {node: '>=20'} + peerDependencies: + postcss: ^8.3.3 + stylelint: ^16.24.0 + peerDependenciesMeta: + postcss: + optional: true - text-path-case@1.2.9: - resolution: {integrity: sha512-s8cJ6r5TkJp5ticXMgtxd7f12odEN4d1CfX5u4aoz6jcUtBR2lDqzIhVimkqWFMJ4UKPSrmilUha8Xc2BPi+ow==} + stylelint-config-recommended@17.0.0: + resolution: {integrity: sha512-WaMSdEiPfZTSFVoYmJbxorJfA610O0tlYuU2aEwY33UQhSPgFbClrVJYWvy3jGJx+XW37O+LyNLiZOEXhKhJmA==} + engines: {node: '>=18.12.0'} + peerDependencies: + stylelint: ^16.23.0 - text-sentence-case@1.2.9: - resolution: {integrity: sha512-/G/Yi5kZfUa1edFRV4O3lGZAkbDZTFvlwW8CYfH7szkEGe2k2MYEYbOyAkGRVQEGV6V6JiuUAaP3VS9c1tB6nQ==} + stylelint-config-standard-scss@16.0.0: + resolution: {integrity: sha512-/FHECLUu+med/e6OaPFpprG86ShC4SYT7Tzb2PTVdDjJsehhFBOioSlWqYFqJxmGPIwO3AMBxNo+kY3dxrbczA==} + engines: {node: '>=20'} + peerDependencies: + postcss: ^8.3.3 + stylelint: ^16.23.1 + peerDependenciesMeta: + postcss: + optional: true - text-snake-case@1.2.9: - resolution: {integrity: sha512-+ZrqK19ynF/TLQZ7ynqVrL2Dy04uu9syYZwsm8PhzUdsY3XrwPy6QiRqhIEFqhyWbShPcfyfmheer5UEQqFxlw==} + stylelint-config-standard@39.0.1: + resolution: {integrity: sha512-b7Fja59EYHRNOTa3aXiuWnhUWXFU2Nfg6h61bLfAb5GS5fX3LMUD0U5t4S8N/4tpHQg3Acs2UVPR9jy2l1g/3A==} + engines: {node: '>=18.12.0'} + peerDependencies: + stylelint: ^16.23.0 - text-swap-case@1.2.9: - resolution: {integrity: sha512-g5fp12ldktYKK9wdHRMvvtSCQrZYNv/D+ZGLumDsvAY4q9T5bCMO2IWMkIP1F5gVQrysdHH6Xv877P/pjUq1iw==} + stylelint-scss@6.12.1: + resolution: {integrity: sha512-UJUfBFIvXfly8WKIgmqfmkGKPilKB4L5j38JfsDd+OCg2GBdU0vGUV08Uw82tsRZzd4TbsUURVVNGeOhJVF7pA==} + engines: {node: '>=18.12.0'} + peerDependencies: + stylelint: ^16.0.2 - text-title-case@1.2.9: - resolution: {integrity: sha512-RAtC9cdmPp41ns5/HXZBsaQg71BsHT7uZpj2ojTtuFa8o2dNuRYYOrSmy5YdLRIAJQ6WK5hQVpV3jHuq7a+4Tw==} + stylelint@16.25.0: + resolution: {integrity: sha512-Li0avYWV4nfv1zPbdnxLYBGq4z8DVZxbRgx4Kn6V+Uftz1rMoF1qiEI3oL4kgWqyYgCgs7gT5maHNZ82Gk03vQ==} + engines: {node: '>=18.12.0'} + hasBin: true - text-upper-case-first@1.2.9: - resolution: {integrity: sha512-wEDD1B6XqJmEV+xEnBJd+2sBCHZ+7fvA/8Rv/o8+dAsp05YWjYP/kjB8sPH6zqzW0s6jtehIg4IlcKjcYxk2CQ==} + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} - text-upper-case@1.2.9: - resolution: {integrity: sha512-K/0DNT7a4z8eah2spARtoJllTZyrNTo6Uc0ujhN/96Ir9uJ/slpahfs13y46H9osL3daaLl3O7iXOkW4xtX6bg==} + supports-hyperlinks@3.2.0: + resolution: {integrity: sha512-zFObLMyZeEwzAoKCyu1B91U79K2t7ApXuQfo8OuxwXLDgcKxuwM+YvcbIhm6QWqz7mHUH1TVytR1PwVVjEuMig==} + engines: {node: '>=14.18'} - through2@2.0.5: - resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==} + svg-tags@1.0.0: + resolution: {integrity: sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==} - through2@4.0.2: - resolution: {integrity: sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==} + tabbable@6.3.0: + resolution: {integrity: sha512-EIHvdY5bPLuWForiR/AN2Bxngzpuwn1is4asboytXtpTgsArc+WmSJKVLlhdh71u7jFcryDqB2A8lQvj78MkyQ==} - through@2.3.8: - resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + table@6.9.0: + resolution: {integrity: sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==} + engines: {node: '>=10.0.0'} tiny-invariant@1.3.3: resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} + tiny-warning@1.0.3: + resolution: {integrity: sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==} + tinyglobby@0.2.15: resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} engines: {node: '>=12.0.0'} @@ -2818,66 +2715,50 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} - tree-kill@1.2.2: - resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} - hasBin: true - trim-lines@3.0.1: resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} - trim-newlines@3.0.1: - resolution: {integrity: sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==} - engines: {node: '>=8'} - trough@2.2.0: resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} + ts-api-utils@2.1.0: + resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - type-fest@0.18.1: - resolution: {integrity: sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==} - engines: {node: '>=10'} - - type-fest@0.6.0: - resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==} - engines: {node: '>=8'} - - type-fest@0.8.1: - resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==} - engines: {node: '>=8'} - - type-fest@4.41.0: - resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} - engines: {node: '>=16'} + tsx@4.20.6: + resolution: {integrity: sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg==} + engines: {node: '>=18.0.0'} + hasBin: true - typedarray@0.0.6: - resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} - typesafe-i18n@5.26.2: - resolution: {integrity: sha512-2QAriFmiY5JwUAJtG7yufoE/XZ1aFBY++wj7YFS2yo89a3jLBfKoWSdq5JfQYk1V2BS7V2c/u+KEcaCQoE65hw==} - hasBin: true + typescript-eslint@8.46.2: + resolution: {integrity: sha512-vbw8bOmiuYNdzzV3lsiWv6sRwjyuKJMQqWulBOU7M0RrxedXledX8G8kBbQeiOYDnTfiXz0Y4081E1QMNB6iQg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - typescript: '>=3.5.1' + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' - typescript@5.9.2: - resolution: {integrity: sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==} + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} engines: {node: '>=14.17'} hasBin: true - uglify-js@3.19.3: - resolution: {integrity: sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==} - engines: {node: '>=0.8.0'} - hasBin: true - - undici-types@7.12.0: - resolution: {integrity: sha512-goOacqME2GYyOZZfb5Lgtu+1IDmAlAEu5xnD3+xTzS10hT0vzpf0SPjkXwAw9Jm+4n/mQGDP3LO8CPbYROeBfQ==} + undici-types@7.16.0: + resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} unified@11.0.5: resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} - unist-util-is@6.0.0: - resolution: {integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==} + unist-util-is@6.0.1: + resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==} unist-util-position@5.0.0: resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} @@ -2885,70 +2766,61 @@ packages: unist-util-stringify-position@4.0.0: resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} - unist-util-visit-parents@6.0.1: - resolution: {integrity: sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==} + unist-util-visit-parents@6.0.2: + resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==} unist-util-visit@5.0.0: resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} - update-browserslist-db@1.1.3: - resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} + unplugin@2.3.10: + resolution: {integrity: sha512-6NCPkv1ClwH+/BGE9QeoTIl09nuiAt0gS28nn1PvYXsGKRwM2TCbFA2QiilmehPDTXIe684k4rZI1yl3A1PCUw==} + engines: {node: '>=18.12.0'} + + update-browserslist-db@1.1.4: + resolution: {integrity: sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' - use-breakpoint@4.0.6: - resolution: {integrity: sha512-1s7vUjf36eeZYTgY1KkmPNXrTbKJVRA9cjBFQdYjK8+pDr0qJgH6/cuX5qQ2zcfkqxN5LieVd/DTVK6ofnwRTQ==} - peerDependencies: - react: '>=18' - react-dom: '>=18' - - use-context-selector@2.0.0: - resolution: {integrity: sha512-owfuSmUNd3eNp3J9CdDl0kMgfidV+MkDvHPpvthN5ThqM+ibMccNE0k+Iq7TWC6JPFvGZqanqiGCuQx6DyV24g==} - peerDependencies: - react: '>=18.0.0' - scheduler: '>=0.19.0' + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} - use-deep-compare-effect@1.8.1: - resolution: {integrity: sha512-kbeNVZ9Zkc0RFGpfMN3MNfaKNvcLNyxOAAd9O4CBZ+kCBXXscn9s/4I+8ytUER4RDpEYs5+O6Rs4PqiZ+rHr5Q==} - engines: {node: '>=10', npm: '>=6'} - peerDependencies: - react: '>=16.13' + urlpattern-polyfill@10.1.0: + resolution: {integrity: sha512-IGjKp/o0NL3Bso1PymYURCJxMPNAf/ILOpendP9f5B6e1rTJgdgiOvgfoT8VxCAdY+Wisb9uhGaJJf3yZ2V9nw==} - use-sync-external-store@1.5.0: - resolution: {integrity: sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==} + use-sync-external-store@1.6.0: + resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - uuid@8.3.2: - resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} + uuid@10.0.0: + resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==} hasBin: true - validate-npm-package-license@3.0.4: - resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} - - vfile-location@5.0.3: - resolution: {integrity: sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==} - vfile-message@4.0.3: resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} vfile@6.0.3: resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} - victory-vendor@37.3.6: - resolution: {integrity: sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ==} - - vite-plugin-package-version@1.1.0: - resolution: {integrity: sha512-TPoFZXNanzcaKCIrC3e2L/TVRkkRLB6l4RPN/S7KbG7rWfyLcCEGsnXvxn6qR7fyZwXalnnSN/I9d6pSFjHpEA==} + vite-plugin-image-optimizer@2.0.3: + resolution: {integrity: sha512-1vrFOTcpSvv6DCY7h8UXab4wqMAjTJB/ndOzG/Kmj1oDOuPF6mbjkNQoGzzCEYeWGe7qU93jc8oQqvoJ57al3A==} + engines: {node: '>=18.17.0'} peerDependencies: - vite: '>=2.0.0-beta.69' + sharp: '>=0.34.0' + svgo: '>=4' + vite: '>=5' + peerDependenciesMeta: + sharp: + optional: true + svgo: + optional: true - vite@7.1.7: - resolution: {integrity: sha512-VbA8ScMvAISJNJVbRDTJdCwqQoAareR/wutevKanhR2/1EkoXVZVkkORaYm/tNVCjP/UDTKtcw3bAkwOUdedmA==} + vite@7.1.12: + resolution: {integrity: sha512-ZWyE8YXEXqJrrSLvYgrRP7p62OziLW7xI5HYGWFzOvupfAlrLvURSzv/FyGyy0eidogEM3ujU+kUG1zuHgb6Ug==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -2987,73 +2859,41 @@ packages: yaml: optional: true - web-namespaces@2.0.1: - resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} - - which-module@2.0.1: - resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} + webpack-virtual-modules@0.6.2: + resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} - wordwrap@1.0.0: - resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} - - wrap-ansi@6.2.0: - resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} - engines: {node: '>=8'} + which@1.3.1: + resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} + hasBin: true - wrap-ansi@7.0.0: - resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} - engines: {node: '>=10'} + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true - xtend@4.0.2: - resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} - engines: {node: '>=0.4'} + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} - y18n@4.0.3: - resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} + write-file-atomic@5.0.1: + resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - y18n@5.0.8: - resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} - engines: {node: '>=10'} + ws@8.18.3: + resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} - yallist@4.0.0: - resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} - - yaml@1.10.2: - resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} - engines: {node: '>= 6'} - - yaml@2.6.1: - resolution: {integrity: sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg==} - engines: {node: '>= 14'} - hasBin: true - - yargs-parser@18.1.3: - resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} - engines: {node: '>=6'} - - yargs-parser@20.2.9: - resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} - engines: {node: '>=10'} - - yargs-parser@21.1.1: - resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} - engines: {node: '>=12'} - - yargs@15.4.1: - resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==} - engines: {node: '>=8'} - - yargs@16.2.0: - resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} - engines: {node: '>=10'} - - yargs@17.7.2: - resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} - engines: {node: '>=12'} - yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} @@ -3061,6 +2901,9 @@ packages: zod@3.25.76: resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + zod@4.1.12: + resolution: {integrity: sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==} + zustand@5.0.8: resolution: {integrity: sha512-gyPKpIaxY9XcO2vSMrLbiER7QMAMGOQZVRdJ6Zi782jkbzZygq5GI9nG8g+sMgitRtndwaBSl7uiqC49o1SSiw==} engines: {node: '>=12.20.0'} @@ -3084,25 +2927,30 @@ packages: snapshots: + '@axa-ch/react-polymorphic-types@1.4.1(@types/react@19.2.2)(react@19.2.0)': + dependencies: + '@types/react': 19.2.2 + react: 19.2.0 + '@babel/code-frame@7.27.1': dependencies: - '@babel/helper-validator-identifier': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 js-tokens: 4.0.0 picocolors: 1.1.1 - '@babel/compat-data@7.28.4': {} + '@babel/compat-data@7.28.5': {} - '@babel/core@7.28.4': + '@babel/core@7.28.5': dependencies: '@babel/code-frame': 7.27.1 - '@babel/generator': 7.28.3 + '@babel/generator': 7.28.5 '@babel/helper-compilation-targets': 7.27.2 - '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.4) + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5) '@babel/helpers': 7.28.4 - '@babel/parser': 7.28.4 + '@babel/parser': 7.28.5 '@babel/template': 7.27.2 - '@babel/traverse': 7.28.4 - '@babel/types': 7.28.4 + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 debug: 4.4.3 @@ -3112,282 +2960,364 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/generator@7.28.3': + '@babel/generator@7.28.5': dependencies: - '@babel/parser': 7.28.4 - '@babel/types': 7.28.4 + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 jsesc: 3.1.0 + '@babel/helper-annotate-as-pure@7.27.3': + dependencies: + '@babel/types': 7.28.5 + '@babel/helper-compilation-targets@7.27.2': dependencies: - '@babel/compat-data': 7.28.4 + '@babel/compat-data': 7.28.5 '@babel/helper-validator-option': 7.27.1 - browserslist: 4.26.2 + browserslist: 4.27.0 lru-cache: 5.1.1 semver: 6.3.1 + '@babel/helper-create-class-features-plugin@7.28.5(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-member-expression-to-functions': 7.28.5 + '@babel/helper-optimise-call-expression': 7.27.1 + '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.5) + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/traverse': 7.28.5 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + '@babel/helper-globals@7.28.0': {} + '@babel/helper-member-expression-to-functions@7.28.5': + dependencies: + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 + transitivePeerDependencies: + - supports-color + '@babel/helper-module-imports@7.27.1': dependencies: - '@babel/traverse': 7.28.4 - '@babel/types': 7.28.4 + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.4)': + '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.4 + '@babel/core': 7.28.5 '@babel/helper-module-imports': 7.27.1 - '@babel/helper-validator-identifier': 7.27.1 - '@babel/traverse': 7.28.4 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/helper-optimise-call-expression@7.27.1': + dependencies: + '@babel/types': 7.28.5 + + '@babel/helper-plugin-utils@7.27.1': {} + + '@babel/helper-replace-supers@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-member-expression-to-functions': 7.28.5 + '@babel/helper-optimise-call-expression': 7.27.1 + '@babel/traverse': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/helper-skip-transparent-expression-wrappers@7.27.1': + dependencies: + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 transitivePeerDependencies: - supports-color '@babel/helper-string-parser@7.27.1': {} - '@babel/helper-validator-identifier@7.27.1': {} + '@babel/helper-validator-identifier@7.28.5': {} '@babel/helper-validator-option@7.27.1': {} '@babel/helpers@7.28.4': dependencies: '@babel/template': 7.27.2 - '@babel/types': 7.28.4 + '@babel/types': 7.28.5 + + '@babel/parser@7.28.5': + dependencies: + '@babel/types': 7.28.5 + + '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/parser@7.28.4': + '@babel/plugin-transform-modules-commonjs@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/types': 7.28.4 + '@babel/core': 7.28.5 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-typescript@7.28.5(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-create-class-features-plugin': 7.28.5(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.5) + transitivePeerDependencies: + - supports-color - '@babel/runtime@7.28.4': {} + '@babel/preset-typescript@7.28.5(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-validator-option': 7.27.1 + '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-modules-commonjs': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-typescript': 7.28.5(@babel/core@7.28.5) + transitivePeerDependencies: + - supports-color '@babel/template@7.27.2': dependencies: '@babel/code-frame': 7.27.1 - '@babel/parser': 7.28.4 - '@babel/types': 7.28.4 + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 - '@babel/traverse@7.28.4': + '@babel/traverse@7.28.5': dependencies: '@babel/code-frame': 7.27.1 - '@babel/generator': 7.28.3 + '@babel/generator': 7.28.5 '@babel/helper-globals': 7.28.0 - '@babel/parser': 7.28.4 + '@babel/parser': 7.28.5 '@babel/template': 7.27.2 - '@babel/types': 7.28.4 + '@babel/types': 7.28.5 debug: 4.4.3 transitivePeerDependencies: - supports-color - '@babel/types@7.28.4': + '@babel/types@7.28.5': dependencies: '@babel/helper-string-parser': 7.27.1 - '@babel/helper-validator-identifier': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 - '@biomejs/biome@2.2.2': + '@biomejs/biome@2.3.3': optionalDependencies: - '@biomejs/cli-darwin-arm64': 2.2.2 - '@biomejs/cli-darwin-x64': 2.2.2 - '@biomejs/cli-linux-arm64': 2.2.2 - '@biomejs/cli-linux-arm64-musl': 2.2.2 - '@biomejs/cli-linux-x64': 2.2.2 - '@biomejs/cli-linux-x64-musl': 2.2.2 - '@biomejs/cli-win32-arm64': 2.2.2 - '@biomejs/cli-win32-x64': 2.2.2 - - '@biomejs/cli-darwin-arm64@2.2.2': + '@biomejs/cli-darwin-arm64': 2.3.3 + '@biomejs/cli-darwin-x64': 2.3.3 + '@biomejs/cli-linux-arm64': 2.3.3 + '@biomejs/cli-linux-arm64-musl': 2.3.3 + '@biomejs/cli-linux-x64': 2.3.3 + '@biomejs/cli-linux-x64-musl': 2.3.3 + '@biomejs/cli-win32-arm64': 2.3.3 + '@biomejs/cli-win32-x64': 2.3.3 + + '@biomejs/cli-darwin-arm64@2.3.3': optional: true - '@biomejs/cli-darwin-x64@2.2.2': + '@biomejs/cli-darwin-x64@2.3.3': optional: true - '@biomejs/cli-linux-arm64-musl@2.2.2': + '@biomejs/cli-linux-arm64-musl@2.3.3': optional: true - '@biomejs/cli-linux-arm64@2.2.2': + '@biomejs/cli-linux-arm64@2.3.3': optional: true - '@biomejs/cli-linux-x64-musl@2.2.2': + '@biomejs/cli-linux-x64-musl@2.3.3': optional: true - '@biomejs/cli-linux-x64@2.2.2': + '@biomejs/cli-linux-x64@2.3.3': optional: true - '@biomejs/cli-win32-arm64@2.2.2': + '@biomejs/cli-win32-arm64@2.3.3': optional: true - '@biomejs/cli-win32-x64@2.2.2': + '@biomejs/cli-win32-x64@2.3.3': optional: true - '@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4)': + '@cacheable/memoize@2.0.3': dependencies: - '@csstools/css-tokenizer': 3.0.4 + '@cacheable/utils': 2.2.0 - '@csstools/css-tokenizer@3.0.4': {} - - '@emotion/babel-plugin@11.13.5': + '@cacheable/memory@2.0.4': dependencies: - '@babel/helper-module-imports': 7.27.1 - '@babel/runtime': 7.28.4 - '@emotion/hash': 0.9.2 - '@emotion/memoize': 0.9.0 - '@emotion/serialize': 1.3.3 - babel-plugin-macros: 3.1.0 - convert-source-map: 1.9.0 - escape-string-regexp: 4.0.0 - find-root: 1.1.0 - source-map: 0.5.7 - stylis: 4.2.0 - transitivePeerDependencies: - - supports-color + '@cacheable/utils': 2.2.0 + '@keyv/bigmap': 1.1.0(keyv@5.5.3) + hookified: 1.12.2 + keyv: 5.5.3 - '@emotion/cache@11.14.0': + '@cacheable/utils@2.2.0': dependencies: - '@emotion/memoize': 0.9.0 - '@emotion/sheet': 1.4.0 - '@emotion/utils': 1.4.2 - '@emotion/weak-memoize': 0.4.0 - stylis: 4.2.0 + keyv: 5.5.3 - '@emotion/hash@0.9.2': {} - - '@emotion/is-prop-valid@1.4.0': + '@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4)': dependencies: - '@emotion/memoize': 0.9.0 - - '@emotion/memoize@0.9.0': {} + '@csstools/css-tokenizer': 3.0.4 - '@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1)': - dependencies: - '@babel/runtime': 7.28.4 - '@emotion/babel-plugin': 11.13.5 - '@emotion/cache': 11.14.0 - '@emotion/serialize': 1.3.3 - '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@19.1.1) - '@emotion/utils': 1.4.2 - '@emotion/weak-memoize': 0.4.0 - hoist-non-react-statics: 3.3.2 - react: 19.1.1 - optionalDependencies: - '@types/react': 19.1.13 - transitivePeerDependencies: - - supports-color + '@csstools/css-tokenizer@3.0.4': {} - '@emotion/serialize@1.3.3': + '@csstools/media-query-list-parser@4.0.3(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': dependencies: - '@emotion/hash': 0.9.2 - '@emotion/memoize': 0.9.0 - '@emotion/unitless': 0.10.0 - '@emotion/utils': 1.4.2 - csstype: 3.1.3 - - '@emotion/sheet@1.4.0': {} + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 - '@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@types/react@19.1.13)(react@19.1.1)': + '@csstools/selector-specificity@5.0.0(postcss-selector-parser@7.1.0)': dependencies: - '@babel/runtime': 7.28.4 - '@emotion/babel-plugin': 11.13.5 - '@emotion/is-prop-valid': 1.4.0 - '@emotion/react': 11.14.0(@types/react@19.1.13)(react@19.1.1) - '@emotion/serialize': 1.3.3 - '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@19.1.1) - '@emotion/utils': 1.4.2 - react: 19.1.1 - optionalDependencies: - '@types/react': 19.1.13 - transitivePeerDependencies: - - supports-color + postcss-selector-parser: 7.1.0 - '@emotion/unitless@0.10.0': {} + '@dual-bundle/import-meta-resolve@4.2.1': {} - '@emotion/use-insertion-effect-with-fallbacks@1.2.0(react@19.1.1)': + '@emnapi/runtime@1.7.0': dependencies: - react: 19.1.1 - - '@emotion/utils@1.4.2': {} - - '@emotion/weak-memoize@0.4.0': {} + tslib: 2.8.1 + optional: true - '@esbuild/aix-ppc64@0.25.10': + '@esbuild/aix-ppc64@0.25.12': optional: true - '@esbuild/android-arm64@0.25.10': + '@esbuild/android-arm64@0.25.12': optional: true - '@esbuild/android-arm@0.25.10': + '@esbuild/android-arm@0.25.12': optional: true - '@esbuild/android-x64@0.25.10': + '@esbuild/android-x64@0.25.12': optional: true - '@esbuild/darwin-arm64@0.25.10': + '@esbuild/darwin-arm64@0.25.12': optional: true - '@esbuild/darwin-x64@0.25.10': + '@esbuild/darwin-x64@0.25.12': optional: true - '@esbuild/freebsd-arm64@0.25.10': + '@esbuild/freebsd-arm64@0.25.12': optional: true - '@esbuild/freebsd-x64@0.25.10': + '@esbuild/freebsd-x64@0.25.12': optional: true - '@esbuild/linux-arm64@0.25.10': + '@esbuild/linux-arm64@0.25.12': optional: true - '@esbuild/linux-arm@0.25.10': + '@esbuild/linux-arm@0.25.12': optional: true - '@esbuild/linux-ia32@0.25.10': + '@esbuild/linux-ia32@0.25.12': optional: true - '@esbuild/linux-loong64@0.25.10': + '@esbuild/linux-loong64@0.25.12': optional: true - '@esbuild/linux-mips64el@0.25.10': + '@esbuild/linux-mips64el@0.25.12': optional: true - '@esbuild/linux-ppc64@0.25.10': + '@esbuild/linux-ppc64@0.25.12': optional: true - '@esbuild/linux-riscv64@0.25.10': + '@esbuild/linux-riscv64@0.25.12': optional: true - '@esbuild/linux-s390x@0.25.10': + '@esbuild/linux-s390x@0.25.12': optional: true - '@esbuild/linux-x64@0.25.10': + '@esbuild/linux-x64@0.25.12': optional: true - '@esbuild/netbsd-arm64@0.25.10': + '@esbuild/netbsd-arm64@0.25.12': optional: true - '@esbuild/netbsd-x64@0.25.10': + '@esbuild/netbsd-x64@0.25.12': optional: true - '@esbuild/openbsd-arm64@0.25.10': + '@esbuild/openbsd-arm64@0.25.12': optional: true - '@esbuild/openbsd-x64@0.25.10': + '@esbuild/openbsd-x64@0.25.12': optional: true - '@esbuild/openharmony-arm64@0.25.10': + '@esbuild/openharmony-arm64@0.25.12': optional: true - '@esbuild/sunos-x64@0.25.10': + '@esbuild/sunos-x64@0.25.12': optional: true - '@esbuild/win32-arm64@0.25.10': + '@esbuild/win32-arm64@0.25.12': optional: true - '@esbuild/win32-ia32@0.25.10': + '@esbuild/win32-ia32@0.25.12': optional: true - '@esbuild/win32-x64@0.25.10': + '@esbuild/win32-x64@0.25.12': optional: true + '@eslint-community/eslint-utils@4.9.0(eslint@9.37.0)': + dependencies: + eslint: 9.37.0 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.2': {} + + '@eslint/config-array@0.21.1': + dependencies: + '@eslint/object-schema': 2.1.7 + debug: 4.4.3 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.4.2': + dependencies: + '@eslint/core': 0.17.0 + + '@eslint/core@0.16.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/core@0.17.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/eslintrc@3.3.1': + dependencies: + ajv: 6.12.6 + debug: 4.4.3 + espree: 10.4.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.37.0': {} + + '@eslint/object-schema@2.1.7': {} + + '@eslint/plugin-kit@0.4.1': + dependencies: + '@eslint/core': 0.17.0 + levn: 0.4.1 + '@floating-ui/core@1.7.3': dependencies: '@floating-ui/utils': 0.2.10 @@ -3397,46 +3327,146 @@ snapshots: '@floating-ui/core': 1.7.3 '@floating-ui/utils': 0.2.10 - '@floating-ui/react-dom@2.1.6(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + '@floating-ui/react-dom@2.1.6(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: '@floating-ui/dom': 1.7.4 - react: 19.1.1 - react-dom: 19.1.1(react@19.1.1) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) - '@floating-ui/react@0.27.16(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + '@floating-ui/react@0.27.16(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: - '@floating-ui/react-dom': 2.1.6(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@floating-ui/react-dom': 2.1.6(react-dom@19.2.0(react@19.2.0))(react@19.2.0) '@floating-ui/utils': 0.2.10 - react: 19.1.1 - react-dom: 19.1.1(react@19.1.1) - tabbable: 6.2.0 + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + tabbable: 6.3.0 + + '@floating-ui/utils@0.2.10': {} + + '@humanfs/core@0.19.1': {} + + '@humanfs/node@0.16.7': + dependencies: + '@humanfs/core': 0.19.1 + '@humanwhocodes/retry': 0.4.3 + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.4.3': {} + + '@img/colour@1.0.0': {} + + '@img/sharp-darwin-arm64@0.34.4': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.2.3 + optional: true + + '@img/sharp-darwin-x64@0.34.4': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.2.3 + optional: true + + '@img/sharp-libvips-darwin-arm64@1.2.3': + optional: true + + '@img/sharp-libvips-darwin-x64@1.2.3': + optional: true + + '@img/sharp-libvips-linux-arm64@1.2.3': + optional: true + + '@img/sharp-libvips-linux-arm@1.2.3': + optional: true + + '@img/sharp-libvips-linux-ppc64@1.2.3': + optional: true + + '@img/sharp-libvips-linux-s390x@1.2.3': + optional: true + + '@img/sharp-libvips-linux-x64@1.2.3': + optional: true + + '@img/sharp-libvips-linuxmusl-arm64@1.2.3': + optional: true + + '@img/sharp-libvips-linuxmusl-x64@1.2.3': + optional: true + + '@img/sharp-linux-arm64@0.34.4': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.2.3 + optional: true + + '@img/sharp-linux-arm@0.34.4': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.2.3 + optional: true + + '@img/sharp-linux-ppc64@0.34.4': + optionalDependencies: + '@img/sharp-libvips-linux-ppc64': 1.2.3 + optional: true + + '@img/sharp-linux-s390x@0.34.4': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.2.3 + optional: true + + '@img/sharp-linux-x64@0.34.4': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.2.3 + optional: true + + '@img/sharp-linuxmusl-arm64@0.34.4': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.2.3 + optional: true + + '@img/sharp-linuxmusl-x64@0.34.4': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.2.3 + optional: true + + '@img/sharp-wasm32@0.34.4': + dependencies: + '@emnapi/runtime': 1.7.0 + optional: true + + '@img/sharp-win32-arm64@0.34.4': + optional: true - '@floating-ui/utils@0.2.10': {} + '@img/sharp-win32-ia32@0.34.4': + optional: true - '@github/webauthn-json@2.1.1': {} + '@img/sharp-win32-x64@0.34.4': + optional: true - '@hookform/devtools@4.4.0(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + '@inlang/paraglide-js@2.4.0': dependencies: - '@emotion/react': 11.14.0(@types/react@19.1.13)(react@19.1.1) - '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.1.13)(react@19.1.1))(@types/react@19.1.13)(react@19.1.1) - '@types/lodash': 4.17.20 - little-state-machine: 4.8.1(react@19.1.1) - lodash: 4.17.21 - react: 19.1.1 - react-dom: 19.1.1(react@19.1.1) - react-simple-animate: 3.5.3(react-dom@19.1.1(react@19.1.1)) - use-deep-compare-effect: 1.8.1(react@19.1.1) - uuid: 8.3.2 + '@inlang/recommend-sherlock': 0.2.1 + '@inlang/sdk': 2.4.9 + commander: 11.1.0 + consola: 3.4.0 + json5: 2.2.3 + unplugin: 2.3.10 + urlpattern-polyfill: 10.1.0 transitivePeerDependencies: - - '@types/react' - - supports-color + - babel-plugin-macros - '@hookform/resolvers@5.2.2(react-hook-form@7.63.0(react@19.1.1))': + '@inlang/recommend-sherlock@0.2.1': dependencies: - '@standard-schema/utils': 0.3.0 - react-hook-form: 7.63.0(react@19.1.1) + comment-json: 4.4.1 - '@hutson/parse-repository-url@3.0.2': {} + '@inlang/sdk@2.4.9': + dependencies: + '@lix-js/sdk': 0.4.7 + '@sinclair/typebox': 0.31.28 + kysely: 0.27.6 + sqlite-wasm-kysely: 0.3.0(kysely@0.27.6) + uuid: 10.0.0 + transitivePeerDependencies: + - babel-plugin-macros '@jridgewell/gen-mapping@0.3.13': dependencies: @@ -3450,12 +3480,6 @@ snapshots: '@jridgewell/resolve-uri@3.1.2': {} - '@jridgewell/source-map@0.3.11': - dependencies: - '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.31 - optional: true - '@jridgewell/sourcemap-codec@1.5.5': {} '@jridgewell/trace-mapping@0.3.31': @@ -3463,112 +3487,207 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 - '@react-hook/latest@1.0.3(react@19.1.1)': + '@keyv/bigmap@1.1.0(keyv@5.5.3)': dependencies: - react: 19.1.1 + hookified: 1.12.2 + keyv: 5.5.3 - '@react-hook/passive-layout-effect@1.2.1(react@19.1.1)': + '@keyv/serialize@1.1.1': {} + + '@lix-js/sdk@0.4.7': dependencies: - react: 19.1.1 + '@lix-js/server-protocol-schema': 0.1.1 + dedent: 1.5.1 + human-id: 4.1.2 + js-sha256: 0.11.1 + kysely: 0.27.6 + sqlite-wasm-kysely: 0.3.0(kysely@0.27.6) + uuid: 10.0.0 + transitivePeerDependencies: + - babel-plugin-macros - '@react-hook/resize-observer@2.0.2(react@19.1.1)': + '@lix-js/server-protocol-schema@0.1.1': {} + + '@nodelib/fs.scandir@2.1.5': dependencies: - '@react-hook/latest': 1.0.3(react@19.1.1) - '@react-hook/passive-layout-effect': 1.2.1(react@19.1.1) - react: 19.1.1 + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} - '@react-rxjs/core@0.10.8(react@19.1.1)(rxjs@7.8.2)': + '@nodelib/fs.walk@1.2.8': dependencies: - '@rx-state/core': 0.1.4(rxjs@7.8.2) - react: 19.1.1 - rxjs: 7.8.2 - use-sync-external-store: 1.5.0(react@19.1.1) + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.19.1 + + '@parcel/watcher-android-arm64@2.5.1': + optional: true + + '@parcel/watcher-darwin-arm64@2.5.1': + optional: true + + '@parcel/watcher-darwin-x64@2.5.1': + optional: true + + '@parcel/watcher-freebsd-x64@2.5.1': + optional: true + + '@parcel/watcher-linux-arm-glibc@2.5.1': + optional: true + + '@parcel/watcher-linux-arm-musl@2.5.1': + optional: true + + '@parcel/watcher-linux-arm64-glibc@2.5.1': + optional: true - '@reduxjs/toolkit@2.9.0(react-redux@9.2.0(@types/react@19.1.13)(react@19.1.1)(redux@5.0.1))(react@19.1.1)': + '@parcel/watcher-linux-arm64-musl@2.5.1': + optional: true + + '@parcel/watcher-linux-x64-glibc@2.5.1': + optional: true + + '@parcel/watcher-linux-x64-musl@2.5.1': + optional: true + + '@parcel/watcher-win32-arm64@2.5.1': + optional: true + + '@parcel/watcher-win32-ia32@2.5.1': + optional: true + + '@parcel/watcher-win32-x64@2.5.1': + optional: true + + '@parcel/watcher@2.5.1': dependencies: - '@standard-schema/spec': 1.0.0 - '@standard-schema/utils': 0.3.0 - immer: 10.1.3 - redux: 5.0.1 - redux-thunk: 3.1.0(redux@5.0.1) - reselect: 5.1.1 + detect-libc: 1.0.3 + is-glob: 4.0.3 + micromatch: 4.0.8 + node-addon-api: 7.1.1 optionalDependencies: - react: 19.1.1 - react-redux: 9.2.0(@types/react@19.1.13)(react@19.1.1)(redux@5.0.1) - - '@remix-run/router@1.23.0': {} + '@parcel/watcher-android-arm64': 2.5.1 + '@parcel/watcher-darwin-arm64': 2.5.1 + '@parcel/watcher-darwin-x64': 2.5.1 + '@parcel/watcher-freebsd-x64': 2.5.1 + '@parcel/watcher-linux-arm-glibc': 2.5.1 + '@parcel/watcher-linux-arm-musl': 2.5.1 + '@parcel/watcher-linux-arm64-glibc': 2.5.1 + '@parcel/watcher-linux-arm64-musl': 2.5.1 + '@parcel/watcher-linux-x64-glibc': 2.5.1 + '@parcel/watcher-linux-x64-musl': 2.5.1 + '@parcel/watcher-win32-arm64': 2.5.1 + '@parcel/watcher-win32-ia32': 2.5.1 + '@parcel/watcher-win32-x64': 2.5.1 + optional: true - '@rolldown/pluginutils@1.0.0-beta.35': {} + '@rolldown/pluginutils@1.0.0-beta.43': {} - '@rollup/rollup-android-arm-eabi@4.52.2': + '@rollup/rollup-android-arm-eabi@4.52.5': optional: true - '@rollup/rollup-android-arm64@4.52.2': + '@rollup/rollup-android-arm64@4.52.5': optional: true - '@rollup/rollup-darwin-arm64@4.52.2': + '@rollup/rollup-darwin-arm64@4.52.5': optional: true - '@rollup/rollup-darwin-x64@4.52.2': + '@rollup/rollup-darwin-x64@4.52.5': optional: true - '@rollup/rollup-freebsd-arm64@4.52.2': + '@rollup/rollup-freebsd-arm64@4.52.5': optional: true - '@rollup/rollup-freebsd-x64@4.52.2': + '@rollup/rollup-freebsd-x64@4.52.5': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.52.2': + '@rollup/rollup-linux-arm-gnueabihf@4.52.5': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.52.2': + '@rollup/rollup-linux-arm-musleabihf@4.52.5': optional: true - '@rollup/rollup-linux-arm64-gnu@4.52.2': + '@rollup/rollup-linux-arm64-gnu@4.52.5': optional: true - '@rollup/rollup-linux-arm64-musl@4.52.2': + '@rollup/rollup-linux-arm64-musl@4.52.5': optional: true - '@rollup/rollup-linux-loong64-gnu@4.52.2': + '@rollup/rollup-linux-loong64-gnu@4.52.5': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.52.2': + '@rollup/rollup-linux-ppc64-gnu@4.52.5': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.52.2': + '@rollup/rollup-linux-riscv64-gnu@4.52.5': optional: true - '@rollup/rollup-linux-riscv64-musl@4.52.2': + '@rollup/rollup-linux-riscv64-musl@4.52.5': optional: true - '@rollup/rollup-linux-s390x-gnu@4.52.2': + '@rollup/rollup-linux-s390x-gnu@4.52.5': optional: true - '@rollup/rollup-linux-x64-gnu@4.52.2': + '@rollup/rollup-linux-x64-gnu@4.52.5': optional: true - '@rollup/rollup-linux-x64-musl@4.52.2': + '@rollup/rollup-linux-x64-musl@4.52.5': optional: true - '@rollup/rollup-openharmony-arm64@4.52.2': + '@rollup/rollup-openharmony-arm64@4.52.5': optional: true - '@rollup/rollup-win32-arm64-msvc@4.52.2': + '@rollup/rollup-win32-arm64-msvc@4.52.5': optional: true - '@rollup/rollup-win32-ia32-msvc@4.52.2': + '@rollup/rollup-win32-ia32-msvc@4.52.5': optional: true - '@rollup/rollup-win32-x64-gnu@4.52.2': + '@rollup/rollup-win32-x64-gnu@4.52.5': optional: true - '@rollup/rollup-win32-x64-msvc@4.52.2': + '@rollup/rollup-win32-x64-msvc@4.52.5': optional: true - '@rx-state/core@0.1.4(rxjs@7.8.2)': + '@shortercode/webzip@1.1.1-0': {} + + '@sinclair/typebox@0.31.28': {} + + '@solid-primitives/event-listener@2.4.3(solid-js@1.9.9)': + dependencies: + '@solid-primitives/utils': 6.3.2(solid-js@1.9.9) + solid-js: 1.9.9 + + '@solid-primitives/keyboard@1.3.3(solid-js@1.9.9)': + dependencies: + '@solid-primitives/event-listener': 2.4.3(solid-js@1.9.9) + '@solid-primitives/rootless': 1.5.2(solid-js@1.9.9) + '@solid-primitives/utils': 6.3.2(solid-js@1.9.9) + solid-js: 1.9.9 + + '@solid-primitives/resize-observer@2.1.3(solid-js@1.9.9)': + dependencies: + '@solid-primitives/event-listener': 2.4.3(solid-js@1.9.9) + '@solid-primitives/rootless': 1.5.2(solid-js@1.9.9) + '@solid-primitives/static-store': 0.1.2(solid-js@1.9.9) + '@solid-primitives/utils': 6.3.2(solid-js@1.9.9) + solid-js: 1.9.9 + + '@solid-primitives/rootless@1.5.2(solid-js@1.9.9)': + dependencies: + '@solid-primitives/utils': 6.3.2(solid-js@1.9.9) + solid-js: 1.9.9 + + '@solid-primitives/static-store@0.1.2(solid-js@1.9.9)': + dependencies: + '@solid-primitives/utils': 6.3.2(solid-js@1.9.9) + solid-js: 1.9.9 + + '@solid-primitives/utils@6.3.2(solid-js@1.9.9)': dependencies: - rxjs: 7.8.2 + solid-js: 1.9.9 + + '@sqlite.org/sqlite-wasm@3.48.0-build4': {} '@stablelib/base64@2.0.1': {} @@ -3597,55 +3716,51 @@ snapshots: '@stablelib/random': 2.0.1 '@stablelib/wipe': 2.0.1 - '@standard-schema/spec@1.0.0': {} - - '@standard-schema/utils@0.3.0': {} - - '@swc/core-darwin-arm64@1.13.19': + '@swc/core-darwin-arm64@1.14.0': optional: true - '@swc/core-darwin-x64@1.13.19': + '@swc/core-darwin-x64@1.14.0': optional: true - '@swc/core-linux-arm-gnueabihf@1.13.19': + '@swc/core-linux-arm-gnueabihf@1.14.0': optional: true - '@swc/core-linux-arm64-gnu@1.13.19': + '@swc/core-linux-arm64-gnu@1.14.0': optional: true - '@swc/core-linux-arm64-musl@1.13.19': + '@swc/core-linux-arm64-musl@1.14.0': optional: true - '@swc/core-linux-x64-gnu@1.13.19': + '@swc/core-linux-x64-gnu@1.14.0': optional: true - '@swc/core-linux-x64-musl@1.13.19': + '@swc/core-linux-x64-musl@1.14.0': optional: true - '@swc/core-win32-arm64-msvc@1.13.19': + '@swc/core-win32-arm64-msvc@1.14.0': optional: true - '@swc/core-win32-ia32-msvc@1.13.19': + '@swc/core-win32-ia32-msvc@1.14.0': optional: true - '@swc/core-win32-x64-msvc@1.13.19': + '@swc/core-win32-x64-msvc@1.14.0': optional: true - '@swc/core@1.13.19': + '@swc/core@1.14.0': dependencies: '@swc/counter': 0.1.3 '@swc/types': 0.1.25 optionalDependencies: - '@swc/core-darwin-arm64': 1.13.19 - '@swc/core-darwin-x64': 1.13.19 - '@swc/core-linux-arm-gnueabihf': 1.13.19 - '@swc/core-linux-arm64-gnu': 1.13.19 - '@swc/core-linux-arm64-musl': 1.13.19 - '@swc/core-linux-x64-gnu': 1.13.19 - '@swc/core-linux-x64-musl': 1.13.19 - '@swc/core-win32-arm64-msvc': 1.13.19 - '@swc/core-win32-ia32-msvc': 1.13.19 - '@swc/core-win32-x64-msvc': 1.13.19 + '@swc/core-darwin-arm64': 1.14.0 + '@swc/core-darwin-x64': 1.14.0 + '@swc/core-linux-arm-gnueabihf': 1.14.0 + '@swc/core-linux-arm64-gnu': 1.14.0 + '@swc/core-linux-arm64-musl': 1.14.0 + '@swc/core-linux-x64-gnu': 1.14.0 + '@swc/core-linux-x64-musl': 1.14.0 + '@swc/core-win32-arm64-msvc': 1.14.0 + '@swc/core-win32-ia32-msvc': 1.14.0 + '@swc/core-win32-x64-msvc': 1.14.0 '@swc/counter@0.1.3': {} @@ -3653,54 +3768,263 @@ snapshots: dependencies: '@swc/counter': 0.1.3 - '@tanstack/query-core@5.90.2': {} + '@tanstack/devtools-client@0.0.4': + dependencies: + '@tanstack/devtools-event-client': 0.3.4 + + '@tanstack/devtools-event-bus@0.3.3': + dependencies: + ws: 8.18.3 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + '@tanstack/devtools-event-client@0.3.4': {} + + '@tanstack/devtools-ui@0.4.4(csstype@3.1.3)(solid-js@1.9.9)': + dependencies: + clsx: 2.1.1 + goober: 2.1.18(csstype@3.1.3) + solid-js: 1.9.9 + transitivePeerDependencies: + - csstype + + '@tanstack/devtools-vite@0.3.11(vite@7.1.12(@types/node@24.10.0)(sass@1.93.3)(tsx@4.20.6))': + dependencies: + '@babel/core': 7.28.5 + '@babel/generator': 7.28.5 + '@babel/parser': 7.28.5 + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 + '@tanstack/devtools-client': 0.0.4 + '@tanstack/devtools-event-bus': 0.3.3 + chalk: 5.6.2 + launch-editor: 2.12.0 + picomatch: 4.0.3 + vite: 7.1.12(@types/node@24.10.0)(sass@1.93.3)(tsx@4.20.6) + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + '@tanstack/devtools@0.8.0(csstype@3.1.3)(solid-js@1.9.9)': + dependencies: + '@solid-primitives/event-listener': 2.4.3(solid-js@1.9.9) + '@solid-primitives/keyboard': 1.3.3(solid-js@1.9.9) + '@solid-primitives/resize-observer': 2.1.3(solid-js@1.9.9) + '@tanstack/devtools-client': 0.0.4 + '@tanstack/devtools-event-bus': 0.3.3 + '@tanstack/devtools-ui': 0.4.4(csstype@3.1.3)(solid-js@1.9.9) + clsx: 2.1.1 + goober: 2.1.18(csstype@3.1.3) + solid-js: 1.9.9 + transitivePeerDependencies: + - bufferutil + - csstype + - utf-8-validate + + '@tanstack/form-core@1.24.4': + dependencies: + '@tanstack/devtools-event-client': 0.3.4 + '@tanstack/pacer': 0.15.4 + '@tanstack/store': 0.7.7 + + '@tanstack/history@1.133.28': {} + + '@tanstack/pacer@0.15.4': + dependencies: + '@tanstack/devtools-event-client': 0.3.4 + '@tanstack/store': 0.7.7 + + '@tanstack/query-core@5.90.6': {} '@tanstack/query-devtools@5.90.1': {} - '@tanstack/react-query-devtools@5.90.2(@tanstack/react-query@5.90.2(react@19.1.1))(react@19.1.1)': + '@tanstack/react-devtools@0.8.0(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(csstype@3.1.3)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(solid-js@1.9.9)': dependencies: - '@tanstack/query-devtools': 5.90.1 - '@tanstack/react-query': 5.90.2(react@19.1.1) - react: 19.1.1 + '@tanstack/devtools': 0.8.0(csstype@3.1.3)(solid-js@1.9.9) + '@types/react': 19.2.2 + '@types/react-dom': 19.2.2(@types/react@19.2.2) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + transitivePeerDependencies: + - bufferutil + - csstype + - solid-js + - utf-8-validate + + '@tanstack/react-form@1.23.8(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@tanstack/form-core': 1.24.4 + '@tanstack/react-store': 0.7.7(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + decode-formdata: 0.9.0 + devalue: 5.4.2 + react: 19.2.0 + transitivePeerDependencies: + - react-dom - '@tanstack/react-query@5.90.2(react@19.1.1)': + '@tanstack/react-query-devtools@5.90.2(@tanstack/react-query@5.90.6(react@19.2.0))(react@19.2.0)': dependencies: - '@tanstack/query-core': 5.90.2 - react: 19.1.1 + '@tanstack/query-devtools': 5.90.1 + '@tanstack/react-query': 5.90.6(react@19.2.0) + react: 19.2.0 - '@tanstack/react-virtual@3.13.12(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + '@tanstack/react-query@5.90.6(react@19.2.0)': dependencies: - '@tanstack/virtual-core': 3.13.12 - react: 19.1.1 - react-dom: 19.1.1(react@19.1.1) + '@tanstack/query-core': 5.90.6 + react: 19.2.0 - '@tanstack/virtual-core@3.13.12': {} + '@tanstack/react-router-devtools@1.134.9(@tanstack/react-router@1.134.9(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(@tanstack/router-core@1.134.9)(@types/node@24.10.0)(csstype@3.1.3)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(sass@1.93.3)(solid-js@1.9.9)(tiny-invariant@1.3.3)(tsx@4.20.6)': + dependencies: + '@tanstack/react-router': 1.134.9(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@tanstack/router-devtools-core': 1.134.9(@tanstack/router-core@1.134.9)(@types/node@24.10.0)(csstype@3.1.3)(sass@1.93.3)(solid-js@1.9.9)(tiny-invariant@1.3.3)(tsx@4.20.6) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + vite: 7.1.12(@types/node@24.10.0)(sass@1.93.3)(tsx@4.20.6) + transitivePeerDependencies: + - '@tanstack/router-core' + - '@types/node' + - csstype + - jiti + - less + - lightningcss + - sass + - sass-embedded + - solid-js + - stylus + - sugarss + - terser + - tiny-invariant + - tsx + - yaml + + '@tanstack/react-router@1.134.9(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@tanstack/history': 1.133.28 + '@tanstack/react-store': 0.8.0(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@tanstack/router-core': 1.134.9 + isbot: 5.1.31 + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + tiny-invariant: 1.3.3 + tiny-warning: 1.0.3 - '@types/byte-size@8.1.2': {} + '@tanstack/react-store@0.7.7(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@tanstack/store': 0.7.7 + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + use-sync-external-store: 1.6.0(react@19.2.0) - '@types/d3-array@3.2.2': {} + '@tanstack/react-store@0.8.0(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@tanstack/store': 0.8.0 + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + use-sync-external-store: 1.6.0(react@19.2.0) - '@types/d3-color@3.1.3': {} + '@tanstack/react-table@8.21.3(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@tanstack/table-core': 8.21.3 + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) - '@types/d3-ease@3.0.2': {} + '@tanstack/react-virtual@3.13.12(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + '@tanstack/virtual-core': 3.13.12 + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) - '@types/d3-interpolate@3.0.4': + '@tanstack/router-core@1.134.9': dependencies: - '@types/d3-color': 3.1.3 + '@tanstack/history': 1.133.28 + '@tanstack/store': 0.8.0 + cookie-es: 2.0.0 + seroval: 1.3.2 + seroval-plugins: 1.3.3(seroval@1.3.2) + tiny-invariant: 1.3.3 + tiny-warning: 1.0.3 - '@types/d3-path@3.1.1': {} + '@tanstack/router-devtools-core@1.134.9(@tanstack/router-core@1.134.9)(@types/node@24.10.0)(csstype@3.1.3)(sass@1.93.3)(solid-js@1.9.9)(tiny-invariant@1.3.3)(tsx@4.20.6)': + dependencies: + '@tanstack/router-core': 1.134.9 + clsx: 2.1.1 + goober: 2.1.18(csstype@3.1.3) + solid-js: 1.9.9 + tiny-invariant: 1.3.3 + vite: 7.1.12(@types/node@24.10.0)(sass@1.93.3)(tsx@4.20.6) + optionalDependencies: + csstype: 3.1.3 + transitivePeerDependencies: + - '@types/node' + - jiti + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - terser + - tsx + - yaml + + '@tanstack/router-generator@1.134.9': + dependencies: + '@tanstack/router-core': 1.134.9 + '@tanstack/router-utils': 1.133.19 + '@tanstack/virtual-file-routes': 1.133.19 + prettier: 3.6.2 + recast: 0.23.11 + source-map: 0.7.6 + tsx: 4.20.6 + zod: 3.25.76 + transitivePeerDependencies: + - supports-color - '@types/d3-scale@4.0.9': + '@tanstack/router-plugin@1.134.9(@tanstack/react-router@1.134.9(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(vite@7.1.12(@types/node@24.10.0)(sass@1.93.3)(tsx@4.20.6))': dependencies: - '@types/d3-time': 3.0.4 + '@babel/core': 7.28.5 + '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.5) + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 + '@tanstack/router-core': 1.134.9 + '@tanstack/router-generator': 1.134.9 + '@tanstack/router-utils': 1.133.19 + '@tanstack/virtual-file-routes': 1.133.19 + babel-dead-code-elimination: 1.0.10 + chokidar: 3.6.0 + unplugin: 2.3.10 + zod: 3.25.76 + optionalDependencies: + '@tanstack/react-router': 1.134.9(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + vite: 7.1.12(@types/node@24.10.0)(sass@1.93.3)(tsx@4.20.6) + transitivePeerDependencies: + - supports-color - '@types/d3-shape@3.1.7': + '@tanstack/router-utils@1.133.19': dependencies: - '@types/d3-path': 3.1.1 + '@babel/core': 7.28.5 + '@babel/generator': 7.28.5 + '@babel/parser': 7.28.5 + '@babel/preset-typescript': 7.28.5(@babel/core@7.28.5) + ansis: 4.2.0 + diff: 8.0.2 + pathe: 2.0.3 + tinyglobby: 0.2.15 + transitivePeerDependencies: + - supports-color + + '@tanstack/store@0.7.7': {} - '@types/d3-time@3.0.4': {} + '@tanstack/store@0.8.0': {} - '@types/d3-timer@3.0.2': {} + '@tanstack/table-core@8.21.3': {} + + '@tanstack/virtual-core@3.13.12': {} + + '@tanstack/virtual-file-routes@1.133.19': {} '@types/debug@4.1.12': dependencies: @@ -3712,15 +4036,11 @@ snapshots: '@types/estree@1.0.8': {} - '@types/file-saver@2.0.7': {} - '@types/hast@3.0.4': dependencies: '@types/unist': 3.0.3 - '@types/history@4.7.11': {} - - '@types/humanize-duration@3.27.4': {} + '@types/json-schema@7.0.15': {} '@types/lodash-es@4.17.12': dependencies: @@ -3732,104 +4052,194 @@ snapshots: dependencies: '@types/unist': 3.0.3 - '@types/minimist@1.2.5': {} - '@types/ms@2.1.0': {} - '@types/node@24.5.2': + '@types/node@24.10.0': dependencies: - undici-types: 7.12.0 + undici-types: 7.16.0 + + '@types/qs@6.14.0': {} - '@types/normalize-package-data@2.4.4': {} + '@types/react-dom@19.2.2(@types/react@19.2.2)': + dependencies: + '@types/react': 19.2.2 - '@types/parse-json@4.0.2': {} + '@types/react@19.2.2': + dependencies: + csstype: 3.1.3 - '@types/qs@6.14.0': {} + '@types/unist@2.0.11': {} + + '@types/unist@3.0.3': {} + + '@typescript-eslint/eslint-plugin@8.46.2(@typescript-eslint/parser@8.46.2(eslint@9.37.0)(typescript@5.9.3))(eslint@9.37.0)(typescript@5.9.3)': + dependencies: + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.46.2(eslint@9.37.0)(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.46.2 + '@typescript-eslint/type-utils': 8.46.2(eslint@9.37.0)(typescript@5.9.3) + '@typescript-eslint/utils': 8.46.2(eslint@9.37.0)(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.46.2 + eslint: 9.37.0 + graphemer: 1.4.0 + ignore: 7.0.5 + natural-compare: 1.4.0 + ts-api-utils: 2.1.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color - '@types/react-dom@19.1.9(@types/react@19.1.13)': + '@typescript-eslint/parser@8.46.2(eslint@9.37.0)(typescript@5.9.3)': dependencies: - '@types/react': 19.1.13 + '@typescript-eslint/scope-manager': 8.46.2 + '@typescript-eslint/types': 8.46.2 + '@typescript-eslint/typescript-estree': 8.46.2(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.46.2 + debug: 4.4.3 + eslint: 9.37.0 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color - '@types/react-router-dom@5.3.3': + '@typescript-eslint/project-service@8.46.2(typescript@5.9.3)': dependencies: - '@types/history': 4.7.11 - '@types/react': 19.1.13 - '@types/react-router': 5.1.20 + '@typescript-eslint/tsconfig-utils': 8.46.2(typescript@5.9.3) + '@typescript-eslint/types': 8.46.2 + debug: 4.4.3 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color - '@types/react-router@5.1.20': + '@typescript-eslint/scope-manager@8.46.2': dependencies: - '@types/history': 4.7.11 - '@types/react': 19.1.13 + '@typescript-eslint/types': 8.46.2 + '@typescript-eslint/visitor-keys': 8.46.2 - '@types/react@19.1.13': + '@typescript-eslint/tsconfig-utils@8.46.2(typescript@5.9.3)': dependencies: - csstype: 3.1.3 + typescript: 5.9.3 - '@types/unist@2.0.11': {} + '@typescript-eslint/type-utils@8.46.2(eslint@9.37.0)(typescript@5.9.3)': + dependencies: + '@typescript-eslint/types': 8.46.2 + '@typescript-eslint/typescript-estree': 8.46.2(typescript@5.9.3) + '@typescript-eslint/utils': 8.46.2(eslint@9.37.0)(typescript@5.9.3) + debug: 4.4.3 + eslint: 9.37.0 + ts-api-utils: 2.1.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color - '@types/unist@3.0.3': {} + '@typescript-eslint/types@8.46.2': {} - '@types/use-sync-external-store@0.0.6': {} + '@typescript-eslint/typescript-estree@8.46.2(typescript@5.9.3)': + dependencies: + '@typescript-eslint/project-service': 8.46.2(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.46.2(typescript@5.9.3) + '@typescript-eslint/types': 8.46.2 + '@typescript-eslint/visitor-keys': 8.46.2 + debug: 4.4.3 + fast-glob: 3.3.3 + is-glob: 4.0.3 + minimatch: 9.0.5 + semver: 7.7.3 + ts-api-utils: 2.1.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color - '@ungap/structured-clone@1.3.0': {} + '@typescript-eslint/utils@8.46.2(eslint@9.37.0)(typescript@5.9.3)': + dependencies: + '@eslint-community/eslint-utils': 4.9.0(eslint@9.37.0) + '@typescript-eslint/scope-manager': 8.46.2 + '@typescript-eslint/types': 8.46.2 + '@typescript-eslint/typescript-estree': 8.46.2(typescript@5.9.3) + eslint: 9.37.0 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color - '@use-gesture/core@10.3.1': {} + '@typescript-eslint/visitor-keys@8.46.2': + dependencies: + '@typescript-eslint/types': 8.46.2 + eslint-visitor-keys: 4.2.1 - '@use-gesture/react@10.3.1(react@19.1.1)': + '@uidotdev/usehooks@2.4.1(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: - '@use-gesture/core': 10.3.1 - react: 19.1.1 + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) + + '@ungap/structured-clone@1.3.0': {} - '@vitejs/plugin-react-swc@4.1.0(vite@7.1.7(@types/node@24.5.2)(jiti@2.4.2)(sass@1.70.0)(terser@5.37.0)(yaml@2.6.1))': + '@vitejs/plugin-react-swc@4.2.0(vite@7.1.12(@types/node@24.10.0)(sass@1.93.3)(tsx@4.20.6))': dependencies: - '@rolldown/pluginutils': 1.0.0-beta.35 - '@swc/core': 1.13.19 - vite: 7.1.7(@types/node@24.5.2)(jiti@2.4.2)(sass@1.70.0)(terser@5.37.0)(yaml@2.6.1) + '@rolldown/pluginutils': 1.0.0-beta.43 + '@swc/core': 1.14.0 + vite: 7.1.12(@types/node@24.10.0)(sass@1.93.3)(tsx@4.20.6) transitivePeerDependencies: - '@swc/helpers' - JSONStream@1.3.5: + acorn-jsx@5.3.2(acorn@8.15.0): dependencies: - jsonparse: 1.3.1 - through: 2.3.8 - - acorn@8.15.0: - optional: true + acorn: 8.15.0 - add-stream@1.0.0: {} + acorn@8.15.0: {} - ansi-regex@5.0.1: {} + ajv@6.12.6: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 - ansi-styles@3.2.1: + ajv@8.17.1: dependencies: - color-convert: 1.9.3 + fast-deep-equal: 3.1.3 + fast-uri: 3.1.0 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + + ansi-colors@4.1.3: {} + + ansi-regex@5.0.1: {} ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 + ansis@4.2.0: {} + anymatch@3.1.3: dependencies: normalize-path: 3.0.0 picomatch: 2.3.1 - array-ify@1.0.0: {} + argparse@2.0.1: {} + + array-timsort@1.0.3: {} + + array-union@2.1.0: {} + + ast-types@0.16.1: + dependencies: + tslib: 2.8.1 - arrify@1.0.1: {} + astral-regex@2.0.0: {} asynckit@0.4.0: {} autoprefixer@10.4.21(postcss@8.5.6): dependencies: - browserslist: 4.26.2 - caniuse-lite: 1.0.30001745 + browserslist: 4.27.0 + caniuse-lite: 1.0.30001753 fraction.js: 4.3.7 normalize-range: 0.1.2 picocolors: 1.1.1 postcss: 8.5.6 postcss-value-parser: 4.2.0 - axios@1.12.2: + axios@1.13.1: dependencies: follow-redirects: 1.15.11 form-data: 4.0.4 @@ -3837,19 +4247,22 @@ snapshots: transitivePeerDependencies: - debug - babel-plugin-macros@3.1.0: + babel-dead-code-elimination@1.0.10: dependencies: - '@babel/runtime': 7.28.4 - cosmiconfig: 7.1.0 - resolve: 1.22.10 + '@babel/core': 7.28.5 + '@babel/parser': 7.28.5 + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 + transitivePeerDependencies: + - supports-color bail@2.0.2: {} balanced-match@1.0.2: {} - baseline-browser-mapping@2.8.7: {} + balanced-match@2.0.0: {} - bignumber.js@9.3.1: {} + baseline-browser-mapping@2.8.23: {} binary-extensions@2.3.0: {} @@ -3858,21 +4271,30 @@ snapshots: balanced-match: 1.0.2 concat-map: 0.0.1 + brace-expansion@2.0.2: + dependencies: + balanced-match: 1.0.2 + braces@3.0.3: dependencies: fill-range: 7.1.1 - browserslist@4.26.2: + browserslist@4.27.0: dependencies: - baseline-browser-mapping: 2.8.7 - caniuse-lite: 1.0.30001745 - electron-to-chromium: 1.5.224 - node-releases: 2.0.21 - update-browserslist-db: 1.1.3(browserslist@4.26.2) - - buffer-from@1.1.2: {} + baseline-browser-mapping: 2.8.23 + caniuse-lite: 1.0.30001753 + electron-to-chromium: 1.5.244 + node-releases: 2.0.27 + update-browserslist-db: 1.1.4(browserslist@4.27.0) - byte-size@9.0.1: {} + cacheable@2.1.1: + dependencies: + '@cacheable/memoize': 2.0.3 + '@cacheable/memory': 2.0.4 + '@cacheable/utils': 2.2.0 + hookified: 1.12.2 + keyv: 5.5.3 + qified: 0.5.1 call-bind-apply-helpers@1.0.2: dependencies: @@ -3886,356 +4308,139 @@ snapshots: callsites@3.1.0: {} - camelcase-keys@6.2.2: - dependencies: - camelcase: 5.3.1 - map-obj: 4.3.0 - quick-lru: 4.0.1 - - camelcase@5.3.1: {} - - caniuse-lite@1.0.30001745: {} + caniuse-lite@1.0.30001753: {} ccount@2.0.1: {} - chalk@2.4.2: - dependencies: - ansi-styles: 3.2.1 - escape-string-regexp: 1.0.5 - supports-color: 5.5.0 - chalk@4.1.2: dependencies: - ansi-styles: 4.3.0 - supports-color: 7.2.0 - - character-entities-html4@2.1.0: {} - - character-entities-legacy@3.0.0: {} - - character-entities@2.0.2: {} - - character-reference-invalid@2.0.1: {} - - chokidar@3.6.0: - dependencies: - anymatch: 3.1.3 - braces: 3.0.3 - glob-parent: 5.1.2 - is-binary-path: 2.1.0 - is-glob: 4.0.3 - normalize-path: 3.0.0 - readdirp: 3.6.0 - optionalDependencies: - fsevents: 2.3.3 - - classnames@2.5.1: {} - - cliui@6.0.0: - dependencies: - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 6.2.0 - - cliui@7.0.4: - dependencies: - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 7.0.0 - - cliui@8.0.1: - dependencies: - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 7.0.0 - - clsx@2.1.1: {} - - color-convert@1.9.3: - dependencies: - color-name: 1.1.3 - - color-convert@2.0.1: - dependencies: - color-name: 1.1.4 - - color-name@1.1.3: {} - - color-name@1.1.4: {} - - combined-stream@1.0.8: - dependencies: - delayed-stream: 1.0.0 - - comma-separated-tokens@2.0.3: {} - - commander@2.20.3: - optional: true - - compare-func@2.0.0: - dependencies: - array-ify: 1.0.0 - dot-prop: 5.3.0 - - concat-map@0.0.1: {} - - concat-stream@2.0.0: - dependencies: - buffer-from: 1.1.2 - inherits: 2.0.4 - readable-stream: 3.6.2 - typedarray: 0.0.6 - - concurrently@9.2.1: - dependencies: - chalk: 4.1.2 - rxjs: 7.8.2 - shell-quote: 1.8.3 - supports-color: 8.1.1 - tree-kill: 1.2.2 - yargs: 17.7.2 - - conventional-changelog-angular@5.0.13: - dependencies: - compare-func: 2.0.0 - q: 1.5.1 - - conventional-changelog-atom@2.0.8: - dependencies: - q: 1.5.1 - - conventional-changelog-codemirror@2.0.8: - dependencies: - q: 1.5.1 + ansi-styles: 4.3.0 + supports-color: 7.2.0 - conventional-changelog-config-spec@2.1.0: {} + chalk@5.6.2: {} - conventional-changelog-conventionalcommits@4.6.3: - dependencies: - compare-func: 2.0.0 - lodash: 4.17.21 - q: 1.5.1 + change-case@5.4.4: {} - conventional-changelog-core@4.2.4: - dependencies: - add-stream: 1.0.0 - conventional-changelog-writer: 5.0.1 - conventional-commits-parser: 3.2.4 - dateformat: 3.0.3 - get-pkg-repo: 4.2.1 - git-raw-commits: 2.0.11 - git-remote-origin-url: 2.0.0 - git-semver-tags: 4.1.1 - lodash: 4.17.21 - normalize-package-data: 3.0.3 - q: 1.5.1 - read-pkg: 3.0.0 - read-pkg-up: 3.0.0 - through2: 4.0.2 + character-entities-html4@2.1.0: {} - conventional-changelog-ember@2.0.9: - dependencies: - q: 1.5.1 + character-entities-legacy@3.0.0: {} - conventional-changelog-eslint@3.0.9: - dependencies: - q: 1.5.1 + character-entities@2.0.2: {} - conventional-changelog-express@2.0.6: - dependencies: - q: 1.5.1 + character-reference-invalid@2.0.1: {} - conventional-changelog-jquery@3.0.11: + chokidar@3.6.0: dependencies: - q: 1.5.1 + anymatch: 3.1.3 + braces: 3.0.3 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 - conventional-changelog-jshint@2.0.9: + chokidar@4.0.3: dependencies: - compare-func: 2.0.0 - q: 1.5.1 + readdirp: 4.1.2 - conventional-changelog-preset-loader@2.3.4: {} + clsx@2.1.1: {} - conventional-changelog-writer@5.0.1: + color-convert@2.0.1: dependencies: - conventional-commits-filter: 2.0.7 - dateformat: 3.0.3 - handlebars: 4.7.8 - json-stringify-safe: 5.0.1 - lodash: 4.17.21 - meow: 8.1.2 - semver: 6.3.1 - split: 1.0.1 - through2: 4.0.2 - - conventional-changelog@3.1.25: - dependencies: - conventional-changelog-angular: 5.0.13 - conventional-changelog-atom: 2.0.8 - conventional-changelog-codemirror: 2.0.8 - conventional-changelog-conventionalcommits: 4.6.3 - conventional-changelog-core: 4.2.4 - conventional-changelog-ember: 2.0.9 - conventional-changelog-eslint: 3.0.9 - conventional-changelog-express: 2.0.6 - conventional-changelog-jquery: 3.0.11 - conventional-changelog-jshint: 2.0.9 - conventional-changelog-preset-loader: 2.3.4 - - conventional-commits-filter@2.0.7: - dependencies: - lodash.ismatch: 4.4.0 - modify-values: 1.0.1 - - conventional-commits-parser@3.2.4: - dependencies: - JSONStream: 1.3.5 - is-text-path: 1.0.1 - lodash: 4.17.21 - meow: 8.1.2 - split2: 3.2.2 - through2: 4.0.2 - - conventional-recommended-bump@6.1.0: - dependencies: - concat-stream: 2.0.0 - conventional-changelog-preset-loader: 2.3.4 - conventional-commits-filter: 2.0.7 - conventional-commits-parser: 3.2.4 - git-raw-commits: 2.0.11 - git-semver-tags: 4.1.1 - meow: 8.1.2 - q: 1.5.1 - - convert-source-map@1.9.0: {} + color-name: 1.1.4 - convert-source-map@2.0.0: {} + color-name@1.1.4: {} - core-util-is@1.0.3: {} + colord@2.9.3: {} - cosmiconfig@7.1.0: + combined-stream@1.0.8: dependencies: - '@types/parse-json': 4.0.2 - import-fresh: 3.3.1 - parse-json: 5.2.0 - path-type: 4.0.0 - yaml: 1.10.2 + delayed-stream: 1.0.0 - csstype@3.1.3: {} + comma-separated-tokens@2.0.3: {} + + commander@11.1.0: {} - d3-array@3.2.4: + comment-json@4.4.1: dependencies: - internmap: 2.0.3 + array-timsort: 1.0.3 + core-util-is: 1.0.3 + esprima: 4.0.1 - d3-color@3.1.0: {} + concat-map@0.0.1: {} - d3-ease@3.0.1: {} + consola@3.4.0: {} - d3-format@3.1.0: {} + convert-source-map@2.0.0: {} - d3-interpolate@3.0.1: - dependencies: - d3-color: 3.1.0 + cookie-es@2.0.0: {} - d3-path@3.1.0: {} + core-util-is@1.0.3: {} - d3-scale@4.0.2: + cosmiconfig@9.0.0(typescript@5.9.3): dependencies: - d3-array: 3.2.4 - d3-format: 3.1.0 - d3-interpolate: 3.0.1 - d3-time: 3.1.0 - d3-time-format: 4.1.0 + env-paths: 2.2.1 + import-fresh: 3.3.1 + js-yaml: 4.1.0 + parse-json: 5.2.0 + optionalDependencies: + typescript: 5.9.3 - d3-shape@3.2.0: + cross-spawn@7.0.6: dependencies: - d3-path: 3.1.0 + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 - d3-time-format@4.1.0: - dependencies: - d3-time: 3.1.0 + css-functions-list@3.2.3: {} - d3-time@3.1.0: + css-tree@3.1.0: dependencies: - d3-array: 3.2.4 - - d3-timer@3.0.1: {} - - dargs@7.0.0: {} + mdn-data: 2.12.2 + source-map-js: 1.2.1 - date-fns@4.1.0: {} + cssesc@3.0.0: {} - dateformat@3.0.3: {} + csstype@3.1.3: {} - dayjs@1.11.18: {} + dayjs@1.11.19: {} debug@4.4.3: dependencies: ms: 2.1.3 - decamelize-keys@1.1.1: - dependencies: - decamelize: 1.2.0 - map-obj: 1.0.1 - - decamelize@1.2.0: {} - - decimal.js-light@2.5.1: {} + decode-formdata@0.9.0: {} decode-named-character-reference@1.2.0: dependencies: character-entities: 2.0.2 - deepmerge-ts@7.1.5: {} + dedent@1.5.1: {} + + deep-is@0.1.4: {} delayed-stream@1.0.0: {} dequal@2.0.3: {} - detect-browser@5.3.0: {} + detect-libc@1.0.3: + optional: true - detect-indent@6.1.0: {} + detect-libc@2.1.2: {} - detect-newline@3.1.0: {} + devalue@5.4.2: {} devlop@1.1.0: dependencies: dequal: 2.0.3 - dice-coefficient@2.1.1: - dependencies: - n-gram: 2.0.2 - - dijkstrajs@1.0.3: {} - - dom-serializer@2.0.0: - dependencies: - domelementtype: 2.3.0 - domhandler: 5.0.3 - entities: 4.5.0 - - domelementtype@2.3.0: {} - - domhandler@5.0.3: - dependencies: - domelementtype: 2.3.0 - - domutils@3.2.2: - dependencies: - dom-serializer: 2.0.0 - domelementtype: 2.3.0 - domhandler: 5.0.3 - - dot-prop@5.3.0: - dependencies: - is-obj: 2.0.0 - - dotenv@17.2.2: {} + diff@8.0.2: {} - dotgitignore@2.1.0: + dir-glob@3.0.1: dependencies: - find-up: 3.0.0 - minimatch: 3.1.2 + path-type: 4.0.0 dunder-proto@1.0.1: dependencies: @@ -4243,13 +4448,11 @@ snapshots: es-errors: 1.3.0 gopd: 1.2.0 - electron-to-chromium@1.5.224: {} + electron-to-chromium@1.5.244: {} emoji-regex@8.0.0: {} - entities@4.5.0: {} - - entities@6.0.1: {} + env-paths@2.2.1: {} error-ex@1.3.4: dependencies: @@ -4270,86 +4473,167 @@ snapshots: has-tostringtag: 1.0.2 hasown: 2.0.2 - es-toolkit@1.39.10: {} - - esbuild@0.25.10: + esbuild@0.25.12: optionalDependencies: - '@esbuild/aix-ppc64': 0.25.10 - '@esbuild/android-arm': 0.25.10 - '@esbuild/android-arm64': 0.25.10 - '@esbuild/android-x64': 0.25.10 - '@esbuild/darwin-arm64': 0.25.10 - '@esbuild/darwin-x64': 0.25.10 - '@esbuild/freebsd-arm64': 0.25.10 - '@esbuild/freebsd-x64': 0.25.10 - '@esbuild/linux-arm': 0.25.10 - '@esbuild/linux-arm64': 0.25.10 - '@esbuild/linux-ia32': 0.25.10 - '@esbuild/linux-loong64': 0.25.10 - '@esbuild/linux-mips64el': 0.25.10 - '@esbuild/linux-ppc64': 0.25.10 - '@esbuild/linux-riscv64': 0.25.10 - '@esbuild/linux-s390x': 0.25.10 - '@esbuild/linux-x64': 0.25.10 - '@esbuild/netbsd-arm64': 0.25.10 - '@esbuild/netbsd-x64': 0.25.10 - '@esbuild/openbsd-arm64': 0.25.10 - '@esbuild/openbsd-x64': 0.25.10 - '@esbuild/openharmony-arm64': 0.25.10 - '@esbuild/sunos-x64': 0.25.10 - '@esbuild/win32-arm64': 0.25.10 - '@esbuild/win32-ia32': 0.25.10 - '@esbuild/win32-x64': 0.25.10 + '@esbuild/aix-ppc64': 0.25.12 + '@esbuild/android-arm': 0.25.12 + '@esbuild/android-arm64': 0.25.12 + '@esbuild/android-x64': 0.25.12 + '@esbuild/darwin-arm64': 0.25.12 + '@esbuild/darwin-x64': 0.25.12 + '@esbuild/freebsd-arm64': 0.25.12 + '@esbuild/freebsd-x64': 0.25.12 + '@esbuild/linux-arm': 0.25.12 + '@esbuild/linux-arm64': 0.25.12 + '@esbuild/linux-ia32': 0.25.12 + '@esbuild/linux-loong64': 0.25.12 + '@esbuild/linux-mips64el': 0.25.12 + '@esbuild/linux-ppc64': 0.25.12 + '@esbuild/linux-riscv64': 0.25.12 + '@esbuild/linux-s390x': 0.25.12 + '@esbuild/linux-x64': 0.25.12 + '@esbuild/netbsd-arm64': 0.25.12 + '@esbuild/netbsd-x64': 0.25.12 + '@esbuild/openbsd-arm64': 0.25.12 + '@esbuild/openbsd-x64': 0.25.12 + '@esbuild/openharmony-arm64': 0.25.12 + '@esbuild/sunos-x64': 0.25.12 + '@esbuild/win32-arm64': 0.25.12 + '@esbuild/win32-ia32': 0.25.12 + '@esbuild/win32-x64': 0.25.12 escalade@3.2.0: {} - escape-string-regexp@1.0.5: {} - escape-string-regexp@4.0.0: {} - estree-util-is-identifier-name@3.0.0: {} + eslint-scope@8.4.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.2.1: {} + + eslint@9.37.0: + dependencies: + '@eslint-community/eslint-utils': 4.9.0(eslint@9.37.0) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.21.1 + '@eslint/config-helpers': 0.4.2 + '@eslint/core': 0.16.0 + '@eslint/eslintrc': 3.3.1 + '@eslint/js': 9.37.0 + '@eslint/plugin-kit': 0.4.1 + '@humanfs/node': 0.16.7 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3 + escape-string-regexp: 4.0.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.6.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.4 + transitivePeerDependencies: + - supports-color + + espree@10.4.0: + dependencies: + acorn: 8.15.0 + acorn-jsx: 5.3.2(acorn@8.15.0) + eslint-visitor-keys: 4.2.1 + + esprima@4.0.1: {} + + esquery@1.6.0: + dependencies: + estraverse: 5.3.0 - eventemitter3@5.0.1: {} + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + estree-util-is-identifier-name@3.0.0: {} - events@3.3.0: {} + esutils@2.0.3: {} extend@3.0.2: {} fast-deep-equal@3.1.3: {} + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fast-uri@3.1.0: {} + + fastest-levenshtein@1.0.16: {} + + fastq@1.19.1: + dependencies: + reusify: 1.1.0 + fdir@6.5.0(picomatch@4.0.3): optionalDependencies: picomatch: 4.0.3 - figures@3.2.0: + file-entry-cache@10.1.4: dependencies: - escape-string-regexp: 1.0.5 + flat-cache: 6.1.18 - file-saver@2.0.5: {} + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 - find-root@1.1.0: {} - - find-up@2.1.0: + find-up@5.0.0: dependencies: - locate-path: 2.0.0 + locate-path: 6.0.0 + path-exists: 4.0.0 - find-up@3.0.0: + flat-cache@4.0.1: dependencies: - locate-path: 3.0.0 + flatted: 3.3.3 + keyv: 4.5.4 - find-up@4.1.0: + flat-cache@6.1.18: dependencies: - locate-path: 5.0.0 - path-exists: 4.0.0 + cacheable: 2.1.1 + flatted: 3.3.3 + hookified: 1.12.2 - find-up@5.0.0: - dependencies: - locate-path: 6.0.0 - path-exists: 4.0.0 + flatted@3.3.3: {} follow-redirects@1.15.11: {} @@ -4363,27 +4647,22 @@ snapshots: fraction.js@4.3.7: {} - framer-motion@12.23.22(@emotion/is-prop-valid@1.4.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1): + framer-motion@12.23.24(react-dom@19.2.0(react@19.2.0))(react@19.2.0): dependencies: - motion-dom: 12.23.21 + motion-dom: 12.23.23 motion-utils: 12.23.6 tslib: 2.8.1 optionalDependencies: - '@emotion/is-prop-valid': 1.4.0 - react: 19.1.1 - react-dom: 19.1.1(react@19.1.1) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) fsevents@2.3.3: optional: true function-bind@1.1.2: {} - fuse.js@7.1.0: {} - gensync@1.0.0-beta.2: {} - get-caller-file@2.0.5: {} - get-intrinsic@1.3.0: dependencies: call-bind-apply-helpers: 1.0.2 @@ -4397,64 +4676,55 @@ snapshots: hasown: 2.0.2 math-intrinsics: 1.1.0 - get-pkg-repo@4.2.1: - dependencies: - '@hutson/parse-repository-url': 3.0.2 - hosted-git-info: 4.1.0 - through2: 2.0.5 - yargs: 16.2.0 - get-proto@1.0.1: dependencies: dunder-proto: 1.0.1 es-object-atoms: 1.1.1 - get-text-width@1.0.3: {} + get-tsconfig@4.13.0: + dependencies: + resolve-pkg-maps: 1.0.0 - git-raw-commits@2.0.11: + glob-parent@5.1.2: dependencies: - dargs: 7.0.0 - lodash: 4.17.21 - meow: 8.1.2 - split2: 3.2.2 - through2: 4.0.2 + is-glob: 4.0.3 - git-remote-origin-url@2.0.0: + glob-parent@6.0.2: dependencies: - gitconfiglocal: 1.0.0 - pify: 2.3.0 + is-glob: 4.0.3 - git-semver-tags@4.1.1: + global-modules@2.0.0: dependencies: - meow: 8.1.2 - semver: 6.3.1 + global-prefix: 3.0.0 - gitconfiglocal@1.0.0: + global-prefix@3.0.0: dependencies: ini: 1.3.8 + kind-of: 6.0.3 + which: 1.3.1 - glob-parent@5.1.2: - dependencies: - is-glob: 4.0.3 + globals@14.0.0: {} - globals@16.4.0: {} + globals@16.5.0: {} - gopd@1.2.0: {} + globby@11.1.0: + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.3 + ignore: 5.3.2 + merge2: 1.4.1 + slash: 3.0.0 - graceful-fs@4.2.11: {} + globjoin@0.1.4: {} - handlebars@4.7.8: + goober@2.1.18(csstype@3.1.3): dependencies: - minimist: 1.2.8 - neo-async: 2.6.2 - source-map: 0.6.1 - wordwrap: 1.0.0 - optionalDependencies: - uglify-js: 3.19.3 + csstype: 3.1.3 - hard-rejection@2.1.0: {} + gopd@1.2.0: {} - has-flag@3.0.0: {} + graphemer@1.4.0: {} has-flag@4.0.0: {} @@ -4468,47 +4738,6 @@ snapshots: dependencies: function-bind: 1.1.2 - hast-util-from-parse5@8.0.3: - dependencies: - '@types/hast': 3.0.4 - '@types/unist': 3.0.3 - devlop: 1.1.0 - hastscript: 9.0.1 - property-information: 7.1.0 - vfile: 6.0.3 - vfile-location: 5.0.3 - web-namespaces: 2.0.1 - - hast-util-is-element@3.0.0: - dependencies: - '@types/hast': 3.0.4 - - hast-util-parse-selector@4.0.0: - dependencies: - '@types/hast': 3.0.4 - - hast-util-raw@9.1.0: - dependencies: - '@types/hast': 3.0.4 - '@types/unist': 3.0.3 - '@ungap/structured-clone': 1.3.0 - hast-util-from-parse5: 8.0.3 - hast-util-to-parse5: 8.0.0 - html-void-elements: 3.0.0 - mdast-util-to-hast: 13.2.0 - parse5: 7.3.0 - unist-util-position: 5.0.0 - unist-util-visit: 5.0.0 - vfile: 6.0.3 - web-namespaces: 2.0.1 - zwitch: 2.0.4 - - hast-util-sanitize@5.0.2: - dependencies: - '@types/hast': 3.0.4 - '@ungap/structured-clone': 1.3.0 - unist-util-position: 5.0.0 - hast-util-to-jsx-runtime@2.3.6: dependencies: '@types/estree': 1.0.8 @@ -4523,96 +4752,40 @@ snapshots: mdast-util-mdxjs-esm: 2.0.1 property-information: 7.1.0 space-separated-tokens: 2.0.2 - style-to-js: 1.1.17 + style-to-js: 1.1.19 unist-util-position: 5.0.0 vfile-message: 4.0.3 transitivePeerDependencies: - supports-color - hast-util-to-parse5@8.0.0: - dependencies: - '@types/hast': 3.0.4 - comma-separated-tokens: 2.0.3 - devlop: 1.1.0 - property-information: 6.5.0 - space-separated-tokens: 2.0.2 - web-namespaces: 2.0.1 - zwitch: 2.0.4 - hast-util-whitespace@3.0.0: dependencies: '@types/hast': 3.0.4 - hastscript@9.0.1: - dependencies: - '@types/hast': 3.0.4 - comma-separated-tokens: 2.0.3 - hast-util-parse-selector: 4.0.0 - property-information: 7.1.0 - space-separated-tokens: 2.0.2 - - hex-rgb@5.0.0: {} - - hoist-non-react-statics@3.3.2: - dependencies: - react-is: 16.13.1 - - hosted-git-info@2.8.9: {} - - hosted-git-info@4.1.0: - dependencies: - lru-cache: 6.0.0 + hookified@1.12.2: {} - html-dom-parser@5.1.1: - dependencies: - domhandler: 5.0.3 - htmlparser2: 10.0.0 - - html-react-parser@5.2.6(@types/react@19.1.13)(react@19.1.1): - dependencies: - domhandler: 5.0.3 - html-dom-parser: 5.1.1 - react: 19.1.1 - react-property: 2.0.2 - style-to-js: 1.1.17 - optionalDependencies: - '@types/react': 19.1.13 + html-tags@3.3.1: {} html-url-attributes@3.0.1: {} - html-void-elements@3.0.0: {} + human-id@4.1.2: {} - htmlparser2@10.0.0: - dependencies: - domelementtype: 2.3.0 - domhandler: 5.0.3 - domutils: 3.2.2 - entities: 6.0.1 - - humanize-duration@3.33.1: {} + ignore@5.3.2: {} - immer@10.1.3: {} + ignore@7.0.5: {} - immutable@4.3.7: {} + immutable@5.1.4: {} import-fresh@3.3.1: dependencies: parent-module: 1.0.1 resolve-from: 4.0.0 - indent-string@4.0.0: {} - - inherits@2.0.4: {} + imurmurhash@0.1.4: {} ini@1.3.8: {} - inline-style-parser@0.2.4: {} - - internmap@2.0.3: {} - - ipaddr.js@2.2.0: {} - - is-absolute-url@4.0.1: {} + inline-style-parser@0.2.6: {} is-alphabetical@2.0.1: {} @@ -4627,10 +4800,6 @@ snapshots: dependencies: binary-extensions: 2.3.0 - is-core-module@2.16.1: - dependencies: - hasown: 2.0.2 - is-decimal@2.0.1: {} is-extglob@2.1.1: {} @@ -4645,67 +4814,63 @@ snapshots: is-number@7.0.0: {} - is-obj@2.0.0: {} - - is-plain-obj@1.1.0: {} - is-plain-obj@4.1.0: {} - is-text-path@1.0.1: - dependencies: - text-extensions: 1.9.0 - - isarray@1.0.0: {} + is-plain-object@5.0.0: {} - itertools@2.5.0: {} + isbot@5.1.31: {} - jiti@2.4.2: - optional: true + isexe@2.0.0: {} - js-base64@3.7.8: {} + js-sha256@0.11.1: {} js-tokens@4.0.0: {} + js-yaml@4.1.0: + dependencies: + argparse: 2.0.1 + jsesc@3.1.0: {} - json-parse-better-errors@1.0.2: {} + json-buffer@3.0.1: {} json-parse-even-better-errors@2.3.1: {} - json-stringify-safe@5.0.1: {} + json-schema-traverse@0.4.1: {} + + json-schema-traverse@1.0.0: {} + + json-stable-stringify-without-jsonify@1.0.1: {} json5@2.2.3: {} - jsonparse@1.3.1: {} + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + keyv@5.5.3: + dependencies: + '@keyv/serialize': 1.1.1 kind-of@6.0.3: {} - lines-and-columns@1.2.4: {} + known-css-properties@0.36.0: {} - little-state-machine@4.8.1(react@19.1.1): - dependencies: - react: 19.1.1 + known-css-properties@0.37.0: {} - load-json-file@4.0.0: - dependencies: - graceful-fs: 4.2.11 - parse-json: 4.0.0 - pify: 3.0.0 - strip-bom: 3.0.0 + kysely@0.27.6: {} - locate-path@2.0.0: + launch-editor@2.12.0: dependencies: - p-locate: 2.0.0 - path-exists: 3.0.0 + picocolors: 1.1.1 + shell-quote: 1.8.3 - locate-path@3.0.0: + levn@0.4.1: dependencies: - p-locate: 3.0.0 - path-exists: 3.0.0 + prelude-ls: 1.2.1 + type-check: 0.4.0 - locate-path@5.0.0: - dependencies: - p-locate: 4.1.0 + lines-and-columns@1.2.4: {} locate-path@6.0.0: dependencies: @@ -4713,30 +4878,20 @@ snapshots: lodash-es@4.17.21: {} - lodash.ismatch@4.4.0: {} + lodash.merge@4.6.2: {} - lodash@4.17.21: {} + lodash.truncate@4.4.2: {} longest-streak@3.1.0: {} - loose-envify@1.4.0: - dependencies: - js-tokens: 4.0.0 - lru-cache@5.1.1: dependencies: yallist: 3.1.1 - lru-cache@6.0.0: - dependencies: - yallist: 4.0.0 - - map-obj@1.0.1: {} - - map-obj@4.3.0: {} - math-intrinsics@1.1.0: {} + mathml-tag-names@2.1.3: {} + mdast-util-from-markdown@2.0.2: dependencies: '@types/mdast': 4.0.4 @@ -4796,7 +4951,7 @@ snapshots: mdast-util-phrasing@4.1.0: dependencies: '@types/mdast': 4.0.4 - unist-util-is: 6.0.0 + unist-util-is: 6.0.1 mdast-util-to-hast@13.2.0: dependencies: @@ -4826,23 +4981,13 @@ snapshots: dependencies: '@types/mdast': 4.0.4 - meow@8.1.2: - dependencies: - '@types/minimist': 1.2.5 - camelcase-keys: 6.2.2 - decamelize-keys: 1.1.1 - hard-rejection: 2.1.0 - minimist-options: 4.1.0 - normalize-package-data: 3.0.3 - read-pkg-up: 7.0.1 - redent: 3.0.0 - trim-newlines: 3.0.1 - type-fest: 0.18.1 - yargs-parser: 20.2.9 - - merge-refs@2.0.0(@types/react@19.1.13): - optionalDependencies: - '@types/react': 19.1.13 + mdn-data@2.12.2: {} + + mdn-data@2.25.0: {} + + meow@13.2.0: {} + + merge2@1.4.1: {} micromark-core-commonmark@2.0.3: dependencies: @@ -4977,9 +5122,10 @@ snapshots: transitivePeerDependencies: - supports-color - millify@6.1.0: + micromatch@4.0.8: dependencies: - yargs: 17.7.2 + braces: 3.0.3 + picomatch: 2.3.1 mime-db@1.52.0: {} @@ -4987,105 +5133,62 @@ snapshots: dependencies: mime-db: 1.52.0 - min-indent@1.0.1: {} - minimatch@3.1.2: dependencies: brace-expansion: 1.1.12 - minimist-options@4.1.0: + minimatch@9.0.5: dependencies: - arrify: 1.0.1 - is-plain-obj: 1.1.0 - kind-of: 6.0.3 + brace-expansion: 2.0.2 - minimist@1.2.8: {} - - modify-values@1.0.1: {} - - motion-dom@12.23.21: + motion-dom@12.23.23: dependencies: motion-utils: 12.23.6 motion-utils@12.23.6: {} - motion@12.23.22(@emotion/is-prop-valid@1.4.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1): + motion@12.23.24(react-dom@19.2.0(react@19.2.0))(react@19.2.0): dependencies: - framer-motion: 12.23.22(@emotion/is-prop-valid@1.4.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + framer-motion: 12.23.24(react-dom@19.2.0(react@19.2.0))(react@19.2.0) tslib: 2.8.1 optionalDependencies: - '@emotion/is-prop-valid': 1.4.0 - react: 19.1.1 - react-dom: 19.1.1(react@19.1.1) + react: 19.2.0 + react-dom: 19.2.0(react@19.2.0) ms@2.1.3: {} - n-gram@2.0.2: {} - nanoid@3.3.11: {} - neo-async@2.6.2: {} - - node-releases@2.0.21: {} + natural-compare@1.4.0: {} - normalize-package-data@2.5.0: - dependencies: - hosted-git-info: 2.8.9 - resolve: 1.22.10 - semver: 5.7.2 - validate-npm-package-license: 3.0.4 + node-addon-api@7.1.1: + optional: true - normalize-package-data@3.0.3: - dependencies: - hosted-git-info: 4.1.0 - is-core-module: 2.16.1 - semver: 7.7.2 - validate-npm-package-license: 3.0.4 + node-releases@2.0.27: {} normalize-path@3.0.0: {} normalize-range@0.1.2: {} - numbro@2.5.0: - dependencies: - bignumber.js: 9.3.1 - - object-assign@4.1.1: {} - object-inspect@1.13.4: {} - p-limit@1.3.0: - dependencies: - p-try: 1.0.0 - - p-limit@2.3.0: + optionator@0.9.4: dependencies: - p-try: 2.2.0 + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 p-limit@3.1.0: dependencies: yocto-queue: 0.1.0 - p-locate@2.0.0: - dependencies: - p-limit: 1.3.0 - - p-locate@3.0.0: - dependencies: - p-limit: 2.3.0 - - p-locate@4.1.0: - dependencies: - p-limit: 2.3.0 - p-locate@5.0.0: dependencies: p-limit: 3.1.0 - p-try@1.0.0: {} - - p-try@2.2.0: {} - parent-module@1.0.1: dependencies: callsites: 3.1.0 @@ -5100,45 +5203,43 @@ snapshots: is-decimal: 2.0.1 is-hexadecimal: 2.0.1 - parse-json@4.0.0: - dependencies: - error-ex: 1.3.4 - json-parse-better-errors: 1.0.2 - parse-json@5.2.0: dependencies: '@babel/code-frame': 7.27.1 error-ex: 1.3.4 - json-parse-even-better-errors: 2.3.1 - lines-and-columns: 1.2.4 - - parse5@7.3.0: - dependencies: - entities: 6.0.1 - - path-exists@3.0.0: {} + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 path-exists@4.0.0: {} - path-parse@1.0.7: {} - - path-type@3.0.0: - dependencies: - pify: 3.0.0 + path-key@3.1.1: {} path-type@4.0.0: {} + pathe@2.0.3: {} + picocolors@1.1.1: {} picomatch@2.3.1: {} picomatch@4.0.3: {} - pify@2.3.0: {} + postcss-media-query-parser@0.2.3: {} + + postcss-resolve-nested-selector@0.1.6: {} + + postcss-safe-parser@7.0.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 - pify@3.0.0: {} + postcss-scss@4.0.9(postcss@8.5.6): + dependencies: + postcss: 8.5.6 - pngjs@5.0.0: {} + postcss-selector-parser@7.1.0: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 postcss-value-parser@4.2.0: {} @@ -5148,93 +5249,45 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 - prettier@3.6.2: {} - - process-nextick-args@2.0.1: {} - - prop-types@15.8.1: - dependencies: - loose-envify: 1.4.0 - object-assign: 4.1.1 - react-is: 16.13.1 + prelude-ls@1.2.1: {} - property-information@6.5.0: {} + prettier@3.6.2: {} property-information@7.1.0: {} - proxy-compare@3.0.1: {} - proxy-from-env@1.1.0: {} - q@1.5.1: {} + punycode@2.3.1: {} - qr.js@0.0.0: {} + qified@0.5.1: + dependencies: + hookified: 1.12.2 - qrcode@1.5.4: + qrcode.react@4.2.0(react@19.2.0): dependencies: - dijkstrajs: 1.0.3 - pngjs: 5.0.0 - yargs: 15.4.1 + react: 19.2.0 qs@6.14.0: dependencies: side-channel: 1.1.0 - quick-lru@4.0.1: {} - - radash@12.1.1: {} - - react-click-away-listener@2.4.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1): - dependencies: - react: 19.1.1 - react-dom: 19.1.1(react@19.1.1) - - react-datepicker@8.7.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1): - dependencies: - '@floating-ui/react': 0.27.16(react-dom@19.1.1(react@19.1.1))(react@19.1.1) - clsx: 2.1.1 - date-fns: 4.1.0 - react: 19.1.1 - react-dom: 19.1.1(react@19.1.1) - - react-dom@19.1.1(react@19.1.1): - dependencies: - react: 19.1.1 - scheduler: 0.26.0 - - react-hook-form@7.63.0(react@19.1.1): - dependencies: - react: 19.1.1 - - react-idle-timer@5.7.2(react-dom@19.1.1(react@19.1.1))(react@19.1.1): - dependencies: - react: 19.1.1 - react-dom: 19.1.1(react@19.1.1) - - react-intersection-observer@9.16.0(react-dom@19.1.1(react@19.1.1))(react@19.1.1): - dependencies: - react: 19.1.1 - optionalDependencies: - react-dom: 19.1.1(react@19.1.1) - - react-is@16.13.1: {} + queue-microtask@1.2.3: {} - react-is@19.1.1: {} - - react-loading-skeleton@3.5.0(react@19.1.1): + react-dom@19.2.0(react@19.2.0): dependencies: - react: 19.1.1 + react: 19.2.0 + scheduler: 0.27.0 - react-markdown@10.1.0(@types/react@19.1.13)(react@19.1.1): + react-markdown@10.1.0(@types/react@19.2.2)(react@19.2.0): dependencies: '@types/hast': 3.0.4 '@types/mdast': 4.0.4 - '@types/react': 19.1.13 + '@types/react': 19.2.2 devlop: 1.1.0 hast-util-to-jsx-runtime: 2.3.6 html-url-attributes: 3.0.1 mdast-util-to-hast: 13.2.0 - react: 19.1.1 + react: 19.2.0 remark-parse: 11.0.0 remark-rehype: 11.1.2 unified: 11.0.5 @@ -5243,152 +5296,21 @@ snapshots: transitivePeerDependencies: - supports-color - react-property@2.0.2: {} - - react-qr-code@2.0.18(react@19.1.1): - dependencies: - prop-types: 15.8.1 - qr.js: 0.0.0 - react: 19.1.1 - - react-redux@9.2.0(@types/react@19.1.13)(react@19.1.1)(redux@5.0.1): - dependencies: - '@types/use-sync-external-store': 0.0.6 - react: 19.1.1 - use-sync-external-store: 1.5.0(react@19.1.1) - optionalDependencies: - '@types/react': 19.1.13 - redux: 5.0.1 - - react-resize-detector@12.3.0(react@19.1.1): - dependencies: - es-toolkit: 1.39.10 - react: 19.1.1 - - react-router-dom@6.30.1(react-dom@19.1.1(react@19.1.1))(react@19.1.1): - dependencies: - '@remix-run/router': 1.23.0 - react: 19.1.1 - react-dom: 19.1.1(react@19.1.1) - react-router: 6.30.1(react@19.1.1) - - react-router@6.30.1(react@19.1.1): - dependencies: - '@remix-run/router': 1.23.0 - react: 19.1.1 - - react-simple-animate@3.5.3(react-dom@19.1.1(react@19.1.1)): - dependencies: - react-dom: 19.1.1(react@19.1.1) - - react-tracked@2.0.1(react@19.1.1)(scheduler@0.26.0): - dependencies: - proxy-compare: 3.0.1 - react: 19.1.1 - scheduler: 0.26.0 - use-context-selector: 2.0.0(react@19.1.1)(scheduler@0.26.0) - - react-virtualized-auto-sizer@1.0.26(react-dom@19.1.1(react@19.1.1))(react@19.1.1): - dependencies: - react: 19.1.1 - react-dom: 19.1.1(react@19.1.1) - - react@19.1.1: {} - - read-pkg-up@3.0.0: - dependencies: - find-up: 2.1.0 - read-pkg: 3.0.0 - - read-pkg-up@7.0.1: - dependencies: - find-up: 4.1.0 - read-pkg: 5.2.0 - type-fest: 0.8.1 - - read-pkg@3.0.0: - dependencies: - load-json-file: 4.0.0 - normalize-package-data: 2.5.0 - path-type: 3.0.0 - - read-pkg@5.2.0: - dependencies: - '@types/normalize-package-data': 2.4.4 - normalize-package-data: 2.5.0 - parse-json: 5.2.0 - type-fest: 0.6.0 - - readable-stream@2.3.8: - dependencies: - core-util-is: 1.0.3 - inherits: 2.0.4 - isarray: 1.0.0 - process-nextick-args: 2.0.1 - safe-buffer: 5.1.2 - string_decoder: 1.1.1 - util-deprecate: 1.0.2 - - readable-stream@3.6.2: - dependencies: - inherits: 2.0.4 - string_decoder: 1.3.0 - util-deprecate: 1.0.2 + react@19.2.0: {} readdirp@3.6.0: dependencies: picomatch: 2.3.1 - recharts@3.2.1(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react-is@19.1.1)(react@19.1.1)(redux@5.0.1): - dependencies: - '@reduxjs/toolkit': 2.9.0(react-redux@9.2.0(@types/react@19.1.13)(react@19.1.1)(redux@5.0.1))(react@19.1.1) - clsx: 2.1.1 - decimal.js-light: 2.5.1 - es-toolkit: 1.39.10 - eventemitter3: 5.0.1 - immer: 10.1.3 - react: 19.1.1 - react-dom: 19.1.1(react@19.1.1) - react-is: 19.1.1 - react-redux: 9.2.0(@types/react@19.1.13)(react@19.1.1)(redux@5.0.1) - reselect: 5.1.1 - tiny-invariant: 1.3.3 - use-sync-external-store: 1.5.0(react@19.1.1) - victory-vendor: 37.3.6 - transitivePeerDependencies: - - '@types/react' - - redux - - redent@3.0.0: - dependencies: - indent-string: 4.0.0 - strip-indent: 3.0.0 - - redux-thunk@3.1.0(redux@5.0.1): - dependencies: - redux: 5.0.1 - - redux@5.0.1: {} + readdirp@4.1.2: {} - rehype-external-links@3.0.0: + recast@0.23.11: dependencies: - '@types/hast': 3.0.4 - '@ungap/structured-clone': 1.3.0 - hast-util-is-element: 3.0.0 - is-absolute-url: 4.0.1 - space-separated-tokens: 2.0.2 - unist-util-visit: 5.0.0 - - rehype-raw@7.0.0: - dependencies: - '@types/hast': 3.0.4 - hast-util-raw: 9.1.0 - vfile: 6.0.3 - - rehype-sanitize@6.0.0: - dependencies: - '@types/hast': 3.0.4 - hast-util-sanitize: 5.0.2 + ast-types: 0.16.1 + esprima: 4.0.1 + source-map: 0.6.1 + tiny-invariant: 1.3.3 + tslib: 2.8.1 remark-parse@11.0.0: dependencies: @@ -5407,71 +5329,106 @@ snapshots: unified: 11.0.5 vfile: 6.0.3 - require-directory@2.1.1: {} + require-from-string@2.0.2: {} - require-main-filename@2.0.0: {} + resolve-from@4.0.0: {} - reselect@5.1.1: {} + resolve-from@5.0.0: {} - resolve-from@4.0.0: {} + resolve-pkg-maps@1.0.0: {} - resolve@1.22.10: - dependencies: - is-core-module: 2.16.1 - path-parse: 1.0.7 - supports-preserve-symlinks-flag: 1.0.0 + reusify@1.1.0: {} - rollup@4.52.2: + rollup@4.52.5: dependencies: '@types/estree': 1.0.8 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.52.2 - '@rollup/rollup-android-arm64': 4.52.2 - '@rollup/rollup-darwin-arm64': 4.52.2 - '@rollup/rollup-darwin-x64': 4.52.2 - '@rollup/rollup-freebsd-arm64': 4.52.2 - '@rollup/rollup-freebsd-x64': 4.52.2 - '@rollup/rollup-linux-arm-gnueabihf': 4.52.2 - '@rollup/rollup-linux-arm-musleabihf': 4.52.2 - '@rollup/rollup-linux-arm64-gnu': 4.52.2 - '@rollup/rollup-linux-arm64-musl': 4.52.2 - '@rollup/rollup-linux-loong64-gnu': 4.52.2 - '@rollup/rollup-linux-ppc64-gnu': 4.52.2 - '@rollup/rollup-linux-riscv64-gnu': 4.52.2 - '@rollup/rollup-linux-riscv64-musl': 4.52.2 - '@rollup/rollup-linux-s390x-gnu': 4.52.2 - '@rollup/rollup-linux-x64-gnu': 4.52.2 - '@rollup/rollup-linux-x64-musl': 4.52.2 - '@rollup/rollup-openharmony-arm64': 4.52.2 - '@rollup/rollup-win32-arm64-msvc': 4.52.2 - '@rollup/rollup-win32-ia32-msvc': 4.52.2 - '@rollup/rollup-win32-x64-gnu': 4.52.2 - '@rollup/rollup-win32-x64-msvc': 4.52.2 + '@rollup/rollup-android-arm-eabi': 4.52.5 + '@rollup/rollup-android-arm64': 4.52.5 + '@rollup/rollup-darwin-arm64': 4.52.5 + '@rollup/rollup-darwin-x64': 4.52.5 + '@rollup/rollup-freebsd-arm64': 4.52.5 + '@rollup/rollup-freebsd-x64': 4.52.5 + '@rollup/rollup-linux-arm-gnueabihf': 4.52.5 + '@rollup/rollup-linux-arm-musleabihf': 4.52.5 + '@rollup/rollup-linux-arm64-gnu': 4.52.5 + '@rollup/rollup-linux-arm64-musl': 4.52.5 + '@rollup/rollup-linux-loong64-gnu': 4.52.5 + '@rollup/rollup-linux-ppc64-gnu': 4.52.5 + '@rollup/rollup-linux-riscv64-gnu': 4.52.5 + '@rollup/rollup-linux-riscv64-musl': 4.52.5 + '@rollup/rollup-linux-s390x-gnu': 4.52.5 + '@rollup/rollup-linux-x64-gnu': 4.52.5 + '@rollup/rollup-linux-x64-musl': 4.52.5 + '@rollup/rollup-openharmony-arm64': 4.52.5 + '@rollup/rollup-win32-arm64-msvc': 4.52.5 + '@rollup/rollup-win32-ia32-msvc': 4.52.5 + '@rollup/rollup-win32-x64-gnu': 4.52.5 + '@rollup/rollup-win32-x64-msvc': 4.52.5 fsevents: 2.3.3 + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + rxjs@7.8.2: dependencies: tslib: 2.8.1 - safe-buffer@5.1.2: {} - - safe-buffer@5.2.1: {} - - sass@1.70.0: + sass@1.93.3: dependencies: - chokidar: 3.6.0 - immutable: 4.3.7 + chokidar: 4.0.3 + immutable: 5.1.4 source-map-js: 1.2.1 + optionalDependencies: + '@parcel/watcher': 2.5.1 - scheduler@0.26.0: {} - - semver@5.7.2: {} + scheduler@0.27.0: {} semver@6.3.1: {} - semver@7.7.2: {} + semver@7.7.3: {} - set-blocking@2.0.0: {} + seroval-plugins@1.3.3(seroval@1.3.2): + dependencies: + seroval: 1.3.2 + + seroval@1.3.2: {} + + sharp@0.34.4: + dependencies: + '@img/colour': 1.0.0 + detect-libc: 2.1.2 + semver: 7.7.3 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.34.4 + '@img/sharp-darwin-x64': 0.34.4 + '@img/sharp-libvips-darwin-arm64': 1.2.3 + '@img/sharp-libvips-darwin-x64': 1.2.3 + '@img/sharp-libvips-linux-arm': 1.2.3 + '@img/sharp-libvips-linux-arm64': 1.2.3 + '@img/sharp-libvips-linux-ppc64': 1.2.3 + '@img/sharp-libvips-linux-s390x': 1.2.3 + '@img/sharp-libvips-linux-x64': 1.2.3 + '@img/sharp-libvips-linuxmusl-arm64': 1.2.3 + '@img/sharp-libvips-linuxmusl-x64': 1.2.3 + '@img/sharp-linux-arm': 0.34.4 + '@img/sharp-linux-arm64': 0.34.4 + '@img/sharp-linux-ppc64': 0.34.4 + '@img/sharp-linux-s390x': 0.34.4 + '@img/sharp-linux-x64': 0.34.4 + '@img/sharp-linuxmusl-arm64': 0.34.4 + '@img/sharp-linuxmusl-x64': 0.34.4 + '@img/sharp-wasm32': 0.34.4 + '@img/sharp-win32-arm64': 0.34.4 + '@img/sharp-win32-ia32': 0.34.4 + '@img/sharp-win32-x64': 0.34.4 + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} shell-quote@1.8.3: {} @@ -5503,58 +5460,34 @@ snapshots: side-channel-map: 1.0.1 side-channel-weakmap: 1.0.2 - source-map-js@1.2.1: {} - - source-map-support@0.5.21: - dependencies: - buffer-from: 1.1.2 - source-map: 0.6.1 - optional: true - - source-map@0.5.7: {} - - source-map@0.6.1: {} + signal-exit@4.1.0: {} - space-separated-tokens@2.0.2: {} + slash@3.0.0: {} - spdx-correct@3.2.0: + slice-ansi@4.0.0: dependencies: - spdx-expression-parse: 3.0.1 - spdx-license-ids: 3.0.22 - - spdx-exceptions@2.5.0: {} + ansi-styles: 4.3.0 + astral-regex: 2.0.0 + is-fullwidth-code-point: 3.0.0 - spdx-expression-parse@3.0.1: + solid-js@1.9.9: dependencies: - spdx-exceptions: 2.5.0 - spdx-license-ids: 3.0.22 + csstype: 3.1.3 + seroval: 1.3.2 + seroval-plugins: 1.3.3(seroval@1.3.2) - spdx-license-ids@3.0.22: {} + source-map-js@1.2.1: {} - split2@3.2.2: - dependencies: - readable-stream: 3.6.2 + source-map@0.6.1: {} - split@1.0.1: - dependencies: - through: 2.3.8 + source-map@0.7.6: {} + + space-separated-tokens@2.0.2: {} - standard-version@9.5.0: + sqlite-wasm-kysely@0.3.0(kysely@0.27.6): dependencies: - chalk: 2.4.2 - conventional-changelog: 3.1.25 - conventional-changelog-config-spec: 2.1.0 - conventional-changelog-conventionalcommits: 4.6.3 - conventional-recommended-bump: 6.1.0 - detect-indent: 6.1.0 - detect-newline: 3.1.0 - dotgitignore: 2.1.0 - figures: 3.2.0 - find-up: 5.0.0 - git-semver-tags: 4.1.1 - semver: 7.7.2 - stringify-package: 1.0.1 - yargs: 16.2.0 + '@sqlite.org/sqlite-wasm': 3.48.0-build4 + kysely: 0.27.6 string-width@4.2.3: dependencies: @@ -5562,173 +5495,132 @@ snapshots: is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 - string_decoder@1.1.1: - dependencies: - safe-buffer: 5.1.2 - - string_decoder@1.3.0: - dependencies: - safe-buffer: 5.2.1 - stringify-entities@4.0.4: dependencies: character-entities-html4: 2.1.0 character-entities-legacy: 3.0.0 - stringify-package@1.0.1: {} - strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 - strip-bom@3.0.0: {} - - strip-indent@3.0.0: - dependencies: - min-indent: 1.0.1 - - style-to-js@1.1.17: - dependencies: - style-to-object: 1.0.9 - - style-to-object@1.0.9: - dependencies: - inline-style-parser: 0.2.4 - - stylis@4.2.0: {} - - supports-color@5.5.0: - dependencies: - has-flag: 3.0.0 - - supports-color@7.2.0: - dependencies: - has-flag: 4.0.0 - - supports-color@8.1.1: - dependencies: - has-flag: 4.0.0 - - supports-preserve-symlinks-flag@1.0.0: {} - - tabbable@6.2.0: {} - - terser@5.37.0: - dependencies: - '@jridgewell/source-map': 0.3.11 - acorn: 8.15.0 - commander: 2.20.3 - source-map-support: 0.5.21 - optional: true - - text-camel-case@1.2.9: - dependencies: - text-pascal-case: 1.2.9 - - text-capital-case@1.2.9: - dependencies: - text-no-case: 1.2.9 - text-upper-case-first: 1.2.9 - - text-case@1.2.9: - dependencies: - text-camel-case: 1.2.9 - text-capital-case: 1.2.9 - text-constant-case: 1.2.9 - text-dot-case: 1.2.9 - text-header-case: 1.2.9 - text-is-lower-case: 1.2.9 - text-is-upper-case: 1.2.9 - text-kebab-case: 1.2.9 - text-lower-case: 1.2.9 - text-lower-case-first: 1.2.9 - text-no-case: 1.2.9 - text-param-case: 1.2.9 - text-pascal-case: 1.2.9 - text-path-case: 1.2.9 - text-sentence-case: 1.2.9 - text-snake-case: 1.2.9 - text-swap-case: 1.2.9 - text-title-case: 1.2.9 - text-upper-case: 1.2.9 - text-upper-case-first: 1.2.9 - - text-constant-case@1.2.9: - dependencies: - text-no-case: 1.2.9 - text-upper-case: 1.2.9 + strip-json-comments@3.1.1: {} - text-dot-case@1.2.9: + style-to-js@1.1.19: dependencies: - text-no-case: 1.2.9 + style-to-object: 1.0.12 - text-extensions@1.9.0: {} - - text-header-case@1.2.9: + style-to-object@1.0.12: dependencies: - text-capital-case: 1.2.9 - - text-is-lower-case@1.2.9: {} + inline-style-parser: 0.2.6 - text-is-upper-case@1.2.9: {} - - text-kebab-case@1.2.9: + stylelint-config-recommended-scss@16.0.2(postcss@8.5.6)(stylelint@16.25.0(typescript@5.9.3)): dependencies: - text-no-case: 1.2.9 - - text-lower-case-first@1.2.9: {} - - text-lower-case@1.2.9: {} + postcss-scss: 4.0.9(postcss@8.5.6) + stylelint: 16.25.0(typescript@5.9.3) + stylelint-config-recommended: 17.0.0(stylelint@16.25.0(typescript@5.9.3)) + stylelint-scss: 6.12.1(stylelint@16.25.0(typescript@5.9.3)) + optionalDependencies: + postcss: 8.5.6 - text-no-case@1.2.9: + stylelint-config-recommended@17.0.0(stylelint@16.25.0(typescript@5.9.3)): dependencies: - text-lower-case: 1.2.9 + stylelint: 16.25.0(typescript@5.9.3) - text-param-case@1.2.9: + stylelint-config-standard-scss@16.0.0(postcss@8.5.6)(stylelint@16.25.0(typescript@5.9.3)): dependencies: - text-dot-case: 1.2.9 + stylelint: 16.25.0(typescript@5.9.3) + stylelint-config-recommended-scss: 16.0.2(postcss@8.5.6)(stylelint@16.25.0(typescript@5.9.3)) + stylelint-config-standard: 39.0.1(stylelint@16.25.0(typescript@5.9.3)) + optionalDependencies: + postcss: 8.5.6 - text-pascal-case@1.2.9: + stylelint-config-standard@39.0.1(stylelint@16.25.0(typescript@5.9.3)): dependencies: - text-no-case: 1.2.9 + stylelint: 16.25.0(typescript@5.9.3) + stylelint-config-recommended: 17.0.0(stylelint@16.25.0(typescript@5.9.3)) - text-path-case@1.2.9: + stylelint-scss@6.12.1(stylelint@16.25.0(typescript@5.9.3)): dependencies: - text-dot-case: 1.2.9 + css-tree: 3.1.0 + is-plain-object: 5.0.0 + known-css-properties: 0.36.0 + mdn-data: 2.25.0 + postcss-media-query-parser: 0.2.3 + postcss-resolve-nested-selector: 0.1.6 + postcss-selector-parser: 7.1.0 + postcss-value-parser: 4.2.0 + stylelint: 16.25.0(typescript@5.9.3) - text-sentence-case@1.2.9: + stylelint@16.25.0(typescript@5.9.3): dependencies: - text-no-case: 1.2.9 - text-upper-case-first: 1.2.9 + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + '@csstools/media-query-list-parser': 4.0.3(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/selector-specificity': 5.0.0(postcss-selector-parser@7.1.0) + '@dual-bundle/import-meta-resolve': 4.2.1 + balanced-match: 2.0.0 + colord: 2.9.3 + cosmiconfig: 9.0.0(typescript@5.9.3) + css-functions-list: 3.2.3 + css-tree: 3.1.0 + debug: 4.4.3 + fast-glob: 3.3.3 + fastest-levenshtein: 1.0.16 + file-entry-cache: 10.1.4 + global-modules: 2.0.0 + globby: 11.1.0 + globjoin: 0.1.4 + html-tags: 3.3.1 + ignore: 7.0.5 + imurmurhash: 0.1.4 + is-plain-object: 5.0.0 + known-css-properties: 0.37.0 + mathml-tag-names: 2.1.3 + meow: 13.2.0 + micromatch: 4.0.8 + normalize-path: 3.0.0 + picocolors: 1.1.1 + postcss: 8.5.6 + postcss-resolve-nested-selector: 0.1.6 + postcss-safe-parser: 7.0.1(postcss@8.5.6) + postcss-selector-parser: 7.1.0 + postcss-value-parser: 4.2.0 + resolve-from: 5.0.0 + string-width: 4.2.3 + supports-hyperlinks: 3.2.0 + svg-tags: 1.0.0 + table: 6.9.0 + write-file-atomic: 5.0.1 + transitivePeerDependencies: + - supports-color + - typescript - text-snake-case@1.2.9: + supports-color@7.2.0: dependencies: - text-dot-case: 1.2.9 - - text-swap-case@1.2.9: {} + has-flag: 4.0.0 - text-title-case@1.2.9: + supports-hyperlinks@3.2.0: dependencies: - text-no-case: 1.2.9 - text-upper-case-first: 1.2.9 - - text-upper-case-first@1.2.9: {} + has-flag: 4.0.0 + supports-color: 7.2.0 - text-upper-case@1.2.9: {} + svg-tags@1.0.0: {} - through2@2.0.5: - dependencies: - readable-stream: 2.3.8 - xtend: 4.0.2 + tabbable@6.3.0: {} - through2@4.0.2: + table@6.9.0: dependencies: - readable-stream: 3.6.2 - - through@2.3.8: {} + ajv: 8.17.1 + lodash.truncate: 4.4.2 + slice-ansi: 4.0.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 tiny-invariant@1.3.3: {} + tiny-warning@1.0.3: {} + tinyglobby@0.2.15: dependencies: fdir: 6.5.0(picomatch@4.0.3) @@ -5738,36 +5630,41 @@ snapshots: dependencies: is-number: 7.0.0 - tree-kill@1.2.2: {} - trim-lines@3.0.1: {} - trim-newlines@3.0.1: {} - trough@2.2.0: {} - tslib@2.8.1: {} - - type-fest@0.18.1: {} - - type-fest@0.6.0: {} - - type-fest@0.8.1: {} + ts-api-utils@2.1.0(typescript@5.9.3): + dependencies: + typescript: 5.9.3 - type-fest@4.41.0: {} + tslib@2.8.1: {} - typedarray@0.0.6: {} + tsx@4.20.6: + dependencies: + esbuild: 0.25.12 + get-tsconfig: 4.13.0 + optionalDependencies: + fsevents: 2.3.3 - typesafe-i18n@5.26.2(typescript@5.9.2): + type-check@0.4.0: dependencies: - typescript: 5.9.2 + prelude-ls: 1.2.1 - typescript@5.9.2: {} + typescript-eslint@8.46.2(eslint@9.37.0)(typescript@5.9.3): + dependencies: + '@typescript-eslint/eslint-plugin': 8.46.2(@typescript-eslint/parser@8.46.2(eslint@9.37.0)(typescript@5.9.3))(eslint@9.37.0)(typescript@5.9.3) + '@typescript-eslint/parser': 8.46.2(eslint@9.37.0)(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.46.2(typescript@5.9.3) + '@typescript-eslint/utils': 8.46.2(eslint@9.37.0)(typescript@5.9.3) + eslint: 9.37.0 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color - uglify-js@3.19.3: - optional: true + typescript@5.9.3: {} - undici-types@7.12.0: {} + undici-types@7.16.0: {} unified@11.0.5: dependencies: @@ -5779,7 +5676,7 @@ snapshots: trough: 2.2.0 vfile: 6.0.3 - unist-util-is@6.0.0: + unist-util-is@6.0.1: dependencies: '@types/unist': 3.0.3 @@ -5791,56 +5688,43 @@ snapshots: dependencies: '@types/unist': 3.0.3 - unist-util-visit-parents@6.0.1: + unist-util-visit-parents@6.0.2: dependencies: '@types/unist': 3.0.3 - unist-util-is: 6.0.0 + unist-util-is: 6.0.1 unist-util-visit@5.0.0: dependencies: '@types/unist': 3.0.3 - unist-util-is: 6.0.0 - unist-util-visit-parents: 6.0.1 + unist-util-is: 6.0.1 + unist-util-visit-parents: 6.0.2 - update-browserslist-db@1.1.3(browserslist@4.26.2): + unplugin@2.3.10: dependencies: - browserslist: 4.26.2 - escalade: 3.2.0 - picocolors: 1.1.1 + '@jridgewell/remapping': 2.3.5 + acorn: 8.15.0 + picomatch: 4.0.3 + webpack-virtual-modules: 0.6.2 - use-breakpoint@4.0.6(react-dom@19.1.1(react@19.1.1))(react@19.1.1): + update-browserslist-db@1.1.4(browserslist@4.27.0): dependencies: - react: 19.1.1 - react-dom: 19.1.1(react@19.1.1) + browserslist: 4.27.0 + escalade: 3.2.0 + picocolors: 1.1.1 - use-context-selector@2.0.0(react@19.1.1)(scheduler@0.26.0): + uri-js@4.4.1: dependencies: - react: 19.1.1 - scheduler: 0.26.0 + punycode: 2.3.1 - use-deep-compare-effect@1.8.1(react@19.1.1): - dependencies: - '@babel/runtime': 7.28.4 - dequal: 2.0.3 - react: 19.1.1 + urlpattern-polyfill@10.1.0: {} - use-sync-external-store@1.5.0(react@19.1.1): + use-sync-external-store@1.6.0(react@19.2.0): dependencies: - react: 19.1.1 + react: 19.2.0 util-deprecate@1.0.2: {} - uuid@8.3.2: {} - - validate-npm-package-license@3.0.4: - dependencies: - spdx-correct: 3.2.0 - spdx-expression-parse: 3.0.1 - - vfile-location@5.0.3: - dependencies: - '@types/unist': 3.0.3 - vfile: 6.0.3 + uuid@10.0.0: {} vfile-message@4.0.3: dependencies: @@ -5852,128 +5736,59 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - victory-vendor@37.3.6: + vite-plugin-image-optimizer@2.0.3(sharp@0.34.4)(vite@7.1.12(@types/node@24.10.0)(sass@1.93.3)(tsx@4.20.6)): dependencies: - '@types/d3-array': 3.2.2 - '@types/d3-ease': 3.0.2 - '@types/d3-interpolate': 3.0.4 - '@types/d3-scale': 4.0.9 - '@types/d3-shape': 3.1.7 - '@types/d3-time': 3.0.4 - '@types/d3-timer': 3.0.2 - d3-array: 3.2.4 - d3-ease: 3.0.1 - d3-interpolate: 3.0.1 - d3-scale: 4.0.2 - d3-shape: 3.2.0 - d3-time: 3.1.0 - d3-timer: 3.0.1 - - vite-plugin-package-version@1.1.0(vite@7.1.7(@types/node@24.5.2)(jiti@2.4.2)(sass@1.70.0)(terser@5.37.0)(yaml@2.6.1)): - dependencies: - vite: 7.1.7(@types/node@24.5.2)(jiti@2.4.2)(sass@1.70.0)(terser@5.37.0)(yaml@2.6.1) + ansi-colors: 4.1.3 + pathe: 2.0.3 + vite: 7.1.12(@types/node@24.10.0)(sass@1.93.3)(tsx@4.20.6) + optionalDependencies: + sharp: 0.34.4 - vite@7.1.7(@types/node@24.5.2)(jiti@2.4.2)(sass@1.70.0)(terser@5.37.0)(yaml@2.6.1): + vite@7.1.12(@types/node@24.10.0)(sass@1.93.3)(tsx@4.20.6): dependencies: - esbuild: 0.25.10 + esbuild: 0.25.12 fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 postcss: 8.5.6 - rollup: 4.52.2 + rollup: 4.52.5 tinyglobby: 0.2.15 optionalDependencies: - '@types/node': 24.5.2 + '@types/node': 24.10.0 fsevents: 2.3.3 - jiti: 2.4.2 - sass: 1.70.0 - terser: 5.37.0 - yaml: 2.6.1 - - web-namespaces@2.0.1: {} + sass: 1.93.3 + tsx: 4.20.6 - which-module@2.0.1: {} - - wordwrap@1.0.0: {} - - wrap-ansi@6.2.0: - dependencies: - ansi-styles: 4.3.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 + webpack-virtual-modules@0.6.2: {} - wrap-ansi@7.0.0: + which@1.3.1: dependencies: - ansi-styles: 4.3.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - - xtend@4.0.2: {} - - y18n@4.0.3: {} - - y18n@5.0.8: {} + isexe: 2.0.0 - yallist@3.1.1: {} - - yallist@4.0.0: {} - - yaml@1.10.2: {} - - yaml@2.6.1: - optional: true - - yargs-parser@18.1.3: + which@2.0.2: dependencies: - camelcase: 5.3.1 - decamelize: 1.2.0 + isexe: 2.0.0 - yargs-parser@20.2.9: {} + word-wrap@1.2.5: {} - yargs-parser@21.1.1: {} - - yargs@15.4.1: + write-file-atomic@5.0.1: dependencies: - cliui: 6.0.0 - decamelize: 1.2.0 - find-up: 4.1.0 - get-caller-file: 2.0.5 - require-directory: 2.1.1 - require-main-filename: 2.0.0 - set-blocking: 2.0.0 - string-width: 4.2.3 - which-module: 2.0.1 - y18n: 4.0.3 - yargs-parser: 18.1.3 + imurmurhash: 0.1.4 + signal-exit: 4.1.0 - yargs@16.2.0: - dependencies: - cliui: 7.0.4 - escalade: 3.2.0 - get-caller-file: 2.0.5 - require-directory: 2.1.1 - string-width: 4.2.3 - y18n: 5.0.8 - yargs-parser: 20.2.9 + ws@8.18.3: {} - yargs@17.7.2: - dependencies: - cliui: 8.0.1 - escalade: 3.2.0 - get-caller-file: 2.0.5 - require-directory: 2.1.1 - string-width: 4.2.3 - y18n: 5.0.8 - yargs-parser: 21.1.1 + yallist@3.1.1: {} yocto-queue@0.1.0: {} zod@3.25.76: {} - zustand@5.0.8(@types/react@19.1.13)(immer@10.1.3)(react@19.1.1)(use-sync-external-store@1.5.0(react@19.1.1)): + zod@4.1.12: {} + + zustand@5.0.8(@types/react@19.2.2)(react@19.2.0)(use-sync-external-store@1.6.0(react@19.2.0)): optionalDependencies: - '@types/react': 19.1.13 - immer: 10.1.3 - react: 19.1.1 - use-sync-external-store: 1.5.0(react@19.1.1) + '@types/react': 19.2.2 + react: 19.2.0 + use-sync-external-store: 1.6.0(react@19.2.0) zwitch@2.0.4: {} diff --git a/web/pnpm-workspace.yaml b/web/pnpm-workspace.yaml new file mode 100644 index 000000000..17a31e5ca --- /dev/null +++ b/web/pnpm-workspace.yaml @@ -0,0 +1,5 @@ +onlyBuiltDependencies: + - '@parcel/watcher' + - '@swc/core' + - esbuild + - sharp diff --git a/web/postcss.config.js b/web/postcss.config.js deleted file mode 100644 index a47ef4f95..000000000 --- a/web/postcss.config.js +++ /dev/null @@ -1,5 +0,0 @@ -module.exports = { - plugins: { - autoprefixer: {}, - }, -}; diff --git a/web/project.inlang/.gitignore b/web/project.inlang/.gitignore new file mode 100644 index 000000000..5e4659675 --- /dev/null +++ b/web/project.inlang/.gitignore @@ -0,0 +1 @@ +cache \ No newline at end of file diff --git a/web/project.inlang/project_id b/web/project.inlang/project_id new file mode 100644 index 000000000..7f59fbcea --- /dev/null +++ b/web/project.inlang/project_id @@ -0,0 +1 @@ +hx4EvECp61fjNPUXtn \ No newline at end of file diff --git a/web/project.inlang/settings.json b/web/project.inlang/settings.json new file mode 100644 index 000000000..78e1b66ac --- /dev/null +++ b/web/project.inlang/settings.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://inlang.com/schema/project-settings", + "baseLocale": "en", + "locales": [ + "en" + ], + "modules": [ + "https://cdn.jsdelivr.net/npm/@inlang/plugin-message-format@latest/dist/index.js" + ], + "plugin.inlang.messageFormat": { + "pathPattern": [ + "./messages/{locale}/auth.json", + "./messages/{locale}/common.json", + "./messages/{locale}/form.json", + "./messages/{locale}/profile.json", + "./messages/{locale}/modal.json", + "./messages/{locale}/components.json", + "./messages/{locale}/users.json" + ] + } +} diff --git a/web/public/fonts/Poppins/Poppins-Black.woff2 b/web/public/fonts/Poppins/Poppins-Black.woff2 deleted file mode 100644 index 2dfde80a5..000000000 Binary files a/web/public/fonts/Poppins/Poppins-Black.woff2 and /dev/null differ diff --git a/web/public/fonts/Poppins/Poppins-BlackItalic.woff2 b/web/public/fonts/Poppins/Poppins-BlackItalic.woff2 deleted file mode 100644 index c53e44b3b..000000000 Binary files a/web/public/fonts/Poppins/Poppins-BlackItalic.woff2 and /dev/null differ diff --git a/web/public/fonts/Poppins/Poppins-Bold.woff2 b/web/public/fonts/Poppins/Poppins-Bold.woff2 deleted file mode 100644 index 13e0e28bc..000000000 Binary files a/web/public/fonts/Poppins/Poppins-Bold.woff2 and /dev/null differ diff --git a/web/public/fonts/Poppins/Poppins-BoldItalic.woff2 b/web/public/fonts/Poppins/Poppins-BoldItalic.woff2 deleted file mode 100644 index f7f7fe4bc..000000000 Binary files a/web/public/fonts/Poppins/Poppins-BoldItalic.woff2 and /dev/null differ diff --git a/web/public/fonts/Poppins/Poppins-ExtraBold.woff2 b/web/public/fonts/Poppins/Poppins-ExtraBold.woff2 deleted file mode 100644 index ad86d0277..000000000 Binary files a/web/public/fonts/Poppins/Poppins-ExtraBold.woff2 and /dev/null differ diff --git a/web/public/fonts/Poppins/Poppins-ExtraBoldItalic.woff2 b/web/public/fonts/Poppins/Poppins-ExtraBoldItalic.woff2 deleted file mode 100644 index 9cf7164d8..000000000 Binary files a/web/public/fonts/Poppins/Poppins-ExtraBoldItalic.woff2 and /dev/null differ diff --git a/web/public/fonts/Poppins/Poppins-ExtraLight.woff2 b/web/public/fonts/Poppins/Poppins-ExtraLight.woff2 deleted file mode 100644 index 5be09909b..000000000 Binary files a/web/public/fonts/Poppins/Poppins-ExtraLight.woff2 and /dev/null differ diff --git a/web/public/fonts/Poppins/Poppins-ExtraLightItalic.woff2 b/web/public/fonts/Poppins/Poppins-ExtraLightItalic.woff2 deleted file mode 100644 index 8b7c5ef1f..000000000 Binary files a/web/public/fonts/Poppins/Poppins-ExtraLightItalic.woff2 and /dev/null differ diff --git a/web/public/fonts/Poppins/Poppins-Italic.woff2 b/web/public/fonts/Poppins/Poppins-Italic.woff2 deleted file mode 100644 index 1db484df9..000000000 Binary files a/web/public/fonts/Poppins/Poppins-Italic.woff2 and /dev/null differ diff --git a/web/public/fonts/Poppins/Poppins-Light.woff2 b/web/public/fonts/Poppins/Poppins-Light.woff2 deleted file mode 100644 index 7eba2c4f7..000000000 Binary files a/web/public/fonts/Poppins/Poppins-Light.woff2 and /dev/null differ diff --git a/web/public/fonts/Poppins/Poppins-LightItalic.woff2 b/web/public/fonts/Poppins/Poppins-LightItalic.woff2 deleted file mode 100644 index 748c1a34a..000000000 Binary files a/web/public/fonts/Poppins/Poppins-LightItalic.woff2 and /dev/null differ diff --git a/web/public/fonts/Poppins/Poppins-Medium.woff2 b/web/public/fonts/Poppins/Poppins-Medium.woff2 deleted file mode 100644 index 406acb667..000000000 Binary files a/web/public/fonts/Poppins/Poppins-Medium.woff2 and /dev/null differ diff --git a/web/public/fonts/Poppins/Poppins-MediumItalic.woff2 b/web/public/fonts/Poppins/Poppins-MediumItalic.woff2 deleted file mode 100644 index 39177dc3f..000000000 Binary files a/web/public/fonts/Poppins/Poppins-MediumItalic.woff2 and /dev/null differ diff --git a/web/public/fonts/Poppins/Poppins-Regular.woff2 b/web/public/fonts/Poppins/Poppins-Regular.woff2 deleted file mode 100644 index 964d6d2f2..000000000 Binary files a/web/public/fonts/Poppins/Poppins-Regular.woff2 and /dev/null differ diff --git a/web/public/fonts/Poppins/Poppins-SemiBold.woff2 b/web/public/fonts/Poppins/Poppins-SemiBold.woff2 deleted file mode 100644 index 9e4d0c0eb..000000000 Binary files a/web/public/fonts/Poppins/Poppins-SemiBold.woff2 and /dev/null differ diff --git a/web/public/fonts/Poppins/Poppins-SemiBoldItalic.woff2 b/web/public/fonts/Poppins/Poppins-SemiBoldItalic.woff2 deleted file mode 100644 index 605065152..000000000 Binary files a/web/public/fonts/Poppins/Poppins-SemiBoldItalic.woff2 and /dev/null differ diff --git a/web/public/fonts/Poppins/Poppins-Thin.woff2 b/web/public/fonts/Poppins/Poppins-Thin.woff2 deleted file mode 100644 index f98239af2..000000000 Binary files a/web/public/fonts/Poppins/Poppins-Thin.woff2 and /dev/null differ diff --git a/web/public/fonts/Poppins/Poppins-ThinItalic.woff2 b/web/public/fonts/Poppins/Poppins-ThinItalic.woff2 deleted file mode 100644 index 86ee4fd46..000000000 Binary files a/web/public/fonts/Poppins/Poppins-ThinItalic.woff2 and /dev/null differ diff --git a/web/public/fonts/Roboto/Roboto-Black.woff2 b/web/public/fonts/Roboto/Roboto-Black.woff2 deleted file mode 100644 index beeec681b..000000000 Binary files a/web/public/fonts/Roboto/Roboto-Black.woff2 and /dev/null differ diff --git a/web/public/fonts/Roboto/Roboto-BlackItalic.woff2 b/web/public/fonts/Roboto/Roboto-BlackItalic.woff2 deleted file mode 100644 index 2ceeea8ad..000000000 Binary files a/web/public/fonts/Roboto/Roboto-BlackItalic.woff2 and /dev/null differ diff --git a/web/public/fonts/Roboto/Roboto-Bold.woff2 b/web/public/fonts/Roboto/Roboto-Bold.woff2 deleted file mode 100644 index b102004e8..000000000 Binary files a/web/public/fonts/Roboto/Roboto-Bold.woff2 and /dev/null differ diff --git a/web/public/fonts/Roboto/Roboto-BoldItalic.woff2 b/web/public/fonts/Roboto/Roboto-BoldItalic.woff2 deleted file mode 100644 index 9a46768a5..000000000 Binary files a/web/public/fonts/Roboto/Roboto-BoldItalic.woff2 and /dev/null differ diff --git a/web/public/fonts/Roboto/Roboto-Italic.woff2 b/web/public/fonts/Roboto/Roboto-Italic.woff2 deleted file mode 100644 index 3d60e7489..000000000 Binary files a/web/public/fonts/Roboto/Roboto-Italic.woff2 and /dev/null differ diff --git a/web/public/fonts/Roboto/Roboto-Light.woff2 b/web/public/fonts/Roboto/Roboto-Light.woff2 deleted file mode 100644 index 3e13c5f31..000000000 Binary files a/web/public/fonts/Roboto/Roboto-Light.woff2 and /dev/null differ diff --git a/web/public/fonts/Roboto/Roboto-LightItalic.woff2 b/web/public/fonts/Roboto/Roboto-LightItalic.woff2 deleted file mode 100644 index a0238dfea..000000000 Binary files a/web/public/fonts/Roboto/Roboto-LightItalic.woff2 and /dev/null differ diff --git a/web/public/fonts/Roboto/Roboto-Medium.woff2 b/web/public/fonts/Roboto/Roboto-Medium.woff2 deleted file mode 100644 index 8b1aebb23..000000000 Binary files a/web/public/fonts/Roboto/Roboto-Medium.woff2 and /dev/null differ diff --git a/web/public/fonts/Roboto/Roboto-MediumItalic.woff2 b/web/public/fonts/Roboto/Roboto-MediumItalic.woff2 deleted file mode 100644 index 1cbf304c4..000000000 Binary files a/web/public/fonts/Roboto/Roboto-MediumItalic.woff2 and /dev/null differ diff --git a/web/public/fonts/Roboto/Roboto-Regular.woff2 b/web/public/fonts/Roboto/Roboto-Regular.woff2 deleted file mode 100644 index 0aa90fc17..000000000 Binary files a/web/public/fonts/Roboto/Roboto-Regular.woff2 and /dev/null differ diff --git a/web/public/fonts/Roboto/Roboto-Thin.woff2 b/web/public/fonts/Roboto/Roboto-Thin.woff2 deleted file mode 100644 index 85c029e11..000000000 Binary files a/web/public/fonts/Roboto/Roboto-Thin.woff2 and /dev/null differ diff --git a/web/public/fonts/Roboto/Roboto-ThinItalic.woff2 b/web/public/fonts/Roboto/Roboto-ThinItalic.woff2 deleted file mode 100644 index f698c77d4..000000000 Binary files a/web/public/fonts/Roboto/Roboto-ThinItalic.woff2 and /dev/null differ diff --git a/web/public/fonts/SourceCodePro/SourceCodePro-Italic.woff2 b/web/public/fonts/SourceCodePro/SourceCodePro-Italic.woff2 deleted file mode 100644 index 6f9d8a73f..000000000 Binary files a/web/public/fonts/SourceCodePro/SourceCodePro-Italic.woff2 and /dev/null differ diff --git a/web/public/fonts/SourceCodePro/SourceCodePro-Regular.woff2 b/web/public/fonts/SourceCodePro/SourceCodePro-Regular.woff2 deleted file mode 100644 index 3f5cf9e08..000000000 Binary files a/web/public/fonts/SourceCodePro/SourceCodePro-Regular.woff2 and /dev/null differ diff --git a/web/public/fonts/geist/Geist-Bold.woff2 b/web/public/fonts/geist/Geist-Bold.woff2 new file mode 100644 index 000000000..46f524f4f Binary files /dev/null and b/web/public/fonts/geist/Geist-Bold.woff2 differ diff --git a/web/public/fonts/geist/Geist-BoldItalic.woff2 b/web/public/fonts/geist/Geist-BoldItalic.woff2 new file mode 100644 index 000000000..240acb99e Binary files /dev/null and b/web/public/fonts/geist/Geist-BoldItalic.woff2 differ diff --git a/web/public/fonts/geist/Geist-Medium.woff2 b/web/public/fonts/geist/Geist-Medium.woff2 new file mode 100644 index 000000000..ef6dbb211 Binary files /dev/null and b/web/public/fonts/geist/Geist-Medium.woff2 differ diff --git a/web/public/fonts/geist/Geist-MediumItalic.woff2 b/web/public/fonts/geist/Geist-MediumItalic.woff2 new file mode 100644 index 000000000..344fedaf9 Binary files /dev/null and b/web/public/fonts/geist/Geist-MediumItalic.woff2 differ diff --git a/web/public/fonts/geist/Geist-Regular.woff2 b/web/public/fonts/geist/Geist-Regular.woff2 new file mode 100644 index 000000000..0db0f1943 Binary files /dev/null and b/web/public/fonts/geist/Geist-Regular.woff2 differ diff --git a/web/public/fonts/geist/Geist-RegularItalic.woff2 b/web/public/fonts/geist/Geist-RegularItalic.woff2 new file mode 100644 index 000000000..33e9948be Binary files /dev/null and b/web/public/fonts/geist/Geist-RegularItalic.woff2 differ diff --git a/web/public/fonts/geist/Geist-SemiBold.woff2 b/web/public/fonts/geist/Geist-SemiBold.woff2 new file mode 100644 index 000000000..838452303 Binary files /dev/null and b/web/public/fonts/geist/Geist-SemiBold.woff2 differ diff --git a/web/public/fonts/geist/Geist-SemiBoldItalic.woff2 b/web/public/fonts/geist/Geist-SemiBoldItalic.woff2 new file mode 100644 index 000000000..2f53ced42 Binary files /dev/null and b/web/public/fonts/geist/Geist-SemiBoldItalic.woff2 differ diff --git a/web/public/fonts/source_code_pro/SourceCodePro-Regular.woff2 b/web/public/fonts/source_code_pro/SourceCodePro-Regular.woff2 new file mode 100644 index 000000000..40826f1a6 Binary files /dev/null and b/web/public/fonts/source_code_pro/SourceCodePro-Regular.woff2 differ diff --git a/web/src/app/App.tsx b/web/src/app/App.tsx new file mode 100644 index 000000000..ef1daa01f --- /dev/null +++ b/web/src/app/App.tsx @@ -0,0 +1,14 @@ +import './day'; + +import { QueryClientProvider } from '@tanstack/react-query'; +import { RouterProvider } from '@tanstack/react-router'; +import { queryClient } from './query'; +import { router } from './router'; + +export const App = () => { + return ( + + + + ); +}; diff --git a/web/src/app/day.ts b/web/src/app/day.ts new file mode 100644 index 000000000..8a2552ea8 --- /dev/null +++ b/web/src/app/day.ts @@ -0,0 +1,4 @@ +import dayjs from 'dayjs'; +import utc from 'dayjs/plugin/utc'; + +dayjs.extend(utc); diff --git a/web/src/app/query.ts b/web/src/app/query.ts new file mode 100644 index 000000000..f4bc7aa56 --- /dev/null +++ b/web/src/app/query.ts @@ -0,0 +1,38 @@ +import { MutationCache, QueryClient, type QueryKey } from '@tanstack/react-query'; + +type InvalidateMeta = { invalidate?: QueryKey[] | QueryKey }; + +let queryClient: QueryClient; + +type RO = readonly unknown[]; + +const isArrayFlat = (arr: RO | readonly RO[]): boolean => + arr.every((item) => !Array.isArray(item)); + +const mutationCache = new MutationCache({ + onSuccess: async (_data, _variables, _context, mutation) => { + const keys = (mutation.meta as InvalidateMeta | undefined)?.invalidate; + if (!Array.isArray(keys) || keys.length === 0) return; + if (isArrayFlat(keys)) { + await queryClient.invalidateQueries({ queryKey: keys }); + } else { + await Promise.all( + keys.map((key) => queryClient.invalidateQueries({ queryKey: key as QueryKey })), + ); + } + }, +}); + +queryClient = new QueryClient({ + mutationCache, + defaultOptions: { + queries: { + staleTime: Infinity, + refetchOnWindowFocus: false, + refetchOnReconnect: false, + retry: false, + }, + }, +}); + +export { queryClient }; diff --git a/web/src/app/router.ts b/web/src/app/router.ts new file mode 100644 index 000000000..57a091480 --- /dev/null +++ b/web/src/app/router.ts @@ -0,0 +1,17 @@ +import { createRouter } from '@tanstack/react-router'; +import { routeTree } from '../routeTree.gen'; +import { queryClient } from './query'; + +export const router = createRouter({ + routeTree, + defaultPreloadStaleTime: 0, + context: { + queryClient, + }, +}); + +declare module '@tanstack/react-router' { + interface Register { + router: typeof router; + } +} diff --git a/web/src/components/App/App.tsx b/web/src/components/App/App.tsx deleted file mode 100644 index 1f05e4b30..000000000 --- a/web/src/components/App/App.tsx +++ /dev/null @@ -1,216 +0,0 @@ -import 'react-loading-skeleton/dist/skeleton.css'; - -import { Navigate, Route, BrowserRouter as Router, Routes } from 'react-router-dom'; - -import { AclRoutes } from '../../pages/acl/AclRoutes'; -import { ActivityLogPage } from '../../pages/activity-log/ActivityLogPage'; -import { AddDevicePage } from '../../pages/addDevice/AddDevicePage'; -import { OpenidAllowPage } from '../../pages/allow/OpenidAllowPage'; -import { AuthPage } from '../../pages/auth/AuthPage'; -import { DevicesPage } from '../../pages/devices/DevicesPage'; -import { EnrollmentPage } from '../../pages/enrollment/EnrollmentPage'; -import { GroupsPage } from '../../pages/groups/GroupsPage'; -import { NetworkPage } from '../../pages/network/NetworkPage'; -import { OpenidClientsListPage } from '../../pages/openid/OpenidClientsListPage/OpenidClientsListPage'; -import { OverviewPage } from '../../pages/overview/OverviewPage'; -import { OverviewIndexPage } from '../../pages/overview-index/OverviewIndexPage'; -import { ProvisionersPage } from '../../pages/provisioners/ProvisionersPage'; -import { SettingsPage } from '../../pages/settings/SettingsPage'; -import { SupportPage } from '../../pages/support/SupportPage'; -import { UserProfile } from '../../pages/users/UserProfile/UserProfile'; -import { UsersPage } from '../../pages/users/UsersPage'; -import { UsersSharedModals } from '../../pages/users/UsersSharedModals'; -import { WebhooksListPage } from '../../pages/webhooks/WebhooksListPage'; -import { WizardPage } from '../../pages/wizard/WizardPage'; -import { PageContainer } from '../../shared/components/Layout/PageContainer/PageContainer'; -import { UpgradeLicenseModal } from '../../shared/components/Layout/UpgradeLicenseModal/UpgradeLicenseModal'; -import { OutdatedComponentsModal } from '../../shared/components/modals/OutdatedComponentsModal/OutdatedComponentsModal'; -import { UpdateNotificationModal } from '../../shared/components/modals/UpdateNotificationModal/UpdateNotificationModal'; -import { ProtectedRoute } from '../../shared/components/Router/Guards/ProtectedRoute/ProtectedRoute'; -import { ToastManager } from '../../shared/defguard-ui/components/Layout/ToastManager/ToastManager'; -import { useAuthStore } from '../../shared/hooks/store/useAuthStore'; -import { Navigation } from '../Navigation/Navigation'; - -const App = () => { - const currentUser = useAuthStore((state) => state.user); - const isAdmin = useAuthStore((state) => state.user?.is_admin); - return ( - <> -
- - - - - - } - /> - - - - } - /> - } /> - - } /> - - - - } - /> - - - - } - /> - - - - } - /> - - - - } - /> - - - - } - /> - - - - } - /> - - - - } - /> - - - - } - /> - - - - } - /> - - - - } - /> - - - - } - /> - - - - } - /> - - - - } - /> - } /> - - - - - } - /> - - - - - - - } - /> - - - - } - /> - - ) : ( - - ) - } - /> - - - - - - -
- - - ); -}; - -export default App; diff --git a/web/src/components/AppLoader.tsx b/web/src/components/AppLoader.tsx deleted file mode 100644 index dd78829b1..000000000 --- a/web/src/components/AppLoader.tsx +++ /dev/null @@ -1,171 +0,0 @@ -import { useQuery } from '@tanstack/react-query'; -import { isUndefined } from 'lodash-es'; -import { lazy, Suspense, useEffect } from 'react'; -import { shallow } from 'zustand/shallow'; - -import { useI18nContext } from '../i18n/i18n-react'; -import { LoaderPage } from '../pages/loader/LoaderPage'; -import { useOutdatedComponentsModal } from '../shared/components/modals/OutdatedComponentsModal/useOutdatedComponentsModal'; -import { useToaster } from '../shared/defguard-ui/hooks/toasts/useToaster'; -import { isPresent } from '../shared/defguard-ui/utils/isPresent'; -import { useAppStore } from '../shared/hooks/store/useAppStore'; -import { useAuthStore } from '../shared/hooks/store/useAuthStore'; -import { useUpdatesStore } from '../shared/hooks/store/useUpdatesStore'; -import useApi from '../shared/hooks/useApi'; -import { QueryKeys } from '../shared/queries'; - -/** - * Fetches data needed by app before it's rendered. - * **/ -export const AppLoader = () => { - const toaster = useToaster(); - const [currentUser, resetAuthState, setAuthState] = useAuthStore( - (state) => [state.user, state.resetState, state.setState], - shallow, - ); - const appSettings = useAppStore((state) => state.settings); - const { - getAppInfo, - getNewVersion, - getOutdatedInfo, - user: { getMe }, - settings: { getEssentialSettings, getEnterpriseSettings }, - } = useApi(); - const setAppStore = useAppStore((state) => state.setState); - const { LL } = useI18nContext(); - const setUpdateStore = useUpdatesStore((s) => s.setUpdate); - const openOutdatedComponentsModal = useOutdatedComponentsModal((s) => s.open); - - const { data: outdatedInfo } = useQuery({ - queryFn: getOutdatedInfo, - queryKey: ['outdated'], - enabled: isPresent(currentUser) && currentUser.is_admin, - refetchOnWindowFocus: false, - refetchOnMount: true, - }); - - const { - data: meData, - isLoading: userLoading, - error: meFetchError, - } = useQuery({ - queryFn: getMe, - queryKey: [QueryKeys.FETCH_ME], - refetchOnMount: true, - refetchOnWindowFocus: false, - retry: false, - }); - - // biome-ignore lint/correctness/useExhaustiveDependencies: sideEffect - useEffect(() => { - if (meFetchError && currentUser) { - if (currentUser) { - resetAuthState(); - } - } - }, [meFetchError]); - - useEffect(() => { - if (meData) { - setAuthState({ user: meData }); - } - }, [meData, setAuthState]); - - const { data: appInfoData, error: appInfoError } = useQuery({ - queryFn: getAppInfo, - queryKey: [QueryKeys.FETCH_APP_INFO], - refetchOnWindowFocus: true, - refetchOnMount: true, - enabled: !isUndefined(currentUser), - }); - - // biome-ignore lint/correctness/useExhaustiveDependencies: sideEffect - useEffect(() => { - if (appInfoError) { - toaster.error(LL.messages.errorVersion()); - console.error(appInfoError); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [appInfoError]); - - useEffect(() => { - if (appInfoData) { - setAppStore({ appInfo: appInfoData }); - } - }, [appInfoData, setAppStore]); - - const { data: enterpriseSettingsData, error: enterpriseSettingsError } = useQuery({ - queryFn: getEnterpriseSettings, - queryKey: [QueryKeys.FETCH_ENTERPRISE_SETTINGS], - refetchOnWindowFocus: true, - retry: false, - enabled: !isUndefined(currentUser), - }); - - useEffect(() => { - if (enterpriseSettingsError) { - console.error(enterpriseSettingsError); - } - }, [enterpriseSettingsError]); - - useEffect(() => { - setAppStore({ enterprise_settings: enterpriseSettingsData }); - }, [setAppStore, enterpriseSettingsData]); - - const { isLoading: settingsLoading, data: essentialSettings } = useQuery({ - queryFn: getEssentialSettings, - queryKey: [QueryKeys.FETCH_ESSENTIAL_SETTINGS], - refetchOnMount: true, - }); - - // setAppSettings - useEffect(() => { - if (essentialSettings) { - if (document.title !== essentialSettings.instance_name) { - document.title = essentialSettings.instance_name; - } - setAppStore({ settings: essentialSettings }); - } - }, [essentialSettings, setAppStore]); - - const { data: newVersionData, error: newVersionError } = useQuery({ - queryFn: getNewVersion, - queryKey: [QueryKeys.FETCH_NEW_VERSION], - refetchOnWindowFocus: false, - refetchOnMount: true, - enabled: !isUndefined(currentUser) && currentUser.is_admin, - }); - - useEffect(() => { - if (newVersionError) { - console.error(newVersionError); - } - }, [newVersionError]); - - useEffect(() => { - if (newVersionData) { - setUpdateStore(newVersionData); - } - }, [newVersionData, setUpdateStore]); - - useEffect(() => { - if ( - outdatedInfo && - (outdatedInfo.proxy != null || outdatedInfo.gateways.length > 0) - ) { - openOutdatedComponentsModal(outdatedInfo); - } - }, [outdatedInfo, openOutdatedComponentsModal]); - - if (userLoading || (settingsLoading && isUndefined(appSettings))) { - return ; - } - - return ( - }> - - - ); -}; - -const App = lazy(() => import('./App/App')); diff --git a/web/src/components/I18nProvider.tsx b/web/src/components/I18nProvider.tsx deleted file mode 100644 index 23b54ed28..000000000 --- a/web/src/components/I18nProvider.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import { enUS as datePickerLocaleEnUS } from 'date-fns/locale/en-US'; -import { ko as datePickerLocaleKO } from 'date-fns/locale/ko'; -import { pl as datePickerLocalePL } from 'date-fns/locale/pl'; -import { type PropsWithChildren, useEffect, useState } from 'react'; -import { registerLocale, setDefaultLocale } from 'react-datepicker'; -import { navigatorDetector } from 'typesafe-i18n/detectors'; -import { shallow } from 'zustand/shallow'; - -import TypesafeI18n from '../i18n/i18n-react'; -import { baseLocale, detectLocale } from '../i18n/i18n-util'; -import { loadLocale } from '../i18n/i18n-util.sync'; -import { useAppStore } from '../shared/hooks/store/useAppStore'; -import { localeToDatePicker } from '../shared/utils/localeToDatepicker'; - -// Setups i18n so useI18nContext hooks can work -export const I18nProvider = ({ children }: PropsWithChildren) => { - const setAppState = useAppStore((s) => s.setState, shallow); - const detectedLocale = detectLocale(navigatorDetector); - const [localeLoaded, setLocaleLoaded] = useState(false); - - // biome-ignore lint/correctness/useExhaustiveDependencies: migration - useEffect(() => { - const lang = detectedLocale ?? baseLocale; - loadLocale(lang); - setLocaleLoaded(true); - setAppState({ language: lang }); - document.documentElement.lang = lang; - //react-datepicker - switch (lang) { - case 'en': - registerLocale('en-US', datePickerLocaleEnUS); - break; - case 'ko': - registerLocale('ko', datePickerLocaleKO); - break; - case 'pl': - registerLocale('pl', datePickerLocalePL); - } - setDefaultLocale(localeToDatePicker(lang)); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [detectedLocale]); - - if (!localeLoaded) return null; - - return {children}; -}; diff --git a/web/src/components/Navigation/Navigation.tsx b/web/src/components/Navigation/Navigation.tsx deleted file mode 100644 index 4f67f60b0..000000000 --- a/web/src/components/Navigation/Navigation.tsx +++ /dev/null @@ -1,267 +0,0 @@ -import './style.scss'; - -import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; -import { useMemo } from 'react'; -import { useLocation } from 'react-router'; -import { useBreakpoint } from 'use-breakpoint'; -import { shallow } from 'zustand/shallow'; - -import { useI18nContext } from '../../i18n/i18n-react'; -import SvgIconNavGroups from '../../shared/components/svg/IconNavGroups'; -import SvgIconNavKey from '../../shared/components/svg/IconNavKey'; -import SvgIconNavOpenId from '../../shared/components/svg/IconNavOpenid'; -import SvgIconNavProfile from '../../shared/components/svg/IconNavProfile'; -import SvgIconNavProvisioners from '../../shared/components/svg/IconNavProvisioners'; -import SvgIconNavSettings from '../../shared/components/svg/IconNavSettings'; -import SvgIconNavSupport from '../../shared/components/svg/IconNavSupport'; -import SvgIconNavUsers from '../../shared/components/svg/IconNavUsers'; -import SvgIconNavVpn from '../../shared/components/svg/IconNavVpn'; -import SvgIconNavWebhooks from '../../shared/components/svg/IconNavWebhooks'; -import { deviceBreakpoints } from '../../shared/constants'; -import { useAppStore } from '../../shared/hooks/store/useAppStore'; -import { useAuthStore } from '../../shared/hooks/store/useAuthStore'; -import { useUserProfileStore } from '../../shared/hooks/store/useUserProfileStore'; -import useApi from '../../shared/hooks/useApi'; -import { QueryKeys } from '../../shared/queries'; -import type { User } from '../../shared/types'; -import { invalidateMultipleQueries } from '../../shared/utils/invalidateMultipleQueries'; -import { DevicePageNavigationIcon } from './components/DevicesPageNavigationIcon'; -import { NavigationActivityLogPageIcon } from './components/icons/NavigationActivityLogPageIcon'; -import { NavigationDesktop } from './components/NavigationDesktop/NavigationDesktop'; -import { NavigationMobile } from './components/NavigationMobile/NavigationMobile'; -import { navigationExcludedRoutes } from './config'; -import { useNavigationStore } from './hooks/useNavigationStore'; -import type { NavigationItem, NavigationItems } from './types'; - -export const Navigation = () => { - const { pathname } = useLocation(); - const { LL } = useI18nContext(); - const [currentUser, resetAuthStore] = useAuthStore( - (state) => [state.user, state.resetState], - shallow, - ); - const setStore = useNavigationStore((state) => state.setState); - const networksPresent = useAppStore((state) => state.appInfo?.network_present); - const resetUserProfile = useUserProfileStore((state) => state.reset); - const queryClient = useQueryClient(); - const isAdmin = useAuthStore((s) => s.user?.is_admin ?? false); - - const { - auth: { logout }, - network: { getNetworks }, - } = useApi(); - - const { data: networks } = useQuery({ - queryKey: ['network'], - queryFn: getNetworks, - enabled: isAdmin, - }); - - const onlyOneNetworkPresent = useMemo(() => { - if (networks) { - return networks.length === 1; - } - return false; - }, [networks]); - - const { mutate: logOutMutation } = useMutation({ - mutationFn: logout, - onSuccess: () => { - resetAuthStore(); - resetUserProfile(); - setStore({ isOpen: false }); - }, - }); - - const settings = useAppStore((state) => state.settings); - const { breakpoint } = useBreakpoint(deviceBreakpoints); - - const navItems = useMemo((): NavigationItems => { - if (!currentUser) { - return { - middle: [], - bottom: [], - }; - } - - let overviewLink = '/admin/overview'; - - if (!networksPresent) { - overviewLink = '/admin/overview'; - } - - if (networks && onlyOneNetworkPresent) { - const networkId = networks[0].id; - overviewLink = `/admin/overview/${networkId}`; - } - - let bottom: NavigationItem[] = [ - { - title: LL.navigation.bar.settings(), - linkPath: '/admin/settings', - icon: , - adminOnly: true, - enabled: true, - }, - { - title: LL.navigation.bar.support(), - icon: , - linkPath: '/support', - adminOnly: false, - enabled: true, - className: 'support', - }, - ]; - let middle: NavigationItem[] = [ - { - title: LL.navigation.bar.overview(), - linkPath: overviewLink, - icon: , - adminOnly: true, - enabled: settings?.wireguard_enabled, - }, - { - title: LL.navigation.bar.users(), - linkPath: '/admin/users', - icon: , - adminOnly: true, - enabled: true, - }, - { - title: LL.navigation.bar.groups(), - linkPath: '/admin/groups', - icon: , - adminOnly: true, - enabled: true, - }, - { - title: LL.navigation.bar.acl(), - linkPath: '/admin/acl', - icon: ( - - - - ), - adminOnly: true, - enabled: true, - enterpriseOnly: true, - }, - { - title: LL.navigation.bar.devices(), - linkPath: '/admin/devices', - icon: , - adminOnly: true, - enabled: true, - }, - { - title: LL.navigation.bar.openId(), - linkPath: '/admin/openid', - icon: , - adminOnly: true, - enabled: settings?.openid_enabled, - }, - { - title: LL.navigation.bar.webhooks(), - linkPath: '/admin/webhooks', - icon: , - adminOnly: true, - enabled: settings?.webhooks_enabled, - }, - { - title: LL.navigation.bar.provisioners(), - linkPath: '/admin/provisioners', - icon: , - adminOnly: true, - enabled: settings?.worker_enabled, - }, - { - title: LL.navigation.bar.enrollment(), - linkPath: '/admin/enrollment', - icon: , - adminOnly: true, - enabled: true, - }, - { - title: LL.navigation.bar.activity(), - linkPath: '/activity', - icon: , - adminOnly: false, - enabled: true, - }, - { - title: LL.navigation.bar.myProfile(), - linkPath: `/me`, - icon: , - adminOnly: false, - enabled: true, - onClick: () => { - invalidateMultipleQueries(queryClient, [ - [QueryKeys.FETCH_ME], - [QueryKeys.FETCH_USER_PROFILE], - ]); - }, - }, - ]; - middle = filterNavItems(middle, currentUser); - bottom = filterNavItems(bottom, currentUser); - return { - middle, - bottom, - }; - }, [ - LL.navigation.bar, - currentUser, - networks, - networksPresent, - onlyOneNetworkPresent, - queryClient, - settings?.openid_enabled, - settings?.webhooks_enabled, - settings?.wireguard_enabled, - settings?.worker_enabled, - ]); - - const renderNav = useMemo(() => { - for (const path of navigationExcludedRoutes) { - if (pathname.includes(path)) { - return false; - } - } - return true; - }, [pathname]); - - if (!renderNav) return null; - - return ( - <> - {breakpoint === 'desktop' && ( - logOutMutation()} /> - )} - {breakpoint !== 'desktop' && ( - logOutMutation()} /> - )} - - ); -}; - -const filterNavItems = (items: NavigationItem[], currentUser: User): NavigationItem[] => - items - .filter((item) => item.enabled) - .filter((item) => { - if (item.adminOnly) { - return currentUser ? currentUser.is_admin : false; - } else { - return true; - } - }); diff --git a/web/src/components/Navigation/components/ApplicationVersion/ApplicationVersion.tsx b/web/src/components/Navigation/components/ApplicationVersion/ApplicationVersion.tsx deleted file mode 100644 index 462f6f668..000000000 --- a/web/src/components/Navigation/components/ApplicationVersion/ApplicationVersion.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import './style.scss'; - -import { useI18nContext } from '../../../../i18n/i18n-react'; -import { useAppStore } from '../../../../shared/hooks/store/useAppStore'; - -type Props = { - isOpen: boolean; -}; - -export const ApplicationVersion = ({ isOpen }: Props) => { - const version = useAppStore((store) => store.appInfo?.version); - const { LL } = useI18nContext(); - - return ( - - ); -}; diff --git a/web/src/components/Navigation/components/ApplicationVersion/style.scss b/web/src/components/Navigation/components/ApplicationVersion/style.scss deleted file mode 100644 index e0dbbd316..000000000 --- a/web/src/components/Navigation/components/ApplicationVersion/style.scss +++ /dev/null @@ -1,30 +0,0 @@ -@use '@scssutils' as *; - -.app-version { - display: flex; - flex-flow: column; - width: 100%; - align-items: center; - justify-content: center; - align-content: center; - box-sizing: border-box; - min-height: 60px; - max-width: 100%; - overflow: hidden; - - span, - a { - @include typography-legacy(10px, 13px, regular, var(--gray-light)); - display: inline-block; - } - - p { - @include typography-legacy(10px, 13px, regular, var(--gray-light)); - } - - a { - text-wrap: auto; - word-break: break-word; - text-align: center; - } -} diff --git a/web/src/components/Navigation/components/DevicesPageNavigationIcon.tsx b/web/src/components/Navigation/components/DevicesPageNavigationIcon.tsx deleted file mode 100644 index 5f16eb874..000000000 --- a/web/src/components/Navigation/components/DevicesPageNavigationIcon.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import { useId } from 'react'; - -export const DevicePageNavigationIcon = () => { - const id = useId(); - return ( - - - - - - - - - - - - ); -}; diff --git a/web/src/components/Navigation/components/NavigationBar/NavigationBar.tsx b/web/src/components/Navigation/components/NavigationBar/NavigationBar.tsx deleted file mode 100644 index 2ecee40ac..000000000 --- a/web/src/components/Navigation/components/NavigationBar/NavigationBar.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import './style.scss'; - -import classNames from 'classnames'; -import clsx from 'clsx'; -import { useMemo } from 'react'; - -import { useI18nContext } from '../../../../i18n/i18n-react'; -import SvgDefguardNavLogoCollapsed from '../../../../shared/components/svg/DefguardNavLogoCollapsed'; -import SvgIconNavLogout from '../../../../shared/components/svg/IconNavLogout'; -import { useAppStore } from '../../../../shared/hooks/store/useAppStore'; -import type { NavigationItems } from '../../types'; -import { ApplicationVersion } from '../ApplicationVersion/ApplicationVersion'; -import { NavigationLink } from '../NavigationLink/NavigationLink'; - -type Props = { - navItems: NavigationItems; - onLogout: () => void; - isOpen: boolean; -}; - -export const NavigationBar = ({ navItems, onLogout, isOpen }: Props) => { - const settings = useAppStore((state) => state.settings); - const { LL } = useI18nContext(); - - const cn = useMemo( - () => - classNames('nav-bar', { - open: isOpen, - }), - [isOpen], - ); - - return ( - - ); -}; diff --git a/web/src/components/Navigation/components/NavigationBar/style.scss b/web/src/components/Navigation/components/NavigationBar/style.scss deleted file mode 100644 index 233f358d8..000000000 --- a/web/src/components/Navigation/components/NavigationBar/style.scss +++ /dev/null @@ -1,123 +0,0 @@ -.nav-bar { - display: grid; - grid-template-rows: 108px 1fr 70px; - grid-template-columns: 1fr; - height: 100%; - width: 88px; - position: fixed; - inset: 0; - overflow-x: hidden; - overflow-y: auto; - max-height: 100%; - background-color: var(--white); - box-sizing: border-box; - border-right: 1px solid var(--gray-border); - - &::-webkit-scrollbar { - display: none; - } - - -ms-overflow-style: none; - scrollbar-width: none; - - .logo-container { - user-select: none; - height: 100%; - width: 100%; - display: flex; - flex-flow: row nowrap; - overflow: hidden; - align-items: center; - justify-content: center; - box-sizing: border-box; - border-bottom: 1px solid var(--gray-border); - } - - &.open { - width: 230px; - } - - .links { - border-bottom: 1px solid var(--gray-border); - width: 100%; - box-sizing: border-box; - display: grid; - grid-template-rows: 1fr auto; - - .middle, - .bottom { - width: 100%; - } - - .middle { - display: flex; - flex-flow: column; - height: 100%; - align-items: center; - justify-content: center; - } - - a, - .log-out { - user-select: none; - position: relative; - display: grid; - grid-template-rows: 1fr; - align-items: center; - justify-items: start; - width: 100%; - column-gap: 18px; - box-sizing: border-box; - padding: var(--spacing-xs) var(--spacing-s); - max-width: 100%; - overflow: hidden; - background-color: transparent; - border: 0px solid transparent; - text-decoration: none; - cursor: pointer; - color: var(--text-body-tertiary); - grid-template-columns: 24px 1fr; - - &.compact { - grid-template-columns: 1fr; - align-items: center; - justify-items: center; - } - - & > span { - @include typography(app-side-bar); - color: inherit; - overflow: hidden; - max-width: 100%; - display: inline-block; - word-break: normal; - text-wrap: wrap; - text-align: left; - } - - &:hover, - &.active { - color: var(--text-main); - - & > svg { - g, - path, - rect { - fill: var(--primary); - } - } - } - - & > .active-line { - content: ' '; - display: block; - height: 100%; - width: 2px; - background-color: var(--primary); - position: absolute; - right: 0; - top: 0; - } - } - } -} diff --git a/web/src/components/Navigation/components/NavigationDesktop/NavigationCollapse/NavigationCollapse.tsx b/web/src/components/Navigation/components/NavigationDesktop/NavigationCollapse/NavigationCollapse.tsx deleted file mode 100644 index 5eb8dcafe..000000000 --- a/web/src/components/Navigation/components/NavigationDesktop/NavigationCollapse/NavigationCollapse.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import './style.scss'; - -import classNames from 'classnames'; -import { motion, type TargetAndTransition } from 'motion/react'; -import { useMemo, useState } from 'react'; - -import { ColorsRGB } from '../../../../../shared/constants'; -import { useNavigationStore } from '../../../hooks/useNavigationStore'; - -export const NavigationCollapse = () => { - const [hovered, setHovered] = useState(false); - const isOpen = useNavigationStore((state) => state.isOpen); - const setState = useNavigationStore((state) => state.setState); - const getAnimate = useMemo((): TargetAndTransition => { - const res: TargetAndTransition = { - borderColor: ColorsRGB.GrayBorder, - backgroundColor: ColorsRGB.White, - }; - if (hovered) { - res.borderColor = ColorsRGB.Primary; - res.backgroundColor = ColorsRGB.Primary; - } - return res; - }, [hovered]); - - const cn = classNames('navigation-collapse', { - open: isOpen, - }); - - return ( - setState({ isOpen: !isOpen })} - animate={getAnimate} - initial={false} - onMouseEnter={() => setHovered(true)} - onMouseLeave={() => setHovered(false)} - > - - - - - - - - ); -}; diff --git a/web/src/components/Navigation/components/NavigationDesktop/NavigationCollapse/style.scss b/web/src/components/Navigation/components/NavigationDesktop/NavigationCollapse/style.scss deleted file mode 100644 index 266779740..000000000 --- a/web/src/components/Navigation/components/NavigationDesktop/NavigationCollapse/style.scss +++ /dev/null @@ -1,31 +0,0 @@ -@use '@scssutils' as *; - -.navigation-collapse { - width: 30px; - height: 30px; - box-sizing: border-box; - border: 1px solid var(--gray-border); - border-radius: 10px; - display: flex; - flex-flow: row nowrap; - overflow: hidden; - align-items: center; - justify-content: center; - cursor: pointer; - position: fixed; - top: 40px; - left: 73px; - background-color: var(--white); - z-index: 3; - padding: 0; - margin: 0; - - &.open { - left: 215px; - } - - &:hover { - border-color: var(--primary); - background-color: var(--primary); - } -} diff --git a/web/src/components/Navigation/components/NavigationDesktop/NavigationDesktop.tsx b/web/src/components/Navigation/components/NavigationDesktop/NavigationDesktop.tsx deleted file mode 100644 index 2509cd77d..000000000 --- a/web/src/components/Navigation/components/NavigationDesktop/NavigationDesktop.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { useNavigationStore } from '../../hooks/useNavigationStore'; -import type { NavigationItems } from '../../types'; -import { NavigationBar } from '../NavigationBar/NavigationBar'; -import { NavigationCollapse } from './NavigationCollapse/NavigationCollapse'; - -type Props = { - navItems: NavigationItems; - onLogout: () => void; -}; - -export const NavigationDesktop = ({ navItems, onLogout }: Props) => { - const isOpen = useNavigationStore((state) => state.isOpen); - return ( - <> - - - - ); -}; diff --git a/web/src/components/Navigation/components/NavigationLink/NavigationLink.tsx b/web/src/components/Navigation/components/NavigationLink/NavigationLink.tsx deleted file mode 100644 index 54f7e7189..000000000 --- a/web/src/components/Navigation/components/NavigationLink/NavigationLink.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import './style.scss'; - -import clsx from 'clsx'; -import { useMatch } from 'react-router'; -import { Link } from 'react-router-dom'; -import { shallow } from 'zustand/shallow'; - -import { useUpgradeLicenseModal } from '../../../../shared/components/Layout/UpgradeLicenseModal/store'; -import { UpgradeLicenseModalVariant } from '../../../../shared/components/Layout/UpgradeLicenseModal/types'; -import { useAppStore } from '../../../../shared/hooks/store/useAppStore'; -import { useNavigationStore } from '../../hooks/useNavigationStore'; -import type { NavigationItem } from '../../types'; - -interface NavigationLinkProps { - item: NavigationItem; - callback?: () => void; -} - -export const NavigationLink = ({ item, callback }: NavigationLinkProps) => { - const isOpen = useNavigationStore((s) => s.isOpen); - const openUpgradeLicenseModal = useUpgradeLicenseModal((s) => s.open, shallow); - const enterpriseEnabled = useAppStore((s) => s.appInfo?.license_info.enterprise); - const match = useMatch(item.linkPath); - - return ( - { - if (item.enterpriseOnly && !enterpriseEnabled) { - event.preventDefault(); - openUpgradeLicenseModal({ - modalVariant: UpgradeLicenseModalVariant.ENTERPRISE_NOTICE, - }); - } - if (callback) { - callback(); - } - if (item.onClick) { - item.onClick(); - } - }} - > - {item.icon} - {isOpen && {item.title}} - {match ?
: null} - - ); -}; diff --git a/web/src/components/Navigation/components/NavigationLink/style.scss b/web/src/components/Navigation/components/NavigationLink/style.scss deleted file mode 100644 index cd1d84e41..000000000 --- a/web/src/components/Navigation/components/NavigationLink/style.scss +++ /dev/null @@ -1,32 +0,0 @@ -.navigation-link { - svg { - rect { - fill: var(--surface-icon-primary); - } - - path { - fill: var(--surface-icon-primary); - } - - circle { - stroke: var(--surface-icon-primary); - } - } - - &:hover, - &.active { - svg { - rect { - fill: var(--surface-main-primary); - } - - path { - fill: var(--surface-main-primary); - } - - circle { - stroke: var(--surface-main-primary); - } - } - } -} diff --git a/web/src/components/Navigation/components/NavigationMobile/MobileNavModal/MobileNavModal.tsx b/web/src/components/Navigation/components/NavigationMobile/MobileNavModal/MobileNavModal.tsx deleted file mode 100644 index 877f27b8c..000000000 --- a/web/src/components/Navigation/components/NavigationMobile/MobileNavModal/MobileNavModal.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import './style.scss'; - -import SvgIconHamburgerClose from '../../../../../shared/components/svg/IconHamburgerClose'; -import { Modal } from '../../../../../shared/defguard-ui/components/Layout/modals/Modal/Modal'; -import { useNavigationStore } from '../../../hooks/useNavigationStore'; -import type { NavigationItems } from '../../../types'; -import { NavigationBar } from '../../NavigationBar/NavigationBar'; - -interface Props { - navItems: NavigationItems; - onLogout: () => void; -} - -export const MobileNavModal = ({ navItems, onLogout }: Props) => { - const setStore = useNavigationStore((state) => state.setState); - const isOpen = useNavigationStore((state) => state.isOpen); - return ( - setStore({ isOpen: val })} - backdrop - > - - - - ); -}; diff --git a/web/src/components/Navigation/components/NavigationMobile/MobileNavModal/style.scss b/web/src/components/Navigation/components/NavigationMobile/MobileNavModal/style.scss deleted file mode 100644 index f92f93ebe..000000000 --- a/web/src/components/Navigation/components/NavigationMobile/MobileNavModal/style.scss +++ /dev/null @@ -1,37 +0,0 @@ -.modal-root { - & > .modal-wrap { - & > .modal { - &.mobile-nav { - height: 100%; - padding: 0; - - .modal-content { - width: 230px; - position: relative; - border-radius: 0; - display: flex; - flex-direction: column; - background-color: transparent; - - .close-mobile-nav { - position: fixed; - left: 240px; - top: 15px; - height: 40px; - width: 40px; - border-radius: 1rem; - box-shadow: none; - border: 0 solid transparent; - background-color: var(--white); - display: flex; - flex-direction: column; - align-content: center; - align-items: center; - justify-content: center; - cursor: pointer; - } - } - } - } - } -} diff --git a/web/src/components/Navigation/components/NavigationMobile/NavigationMobile.tsx b/web/src/components/Navigation/components/NavigationMobile/NavigationMobile.tsx deleted file mode 100644 index 07bd0e0f9..000000000 --- a/web/src/components/Navigation/components/NavigationMobile/NavigationMobile.tsx +++ /dev/null @@ -1,106 +0,0 @@ -import './style.scss'; - -import { useMemo } from 'react'; -import { useLocation } from 'react-router'; - -import { useI18nContext } from '../../../../i18n/i18n-react'; -import SvgDefguardNavLogoCollapsed from '../../../../shared/components/svg/DefguardNavLogoCollapsed'; -import SvgIconNavHamburger from '../../../../shared/components/svg/IconNavHamburger'; -import { useNavigationStore } from '../../hooks/useNavigationStore'; -import type { NavigationItems } from '../../types'; -import { MobileNavModal } from './MobileNavModal/MobileNavModal'; - -type Props = { - onLogout: () => void; - navItems: NavigationItems; -}; - -export const NavigationMobile = ({ navItems, onLogout }: Props) => { - const { LL } = useI18nContext(); - const { pathname } = useLocation(); - const setStore = useNavigationStore((state) => state.setState); - - const titleMap = useMemo( - () => [ - { - path: '/admin/settings', - title: LL.navigation.mobileTitles.settings(), - }, - { - path: '/admin/users', - title: LL.navigation.mobileTitles.users(), - }, - { - path: '/admin/user', - title: LL.navigation.mobileTitles.user(), - }, - { - path: '/admin/me', - title: LL.navigation.mobileTitles.user(), - }, - { - path: '/admin/provisioners', - title: LL.navigation.mobileTitles.provisioners(), - }, - { - path: '/admin/webhooks', - title: LL.navigation.mobileTitles.webhooks(), - }, - { - path: '/admin/wizard', - title: LL.navigation.mobileTitles.wizard(), - }, - { - path: '/admin/activity', - title: LL.navigation.mobileTitles.activity(), - }, - { - path: '/admin/network', - title: LL.navigation.mobileTitles.networkSettings(), - }, - { - path: '/admin/overview', - title: LL.navigation.mobileTitles.overview(), - }, - { - path: '/admin/enrollment', - title: LL.navigation.mobileTitles.enrollment(), - }, - { - path: '/admin/openid', - title: LL.navigation.mobileTitles.openId(), - }, - { - path: '/admin/groups', - title: LL.navigation.mobileTitles.groups(), - }, - { - path: '/admin/devices', - title: LL.navigation.mobileTitles.devices(), - }, - ], - [LL.navigation.mobileTitles], - ); - - const getPageTitle = useMemo(() => { - for (const item of titleMap) { - if (pathname.includes(item.path)) { - return item.title; - } - } - return ''; - }, [pathname, titleMap]); - - return ( - <> - - - - ); -}; diff --git a/web/src/components/Navigation/components/NavigationMobile/style.scss b/web/src/components/Navigation/components/NavigationMobile/style.scss deleted file mode 100644 index 7ee817068..000000000 --- a/web/src/components/Navigation/components/NavigationMobile/style.scss +++ /dev/null @@ -1,39 +0,0 @@ -.nav-mobile { - height: 60px; - position: fixed; - inset: 0; - width: 100%; - box-sizing: border-box; - padding: 0 20px; - display: grid; - grid-template-rows: 1fr; - grid-template-columns: 40px 1fr 40px; - align-items: center; - justify-items: center; - background-color: var(--white); - column-gap: 10px; - - .page-title { - width: 100%; - max-width: 100%; - text-align: center; - user-select: none; - - @include text-overflow-dots; - @include typography-legacy(20px, 1.2, semiBold, var(--text-main), 'Poppins'); - } - - .hamburger { - width: 40px; - height: 40px; - display: flex; - flex-flow: row nowrap; - align-items: center; - justify-content: center; - padding: 0; - margin: 0; - background-color: transparent; - border: 0 solid transparent; - cursor: pointer; - } -} diff --git a/web/src/components/Navigation/components/icons/NavigationActivityLogPageIcon.tsx b/web/src/components/Navigation/components/icons/NavigationActivityLogPageIcon.tsx deleted file mode 100644 index 3cc0b40f3..000000000 --- a/web/src/components/Navigation/components/icons/NavigationActivityLogPageIcon.tsx +++ /dev/null @@ -1,50 +0,0 @@ -export const NavigationActivityLogPageIcon = () => { - return ( - - - - - - - - - - - - ); -}; diff --git a/web/src/components/Navigation/config.ts b/web/src/components/Navigation/config.ts deleted file mode 100644 index a4d02754b..000000000 --- a/web/src/components/Navigation/config.ts +++ /dev/null @@ -1,6 +0,0 @@ -export const navigationExcludedRoutes: string[] = [ - '/auth', - '/authorize', - '/redirect', - '/consent', -]; diff --git a/web/src/components/Navigation/hooks/useNavigationStore.ts b/web/src/components/Navigation/hooks/useNavigationStore.ts deleted file mode 100644 index 2ac18853e..000000000 --- a/web/src/components/Navigation/hooks/useNavigationStore.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { pick } from 'lodash-es'; -import { persist } from 'zustand/middleware'; -import { createWithEqualityFn } from 'zustand/traditional'; - -const defaultState: StoreValues = { - isOpen: false, -}; - -export const useNavigationStore = createWithEqualityFn()( - persist( - (set) => ({ - ...defaultState, - setState: (values) => set((old) => ({ ...old, ...values })), - reset: () => set(defaultState), - }), - { - version: 1.5, - name: 'navigation-store', - partialize: (state) => pick(state, ['isOpen']), - }, - ), - Object.is, -); - -type Store = StoreValues & StoreMethods; - -type StoreValues = { - isOpen: boolean; -}; - -type StoreMethods = { - setState: (values: Partial) => void; - reset: () => void; -}; diff --git a/web/src/components/Navigation/style.scss b/web/src/components/Navigation/style.scss deleted file mode 100644 index 007442690..000000000 --- a/web/src/components/Navigation/style.scss +++ /dev/null @@ -1 +0,0 @@ -@use '@scssutils' as *; diff --git a/web/src/components/Navigation/types.ts b/web/src/components/Navigation/types.ts deleted file mode 100644 index 601945d5d..000000000 --- a/web/src/components/Navigation/types.ts +++ /dev/null @@ -1,20 +0,0 @@ -export interface NavigationItem { - title: string; - linkPath: string; - icon?: React.ReactNode; - adminOnly?: boolean; - enabled: boolean | undefined; - onClick?: () => void; - className?: string; - enterpriseOnly?: boolean; -} - -export type NavigationTitleMapItem = { - path: string; - title: string; -}; - -export type NavigationItems = { - middle: NavigationItem[]; - bottom: NavigationItem[]; -}; diff --git a/web/src/gif.d.ts b/web/src/gif.d.ts deleted file mode 100644 index 3b59542ad..000000000 --- a/web/src/gif.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -declare module '*.gif' { - const val: string; - export default val; -} diff --git a/web/src/i18n/en/index.ts b/web/src/i18n/en/index.ts deleted file mode 100644 index b8a7a4472..000000000 --- a/web/src/i18n/en/index.ts +++ /dev/null @@ -1,2759 +0,0 @@ -import type { BaseTranslation } from '../i18n-types'; - -const en: BaseTranslation = { - common: { - conditions: { - or: 'or', - and: 'and', - equal: 'equal', - }, - controls: { - timeRange: 'Time range', - addNew: 'Add new', - add: 'Add', - accept: 'Accept', - next: 'Next', - back: 'Back', - cancel: 'Cancel', - confirm: 'Confirm', - submit: 'Submit', - close: 'Close', - select: 'Select', - finish: 'Finish', - saveChanges: 'Save changes', - save: 'Save', - RestoreDefault: 'Restore default', - delete: 'Delete', - rename: 'Rename', - copy: 'Copy', - edit: 'Edit', - dismiss: 'Dismiss', - show: 'Show', - enable: 'Enable', - enabled: 'Enabled', - disable: 'Disable', - disabled: 'Disabled', - selectAll: 'Select all', - clear: 'Clear', - clearAll: 'Clear all', - filter: 'Filter', - filters: 'Filters', - }, - key: 'Key', - name: 'Name', - noData: 'No data', - unavailable: 'Unavailable', - notSet: 'Not set', - search: 'Search', - time: 'Time', - from: 'From', - until: 'Until', - }, - messages: { - error: 'Error has occurred.', - success: 'Operation succeeded', - errorVersion: 'Failed to get application version.', - insecureContext: 'Context is not secure.', - details: 'Details:', - clipboard: { - error: 'Clipboard is not accessible.', - success: 'Content copied to clipboard.', - }, - }, - modals: { - outdatedComponentsModal: { - title: 'Version mismatch', - subtitle: 'Defguard detected unsupported version in some components.', - content: { - title: 'Incompatible components:', - unknownVersion: 'Unknown version', - unknownHostname: 'Unknown hostname', - }, - }, - upgradeLicenseModal: { - enterprise: { - title: 'Upgrade to Enterprise', - //md - subTitle: `This functionality is an **enterprise feature** and you've exceeded the user, device or network limits to use it. In order to use this feature, purchase an enterprise license or upgrade your existing one.`, - }, - limit: { - title: 'Upgrade', - //md - subTitle: ` - You have **reached the limit** of this functionality. To **[ manage more locations/users/devices ]** purchase of the Enterprise license is required. - `, - }, - //md - content: ` -You can find out more about features like: -- Real time and automatic client synchronization -- External SSO -- Controlling VPN clients behavior - -Full enterprise feature list: [https://docs.defguard.net/enterprise/enterprise-features](https://docs.defguard.net/enterprise/enterprise-features)
-Licensing information: [https://docs.defguard.net/enterprise/license](https://docs.defguard.net/enterprise/license) - `, - controls: { - cancel: 'Maybe later', - confirm: 'See all Enterprise plans', - }, - }, - standaloneDeviceEnrollmentModal: { - title: 'Network device token', - toasters: { - error: 'Token generation failed.', - }, - }, - standaloneDeviceConfigModal: { - title: 'Network device config', - cardTitle: 'Config', - toasters: { - getConfig: { - error: 'Failed to get device config.', - }, - }, - }, - editStandaloneModal: { - title: 'Edit network device', - toasts: { - success: 'Device modified', - failure: 'Modifying the device failed', - }, - }, - deleteStandaloneDevice: { - title: 'Delete network device', - content: 'Device {name: string} will be deleted.', - messages: { - success: 'Device deleted', - error: 'Failed to remove device.', - }, - }, - addStandaloneDevice: { - toasts: { - deviceCreated: 'Device added', - creationFailed: 'Device could not be added.', - }, - infoBox: { - setup: - 'Here you can add definitions or generate configurations for devices that can connect to your VPN. Only locations without Multi-Factor Authentication are available here, as MFA is only supported in Defguard Desktop Client for now.', - }, - form: { - submit: 'Add Device', - labels: { - deviceName: 'Device Name', - location: 'Location', - assignedAddress: 'Assigned IP Address', - description: 'Description', - generation: { - auto: 'Generate key pair', - manual: 'Use my own public key', - }, - publicKey: 'Provide Your Public Key', - }, - }, - steps: { - method: { - title: 'Choose a preferred method', - cards: { - cli: { - title: 'Defguard Command Line Client', - subtitle: - 'When using defguard-cli your device will be automatically configured.', - docs: 'Defguard CLI download and documentation', - }, - manual: { - title: 'Manual WireGuard Client', - subtitle: - 'If your device does not support our CLI binaries you can always generate a WireGuard configuration file and configure it manually - but any updates to the VPN location configuration will require manual changes in device configuration.', - }, - }, - }, - manual: { - title: 'Add new VPN device using WireGuard Client', - finish: { - messageTop: - 'Download the provided configuration file to your device and import it into your VPN client to complete the setup.', - ctaInstruction: - "Use provided configuration file below by scanning QR code or importing it as file on your device's WireGuard app.", - // MD - warningMessage: ` - Please remember that Defguard **doesn't store private keys**. We will securely generate the public and private key pair in your browser, but only store the public key in Defguard database. Please download the configuration generated with the private key for the device, as it will not be accessible later. - `, - actionCard: { - title: 'Config', - }, - }, - }, - cli: { - title: 'Add device using Defguard Command Line Client', - finish: { - topMessage: - 'First download Defguard command line client binary and install it on your server.', - downloadButton: 'Download Defguard CLI Client', - commandCopy: 'Copy and paste this command in your terminal on the device', - }, - setup: { - stepMessage: - 'Here you can add definitions or generate configurations for devices that can connect to your VPN. Only locations without Multi-Factor Authentication are available here, as MFA is only supported in Defguard Desktop Client for now.', - form: { - submit: 'Add Device', - }, - }, - }, - }, - }, - updatesNotificationToaster: { - title: 'New version available {version: string}', - controls: { - more: "See what's new", - }, - }, - enterpriseUpgradeToaster: { - title: `You've reached the enterprise functionality limit.`, - message: `You've exceeded the limit of your current Defguard plan and the enterprise - features will be disabled. Purchase an enterprise license or upgrade your - existing one to continue using these features.`, - link: 'See all enterprise plans', - }, - updatesNotification: { - header: { - title: 'Update Available', - newVersion: 'new version {version: string}', - criticalBadge: 'critical update', - }, - controls: { - visitRelease: 'Visit release page', - }, - }, - addGroup: { - title: 'Add group', - selectAll: 'Select all users', - groupName: 'Group name', - searchPlaceholder: 'Filter/Search', - submit: 'Create group', - groupSettings: 'Group settings', - adminGroup: 'Admin group', - }, - editGroup: { - title: 'Edit group', - selectAll: 'Select all users', - groupName: 'Group name', - searchPlaceholder: 'Filter/Search', - submit: 'Update group', - groupSettings: 'Group settings', - adminGroup: 'Admin group', - }, - deleteGroup: { - title: 'Delete group {name:string}', - subTitle: 'This action will permanently delete this group.', - locationListHeader: 'This group is currently assigned to following VPN Locations:', - locationListFooter: `If this is the only allowed group for a given location, the location will become accessible to all users.`, - submit: 'Delete group', - cancel: 'Cancel', - }, - deviceConfig: { - title: 'Device VPN configurations', - }, - changePasswordSelf: { - title: 'Change password', - messages: { - success: 'Password has been changed', - error: 'Failed to changed password', - }, - form: { - labels: { - newPassword: 'New password', - oldPassword: 'Current password', - repeat: 'Confirm new password', - }, - }, - controls: { - submit: 'Change password', - cancel: 'Cancel', - }, - }, - disableMfa: { - title: 'Disable MFA', - message: 'Do you want to disable MFA for user {username: string}?', - messages: { - success: 'MFA for user {username: string} has been disabled', - error: 'Failed to disable MFA for user {username: string}', - }, - controls: { - submit: 'Disable MFA', - cancel: 'Cancel', - }, - }, - startEnrollment: { - title: 'Start enrollment', - desktopTitle: 'Desktop activation', - messages: { - success: 'User enrollment started', - successDesktop: 'Desktop configuration started', - error: 'Failed to start user enrollment', - errorDesktop: 'Failed to start desktop activation', - }, - messageBox: { - clientForm: - 'You can share the following URL and token with the user to configure their Defguard desktop or mobile client.', - clientQr: - 'You can share this QR code for easy Defguard mobile client configuration.', - }, - form: { - email: { - label: 'Email', - }, - mode: { - options: { - email: 'Send token by email', - manual: 'Deliver token yourself', - }, - }, - submit: 'Start enrollment', - submitDesktop: 'Activate desktop', - smtpDisabled: 'Configure SMTP to send token by email. Go to Settings -> SMTP.', - }, - tokenCard: { - title: 'Activation token', - }, - urlCard: { - title: 'Defguard Instance URL', - }, - }, - deleteNetwork: { - title: 'Delete {name:string} location', - subTitle: 'This action will permanently delete this location.', - submit: 'Delete location', - cancel: 'Cancel', - }, - changeWebhook: { - messages: { - success: 'Webhook changed.', - }, - }, - manageWebAuthNKeys: { - title: 'Security keys', - messages: { - deleted: 'WebAuthN key has been deleted.', - duplicateKeyError: 'Key is already registered', - }, - infoMessage: ` -

- Security keys can be used as your second factor of authentication - instead of a verification code. Learn more about configuring a - security key. -

-`, - form: { - messages: { - success: 'Security key added.', - }, - fields: { - name: { - label: 'New key name', - }, - }, - controls: { - submit: 'Add new Key', - }, - }, - }, - recoveryCodes: { - title: 'Recovery codes', - submit: 'I have saved my codes', - messages: { - copied: 'Codes copied.', - }, - infoMessage: ` -

- Treat your recovery codes with the same level of attention as you - would your password! We recommend saving them with a password manager - such as Lastpass, bitwarden or Keeper. -

-`, - }, - registerTOTP: { - title: 'Authenticator App Setup', - infoMessage: ` -

- To setup your MFA, scan this QR code with your authenticator app, then - enter the code in the field below: -

-`, - messages: { - totpCopied: 'TOTP path copied.', - success: 'TOTP Enabled', - }, - copyPath: 'Copy TOTP path', - form: { - fields: { - code: { - label: 'Authenticator code', - error: 'Code is invalid', - }, - }, - controls: { - submit: 'Verify code', - }, - }, - }, - registerEmailMFA: { - title: 'Email MFA Setup', - infoMessage: ` -

- To setup your MFA enter the code that was sent to your account email: {email: string} -

-`, - messages: { - success: 'Email MFA Enabled', - resend: 'Verification code resent', - }, - form: { - fields: { - code: { - label: 'Email code', - error: 'Code is invalid', - }, - }, - controls: { - submit: 'Verify code', - resend: 'Resend email', - }, - }, - }, - editDevice: { - title: 'Edit device', - messages: { - success: 'Device has been updated.', - }, - form: { - fields: { - name: { - label: 'Device Name', - }, - publicKey: { - label: 'Device Public Key (WireGuard)', - }, - }, - controls: { - submit: 'Edit device', - }, - }, - }, - deleteDevice: { - title: 'Delete device', - message: 'Do you want to delete {deviceName} device ?', - submit: 'Delete device', - messages: { - success: 'Device has been deleted.', - }, - }, - keyDetails: { - title: 'YubiKey details', - downloadAll: 'Download all keys', - }, - deleteUser: { - title: 'Delete account', - controls: { - submit: 'Delete account', - }, - message: 'Do you want to delete {username: string} account permanently?', - messages: { - success: '{username: string} deleted.', - }, - }, - disableUser: { - title: 'Disable account', - controls: { - submit: 'Disable account', - }, - message: 'Do you want to disable {username: string} account?', - messages: { - success: '{username: string} disabled.', - }, - }, - enableUser: { - title: 'Enable account', - controls: { - submit: 'Enable account', - }, - message: 'Do you want to enable {username: string} account?', - messages: { - success: '{username: string} enabled.', - }, - }, - deleteProvisioner: { - title: 'Delete provisioner', - controls: { - submit: 'Delete provisioner', - }, - message: 'Do you want to delete {id: string} provisioner?', - messages: { - success: '{provisioner: string} deleted.', - }, - }, - changeUserPassword: { - messages: { - success: 'Password changed.', - }, - title: 'Change user password', - form: { - controls: { - submit: 'Save new password', - }, - fields: { - newPassword: { - label: 'New password', - }, - confirmPassword: { - label: 'Repeat password', - }, - }, - }, - }, - provisionKeys: { - title: 'Yubikey provisioning:', - warning: - 'Please be advised that this operation wll wipe openpgp application on yubikey and reconfigure it.', - infoBox: `The selected provisioner must have a clean YubiKey - plugged in be provisioned. To clean a used YubiKey - gpg --card-edit before provisioning.`, - selectionLabel: 'Select one of the following provisioners to provision a YubiKey:', - noData: { - workers: 'No workers found, waiting...', - }, - controls: { - submit: 'Provision YubiKey', - }, - messages: { - success: 'Keys provisioned', - errorStatus: 'Error while getting worker status.', - }, - }, - addUser: { - title: 'Add new user', - messages: { - userAdded: 'User added', - }, - form: { - submit: 'Add user', - error: { - emailReserved: 'Email already taken', - }, - fields: { - username: { - placeholder: 'login', - label: 'Login', - }, - password: { - placeholder: 'Password', - label: 'Password', - }, - email: { - placeholder: 'User e-mail', - label: 'User e-mail', - }, - firstName: { - placeholder: 'First name', - label: 'First name', - }, - lastName: { - placeholder: 'Last name', - label: 'Last name', - }, - phone: { - placeholder: 'Phone', - label: 'Phone', - }, - enableEnrollment: { - label: 'Use user self-enrollment process', - link: 'more information here', - }, - }, - }, - }, - webhookModal: { - title: { - addWebhook: 'Add webhook.', - editWebhook: 'Edit webhook', - }, - messages: { - clientIdCopy: 'Client ID copied.', - clientSecretCopy: 'Client secret copied.', - }, - form: { - triggers: 'Trigger events:', - messages: { - successAdd: 'Webhook created.', - successModify: 'Webhook modified.', - }, - error: { - urlRequired: 'URL is required.', - validUrl: 'Must be a valid URL.', - scopeValidation: 'Must have at least one trigger.', - tokenRequired: 'Token is required.', - }, - fields: { - description: { - label: 'Description', - placeholder: 'Webhook to create gmail account on new user', - }, - token: { - label: 'Secret token', - placeholder: 'Authorization token', - }, - url: { - label: 'Webhook URL', - placeholder: 'https://example.com/webhook', - }, - userCreated: { - label: 'New user Created', - }, - userDeleted: { - label: 'User deleted', - }, - userModified: { - label: 'User modified', - }, - hwkeyProvision: { - label: 'User Yubikey provision', - }, - }, - }, - }, - deleteWebhook: { - title: 'Delete webhook', - message: 'Do you want to delete {name: string} webhook ?', - submit: 'Delete', - messages: { - success: 'Webhook deleted.', - }, - }, - }, - addDevicePage: { - title: 'Add device', - helpers: { - setupOpt: `You can add a device using this wizard. Opt for our native application "defguard" or any other WireGuard client. If you're unsure, we recommend using defguard for simplicity.`, - client: `Please download defguard desktop client here and then follow this guide.`, - }, - messages: { - deviceAdded: 'Device added', - }, - steps: { - setupMethod: { - title: 'Choose Your Connection Method', - message: - "You can add a device using this wizard. To proceed, you'll need to install the defguard Client on the device you're adding. You can also use any standard WireGuard® client, but for the best experience and ease of setup, we recommend using our native defguard Client.", - methods: { - client: { - title: 'Remote Device Activation', - description: - 'Use the Defguard Client to set up your device. Easily configure it with a single token or by scanning a QR code.', - }, - wg: { - title: 'Manual WireGuard Client', - description: - 'For advanced users, get a unique config via download or QR code. Download any WireGuard® client and take control of your VPN setup.', - }, - }, - }, - client: { - title: 'Client Activation', - desktopDeepLinkHelp: - 'If you want to configure your Defguard desktop client, please install the client (links below), open it and just press the One-Click Desktop Configuration button', - //md - message: - 'If you are having trouble with the One-Click configuration you can do it manually by clicking *Add Instance* in the desktop client, and entering the following URL and Token:', - qrDescription: - "Scan the QR code with your installed Defguard app. If you haven't installed it yet, use your device's app store or the link below.", - qrHelp: - 'If you want to configure your Mobile Defguard Client, please just scan this QR code in the app:', - desktopDownload: 'Download for Desktop', - tokenCopy: 'Token copied to clipboard', - tokenFailure: 'Failed to prepare client setup', - labels: { - mergedToken: 'Defguard Instance Token (new)', - token: 'Authentication Token', - url: 'URL', - }, - }, - configDevice: { - title: 'Configure device', - messages: { - copyConfig: 'Configuration has been copied to the clipboard', - }, - helpers: { - warningAutoMode: ` -

- Please be advised that you have to download the configuration now, - since we do not store your private key. After this - page is closed, you will not be able to get your - full configuration file (with private keys, only blank template). -

-`, - warningManualMode: ` -

- Please be advised that configuration provided here does not include private key and uses public key to fill it's place you will need to replace it on your own for configuration to work properly. -

-`, - warningNoNetworks: "You don't have access to any network.", - qrHelper: ` -

- You can setup your device faster with wireguard application by scanning this QR code. -

`, - }, - qrInfo: - 'Use provided configuration file below by scanning QR Code or importing it as file on your devices WireGuard instance.', - inputNameLabel: 'Device Name', - qrLabel: 'WireGuard Config File', - }, - setupDevice: { - title: 'Create VPN device', - infoMessage: ` -

- You need to configure WireGuard® VPN on your device, please visit  - documentation if you don't know how to do it. -

-`, - options: { - auto: 'Generate key pair', - manual: 'Use my own public key', - }, - form: { - fields: { - name: { - label: 'Device Name', - }, - publicKey: { - label: 'Provide Your Public Key', - }, - }, - errors: { - name: { - duplicatedName: 'Device with this name already exists', - }, - }, - }, - }, - copyToken: { - title: 'Client activation', - tokenCardTitle: 'Activation token', - urlCardTitle: 'Defguard Instance URL', - }, - }, - }, - userPage: { - title: { - view: 'User Profile', - edit: 'Edit User Profile', - }, - messages: { - editSuccess: 'User updated.', - failedToFetchUserData: 'Could not get user information.', - passwordResetEmailSent: 'Password reset email has been sent.', - }, - userDetails: { - header: 'Profile Details', - messages: { - deleteApp: 'App and all tokens deleted.', - }, - warningModals: { - title: 'Warning', - content: { - usernameChange: `Changing the username has a significant impact on services the user has logged into using Defguard. After changing it, the user may lose access to applications (since they will not recognize them). Are you sure you want to proceed?`, - emailChange: `If you are using external OpenID Connect (OIDC) providers to authenticate users, changing a user's email address may have a significant impact on their ability to log in to Defguard. Are you sure you want to proceed?`, - }, - buttons: { - proceed: 'Proceed', - cancel: 'Cancel', - }, - }, - fields: { - username: { - label: 'Username', - }, - firstName: { - label: 'First name', - }, - lastName: { - label: 'Last name', - }, - phone: { - label: 'Phone number', - }, - email: { - label: 'E-mail', - }, - status: { - label: 'Status', - active: 'Active', - disabled: 'Disabled', - }, - groups: { - label: 'User groups', - noData: 'No groups', - }, - apps: { - label: 'Authorized apps', - noData: 'No authorized apps', - }, - }, - }, - userAuthInfo: { - header: 'Password and authentication', - password: { - header: 'Password settings', - changePassword: 'Change password', - ldap_change_heading: '{ldapName:string} password update required', - ldap_change_message: - "Defguard doesn't store your password in plain text, so we can’t retrieve it for automatic synchronization with your {ldapName:string} credentials. To enable {ldapName:string} login to other services, please update your Defguard password for your {ldapName:string} password to be set — you can re-enter your current password if you wish. This step is necessary to ensure consistent and secure authentication across both systems.", - }, - recovery: { - header: 'Recovery options', - codes: { - label: 'Recovery Codes', - viewed: 'Viewed', - }, - }, - mfa: { - header: 'Two-factor methods', - edit: { - disable: 'Disable MFA', - }, - messages: { - mfaDisabled: 'MFA disabled.', - OTPDisabled: 'One time password disabled.', - EmailMFADisabled: 'Email MFA disabled.', - changeMFAMethod: 'MFA method changed', - }, - securityKey: { - singular: 'security key', - plural: 'security keys', - }, - default: 'default', - enabled: 'Enabled', - disabled: 'Disabled', - labels: { - totp: 'Time based one time passwords', - email: 'Email', - webauth: 'Security keys', - }, - editMode: { - enable: 'Enable', - disable: 'Disable', - makeDefault: 'Make default', - webauth: { - manage: 'Manage security keys', - }, - }, - }, - }, - controls: { - editButton: 'Edit profile', - deleteAccount: 'Delete account', - }, - devices: { - header: 'User devices', - addDevice: { - web: 'Add new device', - desktop: 'Add this device', - }, - card: { - labels: { - publicIP: 'Public IP', - connectedThrough: 'Connected through', - connectionDate: 'Connected date', - lastLocation: 'Last connected from', - lastConnected: 'Last connected', - assignedIp: 'Assigned IP', - active: 'active', - noData: 'Never connected', - }, - edit: { - edit: 'Edit device', - delete: 'Delete device', - showConfigurations: 'Show configuration', - }, - }, - }, - yubiKey: { - header: 'User YubiKey', - provision: 'Provision a YubiKey', - keys: { - pgp: 'PGP key', - ssh: 'SSH key', - }, - noLicense: { - moduleName: 'YubiKey module', - line1: 'This is enterprise module for YubiKey', - line2: 'management and provisioning.', - }, - }, - authenticationKeys: { - header: 'User Authentication Keys', - addKey: 'Add new Key', - keysList: { - common: { - rename: 'Rename', - key: 'Key', - download: 'Download', - copy: 'Copy', - serialNumber: 'Serial Number', - delete: 'Delete', - }, - }, - deleteModal: { - title: 'Delete Authentication Key', - confirmMessage: 'Key {name: string} will be deleted permanently.', - }, - addModal: { - header: 'Add new Authentication Key', - keyType: 'Key Type', - keyForm: { - placeholders: { - title: 'Key Name', - key: { - ssh: 'Begins with ssh-rsa, ecdsa-sha2-nistp256, ...', - gpg: 'Begins with -----BEGIN PGP PUBLIC KEY BLOCK-----', - }, - }, - labels: { - title: 'Name', - key: 'Key', - }, - submit: 'Add {name: string} key', - }, - yubikeyForm: { - selectWorker: { - info: 'Please be advised that this operation will wipe openpgp application on YubiKey and reconfigure it.', - selectLabel: 'Select on of the following provisioners to provision a YubiKey', - noData: 'No workers are registered right now.', - available: 'Available', - unavailable: 'Unavailable', - }, - provisioning: { - inProgress: 'Provisioning in progress, please wait.', - error: 'Provisioning failed !', - success: 'Yubikey provisioned successfully', - }, - submit: 'Provision Yubikey', - }, - messages: { - keyAdded: 'Key added.', - keyExists: 'Key has already been added.', - unsupportedKeyFormat: 'Unsupported key format.', - genericError: 'Could not add the key. Please try again later.', - }, - }, - }, - apiTokens: { - header: 'User API Tokens', - addToken: 'Add new API Token', - tokensList: { - common: { - rename: 'Rename', - token: 'Token', - copy: 'Copy', - delete: 'Delete', - createdAt: 'Created at', - }, - }, - deleteModal: { - title: 'Delete API Token', - confirmMessage: 'API token {name: string} will be deleted permanently.', - }, - addModal: { - header: 'Add new API Token', - tokenForm: { - placeholders: { - name: 'API Token Name', - }, - labels: { - name: 'Name', - }, - submit: 'Add API token', - }, - copyToken: { - warningMessage: - "Please copy the API token below now. You won't be able to see it again.", - header: 'Copy new API Token', - }, - messages: { - tokenAdded: 'API token added.', - genericError: 'Could not add API token. Please try again later.', - }, - }, - }, - }, - usersOverview: { - pageTitle: 'Users', - grid: { - usersTitle: 'Connected Users', - devicesTitle: 'Connected Network Devices', - }, - search: { - placeholder: 'Find users', - }, - filterLabels: { - all: 'All users', - admin: 'Admins only', - users: 'Users only', - }, - usersCount: 'All users', - addNewUser: 'Add new', - list: { - headers: { - name: 'User name', - username: 'Login', - phone: 'Phone', - actions: 'Actions', - }, - editButton: { - changePassword: 'Change password', - edit: 'Edit account', - addYubikey: 'Add YubiKey', - addSSH: 'Add SSH Key', - addGPG: 'Add GPG Key', - delete: 'Delete account', - startEnrollment: 'Start enrollment', - activateDesktop: 'Configure Desktop Client', - resetPassword: 'Reset password', - disableMfa: 'Disable MFA', - }, - }, - }, - navigation: { - bar: { - overview: 'VPN Overview', - users: 'Users', - provisioners: 'YubiKeys', - webhooks: 'Webhooks', - openId: 'OpenID Apps', - myProfile: 'My Profile', - settings: 'Settings', - logOut: 'Log out', - enrollment: 'Enrollment', - support: 'Support', - groups: 'Groups', - devices: 'Network Devices', - acl: 'Access Control', - activity: 'Activity log', - }, - mobileTitles: { - activity: 'Activity log', - groups: 'Groups', - wizard: 'Create location', - users: 'Users', - settings: 'Settings', - user: 'User Profile', - provisioners: 'Yubikey', - webhooks: 'Webhooks', - openId: 'OpenId Apps', - overview: 'Location Overview', - networkSettings: 'Edit Location', - enrollment: 'Enrollment', - support: 'Support', - devices: 'Network Devices', - }, - copyright: 'Copyright ©2023-2025', - version: { - open: 'Application version: {version: string}', - closed: 'v{version: string}', - }, - }, - form: { - download: 'Download', - copy: 'Copy', - saveChanges: 'Save changes', - submit: 'Submit', - login: 'Sign in', - cancel: 'Cancel', - close: 'Close', - placeholders: { - password: 'Password', - username: 'Username', - username_or_email: 'Username or email', - }, - error: { - urlInvalid: 'Enter valid URL', - reservedName: 'Name is already taken.', - invalidIp: 'IP is invalid.', - reservedIp: 'IP is already in use.', - forbiddenCharacter: 'Field contains forbidden characters.', - usernameTaken: 'Username is already in use.', - invalidKey: 'Key is invalid.', - invalid: 'Field is invalid.', - required: 'Field is required.', - invalidCode: 'Submitted code is invalid.', - maximumLength: 'Maximum length exceeded.', - maximumLengthOf: `Field length cannot exceed {length: number}`, - minimumLength: 'Minimum length not reached.', - minimumLengthOf: `Minimum length of {length: number} not reached.`, - noSpecialChars: 'No special characters are allowed.', - oneDigit: 'One digit required.', - oneSpecial: 'Special character required.', - oneUppercase: 'One uppercase character required.', - oneLowercase: 'One lowercase character required.', - portMax: 'Maximum port is 65535.', - endpoint: 'Enter a valid endpoint.', - address: 'Enter a valid address.', - addressNetmask: 'Enter a valid address with a netmask.', - validPort: 'Enter a valid port.', - validCode: 'Code should have 6 digits.', - allowedIps: 'Only valid IP or domain is allowed.', - startFromNumber: 'Cannot start from number.', - repeat: `Fields don't match.`, - number: 'Expected a valid number.', - minimumValue: `Minimum value of {value: number} not reached.`, - maximumValue: 'Maximum value of {value: number} exceeded.', - tooManyBadLoginAttempts: `Too many bad login attempts. Please try again in a few minutes.`, - }, - floatingErrors: { - title: 'Please correct the following:', - }, - }, - components: { - openClientDeepLink: 'One-Click Desktop Configuration', - aclDefaultPolicySelect: { - label: 'Default ACL Policy', - options: { - allow: 'Allow', - deny: 'Deny', - }, - }, - standaloneDeviceTokenModalContent: { - headerMessage: - 'First download defguard command line client binaries and install them on your server.', - downloadButton: 'Download Defguard CLI Client', - expandableCard: { - title: 'Copy and paste this command in your terminal on the device', - }, - }, - deviceConfigsCard: { - cardTitle: 'WireGuard Config for location:', - messages: { - copyConfig: 'Configuration copied to the clipboard', - }, - }, - gatewaysStatus: { - label: 'Gateways', - states: { - all: 'All ({count: number}) Connected', - some: 'Some ({count: number}) Connected', - none: 'None connected', - error: 'Status check failed', - }, - messages: { - error: 'Failed to get gateways status', - deleteError: 'Failed to delete gateway', - }, - }, - noLicenseBox: { - footer: { - get: 'Get an enterprise license', - contact: 'by contacting:', - }, - }, - locationMfaModeSelect: { - label: 'MFA Requirement', - options: { - disabled: 'Do not enforce MFA', - internal: 'Internal MFA', - external: 'External MFA', - }, - }, - }, - settingsPage: { - title: 'Settings', - tabs: { - smtp: 'SMTP', - global: 'Global settings', - ldap: 'LDAP', - openid: 'OpenID', - enterprise: 'Enterprise features', - gatewayNotifications: 'Gateway notifications', - activityLogStream: 'Activity log streaming', - }, - messages: { - editSuccess: 'Settings updated', - challengeSuccess: 'Challenge message changed', - }, - enterpriseOnly: { - title: 'This feature is available only in Defguard Enterprise.', - currentExpired: 'Your current license has expired.', - subtitle: 'To learn more, visit our ', - website: 'website', - }, - activityLogStreamSettings: { - messages: { - destinationCrud: { - create: '{destination: string} destination added', - modify: '{destination: string} destination modified', - delete: '{destination: string} destination removed', - }, - }, - modals: { - selectDestination: { - title: 'Select destination', - }, - vector: { - create: 'Add Vector destination', - modify: 'Edit Vector destination', - }, - logstash: { - create: 'Add Logstash destination', - modify: 'Edit Logstash destination', - }, - shared: { - formLabels: { - name: 'Name', - url: 'Url', - username: 'Username', - password: 'Password', - cert: 'Certificate', - }, - }, - }, - title: 'Activity log streaming', - list: { - noData: 'No destinations', - headers: { - name: 'Name', - destination: 'Destination', - }, - }, - }, - ldapSettings: { - title: 'LDAP Settings', - sync: { - header: 'LDAP two-way synchronization', - info: 'Before enabling synchronization, please read more about it in our [documentation](https://docs.defguard.net/features/ldap-and-active-directory-integration/two-way-ldap-and-active-directory-synchronization).', - info_enterprise: 'This feature is available only in Defguard Enterprise.', - helpers: { - heading: - 'Configure LDAP synchronization settings here. If configured, Defguard will pull user information from LDAP and synchronize it with local users.', - sync_enabled: - 'If enabled, Defguard will attempt to pull LDAP user data at the specified interval.', - authority: `Defguard will use the selected server as the authoritative source of - user data, meaning that if LDAP is selected, Defguard data will be overwritten with the LDAP - data in case of a desynchronization. If Defguard was selected as the authority, it's data will - overwrite LDAP data if necessary. - Make sure to check the documentation to understand the implications of this - setting.`, - interval: 'The interval with which the synchronization will be attempted.', - groups: `Defguard will attempt to synchronize only users belonging to the provided groups. Provide a comma-separated list of groups. If empty, all users will be synchronized.`, - }, - }, - form: { - labels: { - ldap_enable: 'Enable LDAP integration', - ldap_url: 'URL', - ldap_bind_username: 'Bind Username', - ldap_bind_password: 'Bind Password', - ldap_member_attr: 'Member Attribute', - ldap_username_attr: 'Username Attribute', - ldap_user_obj_class: 'User Object Class', - ldap_user_search_base: 'User Search Base', - ldap_user_auxiliary_obj_classes: 'Additional User Object Classes', - ldap_groupname_attr: 'Groupname Attribute', - ldap_group_search_base: 'Group Search Base', - ldap_group_member_attr: 'Group Member Attribute', - ldap_group_obj_class: 'Group Object Class', - ldap_sync_enabled: 'Enable LDAP two-way synchronization', - ldap_authoritative_source: 'Consider the following source as the authority', - ldap_sync_interval: 'Synchronization interval', - ldap_use_starttls: 'Use StartTLS', - ldap_tls_verify_cert: 'Verify TLS certificate', - ldap_uses_ad: 'LDAP server is Active Directory', - ldap_user_rdn_attr: 'User RDN Attribute', - ldap_sync_groups: 'Limit synchronization to these groups', - }, - helpers: { - ldap_user_obj_class: - 'The object class that will be added to the user object during its creation. This is used to determine if an LDAP object is a user.', - ldap_user_auxiliary_obj_classes: - "The additional object classes that will be added to the user object during its creation. They may also influence the added user's attributes (e.g. simpleSecurityObject class will add userPassword attribute).", - user_settings: - 'Configure LDAP user settings here. These settings determine how Defguard maps and synchronizes LDAP user information with local users.', - connection_settings: - 'Configure LDAP connection settings here. These settings determine how Defguard connects to your LDAP server. Encrypted connections are also supported (StartTLS, LDAPS).', - group_settings: - 'Configure LDAP group settings here. These settings determine how Defguard maps and synchronizes LDAP group information with local groups.', - ldap_group_obj_class: - 'The object class that represents a group in LDAP. This is used to determine if an LDAP object is a group.', - ldap_user_rdn_attr: - "If your user's RDN attribute is different than your username attribute, please provide it here, otherwise leave it empty to use the username attribute as the user's RDN.", - }, - headings: { - user_settings: 'User settings', - connection_settings: 'Connection settings', - group_settings: 'Group settings', - }, - delete: 'Delete configuration', - }, - test: { - title: 'Test LDAP Connection', - submit: 'Test', - messages: { - success: 'LDAP connected successfully', - error: 'LDAP connection rejected', - }, - }, - }, - openIdSettings: { - heading: 'External OpenID settings', - general: { - title: 'General settings', - helper: 'Here you can change general OpenID behavior in your Defguard instance.', - createAccount: { - label: - 'Automatically create user account when logging in for the first time through external OpenID.', - helper: - 'If this option is enabled, Defguard automatically creates new accounts for users who log in for the first time using an external OpenID provider. Otherwise, the user account must first be created by an administrator.', - }, - usernameHandling: { - label: 'Username handling', - helper: - 'Configure the method for handling invalid characters in usernames provided by your identity provider.', - options: { - remove: 'Remove forbidden characters', - replace: 'Replace forbidden characters', - prune_email: 'Prune email domain', - }, - }, - }, - form: { - title: 'Client settings', - helper: - 'Here you can configure the OpenID client settings with values provided by your external OpenID provider.', - custom: 'Custom', - none: 'None', - documentation: - 'Make sure to check our [documentation](https://docs.defguard.net/features/external-openid-providers) for more information and examples.', - delete: 'Delete provider', - directory_sync_settings: { - title: 'Directory synchronization settings', - helper: - "Directory synchronization allows you to automatically synchronize users' status and groups from an external provider.", - notSupported: 'Directory sync is not supported for this provider.', - connectionTest: { - success: 'Connection successful', - error: 'Connection failed with error:', - }, - }, - selects: { - synchronize: { - all: 'All', - users: 'Users', - groups: 'Groups', - }, - behavior: { - keep: 'Keep', - disable: 'Disable', - delete: 'Delete', - }, - }, - labels: { - provider: { - label: 'Provider', - helper: - 'Select your OpenID provider. You can use custom provider and fill in the base URL by yourself.', - }, - client_id: { - label: 'Client ID', - helper: 'Client ID provided by your OpenID provider.', - }, - client_secret: { - label: 'Client Secret', - helper: 'Client Secret provided by your OpenID provider.', - }, - base_url: { - label: 'Base URL', - helper: - 'Base URL of your OpenID provider, e.g. https://accounts.google.com. Make sure to check our documentation for more information and examples.', - }, - display_name: { - label: 'Display Name', - helper: - "Name of the OpenID provider to display on the login's page button. If not provided, the button will display generic 'Login with OIDC' text.", - }, - enable_directory_sync: { - label: 'Enable directory synchronization', - }, - sync_target: { - label: 'Synchronize', - helper: - "What to synchronize from the external provider. You can choose between synchronizing both users' state and group memberships, or narrow it down to just one of these.", - }, - sync_interval: { - label: 'Synchronization interval', - helper: 'Interval in seconds between directory synchronizations.', - }, - user_behavior: { - label: 'User behavior', - helper: - 'Choose how to handle users that are not present in the external provider anymore. You can select between keeping, disabling, or deleting them.', - }, - admin_behavior: { - label: 'Admin behavior', - helper: - 'Choose how to handle Defguard admins that are not present in the external provider anymore. You can select between keeping them, disabling them or completely deleting them.', - }, - admin_email: { - label: 'Admin email', - helper: - 'Email address of the account on which behalf the synchronization checks will be performed, e.g. the person who setup the Google service account. See our documentation for more details.', - }, - service_account_used: { - label: 'Service account in use', - helper: - 'The service account currently being used for synchronization. You can change it by uploading a new service account key file.', - }, - service_account_key_file: { - label: 'Service Account Key file', - helper: - "Upload a new service account key file to set the service account used for synchronization. NOTE: The uploaded file won't be visible after saving the settings and reloading the page as it's contents are sensitive and are never sent back to the dashboard.", - uploaded: 'File uploaded', - uploadPrompt: 'Upload a service account key file', - }, - okta_client_id: { - label: 'Directory Sync Client ID', - helper: 'Client ID for the Okta directory sync application.', - }, - okta_client_key: { - label: 'Directory Sync Client Private Key', - helper: - "Client private key for the Okta directory sync application in the JWK format. It won't be shown again here.", - }, - jumpcloud_api_key: { - label: 'JumpCloud API Key', - helper: - 'API Key for the JumpCloud directory sync. It will be used to periodically query JumpCloud for user state and group membership changes.', - }, - group_match: { - label: 'Sync only matching groups', - helper: - 'Provide a comma separated list of group names that should be synchronized. If left empty, all groups from the provider will be synchronized.', - }, - }, - }, - }, - modulesVisibility: { - header: 'Modules Visibility', - helper: `

- Hide unused modules. -

- - Read more in documentation. - `, - fields: { - wireguard_enabled: { - label: 'WireGuard VPN', - }, - webhooks_enabled: { - label: 'Webhooks', - }, - worker_enabled: { - label: 'Yubikey provisioning', - }, - openid_enabled: { - label: 'OpenID Connect', - }, - }, - }, - defaultNetworkSelect: { - header: 'Default location view', - helper: `

Here you can change your default location view.

- - Read more in documentation. - `, - filterLabels: { - grid: 'Grid view', - list: 'List view', - }, - }, - instanceBranding: { - header: 'Instance Branding', - form: { - title: 'Name & Logo:', - fields: { - instanceName: { - label: 'Instance name', - placeholder: 'Defguard', - }, - mainLogoUrl: { - label: 'Login logo url', - helper: 'Maximum picture size is 250x100 px', - placeholder: 'Default image', - }, - navLogoUrl: { - label: 'Menu & navigation small logo', - helper: 'Maximum picture size is 100x100 px', - placeholder: 'Default image', - }, - }, - controls: { - restoreDefault: 'Restore default', - submit: 'Save changes', - }, - }, - helper: ` -

- Here you can add url of your logo and name for your defguard - instance it will be displayed instead of defguard. -

- - Read more in documentation. - - `, - }, - license: { - header: 'Enterprise', - helpers: { - enterpriseHeader: { - text: 'Here you can manage your Defguard Enterprise version license.', - link: 'To learn more about Defguard Enterprise, visit our webiste.', - }, - licenseKey: { - text: 'Enter your Defguard Enterprise license key below. You should receive it via email after purchasing the license.', - link: 'You can purchase the license here.', - }, - }, - form: { - title: 'License', - fields: { - key: { - label: 'License key', - placeholder: 'Your Defguard license key', - }, - }, - }, - licenseInfo: { - title: 'License information', - status: { - noLicense: 'No valid license', - expired: 'Expired', - limitsExceeded: 'Limits Exceeded', - active: 'Active', - }, - licenseNotRequired: - "

You have access to this enterprise feature, as you haven't exceeded any of the usage limits yet. Check the documentation for more information.

", - types: { - subscription: { - label: 'Subscription', - helper: 'A license that automatically renews at regular intervals', - }, - offline: { - label: 'Offline', - helper: - 'The license is valid until the expiry date and does not automatically renew', - }, - }, - fields: { - status: { - label: 'Status', - active: 'Active', - expired: 'Expired', - subscriptionHelper: - 'A subscription license is considered valid for some time after the expiration date to account for possible automatic payment delays.', - }, - type: { - label: 'Type', - }, - validUntil: { - label: 'Valid until', - }, - }, - }, - }, - smtp: { - form: { - title: 'SMTP configuration', - sections: { - server: 'Server settings', - }, - fields: { - encryption: { - label: 'Encryption', - }, - server: { - label: 'Server address', - placeholder: 'Address', - }, - port: { - label: 'Server port', - placeholder: 'Port', - }, - user: { - label: 'Server username', - placeholder: 'Username', - }, - password: { - label: 'Server password', - placeholder: 'Password', - }, - sender: { - label: 'Sender email address', - placeholder: 'Address', - helper: ` -

- System messages will be sent from this address. - E.g. no-reply@my-company.com. -

- `, - }, - }, - controls: { - submit: 'Save changes', - }, - }, - delete: 'Delete configuration', - testForm: { - title: 'Send test email', - subtitle: 'Enter recipent email address', - fields: { - to: { - label: 'Send test email to', - placeholder: 'Address', - }, - }, - controls: { - submit: 'Send', - resend: 'Resend', - retry: 'Retry', - success: 'Test email sent', - error: 'Error sending email', - }, - success: { - message: 'Test email has been sent successully.', - }, - error: { - message: - 'There was an error sending the test email. Please check your SMTP configuration and try again.', - fullError: 'Error: {error: string}', - }, - }, - helper: `Here you can configure SMTP server used to send system messages to the users.`, - }, - enrollment: { - helper: - 'Enrollment is a process by which a new employee will be able to activate their new account, create a password and configure a VPN device.', - vpnOptionality: { - header: 'VPN step optionality', - helper: - 'You can choose whether creating a VPN device is optional or mandatory during enrollment', - }, - welcomeMessage: { - header: 'Welcome message', - helper: ` -

In this text input you can use Markdown:

-
    -
  • Headings start with a hash #
  • -
  • Use asterisks for *italics*
  • -
  • Use two asterisks for **bold**
  • -
- `, - }, - welcomeEmail: { - header: 'Welcome e-mail', - helper: ` -

In this text input you can use Markdown:

-
    -
  • Headings start with a hash #
  • -
  • Use asterisks for *italics*
  • -
  • Use two asterisks for **bold**
  • -
- `, - }, - form: { - controls: { - submit: 'Save changes', - }, - welcomeMessage: { - helper: - 'This information will be displayed for the user once enrollment is completed. We advise you to insert relevant links and explain next steps briefly.', - placeholder: 'Please input welcome message', - }, - welcomeEmail: { - helper: - 'This information will be sent to the user once enrollment is completed. We advise you to insert relevant links and explain next steps briefly. You can reuse the welcome message here.', - placeholder: 'Please input welcome email', - }, - welcomeEmailSubject: { - label: 'Subject', - }, - useMessageAsEmail: { - label: 'Same as welcome message', - }, - }, - }, - enterprise: { - header: 'Enterprise Features', - helper: 'Here you can change enterprise settings.', - fields: { - deviceManagement: { - label: "Disable users' ability to manage their devices", - helper: - "When this option is enabled, only users in the Admin group can manage devices in user profile (it's disabled for all other users)", - }, - disableAllTraffic: { - label: 'Disable the option to route all traffic through VPN', - helper: - 'When this option is enabled, users will not be able to route all traffic through the VPN using the defguard client.', - }, - manualConfig: { - label: "Disable users' ability to manually configure WireGuard client", - helper: - "When this option is enabled, users won't be able to view or download configuration for the manual WireGuard client setup. Only the Defguard desktop client configuration will be available.", - }, - }, - }, - gatewayNotifications: { - smtpWarning: 'To enable notifications you must first configure an SMTP server', - header: 'Notifications', - sections: { - gateway: 'Gateway disconnect notifications', - }, - helper: 'Here you can manage email notifications.', - form: { - submit: 'Save changes', - fields: { - disconnectNotificationsEnabled: { - label: 'Enable gateway disconnect notifications', - help: 'Send email notification to admin users once a gateway is disconnected', - }, - inactivityThreshold: { - label: 'Gateway inactivity time [minutes]', - help: 'Time (in minutes) that a gateway needs to stay disconnected before a notification is sent', - }, - reconnectNotificationsEnabled: { - label: 'Enable gateway reconnect notifications', - help: 'Send email notification to admin users once a gateway is reconnected', - }, - }, - }, - }, - }, - openidOverview: { - pageTitle: 'OpenID Apps', - search: { - placeholder: 'Find apps', - }, - filterLabels: { - all: 'All apps', - enabled: 'Enabled', - disabled: 'Disabled', - }, - clientCount: 'All apps', - addNewApp: 'Add new', - list: { - headers: { - name: 'Name', - status: 'Status', - actions: 'Actions', - }, - editButton: { - edit: 'Edit app', - delete: 'Delete app', - disable: 'Disable', - enable: 'Enable', - copy: 'Copy client ID', - }, - status: { - enabled: 'Enabled', - disabled: 'Disabled', - }, - }, - messages: { - copySuccess: 'Client ID copied.', - noLicenseMessage: "You don't have a license for this feature.", - noClientsFound: 'No results found.', - }, - deleteApp: { - title: 'Delete app', - message: 'Do you want to delete {appName: string} app ?', - submit: 'Delete app', - messages: { - success: 'App deleted.', - }, - }, - enableApp: { - messages: { - success: 'App enabled.', - }, - }, - disableApp: { - messages: { - success: 'App disabled.', - }, - }, - modals: { - openidClientModal: { - title: { - addApp: 'Add Application', - editApp: 'Edit {appName: string} app', - }, - scopes: 'Scopes:', - messages: { - clientIdCopy: 'Client ID copied.', - clientSecretCopy: 'Client secret copied.', - }, - form: { - messages: { - successAdd: 'App created.', - successModify: 'App modified.', - }, - error: { - urlRequired: 'URL is required.', - validUrl: 'Must be a valid URL.', - scopeValidation: 'Must have at least one scope.', - }, - fields: { - name: { - label: 'App name', - }, - redirectUri: { - label: 'Redirect URL {count: number}', - placeholder: 'https://example.com/redirect', - }, - openid: { - label: 'OpenID', - }, - profile: { - label: 'Profile', - }, - email: { - label: 'Email', - }, - phone: { - label: 'Phone', - }, - groups: { - label: 'Groups', - }, - }, - controls: { - addUrl: 'Add URL', - }, - }, - clientId: 'Client ID', - clientSecret: 'Client secret', - }, - }, - }, - webhooksOverview: { - pageTitle: 'Webhooks', - search: { - placeholder: 'Find webhooks by url', - }, - filterLabels: { - all: 'All webhooks', - enabled: 'Enabled', - disabled: 'Disabled', - }, - webhooksCount: 'All webhooks', - addNewWebhook: 'Add new', - noWebhooksFound: 'No webhooks found.', - list: { - headers: { - name: 'Name', - description: 'Description', - status: 'Status', - actions: 'Actions', - }, - editButton: { - edit: 'Edit', - delete: 'Delete webhook', - disable: 'Disable', - enable: 'Enable', - }, - status: { - enabled: 'Enabled', - disabled: 'Disabled', - }, - }, - }, - provisionersOverview: { - pageTitle: 'Provisioners', - search: { - placeholder: 'Find provisioners', - }, - filterLabels: { - all: 'All', - available: 'Available', - unavailable: 'Unavailable', - }, - provisionersCount: 'All provisioners', - noProvisionersFound: 'No provisioners found.', - noLicenseMessage: "You don't have a license for this feature.", - provisioningStation: { - header: 'YubiKey provisioning station', - content: `In order to be able to provision your YubiKeys, first you need to set up - physical machine with USB slot. Run provided command on your chosen - machine to register it and start provisioning your keys.`, - dockerCard: { - title: 'Provisioning station docker setup command', - }, - tokenCard: { - title: 'Access token', - }, - }, - list: { - headers: { - name: 'Name', - ip: 'IP address', - status: 'Status', - actions: 'Actions', - }, - editButton: { - delete: 'Delete provisioner', - }, - status: { - available: 'Available', - unavailable: 'Unavailable', - }, - }, - messages: { - copy: { - token: 'Token copied', - command: 'Command copied', - }, - }, - }, - openidAllow: { - header: '{name: string} would like to:', - scopes: { - openid: 'Use your profile data for future logins.', - profile: 'Know basic information from your profile like name, profile picture etc.', - email: 'Know your email address.', - phone: 'Know your phone number.', - groups: 'Know your groups membership.', - }, - controls: { - accept: 'Accept', - cancel: 'Cancel', - }, - }, - networkOverview: { - networkSelection: { - all: 'All locations summary', - placeholder: 'Select location', - }, - timeRangeSelectionLabel: '{value: number}h period', - pageTitle: 'Location overview', - controls: { - editNetworks: 'Edit Locations settings', - selectNetwork: { - placeholder: 'Loading locations', - }, - }, - filterLabels: { - grid: 'Grid view', - list: 'List view', - }, - gatewayStatus: { - all: 'All ({count: number}) Connected', - some: 'Some ({count: number}) Connected', - none: 'None connected', - }, - stats: { - currentlyActiveUsers: 'Currently active users', - currentlyActiveNetworkDevices: 'Currently active network devices', - totalUserDevices: 'Total user devices: {count: number}', - activeNetworkDevices: 'Active network devices in {hour: number}h', - activeUsersFilter: 'Active users in {hour: number}h', - activeDevicesFilter: 'Active devices in {hour: number}h', - activityIn: 'Activity in {hour: number}H', - networkUsage: 'Network usage', - peak: 'Peak', - in: 'In:', - out: 'Out:', - gatewayDisconnected: 'Gateway disconnected', - }, - cardsLabels: { - users: 'Connected Users', - devices: 'Connected Network Devices', - }, - }, - connectedUsersOverview: { - pageTitle: 'Connected users', - noUsersMessage: 'Currently there are no connected users', - userList: { - username: 'Username', - device: 'Device', - connected: 'Connected', - deviceLocation: 'Device location', - networkUsage: 'Network usage', - }, - }, - networkPage: { - pageTitle: 'Edit Location', - addNetwork: '+ Add new location', - controls: { - networkSelect: { - label: 'Location choice', - }, - }, - }, - activityOverview: { - header: 'Activity stream', - noData: 'Currently there is no activity detected', - }, - networkConfiguration: { - messages: { - delete: { - success: 'Network deleted', - error: 'Failed to delete network', - }, - }, - header: 'Location configuration', - importHeader: 'Location import', - form: { - helpers: { - address: - 'Based on this address VPN network address will be defined, eg. 10.10.10.1/24 (and VPN network will be: 10.10.10.0/24). You can optionally specify multiple addresses separated by a comma. The first address is the primary address, and this one will be used for IP address assignment for devices. The other IP addresses are auxiliary and are not managed by Defguard.', - endpoint: - 'Public IP address or domain name to which the remote peers/users will connect to. This address will be used in the configuration for the clients, but Defguard Gateways do not bind to this address.', - gateway: 'Gateway public address, used by VPN users to connect', - dns: 'Specify the DNS resolvers to query when the wireguard interface is up.', - allowedIps: - 'List of addresses/masks that should be routed through the VPN network.', - allowedGroups: - 'By default, all users will be allowed to connect to this location. If you want to restrict access to this location to a specific group, please select it below.', - aclFeatureDisabled: - "ACL functionality is an enterprise feature and you've exceeded the user, device or network limits to use it. In order to use this feature, purchase an enterprise license or upgrade your existing one.", - peerDisconnectThreshold: - 'Clients authorized with MFA will be disconnected from the location once there has been no network activity detected between them and the VPN gateway for a length of time configured below.', - locationMfaMode: { - description: 'Choose how MFA is enforced when connecting to this location:', - internal: - "Internal MFA - MFA is enforced using Defguard's built-in MFA (e.g. TOTP, WebAuthn) with internal identity", - external: - 'External MFA - If configured (see [OpenID settings](settings)) this option uses external identity provider for MFA', - }, - }, - sections: { - accessControl: { - header: 'Access Control & Firewall', - }, - mfa: { - header: 'Multi-Factor Authentication', - }, - }, - messages: { - networkModified: 'Location modified.', - networkCreated: 'Location created', - }, - fields: { - name: { - label: 'Location name', - }, - address: { - label: 'Gateway VPN IP address and netmask', - }, - endpoint: { - label: 'Gateway IP address or domain name', - }, - allowedIps: { - label: 'Allowed Ips', - }, - port: { - label: 'Gateway port', - }, - dns: { - label: 'DNS', - }, - allowedGroups: { - label: 'Allowed groups', - placeholder: 'All groups', - }, - keepalive_interval: { - label: 'Keepalive interval [seconds]', - }, - peer_disconnect_threshold: { - label: 'Client disconnect threshold [seconds]', - }, - acl_enabled: { - label: 'Enable ACL for this location', - }, - acl_default_allow: { - label: 'Default ACL policy', - }, - location_mfa_mode: { - label: 'MFA requirement', - }, - }, - controls: { - submit: 'Save changes', - cancel: 'Back to Overview', - delete: 'Remove location', - }, - }, - }, - gatewaySetup: { - header: { - main: 'Gateway server setup', - dockerBasedGatewaySetup: `Docker Based Gateway Setup`, - fromPackage: `From Package`, - oneLineInstall: `One Line Install`, - }, - card: { - title: 'Docker based gateway setup', - authToken: `Authentication Token`, - }, - button: { - availablePackages: `Available Packages`, - }, - controls: { - status: 'Check connection status', - }, - messages: { - runCommand: `Defguard requires to deploy a gateway node to control wireguard VPN on the vpn server. - More details can be found in the [documentation]({setupGatewayDocs:string}). - There are several ways to deploy the gateway server, - below is a Docker based example, for other examples please visit [documentation]({setupGatewayDocs:string}).`, - createNetwork: `Please create the network before running the gateway process.`, - noConnection: `No connection established, please run provided command.`, - connected: `Gateway connected.`, - statusError: 'Failed to get gateway status', - oneLineInstall: `If you are doing one line install: https://docs.defguard.net/getting-started/one-line-install - you don't need to do anything.`, - fromPackage: `Install the package available at https://github.com/DefGuard/gateway/releases/latest and configure \`/etc/defguard/gateway.toml\` - according to the [documentation]({setupGatewayDocs:string}).`, - authToken: `Token below is required to authenticate and configure the gateway node. Ensure you keep this token secure and follow the deployment instructions - provided in the [documentation]({setupGatewayDocs:string}) to successfully set up the gateway server. - For more details and exact steps, please refer to the [documentation]({setupGatewayDocs:string}).`, - dockerBasedGatewaySetup: `Below is a Docker based example. For more details and exact steps, please refer to the [documentation]({setupGatewayDocs:string}).`, - }, - }, - loginPage: { - pageTitle: 'Enter your credentials', - oidcLogin: 'Sign in with', - callback: { - return: 'Go back to login', - error: 'An error occurred during external OpenID login', - }, - mfa: { - title: 'Two-factor authentication', - controls: { - useAuthenticator: 'Use Authenticator app instead', - useWebauthn: 'Use security key instead', - useRecoveryCode: 'Use recovery code instead', - useEmail: 'Use E-mail instead', - }, - email: { - header: 'Use code we sent to your e-mail to proceed.', - form: { - labels: { - code: 'Code', - }, - controls: { - resendCode: 'Resend Code', - }, - }, - }, - totp: { - header: 'Use code from your authentication app and click button to proceed.', - form: { - fields: { - code: { - placeholder: 'Enter Authenticator code', - }, - }, - controls: { - submit: 'Use authenticator code', - }, - }, - }, - recoveryCode: { - header: 'Enter one of active recovery codes and click button to log in.', - form: { - fields: { - code: { - placeholder: 'Recovery code', - }, - }, - controls: { - submit: 'Use recovery code', - }, - }, - }, - webauthn: { - header: 'When you are ready to authenticate, press the button below.', - controls: { - submit: 'Use security key', - }, - messages: { - error: 'Failed to read key. Please try again.', - }, - }, - }, - }, - wizard: { - completed: 'Location setup completed', - configuration: { - successMessage: 'Location created', - }, - welcome: { - header: 'Welcome to location wizard!', - sub: 'Before you start using VPN you need to setup your first location. When in doubt click on icon.', - button: 'Setup location', - }, - navigation: { - top: 'Location setup', - titles: { - welcome: 'Location setup', - choseNetworkSetup: 'Chose Location setup', - importConfig: 'Import existing location', - manualConfig: 'Configure location', - mapDevices: 'Map imported devices', - }, - buttons: { - next: 'Next', - back: 'Back', - }, - }, - deviceMap: { - messages: { - crateSuccess: 'Devices added', - errorsInForm: 'Please fill marked fields.', - }, - list: { - headers: { - deviceName: 'Device Name', - deviceIP: 'IP', - user: 'User', - }, - }, - }, - wizardType: { - manual: { - title: 'Manual Configuration', - description: 'Manual location configuration', - }, - import: { - title: 'Import From File', - description: 'Import from WireGuard config file', - }, - createNetwork: 'Create location', - }, - common: { - select: 'Select', - }, - locations: { - form: { - name: 'Name', - ip: 'IP address', - user: 'User', - fileName: 'File', - selectFile: 'Select file', - messages: { devicesCreated: 'Devices created' }, - validation: { invalidAddress: 'Invalid address' }, - }, - }, - }, - layout: { - select: { - addNewOptionDefault: 'Add new +', - }, - }, - redirectPage: { - title: 'You have been logged in', - subtitle: 'You will be redirected in a moment...', - }, - enrollmentPage: { - title: 'Enrollment', - controls: { - default: 'Restore default', - save: 'Save changes', - }, - messages: { - edit: { - success: 'Settings changed', - error: 'Save failed', - }, - }, - messageBox: - 'Enrollment is a process by which a new employee will be able to activate their new account, create a password and configure a VPN device. You can customize it here.', - settings: { - welcomeMessage: { - title: 'Welcome message', - messageBox: - 'This information will be displayed for user in service once enrollment is completed. We advise to insert links and explain next steps briefly. You can use same message as in the e-mail.', - }, - vpnOptionality: { - title: 'VPN set optionallity', - select: { - options: { - optional: 'Optional', - mandatory: 'Mandatory', - }, - }, - }, - welcomeEmail: { - title: 'Welcome e-mail', - subject: { - label: 'E-mail subject', - }, - messageBox: - 'This information will be sent to user once enrollment is completed. We advise to insert links and explain next steps briefly.', - controls: { - duplicateWelcome: 'Same as welcome message', - }, - }, - }, - }, - supportPage: { - title: 'Support', - modals: { - confirmDataSend: { - title: 'Send Support Data', - subTitle: - 'Please confirm that you actually want to send support debug information. None of your private information will be sent (wireguard keys, email addresses, etc. will not be sent).', - submit: 'Send support data', - }, - }, - debugDataCard: { - title: 'Support data', - body: ` -If you need assistance or you were asked to generate support data by our team (for example on our Matrix support channel: **#defguard-support:teonite.com**), you have two options: -* Either you can configure SMTP settings and click "Send support data" -* Or click "Download support data" and create a bug report in our GitHub attaching this file. -`, - downloadSupportData: 'Download support data', - downloadLogs: 'Download logs', - sendMail: 'Send support data', - mailSent: 'Email sent', - mailError: 'Error sending email', - }, - supportCard: { - title: 'Support', - body: ` -Before contacting or submitting any issues to GitHub please get familiar with Defguard documentation available at [docs.defguard.net](https://docs.defguard.net/) - -To submit: -* Bugs - please go to [GitHub](https://github.com/DefGuard/defguard/issues/new?assignees=&labels=bug&template=bug_report.md&title=) -* Feature request - please go to [GitHub](https://github.com/DefGuard/defguard/issues/new?assignees=&labels=feature&template=feature_request.md&title=) - -Any other requests you can reach us at: support@defguard.net -`, - }, - }, - devicesPage: { - title: 'Network Devices', - search: { - placeholder: 'Find', - }, - bar: { - itemsCount: 'All devices', - filters: {}, - actions: { - addNewDevice: 'Add new', - }, - }, - list: { - columns: { - labels: { - name: 'Device Name', - location: 'Location', - assignedIps: 'IP Addresses', - description: 'Description', - addedBy: 'Added By', - addedAt: 'Add Date', - edit: 'Edit', - }, - }, - edit: { - actionLabels: { - config: 'View config', - generateToken: 'Generate auth token', - }, - }, - }, - }, - acl: { - messageBoxes: { - aclAliasKind: { - component: { - name: 'Component', - description: 'combined with manually configured destination fields in ACL', - }, - destination: { - name: 'Destination', - description: 'translated into a separate set of firewall rules', - }, - }, - networkSelectionIndicatorsHelper: { - //md - denied: ` - Location access **denied** by default – network traffic not explicitly defined by the rules will be blocked. - `, - //md - allowed: ` - Location access **allowed** by default – network traffic not explicitly defined by the rules will be passed. - `, - //md - unmanaged: ` - Location access unmanaged (ACL disabled) - `, - }, - }, - sharedTitle: 'Access Control List', - fieldsSelectionLabels: { - ports: 'All ports', - protocols: 'All protocols', - }, - ruleStatus: { - new: 'New', - applied: 'Applied', - modified: 'Pending Change', - deleted: 'Pending Deletion', - enable: 'Enable', - enabled: 'Enabled', - disable: 'Disable', - disabled: 'Disabled', - expired: 'Expired', - }, - listPage: { - tabs: { - rules: 'Rules', - aliases: 'Aliases', - }, - message: { - changeDiscarded: 'Change discarded', - changeAdded: 'Pending change added', - changeFail: 'Failed to make change', - applyChanges: 'Pending changes applied', - applyFail: 'Failed to apply changes', - }, - rules: { - modals: { - applyConfirm: { - title: 'Deploy pending changes', - subtitle: '{count: number} changes will be deployed', - submit: 'Deploy changes', - }, - filterGroupsModal: { - groupHeaders: { - alias: 'Aliases', - location: 'Locations', - groups: 'Groups', - status: 'Status', - }, - submit: 'Save Filter', - }, - }, - listControls: { - searchPlaceholder: 'Find name', - addNew: 'Add new', - filter: { - nothingApplied: 'Filter', - applied: 'Filters ({count: number})', - }, - apply: { - noChanges: 'Deploy pending changes', - all: 'Deploy pending changes ({count: number})', - selective: 'Deploy selected changes ({count: number})', - }, - }, - list: { - pendingList: { - title: 'Pending Changes', - noData: 'No pending changes', - noDataSearch: 'No pending changes found', - }, - deployedList: { - title: 'Deployed Rules', - noData: 'No deployed rules', - noDataSearch: 'No deployed rules found', - }, - headers: { - name: 'Rule name', - id: 'ID', - destination: 'Destination', - allowed: 'Allowed', - denied: 'Denied', - locations: 'Locations', - status: 'Status', - edit: 'Edit', - }, - tags: { - all: 'All', - allDenied: 'All denied', - allAllowed: 'All allowed', - }, - editMenu: { - discard: 'Discard Changes', - delete: 'Mark for Deletion', - }, - }, - }, - aliases: { - message: { - rulesApply: 'Pending changes applied', - rulesApplyFail: 'Failed to apply changes', - aliasDeleted: 'Alias deleted', - aliasDeleteFail: 'Alias deletion failed', - }, - modals: { - applyConfirm: { - title: 'Confirm Alias Deployment', - message: `The updated aliases will modify the following rule(s) currently deployed on the gateway.\nPlease ensure these changes are intended before proceeding.`, - listLabel: 'Affected Rules', - submit: 'Deploy Changes', - }, - deleteBlock: { - title: 'Deletion blocked', - //md - content: ` -This alias is currently in use by the following rule(s) and cannot be deleted. To proceed with deletion, you must first remove it from these rules({rulesCount: number}): -`, - }, - filterGroupsModal: { - groupLabels: { - rules: 'Rules', - status: 'Status', - }, - }, - create: { - labels: { - name: 'Alias name', - kind: 'Alias kind', - ip: 'IPv4/6 CIDR range address', - ports: 'Ports or Port Ranges', - protocols: 'Protocols', - }, - placeholders: { - protocols: 'All Protocols', - ports: 'All Ports', - ip: 'All IP addresses', - }, - kindOptions: { - destination: 'Destination', - component: 'Component', - }, - controls: { - cancel: 'Cancel', - edit: 'Edit Alias', - create: 'Create Alias', - }, - messages: { - modified: 'Alias modified', - created: 'Alias created', - }, - }, - }, - listControls: { - searchPlaceholder: 'Find name', - addNew: 'Add new', - filter: { - nothingApplied: 'Filter', - applied: 'Filters ({count: number})', - }, - apply: { - noChanges: 'Deploy pending changes', - all: 'Deploy pending changes ({count: number})', - selective: 'Deploy selected changes ({count: number})', - }, - }, - list: { - pendingList: { - title: 'Pending Changes', - noData: 'No pending changes', - noDataSearch: 'No pending changes found', - }, - deployedList: { - title: 'Deployed Aliases', - noData: 'No deployed aliases', - noDataSearch: 'No deployed aliases found', - }, - headers: { - id: 'ID', - name: 'Alias name', - kind: 'Alias kind', - ip: 'IPv4/6 CIDR range address', - ports: 'Ports', - protocols: 'Protocols', - status: 'Status', - edit: 'Edit', - rules: 'Rules', - }, - status: { - applied: 'Applied', - changed: 'Modified', - }, - tags: { - allDenied: 'All denied', - allAllowed: 'All allowed', - }, - editMenu: { - discardChanges: 'Discard changes', - delete: 'Delete alias', - }, - }, - }, - }, - createPage: { - formError: { - allowDenyConflict: 'Conflicting members', - allowNotConfigured: 'Must configure some allowed users, groups or devices', - }, - infoBox: { - // md - allowInstructions: ` - Specify one or more fields (Users, Groups or Devices) to define this rule. The rule will consider all inputs provided for matching conditions. Leave any fields blank if not needed.`, - // md - destinationInstructions: ` - Specify one or more fields (IP Addresses or Ports) to define this rule. The rule will consider all inputs provided for matching conditions. Leave any fields blank if not needed.`, - }, - message: { - create: 'Rule created and added to pending changes.', - createFail: 'Rule creation failed', - }, - headers: { - rule: 'Rule', - createRule: 'Create Rule', - allowed: 'Allowed Users/Groups/Devices', - denied: 'Denied Users/Groups/Devices', - destination: 'Destination', - }, - labels: { - name: 'Rule name', - priority: 'Priority', - status: 'Status', - locations: 'Locations', - allowAllUsers: 'Allow all users', - allowAllNetworks: 'Include all locations', - allowAllNetworkDevices: 'Allow all network devices', - denyAllUsers: 'Deny all users', - denyAllNetworkDevices: 'Deny all network devices', - users: 'Users', - groups: 'Groups', - devices: 'Network devices', - protocols: 'Protocols', - manualIp: 'IPv4/6 CIDR range or address', - ports: 'Ports', - aliases: 'Aliases', - expires: 'Expiration Date', - manualInput: 'Manual Input', - }, - placeholders: { - allProtocols: 'All protocols', - allIps: 'All IP addresses', - }, - }, - }, - activity: { - title: 'Activity log', - modals: { - timeRange: { - title: 'Activity time', - }, - }, - list: { - allLabel: 'All activity', - headers: { - date: 'Date', - user: 'User', - ip: 'IP', - location: 'Location', - event: 'Event', - module: 'Module', - device: 'Device', - description: 'Description', - }, - noData: { - data: 'No activities present', - search: 'No activities found', - }, - }, - }, - enums: { - activityLogEventType: { - user_login: 'User login', - user_login_failed: 'User login failed', - user_mfa_login: 'User MFA login', - user_mfa_login_failed: 'User MFA login failed', - recovery_code_used: 'Recovery code used', - user_logout: 'User logout', - user_added: 'User added', - user_removed: 'User removed', - user_modified: 'User modified', - user_groups_modified: 'User groups modified', - mfa_enabled: 'MFA enabled', - mfa_disabled: 'MFA disabled', - user_mfa_disabled: 'User MFA disabled', - mfa_totp_enabled: 'MFA TOTP enabled', - mfa_totp_disabled: 'MFA TOTP disabled', - mfa_email_enabled: 'MFA email enabled', - mfa_email_disabled: 'MFA email disabled', - mfa_security_key_added: 'MFA security key added', - mfa_security_key_removed: 'MFA security key removed', - device_added: 'Device added', - device_removed: 'Device removed', - device_modified: 'Device modified', - network_device_added: 'Network device added', - network_device_removed: 'Network device removed', - network_device_modified: 'Network device modified', - activity_log_stream_created: 'Activity log stream created', - activity_log_stream_modified: 'Activity log stream modified', - activity_log_stream_removed: 'Activity log stream removed', - vpn_client_connected: 'VPN client connected', - vpn_client_disconnected: 'VPN client disconnected', - vpn_client_connected_mfa: 'VPN client connected to MFA location', - vpn_client_disconnected_mfa: 'VPN client disconnected from MFA location', - vpn_client_mfa_failed: 'VPN client failed MFA authentication', - enrollment_token_added: 'Enrollment token added', - enrollment_started: 'Enrollment started', - enrollment_device_added: 'Device added', - enrollment_completed: 'Enrollment completed', - password_reset_requested: 'Password reset requested', - password_reset_started: 'Password reset started', - password_reset_completed: 'Password reset completed', - vpn_location_added: 'VPN location added', - vpn_location_removed: 'VPN location removed', - vpn_location_modified: 'VPN location modified', - api_token_added: 'API token added', - api_token_removed: 'API token removed', - api_token_renamed: 'API token renamed', - open_id_app_added: 'OpenID app added', - open_id_app_removed: 'OpenID app removed', - open_id_app_modified: 'OpenID app modified', - open_id_app_state_changed: 'OpenID app state changed', - open_id_provider_removed: 'OpenID provider removed', - open_id_provider_modified: 'OpenID provider modified', - settings_updated: 'Settings updated', - settings_updated_partial: 'Settings partially updated', - settings_default_branding_restored: 'Default branding restored', - groups_bulk_assigned: 'Groups bulk assigned', - group_added: 'Group added', - group_modified: 'Group modified', - group_removed: 'Group removed', - group_member_added: 'Group member added', - group_member_removed: 'Group member removed', - group_members_modified: 'Group members modified', - web_hook_added: 'Webhook added', - web_hook_modified: 'Webhook modified', - web_hook_removed: 'Webhook removed', - web_hook_state_changed: 'Webhook state changed', - authentication_key_added: 'Authentication key added', - authentication_key_removed: 'Authentication key removed', - authentication_key_renamed: 'Authentication key renamed', - password_changed: 'Password changed', - password_changed_by_admin: 'Password changed by admin', - password_reset: 'Password reset', - client_configuration_token_added: 'Client configuration token added', - user_snat_binding_added: 'User SNAT binding added', - user_snat_binding_modified: 'User SNAT binding modified', - user_snat_binding_removed: 'User SNAT binding removed', - }, - activityLogModule: { - defguard: 'Defguard', - client: 'Client', - enrollment: 'Enrollment', - vpn: 'VPN', - }, - }, -}; - -export default en; diff --git a/web/src/i18n/formatters.ts b/web/src/i18n/formatters.ts deleted file mode 100644 index 6f3d0997e..000000000 --- a/web/src/i18n/formatters.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { FormattersInitializer } from 'typesafe-i18n'; - -import type { Formatters, Locales } from './i18n-types'; - -export const initFormatters: FormattersInitializer = ( - //@ts-ignore - locale: Locales, -) => { - const formatters: Formatters = { - // add your formatter functions here - }; - - return formatters; -}; diff --git a/web/src/i18n/i18n-react.tsx b/web/src/i18n/i18n-react.tsx deleted file mode 100644 index f113051fa..000000000 --- a/web/src/i18n/i18n-react.tsx +++ /dev/null @@ -1,16 +0,0 @@ -// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten. -/* eslint-disable */ - -import { useContext } from 'react' -import { initI18nReact } from 'typesafe-i18n/react' -import type { I18nContextType } from 'typesafe-i18n/react' -import type { Formatters, Locales, TranslationFunctions, Translations } from './i18n-types' -import { loadedFormatters, loadedLocales } from './i18n-util' - -const { component: TypesafeI18n, context: I18nContext } = initI18nReact(loadedLocales, loadedFormatters) - -const useI18nContext = (): I18nContextType => useContext(I18nContext) - -export { I18nContext, useI18nContext } - -export default TypesafeI18n diff --git a/web/src/i18n/i18n-types.ts b/web/src/i18n/i18n-types.ts deleted file mode 100644 index 2bd9e2058..000000000 --- a/web/src/i18n/i18n-types.ts +++ /dev/null @@ -1,13367 +0,0 @@ -// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten. -/* eslint-disable */ -import type { BaseTranslation as BaseTranslationType, LocalizedString, RequiredParams } from 'typesafe-i18n' - -export type BaseTranslation = BaseTranslationType -export type BaseLocale = 'en' - -export type Locales = - | 'en' - | 'ko' - | 'pl' - -export type Translation = RootTranslation - -export type Translations = RootTranslation - -type RootTranslation = { - common: { - conditions: { - /** - * o​r - */ - or: string - /** - * a​n​d - */ - and: string - /** - * e​q​u​a​l - */ - equal: string - } - controls: { - /** - * T​i​m​e​ ​r​a​n​g​e - */ - timeRange: string - /** - * A​d​d​ ​n​e​w - */ - addNew: string - /** - * A​d​d - */ - add: string - /** - * A​c​c​e​p​t - */ - accept: string - /** - * N​e​x​t - */ - next: string - /** - * B​a​c​k - */ - back: string - /** - * C​a​n​c​e​l - */ - cancel: string - /** - * C​o​n​f​i​r​m - */ - confirm: string - /** - * S​u​b​m​i​t - */ - submit: string - /** - * C​l​o​s​e - */ - close: string - /** - * S​e​l​e​c​t - */ - select: string - /** - * F​i​n​i​s​h - */ - finish: string - /** - * S​a​v​e​ ​c​h​a​n​g​e​s - */ - saveChanges: string - /** - * S​a​v​e - */ - save: string - /** - * R​e​s​t​o​r​e​ ​d​e​f​a​u​l​t - */ - RestoreDefault: string - /** - * D​e​l​e​t​e - */ - 'delete': string - /** - * R​e​n​a​m​e - */ - rename: string - /** - * C​o​p​y - */ - copy: string - /** - * E​d​i​t - */ - edit: string - /** - * D​i​s​m​i​s​s - */ - dismiss: string - /** - * S​h​o​w - */ - show: string - /** - * E​n​a​b​l​e - */ - enable: string - /** - * E​n​a​b​l​e​d - */ - enabled: string - /** - * D​i​s​a​b​l​e - */ - disable: string - /** - * D​i​s​a​b​l​e​d - */ - disabled: string - /** - * S​e​l​e​c​t​ ​a​l​l - */ - selectAll: string - /** - * C​l​e​a​r - */ - clear: string - /** - * C​l​e​a​r​ ​a​l​l - */ - clearAll: string - /** - * F​i​l​t​e​r - */ - filter: string - /** - * F​i​l​t​e​r​s - */ - filters: string - } - /** - * K​e​y - */ - key: string - /** - * N​a​m​e - */ - name: string - /** - * N​o​ ​d​a​t​a - */ - noData: string - /** - * U​n​a​v​a​i​l​a​b​l​e - */ - unavailable: string - /** - * N​o​t​ ​s​e​t - */ - notSet: string - /** - * S​e​a​r​c​h - */ - search: string - /** - * T​i​m​e - */ - time: string - /** - * F​r​o​m - */ - from: string - /** - * U​n​t​i​l - */ - until: string - } - messages: { - /** - * E​r​r​o​r​ ​h​a​s​ ​o​c​c​u​r​r​e​d​. - */ - error: string - /** - * O​p​e​r​a​t​i​o​n​ ​s​u​c​c​e​e​d​e​d - */ - success: string - /** - * F​a​i​l​e​d​ ​t​o​ ​g​e​t​ ​a​p​p​l​i​c​a​t​i​o​n​ ​v​e​r​s​i​o​n​. - */ - errorVersion: string - /** - * C​o​n​t​e​x​t​ ​i​s​ ​n​o​t​ ​s​e​c​u​r​e​. - */ - insecureContext: string - /** - * D​e​t​a​i​l​s​: - */ - details: string - clipboard: { - /** - * C​l​i​p​b​o​a​r​d​ ​i​s​ ​n​o​t​ ​a​c​c​e​s​s​i​b​l​e​. - */ - error: string - /** - * C​o​n​t​e​n​t​ ​c​o​p​i​e​d​ ​t​o​ ​c​l​i​p​b​o​a​r​d​. - */ - success: string - } - } - modals: { - outdatedComponentsModal: { - /** - * V​e​r​s​i​o​n​ ​m​i​s​m​a​t​c​h - */ - title: string - /** - * D​e​f​g​u​a​r​d​ ​d​e​t​e​c​t​e​d​ ​u​n​s​u​p​p​o​r​t​e​d​ ​v​e​r​s​i​o​n​ ​i​n​ ​s​o​m​e​ ​c​o​m​p​o​n​e​n​t​s​. - */ - subtitle: string - content: { - /** - * I​n​c​o​m​p​a​t​i​b​l​e​ ​c​o​m​p​o​n​e​n​t​s​: - */ - title: string - /** - * U​n​k​n​o​w​n​ ​v​e​r​s​i​o​n - */ - unknownVersion: string - /** - * U​n​k​n​o​w​n​ ​h​o​s​t​n​a​m​e - */ - unknownHostname: string - } - } - upgradeLicenseModal: { - enterprise: { - /** - * U​p​g​r​a​d​e​ ​t​o​ ​E​n​t​e​r​p​r​i​s​e - */ - title: string - /** - * T​h​i​s​ ​f​u​n​c​t​i​o​n​a​l​i​t​y​ ​i​s​ ​a​n​ ​*​*​e​n​t​e​r​p​r​i​s​e​ ​f​e​a​t​u​r​e​*​*​ ​a​n​d​ ​y​o​u​'​v​e​ ​e​x​c​e​e​d​e​d​ ​t​h​e​ ​u​s​e​r​,​ ​d​e​v​i​c​e​ ​o​r​ ​n​e​t​w​o​r​k​ ​l​i​m​i​t​s​ ​t​o​ ​u​s​e​ ​i​t​.​ ​I​n​ ​o​r​d​e​r​ ​t​o​ ​u​s​e​ ​t​h​i​s​ ​f​e​a​t​u​r​e​,​ ​p​u​r​c​h​a​s​e​ ​a​n​ ​e​n​t​e​r​p​r​i​s​e​ ​l​i​c​e​n​s​e​ ​o​r​ ​u​p​g​r​a​d​e​ ​y​o​u​r​ ​e​x​i​s​t​i​n​g​ ​o​n​e​. - */ - subTitle: string - } - limit: { - /** - * U​p​g​r​a​d​e - */ - title: string - /** - * - ​ ​ ​ ​ ​ ​ ​ ​ ​Y​o​u​ ​h​a​v​e​ ​*​*​r​e​a​c​h​e​d​ ​t​h​e​ ​l​i​m​i​t​*​*​ ​o​f​ ​t​h​i​s​ ​f​u​n​c​t​i​o​n​a​l​i​t​y​.​ ​T​o​ ​*​*​[​ ​m​a​n​a​g​e​ ​m​o​r​e​ ​l​o​c​a​t​i​o​n​s​/​u​s​e​r​s​/​d​e​v​i​c​e​s​ ​]​*​*​ ​p​u​r​c​h​a​s​e​ ​o​f​ ​t​h​e​ ​E​n​t​e​r​p​r​i​s​e​ ​l​i​c​e​n​s​e​ ​i​s​ ​r​e​q​u​i​r​e​d​.​ - ​ ​ ​ ​ ​ ​ ​ ​ - */ - subTitle: string - } - /** - * - ​Y​o​u​ ​c​a​n​ ​f​i​n​d​ ​o​u​t​ ​m​o​r​e​ ​a​b​o​u​t​ ​f​e​a​t​u​r​e​s​ ​l​i​k​e​:​ - ​-​ ​R​e​a​l​ ​t​i​m​e​ ​a​n​d​ ​a​u​t​o​m​a​t​i​c​ ​c​l​i​e​n​t​ ​s​y​n​c​h​r​o​n​i​z​a​t​i​o​n​ - ​-​ ​E​x​t​e​r​n​a​l​ ​S​S​O​ - ​-​ ​C​o​n​t​r​o​l​l​i​n​g​ ​V​P​N​ ​c​l​i​e​n​t​s​ ​b​e​h​a​v​i​o​r​ - ​ - ​F​u​l​l​ ​e​n​t​e​r​p​r​i​s​e​ ​f​e​a​t​u​r​e​ ​l​i​s​t​:​ ​[​h​t​t​p​s​:​/​/​d​o​c​s​.​d​e​f​g​u​a​r​d​.​n​e​t​/​e​n​t​e​r​p​r​i​s​e​/​e​n​t​e​r​p​r​i​s​e​-​f​e​a​t​u​r​e​s​]​(​h​t​t​p​s​:​/​/​d​o​c​s​.​d​e​f​g​u​a​r​d​.​n​e​t​/​e​n​t​e​r​p​r​i​s​e​/​e​n​t​e​r​p​r​i​s​e​-​f​e​a​t​u​r​e​s​)​<​/​b​r​>​ - ​L​i​c​e​n​s​i​n​g​ ​i​n​f​o​r​m​a​t​i​o​n​:​ ​[​h​t​t​p​s​:​/​/​d​o​c​s​.​d​e​f​g​u​a​r​d​.​n​e​t​/​e​n​t​e​r​p​r​i​s​e​/​l​i​c​e​n​s​e​]​(​h​t​t​p​s​:​/​/​d​o​c​s​.​d​e​f​g​u​a​r​d​.​n​e​t​/​e​n​t​e​r​p​r​i​s​e​/​l​i​c​e​n​s​e​)​ - ​ ​ ​ ​ ​ ​ - */ - content: string - controls: { - /** - * M​a​y​b​e​ ​l​a​t​e​r - */ - cancel: string - /** - * S​e​e​ ​a​l​l​ ​E​n​t​e​r​p​r​i​s​e​ ​p​l​a​n​s - */ - confirm: string - } - } - standaloneDeviceEnrollmentModal: { - /** - * N​e​t​w​o​r​k​ ​d​e​v​i​c​e​ ​t​o​k​e​n - */ - title: string - toasters: { - /** - * T​o​k​e​n​ ​g​e​n​e​r​a​t​i​o​n​ ​f​a​i​l​e​d​. - */ - error: string - } - } - standaloneDeviceConfigModal: { - /** - * N​e​t​w​o​r​k​ ​d​e​v​i​c​e​ ​c​o​n​f​i​g - */ - title: string - /** - * C​o​n​f​i​g - */ - cardTitle: string - toasters: { - getConfig: { - /** - * F​a​i​l​e​d​ ​t​o​ ​g​e​t​ ​d​e​v​i​c​e​ ​c​o​n​f​i​g​. - */ - error: string - } - } - } - editStandaloneModal: { - /** - * E​d​i​t​ ​n​e​t​w​o​r​k​ ​d​e​v​i​c​e - */ - title: string - toasts: { - /** - * D​e​v​i​c​e​ ​m​o​d​i​f​i​e​d - */ - success: string - /** - * M​o​d​i​f​y​i​n​g​ ​t​h​e​ ​d​e​v​i​c​e​ ​f​a​i​l​e​d - */ - failure: string - } - } - deleteStandaloneDevice: { - /** - * D​e​l​e​t​e​ ​n​e​t​w​o​r​k​ ​d​e​v​i​c​e - */ - title: string - /** - * D​e​v​i​c​e​ ​{​n​a​m​e​}​ ​w​i​l​l​ ​b​e​ ​d​e​l​e​t​e​d​. - * @param {string} name - */ - content: RequiredParams<'name'> - messages: { - /** - * D​e​v​i​c​e​ ​d​e​l​e​t​e​d - */ - success: string - /** - * F​a​i​l​e​d​ ​t​o​ ​r​e​m​o​v​e​ ​d​e​v​i​c​e​. - */ - error: string - } - } - addStandaloneDevice: { - toasts: { - /** - * D​e​v​i​c​e​ ​a​d​d​e​d - */ - deviceCreated: string - /** - * D​e​v​i​c​e​ ​c​o​u​l​d​ ​n​o​t​ ​b​e​ ​a​d​d​e​d​. - */ - creationFailed: string - } - infoBox: { - /** - * H​e​r​e​ ​y​o​u​ ​c​a​n​ ​a​d​d​ ​d​e​f​i​n​i​t​i​o​n​s​ ​o​r​ ​g​e​n​e​r​a​t​e​ ​c​o​n​f​i​g​u​r​a​t​i​o​n​s​ ​f​o​r​ ​d​e​v​i​c​e​s​ ​t​h​a​t​ ​c​a​n​ ​c​o​n​n​e​c​t​ ​t​o​ ​y​o​u​r​ ​V​P​N​.​ ​O​n​l​y​ ​l​o​c​a​t​i​o​n​s​ ​w​i​t​h​o​u​t​ ​M​u​l​t​i​-​F​a​c​t​o​r​ ​A​u​t​h​e​n​t​i​c​a​t​i​o​n​ ​a​r​e​ ​a​v​a​i​l​a​b​l​e​ ​h​e​r​e​,​ ​a​s​ ​M​F​A​ ​i​s​ ​o​n​l​y​ ​s​u​p​p​o​r​t​e​d​ ​i​n​ ​D​e​f​g​u​a​r​d​ ​D​e​s​k​t​o​p​ ​C​l​i​e​n​t​ ​f​o​r​ ​n​o​w​. - */ - setup: string - } - form: { - /** - * A​d​d​ ​D​e​v​i​c​e - */ - submit: string - labels: { - /** - * D​e​v​i​c​e​ ​N​a​m​e - */ - deviceName: string - /** - * L​o​c​a​t​i​o​n - */ - location: string - /** - * A​s​s​i​g​n​e​d​ ​I​P​ ​A​d​d​r​e​s​s - */ - assignedAddress: string - /** - * D​e​s​c​r​i​p​t​i​o​n - */ - description: string - generation: { - /** - * G​e​n​e​r​a​t​e​ ​k​e​y​ ​p​a​i​r - */ - auto: string - /** - * U​s​e​ ​m​y​ ​o​w​n​ ​p​u​b​l​i​c​ ​k​e​y - */ - manual: string - } - /** - * P​r​o​v​i​d​e​ ​Y​o​u​r​ ​P​u​b​l​i​c​ ​K​e​y - */ - publicKey: string - } - } - steps: { - method: { - /** - * C​h​o​o​s​e​ ​a​ ​p​r​e​f​e​r​r​e​d​ ​m​e​t​h​o​d - */ - title: string - cards: { - cli: { - /** - * D​e​f​g​u​a​r​d​ ​C​o​m​m​a​n​d​ ​L​i​n​e​ ​C​l​i​e​n​t - */ - title: string - /** - * W​h​e​n​ ​u​s​i​n​g​ ​d​e​f​g​u​a​r​d​-​c​l​i​ ​y​o​u​r​ ​d​e​v​i​c​e​ ​w​i​l​l​ ​b​e​ ​a​u​t​o​m​a​t​i​c​a​l​l​y​ ​c​o​n​f​i​g​u​r​e​d​. - */ - subtitle: string - /** - * D​e​f​g​u​a​r​d​ ​C​L​I​ ​d​o​w​n​l​o​a​d​ ​a​n​d​ ​d​o​c​u​m​e​n​t​a​t​i​o​n - */ - docs: string - } - manual: { - /** - * M​a​n​u​a​l​ ​W​i​r​e​G​u​a​r​d​ ​C​l​i​e​n​t - */ - title: string - /** - * I​f​ ​y​o​u​r​ ​d​e​v​i​c​e​ ​d​o​e​s​ ​n​o​t​ ​s​u​p​p​o​r​t​ ​o​u​r​ ​C​L​I​ ​b​i​n​a​r​i​e​s​ ​y​o​u​ ​c​a​n​ ​a​l​w​a​y​s​ ​g​e​n​e​r​a​t​e​ ​a​ ​W​i​r​e​G​u​a​r​d​ ​c​o​n​f​i​g​u​r​a​t​i​o​n​ ​f​i​l​e​ ​a​n​d​ ​c​o​n​f​i​g​u​r​e​ ​i​t​ ​m​a​n​u​a​l​l​y​ ​-​ ​b​u​t​ ​a​n​y​ ​u​p​d​a​t​e​s​ ​t​o​ ​t​h​e​ ​V​P​N​ ​l​o​c​a​t​i​o​n​ ​c​o​n​f​i​g​u​r​a​t​i​o​n​ ​w​i​l​l​ ​r​e​q​u​i​r​e​ ​m​a​n​u​a​l​ ​c​h​a​n​g​e​s​ ​i​n​ ​d​e​v​i​c​e​ ​c​o​n​f​i​g​u​r​a​t​i​o​n​. - */ - subtitle: string - } - } - } - manual: { - /** - * A​d​d​ ​n​e​w​ ​V​P​N​ ​d​e​v​i​c​e​ ​u​s​i​n​g​ ​W​i​r​e​G​u​a​r​d​ ​C​l​i​e​n​t - */ - title: string - finish: { - /** - * D​o​w​n​l​o​a​d​ ​t​h​e​ ​p​r​o​v​i​d​e​d​ ​c​o​n​f​i​g​u​r​a​t​i​o​n​ ​f​i​l​e​ ​t​o​ ​y​o​u​r​ ​d​e​v​i​c​e​ ​a​n​d​ ​i​m​p​o​r​t​ ​i​t​ ​i​n​t​o​ ​y​o​u​r​ ​V​P​N​ ​c​l​i​e​n​t​ ​t​o​ ​c​o​m​p​l​e​t​e​ ​t​h​e​ ​s​e​t​u​p​. - */ - messageTop: string - /** - * U​s​e​ ​p​r​o​v​i​d​e​d​ ​c​o​n​f​i​g​u​r​a​t​i​o​n​ ​f​i​l​e​ ​b​e​l​o​w​ ​b​y​ ​s​c​a​n​n​i​n​g​ ​Q​R​ ​c​o​d​e​ ​o​r​ ​i​m​p​o​r​t​i​n​g​ ​i​t​ ​a​s​ ​f​i​l​e​ ​o​n​ ​y​o​u​r​ ​d​e​v​i​c​e​'​s​ ​W​i​r​e​G​u​a​r​d​ ​a​p​p​. - */ - ctaInstruction: string - /** - * - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​P​l​e​a​s​e​ ​r​e​m​e​m​b​e​r​ ​t​h​a​t​ ​D​e​f​g​u​a​r​d​ ​*​*​d​o​e​s​n​'​t​ ​s​t​o​r​e​ ​p​r​i​v​a​t​e​ ​k​e​y​s​*​*​.​ ​W​e​ ​w​i​l​l​ ​s​e​c​u​r​e​l​y​ ​g​e​n​e​r​a​t​e​ ​t​h​e​ ​p​u​b​l​i​c​ ​a​n​d​ ​p​r​i​v​a​t​e​ ​k​e​y​ ​p​a​i​r​ ​i​n​ ​y​o​u​r​ ​b​r​o​w​s​e​r​,​ ​b​u​t​ ​o​n​l​y​ ​s​t​o​r​e​ ​t​h​e​ ​p​u​b​l​i​c​ ​k​e​y​ ​i​n​ ​D​e​f​g​u​a​r​d​ ​d​a​t​a​b​a​s​e​.​ ​P​l​e​a​s​e​ ​d​o​w​n​l​o​a​d​ ​t​h​e​ ​c​o​n​f​i​g​u​r​a​t​i​o​n​ ​g​e​n​e​r​a​t​e​d​ ​w​i​t​h​ ​t​h​e​ ​p​r​i​v​a​t​e​ ​k​e​y​ ​f​o​r​ ​t​h​e​ ​d​e​v​i​c​e​,​ ​a​s​ ​i​t​ ​w​i​l​l​ ​n​o​t​ ​b​e​ ​a​c​c​e​s​s​i​b​l​e​ ​l​a​t​e​r​.​ - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ - */ - warningMessage: string - actionCard: { - /** - * C​o​n​f​i​g - */ - title: string - } - } - } - cli: { - /** - * A​d​d​ ​d​e​v​i​c​e​ ​u​s​i​n​g​ ​D​e​f​g​u​a​r​d​ ​C​o​m​m​a​n​d​ ​L​i​n​e​ ​C​l​i​e​n​t - */ - title: string - finish: { - /** - * F​i​r​s​t​ ​d​o​w​n​l​o​a​d​ ​D​e​f​g​u​a​r​d​ ​c​o​m​m​a​n​d​ ​l​i​n​e​ ​c​l​i​e​n​t​ ​b​i​n​a​r​y​ ​a​n​d​ ​i​n​s​t​a​l​l​ ​i​t​ ​o​n​ ​y​o​u​r​ ​s​e​r​v​e​r​. - */ - topMessage: string - /** - * D​o​w​n​l​o​a​d​ ​D​e​f​g​u​a​r​d​ ​C​L​I​ ​C​l​i​e​n​t - */ - downloadButton: string - /** - * C​o​p​y​ ​a​n​d​ ​p​a​s​t​e​ ​t​h​i​s​ ​c​o​m​m​a​n​d​ ​i​n​ ​y​o​u​r​ ​t​e​r​m​i​n​a​l​ ​o​n​ ​t​h​e​ ​d​e​v​i​c​e - */ - commandCopy: string - } - setup: { - /** - * H​e​r​e​ ​y​o​u​ ​c​a​n​ ​a​d​d​ ​d​e​f​i​n​i​t​i​o​n​s​ ​o​r​ ​g​e​n​e​r​a​t​e​ ​c​o​n​f​i​g​u​r​a​t​i​o​n​s​ ​f​o​r​ ​d​e​v​i​c​e​s​ ​t​h​a​t​ ​c​a​n​ ​c​o​n​n​e​c​t​ ​t​o​ ​y​o​u​r​ ​V​P​N​.​ ​O​n​l​y​ ​l​o​c​a​t​i​o​n​s​ ​w​i​t​h​o​u​t​ ​M​u​l​t​i​-​F​a​c​t​o​r​ ​A​u​t​h​e​n​t​i​c​a​t​i​o​n​ ​a​r​e​ ​a​v​a​i​l​a​b​l​e​ ​h​e​r​e​,​ ​a​s​ ​M​F​A​ ​i​s​ ​o​n​l​y​ ​s​u​p​p​o​r​t​e​d​ ​i​n​ ​D​e​f​g​u​a​r​d​ ​D​e​s​k​t​o​p​ ​C​l​i​e​n​t​ ​f​o​r​ ​n​o​w​. - */ - stepMessage: string - form: { - /** - * A​d​d​ ​D​e​v​i​c​e - */ - submit: string - } - } - } - } - } - updatesNotificationToaster: { - /** - * N​e​w​ ​v​e​r​s​i​o​n​ ​a​v​a​i​l​a​b​l​e​ ​{​v​e​r​s​i​o​n​} - * @param {string} version - */ - title: RequiredParams<'version'> - controls: { - /** - * S​e​e​ ​w​h​a​t​'​s​ ​n​e​w - */ - more: string - } - } - enterpriseUpgradeToaster: { - /** - * Y​o​u​'​v​e​ ​r​e​a​c​h​e​d​ ​t​h​e​ ​e​n​t​e​r​p​r​i​s​e​ ​f​u​n​c​t​i​o​n​a​l​i​t​y​ ​l​i​m​i​t​. - */ - title: string - /** - * Y​o​u​'​v​e​ ​e​x​c​e​e​d​e​d​ ​t​h​e​ ​l​i​m​i​t​ ​o​f​ ​y​o​u​r​ ​c​u​r​r​e​n​t​ ​D​e​f​g​u​a​r​d​ ​p​l​a​n​ ​a​n​d​ ​t​h​e​ ​e​n​t​e​r​p​r​i​s​e​ - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​f​e​a​t​u​r​e​s​ ​w​i​l​l​ ​b​e​ ​d​i​s​a​b​l​e​d​.​ ​P​u​r​c​h​a​s​e​ ​a​n​ ​e​n​t​e​r​p​r​i​s​e​ ​l​i​c​e​n​s​e​ ​o​r​ ​u​p​g​r​a​d​e​ ​y​o​u​r​ - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​x​i​s​t​i​n​g​ ​o​n​e​ ​t​o​ ​c​o​n​t​i​n​u​e​ ​u​s​i​n​g​ ​t​h​e​s​e​ ​f​e​a​t​u​r​e​s​. - */ - message: string - /** - * S​e​e​ ​a​l​l​ ​e​n​t​e​r​p​r​i​s​e​ ​p​l​a​n​s - */ - link: string - } - updatesNotification: { - header: { - /** - * U​p​d​a​t​e​ ​A​v​a​i​l​a​b​l​e - */ - title: string - /** - * n​e​w​ ​v​e​r​s​i​o​n​ ​{​v​e​r​s​i​o​n​} - * @param {string} version - */ - newVersion: RequiredParams<'version'> - /** - * c​r​i​t​i​c​a​l​ ​u​p​d​a​t​e - */ - criticalBadge: string - } - controls: { - /** - * V​i​s​i​t​ ​r​e​l​e​a​s​e​ ​p​a​g​e - */ - visitRelease: string - } - } - addGroup: { - /** - * A​d​d​ ​g​r​o​u​p - */ - title: string - /** - * S​e​l​e​c​t​ ​a​l​l​ ​u​s​e​r​s - */ - selectAll: string - /** - * G​r​o​u​p​ ​n​a​m​e - */ - groupName: string - /** - * F​i​l​t​e​r​/​S​e​a​r​c​h - */ - searchPlaceholder: string - /** - * C​r​e​a​t​e​ ​g​r​o​u​p - */ - submit: string - /** - * G​r​o​u​p​ ​s​e​t​t​i​n​g​s - */ - groupSettings: string - /** - * A​d​m​i​n​ ​g​r​o​u​p - */ - adminGroup: string - } - editGroup: { - /** - * E​d​i​t​ ​g​r​o​u​p - */ - title: string - /** - * S​e​l​e​c​t​ ​a​l​l​ ​u​s​e​r​s - */ - selectAll: string - /** - * G​r​o​u​p​ ​n​a​m​e - */ - groupName: string - /** - * F​i​l​t​e​r​/​S​e​a​r​c​h - */ - searchPlaceholder: string - /** - * U​p​d​a​t​e​ ​g​r​o​u​p - */ - submit: string - /** - * G​r​o​u​p​ ​s​e​t​t​i​n​g​s - */ - groupSettings: string - /** - * A​d​m​i​n​ ​g​r​o​u​p - */ - adminGroup: string - } - deleteGroup: { - /** - * D​e​l​e​t​e​ ​g​r​o​u​p​ ​{​n​a​m​e​} - * @param {string} name - */ - title: RequiredParams<'name'> - /** - * T​h​i​s​ ​a​c​t​i​o​n​ ​w​i​l​l​ ​p​e​r​m​a​n​e​n​t​l​y​ ​d​e​l​e​t​e​ ​t​h​i​s​ ​g​r​o​u​p​. - */ - subTitle: string - /** - * T​h​i​s​ ​g​r​o​u​p​ ​i​s​ ​c​u​r​r​e​n​t​l​y​ ​a​s​s​i​g​n​e​d​ ​t​o​ ​f​o​l​l​o​w​i​n​g​ ​V​P​N​ ​L​o​c​a​t​i​o​n​s​: - */ - locationListHeader: string - /** - * I​f​ ​t​h​i​s​ ​i​s​ ​t​h​e​ ​o​n​l​y​ ​a​l​l​o​w​e​d​ ​g​r​o​u​p​ ​f​o​r​ ​a​ ​g​i​v​e​n​ ​l​o​c​a​t​i​o​n​,​ ​t​h​e​ ​l​o​c​a​t​i​o​n​ ​w​i​l​l​ ​b​e​c​o​m​e​ ​<​b​>​a​c​c​e​s​s​i​b​l​e​ ​t​o​ ​a​l​l​ ​u​s​e​r​s​<​/​b​>​. - */ - locationListFooter: string - /** - * D​e​l​e​t​e​ ​g​r​o​u​p - */ - submit: string - /** - * C​a​n​c​e​l - */ - cancel: string - } - deviceConfig: { - /** - * D​e​v​i​c​e​ ​V​P​N​ ​c​o​n​f​i​g​u​r​a​t​i​o​n​s - */ - title: string - } - changePasswordSelf: { - /** - * C​h​a​n​g​e​ ​p​a​s​s​w​o​r​d - */ - title: string - messages: { - /** - * P​a​s​s​w​o​r​d​ ​h​a​s​ ​b​e​e​n​ ​c​h​a​n​g​e​d - */ - success: string - /** - * F​a​i​l​e​d​ ​t​o​ ​c​h​a​n​g​e​d​ ​p​a​s​s​w​o​r​d - */ - error: string - } - form: { - labels: { - /** - * N​e​w​ ​p​a​s​s​w​o​r​d - */ - newPassword: string - /** - * C​u​r​r​e​n​t​ ​p​a​s​s​w​o​r​d - */ - oldPassword: string - /** - * C​o​n​f​i​r​m​ ​n​e​w​ ​p​a​s​s​w​o​r​d - */ - repeat: string - } - } - controls: { - /** - * C​h​a​n​g​e​ ​p​a​s​s​w​o​r​d - */ - submit: string - /** - * C​a​n​c​e​l - */ - cancel: string - } - } - disableMfa: { - /** - * D​i​s​a​b​l​e​ ​M​F​A - */ - title: string - /** - * D​o​ ​y​o​u​ ​w​a​n​t​ ​t​o​ ​d​i​s​a​b​l​e​ ​M​F​A​ ​f​o​r​ ​u​s​e​r​ ​{​u​s​e​r​n​a​m​e​}​? - * @param {string} username - */ - message: RequiredParams<'username'> - messages: { - /** - * M​F​A​ ​f​o​r​ ​u​s​e​r​ ​{​u​s​e​r​n​a​m​e​}​ ​h​a​s​ ​b​e​e​n​ ​d​i​s​a​b​l​e​d - * @param {string} username - */ - success: RequiredParams<'username'> - /** - * F​a​i​l​e​d​ ​t​o​ ​d​i​s​a​b​l​e​ ​M​F​A​ ​f​o​r​ ​u​s​e​r​ ​{​u​s​e​r​n​a​m​e​} - * @param {string} username - */ - error: RequiredParams<'username'> - } - controls: { - /** - * D​i​s​a​b​l​e​ ​M​F​A - */ - submit: string - /** - * C​a​n​c​e​l - */ - cancel: string - } - } - startEnrollment: { - /** - * S​t​a​r​t​ ​e​n​r​o​l​l​m​e​n​t - */ - title: string - /** - * D​e​s​k​t​o​p​ ​a​c​t​i​v​a​t​i​o​n - */ - desktopTitle: string - messages: { - /** - * U​s​e​r​ ​e​n​r​o​l​l​m​e​n​t​ ​s​t​a​r​t​e​d - */ - success: string - /** - * D​e​s​k​t​o​p​ ​c​o​n​f​i​g​u​r​a​t​i​o​n​ ​s​t​a​r​t​e​d - */ - successDesktop: string - /** - * F​a​i​l​e​d​ ​t​o​ ​s​t​a​r​t​ ​u​s​e​r​ ​e​n​r​o​l​l​m​e​n​t - */ - error: string - /** - * F​a​i​l​e​d​ ​t​o​ ​s​t​a​r​t​ ​d​e​s​k​t​o​p​ ​a​c​t​i​v​a​t​i​o​n - */ - errorDesktop: string - } - messageBox: { - /** - * Y​o​u​ ​c​a​n​ ​s​h​a​r​e​ ​t​h​e​ ​f​o​l​l​o​w​i​n​g​ ​U​R​L​ ​a​n​d​ ​t​o​k​e​n​ ​w​i​t​h​ ​t​h​e​ ​u​s​e​r​ ​t​o​ ​c​o​n​f​i​g​u​r​e​ ​t​h​e​i​r​ ​D​e​f​g​u​a​r​d​ ​d​e​s​k​t​o​p​ ​o​r​ ​m​o​b​i​l​e​ ​c​l​i​e​n​t​. - */ - clientForm: string - /** - * Y​o​u​ ​c​a​n​ ​s​h​a​r​e​ ​t​h​i​s​ ​Q​R​ ​c​o​d​e​ ​f​o​r​ ​e​a​s​y​ ​D​e​f​g​u​a​r​d​ ​m​o​b​i​l​e​ ​c​l​i​e​n​t​ ​c​o​n​f​i​g​u​r​a​t​i​o​n​. - */ - clientQr: string - } - form: { - email: { - /** - * E​m​a​i​l - */ - label: string - } - mode: { - options: { - /** - * S​e​n​d​ ​t​o​k​e​n​ ​b​y​ ​e​m​a​i​l - */ - email: string - /** - * D​e​l​i​v​e​r​ ​t​o​k​e​n​ ​y​o​u​r​s​e​l​f - */ - manual: string - } - } - /** - * S​t​a​r​t​ ​e​n​r​o​l​l​m​e​n​t - */ - submit: string - /** - * A​c​t​i​v​a​t​e​ ​d​e​s​k​t​o​p - */ - submitDesktop: string - /** - * C​o​n​f​i​g​u​r​e​ ​S​M​T​P​ ​t​o​ ​s​e​n​d​ ​t​o​k​e​n​ ​b​y​ ​e​m​a​i​l​.​ ​G​o​ ​t​o​ ​S​e​t​t​i​n​g​s​ ​-​>​ ​S​M​T​P​. - */ - smtpDisabled: string - } - tokenCard: { - /** - * A​c​t​i​v​a​t​i​o​n​ ​t​o​k​e​n - */ - title: string - } - urlCard: { - /** - * D​e​f​g​u​a​r​d​ ​I​n​s​t​a​n​c​e​ ​U​R​L - */ - title: string - } - } - deleteNetwork: { - /** - * D​e​l​e​t​e​ ​{​n​a​m​e​}​ ​l​o​c​a​t​i​o​n - * @param {string} name - */ - title: RequiredParams<'name'> - /** - * T​h​i​s​ ​a​c​t​i​o​n​ ​w​i​l​l​ ​p​e​r​m​a​n​e​n​t​l​y​ ​d​e​l​e​t​e​ ​t​h​i​s​ ​l​o​c​a​t​i​o​n​. - */ - subTitle: string - /** - * D​e​l​e​t​e​ ​l​o​c​a​t​i​o​n - */ - submit: string - /** - * C​a​n​c​e​l - */ - cancel: string - } - changeWebhook: { - messages: { - /** - * W​e​b​h​o​o​k​ ​c​h​a​n​g​e​d​. - */ - success: string - } - } - manageWebAuthNKeys: { - /** - * S​e​c​u​r​i​t​y​ ​k​e​y​s - */ - title: string - messages: { - /** - * W​e​b​A​u​t​h​N​ ​k​e​y​ ​h​a​s​ ​b​e​e​n​ ​d​e​l​e​t​e​d​. - */ - deleted: string - /** - * K​e​y​ ​i​s​ ​a​l​r​e​a​d​y​ ​r​e​g​i​s​t​e​r​e​d - */ - duplicateKeyError: string - } - /** - * - ​ ​ ​ ​ ​ ​ ​ ​ ​<​p​>​ - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​S​e​c​u​r​i​t​y​ ​k​e​y​s​ ​c​a​n​ ​b​e​ ​u​s​e​d​ ​a​s​ ​y​o​u​r​ ​s​e​c​o​n​d​ ​f​a​c​t​o​r​ ​o​f​ ​a​u​t​h​e​n​t​i​c​a​t​i​o​n​ - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​n​s​t​e​a​d​ ​o​f​ ​a​ ​v​e​r​i​f​i​c​a​t​i​o​n​ ​c​o​d​e​.​ ​L​e​a​r​n​ ​m​o​r​e​ ​a​b​o​u​t​ ​c​o​n​f​i​g​u​r​i​n​g​ ​a​ - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​e​c​u​r​i​t​y​ ​k​e​y​.​ - ​ ​ ​ ​ ​ ​ ​ ​ ​<​/​p​>​ - - */ - infoMessage: string - form: { - messages: { - /** - * S​e​c​u​r​i​t​y​ ​k​e​y​ ​a​d​d​e​d​. - */ - success: string - } - fields: { - name: { - /** - * N​e​w​ ​k​e​y​ ​n​a​m​e - */ - label: string - } - } - controls: { - /** - * A​d​d​ ​n​e​w​ ​K​e​y - */ - submit: string - } - } - } - recoveryCodes: { - /** - * R​e​c​o​v​e​r​y​ ​c​o​d​e​s - */ - title: string - /** - * I​ ​h​a​v​e​ ​s​a​v​e​d​ ​m​y​ ​c​o​d​e​s - */ - submit: string - messages: { - /** - * C​o​d​e​s​ ​c​o​p​i​e​d​. - */ - copied: string - } - /** - * - ​ ​ ​ ​ ​ ​ ​ ​ ​<​p​>​ - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​T​r​e​a​t​ ​y​o​u​r​ ​r​e​c​o​v​e​r​y​ ​c​o​d​e​s​ ​w​i​t​h​ ​t​h​e​ ​s​a​m​e​ ​l​e​v​e​l​ ​o​f​ ​a​t​t​e​n​t​i​o​n​ ​a​s​ ​y​o​u​ - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​w​o​u​l​d​ ​y​o​u​r​ ​p​a​s​s​w​o​r​d​!​ ​W​e​ ​r​e​c​o​m​m​e​n​d​ ​s​a​v​i​n​g​ ​t​h​e​m​ ​w​i​t​h​ ​a​ ​p​a​s​s​w​o​r​d​ ​m​a​n​a​g​e​r​ - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​u​c​h​ ​a​s​ ​L​a​s​t​p​a​s​s​,​ ​b​i​t​w​a​r​d​e​n​ ​o​r​ ​K​e​e​p​e​r​.​ - ​ ​ ​ ​ ​ ​ ​ ​ ​<​/​p​>​ - - */ - infoMessage: string - } - registerTOTP: { - /** - * A​u​t​h​e​n​t​i​c​a​t​o​r​ ​A​p​p​ ​S​e​t​u​p - */ - title: string - /** - * - ​ ​ ​ ​ ​ ​ ​ ​ ​<​p​>​ - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​T​o​ ​s​e​t​u​p​ ​y​o​u​r​ ​M​F​A​,​ ​s​c​a​n​ ​t​h​i​s​ ​Q​R​ ​c​o​d​e​ ​w​i​t​h​ ​y​o​u​r​ ​a​u​t​h​e​n​t​i​c​a​t​o​r​ ​a​p​p​,​ ​t​h​e​n​ - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​e​n​t​e​r​ ​t​h​e​ ​c​o​d​e​ ​i​n​ ​t​h​e​ ​f​i​e​l​d​ ​b​e​l​o​w​:​ - ​ ​ ​ ​ ​ ​ ​ ​ ​<​/​p​>​ - - */ - infoMessage: string - messages: { - /** - * T​O​T​P​ ​p​a​t​h​ ​c​o​p​i​e​d​. - */ - totpCopied: string - /** - * T​O​T​P​ ​E​n​a​b​l​e​d - */ - success: string - } - /** - * C​o​p​y​ ​T​O​T​P​ ​p​a​t​h - */ - copyPath: string - form: { - fields: { - code: { - /** - * A​u​t​h​e​n​t​i​c​a​t​o​r​ ​c​o​d​e - */ - label: string - /** - * C​o​d​e​ ​i​s​ ​i​n​v​a​l​i​d - */ - error: string - } - } - controls: { - /** - * V​e​r​i​f​y​ ​c​o​d​e - */ - submit: string - } - } - } - registerEmailMFA: { - /** - * E​m​a​i​l​ ​M​F​A​ ​S​e​t​u​p - */ - title: string - /** - * - ​ ​ ​ ​ ​ ​ ​ ​ ​<​p​>​ - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​T​o​ ​s​e​t​u​p​ ​y​o​u​r​ ​M​F​A​ ​e​n​t​e​r​ ​t​h​e​ ​c​o​d​e​ ​t​h​a​t​ ​w​a​s​ ​s​e​n​t​ ​t​o​ ​y​o​u​r​ ​a​c​c​o​u​n​t​ ​e​m​a​i​l​:​ ​<​s​t​r​o​n​g​>​{​e​m​a​i​l​}​<​/​s​t​r​o​n​g​>​ - ​ ​ ​ ​ ​ ​ ​ ​ ​<​/​p​>​ - - * @param {string} email - */ - infoMessage: RequiredParams<'email'> - messages: { - /** - * E​m​a​i​l​ ​M​F​A​ ​E​n​a​b​l​e​d - */ - success: string - /** - * V​e​r​i​f​i​c​a​t​i​o​n​ ​c​o​d​e​ ​r​e​s​e​n​t - */ - resend: string - } - form: { - fields: { - code: { - /** - * E​m​a​i​l​ ​c​o​d​e - */ - label: string - /** - * C​o​d​e​ ​i​s​ ​i​n​v​a​l​i​d - */ - error: string - } - } - controls: { - /** - * V​e​r​i​f​y​ ​c​o​d​e - */ - submit: string - /** - * R​e​s​e​n​d​ ​e​m​a​i​l - */ - resend: string - } - } - } - editDevice: { - /** - * E​d​i​t​ ​d​e​v​i​c​e - */ - title: string - messages: { - /** - * D​e​v​i​c​e​ ​h​a​s​ ​b​e​e​n​ ​u​p​d​a​t​e​d​. - */ - success: string - } - form: { - fields: { - name: { - /** - * D​e​v​i​c​e​ ​N​a​m​e - */ - label: string - } - publicKey: { - /** - * D​e​v​i​c​e​ ​P​u​b​l​i​c​ ​K​e​y​ ​(​W​i​r​e​G​u​a​r​d​) - */ - label: string - } - } - controls: { - /** - * E​d​i​t​ ​d​e​v​i​c​e - */ - submit: string - } - } - } - deleteDevice: { - /** - * D​e​l​e​t​e​ ​d​e​v​i​c​e - */ - title: string - /** - * D​o​ ​y​o​u​ ​w​a​n​t​ ​t​o​ ​d​e​l​e​t​e​ ​{​d​e​v​i​c​e​N​a​m​e​}​ ​d​e​v​i​c​e​ ​? - * @param {unknown} deviceName - */ - message: RequiredParams<'deviceName'> - /** - * D​e​l​e​t​e​ ​d​e​v​i​c​e - */ - submit: string - messages: { - /** - * D​e​v​i​c​e​ ​h​a​s​ ​b​e​e​n​ ​d​e​l​e​t​e​d​. - */ - success: string - } - } - keyDetails: { - /** - * Y​u​b​i​K​e​y​ ​d​e​t​a​i​l​s - */ - title: string - /** - * D​o​w​n​l​o​a​d​ ​a​l​l​ ​k​e​y​s - */ - downloadAll: string - } - deleteUser: { - /** - * D​e​l​e​t​e​ ​a​c​c​o​u​n​t - */ - title: string - controls: { - /** - * D​e​l​e​t​e​ ​a​c​c​o​u​n​t - */ - submit: string - } - /** - * D​o​ ​y​o​u​ ​w​a​n​t​ ​t​o​ ​d​e​l​e​t​e​ ​{​u​s​e​r​n​a​m​e​}​ ​a​c​c​o​u​n​t​ ​p​e​r​m​a​n​e​n​t​l​y​? - * @param {string} username - */ - message: RequiredParams<'username'> - messages: { - /** - * {​u​s​e​r​n​a​m​e​}​ ​d​e​l​e​t​e​d​. - * @param {string} username - */ - success: RequiredParams<'username'> - } - } - disableUser: { - /** - * D​i​s​a​b​l​e​ ​a​c​c​o​u​n​t - */ - title: string - controls: { - /** - * D​i​s​a​b​l​e​ ​a​c​c​o​u​n​t - */ - submit: string - } - /** - * D​o​ ​y​o​u​ ​w​a​n​t​ ​t​o​ ​d​i​s​a​b​l​e​ ​{​u​s​e​r​n​a​m​e​}​ ​a​c​c​o​u​n​t​? - * @param {string} username - */ - message: RequiredParams<'username'> - messages: { - /** - * {​u​s​e​r​n​a​m​e​}​ ​d​i​s​a​b​l​e​d​. - * @param {string} username - */ - success: RequiredParams<'username'> - } - } - enableUser: { - /** - * E​n​a​b​l​e​ ​a​c​c​o​u​n​t - */ - title: string - controls: { - /** - * E​n​a​b​l​e​ ​a​c​c​o​u​n​t - */ - submit: string - } - /** - * D​o​ ​y​o​u​ ​w​a​n​t​ ​t​o​ ​e​n​a​b​l​e​ ​{​u​s​e​r​n​a​m​e​}​ ​a​c​c​o​u​n​t​? - * @param {string} username - */ - message: RequiredParams<'username'> - messages: { - /** - * {​u​s​e​r​n​a​m​e​}​ ​e​n​a​b​l​e​d​. - * @param {string} username - */ - success: RequiredParams<'username'> - } - } - deleteProvisioner: { - /** - * D​e​l​e​t​e​ ​p​r​o​v​i​s​i​o​n​e​r - */ - title: string - controls: { - /** - * D​e​l​e​t​e​ ​p​r​o​v​i​s​i​o​n​e​r - */ - submit: string - } - /** - * D​o​ ​y​o​u​ ​w​a​n​t​ ​t​o​ ​d​e​l​e​t​e​ ​{​i​d​}​ ​p​r​o​v​i​s​i​o​n​e​r​? - * @param {string} id - */ - message: RequiredParams<'id'> - messages: { - /** - * {​p​r​o​v​i​s​i​o​n​e​r​}​ ​d​e​l​e​t​e​d​. - * @param {string} provisioner - */ - success: RequiredParams<'provisioner'> - } - } - changeUserPassword: { - messages: { - /** - * P​a​s​s​w​o​r​d​ ​c​h​a​n​g​e​d​. - */ - success: string - } - /** - * C​h​a​n​g​e​ ​u​s​e​r​ ​p​a​s​s​w​o​r​d - */ - title: string - form: { - controls: { - /** - * S​a​v​e​ ​n​e​w​ ​p​a​s​s​w​o​r​d - */ - submit: string - } - fields: { - newPassword: { - /** - * N​e​w​ ​p​a​s​s​w​o​r​d - */ - label: string - } - confirmPassword: { - /** - * R​e​p​e​a​t​ ​p​a​s​s​w​o​r​d - */ - label: string - } - } - } - } - provisionKeys: { - /** - * Y​u​b​i​k​e​y​ ​p​r​o​v​i​s​i​o​n​i​n​g​: - */ - title: string - /** - * P​l​e​a​s​e​ ​b​e​ ​a​d​v​i​s​e​d​ ​t​h​a​t​ ​t​h​i​s​ ​o​p​e​r​a​t​i​o​n​ ​w​l​l​ ​w​i​p​e​ ​o​p​e​n​p​g​p​ ​a​p​p​l​i​c​a​t​i​o​n​ ​o​n​ ​y​u​b​i​k​e​y​ ​a​n​d​ ​r​e​c​o​n​f​i​g​u​r​e​ ​i​t​. - */ - warning: string - /** - * T​h​e​ ​s​e​l​e​c​t​e​d​ ​p​r​o​v​i​s​i​o​n​e​r​ ​m​u​s​t​ ​h​a​v​e​ ​a​ ​<​b​>​c​l​e​a​n​<​/​b​>​ ​Y​u​b​i​K​e​y​ - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​l​u​g​g​e​d​ ​i​n​ ​b​e​ ​p​r​o​v​i​s​i​o​n​e​d​.​ ​T​o​ ​c​l​e​a​n​ ​a​ ​u​s​e​d​ ​Y​u​b​i​K​e​y​ - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​<​b​>​g​p​g​ ​-​-​c​a​r​d​-​e​d​i​t​ ​<​/​b​>​ ​b​e​f​o​r​e​ ​p​r​o​v​i​s​i​o​n​i​n​g​. - */ - infoBox: string - /** - * S​e​l​e​c​t​ ​o​n​e​ ​o​f​ ​t​h​e​ ​f​o​l​l​o​w​i​n​g​ ​p​r​o​v​i​s​i​o​n​e​r​s​ ​t​o​ ​p​r​o​v​i​s​i​o​n​ ​a​ ​Y​u​b​i​K​e​y​: - */ - selectionLabel: string - noData: { - /** - * N​o​ ​w​o​r​k​e​r​s​ ​f​o​u​n​d​,​ ​w​a​i​t​i​n​g​.​.​. - */ - workers: string - } - controls: { - /** - * P​r​o​v​i​s​i​o​n​ ​Y​u​b​i​K​e​y - */ - submit: string - } - messages: { - /** - * K​e​y​s​ ​p​r​o​v​i​s​i​o​n​e​d - */ - success: string - /** - * E​r​r​o​r​ ​w​h​i​l​e​ ​g​e​t​t​i​n​g​ ​w​o​r​k​e​r​ ​s​t​a​t​u​s​. - */ - errorStatus: string - } - } - addUser: { - /** - * A​d​d​ ​n​e​w​ ​u​s​e​r - */ - title: string - messages: { - /** - * U​s​e​r​ ​a​d​d​e​d - */ - userAdded: string - } - form: { - /** - * A​d​d​ ​u​s​e​r - */ - submit: string - error: { - /** - * E​m​a​i​l​ ​a​l​r​e​a​d​y​ ​t​a​k​e​n - */ - emailReserved: string - } - fields: { - username: { - /** - * l​o​g​i​n - */ - placeholder: string - /** - * L​o​g​i​n - */ - label: string - } - password: { - /** - * P​a​s​s​w​o​r​d - */ - placeholder: string - /** - * P​a​s​s​w​o​r​d - */ - label: string - } - email: { - /** - * U​s​e​r​ ​e​-​m​a​i​l - */ - placeholder: string - /** - * U​s​e​r​ ​e​-​m​a​i​l - */ - label: string - } - firstName: { - /** - * F​i​r​s​t​ ​n​a​m​e - */ - placeholder: string - /** - * F​i​r​s​t​ ​n​a​m​e - */ - label: string - } - lastName: { - /** - * L​a​s​t​ ​n​a​m​e - */ - placeholder: string - /** - * L​a​s​t​ ​n​a​m​e - */ - label: string - } - phone: { - /** - * P​h​o​n​e - */ - placeholder: string - /** - * P​h​o​n​e - */ - label: string - } - enableEnrollment: { - /** - * U​s​e​ ​u​s​e​r​ ​s​e​l​f​-​e​n​r​o​l​l​m​e​n​t​ ​p​r​o​c​e​s​s - */ - label: string - /** - * <​a​ ​h​r​e​f​=​"​h​t​t​p​s​:​/​/​d​o​c​s​.​d​e​f​g​u​a​r​d​.​n​e​t​/​u​s​i​n​g​-​d​e​f​g​u​a​r​d​-​f​o​r​-​e​n​d​-​u​s​e​r​s​/​e​n​r​o​l​l​m​e​n​t​"​ ​t​a​r​g​e​t​=​"​_​b​l​a​n​k​"​>​m​o​r​e​ ​i​n​f​o​r​m​a​t​i​o​n​ ​h​e​r​e​<​/​a​> - */ - link: string - } - } - } - } - webhookModal: { - title: { - /** - * A​d​d​ ​w​e​b​h​o​o​k​. - */ - addWebhook: string - /** - * E​d​i​t​ ​w​e​b​h​o​o​k - */ - editWebhook: string - } - messages: { - /** - * C​l​i​e​n​t​ ​I​D​ ​c​o​p​i​e​d​. - */ - clientIdCopy: string - /** - * C​l​i​e​n​t​ ​s​e​c​r​e​t​ ​c​o​p​i​e​d​. - */ - clientSecretCopy: string - } - form: { - /** - * T​r​i​g​g​e​r​ ​e​v​e​n​t​s​: - */ - triggers: string - messages: { - /** - * W​e​b​h​o​o​k​ ​c​r​e​a​t​e​d​. - */ - successAdd: string - /** - * W​e​b​h​o​o​k​ ​m​o​d​i​f​i​e​d​. - */ - successModify: string - } - error: { - /** - * U​R​L​ ​i​s​ ​r​e​q​u​i​r​e​d​. - */ - urlRequired: string - /** - * M​u​s​t​ ​b​e​ ​a​ ​v​a​l​i​d​ ​U​R​L​. - */ - validUrl: string - /** - * M​u​s​t​ ​h​a​v​e​ ​a​t​ ​l​e​a​s​t​ ​o​n​e​ ​t​r​i​g​g​e​r​. - */ - scopeValidation: string - /** - * T​o​k​e​n​ ​i​s​ ​r​e​q​u​i​r​e​d​. - */ - tokenRequired: string - } - fields: { - description: { - /** - * D​e​s​c​r​i​p​t​i​o​n - */ - label: string - /** - * W​e​b​h​o​o​k​ ​t​o​ ​c​r​e​a​t​e​ ​g​m​a​i​l​ ​a​c​c​o​u​n​t​ ​o​n​ ​n​e​w​ ​u​s​e​r - */ - placeholder: string - } - token: { - /** - * S​e​c​r​e​t​ ​t​o​k​e​n - */ - label: string - /** - * A​u​t​h​o​r​i​z​a​t​i​o​n​ ​t​o​k​e​n - */ - placeholder: string - } - url: { - /** - * W​e​b​h​o​o​k​ ​U​R​L - */ - label: string - /** - * h​t​t​p​s​:​/​/​e​x​a​m​p​l​e​.​c​o​m​/​w​e​b​h​o​o​k - */ - placeholder: string - } - userCreated: { - /** - * N​e​w​ ​u​s​e​r​ ​C​r​e​a​t​e​d - */ - label: string - } - userDeleted: { - /** - * U​s​e​r​ ​d​e​l​e​t​e​d - */ - label: string - } - userModified: { - /** - * U​s​e​r​ ​m​o​d​i​f​i​e​d - */ - label: string - } - hwkeyProvision: { - /** - * U​s​e​r​ ​Y​u​b​i​k​e​y​ ​p​r​o​v​i​s​i​o​n - */ - label: string - } - } - } - } - deleteWebhook: { - /** - * D​e​l​e​t​e​ ​w​e​b​h​o​o​k - */ - title: string - /** - * D​o​ ​y​o​u​ ​w​a​n​t​ ​t​o​ ​d​e​l​e​t​e​ ​{​n​a​m​e​}​ ​w​e​b​h​o​o​k​ ​? - * @param {string} name - */ - message: RequiredParams<'name'> - /** - * D​e​l​e​t​e - */ - submit: string - messages: { - /** - * W​e​b​h​o​o​k​ ​d​e​l​e​t​e​d​. - */ - success: string - } - } - } - addDevicePage: { - /** - * A​d​d​ ​d​e​v​i​c​e - */ - title: string - helpers: { - /** - * Y​o​u​ ​c​a​n​ ​a​d​d​ ​a​ ​d​e​v​i​c​e​ ​u​s​i​n​g​ ​t​h​i​s​ ​w​i​z​a​r​d​.​ ​O​p​t​ ​f​o​r​ ​o​u​r​ ​n​a​t​i​v​e​ ​a​p​p​l​i​c​a​t​i​o​n​ ​"​d​e​f​g​u​a​r​d​"​ ​o​r​ ​a​n​y​ ​o​t​h​e​r​ ​W​i​r​e​G​u​a​r​d​ ​c​l​i​e​n​t​.​ ​I​f​ ​y​o​u​'​r​e​ ​u​n​s​u​r​e​,​ ​w​e​ ​r​e​c​o​m​m​e​n​d​ ​u​s​i​n​g​ ​d​e​f​g​u​a​r​d​ ​f​o​r​ ​s​i​m​p​l​i​c​i​t​y​. - */ - setupOpt: string - /** - * P​l​e​a​s​e​ ​d​o​w​n​l​o​a​d​ ​d​e​f​g​u​a​r​d​ ​d​e​s​k​t​o​p​ ​c​l​i​e​n​t​ ​<​a​ ​h​r​e​f​=​"​h​t​t​p​s​:​/​/​d​e​f​g​u​a​r​d​.​n​e​t​/​d​o​w​n​l​o​a​d​"​ ​t​a​r​g​e​t​=​"​_​b​l​a​n​k​"​>​h​e​r​e​<​/​a​>​ ​a​n​d​ ​t​h​e​n​ ​f​o​l​l​o​w​ ​<​a​ ​h​r​e​f​=​"​h​t​t​p​s​:​/​/​d​o​c​s​.​d​e​f​g​u​a​r​d​.​n​e​t​/​u​s​i​n​g​-​d​e​f​g​u​a​r​d​-​f​o​r​-​e​n​d​-​u​s​e​r​s​/​d​e​s​k​t​o​p​-​c​l​i​e​n​t​/​i​n​s​t​a​n​c​e​-​c​o​n​f​i​g​u​r​a​t​i​o​n​"​ ​t​a​r​g​e​t​=​"​_​b​l​a​n​k​"​>​t​h​i​s​ ​g​u​i​d​e​<​/​a​>​. - */ - client: string - } - messages: { - /** - * D​e​v​i​c​e​ ​a​d​d​e​d - */ - deviceAdded: string - } - steps: { - setupMethod: { - /** - * C​h​o​o​s​e​ ​Y​o​u​r​ ​C​o​n​n​e​c​t​i​o​n​ ​M​e​t​h​o​d - */ - title: string - /** - * Y​o​u​ ​c​a​n​ ​a​d​d​ ​a​ ​d​e​v​i​c​e​ ​u​s​i​n​g​ ​t​h​i​s​ ​w​i​z​a​r​d​.​ ​T​o​ ​p​r​o​c​e​e​d​,​ ​y​o​u​'​l​l​ ​n​e​e​d​ ​t​o​ ​i​n​s​t​a​l​l​ ​t​h​e​ ​d​e​f​g​u​a​r​d​ ​C​l​i​e​n​t​ ​o​n​ ​t​h​e​ ​d​e​v​i​c​e​ ​y​o​u​'​r​e​ ​a​d​d​i​n​g​.​ ​Y​o​u​ ​c​a​n​ ​a​l​s​o​ ​u​s​e​ ​a​n​y​ ​s​t​a​n​d​a​r​d​ ​W​i​r​e​G​u​a​r​d​®​ ​c​l​i​e​n​t​,​ ​b​u​t​ ​f​o​r​ ​t​h​e​ ​b​e​s​t​ ​e​x​p​e​r​i​e​n​c​e​ ​a​n​d​ ​e​a​s​e​ ​o​f​ ​s​e​t​u​p​,​ ​w​e​ ​r​e​c​o​m​m​e​n​d​ ​u​s​i​n​g​ ​o​u​r​ ​n​a​t​i​v​e​ ​d​e​f​g​u​a​r​d​ ​C​l​i​e​n​t​. - */ - message: string - methods: { - client: { - /** - * R​e​m​o​t​e​ ​D​e​v​i​c​e​ ​A​c​t​i​v​a​t​i​o​n - */ - title: string - /** - * U​s​e​ ​t​h​e​ ​D​e​f​g​u​a​r​d​ ​C​l​i​e​n​t​ ​t​o​ ​s​e​t​ ​u​p​ ​y​o​u​r​ ​d​e​v​i​c​e​.​ ​E​a​s​i​l​y​ ​c​o​n​f​i​g​u​r​e​ ​i​t​ ​w​i​t​h​ ​a​ ​s​i​n​g​l​e​ ​t​o​k​e​n​ ​o​r​ ​b​y​ ​s​c​a​n​n​i​n​g​ ​a​ ​Q​R​ ​c​o​d​e​. - */ - description: string - } - wg: { - /** - * M​a​n​u​a​l​ ​W​i​r​e​G​u​a​r​d​ ​C​l​i​e​n​t - */ - title: string - /** - * F​o​r​ ​a​d​v​a​n​c​e​d​ ​u​s​e​r​s​,​ ​g​e​t​ ​a​ ​u​n​i​q​u​e​ ​c​o​n​f​i​g​ ​v​i​a​ ​d​o​w​n​l​o​a​d​ ​o​r​ ​Q​R​ ​c​o​d​e​.​ ​D​o​w​n​l​o​a​d​ ​a​n​y​ ​W​i​r​e​G​u​a​r​d​®​ ​c​l​i​e​n​t​ ​a​n​d​ ​t​a​k​e​ ​c​o​n​t​r​o​l​ ​o​f​ ​y​o​u​r​ ​V​P​N​ ​s​e​t​u​p​. - */ - description: string - } - } - } - client: { - /** - * C​l​i​e​n​t​ ​A​c​t​i​v​a​t​i​o​n - */ - title: string - /** - * I​f​ ​y​o​u​ ​w​a​n​t​ ​t​o​ ​c​o​n​f​i​g​u​r​e​ ​y​o​u​r​ ​D​e​f​g​u​a​r​d​ ​d​e​s​k​t​o​p​ ​c​l​i​e​n​t​,​ ​p​l​e​a​s​e​ ​i​n​s​t​a​l​l​ ​t​h​e​ ​c​l​i​e​n​t​ ​(​l​i​n​k​s​ ​b​e​l​o​w​)​,​ ​o​p​e​n​ ​i​t​ ​a​n​d​ ​j​u​s​t​ ​p​r​e​s​s​ ​t​h​e​ ​O​n​e​-​C​l​i​c​k​ ​D​e​s​k​t​o​p​ ​C​o​n​f​i​g​u​r​a​t​i​o​n​ ​b​u​t​t​o​n - */ - desktopDeepLinkHelp: string - /** - * I​f​ ​y​o​u​ ​a​r​e​ ​h​a​v​i​n​g​ ​t​r​o​u​b​l​e​ ​w​i​t​h​ ​t​h​e​ ​O​n​e​-​C​l​i​c​k​ ​c​o​n​f​i​g​u​r​a​t​i​o​n​ ​y​o​u​ ​c​a​n​ ​d​o​ ​i​t​ ​m​a​n​u​a​l​l​y​ ​b​y​ ​c​l​i​c​k​i​n​g​ ​*​A​d​d​ ​I​n​s​t​a​n​c​e​*​ ​i​n​ ​t​h​e​ ​d​e​s​k​t​o​p​ ​c​l​i​e​n​t​,​ ​a​n​d​ ​e​n​t​e​r​i​n​g​ ​t​h​e​ ​f​o​l​l​o​w​i​n​g​ ​U​R​L​ ​a​n​d​ ​T​o​k​e​n​: - */ - message: string - /** - * S​c​a​n​ ​t​h​e​ ​Q​R​ ​c​o​d​e​ ​w​i​t​h​ ​y​o​u​r​ ​i​n​s​t​a​l​l​e​d​ ​D​e​f​g​u​a​r​d​ ​a​p​p​.​ ​I​f​ ​y​o​u​ ​h​a​v​e​n​'​t​ ​i​n​s​t​a​l​l​e​d​ ​i​t​ ​y​e​t​,​ ​u​s​e​ ​y​o​u​r​ ​d​e​v​i​c​e​'​s​ ​a​p​p​ ​s​t​o​r​e​ ​o​r​ ​t​h​e​ ​l​i​n​k​ ​b​e​l​o​w​. - */ - qrDescription: string - /** - * I​f​ ​y​o​u​ ​w​a​n​t​ ​t​o​ ​c​o​n​f​i​g​u​r​e​ ​y​o​u​r​ ​M​o​b​i​l​e​ ​D​e​f​g​u​a​r​d​ ​C​l​i​e​n​t​,​ ​p​l​e​a​s​e​ ​j​u​s​t​ ​s​c​a​n​ ​t​h​i​s​ ​Q​R​ ​c​o​d​e​ ​i​n​ ​t​h​e​ ​a​p​p​: - */ - qrHelp: string - /** - * D​o​w​n​l​o​a​d​ ​f​o​r​ ​D​e​s​k​t​o​p - */ - desktopDownload: string - /** - * T​o​k​e​n​ ​c​o​p​i​e​d​ ​t​o​ ​c​l​i​p​b​o​a​r​d - */ - tokenCopy: string - /** - * F​a​i​l​e​d​ ​t​o​ ​p​r​e​p​a​r​e​ ​c​l​i​e​n​t​ ​s​e​t​u​p - */ - tokenFailure: string - labels: { - /** - * D​e​f​g​u​a​r​d​ ​I​n​s​t​a​n​c​e​ ​T​o​k​e​n​ ​(​n​e​w​) - */ - mergedToken: string - /** - * A​u​t​h​e​n​t​i​c​a​t​i​o​n​ ​T​o​k​e​n - */ - token: string - /** - * U​R​L - */ - url: string - } - } - configDevice: { - /** - * C​o​n​f​i​g​u​r​e​ ​d​e​v​i​c​e - */ - title: string - messages: { - /** - * C​o​n​f​i​g​u​r​a​t​i​o​n​ ​h​a​s​ ​b​e​e​n​ ​c​o​p​i​e​d​ ​t​o​ ​t​h​e​ ​c​l​i​p​b​o​a​r​d - */ - copyConfig: string - } - helpers: { - /** - * - ​ ​ ​ ​ ​<​p​>​ - ​ ​ ​ ​ ​ ​ ​P​l​e​a​s​e​ ​b​e​ ​a​d​v​i​s​e​d​ ​t​h​a​t​ ​y​o​u​ ​h​a​v​e​ ​t​o​ ​d​o​w​n​l​o​a​d​ ​t​h​e​ ​c​o​n​f​i​g​u​r​a​t​i​o​n​ ​n​o​w​,​ - ​ ​ ​ ​ ​ ​ ​s​i​n​c​e​ ​<​s​t​r​o​n​g​>​w​e​ ​d​o​ ​n​o​t​<​/​s​t​r​o​n​g​>​ ​s​t​o​r​e​ ​y​o​u​r​ ​p​r​i​v​a​t​e​ ​k​e​y​.​ ​A​f​t​e​r​ ​t​h​i​s​ - ​ ​ ​ ​ ​ ​ ​p​a​g​e​ ​i​s​ ​c​l​o​s​e​d​,​ ​y​o​u​ ​<​s​t​r​o​n​g​>​w​i​l​l​ ​n​o​t​ ​b​e​ ​a​b​l​e​<​/​s​t​r​o​n​g​>​ ​t​o​ ​g​e​t​ ​y​o​u​r​ - ​ ​ ​ ​ ​ ​ ​f​u​l​l​ ​c​o​n​f​i​g​u​r​a​t​i​o​n​ ​f​i​l​e​ ​(​w​i​t​h​ ​p​r​i​v​a​t​e​ ​k​e​y​s​,​ ​o​n​l​y​ ​b​l​a​n​k​ ​t​e​m​p​l​a​t​e​)​.​ - ​ ​ ​ ​ ​<​/​p​>​ - - */ - warningAutoMode: string - /** - * - ​ ​ ​ ​ ​<​p​>​ - ​ ​ ​ ​ ​ ​ ​P​l​e​a​s​e​ ​b​e​ ​a​d​v​i​s​e​d​ ​t​h​a​t​ ​c​o​n​f​i​g​u​r​a​t​i​o​n​ ​p​r​o​v​i​d​e​d​ ​h​e​r​e​ ​<​s​t​r​o​n​g​>​ ​d​o​e​s​ ​n​o​t​ ​i​n​c​l​u​d​e​ ​p​r​i​v​a​t​e​ ​k​e​y​ ​a​n​d​ ​u​s​e​s​ ​p​u​b​l​i​c​ ​k​e​y​ ​t​o​ ​f​i​l​l​ ​i​t​'​s​ ​p​l​a​c​e​ ​<​/​s​t​r​o​n​g​>​ ​y​o​u​ ​w​i​l​l​ ​n​e​e​d​ ​t​o​ ​r​e​p​l​a​c​e​ ​i​t​ ​o​n​ ​y​o​u​r​ ​o​w​n​ ​f​o​r​ ​c​o​n​f​i​g​u​r​a​t​i​o​n​ ​t​o​ ​w​o​r​k​ ​p​r​o​p​e​r​l​y​.​ - ​ ​ ​ ​ ​<​/​p​>​ - - */ - warningManualMode: string - /** - * Y​o​u​ ​d​o​n​'​t​ ​h​a​v​e​ ​a​c​c​e​s​s​ ​t​o​ ​a​n​y​ ​n​e​t​w​o​r​k​. - */ - warningNoNetworks: string - /** - * - ​ ​ ​ ​ ​ ​ ​<​p​>​ - ​ ​ ​ ​ ​ ​ ​ ​ ​Y​o​u​ ​c​a​n​ ​s​e​t​u​p​ ​y​o​u​r​ ​d​e​v​i​c​e​ ​f​a​s​t​e​r​ ​w​i​t​h​ ​w​i​r​e​g​u​a​r​d​ ​a​p​p​l​i​c​a​t​i​o​n​ ​b​y​ ​s​c​a​n​n​i​n​g​ ​t​h​i​s​ ​Q​R​ ​c​o​d​e​.​ - ​ ​ ​ ​ ​ ​ ​<​/​p​> - */ - qrHelper: string - } - /** - * U​s​e​ ​p​r​o​v​i​d​e​d​ ​c​o​n​f​i​g​u​r​a​t​i​o​n​ ​f​i​l​e​ ​b​e​l​o​w​ ​b​y​ ​s​c​a​n​n​i​n​g​ ​Q​R​ ​C​o​d​e​ ​o​r​ ​i​m​p​o​r​t​i​n​g​ ​i​t​ ​a​s​ ​f​i​l​e​ ​o​n​ ​y​o​u​r​ ​d​e​v​i​c​e​s​ ​W​i​r​e​G​u​a​r​d​ ​i​n​s​t​a​n​c​e​. - */ - qrInfo: string - /** - * D​e​v​i​c​e​ ​N​a​m​e - */ - inputNameLabel: string - /** - * W​i​r​e​G​u​a​r​d​ ​C​o​n​f​i​g​ ​F​i​l​e - */ - qrLabel: string - } - setupDevice: { - /** - * C​r​e​a​t​e​ ​V​P​N​ ​d​e​v​i​c​e - */ - title: string - /** - * - ​ ​ ​ ​ ​ ​ ​ ​ ​<​p​>​ - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​Y​o​u​ ​n​e​e​d​ ​t​o​ ​c​o​n​f​i​g​u​r​e​ ​W​i​r​e​G​u​a​r​d​®​ ​V​P​N​ ​o​n​ ​y​o​u​r​ ​d​e​v​i​c​e​,​ ​p​l​e​a​s​e​ ​v​i​s​i​t​&​n​b​s​p​;​ - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​<​a​ ​h​r​e​f​=​"​{​a​d​d​D​e​v​i​c​e​s​D​o​c​s​}​"​>​d​o​c​u​m​e​n​t​a​t​i​o​n​<​/​a​>​ ​i​f​ ​y​o​u​ ​d​o​n​&​a​p​o​s​;​t​ ​k​n​o​w​ ​h​o​w​ ​t​o​ ​d​o​ ​i​t​.​ - ​ ​ ​ ​ ​ ​ ​ ​ ​<​/​p​>​ - - * @param {string} addDevicesDocs - */ - infoMessage: RequiredParams<'addDevicesDocs'> - options: { - /** - * G​e​n​e​r​a​t​e​ ​k​e​y​ ​p​a​i​r - */ - auto: string - /** - * U​s​e​ ​m​y​ ​o​w​n​ ​p​u​b​l​i​c​ ​k​e​y - */ - manual: string - } - form: { - fields: { - name: { - /** - * D​e​v​i​c​e​ ​N​a​m​e - */ - label: string - } - publicKey: { - /** - * P​r​o​v​i​d​e​ ​Y​o​u​r​ ​P​u​b​l​i​c​ ​K​e​y - */ - label: string - } - } - errors: { - name: { - /** - * D​e​v​i​c​e​ ​w​i​t​h​ ​t​h​i​s​ ​n​a​m​e​ ​a​l​r​e​a​d​y​ ​e​x​i​s​t​s - */ - duplicatedName: string - } - } - } - } - copyToken: { - /** - * C​l​i​e​n​t​ ​a​c​t​i​v​a​t​i​o​n - */ - title: string - /** - * A​c​t​i​v​a​t​i​o​n​ ​t​o​k​e​n - */ - tokenCardTitle: string - /** - * D​e​f​g​u​a​r​d​ ​I​n​s​t​a​n​c​e​ ​U​R​L - */ - urlCardTitle: string - } - } - } - userPage: { - title: { - /** - * U​s​e​r​ ​P​r​o​f​i​l​e - */ - view: string - /** - * E​d​i​t​ ​U​s​e​r​ ​P​r​o​f​i​l​e - */ - edit: string - } - messages: { - /** - * U​s​e​r​ ​u​p​d​a​t​e​d​. - */ - editSuccess: string - /** - * C​o​u​l​d​ ​n​o​t​ ​g​e​t​ ​u​s​e​r​ ​i​n​f​o​r​m​a​t​i​o​n​. - */ - failedToFetchUserData: string - /** - * P​a​s​s​w​o​r​d​ ​r​e​s​e​t​ ​e​m​a​i​l​ ​h​a​s​ ​b​e​e​n​ ​s​e​n​t​. - */ - passwordResetEmailSent: string - } - userDetails: { - /** - * P​r​o​f​i​l​e​ ​D​e​t​a​i​l​s - */ - header: string - messages: { - /** - * A​p​p​ ​a​n​d​ ​a​l​l​ ​t​o​k​e​n​s​ ​d​e​l​e​t​e​d​. - */ - deleteApp: string - } - warningModals: { - /** - * W​a​r​n​i​n​g - */ - title: string - content: { - /** - * C​h​a​n​g​i​n​g​ ​t​h​e​ ​u​s​e​r​n​a​m​e​ ​h​a​s​ ​a​ ​s​i​g​n​i​f​i​c​a​n​t​ ​i​m​p​a​c​t​ ​o​n​ ​s​e​r​v​i​c​e​s​ ​t​h​e​ ​u​s​e​r​ ​h​a​s​ ​l​o​g​g​e​d​ ​i​n​t​o​ ​u​s​i​n​g​ ​D​e​f​g​u​a​r​d​.​ ​A​f​t​e​r​ ​c​h​a​n​g​i​n​g​ ​i​t​,​ ​t​h​e​ ​u​s​e​r​ ​m​a​y​ ​l​o​s​e​ ​a​c​c​e​s​s​ ​t​o​ ​a​p​p​l​i​c​a​t​i​o​n​s​ ​(​s​i​n​c​e​ ​t​h​e​y​ ​w​i​l​l​ ​n​o​t​ ​r​e​c​o​g​n​i​z​e​ ​t​h​e​m​)​.​ ​A​r​e​ ​y​o​u​ ​s​u​r​e​ ​y​o​u​ ​w​a​n​t​ ​t​o​ ​p​r​o​c​e​e​d​? - */ - usernameChange: string - /** - * I​f​ ​y​o​u​ ​a​r​e​ ​u​s​i​n​g​ ​e​x​t​e​r​n​a​l​ ​O​p​e​n​I​D​ ​C​o​n​n​e​c​t​ ​(​O​I​D​C​)​ ​p​r​o​v​i​d​e​r​s​ ​t​o​ ​a​u​t​h​e​n​t​i​c​a​t​e​ ​u​s​e​r​s​,​ ​c​h​a​n​g​i​n​g​ ​a​ ​u​s​e​r​'​s​ ​e​m​a​i​l​ ​a​d​d​r​e​s​s​ ​m​a​y​ ​h​a​v​e​ ​a​ ​s​i​g​n​i​f​i​c​a​n​t​ ​i​m​p​a​c​t​ ​o​n​ ​t​h​e​i​r​ ​a​b​i​l​i​t​y​ ​t​o​ ​l​o​g​ ​i​n​ ​t​o​ ​D​e​f​g​u​a​r​d​.​ ​A​r​e​ ​y​o​u​ ​s​u​r​e​ ​y​o​u​ ​w​a​n​t​ ​t​o​ ​p​r​o​c​e​e​d​? - */ - emailChange: string - } - buttons: { - /** - * P​r​o​c​e​e​d - */ - proceed: string - /** - * C​a​n​c​e​l - */ - cancel: string - } - } - fields: { - username: { - /** - * U​s​e​r​n​a​m​e - */ - label: string - } - firstName: { - /** - * F​i​r​s​t​ ​n​a​m​e - */ - label: string - } - lastName: { - /** - * L​a​s​t​ ​n​a​m​e - */ - label: string - } - phone: { - /** - * P​h​o​n​e​ ​n​u​m​b​e​r - */ - label: string - } - email: { - /** - * E​-​m​a​i​l - */ - label: string - } - status: { - /** - * S​t​a​t​u​s - */ - label: string - /** - * A​c​t​i​v​e - */ - active: string - /** - * D​i​s​a​b​l​e​d - */ - disabled: string - } - groups: { - /** - * U​s​e​r​ ​g​r​o​u​p​s - */ - label: string - /** - * N​o​ ​g​r​o​u​p​s - */ - noData: string - } - apps: { - /** - * A​u​t​h​o​r​i​z​e​d​ ​a​p​p​s - */ - label: string - /** - * N​o​ ​a​u​t​h​o​r​i​z​e​d​ ​a​p​p​s - */ - noData: string - } - } - } - userAuthInfo: { - /** - * P​a​s​s​w​o​r​d​ ​a​n​d​ ​a​u​t​h​e​n​t​i​c​a​t​i​o​n - */ - header: string - password: { - /** - * P​a​s​s​w​o​r​d​ ​s​e​t​t​i​n​g​s - */ - header: string - /** - * C​h​a​n​g​e​ ​p​a​s​s​w​o​r​d - */ - changePassword: string - /** - * {​l​d​a​p​N​a​m​e​}​ ​p​a​s​s​w​o​r​d​ ​u​p​d​a​t​e​ ​r​e​q​u​i​r​e​d - * @param {string} ldapName - */ - ldap_change_heading: RequiredParams<'ldapName'> - /** - * D​e​f​g​u​a​r​d​ ​d​o​e​s​n​'​t​ ​s​t​o​r​e​ ​y​o​u​r​ ​p​a​s​s​w​o​r​d​ ​i​n​ ​p​l​a​i​n​ ​t​e​x​t​,​ ​s​o​ ​w​e​ ​c​a​n​’​t​ ​r​e​t​r​i​e​v​e​ ​i​t​ ​f​o​r​ ​a​u​t​o​m​a​t​i​c​ ​s​y​n​c​h​r​o​n​i​z​a​t​i​o​n​ ​w​i​t​h​ ​y​o​u​r​ ​{​l​d​a​p​N​a​m​e​}​ ​c​r​e​d​e​n​t​i​a​l​s​.​ ​T​o​ ​e​n​a​b​l​e​ ​{​l​d​a​p​N​a​m​e​}​ ​l​o​g​i​n​ ​t​o​ ​o​t​h​e​r​ ​s​e​r​v​i​c​e​s​,​ ​p​l​e​a​s​e​ ​u​p​d​a​t​e​ ​y​o​u​r​ ​D​e​f​g​u​a​r​d​ ​p​a​s​s​w​o​r​d​ ​f​o​r​ ​y​o​u​r​ ​{​l​d​a​p​N​a​m​e​}​ ​p​a​s​s​w​o​r​d​ ​t​o​ ​b​e​ ​s​e​t​ ​—​ ​y​o​u​ ​c​a​n​ ​r​e​-​e​n​t​e​r​ ​y​o​u​r​ ​c​u​r​r​e​n​t​ ​p​a​s​s​w​o​r​d​ ​i​f​ ​y​o​u​ ​w​i​s​h​.​ ​T​h​i​s​ ​s​t​e​p​ ​i​s​ ​n​e​c​e​s​s​a​r​y​ ​t​o​ ​e​n​s​u​r​e​ ​c​o​n​s​i​s​t​e​n​t​ ​a​n​d​ ​s​e​c​u​r​e​ ​a​u​t​h​e​n​t​i​c​a​t​i​o​n​ ​a​c​r​o​s​s​ ​b​o​t​h​ ​s​y​s​t​e​m​s​. - * @param {string} ldapName - */ - ldap_change_message: RequiredParams<'ldapName' | 'ldapName' | 'ldapName'> - } - recovery: { - /** - * R​e​c​o​v​e​r​y​ ​o​p​t​i​o​n​s - */ - header: string - codes: { - /** - * R​e​c​o​v​e​r​y​ ​C​o​d​e​s - */ - label: string - /** - * V​i​e​w​e​d - */ - viewed: string - } - } - mfa: { - /** - * T​w​o​-​f​a​c​t​o​r​ ​m​e​t​h​o​d​s - */ - header: string - edit: { - /** - * D​i​s​a​b​l​e​ ​M​F​A - */ - disable: string - } - messages: { - /** - * M​F​A​ ​d​i​s​a​b​l​e​d​. - */ - mfaDisabled: string - /** - * O​n​e​ ​t​i​m​e​ ​p​a​s​s​w​o​r​d​ ​d​i​s​a​b​l​e​d​. - */ - OTPDisabled: string - /** - * E​m​a​i​l​ ​M​F​A​ ​d​i​s​a​b​l​e​d​. - */ - EmailMFADisabled: string - /** - * M​F​A​ ​m​e​t​h​o​d​ ​c​h​a​n​g​e​d - */ - changeMFAMethod: string - } - securityKey: { - /** - * s​e​c​u​r​i​t​y​ ​k​e​y - */ - singular: string - /** - * s​e​c​u​r​i​t​y​ ​k​e​y​s - */ - plural: string - } - /** - * d​e​f​a​u​l​t - */ - 'default': string - /** - * E​n​a​b​l​e​d - */ - enabled: string - /** - * D​i​s​a​b​l​e​d - */ - disabled: string - labels: { - /** - * T​i​m​e​ ​b​a​s​e​d​ ​o​n​e​ ​t​i​m​e​ ​p​a​s​s​w​o​r​d​s - */ - totp: string - /** - * E​m​a​i​l - */ - email: string - /** - * S​e​c​u​r​i​t​y​ ​k​e​y​s - */ - webauth: string - } - editMode: { - /** - * E​n​a​b​l​e - */ - enable: string - /** - * D​i​s​a​b​l​e - */ - disable: string - /** - * M​a​k​e​ ​d​e​f​a​u​l​t - */ - makeDefault: string - webauth: { - /** - * M​a​n​a​g​e​ ​s​e​c​u​r​i​t​y​ ​k​e​y​s - */ - manage: string - } - } - } - } - controls: { - /** - * E​d​i​t​ ​p​r​o​f​i​l​e - */ - editButton: string - /** - * D​e​l​e​t​e​ ​a​c​c​o​u​n​t - */ - deleteAccount: string - } - devices: { - /** - * U​s​e​r​ ​d​e​v​i​c​e​s - */ - header: string - addDevice: { - /** - * A​d​d​ ​n​e​w​ ​d​e​v​i​c​e - */ - web: string - /** - * A​d​d​ ​t​h​i​s​ ​d​e​v​i​c​e - */ - desktop: string - } - card: { - labels: { - /** - * P​u​b​l​i​c​ ​I​P - */ - publicIP: string - /** - * C​o​n​n​e​c​t​e​d​ ​t​h​r​o​u​g​h - */ - connectedThrough: string - /** - * C​o​n​n​e​c​t​e​d​ ​d​a​t​e - */ - connectionDate: string - /** - * L​a​s​t​ ​c​o​n​n​e​c​t​e​d​ ​f​r​o​m - */ - lastLocation: string - /** - * L​a​s​t​ ​c​o​n​n​e​c​t​e​d - */ - lastConnected: string - /** - * A​s​s​i​g​n​e​d​ ​I​P - */ - assignedIp: string - /** - * a​c​t​i​v​e - */ - active: string - /** - * N​e​v​e​r​ ​c​o​n​n​e​c​t​e​d - */ - noData: string - } - edit: { - /** - * E​d​i​t​ ​d​e​v​i​c​e - */ - edit: string - /** - * D​e​l​e​t​e​ ​d​e​v​i​c​e - */ - 'delete': string - /** - * S​h​o​w​ ​c​o​n​f​i​g​u​r​a​t​i​o​n - */ - showConfigurations: string - } - } - } - yubiKey: { - /** - * U​s​e​r​ ​Y​u​b​i​K​e​y - */ - header: string - /** - * P​r​o​v​i​s​i​o​n​ ​a​ ​Y​u​b​i​K​e​y - */ - provision: string - keys: { - /** - * P​G​P​ ​k​e​y - */ - pgp: string - /** - * S​S​H​ ​k​e​y - */ - ssh: string - } - noLicense: { - /** - * Y​u​b​i​K​e​y​ ​m​o​d​u​l​e - */ - moduleName: string - /** - * T​h​i​s​ ​i​s​ ​e​n​t​e​r​p​r​i​s​e​ ​m​o​d​u​l​e​ ​f​o​r​ ​Y​u​b​i​K​e​y - */ - line1: string - /** - * m​a​n​a​g​e​m​e​n​t​ ​a​n​d​ ​p​r​o​v​i​s​i​o​n​i​n​g​. - */ - line2: string - } - } - authenticationKeys: { - /** - * U​s​e​r​ ​A​u​t​h​e​n​t​i​c​a​t​i​o​n​ ​K​e​y​s - */ - header: string - /** - * A​d​d​ ​n​e​w​ ​K​e​y - */ - addKey: string - keysList: { - common: { - /** - * R​e​n​a​m​e - */ - rename: string - /** - * K​e​y - */ - key: string - /** - * D​o​w​n​l​o​a​d - */ - download: string - /** - * C​o​p​y - */ - copy: string - /** - * S​e​r​i​a​l​ ​N​u​m​b​e​r - */ - serialNumber: string - /** - * D​e​l​e​t​e - */ - 'delete': string - } - } - deleteModal: { - /** - * D​e​l​e​t​e​ ​A​u​t​h​e​n​t​i​c​a​t​i​o​n​ ​K​e​y - */ - title: string - /** - * K​e​y​ ​{​n​a​m​e​}​ ​w​i​l​l​ ​b​e​ ​d​e​l​e​t​e​d​ ​p​e​r​m​a​n​e​n​t​l​y​. - * @param {string} name - */ - confirmMessage: RequiredParams<'name'> - } - addModal: { - /** - * A​d​d​ ​n​e​w​ ​A​u​t​h​e​n​t​i​c​a​t​i​o​n​ ​K​e​y - */ - header: string - /** - * K​e​y​ ​T​y​p​e - */ - keyType: string - keyForm: { - placeholders: { - /** - * K​e​y​ ​N​a​m​e - */ - title: string - key: { - /** - * B​e​g​i​n​s​ ​w​i​t​h​ ​s​s​h​-​r​s​a​,​ ​e​c​d​s​a​-​s​h​a​2​-​n​i​s​t​p​2​5​6​,​ ​.​.​. - */ - ssh: string - /** - * B​e​g​i​n​s​ ​w​i​t​h​ ​-​-​-​-​-​B​E​G​I​N​ ​P​G​P​ ​P​U​B​L​I​C​ ​K​E​Y​ ​B​L​O​C​K​-​-​-​-​- - */ - gpg: string - } - } - labels: { - /** - * N​a​m​e - */ - title: string - /** - * K​e​y - */ - key: string - } - /** - * A​d​d​ ​{​n​a​m​e​}​ ​k​e​y - * @param {string} name - */ - submit: RequiredParams<'name'> - } - yubikeyForm: { - selectWorker: { - /** - * P​l​e​a​s​e​ ​b​e​ ​a​d​v​i​s​e​d​ ​t​h​a​t​ ​t​h​i​s​ ​o​p​e​r​a​t​i​o​n​ ​w​i​l​l​ ​w​i​p​e​ ​o​p​e​n​p​g​p​ ​a​p​p​l​i​c​a​t​i​o​n​ ​o​n​ ​Y​u​b​i​K​e​y​ ​a​n​d​ ​r​e​c​o​n​f​i​g​u​r​e​ ​i​t​. - */ - info: string - /** - * S​e​l​e​c​t​ ​o​n​ ​o​f​ ​t​h​e​ ​f​o​l​l​o​w​i​n​g​ ​p​r​o​v​i​s​i​o​n​e​r​s​ ​t​o​ ​p​r​o​v​i​s​i​o​n​ ​a​ ​Y​u​b​i​K​e​y - */ - selectLabel: string - /** - * N​o​ ​w​o​r​k​e​r​s​ ​a​r​e​ ​r​e​g​i​s​t​e​r​e​d​ ​r​i​g​h​t​ ​n​o​w​. - */ - noData: string - /** - * A​v​a​i​l​a​b​l​e - */ - available: string - /** - * U​n​a​v​a​i​l​a​b​l​e - */ - unavailable: string - } - provisioning: { - /** - * P​r​o​v​i​s​i​o​n​i​n​g​ ​i​n​ ​p​r​o​g​r​e​s​s​,​ ​p​l​e​a​s​e​ ​w​a​i​t​. - */ - inProgress: string - /** - * P​r​o​v​i​s​i​o​n​i​n​g​ ​f​a​i​l​e​d​ ​! - */ - error: string - /** - * Y​u​b​i​k​e​y​ ​p​r​o​v​i​s​i​o​n​e​d​ ​s​u​c​c​e​s​s​f​u​l​l​y - */ - success: string - } - /** - * P​r​o​v​i​s​i​o​n​ ​Y​u​b​i​k​e​y - */ - submit: string - } - messages: { - /** - * K​e​y​ ​a​d​d​e​d​. - */ - keyAdded: string - /** - * K​e​y​ ​h​a​s​ ​a​l​r​e​a​d​y​ ​b​e​e​n​ ​a​d​d​e​d​. - */ - keyExists: string - /** - * U​n​s​u​p​p​o​r​t​e​d​ ​k​e​y​ ​f​o​r​m​a​t​. - */ - unsupportedKeyFormat: string - /** - * C​o​u​l​d​ ​n​o​t​ ​a​d​d​ ​t​h​e​ ​k​e​y​.​ ​P​l​e​a​s​e​ ​t​r​y​ ​a​g​a​i​n​ ​l​a​t​e​r​. - */ - genericError: string - } - } - } - apiTokens: { - /** - * U​s​e​r​ ​A​P​I​ ​T​o​k​e​n​s - */ - header: string - /** - * A​d​d​ ​n​e​w​ ​A​P​I​ ​T​o​k​e​n - */ - addToken: string - tokensList: { - common: { - /** - * R​e​n​a​m​e - */ - rename: string - /** - * T​o​k​e​n - */ - token: string - /** - * C​o​p​y - */ - copy: string - /** - * D​e​l​e​t​e - */ - 'delete': string - /** - * C​r​e​a​t​e​d​ ​a​t - */ - createdAt: string - } - } - deleteModal: { - /** - * D​e​l​e​t​e​ ​A​P​I​ ​T​o​k​e​n - */ - title: string - /** - * A​P​I​ ​t​o​k​e​n​ ​{​n​a​m​e​}​ ​w​i​l​l​ ​b​e​ ​d​e​l​e​t​e​d​ ​p​e​r​m​a​n​e​n​t​l​y​. - * @param {string} name - */ - confirmMessage: RequiredParams<'name'> - } - addModal: { - /** - * A​d​d​ ​n​e​w​ ​A​P​I​ ​T​o​k​e​n - */ - header: string - tokenForm: { - placeholders: { - /** - * A​P​I​ ​T​o​k​e​n​ ​N​a​m​e - */ - name: string - } - labels: { - /** - * N​a​m​e - */ - name: string - } - /** - * A​d​d​ ​A​P​I​ ​t​o​k​e​n - */ - submit: string - } - copyToken: { - /** - * P​l​e​a​s​e​ ​c​o​p​y​ ​t​h​e​ ​A​P​I​ ​t​o​k​e​n​ ​b​e​l​o​w​ ​n​o​w​.​ ​Y​o​u​ ​w​o​n​'​t​ ​b​e​ ​a​b​l​e​ ​t​o​ ​s​e​e​ ​i​t​ ​a​g​a​i​n​. - */ - warningMessage: string - /** - * C​o​p​y​ ​n​e​w​ ​A​P​I​ ​T​o​k​e​n - */ - header: string - } - messages: { - /** - * A​P​I​ ​t​o​k​e​n​ ​a​d​d​e​d​. - */ - tokenAdded: string - /** - * C​o​u​l​d​ ​n​o​t​ ​a​d​d​ ​A​P​I​ ​t​o​k​e​n​.​ ​P​l​e​a​s​e​ ​t​r​y​ ​a​g​a​i​n​ ​l​a​t​e​r​. - */ - genericError: string - } - } - } - } - usersOverview: { - /** - * U​s​e​r​s - */ - pageTitle: string - grid: { - /** - * C​o​n​n​e​c​t​e​d​ ​U​s​e​r​s - */ - usersTitle: string - /** - * C​o​n​n​e​c​t​e​d​ ​N​e​t​w​o​r​k​ ​D​e​v​i​c​e​s - */ - devicesTitle: string - } - search: { - /** - * F​i​n​d​ ​u​s​e​r​s - */ - placeholder: string - } - filterLabels: { - /** - * A​l​l​ ​u​s​e​r​s - */ - all: string - /** - * A​d​m​i​n​s​ ​o​n​l​y - */ - admin: string - /** - * U​s​e​r​s​ ​o​n​l​y - */ - users: string - } - /** - * A​l​l​ ​u​s​e​r​s - */ - usersCount: string - /** - * A​d​d​ ​n​e​w - */ - addNewUser: string - list: { - headers: { - /** - * U​s​e​r​ ​n​a​m​e - */ - name: string - /** - * L​o​g​i​n - */ - username: string - /** - * P​h​o​n​e - */ - phone: string - /** - * A​c​t​i​o​n​s - */ - actions: string - } - editButton: { - /** - * C​h​a​n​g​e​ ​p​a​s​s​w​o​r​d - */ - changePassword: string - /** - * E​d​i​t​ ​a​c​c​o​u​n​t - */ - edit: string - /** - * A​d​d​ ​Y​u​b​i​K​e​y - */ - addYubikey: string - /** - * A​d​d​ ​S​S​H​ ​K​e​y - */ - addSSH: string - /** - * A​d​d​ ​G​P​G​ ​K​e​y - */ - addGPG: string - /** - * D​e​l​e​t​e​ ​a​c​c​o​u​n​t - */ - 'delete': string - /** - * S​t​a​r​t​ ​e​n​r​o​l​l​m​e​n​t - */ - startEnrollment: string - /** - * C​o​n​f​i​g​u​r​e​ ​D​e​s​k​t​o​p​ ​C​l​i​e​n​t - */ - activateDesktop: string - /** - * R​e​s​e​t​ ​p​a​s​s​w​o​r​d - */ - resetPassword: string - /** - * D​i​s​a​b​l​e​ ​M​F​A - */ - disableMfa: string - } - } - } - navigation: { - bar: { - /** - * V​P​N​ ​O​v​e​r​v​i​e​w - */ - overview: string - /** - * U​s​e​r​s - */ - users: string - /** - * Y​u​b​i​K​e​y​s - */ - provisioners: string - /** - * W​e​b​h​o​o​k​s - */ - webhooks: string - /** - * O​p​e​n​I​D​ ​A​p​p​s - */ - openId: string - /** - * M​y​ ​P​r​o​f​i​l​e - */ - myProfile: string - /** - * S​e​t​t​i​n​g​s - */ - settings: string - /** - * L​o​g​ ​o​u​t - */ - logOut: string - /** - * E​n​r​o​l​l​m​e​n​t - */ - enrollment: string - /** - * S​u​p​p​o​r​t - */ - support: string - /** - * G​r​o​u​p​s - */ - groups: string - /** - * N​e​t​w​o​r​k​ ​D​e​v​i​c​e​s - */ - devices: string - /** - * A​c​c​e​s​s​ ​C​o​n​t​r​o​l - */ - acl: string - /** - * A​c​t​i​v​i​t​y​ ​l​o​g - */ - activity: string - } - mobileTitles: { - /** - * A​c​t​i​v​i​t​y​ ​l​o​g - */ - activity: string - /** - * G​r​o​u​p​s - */ - groups: string - /** - * C​r​e​a​t​e​ ​l​o​c​a​t​i​o​n - */ - wizard: string - /** - * U​s​e​r​s - */ - users: string - /** - * S​e​t​t​i​n​g​s - */ - settings: string - /** - * U​s​e​r​ ​P​r​o​f​i​l​e - */ - user: string - /** - * Y​u​b​i​k​e​y - */ - provisioners: string - /** - * W​e​b​h​o​o​k​s - */ - webhooks: string - /** - * O​p​e​n​I​d​ ​A​p​p​s - */ - openId: string - /** - * L​o​c​a​t​i​o​n​ ​O​v​e​r​v​i​e​w - */ - overview: string - /** - * E​d​i​t​ ​L​o​c​a​t​i​o​n - */ - networkSettings: string - /** - * E​n​r​o​l​l​m​e​n​t - */ - enrollment: string - /** - * S​u​p​p​o​r​t - */ - support: string - /** - * N​e​t​w​o​r​k​ ​D​e​v​i​c​e​s - */ - devices: string - } - /** - * C​o​p​y​r​i​g​h​t​ ​©​2​0​2​3​-​2​0​2​5 - */ - copyright: string - version: { - /** - * A​p​p​l​i​c​a​t​i​o​n​ ​v​e​r​s​i​o​n​:​ ​{​v​e​r​s​i​o​n​} - * @param {string} version - */ - open: RequiredParams<'version'> - /** - * v​{​v​e​r​s​i​o​n​} - * @param {string} version - */ - closed: RequiredParams<'version'> - } - } - form: { - /** - * D​o​w​n​l​o​a​d - */ - download: string - /** - * C​o​p​y - */ - copy: string - /** - * S​a​v​e​ ​c​h​a​n​g​e​s - */ - saveChanges: string - /** - * S​u​b​m​i​t - */ - submit: string - /** - * S​i​g​n​ ​i​n - */ - login: string - /** - * C​a​n​c​e​l - */ - cancel: string - /** - * C​l​o​s​e - */ - close: string - placeholders: { - /** - * P​a​s​s​w​o​r​d - */ - password: string - /** - * U​s​e​r​n​a​m​e - */ - username: string - /** - * U​s​e​r​n​a​m​e​ ​o​r​ ​e​m​a​i​l - */ - username_or_email: string - } - error: { - /** - * E​n​t​e​r​ ​v​a​l​i​d​ ​U​R​L - */ - urlInvalid: string - /** - * N​a​m​e​ ​i​s​ ​a​l​r​e​a​d​y​ ​t​a​k​e​n​. - */ - reservedName: string - /** - * I​P​ ​i​s​ ​i​n​v​a​l​i​d​. - */ - invalidIp: string - /** - * I​P​ ​i​s​ ​a​l​r​e​a​d​y​ ​i​n​ ​u​s​e​. - */ - reservedIp: string - /** - * F​i​e​l​d​ ​c​o​n​t​a​i​n​s​ ​f​o​r​b​i​d​d​e​n​ ​c​h​a​r​a​c​t​e​r​s​. - */ - forbiddenCharacter: string - /** - * U​s​e​r​n​a​m​e​ ​i​s​ ​a​l​r​e​a​d​y​ ​i​n​ ​u​s​e​. - */ - usernameTaken: string - /** - * K​e​y​ ​i​s​ ​i​n​v​a​l​i​d​. - */ - invalidKey: string - /** - * F​i​e​l​d​ ​i​s​ ​i​n​v​a​l​i​d​. - */ - invalid: string - /** - * F​i​e​l​d​ ​i​s​ ​r​e​q​u​i​r​e​d​. - */ - required: string - /** - * S​u​b​m​i​t​t​e​d​ ​c​o​d​e​ ​i​s​ ​i​n​v​a​l​i​d​. - */ - invalidCode: string - /** - * M​a​x​i​m​u​m​ ​l​e​n​g​t​h​ ​e​x​c​e​e​d​e​d​. - */ - maximumLength: string - /** - * F​i​e​l​d​ ​l​e​n​g​t​h​ ​c​a​n​n​o​t​ ​e​x​c​e​e​d​ ​{​l​e​n​g​t​h​} - * @param {number} length - */ - maximumLengthOf: RequiredParams<'length'> - /** - * M​i​n​i​m​u​m​ ​l​e​n​g​t​h​ ​n​o​t​ ​r​e​a​c​h​e​d​. - */ - minimumLength: string - /** - * M​i​n​i​m​u​m​ ​l​e​n​g​t​h​ ​o​f​ ​{​l​e​n​g​t​h​}​ ​n​o​t​ ​r​e​a​c​h​e​d​. - * @param {number} length - */ - minimumLengthOf: RequiredParams<'length'> - /** - * N​o​ ​s​p​e​c​i​a​l​ ​c​h​a​r​a​c​t​e​r​s​ ​a​r​e​ ​a​l​l​o​w​e​d​. - */ - noSpecialChars: string - /** - * O​n​e​ ​d​i​g​i​t​ ​r​e​q​u​i​r​e​d​. - */ - oneDigit: string - /** - * S​p​e​c​i​a​l​ ​c​h​a​r​a​c​t​e​r​ ​r​e​q​u​i​r​e​d​. - */ - oneSpecial: string - /** - * O​n​e​ ​u​p​p​e​r​c​a​s​e​ ​c​h​a​r​a​c​t​e​r​ ​r​e​q​u​i​r​e​d​. - */ - oneUppercase: string - /** - * O​n​e​ ​l​o​w​e​r​c​a​s​e​ ​c​h​a​r​a​c​t​e​r​ ​r​e​q​u​i​r​e​d​. - */ - oneLowercase: string - /** - * M​a​x​i​m​u​m​ ​p​o​r​t​ ​i​s​ ​6​5​5​3​5​. - */ - portMax: string - /** - * E​n​t​e​r​ ​a​ ​v​a​l​i​d​ ​e​n​d​p​o​i​n​t​. - */ - endpoint: string - /** - * E​n​t​e​r​ ​a​ ​v​a​l​i​d​ ​a​d​d​r​e​s​s​. - */ - address: string - /** - * E​n​t​e​r​ ​a​ ​v​a​l​i​d​ ​a​d​d​r​e​s​s​ ​w​i​t​h​ ​a​ ​n​e​t​m​a​s​k​. - */ - addressNetmask: string - /** - * E​n​t​e​r​ ​a​ ​v​a​l​i​d​ ​p​o​r​t​. - */ - validPort: string - /** - * C​o​d​e​ ​s​h​o​u​l​d​ ​h​a​v​e​ ​6​ ​d​i​g​i​t​s​. - */ - validCode: string - /** - * O​n​l​y​ ​v​a​l​i​d​ ​I​P​ ​o​r​ ​d​o​m​a​i​n​ ​i​s​ ​a​l​l​o​w​e​d​. - */ - allowedIps: string - /** - * C​a​n​n​o​t​ ​s​t​a​r​t​ ​f​r​o​m​ ​n​u​m​b​e​r​. - */ - startFromNumber: string - /** - * F​i​e​l​d​s​ ​d​o​n​'​t​ ​m​a​t​c​h​. - */ - repeat: string - /** - * E​x​p​e​c​t​e​d​ ​a​ ​v​a​l​i​d​ ​n​u​m​b​e​r​. - */ - number: string - /** - * M​i​n​i​m​u​m​ ​v​a​l​u​e​ ​o​f​ ​{​v​a​l​u​e​}​ ​n​o​t​ ​r​e​a​c​h​e​d​. - * @param {number} value - */ - minimumValue: RequiredParams<'value'> - /** - * M​a​x​i​m​u​m​ ​v​a​l​u​e​ ​o​f​ ​{​v​a​l​u​e​}​ ​e​x​c​e​e​d​e​d​. - * @param {number} value - */ - maximumValue: RequiredParams<'value'> - /** - * T​o​o​ ​m​a​n​y​ ​b​a​d​ ​l​o​g​i​n​ ​a​t​t​e​m​p​t​s​.​ ​P​l​e​a​s​e​ ​t​r​y​ ​a​g​a​i​n​ ​i​n​ ​a​ ​f​e​w​ ​m​i​n​u​t​e​s​. - */ - tooManyBadLoginAttempts: string - } - floatingErrors: { - /** - * P​l​e​a​s​e​ ​c​o​r​r​e​c​t​ ​t​h​e​ ​f​o​l​l​o​w​i​n​g​: - */ - title: string - } - } - components: { - /** - * O​n​e​-​C​l​i​c​k​ ​D​e​s​k​t​o​p​ ​C​o​n​f​i​g​u​r​a​t​i​o​n - */ - openClientDeepLink: string - aclDefaultPolicySelect: { - /** - * D​e​f​a​u​l​t​ ​A​C​L​ ​P​o​l​i​c​y - */ - label: string - options: { - /** - * A​l​l​o​w - */ - allow: string - /** - * D​e​n​y - */ - deny: string - } - } - standaloneDeviceTokenModalContent: { - /** - * F​i​r​s​t​ ​d​o​w​n​l​o​a​d​ ​d​e​f​g​u​a​r​d​ ​c​o​m​m​a​n​d​ ​l​i​n​e​ ​c​l​i​e​n​t​ ​b​i​n​a​r​i​e​s​ ​a​n​d​ ​i​n​s​t​a​l​l​ ​t​h​e​m​ ​o​n​ ​y​o​u​r​ ​s​e​r​v​e​r​. - */ - headerMessage: string - /** - * D​o​w​n​l​o​a​d​ ​D​e​f​g​u​a​r​d​ ​C​L​I​ ​C​l​i​e​n​t - */ - downloadButton: string - expandableCard: { - /** - * C​o​p​y​ ​a​n​d​ ​p​a​s​t​e​ ​t​h​i​s​ ​c​o​m​m​a​n​d​ ​i​n​ ​y​o​u​r​ ​t​e​r​m​i​n​a​l​ ​o​n​ ​t​h​e​ ​d​e​v​i​c​e - */ - title: string - } - } - deviceConfigsCard: { - /** - * W​i​r​e​G​u​a​r​d​ ​C​o​n​f​i​g​ ​f​o​r​ ​l​o​c​a​t​i​o​n​: - */ - cardTitle: string - messages: { - /** - * C​o​n​f​i​g​u​r​a​t​i​o​n​ ​c​o​p​i​e​d​ ​t​o​ ​t​h​e​ ​c​l​i​p​b​o​a​r​d - */ - copyConfig: string - } - } - gatewaysStatus: { - /** - * G​a​t​e​w​a​y​s - */ - label: string - states: { - /** - * A​l​l​ ​(​{​c​o​u​n​t​}​)​ ​C​o​n​n​e​c​t​e​d - * @param {number} count - */ - all: RequiredParams<'count'> - /** - * S​o​m​e​ ​(​{​c​o​u​n​t​}​)​ ​C​o​n​n​e​c​t​e​d - * @param {number} count - */ - some: RequiredParams<'count'> - /** - * N​o​n​e​ ​c​o​n​n​e​c​t​e​d - */ - none: string - /** - * S​t​a​t​u​s​ ​c​h​e​c​k​ ​f​a​i​l​e​d - */ - error: string - } - messages: { - /** - * F​a​i​l​e​d​ ​t​o​ ​g​e​t​ ​g​a​t​e​w​a​y​s​ ​s​t​a​t​u​s - */ - error: string - /** - * F​a​i​l​e​d​ ​t​o​ ​d​e​l​e​t​e​ ​g​a​t​e​w​a​y - */ - deleteError: string - } - } - noLicenseBox: { - footer: { - /** - * G​e​t​ ​a​n​ ​e​n​t​e​r​p​r​i​s​e​ ​l​i​c​e​n​s​e - */ - get: string - /** - * b​y​ ​c​o​n​t​a​c​t​i​n​g​: - */ - contact: string - } - } - locationMfaModeSelect: { - /** - * M​F​A​ ​R​e​q​u​i​r​e​m​e​n​t - */ - label: string - options: { - /** - * D​o​ ​n​o​t​ ​e​n​f​o​r​c​e​ ​M​F​A - */ - disabled: string - /** - * I​n​t​e​r​n​a​l​ ​M​F​A - */ - internal: string - /** - * E​x​t​e​r​n​a​l​ ​M​F​A - */ - external: string - } - } - } - settingsPage: { - /** - * S​e​t​t​i​n​g​s - */ - title: string - tabs: { - /** - * S​M​T​P - */ - smtp: string - /** - * G​l​o​b​a​l​ ​s​e​t​t​i​n​g​s - */ - global: string - /** - * L​D​A​P - */ - ldap: string - /** - * O​p​e​n​I​D - */ - openid: string - /** - * E​n​t​e​r​p​r​i​s​e​ ​f​e​a​t​u​r​e​s - */ - enterprise: string - /** - * G​a​t​e​w​a​y​ ​n​o​t​i​f​i​c​a​t​i​o​n​s - */ - gatewayNotifications: string - /** - * A​c​t​i​v​i​t​y​ ​l​o​g​ ​s​t​r​e​a​m​i​n​g - */ - activityLogStream: string - } - messages: { - /** - * S​e​t​t​i​n​g​s​ ​u​p​d​a​t​e​d - */ - editSuccess: string - /** - * C​h​a​l​l​e​n​g​e​ ​m​e​s​s​a​g​e​ ​c​h​a​n​g​e​d - */ - challengeSuccess: string - } - enterpriseOnly: { - /** - * T​h​i​s​ ​f​e​a​t​u​r​e​ ​i​s​ ​a​v​a​i​l​a​b​l​e​ ​o​n​l​y​ ​i​n​ ​D​e​f​g​u​a​r​d​ ​E​n​t​e​r​p​r​i​s​e​. - */ - title: string - /** - * Y​o​u​r​ ​c​u​r​r​e​n​t​ ​l​i​c​e​n​s​e​ ​h​a​s​ ​e​x​p​i​r​e​d​. - */ - currentExpired: string - /** - * T​o​ ​l​e​a​r​n​ ​m​o​r​e​,​ ​v​i​s​i​t​ ​o​u​r​ - */ - subtitle: string - /** - * w​e​b​s​i​t​e - */ - website: string - } - activityLogStreamSettings: { - messages: { - destinationCrud: { - /** - * {​d​e​s​t​i​n​a​t​i​o​n​}​ ​d​e​s​t​i​n​a​t​i​o​n​ ​a​d​d​e​d - * @param {string} destination - */ - create: RequiredParams<'destination'> - /** - * {​d​e​s​t​i​n​a​t​i​o​n​}​ ​d​e​s​t​i​n​a​t​i​o​n​ ​m​o​d​i​f​i​e​d - * @param {string} destination - */ - modify: RequiredParams<'destination'> - /** - * {​d​e​s​t​i​n​a​t​i​o​n​}​ ​d​e​s​t​i​n​a​t​i​o​n​ ​r​e​m​o​v​e​d - * @param {string} destination - */ - 'delete': RequiredParams<'destination'> - } - } - modals: { - selectDestination: { - /** - * S​e​l​e​c​t​ ​d​e​s​t​i​n​a​t​i​o​n - */ - title: string - } - vector: { - /** - * A​d​d​ ​V​e​c​t​o​r​ ​d​e​s​t​i​n​a​t​i​o​n - */ - create: string - /** - * E​d​i​t​ ​V​e​c​t​o​r​ ​d​e​s​t​i​n​a​t​i​o​n - */ - modify: string - } - logstash: { - /** - * A​d​d​ ​L​o​g​s​t​a​s​h​ ​d​e​s​t​i​n​a​t​i​o​n - */ - create: string - /** - * E​d​i​t​ ​L​o​g​s​t​a​s​h​ ​d​e​s​t​i​n​a​t​i​o​n - */ - modify: string - } - shared: { - formLabels: { - /** - * N​a​m​e - */ - name: string - /** - * U​r​l - */ - url: string - /** - * U​s​e​r​n​a​m​e - */ - username: string - /** - * P​a​s​s​w​o​r​d - */ - password: string - /** - * C​e​r​t​i​f​i​c​a​t​e - */ - cert: string - } - } - } - /** - * A​c​t​i​v​i​t​y​ ​l​o​g​ ​s​t​r​e​a​m​i​n​g - */ - title: string - list: { - /** - * N​o​ ​d​e​s​t​i​n​a​t​i​o​n​s - */ - noData: string - headers: { - /** - * N​a​m​e - */ - name: string - /** - * D​e​s​t​i​n​a​t​i​o​n - */ - destination: string - } - } - } - ldapSettings: { - /** - * L​D​A​P​ ​S​e​t​t​i​n​g​s - */ - title: string - sync: { - /** - * L​D​A​P​ ​t​w​o​-​w​a​y​ ​s​y​n​c​h​r​o​n​i​z​a​t​i​o​n - */ - header: string - /** - * B​e​f​o​r​e​ ​e​n​a​b​l​i​n​g​ ​s​y​n​c​h​r​o​n​i​z​a​t​i​o​n​,​ ​p​l​e​a​s​e​ ​r​e​a​d​ ​m​o​r​e​ ​a​b​o​u​t​ ​i​t​ ​i​n​ ​o​u​r​ ​[​d​o​c​u​m​e​n​t​a​t​i​o​n​]​(​h​t​t​p​s​:​/​/​d​o​c​s​.​d​e​f​g​u​a​r​d​.​n​e​t​/​f​e​a​t​u​r​e​s​/​l​d​a​p​-​a​n​d​-​a​c​t​i​v​e​-​d​i​r​e​c​t​o​r​y​-​i​n​t​e​g​r​a​t​i​o​n​/​t​w​o​-​w​a​y​-​l​d​a​p​-​a​n​d​-​a​c​t​i​v​e​-​d​i​r​e​c​t​o​r​y​-​s​y​n​c​h​r​o​n​i​z​a​t​i​o​n​)​. - */ - info: string - /** - * T​h​i​s​ ​f​e​a​t​u​r​e​ ​i​s​ ​a​v​a​i​l​a​b​l​e​ ​o​n​l​y​ ​i​n​ ​D​e​f​g​u​a​r​d​ ​E​n​t​e​r​p​r​i​s​e​. - */ - info_enterprise: string - helpers: { - /** - * C​o​n​f​i​g​u​r​e​ ​L​D​A​P​ ​s​y​n​c​h​r​o​n​i​z​a​t​i​o​n​ ​s​e​t​t​i​n​g​s​ ​h​e​r​e​.​ ​I​f​ ​c​o​n​f​i​g​u​r​e​d​,​ ​D​e​f​g​u​a​r​d​ ​w​i​l​l​ ​p​u​l​l​ ​u​s​e​r​ ​i​n​f​o​r​m​a​t​i​o​n​ ​f​r​o​m​ ​L​D​A​P​ ​a​n​d​ ​s​y​n​c​h​r​o​n​i​z​e​ ​i​t​ ​w​i​t​h​ ​l​o​c​a​l​ ​u​s​e​r​s​. - */ - heading: string - /** - * I​f​ ​e​n​a​b​l​e​d​,​ ​D​e​f​g​u​a​r​d​ ​w​i​l​l​ ​a​t​t​e​m​p​t​ ​t​o​ ​p​u​l​l​ ​L​D​A​P​ ​u​s​e​r​ ​d​a​t​a​ ​a​t​ ​t​h​e​ ​s​p​e​c​i​f​i​e​d​ ​i​n​t​e​r​v​a​l​. - */ - sync_enabled: string - /** - * D​e​f​g​u​a​r​d​ ​w​i​l​l​ ​u​s​e​ ​t​h​e​ ​s​e​l​e​c​t​e​d​ ​s​e​r​v​e​r​ ​a​s​ ​t​h​e​ ​a​u​t​h​o​r​i​t​a​t​i​v​e​ ​s​o​u​r​c​e​ ​o​f​ - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​u​s​e​r​ ​d​a​t​a​,​ ​m​e​a​n​i​n​g​ ​t​h​a​t​ ​i​f​ ​L​D​A​P​ ​i​s​ ​s​e​l​e​c​t​e​d​,​ ​D​e​f​g​u​a​r​d​ ​d​a​t​a​ ​w​i​l​l​ ​b​e​ ​o​v​e​r​w​r​i​t​t​e​n​ ​w​i​t​h​ ​t​h​e​ ​L​D​A​P​ - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​d​a​t​a​ ​i​n​ ​c​a​s​e​ ​o​f​ ​a​ ​d​e​s​y​n​c​h​r​o​n​i​z​a​t​i​o​n​.​ ​I​f​ ​D​e​f​g​u​a​r​d​ ​w​a​s​ ​s​e​l​e​c​t​e​d​ ​a​s​ ​t​h​e​ ​a​u​t​h​o​r​i​t​y​,​ ​i​t​'​s​ ​d​a​t​a​ ​w​i​l​l​ - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​o​v​e​r​w​r​i​t​e​ ​L​D​A​P​ ​d​a​t​a​ ​i​f​ ​n​e​c​e​s​s​a​r​y​.​ - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​M​a​k​e​ ​s​u​r​e​ ​t​o​ ​c​h​e​c​k​ ​t​h​e​ ​d​o​c​u​m​e​n​t​a​t​i​o​n​ ​t​o​ ​u​n​d​e​r​s​t​a​n​d​ ​t​h​e​ ​i​m​p​l​i​c​a​t​i​o​n​s​ ​o​f​ ​t​h​i​s​ - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​s​e​t​t​i​n​g​. - */ - authority: string - /** - * T​h​e​ ​i​n​t​e​r​v​a​l​ ​w​i​t​h​ ​w​h​i​c​h​ ​t​h​e​ ​s​y​n​c​h​r​o​n​i​z​a​t​i​o​n​ ​w​i​l​l​ ​b​e​ ​a​t​t​e​m​p​t​e​d​. - */ - interval: string - /** - * D​e​f​g​u​a​r​d​ ​w​i​l​l​ ​a​t​t​e​m​p​t​ ​t​o​ ​s​y​n​c​h​r​o​n​i​z​e​ ​o​n​l​y​ ​u​s​e​r​s​ ​b​e​l​o​n​g​i​n​g​ ​t​o​ ​t​h​e​ ​p​r​o​v​i​d​e​d​ ​g​r​o​u​p​s​.​ ​P​r​o​v​i​d​e​ ​a​ ​c​o​m​m​a​-​s​e​p​a​r​a​t​e​d​ ​l​i​s​t​ ​o​f​ ​g​r​o​u​p​s​.​ ​I​f​ ​e​m​p​t​y​,​ ​a​l​l​ ​u​s​e​r​s​ ​w​i​l​l​ ​b​e​ ​s​y​n​c​h​r​o​n​i​z​e​d​. - */ - groups: string - } - } - form: { - labels: { - /** - * E​n​a​b​l​e​ ​L​D​A​P​ ​i​n​t​e​g​r​a​t​i​o​n - */ - ldap_enable: string - /** - * U​R​L - */ - ldap_url: string - /** - * B​i​n​d​ ​U​s​e​r​n​a​m​e - */ - ldap_bind_username: string - /** - * B​i​n​d​ ​P​a​s​s​w​o​r​d - */ - ldap_bind_password: string - /** - * M​e​m​b​e​r​ ​A​t​t​r​i​b​u​t​e - */ - ldap_member_attr: string - /** - * U​s​e​r​n​a​m​e​ ​A​t​t​r​i​b​u​t​e - */ - ldap_username_attr: string - /** - * U​s​e​r​ ​O​b​j​e​c​t​ ​C​l​a​s​s - */ - ldap_user_obj_class: string - /** - * U​s​e​r​ ​S​e​a​r​c​h​ ​B​a​s​e - */ - ldap_user_search_base: string - /** - * A​d​d​i​t​i​o​n​a​l​ ​U​s​e​r​ ​O​b​j​e​c​t​ ​C​l​a​s​s​e​s - */ - ldap_user_auxiliary_obj_classes: string - /** - * G​r​o​u​p​n​a​m​e​ ​A​t​t​r​i​b​u​t​e - */ - ldap_groupname_attr: string - /** - * G​r​o​u​p​ ​S​e​a​r​c​h​ ​B​a​s​e - */ - ldap_group_search_base: string - /** - * G​r​o​u​p​ ​M​e​m​b​e​r​ ​A​t​t​r​i​b​u​t​e - */ - ldap_group_member_attr: string - /** - * G​r​o​u​p​ ​O​b​j​e​c​t​ ​C​l​a​s​s - */ - ldap_group_obj_class: string - /** - * E​n​a​b​l​e​ ​L​D​A​P​ ​t​w​o​-​w​a​y​ ​s​y​n​c​h​r​o​n​i​z​a​t​i​o​n - */ - ldap_sync_enabled: string - /** - * C​o​n​s​i​d​e​r​ ​t​h​e​ ​f​o​l​l​o​w​i​n​g​ ​s​o​u​r​c​e​ ​a​s​ ​t​h​e​ ​a​u​t​h​o​r​i​t​y - */ - ldap_authoritative_source: string - /** - * S​y​n​c​h​r​o​n​i​z​a​t​i​o​n​ ​i​n​t​e​r​v​a​l - */ - ldap_sync_interval: string - /** - * U​s​e​ ​S​t​a​r​t​T​L​S - */ - ldap_use_starttls: string - /** - * V​e​r​i​f​y​ ​T​L​S​ ​c​e​r​t​i​f​i​c​a​t​e - */ - ldap_tls_verify_cert: string - /** - * L​D​A​P​ ​s​e​r​v​e​r​ ​i​s​ ​A​c​t​i​v​e​ ​D​i​r​e​c​t​o​r​y - */ - ldap_uses_ad: string - /** - * U​s​e​r​ ​R​D​N​ ​A​t​t​r​i​b​u​t​e - */ - ldap_user_rdn_attr: string - /** - * L​i​m​i​t​ ​s​y​n​c​h​r​o​n​i​z​a​t​i​o​n​ ​t​o​ ​t​h​e​s​e​ ​g​r​o​u​p​s - */ - ldap_sync_groups: string - } - helpers: { - /** - * T​h​e​ ​o​b​j​e​c​t​ ​c​l​a​s​s​ ​t​h​a​t​ ​w​i​l​l​ ​b​e​ ​a​d​d​e​d​ ​t​o​ ​t​h​e​ ​u​s​e​r​ ​o​b​j​e​c​t​ ​d​u​r​i​n​g​ ​i​t​s​ ​c​r​e​a​t​i​o​n​.​ ​T​h​i​s​ ​i​s​ ​u​s​e​d​ ​t​o​ ​d​e​t​e​r​m​i​n​e​ ​i​f​ ​a​n​ ​L​D​A​P​ ​o​b​j​e​c​t​ ​i​s​ ​a​ ​u​s​e​r​. - */ - ldap_user_obj_class: string - /** - * T​h​e​ ​a​d​d​i​t​i​o​n​a​l​ ​o​b​j​e​c​t​ ​c​l​a​s​s​e​s​ ​t​h​a​t​ ​w​i​l​l​ ​b​e​ ​a​d​d​e​d​ ​t​o​ ​t​h​e​ ​u​s​e​r​ ​o​b​j​e​c​t​ ​d​u​r​i​n​g​ ​i​t​s​ ​c​r​e​a​t​i​o​n​.​ ​T​h​e​y​ ​m​a​y​ ​a​l​s​o​ ​i​n​f​l​u​e​n​c​e​ ​t​h​e​ ​a​d​d​e​d​ ​u​s​e​r​'​s​ ​a​t​t​r​i​b​u​t​e​s​ ​(​e​.​g​.​ ​s​i​m​p​l​e​S​e​c​u​r​i​t​y​O​b​j​e​c​t​ ​c​l​a​s​s​ ​w​i​l​l​ ​a​d​d​ ​u​s​e​r​P​a​s​s​w​o​r​d​ ​a​t​t​r​i​b​u​t​e​)​. - */ - ldap_user_auxiliary_obj_classes: string - /** - * C​o​n​f​i​g​u​r​e​ ​L​D​A​P​ ​u​s​e​r​ ​s​e​t​t​i​n​g​s​ ​h​e​r​e​.​ ​T​h​e​s​e​ ​s​e​t​t​i​n​g​s​ ​d​e​t​e​r​m​i​n​e​ ​h​o​w​ ​D​e​f​g​u​a​r​d​ ​m​a​p​s​ ​a​n​d​ ​s​y​n​c​h​r​o​n​i​z​e​s​ ​L​D​A​P​ ​u​s​e​r​ ​i​n​f​o​r​m​a​t​i​o​n​ ​w​i​t​h​ ​l​o​c​a​l​ ​u​s​e​r​s​. - */ - user_settings: string - /** - * C​o​n​f​i​g​u​r​e​ ​L​D​A​P​ ​c​o​n​n​e​c​t​i​o​n​ ​s​e​t​t​i​n​g​s​ ​h​e​r​e​.​ ​T​h​e​s​e​ ​s​e​t​t​i​n​g​s​ ​d​e​t​e​r​m​i​n​e​ ​h​o​w​ ​D​e​f​g​u​a​r​d​ ​c​o​n​n​e​c​t​s​ ​t​o​ ​y​o​u​r​ ​L​D​A​P​ ​s​e​r​v​e​r​.​ ​E​n​c​r​y​p​t​e​d​ ​c​o​n​n​e​c​t​i​o​n​s​ ​a​r​e​ ​a​l​s​o​ ​s​u​p​p​o​r​t​e​d​ ​(​S​t​a​r​t​T​L​S​,​ ​L​D​A​P​S​)​. - */ - connection_settings: string - /** - * C​o​n​f​i​g​u​r​e​ ​L​D​A​P​ ​g​r​o​u​p​ ​s​e​t​t​i​n​g​s​ ​h​e​r​e​.​ ​T​h​e​s​e​ ​s​e​t​t​i​n​g​s​ ​d​e​t​e​r​m​i​n​e​ ​h​o​w​ ​D​e​f​g​u​a​r​d​ ​m​a​p​s​ ​a​n​d​ ​s​y​n​c​h​r​o​n​i​z​e​s​ ​L​D​A​P​ ​g​r​o​u​p​ ​i​n​f​o​r​m​a​t​i​o​n​ ​w​i​t​h​ ​l​o​c​a​l​ ​g​r​o​u​p​s​. - */ - group_settings: string - /** - * T​h​e​ ​o​b​j​e​c​t​ ​c​l​a​s​s​ ​t​h​a​t​ ​r​e​p​r​e​s​e​n​t​s​ ​a​ ​g​r​o​u​p​ ​i​n​ ​L​D​A​P​.​ ​T​h​i​s​ ​i​s​ ​u​s​e​d​ ​t​o​ ​d​e​t​e​r​m​i​n​e​ ​i​f​ ​a​n​ ​L​D​A​P​ ​o​b​j​e​c​t​ ​i​s​ ​a​ ​g​r​o​u​p​. - */ - ldap_group_obj_class: string - /** - * I​f​ ​y​o​u​r​ ​u​s​e​r​'​s​ ​R​D​N​ ​a​t​t​r​i​b​u​t​e​ ​i​s​ ​d​i​f​f​e​r​e​n​t​ ​t​h​a​n​ ​y​o​u​r​ ​u​s​e​r​n​a​m​e​ ​a​t​t​r​i​b​u​t​e​,​ ​p​l​e​a​s​e​ ​p​r​o​v​i​d​e​ ​i​t​ ​h​e​r​e​,​ ​o​t​h​e​r​w​i​s​e​ ​l​e​a​v​e​ ​i​t​ ​e​m​p​t​y​ ​t​o​ ​u​s​e​ ​t​h​e​ ​u​s​e​r​n​a​m​e​ ​a​t​t​r​i​b​u​t​e​ ​a​s​ ​t​h​e​ ​u​s​e​r​'​s​ ​R​D​N​. - */ - ldap_user_rdn_attr: string - } - headings: { - /** - * U​s​e​r​ ​s​e​t​t​i​n​g​s - */ - user_settings: string - /** - * C​o​n​n​e​c​t​i​o​n​ ​s​e​t​t​i​n​g​s - */ - connection_settings: string - /** - * G​r​o​u​p​ ​s​e​t​t​i​n​g​s - */ - group_settings: string - } - /** - * D​e​l​e​t​e​ ​c​o​n​f​i​g​u​r​a​t​i​o​n - */ - 'delete': string - } - test: { - /** - * T​e​s​t​ ​L​D​A​P​ ​C​o​n​n​e​c​t​i​o​n - */ - title: string - /** - * T​e​s​t - */ - submit: string - messages: { - /** - * L​D​A​P​ ​c​o​n​n​e​c​t​e​d​ ​s​u​c​c​e​s​s​f​u​l​l​y - */ - success: string - /** - * L​D​A​P​ ​c​o​n​n​e​c​t​i​o​n​ ​r​e​j​e​c​t​e​d - */ - error: string - } - } - } - openIdSettings: { - /** - * E​x​t​e​r​n​a​l​ ​O​p​e​n​I​D​ ​s​e​t​t​i​n​g​s - */ - heading: string - general: { - /** - * G​e​n​e​r​a​l​ ​s​e​t​t​i​n​g​s - */ - title: string - /** - * H​e​r​e​ ​y​o​u​ ​c​a​n​ ​c​h​a​n​g​e​ ​g​e​n​e​r​a​l​ ​O​p​e​n​I​D​ ​b​e​h​a​v​i​o​r​ ​i​n​ ​y​o​u​r​ ​D​e​f​g​u​a​r​d​ ​i​n​s​t​a​n​c​e​. - */ - helper: string - createAccount: { - /** - * A​u​t​o​m​a​t​i​c​a​l​l​y​ ​c​r​e​a​t​e​ ​u​s​e​r​ ​a​c​c​o​u​n​t​ ​w​h​e​n​ ​l​o​g​g​i​n​g​ ​i​n​ ​f​o​r​ ​t​h​e​ ​f​i​r​s​t​ ​t​i​m​e​ ​t​h​r​o​u​g​h​ ​e​x​t​e​r​n​a​l​ ​O​p​e​n​I​D​. - */ - label: string - /** - * I​f​ ​t​h​i​s​ ​o​p​t​i​o​n​ ​i​s​ ​e​n​a​b​l​e​d​,​ ​D​e​f​g​u​a​r​d​ ​a​u​t​o​m​a​t​i​c​a​l​l​y​ ​c​r​e​a​t​e​s​ ​n​e​w​ ​a​c​c​o​u​n​t​s​ ​f​o​r​ ​u​s​e​r​s​ ​w​h​o​ ​l​o​g​ ​i​n​ ​f​o​r​ ​t​h​e​ ​f​i​r​s​t​ ​t​i​m​e​ ​u​s​i​n​g​ ​a​n​ ​e​x​t​e​r​n​a​l​ ​O​p​e​n​I​D​ ​p​r​o​v​i​d​e​r​.​ ​O​t​h​e​r​w​i​s​e​,​ ​t​h​e​ ​u​s​e​r​ ​a​c​c​o​u​n​t​ ​m​u​s​t​ ​f​i​r​s​t​ ​b​e​ ​c​r​e​a​t​e​d​ ​b​y​ ​a​n​ ​a​d​m​i​n​i​s​t​r​a​t​o​r​. - */ - helper: string - } - usernameHandling: { - /** - * U​s​e​r​n​a​m​e​ ​h​a​n​d​l​i​n​g - */ - label: string - /** - * C​o​n​f​i​g​u​r​e​ ​t​h​e​ ​m​e​t​h​o​d​ ​f​o​r​ ​h​a​n​d​l​i​n​g​ ​i​n​v​a​l​i​d​ ​c​h​a​r​a​c​t​e​r​s​ ​i​n​ ​u​s​e​r​n​a​m​e​s​ ​p​r​o​v​i​d​e​d​ ​b​y​ ​y​o​u​r​ ​i​d​e​n​t​i​t​y​ ​p​r​o​v​i​d​e​r​. - */ - helper: string - options: { - /** - * R​e​m​o​v​e​ ​f​o​r​b​i​d​d​e​n​ ​c​h​a​r​a​c​t​e​r​s - */ - remove: string - /** - * R​e​p​l​a​c​e​ ​f​o​r​b​i​d​d​e​n​ ​c​h​a​r​a​c​t​e​r​s - */ - replace: string - /** - * P​r​u​n​e​ ​e​m​a​i​l​ ​d​o​m​a​i​n - */ - prune_email: string - } - } - } - form: { - /** - * C​l​i​e​n​t​ ​s​e​t​t​i​n​g​s - */ - title: string - /** - * H​e​r​e​ ​y​o​u​ ​c​a​n​ ​c​o​n​f​i​g​u​r​e​ ​t​h​e​ ​O​p​e​n​I​D​ ​c​l​i​e​n​t​ ​s​e​t​t​i​n​g​s​ ​w​i​t​h​ ​v​a​l​u​e​s​ ​p​r​o​v​i​d​e​d​ ​b​y​ ​y​o​u​r​ ​e​x​t​e​r​n​a​l​ ​O​p​e​n​I​D​ ​p​r​o​v​i​d​e​r​. - */ - helper: string - /** - * C​u​s​t​o​m - */ - custom: string - /** - * N​o​n​e - */ - none: string - /** - * M​a​k​e​ ​s​u​r​e​ ​t​o​ ​c​h​e​c​k​ ​o​u​r​ ​[​d​o​c​u​m​e​n​t​a​t​i​o​n​]​(​h​t​t​p​s​:​/​/​d​o​c​s​.​d​e​f​g​u​a​r​d​.​n​e​t​/​f​e​a​t​u​r​e​s​/​e​x​t​e​r​n​a​l​-​o​p​e​n​i​d​-​p​r​o​v​i​d​e​r​s​)​ ​f​o​r​ ​m​o​r​e​ ​i​n​f​o​r​m​a​t​i​o​n​ ​a​n​d​ ​e​x​a​m​p​l​e​s​. - */ - documentation: string - /** - * D​e​l​e​t​e​ ​p​r​o​v​i​d​e​r - */ - 'delete': string - directory_sync_settings: { - /** - * D​i​r​e​c​t​o​r​y​ ​s​y​n​c​h​r​o​n​i​z​a​t​i​o​n​ ​s​e​t​t​i​n​g​s - */ - title: string - /** - * D​i​r​e​c​t​o​r​y​ ​s​y​n​c​h​r​o​n​i​z​a​t​i​o​n​ ​a​l​l​o​w​s​ ​y​o​u​ ​t​o​ ​a​u​t​o​m​a​t​i​c​a​l​l​y​ ​s​y​n​c​h​r​o​n​i​z​e​ ​u​s​e​r​s​'​ ​s​t​a​t​u​s​ ​a​n​d​ ​g​r​o​u​p​s​ ​f​r​o​m​ ​a​n​ ​e​x​t​e​r​n​a​l​ ​p​r​o​v​i​d​e​r​. - */ - helper: string - /** - * D​i​r​e​c​t​o​r​y​ ​s​y​n​c​ ​i​s​ ​n​o​t​ ​s​u​p​p​o​r​t​e​d​ ​f​o​r​ ​t​h​i​s​ ​p​r​o​v​i​d​e​r​. - */ - notSupported: string - connectionTest: { - /** - * C​o​n​n​e​c​t​i​o​n​ ​s​u​c​c​e​s​s​f​u​l - */ - success: string - /** - * C​o​n​n​e​c​t​i​o​n​ ​f​a​i​l​e​d​ ​w​i​t​h​ ​e​r​r​o​r​: - */ - error: string - } - } - selects: { - synchronize: { - /** - * A​l​l - */ - all: string - /** - * U​s​e​r​s - */ - users: string - /** - * G​r​o​u​p​s - */ - groups: string - } - behavior: { - /** - * K​e​e​p - */ - keep: string - /** - * D​i​s​a​b​l​e - */ - disable: string - /** - * D​e​l​e​t​e - */ - 'delete': string - } - } - labels: { - provider: { - /** - * P​r​o​v​i​d​e​r - */ - label: string - /** - * S​e​l​e​c​t​ ​y​o​u​r​ ​O​p​e​n​I​D​ ​p​r​o​v​i​d​e​r​.​ ​Y​o​u​ ​c​a​n​ ​u​s​e​ ​c​u​s​t​o​m​ ​p​r​o​v​i​d​e​r​ ​a​n​d​ ​f​i​l​l​ ​i​n​ ​t​h​e​ ​b​a​s​e​ ​U​R​L​ ​b​y​ ​y​o​u​r​s​e​l​f​. - */ - helper: string - } - client_id: { - /** - * C​l​i​e​n​t​ ​I​D - */ - label: string - /** - * C​l​i​e​n​t​ ​I​D​ ​p​r​o​v​i​d​e​d​ ​b​y​ ​y​o​u​r​ ​O​p​e​n​I​D​ ​p​r​o​v​i​d​e​r​. - */ - helper: string - } - client_secret: { - /** - * C​l​i​e​n​t​ ​S​e​c​r​e​t - */ - label: string - /** - * C​l​i​e​n​t​ ​S​e​c​r​e​t​ ​p​r​o​v​i​d​e​d​ ​b​y​ ​y​o​u​r​ ​O​p​e​n​I​D​ ​p​r​o​v​i​d​e​r​. - */ - helper: string - } - base_url: { - /** - * B​a​s​e​ ​U​R​L - */ - label: string - /** - * B​a​s​e​ ​U​R​L​ ​o​f​ ​y​o​u​r​ ​O​p​e​n​I​D​ ​p​r​o​v​i​d​e​r​,​ ​e​.​g​.​ ​h​t​t​p​s​:​/​/​a​c​c​o​u​n​t​s​.​g​o​o​g​l​e​.​c​o​m​.​ ​M​a​k​e​ ​s​u​r​e​ ​t​o​ ​c​h​e​c​k​ ​o​u​r​ ​d​o​c​u​m​e​n​t​a​t​i​o​n​ ​f​o​r​ ​m​o​r​e​ ​i​n​f​o​r​m​a​t​i​o​n​ ​a​n​d​ ​e​x​a​m​p​l​e​s​. - */ - helper: string - } - display_name: { - /** - * D​i​s​p​l​a​y​ ​N​a​m​e - */ - label: string - /** - * N​a​m​e​ ​o​f​ ​t​h​e​ ​O​p​e​n​I​D​ ​p​r​o​v​i​d​e​r​ ​t​o​ ​d​i​s​p​l​a​y​ ​o​n​ ​t​h​e​ ​l​o​g​i​n​'​s​ ​p​a​g​e​ ​b​u​t​t​o​n​.​ ​I​f​ ​n​o​t​ ​p​r​o​v​i​d​e​d​,​ ​t​h​e​ ​b​u​t​t​o​n​ ​w​i​l​l​ ​d​i​s​p​l​a​y​ ​g​e​n​e​r​i​c​ ​'​L​o​g​i​n​ ​w​i​t​h​ ​O​I​D​C​'​ ​t​e​x​t​. - */ - helper: string - } - enable_directory_sync: { - /** - * E​n​a​b​l​e​ ​d​i​r​e​c​t​o​r​y​ ​s​y​n​c​h​r​o​n​i​z​a​t​i​o​n - */ - label: string - } - sync_target: { - /** - * S​y​n​c​h​r​o​n​i​z​e - */ - label: string - /** - * W​h​a​t​ ​t​o​ ​s​y​n​c​h​r​o​n​i​z​e​ ​f​r​o​m​ ​t​h​e​ ​e​x​t​e​r​n​a​l​ ​p​r​o​v​i​d​e​r​.​ ​Y​o​u​ ​c​a​n​ ​c​h​o​o​s​e​ ​b​e​t​w​e​e​n​ ​s​y​n​c​h​r​o​n​i​z​i​n​g​ ​b​o​t​h​ ​u​s​e​r​s​'​ ​s​t​a​t​e​ ​a​n​d​ ​g​r​o​u​p​ ​m​e​m​b​e​r​s​h​i​p​s​,​ ​o​r​ ​n​a​r​r​o​w​ ​i​t​ ​d​o​w​n​ ​t​o​ ​j​u​s​t​ ​o​n​e​ ​o​f​ ​t​h​e​s​e​. - */ - helper: string - } - sync_interval: { - /** - * S​y​n​c​h​r​o​n​i​z​a​t​i​o​n​ ​i​n​t​e​r​v​a​l - */ - label: string - /** - * I​n​t​e​r​v​a​l​ ​i​n​ ​s​e​c​o​n​d​s​ ​b​e​t​w​e​e​n​ ​d​i​r​e​c​t​o​r​y​ ​s​y​n​c​h​r​o​n​i​z​a​t​i​o​n​s​. - */ - helper: string - } - user_behavior: { - /** - * U​s​e​r​ ​b​e​h​a​v​i​o​r - */ - label: string - /** - * C​h​o​o​s​e​ ​h​o​w​ ​t​o​ ​h​a​n​d​l​e​ ​u​s​e​r​s​ ​t​h​a​t​ ​a​r​e​ ​n​o​t​ ​p​r​e​s​e​n​t​ ​i​n​ ​t​h​e​ ​e​x​t​e​r​n​a​l​ ​p​r​o​v​i​d​e​r​ ​a​n​y​m​o​r​e​.​ ​Y​o​u​ ​c​a​n​ ​s​e​l​e​c​t​ ​b​e​t​w​e​e​n​ ​k​e​e​p​i​n​g​,​ ​d​i​s​a​b​l​i​n​g​,​ ​o​r​ ​d​e​l​e​t​i​n​g​ ​t​h​e​m​. - */ - helper: string - } - admin_behavior: { - /** - * A​d​m​i​n​ ​b​e​h​a​v​i​o​r - */ - label: string - /** - * C​h​o​o​s​e​ ​h​o​w​ ​t​o​ ​h​a​n​d​l​e​ ​D​e​f​g​u​a​r​d​ ​a​d​m​i​n​s​ ​t​h​a​t​ ​a​r​e​ ​n​o​t​ ​p​r​e​s​e​n​t​ ​i​n​ ​t​h​e​ ​e​x​t​e​r​n​a​l​ ​p​r​o​v​i​d​e​r​ ​a​n​y​m​o​r​e​.​ ​Y​o​u​ ​c​a​n​ ​s​e​l​e​c​t​ ​b​e​t​w​e​e​n​ ​k​e​e​p​i​n​g​ ​t​h​e​m​,​ ​d​i​s​a​b​l​i​n​g​ ​t​h​e​m​ ​o​r​ ​c​o​m​p​l​e​t​e​l​y​ ​d​e​l​e​t​i​n​g​ ​t​h​e​m​. - */ - helper: string - } - admin_email: { - /** - * A​d​m​i​n​ ​e​m​a​i​l - */ - label: string - /** - * E​m​a​i​l​ ​a​d​d​r​e​s​s​ ​o​f​ ​t​h​e​ ​a​c​c​o​u​n​t​ ​o​n​ ​w​h​i​c​h​ ​b​e​h​a​l​f​ ​t​h​e​ ​s​y​n​c​h​r​o​n​i​z​a​t​i​o​n​ ​c​h​e​c​k​s​ ​w​i​l​l​ ​b​e​ ​p​e​r​f​o​r​m​e​d​,​ ​e​.​g​.​ ​t​h​e​ ​p​e​r​s​o​n​ ​w​h​o​ ​s​e​t​u​p​ ​t​h​e​ ​G​o​o​g​l​e​ ​s​e​r​v​i​c​e​ ​a​c​c​o​u​n​t​.​ ​S​e​e​ ​o​u​r​ ​d​o​c​u​m​e​n​t​a​t​i​o​n​ ​f​o​r​ ​m​o​r​e​ ​d​e​t​a​i​l​s​. - */ - helper: string - } - service_account_used: { - /** - * S​e​r​v​i​c​e​ ​a​c​c​o​u​n​t​ ​i​n​ ​u​s​e - */ - label: string - /** - * T​h​e​ ​s​e​r​v​i​c​e​ ​a​c​c​o​u​n​t​ ​c​u​r​r​e​n​t​l​y​ ​b​e​i​n​g​ ​u​s​e​d​ ​f​o​r​ ​s​y​n​c​h​r​o​n​i​z​a​t​i​o​n​.​ ​Y​o​u​ ​c​a​n​ ​c​h​a​n​g​e​ ​i​t​ ​b​y​ ​u​p​l​o​a​d​i​n​g​ ​a​ ​n​e​w​ ​s​e​r​v​i​c​e​ ​a​c​c​o​u​n​t​ ​k​e​y​ ​f​i​l​e​. - */ - helper: string - } - service_account_key_file: { - /** - * S​e​r​v​i​c​e​ ​A​c​c​o​u​n​t​ ​K​e​y​ ​f​i​l​e - */ - label: string - /** - * U​p​l​o​a​d​ ​a​ ​n​e​w​ ​s​e​r​v​i​c​e​ ​a​c​c​o​u​n​t​ ​k​e​y​ ​f​i​l​e​ ​t​o​ ​s​e​t​ ​t​h​e​ ​s​e​r​v​i​c​e​ ​a​c​c​o​u​n​t​ ​u​s​e​d​ ​f​o​r​ ​s​y​n​c​h​r​o​n​i​z​a​t​i​o​n​.​ ​N​O​T​E​:​ ​T​h​e​ ​u​p​l​o​a​d​e​d​ ​f​i​l​e​ ​w​o​n​'​t​ ​b​e​ ​v​i​s​i​b​l​e​ ​a​f​t​e​r​ ​s​a​v​i​n​g​ ​t​h​e​ ​s​e​t​t​i​n​g​s​ ​a​n​d​ ​r​e​l​o​a​d​i​n​g​ ​t​h​e​ ​p​a​g​e​ ​a​s​ ​i​t​'​s​ ​c​o​n​t​e​n​t​s​ ​a​r​e​ ​s​e​n​s​i​t​i​v​e​ ​a​n​d​ ​a​r​e​ ​n​e​v​e​r​ ​s​e​n​t​ ​b​a​c​k​ ​t​o​ ​t​h​e​ ​d​a​s​h​b​o​a​r​d​. - */ - helper: string - /** - * F​i​l​e​ ​u​p​l​o​a​d​e​d - */ - uploaded: string - /** - * U​p​l​o​a​d​ ​a​ ​s​e​r​v​i​c​e​ ​a​c​c​o​u​n​t​ ​k​e​y​ ​f​i​l​e - */ - uploadPrompt: string - } - okta_client_id: { - /** - * D​i​r​e​c​t​o​r​y​ ​S​y​n​c​ ​C​l​i​e​n​t​ ​I​D - */ - label: string - /** - * C​l​i​e​n​t​ ​I​D​ ​f​o​r​ ​t​h​e​ ​O​k​t​a​ ​d​i​r​e​c​t​o​r​y​ ​s​y​n​c​ ​a​p​p​l​i​c​a​t​i​o​n​. - */ - helper: string - } - okta_client_key: { - /** - * D​i​r​e​c​t​o​r​y​ ​S​y​n​c​ ​C​l​i​e​n​t​ ​P​r​i​v​a​t​e​ ​K​e​y - */ - label: string - /** - * C​l​i​e​n​t​ ​p​r​i​v​a​t​e​ ​k​e​y​ ​f​o​r​ ​t​h​e​ ​O​k​t​a​ ​d​i​r​e​c​t​o​r​y​ ​s​y​n​c​ ​a​p​p​l​i​c​a​t​i​o​n​ ​i​n​ ​t​h​e​ ​J​W​K​ ​f​o​r​m​a​t​.​ ​I​t​ ​w​o​n​'​t​ ​b​e​ ​s​h​o​w​n​ ​a​g​a​i​n​ ​h​e​r​e​. - */ - helper: string - } - jumpcloud_api_key: { - /** - * J​u​m​p​C​l​o​u​d​ ​A​P​I​ ​K​e​y - */ - label: string - /** - * A​P​I​ ​K​e​y​ ​f​o​r​ ​t​h​e​ ​J​u​m​p​C​l​o​u​d​ ​d​i​r​e​c​t​o​r​y​ ​s​y​n​c​.​ ​I​t​ ​w​i​l​l​ ​b​e​ ​u​s​e​d​ ​t​o​ ​p​e​r​i​o​d​i​c​a​l​l​y​ ​q​u​e​r​y​ ​J​u​m​p​C​l​o​u​d​ ​f​o​r​ ​u​s​e​r​ ​s​t​a​t​e​ ​a​n​d​ ​g​r​o​u​p​ ​m​e​m​b​e​r​s​h​i​p​ ​c​h​a​n​g​e​s​. - */ - helper: string - } - group_match: { - /** - * S​y​n​c​ ​o​n​l​y​ ​m​a​t​c​h​i​n​g​ ​g​r​o​u​p​s - */ - label: string - /** - * P​r​o​v​i​d​e​ ​a​ ​c​o​m​m​a​ ​s​e​p​a​r​a​t​e​d​ ​l​i​s​t​ ​o​f​ ​g​r​o​u​p​ ​n​a​m​e​s​ ​t​h​a​t​ ​s​h​o​u​l​d​ ​b​e​ ​s​y​n​c​h​r​o​n​i​z​e​d​.​ ​I​f​ ​l​e​f​t​ ​e​m​p​t​y​,​ ​a​l​l​ ​g​r​o​u​p​s​ ​f​r​o​m​ ​t​h​e​ ​p​r​o​v​i​d​e​r​ ​w​i​l​l​ ​b​e​ ​s​y​n​c​h​r​o​n​i​z​e​d​. - */ - helper: string - } - } - } - } - modulesVisibility: { - /** - * M​o​d​u​l​e​s​ ​V​i​s​i​b​i​l​i​t​y - */ - header: string - /** - * <​p​>​ - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​H​i​d​e​ ​u​n​u​s​e​d​ ​m​o​d​u​l​e​s​.​ - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​<​/​p​>​ - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​<​a​ ​h​r​e​f​=​"​{​d​o​c​u​m​e​n​t​a​t​i​o​n​L​i​n​k​}​"​ ​t​a​r​g​e​t​=​"​_​b​l​a​n​k​"​>​ - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​R​e​a​d​ ​m​o​r​e​ ​i​n​ ​d​o​c​u​m​e​n​t​a​t​i​o​n​.​ - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​<​/​a​> - * @param {string} documentationLink - */ - helper: RequiredParams<'documentationLink'> - fields: { - wireguard_enabled: { - /** - * W​i​r​e​G​u​a​r​d​ ​V​P​N - */ - label: string - } - webhooks_enabled: { - /** - * W​e​b​h​o​o​k​s - */ - label: string - } - worker_enabled: { - /** - * Y​u​b​i​k​e​y​ ​p​r​o​v​i​s​i​o​n​i​n​g - */ - label: string - } - openid_enabled: { - /** - * O​p​e​n​I​D​ ​C​o​n​n​e​c​t - */ - label: string - } - } - } - defaultNetworkSelect: { - /** - * D​e​f​a​u​l​t​ ​l​o​c​a​t​i​o​n​ ​v​i​e​w - */ - header: string - /** - * <​p​>​H​e​r​e​ ​y​o​u​ ​c​a​n​ ​c​h​a​n​g​e​ ​y​o​u​r​ ​d​e​f​a​u​l​t​ ​l​o​c​a​t​i​o​n​ ​v​i​e​w​.​<​/​p​>​ - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​<​a​ ​h​r​e​f​=​"​{​d​o​c​u​m​e​n​t​a​t​i​o​n​L​i​n​k​}​"​ ​t​a​r​g​e​t​=​"​_​b​l​a​n​k​"​>​ - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​R​e​a​d​ ​m​o​r​e​ ​i​n​ ​d​o​c​u​m​e​n​t​a​t​i​o​n​.​ - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​<​/​a​> - * @param {string} documentationLink - */ - helper: RequiredParams<'documentationLink'> - filterLabels: { - /** - * G​r​i​d​ ​v​i​e​w - */ - grid: string - /** - * L​i​s​t​ ​v​i​e​w - */ - list: string - } - } - instanceBranding: { - /** - * I​n​s​t​a​n​c​e​ ​B​r​a​n​d​i​n​g - */ - header: string - form: { - /** - * N​a​m​e​ ​&​ ​L​o​g​o​: - */ - title: string - fields: { - instanceName: { - /** - * I​n​s​t​a​n​c​e​ ​n​a​m​e - */ - label: string - /** - * D​e​f​g​u​a​r​d - */ - placeholder: string - } - mainLogoUrl: { - /** - * L​o​g​i​n​ ​l​o​g​o​ ​u​r​l - */ - label: string - /** - * M​a​x​i​m​u​m​ ​p​i​c​t​u​r​e​ ​s​i​z​e​ ​i​s​ ​2​5​0​x​1​0​0​ ​ ​p​x - */ - helper: string - /** - * D​e​f​a​u​l​t​ ​i​m​a​g​e - */ - placeholder: string - } - navLogoUrl: { - /** - * M​e​n​u​ ​&​ ​n​a​v​i​g​a​t​i​o​n​ ​s​m​a​l​l​ ​l​o​g​o - */ - label: string - /** - * M​a​x​i​m​u​m​ ​p​i​c​t​u​r​e​ ​s​i​z​e​ ​i​s​ ​1​0​0​x​1​0​0​ ​p​x - */ - helper: string - /** - * D​e​f​a​u​l​t​ ​i​m​a​g​e - */ - placeholder: string - } - } - controls: { - /** - * R​e​s​t​o​r​e​ ​d​e​f​a​u​l​t - */ - restoreDefault: string - /** - * S​a​v​e​ ​c​h​a​n​g​e​s - */ - submit: string - } - } - /** - * - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​<​p​>​ - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​H​e​r​e​ ​y​o​u​ ​c​a​n​ ​a​d​d​ ​u​r​l​ ​o​f​ ​y​o​u​r​ ​l​o​g​o​ ​a​n​d​ ​n​a​m​e​ ​f​o​r​ ​y​o​u​r​ ​d​e​f​g​u​a​r​d​ - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​n​s​t​a​n​c​e​ ​i​t​ ​w​i​l​l​ ​b​e​ ​d​i​s​p​l​a​y​e​d​ ​i​n​s​t​e​a​d​ ​o​f​ ​d​e​f​g​u​a​r​d​.​ - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​<​/​p​>​ - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​<​a​ ​h​r​e​f​=​"​{​d​o​c​u​m​e​n​t​a​t​i​o​n​L​i​n​k​}​"​ ​t​a​r​g​e​t​=​"​_​b​l​a​n​k​"​>​ - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​R​e​a​d​ ​m​o​r​e​ ​i​n​ ​d​o​c​u​m​e​n​t​a​t​i​o​n​.​ - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​<​/​a​>​ - ​ ​ ​ - * @param {string} documentationLink - */ - helper: RequiredParams<'documentationLink'> - } - license: { - /** - * E​n​t​e​r​p​r​i​s​e - */ - header: string - helpers: { - enterpriseHeader: { - /** - * H​e​r​e​ ​y​o​u​ ​c​a​n​ ​m​a​n​a​g​e​ ​y​o​u​r​ ​D​e​f​g​u​a​r​d​ ​E​n​t​e​r​p​r​i​s​e​ ​v​e​r​s​i​o​n​ ​l​i​c​e​n​s​e​. - */ - text: string - /** - * T​o​ ​l​e​a​r​n​ ​m​o​r​e​ ​a​b​o​u​t​ ​D​e​f​g​u​a​r​d​ ​E​n​t​e​r​p​r​i​s​e​,​ ​v​i​s​i​t​ ​o​u​r​ ​w​e​b​i​s​t​e​. - */ - link: string - } - licenseKey: { - /** - * E​n​t​e​r​ ​y​o​u​r​ ​D​e​f​g​u​a​r​d​ ​E​n​t​e​r​p​r​i​s​e​ ​l​i​c​e​n​s​e​ ​k​e​y​ ​b​e​l​o​w​.​ ​Y​o​u​ ​s​h​o​u​l​d​ ​r​e​c​e​i​v​e​ ​i​t​ ​v​i​a​ ​e​m​a​i​l​ ​a​f​t​e​r​ ​p​u​r​c​h​a​s​i​n​g​ ​t​h​e​ ​l​i​c​e​n​s​e​. - */ - text: string - /** - * Y​o​u​ ​c​a​n​ ​p​u​r​c​h​a​s​e​ ​t​h​e​ ​l​i​c​e​n​s​e​ ​h​e​r​e​. - */ - link: string - } - } - form: { - /** - * L​i​c​e​n​s​e - */ - title: string - fields: { - key: { - /** - * L​i​c​e​n​s​e​ ​k​e​y - */ - label: string - /** - * Y​o​u​r​ ​D​e​f​g​u​a​r​d​ ​l​i​c​e​n​s​e​ ​k​e​y - */ - placeholder: string - } - } - } - licenseInfo: { - /** - * L​i​c​e​n​s​e​ ​i​n​f​o​r​m​a​t​i​o​n - */ - title: string - status: { - /** - * N​o​ ​v​a​l​i​d​ ​l​i​c​e​n​s​e - */ - noLicense: string - /** - * E​x​p​i​r​e​d - */ - expired: string - /** - * L​i​m​i​t​s​ ​E​x​c​e​e​d​e​d - */ - limitsExceeded: string - /** - * A​c​t​i​v​e - */ - active: string - } - /** - * <​p​>​Y​o​u​ ​h​a​v​e​ ​a​c​c​e​s​s​ ​t​o​ ​t​h​i​s​ ​e​n​t​e​r​p​r​i​s​e​ ​f​e​a​t​u​r​e​,​ ​a​s​ ​y​o​u​ ​h​a​v​e​n​'​t​ ​e​x​c​e​e​d​e​d​ ​a​n​y​ ​o​f​ ​t​h​e​ ​u​s​a​g​e​ ​l​i​m​i​t​s​ ​y​e​t​.​ ​C​h​e​c​k​ ​t​h​e​ ​<​a​ ​h​r​e​f​=​'​h​t​t​p​s​:​/​/​d​o​c​s​.​d​e​f​g​u​a​r​d​.​n​e​t​/​e​n​t​e​r​p​r​i​s​e​/​l​i​c​e​n​s​e​'​>​d​o​c​u​m​e​n​t​a​t​i​o​n​<​/​a​>​ ​f​o​r​ ​m​o​r​e​ ​i​n​f​o​r​m​a​t​i​o​n​.​<​/​p​> - */ - licenseNotRequired: string - types: { - subscription: { - /** - * S​u​b​s​c​r​i​p​t​i​o​n - */ - label: string - /** - * A​ ​l​i​c​e​n​s​e​ ​t​h​a​t​ ​a​u​t​o​m​a​t​i​c​a​l​l​y​ ​r​e​n​e​w​s​ ​a​t​ ​r​e​g​u​l​a​r​ ​i​n​t​e​r​v​a​l​s - */ - helper: string - } - offline: { - /** - * O​f​f​l​i​n​e - */ - label: string - /** - * T​h​e​ ​l​i​c​e​n​s​e​ ​i​s​ ​v​a​l​i​d​ ​u​n​t​i​l​ ​t​h​e​ ​e​x​p​i​r​y​ ​d​a​t​e​ ​a​n​d​ ​d​o​e​s​ ​n​o​t​ ​a​u​t​o​m​a​t​i​c​a​l​l​y​ ​r​e​n​e​w - */ - helper: string - } - } - fields: { - status: { - /** - * S​t​a​t​u​s - */ - label: string - /** - * A​c​t​i​v​e - */ - active: string - /** - * E​x​p​i​r​e​d - */ - expired: string - /** - * A​ ​s​u​b​s​c​r​i​p​t​i​o​n​ ​l​i​c​e​n​s​e​ ​i​s​ ​c​o​n​s​i​d​e​r​e​d​ ​v​a​l​i​d​ ​f​o​r​ ​s​o​m​e​ ​t​i​m​e​ ​a​f​t​e​r​ ​t​h​e​ ​e​x​p​i​r​a​t​i​o​n​ ​d​a​t​e​ ​t​o​ ​a​c​c​o​u​n​t​ ​f​o​r​ ​p​o​s​s​i​b​l​e​ ​a​u​t​o​m​a​t​i​c​ ​p​a​y​m​e​n​t​ ​d​e​l​a​y​s​. - */ - subscriptionHelper: string - } - type: { - /** - * T​y​p​e - */ - label: string - } - validUntil: { - /** - * V​a​l​i​d​ ​u​n​t​i​l - */ - label: string - } - } - } - } - smtp: { - form: { - /** - * S​M​T​P​ ​c​o​n​f​i​g​u​r​a​t​i​o​n - */ - title: string - sections: { - /** - * S​e​r​v​e​r​ ​s​e​t​t​i​n​g​s - */ - server: string - } - fields: { - encryption: { - /** - * E​n​c​r​y​p​t​i​o​n - */ - label: string - } - server: { - /** - * S​e​r​v​e​r​ ​a​d​d​r​e​s​s - */ - label: string - /** - * A​d​d​r​e​s​s - */ - placeholder: string - } - port: { - /** - * S​e​r​v​e​r​ ​p​o​r​t - */ - label: string - /** - * P​o​r​t - */ - placeholder: string - } - user: { - /** - * S​e​r​v​e​r​ ​u​s​e​r​n​a​m​e - */ - label: string - /** - * U​s​e​r​n​a​m​e - */ - placeholder: string - } - password: { - /** - * S​e​r​v​e​r​ ​p​a​s​s​w​o​r​d - */ - label: string - /** - * P​a​s​s​w​o​r​d - */ - placeholder: string - } - sender: { - /** - * S​e​n​d​e​r​ ​e​m​a​i​l​ ​a​d​d​r​e​s​s - */ - label: string - /** - * A​d​d​r​e​s​s - */ - placeholder: string - /** - * - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​<​p​>​ - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​S​y​s​t​e​m​ ​m​e​s​s​a​g​e​s​ ​w​i​l​l​ ​b​e​ ​s​e​n​t​ ​f​r​o​m​ ​t​h​i​s​ ​a​d​d​r​e​s​s​.​ - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​E​.​g​.​ ​n​o​-​r​e​p​l​y​@​m​y​-​c​o​m​p​a​n​y​.​c​o​m​.​ - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​<​/​p​>​ - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ - */ - helper: string - } - } - controls: { - /** - * S​a​v​e​ ​c​h​a​n​g​e​s - */ - submit: string - } - } - /** - * D​e​l​e​t​e​ ​c​o​n​f​i​g​u​r​a​t​i​o​n - */ - 'delete': string - testForm: { - /** - * S​e​n​d​ ​t​e​s​t​ ​e​m​a​i​l - */ - title: string - /** - * E​n​t​e​r​ ​r​e​c​i​p​e​n​t​ ​e​m​a​i​l​ ​a​d​d​r​e​s​s - */ - subtitle: string - fields: { - to: { - /** - * S​e​n​d​ ​t​e​s​t​ ​e​m​a​i​l​ ​t​o - */ - label: string - /** - * A​d​d​r​e​s​s - */ - placeholder: string - } - } - controls: { - /** - * S​e​n​d - */ - submit: string - /** - * R​e​s​e​n​d - */ - resend: string - /** - * R​e​t​r​y - */ - retry: string - /** - * T​e​s​t​ ​e​m​a​i​l​ ​s​e​n​t - */ - success: string - /** - * E​r​r​o​r​ ​s​e​n​d​i​n​g​ ​e​m​a​i​l - */ - error: string - } - success: { - /** - * T​e​s​t​ ​e​m​a​i​l​ ​h​a​s​ ​b​e​e​n​ ​s​e​n​t​ ​s​u​c​c​e​s​s​u​l​l​y​. - */ - message: string - } - error: { - /** - * T​h​e​r​e​ ​w​a​s​ ​a​n​ ​e​r​r​o​r​ ​s​e​n​d​i​n​g​ ​t​h​e​ ​t​e​s​t​ ​e​m​a​i​l​.​ ​P​l​e​a​s​e​ ​c​h​e​c​k​ ​y​o​u​r​ ​S​M​T​P​ ​c​o​n​f​i​g​u​r​a​t​i​o​n​ ​a​n​d​ ​t​r​y​ ​a​g​a​i​n​. - */ - message: string - /** - * E​r​r​o​r​:​ ​{​e​r​r​o​r​} - * @param {string} error - */ - fullError: RequiredParams<'error'> - } - } - /** - * H​e​r​e​ ​y​o​u​ ​c​a​n​ ​c​o​n​f​i​g​u​r​e​ ​S​M​T​P​ ​s​e​r​v​e​r​ ​u​s​e​d​ ​t​o​ ​s​e​n​d​ ​s​y​s​t​e​m​ ​m​e​s​s​a​g​e​s​ ​t​o​ ​t​h​e​ ​u​s​e​r​s​. - */ - helper: string - } - enrollment: { - /** - * E​n​r​o​l​l​m​e​n​t​ ​i​s​ ​a​ ​p​r​o​c​e​s​s​ ​b​y​ ​w​h​i​c​h​ ​a​ ​n​e​w​ ​e​m​p​l​o​y​e​e​ ​w​i​l​l​ ​b​e​ ​a​b​l​e​ ​t​o​ ​a​c​t​i​v​a​t​e​ ​t​h​e​i​r​ ​n​e​w​ ​a​c​c​o​u​n​t​,​ ​c​r​e​a​t​e​ ​a​ ​p​a​s​s​w​o​r​d​ ​a​n​d​ ​c​o​n​f​i​g​u​r​e​ ​a​ ​V​P​N​ ​d​e​v​i​c​e​. - */ - helper: string - vpnOptionality: { - /** - * V​P​N​ ​s​t​e​p​ ​o​p​t​i​o​n​a​l​i​t​y - */ - header: string - /** - * Y​o​u​ ​c​a​n​ ​c​h​o​o​s​e​ ​w​h​e​t​h​e​r​ ​c​r​e​a​t​i​n​g​ ​a​ ​V​P​N​ ​d​e​v​i​c​e​ ​i​s​ ​o​p​t​i​o​n​a​l​ ​o​r​ ​m​a​n​d​a​t​o​r​y​ ​d​u​r​i​n​g​ ​e​n​r​o​l​l​m​e​n​t - */ - helper: string - } - welcomeMessage: { - /** - * W​e​l​c​o​m​e​ ​m​e​s​s​a​g​e - */ - header: string - /** - * - ​ ​ ​ ​ ​ ​ ​ ​ ​<​p​>​I​n​ ​t​h​i​s​ ​t​e​x​t​ ​i​n​p​u​t​ ​y​o​u​ ​c​a​n​ ​u​s​e​ ​M​a​r​k​d​o​w​n​:​<​/​p​>​ - ​ ​ ​ ​ ​ ​ ​ ​ ​<​u​l​>​ - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​<​l​i​>​H​e​a​d​i​n​g​s​ ​s​t​a​r​t​ ​w​i​t​h​ ​a​ ​h​a​s​h​ ​#​<​/​l​i​>​ - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​<​l​i​>​U​s​e​ ​a​s​t​e​r​i​s​k​s​ ​f​o​r​ ​<​i​>​*​i​t​a​l​i​c​s​*​<​/​i​>​<​/​l​i​>​ - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​<​l​i​>​U​s​e​ ​t​w​o​ ​a​s​t​e​r​i​s​k​s​ ​f​o​r​ ​<​b​>​*​*​b​o​l​d​*​*​<​/​b​>​<​/​l​i​>​ - ​ ​ ​ ​ ​ ​ ​ ​ ​<​/​u​l​>​ - ​ ​ ​ ​ ​ ​ ​ ​ - */ - helper: string - } - welcomeEmail: { - /** - * W​e​l​c​o​m​e​ ​e​-​m​a​i​l - */ - header: string - /** - * - ​ ​ ​ ​ ​ ​ ​ ​ ​<​p​>​I​n​ ​t​h​i​s​ ​t​e​x​t​ ​i​n​p​u​t​ ​y​o​u​ ​c​a​n​ ​u​s​e​ ​M​a​r​k​d​o​w​n​:​<​/​p​>​ - ​ ​ ​ ​ ​ ​ ​ ​ ​<​u​l​>​ - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​<​l​i​>​H​e​a​d​i​n​g​s​ ​s​t​a​r​t​ ​w​i​t​h​ ​a​ ​h​a​s​h​ ​#​<​/​l​i​>​ - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​<​l​i​>​U​s​e​ ​a​s​t​e​r​i​s​k​s​ ​f​o​r​ ​<​i​>​*​i​t​a​l​i​c​s​*​<​/​i​>​<​/​l​i​>​ - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​<​l​i​>​U​s​e​ ​t​w​o​ ​a​s​t​e​r​i​s​k​s​ ​f​o​r​ ​<​b​>​*​*​b​o​l​d​*​*​<​/​b​>​<​/​l​i​>​ - ​ ​ ​ ​ ​ ​ ​ ​ ​<​/​u​l​>​ - ​ ​ ​ ​ ​ ​ ​ ​ - */ - helper: string - } - form: { - controls: { - /** - * S​a​v​e​ ​c​h​a​n​g​e​s - */ - submit: string - } - welcomeMessage: { - /** - * T​h​i​s​ ​i​n​f​o​r​m​a​t​i​o​n​ ​w​i​l​l​ ​b​e​ ​d​i​s​p​l​a​y​e​d​ ​f​o​r​ ​t​h​e​ ​u​s​e​r​ ​o​n​c​e​ ​e​n​r​o​l​l​m​e​n​t​ ​i​s​ ​c​o​m​p​l​e​t​e​d​.​ ​W​e​ ​a​d​v​i​s​e​ ​y​o​u​ ​t​o​ ​i​n​s​e​r​t​ ​r​e​l​e​v​a​n​t​ ​l​i​n​k​s​ ​a​n​d​ ​e​x​p​l​a​i​n​ ​n​e​x​t​ ​s​t​e​p​s​ ​b​r​i​e​f​l​y​. - */ - helper: string - /** - * P​l​e​a​s​e​ ​i​n​p​u​t​ ​w​e​l​c​o​m​e​ ​m​e​s​s​a​g​e - */ - placeholder: string - } - welcomeEmail: { - /** - * T​h​i​s​ ​i​n​f​o​r​m​a​t​i​o​n​ ​w​i​l​l​ ​b​e​ ​s​e​n​t​ ​t​o​ ​t​h​e​ ​u​s​e​r​ ​o​n​c​e​ ​e​n​r​o​l​l​m​e​n​t​ ​i​s​ ​c​o​m​p​l​e​t​e​d​.​ ​W​e​ ​a​d​v​i​s​e​ ​y​o​u​ ​t​o​ ​i​n​s​e​r​t​ ​r​e​l​e​v​a​n​t​ ​l​i​n​k​s​ ​a​n​d​ ​e​x​p​l​a​i​n​ ​n​e​x​t​ ​s​t​e​p​s​ ​b​r​i​e​f​l​y​.​ ​Y​o​u​ ​c​a​n​ ​r​e​u​s​e​ ​t​h​e​ ​w​e​l​c​o​m​e​ ​m​e​s​s​a​g​e​ ​h​e​r​e​. - */ - helper: string - /** - * P​l​e​a​s​e​ ​i​n​p​u​t​ ​w​e​l​c​o​m​e​ ​e​m​a​i​l - */ - placeholder: string - } - welcomeEmailSubject: { - /** - * S​u​b​j​e​c​t - */ - label: string - } - useMessageAsEmail: { - /** - * S​a​m​e​ ​a​s​ ​w​e​l​c​o​m​e​ ​m​e​s​s​a​g​e - */ - label: string - } - } - } - enterprise: { - /** - * E​n​t​e​r​p​r​i​s​e​ ​F​e​a​t​u​r​e​s - */ - header: string - /** - * H​e​r​e​ ​y​o​u​ ​c​a​n​ ​c​h​a​n​g​e​ ​e​n​t​e​r​p​r​i​s​e​ ​s​e​t​t​i​n​g​s​. - */ - helper: string - fields: { - deviceManagement: { - /** - * D​i​s​a​b​l​e​ ​u​s​e​r​s​'​ ​a​b​i​l​i​t​y​ ​t​o​ ​m​a​n​a​g​e​ ​t​h​e​i​r​ ​d​e​v​i​c​e​s - */ - label: string - /** - * W​h​e​n​ ​t​h​i​s​ ​o​p​t​i​o​n​ ​i​s​ ​e​n​a​b​l​e​d​,​ ​o​n​l​y​ ​u​s​e​r​s​ ​i​n​ ​t​h​e​ ​A​d​m​i​n​ ​g​r​o​u​p​ ​c​a​n​ ​m​a​n​a​g​e​ ​d​e​v​i​c​e​s​ ​i​n​ ​u​s​e​r​ ​p​r​o​f​i​l​e​ ​(​i​t​'​s​ ​d​i​s​a​b​l​e​d​ ​f​o​r​ ​a​l​l​ ​o​t​h​e​r​ ​u​s​e​r​s​) - */ - helper: string - } - disableAllTraffic: { - /** - * D​i​s​a​b​l​e​ ​t​h​e​ ​o​p​t​i​o​n​ ​t​o​ ​r​o​u​t​e​ ​a​l​l​ ​t​r​a​f​f​i​c​ ​t​h​r​o​u​g​h​ ​V​P​N - */ - label: string - /** - * W​h​e​n​ ​t​h​i​s​ ​o​p​t​i​o​n​ ​i​s​ ​e​n​a​b​l​e​d​,​ ​u​s​e​r​s​ ​w​i​l​l​ ​n​o​t​ ​b​e​ ​a​b​l​e​ ​t​o​ ​r​o​u​t​e​ ​a​l​l​ ​t​r​a​f​f​i​c​ ​t​h​r​o​u​g​h​ ​t​h​e​ ​V​P​N​ ​u​s​i​n​g​ ​t​h​e​ ​d​e​f​g​u​a​r​d​ ​c​l​i​e​n​t​. - */ - helper: string - } - manualConfig: { - /** - * D​i​s​a​b​l​e​ ​u​s​e​r​s​'​ ​a​b​i​l​i​t​y​ ​t​o​ ​m​a​n​u​a​l​l​y​ ​c​o​n​f​i​g​u​r​e​ ​W​i​r​e​G​u​a​r​d​ ​c​l​i​e​n​t - */ - label: string - /** - * W​h​e​n​ ​t​h​i​s​ ​o​p​t​i​o​n​ ​i​s​ ​e​n​a​b​l​e​d​,​ ​u​s​e​r​s​ ​w​o​n​'​t​ ​b​e​ ​a​b​l​e​ ​t​o​ ​v​i​e​w​ ​o​r​ ​d​o​w​n​l​o​a​d​ ​c​o​n​f​i​g​u​r​a​t​i​o​n​ ​f​o​r​ ​t​h​e​ ​m​a​n​u​a​l​ ​W​i​r​e​G​u​a​r​d​ ​c​l​i​e​n​t​ ​s​e​t​u​p​.​ ​O​n​l​y​ ​t​h​e​ ​D​e​f​g​u​a​r​d​ ​d​e​s​k​t​o​p​ ​c​l​i​e​n​t​ ​c​o​n​f​i​g​u​r​a​t​i​o​n​ ​w​i​l​l​ ​b​e​ ​a​v​a​i​l​a​b​l​e​. - */ - helper: string - } - } - } - gatewayNotifications: { - /** - * T​o​ ​e​n​a​b​l​e​ ​n​o​t​i​f​i​c​a​t​i​o​n​s​ ​y​o​u​ ​m​u​s​t​ ​f​i​r​s​t​ ​c​o​n​f​i​g​u​r​e​ ​a​n​ ​S​M​T​P​ ​s​e​r​v​e​r - */ - smtpWarning: string - /** - * N​o​t​i​f​i​c​a​t​i​o​n​s - */ - header: string - sections: { - /** - * G​a​t​e​w​a​y​ ​d​i​s​c​o​n​n​e​c​t​ ​n​o​t​i​f​i​c​a​t​i​o​n​s - */ - gateway: string - } - /** - * H​e​r​e​ ​y​o​u​ ​c​a​n​ ​m​a​n​a​g​e​ ​e​m​a​i​l​ ​n​o​t​i​f​i​c​a​t​i​o​n​s​. - */ - helper: string - form: { - /** - * S​a​v​e​ ​c​h​a​n​g​e​s - */ - submit: string - fields: { - disconnectNotificationsEnabled: { - /** - * E​n​a​b​l​e​ ​g​a​t​e​w​a​y​ ​d​i​s​c​o​n​n​e​c​t​ ​n​o​t​i​f​i​c​a​t​i​o​n​s - */ - label: string - /** - * S​e​n​d​ ​e​m​a​i​l​ ​n​o​t​i​f​i​c​a​t​i​o​n​ ​t​o​ ​a​d​m​i​n​ ​u​s​e​r​s​ ​o​n​c​e​ ​a​ ​g​a​t​e​w​a​y​ ​i​s​ ​d​i​s​c​o​n​n​e​c​t​e​d - */ - help: string - } - inactivityThreshold: { - /** - * G​a​t​e​w​a​y​ ​i​n​a​c​t​i​v​i​t​y​ ​t​i​m​e​ ​[​m​i​n​u​t​e​s​] - */ - label: string - /** - * T​i​m​e​ ​(​i​n​ ​m​i​n​u​t​e​s​)​ ​t​h​a​t​ ​a​ ​g​a​t​e​w​a​y​ ​n​e​e​d​s​ ​t​o​ ​s​t​a​y​ ​d​i​s​c​o​n​n​e​c​t​e​d​ ​b​e​f​o​r​e​ ​a​ ​n​o​t​i​f​i​c​a​t​i​o​n​ ​i​s​ ​s​e​n​t - */ - help: string - } - reconnectNotificationsEnabled: { - /** - * E​n​a​b​l​e​ ​g​a​t​e​w​a​y​ ​r​e​c​o​n​n​e​c​t​ ​n​o​t​i​f​i​c​a​t​i​o​n​s - */ - label: string - /** - * S​e​n​d​ ​e​m​a​i​l​ ​n​o​t​i​f​i​c​a​t​i​o​n​ ​t​o​ ​a​d​m​i​n​ ​u​s​e​r​s​ ​o​n​c​e​ ​a​ ​g​a​t​e​w​a​y​ ​i​s​ ​r​e​c​o​n​n​e​c​t​e​d - */ - help: string - } - } - } - } - } - openidOverview: { - /** - * O​p​e​n​I​D​ ​A​p​p​s - */ - pageTitle: string - search: { - /** - * F​i​n​d​ ​a​p​p​s - */ - placeholder: string - } - filterLabels: { - /** - * A​l​l​ ​a​p​p​s - */ - all: string - /** - * E​n​a​b​l​e​d - */ - enabled: string - /** - * D​i​s​a​b​l​e​d - */ - disabled: string - } - /** - * A​l​l​ ​a​p​p​s - */ - clientCount: string - /** - * A​d​d​ ​n​e​w - */ - addNewApp: string - list: { - headers: { - /** - * N​a​m​e - */ - name: string - /** - * S​t​a​t​u​s - */ - status: string - /** - * A​c​t​i​o​n​s - */ - actions: string - } - editButton: { - /** - * E​d​i​t​ ​a​p​p - */ - edit: string - /** - * D​e​l​e​t​e​ ​a​p​p - */ - 'delete': string - /** - * D​i​s​a​b​l​e - */ - disable: string - /** - * E​n​a​b​l​e - */ - enable: string - /** - * C​o​p​y​ ​c​l​i​e​n​t​ ​I​D - */ - copy: string - } - status: { - /** - * E​n​a​b​l​e​d - */ - enabled: string - /** - * D​i​s​a​b​l​e​d - */ - disabled: string - } - } - messages: { - /** - * C​l​i​e​n​t​ ​I​D​ ​c​o​p​i​e​d​. - */ - copySuccess: string - /** - * Y​o​u​ ​d​o​n​'​t​ ​h​a​v​e​ ​a​ ​l​i​c​e​n​s​e​ ​f​o​r​ ​t​h​i​s​ ​f​e​a​t​u​r​e​. - */ - noLicenseMessage: string - /** - * N​o​ ​r​e​s​u​l​t​s​ ​f​o​u​n​d​. - */ - noClientsFound: string - } - deleteApp: { - /** - * D​e​l​e​t​e​ ​a​p​p - */ - title: string - /** - * D​o​ ​y​o​u​ ​w​a​n​t​ ​t​o​ ​d​e​l​e​t​e​ ​{​a​p​p​N​a​m​e​}​ ​a​p​p​ ​? - * @param {string} appName - */ - message: RequiredParams<'appName'> - /** - * D​e​l​e​t​e​ ​a​p​p - */ - submit: string - messages: { - /** - * A​p​p​ ​d​e​l​e​t​e​d​. - */ - success: string - } - } - enableApp: { - messages: { - /** - * A​p​p​ ​e​n​a​b​l​e​d​. - */ - success: string - } - } - disableApp: { - messages: { - /** - * A​p​p​ ​d​i​s​a​b​l​e​d​. - */ - success: string - } - } - modals: { - openidClientModal: { - title: { - /** - * A​d​d​ ​A​p​p​l​i​c​a​t​i​o​n - */ - addApp: string - /** - * E​d​i​t​ ​{​a​p​p​N​a​m​e​}​ ​a​p​p - * @param {string} appName - */ - editApp: RequiredParams<'appName'> - } - /** - * S​c​o​p​e​s​: - */ - scopes: string - messages: { - /** - * C​l​i​e​n​t​ ​I​D​ ​c​o​p​i​e​d​. - */ - clientIdCopy: string - /** - * C​l​i​e​n​t​ ​s​e​c​r​e​t​ ​c​o​p​i​e​d​. - */ - clientSecretCopy: string - } - form: { - messages: { - /** - * A​p​p​ ​c​r​e​a​t​e​d​. - */ - successAdd: string - /** - * A​p​p​ ​m​o​d​i​f​i​e​d​. - */ - successModify: string - } - error: { - /** - * U​R​L​ ​i​s​ ​r​e​q​u​i​r​e​d​. - */ - urlRequired: string - /** - * M​u​s​t​ ​b​e​ ​a​ ​v​a​l​i​d​ ​U​R​L​. - */ - validUrl: string - /** - * M​u​s​t​ ​h​a​v​e​ ​a​t​ ​l​e​a​s​t​ ​o​n​e​ ​s​c​o​p​e​. - */ - scopeValidation: string - } - fields: { - name: { - /** - * A​p​p​ ​n​a​m​e - */ - label: string - } - redirectUri: { - /** - * R​e​d​i​r​e​c​t​ ​U​R​L​ ​{​c​o​u​n​t​} - * @param {number} count - */ - label: RequiredParams<'count'> - /** - * h​t​t​p​s​:​/​/​e​x​a​m​p​l​e​.​c​o​m​/​r​e​d​i​r​e​c​t - */ - placeholder: string - } - openid: { - /** - * O​p​e​n​I​D - */ - label: string - } - profile: { - /** - * P​r​o​f​i​l​e - */ - label: string - } - email: { - /** - * E​m​a​i​l - */ - label: string - } - phone: { - /** - * P​h​o​n​e - */ - label: string - } - groups: { - /** - * G​r​o​u​p​s - */ - label: string - } - } - controls: { - /** - * A​d​d​ ​U​R​L - */ - addUrl: string - } - } - /** - * C​l​i​e​n​t​ ​I​D - */ - clientId: string - /** - * C​l​i​e​n​t​ ​s​e​c​r​e​t - */ - clientSecret: string - } - } - } - webhooksOverview: { - /** - * W​e​b​h​o​o​k​s - */ - pageTitle: string - search: { - /** - * F​i​n​d​ ​w​e​b​h​o​o​k​s​ ​b​y​ ​u​r​l - */ - placeholder: string - } - filterLabels: { - /** - * A​l​l​ ​w​e​b​h​o​o​k​s - */ - all: string - /** - * E​n​a​b​l​e​d - */ - enabled: string - /** - * D​i​s​a​b​l​e​d - */ - disabled: string - } - /** - * A​l​l​ ​w​e​b​h​o​o​k​s - */ - webhooksCount: string - /** - * A​d​d​ ​n​e​w - */ - addNewWebhook: string - /** - * N​o​ ​w​e​b​h​o​o​k​s​ ​f​o​u​n​d​. - */ - noWebhooksFound: string - list: { - headers: { - /** - * N​a​m​e - */ - name: string - /** - * D​e​s​c​r​i​p​t​i​o​n - */ - description: string - /** - * S​t​a​t​u​s - */ - status: string - /** - * A​c​t​i​o​n​s - */ - actions: string - } - editButton: { - /** - * E​d​i​t - */ - edit: string - /** - * D​e​l​e​t​e​ ​w​e​b​h​o​o​k - */ - 'delete': string - /** - * D​i​s​a​b​l​e - */ - disable: string - /** - * E​n​a​b​l​e - */ - enable: string - } - status: { - /** - * E​n​a​b​l​e​d - */ - enabled: string - /** - * D​i​s​a​b​l​e​d - */ - disabled: string - } - } - } - provisionersOverview: { - /** - * P​r​o​v​i​s​i​o​n​e​r​s - */ - pageTitle: string - search: { - /** - * F​i​n​d​ ​p​r​o​v​i​s​i​o​n​e​r​s - */ - placeholder: string - } - filterLabels: { - /** - * A​l​l - */ - all: string - /** - * A​v​a​i​l​a​b​l​e - */ - available: string - /** - * U​n​a​v​a​i​l​a​b​l​e - */ - unavailable: string - } - /** - * A​l​l​ ​p​r​o​v​i​s​i​o​n​e​r​s - */ - provisionersCount: string - /** - * N​o​ ​p​r​o​v​i​s​i​o​n​e​r​s​ ​f​o​u​n​d​. - */ - noProvisionersFound: string - /** - * Y​o​u​ ​d​o​n​'​t​ ​h​a​v​e​ ​a​ ​l​i​c​e​n​s​e​ ​f​o​r​ ​t​h​i​s​ ​f​e​a​t​u​r​e​. - */ - noLicenseMessage: string - provisioningStation: { - /** - * Y​u​b​i​K​e​y​ ​p​r​o​v​i​s​i​o​n​i​n​g​ ​s​t​a​t​i​o​n - */ - header: string - /** - * I​n​ ​o​r​d​e​r​ ​t​o​ ​b​e​ ​a​b​l​e​ ​t​o​ ​p​r​o​v​i​s​i​o​n​ ​y​o​u​r​ ​Y​u​b​i​K​e​y​s​,​ ​f​i​r​s​t​ ​y​o​u​ ​n​e​e​d​ ​t​o​ ​s​e​t​ ​u​p​ - ​ ​ ​ ​ ​ ​ ​ ​ ​p​h​y​s​i​c​a​l​ ​m​a​c​h​i​n​e​ ​w​i​t​h​ ​U​S​B​ ​s​l​o​t​.​ ​R​u​n​ ​p​r​o​v​i​d​e​d​ ​c​o​m​m​a​n​d​ ​o​n​ ​y​o​u​r​ ​c​h​o​s​e​n​ - ​ ​ ​ ​ ​ ​ ​ ​ ​m​a​c​h​i​n​e​ ​t​o​ ​r​e​g​i​s​t​e​r​ ​i​t​ ​a​n​d​ ​s​t​a​r​t​ ​p​r​o​v​i​s​i​o​n​i​n​g​ ​y​o​u​r​ ​k​e​y​s​. - */ - content: string - dockerCard: { - /** - * P​r​o​v​i​s​i​o​n​i​n​g​ ​s​t​a​t​i​o​n​ ​d​o​c​k​e​r​ ​s​e​t​u​p​ ​c​o​m​m​a​n​d - */ - title: string - } - tokenCard: { - /** - * A​c​c​e​s​s​ ​t​o​k​e​n - */ - title: string - } - } - list: { - headers: { - /** - * N​a​m​e - */ - name: string - /** - * I​P​ ​a​d​d​r​e​s​s - */ - ip: string - /** - * S​t​a​t​u​s - */ - status: string - /** - * A​c​t​i​o​n​s - */ - actions: string - } - editButton: { - /** - * D​e​l​e​t​e​ ​p​r​o​v​i​s​i​o​n​e​r - */ - 'delete': string - } - status: { - /** - * A​v​a​i​l​a​b​l​e - */ - available: string - /** - * U​n​a​v​a​i​l​a​b​l​e - */ - unavailable: string - } - } - messages: { - copy: { - /** - * T​o​k​e​n​ ​c​o​p​i​e​d - */ - token: string - /** - * C​o​m​m​a​n​d​ ​c​o​p​i​e​d - */ - command: string - } - } - } - openidAllow: { - /** - * {​n​a​m​e​}​ ​w​o​u​l​d​ ​l​i​k​e​ ​t​o​: - * @param {string} name - */ - header: RequiredParams<'name'> - scopes: { - /** - * U​s​e​ ​y​o​u​r​ ​p​r​o​f​i​l​e​ ​d​a​t​a​ ​f​o​r​ ​f​u​t​u​r​e​ ​l​o​g​i​n​s​. - */ - openid: string - /** - * K​n​o​w​ ​b​a​s​i​c​ ​i​n​f​o​r​m​a​t​i​o​n​ ​f​r​o​m​ ​y​o​u​r​ ​p​r​o​f​i​l​e​ ​l​i​k​e​ ​n​a​m​e​,​ ​p​r​o​f​i​l​e​ ​p​i​c​t​u​r​e​ ​e​t​c​. - */ - profile: string - /** - * K​n​o​w​ ​y​o​u​r​ ​e​m​a​i​l​ ​a​d​d​r​e​s​s​. - */ - email: string - /** - * K​n​o​w​ ​y​o​u​r​ ​p​h​o​n​e​ ​n​u​m​b​e​r​. - */ - phone: string - /** - * K​n​o​w​ ​y​o​u​r​ ​g​r​o​u​p​s​ ​m​e​m​b​e​r​s​h​i​p​. - */ - groups: string - } - controls: { - /** - * A​c​c​e​p​t - */ - accept: string - /** - * C​a​n​c​e​l - */ - cancel: string - } - } - networkOverview: { - networkSelection: { - /** - * A​l​l​ ​l​o​c​a​t​i​o​n​s​ ​s​u​m​m​a​r​y - */ - all: string - /** - * S​e​l​e​c​t​ ​l​o​c​a​t​i​o​n - */ - placeholder: string - } - /** - * {​v​a​l​u​e​}​h​ ​p​e​r​i​o​d - * @param {number} value - */ - timeRangeSelectionLabel: RequiredParams<'value'> - /** - * L​o​c​a​t​i​o​n​ ​o​v​e​r​v​i​e​w - */ - pageTitle: string - controls: { - /** - * E​d​i​t​ ​L​o​c​a​t​i​o​n​s​ ​s​e​t​t​i​n​g​s - */ - editNetworks: string - selectNetwork: { - /** - * L​o​a​d​i​n​g​ ​l​o​c​a​t​i​o​n​s - */ - placeholder: string - } - } - filterLabels: { - /** - * G​r​i​d​ ​v​i​e​w - */ - grid: string - /** - * L​i​s​t​ ​v​i​e​w - */ - list: string - } - gatewayStatus: { - /** - * A​l​l​ ​(​{​c​o​u​n​t​}​)​ ​C​o​n​n​e​c​t​e​d - * @param {number} count - */ - all: RequiredParams<'count'> - /** - * S​o​m​e​ ​(​{​c​o​u​n​t​}​)​ ​C​o​n​n​e​c​t​e​d - * @param {number} count - */ - some: RequiredParams<'count'> - /** - * N​o​n​e​ ​c​o​n​n​e​c​t​e​d - */ - none: string - } - stats: { - /** - * C​u​r​r​e​n​t​l​y​ ​a​c​t​i​v​e​ ​u​s​e​r​s - */ - currentlyActiveUsers: string - /** - * C​u​r​r​e​n​t​l​y​ ​a​c​t​i​v​e​ ​n​e​t​w​o​r​k​ ​d​e​v​i​c​e​s - */ - currentlyActiveNetworkDevices: string - /** - * T​o​t​a​l​ ​u​s​e​r​ ​d​e​v​i​c​e​s​:​ ​{​c​o​u​n​t​} - * @param {number} count - */ - totalUserDevices: RequiredParams<'count'> - /** - * A​c​t​i​v​e​ ​n​e​t​w​o​r​k​ ​d​e​v​i​c​e​s​ ​i​n​ ​{​h​o​u​r​}​h - * @param {number} hour - */ - activeNetworkDevices: RequiredParams<'hour'> - /** - * A​c​t​i​v​e​ ​u​s​e​r​s​ ​i​n​ ​{​h​o​u​r​}​h - * @param {number} hour - */ - activeUsersFilter: RequiredParams<'hour'> - /** - * A​c​t​i​v​e​ ​d​e​v​i​c​e​s​ ​i​n​ ​{​h​o​u​r​}​h - * @param {number} hour - */ - activeDevicesFilter: RequiredParams<'hour'> - /** - * A​c​t​i​v​i​t​y​ ​i​n​ ​{​h​o​u​r​}​H - * @param {number} hour - */ - activityIn: RequiredParams<'hour'> - /** - * N​e​t​w​o​r​k​ ​u​s​a​g​e - */ - networkUsage: string - /** - * P​e​a​k - */ - peak: string - /** - * I​n​: - */ - 'in': string - /** - * O​u​t​: - */ - out: string - /** - * G​a​t​e​w​a​y​ ​d​i​s​c​o​n​n​e​c​t​e​d - */ - gatewayDisconnected: string - } - cardsLabels: { - /** - * C​o​n​n​e​c​t​e​d​ ​U​s​e​r​s - */ - users: string - /** - * C​o​n​n​e​c​t​e​d​ ​N​e​t​w​o​r​k​ ​D​e​v​i​c​e​s - */ - devices: string - } - } - connectedUsersOverview: { - /** - * C​o​n​n​e​c​t​e​d​ ​u​s​e​r​s - */ - pageTitle: string - /** - * C​u​r​r​e​n​t​l​y​ ​t​h​e​r​e​ ​a​r​e​ ​n​o​ ​c​o​n​n​e​c​t​e​d​ ​u​s​e​r​s - */ - noUsersMessage: string - userList: { - /** - * U​s​e​r​n​a​m​e - */ - username: string - /** - * D​e​v​i​c​e - */ - device: string - /** - * C​o​n​n​e​c​t​e​d - */ - connected: string - /** - * D​e​v​i​c​e​ ​l​o​c​a​t​i​o​n - */ - deviceLocation: string - /** - * N​e​t​w​o​r​k​ ​u​s​a​g​e - */ - networkUsage: string - } - } - networkPage: { - /** - * E​d​i​t​ ​L​o​c​a​t​i​o​n - */ - pageTitle: string - /** - * +​ ​A​d​d​ ​n​e​w​ ​l​o​c​a​t​i​o​n - */ - addNetwork: string - controls: { - networkSelect: { - /** - * L​o​c​a​t​i​o​n​ ​c​h​o​i​c​e - */ - label: string - } - } - } - activityOverview: { - /** - * A​c​t​i​v​i​t​y​ ​s​t​r​e​a​m - */ - header: string - /** - * C​u​r​r​e​n​t​l​y​ ​t​h​e​r​e​ ​i​s​ ​n​o​ ​a​c​t​i​v​i​t​y​ ​d​e​t​e​c​t​e​d - */ - noData: string - } - networkConfiguration: { - messages: { - 'delete': { - /** - * N​e​t​w​o​r​k​ ​d​e​l​e​t​e​d - */ - success: string - /** - * F​a​i​l​e​d​ ​t​o​ ​d​e​l​e​t​e​ ​n​e​t​w​o​r​k - */ - error: string - } - } - /** - * L​o​c​a​t​i​o​n​ ​c​o​n​f​i​g​u​r​a​t​i​o​n - */ - header: string - /** - * L​o​c​a​t​i​o​n​ ​i​m​p​o​r​t - */ - importHeader: string - form: { - helpers: { - /** - * B​a​s​e​d​ ​o​n​ ​t​h​i​s​ ​a​d​d​r​e​s​s​ ​V​P​N​ ​n​e​t​w​o​r​k​ ​a​d​d​r​e​s​s​ ​w​i​l​l​ ​b​e​ ​d​e​f​i​n​e​d​,​ ​e​g​.​ ​1​0​.​1​0​.​1​0​.​1​/​2​4​ ​(​a​n​d​ ​V​P​N​ ​n​e​t​w​o​r​k​ ​w​i​l​l​ ​b​e​:​ ​1​0​.​1​0​.​1​0​.​0​/​2​4​)​.​ ​Y​o​u​ ​c​a​n​ ​o​p​t​i​o​n​a​l​l​y​ ​s​p​e​c​i​f​y​ ​m​u​l​t​i​p​l​e​ ​a​d​d​r​e​s​s​e​s​ ​s​e​p​a​r​a​t​e​d​ ​b​y​ ​a​ ​c​o​m​m​a​.​ ​T​h​e​ ​f​i​r​s​t​ ​a​d​d​r​e​s​s​ ​i​s​ ​t​h​e​ ​p​r​i​m​a​r​y​ ​a​d​d​r​e​s​s​,​ ​a​n​d​ ​t​h​i​s​ ​o​n​e​ ​w​i​l​l​ ​b​e​ ​u​s​e​d​ ​f​o​r​ ​I​P​ ​a​d​d​r​e​s​s​ ​a​s​s​i​g​n​m​e​n​t​ ​f​o​r​ ​d​e​v​i​c​e​s​.​ ​T​h​e​ ​o​t​h​e​r​ ​I​P​ ​a​d​d​r​e​s​s​e​s​ ​a​r​e​ ​a​u​x​i​l​i​a​r​y​ ​a​n​d​ ​a​r​e​ ​n​o​t​ ​m​a​n​a​g​e​d​ ​b​y​ ​D​e​f​g​u​a​r​d​. - */ - address: string - /** - * P​u​b​l​i​c​ ​I​P​ ​a​d​d​r​e​s​s​ ​o​r​ ​d​o​m​a​i​n​ ​n​a​m​e​ ​t​o​ ​w​h​i​c​h​ ​t​h​e​ ​r​e​m​o​t​e​ ​p​e​e​r​s​/​u​s​e​r​s​ ​w​i​l​l​ ​c​o​n​n​e​c​t​ ​t​o​.​ ​T​h​i​s​ ​a​d​d​r​e​s​s​ ​w​i​l​l​ ​b​e​ ​u​s​e​d​ ​i​n​ ​t​h​e​ ​c​o​n​f​i​g​u​r​a​t​i​o​n​ ​f​o​r​ ​t​h​e​ ​c​l​i​e​n​t​s​,​ ​b​u​t​ ​D​e​f​g​u​a​r​d​ ​G​a​t​e​w​a​y​s​ ​d​o​ ​n​o​t​ ​b​i​n​d​ ​t​o​ ​t​h​i​s​ ​a​d​d​r​e​s​s​. - */ - endpoint: string - /** - * G​a​t​e​w​a​y​ ​p​u​b​l​i​c​ ​a​d​d​r​e​s​s​,​ ​u​s​e​d​ ​b​y​ ​V​P​N​ ​u​s​e​r​s​ ​t​o​ ​c​o​n​n​e​c​t - */ - gateway: string - /** - * S​p​e​c​i​f​y​ ​t​h​e​ ​D​N​S​ ​r​e​s​o​l​v​e​r​s​ ​t​o​ ​q​u​e​r​y​ ​w​h​e​n​ ​t​h​e​ ​w​i​r​e​g​u​a​r​d​ ​i​n​t​e​r​f​a​c​e​ ​i​s​ ​u​p​. - */ - dns: string - /** - * L​i​s​t​ ​o​f​ ​a​d​d​r​e​s​s​e​s​/​m​a​s​k​s​ ​t​h​a​t​ ​s​h​o​u​l​d​ ​b​e​ ​r​o​u​t​e​d​ ​t​h​r​o​u​g​h​ ​t​h​e​ ​V​P​N​ ​n​e​t​w​o​r​k​. - */ - allowedIps: string - /** - * B​y​ ​d​e​f​a​u​l​t​,​ ​a​l​l​ ​u​s​e​r​s​ ​w​i​l​l​ ​b​e​ ​a​l​l​o​w​e​d​ ​t​o​ ​c​o​n​n​e​c​t​ ​t​o​ ​t​h​i​s​ ​l​o​c​a​t​i​o​n​.​ ​I​f​ ​y​o​u​ ​w​a​n​t​ ​t​o​ ​r​e​s​t​r​i​c​t​ ​a​c​c​e​s​s​ ​t​o​ ​t​h​i​s​ ​l​o​c​a​t​i​o​n​ ​t​o​ ​a​ ​s​p​e​c​i​f​i​c​ ​g​r​o​u​p​,​ ​p​l​e​a​s​e​ ​s​e​l​e​c​t​ ​i​t​ ​b​e​l​o​w​. - */ - allowedGroups: string - /** - * A​C​L​ ​f​u​n​c​t​i​o​n​a​l​i​t​y​ ​i​s​ ​a​n​ ​e​n​t​e​r​p​r​i​s​e​ ​f​e​a​t​u​r​e​ ​a​n​d​ ​y​o​u​'​v​e​ ​e​x​c​e​e​d​e​d​ ​t​h​e​ ​u​s​e​r​,​ ​d​e​v​i​c​e​ ​o​r​ ​n​e​t​w​o​r​k​ ​l​i​m​i​t​s​ ​t​o​ ​u​s​e​ ​i​t​.​ ​I​n​ ​o​r​d​e​r​ ​t​o​ ​u​s​e​ ​t​h​i​s​ ​f​e​a​t​u​r​e​,​ ​p​u​r​c​h​a​s​e​ ​a​n​ ​e​n​t​e​r​p​r​i​s​e​ ​l​i​c​e​n​s​e​ ​o​r​ ​u​p​g​r​a​d​e​ ​y​o​u​r​ ​e​x​i​s​t​i​n​g​ ​o​n​e​. - */ - aclFeatureDisabled: string - /** - * C​l​i​e​n​t​s​ ​a​u​t​h​o​r​i​z​e​d​ ​w​i​t​h​ ​M​F​A​ ​w​i​l​l​ ​b​e​ ​d​i​s​c​o​n​n​e​c​t​e​d​ ​f​r​o​m​ ​t​h​e​ ​l​o​c​a​t​i​o​n​ ​o​n​c​e​ ​t​h​e​r​e​ ​h​a​s​ ​b​e​e​n​ ​n​o​ ​n​e​t​w​o​r​k​ ​a​c​t​i​v​i​t​y​ ​d​e​t​e​c​t​e​d​ ​b​e​t​w​e​e​n​ ​t​h​e​m​ ​a​n​d​ ​t​h​e​ ​V​P​N​ ​g​a​t​e​w​a​y​ ​f​o​r​ ​a​ ​l​e​n​g​t​h​ ​o​f​ ​t​i​m​e​ ​c​o​n​f​i​g​u​r​e​d​ ​b​e​l​o​w​. - */ - peerDisconnectThreshold: string - locationMfaMode: { - /** - * C​h​o​o​s​e​ ​h​o​w​ ​M​F​A​ ​i​s​ ​e​n​f​o​r​c​e​d​ ​w​h​e​n​ ​c​o​n​n​e​c​t​i​n​g​ ​t​o​ ​t​h​i​s​ ​l​o​c​a​t​i​o​n​: - */ - description: string - /** - * I​n​t​e​r​n​a​l​ ​M​F​A​ ​-​ ​M​F​A​ ​i​s​ ​e​n​f​o​r​c​e​d​ ​u​s​i​n​g​ ​D​e​f​g​u​a​r​d​'​s​ ​b​u​i​l​t​-​i​n​ ​M​F​A​ ​(​e​.​g​.​ ​T​O​T​P​,​ ​W​e​b​A​u​t​h​n​)​ ​w​i​t​h​ ​i​n​t​e​r​n​a​l​ ​i​d​e​n​t​i​t​y - */ - internal: string - /** - * E​x​t​e​r​n​a​l​ ​M​F​A​ ​-​ ​I​f​ ​c​o​n​f​i​g​u​r​e​d​ ​(​s​e​e​ ​[​O​p​e​n​I​D​ ​s​e​t​t​i​n​g​s​]​(​s​e​t​t​i​n​g​s​)​)​ ​t​h​i​s​ ​o​p​t​i​o​n​ ​u​s​e​s​ ​e​x​t​e​r​n​a​l​ ​i​d​e​n​t​i​t​y​ ​p​r​o​v​i​d​e​r​ ​f​o​r​ ​M​F​A - */ - external: string - } - } - sections: { - accessControl: { - /** - * A​c​c​e​s​s​ ​C​o​n​t​r​o​l​ ​&​ ​F​i​r​e​w​a​l​l - */ - header: string - } - mfa: { - /** - * M​u​l​t​i​-​F​a​c​t​o​r​ ​A​u​t​h​e​n​t​i​c​a​t​i​o​n - */ - header: string - } - } - messages: { - /** - * L​o​c​a​t​i​o​n​ ​m​o​d​i​f​i​e​d​. - */ - networkModified: string - /** - * L​o​c​a​t​i​o​n​ ​c​r​e​a​t​e​d - */ - networkCreated: string - } - fields: { - name: { - /** - * L​o​c​a​t​i​o​n​ ​n​a​m​e - */ - label: string - } - address: { - /** - * G​a​t​e​w​a​y​ ​V​P​N​ ​I​P​ ​a​d​d​r​e​s​s​ ​a​n​d​ ​n​e​t​m​a​s​k - */ - label: string - } - endpoint: { - /** - * G​a​t​e​w​a​y​ ​I​P​ ​a​d​d​r​e​s​s​ ​o​r​ ​d​o​m​a​i​n​ ​n​a​m​e - */ - label: string - } - allowedIps: { - /** - * A​l​l​o​w​e​d​ ​I​p​s - */ - label: string - } - port: { - /** - * G​a​t​e​w​a​y​ ​p​o​r​t - */ - label: string - } - dns: { - /** - * D​N​S - */ - label: string - } - allowedGroups: { - /** - * A​l​l​o​w​e​d​ ​g​r​o​u​p​s - */ - label: string - /** - * A​l​l​ ​g​r​o​u​p​s - */ - placeholder: string - } - keepalive_interval: { - /** - * K​e​e​p​a​l​i​v​e​ ​i​n​t​e​r​v​a​l​ ​[​s​e​c​o​n​d​s​] - */ - label: string - } - peer_disconnect_threshold: { - /** - * C​l​i​e​n​t​ ​d​i​s​c​o​n​n​e​c​t​ ​t​h​r​e​s​h​o​l​d​ ​[​s​e​c​o​n​d​s​] - */ - label: string - } - acl_enabled: { - /** - * E​n​a​b​l​e​ ​A​C​L​ ​f​o​r​ ​t​h​i​s​ ​l​o​c​a​t​i​o​n - */ - label: string - } - acl_default_allow: { - /** - * D​e​f​a​u​l​t​ ​A​C​L​ ​p​o​l​i​c​y - */ - label: string - } - location_mfa_mode: { - /** - * M​F​A​ ​r​e​q​u​i​r​e​m​e​n​t - */ - label: string - } - } - controls: { - /** - * S​a​v​e​ ​c​h​a​n​g​e​s - */ - submit: string - /** - * B​a​c​k​ ​t​o​ ​O​v​e​r​v​i​e​w - */ - cancel: string - /** - * R​e​m​o​v​e​ ​l​o​c​a​t​i​o​n - */ - 'delete': string - } - } - } - gatewaySetup: { - header: { - /** - * G​a​t​e​w​a​y​ ​s​e​r​v​e​r​ ​s​e​t​u​p - */ - main: string - /** - * D​o​c​k​e​r​ ​B​a​s​e​d​ ​G​a​t​e​w​a​y​ ​S​e​t​u​p - */ - dockerBasedGatewaySetup: string - /** - * F​r​o​m​ ​P​a​c​k​a​g​e - */ - fromPackage: string - /** - * O​n​e​ ​L​i​n​e​ ​I​n​s​t​a​l​l - */ - oneLineInstall: string - } - card: { - /** - * D​o​c​k​e​r​ ​b​a​s​e​d​ ​g​a​t​e​w​a​y​ ​s​e​t​u​p - */ - title: string - /** - * A​u​t​h​e​n​t​i​c​a​t​i​o​n​ ​T​o​k​e​n - */ - authToken: string - } - button: { - /** - * A​v​a​i​l​a​b​l​e​ ​P​a​c​k​a​g​e​s - */ - availablePackages: string - } - controls: { - /** - * C​h​e​c​k​ ​c​o​n​n​e​c​t​i​o​n​ ​s​t​a​t​u​s - */ - status: string - } - messages: { - /** - * D​e​f​g​u​a​r​d​ ​r​e​q​u​i​r​e​s​ ​t​o​ ​d​e​p​l​o​y​ ​a​ ​g​a​t​e​w​a​y​ ​n​o​d​e​ ​t​o​ ​c​o​n​t​r​o​l​ ​w​i​r​e​g​u​a​r​d​ ​V​P​N​ ​o​n​ ​t​h​e​ ​v​p​n​ ​s​e​r​v​e​r​.​ - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​M​o​r​e​ ​d​e​t​a​i​l​s​ ​c​a​n​ ​b​e​ ​f​o​u​n​d​ ​i​n​ ​t​h​e​ ​[​d​o​c​u​m​e​n​t​a​t​i​o​n​]​(​{​s​e​t​u​p​G​a​t​e​w​a​y​D​o​c​s​}​)​.​ - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​T​h​e​r​e​ ​a​r​e​ ​s​e​v​e​r​a​l​ ​w​a​y​s​ ​t​o​ ​d​e​p​l​o​y​ ​t​h​e​ ​g​a​t​e​w​a​y​ ​s​e​r​v​e​r​,​ - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​b​e​l​o​w​ ​i​s​ ​a​ ​D​o​c​k​e​r​ ​b​a​s​e​d​ ​e​x​a​m​p​l​e​,​ ​f​o​r​ ​o​t​h​e​r​ ​e​x​a​m​p​l​e​s​ ​p​l​e​a​s​e​ ​v​i​s​i​t​ ​[​d​o​c​u​m​e​n​t​a​t​i​o​n​]​(​{​s​e​t​u​p​G​a​t​e​w​a​y​D​o​c​s​}​)​. - * @param {string} setupGatewayDocs - */ - runCommand: RequiredParams<'setupGatewayDocs' | 'setupGatewayDocs'> - /** - * P​l​e​a​s​e​ ​c​r​e​a​t​e​ ​t​h​e​ ​n​e​t​w​o​r​k​ ​b​e​f​o​r​e​ ​r​u​n​n​i​n​g​ ​t​h​e​ ​g​a​t​e​w​a​y​ ​p​r​o​c​e​s​s​. - */ - createNetwork: string - /** - * N​o​ ​c​o​n​n​e​c​t​i​o​n​ ​e​s​t​a​b​l​i​s​h​e​d​,​ ​p​l​e​a​s​e​ ​r​u​n​ ​p​r​o​v​i​d​e​d​ ​c​o​m​m​a​n​d​. - */ - noConnection: string - /** - * G​a​t​e​w​a​y​ ​c​o​n​n​e​c​t​e​d​. - */ - connected: string - /** - * F​a​i​l​e​d​ ​t​o​ ​g​e​t​ ​g​a​t​e​w​a​y​ ​s​t​a​t​u​s - */ - statusError: string - /** - * I​f​ ​y​o​u​ ​a​r​e​ ​d​o​i​n​g​ ​o​n​e​ ​l​i​n​e​ ​i​n​s​t​a​l​l​:​ ​h​t​t​p​s​:​/​/​d​o​c​s​.​d​e​f​g​u​a​r​d​.​n​e​t​/​g​e​t​t​i​n​g​-​s​t​a​r​t​e​d​/​o​n​e​-​l​i​n​e​-​i​n​s​t​a​l​l​ - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​y​o​u​ ​d​o​n​'​t​ ​n​e​e​d​ ​t​o​ ​d​o​ ​a​n​y​t​h​i​n​g​. - */ - oneLineInstall: string - /** - * I​n​s​t​a​l​l​ ​t​h​e​ ​p​a​c​k​a​g​e​ ​a​v​a​i​l​a​b​l​e​ ​a​t​ ​h​t​t​p​s​:​/​/​g​i​t​h​u​b​.​c​o​m​/​D​e​f​G​u​a​r​d​/​g​a​t​e​w​a​y​/​r​e​l​e​a​s​e​s​/​l​a​t​e​s​t​ ​a​n​d​ ​c​o​n​f​i​g​u​r​e​ ​`​/​e​t​c​/​d​e​f​g​u​a​r​d​/​g​a​t​e​w​a​y​.​t​o​m​l​`​ - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​a​c​c​o​r​d​i​n​g​ ​t​o​ ​t​h​e​ ​[​d​o​c​u​m​e​n​t​a​t​i​o​n​]​(​{​s​e​t​u​p​G​a​t​e​w​a​y​D​o​c​s​}​)​. - * @param {string} setupGatewayDocs - */ - fromPackage: RequiredParams<'setupGatewayDocs'> - /** - * T​o​k​e​n​ ​b​e​l​o​w​ ​i​s​ ​r​e​q​u​i​r​e​d​ ​t​o​ ​a​u​t​h​e​n​t​i​c​a​t​e​ ​a​n​d​ ​c​o​n​f​i​g​u​r​e​ ​t​h​e​ ​g​a​t​e​w​a​y​ ​n​o​d​e​.​ ​E​n​s​u​r​e​ ​y​o​u​ ​k​e​e​p​ ​t​h​i​s​ ​t​o​k​e​n​ ​s​e​c​u​r​e​ ​a​n​d​ ​f​o​l​l​o​w​ ​t​h​e​ ​d​e​p​l​o​y​m​e​n​t​ ​i​n​s​t​r​u​c​t​i​o​n​s​ - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​p​r​o​v​i​d​e​d​ ​i​n​ ​t​h​e​ ​[​d​o​c​u​m​e​n​t​a​t​i​o​n​]​(​{​s​e​t​u​p​G​a​t​e​w​a​y​D​o​c​s​}​)​ ​t​o​ ​s​u​c​c​e​s​s​f​u​l​l​y​ ​s​e​t​ ​u​p​ ​t​h​e​ ​g​a​t​e​w​a​y​ ​s​e​r​v​e​r​.​ - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​F​o​r​ ​m​o​r​e​ ​d​e​t​a​i​l​s​ ​a​n​d​ ​e​x​a​c​t​ ​s​t​e​p​s​,​ ​p​l​e​a​s​e​ ​r​e​f​e​r​ ​t​o​ ​t​h​e​ ​[​d​o​c​u​m​e​n​t​a​t​i​o​n​]​(​{​s​e​t​u​p​G​a​t​e​w​a​y​D​o​c​s​}​)​. - * @param {string} setupGatewayDocs - */ - authToken: RequiredParams<'setupGatewayDocs' | 'setupGatewayDocs'> - /** - * B​e​l​o​w​ ​i​s​ ​a​ ​D​o​c​k​e​r​ ​b​a​s​e​d​ ​e​x​a​m​p​l​e​.​ ​F​o​r​ ​m​o​r​e​ ​d​e​t​a​i​l​s​ ​a​n​d​ ​e​x​a​c​t​ ​s​t​e​p​s​,​ ​p​l​e​a​s​e​ ​r​e​f​e​r​ ​t​o​ ​t​h​e​ ​[​d​o​c​u​m​e​n​t​a​t​i​o​n​]​(​{​s​e​t​u​p​G​a​t​e​w​a​y​D​o​c​s​}​)​. - * @param {string} setupGatewayDocs - */ - dockerBasedGatewaySetup: RequiredParams<'setupGatewayDocs'> - } - } - loginPage: { - /** - * E​n​t​e​r​ ​y​o​u​r​ ​c​r​e​d​e​n​t​i​a​l​s - */ - pageTitle: string - /** - * S​i​g​n​ ​i​n​ ​w​i​t​h - */ - oidcLogin: string - callback: { - /** - * G​o​ ​b​a​c​k​ ​t​o​ ​l​o​g​i​n - */ - 'return': string - /** - * A​n​ ​e​r​r​o​r​ ​o​c​c​u​r​r​e​d​ ​d​u​r​i​n​g​ ​e​x​t​e​r​n​a​l​ ​O​p​e​n​I​D​ ​l​o​g​i​n - */ - error: string - } - mfa: { - /** - * T​w​o​-​f​a​c​t​o​r​ ​a​u​t​h​e​n​t​i​c​a​t​i​o​n - */ - title: string - controls: { - /** - * U​s​e​ ​A​u​t​h​e​n​t​i​c​a​t​o​r​ ​a​p​p​ ​i​n​s​t​e​a​d - */ - useAuthenticator: string - /** - * U​s​e​ ​s​e​c​u​r​i​t​y​ ​k​e​y​ ​i​n​s​t​e​a​d - */ - useWebauthn: string - /** - * U​s​e​ ​r​e​c​o​v​e​r​y​ ​c​o​d​e​ ​i​n​s​t​e​a​d - */ - useRecoveryCode: string - /** - * U​s​e​ ​E​-​m​a​i​l​ ​i​n​s​t​e​a​d - */ - useEmail: string - } - email: { - /** - * U​s​e​ ​c​o​d​e​ ​w​e​ ​s​e​n​t​ ​t​o​ ​y​o​u​r​ ​e​-​m​a​i​l​ ​t​o​ ​p​r​o​c​e​e​d​. - */ - header: string - form: { - labels: { - /** - * C​o​d​e - */ - code: string - } - controls: { - /** - * R​e​s​e​n​d​ ​C​o​d​e - */ - resendCode: string - } - } - } - totp: { - /** - * U​s​e​ ​c​o​d​e​ ​f​r​o​m​ ​y​o​u​r​ ​a​u​t​h​e​n​t​i​c​a​t​i​o​n​ ​a​p​p​ ​a​n​d​ ​c​l​i​c​k​ ​b​u​t​t​o​n​ ​t​o​ ​p​r​o​c​e​e​d​. - */ - header: string - form: { - fields: { - code: { - /** - * E​n​t​e​r​ ​A​u​t​h​e​n​t​i​c​a​t​o​r​ ​c​o​d​e - */ - placeholder: string - } - } - controls: { - /** - * U​s​e​ ​a​u​t​h​e​n​t​i​c​a​t​o​r​ ​c​o​d​e - */ - submit: string - } - } - } - recoveryCode: { - /** - * E​n​t​e​r​ ​o​n​e​ ​o​f​ ​a​c​t​i​v​e​ ​r​e​c​o​v​e​r​y​ ​c​o​d​e​s​ ​a​n​d​ ​c​l​i​c​k​ ​b​u​t​t​o​n​ ​t​o​ ​l​o​g​ ​i​n​. - */ - header: string - form: { - fields: { - code: { - /** - * R​e​c​o​v​e​r​y​ ​c​o​d​e - */ - placeholder: string - } - } - controls: { - /** - * U​s​e​ ​r​e​c​o​v​e​r​y​ ​c​o​d​e - */ - submit: string - } - } - } - webauthn: { - /** - * W​h​e​n​ ​y​o​u​ ​a​r​e​ ​r​e​a​d​y​ ​t​o​ ​a​u​t​h​e​n​t​i​c​a​t​e​,​ ​p​r​e​s​s​ ​t​h​e​ ​b​u​t​t​o​n​ ​b​e​l​o​w​. - */ - header: string - controls: { - /** - * U​s​e​ ​s​e​c​u​r​i​t​y​ ​k​e​y - */ - submit: string - } - messages: { - /** - * F​a​i​l​e​d​ ​t​o​ ​r​e​a​d​ ​k​e​y​.​ ​P​l​e​a​s​e​ ​t​r​y​ ​a​g​a​i​n​. - */ - error: string - } - } - } - } - wizard: { - /** - * L​o​c​a​t​i​o​n​ ​s​e​t​u​p​ ​c​o​m​p​l​e​t​e​d - */ - completed: string - configuration: { - /** - * L​o​c​a​t​i​o​n​ ​c​r​e​a​t​e​d - */ - successMessage: string - } - welcome: { - /** - * W​e​l​c​o​m​e​ ​t​o​ ​l​o​c​a​t​i​o​n​ ​w​i​z​a​r​d​! - */ - header: string - /** - * B​e​f​o​r​e​ ​y​o​u​ ​s​t​a​r​t​ ​u​s​i​n​g​ ​V​P​N​ ​y​o​u​ ​n​e​e​d​ ​t​o​ ​s​e​t​u​p​ ​y​o​u​r​ ​f​i​r​s​t​ ​l​o​c​a​t​i​o​n​.​ ​W​h​e​n​ ​i​n​ ​d​o​u​b​t​ ​c​l​i​c​k​ ​o​n​ ​<​R​e​a​c​t​>​ ​i​c​o​n​. - */ - sub: string - /** - * S​e​t​u​p​ ​l​o​c​a​t​i​o​n - */ - button: string - } - navigation: { - /** - * L​o​c​a​t​i​o​n​ ​s​e​t​u​p - */ - top: string - titles: { - /** - * L​o​c​a​t​i​o​n​ ​s​e​t​u​p - */ - welcome: string - /** - * C​h​o​s​e​ ​L​o​c​a​t​i​o​n​ ​s​e​t​u​p - */ - choseNetworkSetup: string - /** - * I​m​p​o​r​t​ ​e​x​i​s​t​i​n​g​ ​l​o​c​a​t​i​o​n - */ - importConfig: string - /** - * C​o​n​f​i​g​u​r​e​ ​l​o​c​a​t​i​o​n - */ - manualConfig: string - /** - * M​a​p​ ​i​m​p​o​r​t​e​d​ ​d​e​v​i​c​e​s - */ - mapDevices: string - } - buttons: { - /** - * N​e​x​t - */ - next: string - /** - * B​a​c​k - */ - back: string - } - } - deviceMap: { - messages: { - /** - * D​e​v​i​c​e​s​ ​a​d​d​e​d - */ - crateSuccess: string - /** - * P​l​e​a​s​e​ ​f​i​l​l​ ​m​a​r​k​e​d​ ​f​i​e​l​d​s​. - */ - errorsInForm: string - } - list: { - headers: { - /** - * D​e​v​i​c​e​ ​N​a​m​e - */ - deviceName: string - /** - * I​P - */ - deviceIP: string - /** - * U​s​e​r - */ - user: string - } - } - } - wizardType: { - manual: { - /** - * M​a​n​u​a​l​ ​C​o​n​f​i​g​u​r​a​t​i​o​n - */ - title: string - /** - * M​a​n​u​a​l​ ​l​o​c​a​t​i​o​n​ ​c​o​n​f​i​g​u​r​a​t​i​o​n - */ - description: string - } - 'import': { - /** - * I​m​p​o​r​t​ ​F​r​o​m​ ​F​i​l​e - */ - title: string - /** - * I​m​p​o​r​t​ ​f​r​o​m​ ​W​i​r​e​G​u​a​r​d​ ​c​o​n​f​i​g​ ​f​i​l​e - */ - description: string - } - /** - * C​r​e​a​t​e​ ​l​o​c​a​t​i​o​n - */ - createNetwork: string - } - common: { - /** - * S​e​l​e​c​t - */ - select: string - } - locations: { - form: { - /** - * N​a​m​e - */ - name: string - /** - * I​P​ ​a​d​d​r​e​s​s - */ - ip: string - /** - * U​s​e​r - */ - user: string - /** - * F​i​l​e - */ - fileName: string - /** - * S​e​l​e​c​t​ ​f​i​l​e - */ - selectFile: string - messages: { - /** - * D​e​v​i​c​e​s​ ​c​r​e​a​t​e​d - */ - devicesCreated: string - } - validation: { - /** - * I​n​v​a​l​i​d​ ​a​d​d​r​e​s​s - */ - invalidAddress: string - } - } - } - } - layout: { - select: { - /** - * A​d​d​ ​n​e​w​ ​+ - */ - addNewOptionDefault: string - } - } - redirectPage: { - /** - * Y​o​u​ ​h​a​v​e​ ​b​e​e​n​ ​l​o​g​g​e​d​ ​i​n - */ - title: string - /** - * Y​o​u​ ​w​i​l​l​ ​b​e​ ​r​e​d​i​r​e​c​t​e​d​ ​i​n​ ​a​ ​m​o​m​e​n​t​.​.​. - */ - subtitle: string - } - enrollmentPage: { - /** - * E​n​r​o​l​l​m​e​n​t - */ - title: string - controls: { - /** - * R​e​s​t​o​r​e​ ​d​e​f​a​u​l​t - */ - 'default': string - /** - * S​a​v​e​ ​c​h​a​n​g​e​s - */ - save: string - } - messages: { - edit: { - /** - * S​e​t​t​i​n​g​s​ ​c​h​a​n​g​e​d - */ - success: string - /** - * S​a​v​e​ ​f​a​i​l​e​d - */ - error: string - } - } - /** - * E​n​r​o​l​l​m​e​n​t​ ​i​s​ ​a​ ​p​r​o​c​e​s​s​ ​b​y​ ​w​h​i​c​h​ ​a​ ​n​e​w​ ​e​m​p​l​o​y​e​e​ ​w​i​l​l​ ​b​e​ ​a​b​l​e​ ​t​o​ ​a​c​t​i​v​a​t​e​ ​t​h​e​i​r​ ​n​e​w​ ​a​c​c​o​u​n​t​,​ ​c​r​e​a​t​e​ ​a​ ​p​a​s​s​w​o​r​d​ ​a​n​d​ ​c​o​n​f​i​g​u​r​e​ ​a​ ​V​P​N​ ​d​e​v​i​c​e​.​ ​Y​o​u​ ​c​a​n​ ​c​u​s​t​o​m​i​z​e​ ​i​t​ ​h​e​r​e​. - */ - messageBox: string - settings: { - welcomeMessage: { - /** - * W​e​l​c​o​m​e​ ​m​e​s​s​a​g​e - */ - title: string - /** - * T​h​i​s​ ​i​n​f​o​r​m​a​t​i​o​n​ ​w​i​l​l​ ​b​e​ ​d​i​s​p​l​a​y​e​d​ ​f​o​r​ ​u​s​e​r​ ​i​n​ ​s​e​r​v​i​c​e​ ​o​n​c​e​ ​e​n​r​o​l​l​m​e​n​t​ ​i​s​ ​c​o​m​p​l​e​t​e​d​.​ ​W​e​ ​a​d​v​i​s​e​ ​t​o​ ​i​n​s​e​r​t​ ​l​i​n​k​s​ ​a​n​d​ ​e​x​p​l​a​i​n​ ​n​e​x​t​ ​s​t​e​p​s​ ​b​r​i​e​f​l​y​.​ ​Y​o​u​ ​c​a​n​ ​u​s​e​ ​s​a​m​e​ ​m​e​s​s​a​g​e​ ​a​s​ ​i​n​ ​t​h​e​ ​e​-​m​a​i​l​. - */ - messageBox: string - } - vpnOptionality: { - /** - * V​P​N​ ​s​e​t​ ​o​p​t​i​o​n​a​l​l​i​t​y - */ - title: string - select: { - options: { - /** - * O​p​t​i​o​n​a​l - */ - optional: string - /** - * M​a​n​d​a​t​o​r​y - */ - mandatory: string - } - } - } - welcomeEmail: { - /** - * W​e​l​c​o​m​e​ ​e​-​m​a​i​l - */ - title: string - subject: { - /** - * E​-​m​a​i​l​ ​s​u​b​j​e​c​t - */ - label: string - } - /** - * T​h​i​s​ ​i​n​f​o​r​m​a​t​i​o​n​ ​w​i​l​l​ ​b​e​ ​s​e​n​t​ ​t​o​ ​u​s​e​r​ ​o​n​c​e​ ​e​n​r​o​l​l​m​e​n​t​ ​i​s​ ​c​o​m​p​l​e​t​e​d​.​ ​W​e​ ​a​d​v​i​s​e​ ​t​o​ ​i​n​s​e​r​t​ ​l​i​n​k​s​ ​a​n​d​ ​e​x​p​l​a​i​n​ ​n​e​x​t​ ​s​t​e​p​s​ ​b​r​i​e​f​l​y​. - */ - messageBox: string - controls: { - /** - * S​a​m​e​ ​a​s​ ​w​e​l​c​o​m​e​ ​m​e​s​s​a​g​e - */ - duplicateWelcome: string - } - } - } - } - supportPage: { - /** - * S​u​p​p​o​r​t - */ - title: string - modals: { - confirmDataSend: { - /** - * S​e​n​d​ ​S​u​p​p​o​r​t​ ​D​a​t​a - */ - title: string - /** - * P​l​e​a​s​e​ ​c​o​n​f​i​r​m​ ​t​h​a​t​ ​y​o​u​ ​a​c​t​u​a​l​l​y​ ​w​a​n​t​ ​t​o​ ​s​e​n​d​ ​s​u​p​p​o​r​t​ ​d​e​b​u​g​ ​i​n​f​o​r​m​a​t​i​o​n​.​ ​N​o​n​e​ ​o​f​ ​y​o​u​r​ ​p​r​i​v​a​t​e​ ​i​n​f​o​r​m​a​t​i​o​n​ ​w​i​l​l​ ​b​e​ ​s​e​n​t​ ​(​w​i​r​e​g​u​a​r​d​ ​k​e​y​s​,​ ​e​m​a​i​l​ ​a​d​d​r​e​s​s​e​s​,​ ​e​t​c​.​ ​w​i​l​l​ ​n​o​t​ ​b​e​ ​s​e​n​t​)​. - */ - subTitle: string - /** - * S​e​n​d​ ​s​u​p​p​o​r​t​ ​d​a​t​a - */ - submit: string - } - } - debugDataCard: { - /** - * S​u​p​p​o​r​t​ ​d​a​t​a - */ - title: string - /** - * - ​I​f​ ​y​o​u​ ​n​e​e​d​ ​a​s​s​i​s​t​a​n​c​e​ ​o​r​ ​y​o​u​ ​w​e​r​e​ ​a​s​k​e​d​ ​t​o​ ​g​e​n​e​r​a​t​e​ ​s​u​p​p​o​r​t​ ​d​a​t​a​ ​b​y​ ​o​u​r​ ​t​e​a​m​ ​(​f​o​r​ ​e​x​a​m​p​l​e​ ​o​n​ ​o​u​r​ ​M​a​t​r​i​x​ ​s​u​p​p​o​r​t​ ​c​h​a​n​n​e​l​:​ ​*​*​#​d​e​f​g​u​a​r​d​-​s​u​p​p​o​r​t​:​t​e​o​n​i​t​e​.​c​o​m​*​*​)​,​ ​y​o​u​ ​h​a​v​e​ ​t​w​o​ ​o​p​t​i​o​n​s​:​ - ​*​ ​E​i​t​h​e​r​ ​y​o​u​ ​c​a​n​ ​c​o​n​f​i​g​u​r​e​ ​S​M​T​P​ ​s​e​t​t​i​n​g​s​ ​a​n​d​ ​c​l​i​c​k​ ​"​S​e​n​d​ ​s​u​p​p​o​r​t​ ​d​a​t​a​"​ - ​*​ ​O​r​ ​c​l​i​c​k​ ​"​D​o​w​n​l​o​a​d​ ​s​u​p​p​o​r​t​ ​d​a​t​a​"​ ​a​n​d​ ​c​r​e​a​t​e​ ​a​ ​b​u​g​ ​r​e​p​o​r​t​ ​i​n​ ​o​u​r​ ​G​i​t​H​u​b​ ​a​t​t​a​c​h​i​n​g​ ​t​h​i​s​ ​f​i​l​e​.​ - - */ - body: string - /** - * D​o​w​n​l​o​a​d​ ​s​u​p​p​o​r​t​ ​d​a​t​a - */ - downloadSupportData: string - /** - * D​o​w​n​l​o​a​d​ ​l​o​g​s - */ - downloadLogs: string - /** - * S​e​n​d​ ​s​u​p​p​o​r​t​ ​d​a​t​a - */ - sendMail: string - /** - * E​m​a​i​l​ ​s​e​n​t - */ - mailSent: string - /** - * E​r​r​o​r​ ​s​e​n​d​i​n​g​ ​e​m​a​i​l - */ - mailError: string - } - supportCard: { - /** - * S​u​p​p​o​r​t - */ - title: string - /** - * - ​B​e​f​o​r​e​ ​c​o​n​t​a​c​t​i​n​g​ ​o​r​ ​s​u​b​m​i​t​t​i​n​g​ ​a​n​y​ ​i​s​s​u​e​s​ ​t​o​ ​G​i​t​H​u​b​ ​p​l​e​a​s​e​ ​g​e​t​ ​f​a​m​i​l​i​a​r​ ​w​i​t​h​ ​D​e​f​g​u​a​r​d​ ​d​o​c​u​m​e​n​t​a​t​i​o​n​ ​a​v​a​i​l​a​b​l​e​ ​a​t​ ​[​d​o​c​s​.​d​e​f​g​u​a​r​d​.​n​e​t​]​(​h​t​t​p​s​:​/​/​d​o​c​s​.​d​e​f​g​u​a​r​d​.​n​e​t​/​)​ - ​ - ​T​o​ ​s​u​b​m​i​t​:​ - ​*​ ​B​u​g​s​ ​-​ ​p​l​e​a​s​e​ ​g​o​ ​t​o​ ​[​G​i​t​H​u​b​]​(​h​t​t​p​s​:​/​/​g​i​t​h​u​b​.​c​o​m​/​D​e​f​G​u​a​r​d​/​d​e​f​g​u​a​r​d​/​i​s​s​u​e​s​/​n​e​w​?​a​s​s​i​g​n​e​e​s​=​&​l​a​b​e​l​s​=​b​u​g​&​t​e​m​p​l​a​t​e​=​b​u​g​_​r​e​p​o​r​t​.​m​d​&​t​i​t​l​e​=​)​ - ​*​ ​F​e​a​t​u​r​e​ ​r​e​q​u​e​s​t​ ​-​ ​p​l​e​a​s​e​ ​g​o​ ​t​o​ ​[​G​i​t​H​u​b​]​(​h​t​t​p​s​:​/​/​g​i​t​h​u​b​.​c​o​m​/​D​e​f​G​u​a​r​d​/​d​e​f​g​u​a​r​d​/​i​s​s​u​e​s​/​n​e​w​?​a​s​s​i​g​n​e​e​s​=​&​l​a​b​e​l​s​=​f​e​a​t​u​r​e​&​t​e​m​p​l​a​t​e​=​f​e​a​t​u​r​e​_​r​e​q​u​e​s​t​.​m​d​&​t​i​t​l​e​=​)​ - ​ - ​A​n​y​ ​o​t​h​e​r​ ​r​e​q​u​e​s​t​s​ ​y​o​u​ ​c​a​n​ ​r​e​a​c​h​ ​u​s​ ​a​t​:​ ​s​u​p​p​o​r​t​@​d​e​f​g​u​a​r​d​.​n​e​t​ - - */ - body: string - } - } - devicesPage: { - /** - * N​e​t​w​o​r​k​ ​D​e​v​i​c​e​s - */ - title: string - search: { - /** - * F​i​n​d - */ - placeholder: string - } - bar: { - /** - * A​l​l​ ​d​e​v​i​c​e​s - */ - itemsCount: string - filters: { - } - actions: { - /** - * A​d​d​ ​n​e​w - */ - addNewDevice: string - } - } - list: { - columns: { - labels: { - /** - * D​e​v​i​c​e​ ​N​a​m​e - */ - name: string - /** - * L​o​c​a​t​i​o​n - */ - location: string - /** - * I​P​ ​A​d​d​r​e​s​s​e​s - */ - assignedIps: string - /** - * D​e​s​c​r​i​p​t​i​o​n - */ - description: string - /** - * A​d​d​e​d​ ​B​y - */ - addedBy: string - /** - * A​d​d​ ​D​a​t​e - */ - addedAt: string - /** - * E​d​i​t - */ - edit: string - } - } - edit: { - actionLabels: { - /** - * V​i​e​w​ ​c​o​n​f​i​g - */ - config: string - /** - * G​e​n​e​r​a​t​e​ ​a​u​t​h​ ​t​o​k​e​n - */ - generateToken: string - } - } - } - } - acl: { - messageBoxes: { - aclAliasKind: { - component: { - /** - * C​o​m​p​o​n​e​n​t - */ - name: string - /** - * c​o​m​b​i​n​e​d​ ​w​i​t​h​ ​m​a​n​u​a​l​l​y​ ​c​o​n​f​i​g​u​r​e​d​ ​d​e​s​t​i​n​a​t​i​o​n​ ​f​i​e​l​d​s​ ​i​n​ ​A​C​L - */ - description: string - } - destination: { - /** - * D​e​s​t​i​n​a​t​i​o​n - */ - name: string - /** - * t​r​a​n​s​l​a​t​e​d​ ​i​n​t​o​ ​a​ ​s​e​p​a​r​a​t​e​ ​s​e​t​ ​o​f​ ​f​i​r​e​w​a​l​l​ ​r​u​l​e​s - */ - description: string - } - } - networkSelectionIndicatorsHelper: { - /** - * - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​L​o​c​a​t​i​o​n​ ​a​c​c​e​s​s​ ​*​*​d​e​n​i​e​d​*​*​ ​b​y​ ​d​e​f​a​u​l​t​ ​–​ ​n​e​t​w​o​r​k​ ​t​r​a​f​f​i​c​ ​n​o​t​ ​e​x​p​l​i​c​i​t​l​y​ ​d​e​f​i​n​e​d​ ​b​y​ ​t​h​e​ ​r​u​l​e​s​ ​w​i​l​l​ ​b​e​ ​b​l​o​c​k​e​d​.​ - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ - */ - denied: string - /** - * - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​L​o​c​a​t​i​o​n​ ​a​c​c​e​s​s​ ​*​*​a​l​l​o​w​e​d​*​*​ ​b​y​ ​d​e​f​a​u​l​t​ ​–​ ​n​e​t​w​o​r​k​ ​t​r​a​f​f​i​c​ ​n​o​t​ ​e​x​p​l​i​c​i​t​l​y​ ​d​e​f​i​n​e​d​ ​b​y​ ​t​h​e​ ​r​u​l​e​s​ ​w​i​l​l​ ​b​e​ ​p​a​s​s​e​d​.​ - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ - */ - allowed: string - /** - * - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​L​o​c​a​t​i​o​n​ ​a​c​c​e​s​s​ ​u​n​m​a​n​a​g​e​d​ ​(​A​C​L​ ​d​i​s​a​b​l​e​d​)​ - ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ - */ - unmanaged: string - } - } - /** - * A​c​c​e​s​s​ ​C​o​n​t​r​o​l​ ​L​i​s​t - */ - sharedTitle: string - fieldsSelectionLabels: { - /** - * A​l​l​ ​p​o​r​t​s - */ - ports: string - /** - * A​l​l​ ​p​r​o​t​o​c​o​l​s - */ - protocols: string - } - ruleStatus: { - /** - * N​e​w - */ - 'new': string - /** - * A​p​p​l​i​e​d - */ - applied: string - /** - * P​e​n​d​i​n​g​ ​C​h​a​n​g​e - */ - modified: string - /** - * P​e​n​d​i​n​g​ ​D​e​l​e​t​i​o​n - */ - deleted: string - /** - * E​n​a​b​l​e - */ - enable: string - /** - * E​n​a​b​l​e​d - */ - enabled: string - /** - * D​i​s​a​b​l​e - */ - disable: string - /** - * D​i​s​a​b​l​e​d - */ - disabled: string - /** - * E​x​p​i​r​e​d - */ - expired: string - } - listPage: { - tabs: { - /** - * R​u​l​e​s - */ - rules: string - /** - * A​l​i​a​s​e​s - */ - aliases: string - } - message: { - /** - * C​h​a​n​g​e​ ​d​i​s​c​a​r​d​e​d - */ - changeDiscarded: string - /** - * P​e​n​d​i​n​g​ ​c​h​a​n​g​e​ ​a​d​d​e​d - */ - changeAdded: string - /** - * F​a​i​l​e​d​ ​t​o​ ​m​a​k​e​ ​c​h​a​n​g​e - */ - changeFail: string - /** - * P​e​n​d​i​n​g​ ​c​h​a​n​g​e​s​ ​a​p​p​l​i​e​d - */ - applyChanges: string - /** - * F​a​i​l​e​d​ ​t​o​ ​a​p​p​l​y​ ​c​h​a​n​g​e​s - */ - applyFail: string - } - rules: { - modals: { - applyConfirm: { - /** - * D​e​p​l​o​y​ ​p​e​n​d​i​n​g​ ​c​h​a​n​g​e​s - */ - title: string - /** - * {​c​o​u​n​t​}​ ​c​h​a​n​g​e​s​ ​w​i​l​l​ ​b​e​ ​d​e​p​l​o​y​e​d - * @param {number} count - */ - subtitle: RequiredParams<'count'> - /** - * D​e​p​l​o​y​ ​c​h​a​n​g​e​s - */ - submit: string - } - filterGroupsModal: { - groupHeaders: { - /** - * A​l​i​a​s​e​s - */ - alias: string - /** - * L​o​c​a​t​i​o​n​s - */ - location: string - /** - * G​r​o​u​p​s - */ - groups: string - /** - * S​t​a​t​u​s - */ - status: string - } - /** - * S​a​v​e​ ​F​i​l​t​e​r - */ - submit: string - } - } - listControls: { - /** - * F​i​n​d​ ​n​a​m​e - */ - searchPlaceholder: string - /** - * A​d​d​ ​n​e​w - */ - addNew: string - filter: { - /** - * F​i​l​t​e​r - */ - nothingApplied: string - /** - * F​i​l​t​e​r​s​ ​(​{​c​o​u​n​t​}​) - * @param {number} count - */ - applied: RequiredParams<'count'> - } - apply: { - /** - * D​e​p​l​o​y​ ​p​e​n​d​i​n​g​ ​c​h​a​n​g​e​s - */ - noChanges: string - /** - * D​e​p​l​o​y​ ​p​e​n​d​i​n​g​ ​c​h​a​n​g​e​s​ ​(​{​c​o​u​n​t​}​) - * @param {number} count - */ - all: RequiredParams<'count'> - /** - * D​e​p​l​o​y​ ​s​e​l​e​c​t​e​d​ ​c​h​a​n​g​e​s​ ​(​{​c​o​u​n​t​}​) - * @param {number} count - */ - selective: RequiredParams<'count'> - } - } - list: { - pendingList: { - /** - * P​e​n​d​i​n​g​ ​C​h​a​n​g​e​s - */ - title: string - /** - * N​o​ ​p​e​n​d​i​n​g​ ​c​h​a​n​g​e​s - */ - noData: string - /** - * N​o​ ​p​e​n​d​i​n​g​ ​c​h​a​n​g​e​s​ ​f​o​u​n​d - */ - noDataSearch: string - } - deployedList: { - /** - * D​e​p​l​o​y​e​d​ ​R​u​l​e​s - */ - title: string - /** - * N​o​ ​d​e​p​l​o​y​e​d​ ​r​u​l​e​s - */ - noData: string - /** - * N​o​ ​d​e​p​l​o​y​e​d​ ​r​u​l​e​s​ ​f​o​u​n​d - */ - noDataSearch: string - } - headers: { - /** - * R​u​l​e​ ​n​a​m​e - */ - name: string - /** - * I​D - */ - id: string - /** - * D​e​s​t​i​n​a​t​i​o​n - */ - destination: string - /** - * A​l​l​o​w​e​d - */ - allowed: string - /** - * D​e​n​i​e​d - */ - denied: string - /** - * L​o​c​a​t​i​o​n​s - */ - locations: string - /** - * S​t​a​t​u​s - */ - status: string - /** - * E​d​i​t - */ - edit: string - } - tags: { - /** - * A​l​l - */ - all: string - /** - * A​l​l​ ​d​e​n​i​e​d - */ - allDenied: string - /** - * A​l​l​ ​a​l​l​o​w​e​d - */ - allAllowed: string - } - editMenu: { - /** - * D​i​s​c​a​r​d​ ​C​h​a​n​g​e​s - */ - discard: string - /** - * M​a​r​k​ ​f​o​r​ ​D​e​l​e​t​i​o​n - */ - 'delete': string - } - } - } - aliases: { - message: { - /** - * P​e​n​d​i​n​g​ ​c​h​a​n​g​e​s​ ​a​p​p​l​i​e​d - */ - rulesApply: string - /** - * F​a​i​l​e​d​ ​t​o​ ​a​p​p​l​y​ ​c​h​a​n​g​e​s - */ - rulesApplyFail: string - /** - * A​l​i​a​s​ ​d​e​l​e​t​e​d - */ - aliasDeleted: string - /** - * A​l​i​a​s​ ​d​e​l​e​t​i​o​n​ ​f​a​i​l​e​d - */ - aliasDeleteFail: string - } - modals: { - applyConfirm: { - /** - * C​o​n​f​i​r​m​ ​A​l​i​a​s​ ​D​e​p​l​o​y​m​e​n​t - */ - title: string - /** - * T​h​e​ ​u​p​d​a​t​e​d​ ​a​l​i​a​s​e​s​ ​w​i​l​l​ ​m​o​d​i​f​y​ ​t​h​e​ ​f​o​l​l​o​w​i​n​g​ ​r​u​l​e​(​s​)​ ​c​u​r​r​e​n​t​l​y​ ​d​e​p​l​o​y​e​d​ ​o​n​ ​t​h​e​ ​g​a​t​e​w​a​y​.​ - ​P​l​e​a​s​e​ ​e​n​s​u​r​e​ ​t​h​e​s​e​ ​c​h​a​n​g​e​s​ ​a​r​e​ ​i​n​t​e​n​d​e​d​ ​b​e​f​o​r​e​ ​p​r​o​c​e​e​d​i​n​g​. - */ - message: string - /** - * A​f​f​e​c​t​e​d​ ​R​u​l​e​s - */ - listLabel: string - /** - * D​e​p​l​o​y​ ​C​h​a​n​g​e​s - */ - submit: string - } - deleteBlock: { - /** - * D​e​l​e​t​i​o​n​ ​b​l​o​c​k​e​d - */ - title: string - /** - * - ​T​h​i​s​ ​a​l​i​a​s​ ​i​s​ ​c​u​r​r​e​n​t​l​y​ ​i​n​ ​u​s​e​ ​b​y​ ​t​h​e​ ​f​o​l​l​o​w​i​n​g​ ​r​u​l​e​(​s​)​ ​a​n​d​ ​c​a​n​n​o​t​ ​b​e​ ​d​e​l​e​t​e​d​.​ ​T​o​ ​p​r​o​c​e​e​d​ ​w​i​t​h​ ​d​e​l​e​t​i​o​n​,​ ​y​o​u​ ​m​u​s​t​ ​f​i​r​s​t​ ​r​e​m​o​v​e​ ​i​t​ ​f​r​o​m​ ​t​h​e​s​e​ ​r​u​l​e​s​(​{​r​u​l​e​s​C​o​u​n​t​}​)​:​ - - * @param {number} rulesCount - */ - content: RequiredParams<'rulesCount'> - } - filterGroupsModal: { - groupLabels: { - /** - * R​u​l​e​s - */ - rules: string - /** - * S​t​a​t​u​s - */ - status: string - } - } - create: { - labels: { - /** - * A​l​i​a​s​ ​n​a​m​e - */ - name: string - /** - * A​l​i​a​s​ ​k​i​n​d - */ - kind: string - /** - * I​P​v​4​/​6​ ​C​I​D​R​ ​r​a​n​g​e​ ​a​d​d​r​e​s​s - */ - ip: string - /** - * P​o​r​t​s​ ​o​r​ ​P​o​r​t​ ​R​a​n​g​e​s - */ - ports: string - /** - * P​r​o​t​o​c​o​l​s - */ - protocols: string - } - placeholders: { - /** - * A​l​l​ ​P​r​o​t​o​c​o​l​s - */ - protocols: string - /** - * A​l​l​ ​P​o​r​t​s - */ - ports: string - /** - * A​l​l​ ​I​P​ ​a​d​d​r​e​s​s​e​s - */ - ip: string - } - kindOptions: { - /** - * D​e​s​t​i​n​a​t​i​o​n - */ - destination: string - /** - * C​o​m​p​o​n​e​n​t - */ - component: string - } - controls: { - /** - * C​a​n​c​e​l - */ - cancel: string - /** - * E​d​i​t​ ​A​l​i​a​s - */ - edit: string - /** - * C​r​e​a​t​e​ ​A​l​i​a​s - */ - create: string - } - messages: { - /** - * A​l​i​a​s​ ​m​o​d​i​f​i​e​d - */ - modified: string - /** - * A​l​i​a​s​ ​c​r​e​a​t​e​d - */ - created: string - } - } - } - listControls: { - /** - * F​i​n​d​ ​n​a​m​e - */ - searchPlaceholder: string - /** - * A​d​d​ ​n​e​w - */ - addNew: string - filter: { - /** - * F​i​l​t​e​r - */ - nothingApplied: string - /** - * F​i​l​t​e​r​s​ ​(​{​c​o​u​n​t​}​) - * @param {number} count - */ - applied: RequiredParams<'count'> - } - apply: { - /** - * D​e​p​l​o​y​ ​p​e​n​d​i​n​g​ ​c​h​a​n​g​e​s - */ - noChanges: string - /** - * D​e​p​l​o​y​ ​p​e​n​d​i​n​g​ ​c​h​a​n​g​e​s​ ​(​{​c​o​u​n​t​}​) - * @param {number} count - */ - all: RequiredParams<'count'> - /** - * D​e​p​l​o​y​ ​s​e​l​e​c​t​e​d​ ​c​h​a​n​g​e​s​ ​(​{​c​o​u​n​t​}​) - * @param {number} count - */ - selective: RequiredParams<'count'> - } - } - list: { - pendingList: { - /** - * P​e​n​d​i​n​g​ ​C​h​a​n​g​e​s - */ - title: string - /** - * N​o​ ​p​e​n​d​i​n​g​ ​c​h​a​n​g​e​s - */ - noData: string - /** - * N​o​ ​p​e​n​d​i​n​g​ ​c​h​a​n​g​e​s​ ​f​o​u​n​d - */ - noDataSearch: string - } - deployedList: { - /** - * D​e​p​l​o​y​e​d​ ​A​l​i​a​s​e​s - */ - title: string - /** - * N​o​ ​d​e​p​l​o​y​e​d​ ​a​l​i​a​s​e​s - */ - noData: string - /** - * N​o​ ​d​e​p​l​o​y​e​d​ ​a​l​i​a​s​e​s​ ​f​o​u​n​d - */ - noDataSearch: string - } - headers: { - /** - * I​D - */ - id: string - /** - * A​l​i​a​s​ ​n​a​m​e - */ - name: string - /** - * A​l​i​a​s​ ​k​i​n​d - */ - kind: string - /** - * I​P​v​4​/​6​ ​C​I​D​R​ ​r​a​n​g​e​ ​a​d​d​r​e​s​s - */ - ip: string - /** - * P​o​r​t​s - */ - ports: string - /** - * P​r​o​t​o​c​o​l​s - */ - protocols: string - /** - * S​t​a​t​u​s - */ - status: string - /** - * E​d​i​t - */ - edit: string - /** - * R​u​l​e​s - */ - rules: string - } - status: { - /** - * A​p​p​l​i​e​d - */ - applied: string - /** - * M​o​d​i​f​i​e​d - */ - changed: string - } - tags: { - /** - * A​l​l​ ​d​e​n​i​e​d - */ - allDenied: string - /** - * A​l​l​ ​a​l​l​o​w​e​d - */ - allAllowed: string - } - editMenu: { - /** - * D​i​s​c​a​r​d​ ​c​h​a​n​g​e​s - */ - discardChanges: string - /** - * D​e​l​e​t​e​ ​a​l​i​a​s - */ - 'delete': string - } - } - } - } - createPage: { - formError: { - /** - * C​o​n​f​l​i​c​t​i​n​g​ ​m​e​m​b​e​r​s - */ - allowDenyConflict: string - /** - * M​u​s​t​ ​c​o​n​f​i​g​u​r​e​ ​s​o​m​e​ ​a​l​l​o​w​e​d​ ​u​s​e​r​s​,​ ​g​r​o​u​p​s​ ​o​r​ ​d​e​v​i​c​e​s - */ - allowNotConfigured: string - } - infoBox: { - /** - * - ​ ​ ​ ​ ​ ​ ​ ​ ​S​p​e​c​i​f​y​ ​o​n​e​ ​o​r​ ​m​o​r​e​ ​f​i​e​l​d​s​ ​(​U​s​e​r​s​,​ ​G​r​o​u​p​s​ ​o​r​ ​D​e​v​i​c​e​s​)​ ​t​o​ ​d​e​f​i​n​e​ ​t​h​i​s​ ​r​u​l​e​.​ ​T​h​e​ ​r​u​l​e​ ​w​i​l​l​ ​c​o​n​s​i​d​e​r​ ​a​l​l​ ​i​n​p​u​t​s​ ​p​r​o​v​i​d​e​d​ ​f​o​r​ ​m​a​t​c​h​i​n​g​ ​c​o​n​d​i​t​i​o​n​s​.​ ​L​e​a​v​e​ ​a​n​y​ ​f​i​e​l​d​s​ ​b​l​a​n​k​ ​i​f​ ​n​o​t​ ​n​e​e​d​e​d​. - */ - allowInstructions: string - /** - * - ​ ​ ​ ​ ​ ​ ​ ​ ​S​p​e​c​i​f​y​ ​o​n​e​ ​o​r​ ​m​o​r​e​ ​f​i​e​l​d​s​ ​(​I​P​ ​A​d​d​r​e​s​s​e​s​ ​o​r​ ​P​o​r​t​s​)​ ​t​o​ ​d​e​f​i​n​e​ ​t​h​i​s​ ​r​u​l​e​.​ ​T​h​e​ ​r​u​l​e​ ​w​i​l​l​ ​c​o​n​s​i​d​e​r​ ​a​l​l​ ​i​n​p​u​t​s​ ​p​r​o​v​i​d​e​d​ ​f​o​r​ ​m​a​t​c​h​i​n​g​ ​c​o​n​d​i​t​i​o​n​s​.​ ​L​e​a​v​e​ ​a​n​y​ ​f​i​e​l​d​s​ ​b​l​a​n​k​ ​i​f​ ​n​o​t​ ​n​e​e​d​e​d​. - */ - destinationInstructions: string - } - message: { - /** - * R​u​l​e​ ​c​r​e​a​t​e​d​ ​a​n​d​ ​a​d​d​e​d​ ​t​o​ ​p​e​n​d​i​n​g​ ​c​h​a​n​g​e​s​. - */ - create: string - /** - * R​u​l​e​ ​c​r​e​a​t​i​o​n​ ​f​a​i​l​e​d - */ - createFail: string - } - headers: { - /** - * R​u​l​e - */ - rule: string - /** - * C​r​e​a​t​e​ ​R​u​l​e - */ - createRule: string - /** - * A​l​l​o​w​e​d​ ​U​s​e​r​s​/​G​r​o​u​p​s​/​D​e​v​i​c​e​s - */ - allowed: string - /** - * D​e​n​i​e​d​ ​U​s​e​r​s​/​G​r​o​u​p​s​/​D​e​v​i​c​e​s - */ - denied: string - /** - * D​e​s​t​i​n​a​t​i​o​n - */ - destination: string - } - labels: { - /** - * R​u​l​e​ ​n​a​m​e - */ - name: string - /** - * P​r​i​o​r​i​t​y - */ - priority: string - /** - * S​t​a​t​u​s - */ - status: string - /** - * L​o​c​a​t​i​o​n​s - */ - locations: string - /** - * A​l​l​o​w​ ​a​l​l​ ​u​s​e​r​s - */ - allowAllUsers: string - /** - * I​n​c​l​u​d​e​ ​a​l​l​ ​l​o​c​a​t​i​o​n​s - */ - allowAllNetworks: string - /** - * A​l​l​o​w​ ​a​l​l​ ​n​e​t​w​o​r​k​ ​d​e​v​i​c​e​s - */ - allowAllNetworkDevices: string - /** - * D​e​n​y​ ​a​l​l​ ​u​s​e​r​s - */ - denyAllUsers: string - /** - * D​e​n​y​ ​a​l​l​ ​n​e​t​w​o​r​k​ ​d​e​v​i​c​e​s - */ - denyAllNetworkDevices: string - /** - * U​s​e​r​s - */ - users: string - /** - * G​r​o​u​p​s - */ - groups: string - /** - * N​e​t​w​o​r​k​ ​d​e​v​i​c​e​s - */ - devices: string - /** - * P​r​o​t​o​c​o​l​s - */ - protocols: string - /** - * I​P​v​4​/​6​ ​C​I​D​R​ ​r​a​n​g​e​ ​o​r​ ​a​d​d​r​e​s​s - */ - manualIp: string - /** - * P​o​r​t​s - */ - ports: string - /** - * A​l​i​a​s​e​s - */ - aliases: string - /** - * E​x​p​i​r​a​t​i​o​n​ ​D​a​t​e - */ - expires: string - /** - * M​a​n​u​a​l​ ​I​n​p​u​t - */ - manualInput: string - } - placeholders: { - /** - * A​l​l​ ​p​r​o​t​o​c​o​l​s - */ - allProtocols: string - /** - * A​l​l​ ​I​P​ ​a​d​d​r​e​s​s​e​s - */ - allIps: string - } - } - } - activity: { - /** - * A​c​t​i​v​i​t​y​ ​l​o​g - */ - title: string - modals: { - timeRange: { - /** - * A​c​t​i​v​i​t​y​ ​t​i​m​e - */ - title: string - } - } - list: { - /** - * A​l​l​ ​a​c​t​i​v​i​t​y - */ - allLabel: string - headers: { - /** - * D​a​t​e - */ - date: string - /** - * U​s​e​r - */ - user: string - /** - * I​P - */ - ip: string - /** - * L​o​c​a​t​i​o​n - */ - location: string - /** - * E​v​e​n​t - */ - event: string - /** - * M​o​d​u​l​e - */ - module: string - /** - * D​e​v​i​c​e - */ - device: string - /** - * D​e​s​c​r​i​p​t​i​o​n - */ - description: string - } - noData: { - /** - * N​o​ ​a​c​t​i​v​i​t​i​e​s​ ​p​r​e​s​e​n​t - */ - data: string - /** - * N​o​ ​a​c​t​i​v​i​t​i​e​s​ ​f​o​u​n​d - */ - search: string - } - } - } - enums: { - activityLogEventType: { - /** - * U​s​e​r​ ​l​o​g​i​n - */ - user_login: string - /** - * U​s​e​r​ ​l​o​g​i​n​ ​f​a​i​l​e​d - */ - user_login_failed: string - /** - * U​s​e​r​ ​M​F​A​ ​l​o​g​i​n - */ - user_mfa_login: string - /** - * U​s​e​r​ ​M​F​A​ ​l​o​g​i​n​ ​f​a​i​l​e​d - */ - user_mfa_login_failed: string - /** - * R​e​c​o​v​e​r​y​ ​c​o​d​e​ ​u​s​e​d - */ - recovery_code_used: string - /** - * U​s​e​r​ ​l​o​g​o​u​t - */ - user_logout: string - /** - * U​s​e​r​ ​a​d​d​e​d - */ - user_added: string - /** - * U​s​e​r​ ​r​e​m​o​v​e​d - */ - user_removed: string - /** - * U​s​e​r​ ​m​o​d​i​f​i​e​d - */ - user_modified: string - /** - * U​s​e​r​ ​g​r​o​u​p​s​ ​m​o​d​i​f​i​e​d - */ - user_groups_modified: string - /** - * M​F​A​ ​e​n​a​b​l​e​d - */ - mfa_enabled: string - /** - * M​F​A​ ​d​i​s​a​b​l​e​d - */ - mfa_disabled: string - /** - * U​s​e​r​ ​M​F​A​ ​d​i​s​a​b​l​e​d - */ - user_mfa_disabled: string - /** - * M​F​A​ ​T​O​T​P​ ​e​n​a​b​l​e​d - */ - mfa_totp_enabled: string - /** - * M​F​A​ ​T​O​T​P​ ​d​i​s​a​b​l​e​d - */ - mfa_totp_disabled: string - /** - * M​F​A​ ​e​m​a​i​l​ ​e​n​a​b​l​e​d - */ - mfa_email_enabled: string - /** - * M​F​A​ ​e​m​a​i​l​ ​d​i​s​a​b​l​e​d - */ - mfa_email_disabled: string - /** - * M​F​A​ ​s​e​c​u​r​i​t​y​ ​k​e​y​ ​a​d​d​e​d - */ - mfa_security_key_added: string - /** - * M​F​A​ ​s​e​c​u​r​i​t​y​ ​k​e​y​ ​r​e​m​o​v​e​d - */ - mfa_security_key_removed: string - /** - * D​e​v​i​c​e​ ​a​d​d​e​d - */ - device_added: string - /** - * D​e​v​i​c​e​ ​r​e​m​o​v​e​d - */ - device_removed: string - /** - * D​e​v​i​c​e​ ​m​o​d​i​f​i​e​d - */ - device_modified: string - /** - * N​e​t​w​o​r​k​ ​d​e​v​i​c​e​ ​a​d​d​e​d - */ - network_device_added: string - /** - * N​e​t​w​o​r​k​ ​d​e​v​i​c​e​ ​r​e​m​o​v​e​d - */ - network_device_removed: string - /** - * N​e​t​w​o​r​k​ ​d​e​v​i​c​e​ ​m​o​d​i​f​i​e​d - */ - network_device_modified: string - /** - * A​c​t​i​v​i​t​y​ ​l​o​g​ ​s​t​r​e​a​m​ ​c​r​e​a​t​e​d - */ - activity_log_stream_created: string - /** - * A​c​t​i​v​i​t​y​ ​l​o​g​ ​s​t​r​e​a​m​ ​m​o​d​i​f​i​e​d - */ - activity_log_stream_modified: string - /** - * A​c​t​i​v​i​t​y​ ​l​o​g​ ​s​t​r​e​a​m​ ​r​e​m​o​v​e​d - */ - activity_log_stream_removed: string - /** - * V​P​N​ ​c​l​i​e​n​t​ ​c​o​n​n​e​c​t​e​d - */ - vpn_client_connected: string - /** - * V​P​N​ ​c​l​i​e​n​t​ ​d​i​s​c​o​n​n​e​c​t​e​d - */ - vpn_client_disconnected: string - /** - * V​P​N​ ​c​l​i​e​n​t​ ​c​o​n​n​e​c​t​e​d​ ​t​o​ ​M​F​A​ ​l​o​c​a​t​i​o​n - */ - vpn_client_connected_mfa: string - /** - * V​P​N​ ​c​l​i​e​n​t​ ​d​i​s​c​o​n​n​e​c​t​e​d​ ​f​r​o​m​ ​M​F​A​ ​l​o​c​a​t​i​o​n - */ - vpn_client_disconnected_mfa: string - /** - * V​P​N​ ​c​l​i​e​n​t​ ​f​a​i​l​e​d​ ​M​F​A​ ​a​u​t​h​e​n​t​i​c​a​t​i​o​n - */ - vpn_client_mfa_failed: string - /** - * E​n​r​o​l​l​m​e​n​t​ ​t​o​k​e​n​ ​a​d​d​e​d - */ - enrollment_token_added: string - /** - * E​n​r​o​l​l​m​e​n​t​ ​s​t​a​r​t​e​d - */ - enrollment_started: string - /** - * D​e​v​i​c​e​ ​a​d​d​e​d - */ - enrollment_device_added: string - /** - * E​n​r​o​l​l​m​e​n​t​ ​c​o​m​p​l​e​t​e​d - */ - enrollment_completed: string - /** - * P​a​s​s​w​o​r​d​ ​r​e​s​e​t​ ​r​e​q​u​e​s​t​e​d - */ - password_reset_requested: string - /** - * P​a​s​s​w​o​r​d​ ​r​e​s​e​t​ ​s​t​a​r​t​e​d - */ - password_reset_started: string - /** - * P​a​s​s​w​o​r​d​ ​r​e​s​e​t​ ​c​o​m​p​l​e​t​e​d - */ - password_reset_completed: string - /** - * V​P​N​ ​l​o​c​a​t​i​o​n​ ​a​d​d​e​d - */ - vpn_location_added: string - /** - * V​P​N​ ​l​o​c​a​t​i​o​n​ ​r​e​m​o​v​e​d - */ - vpn_location_removed: string - /** - * V​P​N​ ​l​o​c​a​t​i​o​n​ ​m​o​d​i​f​i​e​d - */ - vpn_location_modified: string - /** - * A​P​I​ ​t​o​k​e​n​ ​a​d​d​e​d - */ - api_token_added: string - /** - * A​P​I​ ​t​o​k​e​n​ ​r​e​m​o​v​e​d - */ - api_token_removed: string - /** - * A​P​I​ ​t​o​k​e​n​ ​r​e​n​a​m​e​d - */ - api_token_renamed: string - /** - * O​p​e​n​I​D​ ​a​p​p​ ​a​d​d​e​d - */ - open_id_app_added: string - /** - * O​p​e​n​I​D​ ​a​p​p​ ​r​e​m​o​v​e​d - */ - open_id_app_removed: string - /** - * O​p​e​n​I​D​ ​a​p​p​ ​m​o​d​i​f​i​e​d - */ - open_id_app_modified: string - /** - * O​p​e​n​I​D​ ​a​p​p​ ​s​t​a​t​e​ ​c​h​a​n​g​e​d - */ - open_id_app_state_changed: string - /** - * O​p​e​n​I​D​ ​p​r​o​v​i​d​e​r​ ​r​e​m​o​v​e​d - */ - open_id_provider_removed: string - /** - * O​p​e​n​I​D​ ​p​r​o​v​i​d​e​r​ ​m​o​d​i​f​i​e​d - */ - open_id_provider_modified: string - /** - * S​e​t​t​i​n​g​s​ ​u​p​d​a​t​e​d - */ - settings_updated: string - /** - * S​e​t​t​i​n​g​s​ ​p​a​r​t​i​a​l​l​y​ ​u​p​d​a​t​e​d - */ - settings_updated_partial: string - /** - * D​e​f​a​u​l​t​ ​b​r​a​n​d​i​n​g​ ​r​e​s​t​o​r​e​d - */ - settings_default_branding_restored: string - /** - * G​r​o​u​p​s​ ​b​u​l​k​ ​a​s​s​i​g​n​e​d - */ - groups_bulk_assigned: string - /** - * G​r​o​u​p​ ​a​d​d​e​d - */ - group_added: string - /** - * G​r​o​u​p​ ​m​o​d​i​f​i​e​d - */ - group_modified: string - /** - * G​r​o​u​p​ ​r​e​m​o​v​e​d - */ - group_removed: string - /** - * G​r​o​u​p​ ​m​e​m​b​e​r​ ​a​d​d​e​d - */ - group_member_added: string - /** - * G​r​o​u​p​ ​m​e​m​b​e​r​ ​r​e​m​o​v​e​d - */ - group_member_removed: string - /** - * G​r​o​u​p​ ​m​e​m​b​e​r​s​ ​m​o​d​i​f​i​e​d - */ - group_members_modified: string - /** - * W​e​b​h​o​o​k​ ​a​d​d​e​d - */ - web_hook_added: string - /** - * W​e​b​h​o​o​k​ ​m​o​d​i​f​i​e​d - */ - web_hook_modified: string - /** - * W​e​b​h​o​o​k​ ​r​e​m​o​v​e​d - */ - web_hook_removed: string - /** - * W​e​b​h​o​o​k​ ​s​t​a​t​e​ ​c​h​a​n​g​e​d - */ - web_hook_state_changed: string - /** - * A​u​t​h​e​n​t​i​c​a​t​i​o​n​ ​k​e​y​ ​a​d​d​e​d - */ - authentication_key_added: string - /** - * A​u​t​h​e​n​t​i​c​a​t​i​o​n​ ​k​e​y​ ​r​e​m​o​v​e​d - */ - authentication_key_removed: string - /** - * A​u​t​h​e​n​t​i​c​a​t​i​o​n​ ​k​e​y​ ​r​e​n​a​m​e​d - */ - authentication_key_renamed: string - /** - * P​a​s​s​w​o​r​d​ ​c​h​a​n​g​e​d - */ - password_changed: string - /** - * P​a​s​s​w​o​r​d​ ​c​h​a​n​g​e​d​ ​b​y​ ​a​d​m​i​n - */ - password_changed_by_admin: string - /** - * P​a​s​s​w​o​r​d​ ​r​e​s​e​t - */ - password_reset: string - /** - * C​l​i​e​n​t​ ​c​o​n​f​i​g​u​r​a​t​i​o​n​ ​t​o​k​e​n​ ​a​d​d​e​d - */ - client_configuration_token_added: string - /** - * U​s​e​r​ ​S​N​A​T​ ​b​i​n​d​i​n​g​ ​a​d​d​e​d - */ - user_snat_binding_added: string - /** - * U​s​e​r​ ​S​N​A​T​ ​b​i​n​d​i​n​g​ ​m​o​d​i​f​i​e​d - */ - user_snat_binding_modified: string - /** - * U​s​e​r​ ​S​N​A​T​ ​b​i​n​d​i​n​g​ ​r​e​m​o​v​e​d - */ - user_snat_binding_removed: string - } - activityLogModule: { - /** - * D​e​f​g​u​a​r​d - */ - defguard: string - /** - * C​l​i​e​n​t - */ - client: string - /** - * E​n​r​o​l​l​m​e​n​t - */ - enrollment: string - /** - * V​P​N - */ - vpn: string - } - } -} - -export type TranslationFunctions = { - common: { - conditions: { - /** - * or - */ - or: () => LocalizedString - /** - * and - */ - and: () => LocalizedString - /** - * equal - */ - equal: () => LocalizedString - } - controls: { - /** - * Time range - */ - timeRange: () => LocalizedString - /** - * Add new - */ - addNew: () => LocalizedString - /** - * Add - */ - add: () => LocalizedString - /** - * Accept - */ - accept: () => LocalizedString - /** - * Next - */ - next: () => LocalizedString - /** - * Back - */ - back: () => LocalizedString - /** - * Cancel - */ - cancel: () => LocalizedString - /** - * Confirm - */ - confirm: () => LocalizedString - /** - * Submit - */ - submit: () => LocalizedString - /** - * Close - */ - close: () => LocalizedString - /** - * Select - */ - select: () => LocalizedString - /** - * Finish - */ - finish: () => LocalizedString - /** - * Save changes - */ - saveChanges: () => LocalizedString - /** - * Save - */ - save: () => LocalizedString - /** - * Restore default - */ - RestoreDefault: () => LocalizedString - /** - * Delete - */ - 'delete': () => LocalizedString - /** - * Rename - */ - rename: () => LocalizedString - /** - * Copy - */ - copy: () => LocalizedString - /** - * Edit - */ - edit: () => LocalizedString - /** - * Dismiss - */ - dismiss: () => LocalizedString - /** - * Show - */ - show: () => LocalizedString - /** - * Enable - */ - enable: () => LocalizedString - /** - * Enabled - */ - enabled: () => LocalizedString - /** - * Disable - */ - disable: () => LocalizedString - /** - * Disabled - */ - disabled: () => LocalizedString - /** - * Select all - */ - selectAll: () => LocalizedString - /** - * Clear - */ - clear: () => LocalizedString - /** - * Clear all - */ - clearAll: () => LocalizedString - /** - * Filter - */ - filter: () => LocalizedString - /** - * Filters - */ - filters: () => LocalizedString - } - /** - * Key - */ - key: () => LocalizedString - /** - * Name - */ - name: () => LocalizedString - /** - * No data - */ - noData: () => LocalizedString - /** - * Unavailable - */ - unavailable: () => LocalizedString - /** - * Not set - */ - notSet: () => LocalizedString - /** - * Search - */ - search: () => LocalizedString - /** - * Time - */ - time: () => LocalizedString - /** - * From - */ - from: () => LocalizedString - /** - * Until - */ - until: () => LocalizedString - } - messages: { - /** - * Error has occurred. - */ - error: () => LocalizedString - /** - * Operation succeeded - */ - success: () => LocalizedString - /** - * Failed to get application version. - */ - errorVersion: () => LocalizedString - /** - * Context is not secure. - */ - insecureContext: () => LocalizedString - /** - * Details: - */ - details: () => LocalizedString - clipboard: { - /** - * Clipboard is not accessible. - */ - error: () => LocalizedString - /** - * Content copied to clipboard. - */ - success: () => LocalizedString - } - } - modals: { - outdatedComponentsModal: { - /** - * Version mismatch - */ - title: () => LocalizedString - /** - * Defguard detected unsupported version in some components. - */ - subtitle: () => LocalizedString - content: { - /** - * Incompatible components: - */ - title: () => LocalizedString - /** - * Unknown version - */ - unknownVersion: () => LocalizedString - /** - * Unknown hostname - */ - unknownHostname: () => LocalizedString - } - } - upgradeLicenseModal: { - enterprise: { - /** - * Upgrade to Enterprise - */ - title: () => LocalizedString - /** - * This functionality is an **enterprise feature** and you've exceeded the user, device or network limits to use it. In order to use this feature, purchase an enterprise license or upgrade your existing one. - */ - subTitle: () => LocalizedString - } - limit: { - /** - * Upgrade - */ - title: () => LocalizedString - /** - * - You have **reached the limit** of this functionality. To **[ manage more locations/users/devices ]** purchase of the Enterprise license is required. - - */ - subTitle: () => LocalizedString - } - /** - * - You can find out more about features like: - - Real time and automatic client synchronization - - External SSO - - Controlling VPN clients behavior - - Full enterprise feature list: [https://docs.defguard.net/enterprise/enterprise-features](https://docs.defguard.net/enterprise/enterprise-features)
- Licensing information: [https://docs.defguard.net/enterprise/license](https://docs.defguard.net/enterprise/license) - - */ - content: () => LocalizedString - controls: { - /** - * Maybe later - */ - cancel: () => LocalizedString - /** - * See all Enterprise plans - */ - confirm: () => LocalizedString - } - } - standaloneDeviceEnrollmentModal: { - /** - * Network device token - */ - title: () => LocalizedString - toasters: { - /** - * Token generation failed. - */ - error: () => LocalizedString - } - } - standaloneDeviceConfigModal: { - /** - * Network device config - */ - title: () => LocalizedString - /** - * Config - */ - cardTitle: () => LocalizedString - toasters: { - getConfig: { - /** - * Failed to get device config. - */ - error: () => LocalizedString - } - } - } - editStandaloneModal: { - /** - * Edit network device - */ - title: () => LocalizedString - toasts: { - /** - * Device modified - */ - success: () => LocalizedString - /** - * Modifying the device failed - */ - failure: () => LocalizedString - } - } - deleteStandaloneDevice: { - /** - * Delete network device - */ - title: () => LocalizedString - /** - * Device {name} will be deleted. - */ - content: (arg: { name: string }) => LocalizedString - messages: { - /** - * Device deleted - */ - success: () => LocalizedString - /** - * Failed to remove device. - */ - error: () => LocalizedString - } - } - addStandaloneDevice: { - toasts: { - /** - * Device added - */ - deviceCreated: () => LocalizedString - /** - * Device could not be added. - */ - creationFailed: () => LocalizedString - } - infoBox: { - /** - * Here you can add definitions or generate configurations for devices that can connect to your VPN. Only locations without Multi-Factor Authentication are available here, as MFA is only supported in Defguard Desktop Client for now. - */ - setup: () => LocalizedString - } - form: { - /** - * Add Device - */ - submit: () => LocalizedString - labels: { - /** - * Device Name - */ - deviceName: () => LocalizedString - /** - * Location - */ - location: () => LocalizedString - /** - * Assigned IP Address - */ - assignedAddress: () => LocalizedString - /** - * Description - */ - description: () => LocalizedString - generation: { - /** - * Generate key pair - */ - auto: () => LocalizedString - /** - * Use my own public key - */ - manual: () => LocalizedString - } - /** - * Provide Your Public Key - */ - publicKey: () => LocalizedString - } - } - steps: { - method: { - /** - * Choose a preferred method - */ - title: () => LocalizedString - cards: { - cli: { - /** - * Defguard Command Line Client - */ - title: () => LocalizedString - /** - * When using defguard-cli your device will be automatically configured. - */ - subtitle: () => LocalizedString - /** - * Defguard CLI download and documentation - */ - docs: () => LocalizedString - } - manual: { - /** - * Manual WireGuard Client - */ - title: () => LocalizedString - /** - * If your device does not support our CLI binaries you can always generate a WireGuard configuration file and configure it manually - but any updates to the VPN location configuration will require manual changes in device configuration. - */ - subtitle: () => LocalizedString - } - } - } - manual: { - /** - * Add new VPN device using WireGuard Client - */ - title: () => LocalizedString - finish: { - /** - * Download the provided configuration file to your device and import it into your VPN client to complete the setup. - */ - messageTop: () => LocalizedString - /** - * Use provided configuration file below by scanning QR code or importing it as file on your device's WireGuard app. - */ - ctaInstruction: () => LocalizedString - /** - * - Please remember that Defguard **doesn't store private keys**. We will securely generate the public and private key pair in your browser, but only store the public key in Defguard database. Please download the configuration generated with the private key for the device, as it will not be accessible later. - - */ - warningMessage: () => LocalizedString - actionCard: { - /** - * Config - */ - title: () => LocalizedString - } - } - } - cli: { - /** - * Add device using Defguard Command Line Client - */ - title: () => LocalizedString - finish: { - /** - * First download Defguard command line client binary and install it on your server. - */ - topMessage: () => LocalizedString - /** - * Download Defguard CLI Client - */ - downloadButton: () => LocalizedString - /** - * Copy and paste this command in your terminal on the device - */ - commandCopy: () => LocalizedString - } - setup: { - /** - * Here you can add definitions or generate configurations for devices that can connect to your VPN. Only locations without Multi-Factor Authentication are available here, as MFA is only supported in Defguard Desktop Client for now. - */ - stepMessage: () => LocalizedString - form: { - /** - * Add Device - */ - submit: () => LocalizedString - } - } - } - } - } - updatesNotificationToaster: { - /** - * New version available {version} - */ - title: (arg: { version: string }) => LocalizedString - controls: { - /** - * See what's new - */ - more: () => LocalizedString - } - } - enterpriseUpgradeToaster: { - /** - * You've reached the enterprise functionality limit. - */ - title: () => LocalizedString - /** - * You've exceeded the limit of your current Defguard plan and the enterprise - features will be disabled. Purchase an enterprise license or upgrade your - existing one to continue using these features. - */ - message: () => LocalizedString - /** - * See all enterprise plans - */ - link: () => LocalizedString - } - updatesNotification: { - header: { - /** - * Update Available - */ - title: () => LocalizedString - /** - * new version {version} - */ - newVersion: (arg: { version: string }) => LocalizedString - /** - * critical update - */ - criticalBadge: () => LocalizedString - } - controls: { - /** - * Visit release page - */ - visitRelease: () => LocalizedString - } - } - addGroup: { - /** - * Add group - */ - title: () => LocalizedString - /** - * Select all users - */ - selectAll: () => LocalizedString - /** - * Group name - */ - groupName: () => LocalizedString - /** - * Filter/Search - */ - searchPlaceholder: () => LocalizedString - /** - * Create group - */ - submit: () => LocalizedString - /** - * Group settings - */ - groupSettings: () => LocalizedString - /** - * Admin group - */ - adminGroup: () => LocalizedString - } - editGroup: { - /** - * Edit group - */ - title: () => LocalizedString - /** - * Select all users - */ - selectAll: () => LocalizedString - /** - * Group name - */ - groupName: () => LocalizedString - /** - * Filter/Search - */ - searchPlaceholder: () => LocalizedString - /** - * Update group - */ - submit: () => LocalizedString - /** - * Group settings - */ - groupSettings: () => LocalizedString - /** - * Admin group - */ - adminGroup: () => LocalizedString - } - deleteGroup: { - /** - * Delete group {name} - */ - title: (arg: { name: string }) => LocalizedString - /** - * This action will permanently delete this group. - */ - subTitle: () => LocalizedString - /** - * This group is currently assigned to following VPN Locations: - */ - locationListHeader: () => LocalizedString - /** - * If this is the only allowed group for a given location, the location will become accessible to all users. - */ - locationListFooter: () => LocalizedString - /** - * Delete group - */ - submit: () => LocalizedString - /** - * Cancel - */ - cancel: () => LocalizedString - } - deviceConfig: { - /** - * Device VPN configurations - */ - title: () => LocalizedString - } - changePasswordSelf: { - /** - * Change password - */ - title: () => LocalizedString - messages: { - /** - * Password has been changed - */ - success: () => LocalizedString - /** - * Failed to changed password - */ - error: () => LocalizedString - } - form: { - labels: { - /** - * New password - */ - newPassword: () => LocalizedString - /** - * Current password - */ - oldPassword: () => LocalizedString - /** - * Confirm new password - */ - repeat: () => LocalizedString - } - } - controls: { - /** - * Change password - */ - submit: () => LocalizedString - /** - * Cancel - */ - cancel: () => LocalizedString - } - } - disableMfa: { - /** - * Disable MFA - */ - title: () => LocalizedString - /** - * Do you want to disable MFA for user {username}? - */ - message: (arg: { username: string }) => LocalizedString - messages: { - /** - * MFA for user {username} has been disabled - */ - success: (arg: { username: string }) => LocalizedString - /** - * Failed to disable MFA for user {username} - */ - error: (arg: { username: string }) => LocalizedString - } - controls: { - /** - * Disable MFA - */ - submit: () => LocalizedString - /** - * Cancel - */ - cancel: () => LocalizedString - } - } - startEnrollment: { - /** - * Start enrollment - */ - title: () => LocalizedString - /** - * Desktop activation - */ - desktopTitle: () => LocalizedString - messages: { - /** - * User enrollment started - */ - success: () => LocalizedString - /** - * Desktop configuration started - */ - successDesktop: () => LocalizedString - /** - * Failed to start user enrollment - */ - error: () => LocalizedString - /** - * Failed to start desktop activation - */ - errorDesktop: () => LocalizedString - } - messageBox: { - /** - * You can share the following URL and token with the user to configure their Defguard desktop or mobile client. - */ - clientForm: () => LocalizedString - /** - * You can share this QR code for easy Defguard mobile client configuration. - */ - clientQr: () => LocalizedString - } - form: { - email: { - /** - * Email - */ - label: () => LocalizedString - } - mode: { - options: { - /** - * Send token by email - */ - email: () => LocalizedString - /** - * Deliver token yourself - */ - manual: () => LocalizedString - } - } - /** - * Start enrollment - */ - submit: () => LocalizedString - /** - * Activate desktop - */ - submitDesktop: () => LocalizedString - /** - * Configure SMTP to send token by email. Go to Settings -> SMTP. - */ - smtpDisabled: () => LocalizedString - } - tokenCard: { - /** - * Activation token - */ - title: () => LocalizedString - } - urlCard: { - /** - * Defguard Instance URL - */ - title: () => LocalizedString - } - } - deleteNetwork: { - /** - * Delete {name} location - */ - title: (arg: { name: string }) => LocalizedString - /** - * This action will permanently delete this location. - */ - subTitle: () => LocalizedString - /** - * Delete location - */ - submit: () => LocalizedString - /** - * Cancel - */ - cancel: () => LocalizedString - } - changeWebhook: { - messages: { - /** - * Webhook changed. - */ - success: () => LocalizedString - } - } - manageWebAuthNKeys: { - /** - * Security keys - */ - title: () => LocalizedString - messages: { - /** - * WebAuthN key has been deleted. - */ - deleted: () => LocalizedString - /** - * Key is already registered - */ - duplicateKeyError: () => LocalizedString - } - /** - * -

- Security keys can be used as your second factor of authentication - instead of a verification code. Learn more about configuring a - security key. -

- - */ - infoMessage: () => LocalizedString - form: { - messages: { - /** - * Security key added. - */ - success: () => LocalizedString - } - fields: { - name: { - /** - * New key name - */ - label: () => LocalizedString - } - } - controls: { - /** - * Add new Key - */ - submit: () => LocalizedString - } - } - } - recoveryCodes: { - /** - * Recovery codes - */ - title: () => LocalizedString - /** - * I have saved my codes - */ - submit: () => LocalizedString - messages: { - /** - * Codes copied. - */ - copied: () => LocalizedString - } - /** - * -

- Treat your recovery codes with the same level of attention as you - would your password! We recommend saving them with a password manager - such as Lastpass, bitwarden or Keeper. -

- - */ - infoMessage: () => LocalizedString - } - registerTOTP: { - /** - * Authenticator App Setup - */ - title: () => LocalizedString - /** - * -

- To setup your MFA, scan this QR code with your authenticator app, then - enter the code in the field below: -

- - */ - infoMessage: () => LocalizedString - messages: { - /** - * TOTP path copied. - */ - totpCopied: () => LocalizedString - /** - * TOTP Enabled - */ - success: () => LocalizedString - } - /** - * Copy TOTP path - */ - copyPath: () => LocalizedString - form: { - fields: { - code: { - /** - * Authenticator code - */ - label: () => LocalizedString - /** - * Code is invalid - */ - error: () => LocalizedString - } - } - controls: { - /** - * Verify code - */ - submit: () => LocalizedString - } - } - } - registerEmailMFA: { - /** - * Email MFA Setup - */ - title: () => LocalizedString - /** - * -

- To setup your MFA enter the code that was sent to your account email: {email} -

- - */ - infoMessage: (arg: { email: string }) => LocalizedString - messages: { - /** - * Email MFA Enabled - */ - success: () => LocalizedString - /** - * Verification code resent - */ - resend: () => LocalizedString - } - form: { - fields: { - code: { - /** - * Email code - */ - label: () => LocalizedString - /** - * Code is invalid - */ - error: () => LocalizedString - } - } - controls: { - /** - * Verify code - */ - submit: () => LocalizedString - /** - * Resend email - */ - resend: () => LocalizedString - } - } - } - editDevice: { - /** - * Edit device - */ - title: () => LocalizedString - messages: { - /** - * Device has been updated. - */ - success: () => LocalizedString - } - form: { - fields: { - name: { - /** - * Device Name - */ - label: () => LocalizedString - } - publicKey: { - /** - * Device Public Key (WireGuard) - */ - label: () => LocalizedString - } - } - controls: { - /** - * Edit device - */ - submit: () => LocalizedString - } - } - } - deleteDevice: { - /** - * Delete device - */ - title: () => LocalizedString - /** - * Do you want to delete {deviceName} device ? - */ - message: (arg: { deviceName: unknown }) => LocalizedString - /** - * Delete device - */ - submit: () => LocalizedString - messages: { - /** - * Device has been deleted. - */ - success: () => LocalizedString - } - } - keyDetails: { - /** - * YubiKey details - */ - title: () => LocalizedString - /** - * Download all keys - */ - downloadAll: () => LocalizedString - } - deleteUser: { - /** - * Delete account - */ - title: () => LocalizedString - controls: { - /** - * Delete account - */ - submit: () => LocalizedString - } - /** - * Do you want to delete {username} account permanently? - */ - message: (arg: { username: string }) => LocalizedString - messages: { - /** - * {username} deleted. - */ - success: (arg: { username: string }) => LocalizedString - } - } - disableUser: { - /** - * Disable account - */ - title: () => LocalizedString - controls: { - /** - * Disable account - */ - submit: () => LocalizedString - } - /** - * Do you want to disable {username} account? - */ - message: (arg: { username: string }) => LocalizedString - messages: { - /** - * {username} disabled. - */ - success: (arg: { username: string }) => LocalizedString - } - } - enableUser: { - /** - * Enable account - */ - title: () => LocalizedString - controls: { - /** - * Enable account - */ - submit: () => LocalizedString - } - /** - * Do you want to enable {username} account? - */ - message: (arg: { username: string }) => LocalizedString - messages: { - /** - * {username} enabled. - */ - success: (arg: { username: string }) => LocalizedString - } - } - deleteProvisioner: { - /** - * Delete provisioner - */ - title: () => LocalizedString - controls: { - /** - * Delete provisioner - */ - submit: () => LocalizedString - } - /** - * Do you want to delete {id} provisioner? - */ - message: (arg: { id: string }) => LocalizedString - messages: { - /** - * {provisioner} deleted. - */ - success: (arg: { provisioner: string }) => LocalizedString - } - } - changeUserPassword: { - messages: { - /** - * Password changed. - */ - success: () => LocalizedString - } - /** - * Change user password - */ - title: () => LocalizedString - form: { - controls: { - /** - * Save new password - */ - submit: () => LocalizedString - } - fields: { - newPassword: { - /** - * New password - */ - label: () => LocalizedString - } - confirmPassword: { - /** - * Repeat password - */ - label: () => LocalizedString - } - } - } - } - provisionKeys: { - /** - * Yubikey provisioning: - */ - title: () => LocalizedString - /** - * Please be advised that this operation wll wipe openpgp application on yubikey and reconfigure it. - */ - warning: () => LocalizedString - /** - * The selected provisioner must have a clean YubiKey - plugged in be provisioned. To clean a used YubiKey - gpg --card-edit before provisioning. - */ - infoBox: () => LocalizedString - /** - * Select one of the following provisioners to provision a YubiKey: - */ - selectionLabel: () => LocalizedString - noData: { - /** - * No workers found, waiting... - */ - workers: () => LocalizedString - } - controls: { - /** - * Provision YubiKey - */ - submit: () => LocalizedString - } - messages: { - /** - * Keys provisioned - */ - success: () => LocalizedString - /** - * Error while getting worker status. - */ - errorStatus: () => LocalizedString - } - } - addUser: { - /** - * Add new user - */ - title: () => LocalizedString - messages: { - /** - * User added - */ - userAdded: () => LocalizedString - } - form: { - /** - * Add user - */ - submit: () => LocalizedString - error: { - /** - * Email already taken - */ - emailReserved: () => LocalizedString - } - fields: { - username: { - /** - * login - */ - placeholder: () => LocalizedString - /** - * Login - */ - label: () => LocalizedString - } - password: { - /** - * Password - */ - placeholder: () => LocalizedString - /** - * Password - */ - label: () => LocalizedString - } - email: { - /** - * User e-mail - */ - placeholder: () => LocalizedString - /** - * User e-mail - */ - label: () => LocalizedString - } - firstName: { - /** - * First name - */ - placeholder: () => LocalizedString - /** - * First name - */ - label: () => LocalizedString - } - lastName: { - /** - * Last name - */ - placeholder: () => LocalizedString - /** - * Last name - */ - label: () => LocalizedString - } - phone: { - /** - * Phone - */ - placeholder: () => LocalizedString - /** - * Phone - */ - label: () => LocalizedString - } - enableEnrollment: { - /** - * Use user self-enrollment process - */ - label: () => LocalizedString - /** - * more information here - */ - link: () => LocalizedString - } - } - } - } - webhookModal: { - title: { - /** - * Add webhook. - */ - addWebhook: () => LocalizedString - /** - * Edit webhook - */ - editWebhook: () => LocalizedString - } - messages: { - /** - * Client ID copied. - */ - clientIdCopy: () => LocalizedString - /** - * Client secret copied. - */ - clientSecretCopy: () => LocalizedString - } - form: { - /** - * Trigger events: - */ - triggers: () => LocalizedString - messages: { - /** - * Webhook created. - */ - successAdd: () => LocalizedString - /** - * Webhook modified. - */ - successModify: () => LocalizedString - } - error: { - /** - * URL is required. - */ - urlRequired: () => LocalizedString - /** - * Must be a valid URL. - */ - validUrl: () => LocalizedString - /** - * Must have at least one trigger. - */ - scopeValidation: () => LocalizedString - /** - * Token is required. - */ - tokenRequired: () => LocalizedString - } - fields: { - description: { - /** - * Description - */ - label: () => LocalizedString - /** - * Webhook to create gmail account on new user - */ - placeholder: () => LocalizedString - } - token: { - /** - * Secret token - */ - label: () => LocalizedString - /** - * Authorization token - */ - placeholder: () => LocalizedString - } - url: { - /** - * Webhook URL - */ - label: () => LocalizedString - /** - * https://example.com/webhook - */ - placeholder: () => LocalizedString - } - userCreated: { - /** - * New user Created - */ - label: () => LocalizedString - } - userDeleted: { - /** - * User deleted - */ - label: () => LocalizedString - } - userModified: { - /** - * User modified - */ - label: () => LocalizedString - } - hwkeyProvision: { - /** - * User Yubikey provision - */ - label: () => LocalizedString - } - } - } - } - deleteWebhook: { - /** - * Delete webhook - */ - title: () => LocalizedString - /** - * Do you want to delete {name} webhook ? - */ - message: (arg: { name: string }) => LocalizedString - /** - * Delete - */ - submit: () => LocalizedString - messages: { - /** - * Webhook deleted. - */ - success: () => LocalizedString - } - } - } - addDevicePage: { - /** - * Add device - */ - title: () => LocalizedString - helpers: { - /** - * You can add a device using this wizard. Opt for our native application "defguard" or any other WireGuard client. If you're unsure, we recommend using defguard for simplicity. - */ - setupOpt: () => LocalizedString - /** - * Please download defguard desktop client here and then follow this guide. - */ - client: () => LocalizedString - } - messages: { - /** - * Device added - */ - deviceAdded: () => LocalizedString - } - steps: { - setupMethod: { - /** - * Choose Your Connection Method - */ - title: () => LocalizedString - /** - * You can add a device using this wizard. To proceed, you'll need to install the defguard Client on the device you're adding. You can also use any standard WireGuard® client, but for the best experience and ease of setup, we recommend using our native defguard Client. - */ - message: () => LocalizedString - methods: { - client: { - /** - * Remote Device Activation - */ - title: () => LocalizedString - /** - * Use the Defguard Client to set up your device. Easily configure it with a single token or by scanning a QR code. - */ - description: () => LocalizedString - } - wg: { - /** - * Manual WireGuard Client - */ - title: () => LocalizedString - /** - * For advanced users, get a unique config via download or QR code. Download any WireGuard® client and take control of your VPN setup. - */ - description: () => LocalizedString - } - } - } - client: { - /** - * Client Activation - */ - title: () => LocalizedString - /** - * If you want to configure your Defguard desktop client, please install the client (links below), open it and just press the One-Click Desktop Configuration button - */ - desktopDeepLinkHelp: () => LocalizedString - /** - * If you are having trouble with the One-Click configuration you can do it manually by clicking *Add Instance* in the desktop client, and entering the following URL and Token: - */ - message: () => LocalizedString - /** - * Scan the QR code with your installed Defguard app. If you haven't installed it yet, use your device's app store or the link below. - */ - qrDescription: () => LocalizedString - /** - * If you want to configure your Mobile Defguard Client, please just scan this QR code in the app: - */ - qrHelp: () => LocalizedString - /** - * Download for Desktop - */ - desktopDownload: () => LocalizedString - /** - * Token copied to clipboard - */ - tokenCopy: () => LocalizedString - /** - * Failed to prepare client setup - */ - tokenFailure: () => LocalizedString - labels: { - /** - * Defguard Instance Token (new) - */ - mergedToken: () => LocalizedString - /** - * Authentication Token - */ - token: () => LocalizedString - /** - * URL - */ - url: () => LocalizedString - } - } - configDevice: { - /** - * Configure device - */ - title: () => LocalizedString - messages: { - /** - * Configuration has been copied to the clipboard - */ - copyConfig: () => LocalizedString - } - helpers: { - /** - * -

- Please be advised that you have to download the configuration now, - since we do not store your private key. After this - page is closed, you will not be able to get your - full configuration file (with private keys, only blank template). -

- - */ - warningAutoMode: () => LocalizedString - /** - * -

- Please be advised that configuration provided here does not include private key and uses public key to fill it's place you will need to replace it on your own for configuration to work properly. -

- - */ - warningManualMode: () => LocalizedString - /** - * You don't have access to any network. - */ - warningNoNetworks: () => LocalizedString - /** - * -

- You can setup your device faster with wireguard application by scanning this QR code. -

- */ - qrHelper: () => LocalizedString - } - /** - * Use provided configuration file below by scanning QR Code or importing it as file on your devices WireGuard instance. - */ - qrInfo: () => LocalizedString - /** - * Device Name - */ - inputNameLabel: () => LocalizedString - /** - * WireGuard Config File - */ - qrLabel: () => LocalizedString - } - setupDevice: { - /** - * Create VPN device - */ - title: () => LocalizedString - /** - * -

- You need to configure WireGuard® VPN on your device, please visit  - documentation if you don't know how to do it. -

- - */ - infoMessage: (arg: { addDevicesDocs: string }) => LocalizedString - options: { - /** - * Generate key pair - */ - auto: () => LocalizedString - /** - * Use my own public key - */ - manual: () => LocalizedString - } - form: { - fields: { - name: { - /** - * Device Name - */ - label: () => LocalizedString - } - publicKey: { - /** - * Provide Your Public Key - */ - label: () => LocalizedString - } - } - errors: { - name: { - /** - * Device with this name already exists - */ - duplicatedName: () => LocalizedString - } - } - } - } - copyToken: { - /** - * Client activation - */ - title: () => LocalizedString - /** - * Activation token - */ - tokenCardTitle: () => LocalizedString - /** - * Defguard Instance URL - */ - urlCardTitle: () => LocalizedString - } - } - } - userPage: { - title: { - /** - * User Profile - */ - view: () => LocalizedString - /** - * Edit User Profile - */ - edit: () => LocalizedString - } - messages: { - /** - * User updated. - */ - editSuccess: () => LocalizedString - /** - * Could not get user information. - */ - failedToFetchUserData: () => LocalizedString - /** - * Password reset email has been sent. - */ - passwordResetEmailSent: () => LocalizedString - } - userDetails: { - /** - * Profile Details - */ - header: () => LocalizedString - messages: { - /** - * App and all tokens deleted. - */ - deleteApp: () => LocalizedString - } - warningModals: { - /** - * Warning - */ - title: () => LocalizedString - content: { - /** - * Changing the username has a significant impact on services the user has logged into using Defguard. After changing it, the user may lose access to applications (since they will not recognize them). Are you sure you want to proceed? - */ - usernameChange: () => LocalizedString - /** - * If you are using external OpenID Connect (OIDC) providers to authenticate users, changing a user's email address may have a significant impact on their ability to log in to Defguard. Are you sure you want to proceed? - */ - emailChange: () => LocalizedString - } - buttons: { - /** - * Proceed - */ - proceed: () => LocalizedString - /** - * Cancel - */ - cancel: () => LocalizedString - } - } - fields: { - username: { - /** - * Username - */ - label: () => LocalizedString - } - firstName: { - /** - * First name - */ - label: () => LocalizedString - } - lastName: { - /** - * Last name - */ - label: () => LocalizedString - } - phone: { - /** - * Phone number - */ - label: () => LocalizedString - } - email: { - /** - * E-mail - */ - label: () => LocalizedString - } - status: { - /** - * Status - */ - label: () => LocalizedString - /** - * Active - */ - active: () => LocalizedString - /** - * Disabled - */ - disabled: () => LocalizedString - } - groups: { - /** - * User groups - */ - label: () => LocalizedString - /** - * No groups - */ - noData: () => LocalizedString - } - apps: { - /** - * Authorized apps - */ - label: () => LocalizedString - /** - * No authorized apps - */ - noData: () => LocalizedString - } - } - } - userAuthInfo: { - /** - * Password and authentication - */ - header: () => LocalizedString - password: { - /** - * Password settings - */ - header: () => LocalizedString - /** - * Change password - */ - changePassword: () => LocalizedString - /** - * {ldapName} password update required - */ - ldap_change_heading: (arg: { ldapName: string }) => LocalizedString - /** - * Defguard doesn't store your password in plain text, so we can’t retrieve it for automatic synchronization with your {ldapName} credentials. To enable {ldapName} login to other services, please update your Defguard password for your {ldapName} password to be set — you can re-enter your current password if you wish. This step is necessary to ensure consistent and secure authentication across both systems. - */ - ldap_change_message: (arg: { ldapName: string }) => LocalizedString - } - recovery: { - /** - * Recovery options - */ - header: () => LocalizedString - codes: { - /** - * Recovery Codes - */ - label: () => LocalizedString - /** - * Viewed - */ - viewed: () => LocalizedString - } - } - mfa: { - /** - * Two-factor methods - */ - header: () => LocalizedString - edit: { - /** - * Disable MFA - */ - disable: () => LocalizedString - } - messages: { - /** - * MFA disabled. - */ - mfaDisabled: () => LocalizedString - /** - * One time password disabled. - */ - OTPDisabled: () => LocalizedString - /** - * Email MFA disabled. - */ - EmailMFADisabled: () => LocalizedString - /** - * MFA method changed - */ - changeMFAMethod: () => LocalizedString - } - securityKey: { - /** - * security key - */ - singular: () => LocalizedString - /** - * security keys - */ - plural: () => LocalizedString - } - /** - * default - */ - 'default': () => LocalizedString - /** - * Enabled - */ - enabled: () => LocalizedString - /** - * Disabled - */ - disabled: () => LocalizedString - labels: { - /** - * Time based one time passwords - */ - totp: () => LocalizedString - /** - * Email - */ - email: () => LocalizedString - /** - * Security keys - */ - webauth: () => LocalizedString - } - editMode: { - /** - * Enable - */ - enable: () => LocalizedString - /** - * Disable - */ - disable: () => LocalizedString - /** - * Make default - */ - makeDefault: () => LocalizedString - webauth: { - /** - * Manage security keys - */ - manage: () => LocalizedString - } - } - } - } - controls: { - /** - * Edit profile - */ - editButton: () => LocalizedString - /** - * Delete account - */ - deleteAccount: () => LocalizedString - } - devices: { - /** - * User devices - */ - header: () => LocalizedString - addDevice: { - /** - * Add new device - */ - web: () => LocalizedString - /** - * Add this device - */ - desktop: () => LocalizedString - } - card: { - labels: { - /** - * Public IP - */ - publicIP: () => LocalizedString - /** - * Connected through - */ - connectedThrough: () => LocalizedString - /** - * Connected date - */ - connectionDate: () => LocalizedString - /** - * Last connected from - */ - lastLocation: () => LocalizedString - /** - * Last connected - */ - lastConnected: () => LocalizedString - /** - * Assigned IP - */ - assignedIp: () => LocalizedString - /** - * active - */ - active: () => LocalizedString - /** - * Never connected - */ - noData: () => LocalizedString - } - edit: { - /** - * Edit device - */ - edit: () => LocalizedString - /** - * Delete device - */ - 'delete': () => LocalizedString - /** - * Show configuration - */ - showConfigurations: () => LocalizedString - } - } - } - yubiKey: { - /** - * User YubiKey - */ - header: () => LocalizedString - /** - * Provision a YubiKey - */ - provision: () => LocalizedString - keys: { - /** - * PGP key - */ - pgp: () => LocalizedString - /** - * SSH key - */ - ssh: () => LocalizedString - } - noLicense: { - /** - * YubiKey module - */ - moduleName: () => LocalizedString - /** - * This is enterprise module for YubiKey - */ - line1: () => LocalizedString - /** - * management and provisioning. - */ - line2: () => LocalizedString - } - } - authenticationKeys: { - /** - * User Authentication Keys - */ - header: () => LocalizedString - /** - * Add new Key - */ - addKey: () => LocalizedString - keysList: { - common: { - /** - * Rename - */ - rename: () => LocalizedString - /** - * Key - */ - key: () => LocalizedString - /** - * Download - */ - download: () => LocalizedString - /** - * Copy - */ - copy: () => LocalizedString - /** - * Serial Number - */ - serialNumber: () => LocalizedString - /** - * Delete - */ - 'delete': () => LocalizedString - } - } - deleteModal: { - /** - * Delete Authentication Key - */ - title: () => LocalizedString - /** - * Key {name} will be deleted permanently. - */ - confirmMessage: (arg: { name: string }) => LocalizedString - } - addModal: { - /** - * Add new Authentication Key - */ - header: () => LocalizedString - /** - * Key Type - */ - keyType: () => LocalizedString - keyForm: { - placeholders: { - /** - * Key Name - */ - title: () => LocalizedString - key: { - /** - * Begins with ssh-rsa, ecdsa-sha2-nistp256, ... - */ - ssh: () => LocalizedString - /** - * Begins with -----BEGIN PGP PUBLIC KEY BLOCK----- - */ - gpg: () => LocalizedString - } - } - labels: { - /** - * Name - */ - title: () => LocalizedString - /** - * Key - */ - key: () => LocalizedString - } - /** - * Add {name} key - */ - submit: (arg: { name: string }) => LocalizedString - } - yubikeyForm: { - selectWorker: { - /** - * Please be advised that this operation will wipe openpgp application on YubiKey and reconfigure it. - */ - info: () => LocalizedString - /** - * Select on of the following provisioners to provision a YubiKey - */ - selectLabel: () => LocalizedString - /** - * No workers are registered right now. - */ - noData: () => LocalizedString - /** - * Available - */ - available: () => LocalizedString - /** - * Unavailable - */ - unavailable: () => LocalizedString - } - provisioning: { - /** - * Provisioning in progress, please wait. - */ - inProgress: () => LocalizedString - /** - * Provisioning failed ! - */ - error: () => LocalizedString - /** - * Yubikey provisioned successfully - */ - success: () => LocalizedString - } - /** - * Provision Yubikey - */ - submit: () => LocalizedString - } - messages: { - /** - * Key added. - */ - keyAdded: () => LocalizedString - /** - * Key has already been added. - */ - keyExists: () => LocalizedString - /** - * Unsupported key format. - */ - unsupportedKeyFormat: () => LocalizedString - /** - * Could not add the key. Please try again later. - */ - genericError: () => LocalizedString - } - } - } - apiTokens: { - /** - * User API Tokens - */ - header: () => LocalizedString - /** - * Add new API Token - */ - addToken: () => LocalizedString - tokensList: { - common: { - /** - * Rename - */ - rename: () => LocalizedString - /** - * Token - */ - token: () => LocalizedString - /** - * Copy - */ - copy: () => LocalizedString - /** - * Delete - */ - 'delete': () => LocalizedString - /** - * Created at - */ - createdAt: () => LocalizedString - } - } - deleteModal: { - /** - * Delete API Token - */ - title: () => LocalizedString - /** - * API token {name} will be deleted permanently. - */ - confirmMessage: (arg: { name: string }) => LocalizedString - } - addModal: { - /** - * Add new API Token - */ - header: () => LocalizedString - tokenForm: { - placeholders: { - /** - * API Token Name - */ - name: () => LocalizedString - } - labels: { - /** - * Name - */ - name: () => LocalizedString - } - /** - * Add API token - */ - submit: () => LocalizedString - } - copyToken: { - /** - * Please copy the API token below now. You won't be able to see it again. - */ - warningMessage: () => LocalizedString - /** - * Copy new API Token - */ - header: () => LocalizedString - } - messages: { - /** - * API token added. - */ - tokenAdded: () => LocalizedString - /** - * Could not add API token. Please try again later. - */ - genericError: () => LocalizedString - } - } - } - } - usersOverview: { - /** - * Users - */ - pageTitle: () => LocalizedString - grid: { - /** - * Connected Users - */ - usersTitle: () => LocalizedString - /** - * Connected Network Devices - */ - devicesTitle: () => LocalizedString - } - search: { - /** - * Find users - */ - placeholder: () => LocalizedString - } - filterLabels: { - /** - * All users - */ - all: () => LocalizedString - /** - * Admins only - */ - admin: () => LocalizedString - /** - * Users only - */ - users: () => LocalizedString - } - /** - * All users - */ - usersCount: () => LocalizedString - /** - * Add new - */ - addNewUser: () => LocalizedString - list: { - headers: { - /** - * User name - */ - name: () => LocalizedString - /** - * Login - */ - username: () => LocalizedString - /** - * Phone - */ - phone: () => LocalizedString - /** - * Actions - */ - actions: () => LocalizedString - } - editButton: { - /** - * Change password - */ - changePassword: () => LocalizedString - /** - * Edit account - */ - edit: () => LocalizedString - /** - * Add YubiKey - */ - addYubikey: () => LocalizedString - /** - * Add SSH Key - */ - addSSH: () => LocalizedString - /** - * Add GPG Key - */ - addGPG: () => LocalizedString - /** - * Delete account - */ - 'delete': () => LocalizedString - /** - * Start enrollment - */ - startEnrollment: () => LocalizedString - /** - * Configure Desktop Client - */ - activateDesktop: () => LocalizedString - /** - * Reset password - */ - resetPassword: () => LocalizedString - /** - * Disable MFA - */ - disableMfa: () => LocalizedString - } - } - } - navigation: { - bar: { - /** - * VPN Overview - */ - overview: () => LocalizedString - /** - * Users - */ - users: () => LocalizedString - /** - * YubiKeys - */ - provisioners: () => LocalizedString - /** - * Webhooks - */ - webhooks: () => LocalizedString - /** - * OpenID Apps - */ - openId: () => LocalizedString - /** - * My Profile - */ - myProfile: () => LocalizedString - /** - * Settings - */ - settings: () => LocalizedString - /** - * Log out - */ - logOut: () => LocalizedString - /** - * Enrollment - */ - enrollment: () => LocalizedString - /** - * Support - */ - support: () => LocalizedString - /** - * Groups - */ - groups: () => LocalizedString - /** - * Network Devices - */ - devices: () => LocalizedString - /** - * Access Control - */ - acl: () => LocalizedString - /** - * Activity log - */ - activity: () => LocalizedString - } - mobileTitles: { - /** - * Activity log - */ - activity: () => LocalizedString - /** - * Groups - */ - groups: () => LocalizedString - /** - * Create location - */ - wizard: () => LocalizedString - /** - * Users - */ - users: () => LocalizedString - /** - * Settings - */ - settings: () => LocalizedString - /** - * User Profile - */ - user: () => LocalizedString - /** - * Yubikey - */ - provisioners: () => LocalizedString - /** - * Webhooks - */ - webhooks: () => LocalizedString - /** - * OpenId Apps - */ - openId: () => LocalizedString - /** - * Location Overview - */ - overview: () => LocalizedString - /** - * Edit Location - */ - networkSettings: () => LocalizedString - /** - * Enrollment - */ - enrollment: () => LocalizedString - /** - * Support - */ - support: () => LocalizedString - /** - * Network Devices - */ - devices: () => LocalizedString - } - /** - * Copyright ©2023-2025 - */ - copyright: () => LocalizedString - version: { - /** - * Application version: {version} - */ - open: (arg: { version: string }) => LocalizedString - /** - * v{version} - */ - closed: (arg: { version: string }) => LocalizedString - } - } - form: { - /** - * Download - */ - download: () => LocalizedString - /** - * Copy - */ - copy: () => LocalizedString - /** - * Save changes - */ - saveChanges: () => LocalizedString - /** - * Submit - */ - submit: () => LocalizedString - /** - * Sign in - */ - login: () => LocalizedString - /** - * Cancel - */ - cancel: () => LocalizedString - /** - * Close - */ - close: () => LocalizedString - placeholders: { - /** - * Password - */ - password: () => LocalizedString - /** - * Username - */ - username: () => LocalizedString - /** - * Username or email - */ - username_or_email: () => LocalizedString - } - error: { - /** - * Enter valid URL - */ - urlInvalid: () => LocalizedString - /** - * Name is already taken. - */ - reservedName: () => LocalizedString - /** - * IP is invalid. - */ - invalidIp: () => LocalizedString - /** - * IP is already in use. - */ - reservedIp: () => LocalizedString - /** - * Field contains forbidden characters. - */ - forbiddenCharacter: () => LocalizedString - /** - * Username is already in use. - */ - usernameTaken: () => LocalizedString - /** - * Key is invalid. - */ - invalidKey: () => LocalizedString - /** - * Field is invalid. - */ - invalid: () => LocalizedString - /** - * Field is required. - */ - required: () => LocalizedString - /** - * Submitted code is invalid. - */ - invalidCode: () => LocalizedString - /** - * Maximum length exceeded. - */ - maximumLength: () => LocalizedString - /** - * Field length cannot exceed {length} - */ - maximumLengthOf: (arg: { length: number }) => LocalizedString - /** - * Minimum length not reached. - */ - minimumLength: () => LocalizedString - /** - * Minimum length of {length} not reached. - */ - minimumLengthOf: (arg: { length: number }) => LocalizedString - /** - * No special characters are allowed. - */ - noSpecialChars: () => LocalizedString - /** - * One digit required. - */ - oneDigit: () => LocalizedString - /** - * Special character required. - */ - oneSpecial: () => LocalizedString - /** - * One uppercase character required. - */ - oneUppercase: () => LocalizedString - /** - * One lowercase character required. - */ - oneLowercase: () => LocalizedString - /** - * Maximum port is 65535. - */ - portMax: () => LocalizedString - /** - * Enter a valid endpoint. - */ - endpoint: () => LocalizedString - /** - * Enter a valid address. - */ - address: () => LocalizedString - /** - * Enter a valid address with a netmask. - */ - addressNetmask: () => LocalizedString - /** - * Enter a valid port. - */ - validPort: () => LocalizedString - /** - * Code should have 6 digits. - */ - validCode: () => LocalizedString - /** - * Only valid IP or domain is allowed. - */ - allowedIps: () => LocalizedString - /** - * Cannot start from number. - */ - startFromNumber: () => LocalizedString - /** - * Fields don't match. - */ - repeat: () => LocalizedString - /** - * Expected a valid number. - */ - number: () => LocalizedString - /** - * Minimum value of {value} not reached. - */ - minimumValue: (arg: { value: number }) => LocalizedString - /** - * Maximum value of {value} exceeded. - */ - maximumValue: (arg: { value: number }) => LocalizedString - /** - * Too many bad login attempts. Please try again in a few minutes. - */ - tooManyBadLoginAttempts: () => LocalizedString - } - floatingErrors: { - /** - * Please correct the following: - */ - title: () => LocalizedString - } - } - components: { - /** - * One-Click Desktop Configuration - */ - openClientDeepLink: () => LocalizedString - aclDefaultPolicySelect: { - /** - * Default ACL Policy - */ - label: () => LocalizedString - options: { - /** - * Allow - */ - allow: () => LocalizedString - /** - * Deny - */ - deny: () => LocalizedString - } - } - standaloneDeviceTokenModalContent: { - /** - * First download defguard command line client binaries and install them on your server. - */ - headerMessage: () => LocalizedString - /** - * Download Defguard CLI Client - */ - downloadButton: () => LocalizedString - expandableCard: { - /** - * Copy and paste this command in your terminal on the device - */ - title: () => LocalizedString - } - } - deviceConfigsCard: { - /** - * WireGuard Config for location: - */ - cardTitle: () => LocalizedString - messages: { - /** - * Configuration copied to the clipboard - */ - copyConfig: () => LocalizedString - } - } - gatewaysStatus: { - /** - * Gateways - */ - label: () => LocalizedString - states: { - /** - * All ({count}) Connected - */ - all: (arg: { count: number }) => LocalizedString - /** - * Some ({count}) Connected - */ - some: (arg: { count: number }) => LocalizedString - /** - * None connected - */ - none: () => LocalizedString - /** - * Status check failed - */ - error: () => LocalizedString - } - messages: { - /** - * Failed to get gateways status - */ - error: () => LocalizedString - /** - * Failed to delete gateway - */ - deleteError: () => LocalizedString - } - } - noLicenseBox: { - footer: { - /** - * Get an enterprise license - */ - get: () => LocalizedString - /** - * by contacting: - */ - contact: () => LocalizedString - } - } - locationMfaModeSelect: { - /** - * MFA Requirement - */ - label: () => LocalizedString - options: { - /** - * Do not enforce MFA - */ - disabled: () => LocalizedString - /** - * Internal MFA - */ - internal: () => LocalizedString - /** - * External MFA - */ - external: () => LocalizedString - } - } - } - settingsPage: { - /** - * Settings - */ - title: () => LocalizedString - tabs: { - /** - * SMTP - */ - smtp: () => LocalizedString - /** - * Global settings - */ - global: () => LocalizedString - /** - * LDAP - */ - ldap: () => LocalizedString - /** - * OpenID - */ - openid: () => LocalizedString - /** - * Enterprise features - */ - enterprise: () => LocalizedString - /** - * Gateway notifications - */ - gatewayNotifications: () => LocalizedString - /** - * Activity log streaming - */ - activityLogStream: () => LocalizedString - } - messages: { - /** - * Settings updated - */ - editSuccess: () => LocalizedString - /** - * Challenge message changed - */ - challengeSuccess: () => LocalizedString - } - enterpriseOnly: { - /** - * This feature is available only in Defguard Enterprise. - */ - title: () => LocalizedString - /** - * Your current license has expired. - */ - currentExpired: () => LocalizedString - /** - * To learn more, visit our - */ - subtitle: () => LocalizedString - /** - * website - */ - website: () => LocalizedString - } - activityLogStreamSettings: { - messages: { - destinationCrud: { - /** - * {destination} destination added - */ - create: (arg: { destination: string }) => LocalizedString - /** - * {destination} destination modified - */ - modify: (arg: { destination: string }) => LocalizedString - /** - * {destination} destination removed - */ - 'delete': (arg: { destination: string }) => LocalizedString - } - } - modals: { - selectDestination: { - /** - * Select destination - */ - title: () => LocalizedString - } - vector: { - /** - * Add Vector destination - */ - create: () => LocalizedString - /** - * Edit Vector destination - */ - modify: () => LocalizedString - } - logstash: { - /** - * Add Logstash destination - */ - create: () => LocalizedString - /** - * Edit Logstash destination - */ - modify: () => LocalizedString - } - shared: { - formLabels: { - /** - * Name - */ - name: () => LocalizedString - /** - * Url - */ - url: () => LocalizedString - /** - * Username - */ - username: () => LocalizedString - /** - * Password - */ - password: () => LocalizedString - /** - * Certificate - */ - cert: () => LocalizedString - } - } - } - /** - * Activity log streaming - */ - title: () => LocalizedString - list: { - /** - * No destinations - */ - noData: () => LocalizedString - headers: { - /** - * Name - */ - name: () => LocalizedString - /** - * Destination - */ - destination: () => LocalizedString - } - } - } - ldapSettings: { - /** - * LDAP Settings - */ - title: () => LocalizedString - sync: { - /** - * LDAP two-way synchronization - */ - header: () => LocalizedString - /** - * Before enabling synchronization, please read more about it in our [documentation](https://docs.defguard.net/features/ldap-and-active-directory-integration/two-way-ldap-and-active-directory-synchronization). - */ - info: () => LocalizedString - /** - * This feature is available only in Defguard Enterprise. - */ - info_enterprise: () => LocalizedString - helpers: { - /** - * Configure LDAP synchronization settings here. If configured, Defguard will pull user information from LDAP and synchronize it with local users. - */ - heading: () => LocalizedString - /** - * If enabled, Defguard will attempt to pull LDAP user data at the specified interval. - */ - sync_enabled: () => LocalizedString - /** - * Defguard will use the selected server as the authoritative source of - user data, meaning that if LDAP is selected, Defguard data will be overwritten with the LDAP - data in case of a desynchronization. If Defguard was selected as the authority, it's data will - overwrite LDAP data if necessary. - Make sure to check the documentation to understand the implications of this - setting. - */ - authority: () => LocalizedString - /** - * The interval with which the synchronization will be attempted. - */ - interval: () => LocalizedString - /** - * Defguard will attempt to synchronize only users belonging to the provided groups. Provide a comma-separated list of groups. If empty, all users will be synchronized. - */ - groups: () => LocalizedString - } - } - form: { - labels: { - /** - * Enable LDAP integration - */ - ldap_enable: () => LocalizedString - /** - * URL - */ - ldap_url: () => LocalizedString - /** - * Bind Username - */ - ldap_bind_username: () => LocalizedString - /** - * Bind Password - */ - ldap_bind_password: () => LocalizedString - /** - * Member Attribute - */ - ldap_member_attr: () => LocalizedString - /** - * Username Attribute - */ - ldap_username_attr: () => LocalizedString - /** - * User Object Class - */ - ldap_user_obj_class: () => LocalizedString - /** - * User Search Base - */ - ldap_user_search_base: () => LocalizedString - /** - * Additional User Object Classes - */ - ldap_user_auxiliary_obj_classes: () => LocalizedString - /** - * Groupname Attribute - */ - ldap_groupname_attr: () => LocalizedString - /** - * Group Search Base - */ - ldap_group_search_base: () => LocalizedString - /** - * Group Member Attribute - */ - ldap_group_member_attr: () => LocalizedString - /** - * Group Object Class - */ - ldap_group_obj_class: () => LocalizedString - /** - * Enable LDAP two-way synchronization - */ - ldap_sync_enabled: () => LocalizedString - /** - * Consider the following source as the authority - */ - ldap_authoritative_source: () => LocalizedString - /** - * Synchronization interval - */ - ldap_sync_interval: () => LocalizedString - /** - * Use StartTLS - */ - ldap_use_starttls: () => LocalizedString - /** - * Verify TLS certificate - */ - ldap_tls_verify_cert: () => LocalizedString - /** - * LDAP server is Active Directory - */ - ldap_uses_ad: () => LocalizedString - /** - * User RDN Attribute - */ - ldap_user_rdn_attr: () => LocalizedString - /** - * Limit synchronization to these groups - */ - ldap_sync_groups: () => LocalizedString - } - helpers: { - /** - * The object class that will be added to the user object during its creation. This is used to determine if an LDAP object is a user. - */ - ldap_user_obj_class: () => LocalizedString - /** - * The additional object classes that will be added to the user object during its creation. They may also influence the added user's attributes (e.g. simpleSecurityObject class will add userPassword attribute). - */ - ldap_user_auxiliary_obj_classes: () => LocalizedString - /** - * Configure LDAP user settings here. These settings determine how Defguard maps and synchronizes LDAP user information with local users. - */ - user_settings: () => LocalizedString - /** - * Configure LDAP connection settings here. These settings determine how Defguard connects to your LDAP server. Encrypted connections are also supported (StartTLS, LDAPS). - */ - connection_settings: () => LocalizedString - /** - * Configure LDAP group settings here. These settings determine how Defguard maps and synchronizes LDAP group information with local groups. - */ - group_settings: () => LocalizedString - /** - * The object class that represents a group in LDAP. This is used to determine if an LDAP object is a group. - */ - ldap_group_obj_class: () => LocalizedString - /** - * If your user's RDN attribute is different than your username attribute, please provide it here, otherwise leave it empty to use the username attribute as the user's RDN. - */ - ldap_user_rdn_attr: () => LocalizedString - } - headings: { - /** - * User settings - */ - user_settings: () => LocalizedString - /** - * Connection settings - */ - connection_settings: () => LocalizedString - /** - * Group settings - */ - group_settings: () => LocalizedString - } - /** - * Delete configuration - */ - 'delete': () => LocalizedString - } - test: { - /** - * Test LDAP Connection - */ - title: () => LocalizedString - /** - * Test - */ - submit: () => LocalizedString - messages: { - /** - * LDAP connected successfully - */ - success: () => LocalizedString - /** - * LDAP connection rejected - */ - error: () => LocalizedString - } - } - } - openIdSettings: { - /** - * External OpenID settings - */ - heading: () => LocalizedString - general: { - /** - * General settings - */ - title: () => LocalizedString - /** - * Here you can change general OpenID behavior in your Defguard instance. - */ - helper: () => LocalizedString - createAccount: { - /** - * Automatically create user account when logging in for the first time through external OpenID. - */ - label: () => LocalizedString - /** - * If this option is enabled, Defguard automatically creates new accounts for users who log in for the first time using an external OpenID provider. Otherwise, the user account must first be created by an administrator. - */ - helper: () => LocalizedString - } - usernameHandling: { - /** - * Username handling - */ - label: () => LocalizedString - /** - * Configure the method for handling invalid characters in usernames provided by your identity provider. - */ - helper: () => LocalizedString - options: { - /** - * Remove forbidden characters - */ - remove: () => LocalizedString - /** - * Replace forbidden characters - */ - replace: () => LocalizedString - /** - * Prune email domain - */ - prune_email: () => LocalizedString - } - } - } - form: { - /** - * Client settings - */ - title: () => LocalizedString - /** - * Here you can configure the OpenID client settings with values provided by your external OpenID provider. - */ - helper: () => LocalizedString - /** - * Custom - */ - custom: () => LocalizedString - /** - * None - */ - none: () => LocalizedString - /** - * Make sure to check our [documentation](https://docs.defguard.net/features/external-openid-providers) for more information and examples. - */ - documentation: () => LocalizedString - /** - * Delete provider - */ - 'delete': () => LocalizedString - directory_sync_settings: { - /** - * Directory synchronization settings - */ - title: () => LocalizedString - /** - * Directory synchronization allows you to automatically synchronize users' status and groups from an external provider. - */ - helper: () => LocalizedString - /** - * Directory sync is not supported for this provider. - */ - notSupported: () => LocalizedString - connectionTest: { - /** - * Connection successful - */ - success: () => LocalizedString - /** - * Connection failed with error: - */ - error: () => LocalizedString - } - } - selects: { - synchronize: { - /** - * All - */ - all: () => LocalizedString - /** - * Users - */ - users: () => LocalizedString - /** - * Groups - */ - groups: () => LocalizedString - } - behavior: { - /** - * Keep - */ - keep: () => LocalizedString - /** - * Disable - */ - disable: () => LocalizedString - /** - * Delete - */ - 'delete': () => LocalizedString - } - } - labels: { - provider: { - /** - * Provider - */ - label: () => LocalizedString - /** - * Select your OpenID provider. You can use custom provider and fill in the base URL by yourself. - */ - helper: () => LocalizedString - } - client_id: { - /** - * Client ID - */ - label: () => LocalizedString - /** - * Client ID provided by your OpenID provider. - */ - helper: () => LocalizedString - } - client_secret: { - /** - * Client Secret - */ - label: () => LocalizedString - /** - * Client Secret provided by your OpenID provider. - */ - helper: () => LocalizedString - } - base_url: { - /** - * Base URL - */ - label: () => LocalizedString - /** - * Base URL of your OpenID provider, e.g. https://accounts.google.com. Make sure to check our documentation for more information and examples. - */ - helper: () => LocalizedString - } - display_name: { - /** - * Display Name - */ - label: () => LocalizedString - /** - * Name of the OpenID provider to display on the login's page button. If not provided, the button will display generic 'Login with OIDC' text. - */ - helper: () => LocalizedString - } - enable_directory_sync: { - /** - * Enable directory synchronization - */ - label: () => LocalizedString - } - sync_target: { - /** - * Synchronize - */ - label: () => LocalizedString - /** - * What to synchronize from the external provider. You can choose between synchronizing both users' state and group memberships, or narrow it down to just one of these. - */ - helper: () => LocalizedString - } - sync_interval: { - /** - * Synchronization interval - */ - label: () => LocalizedString - /** - * Interval in seconds between directory synchronizations. - */ - helper: () => LocalizedString - } - user_behavior: { - /** - * User behavior - */ - label: () => LocalizedString - /** - * Choose how to handle users that are not present in the external provider anymore. You can select between keeping, disabling, or deleting them. - */ - helper: () => LocalizedString - } - admin_behavior: { - /** - * Admin behavior - */ - label: () => LocalizedString - /** - * Choose how to handle Defguard admins that are not present in the external provider anymore. You can select between keeping them, disabling them or completely deleting them. - */ - helper: () => LocalizedString - } - admin_email: { - /** - * Admin email - */ - label: () => LocalizedString - /** - * Email address of the account on which behalf the synchronization checks will be performed, e.g. the person who setup the Google service account. See our documentation for more details. - */ - helper: () => LocalizedString - } - service_account_used: { - /** - * Service account in use - */ - label: () => LocalizedString - /** - * The service account currently being used for synchronization. You can change it by uploading a new service account key file. - */ - helper: () => LocalizedString - } - service_account_key_file: { - /** - * Service Account Key file - */ - label: () => LocalizedString - /** - * Upload a new service account key file to set the service account used for synchronization. NOTE: The uploaded file won't be visible after saving the settings and reloading the page as it's contents are sensitive and are never sent back to the dashboard. - */ - helper: () => LocalizedString - /** - * File uploaded - */ - uploaded: () => LocalizedString - /** - * Upload a service account key file - */ - uploadPrompt: () => LocalizedString - } - okta_client_id: { - /** - * Directory Sync Client ID - */ - label: () => LocalizedString - /** - * Client ID for the Okta directory sync application. - */ - helper: () => LocalizedString - } - okta_client_key: { - /** - * Directory Sync Client Private Key - */ - label: () => LocalizedString - /** - * Client private key for the Okta directory sync application in the JWK format. It won't be shown again here. - */ - helper: () => LocalizedString - } - jumpcloud_api_key: { - /** - * JumpCloud API Key - */ - label: () => LocalizedString - /** - * API Key for the JumpCloud directory sync. It will be used to periodically query JumpCloud for user state and group membership changes. - */ - helper: () => LocalizedString - } - group_match: { - /** - * Sync only matching groups - */ - label: () => LocalizedString - /** - * Provide a comma separated list of group names that should be synchronized. If left empty, all groups from the provider will be synchronized. - */ - helper: () => LocalizedString - } - } - } - } - modulesVisibility: { - /** - * Modules Visibility - */ - header: () => LocalizedString - /** - *

- Hide unused modules. -

- - Read more in documentation. - - */ - helper: (arg: { documentationLink: string }) => LocalizedString - fields: { - wireguard_enabled: { - /** - * WireGuard VPN - */ - label: () => LocalizedString - } - webhooks_enabled: { - /** - * Webhooks - */ - label: () => LocalizedString - } - worker_enabled: { - /** - * Yubikey provisioning - */ - label: () => LocalizedString - } - openid_enabled: { - /** - * OpenID Connect - */ - label: () => LocalizedString - } - } - } - defaultNetworkSelect: { - /** - * Default location view - */ - header: () => LocalizedString - /** - *

Here you can change your default location view.

- - Read more in documentation. - - */ - helper: (arg: { documentationLink: string }) => LocalizedString - filterLabels: { - /** - * Grid view - */ - grid: () => LocalizedString - /** - * List view - */ - list: () => LocalizedString - } - } - instanceBranding: { - /** - * Instance Branding - */ - header: () => LocalizedString - form: { - /** - * Name & Logo: - */ - title: () => LocalizedString - fields: { - instanceName: { - /** - * Instance name - */ - label: () => LocalizedString - /** - * Defguard - */ - placeholder: () => LocalizedString - } - mainLogoUrl: { - /** - * Login logo url - */ - label: () => LocalizedString - /** - * Maximum picture size is 250x100 px - */ - helper: () => LocalizedString - /** - * Default image - */ - placeholder: () => LocalizedString - } - navLogoUrl: { - /** - * Menu & navigation small logo - */ - label: () => LocalizedString - /** - * Maximum picture size is 100x100 px - */ - helper: () => LocalizedString - /** - * Default image - */ - placeholder: () => LocalizedString - } - } - controls: { - /** - * Restore default - */ - restoreDefault: () => LocalizedString - /** - * Save changes - */ - submit: () => LocalizedString - } - } - /** - * -

- Here you can add url of your logo and name for your defguard - instance it will be displayed instead of defguard. -

- - Read more in documentation. - - - */ - helper: (arg: { documentationLink: string }) => LocalizedString - } - license: { - /** - * Enterprise - */ - header: () => LocalizedString - helpers: { - enterpriseHeader: { - /** - * Here you can manage your Defguard Enterprise version license. - */ - text: () => LocalizedString - /** - * To learn more about Defguard Enterprise, visit our webiste. - */ - link: () => LocalizedString - } - licenseKey: { - /** - * Enter your Defguard Enterprise license key below. You should receive it via email after purchasing the license. - */ - text: () => LocalizedString - /** - * You can purchase the license here. - */ - link: () => LocalizedString - } - } - form: { - /** - * License - */ - title: () => LocalizedString - fields: { - key: { - /** - * License key - */ - label: () => LocalizedString - /** - * Your Defguard license key - */ - placeholder: () => LocalizedString - } - } - } - licenseInfo: { - /** - * License information - */ - title: () => LocalizedString - status: { - /** - * No valid license - */ - noLicense: () => LocalizedString - /** - * Expired - */ - expired: () => LocalizedString - /** - * Limits Exceeded - */ - limitsExceeded: () => LocalizedString - /** - * Active - */ - active: () => LocalizedString - } - /** - *

You have access to this enterprise feature, as you haven't exceeded any of the usage limits yet. Check the documentation for more information.

- */ - licenseNotRequired: () => LocalizedString - types: { - subscription: { - /** - * Subscription - */ - label: () => LocalizedString - /** - * A license that automatically renews at regular intervals - */ - helper: () => LocalizedString - } - offline: { - /** - * Offline - */ - label: () => LocalizedString - /** - * The license is valid until the expiry date and does not automatically renew - */ - helper: () => LocalizedString - } - } - fields: { - status: { - /** - * Status - */ - label: () => LocalizedString - /** - * Active - */ - active: () => LocalizedString - /** - * Expired - */ - expired: () => LocalizedString - /** - * A subscription license is considered valid for some time after the expiration date to account for possible automatic payment delays. - */ - subscriptionHelper: () => LocalizedString - } - type: { - /** - * Type - */ - label: () => LocalizedString - } - validUntil: { - /** - * Valid until - */ - label: () => LocalizedString - } - } - } - } - smtp: { - form: { - /** - * SMTP configuration - */ - title: () => LocalizedString - sections: { - /** - * Server settings - */ - server: () => LocalizedString - } - fields: { - encryption: { - /** - * Encryption - */ - label: () => LocalizedString - } - server: { - /** - * Server address - */ - label: () => LocalizedString - /** - * Address - */ - placeholder: () => LocalizedString - } - port: { - /** - * Server port - */ - label: () => LocalizedString - /** - * Port - */ - placeholder: () => LocalizedString - } - user: { - /** - * Server username - */ - label: () => LocalizedString - /** - * Username - */ - placeholder: () => LocalizedString - } - password: { - /** - * Server password - */ - label: () => LocalizedString - /** - * Password - */ - placeholder: () => LocalizedString - } - sender: { - /** - * Sender email address - */ - label: () => LocalizedString - /** - * Address - */ - placeholder: () => LocalizedString - /** - * -

- System messages will be sent from this address. - E.g. no-reply@my-company.com. -

- - */ - helper: () => LocalizedString - } - } - controls: { - /** - * Save changes - */ - submit: () => LocalizedString - } - } - /** - * Delete configuration - */ - 'delete': () => LocalizedString - testForm: { - /** - * Send test email - */ - title: () => LocalizedString - /** - * Enter recipent email address - */ - subtitle: () => LocalizedString - fields: { - to: { - /** - * Send test email to - */ - label: () => LocalizedString - /** - * Address - */ - placeholder: () => LocalizedString - } - } - controls: { - /** - * Send - */ - submit: () => LocalizedString - /** - * Resend - */ - resend: () => LocalizedString - /** - * Retry - */ - retry: () => LocalizedString - /** - * Test email sent - */ - success: () => LocalizedString - /** - * Error sending email - */ - error: () => LocalizedString - } - success: { - /** - * Test email has been sent successully. - */ - message: () => LocalizedString - } - error: { - /** - * There was an error sending the test email. Please check your SMTP configuration and try again. - */ - message: () => LocalizedString - /** - * Error: {error} - */ - fullError: (arg: { error: string }) => LocalizedString - } - } - /** - * Here you can configure SMTP server used to send system messages to the users. - */ - helper: () => LocalizedString - } - enrollment: { - /** - * Enrollment is a process by which a new employee will be able to activate their new account, create a password and configure a VPN device. - */ - helper: () => LocalizedString - vpnOptionality: { - /** - * VPN step optionality - */ - header: () => LocalizedString - /** - * You can choose whether creating a VPN device is optional or mandatory during enrollment - */ - helper: () => LocalizedString - } - welcomeMessage: { - /** - * Welcome message - */ - header: () => LocalizedString - /** - * -

In this text input you can use Markdown:

-
    -
  • Headings start with a hash #
  • -
  • Use asterisks for *italics*
  • -
  • Use two asterisks for **bold**
  • -
- - */ - helper: () => LocalizedString - } - welcomeEmail: { - /** - * Welcome e-mail - */ - header: () => LocalizedString - /** - * -

In this text input you can use Markdown:

-
    -
  • Headings start with a hash #
  • -
  • Use asterisks for *italics*
  • -
  • Use two asterisks for **bold**
  • -
- - */ - helper: () => LocalizedString - } - form: { - controls: { - /** - * Save changes - */ - submit: () => LocalizedString - } - welcomeMessage: { - /** - * This information will be displayed for the user once enrollment is completed. We advise you to insert relevant links and explain next steps briefly. - */ - helper: () => LocalizedString - /** - * Please input welcome message - */ - placeholder: () => LocalizedString - } - welcomeEmail: { - /** - * This information will be sent to the user once enrollment is completed. We advise you to insert relevant links and explain next steps briefly. You can reuse the welcome message here. - */ - helper: () => LocalizedString - /** - * Please input welcome email - */ - placeholder: () => LocalizedString - } - welcomeEmailSubject: { - /** - * Subject - */ - label: () => LocalizedString - } - useMessageAsEmail: { - /** - * Same as welcome message - */ - label: () => LocalizedString - } - } - } - enterprise: { - /** - * Enterprise Features - */ - header: () => LocalizedString - /** - * Here you can change enterprise settings. - */ - helper: () => LocalizedString - fields: { - deviceManagement: { - /** - * Disable users' ability to manage their devices - */ - label: () => LocalizedString - /** - * When this option is enabled, only users in the Admin group can manage devices in user profile (it's disabled for all other users) - */ - helper: () => LocalizedString - } - disableAllTraffic: { - /** - * Disable the option to route all traffic through VPN - */ - label: () => LocalizedString - /** - * When this option is enabled, users will not be able to route all traffic through the VPN using the defguard client. - */ - helper: () => LocalizedString - } - manualConfig: { - /** - * Disable users' ability to manually configure WireGuard client - */ - label: () => LocalizedString - /** - * When this option is enabled, users won't be able to view or download configuration for the manual WireGuard client setup. Only the Defguard desktop client configuration will be available. - */ - helper: () => LocalizedString - } - } - } - gatewayNotifications: { - /** - * To enable notifications you must first configure an SMTP server - */ - smtpWarning: () => LocalizedString - /** - * Notifications - */ - header: () => LocalizedString - sections: { - /** - * Gateway disconnect notifications - */ - gateway: () => LocalizedString - } - /** - * Here you can manage email notifications. - */ - helper: () => LocalizedString - form: { - /** - * Save changes - */ - submit: () => LocalizedString - fields: { - disconnectNotificationsEnabled: { - /** - * Enable gateway disconnect notifications - */ - label: () => LocalizedString - /** - * Send email notification to admin users once a gateway is disconnected - */ - help: () => LocalizedString - } - inactivityThreshold: { - /** - * Gateway inactivity time [minutes] - */ - label: () => LocalizedString - /** - * Time (in minutes) that a gateway needs to stay disconnected before a notification is sent - */ - help: () => LocalizedString - } - reconnectNotificationsEnabled: { - /** - * Enable gateway reconnect notifications - */ - label: () => LocalizedString - /** - * Send email notification to admin users once a gateway is reconnected - */ - help: () => LocalizedString - } - } - } - } - } - openidOverview: { - /** - * OpenID Apps - */ - pageTitle: () => LocalizedString - search: { - /** - * Find apps - */ - placeholder: () => LocalizedString - } - filterLabels: { - /** - * All apps - */ - all: () => LocalizedString - /** - * Enabled - */ - enabled: () => LocalizedString - /** - * Disabled - */ - disabled: () => LocalizedString - } - /** - * All apps - */ - clientCount: () => LocalizedString - /** - * Add new - */ - addNewApp: () => LocalizedString - list: { - headers: { - /** - * Name - */ - name: () => LocalizedString - /** - * Status - */ - status: () => LocalizedString - /** - * Actions - */ - actions: () => LocalizedString - } - editButton: { - /** - * Edit app - */ - edit: () => LocalizedString - /** - * Delete app - */ - 'delete': () => LocalizedString - /** - * Disable - */ - disable: () => LocalizedString - /** - * Enable - */ - enable: () => LocalizedString - /** - * Copy client ID - */ - copy: () => LocalizedString - } - status: { - /** - * Enabled - */ - enabled: () => LocalizedString - /** - * Disabled - */ - disabled: () => LocalizedString - } - } - messages: { - /** - * Client ID copied. - */ - copySuccess: () => LocalizedString - /** - * You don't have a license for this feature. - */ - noLicenseMessage: () => LocalizedString - /** - * No results found. - */ - noClientsFound: () => LocalizedString - } - deleteApp: { - /** - * Delete app - */ - title: () => LocalizedString - /** - * Do you want to delete {appName} app ? - */ - message: (arg: { appName: string }) => LocalizedString - /** - * Delete app - */ - submit: () => LocalizedString - messages: { - /** - * App deleted. - */ - success: () => LocalizedString - } - } - enableApp: { - messages: { - /** - * App enabled. - */ - success: () => LocalizedString - } - } - disableApp: { - messages: { - /** - * App disabled. - */ - success: () => LocalizedString - } - } - modals: { - openidClientModal: { - title: { - /** - * Add Application - */ - addApp: () => LocalizedString - /** - * Edit {appName} app - */ - editApp: (arg: { appName: string }) => LocalizedString - } - /** - * Scopes: - */ - scopes: () => LocalizedString - messages: { - /** - * Client ID copied. - */ - clientIdCopy: () => LocalizedString - /** - * Client secret copied. - */ - clientSecretCopy: () => LocalizedString - } - form: { - messages: { - /** - * App created. - */ - successAdd: () => LocalizedString - /** - * App modified. - */ - successModify: () => LocalizedString - } - error: { - /** - * URL is required. - */ - urlRequired: () => LocalizedString - /** - * Must be a valid URL. - */ - validUrl: () => LocalizedString - /** - * Must have at least one scope. - */ - scopeValidation: () => LocalizedString - } - fields: { - name: { - /** - * App name - */ - label: () => LocalizedString - } - redirectUri: { - /** - * Redirect URL {count} - */ - label: (arg: { count: number }) => LocalizedString - /** - * https://example.com/redirect - */ - placeholder: () => LocalizedString - } - openid: { - /** - * OpenID - */ - label: () => LocalizedString - } - profile: { - /** - * Profile - */ - label: () => LocalizedString - } - email: { - /** - * Email - */ - label: () => LocalizedString - } - phone: { - /** - * Phone - */ - label: () => LocalizedString - } - groups: { - /** - * Groups - */ - label: () => LocalizedString - } - } - controls: { - /** - * Add URL - */ - addUrl: () => LocalizedString - } - } - /** - * Client ID - */ - clientId: () => LocalizedString - /** - * Client secret - */ - clientSecret: () => LocalizedString - } - } - } - webhooksOverview: { - /** - * Webhooks - */ - pageTitle: () => LocalizedString - search: { - /** - * Find webhooks by url - */ - placeholder: () => LocalizedString - } - filterLabels: { - /** - * All webhooks - */ - all: () => LocalizedString - /** - * Enabled - */ - enabled: () => LocalizedString - /** - * Disabled - */ - disabled: () => LocalizedString - } - /** - * All webhooks - */ - webhooksCount: () => LocalizedString - /** - * Add new - */ - addNewWebhook: () => LocalizedString - /** - * No webhooks found. - */ - noWebhooksFound: () => LocalizedString - list: { - headers: { - /** - * Name - */ - name: () => LocalizedString - /** - * Description - */ - description: () => LocalizedString - /** - * Status - */ - status: () => LocalizedString - /** - * Actions - */ - actions: () => LocalizedString - } - editButton: { - /** - * Edit - */ - edit: () => LocalizedString - /** - * Delete webhook - */ - 'delete': () => LocalizedString - /** - * Disable - */ - disable: () => LocalizedString - /** - * Enable - */ - enable: () => LocalizedString - } - status: { - /** - * Enabled - */ - enabled: () => LocalizedString - /** - * Disabled - */ - disabled: () => LocalizedString - } - } - } - provisionersOverview: { - /** - * Provisioners - */ - pageTitle: () => LocalizedString - search: { - /** - * Find provisioners - */ - placeholder: () => LocalizedString - } - filterLabels: { - /** - * All - */ - all: () => LocalizedString - /** - * Available - */ - available: () => LocalizedString - /** - * Unavailable - */ - unavailable: () => LocalizedString - } - /** - * All provisioners - */ - provisionersCount: () => LocalizedString - /** - * No provisioners found. - */ - noProvisionersFound: () => LocalizedString - /** - * You don't have a license for this feature. - */ - noLicenseMessage: () => LocalizedString - provisioningStation: { - /** - * YubiKey provisioning station - */ - header: () => LocalizedString - /** - * In order to be able to provision your YubiKeys, first you need to set up - physical machine with USB slot. Run provided command on your chosen - machine to register it and start provisioning your keys. - */ - content: () => LocalizedString - dockerCard: { - /** - * Provisioning station docker setup command - */ - title: () => LocalizedString - } - tokenCard: { - /** - * Access token - */ - title: () => LocalizedString - } - } - list: { - headers: { - /** - * Name - */ - name: () => LocalizedString - /** - * IP address - */ - ip: () => LocalizedString - /** - * Status - */ - status: () => LocalizedString - /** - * Actions - */ - actions: () => LocalizedString - } - editButton: { - /** - * Delete provisioner - */ - 'delete': () => LocalizedString - } - status: { - /** - * Available - */ - available: () => LocalizedString - /** - * Unavailable - */ - unavailable: () => LocalizedString - } - } - messages: { - copy: { - /** - * Token copied - */ - token: () => LocalizedString - /** - * Command copied - */ - command: () => LocalizedString - } - } - } - openidAllow: { - /** - * {name} would like to: - */ - header: (arg: { name: string }) => LocalizedString - scopes: { - /** - * Use your profile data for future logins. - */ - openid: () => LocalizedString - /** - * Know basic information from your profile like name, profile picture etc. - */ - profile: () => LocalizedString - /** - * Know your email address. - */ - email: () => LocalizedString - /** - * Know your phone number. - */ - phone: () => LocalizedString - /** - * Know your groups membership. - */ - groups: () => LocalizedString - } - controls: { - /** - * Accept - */ - accept: () => LocalizedString - /** - * Cancel - */ - cancel: () => LocalizedString - } - } - networkOverview: { - networkSelection: { - /** - * All locations summary - */ - all: () => LocalizedString - /** - * Select location - */ - placeholder: () => LocalizedString - } - /** - * {value}h period - */ - timeRangeSelectionLabel: (arg: { value: number }) => LocalizedString - /** - * Location overview - */ - pageTitle: () => LocalizedString - controls: { - /** - * Edit Locations settings - */ - editNetworks: () => LocalizedString - selectNetwork: { - /** - * Loading locations - */ - placeholder: () => LocalizedString - } - } - filterLabels: { - /** - * Grid view - */ - grid: () => LocalizedString - /** - * List view - */ - list: () => LocalizedString - } - gatewayStatus: { - /** - * All ({count}) Connected - */ - all: (arg: { count: number }) => LocalizedString - /** - * Some ({count}) Connected - */ - some: (arg: { count: number }) => LocalizedString - /** - * None connected - */ - none: () => LocalizedString - } - stats: { - /** - * Currently active users - */ - currentlyActiveUsers: () => LocalizedString - /** - * Currently active network devices - */ - currentlyActiveNetworkDevices: () => LocalizedString - /** - * Total user devices: {count} - */ - totalUserDevices: (arg: { count: number }) => LocalizedString - /** - * Active network devices in {hour}h - */ - activeNetworkDevices: (arg: { hour: number }) => LocalizedString - /** - * Active users in {hour}h - */ - activeUsersFilter: (arg: { hour: number }) => LocalizedString - /** - * Active devices in {hour}h - */ - activeDevicesFilter: (arg: { hour: number }) => LocalizedString - /** - * Activity in {hour}H - */ - activityIn: (arg: { hour: number }) => LocalizedString - /** - * Network usage - */ - networkUsage: () => LocalizedString - /** - * Peak - */ - peak: () => LocalizedString - /** - * In: - */ - 'in': () => LocalizedString - /** - * Out: - */ - out: () => LocalizedString - /** - * Gateway disconnected - */ - gatewayDisconnected: () => LocalizedString - } - cardsLabels: { - /** - * Connected Users - */ - users: () => LocalizedString - /** - * Connected Network Devices - */ - devices: () => LocalizedString - } - } - connectedUsersOverview: { - /** - * Connected users - */ - pageTitle: () => LocalizedString - /** - * Currently there are no connected users - */ - noUsersMessage: () => LocalizedString - userList: { - /** - * Username - */ - username: () => LocalizedString - /** - * Device - */ - device: () => LocalizedString - /** - * Connected - */ - connected: () => LocalizedString - /** - * Device location - */ - deviceLocation: () => LocalizedString - /** - * Network usage - */ - networkUsage: () => LocalizedString - } - } - networkPage: { - /** - * Edit Location - */ - pageTitle: () => LocalizedString - /** - * + Add new location - */ - addNetwork: () => LocalizedString - controls: { - networkSelect: { - /** - * Location choice - */ - label: () => LocalizedString - } - } - } - activityOverview: { - /** - * Activity stream - */ - header: () => LocalizedString - /** - * Currently there is no activity detected - */ - noData: () => LocalizedString - } - networkConfiguration: { - messages: { - 'delete': { - /** - * Network deleted - */ - success: () => LocalizedString - /** - * Failed to delete network - */ - error: () => LocalizedString - } - } - /** - * Location configuration - */ - header: () => LocalizedString - /** - * Location import - */ - importHeader: () => LocalizedString - form: { - helpers: { - /** - * Based on this address VPN network address will be defined, eg. 10.10.10.1/24 (and VPN network will be: 10.10.10.0/24). You can optionally specify multiple addresses separated by a comma. The first address is the primary address, and this one will be used for IP address assignment for devices. The other IP addresses are auxiliary and are not managed by Defguard. - */ - address: () => LocalizedString - /** - * Public IP address or domain name to which the remote peers/users will connect to. This address will be used in the configuration for the clients, but Defguard Gateways do not bind to this address. - */ - endpoint: () => LocalizedString - /** - * Gateway public address, used by VPN users to connect - */ - gateway: () => LocalizedString - /** - * Specify the DNS resolvers to query when the wireguard interface is up. - */ - dns: () => LocalizedString - /** - * List of addresses/masks that should be routed through the VPN network. - */ - allowedIps: () => LocalizedString - /** - * By default, all users will be allowed to connect to this location. If you want to restrict access to this location to a specific group, please select it below. - */ - allowedGroups: () => LocalizedString - /** - * ACL functionality is an enterprise feature and you've exceeded the user, device or network limits to use it. In order to use this feature, purchase an enterprise license or upgrade your existing one. - */ - aclFeatureDisabled: () => LocalizedString - /** - * Clients authorized with MFA will be disconnected from the location once there has been no network activity detected between them and the VPN gateway for a length of time configured below. - */ - peerDisconnectThreshold: () => LocalizedString - locationMfaMode: { - /** - * Choose how MFA is enforced when connecting to this location: - */ - description: () => LocalizedString - /** - * Internal MFA - MFA is enforced using Defguard's built-in MFA (e.g. TOTP, WebAuthn) with internal identity - */ - internal: () => LocalizedString - /** - * External MFA - If configured (see [OpenID settings](settings)) this option uses external identity provider for MFA - */ - external: () => LocalizedString - } - } - sections: { - accessControl: { - /** - * Access Control & Firewall - */ - header: () => LocalizedString - } - mfa: { - /** - * Multi-Factor Authentication - */ - header: () => LocalizedString - } - } - messages: { - /** - * Location modified. - */ - networkModified: () => LocalizedString - /** - * Location created - */ - networkCreated: () => LocalizedString - } - fields: { - name: { - /** - * Location name - */ - label: () => LocalizedString - } - address: { - /** - * Gateway VPN IP address and netmask - */ - label: () => LocalizedString - } - endpoint: { - /** - * Gateway IP address or domain name - */ - label: () => LocalizedString - } - allowedIps: { - /** - * Allowed Ips - */ - label: () => LocalizedString - } - port: { - /** - * Gateway port - */ - label: () => LocalizedString - } - dns: { - /** - * DNS - */ - label: () => LocalizedString - } - allowedGroups: { - /** - * Allowed groups - */ - label: () => LocalizedString - /** - * All groups - */ - placeholder: () => LocalizedString - } - keepalive_interval: { - /** - * Keepalive interval [seconds] - */ - label: () => LocalizedString - } - peer_disconnect_threshold: { - /** - * Client disconnect threshold [seconds] - */ - label: () => LocalizedString - } - acl_enabled: { - /** - * Enable ACL for this location - */ - label: () => LocalizedString - } - acl_default_allow: { - /** - * Default ACL policy - */ - label: () => LocalizedString - } - location_mfa_mode: { - /** - * MFA requirement - */ - label: () => LocalizedString - } - } - controls: { - /** - * Save changes - */ - submit: () => LocalizedString - /** - * Back to Overview - */ - cancel: () => LocalizedString - /** - * Remove location - */ - 'delete': () => LocalizedString - } - } - } - gatewaySetup: { - header: { - /** - * Gateway server setup - */ - main: () => LocalizedString - /** - * Docker Based Gateway Setup - */ - dockerBasedGatewaySetup: () => LocalizedString - /** - * From Package - */ - fromPackage: () => LocalizedString - /** - * One Line Install - */ - oneLineInstall: () => LocalizedString - } - card: { - /** - * Docker based gateway setup - */ - title: () => LocalizedString - /** - * Authentication Token - */ - authToken: () => LocalizedString - } - button: { - /** - * Available Packages - */ - availablePackages: () => LocalizedString - } - controls: { - /** - * Check connection status - */ - status: () => LocalizedString - } - messages: { - /** - * Defguard requires to deploy a gateway node to control wireguard VPN on the vpn server. - More details can be found in the [documentation]({setupGatewayDocs}). - There are several ways to deploy the gateway server, - below is a Docker based example, for other examples please visit [documentation]({setupGatewayDocs}). - */ - runCommand: (arg: { setupGatewayDocs: string }) => LocalizedString - /** - * Please create the network before running the gateway process. - */ - createNetwork: () => LocalizedString - /** - * No connection established, please run provided command. - */ - noConnection: () => LocalizedString - /** - * Gateway connected. - */ - connected: () => LocalizedString - /** - * Failed to get gateway status - */ - statusError: () => LocalizedString - /** - * If you are doing one line install: https://docs.defguard.net/getting-started/one-line-install - you don't need to do anything. - */ - oneLineInstall: () => LocalizedString - /** - * Install the package available at https://github.com/DefGuard/gateway/releases/latest and configure `/etc/defguard/gateway.toml` - according to the [documentation]({setupGatewayDocs}). - */ - fromPackage: (arg: { setupGatewayDocs: string }) => LocalizedString - /** - * Token below is required to authenticate and configure the gateway node. Ensure you keep this token secure and follow the deployment instructions - provided in the [documentation]({setupGatewayDocs}) to successfully set up the gateway server. - For more details and exact steps, please refer to the [documentation]({setupGatewayDocs}). - */ - authToken: (arg: { setupGatewayDocs: string }) => LocalizedString - /** - * Below is a Docker based example. For more details and exact steps, please refer to the [documentation]({setupGatewayDocs}). - */ - dockerBasedGatewaySetup: (arg: { setupGatewayDocs: string }) => LocalizedString - } - } - loginPage: { - /** - * Enter your credentials - */ - pageTitle: () => LocalizedString - /** - * Sign in with - */ - oidcLogin: () => LocalizedString - callback: { - /** - * Go back to login - */ - 'return': () => LocalizedString - /** - * An error occurred during external OpenID login - */ - error: () => LocalizedString - } - mfa: { - /** - * Two-factor authentication - */ - title: () => LocalizedString - controls: { - /** - * Use Authenticator app instead - */ - useAuthenticator: () => LocalizedString - /** - * Use security key instead - */ - useWebauthn: () => LocalizedString - /** - * Use recovery code instead - */ - useRecoveryCode: () => LocalizedString - /** - * Use E-mail instead - */ - useEmail: () => LocalizedString - } - email: { - /** - * Use code we sent to your e-mail to proceed. - */ - header: () => LocalizedString - form: { - labels: { - /** - * Code - */ - code: () => LocalizedString - } - controls: { - /** - * Resend Code - */ - resendCode: () => LocalizedString - } - } - } - totp: { - /** - * Use code from your authentication app and click button to proceed. - */ - header: () => LocalizedString - form: { - fields: { - code: { - /** - * Enter Authenticator code - */ - placeholder: () => LocalizedString - } - } - controls: { - /** - * Use authenticator code - */ - submit: () => LocalizedString - } - } - } - recoveryCode: { - /** - * Enter one of active recovery codes and click button to log in. - */ - header: () => LocalizedString - form: { - fields: { - code: { - /** - * Recovery code - */ - placeholder: () => LocalizedString - } - } - controls: { - /** - * Use recovery code - */ - submit: () => LocalizedString - } - } - } - webauthn: { - /** - * When you are ready to authenticate, press the button below. - */ - header: () => LocalizedString - controls: { - /** - * Use security key - */ - submit: () => LocalizedString - } - messages: { - /** - * Failed to read key. Please try again. - */ - error: () => LocalizedString - } - } - } - } - wizard: { - /** - * Location setup completed - */ - completed: () => LocalizedString - configuration: { - /** - * Location created - */ - successMessage: () => LocalizedString - } - welcome: { - /** - * Welcome to location wizard! - */ - header: () => LocalizedString - /** - * Before you start using VPN you need to setup your first location. When in doubt click on icon. - */ - sub: () => LocalizedString - /** - * Setup location - */ - button: () => LocalizedString - } - navigation: { - /** - * Location setup - */ - top: () => LocalizedString - titles: { - /** - * Location setup - */ - welcome: () => LocalizedString - /** - * Chose Location setup - */ - choseNetworkSetup: () => LocalizedString - /** - * Import existing location - */ - importConfig: () => LocalizedString - /** - * Configure location - */ - manualConfig: () => LocalizedString - /** - * Map imported devices - */ - mapDevices: () => LocalizedString - } - buttons: { - /** - * Next - */ - next: () => LocalizedString - /** - * Back - */ - back: () => LocalizedString - } - } - deviceMap: { - messages: { - /** - * Devices added - */ - crateSuccess: () => LocalizedString - /** - * Please fill marked fields. - */ - errorsInForm: () => LocalizedString - } - list: { - headers: { - /** - * Device Name - */ - deviceName: () => LocalizedString - /** - * IP - */ - deviceIP: () => LocalizedString - /** - * User - */ - user: () => LocalizedString - } - } - } - wizardType: { - manual: { - /** - * Manual Configuration - */ - title: () => LocalizedString - /** - * Manual location configuration - */ - description: () => LocalizedString - } - 'import': { - /** - * Import From File - */ - title: () => LocalizedString - /** - * Import from WireGuard config file - */ - description: () => LocalizedString - } - /** - * Create location - */ - createNetwork: () => LocalizedString - } - common: { - /** - * Select - */ - select: () => LocalizedString - } - locations: { - form: { - /** - * Name - */ - name: () => LocalizedString - /** - * IP address - */ - ip: () => LocalizedString - /** - * User - */ - user: () => LocalizedString - /** - * File - */ - fileName: () => LocalizedString - /** - * Select file - */ - selectFile: () => LocalizedString - messages: { - /** - * Devices created - */ - devicesCreated: () => LocalizedString - } - validation: { - /** - * Invalid address - */ - invalidAddress: () => LocalizedString - } - } - } - } - layout: { - select: { - /** - * Add new + - */ - addNewOptionDefault: () => LocalizedString - } - } - redirectPage: { - /** - * You have been logged in - */ - title: () => LocalizedString - /** - * You will be redirected in a moment... - */ - subtitle: () => LocalizedString - } - enrollmentPage: { - /** - * Enrollment - */ - title: () => LocalizedString - controls: { - /** - * Restore default - */ - 'default': () => LocalizedString - /** - * Save changes - */ - save: () => LocalizedString - } - messages: { - edit: { - /** - * Settings changed - */ - success: () => LocalizedString - /** - * Save failed - */ - error: () => LocalizedString - } - } - /** - * Enrollment is a process by which a new employee will be able to activate their new account, create a password and configure a VPN device. You can customize it here. - */ - messageBox: () => LocalizedString - settings: { - welcomeMessage: { - /** - * Welcome message - */ - title: () => LocalizedString - /** - * This information will be displayed for user in service once enrollment is completed. We advise to insert links and explain next steps briefly. You can use same message as in the e-mail. - */ - messageBox: () => LocalizedString - } - vpnOptionality: { - /** - * VPN set optionallity - */ - title: () => LocalizedString - select: { - options: { - /** - * Optional - */ - optional: () => LocalizedString - /** - * Mandatory - */ - mandatory: () => LocalizedString - } - } - } - welcomeEmail: { - /** - * Welcome e-mail - */ - title: () => LocalizedString - subject: { - /** - * E-mail subject - */ - label: () => LocalizedString - } - /** - * This information will be sent to user once enrollment is completed. We advise to insert links and explain next steps briefly. - */ - messageBox: () => LocalizedString - controls: { - /** - * Same as welcome message - */ - duplicateWelcome: () => LocalizedString - } - } - } - } - supportPage: { - /** - * Support - */ - title: () => LocalizedString - modals: { - confirmDataSend: { - /** - * Send Support Data - */ - title: () => LocalizedString - /** - * Please confirm that you actually want to send support debug information. None of your private information will be sent (wireguard keys, email addresses, etc. will not be sent). - */ - subTitle: () => LocalizedString - /** - * Send support data - */ - submit: () => LocalizedString - } - } - debugDataCard: { - /** - * Support data - */ - title: () => LocalizedString - /** - * - If you need assistance or you were asked to generate support data by our team (for example on our Matrix support channel: **#defguard-support:teonite.com**), you have two options: - * Either you can configure SMTP settings and click "Send support data" - * Or click "Download support data" and create a bug report in our GitHub attaching this file. - - */ - body: () => LocalizedString - /** - * Download support data - */ - downloadSupportData: () => LocalizedString - /** - * Download logs - */ - downloadLogs: () => LocalizedString - /** - * Send support data - */ - sendMail: () => LocalizedString - /** - * Email sent - */ - mailSent: () => LocalizedString - /** - * Error sending email - */ - mailError: () => LocalizedString - } - supportCard: { - /** - * Support - */ - title: () => LocalizedString - /** - * - Before contacting or submitting any issues to GitHub please get familiar with Defguard documentation available at [docs.defguard.net](https://docs.defguard.net/) - - To submit: - * Bugs - please go to [GitHub](https://github.com/DefGuard/defguard/issues/new?assignees=&labels=bug&template=bug_report.md&title=) - * Feature request - please go to [GitHub](https://github.com/DefGuard/defguard/issues/new?assignees=&labels=feature&template=feature_request.md&title=) - - Any other requests you can reach us at: support@defguard.net - - */ - body: () => LocalizedString - } - } - devicesPage: { - /** - * Network Devices - */ - title: () => LocalizedString - search: { - /** - * Find - */ - placeholder: () => LocalizedString - } - bar: { - /** - * All devices - */ - itemsCount: () => LocalizedString - filters: { - } - actions: { - /** - * Add new - */ - addNewDevice: () => LocalizedString - } - } - list: { - columns: { - labels: { - /** - * Device Name - */ - name: () => LocalizedString - /** - * Location - */ - location: () => LocalizedString - /** - * IP Addresses - */ - assignedIps: () => LocalizedString - /** - * Description - */ - description: () => LocalizedString - /** - * Added By - */ - addedBy: () => LocalizedString - /** - * Add Date - */ - addedAt: () => LocalizedString - /** - * Edit - */ - edit: () => LocalizedString - } - } - edit: { - actionLabels: { - /** - * View config - */ - config: () => LocalizedString - /** - * Generate auth token - */ - generateToken: () => LocalizedString - } - } - } - } - acl: { - messageBoxes: { - aclAliasKind: { - component: { - /** - * Component - */ - name: () => LocalizedString - /** - * combined with manually configured destination fields in ACL - */ - description: () => LocalizedString - } - destination: { - /** - * Destination - */ - name: () => LocalizedString - /** - * translated into a separate set of firewall rules - */ - description: () => LocalizedString - } - } - networkSelectionIndicatorsHelper: { - /** - * - Location access **denied** by default – network traffic not explicitly defined by the rules will be blocked. - - */ - denied: () => LocalizedString - /** - * - Location access **allowed** by default – network traffic not explicitly defined by the rules will be passed. - - */ - allowed: () => LocalizedString - /** - * - Location access unmanaged (ACL disabled) - - */ - unmanaged: () => LocalizedString - } - } - /** - * Access Control List - */ - sharedTitle: () => LocalizedString - fieldsSelectionLabels: { - /** - * All ports - */ - ports: () => LocalizedString - /** - * All protocols - */ - protocols: () => LocalizedString - } - ruleStatus: { - /** - * New - */ - 'new': () => LocalizedString - /** - * Applied - */ - applied: () => LocalizedString - /** - * Pending Change - */ - modified: () => LocalizedString - /** - * Pending Deletion - */ - deleted: () => LocalizedString - /** - * Enable - */ - enable: () => LocalizedString - /** - * Enabled - */ - enabled: () => LocalizedString - /** - * Disable - */ - disable: () => LocalizedString - /** - * Disabled - */ - disabled: () => LocalizedString - /** - * Expired - */ - expired: () => LocalizedString - } - listPage: { - tabs: { - /** - * Rules - */ - rules: () => LocalizedString - /** - * Aliases - */ - aliases: () => LocalizedString - } - message: { - /** - * Change discarded - */ - changeDiscarded: () => LocalizedString - /** - * Pending change added - */ - changeAdded: () => LocalizedString - /** - * Failed to make change - */ - changeFail: () => LocalizedString - /** - * Pending changes applied - */ - applyChanges: () => LocalizedString - /** - * Failed to apply changes - */ - applyFail: () => LocalizedString - } - rules: { - modals: { - applyConfirm: { - /** - * Deploy pending changes - */ - title: () => LocalizedString - /** - * {count} changes will be deployed - */ - subtitle: (arg: { count: number }) => LocalizedString - /** - * Deploy changes - */ - submit: () => LocalizedString - } - filterGroupsModal: { - groupHeaders: { - /** - * Aliases - */ - alias: () => LocalizedString - /** - * Locations - */ - location: () => LocalizedString - /** - * Groups - */ - groups: () => LocalizedString - /** - * Status - */ - status: () => LocalizedString - } - /** - * Save Filter - */ - submit: () => LocalizedString - } - } - listControls: { - /** - * Find name - */ - searchPlaceholder: () => LocalizedString - /** - * Add new - */ - addNew: () => LocalizedString - filter: { - /** - * Filter - */ - nothingApplied: () => LocalizedString - /** - * Filters ({count}) - */ - applied: (arg: { count: number }) => LocalizedString - } - apply: { - /** - * Deploy pending changes - */ - noChanges: () => LocalizedString - /** - * Deploy pending changes ({count}) - */ - all: (arg: { count: number }) => LocalizedString - /** - * Deploy selected changes ({count}) - */ - selective: (arg: { count: number }) => LocalizedString - } - } - list: { - pendingList: { - /** - * Pending Changes - */ - title: () => LocalizedString - /** - * No pending changes - */ - noData: () => LocalizedString - /** - * No pending changes found - */ - noDataSearch: () => LocalizedString - } - deployedList: { - /** - * Deployed Rules - */ - title: () => LocalizedString - /** - * No deployed rules - */ - noData: () => LocalizedString - /** - * No deployed rules found - */ - noDataSearch: () => LocalizedString - } - headers: { - /** - * Rule name - */ - name: () => LocalizedString - /** - * ID - */ - id: () => LocalizedString - /** - * Destination - */ - destination: () => LocalizedString - /** - * Allowed - */ - allowed: () => LocalizedString - /** - * Denied - */ - denied: () => LocalizedString - /** - * Locations - */ - locations: () => LocalizedString - /** - * Status - */ - status: () => LocalizedString - /** - * Edit - */ - edit: () => LocalizedString - } - tags: { - /** - * All - */ - all: () => LocalizedString - /** - * All denied - */ - allDenied: () => LocalizedString - /** - * All allowed - */ - allAllowed: () => LocalizedString - } - editMenu: { - /** - * Discard Changes - */ - discard: () => LocalizedString - /** - * Mark for Deletion - */ - 'delete': () => LocalizedString - } - } - } - aliases: { - message: { - /** - * Pending changes applied - */ - rulesApply: () => LocalizedString - /** - * Failed to apply changes - */ - rulesApplyFail: () => LocalizedString - /** - * Alias deleted - */ - aliasDeleted: () => LocalizedString - /** - * Alias deletion failed - */ - aliasDeleteFail: () => LocalizedString - } - modals: { - applyConfirm: { - /** - * Confirm Alias Deployment - */ - title: () => LocalizedString - /** - * The updated aliases will modify the following rule(s) currently deployed on the gateway. - Please ensure these changes are intended before proceeding. - */ - message: () => LocalizedString - /** - * Affected Rules - */ - listLabel: () => LocalizedString - /** - * Deploy Changes - */ - submit: () => LocalizedString - } - deleteBlock: { - /** - * Deletion blocked - */ - title: () => LocalizedString - /** - * - This alias is currently in use by the following rule(s) and cannot be deleted. To proceed with deletion, you must first remove it from these rules({rulesCount}): - - */ - content: (arg: { rulesCount: number }) => LocalizedString - } - filterGroupsModal: { - groupLabels: { - /** - * Rules - */ - rules: () => LocalizedString - /** - * Status - */ - status: () => LocalizedString - } - } - create: { - labels: { - /** - * Alias name - */ - name: () => LocalizedString - /** - * Alias kind - */ - kind: () => LocalizedString - /** - * IPv4/6 CIDR range address - */ - ip: () => LocalizedString - /** - * Ports or Port Ranges - */ - ports: () => LocalizedString - /** - * Protocols - */ - protocols: () => LocalizedString - } - placeholders: { - /** - * All Protocols - */ - protocols: () => LocalizedString - /** - * All Ports - */ - ports: () => LocalizedString - /** - * All IP addresses - */ - ip: () => LocalizedString - } - kindOptions: { - /** - * Destination - */ - destination: () => LocalizedString - /** - * Component - */ - component: () => LocalizedString - } - controls: { - /** - * Cancel - */ - cancel: () => LocalizedString - /** - * Edit Alias - */ - edit: () => LocalizedString - /** - * Create Alias - */ - create: () => LocalizedString - } - messages: { - /** - * Alias modified - */ - modified: () => LocalizedString - /** - * Alias created - */ - created: () => LocalizedString - } - } - } - listControls: { - /** - * Find name - */ - searchPlaceholder: () => LocalizedString - /** - * Add new - */ - addNew: () => LocalizedString - filter: { - /** - * Filter - */ - nothingApplied: () => LocalizedString - /** - * Filters ({count}) - */ - applied: (arg: { count: number }) => LocalizedString - } - apply: { - /** - * Deploy pending changes - */ - noChanges: () => LocalizedString - /** - * Deploy pending changes ({count}) - */ - all: (arg: { count: number }) => LocalizedString - /** - * Deploy selected changes ({count}) - */ - selective: (arg: { count: number }) => LocalizedString - } - } - list: { - pendingList: { - /** - * Pending Changes - */ - title: () => LocalizedString - /** - * No pending changes - */ - noData: () => LocalizedString - /** - * No pending changes found - */ - noDataSearch: () => LocalizedString - } - deployedList: { - /** - * Deployed Aliases - */ - title: () => LocalizedString - /** - * No deployed aliases - */ - noData: () => LocalizedString - /** - * No deployed aliases found - */ - noDataSearch: () => LocalizedString - } - headers: { - /** - * ID - */ - id: () => LocalizedString - /** - * Alias name - */ - name: () => LocalizedString - /** - * Alias kind - */ - kind: () => LocalizedString - /** - * IPv4/6 CIDR range address - */ - ip: () => LocalizedString - /** - * Ports - */ - ports: () => LocalizedString - /** - * Protocols - */ - protocols: () => LocalizedString - /** - * Status - */ - status: () => LocalizedString - /** - * Edit - */ - edit: () => LocalizedString - /** - * Rules - */ - rules: () => LocalizedString - } - status: { - /** - * Applied - */ - applied: () => LocalizedString - /** - * Modified - */ - changed: () => LocalizedString - } - tags: { - /** - * All denied - */ - allDenied: () => LocalizedString - /** - * All allowed - */ - allAllowed: () => LocalizedString - } - editMenu: { - /** - * Discard changes - */ - discardChanges: () => LocalizedString - /** - * Delete alias - */ - 'delete': () => LocalizedString - } - } - } - } - createPage: { - formError: { - /** - * Conflicting members - */ - allowDenyConflict: () => LocalizedString - /** - * Must configure some allowed users, groups or devices - */ - allowNotConfigured: () => LocalizedString - } - infoBox: { - /** - * - Specify one or more fields (Users, Groups or Devices) to define this rule. The rule will consider all inputs provided for matching conditions. Leave any fields blank if not needed. - */ - allowInstructions: () => LocalizedString - /** - * - Specify one or more fields (IP Addresses or Ports) to define this rule. The rule will consider all inputs provided for matching conditions. Leave any fields blank if not needed. - */ - destinationInstructions: () => LocalizedString - } - message: { - /** - * Rule created and added to pending changes. - */ - create: () => LocalizedString - /** - * Rule creation failed - */ - createFail: () => LocalizedString - } - headers: { - /** - * Rule - */ - rule: () => LocalizedString - /** - * Create Rule - */ - createRule: () => LocalizedString - /** - * Allowed Users/Groups/Devices - */ - allowed: () => LocalizedString - /** - * Denied Users/Groups/Devices - */ - denied: () => LocalizedString - /** - * Destination - */ - destination: () => LocalizedString - } - labels: { - /** - * Rule name - */ - name: () => LocalizedString - /** - * Priority - */ - priority: () => LocalizedString - /** - * Status - */ - status: () => LocalizedString - /** - * Locations - */ - locations: () => LocalizedString - /** - * Allow all users - */ - allowAllUsers: () => LocalizedString - /** - * Include all locations - */ - allowAllNetworks: () => LocalizedString - /** - * Allow all network devices - */ - allowAllNetworkDevices: () => LocalizedString - /** - * Deny all users - */ - denyAllUsers: () => LocalizedString - /** - * Deny all network devices - */ - denyAllNetworkDevices: () => LocalizedString - /** - * Users - */ - users: () => LocalizedString - /** - * Groups - */ - groups: () => LocalizedString - /** - * Network devices - */ - devices: () => LocalizedString - /** - * Protocols - */ - protocols: () => LocalizedString - /** - * IPv4/6 CIDR range or address - */ - manualIp: () => LocalizedString - /** - * Ports - */ - ports: () => LocalizedString - /** - * Aliases - */ - aliases: () => LocalizedString - /** - * Expiration Date - */ - expires: () => LocalizedString - /** - * Manual Input - */ - manualInput: () => LocalizedString - } - placeholders: { - /** - * All protocols - */ - allProtocols: () => LocalizedString - /** - * All IP addresses - */ - allIps: () => LocalizedString - } - } - } - activity: { - /** - * Activity log - */ - title: () => LocalizedString - modals: { - timeRange: { - /** - * Activity time - */ - title: () => LocalizedString - } - } - list: { - /** - * All activity - */ - allLabel: () => LocalizedString - headers: { - /** - * Date - */ - date: () => LocalizedString - /** - * User - */ - user: () => LocalizedString - /** - * IP - */ - ip: () => LocalizedString - /** - * Location - */ - location: () => LocalizedString - /** - * Event - */ - event: () => LocalizedString - /** - * Module - */ - module: () => LocalizedString - /** - * Device - */ - device: () => LocalizedString - /** - * Description - */ - description: () => LocalizedString - } - noData: { - /** - * No activities present - */ - data: () => LocalizedString - /** - * No activities found - */ - search: () => LocalizedString - } - } - } - enums: { - activityLogEventType: { - /** - * User login - */ - user_login: () => LocalizedString - /** - * User login failed - */ - user_login_failed: () => LocalizedString - /** - * User MFA login - */ - user_mfa_login: () => LocalizedString - /** - * User MFA login failed - */ - user_mfa_login_failed: () => LocalizedString - /** - * Recovery code used - */ - recovery_code_used: () => LocalizedString - /** - * User logout - */ - user_logout: () => LocalizedString - /** - * User added - */ - user_added: () => LocalizedString - /** - * User removed - */ - user_removed: () => LocalizedString - /** - * User modified - */ - user_modified: () => LocalizedString - /** - * User groups modified - */ - user_groups_modified: () => LocalizedString - /** - * MFA enabled - */ - mfa_enabled: () => LocalizedString - /** - * MFA disabled - */ - mfa_disabled: () => LocalizedString - /** - * User MFA disabled - */ - user_mfa_disabled: () => LocalizedString - /** - * MFA TOTP enabled - */ - mfa_totp_enabled: () => LocalizedString - /** - * MFA TOTP disabled - */ - mfa_totp_disabled: () => LocalizedString - /** - * MFA email enabled - */ - mfa_email_enabled: () => LocalizedString - /** - * MFA email disabled - */ - mfa_email_disabled: () => LocalizedString - /** - * MFA security key added - */ - mfa_security_key_added: () => LocalizedString - /** - * MFA security key removed - */ - mfa_security_key_removed: () => LocalizedString - /** - * Device added - */ - device_added: () => LocalizedString - /** - * Device removed - */ - device_removed: () => LocalizedString - /** - * Device modified - */ - device_modified: () => LocalizedString - /** - * Network device added - */ - network_device_added: () => LocalizedString - /** - * Network device removed - */ - network_device_removed: () => LocalizedString - /** - * Network device modified - */ - network_device_modified: () => LocalizedString - /** - * Activity log stream created - */ - activity_log_stream_created: () => LocalizedString - /** - * Activity log stream modified - */ - activity_log_stream_modified: () => LocalizedString - /** - * Activity log stream removed - */ - activity_log_stream_removed: () => LocalizedString - /** - * VPN client connected - */ - vpn_client_connected: () => LocalizedString - /** - * VPN client disconnected - */ - vpn_client_disconnected: () => LocalizedString - /** - * VPN client connected to MFA location - */ - vpn_client_connected_mfa: () => LocalizedString - /** - * VPN client disconnected from MFA location - */ - vpn_client_disconnected_mfa: () => LocalizedString - /** - * VPN client failed MFA authentication - */ - vpn_client_mfa_failed: () => LocalizedString - /** - * Enrollment token added - */ - enrollment_token_added: () => LocalizedString - /** - * Enrollment started - */ - enrollment_started: () => LocalizedString - /** - * Device added - */ - enrollment_device_added: () => LocalizedString - /** - * Enrollment completed - */ - enrollment_completed: () => LocalizedString - /** - * Password reset requested - */ - password_reset_requested: () => LocalizedString - /** - * Password reset started - */ - password_reset_started: () => LocalizedString - /** - * Password reset completed - */ - password_reset_completed: () => LocalizedString - /** - * VPN location added - */ - vpn_location_added: () => LocalizedString - /** - * VPN location removed - */ - vpn_location_removed: () => LocalizedString - /** - * VPN location modified - */ - vpn_location_modified: () => LocalizedString - /** - * API token added - */ - api_token_added: () => LocalizedString - /** - * API token removed - */ - api_token_removed: () => LocalizedString - /** - * API token renamed - */ - api_token_renamed: () => LocalizedString - /** - * OpenID app added - */ - open_id_app_added: () => LocalizedString - /** - * OpenID app removed - */ - open_id_app_removed: () => LocalizedString - /** - * OpenID app modified - */ - open_id_app_modified: () => LocalizedString - /** - * OpenID app state changed - */ - open_id_app_state_changed: () => LocalizedString - /** - * OpenID provider removed - */ - open_id_provider_removed: () => LocalizedString - /** - * OpenID provider modified - */ - open_id_provider_modified: () => LocalizedString - /** - * Settings updated - */ - settings_updated: () => LocalizedString - /** - * Settings partially updated - */ - settings_updated_partial: () => LocalizedString - /** - * Default branding restored - */ - settings_default_branding_restored: () => LocalizedString - /** - * Groups bulk assigned - */ - groups_bulk_assigned: () => LocalizedString - /** - * Group added - */ - group_added: () => LocalizedString - /** - * Group modified - */ - group_modified: () => LocalizedString - /** - * Group removed - */ - group_removed: () => LocalizedString - /** - * Group member added - */ - group_member_added: () => LocalizedString - /** - * Group member removed - */ - group_member_removed: () => LocalizedString - /** - * Group members modified - */ - group_members_modified: () => LocalizedString - /** - * Webhook added - */ - web_hook_added: () => LocalizedString - /** - * Webhook modified - */ - web_hook_modified: () => LocalizedString - /** - * Webhook removed - */ - web_hook_removed: () => LocalizedString - /** - * Webhook state changed - */ - web_hook_state_changed: () => LocalizedString - /** - * Authentication key added - */ - authentication_key_added: () => LocalizedString - /** - * Authentication key removed - */ - authentication_key_removed: () => LocalizedString - /** - * Authentication key renamed - */ - authentication_key_renamed: () => LocalizedString - /** - * Password changed - */ - password_changed: () => LocalizedString - /** - * Password changed by admin - */ - password_changed_by_admin: () => LocalizedString - /** - * Password reset - */ - password_reset: () => LocalizedString - /** - * Client configuration token added - */ - client_configuration_token_added: () => LocalizedString - /** - * User SNAT binding added - */ - user_snat_binding_added: () => LocalizedString - /** - * User SNAT binding modified - */ - user_snat_binding_modified: () => LocalizedString - /** - * User SNAT binding removed - */ - user_snat_binding_removed: () => LocalizedString - } - activityLogModule: { - /** - * Defguard - */ - defguard: () => LocalizedString - /** - * Client - */ - client: () => LocalizedString - /** - * Enrollment - */ - enrollment: () => LocalizedString - /** - * VPN - */ - vpn: () => LocalizedString - } - } -} - -export type Formatters = {} diff --git a/web/src/i18n/i18n-util.async.ts b/web/src/i18n/i18n-util.async.ts deleted file mode 100644 index 25819ad3e..000000000 --- a/web/src/i18n/i18n-util.async.ts +++ /dev/null @@ -1,28 +0,0 @@ -// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten. -/* eslint-disable */ - -import { initFormatters } from './formatters' -import type { Locales, Translations } from './i18n-types' -import { loadedFormatters, loadedLocales, locales } from './i18n-util' - -const localeTranslationLoaders = { - en: () => import('./en'), - ko: () => import('./ko'), - pl: () => import('./pl'), -} - -const updateDictionary = (locale: Locales, dictionary: Partial): Translations => - loadedLocales[locale] = { ...loadedLocales[locale], ...dictionary } - -export const importLocaleAsync = async (locale: Locales): Promise => - (await localeTranslationLoaders[locale]()).default as unknown as Translations - -export const loadLocaleAsync = async (locale: Locales): Promise => { - updateDictionary(locale, await importLocaleAsync(locale)) - loadFormatters(locale) -} - -export const loadAllLocalesAsync = (): Promise => Promise.all(locales.map(loadLocaleAsync)) - -export const loadFormatters = (locale: Locales): void => - void (loadedFormatters[locale] = initFormatters(locale)) diff --git a/web/src/i18n/i18n-util.sync.ts b/web/src/i18n/i18n-util.sync.ts deleted file mode 100644 index e3a5b51c6..000000000 --- a/web/src/i18n/i18n-util.sync.ts +++ /dev/null @@ -1,28 +0,0 @@ -// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten. -/* eslint-disable */ - -import { initFormatters } from './formatters' -import type { Locales, Translations } from './i18n-types' -import { loadedFormatters, loadedLocales, locales } from './i18n-util' - -import en from './en' -import ko from './ko' -import pl from './pl' - -const localeTranslations = { - en, - ko, - pl, -} - -export const loadLocale = (locale: Locales): void => { - if (loadedLocales[locale]) return - - loadedLocales[locale] = localeTranslations[locale] as unknown as Translations - loadFormatters(locale) -} - -export const loadAllLocales = (): void => locales.forEach(loadLocale) - -export const loadFormatters = (locale: Locales): void => - void (loadedFormatters[locale] = initFormatters(locale)) diff --git a/web/src/i18n/i18n-util.ts b/web/src/i18n/i18n-util.ts deleted file mode 100644 index 65ae07091..000000000 --- a/web/src/i18n/i18n-util.ts +++ /dev/null @@ -1,39 +0,0 @@ -// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten. -/* eslint-disable */ - -import { i18n as initI18n, i18nObject as initI18nObject, i18nString as initI18nString } from 'typesafe-i18n' -import type { LocaleDetector } from 'typesafe-i18n/detectors' -import type { LocaleTranslationFunctions, TranslateByString } from 'typesafe-i18n' -import { detectLocale as detectLocaleFn } from 'typesafe-i18n/detectors' -import { initExtendDictionary } from 'typesafe-i18n/utils' -import type { Formatters, Locales, Translations, TranslationFunctions } from './i18n-types' - -export const baseLocale: Locales = 'en' - -export const locales: Locales[] = [ - 'en', - 'ko', - 'pl' -] - -export const isLocale = (locale: string): locale is Locales => locales.includes(locale as Locales) - -export const loadedLocales: Record = {} as Record - -export const loadedFormatters: Record = {} as Record - -export const extendDictionary = initExtendDictionary() - -export const i18nString = (locale: Locales): TranslateByString => initI18nString(locale, loadedFormatters[locale]) - -export const i18nObject = (locale: Locales): TranslationFunctions => - initI18nObject( - locale, - loadedLocales[locale], - loadedFormatters[locale] - ) - -export const i18n = (): LocaleTranslationFunctions => - initI18n(loadedLocales, loadedFormatters) - -export const detectLocale = (...detectors: LocaleDetector[]): Locales => detectLocaleFn(baseLocale, locales, ...detectors) diff --git a/web/src/i18n/ko/index.ts b/web/src/i18n/ko/index.ts deleted file mode 100644 index 62ee06aff..000000000 --- a/web/src/i18n/ko/index.ts +++ /dev/null @@ -1,1725 +0,0 @@ -import { deepmerge } from 'deepmerge-ts'; -import { PartialDeep } from 'type-fest'; - -import en from '../en'; -import { Translation } from '../i18n-types'; - -const translation: PartialDeep = { - common: { - conditions: { - or: '또는', - and: '그리고', - equal: '같음', - }, - controls: { - next: '다음', - back: '뒤로', - cancel: '취소', - confirm: '확인', - submit: '제출', - close: '닫기', - select: '선택', - finish: '완료', - saveChanges: '변경 사항 저장', - save: '저장', - RestoreDefault: '기본값 복원', - delete: '삭제', - rename: '이름 변경', - copy: '복사', - edit: '편집', - }, - key: '키', - name: '이름', - }, - messages: { - error: '오류가 발생했습니다.', - success: '작업이 성공했습니다', - errorVersion: '애플리케이션 버전을 가져오지 못했습니다.', - insecureContext: '컨텍스트가 안전하지 않습니다.', - details: '상세내용:', - clipboard: { - error: '클립보드에 액세스할 수 없습니다.', - success: '클립보드에 복사되었습니다.', - }, - }, - modals: { - addGroup: { - title: '그룹 추가', - selectAll: '모든 사용자 선택', - groupName: '그룹 이름', - searchPlaceholder: '필터/검색', - submit: '그룹 생성', - }, - editGroup: { - title: '그룹 편집', - selectAll: '모든 사용자 선택', - groupName: '그룹 이름', - searchPlaceholder: '필터/검색', - submit: '그룹 업데이트', - }, - deleteGroup: { - title: '{name} 그룹 삭제', - subTitle: '이 작업은 이 그룹을 영구적으로 삭제합니다.', - locationListHeader: '이 그룹은 현재 다음 VPN 위치에 할당되어 있습니다:', - locationListFooter: `이것이 주어진 위치에 허용된 유일한 그룹인 경우, 해당 위치는 모든 사용자가 액세스할 수 있게 됩니다.`, - submit: '그룹 삭제', - cancel: '취소', - }, - deviceConfig: { - title: '장치 VPN 구성', - }, - changePasswordSelf: { - title: '비밀번호 변경', - messages: { - success: '비밀번호가 변경되었습니다', - error: '비밀번호 변경에 실패했습니다', - }, - form: { - labels: { - newPassword: '새 비밀번호', - oldPassword: '현재 비밀번호', - repeat: '새 비밀번호 확인', - }, - }, - controls: { - submit: '비밀번호 변경', - cancel: '취소', - }, - }, - startEnrollment: { - title: '등록 시작', - desktopTitle: '데스크톱 활성화', - messages: { - success: '사용자 등록이 시작되었습니다', - successDesktop: '데스크톱 구성이 시작되었습니다', - error: '사용자 등록을 시작하지 못했습니다', - errorDesktop: '데스크톱 활성화를 시작하지 못했습니다', - }, - form: { - email: { - label: '이메일', - }, - mode: { - options: { - email: '이메일로 토큰 보내기', - manual: '직접 토큰 전달', - }, - }, - submit: '등록 시작', - submitDesktop: '데스크톱 활성화', - smtpDisabled: - '이메일로 토큰을 보내려면 SMTP를 구성하십시오. 설정 -> SMTP로 이동하십시오.', - }, - tokenCard: { - title: '활성화 토큰', - }, - urlCard: { - title: 'Defguard 인스턴스 URL', - }, - }, - deleteNetwork: { - title: '{name} 위치 삭제', - subTitle: '이 작업은 이 위치를 영구적으로 삭제합니다.', - submit: '위치 삭제', - cancel: '취소', - }, - changeWebhook: { - messages: { - success: 'Webhook이 변경되었습니다.', - }, - }, - manageWebAuthNKeys: { - title: '보안 키', - messages: { - deleted: 'WebAuthN 키가 삭제되었습니다.', - duplicateKeyError: '키가 이미 등록되어 있습니다', - }, - infoMessage: ` -

- 보안 키는 인증 코드 대신 2단계 인증으로 사용될 수 있습니다. - - 보안 키 구성에 대해 자세히 알아보세요. -

-`, - form: { - messages: { - success: '보안 키가 추가되었습니다.', - }, - fields: { - name: { - label: '새 키 이름', - }, - }, - controls: { - submit: '새 키 추가', - }, - }, - }, - recoveryCodes: { - title: '복구 코드', - submit: '코드를 저장했습니다', - messages: { - copied: '코드가 복사되었습니다.', - }, - infoMessage: ` -

- 복구 코드는 비밀번호와 동일한 수준의 주의를 기울여 취급하십시오! - - Lastpass, bitwarden 또는 Keeper와 같은 비밀번호 관리자를 사용하여 저장하는 것을 권장합니다. -

-`, - }, - registerTOTP: { - title: 'Authenticator 앱 설정', - infoMessage: ` -

- MFA를 설정하려면, 이 QR 코드를 인증 앱으로 스캔한 다음, - 아래 필드에 코드를 입력하세요: -

-`, - messages: { - totpCopied: 'TOTP 경로가 복사되었습니다.', - success: 'TOTP가 활성화되었습니다', - }, - copyPath: 'TOTP 경로 복사', - form: { - fields: { - code: { - label: 'Authenticator 코드', - error: '코드가 유효하지 않습니다', - }, - }, - controls: { - submit: '코드 확인', - }, - }, - }, - registerEmailMFA: { - title: '이메일 MFA 설정', - infoMessage: ` -

- MFA를 설정하려면 계정 이메일: {email}로 전송된 코드를 입력하세요 -

-`, - messages: { - success: '이메일 MFA가 활성화되었습니다', - resend: '인증 코드가 재전송되었습니다', - }, - form: { - fields: { - code: { - label: '이메일 코드', - error: '코드가 유효하지 않습니다', - }, - }, - controls: { - submit: '코드 확인', - resend: '이메일 재전송', - }, - }, - }, - editDevice: { - title: '장치 편집', - messages: { - success: '장치가 업데이트되었습니다.', - }, - form: { - fields: { - name: { - label: '장치 이름', - }, - publicKey: { - label: '장치 공개 키 (WireGuard)', - }, - }, - controls: { - submit: '장치 편집', - }, - }, - }, - deleteDevice: { - title: '장치 삭제', - message: '{deviceName} 장치를 삭제하시겠습니까?', - submit: '장치 삭제', - messages: { - success: '장치가 삭제되었습니다.', - }, - }, - keyDetails: { - title: 'YubiKey 세부 정보', - downloadAll: '모든 키 다운로드', - }, - deleteUser: { - title: '계정 삭제', - controls: { - submit: '계정 삭제', - }, - message: '{username} 계정을 영구적으로 삭제하시겠습니까?', - messages: { - success: '{username}이(가) 삭제되었습니다.', - }, - }, - disableUser: { - title: '계정 비활성화', - controls: { - submit: '계정 비활성화', - }, - message: '{username} 계정을 비활성화하시겠습니까?', - messages: { - success: '{username}이(가) 비활성화되었습니다.', - }, - }, - enableUser: { - title: '계정 활성화', - controls: { - submit: '계정 활성화', - }, - message: '{username} 계정을 활성화하시겠습니까?', - messages: { - success: '{username}이(가) 활성화되었습니다.', - }, - }, - deleteProvisioner: { - title: '프로비저너 삭제', - controls: { - submit: '프로비저너 삭제', - }, - message: '{id} 프로비저너를 삭제하시겠습니까?', - messages: { - success: '{provisioner}이(가) 삭제되었습니다.', - }, - }, - changeUserPassword: { - messages: { - success: '비밀번호가 변경되었습니다.', - }, - title: '사용자 비밀번호 변경', - form: { - controls: { - submit: '새 비밀번호 저장', - }, - fields: { - newPassword: { - label: '새 비밀번호', - }, - confirmPassword: { - label: '비밀번호 다시 입력', - }, - }, - }, - }, - provisionKeys: { - title: 'Yubikey 프로비저닝:', - warning: '이 작업은 yubikey의 openpgp 애플리케이션을 삭제하고 재구성합니다.', - infoBox: `선택한 프로비저너에는 프로비저닝할 깨끗한 YubiKey가 - 연결되어 있어야 합니다. 사용된 YubiKey를 청소하려면 프로비저닝하기 전에 - gpg --card-edit 를 실행하십시오.`, - selectionLabel: '다음 프로비저너 중 하나를 선택하여 YubiKey를 프로비저닝하십시오:', - noData: { - workers: '작업자를 찾을 수 없습니다. 대기 중...', - }, - controls: { - submit: 'YubiKey 프로비저닝', - }, - messages: { - success: '키가 프로비저닝되었습니다', - errorStatus: '작업자 상태를 가져오는 중 오류가 발생했습니다.', - }, - }, - addUser: { - title: '새 사용자 추가', - messages: { - userAdded: '사용자가 추가되었습니다', - }, - form: { - submit: '사용자 추가', - fields: { - username: { - placeholder: '로그인', - label: '로그인', - }, - password: { - placeholder: '비밀번호', - label: '비밀번호', - }, - email: { - placeholder: '사용자 이메일', - label: '사용자 이메일', - }, - firstName: { - placeholder: '이름', - label: '이름', - }, - lastName: { - placeholder: '성', - label: '성', - }, - phone: { - placeholder: '전화번호', - label: '전화번호', - }, - enableEnrollment: { - label: '등록 프로세스 사용', - link: '자세한 정보는 여기를 참고하세요', - }, - }, - }, - }, - webhookModal: { - title: { - addWebhook: '웹훅 추가.', - editWebhook: '웹훅 편집', - }, - messages: { - clientIdCopy: '클라이언트 ID가 복사되었습니다.', - clientSecretCopy: '클라이언트 암호가 복사되었습니다.', - }, - form: { - triggers: '트리거 이벤트:', - messages: { - successAdd: '웹훅이 생성되었습니다.', - successModify: '웹훅이 수정되었습니다.', - }, - error: { - urlRequired: 'URL이 필요합니다.', - validUrl: '유효한 URL이어야 합니다.', - scopeValidation: '최소 하나의 트리거가 있어야 합니다.', - tokenRequired: '토큰이 필요합니다.', - }, - fields: { - description: { - label: '설명', - placeholder: '새 사용자 생성 시 gmail 계정을 생성하는 웹훅', - }, - token: { - label: '비밀 토큰', - placeholder: '인증 토큰', - }, - url: { - label: '웹훅 URL', - placeholder: 'https://example.com/webhook', - }, - userCreated: { - label: '새 사용자 생성됨', - }, - userDeleted: { - label: '사용자 삭제됨', - }, - userModified: { - label: '사용자 수정됨', - }, - hwkeyProvision: { - label: '사용자 Yubikey 프로비저닝', - }, - }, - }, - }, - deleteWebhook: { - title: '웹훅 삭제', - message: '{name} 웹훅을 삭제하시겠습니까?', - submit: '삭제', - messages: { - success: '웹훅이 삭제되었습니다.', - }, - }, - }, - addDevicePage: { - title: '장치 추가', - helpers: { - setupOpt: `이 마법사를 사용하여 장치를 추가할 수 있습니다. 당사의 기본 애플리케이션인 "defguard" 또는 다른 WireGuard 클라이언트를 선택하세요. 잘 모르시겠다면 간편하게 defguard를 사용하는 것을 권장합니다.`, - client: `defguard 데스크톱 클라이언트는 여기에서 다운로드하고 이 가이드를 따르세요.`, - }, - messages: { - deviceAdded: '장치가 추가되었습니다', - }, - steps: { - configDevice: { - title: '장치 구성', - messages: { - copyConfig: '구성이 클립보드에 복사되었습니다', - }, - helpers: { - warningAutoMode: ` -

- 개인 키를 저장하지 않으므로 - 지금 구성을 다운로드해야 합니다. - 이 페이지가 닫히면 전체 구성 파일(개인 키 포함, 빈 템플릿만)을 - 가져올 수 없습니다. -

-`, - warningManualMode: ` -

- 여기에 제공된 구성에는 개인 키가 포함되어 있지 않으며 공개 키를 사용하여 채워져 있습니다. 구성이 제대로 작동하려면 직접 교체해야 합니다. -

-`, - warningNoNetworks: '액세스할 수 있는 네트워크가 없습니다.', - qrHelper: ` -

- 이 QR 코드를 스캔하여 wireguard 애플리케이션으로 장치를 더 빠르게 설정할 수 있습니다. -

`, - }, - qrInfo: - '아래 제공된 구성 파일을 QR 코드를 스캔하거나 장치의 WireGuard 인스턴스에 파일로 가져와서 사용하세요.', - inputNameLabel: '장치 이름', - qrLabel: 'WireGuard 구성 파일', - }, - setupDevice: { - title: 'VPN 장치 생성', - infoMessage: ` -

- 장치에서 WireGuardVPN을 구성해야 합니다. 방법을 모르는 경우  - 문서를 참조하세요. -

-`, - options: { - auto: '키 쌍 생성', - manual: '내 공개 키 사용', - }, - form: { - fields: { - name: { - label: '장치 이름', - }, - publicKey: { - label: '공개 키 제공', - }, - }, - errors: { - name: { - duplicatedName: '이 이름을 가진 장치가 이미 존재합니다', - }, - }, - }, - }, - copyToken: { - title: '클라이언트 활성화', - tokenCardTitle: '활성화 토큰', - urlCardTitle: 'Defguard 인스턴스 URL', - }, - }, - }, - userPage: { - title: { - view: '사용자 프로필', - edit: '사용자 프로필 편집', - }, - messages: { - editSuccess: '사용자가 업데이트되었습니다.', - failedToFetchUserData: '사용자 정보를 가져올 수 없습니다.', - passwordResetEmailSent: '비밀번호 재설정 이메일이 전송되었습니다.', - }, - userDetails: { - header: '프로필 세부 정보', - messages: { - deleteApp: '앱 및 모든 토큰이 삭제되었습니다.', - }, - warningModals: { - title: '경고', - content: { - usernameChange: `사용자 이름을 변경하면 Defguard를 사용하여 로그인한 서비스에 큰 영향을 미칩니다. 사용자 이름을 변경하면 사용자가 애플리케이션에 대한 액세스 권한을 잃을 수 있습니다(애플리케이션에서 해당 사용자를 인식하지 못하기 때문에). 계속 진행하시겠습니까?`, - emailChange: `외부 OpenID Connect(OIDC) 공급자를 사용하여 사용자를 인증하는 경우 사용자의 이메일 주소를 변경하면 Defguard에 로그인하는 기능에 큰 영향을 미칠 수 있습니다. 계속 진행하시겠습니까?`, - }, - buttons: { - proceed: '진행', - cancel: '취소', - }, - }, - fields: { - username: { - label: '사용자 이름', - }, - firstName: { - label: '이름', - }, - lastName: { - label: '성', - }, - phone: { - label: '전화번호', - }, - email: { - label: '이메일', - }, - status: { - label: '상태', - active: '활성', - disabled: '비활성', - }, - groups: { - label: '사용자 그룹', - noData: '그룹 없음', - }, - apps: { - label: '승인된 앱', - noData: '승인된 앱 없음', - }, - }, - }, - userAuthInfo: { - header: '비밀번호 및 인증', - password: { - header: '비밀번호 설정', - changePassword: '비밀번호 변경', - }, - recovery: { - header: '복구 옵션', - codes: { - label: '복구 코드', - viewed: '조회됨', - }, - }, - mfa: { - header: '이중 인증 방법', - edit: { - disable: 'MFA 비활성화', - }, - messages: { - mfaDisabled: 'MFA가 비활성화되었습니다.', - OTPDisabled: '일회용 비밀번호가 비활성화되었습니다.', - EmailMFADisabled: '이메일 MFA가 비활성화되었습니다.', - changeMFAMethod: 'MFA 방법이 변경되었습니다', - }, - securityKey: { - singular: '보안 키', - plural: '보안 키', - }, - default: '기본값', - enabled: '활성화됨', - disabled: '비활성화됨', - labels: { - totp: '시간 기반 일회용 비밀번호', - email: '이메일', - webauth: '보안 키', - }, - editMode: { - enable: '활성화', - disable: '비활성화', - makeDefault: '기본값으로 설정', - webauth: { - manage: '보안 키 관리', - }, - }, - }, - }, - controls: { - editButton: '프로필 편집', - deleteAccount: '계정 삭제', - }, - devices: { - header: '사용자 장치', - addDevice: { - web: '새 장치 추가', - desktop: '이 장치 추가', - }, - card: { - labels: { - publicIP: '공개 IP', - connectedThrough: '연결 방식', - connectionDate: '연결 날짜', - lastLocation: '마지막 연결 위치', - lastConnected: '마지막 연결', - assignedIp: '할당된 IP', - active: '활성', - noData: '연결된 적 없음', - }, - edit: { - edit: '장치 편집', - delete: '장치 삭제', - showConfigurations: '구성 보기', - }, - }, - }, - yubiKey: { - header: '사용자 YubiKey', - provision: 'YubiKey 프로비저닝', - keys: { - pgp: 'PGP 키', - ssh: 'SSH 키', - }, - noLicense: { - moduleName: 'YubiKey 모듈', - line1: 'YubiKey 관리 및 프로비저닝을 위한 엔터프라이즈 모듈입니다.', - line2: '', - }, - }, - authenticationKeys: { - header: '사용자 인증 키', - addKey: '새 키 추가', - keysList: { - common: { - rename: '이름 변경', - key: '키', - download: '다운로드', - copy: '복사', - serialNumber: '시리얼 번호', - delete: '삭제', - }, - }, - deleteModal: { - title: '인증 키 삭제', - confirmMessage: '{name} 키가 영구적으로 삭제됩니다.', - }, - addModal: { - header: '새 인증 키 추가', - keyType: '키 유형', - keyForm: { - placeholders: { - title: '키 이름', - key: { - ssh: 'ssh-rsa, ecdsa-sha2-nistp256, ... 로 시작', - gpg: '-----BEGIN PGP PUBLIC KEY BLOCK----- 로 시작', - }, - }, - labels: { - title: '이름', - key: '키', - }, - submit: '{name} 키 추가', - }, - yubikeyForm: { - selectWorker: { - info: '이 작업은 YubiKey의 openpgp 애플리케이션을 삭제하고 재구성합니다.', - selectLabel: - '다음 프로비저너 중 하나를 선택하여 YubiKey를 프로비저닝하십시오', - noData: '현재 등록된 작업자가 없습니다.', - available: '사용 가능', - unavailable: '사용 불가', - }, - provisioning: { - inProgress: '프로비저닝 진행 중, 잠시 기다려 주세요.', - error: '프로비저닝 실패!', - success: 'Yubikey가 성공적으로 프로비저닝되었습니다', - }, - submit: 'Yubikey 프로비저닝', - }, - messages: { - keyAdded: '키가 추가되었습니다.', - keyExists: '키가 이미 추가되었습니다.', - unsupportedKeyFormat: '지원되지 않는 키 형식입니다.', - genericError: '키를 추가할 수 없습니다. 나중에 다시 시도하십시오.', - }, - }, - }, - }, - usersOverview: { - pageTitle: '사용자', - search: { - placeholder: '사용자 찾기', - }, - filterLabels: { - all: '모든 사용자', - admin: '관리자만', - users: '사용자만', - }, - usersCount: '모든 사용자', - addNewUser: '새 추가', - list: { - headers: { - name: '사용자 이름', - username: '로그인', - phone: '전화', - actions: '작업', - }, - editButton: { - changePassword: '비밀번호 변경', - edit: '계정 편집', - addYubikey: 'YubiKey 추가', - addSSH: 'SSH 키 추가', - addGPG: 'GPG 키 추가', - delete: '계정 삭제', - startEnrollment: '등록 시작', - activateDesktop: '데스크톱 클라이언트 구성', - resetPassword: '비밀번호 재설정', - }, - }, - }, - navigation: { - bar: { - overview: 'VPN 개요', - users: '사용자', - provisioners: 'YubiKeys', - webhooks: 'Webhooks', - openId: 'OpenID 앱', - myProfile: '내 프로필', - settings: '설정', - logOut: '로그아웃', - enrollment: '등록', - support: '지원', - groups: '그룹', - }, - mobileTitles: { - groups: '그룹', - wizard: 'Location 생성', - users: '사용자', - settings: '설정', - user: '사용자 프로필', - provisioners: 'Yubikey', - webhooks: 'Webhooks', - openId: 'OpenId 앱', - overview: '위치 개요', - networkSettings: '위치 편집', - enrollment: '등록', - support: '지원', - }, - copyright: 'Copyright ©2023-2025', - version: { - open: '애플리케이션 버전: {version}', - closed: 'v{version}', - }, - }, - form: { - download: '다운로드', - copy: '복사', - saveChanges: '변경 사항 저장', - submit: '제출', - login: '로그인', - cancel: '취소', - close: '닫기', - placeholders: { - password: '비밀번호', - username: '사용자 이름', - }, - error: { - forbiddenCharacter: '필드에 금지된 문자가 포함되어 있습니다.', - usernameTaken: '사용자 이름이 이미 사용 중입니다.', - invalidKey: '키가 유효하지 않습니다.', - invalid: '필드가 유효하지 않습니다.', - required: '필드는 필수입니다.', - invalidCode: '제출된 코드가 유효하지 않습니다.', - maximumLength: '최대 길이를 초과했습니다.', - minimumLength: '최소 길이에 도달하지 않았습니다.', - noSpecialChars: '특수 문자는 허용되지 않습니다.', - oneDigit: '숫자 하나가 필요합니다.', - oneSpecial: '특수 문자가 필요합니다.', - oneUppercase: '대문자 하나가 필요합니다.', - oneLowercase: '소문자 하나가 필요합니다.', - portMax: '최대 포트는 65535입니다.', - endpoint: '유효한 엔드포인트를 입력하세요.', - address: '유효한 주소를 입력하세요.', - validPort: '유효한 포트를 입력하세요.', - validCode: '코드는 6자리여야 합니다.', - allowedIps: '유효한 IP 또는 도메인만 허용됩니다.', - startFromNumber: '숫자로 시작할 수 없습니다.', - repeat: `필드가 일치하지 않습니다.`, - number: '유효한 숫자를 입력해야 합니다.', - minimumValue: `{value}의 최솟값에 도달하지 않았습니다.`, - maximumValue: '{value}의 최댓값을 초과했습니다.', - tooManyBadLoginAttempts: `잘못된 로그인 시도가 너무 많습니다. 몇 분 후에 다시 시도하십시오.`, - }, - floatingErrors: { - title: '다음을 수정하십시오:', - }, - }, - components: { - deviceConfigsCard: { - cardTitle: '위치에 대한 WireGuard 구성:', - messages: { - copyConfig: '클립보드에 구성이 복사되었습니다.', - }, - }, - gatewaysStatus: { - label: '게이트웨이', - states: { - error: '연결 정보를 가져오는 데 실패했습니다.', - }, - messages: { - error: '게이트웨이 상태를 가져오지 못했습니다', - deleteError: '게이트웨이를 삭제하지 못했습니다', - }, - }, - noLicenseBox: { - footer: { - get: '엔터프라이즈 라이선스 받기', - contact: '연락처:', - }, - }, - }, - settingsPage: { - title: '설정', - tabs: { - smtp: 'SMTP', - global: '전역 설정', - ldap: 'LDAP', - openid: 'OpenID', - enterprise: '엔터프라이즈 기능', - }, - messages: { - editSuccess: '설정이 업데이트되었습니다', - challengeSuccess: '챌린지 메시지가 변경되었습니다', - }, - enterpriseOnly: { - title: '이 기능은 Defguard Enterprise에서만 사용할 수 있습니다.', - subtitle: '자세한 내용은 ', - website: '웹사이트', - }, - ldapSettings: { - title: 'LDAP 설정', - form: { - labels: { - ldap_url: 'URL', - ldap_bind_username: '바인드 사용자 이름', - ldap_bind_password: '바인드 비밀번호', - ldap_member_attr: '멤버 속성', - ldap_username_attr: '사용자 이름 속성', - ldap_user_obj_class: '사용자 객체 클래스', - ldap_user_search_base: '사용자 검색 기준', - ldap_groupname_attr: '그룹 이름 속성', - ldap_group_search_base: '그룹 검색 기준', - ldap_group_member_attr: '그룹 멤버 속성', - ldap_group_obj_class: '그룹 객체 클래스', - }, - delete: '구성 삭제', - }, - test: { - title: 'LDAP 연결 테스트', - submit: '테스트', - messages: { - success: 'LDAP 연결 성공', - error: 'LDAP 연결 거부됨', - }, - }, - }, - openIdSettings: { - general: { - title: '외부 OpenID 설정', - helper: '여기에서 Defguard 인스턴스의 일반 OpenID 동작을 변경할 수 있습니다.', - createAccount: { - label: '외부 OpenID를 통해 처음 로그인할 때 사용자 계정을 자동으로 생성합니다.', - helper: - '이 옵션을 활성화하면 Defguard는 외부 OpenID 공급자를 사용하여 처음 로그인하는 사용자에 대한 새 계정을 자동으로 생성합니다. 그렇지 않으면 관리자가 먼저 사용자 계정을 생성해야 합니다.', - }, - }, - form: { - title: '외부 OpenID 클라이언트 설정', - helper: - '여기에서 외부 OpenID 공급자가 제공한 값으로 OpenID 클라이언트 설정을 구성할 수 있습니다.', - custom: '사용자 정의', - documentation: '설명서', - delete: '공급자 삭제', - labels: { - provider: { - label: '공급자', - helper: - 'OpenID 공급자를 선택하세요. 사용자 정의 공급자를 사용하고 직접 기본 URL을 입력할 수 있습니다.', - }, - client_id: { - label: '클라이언트 ID', - helper: 'OpenID 공급자가 제공한 클라이언트 ID입니다.', - }, - client_secret: { - label: '클라이언트 보안 비밀', - helper: 'OpenID 공급자가 제공한 클라이언트 보안 비밀입니다.', - }, - base_url: { - label: '기본 URL', - helper: - 'OpenID 공급자의 기본 URL입니다(예: https://accounts.google.com). 자세한 정보 및 예는 설명서를 확인하십시오.', - }, - }, - }, - }, - modulesVisibility: { - header: '모듈 가시성', - helper: `

- 사용하지 않는 모듈이 있는 경우 해당 모듈의 가시성을 비활성화할 수 있습니다. -

- - 자세한 내용은 설명서를 참조하십시오. - `, - fields: { - wireguard_enabled: { - label: 'WireGuard VPN', - }, - webhooks_enabled: { - label: '웹훅', - }, - worker_enabled: { - label: 'Yubikey 프로비저닝', - }, - openid_enabled: { - label: 'OpenID Connect', - }, - }, - }, - defaultNetworkSelect: { - header: '기본 위치 보기', - helper: `

여기에서 기본 위치 보기를 변경할 수 있습니다.

- - 자세한 내용은 설명서를 참조하십시오. - `, - filterLabels: { - grid: '그리드 보기', - list: '목록 보기', - }, - }, - instanceBranding: { - header: '인스턴스 브랜딩', - form: { - title: '이름 및 로고:', - fields: { - instanceName: { - label: '인스턴스 이름', - placeholder: 'Defguard', - }, - mainLogoUrl: { - label: '로그인 로고 url', - helper: '

최대 사진 크기는 250x100 px입니다

', - placeholder: '기본 이미지', - }, - navLogoUrl: { - label: '메뉴 및 탐색 작은 로고', - helper: '

최대 사진 크기는 100x100 px입니다

', - placeholder: '기본 이미지', - }, - }, - controls: { - restoreDefault: '기본값 복원', - submit: '변경 사항 저장', - }, - }, - helper: ` -

- 여기에서 defguard 인스턴스의 로고 및 이름 url을 - 추가할 수 있습니다. defguard 대신 표시됩니다. -

- - 자세한 내용은 설명서를 참조하십시오. - - `, - }, - license: { - header: '엔터프라이즈', - helpers: { - enterpriseHeader: { - text: '여기에서 Defguard Enterprise 버전 라이선스를 관리할 수 있습니다.', - link: 'Defguard Enterprise에 대한 자세한 내용은 웹사이트를 방문하십시오.', - }, - licenseKey: { - text: '아래에 Defguard Enterprise 라이선스 키를 입력하세요. 라이선스 구매 후 이메일을 통해 받아야 합니다.', - link: '라이선스는 여기에서 구입할 수 있습니다.', - }, - }, - form: { - title: '라이선스', - fields: { - key: { - label: '라이선스 키', - placeholder: 'Defguard 라이선스 키', - }, - }, - }, - licenseInfo: { - title: '라이선스 정보', - types: { - subscription: { - label: '구독', - helper: '정기적으로 자동 갱신되는 라이선스', - }, - offline: { - label: '오프라인', - helper: '라이선스는 만료 날짜까지 유효하며 자동으로 갱신되지 않습니다', - }, - }, - fields: { - type: { - label: '유형', - }, - validUntil: { - label: '유효 기간', - }, - }, - }, - }, - smtp: { - form: { - title: 'SMTP 구성', - fields: { - encryption: { - label: '암호화', - }, - server: { - label: '서버 주소', - placeholder: '주소', - }, - port: { - label: '서버 포트', - placeholder: '포트', - }, - user: { - label: '서버 사용자 이름', - placeholder: '사용자 이름', - }, - password: { - label: '서버 비밀번호', - placeholder: '비밀번호', - }, - sender: { - label: '보내는 사람 이메일 주소', - placeholder: '주소', - helper: ` -

- 시스템 메시지는 이 주소에서 발송됩니다. - 예: no-reply@my-company.com. -

- `, - }, - }, - controls: { - submit: '변경 사항 저장', - }, - }, - delete: '구성 삭제', - testForm: { - title: '테스트 이메일 보내기', - fields: { - to: { - label: '주소', - placeholder: '주소', - }, - }, - controls: { - submit: '보내기', - success: '테스트 이메일 전송됨', - error: '이메일 전송 오류', - }, - }, - helper: ` -

- 여기에서 사용자에게 시스템 메시지를 보내는 데 사용되는 SMTP 서버를 구성할 수 있습니다. -

- `, - }, - enrollment: { - helper: - '등록은 신규 직원이 새 계정을 활성화 및 비밀번호를 생성하고, VPN 장치를 구성할 수 있도록 하는 프로세스입니다.', - vpnOptionality: { - header: 'VPN 단계 선택 사항', - helper: - '등록 중 VPN 장치 생성을 선택 사항 또는 필수 사항으로 선택할 수 있습니다.', - }, - welcomeMessage: { - header: '환영 메시지', - helper: ` -

이 텍스트 입력란에서는 Markdown을 사용할 수 있습니다:

-
    -
  • 제목은 해시 #로 시작합니다
  • -
  • 별표를 사용하여 *이탤릭체*를 만듭니다
  • -
  • 별표 두 개를 사용하여 **굵게** 만듭니다
  • -
- `, - }, - welcomeEmail: { - header: '환영 이메일', - helper: ` -

이 텍스트 입력란에서는 Markdown을 사용할 수 있습니다:

-
    -
  • 제목은 해시 #로 시작합니다
  • -
  • 별표를 사용하여 *이탤릭체*를 만듭니다
  • -
  • 별표 두 개를 사용하여 **굵게** 만듭니다
  • -
- `, - }, - form: { - controls: { - submit: '변경 사항 저장', - }, - welcomeMessage: { - helper: - '등록이 완료되면 사용자에게 이 정보가 표시됩니다. 관련 링크를 삽입하고 다음 단계를 간략하게 설명하는 것이 좋습니다.', - placeholder: '환영 메시지를 입력하세요', - }, - welcomeEmail: { - helper: - '등록이 완료되면 사용자에게 이 정보가 전송됩니다. 관련 링크를 삽입하고 다음 단계를 간략하게 설명하는 것이 좋습니다. 환영 메시지를 여기에서 다시 사용할 수 있습니다.', - placeholder: '환영 이메일을 입력하세요', - }, - welcomeEmailSubject: { - label: '제목', - }, - useMessageAsEmail: { - label: '환영 메시지와 동일하게', - }, - }, - }, - enterprise: { - header: '엔터프라이즈 기능', - helper: '

여기에서 엔터프라이즈 설정을 변경할 수 있습니다.

', - fields: { - deviceManagement: { - label: '사용자가 자신의 장치를 관리하는 기능 비활성화', - helper: - '이 옵션을 활성화하면 관리자 그룹의 사용자만 사용자 프로필에서 장치를 관리할 수 있습니다(다른 모든 사용자는 비활성화됨)', - }, - manualConfig: { - label: '사용자가 수동 WireGuard 구성을 다운로드하는 기능 비활성화', - helper: - '이 옵션을 활성화하면 사용자에게 수동 클라이언트 설정을 위한 WireGuard 구성이 표시되지 않습니다.', - }, - }, - }, - }, - openidOverview: { - pageTitle: 'OpenID 앱', - search: { - placeholder: '앱 찾기', - }, - filterLabels: { - all: '모든 앱', - enabled: '활성화됨', - disabled: '비활성화됨', - }, - clientCount: '모든 앱', - addNewApp: '새 추가', - list: { - headers: { - name: '이름', - status: '상태', - actions: '작업', - }, - editButton: { - edit: '앱 편집', - delete: '앱 삭제', - disable: '비활성화', - enable: '활성화', - copy: '클라이언트 ID 복사', - }, - status: { - enabled: '활성화됨', - disabled: '비활성화됨', - }, - }, - messages: { - copySuccess: '클라이언트 ID가 복사되었습니다.', - noLicenseMessage: '이 기능에 대한 라이선스가 없습니다.', - noClientsFound: '결과를 찾을 수 없습니다.', - }, - deleteApp: { - title: '앱 삭제', - message: '{appName} 앱을 삭제하시겠습니까?', - submit: '앱 삭제', - messages: { - success: '앱이 삭제되었습니다.', - }, - }, - enableApp: { - messages: { - success: '앱이 활성화되었습니다.', - }, - }, - disableApp: { - messages: { - success: '앱이 비활성화되었습니다.', - }, - }, - modals: { - openidClientModal: { - title: { - addApp: '애플리케이션 추가', - editApp: '{appName} 앱 편집', - }, - scopes: '범위:', - messages: { - clientIdCopy: '클라이언트 ID 복사됨.', - clientSecretCopy: '클라이언트 암호 복사됨.', - }, - form: { - messages: { - successAdd: '앱 생성됨.', - successModify: '앱 수정됨.', - }, - error: { - urlRequired: 'URL이 필요합니다.', - validUrl: '유효한 URL이어야 합니다.', - scopeValidation: '최소 하나의 범위가 있어야 합니다.', - }, - fields: { - name: { - label: '앱 이름', - }, - redirectUri: { - label: '리디렉션 URL {count}', - placeholder: 'https://example.com/redirect', - }, - openid: { - label: 'OpenID', - }, - profile: { - label: '프로필', - }, - email: { - label: '이메일', - }, - phone: { - label: '전화', - }, - groups: { - label: '그룹', - }, - }, - controls: { - addUrl: 'URL 추가', - }, - }, - clientId: '클라이언트 ID', - clientSecret: '클라이언트 암호', - }, - }, - }, - webhooksOverview: { - pageTitle: 'Webhooks', - search: { - placeholder: 'URL로 웹훅 찾기', - }, - filterLabels: { - all: '모든 웹훅', - enabled: '활성화됨', - disabled: '비활성화됨', - }, - webhooksCount: '모든 웹훅', - addNewWebhook: '새 추가', - noWebhooksFound: '웹훅을 찾을 수 없습니다.', - list: { - headers: { - name: '이름', - description: '설명', - status: '상태', - actions: '작업', - }, - editButton: { - edit: '편집', - delete: '웹훅 삭제', - disable: '비활성화', - enable: '활성화', - }, - status: { - enabled: '활성화됨', - disabled: '비활성화됨', - }, - }, - }, - provisionersOverview: { - pageTitle: '프로비저너', - search: { - placeholder: '프로비저너 찾기', - }, - filterLabels: { - all: '전체', - available: '사용 가능', - unavailable: '사용 불가', - }, - provisionersCount: '모든 프로비저너', - noProvisionersFound: '프로비저너를 찾을 수 없습니다.', - noLicenseMessage: '이 기능에 대한 라이선스가 없습니다.', - provisioningStation: { - header: 'YubiKey 프로비저닝 스테이션', - content: `YubiKeys를 프로비저닝하려면 먼저 USB 슬롯이 있는 물리적 시스템을 - 설정해야 합니다. 선택한 시스템에서 제공된 명령을 실행하여 등록하고 - 키 프로비저닝을 시작하세요.`, - dockerCard: { - title: '프로비저닝 스테이션 도커 설정 명령', - }, - tokenCard: { - title: '액세스 토큰', - }, - }, - list: { - headers: { - name: '이름', - ip: 'IP 주소', - status: '상태', - actions: '작업', - }, - editButton: { - delete: '프로비저너 삭제', - }, - status: { - available: '사용 가능', - unavailable: '사용 불가', - }, - }, - messages: { - copy: { - token: '토큰 복사됨', - command: '명령 복사됨', - }, - }, - }, - openidAllow: { - header: '{name}이(가) 다음을 원합니다:', - scopes: { - openid: '향후 로그인을 위해 프로필 데이터를 사용합니다.', - profile: '이름, 프로필 사진 등 프로필의 기본 정보를 알고 있습니다.', - email: '이메일 주소를 알고 있습니다.', - phone: '전화번호를 알고 있습니다.', - groups: '그룹 멤버십을 알고 있습니다.', - }, - controls: { - accept: '수락', - cancel: '취소', - }, - }, - networkOverview: { - pageTitle: '위치 개요', - controls: { - editNetworks: '위치 설정 편집', - selectNetwork: { - placeholder: '위치 로드 중', - }, - }, - filterLabels: { - grid: '그리드 보기', - list: '목록 보기', - }, - stats: { - currentlyActiveUsers: '현재 활성 사용자', - activeUsersFilter: '{hour}시간 내 활성 사용자', - activeDevicesFilter: '{hour}시간 내 활성 장치', - activityIn: '{hour}시간 내 활동', - in: '들어오는 트래픽:', - out: '나가는 트래픽:', - gatewayDisconnected: '게이트웨이 연결 끊김', - }, - }, - connectedUsersOverview: { - pageTitle: '연결된 사용자', - noUsersMessage: '현재 연결된 사용자가 없습니다', - userList: { - username: '사용자 이름', - device: '장치', - connected: '연결됨', - deviceLocation: '장치 위치', - networkUsage: '네트워크 사용량', - }, - }, - networkPage: { - pageTitle: '위치 편집', - addNetwork: '+ 새 위치 추가', - controls: { - networkSelect: { - label: '위치 선택', - }, - }, - }, - activityOverview: { - header: '활동 스트림', - noData: '현재 감지된 활동이 없습니다', - }, - networkConfiguration: { - messages: { - delete: { - success: '네트워크 삭제됨', - error: '네트워크 삭제 실패', - }, - }, - header: '위치 구성', - importHeader: '위치 가져오기', - form: { - helpers: { - address: - '이 주소를 기반으로 VPN 네트워크 주소가 정의됩니다. 예: 10.10.10.1/24 (VPN 네트워크는 10.10.10.0/24가 됩니다)', - gateway: 'VPN 사용자가 연결하는 데 사용되는 게이트웨이 공개 주소', - dns: 'wireguard 인터페이스가 활성화될 때 쿼리할 DNS 확인자를 지정합니다.', - allowedIps: 'VPN 네트워크를 통해 라우팅되어야 하는 주소/마스크 목록입니다.', - allowedGroups: - '기본적으로 모든 사용자가 이 위치에 연결할 수 있습니다. 특정 그룹으로 이 위치에 대한 액세스를 제한하려면 아래에서 선택하십시오.', - }, - messages: { - networkModified: '위치가 수정되었습니다.', - networkCreated: '위치가 생성되었습니다', - }, - fields: { - name: { - label: '위치 이름', - }, - address: { - label: '게이트웨이 VPN IP 주소 및 넷마스크', - }, - endpoint: { - label: '게이트웨이 주소', - }, - allowedIps: { - label: '허용된 IP', - }, - port: { - label: '게이트웨이 포트', - }, - dns: { - label: 'DNS', - }, - allowedGroups: { - label: '허용된 그룹', - placeholder: '모든 그룹', - }, - keepalive_interval: { - label: 'Keepalive 간격 [초]', - }, - peer_disconnect_threshold: { - label: '피어 연결 끊김 임계값 [초]', - }, - acl_enabled: { - label: '이 위치에 대한 ACL 활성화', - }, - acl_default_allow: { - label: '기본 ACL 정책', - }, - }, - controls: { - submit: '변경 사항 저장', - cancel: '개요로 돌아가기', - delete: '위치 제거', - }, - }, - }, - gatewaySetup: { - header: { - main: '게이트웨이 서버 설정', - dockerBasedGatewaySetup: `Docker 기반 게이트웨이 설정`, - fromPackage: `패키지로부터`, - oneLineInstall: `한 줄 설치`, - }, - card: { - title: 'Docker 기반 게이트웨이 설정', - authToken: `인증 토큰`, - }, - button: { - availablePackages: `사용 가능한 패키지`, - }, - controls: { - status: '연결 상태 확인', - }, - messages: { - runCommand: `Defguard는 vpn 서버에서 wireguard VPN을 제어하기 위해 게이트웨이 노드를 배포해야 합니다. - 자세한 내용은 [문서]({setupGatewayDocs})를 참조하십시오. - 게이트웨이 서버를 배포하는 방법에는 여러 가지가 있으며, - 아래는 Docker 기반 예시입니다. 다른 예시는 [문서]({setupGatewayDocs})를 참조하십시오.`, - createNetwork: `게이트웨이 프로세스를 실행하기 전에 네트워크를 생성하십시오.`, - noConnection: `연결이 설정되지 않았습니다. 제공된 명령을 실행하십시오.`, - connected: `게이트웨이가 연결되었습니다.`, - statusError: '게이트웨이 상태를 가져오지 못했습니다', - oneLineInstall: `한 줄 설치를 수행하는 경우: https://docs.defguard.net/getting-started/one-line-install - 아무 것도 할 필요가 없습니다.`, - fromPackage: `https://github.com/DefGuard/gateway/releases/latest에서 사용 가능한 패키지를 설치하고 [문서]({setupGatewayDocs})에 따라 \`/etc/defguard/gateway.toml\`을 구성하십시오. - `, - authToken: `아래 토큰은 게이트웨이 노드를 인증하고 구성하는 데 필요합니다. 이 토큰을 안전하게 보관하고 - [문서]({setupGatewayDocs})에 제공된 배포 지침에 따라 게이트웨이 서버를 성공적으로 설정하십시오. - 자세한 내용 및 정확한 단계는 [문서]({setupGatewayDocs})를 참조하십시오.`, - dockerBasedGatewaySetup: `아래는 Docker 기반 예시입니다. 자세한 내용 및 정확한 단계는 [문서]({setupGatewayDocs})를 참조하십시오.`, - }, - }, - loginPage: { - pageTitle: '자격 증명을 입력하세요', - callback: { - return: '로그인으로 돌아가기', - error: '외부 OpenID 로그인 중 오류가 발생했습니다', - }, - mfa: { - title: '이중 인증', - controls: { - useAuthenticator: '대신 인증 앱 사용', - useWebauthn: '대신 보안 키 사용', - useRecoveryCode: '대신 복구 코드 사용', - useEmail: '대신 이메일 사용', - }, - email: { - header: '이메일로 전송된 코드를 사용하여 진행하십시오.', - form: { - labels: { - code: '코드', - }, - controls: { - resendCode: '코드 재전송', - }, - }, - }, - totp: { - header: '인증 앱의 코드를 사용하고 버튼을 클릭하여 진행하십시오.', - form: { - fields: { - code: { - placeholder: '인증 코드 입력', - }, - }, - controls: { - submit: '인증 코드 사용', - }, - }, - }, - recoveryCode: { - header: '활성 복구 코드 중 하나를 입력하고 버튼을 클릭하여 로그인하십시오.', - form: { - fields: { - code: { - placeholder: '복구 코드', - }, - }, - controls: { - submit: '복구 코드 사용', - }, - }, - }, - webauthn: { - header: '인증할 준비가 되면 아래 버튼을 누르십시오.', - controls: { - submit: '보안 키 사용', - }, - messages: { - error: '키를 읽지 못했습니다. 다시 시도하십시오.', - }, - }, - }, - }, - wizard: { - completed: '위치 설정 완료', - configuration: { - successMessage: '위치 생성됨', - }, - welcome: { - header: '위치 마법사에 오신 것을 환영합니다!', - sub: 'VPN을 사용하기 전에 먼저 위치를 설정해야 합니다. 확실하지 않은 경우 아이콘을 클릭하십시오.', - button: '위치 설정', - }, - navigation: { - top: '위치 설정', - titles: { - welcome: '위치 설정', - choseNetworkSetup: '위치 설정 선택', - importConfig: '기존 위치 가져오기', - manualConfig: '위치 구성', - mapDevices: '가져온 장치 매핑', - }, - buttons: { - next: '다음', - back: '뒤로', - }, - }, - deviceMap: { - messages: { - crateSuccess: '장치 추가됨', - errorsInForm: '표시된 필드를 채워주세요.', - }, - list: { - headers: { - deviceName: '장치 이름', - deviceIP: 'IP', - user: '사용자', - }, - }, - }, - wizardType: { - manual: { - title: '수동 구성', - description: '수동 위치 구성', - }, - import: { - title: '파일에서 가져오기', - description: 'WireGuard 구성 파일에서 가져오기', - }, - createNetwork: '위치 생성', - }, - common: { - select: '선택', - }, - locations: { - form: { - name: '이름', - ip: 'IP 주소', - user: '사용자', - fileName: '파일', - selectFile: '파일 선택', - messages: { devicesCreated: '장치 생성됨' }, - validation: { invalidAddress: '잘못된 주소' }, - }, - }, - }, - layout: { - select: { - addNewOptionDefault: '새 추가 +', - }, - }, - redirectPage: { - title: '로그인되었습니다', - subtitle: '잠시 후 리디렉션됩니다...', - }, - enrollmentPage: { - title: '등록', - controls: { - default: '기본값 복원', - save: '변경 사항 저장', - }, - messages: { - edit: { - success: '설정이 변경되었습니다', - error: '저장 실패', - }, - }, - messageBox: - '등록은 신입 직원이 새 계정을 확인하고, 비밀번호를 생성하고, VPN 장치를 구성할 수 있도록 하는 프로세스입니다. 이 패널에서 관련 메시지를 사용자 지정할 수 있습니다.', - settings: { - welcomeMessage: { - title: '환영 메시지', - messageBox: - '이 정보는 등록이 완료되면 서비스 내 사용자에게 표시됩니다. 링크를 삽입하고 다음 단계를 간략하게 설명하는 것이 좋습니다. 이메일에 있는 것과 동일한 메시지를 사용할 수 있습니다.', - }, - vpnOptionality: { - title: 'VPN 설정 선택 사항', - select: { - options: { - optional: '선택 사항', - mandatory: '필수', - }, - }, - }, - welcomeEmail: { - title: '환영 이메일', - subject: { - label: '이메일 제목', - }, - messageBox: - '등록이 완료되면 사용자에게 이 정보가 전송됩니다. 관련 링크를 삽입하고 다음 단계를 간략하게 설명하는 것이 좋습니다.', - controls: { - duplicateWelcome: '환영 메시지와 동일', - }, - }, - }, - }, - supportPage: { - title: '지원', - modals: { - confirmDataSend: { - title: '지원 데이터 보내기', - subTitle: - '실제로 지원 디버그 정보를 보내려는 것인지 확인하십시오. 개인 정보는 전송되지 않습니다(wireguard 키, 이메일 주소 등은 전송되지 않음).', - submit: '지원 데이터 보내기', - }, - }, - debugDataCard: { - title: '지원 데이터', - body: ` -지원이 필요하거나 저희 팀(예: Matrix 지원 채널: **#defguard-support:teonite.com**)에서 지원 데이터 생성을 요청받은 경우 다음 두 가지 옵션이 있습니다. -* SMTP 설정을 구성하고 "지원 데이터 보내기"를 클릭합니다. -* 또는 "지원 데이터 다운로드"를 클릭하고 이 파일을 첨부하여 GitHub에 버그 보고서를 생성합니다. -`, - downloadSupportData: '지원 데이터 다운로드', - downloadLogs: '로그 다운로드', - sendMail: '지원 데이터 보내기', - mailSent: '이메일 전송됨', - mailError: '이메일 전송 오류', - }, - supportCard: { - title: '지원', - body: ` -GitHub에 문의하거나 문제를 제출하기 전에 [docs.defguard.net](https://docs.defguard.net/)에서 제공되는 Defguard 문서를 숙지하십시오. - -제출하려면: -* 버그 - [GitHub](https://github.com/DefGuard/defguard/issues/new?assignees=&labels=bug&template=bug_report.md&title=)로 이동하십시오. -* 기능 요청 - [GitHub](https://github.com/DefGuard/defguard/issues/new?assignees=&labels=feature&template=feature_request.md&title=)로 이동하십시오. - -기타 요청은 support@defguard.net으로 문의하십시오. -`, - }, - }, -}; - -const ko = deepmerge(en, translation); - -export default ko; diff --git a/web/src/i18n/pl/index.ts b/web/src/i18n/pl/index.ts deleted file mode 100644 index 588a4a60e..000000000 --- a/web/src/i18n/pl/index.ts +++ /dev/null @@ -1,2394 +0,0 @@ -import { deepmerge } from 'deepmerge-ts'; -import { PartialDeep } from 'type-fest'; - -import en from '../en'; -import { Translation } from '../i18n-types'; - -const translation: PartialDeep = { - common: { - conditions: { - and: 'I', - equal: 'Równy', - or: 'Albo', - }, - controls: { - accept: 'Akceptuj', - next: 'Następny', - back: 'Wróć', - cancel: 'Anuluj', - confirm: 'Potwierdź', - submit: 'Wyślij', - close: 'Zamknij', - select: 'Wybierz', - finish: 'Zakończ', - saveChanges: 'Zapisz zmiany', - save: 'Zapisz', - RestoreDefault: 'Przywróć domyślne', - delete: 'Usuń', - rename: 'Zmień nazwę', - copy: 'Skopiuj', - edit: 'Edytuj', - dismiss: 'Odrzuć', - show: 'Pokaż', - enable: 'Włącz', - enabled: 'Włączony', - disable: 'Wyłącz', - disabled: 'Wyłączony', - selectAll: 'Zaznacz wszystko', - clear: 'Wyczyść', - clearAll: 'Wyczyść wszystko', - }, - key: 'Klucz', - name: 'Nazwa', - noData: 'Brak danych', - unavailable: 'Niedostępne', - notSet: 'Nieustawione', - search: 'Szukaj', - }, - messages: { - error: 'Wystąpił błąd.', - success: 'Operacja zakończyła się sukcesem', - errorVersion: 'Nie udało się uzyskać wersji aplikacji.', - details: 'Szczegóły:', - clipboard: { - success: 'Skopiowano do schowka', - error: 'Schowek nie jest dostępny', - }, - insecureContext: 'Kontekst nie jest bezpieczny', - }, - modals: { - upgradeLicenseModal: { - enterprise: { - title: 'Podnieś do Enterprise', - //md - subTitle: `Został przekroczony limit użytkowników, urządzeń lub sieci, a ta funkcjonalność jest dostępna tylko w wersji **enterprise**. Aby użyć tej funkcjonalności, należy zakupić lub podnieść obecną licencję enterprise.`, - }, - limit: { - title: 'Podnieś', - //md - subTitle: ` - **Osiągnięto limit** funkcjonalności. Aby **[ zarządzać większą liczbą lokalizacji/użytkowników/urządzeń ]** wymagany jest zakup licencji Enterprise. - `, - }, - //md - content: ` -Aby dowiedzieć się więcej o: -- Automatyczniej synchronizacji klientów w czasie rzeczywistym -- Zewnętrznym SSO -- Kontrolowaniu działania klientów VPN - -Pełna lista funkcjonalności enterprise: [https://docs.defguard.net/enterprise/enterprise-features](https://docs.defguard.net/enterprise/enterprise-features)
-Informacja o licencjonowaniu: [https://docs.defguard.net/enterprise/license](https://docs.defguard.net/enterprise/license) - `, - controls: { - cancel: 'Może później', - confirm: 'Wszystkie plany Enterprise', - }, - }, - standaloneDeviceEnrollmentModal: { - title: 'Network device token', - toasters: { - error: 'Token generation failed.', - }, - }, - standaloneDeviceConfigModal: { - title: 'Konfiguracja urządzenia sieciowego', - cardTitle: 'Konfiguracja', - toasters: { - getConfig: { - error: 'Nie udało się pobrać konfiguracji urządzenia.', - }, - }, - }, - editStandaloneModal: { - title: 'Edycja urządzenia sieciowego', - toasts: { - success: 'Urządzenia zostało zmienione', - failure: 'Nie udało się zmienić urządzenia.', - }, - }, - deleteStandaloneDevice: { - title: 'Usuń urządzenie sieciowe', - content: 'Urządzenie {name} zostanie usunięte.', - messages: { - success: 'Urządzenie zostało usunięte', - error: 'Nie udało się usunąć urządzenia.', - }, - }, - addStandaloneDevice: { - toasts: { - deviceCreated: 'Urządzenie zostało dodane', - creationFailed: 'Urządzenie nie mogło być dodane.', - }, - infoBox: { - setup: - 'Tu można dodać definicje lub utworzyć konfiguracje dla urządzeń, które można podłączyć do sieci VPN. Dostępne są jedynie lokalizacje bez uwierzytelniania wieloskładnikowego (MFA), ponieważ póki co ta funkcjonalność jest dostępna tylko w kliencie Defguard Desktop.', - }, - form: { - submit: 'Dodaj urządzenie', - labels: { - deviceName: 'Nazwa urządzenia', - location: 'Położenie', - assignedAddress: 'Przydzielony adres IP', - description: 'Opis', - generation: { - auto: 'Utwórz parę kluczy', - manual: 'Własny klucz publiczny', - }, - publicKey: 'Podaj swój klucz publiczny', - }, - }, - steps: { - method: { - title: 'Wybierz preferowaną metodę', - cards: { - cli: { - title: 'Klient Defguard CLI', - subtitle: - 'Używając defguard-cli urządznie zostanie automatycznie skonfigurowane.', - docs: 'Pobieranie i dokumentacja klienta Defguard CLI', - }, - manual: { - title: 'Ręczny klient WireGuard', - subtitle: - 'Jeżeli Twoje urządzenie nie wspiera naszych programów CLI, zawsze można utworzyć plik konfiguracyjny WireGuard i skonfigurowć je ręcznie - ale w takim przypadku uaktualnienia lokalizacji VPN będą wymagały ręcznych zmian w urządzeniu.', - }, - }, - }, - manual: { - title: 'Dodaj nowe urządzenie VPN używając klienta WireGuard', - finish: { - messageTop: - 'Pobierz podany plik konfiguracyjny na urządzeniu i zaimportuj go do klienta VPN żeby zakończyć jego konfigurowanie.', - ctaInstruction: - 'Użyj podanego niżej pliku konfiguracyjnego skanując kod QR lub importując go jako plik w aplikacji WireGuard na urządzeniu.', - // MD - warningMessage: ` - Należy pamiętać, że Defguard **nie przechowuje kluczy prywatnych**. Para kluczy (publiczny i prywatny) zostanie bezpiecznie utworzona w przeglądarce, ale jedynie klucz publiczny zostanie zapisany w bazie danych Defguard. Proszę pobrać utworzoną konfigurację zawierającą klucz prywatny dla urządzenia, gdyż nie będzie ona później dostępna. - `, - actionCard: { - title: 'Konfiguracja', - }, - }, - }, - cli: { - title: 'Dodaj urządzenie używając klienta Defguard CLI', - finish: { - topMessage: - 'Najpierw pobierz klienta Defguard CLI i zainstaluj go na serwerze.', - downloadButton: 'Pobierz klienta Defguard CLI', - commandCopy: 'Skopiuj i wklej to polecenie w terminalu na urządzeniu', - }, - setup: { - stepMessage: - 'Tu można dodać definicje lub utworzyć konfiguracje dla urządzeń, które mogą łączyć się do sieci VPN. Tutaj dostępne są jedynie lokalizacje bez uwierzytelniania wieloskładnikowego (MFA) ponieważ póki co MFA jest wspierane jedynie w kliencie Defguard Desktop.', - form: { - submit: 'Dodaj urządzenie', - }, - }, - }, - }, - }, - updatesNotification: { - header: { - criticalBadge: 'Aktualizacja krytyczna', - newVersion: 'Nowa wersja {version}', - title: 'Aktualizacja dostępna', - }, - controls: { - visitRelease: 'Zobacz stronę aktualizacji', - }, - }, - updatesNotificationToaster: { - title: 'Nowa wersja dostępna {version}', - controls: { - more: 'Zobacz co nowego', - }, - }, - addGroup: { - groupName: 'Nazwa grupy', - searchPlaceholder: 'Szukaj', - selectAll: 'Zaznacz wszystkich', - submit: 'Stwórz grupę', - title: 'Dodaj grupę', - groupSettings: 'Ustawienia grupy', - adminGroup: 'Grupa administratorska', - }, - editGroup: { - groupName: 'Nazwa grupy', - searchPlaceholder: 'Szukaj', - selectAll: 'Zaznacz wszystkich', - submit: 'Zmień grupę', - title: 'Edytuj grupę', - groupSettings: 'Ustawienia grupy', - adminGroup: 'Grupa administratorska', - }, - deleteGroup: { - title: 'Usuń grupę {name}', - subTitle: 'Grupa zostanie nieodwołalnie usunięta.', - locationListHeader: - 'Ta grupa jest obecnie przypisana do następujących lokalizacji:', - locationListFooter: `Jeżeli to jedyna dozwolona grupa dla danej lokalizacji, stanie się ona dostępna dla wszystkich użytkowników.`, - submit: 'Usuń grupę', - cancel: 'Wróć', - }, - registerEmailMFA: { - title: 'Skonfiguruj e-mail MFA', - form: { - controls: { - resend: 'Wyślij kod ponownie', - submit: 'Zweryfikuj kod', - }, - fields: { - code: { - error: 'Podany kod jest nieprawidłowy', - label: 'Kod', - }, - }, - }, - infoMessage: ` -

- Aby zakończyć konfigurację, wpisz kod, który został wysłany na adres: {email} -

- `, - messages: { - resend: 'Kod wysłany ponownie', - success: 'Metoda MFA e-mail włączona', - }, - }, - deviceConfig: { - title: 'Konfiguracje VPN urządzenia', - }, - changePasswordSelf: { - title: 'Zmień hasło', - messages: { - success: 'Hasło zostało zmienione', - error: 'Błąd zmiany hasła', - }, - form: { - labels: { - repeat: 'Powtórz hasło', - newPassword: 'Nowe hasło', - oldPassword: 'Obecne hasło', - }, - }, - controls: { - cancel: 'Wróć', - submit: 'Zmień hasło', - }, - }, - startEnrollment: { - title: 'Rozpocznij rejestrację', - desktopTitle: 'Konfiguracja klienta desktop', - messages: { - success: 'Rejestracja użytkownika rozpoczęta', - successDesktop: 'Konfiguracja klienta rozpoczęta', - errorDesktop: 'Błąd konfiguracji klienta desktop', - error: 'Błąd rejestracji użytkownika', - }, - form: { - email: { - label: 'E-mail', - }, - mode: { - options: { - email: 'Wyślij token przez e-mail', - manual: 'Przekaż token ręcznie', - }, - }, - submit: 'Rozpocznij rejestrację', - submitDesktop: 'Aktywacja desktop', - smtpDisabled: - 'Skonfiguruj SMTP, żeby wysłać token przez e-mail. Przejdź do Ustawienia -> SMTP.', - }, - tokenCard: { - title: 'Token aktywacji', - }, - urlCard: { - title: 'URL instancji Defguard', - }, - }, - deleteNetwork: { - cancel: 'Wróć', - submit: 'Usuń lokalizację', - subTitle: 'Lokalizacja zostanie nieodwołalnie usunięta.', - title: 'Usuń lokalizację {name}', - }, - changeWebhook: { - messages: { - success: 'Webhook zmieniony.', - }, - }, - manageWebAuthNKeys: { - title: 'Klucze bezpieczeństwa', - messages: { - deleted: 'Klucz WebAuthN został usunięty.', - duplicateKeyError: 'Klucz jest już zarejestrowany', - }, - infoMessage: ` -

- Klucze bezpieczeństwa mogą być używane jako drugi czynnik uwierzytelniający - zamiast kodu weryfikacyjnego. Dowiedz się więcej o konfiguracji - klucza bezpieczeństwa. -

-`, - form: { - messages: { - success: 'Klucz bezpieczeństwa dodany.', - }, - fields: { - name: { - label: 'Nazwa nowego klucza', - }, - }, - controls: { - submit: 'Dodaj nowy klucz', - }, - }, - }, - recoveryCodes: { - title: 'Kody odzysku', - submit: 'Zapisałem swoje kody', - messages: { - copied: 'Kody skopiowane.', - }, - infoMessage: ` -

- Traktuj swoje kody odzyskiwania z takim samym poziomem uwagi jak - jak swoje hasło! Zalecamy zapisywanie ich za pomocą menedżera haseł - takich jak Lastpass, bitwarden czy Keeper. -

-`, - }, - registerTOTP: { - title: 'Authenticator App Setup', - infoMessage: ` -

- Aby skonfigurować MFA, zeskanuj ten kod QR za pomocą aplikacji uwierzytelniającej, a następnie - wprowadź kod w polu poniżej: -

-`, - messages: { - totpCopied: 'Ścieżka TOTP skopiowana.', - success: 'TOTP Enabled', - }, - copyPath: 'Kopiuj ścieżkę TOTP', - form: { - fields: { - code: { - label: 'Kod uwierzytelniający', - error: 'Kod jest nieprawidłowy', - }, - }, - controls: { - submit: 'Weryfikuj kod', - }, - }, - }, - editDevice: { - title: 'Edytuj urządzenie', - messages: { - success: 'Urządzenie zostało zaktualizowane.', - }, - form: { - fields: { - name: { - label: 'Nazwa urządzenia', - }, - publicKey: { - label: 'Klucz publiczny urządzenia (WireGuard)', - }, - }, - controls: { - submit: 'Edytuj urządzenie', - }, - }, - }, - deleteDevice: { - title: 'Usuń urządzenie', - message: 'Czy chcesz usunąć urządzenie {deviceName} ?', - submit: 'Usuń urządzenie', - messages: { - success: 'Urządzenie zostało usunięte.', - }, - }, - keyDetails: { - title: 'Szczegóły YubiKey', - downloadAll: 'Pobierz wszystkie klucze', - }, - deleteUser: { - title: 'Usuń użytkownika', - controls: { - submit: 'Usuń użytkownika', - }, - message: 'Czy chcesz trwale usunąć konto {username} ?', - messages: { - success: '{username} usunięte.', - }, - }, - disableUser: { - title: 'Dezaktywuj użytkownika', - controls: { - submit: 'Dezaktywuj użytkownika', - }, - message: 'Czy chcesz dezaktywować użytkownika {username}?', - messages: { - success: 'Użytkownik {username} został dezaktywowany.', - }, - }, - enableUser: { - title: 'Aktywuj użytkownika', - controls: { - submit: 'Aktywuj użytkownika', - }, - message: 'Czy chcesz aktywować użytkownika {username}?', - messages: { - success: 'Użytkownik {username} został aktywowany.', - }, - }, - deleteProvisioner: { - title: 'Usuń provisionera', - controls: { - submit: 'Usuń provisionera', - }, - message: 'Czy chcesz usunąć {id} provisionera?', - messages: { - success: '{provisioner} usunięty.', - }, - }, - changeUserPassword: { - messages: { - success: 'Hasło zmienione.', - }, - title: 'Zmiana hasła użytkownika', - form: { - controls: { - submit: 'Zapisz nowe hasło', - }, - fields: { - newPassword: { - label: 'Nowe hasło', - }, - confirmPassword: { - label: 'Powtórz hasło', - }, - }, - }, - }, - provisionKeys: { - warning: 'Ta operacja bezpowrotnie usunie dane z aplikacji OpenPGP klucza.', - title: 'Provisionowanie klucza YubiKey:', - infoBox: `Wybrany provisioner musi mieć podłączony pusty YubiKey. - Aby zresetować YubiKey uruchom - gpg --card-edit przed generowaniem kluczy.`, - selectionLabel: - 'Wybierz jeden z następujących provisionerów, aby wygenrować klucze na YubiKey:', - noData: { - workers: 'Nie znaleziono workerów...', - }, - controls: { - submit: 'Wygeneruj klucze dla YubiKey', - }, - messages: { - success: 'Klucze zostały przetransferowane na YubiKey', - errorStatus: 'Wystapił błąd podczas pobierania statusu.', - }, - }, - addUser: { - messages: { - userAdded: 'Stworzono użytkownika', - }, - title: 'Dodaj nowego użytkownika', - form: { - submit: 'Dodaj użytkownika', - fields: { - username: { - placeholder: 'login', - label: 'Login', - }, - password: { - placeholder: 'Hasło', - label: 'Hasło', - }, - email: { - placeholder: 'E-mail użytkownika', - label: 'E-mail użytkownika', - }, - firstName: { - placeholder: 'Imię', - label: 'Imię', - }, - lastName: { - placeholder: 'Nazwisko', - label: 'Nazwisko', - }, - phone: { - placeholder: 'Telefon', - label: 'Telefon', - }, - enableEnrollment: { - label: 'Użyj zdalnej rejestracji', - link: 'więcej informacji tutaj', - }, - }, - }, - }, - webhookModal: { - title: { - addWebhook: 'Dodaj webhook', - editWebhook: 'Edytuj webhook', - }, - messages: { - clientIdCopy: 'Skopiowano identyfikator klienta', - clientSecretCopy: 'Sekret klienta skopiowany.', - }, - form: { - triggers: 'Zdarzenia wyzwalające:', - messages: { - successAdd: 'Webhook utworzony.', - successModify: 'Webhook zmodyfikowany.', - }, - error: { - urlRequired: 'URL jest wymagany.', - validUrl: 'Musi być poprawnym adresem URL.', - scopeValidation: 'Musi mieć co najmniej jeden wyzwalacz.', - tokenRequired: 'Token jest wymagany.', - }, - fields: { - description: { - label: 'Opis', - placeholder: 'Webhook do tworzenia konta gmail na nowym użytkowniku', - }, - token: { - label: 'Secret token', - placeholder: 'Token autoryzacyjny', - }, - url: { - label: 'Webhook URL', - placeholder: 'https://example.com/webhook', - }, - userCreated: { - label: 'Stworzenie nowego użytkownika', - }, - userDeleted: { - label: 'Użytkownik usunięty', - }, - userModified: { - label: 'Użytkownik zmodyfikowany', - }, - hwkeyProvision: { - label: 'Stworzenie kluczy na YubiKey dla użytkownika', - }, - }, - }, - }, - deleteWebhook: { - title: 'Usuń webhook', - message: 'Czy chcesz usunąć {name} webhook ?', - submit: 'Usuń', - messages: { - success: 'Webhook usunięty.', - }, - }, - }, - addDevicePage: { - title: 'Dodaj urządzenie', - messages: { - deviceAdded: 'Urządzenie dodane', - }, - helpers: { - setupOpt: `Możesz dodać urządzenie używając naszego klienta lub samemu skonfigurwać urządzenie.`, - client: `Pobierz klienta defguard tutaj, a następnie postępuj zgodnie z instrukcją w celu jego konfiguracji.`, - }, - - steps: { - setupDevice: { - title: 'Dodaj urządzenie', - form: { - errors: { - name: { - duplicatedName: 'Nazwa jest już zajęta', - }, - }, - fields: { - name: { - label: 'Nazwa', - }, - publicKey: { - label: 'Klucz publiczny', - }, - }, - }, - options: { - auto: 'Generuj klucze', - manual: 'Użyj własnych', - }, - infoMessage: `

W razie problemów możesz odwiedzić dokumentacje.

`, - }, - configDevice: { - title: 'Skonfiguruj urządzenie', - messages: { - copyConfig: 'Konfiguracja skopiowa', - }, - qrInfo: - 'Użyj poniższych konfiguracji aby połączyć się z wybranymi lokalizacjami.', - helpers: { - warningNoNetworks: 'Nie posiadasz dostępu do żadnej sieci.', - qrHelper: `

Możesz skonfigurować WireGuard na telefonie skanując QR kod używając aplikacji WireGuard.

`, - warningAutoMode: ` -

Uwaga, Defguard nie przechowuje twojego klucza prywatnego. Gdy opuścisz obecną stronę nie będziesz mógł pobrać ponownie konfiguracji z kluczem prywatnym.

-`, - warningManualMode: `

-Uwaga, podane tutaj konfiguracje nie posiadają klucza prywatnego. Musisz uzupełnić pobraną konfigurację o swój klucz prywatny. -

`, - }, - qrLabel: 'Konfiguracja WireGuard', - inputNameLabel: 'Nazwa urządzenia', - }, - copyToken: { - title: 'Autoryzacja klienta', - urlCardTitle: 'Url', - tokenCardTitle: 'Token', - }, - }, - }, - userPage: { - title: { - view: 'Profil użytkownika', - edit: 'Edycja profilu użytkownika', - }, - messages: { - editSuccess: 'Użytkownik zaktualizowany.', - failedToFetchUserData: 'Błąd pobierania informacji o użytkowniku.', - passwordResetEmailSent: 'E-mail zerowania hasła został wysłany.', - }, - userDetails: { - header: 'Szczegóły profilu', - messages: { - deleteApp: 'Aplikacja i wszystkie tokeny usunięte.', - }, - warningModals: { - title: 'Ostrzeżenie', - content: { - usernameChange: `Zmiana nazwy użytkownika ma znaczący wpływ na usługi, do których użytkownik zalogował się za pomocą Defguard. Po zmianie nazwy użytkownika użytkownik może stracić do nich dostęp (ponieważ nie będą go rozpoznawać). Czy na pewno chcesz kontynuować?`, - emailChange: `Jeśli korzystasz z zewnętrznych dostawców OpenID Connect (OIDC) do uwierzytelniania użytkowników, zmiana adresu e-mail użytkownika może mieć wpływ na jego możliwość zalogowania się do Defguarda. Czy na pewno chcesz kontynuować?`, - }, - buttons: { - proceed: 'Proceed', - cancel: 'Cancel', - }, - }, - fields: { - username: { - label: 'Nazwa użytkownika', - }, - firstName: { - label: 'Imię', - }, - lastName: { - label: 'Nazwisko', - }, - phone: { - label: 'Numer telefonu', - }, - email: { - label: 'E-mail', - }, - status: { - label: 'Status', - active: 'Aktywny', - disabled: 'Nieaktywny', - }, - groups: { - label: 'Grupy użytkowników', - noData: 'Brak grup', - }, - apps: { - label: 'Autoryzowane aplikacje', - noData: 'Brak autoryzowanych aplikacji', - }, - }, - }, - userAuthInfo: { - header: 'Hasło i uwierzytelnienie', - password: { - header: 'Ustawienia hasła', - changePassword: 'Zmiana hasła', - ldap_change_heading: 'Wymagana aktualizacja hasła {ldapName}', - ldap_change_message: - 'Defguard nie ma możliwości odczytania twojego hasła, więc nie możemy go pobrać do automatycznej synchronizacji z danymi logowania {ldapName}. Aby umożliwić logowanie do innych usług za pomocą {ldapName}, zaktualizuj swoje hasło Defguard, aby jednocześnie ustawić hasło {ldapName} — możesz ponownie wpisać swoje obecne hasło, jeśli chcesz. Ten krok jest konieczny, aby zapewnić spójną i bezpieczną autoryzację w obu systemach.', - }, - recovery: { - header: 'Opcje odzyskiwania danych', - codes: { - label: 'Kody odzyskiwania', - viewed: 'Obejrzane', - }, - }, - mfa: { - header: 'Metody dwuskładnikowe', - edit: { - disable: 'Wyłącz MFA', - }, - messages: { - mfaDisabled: 'MFA wyłączone.', - OTPDisabled: 'Hasło jednorazowe wyłączone.', - changeMFAMethod: 'Metoda MFA zmieniona.', - EmailMFADisabled: 'Metoda e-mail wyłączona.', - }, - securityKey: { - singular: 'klucz bezpieczeństwa', - plural: 'klucze bezpieczeństwa', - }, - default: 'domyślny', - enabled: 'Włączony', - disabled: 'Wyłączony', - labels: { - totp: 'Hasła jednorazowe oparte na czasie', - webauth: 'Klucze bezpieczeństwa', - email: 'E-mail', - }, - editMode: { - enable: 'Włącz', - disable: 'Wyłącz', - makeDefault: 'Uczyń domyślnym', - webauth: { - manage: 'Zarządzaj kluczami bezpieczeństwa', - }, - }, - }, - }, - controls: { - editButton: 'Edytuj profil', - deleteAccount: 'Usuń konto', - }, - devices: { - header: 'Urządzenia użytkownika', - addDevice: { - web: 'Dodaj nowe urządzenie', - desktop: 'Dodaj to urządzenie', - }, - card: { - labels: { - noData: 'Nie połączono', - connectedThrough: 'Połączone przez', - publicIP: 'Publiczny adres IP', - connectionDate: 'Data połączenia', - lastLocation: 'Ostatnie połączenie z', - active: 'aktywne', - assignedIp: 'Przydzielony adres IP', - lastConnected: 'Ostatnio połączone', - }, - edit: { - edit: 'Edycja urządzenia', - delete: 'Usuń urządzenie', - showConfigurations: 'Pokaż konfiguracje', - }, - }, - }, - yubiKey: { - header: 'YubiKey użytkownika', - provision: 'Sprovisionuj YubiKey', - keys: { - pgp: 'Klucz PGP', - ssh: 'Klucz SSH', - }, - noLicense: { - moduleName: 'Moduł YubiKey', - line1: 'To jest płatny moduł dla YubiKey', - line2: 'zarządzanie i provisioning.', - }, - }, - authenticationKeys: { - header: 'Klucze autoryzacyjne użytkownika', - addKey: 'Dodaj nowy klucz', - keysList: { - common: { - copy: 'Skopiuj', - delete: 'Usuń', - download: 'Pobierz', - key: 'Klucz', - rename: 'Zmień nazwę', - serialNumber: 'Numer seryjny', - }, - }, - deleteModal: { - confirmMessage: 'Klucz {name} zostanie trwale usunięty.', - title: 'Usuń klucz autoryzacyjny', - }, - addModal: { - header: 'Dodaj nowy klucz autoryzacyjny', - keyType: 'Typ Klucza', - keyForm: { - labels: { - key: 'Klucz', - title: 'Nazwa', - }, - placeholders: { - title: 'Nazwa Klucza', - key: { - ssh: 'Rozpoczyna się z ‘ssh-rsa’, ‘ecdsa-sha2-nistp256’, ...', - gpg: 'Rozpoczyna się z ‘-----BEGIN PGP PUBLIC KEY BLOCK-----‘', - }, - }, - submit: 'Dodaj klucz {name}', - }, - messages: { - keyAdded: 'Klucz dodany.', - keyExists: 'Klucz już został dodany.', - unsupportedKeyFormat: 'Format klucza nie jest wspierany.', - genericError: 'Nie udało się dodać klucza. Proszę spróbować ponownie później.', - }, - yubikeyForm: { - selectWorker: { - info: 'Ta operacja wyzeruje moduł GPG do ustawień fabrycznych po czym ponownie go skonfiguruje. Ta operacja jest nieodwracalna.', - selectLabel: 'Wybierz jedną stację do konfiguracji klucza.', - noData: 'Obecnie nie ma dostępnych stacji.', - available: 'Dostępny', - unavailable: 'Niedostępny', - }, - provisioning: { - inProgress: 'Klucz jest konfigurowany, proszę czekać.', - error: 'Konfiguracja klucza zakończyła się niepowodzeniem.', - success: 'Klucz skonfigurowany pomyślnie.', - }, - submit: 'Skonfiguruj klucz', - }, - }, - }, - apiTokens: { - header: 'API Tokeny użytkownika', - addToken: 'Dodaj nowy API Token', - tokensList: { - common: { - rename: 'Zmień nazwę', - token: 'Token', - copy: 'Skopiuj', - delete: 'Usuń', - createdAt: 'Utworzono', - }, - }, - deleteModal: { - title: 'Usuń API Token', - confirmMessage: 'API token {name} zostanie trwale usunięty.', - }, - addModal: { - header: 'Dodaj nowy API Token', - tokenForm: { - placeholders: { - name: 'Nazwa API Tokena', - }, - labels: { - name: 'Nazwa', - }, - submit: 'Dodaj API token', - }, - copyToken: { - warningMessage: - 'Skopiuj poniższy API token teraz. Nie będzie on dostępny w późniejszym czasie.', - header: 'Skopiuj nowy API Token', - }, - messages: { - tokenAdded: 'API token dodany.', - genericError: 'Nie udało się dodać API tokena. Spróbuj ponownie później.', - }, - }, - }, - }, - usersOverview: { - pageTitle: 'Użytkownicy', - search: { - placeholder: 'Znajdź użytkowników', - }, - filterLabels: { - all: 'Wszyscy użytkownicy', - admin: 'Tylko administratorzy', - users: 'Tylko użytkownicy', - }, - usersCount: 'Wszyscy użytkownicy', - addNewUser: 'Dodaj użytkownika', - list: { - headers: { - name: 'Nazwa użytkownika', - username: 'Login', - phone: 'Telefon', - actions: 'Akcje', - }, - editButton: { - activateDesktop: 'Aktywacja klienta desktop', - changePassword: 'Zmień hasło', - edit: 'Edytuj konto', - delete: 'Usuń konto', - startEnrollment: 'Rozpocznij rejestrację', - resetPassword: 'Resetuj hasło', - addGPG: 'Dodaj klucz GPG', - addSSH: 'Dodaj klucz SSH', - addYubikey: 'Dodaj YubiKey', - }, - }, - }, - navigation: { - bar: { - overview: 'Przegląd sieci', - users: 'Użytkownicy', - provisioners: 'YubiKey Provisioners', - webhooks: 'Webhooki', - openId: 'Aplikacje OpenID', - myProfile: 'Mój profil', - settings: 'Ustawienia', - logOut: 'Wyloguj się', - enrollment: 'Rejestracja', - support: 'Wsparcie', - groups: 'Grupy', - devices: 'Urządzenia sieciowe', - acl: 'Kontrola dostępu', - }, - mobileTitles: { - wizard: 'Konfiguracja VPN', - users: 'Użytkownicy', - settings: 'Ustawienia globalne Defguard', - user: 'Profil użytkownika', - provisioners: 'YubiKey Provisioners', - webhooks: 'Webhooki', - openId: 'Aplikacje OpenID', - overview: 'Przegląd lokalizacji', - networkSettings: 'Edycja lokalizacji', - enrollment: 'Rejestracja', - support: 'Wsparcie', - groups: 'Grupy', - devices: 'Urządzenia sieciowe', - }, - copyright: 'Copyright ©2023-2025', - version: { - open: 'Wersja aplikacji: {version}', - closed: 'v{version}', - }, - }, - form: { - download: 'Pobierz', - copy: 'Kopiuj', - saveChanges: 'Zapisz zmiany', - submit: 'Zapisz', - login: 'Zaloguj się', - cancel: 'Anuluj', - close: 'Zamknij', - placeholders: { - password: 'Hasło', - username: 'Nazwa użytkownika', - }, - error: { - invalidCode: 'Podany kod jest niewłaściwy.', - forbiddenCharacter: 'Pole zawiera niedozwolone znaki.', - usernameTaken: 'Nazwa użytkownika jest już w użyciu.', - invalidKey: 'Klucz jest nieprawidłowy.', - invalid: 'Pole jest nieprawidłowe.', - required: 'Pole jest wymagane.', - maximumLength: 'Maksymalna długość przekroczona.', - minimumLength: 'Minimalna długość nie została osiągnięta.', - noSpecialChars: 'Nie wolno używać znaków specjalnych.', - oneDigit: 'Wymagana jedna cyfra.', - oneSpecial: 'Wymagany jest znak specjalny.', - oneUppercase: 'Wymagana jedna duża litera.', - oneLowercase: 'Wymagana jedna mała litera.', - portMax: 'Maksymalny numer portu to 65535.', - endpoint: 'Wpisz poprawny adres.', - address: 'Wprowadź poprawny adres.', - addressNetmask: 'Wprowadź poprawny adres IP oraz maskę sieci.', - validPort: 'Wprowadź prawidłowy port.', - validCode: 'Kod powinien mieć 6 cyfr.', - allowedIps: 'Tylko poprawne adresy IP oraz domeny.', - startFromNumber: 'Nie może zaczynać się od liczby.', - repeat: 'Wartości się nie pokrywają.', - maximumValue: 'Maksymalna wartość {value} przekroczona.', - minimumValue: 'Minimalna wartość {value} nie osiągnięta.', - tooManyBadLoginAttempts: - 'Zbyt duża ilość nieprawidłowego logowania. Spróbuj ponownie za kilka minut.', - number: 'Wartość musi być liczbą.', - }, - floatingErrors: { - title: 'Popraw następujące błędy:', - }, - }, - components: { - deviceConfigsCard: { - cardTitle: 'Konfiguracja lokalizacji', - messages: { - copyConfig: 'Konfiguracja skopiowana', - }, - }, - gatewaysStatus: { - label: 'Gateways', - states: { - error: 'Błąd pobierania statusu', - }, - messages: { - error: 'Błąd pobierania statusu połączeń gateway', - deleteError: 'Błąd usuwania gateway', - }, - }, - noLicenseBox: { - footer: { - get: 'Uzyskaj licencję enterprise', - contact: 'poprzez kontakt:', - }, - }, - }, - settingsPage: { - title: 'Ustawienia', - tabs: { - smtp: 'SMTP', - global: 'Globalne', - ldap: 'LDAP', - openid: 'OpenID', - enterprise: 'Funkcjonalności enterprise', - }, - messages: { - editSuccess: 'Ustawienia zaktualizowane.', - challengeSuccess: 'Zmieniono wiadomość do podpisu.', - }, - enterpriseOnly: { - title: 'Ta funkcja jest dostępna tylko w wersji Defguard Enterprise', - currentExpired: 'Twoja obecna licencja wygasła.', - subtitle: 'Aby uzyskać więcej informacji, odwiedź naszą ', - website: 'stronę internetową', - }, - ldapSettings: { - title: 'Ustawienia LDAP', - sync: { - header: 'Obustronna synchronizacja LDAP', - info: 'Przed włączeniem synchronizacji, zapoznaj się z [dokumentacją](https://docs.defguard.net/enterprise/all-enteprise-features).', - }, - form: { - labels: { - ldap_enable: 'Włącz integrację z LDAP', - ldap_url: 'URL', - ldap_bind_username: 'Bind Username', - ldap_bind_password: 'Bind Password', - ldap_member_attr: 'Member Attribute', - ldap_username_attr: 'Username Attribute', - ldap_user_obj_class: 'User Object Class', - ldap_user_search_base: 'User Search Base', - ldap_user_auxiliary_obj_classes: 'Additional User Object Classes', - ldap_groupname_attr: 'Groupname Attribute', - ldap_group_search_base: 'Group Search Base', - ldap_group_member_attr: 'Group Member Attribute', - ldap_group_obj_class: 'Group Object Class', - ldap_sync_enabled: 'Włącz synchronizację w dwie strony', - ldap_authoritative_source: 'Użyj autorytatywne źródło danych', - ldap_sync_interval: 'Interwał synchronizacji', - ldap_use_starttls: 'Użyj StartTLS', - ldap_tls_verify_cert: 'Sprawdzaj certyfikat TLS', - ldap_uses_ad: 'Serwer LDAP jest serwerem Active Directory', - }, - delete: 'Usuń konfigurację', - }, - test: { - title: 'Test połączenia LDAP', - messages: { - error: 'Brak połączenia', - success: 'Połączono z LDAP', - }, - submit: 'Test', - }, - }, - openIdSettings: { - heading: 'Ustawienia zewnętrznego OpenID', - general: { - title: 'Ogólne ustawienia', - helper: - 'Możesz tu zmienić ogólną mechanikę działania zewnętrznego OpenID w twojej instancji Defguarda.', - createAccount: { - label: - 'Automatycznie twórz konta w momencie logowania przez zewnętrznego dostawcę OpenID', - helper: - 'Jeśli ta opcja jest włączona, Defguard automatycznie tworzy nowe konta dla użytkowników, którzy logują się po raz pierwszy za pomocą zewnętrznego dostawcy OpenID. W innym przypadku konto użytkownika musi zostać najpierw utworzone przez administratora.', - }, - useOpenIdForMfa: { - label: 'Używaj zewnętrznego OpenID dla MFA klienta', - helper: - 'Gdy zewnętrzny proces Multi-Factor Authentication (MFA) OpenID SSO jest włączony, użytkownicy łączący się z lokalizacjami VPN wymagającymi MFA będą musieli uwierzytelniać się przez swoją przeglądarkę używając skonfigurowanego dostawcy dla każdego połączenia. Jeśli to ustawienie jest wyłączone, MFA dla tych lokalizacji VPN będzie obsługiwane przez wewnętrzny system SSO Defguard. W takim przypadku użytkownicy muszą mieć skonfigurowane TOTP lub MFA oparte na e-mailu.', - }, - usernameHandling: { - label: 'Obsługa nazw użytkowników', - helper: - 'Skonfiguruj metodę obsługi nieprawidłowych znaków w nazwach użytkowników twojego dostawcy tożsamości.', - options: { - remove: 'Usuń niedozwolone znaki', - replace: 'Zamień niedozwolone znaki', - prune_email: 'Przytnij adres e-mail', - }, - }, - }, - form: { - title: 'Ustawienia klienta', - helper: - 'Tutaj możesz skonfigurować ustawienia klienta OpenID z wartościami dostarczonymi przez zewnętrznego dostawcę OpenID.', - custom: 'Niestandardowy', - none: 'Brak', - documentation: - 'Przeczytaj więcej o tej funkcji w naszej [dokumentacji](https://docs.defguard.net/enterprise/enterprise-features).', - delete: 'Usuń dostawcę', - directory_sync_settings: { - title: 'Ustawienia synchronizacji katalogu', - helper: - 'Synchronizacja katalogu pozwala na automatyczną synchronizację grup użytkowników i ich statusu na podstawie zewnętrznego dostawcy.', - notSupported: 'Synchronizacja katalogu nie jest obsługiwana dla tego dostawcy.', - connectionTest: { - success: 'Połączenie zakończone sukcesem.', - error: 'Wystąpił błąd podczas próby połączenia:', - }, - }, - selects: { - synchronize: { - all: 'Wszystko', - users: 'Użytkownicy', - groups: 'Grupy', - }, - behavior: { - keep: 'Zachowaj', - disable: 'Dezaktywuj', - delete: 'Usuń', - }, - }, - labels: { - provider: { - label: 'Dostawca', - helper: - 'Wybierz swojego dostawcę OpenID. Możesz użyć dostawcy niestandardowego i samodzielnie wypełnić pole URL bazowego.', - }, - client_id: { - label: 'ID klienta', - helper: 'ID klienta dostarczone przez dostawcę OpenID.', - }, - client_secret: { - label: 'Sekret klienta', - helper: 'Sekret klienta dostarczony przez dostawcę OpenID.', - }, - base_url: { - label: 'URL bazowy', - helper: - 'Podstawowy adres URL twojego dostawcy OpenID, np. https://accounts.google.com. Sprawdź naszą dokumentację, aby uzyskać więcej informacji i zobaczyć przykłady.', - }, - display_name: { - label: 'Wyświetlana nazwa', - helper: - 'Nazwa dostawcy OpenID, która będzie wyświetlana na przycisku logowania. Jeśli zostawisz to pole puste, przycisk będzie miał tekst "Zaloguj przez OIDC".', - }, - enable_directory_sync: { - label: 'Włącz synchronizację katalogu', - }, - sync_target: { - label: 'Synchronizuj', - helper: - 'Co będzie synchronizowane z zewnętrznym dostawcą OpenID. Możesz wybrać pomiędzy synchronizacją statusu użytkowników, ich przynależności do grup lub synchronizacją obu.', - }, - sync_interval: { - label: 'Interwał synchronizacji', - helper: 'Odstęp czasu w sekundach pomiędzy synchronizacjami katalogu.', - }, - user_behavior: { - label: 'Zachowanie kont użytkowników', - helper: - 'Wybierz jak postępować z kontami użytkowników, które nie znajdują się w katalogu zewnętrznego dostawcy. Możesz wybrać między zachowaniem ich, dezaktywacją lub całkowitym usunięciem.', - }, - admin_behavior: { - label: 'Zachowanie kont administratorów', - helper: - 'Wybierz, jak postępować z kontami administratorów Defguard, które nie znajdują się w katalogu zewnętrznego dostawcy. Możesz wybrać między zachowaniem ich, dezaktywacją lub całkowitym usunięciem.', - }, - admin_email: { - label: 'E-mail administratora', - helper: - 'Adres e-mail konta, za pośrednictwem którego będzię odbywać się synchronizacja, np. e-mail konta osoby, która skonfigurowała konto usługi Google. Więcej szczegółów możesz znaleźć w naszej dokumentacji.', - }, - service_account_used: { - label: 'Używane konto usługi', - helper: - 'Obecnie używane konto usługi Google do synchronizacji. Możesz je zmienić, przesyłając nowy plik klucza konta usługi.', - }, - service_account_key_file: { - label: 'Plik klucza konta usługi', - helper: - 'Prześlij nowy plik klucza konta usługi, aby ustawić konto usługi używane do synchronizacji. UWAGA: Przesłany plik nie będzie widoczny po zapisaniu ustawień i ponownym załadowaniu strony, ponieważ jego zawartość jest poufna i nie jest przesyłana z powrotem do panelu.', - uploaded: 'Przesłany plik', - uploadPrompt: 'Prześlij plik klucza konta usługi', - }, - okta_client_id: { - label: 'ID klienta synchronizacji Okta', - helper: 'ID klienta dla aplikacji synchronizacji Okta.', - }, - okta_client_key: { - label: 'Klucz prywatny klienta synchronizacji Okta', - helper: - 'Klucz prywatny dla aplikacji synchronizacji Okta w formacie JWK. Klucz nie jest wyświetlany ponownie po wgraniu.', - }, - jumpcloud_api_key: { - label: 'Klucz API JumpCloud', - helper: - 'Klucz API JumpCloud używany do synchronizacji stanu użytkowników i grup.', - }, - group_match: { - label: 'Synchronizuj tylko pasujące grupy', - helper: - 'Podaj listę nazw grup oddzielonych przecinkami, które powinny być synchronizowane. Jeśli pole zostanie puste, wszystkie grupy dostawcy zostaną zsynchronizowane.', - }, - }, - }, - }, - modulesVisibility: { - header: 'Widoczność modułów', - helper: `

- Jeśli nie używasz niektórych modułów, możesz zmienić ich widoczność -

- - Przeczytaj więcej w dokumentacji. - `, - fields: { - wireguard_enabled: { - label: 'WireGuard VPN', - }, - webhooks_enabled: { - label: 'Webhooks', - }, - worker_enabled: { - label: 'YubiBridge', - }, - openid_enabled: { - label: 'OpenID connect', - }, - }, - }, - defaultNetworkSelect: { - header: 'Domyślny widok sieci', - helper: `

Tutaj możesz zmienić domyślny widok sieci.

- - Przeczytaj więcej w dokumentacji. - `, - filterLabels: { - grid: 'Widok siatki', - list: 'Widok listy', - }, - }, - instanceBranding: { - header: 'Brandowanie instancji', - form: { - title: 'Nazwa i logo', - fields: { - instanceName: { - label: 'Nazwa instancji', - placeholder: 'Defguard', - }, - mainLogoUrl: { - label: 'URL logo na stronie logowania', - helper: 'Maksymalna wielkość zdjęcia to 250x100 px.', - placeholder: 'Domyślny obrazek', - }, - navLogoUrl: { - label: 'Menu i nawigacja - małe logo', - helper: 'Maksymalna wielkość zdjęcia to 100x100 px.', - placeholder: 'Domyślny obrazek', - }, - }, - controls: { - restoreDefault: 'Przywróć domyślne', - submit: 'Zapisz zmiany', - }, - }, - helper: ` -

- Tutaj możesz dodać URL swojego logo i nazwę dla swojej instancji defguard; - będzie ona wyświetlana zamiast defguard. -

- - Przeczytaj więcej w dokumentacji. - - `, - }, - license: { - header: 'Funkcje enterprise', - helpers: { - enterpriseHeader: { - text: 'Tutaj możesz zarządzać swoją licencją Defguard Enterprise.', - link: 'By dowiedzieć się więcej, odwiedź naszą stronę.', - }, - licenseKey: { - text: 'Wprowadź poniżej klucz licencyjny Defguard Enterprise. Powinieneś otrzymać go na swoją skrzynkę e-mailową po zakupie licencji.', - link: 'Licencję możesz zakupić tutaj.', - }, - }, - form: { - title: 'Licencja', - fields: { - key: { - label: 'Klucz licencji', - placeholder: 'Klucz licencji dla twojej instancji Defguard', - }, - }, - }, - licenseInfo: { - title: 'Informacje o licencji', - licenseNotRequired: - "

Posiadasz dostęp do tej funkcji enterprise, ponieważ nie przekroczyłeś jeszcze żadnych limitów. Sprawdź dokumentację, aby uzyskać więcej informacji.

", - types: { - subscription: { - label: 'Subskrypcja', - helper: 'Subskrypcja automatycznie odnawiana cyklicznie', - }, - offline: { - label: 'Offline', - helper: 'Licencja ważna do daty wygaśnięcia, odnawiana ręcznie', - }, - }, - fields: { - status: { - label: 'Status', - active: 'Aktywna', - expired: 'Wygasła', - subscriptionHelper: - 'Licencja w formie subskrypcji jest ważna przez pewien czas po dacie wygaśnięcia, by uwzględnić możliwe opóźnienia w automatycznej płatności.', - }, - type: { - label: 'Typ', - }, - validUntil: { - label: 'Ważna do', - }, - }, - }, - }, - smtp: { - form: { - title: 'Ustawienia', - sections: { - server: 'Ustawienia serwera', - }, - fields: { - server: { - label: 'Adres serwera', - placeholder: 'Adres', - }, - port: { - label: 'Port', - placeholder: 'Port', - }, - encryption: { - label: 'Szyfrowanie', - }, - user: { - label: 'Użytkownik', - placeholder: 'Użytkownik', - }, - password: { - label: 'Hasło', - placeholder: 'Hasło', - }, - sender: { - label: 'Adres wysyłającego', - placeholder: 'Adres', - helper: ` -

- Systemowe wiadomości będą wysyłane z tego adresu, np. no-reply@my-company.com. -

- `, - }, - }, - controls: { - submit: 'Zapisz zmiany', - }, - }, - delete: 'Usuń konfigurację', - testForm: { - title: 'Wyślij testowy e-mail', - subtitle: 'Wprowadź adres e-mail odbiorcy', - fields: { - to: { - label: 'Adres odbiorcy', - placeholder: 'Adres', - }, - }, - controls: { - submit: 'Wyślij', - resend: 'Wyślij ponownie', - retry: 'Spróbuj ponownie', - success: 'E-mail wysłany pomyślnie', - error: 'Błąd wysyłania e-maila', - }, - }, - helper: - 'Skonfiguruj serwer SMTP do wysyłania wiadomości systemowych do użytkowników.', - }, - enrollment: { - helper: - 'Rejestracja to proces, w ramach którego nowy użytkownik może samodzielnie aktywować swoje konto, ustawić hasło i skonfigurować urządzenie VPN.', - vpnOptionality: { - header: 'Opcjonalność kroku VPN', - helper: - 'Możesz zdecydować czy dodawanie urządzenia VPN jest obowiązkowym czy opcjonalnym krokiem rejestracji', - }, - welcomeMessage: { - header: 'Wiadomość powitalna', - helper: ` -

W tym polu możesz używać Markdown:

-
    -
  • Nagłówki zaczynają się od #
  • -
  • Użyj asterysków aby uzyskać *kursywę*
  • -
  • Użyj dwóch asterysków aby uzyskać **pogrubienie**
  • -
- `, - }, - welcomeEmail: { - header: 'E-mail powitalny', - helper: ` -

W tym polu możesz używać Markdown:

-
    -
  • Nagłówki zaczynają się od #
  • -
  • Użyj asterysków aby uzyskać *kursywę*
  • -
  • Użyj dwóch asterysków aby uzyskać **pogrubienie**
  • -
- `, - }, - form: { - controls: { - submit: 'Zapisz zmiany', - }, - welcomeMessage: { - helper: - 'Ta wiadomość będzie pokazywana użytkownikom po zakończeniu rejestracji. Sugerujemy wymienienie w niej istotnych linków oraz krótkie wyjaśnienie kolejnych kroków.', - placeholder: 'Wpisz wiadomość powitalną', - }, - welcomeEmail: { - helper: - 'Ta wiadomość zostanie wysłana do użytkowników po zakończeniu rejestracji. Sugerujemy wymienienie w niej istotnych linków oraz krótkie wyjaśnienie kolejnych kroków. Możesz użyć tej samej treści co w wiadomości powitalnej.', - placeholder: 'Wpisz e-mail powitalny', - }, - welcomeEmailSubject: { - label: 'Temat', - }, - useMessageAsEmail: { - label: 'Taki sam jak wiadomość powitalna', - }, - }, - }, - enterprise: { - header: 'Funkcjonalności Enterprise', - helper: 'Tutaj możesz zmienić ustawienia enterprise.', - fields: { - deviceManagement: { - label: 'Zablokuj możliwość zarządzania urządzeniami przez użytkowników', - helper: - 'Kiedy ta opcja jest włączona, tylko użytkownicy w grupie "Admin" mogą zarządzać urządzeniami w profilu użytkownika', - }, - disableAllTraffic: { - label: 'Zablokuj możliwość przekierowania całego ruchu przez VPN', - helper: - 'Kiedy ta opcja jest włączona, użytkownicy nie będą mogli przekierować całego ruchu przez VPN za pomocą klienta Defguard.', - }, - manualConfig: { - label: 'Wyłącz manualną konfigurację WireGuard', - helper: - 'Kiedy ta opcja jest włączona, użytkownicy nie będą mogli pobrać ani wyświetlić danych do manualnej konfiguracji WireGuard. Możliwe będzie wyłącznie skonfigurowanie klienta Defguard.', - }, - }, - }, - gatewayNotifications: { - smtpWarning: 'Aby włączyć powiadomienia należy najpierw skonfigurować serwer SMTP', - header: 'Powiadomienia', - helper: 'Tutaj możesz włączyć powiadomienia e-mail.', - sections: { - gateway: 'Powiadomienia o rozłączeniu Gatewaya', - }, - form: { - submit: 'Zapisz zmiany', - fields: { - disconnectNotificationsEnabled: { - label: 'Włącz powiadomienia o rozłączeniu', - help: "Wyślij powiadomienie do administratorów po rozłączeniu się Gateway'a", - }, - inactivityThreshold: { - label: 'Czas nieaktywności [minuty]', - help: 'Czas (w minutach), który musi upłynąć od rozłączenia zanim zostanie wysłane powiadomienie', - }, - reconnectNotificationsEnabled: { - label: 'Włącz powiadomienia o ponownym połączeniu', - help: "Wyślij powiadomienie do administratorów po ponownym nawiązaniu połączenia z Gateway'em", - }, - }, - }, - }, - }, - openidOverview: { - pageTitle: 'Aplikacje OpenID', - search: { - placeholder: 'Znajdź aplikacje', - }, - filterLabels: { - all: 'Wszystkie aplikacje', - enabled: 'Włączone', - disabled: 'Wyłączone', - }, - clientCount: 'Wszystkie aplikacje', - addNewApp: 'Dodaj aplikację', - list: { - headers: { - name: 'Nazwa', - status: 'Status', - actions: 'Akcję', - }, - editButton: { - edit: 'Edytuj aplikację', - delete: 'Usuń aplikację', - disable: 'Wyłącz', - enable: 'Włącz', - copy: 'Skopuj ID', - }, - status: { - enabled: 'Włączona', - disabled: 'Wyłączona', - }, - }, - messages: { - noLicenseMessage: 'Nie masz licencji dla tej funkcjonalności.', - noClientsFound: 'Nie znaleziono żadnych wyników.', - copySuccess: 'ID skopiowane', - }, - deleteApp: { - title: 'Usuń aplikację', - message: 'Czy chcesz usunąć aplikację {appName} ?', - submit: 'Usuń aplikację', - messages: { - success: 'Aplikacja usunięta.', - }, - }, - enableApp: { - messages: { - success: 'Aplikacja włączona', - }, - }, - disableApp: { - messages: { - success: 'Aplikacja wyłączona', - }, - }, - modals: { - openidClientModal: { - title: { - addApp: 'Dodaj aplikację', - editApp: 'Edytuj aplikację: {appName}', - }, - scopes: 'Zakresy:', - messages: { - clientIdCopy: 'Client ID zostało skopiowane.', - clientSecretCopy: 'Client secret zostało skopiowane.', - }, - form: { - messages: { - successAdd: 'Aplikacja utworzona.', - successModify: 'Aplikacja zmodyfikowana.', - }, - error: { - urlRequired: 'URL jest wymagany.', - validUrl: 'URL musi być poprawny.', - scopeValidation: 'Musi mieć co najmniej jeden zakres.', - }, - fields: { - name: { - label: 'Nazwa aplikacji', - }, - redirectUri: { - label: 'Przekierowujący URL {count}', - placeholder: 'https://example.com/redirect', - }, - openid: { - label: 'OpenID', - }, - profile: { - label: 'Profil', - }, - email: { - label: 'E-mail', - }, - phone: { - label: 'Telefon', - }, - groups: { - label: 'Grupy', - }, - }, - controls: { - addUrl: 'Dodaj URL', - }, - }, - clientId: 'Client ID', - clientSecret: 'Client secret', - }, - }, - }, - webhooksOverview: { - pageTitle: 'Webhooki', - search: { - placeholder: 'Znajdź webhooki po adresie URL', - }, - filterLabels: { - all: 'Wszystkie webhooki', - enabled: 'Włączone', - disabled: 'Wyłączone', - }, - webhooksCount: 'Wszystkie webhooki', - addNewWebhook: 'Dodaj webhook', - noWebhooksFound: 'Nie znaleziono żadnych webhooków', - list: { - headers: { - name: 'Nazwa', - description: 'Opis', - status: 'Status', - actions: 'Akcję', - }, - editButton: { - edit: 'Edytuj', - delete: 'Usuń webhook', - disable: 'Wyłącz', - enable: 'Włącz', - }, - status: { - enabled: 'Włączony', - disabled: 'Wyłączony', - }, - }, - }, - provisionersOverview: { - pageTitle: 'Provisionery', - search: { - placeholder: 'Wyszukaj provisionera', - }, - filterLabels: { - all: 'Wszystkie', - available: 'Dostępne', - unavailable: 'Niedostępne', - }, - provisionersCount: 'Wszystkie provisionery', - noProvisionersFound: 'Nie znaleziono provisionerów.', - noLicenseMessage: 'Nie masz licencji na tę funkcję.', - provisioningStation: { - header: 'Stacja provisionująca YubiKey', - content: `Aby móc sprovisionować YubiKeya, należy najpierw skonfigurować - fizyczną maszynę z gniazdem USB. Uruchom podane polecenie na wybranej maszynie - aby zarejestrować maszynę i rozpocząć generowanie kluczy.`, - tokenCard: { - title: 'Token autoryzacyjny', - }, - dockerCard: { - title: 'Przykład Docker', - }, - }, - list: { - headers: { - name: 'Nazwa', - ip: 'Adres IP', - status: 'Status', - actions: 'Akcję', - }, - editButton: { - delete: 'Usuń provisionera', - }, - status: { - available: 'Dostępny', - unavailable: 'Niedostępny', - }, - }, - messages: { - copy: { - command: 'Komenda skopiowa', - token: 'Token skopiowany', - }, - }, - }, - openidAllow: { - header: '{name} chciałby:', - scopes: { - openid: 'Użyć danych z twojego profilu do przyszłych logowań.', - profile: - 'Poznać podstawowe informacje z twojego profilu, takie jak login, imię itp', - email: 'Poznać twój adres e-mail.', - phone: 'Poznać twój numer telefonu.', - groups: 'Poznać twoje grupy.', - }, - controls: { - accept: 'Akceptuj', - cancel: 'Anuluj', - }, - }, - networkOverview: { - pageTitle: 'Przegląd lokalizacji', - controls: { - editNetworks: 'Edycja lokalizacji', - selectNetwork: { - placeholder: 'Oczekiwanie na lokalizacje', - }, - }, - filterLabels: { - grid: 'Widok siatki', - list: 'Widok listy', - }, - stats: { - currentlyActiveUsers: 'Obecnie aktywni użytkownicy', - activeUsersFilter: 'Aktywni użytkownicy w {hour}H', - activeDevicesFilter: 'Aktywne urządzenia w {hour}H', - activityIn: 'Aktywność w {hour}H', - in: 'Przychodzący:', - out: 'Wychodzący:', - gatewayDisconnected: 'Gateway rozłączony', - }, - }, - connectedUsersOverview: { - pageTitle: 'Podłączeni użytkownicy', - noUsersMessage: 'Obecnie nie ma żadnych podłączonych użytkowników', - userList: { - username: 'Nazwa użytkownika', - device: 'Urządzenia:', - connected: 'Połączony:', - deviceLocation: 'Lokacja urządzenia', - networkUsage: 'Użycie sieci', - }, - }, - networkPage: { - pageTitle: 'Edycja lokalizacji', - addNetwork: '+ Dodaj lokalizację', - controls: { - networkSelect: { - label: 'Wybór lokalizacji', - }, - }, - }, - activityOverview: { - header: 'Strumień aktywności', - noData: 'Obecnie nie wykryto żadnej aktywności', - }, - networkConfiguration: { - messages: { - delete: { - error: 'Błąd podczas próby usunięcia lokalizacji', - success: 'Lokalizacja usunięta', - }, - }, - header: 'Konfiguracja lokalizacji', - importHeader: 'Import lokalizacji', - form: { - helpers: { - address: - 'Na podstawie tego adresu będzie stworzona sieć VPN, np. 10.10.10.1/24 (sieć VPN: 10.10.10.0/24). Opcjonalnie możesz podać wiele adresów, oddzielając je przecinkiem. Pierwszy adres będzie adresem głównym i zostanie użyty do przypisywania adresów IP urządzeniom. Pozostałe adresy są dodatkowe i nie będą zarządzane przez Defguarda.', - endpoint: - 'Publiczny adres IP lub domena internetowa, do której będą łączyć się użytkownicy/urządzenia. Ten adres zostanie użyty w konfiguracji klientów, ale Gatewaye Defguard nie wiążą się z tym adresem.', - gateway: - 'Adres publiczny Gatewaya, używany przez użytkowników VPN do łączenia się.', - dns: 'Określ resolwery DNS, które mają odpytywać, gdy interfejs WireGuard jest aktywny.', - allowedIps: 'Lista adresów/masek, które powinny być routowane przez sieć VPN.', - allowedGroups: - 'Domyślnie wszyscy użytkownicy będą mogli połączyć się z tą lokalizacją. Jeżeli chcesz ogranicznyć dostęp do tej lokalizacji do wybranej grupy użytkowników, wybierz ją poniżej.', - }, - messages: { - networkModified: 'Lokalizacja zmodyfikowana', - networkCreated: 'Lokalizacja utworzona', - }, - fields: { - name: { - label: 'Nazwa lokalizacji', - }, - address: { - label: 'Adres i maska sieci VPN', - }, - endpoint: { - label: 'Adres IP lub domena internetowa Gatewaya', - }, - allowedIps: { - label: 'Dozwolone adresy IP', - }, - port: { - label: 'Port Gatewaya', - }, - dns: { - label: 'DNS', - }, - allowedGroups: { - label: 'Dozwolone grupy', - placeholder: 'Wszystkie grupy', - }, - mfa_enabled: { - label: 'Wymagaj MFA dla tej lokalizacji', - }, - keepalive_interval: { - label: 'Utrzymanie połączenia [sekundy]', - }, - peer_disconnect_threshold: { - label: 'Próg rozłączania [sekundy]', - }, - acl_enabled: { - label: 'Włącz ACL dla tej lokacji', - }, - acl_default_allow: { - label: 'Domyślna polityka ACL', - }, - }, - controls: { - submit: 'Zapisz zmiany', - cancel: 'Wróć', - delete: 'Usuń lokalizację', - }, - }, - }, - gatewaySetup: { - header: { - main: 'Uruchomienie serwera gateway', - dockerBasedGatewaySetup: `Konfiguracja gateway za pomocą narzędzia docker`, - fromPackage: `Z pakietu`, - oneLineInstall: `Instalacja za pomocą jednej linii`, - }, - card: { - title: 'Komenda Dockera uruchamiająca serwer gateway', - authToken: 'Token Autoryzacyjny', - }, - button: { - availablePackages: `Dostępne pakiety`, - }, - controls: { - status: 'Sprawdź status połączenia', - }, - messages: { - runCommand: `Defguard wymaga uruchomienia serwera gateway w celu kontrolowania VPN. - Szczegóły znajdziesz w [dokumentacji]({setupGatewayDocs}). - Istnieje wiele sposobów na uruchomienie serwera gateway, poniższy przykład używa technologii Docker, - więcej przykładów znajdziesz w [dokumentacji]({setupGatewayDocs}).`, - createNetwork: `Utwórz sieć przed uruchomieniem procesu gateway.`, - noConnection: `Brak połączenia proszę uruchom poniższą komendę.`, - connected: `Gateway połączony.`, - statusError: 'Nie udało się uzyskać statusu', - oneLineInstall: `Jeśli wykonujesz instalację w jednej linii: https://docs.defguard.net/getting-started/one-line-install - nie ma potrzeby wykonywania dalszych kroków.`, - fromPackage: `Zainstaluj pakiet dostępny na https://github.com/DefGuard/gateway/releases/latest i skonfiguruj \`/etc/defguard/gateway.toml\` - na podstawie [dokumentacji]({setupGatewayDocs}).`, - authToken: `Poniższy token jest wymagany do autoryzacji i konfiguracji węzła gateway. Upewnij się, że zachowasz ten token w bezpiecznym miejscu, - a następnie podążaj za instrukcją wdrażania usługi znajdującej się w [dokumentacji]({setupGatewayDocs}), aby pomyślnie skonfigurować serwer gateway. - Po więcej szczegółów i dokładnych kroków, proszę zapoznaj się z [dokumentacją](setupGatewayDocs).`, - dockerBasedGatewaySetup: `Poniżej znajduje się przykład oparty na Dockerze. - Więcej szczegółów i dokładnych kroków można znaleźć w [dokumentacji]({setupGatewayDocs}).`, - }, - }, - loginPage: { - pageTitle: 'Wprowadź swoje dane logowania', - callback: { - return: 'Powrót do logowania', - error: 'Wystąpił błąd podczas logowania przez zewnętrznego dostawcę OpenID', - }, - oidcLogin: 'Zaloguj się przez', - mfa: { - title: 'Autoryzacja dwuetapowa.', - controls: { - useAuthenticator: 'Zamiast tego użyj aplikacji Authenticator', - useWebauthn: 'Zamiast tego użyj klucza bezpieczeństwa', - useRecoveryCode: 'Zamiast tego użyj kodu odzyskiwania', - useEmail: 'Zamiast tego użyj e-mail', - }, - email: { - header: 'Użyj kodu wysłanego na e-mail aby kontynuować', - form: { - controls: { - resendCode: 'Wyślij kod ponownie', - }, - labels: { - code: 'Kod', - }, - }, - }, - totp: { - header: - 'Użyj kodu z aplikacji uwierzytelniającej i kliknij przycisk, aby kontynuować', - form: { - fields: { - code: { - placeholder: 'Wprowadź kod uwierzytelniający', - }, - }, - controls: { - submit: 'Użyj kodu uwierzytelniającego', - }, - }, - }, - recoveryCode: { - header: - 'Wpisz jeden z aktywnych kodów odzyskiwania i kliknij przycisk, aby się zalogować.', - form: { - fields: { - code: { - placeholder: 'Kod odzyskiwania', - }, - }, - controls: { - submit: 'Użyj kodu odzyskiwania', - }, - }, - }, - webauthn: { - header: 'Gdy jesteś gotowy do uwierzytelnienia, naciśnij przycisk poniżej.', - controls: { - submit: 'Użyj klucza bezpieczeństwa', - }, - messages: { - error: 'Nie udało się odczytać klucza. Proszę spróbować ponownie.', - }, - }, - }, - }, - wizard: { - completed: 'Sieć skonfigurowana', - configuration: { - successMessage: 'Sieć utworzona', - }, - navigation: { - top: 'Konfiguracja sieci', - titles: { - welcome: 'Konfiguracja sieci', - choseNetworkSetup: 'Wybierz tryb konfiguracji', - importConfig: 'Importuj istnijącą sieć', - manualConfig: 'Konfiguracja sieci', - mapDevices: 'Mapowanie importowanych urządzeń', - }, - buttons: { - next: 'Dalej', - back: 'Wróć', - }, - }, - welcome: { - header: 'Witaj w asystencie konfiguracji lokalizacji!', - sub: 'Zanim zaczniesz, musisz wybrać tryb konfiguracji. Ikony zawierają przydane informacje.', - button: 'Zacznij konfigurację', - }, - deviceMap: { - messages: { - crateSuccess: 'Urządzenie dodane', - errorsInForm: 'Uzupełnij oznaczone pola', - }, - list: { - headers: { - deviceName: 'Nazwa', - deviceIP: 'IP', - user: 'Użytkownik', - }, - }, - }, - wizardType: { - manual: { - title: 'Manualny', - description: 'Ręczna konfiguracja sieci WireGuard', - }, - import: { - title: 'Import', - description: 'Import z pliku konfiguracyjnego WireGuard', - }, - createNetwork: 'Utwórz sieć WireGuard', - }, - common: { - select: 'Wybierz', - }, - locations: { - form: { - name: 'Nazwa', - ip: 'Adres IP', - user: 'Użytkownik', - fileName: 'Plik', - selectFile: 'Wybierz plik', - messages: { devicesCreated: 'Urządzenia utworzone.' }, - validation: { invalidAddress: 'Nieprawidłowy adres.' }, - }, - }, - }, - layout: { - select: { - addNewOptionDefault: 'Dodaj +', - }, - }, - redirectPage: { - title: 'Zostałeś zalogowany', - subtitle: 'Wkrótce nastąpi przekierowanie...', - }, - enrollmentPage: { - title: 'Rejestracja', - controls: { - default: 'Domyślne', - save: 'Zapisz zmiany', - }, - messages: { - edit: { - error: 'Zapis nieudany', - success: 'Zapisano zmiany', - }, - }, - settings: { - welcomeMessage: { - title: 'Powitalna wiadomość', - messageBox: 'Ta informacja będzie wyświetlona w końcowym kroku rejestracj', - }, - welcomeEmail: { - subject: { - label: 'Temat wiadomości', - }, - title: 'Powitalny e-mail', - messageBox: 'Ta informacja będzie wysłana gdy użytkownik zakończy rejestrację.', - controls: { - duplicateWelcome: 'Identyczna jak wiadomość powitalna', - }, - }, - vpnOptionality: { - title: 'Opcjonalność kroku VPN', - select: { - options: { - optional: 'Opcjonalny', - mandatory: 'Obowiązkowy', - }, - }, - }, - }, - messageBox: - 'Proces rejestracji pozwala użytkownikowi na potwierdzenie swoich informacji, ustawienie hasła oraz skonfigurowanie VPN na swoim urządzeniu. Tutaj możesz skonfigurować ten proces.', - }, - supportPage: { - title: 'Wsparcie', - modals: { - confirmDataSend: { - title: 'Potwierdź przekazanie danych', - submit: 'Wyślij', - subTitle: - 'Potwierdź przesłanie danych diagnostycznych. Żadne poufne dane nie zostaną przesłane. (Klucze WireGuard, adresy e-mail, itp.)', - }, - }, - debugDataCard: { - title: 'Dane wsparcia technicznego', - body: ` -Jeśli potrzebujesz pomocy lub zostałeś poproszony przez nasz zespół o utworzenie danych wsparcia technicznego (np. na naszym kanale Matrix: **#defguard-support:teonite.com**), masz dwie opcje: -* Możesz skonfigurować ustawienia SMTP i kliknąć: "Wyślij dane wsparcia technicznego". -* Lub kliknąć "Pobierz dane wsparcia technicznego" i stworzyć zlecenie w naszym repozytorium GitHub załączając te pliki. -`, - downloadSupportData: 'Pobierz dane wsparcia technicznego', - downloadLogs: 'Pobierz dzienniki', - sendMail: 'Wyślij e-mail', - mailSent: 'E-mail wysłany', - mailError: 'Błąd wysyłania e-mail', - }, - - supportCard: { - title: 'Wsparcie', - body: ` -Przed zgłoszeniem problemów na GitHub należy zapoznać z dokumentacją dostępną na [docs.defguard.net](https://docs.defguard.net/) - -Aby zgłosić: -* Problem - przejdź do [GitHub](https://github.com/DefGuard/defguard/issues/new?assignees=&labels=bug&template=bug_report.md&title=) -* Prośbę o nową funkcjonalność - przejdź do [GitHub](https://github.com/DefGuard/defguard/issues/new?assignees=&labels=feature&template=feature_request.md&title=) - -W przypadku innych zgłoszeń skontaktuj się z nami: support@defguard.net -`, - }, - }, - devicesPage: { - title: 'Urządzenia sieciowe', - search: { - placeholder: 'Znajdź', - }, - bar: { - itemsCount: 'Wszystkie urządzenia', - filters: {}, - actions: { - addNewDevice: 'Dodaj nowe', - }, - }, - list: { - columns: { - labels: { - name: 'Nazwa', - location: 'Położenie', - description: 'Opis', - addedBy: 'Dodane przez', - addedAt: 'Data dodania', - edit: 'Zmień', - }, - }, - edit: { - actionLabels: { - config: 'Zobacz konfigurację', - generateToken: 'Utwórz kupon autoryzacyjny', - }, - }, - }, - }, - acl: { - messageBoxes: { - aclAliasKind: { - component: { - name: 'Komponent', - description: - 'w połączeniu z ręcznie skonfigurowanymi miejscami docelowymi w ACL', - }, - destination: { - name: 'Miejsce docelowe', - description: 'zostanie zamienione na osobny zestaw reguł firewalla', - }, - }, - networkSelectionIndicatorsHelper: { - //md - denied: ` - Dostęp do lokalizacji domyślnie jest **zabroniony** – ruch sieciowy nie określony przez reguły będzie blokowany. - `, - //md - allowed: ` - Dostęp do lokalizacji domyślnie jest **dozwolony** – ruch sieciowy nie określony przez reguły będzie przepuszczany. - `, - //md - unmanaged: ` - Dostęp do lokalizacji nie jest zarządzany (wyłączona kontrola ACL) - `, - }, - }, - sharedTitle: 'Lista kontroli dostępu', - fieldsSelectionLabels: { - ports: 'Wszystkie porty', - protocols: 'Wszystkie protokoły', - }, - ruleStatus: { - new: 'Nowa', - applied: 'Zastosowana', - modified: 'Czeka na zmianę', - deleted: 'Czeka na usunięcie', - enable: 'Włącz', - enabled: 'Włączona', - disable: 'Wyłącz', - disabled: 'Wyłączona', - expired: 'Przedawniona', - }, - listPage: { - message: { - changeDiscarded: 'Zmiana odrzucona', - changeAdded: 'Dodana zmiana oczekująca', - changeFail: 'Nie udało się wykonać zmiany', - applyChanges: 'Zmiana została zastosowana', - applyFail: 'Nie udało się zastosować zmiany', - }, - rules: { - modals: { - applyConfirm: { - title: 'Wdróż oczekujące zmiany', - subtitle: '{count} zmian zostanie zastosowanych', - submit: 'Wdróż zmiany', - }, - filterGroupsModal: { - groupHeaders: { - alias: 'Aliasy', - location: 'Lokalizacje', - groups: 'Grupy', - status: 'Status', - }, - submit: 'Zapisz filtr', - }, - }, - listControls: { - searchPlaceholder: 'Znajdź nazwę', - addNew: 'Dodaj nową', - filter: { - nothingApplied: 'Filtr', - applied: 'Filtry ({count})', - }, - apply: { - noChanges: 'Wdróż oczekujące zmiany', - all: 'Wdróż oczekujące zmiany ({count})', - selective: 'Wdróż zaznaczone zmiany ({count})', - }, - }, - list: { - pendingList: { - title: 'Oczekujące zmiany', - noData: 'Brak oczekujących zmian', - noDataSearch: 'Nie znaleziono oczekujących zmian', - }, - deployedList: { - title: 'Wdrożone reguły', - noData: 'Brak wdrożonych reguł', - noDataSearch: 'Nie znaleziono wdrożonych reguł', - }, - headers: { - name: 'Nazwa reguły', - id: 'ID', - destination: 'Miejsce docelowe', - allowed: 'Zazwolone', - denied: 'Zabronione', - locations: 'Lokalizacje', - status: 'Status', - edit: 'Zmień', - }, - tags: { - all: 'Wszystkie', - allDenied: 'Wszystkie zabronione', - allAllowed: 'Wszystkie zezwolne', - }, - editMenu: { - discard: 'Odrzuć zmiany', - delete: 'Zaznacz do usunięcia', - }, - }, - }, - aliases: { - message: { - rulesApply: 'Oczekujące zmiany zostały zastosowane', - rulesApplyFail: 'Nie udało się zastosować zmian', - aliasDeleted: 'Alias usunięty', - aliasDeleteFail: 'Nie udało się usunąć aliasu', - }, - modals: { - applyConfirm: { - title: 'Potwierdź wdrożenie aliasu', - message: `Uaktualnione aliasy zmienią następujące reguły obecnie wdrożone na Gatewayu.\nZanim przejdziesz dalej, upewnij się, że te zmiany są zamierzone.`, - listLabel: 'Dotyczy reguł', - submit: 'Wdóż zmiany', - }, - deleteBlock: { - title: 'Usuwanie zablokowane', - //md - content: ` -Ten alias jest obecnie używany przez nastąpujące reguły i nie może być usunięty. Aby go usunąć, należy najpierw wykasować go z tych reguł({rulesCount}): -`, - }, - filterGroupsModal: { - groupLabels: { - rules: 'Reguły', - status: 'Status', - }, - }, - create: { - labels: { - name: 'Nazwa aliasu', - kind: 'Rodzajj aliasu', - ip: 'Zakres adresów IPv4/6 CIDR', - ports: 'Porty lub zakres portów', - protocols: 'Protokoły', - }, - placeholders: { - protocols: 'Wszystkie protokoły', - ports: 'Wszystkie porty', - ip: 'Wszystkie adresy IP', - }, - kindOptions: { - destination: 'Miejsce docelowe', - component: 'Komponent', - }, - controls: { - cancel: 'Anuluj', - edit: 'Edytuj alias', - create: 'Utwórz alias', - }, - messages: { - modified: 'Alias zmienione', - created: 'Alias utworzony', - }, - }, - }, - listControls: { - searchPlaceholder: 'Znajdź nazwę', - addNew: 'Dodaj nową', - filter: { - nothingApplied: 'Filtr', - applied: 'Filtry ({count})', - }, - apply: { - noChanges: 'Wdróż oczkujące zmiany', - all: 'Wdróż oczkujące zmiany ({count})', - selective: 'Wdróż zaznaczone zmiany ({count})', - }, - }, - list: { - pendingList: { - title: 'Oczkujące zmiany', - noData: 'Brak oczkujących zmian', - noDataSearch: 'Nie znaleziono oczkujących zmian', - }, - deployedList: { - title: 'Wdrożone aliasy', - noData: 'Brak wdrożonych aliasów', - noDataSearch: 'Nie znaleziono wdrożonych aliasów', - }, - headers: { - id: 'ID', - name: 'Nazwa aliasu', - kind: 'Rodzaj aliasu', - ip: 'Zakres adresów IPv4/6 CIDR', - ports: 'Porty', - protocols: 'Protokoły', - status: 'Status', - edit: 'Zmień', - rules: 'Reguły', - }, - status: { - applied: 'Zastosowane', - changed: 'Zmieione', - }, - tags: { - allDenied: 'Wszystkie zabronione', - allAllowed: 'Wszystkie dozwolne', - }, - editMenu: { - discardChanges: 'Odrzuć zmiany', - delete: 'Usuń alias', - }, - }, - }, - }, - createPage: { - formError: { - allowDenyConflict: 'Konfliktujący członkowie', - allowNotConfigured: - 'Trzeba skonfigurowć dostęp dla użytkowników, grup lub urządzeń', - }, - infoBox: { - // md - allowInstructions: ` - Podaj jedno lub więcej pól (użytkownicy, grupy lub urządzenia) aby zdefinionwać tę regułę. Reguła uwzględni wszystkie podane wejścia dla pasujących warunków. Pozostaw puste pola, jeżeli nie są potrzebne.`, - // md - destinationInstructions: ` - Podaj jedno lub więcej pól (adresy IP lub porty) aby zdefinionwać tę regułę. Reguła uwzględni wszystkie podane wejścia dla pasujących warunków. Pozostaw puste pola, jeżeli nie są potrzebne.`, - }, - message: { - create: 'Reguła została utworzona i dodana do oczekujących zmian.', - createFail: 'Nie można było utworzyć reguły.', - }, - headers: { - rule: 'Reguła', - createRule: 'Utwórz regułę', - allowed: 'Zezwoleni użytkownicy/grupy/urządzenia', - denied: 'Zablokowani użytkownicy/grupy/urządzenia', - destination: 'Miejsce docelowe', - }, - labels: { - name: 'Nazwa reguły', - priority: 'Priorytet', - status: 'Status', - locations: 'Lokalizacje', - allowAllUsers: 'Zezwól wszystkim użytkownikom', - allowAllNetworks: 'Włącz wszystkie lokalizacje', - allowAllNetworkDevices: 'Zezwól wszystkim urządzeniom sieciowym', - denyAllUsers: 'Zablokuj wszystkich użytkowników', - denyAllNetworkDevices: 'Zablokuj wszystkie urządzenia sieciowe', - users: 'Użytkownicy', - groups: 'Grupy', - devices: 'Urządzenia sieciowe', - protocols: 'Protokoły', - manualIp: 'Zakres lub adres IPv4/6 CIDR', - ports: 'Porty', - aliases: 'Aliasy', - expires: 'Data wygaśnięcia', - manualInput: 'Ręczne wprowadzenie', - }, - placeholders: { - allProtocols: 'Wszystkie protokoły', - allIps: 'Wszystkie adresy IP', - }, - }, - }, -} as PartialDeep; - -const pl = deepmerge(en, translation) as Translation; - -export default pl; diff --git a/web/src/main.tsx b/web/src/main.tsx index ef6ffc73c..202ec830f 100644 --- a/web/src/main.tsx +++ b/web/src/main.tsx @@ -1,32 +1,11 @@ -import './shared/scss/styles.scss'; -import './shared/defguard-ui/scss/index.scss'; - -import { QueryClientProvider } from '@tanstack/react-query'; -// import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; -import dayjs from 'dayjs'; -import LocalizedFormat from 'dayjs/plugin/localizedFormat'; -import utc from 'dayjs/plugin/utc'; import { StrictMode } from 'react'; import { createRoot } from 'react-dom/client'; +import './shared/defguard-ui/scss/index.scss'; +import { App } from './app/App.tsx'; -import { AppLoader } from './components/AppLoader'; -import { I18nProvider } from './components/I18nProvider'; -import { ApiProvider } from './shared/hooks/api/provider'; -import queryClient from './shared/query-client'; - -dayjs.extend(utc); -dayjs.extend(LocalizedFormat); - -const root = createRoot(document.getElementById('root') as HTMLElement); -root.render( +// biome-ignore lint/style/noNonNullAssertion: always there +createRoot(document.getElementById('root')!).render( - - - - - {/* */} - - - + , ); diff --git a/web/src/markdown.d.ts b/web/src/markdown.d.ts deleted file mode 100644 index ddd15b04a..000000000 --- a/web/src/markdown.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -declare module '*.md' { - const val: string; - export default val; -} diff --git a/web/src/pages/UsersOverviewPage/UsersOverviewPage.tsx b/web/src/pages/UsersOverviewPage/UsersOverviewPage.tsx new file mode 100644 index 000000000..7fee217db --- /dev/null +++ b/web/src/pages/UsersOverviewPage/UsersOverviewPage.tsx @@ -0,0 +1,17 @@ +import { useQuery } from '@tanstack/react-query'; +import { LayoutGrid } from '../../shared/components/LayoutGrid/LayoutGrid'; +import { Page } from '../../shared/components/Page/Page'; +import './style.scss'; +import { m } from '../../paraglide/messages'; +import { isPresent } from '../../shared/defguard-ui/utils/isPresent'; +import { getUsersQueryOptions } from '../../shared/query'; +import { UsersTable } from './UsersTable'; + +export const UsersOverviewPage = () => { + const { data: users } = useQuery(getUsersQueryOptions); + return ( + + {isPresent(users) && } + + ); +}; diff --git a/web/src/pages/UsersOverviewPage/UsersTable.tsx b/web/src/pages/UsersOverviewPage/UsersTable.tsx new file mode 100644 index 000000000..8d36df230 --- /dev/null +++ b/web/src/pages/UsersOverviewPage/UsersTable.tsx @@ -0,0 +1,124 @@ +import { + createColumnHelper, + getCoreRowModel, + type SortingState, + useReactTable, +} from '@tanstack/react-table'; +import { useMemo, useState } from 'react'; +import { m } from '../../paraglide/messages'; +import type { User } from '../../shared/api/types'; +import { Avatar } from '../../shared/defguard-ui/components/Avatar/Avatar'; +import { IconButtonMenu } from '../../shared/defguard-ui/components/IconButtonMenu/IconButtonMenu'; +import { tableEditColumnSize } from '../../shared/defguard-ui/components/table/consts'; +import { TableBody } from '../../shared/defguard-ui/components/table/TableBody/TableBody'; +import { TableCell } from '../../shared/defguard-ui/components/table/TableCell/TableCell'; +import { TableTop } from '../../shared/defguard-ui/components/table/TableTop/TableTop'; + +type Props = { + users: User[]; +}; + +type RowData = User; + +const columnHelper = createColumnHelper(); + +export const UsersTable = ({ users }: Props) => { + const transformedData = useMemo(() => users, [users]); + const [sortingState, setSortingState] = useState([ + { + id: 'name', + desc: false, + }, + ]); + + const columns = useMemo( + () => [ + columnHelper.display({ + id: 'name', + header: m.users_col_name(), + enableSorting: true, + meta: { + flex: true, + }, + cell: (info) => { + const rowData = info.row.original; + const name = `${rowData.first_name} ${rowData.last_name}`; + return ( + + + {name} + + ); + }, + }), + columnHelper.accessor('username', { + header: m.users_col_login(), + size: 170, + enableSorting: false, + cell: (info) => ( + + {info.getValue()} + + ), + }), + columnHelper.accessor('phone', { + size: 175, + header: m.users_col_phone(), + enableSorting: false, + cell: (info) => ( + + {info.getValue()} + + ), + }), + columnHelper.accessor('groups', { + header: m.users_col_groups(), + size: 370, + enableSorting: false, + cell: (info) => ( + + {info.getValue().join(', ')} + + ), + }), + columnHelper.display({ + id: 'edit', + size: tableEditColumnSize, + header: '', + enableSorting: false, + cell: (_info) => { + // const _rowData = info.row.original; + return ( + + + + ); + }, + }), + ], + [], + ); + + const table = useReactTable({ + state: { + sorting: sortingState, + }, + columns, + data: transformedData, + getCoreRowModel: getCoreRowModel(), + manualSorting: true, + onSortingChange: setSortingState, + }); + + return ( + <> + + + + ); +}; diff --git a/web/src/pages/UsersOverviewPage/style.scss b/web/src/pages/UsersOverviewPage/style.scss new file mode 100644 index 000000000..442ec6c81 --- /dev/null +++ b/web/src/pages/UsersOverviewPage/style.scss @@ -0,0 +1,11 @@ +#users-overview-page { + .table, + .table-top { + grid-column: 1 / 13; + } + + .page-content { + padding-top: var(--spacing-3xl); + box-sizing: border-box; + } +} diff --git a/web/src/pages/acl/AclCreateDataProvider.tsx b/web/src/pages/acl/AclCreateDataProvider.tsx deleted file mode 100644 index b51a93341..000000000 --- a/web/src/pages/acl/AclCreateDataProvider.tsx +++ /dev/null @@ -1,102 +0,0 @@ -import { useQuery } from '@tanstack/react-query'; -import { type PropsWithChildren, useEffect, useMemo } from 'react'; -import { useLocation } from 'react-router'; -import { useSearchParams } from 'react-router-dom'; - -import { isPresent } from '../../shared/defguard-ui/utils/isPresent'; -import useApi from '../../shared/hooks/useApi'; -import { QueryKeys } from '../../shared/queries'; -import { useAclCreateSelector, useUpdateAclCreateContext } from './acl-context'; - -type Props = PropsWithChildren; -export const AclCreateDataProvider = ({ children }: Props) => { - const location = useLocation(); - const [searchParams] = useSearchParams(); - const updateContext = useUpdateAclCreateContext(); - const baseContextSet = useAclCreateSelector( - (s) => ![s.devices, s.groups, s.networks, s.users].includes(undefined), - ); - const ruleEditSet = useAclCreateSelector((s) => isPresent(s.editRule)); - - const { - standaloneDevice: { getDevicesList }, - groups: { getGroupsInfo }, - user: { getUsers }, - network: { getNetworks }, - acl: { - aliases: { getAliases }, - rules: { getRule }, - }, - } = useApi(); - - const isRuleEdit = useMemo( - () => location.pathname.includes('/acl/form') && location.search.includes('edit=1'), - [location.pathname, location.search], - ); - - const editRuleId = useMemo(() => { - if (isRuleEdit) { - return parseInt(searchParams.get('rule') as string, 10); - } - }, [isRuleEdit, searchParams]); - - const { data: editRuleData } = useQuery({ - queryFn: () => getRule(editRuleId as number), - queryKey: [QueryKeys.FETCH_ACL_RULE_EDIT, editRuleId], - enabled: isRuleEdit && isPresent(editRuleId), - refetchOnMount: true, - refetchOnWindowFocus: false, - }); - - const { data: aclData } = useQuery({ - queryKey: [ - QueryKeys.FETCH_ACL_CREATE_CONTEXT, - QueryKeys.FETCH_USERS_LIST, - QueryKeys.FETCH_GROUPS_INFO, - QueryKeys.FETCH_NETWORKS, - QueryKeys.FETCH_STANDALONE_DEVICE_LIST, - QueryKeys.FETCH_ACL_ALIASES, - ], - queryFn: () => - Promise.all([ - getNetworks(), - getGroupsInfo(), - getUsers(), - getDevicesList(), - getAliases(), - ]), - refetchOnReconnect: true, - refetchOnWindowFocus: false, - refetchOnMount: true, - }); - - const contextSet = useMemo(() => { - if (isRuleEdit) { - return baseContextSet && ruleEditSet; - } - return baseContextSet; - }, [baseContextSet, isRuleEdit, ruleEditSet]); - - useEffect(() => { - if (aclData) { - const [networks, groups, users, devices, aliases] = aclData; - updateContext({ - devices, - groups, - networks, - users, - aliases, - }); - } - }, [aclData, updateContext]); - - useEffect(() => { - updateContext({ - editRule: editRuleData, - }); - }, [editRuleData, updateContext]); - - if (!contextSet) return null; - - return <>{children}; -}; diff --git a/web/src/pages/acl/AclCreatePage/AclCreatePage.tsx b/web/src/pages/acl/AclCreatePage/AclCreatePage.tsx deleted file mode 100644 index 34d11a545..000000000 --- a/web/src/pages/acl/AclCreatePage/AclCreatePage.tsx +++ /dev/null @@ -1,648 +0,0 @@ -import './style.scss'; - -import { zodResolver } from '@hookform/resolvers/zod'; -import { useMutation, useQueryClient } from '@tanstack/react-query'; -import type { AxiosError } from 'axios'; -import { intersection } from 'lodash-es'; -import { useCallback, useMemo, useRef, useState } from 'react'; -import { type SubmitHandler, useForm } from 'react-hook-form'; -import { useNavigate } from 'react-router'; -import { useSearchParams } from 'react-router-dom'; -import { z } from 'zod'; - -import { useI18nContext } from '../../../i18n/i18n-react'; -import { FormDateInput } from '../../../shared/components/Layout/DateInput/FormDateInput'; -import { PageContainer } from '../../../shared/components/Layout/PageContainer/PageContainer'; -import { RenderMarkdown } from '../../../shared/components/Layout/RenderMarkdown/RenderMarkdown'; -import { SectionWithCard } from '../../../shared/components/Layout/SectionWithCard/SectionWithCard'; -import { FormCheckBox } from '../../../shared/defguard-ui/components/Form/FormCheckBox/FormCheckBox'; -import { FormInput } from '../../../shared/defguard-ui/components/Form/FormInput/FormInput'; -import { FormSelect } from '../../../shared/defguard-ui/components/Form/FormSelect/FormSelect'; -import { FormTextarea } from '../../../shared/defguard-ui/components/Form/FormTextarea/FormTextarea'; -import { Button } from '../../../shared/defguard-ui/components/Layout/Button/Button'; -import { - ButtonSize, - ButtonStyleVariant, -} from '../../../shared/defguard-ui/components/Layout/Button/types'; -import { LabeledCheckbox } from '../../../shared/defguard-ui/components/Layout/LabeledCheckbox/LabeledCheckbox'; -import { MessageBox } from '../../../shared/defguard-ui/components/Layout/MessageBox/MessageBox'; -import { - MessageBoxStyleVariant, - MessageBoxType, -} from '../../../shared/defguard-ui/components/Layout/MessageBox/types'; -import { isPresent } from '../../../shared/defguard-ui/utils/isPresent'; -import useApi from '../../../shared/hooks/useApi'; -import { useToaster } from '../../../shared/hooks/useToaster'; -import { QueryKeys } from '../../../shared/queries'; -import type { - AclRuleInfo, - CreateAclRuleRequest, - EditAclRuleRequest, - GroupInfo, - Network, - StandaloneDevice, - User, -} from '../../../shared/types'; -import { trimObjectStrings } from '../../../shared/utils/trimObjectStrings'; -import { AclAliasKindIcon } from '../AclIndexPage/components/shared/AclAliasKindIcon'; -import { AclMessageBoxes } from '../AclIndexPage/components/shared/AclMessageBoxes/AclMessageBoxes'; -import { NetworkAccessTypeIcon } from '../AclIndexPage/components/shared/NetworkAccessTypeIcon'; -import { useAclLoadedContext } from '../acl-context'; -import { type AclAlias, AclAliasStatus } from '../types'; -import { networkToNetworkAccessType, protocolOptions, protocolToString } from '../utils'; -import { aclDestinationValidator, aclPortsValidator } from '../validators'; -import { FormDialogSelect } from './components/DialogSelect/FormDialogSelect'; - -type AclForm = Omit; - -export const AlcCreatePage = () => { - const [searchParams] = useSearchParams(); - const editMode = ['1', 'true'].includes(searchParams.get('edit') ?? ''); - const { LL } = useI18nContext(); - const localLL = LL.acl.createPage; - const labelsLL = localLL.labels; - const formErrors = LL.form.error; - const { networks, devices, groups, users, ruleToEdit } = useAclLoadedContext(); - const queryClient = useQueryClient(); - const aclContext = useAclLoadedContext(); - const { aliases } = aclContext; - - const initialValue = useMemo(() => { - if (editMode) { - return ruleToEdit as AclForm; - } - const defaultValue: AclForm = { - aliases: [], - all_networks: false, - allow_all_users: false, - allow_all_network_devices: false, - allowed_devices: [], - allowed_groups: [], - allowed_users: [], - denied_devices: [], - denied_groups: [], - denied_users: [], - deny_all_users: false, - deny_all_network_devices: false, - destination: '', - id: 0, - name: '', - networks: [], - ports: '', - protocols: [], - expires: undefined, - enabled: true, - }; - return defaultValue; - }, [editMode, ruleToEdit]); - - const [neverExpires, setNeverExpires] = useState(!isPresent(initialValue.expires)); - const [allowAllLocations, setAllowAllLocations] = useState(initialValue.all_networks); - const submitRef = useRef(null); - const toaster = useToaster(); - const aliasesOptions = aliases.filter( - (alias) => alias.state === AclAliasStatus.APPLIED, - ); - - const navigate = useNavigate(); - - const { - acl: { - rules: { createRule, editRule }, - }, - } = useApi(); - - const handleSuccess = useCallback(() => { - const keys = [QueryKeys.FETCH_ACL_RULES, QueryKeys.FETCH_ACL_RULE_EDIT]; - for (const key of keys) { - void queryClient.invalidateQueries({ - predicate: (query) => query.queryKey.includes(key), - }); - } - navigate('/admin/acl'); - }, [navigate, queryClient]); - - const handleError = useCallback( - (err: AxiosError) => { - toaster.error(LL.acl.listPage.message.changeFail()); - console.error(err.message ?? err); - }, - [LL.acl.listPage.message, toaster], - ); - - const { mutate: mutatePost, isPending: postPending } = useMutation({ - mutationFn: createRule, - onSuccess: () => { - handleSuccess(); - }, - onError: handleError, - }); - - const { mutate: mutatePut, isPending: putPending } = useMutation({ - mutationFn: editRule, - onSuccess: () => { - handleSuccess(); - }, - onError: handleError, - }); - - const schema = useMemo( - () => - z - .object({ - name: z - .string({ - required_error: formErrors.required(), - }) - .min(1, formErrors.required()), - networks: z.number().array(), - expires: z.string().nullable(), - enabled: z.boolean(), - allow_all_users: z.boolean(), - deny_all_users: z.boolean(), - allow_all_network_devices: z.boolean(), - deny_all_network_devices: z.boolean(), - allowed_users: z.number().array(), - denied_users: z.number().array(), - allowed_groups: z.number().array(), - denied_groups: z.number().array(), - allowed_devices: z.number().array(), - denied_devices: z.number().array(), - aliases: z.number().array(), - destination: aclDestinationValidator(LL), - ports: aclPortsValidator(LL), - protocols: z.number().array(), - }) - .superRefine((vals, ctx) => { - // check for collisions - const message = LL.acl.createPage.formError.allowDenyConflict(); - if (!vals.allow_all_users && !vals.deny_all_users) { - if (intersection(vals.allowed_users, vals.denied_users).length) { - ctx.addIssue({ - path: ['allowed_users'], - code: 'custom', - message, - }); - ctx.addIssue({ - path: ['denied_users'], - code: 'custom', - message, - }); - } - if (intersection(vals.allowed_groups, vals.denied_groups).length) { - ctx.addIssue({ - path: ['allowed_groups'], - code: 'custom', - message, - }); - ctx.addIssue({ - path: ['denied_groups'], - code: 'custom', - message, - }); - } - } - if (!vals.allow_all_network_devices && !vals.deny_all_network_devices) { - if (intersection(vals.allowed_devices, vals.denied_devices).length) { - ctx.addIssue({ - path: ['allowed_devices'], - code: 'custom', - message, - }); - ctx.addIssue({ - path: ['denied_devices'], - code: 'custom', - message, - }); - } - } - - // check if one of allowed users/groups/devices fields is set - const isAllowConfigured = - vals.allow_all_users || - vals.allow_all_network_devices || - vals.allowed_users.length !== 0 || - vals.allowed_groups.length !== 0 || - vals.allowed_devices.length !== 0; - if (!isAllowConfigured) { - const message = LL.acl.createPage.formError.allowNotConfigured(); - - ctx.addIssue({ - path: ['allow_all_users'], - code: 'custom', - message, - }); - ctx.addIssue({ - path: ['allowed_users'], - code: 'custom', - message, - }); - ctx.addIssue({ - path: ['allowed_groups'], - code: 'custom', - message, - }); - ctx.addIssue({ - path: ['allow_all_network_devices'], - code: 'custom', - message, - }); - ctx.addIssue({ - path: ['allowed_devices'], - code: 'custom', - message, - }); - } - }), - [LL, formErrors], - ); - - type FormFields = z.infer; - - const defaultValues = useMemo((): FormFields => { - const res: FormFields = { - aliases: initialValue.aliases, - allow_all_users: initialValue.allow_all_users, - allow_all_network_devices: initialValue.allow_all_network_devices, - allowed_devices: initialValue.allowed_devices, - allowed_groups: initialValue.allowed_groups, - allowed_users: initialValue.allowed_users, - deny_all_users: initialValue.deny_all_users, - deny_all_network_devices: initialValue.deny_all_network_devices, - denied_devices: initialValue.denied_devices, - denied_groups: initialValue.denied_groups, - denied_users: initialValue.denied_users, - destination: initialValue.destination, - expires: initialValue.expires ?? null, - name: initialValue.name, - networks: initialValue.networks, - ports: initialValue.ports, - protocols: initialValue.protocols, - enabled: initialValue.enabled, - }; - return res; - }, [initialValue]); - - const { control, handleSubmit, watch, setValue } = useForm({ - defaultValues, - mode: 'all', - resolver: zodResolver(schema), - criteriaMode: 'all', - }); - - const watchedExpires = watch('expires'); - - const handleValidSubmit: SubmitHandler = (values) => { - const cleaned = trimObjectStrings(values); - if (editMode) { - const requestData: EditAclRuleRequest = { - ...cleaned, - all_networks: allowAllLocations, - id: initialValue.id, - }; - mutatePut(requestData); - } else { - const requestData: CreateAclRuleRequest = { - ...cleaned, - all_networks: allowAllLocations, - }; - mutatePost(requestData); - } - }; - - const allowAllUsers = watch('allow_all_users'); - const denyAllUsers = watch('deny_all_users'); - const allowAllNetworkDevices = watch('allow_all_network_devices'); - const denyAllNetworkDevices = watch('deny_all_network_devices'); - - return ( - -
-

{LL.acl.sharedTitle()}

-
-
-
-
-
- - - - - } - forceShowErrorMessage - /> - - { - if (change) { - setValue('expires', null, { - shouldValidate: false, - shouldDirty: true, - }); - } - setNeverExpires(change); - }} - /> - - - - - - - } - controller={{ control, name: 'aliases' }} - options={aliasesOptions} - label={localLL.labels.aliases()} - identKey="id" - renderTagContent={renderAlias} - searchKeys={['name']} - /> - - - - ({ - displayValue: protocolToString(val), - key: val, - })} - disposable - /> - -
-
- - - - - - - - - - - - - - - - - - - - -
- -
-
- ); -}; - -const CardHeader = ({ title }: { title: string }) => { - return ( -
-

{title}

-
-
- ); -}; - -const renderNetworkSelectTag = (network: Network) => ( - <> -

{network.name}

- - -); - -const renderUserTag = (user: User) =>

{user.username}

; - -const renderUserListItem = (user: User) => ( - <> -

{`${user.first_name} ${user.last_name} (${user.username})`}

- -); - -const renderNetworkDevice = (device: StandaloneDevice) =>

{device.name}

; - -const renderAlias = (alias: AclAlias) => ( - <> -

{alias.name}

- - -); - -const renderGroup = (group: GroupInfo) =>

{group.name}

; diff --git a/web/src/pages/acl/AclCreatePage/components/DialogSelect/DialogSelect.tsx b/web/src/pages/acl/AclCreatePage/components/DialogSelect/DialogSelect.tsx deleted file mode 100644 index 7b4bc21f4..000000000 --- a/web/src/pages/acl/AclCreatePage/components/DialogSelect/DialogSelect.tsx +++ /dev/null @@ -1,124 +0,0 @@ -import './style.scss'; - -import useResizeObserver from '@react-hook/resize-observer'; -import clsx from 'clsx'; -import { useCallback, useMemo, useRef, useState } from 'react'; - -import { FieldError } from '../../../../../shared/defguard-ui/components/Layout/FieldError/FieldError'; -import { FloatingMenu } from '../../../../../shared/defguard-ui/components/Layout/FloatingMenu/FloatingMenu'; -import { FloatingMenuProvider } from '../../../../../shared/defguard-ui/components/Layout/FloatingMenu/FloatingMenuProvider'; -import { FloatingMenuTrigger } from '../../../../../shared/defguard-ui/components/Layout/FloatingMenu/FloatingMenuTrigger'; -import { Label } from '../../../../../shared/defguard-ui/components/Layout/Label/Label'; -import { isPresent } from '../../../../../shared/defguard-ui/utils/isPresent'; -import { DialogSelectButtonIcon } from './DialogSelectButtonIcon'; -import { DialogSelectModal } from './DialogSelectModal/DialogSelectModal'; -import type { DialogSelectProps } from './types'; - -export const DialogSelect = ({ - options, - selected, - identKey, - label, - onChange, - renderTagContent, - renderDialogListItem, - searchFn, - searchKeys, - errorMessage, - modalExtrasTop, - disabled = false, -}: DialogSelectProps) => { - const containerRef = useRef(null); - const [overflows, setOverflows] = useState(false); - - const handleResize = useCallback(() => { - if (containerRef.current) { - setOverflows(containerRef.current.scrollWidth > containerRef.current.clientWidth); - } - }, []); - - useResizeObserver(containerRef, handleResize); - const [modalOpen, setModalOpen] = useState(false); - const getIdent = useCallback((val: T): I => val[identKey] as I, [identKey]); - - const selectedOptions = useMemo( - () => options.filter((o) => selected.includes(getIdent(o))), - [getIdent, options, selected], - ); - - const error = !disabled ? errorMessage : undefined; - - const getLabel = renderDialogListItem ? renderDialogListItem : renderTagContent; - - return ( - <> -
-
- {label !== undefined && } -
- - -
-
- {renderTagContent !== undefined && - selectedOptions.map((o) => { - const id = getIdent(o); - return ( -
- {renderTagContent(o)} -
- ); - })} -
-
-
- -
    - {selectedOptions.map((o) => { - const id = getIdent(o); - return
  • {getLabel(o)}
  • ; - })} -
-
-
- -
- -
-
- { - onChange?.(vals); - }} - /> - - ); -}; diff --git a/web/src/pages/acl/AclCreatePage/components/DialogSelect/DialogSelectButtonIcon.tsx b/web/src/pages/acl/AclCreatePage/components/DialogSelect/DialogSelectButtonIcon.tsx deleted file mode 100644 index a0e30aa4f..000000000 --- a/web/src/pages/acl/AclCreatePage/components/DialogSelect/DialogSelectButtonIcon.tsx +++ /dev/null @@ -1,13 +0,0 @@ -export const DialogSelectButtonIcon = () => { - return ( - - - - ); -}; diff --git a/web/src/pages/acl/AclCreatePage/components/DialogSelect/DialogSelectModal/DialogSelectModal.tsx b/web/src/pages/acl/AclCreatePage/components/DialogSelect/DialogSelectModal/DialogSelectModal.tsx deleted file mode 100644 index d970ddc8d..000000000 --- a/web/src/pages/acl/AclCreatePage/components/DialogSelect/DialogSelectModal/DialogSelectModal.tsx +++ /dev/null @@ -1,156 +0,0 @@ -import './style.scss'; - -import { type ReactNode, useCallback, useEffect, useMemo, useState } from 'react'; - -import { useI18nContext } from '../../../../../../i18n/i18n-react'; -import { Button } from '../../../../../../shared/defguard-ui/components/Layout/Button/Button'; -import { - ButtonSize, - ButtonStyleVariant, -} from '../../../../../../shared/defguard-ui/components/Layout/Button/types'; -import { CheckBox } from '../../../../../../shared/defguard-ui/components/Layout/Checkbox/CheckBox'; -import { Modal } from '../../../../../../shared/defguard-ui/components/Layout/modals/Modal/Modal'; -import { Search } from '../../../../../../shared/defguard-ui/components/Layout/Search/Search'; -import { isPresent } from '../../../../../../shared/defguard-ui/utils/isPresent'; -import { searchByKeys } from '../../../../../../shared/utils/searchByKeys'; -import type { DialogSelectProps } from '../types'; - -type Props = { - initiallySelected: I[]; - options: T[]; - getIdent: (value: T) => I; - getLabel: (value: T) => ReactNode; - open: boolean; - setOpen: (val: boolean) => void; - onChange: (selected: I[]) => void; - extrasTop?: ReactNode; -} & Pick, 'searchFn' | 'searchKeys'>; - -export const DialogSelectModal = ({ - getIdent, - initiallySelected, - getLabel, - open, - setOpen, - options, - onChange, - searchFn, - searchKeys, - extrasTop, -}: Props) => { - const { LL } = useI18nContext(); - const [searchValue, setSearch] = useState(''); - const [selected, setSelected] = useState(initiallySelected); - - const handleSelect = useCallback((id: I, selected: boolean) => { - if (selected) { - setSelected((s) => s.filter((i) => i !== id)); - } else { - setSelected((s) => [...s, id]); - } - }, []); - - const handleSelectAll = () => { - if (selected.length === options.length) { - setSelected([]); - } else { - setSelected(options.map((o) => getIdent(o))); - } - }; - - const searchEnabled = isPresent(searchFn) || isPresent(searchKeys); - - const filteredOptions = useMemo(() => { - if (!searchEnabled) return options; - if (searchFn) { - return options.filter((o) => searchFn(o, searchValue)); - } - if (searchKeys) { - return options.filter((o) => { - const res = searchByKeys(o, searchKeys, searchValue); - return res; - }); - } - return options; - }, [searchEnabled, searchFn, options, searchValue, searchKeys]); - - useEffect(() => { - setSelected(initiallySelected); - }, [initiallySelected]); - - return ( - { - setOpen(false); - }} - afterClose={() => { - setSearch(''); - }} - className="modal-dialog-select" - > - {extrasTop} - {searchEnabled && ( - { - setSearch(value); - }} - placeholder="Filter/Search" - /> - )} -
{ - handleSelectAll(); - }} - > - -

Select all

-
-
-
    - {filteredOptions.length === 0 && searchValue === '' && ( -

    No options

    - )} - {filteredOptions.length === 0 && searchValue !== '' && ( -

    Not found

    - )} - {filteredOptions.map((o) => { - const id = getIdent(o); - const isSelected = selected.includes(id); - return ( -
  • { - handleSelect(id, isSelected); - }} - > - - {getLabel(o)} -
  • - ); - })} -
-
-
-
-
- ); -}; diff --git a/web/src/pages/acl/AclCreatePage/components/DialogSelect/DialogSelectModal/style.scss b/web/src/pages/acl/AclCreatePage/components/DialogSelect/DialogSelectModal/style.scss deleted file mode 100644 index 8b1d29199..000000000 --- a/web/src/pages/acl/AclCreatePage/components/DialogSelect/DialogSelectModal/style.scss +++ /dev/null @@ -1,76 +0,0 @@ -.modal.modal-dialog-select { - min-height: 100dvh; - justify-items: center; - align-items: start; - padding: 10dvh 0 40px !important; - - .modal-content { - width: 100%; - max-width: 580px; - border-radius: 10px; - padding: 18px 10px; - box-sizing: border-box; - overflow: hidden; - - & > .spacer { - padding-bottom: var(--spacing-xs); - } - - hr { - border: 0.5px solid var(--border-primary); - margin: var(--spacing-xs) 0; - } - - & > .search { - margin-bottom: var(--spacing-xs); - } - - .no-data { - width: 100%; - text-align: center; - color: var(--text-body-tertiary); - - @include typography(app-body-2); - } - - .option { - min-height: 24px; - display: flex; - flex-flow: row; - align-items: center; - justify-content: flex-start; - column-gap: var(--spacing-xs); - cursor: pointer; - user-select: none; - - p, - span { - @include typography(app-modal-1); - } - - span { - color: var(--text-body-tertiary); - } - } - - .options { - list-style: none; - display: flex; - flex-flow: column; - row-gap: var(--spacing-xs); - min-height: calc(24px * 6 + 50px); - max-height: calc(240px + (9 * var(--spacing-xs))); - overflow-x: hidden; - overflow-y: auto; - } - - .controls { - padding-top: var(--spacing-xs); - display: flex; - flex-flow: row; - align-items: center; - justify-content: center; - column-gap: var(--spacing-l); - } - } -} diff --git a/web/src/pages/acl/AclCreatePage/components/DialogSelect/FormDialogSelect.tsx b/web/src/pages/acl/AclCreatePage/components/DialogSelect/FormDialogSelect.tsx deleted file mode 100644 index 661cf4785..000000000 --- a/web/src/pages/acl/AclCreatePage/components/DialogSelect/FormDialogSelect.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import { useMemo } from 'react'; -import { - type FieldValues, - type UseControllerProps, - useController, -} from 'react-hook-form'; - -import { DialogSelect } from './DialogSelect'; -import type { DialogSelectProps } from './types'; - -type Props = { - controller: UseControllerProps; - forceShowErrorMessage?: boolean; - onChange?: () => void; -} & Omit, 'selected' | 'errorMessage'>; - -export const FormDialogSelect = < - T extends FieldValues, - B extends object, - I extends number | string, ->({ - controller, - onChange: onChangeExternal, - forceShowErrorMessage = false, - ...selectProps -}: Props) => { - const { - field: { value, onChange }, - fieldState: { error, isDirty, isTouched }, - formState: { isSubmitted }, - } = useController(controller); - - const errorMessage = useMemo(() => { - if ( - (error && (isDirty || isTouched)) || - (!error && isSubmitted) || - forceShowErrorMessage - ) { - return error?.message; - } - return undefined; - }, [error, forceShowErrorMessage, isDirty, isSubmitted, isTouched]); - - return ( - { - onChange(selected); - onChangeExternal?.(); - }} - selected={value} - errorMessage={errorMessage} - /> - ); -}; diff --git a/web/src/pages/acl/AclCreatePage/components/DialogSelect/style.scss b/web/src/pages/acl/AclCreatePage/components/DialogSelect/style.scss deleted file mode 100644 index f51846251..000000000 --- a/web/src/pages/acl/AclCreatePage/components/DialogSelect/style.scss +++ /dev/null @@ -1,180 +0,0 @@ -.dialog-select-field { - width: 100%; - display: inline-grid; - grid-template-rows: auto; - grid-template-columns: 1fr 40px; - column-gap: var(--spacing-xs); - align-items: center; - - .open-button { - grid-row: 1; - grid-column: 2 / 3; - background-color: var(--surface-icon-on-dark); - border: none; - border-radius: 10px; - width: 40px; - height: 40px; - display: flex; - flex-flow: row; - align-items: center; - justify-content: center; - cursor: pointer; - opacity: 1; - - @include animate-field; - transition-property: background-color, opacity; - - svg { - path { - @include animate-field; - transition-property: fill; - fill: var(--surface-icon-primary); - } - } - - &:disabled { - opacity: var(--disabled-opacity); - } - - &:not(:disabled) { - &:hover { - background-color: var(--surface-main-primary); - - svg { - path { - fill: var(--surface-icon-secondary); - } - } - } - } - } - - & > .track { - grid-row: 1; - grid-column: 1 / 2; - width: 100%; - box-sizing: border-box; - padding: 8px 10px 8px 18px; - border-radius: 10px; - border: 1px solid var(--border-primary); - min-height: 50px; - background-color: var(--surface-frame-bg); - position: relative; - overflow: hidden; - opacity: 1; - - @include animate-field; - transition-property: border-color, opacity; - - &.overflows { - &::after { - position: absolute; - top: 0; - right: 0; - width: 65px; - height: 100%; - content: ' '; - background: linear-gradient( - 90deg, - rgba(0, 0, 0, 0) 0%, - var(--surface-default-modal) 100% - ); - } - } - - .options { - display: flex; - flex-flow: row nowrap; - align-items: center; - justify-content: flex-start; - column-gap: 8px; - z-index: 1; - - .dialog-select-tag { - display: inline-flex; - flex-flow: row; - align-items: center; - justify-content: flex-start; - box-sizing: border-box; - padding: 7px 10px; - border: 1px solid var(--border-primary); - column-gap: 10px; - border-radius: 10px; - background-color: var(--surface-default-modal); - - span, - p { - @include typography(app-input); - user-select: none; - text-wrap: nowrap; - } - } - } - - & > svg { - border-radius: 10px; - user-select: none; - z-index: 2; - position: absolute; - right: -1px; - top: 0px; - } - } - - &.disabled { - & > .track { - cursor: not-allowed; - } - - & > .open-button { - cursor: not-allowed; - } - } - - &.invalid { - & > .track { - border-color: var(--border-alert); - } - } - - &.disabled { - & > .open-button { - opacity: var(--disabled-opacity); - } - - & > .track { - opacity: var(--disabled-opacity); - } - } -} - -.dialog-select { - & > .inner { - position: relative; - - & > label { - padding-bottom: var(--spacing-xs); - } - } -} - -.dialog-select-track-floating-menu { - min-width: 200px; - max-width: 100dvh; - - ul { - list-style: none; - display: flex; - flex-flow: column; - row-gap: var(--spacing-xs); - - li { - @include typography(app-modal-2); - display: flex; - flex-flow: row wrap; - gap: var(--spacing-xs); - align-items: center; - justify-content: flex-start; - } - } -} diff --git a/web/src/pages/acl/AclCreatePage/components/DialogSelect/types.ts b/web/src/pages/acl/AclCreatePage/components/DialogSelect/types.ts deleted file mode 100644 index e276c3090..000000000 --- a/web/src/pages/acl/AclCreatePage/components/DialogSelect/types.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { ReactNode } from 'react'; - -export type DialogSelectProps = { - options: T[]; - identKey: keyof T; - selected: I[]; - renderTagContent: (value: T) => ReactNode; - // if not provided will use renderTagContent instead - renderDialogListItem?: (value: T) => ReactNode; - errorMessage?: string; - label?: string; - // Can replace searchFn, when given only keys it will use util searchByKeys, searchFn prop takes priority if both given. - searchKeys?: Array; - disabled?: boolean; - searchFn?: DialogSelectSearch; - onChange?: (values: I[]) => void; - modalExtrasTop?: ReactNode; -}; - -export type DialogSelectSearch = (obj: T, searchedValue: string) => boolean; diff --git a/web/src/pages/acl/AclCreatePage/style.scss b/web/src/pages/acl/AclCreatePage/style.scss deleted file mode 100644 index a620f85d0..000000000 --- a/web/src/pages/acl/AclCreatePage/style.scss +++ /dev/null @@ -1,115 +0,0 @@ -#acl-create-page { - .labeled-checkbox, - .form-checkbox { - label { - @include typography(app-modal-1); - color: var(--text-body-primary); - } - } - - & > .page-content { - box-sizing: border-box; - padding: var(--spacing-s); - - @include media-breakpoint-up(lg) { - padding: var(--spacing-xl); - } - - & > .header { - display: flex; - flex-flow: column; - row-gap: var(--spacing-s); - padding-bottom: var(--spacing-m); - - @include media-breakpoint-up(lg) { - flex-flow: row; - align-items: center; - justify-content: flex-start; - gap: var(--spacing-s); - } - - h1 { - @include typography(app-title); - } - - .controls { - margin-left: auto; - display: flex; - flex-flow: row; - align-items: center; - justify-content: flex-end; - column-gap: var(--spacing-s); - } - } - } -} - -#acl-sections { - width: 100%; - display: flex; - flex-flow: column; - row-gap: var(--spacing-s); - - @include media-breakpoint-up(xl) { - flex-flow: row; - column-gap: var(--spacing-m); - } - - #rule-card { - grid-area: rule; - } - - #allowed-card { - grid-area: allowed; - } - - #destination-card { - grid-area: destination; - } - - #denied-card { - grid-area: denied; - } - - & > .column { - display: flex; - flex-flow: column; - row-gap: var(--spacing-s); - } - - h3 { - text-wrap: nowrap; - @include typography(app-side-bar); - } - - .card { - .input, - .dialog-select-spacer, - .labeled-checkbox, - .form-checkbox, - .spacer, - & > .header { - &:not(:last-child) { - padding-bottom: var(--spacing-s); - } - } - - .header { - display: flex; - flex-flow: row nowrap; - align-items: center; - justify-content: flex-start; - column-gap: 8px; - width: 100%; - min-height: 23px; - - hr { - width: 100%; - align-self: flex-end; - border: 0.5px solid var(--border-primary); - margin: 0; - padding: 0; - } - } - } -} diff --git a/web/src/pages/acl/AclIndexPage/AclIndexPage.tsx b/web/src/pages/acl/AclIndexPage/AclIndexPage.tsx deleted file mode 100644 index 9f3a9a57b..000000000 --- a/web/src/pages/acl/AclIndexPage/AclIndexPage.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import './style.scss'; - -import { useMemo, useState } from 'react'; - -import { useI18nContext } from '../../../i18n/i18n-react'; -import { PageLayout } from '../../../shared/components/Layout/PageLayout/PageLayout'; -import { CardTabs } from '../../../shared/defguard-ui/components/Layout/CardTabs/CardTabs'; -import type { CardTabsData } from '../../../shared/defguard-ui/components/Layout/CardTabs/types'; -import { AclIndexAliases } from './components/AclIndexAliases/AclIndexAliases'; -import { AclIndexRules } from './components/AclIndexRules/AclIndexRules'; - -enum AclTab { - ALIASES = 'aliases', - RULES = 'rules', -} - -export const AclIndexPage = () => { - const [activeTab, setActiveTab] = useState(AclTab.RULES); - const { LL } = useI18nContext(); - - const availableTabs: CardTabsData[] = [ - { - key: AclTab.RULES, - active: activeTab === AclTab.RULES, - content:

{LL.acl.listPage.tabs.rules()}

, - onClick: () => setActiveTab(AclTab.RULES), - }, - { - key: AclTab.ALIASES, - active: activeTab === AclTab.ALIASES, - content:

{LL.acl.listPage.tabs.aliases()}

, - onClick: () => setActiveTab(AclTab.ALIASES), - }, - ]; - - const tabRender = useMemo(() => { - switch (activeTab) { - case AclTab.RULES: - return ; - case AclTab.ALIASES: - return ; - } - }, [activeTab]); - - return ( - -
-

{LL.acl.sharedTitle()}

-
- -
{tabRender}
-
- ); -}; diff --git a/web/src/pages/acl/AclIndexPage/components/AclIndexAliases/AclIndexAliases.tsx b/web/src/pages/acl/AclIndexPage/components/AclIndexAliases/AclIndexAliases.tsx deleted file mode 100644 index 56ad450cf..000000000 --- a/web/src/pages/acl/AclIndexPage/components/AclIndexAliases/AclIndexAliases.tsx +++ /dev/null @@ -1,470 +0,0 @@ -import './style.scss'; - -import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; -import { intersection } from 'lodash-es'; -import { useCallback, useEffect, useMemo, useState } from 'react'; -import { shallow } from 'zustand/shallow'; - -import { useI18nContext } from '../../../../../i18n/i18n-react'; -import { FilterGroupsModal } from '../../../../../shared/components/modals/FilterGroupsModal/FilterGroupsModal'; -import type { FilterGroupsModalFilter } from '../../../../../shared/components/modals/FilterGroupsModal/types'; -import { Button } from '../../../../../shared/defguard-ui/components/Layout/Button/Button'; -import { - ButtonSize, - ButtonStyleVariant, -} from '../../../../../shared/defguard-ui/components/Layout/Button/types'; -import { ListItemCount } from '../../../../../shared/defguard-ui/components/Layout/ListItemCount/ListItemCount'; -import { Search } from '../../../../../shared/defguard-ui/components/Layout/Search/Search'; -import useApi from '../../../../../shared/hooks/useApi'; -import { useToaster } from '../../../../../shared/hooks/useToaster'; -import { QueryKeys } from '../../../../../shared/queries'; -import { useAclLoadedContext } from '../../../acl-context'; -import { type AclAlias, AclAliasStatus } from '../../../types'; -import { - aclAliasStatusToInt, - aclDestinationToListTagDisplay, - aclPortsToListTagDisplay, - aclProtocolsToListTagDisplay, - aclRuleToListTagDisplay, -} from '../../../utils'; -import { AclListSkeleton } from '../AclListSkeleton/AclListSkeleton'; -import { DeployChangesIcon } from '../DeployChangesIcon'; -import { AliasesList } from './components/AliasesList'; -import { AclAliasApplyConfirmModal } from './modals/AclAliasApplyConfirmModal/AclAliasApplyConfirmModal'; -import { AclAliasDeleteBlockModal } from './modals/AclAliasDeleteBlockModal/AclAliasDeleteBlockModal'; -import { AlcAliasCEModal } from './modals/AlcAliasCEModal/AlcAliasCEModal'; -import { useAclAliasCEModal } from './modals/AlcAliasCEModal/store'; -import type { AclAliasListData } from './types'; - -type ListTagDisplay = { - key: string | number; - label: string; - displayAsTag?: boolean; -}; - -type AliasesFilters = { - rules: number[]; - status: number[]; -}; - -type ApplyConfirmState = { - rules: string[]; - open: boolean; - aliasesToApply: number[]; -}; - -const defaultApplyConfirmState: ApplyConfirmState = { - rules: [], - aliasesToApply: [], - open: false, -}; - -export type ListData = { - context: { - usedBy: ListTagDisplay[]; - }; -} & AclAlias; - -const defaultFilters: AliasesFilters = { - rules: [], - status: [], -}; - -const intersects = (...args: Array): boolean => intersection(args).length > 0; - -export const AclIndexAliases = () => { - const toaster = useToaster(); - const { - acl: { - rules: { getRules }, - aliases: { applyAliases }, - }, - } = useApi(); - - const { data: aclRules, isLoading: aliasesLoading } = useQuery({ - queryFn: getRules, - queryKey: [QueryKeys.FETCH_ACL_RULES], - refetchOnMount: true, - }); - - const queryClient = useQueryClient(); - const openCEModal = useAclAliasCEModal((s) => s.open, shallow); - const [applyConfirmModalState, setApplyConfirmModalState] = useState( - defaultApplyConfirmState, - ); - const aclContext = useAclLoadedContext(); - const { aliases } = aclContext; - const [appliedFilters, setAppliedFilters] = useState(defaultFilters); - const filtersPresent = useMemo( - () => Object.values(appliedFilters).flat(1).length > 0, - [appliedFilters], - ); - const [filtersModalOpen, setFiltersModalOpen] = useState(false); - const [searchValue, setSearchValue] = useState(''); - const { LL } = useI18nContext(); - const localLL = LL.acl.listPage.aliases; - - const { mutate: applyMutation, isPending: applyPending } = useMutation({ - mutationFn: applyAliases, - onSuccess: () => { - toaster.success(LL.acl.listPage.message.applyChanges()); - void queryClient.invalidateQueries({ - predicate: (query) => query.queryKey.includes(QueryKeys.FETCH_ACL_ALIASES), - }); - }, - onError: (error) => { - toaster.error(LL.acl.listPage.message.applyFail()); - console.error(error); - }, - }); - - const pendingAliasesCount = useMemo(() => { - if (aliases) { - return aliases.filter((alias) => alias.state !== AclAliasStatus.APPLIED).length; - } - return 0; - }, [aliases]); - - const applySearch = useCallback( - (data: AclAlias[]) => { - if (searchValue) { - return data.filter((alias) => - alias.name.trim().toLowerCase().includes(searchValue.toLowerCase().trim()), - ); - } - return data; - }, - [searchValue], - ); - - const [selectedPending, setSelectedPending] = useState< - Record - >({}); - - const handlePendingSelect = useCallback((key: number, value: boolean) => { - setSelectedPending((s) => ({ ...s, [key]: value })); - }, []); - - const handlePendingSelectAll = useCallback( - (value: boolean, state: Record) => { - const newState = { ...state }; - for (const key in newState) { - newState[key] = value; - } - setSelectedPending(newState); - }, - [], - ); - - const pendingSelectionCount = useMemo(() => { - let count = 0; - for (const key in selectedPending) { - if (selectedPending[key]) count++; - } - return count; - }, [selectedPending]); - - const prepareDisplay = useCallback( - (data: AclAlias[], filters: AliasesFilters) => { - if (!aclRules) return []; - if (filters.rules.length) { - data = data.filter((alias) => intersects(alias.rules, filters.rules)); - } - const res: AclAliasListData[] = []; - for (const alias of data) { - const rules = aclRules.filter((rule) => alias.rules.includes(rule.id)); - res.push({ - ...alias, - display: { - destination: aclDestinationToListTagDisplay(alias.destination), - ports: aclPortsToListTagDisplay(alias.ports), - protocols: aclProtocolsToListTagDisplay(alias.protocols), - rules: aclRuleToListTagDisplay(rules), - }, - }); - } - return res; - }, - [aclRules], - ); - - const deployed = useMemo(() => { - if (aliases) { - return aliases.filter((alias) => alias.state === AclAliasStatus.APPLIED); - } - return []; - }, [aliases]); - - const deployedDisplay = useMemo(() => { - if (aliases) { - return prepareDisplay(applySearch(deployed), appliedFilters); - } - return []; - }, [aliases, prepareDisplay, applySearch, deployed, appliedFilters]); - - const pending = useMemo(() => { - if (aliases) { - return aliases.filter((alias) => alias.state === AclAliasStatus.MODIFIED); - } - return []; - }, [aliases]); - - const pendingDisplay = useMemo(() => { - if (aliases) { - return prepareDisplay(applySearch(pending), appliedFilters); - } - return []; - }, [aliases, appliedFilters, applySearch, pending, prepareDisplay]); - - const displayItemsCount = useMemo( - () => deployedDisplay.length + pendingDisplay.length, - [deployedDisplay.length, pendingDisplay.length], - ); - - const applyText = useMemo(() => { - if (!pending.length) return localLL.listControls.apply.noChanges(); - if (pendingSelectionCount) { - return localLL.listControls.apply.selective({ - count: pendingSelectionCount, - }); - } - return localLL.listControls.apply.all({ - count: pending.length, - }); - }, [localLL.listControls.apply, pending.length, pendingSelectionCount]); - - const filters = useMemo(() => { - const res: Record = { - rules: { - identifier: 'rules', - label: localLL.modals.filterGroupsModal.groupLabels.rules(), - items: - aclRules?.map((rule) => ({ - label: rule.name, - searchValues: [rule.name], - value: rule.id, - })) ?? [], - order: 2, - }, - status: { - identifier: 'status', - label: localLL.modals.filterGroupsModal.groupLabels.status(), - items: [ - { - label: localLL.list.status.changed(), - searchValues: [LL.acl.ruleStatus.modified()], - value: aclAliasStatusToInt(AclAliasStatus.MODIFIED), - }, - { - label: localLL.list.status.applied(), - searchValues: [localLL.list.status.applied()], - value: aclAliasStatusToInt(AclAliasStatus.APPLIED), - }, - ], - order: 1, - }, - }; - return res; - }, [ - LL.acl.ruleStatus, - aclRules, - localLL.list.status, - localLL.modals.filterGroupsModal.groupLabels, - ]); - - const handleApply = () => { - if (aliases && aclRules) { - let toApply: AclAlias[]; - if (!pendingSelectionCount) { - toApply = pending; - } else { - const ids: number[] = Object.keys(selectedPending) - .filter((key: string) => selectedPending[Number(key)]) - .map((key) => Number(key)); - toApply = pending.filter((alias) => ids.includes(alias.id)); - } - const aliasesIds = toApply.map((alias) => alias.id); - const rulesWithin = new Set(); - toApply.forEach((alias) => { - alias.rules.forEach((rule) => { - rulesWithin.add(rule); - }); - }); - // check if need to confirm - if (rulesWithin.size) { - //prepare and open modal - const ruleNames: string[] = aclRules - .filter((rule) => rulesWithin.has(rule.id)) - .map((rule) => rule.name) - .sort(); - setApplyConfirmModalState({ - aliasesToApply: aliasesIds, - open: true, - rules: ruleNames, - }); - } else { - void applyMutation(aliasesIds); - } - } - }; - - // update or build selection state for list when rules are done loading - useEffect(() => { - if (aliases) { - const pending = aliases.filter((rule) => rule.state !== AclAliasStatus.APPLIED); - const selectionEntries = Object.keys(selectedPending).length; - if (selectionEntries !== pending.length) { - const newSelectionState: Record = {}; - for (const rule of pending) { - newSelectionState[rule.id] = newSelectionState[rule.id] ?? false; - } - setSelectedPending(newSelectionState); - } - } - }, [aliases, selectedPending]); - - return ( - <> - - { - setApplyConfirmModalState((s) => ({ ...s, open: val })); - }} - onSubmit={() => { - void applyMutation(applyConfirmModalState.aliasesToApply); - setApplyConfirmModalState(defaultApplyConfirmState); - }} - /> -
-
-

Aliases

- - { - setSearchValue(searchChange); - }} - /> -
-
-
- {aliasesLoading && } - {!aliasesLoading && ( - <> - - - - )} - - { - setAppliedFilters(newFilters as AliasesFilters); - }} - onCancel={() => { - setFiltersModalOpen(false); - }} - /> -
- - ); -}; diff --git a/web/src/pages/acl/AclIndexPage/components/AclIndexAliases/components/AclAliasStatus/AclAliasStatus.tsx b/web/src/pages/acl/AclIndexPage/components/AclIndexAliases/components/AclAliasStatus/AclAliasStatus.tsx deleted file mode 100644 index b53fe3931..000000000 --- a/web/src/pages/acl/AclIndexPage/components/AclIndexAliases/components/AclAliasStatus/AclAliasStatus.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import './style.scss'; - -import clsx from 'clsx'; -import { useMemo } from 'react'; - -import { useI18nContext } from '../../../../../../../i18n/i18n-react'; -import { ActivityIcon } from '../../../../../../../shared/defguard-ui/components/icons/ActivityIcon/ActivityIcon'; -import { ActivityIconVariant } from '../../../../../../../shared/defguard-ui/components/icons/ActivityIcon/types'; -import { AclAliasStatus } from '../../../../../types'; - -type Props = { - status: AclAliasStatus; -}; - -export const AclAliasStatusDisplay = ({ status }: Props) => { - const { LL } = useI18nContext(); - const statusLL = LL.acl.listPage.aliases.list.status; - - const [label, iconStatus] = useMemo(() => { - switch (status) { - case AclAliasStatus.APPLIED: - return [statusLL.applied(), ActivityIconVariant.CONNECTED]; - case AclAliasStatus.MODIFIED: - return [statusLL.changed(), ActivityIconVariant.DISCONNECTED]; - } - }, [status, statusLL]); - - return ( -
-

{label}

- -
- ); -}; diff --git a/web/src/pages/acl/AclIndexPage/components/AclIndexAliases/components/AclAliasStatus/style.scss b/web/src/pages/acl/AclIndexPage/components/AclIndexAliases/components/AclAliasStatus/style.scss deleted file mode 100644 index c422f4b39..000000000 --- a/web/src/pages/acl/AclIndexPage/components/AclIndexAliases/components/AclAliasStatus/style.scss +++ /dev/null @@ -1,28 +0,0 @@ -.acl-alias-status { - width: 100%; - max-width: 100%; - display: inline-flex; - flex-flow: row nowrap; - column-gap: 5px; - user-select: none; - overflow: hidden; - align-items: center; - justify-content: flex-start; - - & > p { - @include typography(app-modal-3); - color: inherit; - max-width: calc(100% - 13px); - overflow: hidden; - - @include text-overflow-dots; - } - - &.status-modified { - color: var(--text-important); - } - - &.status-applied { - color: var(--text-positive); - } -} diff --git a/web/src/pages/acl/AclIndexPage/components/AclIndexAliases/components/AliasEditButton.tsx b/web/src/pages/acl/AclIndexPage/components/AclIndexAliases/components/AliasEditButton.tsx deleted file mode 100644 index b13f1802f..000000000 --- a/web/src/pages/acl/AclIndexPage/components/AclIndexAliases/components/AliasEditButton.tsx +++ /dev/null @@ -1,91 +0,0 @@ -import { useMutation, useQueryClient } from '@tanstack/react-query'; -import type { AxiosError } from 'axios'; -import { useCallback } from 'react'; -import { shallow } from 'zustand/shallow'; - -import { useI18nContext } from '../../../../../../i18n/i18n-react'; -import { EditButton } from '../../../../../../shared/defguard-ui/components/Layout/EditButton/EditButton'; -import { EditButtonOption } from '../../../../../../shared/defguard-ui/components/Layout/EditButton/EditButtonOption'; -import { EditButtonOptionStyleVariant } from '../../../../../../shared/defguard-ui/components/Layout/EditButton/types'; -import useApi from '../../../../../../shared/hooks/useApi'; -import { useToaster } from '../../../../../../shared/hooks/useToaster'; -import { QueryKeys } from '../../../../../../shared/queries'; -import { AclAliasStatus } from '../../../../types'; -import { useAclAliasDeleteBlockModal } from '../modals/AclAliasDeleteBlockModal/store'; -import { useAclAliasCEModal } from '../modals/AlcAliasCEModal/store'; -import type { AclAliasListData } from '../types'; - -type EditProps = { - alias: AclAliasListData; -}; - -export const AliasEditButton = ({ alias }: EditProps) => { - const queryClient = useQueryClient(); - const isApplied = alias.state === AclAliasStatus.APPLIED; - const { LL } = useI18nContext(); - const localLL = LL.acl.listPage.aliases.list.editMenu; - const toaster = useToaster(); - const openDeleteBlockModal = useAclAliasDeleteBlockModal((s) => s.open, shallow); - - const { - acl: { - aliases: { deleteAlias }, - }, - } = useApi(); - - const invalidateQueries = useCallback(() => { - void queryClient.invalidateQueries({ - predicate: (query) => query.queryKey.includes(QueryKeys.FETCH_ACL_ALIASES), - }); - }, [queryClient]); - - const handleError = useCallback( - (err: AxiosError) => { - toaster.error(LL.acl.listPage.message.changeFail()); - console.error(err.message ?? err); - }, - [LL.acl.listPage.message, toaster], - ); - - const { mutate: deleteAliasMutation, isPending: deletionPending } = useMutation({ - mutationFn: deleteAlias, - onSuccess: () => { - invalidateQueries(); - if (isApplied) { - toaster.success(LL.acl.listPage.aliases.message.aliasDeleted()); - } else { - toaster.success(LL.acl.listPage.message.changeDiscarded()); - } - }, - onError: handleError, - }); - - const openEditModal = useAclAliasCEModal((s) => s.open, shallow); - - return ( - - { - openEditModal({ alias }); - }} - disabled={deletionPending} - /> - { - if (alias.rules.length === 0) { - deleteAliasMutation(alias.id); - } else { - openDeleteBlockModal( - alias, - alias.display.rules.map((tag) => tag.label).sort(), - ); - } - }} - /> - - ); -}; diff --git a/web/src/pages/acl/AclIndexPage/components/AclIndexAliases/components/AliasesList.tsx b/web/src/pages/acl/AclIndexPage/components/AclIndexAliases/components/AliasesList.tsx deleted file mode 100644 index cdcc24bf9..000000000 --- a/web/src/pages/acl/AclIndexPage/components/AclIndexAliases/components/AliasesList.tsx +++ /dev/null @@ -1,198 +0,0 @@ -import clsx from 'clsx'; -import { orderBy } from 'lodash-es'; -import { type ReactNode, useMemo, useState } from 'react'; -import { upperCaseFirst } from 'text-case'; - -import { useI18nContext } from '../../../../../../i18n/i18n-react'; -import { ListCellTags } from '../../../../../../shared/components/Layout/ListCellTags/ListCellTags'; -import { ListHeader } from '../../../../../../shared/components/Layout/ListHeader/ListHeader'; -import type { ListHeaderColumnConfig } from '../../../../../../shared/components/Layout/ListHeader/types'; -import { CheckBox } from '../../../../../../shared/defguard-ui/components/Layout/Checkbox/CheckBox'; -import { InteractionBox } from '../../../../../../shared/defguard-ui/components/Layout/InteractionBox/InteractionBox'; -import { ListCellText } from '../../../../../../shared/defguard-ui/components/Layout/ListCellText/ListCellText'; -import { NoData } from '../../../../../../shared/defguard-ui/components/Layout/NoData/NoData'; -import { ListSortDirection } from '../../../../../../shared/defguard-ui/components/Layout/VirtualizedList/types'; -import { isPresent } from '../../../../../../shared/defguard-ui/utils/isPresent'; -import type { AclAlias } from '../../../../types'; -import { DividerHeader } from '../../shared/DividerHeader'; -import type { AclAliasListData } from '../types'; -import { AclAliasStatusDisplay } from './AclAliasStatus/AclAliasStatus'; -import { AliasEditButton } from './AliasEditButton'; - -type AliasesListProps = { - data: AclAliasListData[]; - header: { - text: string; - extras?: ReactNode; - }; - noDataMessage: string; - isAppliedList?: boolean; - selected?: Record; - allSelected?: boolean; - onSelect?: (key: number, value: boolean) => void; - onSelectAll?: (value: boolean, state: Record) => void; -}; - -export const AliasesList = ({ - data, - header, - noDataMessage, - selected, - allSelected, - onSelect, - onSelectAll, -}: AliasesListProps) => { - const { LL } = useI18nContext(); - const headersLL = LL.acl.listPage.aliases.list.headers; - const [sortKey, setSortKey] = useState('name'); - const [sortDir, setSortDir] = useState(ListSortDirection.ASC); - - const selectionEnabled = useMemo( - () => - isPresent(onSelect) && - isPresent(onSelectAll) && - isPresent(selected) && - isPresent(allSelected), - [onSelect, onSelectAll, selected, allSelected], - ); - - const sortedAliases = useMemo( - () => orderBy(data, [sortKey], [sortDir.valueOf().toLowerCase() as 'asc' | 'desc']), - [data, sortDir, sortKey], - ); - - const listHeaders = useMemo( - (): ListHeaderColumnConfig[] => [ - { - label: headersLL.name(), - sortKey: 'name', - enabled: true, - }, - { - label: headersLL.ip(), - key: 'ip', - enabled: false, - }, - { - label: headersLL.ports(), - key: 'ports', - enabled: false, - }, - { - label: headersLL.protocols(), - key: 'protocols', - enabled: false, - }, - { - label: headersLL.rules(), - key: 'rules', - enabled: false, - }, - { - label: headersLL.status(), - key: 'status', - enabled: false, - }, - { - label: headersLL.kind(), - sortKey: 'kind', - enabled: true, - }, - { - label: headersLL.edit(), - key: 'edit', - enabled: false, - }, - ], - [headersLL], - ); - - return ( -
- {header.extras} - {sortedAliases.length === 0 && ( - - )} - {sortedAliases.length > 0 && ( -
-
- - headers={listHeaders} - sortDirection={sortDir} - activeKey={sortKey} - selectAll={allSelected} - onSelectAll={(val) => { - if (selectionEnabled) { - onSelectAll?.(val, selected ?? {}); - } - }} - onChange={(key, dir) => { - setSortKey(key); - setSortDir(dir); - }} - /> -
-
    - {sortedAliases.map((alias) => { - let aliasSelected = false; - if (selected) { - aliasSelected = selected[alias.id] ?? false; - } - return ( -
  • - {!selectionEnabled &&
    } - {selectionEnabled && ( -
    - { - onSelect?.(alias.id, !aliasSelected); - }} - > - - -
    - )} -
    - -
    -
    - -
    -
    - -
    -
    - -
    -
    - -
    -
    - -
    -
    - -
    -
    - -
    -
  • - ); - })} -
-
- )} -
- ); -}; diff --git a/web/src/pages/acl/AclIndexPage/components/AclIndexAliases/modals/AclAliasApplyConfirmModal/AclAliasApplyConfirmModal.tsx b/web/src/pages/acl/AclIndexPage/components/AclIndexAliases/modals/AclAliasApplyConfirmModal/AclAliasApplyConfirmModal.tsx deleted file mode 100644 index eae2e06aa..000000000 --- a/web/src/pages/acl/AclIndexPage/components/AclIndexAliases/modals/AclAliasApplyConfirmModal/AclAliasApplyConfirmModal.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import './style.scss'; - -import { useI18nContext } from '../../../../../../../i18n/i18n-react'; -import { ConfirmModal } from '../../../../../../../shared/defguard-ui/components/Layout/modals/ConfirmModal/ConfirmModal'; - -type Props = { - isOpen: boolean; - setOpen: (val: boolean) => void; - rules: string[]; - onSubmit: () => void; -}; - -export const AclAliasApplyConfirmModal = ({ - isOpen, - onSubmit, - rules, - setOpen, -}: Props) => { - const { LL } = useI18nContext(); - const localLL = LL.acl.listPage.aliases.modals.applyConfirm; - - return ( - { - setOpen(false); - }} - onSubmit={() => { - onSubmit(); - }} - submitText={localLL.submit()} - title={localLL.title()} - > -
-

{localLL.message()}

-

{`${localLL.listLabel()}(${rules.length})`}:

-

{rules.join(', ')}

-
-
- ); -}; diff --git a/web/src/pages/acl/AclIndexPage/components/AclIndexAliases/modals/AclAliasApplyConfirmModal/style.scss b/web/src/pages/acl/AclIndexPage/components/AclIndexAliases/modals/AclAliasApplyConfirmModal/style.scss deleted file mode 100644 index 284adbede..000000000 --- a/web/src/pages/acl/AclIndexPage/components/AclIndexAliases/modals/AclAliasApplyConfirmModal/style.scss +++ /dev/null @@ -1,26 +0,0 @@ -#acl-aliases-apply-confirm-modal { - .title { - color: var(--text-button-primary); - text-align: center; - } - - .content { - display: flex; - flex-flow: column; - row-gap: var(--spacing-xs); - max-width: 100%; - overflow: hidden; - - p { - @include typography(app-body-2); - color: var(--text-button-primary); - text-align: left; - overflow-wrap: break-word; - width: 100%; - - &.rules { - font-weight: 700; - } - } - } -} diff --git a/web/src/pages/acl/AclIndexPage/components/AclIndexAliases/modals/AclAliasDeleteBlockModal/AclAliasDeleteBlockModal.tsx b/web/src/pages/acl/AclIndexPage/components/AclIndexAliases/modals/AclAliasDeleteBlockModal/AclAliasDeleteBlockModal.tsx deleted file mode 100644 index 0cd72d66b..000000000 --- a/web/src/pages/acl/AclIndexPage/components/AclIndexAliases/modals/AclAliasDeleteBlockModal/AclAliasDeleteBlockModal.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import './style.scss'; - -import { useEffect } from 'react'; -import { shallow } from 'zustand/shallow'; - -import { useI18nContext } from '../../../../../../../i18n/i18n-react'; -import { RenderMarkdown } from '../../../../../../../shared/components/Layout/RenderMarkdown/RenderMarkdown'; -import { ConfirmModal } from '../../../../../../../shared/defguard-ui/components/Layout/modals/ConfirmModal/ConfirmModal'; -import { ConfirmModalType } from '../../../../../../../shared/defguard-ui/components/Layout/modals/ConfirmModal/types'; -import { isPresent } from '../../../../../../../shared/defguard-ui/utils/isPresent'; -import { useAclAliasDeleteBlockModal } from './store'; - -export const AclAliasDeleteBlockModal = () => { - const { LL } = useI18nContext(); - const localLL = LL.acl.listPage.aliases.modals.deleteBlock; - const [close, reset] = useAclAliasDeleteBlockModal((s) => [s.close, s.reset], shallow); - const alias = useAclAliasDeleteBlockModal((s) => s.alias); - const rules = useAclAliasDeleteBlockModal((s) => s.rulesNames); - const isOpen = useAclAliasDeleteBlockModal((s) => s.visible); - - // biome-ignore lint/correctness/useExhaustiveDependencies: migration, checkMeLater - useEffect(() => { - return () => { - reset?.(); - }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - return ( - { - close(); - }} - afterClose={() => { - reset(); - }} - > -
- {isPresent(alias) && ( - - )} - {rules.length > 0 &&

{rules.join(', ')}

} -
-
- ); -}; diff --git a/web/src/pages/acl/AclIndexPage/components/AclIndexAliases/modals/AclAliasDeleteBlockModal/store.tsx b/web/src/pages/acl/AclIndexPage/components/AclIndexAliases/modals/AclAliasDeleteBlockModal/store.tsx deleted file mode 100644 index a6685026a..000000000 --- a/web/src/pages/acl/AclIndexPage/components/AclIndexAliases/modals/AclAliasDeleteBlockModal/store.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import { createWithEqualityFn } from 'zustand/traditional'; - -import type { AclAlias } from '../../../../../types'; - -const defaults: StoreValues = { - visible: false, - alias: undefined, - rulesNames: [], -}; - -export const useAclAliasDeleteBlockModal = createWithEqualityFn( - (set) => ({ - ...defaults, - open: (alias, rules) => set({ alias: alias, rulesNames: rules, visible: true }), - close: () => set({ visible: false }), - reset: () => set(defaults), - }), - Object.is, -); - -type Store = StoreValues & StoreMethods; - -type StoreValues = { - visible: boolean; - rulesNames: string[]; - alias?: AclAlias; -}; - -type StoreMethods = { - open: (alias: AclAlias, rules: string[]) => void; - close: () => void; - reset: () => void; -}; diff --git a/web/src/pages/acl/AclIndexPage/components/AclIndexAliases/modals/AclAliasDeleteBlockModal/style.scss b/web/src/pages/acl/AclIndexPage/components/AclIndexAliases/modals/AclAliasDeleteBlockModal/style.scss deleted file mode 100644 index 71223bade..000000000 --- a/web/src/pages/acl/AclIndexPage/components/AclIndexAliases/modals/AclAliasDeleteBlockModal/style.scss +++ /dev/null @@ -1,26 +0,0 @@ -#acl-aliases-delete-alias-block-modal { - .title { - text-align: center; - color: var(--text-button-primary); - } - - .content { - display: flex; - flex-flow: column; - row-gap: var(--spacing-xs); - text-align: left; - - p { - @include typography(app-body-2); - color: var(--text-button-primary); - text-align: left; - - &.rules { - text-wrap: wrap; - max-width: 100%; - overflow-wrap: break-word; - font-weight: 700; - } - } - } -} diff --git a/web/src/pages/acl/AclIndexPage/components/AclIndexAliases/modals/AlcAliasCEModal/AlcAliasCEModal.tsx b/web/src/pages/acl/AclIndexPage/components/AclIndexAliases/modals/AlcAliasCEModal/AlcAliasCEModal.tsx deleted file mode 100644 index 6a68bf790..000000000 --- a/web/src/pages/acl/AclIndexPage/components/AclIndexAliases/modals/AlcAliasCEModal/AlcAliasCEModal.tsx +++ /dev/null @@ -1,216 +0,0 @@ -import './style.scss'; - -import { zodResolver } from '@hookform/resolvers/zod'; -import { useQueryClient } from '@tanstack/react-query'; -import { omit } from 'lodash-es'; -import { useEffect, useMemo } from 'react'; -import { type SubmitHandler, useForm } from 'react-hook-form'; -import { z } from 'zod'; -import { shallow } from 'zustand/shallow'; - -import { useI18nContext } from '../../../../../../../i18n/i18n-react'; -import { FormInput } from '../../../../../../../shared/defguard-ui/components/Form/FormInput/FormInput'; -import { FormSelect } from '../../../../../../../shared/defguard-ui/components/Form/FormSelect/FormSelect'; -import { Button } from '../../../../../../../shared/defguard-ui/components/Layout/Button/Button'; -import { - ButtonSize, - ButtonStyleVariant, -} from '../../../../../../../shared/defguard-ui/components/Layout/Button/types'; -import { ModalWithTitle } from '../../../../../../../shared/defguard-ui/components/Layout/modals/ModalWithTitle/ModalWithTitle'; -import type { SelectOption } from '../../../../../../../shared/defguard-ui/components/Layout/Select/types'; -import { isPresent } from '../../../../../../../shared/defguard-ui/utils/isPresent'; -import useApi from '../../../../../../../shared/hooks/useApi'; -import { useToaster } from '../../../../../../../shared/hooks/useToaster'; -import { QueryKeys } from '../../../../../../../shared/queries'; -import { AclAliasKind } from '../../../../../types'; -import { protocolOptions, protocolToString } from '../../../../../utils'; -import { aclDestinationValidator, aclPortsValidator } from '../../../../../validators'; -import { AclMessageBoxes } from '../../../shared/AclMessageBoxes/AclMessageBoxes'; -import { useAclAliasCEModal } from './store'; - -export const AlcAliasCEModal = () => { - const isOpen = useAclAliasCEModal((s) => s.visible); - const alias = useAclAliasCEModal((s) => s.alias); - const isEdit = isPresent(alias); - - const [close, reset] = useAclAliasCEModal((s) => [s.close, s.reset], shallow); - - // biome-ignore lint/correctness/useExhaustiveDependencies: migration, checkMeLater - useEffect(() => { - return () => { - reset(); - }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - return ( - { - close(); - }} - afterClose={() => { - reset(); - }} - > - - - ); -}; - -const ModalContent = () => { - const queryClient = useQueryClient(); - const closeModal = useAclAliasCEModal((s) => s.close, shallow); - const initialAlias = useAclAliasCEModal((s) => s.alias); - const isEditMode = isPresent(initialAlias); - const toaster = useToaster(); - - const { LL } = useI18nContext(); - const localLL = LL.acl.listPage.aliases.modals.create; - const formErrors = LL.form.error; - const { - acl: { - aliases: { createAlias, editAlias }, - }, - } = useApi(); - - const schema = useMemo( - () => - z.object({ - name: z - .string({ - required_error: formErrors.required(), - }) - .trim() - .min(1, formErrors.required()), - kind: z.nativeEnum(AclAliasKind), - ports: aclPortsValidator(LL), - destination: aclDestinationValidator(LL), - protocols: z.number().array(), - }), - [LL, formErrors], - ); - - type FormFields = z.infer; - - const defaultValues = useMemo((): FormFields => { - let defaultValues: FormFields; - if (isPresent(initialAlias)) { - defaultValues = omit(initialAlias, ['id']); - } else { - defaultValues = { - destination: '', - name: '', - kind: AclAliasKind.DESTINATION, - ports: '', - protocols: [], - }; - } - return defaultValues; - }, [initialAlias]); - - const { - handleSubmit, - control, - formState: { isSubmitting }, - } = useForm({ - mode: 'all', - resolver: zodResolver(schema), - defaultValues, - }); - - const handleValidSubmit: SubmitHandler = async (values) => { - try { - if (isEditMode) { - await editAlias({ - ...values, - id: initialAlias.id, - }); - toaster.success(localLL.messages.modified()); - } else { - await createAlias(values); - toaster.success(localLL.messages.created()); - } - await queryClient.invalidateQueries({ - predicate: (query) => query.queryKey.includes(QueryKeys.FETCH_ACL_ALIASES), - }); - closeModal(); - } catch (e) { - toaster.error(LL.messages.error()); - console.error(e); - } - }; - - const aliasKindOptions = useMemo( - (): SelectOption[] => [ - { - key: AclAliasKind.DESTINATION, - value: AclAliasKind.DESTINATION, - label: localLL.kindOptions.destination(), - }, - { - key: AclAliasKind.COMPONENT, - value: AclAliasKind.COMPONENT, - label: localLL.kindOptions.component(), - }, - ], - [localLL.kindOptions], - ); - - return ( -
- - - -
-

Destination

-
- - - ({ displayValue: protocolToString(val), key: val })} - disposable - /> -
-
- - ); -}; diff --git a/web/src/pages/acl/AclIndexPage/components/AclIndexAliases/modals/AlcAliasCEModal/store.tsx b/web/src/pages/acl/AclIndexPage/components/AclIndexAliases/modals/AlcAliasCEModal/store.tsx deleted file mode 100644 index 72ba8e2b6..000000000 --- a/web/src/pages/acl/AclIndexPage/components/AclIndexAliases/modals/AlcAliasCEModal/store.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import { createWithEqualityFn } from 'zustand/traditional'; - -import type { AclAlias } from '../../../../../types'; - -const defaults: StoreValues = { - visible: false, - alias: undefined, -}; - -export const useAclAliasCEModal = createWithEqualityFn( - (set) => ({ - ...defaults, - open: (vals) => { - if (vals) { - set({ ...defaults, ...vals, visible: true }); - } else { - set({ ...defaults, visible: true }); - } - }, - close: () => { - set({ visible: false }); - }, - reset: () => { - set(defaults); - }, - }), - Object.is, -); - -type Store = StoreValues & StoreMethods; - -type StoreValues = { - visible: boolean; - alias?: AclAlias; -}; - -type StoreMethods = { - open: (vals?: Partial) => void; - close: () => void; - reset: () => void; -}; diff --git a/web/src/pages/acl/AclIndexPage/components/AclIndexAliases/modals/AlcAliasCEModal/style.scss b/web/src/pages/acl/AclIndexPage/components/AclIndexAliases/modals/AlcAliasCEModal/style.scss deleted file mode 100644 index bc944d874..000000000 --- a/web/src/pages/acl/AclIndexPage/components/AclIndexAliases/modals/AlcAliasCEModal/style.scss +++ /dev/null @@ -1,33 +0,0 @@ -#acl-alias-ce-modal { - form { - .input { - padding-bottom: var(--spacing-m); - } - - .select-container { - margin-bottom: var(--spacing-m); - } - - .header { - display: flex; - flex-flow: row; - align-items: center; - justify-content: flex-start; - padding-bottom: var(--spacing-m); - - h2 { - @include typography(app-body-1); - } - } - } - - .spacer { - padding-bottom: var(--spacing-m); - } -} - -.acl-alias-ce-modal-spacing { - @include media-breakpoint-up(lg) { - padding-top: var(--spacing-m) !important; - } -} diff --git a/web/src/pages/acl/AclIndexPage/components/AclIndexAliases/style.scss b/web/src/pages/acl/AclIndexPage/components/AclIndexAliases/style.scss deleted file mode 100644 index a4631311f..000000000 --- a/web/src/pages/acl/AclIndexPage/components/AclIndexAliases/style.scss +++ /dev/null @@ -1,65 +0,0 @@ -@mixin list-row { - display: inline-grid; - grid-template-rows: 1fr; - column-gap: var(--spacing-s); - justify-content: space-between; - align-items: center; - box-sizing: border-box; - - grid-template-columns: 40px 200px repeat(2, 250px) 160px 250px 100px 100px 60px; - - @include media-breakpoint-up(lg) { - grid-template-columns: 40px 0.5fr repeat(2, 0.75fr) 160px 0.75fr 100px 100px 60px; - } - - .cell { - display: inline-flex; - justify-content: flex-start; - align-items: center; - width: 100%; - overflow: hidden; - - &:last-child { - justify-content: center; - } - } - - .select-cell { - align-items: center; - justify-content: center; - width: 100%; - - & > .interaction-box { - width: 18px; - height: 18px; - } - } -} - -.aliases-list { - .list-headers { - @include list-row; - padding-bottom: var(--spacing-xs); - } - - ul { - list-style: none; - display: flex; - flex-flow: column; - row-gap: var(--spacing-s); - - & > li { - @include list-row; - box-sizing: border-box; - padding: 9px 0px; - - @include typography(app-modal-2); - - .cell { - &.name { - @include typography(app-modal-1); - } - } - } - } -} diff --git a/web/src/pages/acl/AclIndexPage/components/AclIndexAliases/types.ts b/web/src/pages/acl/AclIndexPage/components/AclIndexAliases/types.ts deleted file mode 100644 index 3c13d9ea7..000000000 --- a/web/src/pages/acl/AclIndexPage/components/AclIndexAliases/types.ts +++ /dev/null @@ -1,13 +0,0 @@ -import type { AclAlias } from '../../../types'; -import type { ListCellTag } from '../shared/types'; - -export type AclAliasListData = { - display: { - ports: ListCellTag[]; - destination: ListCellTag[]; - protocols: ListCellTag[]; - rules: ListCellTag[]; - }; -} & AclAlias; - -export type AclAliasListSelection = Record; diff --git a/web/src/pages/acl/AclIndexPage/components/AclIndexRules/AclIndexRules.tsx b/web/src/pages/acl/AclIndexPage/components/AclIndexRules/AclIndexRules.tsx deleted file mode 100644 index d277cf280..000000000 --- a/web/src/pages/acl/AclIndexPage/components/AclIndexRules/AclIndexRules.tsx +++ /dev/null @@ -1,891 +0,0 @@ -import './style.scss'; - -import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; -import type { AxiosError } from 'axios'; -import clsx from 'clsx'; -import { concat, intersection, orderBy } from 'lodash-es'; -import { type ReactNode, useCallback, useEffect, useMemo, useState } from 'react'; -import { useNavigate } from 'react-router'; -import { upperCaseFirst } from 'text-case'; - -import { useI18nContext } from '../../../../../i18n/i18n-react'; -import { ListCellTags } from '../../../../../shared/components/Layout/ListCellTags/ListCellTags'; -import { ListHeader } from '../../../../../shared/components/Layout/ListHeader/ListHeader'; -import type { ListHeaderColumnConfig } from '../../../../../shared/components/Layout/ListHeader/types'; -import { FilterGroupsModal } from '../../../../../shared/components/modals/FilterGroupsModal/FilterGroupsModal'; -import type { FilterGroupsModalFilter } from '../../../../../shared/components/modals/FilterGroupsModal/types'; -import { Button } from '../../../../../shared/defguard-ui/components/Layout/Button/Button'; -import { - ButtonSize, - ButtonStyleVariant, -} from '../../../../../shared/defguard-ui/components/Layout/Button/types'; -import { CheckBox } from '../../../../../shared/defguard-ui/components/Layout/Checkbox/CheckBox'; -import { EditButton } from '../../../../../shared/defguard-ui/components/Layout/EditButton/EditButton'; -import { EditButtonOption } from '../../../../../shared/defguard-ui/components/Layout/EditButton/EditButtonOption'; -import { EditButtonOptionStyleVariant } from '../../../../../shared/defguard-ui/components/Layout/EditButton/types'; -import { InteractionBox } from '../../../../../shared/defguard-ui/components/Layout/InteractionBox/InteractionBox'; -import { ListCellText } from '../../../../../shared/defguard-ui/components/Layout/ListCellText/ListCellText'; -import { ListItemCount } from '../../../../../shared/defguard-ui/components/Layout/ListItemCount/ListItemCount'; -import { NoData } from '../../../../../shared/defguard-ui/components/Layout/NoData/NoData'; -import { Search } from '../../../../../shared/defguard-ui/components/Layout/Search/Search'; -import { ListSortDirection } from '../../../../../shared/defguard-ui/components/Layout/VirtualizedList/types'; -import { isPresent } from '../../../../../shared/defguard-ui/utils/isPresent'; -import useApi from '../../../../../shared/hooks/useApi'; -import { useToaster } from '../../../../../shared/hooks/useToaster'; -import { QueryKeys } from '../../../../../shared/queries'; -import type { AclRuleInfo } from '../../../../../shared/types'; -import { useAclLoadedContext } from '../../../acl-context'; -import { - type AclAlias, - AclAliasStatus, - type AclCreateContextLoaded, - AclStatus, -} from '../../../types'; -import { aclRuleToStatusInt, aclStatusToInt } from '../../../utils'; -import { AclListSkeleton } from '../AclListSkeleton/AclListSkeleton'; -import { DeployChangesIcon } from '../DeployChangesIcon'; -import { DividerHeader } from '../shared/DividerHeader'; -import type { ListCellTag } from '../shared/types'; -import { AclRuleStatus } from './components/AclRuleStatus/AclRuleStatus'; -import { AclRulesApplyConfirmModal } from './components/AclRulesApplyConfirmModal/AclRulesApplyConfirmModal'; - -type RulesFilters = { - networks: number[]; - aliases: number[]; - status: number[]; - groups: number[]; -}; - -type ListData = { - context: { - denied: ListCellTag[]; - allowed: ListCellTag[]; - networks: ListCellTag[]; - destination: ListCellTag[]; - }; -} & AclRuleInfo; - -const defaultFilters: RulesFilters = { - aliases: [], - networks: [], - status: [], - groups: [], -}; - -export const AclIndexRules = () => { - const navigate = useNavigate(); - const { - acl: { - rules: { applyRules }, - }, - } = useApi(); - const { LL } = useI18nContext(); - const localLL = LL.acl.listPage.rules; - const messagesLL = LL.acl.listPage.message; - const ruleStatusLL = LL.acl.ruleStatus; - const aclContext = useAclLoadedContext(); - const [filtersOpen, setFiltersOpen] = useState(false); - const [appliedFilters, setAppliedFilters] = useState(defaultFilters); - const appliedFiltersCount = useMemo( - () => Object.values(appliedFilters).reduce((acc, filters) => acc + filters.length, 0), - [appliedFilters], - ); - const [searchValue, setSearchValue] = useState(''); - const [applyConfirmOpen, setApplyConfirmOpen] = useState(false); - const toaster = useToaster(); - const queryClient = useQueryClient(); - - const { mutate: applyPendingChangesMutation, isPending: applyPending } = useMutation({ - mutationFn: applyRules, - onSuccess: () => { - toaster.success(messagesLL.applyChanges()); - void queryClient.invalidateQueries({ - predicate: (query) => query.queryKey.includes(QueryKeys.FETCH_ACL_RULES), - }); - }, - onError: (e) => { - toaster.error(messagesLL.applyFail()); - console.error(e); - }, - }); - - const { - acl: { - rules: { getRules }, - }, - } = useApi(); - - const { data: aclRules, isLoading: rulesLoading } = useQuery({ - queryFn: getRules, - queryKey: [QueryKeys.FETCH_ACL_RULES], - refetchOnMount: true, - }); - - const pendingRulesCount = useMemo(() => { - if (aclRules) { - return aclRules.filter( - (rule) => rule.state !== AclStatus.APPLIED && rule.state !== AclStatus.EXPIRED, - ).length; - } - return 0; - }, [aclRules]); - - const rulesAfterSearch = useMemo(() => { - if (aclRules && searchValue) { - return aclRules.filter((rule) => - rule.name.trim().toLowerCase().includes(searchValue.toLowerCase().trim()), - ); - } - return aclRules ?? []; - }, [aclRules, searchValue]); - - const pendingRules = useMemo( - () => - isPresent(aclRules) - ? prepareDisplay( - rulesAfterSearch, - appliedFilters, - aclContext.aliases, - localLL.list.tags.all(), - localLL.list.tags.all(), - true, - aclContext, - ) - : [], - [aclContext, aclRules, appliedFilters, localLL.list.tags, rulesAfterSearch], - ); - - const [selectedPending, setSelectedPending] = useState>({}); - const handlePendingSelect = useCallback((key: number, value: boolean) => { - setSelectedPending((s) => ({ ...s, [key]: value })); - }, []); - const handlePendingSelectAll = useCallback( - (value: boolean, state: Record) => { - const newState = { ...state }; - for (const key in newState) { - newState[key] = value; - } - setSelectedPending(newState); - }, - [], - ); - const pendingSelectionCount = useMemo(() => { - let count = 0; - for (const key in selectedPending) { - if (selectedPending[key]) count++; - } - return count; - }, [selectedPending]); - - const deployedRules = useMemo(() => { - if (aclRules) { - return prepareDisplay( - rulesAfterSearch, - appliedFilters, - aclContext.aliases, - localLL.list.tags.allAllowed(), - localLL.list.tags.allDenied(), - false, - aclContext, - ); - } - return []; - }, [aclContext, aclRules, appliedFilters, localLL.list.tags, rulesAfterSearch]); - - const displayItemsCount = useMemo( - () => deployedRules.length + pendingRules.length, - [deployedRules.length, pendingRules.length], - ); - - const filters = useMemo(() => { - const res: Record = {}; - const filterLL = localLL.modals.filterGroupsModal.groupHeaders; - res.groups = { - identifier: 'id', - label: filterLL.groups(), - order: 3, - items: aclContext.groups.map((group) => ({ - label: group.name, - searchValues: [group.name], - value: group.id, - })), - }; - res.networks = { - label: filterLL.location(), - order: 1, - identifier: 'id', - items: aclContext.networks.map((network) => ({ - label: network.name, - searchValues: [network.name], - value: network.id, - })), - }; - res.aliases = { - identifier: 'id', - label: filterLL.alias(), - order: 2, - items: aclContext.aliases - .filter((alias) => alias.state === AclAliasStatus.APPLIED) - .map((alias) => ({ - label: alias.name, - searchValues: [alias.name], - value: alias.id, - })), - }; - - res.status = { - identifier: 'value', - label: filterLL.status(), - order: 4, - items: [ - { - label: ruleStatusLL.enabled(), - value: 1000, - searchValues: [ruleStatusLL.enabled()], - }, - { - label: ruleStatusLL.disabled(), - value: 999, - searchValues: [ruleStatusLL.disabled()], - }, - { - label: ruleStatusLL.new(), - value: aclStatusToInt(AclStatus.NEW), - searchValues: [ruleStatusLL.new()], - }, - { - label: ruleStatusLL.modified(), - value: aclStatusToInt(AclStatus.MODIFIED), - searchValues: [ruleStatusLL.modified()], - }, - { - label: ruleStatusLL.deleted(), - value: aclStatusToInt(AclStatus.DELETED), - searchValues: [ruleStatusLL.deleted()], - }, - { - label: ruleStatusLL.expired(), - value: aclStatusToInt(AclStatus.EXPIRED), - searchValues: [ruleStatusLL.expired()], - }, - ], - }; - return res; - }, [ - aclContext.aliases, - aclContext.groups, - aclContext.networks, - localLL.modals.filterGroupsModal.groupHeaders, - ruleStatusLL, - ]); - - const controlFilterDisplay = useMemo(() => { - return appliedFiltersCount - ? localLL.listControls.filter.applied({ count: appliedFiltersCount }) - : localLL.listControls.filter.nothingApplied(); - }, [appliedFiltersCount, localLL.listControls.filter]); - - const filtersPresent = appliedFiltersCount > 0; - - const applyText = useMemo(() => { - if (pendingSelectionCount) { - return localLL.listControls.apply.selective({ count: pendingSelectionCount }); - } - if (pendingRulesCount) { - return localLL.listControls.apply.all({ count: pendingRulesCount }); - } - return localLL.listControls.apply.noChanges(); - }, [localLL.listControls.apply, pendingRulesCount, pendingSelectionCount]); - - const handleApply = useCallback(() => { - if (aclRules) { - if (pendingSelectionCount === 0) { - const rulesToApply = aclRules - .filter( - (rule) => - rule.state !== AclStatus.APPLIED && rule.state !== AclStatus.EXPIRED, - ) - .map((rule) => rule.id); - applyPendingChangesMutation(rulesToApply); - } else { - const rulesToApply: number[] = []; - for (const key in selectedPending) { - if (selectedPending[key]) { - rulesToApply.push(Number(key)); - } - } - applyPendingChangesMutation(rulesToApply); - } - } - }, [aclRules, applyPendingChangesMutation, pendingSelectionCount, selectedPending]); - - // update or build selection state for list when rules are done loading - useEffect(() => { - if (aclRules) { - const pending = aclRules.filter((rule) => rule.state !== AclStatus.APPLIED); - const selectionEntries = Object.keys(selectedPending).length; - if (selectionEntries !== pending.length) { - const newSelectionState: Record = {}; - for (const rule of pending) { - newSelectionState[rule.id] = newSelectionState[rule.id] ?? false; - } - setSelectedPending(newSelectionState); - } - } - }, [aclRules, selectedPending]); - - return ( -
-
-

Rules

- - { - setSearchValue(searchChange); - }} - /> -
-
-
- {rulesLoading && } - {!rulesLoading && ( - <> - - - - )} - { - setFiltersOpen(false); - }} - onSubmit={(vals) => { - setAppliedFilters(vals as RulesFilters); - setFiltersOpen(false); - }} - /> - -
- ); -}; - -type RulesListProps = { - data: ListData[]; - header: { - text: string; - extras?: ReactNode; - }; - noDataMessage: string; - isAppliedList?: boolean; - selected?: Record; - allSelected?: boolean; - onSelect?: (key: number, value: boolean) => void; - onSelectAll?: (value: boolean, state: Record) => void; -}; - -const RulesList = ({ - data, - header, - noDataMessage, - selected, - allSelected, - onSelect, - onSelectAll, -}: RulesListProps) => { - const { LL } = useI18nContext(); - const headersLL = LL.acl.listPage.rules.list.headers; - const [sortKey, setSortKey] = useState('name'); - const [sortDir, setSortDir] = useState(ListSortDirection.ASC); - - const selectionEnabled = useMemo( - () => - isPresent(onSelect) && - isPresent(onSelectAll) && - isPresent(selected) && - isPresent(allSelected), - [onSelect, onSelectAll, selected, allSelected], - ); - - const sortedRules = useMemo( - () => orderBy(data, [sortKey], [sortDir.valueOf().toLowerCase() as 'asc' | 'desc']), - [data, sortDir, sortKey], - ); - - const listHeaders = useMemo( - (): ListHeaderColumnConfig[] => [ - { - label: headersLL.name(), - sortKey: 'name', - enabled: true, - }, - { - label: headersLL.destination(), - sortKey: 'destination', - enabled: false, - }, - { - label: headersLL.allowed(), - key: 'allowed', - enabled: false, - }, - { - label: headersLL.denied(), - key: 'denied', - enabled: false, - }, - { - label: headersLL.locations(), - key: 'networks', - enabled: false, - }, - { - label: headersLL.status(), - key: 'status', - enabled: false, - }, - { - label: headersLL.edit(), - key: 'edit', - enabled: false, - }, - ], - [headersLL], - ); - - return ( -
- {header.extras} - {sortedRules.length === 0 && ( - - )} - {sortedRules.length > 0 && ( -
-
- - headers={listHeaders} - sortDirection={sortDir} - activeKey={sortKey} - selectAll={allSelected} - onSelectAll={(val) => { - if (selectionEnabled) { - onSelectAll?.(val, selected ?? {}); - } - }} - onChange={(key, dir) => { - setSortKey(key); - setSortDir(dir); - }} - /> -
-
    - {sortedRules.map((rule) => { - let ruleSelected = false; - if (selected) { - ruleSelected = selected[rule.id] ?? false; - } - return ( -
  • - {!selectionEnabled &&
    } - {selectionEnabled && ( -
    - { - onSelect?.(rule.id, !ruleSelected); - }} - > - - -
    - )} -
    - -
    -
    - -
    -
    - -
    -
    - -
    -
    - -
    -
    - -
    -
    - -
    -
  • - ); - })} -
-
- )} -
- ); -}; - -type EditProps = { - rule: ListData; -}; - -const RuleEditButton = ({ rule }: EditProps) => { - const queryClient = useQueryClient(); - const isApplied = rule.state === AclStatus.APPLIED; - const isDeleted = rule.state === AclStatus.DELETED; - const { LL } = useI18nContext(); - const localLL = LL.acl.listPage.rules.list.editMenu; - const statusLL = LL.acl.ruleStatus; - const toaster = useToaster(); - - const { - acl: { - rules: { deleteRule, editRule }, - }, - } = useApi(); - - const invalidateQueries = useCallback(() => { - void queryClient.invalidateQueries({ - predicate: (query) => query.queryKey.includes(QueryKeys.FETCH_ACL_RULES), - }); - }, [queryClient]); - - const handleError = useCallback( - (err: AxiosError) => { - toaster.error(LL.acl.listPage.message.changeFail()); - console.error(err.message ?? err); - }, - [LL.acl.listPage.message, toaster], - ); - - const { mutate: editRuleMutation, isPending: editPending } = useMutation({ - mutationFn: editRule, - mutationKey: ['rule', 'delete', rule.id], - onSuccess: () => { - invalidateQueries(); - toaster.success(LL.acl.listPage.message.changeAdded()); - }, - onError: handleError, - }); - - const { mutate: deleteRuleMutation, isPending: deletionPending } = useMutation({ - mutationFn: deleteRule, - onSuccess: () => { - invalidateQueries(); - if (isApplied) { - toaster.success(LL.acl.listPage.message.changeAdded()); - } else { - toaster.success(LL.acl.listPage.message.changeDiscarded()); - } - }, - onError: handleError, - }); - - const handleEnableChange = useCallback( - (newState: boolean, rule: AclRuleInfo | ListData) => { - editRuleMutation({ ...rule, enabled: newState, expires: rule.expires ?? null }); - }, - [editRuleMutation], - ); - - const navigate = useNavigate(); - - return ( - - {!isDeleted && ( - { - navigate(`/admin/acl/form?edit=1&rule=${rule.id}`); - }} - /> - )} - {isApplied && ( - <> - {!rule.enabled && ( - { - handleEnableChange(true, rule); - }} - /> - )} - {rule.enabled && ( - { - handleEnableChange(false, rule); - }} - /> - )} - - )} - { - deleteRuleMutation(rule.id); - }} - /> - - ); -}; - -const prepareDisplay = ( - aclRules: AclRuleInfo[], - appliedFilters: RulesFilters, - aliases: AclAlias[], - allAllowedLabel: string, - allDeniedLabel: string, - pending: boolean, - aclContext: Omit, -): ListData[] => { - let rules: AclRuleInfo[]; - - if (pending) { - rules = aclRules.filter( - (rule) => rule.state !== AclStatus.APPLIED && rule.state !== AclStatus.EXPIRED, - ); - } else { - rules = aclRules.filter( - (rule) => rule.state === AclStatus.APPLIED || rule.state === AclStatus.EXPIRED, - ); - } - - rules = rules.filter((rule) => { - const filterChecks: boolean[] = []; - if (appliedFilters.status.length) { - filterChecks.push(appliedFilters.status.includes(aclRuleToStatusInt(rule))); - } - if (appliedFilters.networks.length && !rule.all_networks) { - filterChecks.push(intersection(rule.networks, appliedFilters.networks).length > 0); - } - if (appliedFilters.aliases.length) { - filterChecks.push(intersection(rule.aliases, appliedFilters.aliases).length > 0); - } - if (appliedFilters.groups.length) { - const groups = concat(rule.denied_groups, rule.allowed_groups); - filterChecks.push(intersection(groups, appliedFilters.groups).length > 0); - } - return !filterChecks.includes(false); - }); - - const listData: ListData[] = rules.map((rule) => { - let allowed: ListCellTag[]; - let denied: ListCellTag[]; - let networks: ListCellTag[]; - - if (rule.allow_all_users) { - allowed = [{ key: 'all', label: allAllowedLabel, displayAsTag: false }]; - } else { - allowed = concat( - aclContext.users - .filter((u) => rule.allowed_users.includes(u.id)) - .map((u) => ({ - key: `user-${u.id}`, - label: u.username, - displayAsTag: true, - })), - aclContext.groups - .filter((g) => rule.allowed_groups.includes(g.id)) - .map((group) => ({ - key: `group-${group.id}`, - label: group.name, - displayAsTag: true, - })), - aclContext.devices - .filter((device) => rule.allowed_devices.includes(device.id)) - .map((device) => ({ - key: `device-${device.id}`, - label: device.name, - displayAsTag: true, - })), - ); - } - - if (rule.deny_all_users) { - denied = [{ key: 'all', label: allDeniedLabel, displayAsTag: false }]; - } else { - denied = concat( - aclContext.users - .filter((u) => rule.denied_users.includes(u.id)) - .map((user) => ({ - key: `user-${user.id}`, - label: user.username, - displayAsTag: true, - })), - aclContext.groups - .filter((g) => rule.denied_groups.includes(g.id)) - .map((group) => ({ - key: `group-${group.id}`, - label: group.name, - displayAsTag: true, - })), - aclContext.devices - .filter((device) => rule.denied_devices.includes(device.id)) - .map((device) => ({ - key: `device-${device.id}`, - label: device.name, - displayAsTag: true, - })), - ); - } - - if (rule.all_networks) { - networks = [ - { - key: 'all', - label: allAllowedLabel, - }, - ]; - } else { - networks = aclContext.networks - .filter((network) => rule.networks.includes(network.id)) - .map((network) => ({ - key: network.id, - label: network.name, - displayAsTag: true, - })); - } - - const destination: ListCellTag[] = concat( - aliases - .filter((alias) => rule.aliases.includes(alias.id)) - .map((alias) => ({ - key: `alias-${alias.id}`, - label: alias.name, - displayAsTag: true, - })), - rule.destination - .split(',') - .filter((s) => s !== '') - .map((dest, index) => ({ - key: `rule-destination-${index}`, - label: dest, - displayAsTag: false, - })), - ); - - return { - ...rule, - context: { - allowed, - denied, - destination, - networks, - }, - }; - }); - - return listData; -}; diff --git a/web/src/pages/acl/AclIndexPage/components/AclIndexRules/components/AclRuleStatus/AclRuleStatus.tsx b/web/src/pages/acl/AclIndexPage/components/AclIndexRules/components/AclRuleStatus/AclRuleStatus.tsx deleted file mode 100644 index 30b4d8ace..000000000 --- a/web/src/pages/acl/AclIndexPage/components/AclIndexRules/components/AclRuleStatus/AclRuleStatus.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import './style.scss'; - -import clsx from 'clsx'; -import { useMemo } from 'react'; - -import { useI18nContext } from '../../../../../../../i18n/i18n-react'; -import { ActivityIcon } from '../../../../../../../shared/defguard-ui/components/icons/ActivityIcon/ActivityIcon'; -import { ActivityIconVariant } from '../../../../../../../shared/defguard-ui/components/icons/ActivityIcon/types'; -import { AclStatus } from '../../../../../types'; - -type Props = { - status: AclStatus; - enabled: boolean; -}; - -export const AclRuleStatus = ({ enabled, status }: Props) => { - const { LL } = useI18nContext(); - const statusLL = LL.acl.ruleStatus; - - const [label, iconStatus] = useMemo(() => { - if (status === AclStatus.APPLIED) { - switch (enabled) { - case true: - return [statusLL.enabled(), ActivityIconVariant.CONNECTED]; - case false: - return [statusLL.disabled(), ActivityIconVariant.DISCONNECTED]; - } - } - switch (status) { - case AclStatus.DELETED: - return [statusLL.deleted(), ActivityIconVariant.ERROR]; - case AclStatus.NEW: - return [statusLL.new(), ActivityIconVariant.CONNECTED]; - case AclStatus.MODIFIED: - return [statusLL.modified(), ActivityIconVariant.DISCONNECTED]; - case AclStatus.EXPIRED: - return [statusLL.expired(), ActivityIconVariant.DISABLED]; - default: - return [statusLL.new(), ActivityIconVariant.CONNECTED]; - } - }, [enabled, status, statusLL]); - - return ( -
-

{label}

- -
- ); -}; diff --git a/web/src/pages/acl/AclIndexPage/components/AclIndexRules/components/AclRuleStatus/style.scss b/web/src/pages/acl/AclIndexPage/components/AclIndexRules/components/AclRuleStatus/style.scss deleted file mode 100644 index 64a3f3242..000000000 --- a/web/src/pages/acl/AclIndexPage/components/AclIndexRules/components/AclRuleStatus/style.scss +++ /dev/null @@ -1,46 +0,0 @@ -.acl-rule-status { - width: 100%; - max-width: 100%; - display: inline-flex; - flex-flow: row nowrap; - column-gap: 5px; - user-select: none; - overflow: hidden; - align-items: center; - justify-content: flex-start; - - & > p { - @include typography(app-modal-3); - color: inherit; - max-width: calc(100% - 13px); - overflow: hidden; - - @include text-overflow-dots; - } - - &.status-new { - color: var(--text-positive); - } - - &.status-modified { - color: var(--text-important); - } - - &.status-deleted { - color: var(--text-alert); - } - - &.status-applied { - &.disabled { - color: var(--text-important); - } - - &.enabled { - color: var(--text-positive); - } - } - - &.status-expired { - color: var(--text-body-tertiary); - } -} diff --git a/web/src/pages/acl/AclIndexPage/components/AclIndexRules/components/AclRulesApplyConfirmModal/AclRulesApplyConfirmModal.tsx b/web/src/pages/acl/AclIndexPage/components/AclIndexRules/components/AclRulesApplyConfirmModal/AclRulesApplyConfirmModal.tsx deleted file mode 100644 index d7bd89e3a..000000000 --- a/web/src/pages/acl/AclIndexPage/components/AclIndexRules/components/AclRulesApplyConfirmModal/AclRulesApplyConfirmModal.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import { useI18nContext } from '../../../../../../../i18n/i18n-react'; -import { ConfirmModal } from '../../../../../../../shared/defguard-ui/components/Layout/modals/ConfirmModal/ConfirmModal'; - -type Props = { - isOpen: boolean; - setOpen: (val: boolean) => void; - onSubmit: () => void; - changesCount: number; -}; - -export const AclRulesApplyConfirmModal = ({ - isOpen, - setOpen, - onSubmit, - changesCount, -}: Props) => { - const { LL } = useI18nContext(); - const localLL = LL.acl.listPage.rules.modals.applyConfirm; - const close = () => setOpen(false); - - return ( - { - close(); - }} - onSubmit={() => { - onSubmit(); - close(); - }} - /> - ); -}; diff --git a/web/src/pages/acl/AclIndexPage/components/AclIndexRules/style.scss b/web/src/pages/acl/AclIndexPage/components/AclIndexRules/style.scss deleted file mode 100644 index 5b5638f82..000000000 --- a/web/src/pages/acl/AclIndexPage/components/AclIndexRules/style.scss +++ /dev/null @@ -1,111 +0,0 @@ -@mixin grid-config { - box-sizing: border-box; - display: inline-grid; - grid-template-rows: 1fr; - justify-content: space-between; - padding: 0 var(--spacing-s) 0 0; - column-gap: var(--spacing-xs); - - grid-template-columns: 40px 250px 350px repeat(3, 250px) 100px 40px; - - @include media-breakpoint-up(lg) { - grid-template-columns: 40px 1fr 1.25fr repeat(3, 1fr) 100px 45px; - } - - & > .cell { - display: inline-flex; - justify-content: flex-start; - align-items: center; - width: 100%; - overflow: hidden; - - &:last-child { - justify-content: center; - } - } - - .select-cell { - align-items: center; - justify-content: center; - width: 100%; - - & > .interaction-box { - width: 18px; - height: 18px; - } - } -} - -#acl-rules { - width: 100%; - - .rules-list { - width: 100%; - - &:not(:last-child) { - padding-bottom: var(--spacing-s); - } - - .list-headers { - @include grid-config; - } - - .header-track { - display: flex; - flex-flow: row; - align-items: center; - justify-content: flex-start; - column-gap: var(--spacing-xs); - padding-bottom: var(--spacing-xs); - - .select-cell { - width: 40px; - height: 22px; - display: inline-flex; - flex-flow: row; - align-items: center; - justify-content: center; - - .interaction-box { - width: 18px; - height: 18px; - } - } - } - - .list-container { - overflow: auto; - position: relative; - max-height: 600px; - scrollbar-gutter: stable; - - & > .header-track { - position: sticky; - top: 0; - background-color: var(--surface-default-modal); - z-index: 2; - } - } - - ul { - list-style: none; - display: flex; - flex-flow: column; - row-gap: var(--spacing-m); - - li { - @include grid-config(); - - .btn { - width: 100%; - } - - .cell { - &.name { - @include typography(app-modal-1); - } - } - } - } - } -} diff --git a/web/src/pages/acl/AclIndexPage/components/AclListSkeleton/AclListSkeleton.tsx b/web/src/pages/acl/AclIndexPage/components/AclListSkeleton/AclListSkeleton.tsx deleted file mode 100644 index 474c94a22..000000000 --- a/web/src/pages/acl/AclIndexPage/components/AclListSkeleton/AclListSkeleton.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import './style.scss'; - -import { range } from 'lodash-es'; -import Skeleton from 'react-loading-skeleton'; - -export const AclListSkeleton = () => { - return ( -
- {range(10).map((val) => ( - - ))} -
- ); -}; diff --git a/web/src/pages/acl/AclIndexPage/components/AclListSkeleton/style.scss b/web/src/pages/acl/AclIndexPage/components/AclListSkeleton/style.scss deleted file mode 100644 index b23c71c50..000000000 --- a/web/src/pages/acl/AclIndexPage/components/AclListSkeleton/style.scss +++ /dev/null @@ -1,11 +0,0 @@ -.acl-list-skeleton { - display: flex; - flex-flow: column; - row-gap: var(--spacing-s); - width: 100%; - - .react-loading-skeleton { - border-radius: 10px; - height: 60px; - } -} diff --git a/web/src/pages/acl/AclIndexPage/components/DeployChangesIcon.tsx b/web/src/pages/acl/AclIndexPage/components/DeployChangesIcon.tsx deleted file mode 100644 index 36a09072d..000000000 --- a/web/src/pages/acl/AclIndexPage/components/DeployChangesIcon.tsx +++ /dev/null @@ -1,20 +0,0 @@ -export const DeployChangesIcon = () => { - return ( - - - - - ); -}; diff --git a/web/src/pages/acl/AclIndexPage/components/shared/AclAliasKindIcon.tsx b/web/src/pages/acl/AclIndexPage/components/shared/AclAliasKindIcon.tsx deleted file mode 100644 index 9a5c3cba4..000000000 --- a/web/src/pages/acl/AclIndexPage/components/shared/AclAliasKindIcon.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import { AclAliasKind } from '../../../types'; - -type Props = { - kind: AclAliasKind; -}; -export const AclAliasKindIcon = ({ kind }: Props) => { - switch (kind) { - case AclAliasKind.COMPONENT: - return ( - - - - - - - ); - case AclAliasKind.DESTINATION: - return ( - - - - - - - - ); - } -}; diff --git a/web/src/pages/acl/AclIndexPage/components/shared/AclMessageBoxes/AclMessageBoxes.tsx b/web/src/pages/acl/AclIndexPage/components/shared/AclMessageBoxes/AclMessageBoxes.tsx deleted file mode 100644 index fa424e96e..000000000 --- a/web/src/pages/acl/AclIndexPage/components/shared/AclMessageBoxes/AclMessageBoxes.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import './style.scss'; - -import { useI18nContext } from '../../../../../../i18n/i18n-react'; -import { RenderMarkdown } from '../../../../../../shared/components/Layout/RenderMarkdown/RenderMarkdown'; -import { MessageBox } from '../../../../../../shared/defguard-ui/components/Layout/MessageBox/MessageBox'; -import { MessageBoxType } from '../../../../../../shared/defguard-ui/components/Layout/MessageBox/types'; -import { AclAliasKind, NetworkAccessType } from '../../../../types'; -import { AclAliasKindIcon } from '../AclAliasKindIcon'; -import { NetworkAccessTypeIcon } from '../NetworkAccessTypeIcon'; - -type Props = { - message: 'acl-alias-kind' | 'acl-network-access'; - dismissable?: boolean; -}; - -export const AclMessageBoxes = ({ message, dismissable = true }: Props) => { - const { LL } = useI18nContext(); - const aliasKindLL = LL.acl.messageBoxes.aclAliasKind; - const networkAccessLL = LL.acl.messageBoxes.networkSelectionIndicatorsHelper; - - switch (message) { - case 'acl-alias-kind': - return ( - -
    -
  • - -

    {`${aliasKindLL.destination.name()} — ${aliasKindLL.destination.description()}`}

    -
  • -
  • - -

    {`${aliasKindLL.component.name()} — ${aliasKindLL.component.description()}`}

    -
  • -
-
- ); - case 'acl-network-access': - return ( - -
    -
  • - -

    - -
  • -
  • - -

    - -
  • -
  • - -

    - -
  • -
-
- ); - } -}; diff --git a/web/src/pages/acl/AclIndexPage/components/shared/AclMessageBoxes/style.scss b/web/src/pages/acl/AclIndexPage/components/shared/AclMessageBoxes/style.scss deleted file mode 100644 index 7ef4f2cdb..000000000 --- a/web/src/pages/acl/AclIndexPage/components/shared/AclMessageBoxes/style.scss +++ /dev/null @@ -1,18 +0,0 @@ -.acl-explain-message-box { - ul { - width: 100%; - display: flex; - flex-flow: column; - row-gap: var(--spacing-xs); - - li { - display: inline-flex; - flex-flow: row nowrap; - align-items: center; - - svg { - display: inline-block; - } - } - } -} diff --git a/web/src/pages/acl/AclIndexPage/components/shared/DividerHeader.tsx b/web/src/pages/acl/AclIndexPage/components/shared/DividerHeader.tsx deleted file mode 100644 index 86243ea08..000000000 --- a/web/src/pages/acl/AclIndexPage/components/shared/DividerHeader.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import type { PropsWithChildren } from 'react'; - -type DividerHeaderProps = { - text: string; -} & PropsWithChildren; - -export const DividerHeader = ({ text, children }: DividerHeaderProps) => { - return ( -
-
-

{text}

- {children} -
-
- ); -}; diff --git a/web/src/pages/acl/AclIndexPage/components/shared/NetworkAccessTypeIcon.tsx b/web/src/pages/acl/AclIndexPage/components/shared/NetworkAccessTypeIcon.tsx deleted file mode 100644 index f0e545e19..000000000 --- a/web/src/pages/acl/AclIndexPage/components/shared/NetworkAccessTypeIcon.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import { NetworkAccessType } from '../../../types'; - -type Props = { - type: NetworkAccessType; -}; - -export const NetworkAccessTypeIcon = ({ type }: Props) => { - switch (type) { - case NetworkAccessType.ALLOWED: - return ( - - - - ); - case NetworkAccessType.DENIED: - return ( - - - - - ); - case NetworkAccessType.UNMANAGED: - return ( - - - - ); - } -}; diff --git a/web/src/pages/acl/AclIndexPage/components/shared/types.ts b/web/src/pages/acl/AclIndexPage/components/shared/types.ts deleted file mode 100644 index b30a13c1a..000000000 --- a/web/src/pages/acl/AclIndexPage/components/shared/types.ts +++ /dev/null @@ -1,5 +0,0 @@ -export type ListCellTag = { - key: string | number; - label: string; - displayAsTag?: boolean; -}; diff --git a/web/src/pages/acl/AclIndexPage/style.scss b/web/src/pages/acl/AclIndexPage/style.scss deleted file mode 100644 index 3a70698f4..000000000 --- a/web/src/pages/acl/AclIndexPage/style.scss +++ /dev/null @@ -1,105 +0,0 @@ -#acl-index-page { - #content-card { - background-color: var(--surface-default-modal); - border-radius: 15px; - box-sizing: border-box; - padding: var(--spacing-s); - border-top-left-radius: 0; - - & > div { - & > header { - width: 100%; - display: flex; - flex-flow: row; - align-items: center; - justify-content: flex-start; - column-gap: var(--spacing-xs); - padding-bottom: var(--spacing-m); - - & > .controls { - margin-left: auto; - display: flex; - flex-flow: row; - align-items: center; - justify-content: flex-end; - column-gap: var(--spacing-s); - - .btn.filter { - svg path { - @include animate-standard; - transition-property: fill; - fill: var(--text-button-primary); - } - - &:hover { - svg path { - fill: var(--surface-main-primary); - } - } - } - } - } - } - - .divider-header { - padding-bottom: var(--spacing-s); - - .inner { - display: flex; - flex-flow: row; - align-items: center; - justify-content: flex-start; - border-bottom: 1px solid var(--border-primary); - } - - .header { - @include typography(app-side-bar); - } - } - } - - .page-content { - & > header { - padding-bottom: var(--spacing-l); - } - } - - .no-data { - @include typography(app-side-bar); - color: var(--text-body-tertiary); - margin: var(--spacing-s) 0; - } -} - -#acl-index-page { - .acl-list { - width: 100%; - - &:not(:last-child) { - padding-bottom: var(--spacing-s); - } - - .header-track { - display: flex; - flex-flow: row; - align-items: center; - justify-content: flex-start; - column-gap: var(--spacing-xs); - padding-bottom: var(--spacing-xs); - - .select-cell { - width: 40px; - height: 22px; - display: inline-flex; - flex-flow: row; - align-items: center; - justify-content: center; - - .interaction-box { - width: 18px; - height: 18px; - } - } - } - } -} diff --git a/web/src/pages/acl/AclRoutes.tsx b/web/src/pages/acl/AclRoutes.tsx deleted file mode 100644 index 9c83f8089..000000000 --- a/web/src/pages/acl/AclRoutes.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import type { PropsWithChildren } from 'react'; -import { Route, Routes } from 'react-router'; -import { AclCreateDataProvider } from './AclCreateDataProvider'; -import { AlcCreatePage } from './AclCreatePage/AclCreatePage'; -import { AclIndexPage } from './AclIndexPage/AclIndexPage'; -import { AclCreateTrackedProvider } from './acl-context'; - -const AclProvide = ({ children }: PropsWithChildren) => { - return ( - - {children} - - ); -}; - -export const AclRoutes = () => { - return ( - - - - - } - /> - - - - } - /> - - ); -}; diff --git a/web/src/pages/acl/acl-context.tsx b/web/src/pages/acl/acl-context.tsx deleted file mode 100644 index 6713bb076..000000000 --- a/web/src/pages/acl/acl-context.tsx +++ /dev/null @@ -1,64 +0,0 @@ -/* eslint-disable react-refresh/only-export-components */ -import { useCallback, useState } from 'react'; -import { createContainer } from 'react-tracked'; - -import type { AclCreateContext, AclCreateContextLoaded } from './types'; - -const init: AclCreateContext = { - devices: undefined, - groups: undefined, - networks: undefined, - users: undefined, - editRule: undefined, -}; - -const useValue = () => useState(init); - -const { - useUpdate, - Provider: AclCreateTrackedProvider, - useTrackedState, - useSelector: useAclCreateSelector, -} = createContainer(useValue); - -const useUpdateAclCreateContext = () => { - const updateInner = useUpdate(); - - const update = useCallback( - (values: Partial) => { - updateInner((s) => ({ ...s, ...values })); - }, - [updateInner], - ); - - return update; -}; - -const useAclLoadedContext = () => { - const { devices, groups, networks, users, editRule, aliases } = useTrackedState(); - - if ( - devices === undefined || - groups === undefined || - networks === undefined || - users === undefined || - aliases === undefined - ) { - throw Error('Use of ACL data before it was loaded'); - } - return { - devices, - groups, - networks, - users, - aliases, - ruleToEdit: editRule, - } as AclCreateContextLoaded; -}; - -export { - AclCreateTrackedProvider, - useAclCreateSelector, - useAclLoadedContext, - useUpdateAclCreateContext, -}; diff --git a/web/src/pages/acl/types.ts b/web/src/pages/acl/types.ts deleted file mode 100644 index aeb6b6a96..000000000 --- a/web/src/pages/acl/types.ts +++ /dev/null @@ -1,73 +0,0 @@ -import type { - AclRuleInfo, - GroupInfo, - Network, - StandaloneDevice, - User, -} from '../../shared/types'; - -export type AclCreateContext = { - groups?: GroupInfo[]; - users?: User[]; - devices?: StandaloneDevice[]; - networks?: Network[]; - editRule?: AclRuleInfo; - aliases?: AclAlias[]; -}; - -export type AclCreateContextLoaded = { - groups: GroupInfo[]; - users: User[]; - devices: StandaloneDevice[]; - networks: Network[]; - aliases: AclAlias[]; - ruleToEdit?: AclRuleInfo; -}; - -export type AclAlias = { - id: number; - name: string; - kind: AclAliasKind; - state: AclAliasStatus; - destination: string; - ports: string; - protocols: AclProtocol[]; - rules: number[]; -}; - -export type AclAliasPost = Omit; - -export enum AclProtocol { - TCP = 6, - UDP = 17, - ICMP = 1, -} - -export enum AclStatus { - NEW = 'New', - APPLIED = 'Applied', - MODIFIED = 'Modified', - DELETED = 'Deleted', - EXPIRED = 'Expired', -} - -export enum AclAliasStatus { - APPLIED = AclStatus.APPLIED, - MODIFIED = AclStatus.MODIFIED, -} - -export enum AclKind { - DESTINATION = 'Destination', - COMPONENT = 'Component', -} - -export enum AclAliasKind { - DESTINATION = AclKind.DESTINATION, - COMPONENT = AclKind.COMPONENT, -} - -export enum NetworkAccessType { - ALLOWED, - DENIED, - UNMANAGED, -} diff --git a/web/src/pages/acl/utils.ts b/web/src/pages/acl/utils.ts deleted file mode 100644 index abbca3b6f..000000000 --- a/web/src/pages/acl/utils.ts +++ /dev/null @@ -1,141 +0,0 @@ -import type { SelectOption } from '../../shared/defguard-ui/components/Layout/Select/types'; -import type { AclRuleInfo, Network } from '../../shared/types'; -import type { ListCellTag } from './AclIndexPage/components/shared/types'; -import { AclAliasStatus, AclProtocol, AclStatus, NetworkAccessType } from './types'; - -// used by acl rules index page, bcs we don't show Applied in UI but instead enabled / disabled when state is "applied" -export const aclRuleToStatusInt = (rule: AclRuleInfo): number => { - const status = rule.state; - if (status === AclStatus.APPLIED) { - if (rule.enabled) { - return 1000; - } else { - return 999; - } - } - return aclStatusToInt(rule.state); -}; - -export const aclStatusToInt = (status: AclStatus): number => { - switch (status) { - case AclStatus.NEW: - return 0; - case AclStatus.MODIFIED: - return 1; - case AclStatus.APPLIED: - return 2; - case AclStatus.DELETED: - return 3; - case AclStatus.EXPIRED: - return 4; - } -}; - -export const aclAliasStatusToInt = (status: AclAliasStatus): number => { - switch (status) { - case AclAliasStatus.APPLIED: - return 2; - case AclAliasStatus.MODIFIED: - return 1; - } -}; - -export const aclStatusFromInt = (statusInt: number): AclStatus => { - switch (statusInt) { - case 0: - return AclStatus.NEW; - case 1: - return AclStatus.MODIFIED; - case 2: - return AclStatus.APPLIED; - case 3: - return AclStatus.DELETED; - default: - throw Error(`Mapping ACL Rule from int failed ! Unrecognized int of ${statusInt}`); - } -}; - -export const aclAliasStatusFromInt = (statusInt: number): AclAliasStatus => { - switch (statusInt) { - case 1: - return AclAliasStatus.APPLIED; - case 2: - return AclAliasStatus.MODIFIED; - default: - throw Error(`Unexpected alias status code of ${statusInt}`); - } -}; - -export const protocolToString = (value: AclProtocol): string => { - switch (value) { - case AclProtocol.TCP: - return 'TCP'; - case AclProtocol.UDP: - return 'UDP'; - case AclProtocol.ICMP: - return 'ICMP'; - } -}; - -export const protocolOptions: SelectOption[] = [ - { - key: AclProtocol.TCP, - label: 'TCP', - value: AclProtocol.TCP, - }, - { - key: AclProtocol.UDP, - label: 'UDP', - value: AclProtocol.UDP, - }, - { - key: AclProtocol.ICMP, - label: 'ICMP', - value: AclProtocol.ICMP, - }, -]; - -export const aclDestinationToListTagDisplay = (destination: string): ListCellTag[] => - destination - .split(',') - .filter((s) => s !== '') - .map((dest, index) => ({ - key: `destination-${index}`, - label: dest, - displayAsTag: false, - })); - -export const aclPortsToListTagDisplay = (ports: string): ListCellTag[] => - ports - .split(',') - .filter((s) => s !== '') - .map((port, index) => ({ - key: `port-${index}`, - label: port, - displayAsTag: false, - })); - -export const aclProtocolsToListTagDisplay = (protocols: AclProtocol[]): ListCellTag[] => - protocols.map((protocol) => ({ - key: protocol.toString(), - label: protocolToString(protocol), - displayAsTag: false, - })); - -export const aclRuleToListTagDisplay = (rules: AclRuleInfo[]): ListCellTag[] => - rules.map((rule) => ({ - key: rule.id, - label: rule.name, - displayAsTag: true, - })); - -export const networkToNetworkAccessType = (network: Network): NetworkAccessType => { - if (!network.acl_enabled) { - return NetworkAccessType.UNMANAGED; - } - if (network.acl_default_allow) { - return NetworkAccessType.ALLOWED; - } else { - return NetworkAccessType.DENIED; - } -}; diff --git a/web/src/pages/acl/validators.ts b/web/src/pages/acl/validators.ts deleted file mode 100644 index 83151eb68..000000000 --- a/web/src/pages/acl/validators.ts +++ /dev/null @@ -1,150 +0,0 @@ -import * as ipaddr from 'ipaddr.js'; -import { z } from 'zod'; - -import type { TranslationFunctions } from '../../i18n/i18n-types'; -import { patternStrictIpV4 } from '../../shared/patterns'; - -export const aclPortsValidator = (LL: TranslationFunctions) => - z - .string() - .refine((value: string) => { - if (value === '') return true; - const regexp = new RegExp(/^(?:\d+(?:-\d+)*)(?:(?:\s*,\s*|\s+)\d+(?:-\d+)*)*$/); - return regexp.test(value); - }, LL.form.error.invalid()) - .refine((value: string) => { - if (value === '') return true; - // check if there is no duplicates in given port field - const trimmed = value - .replaceAll(' ', '') - .replaceAll('-', ' ') - .replaceAll(',', ' ') - .split(' ') - .filter((v) => v !== ''); - const found: number[] = []; - for (const entry of trimmed) { - const num = parseInt(entry, 10); - if (Number.isNaN(num)) { - return false; - } - if (found.includes(num)) { - return false; - } - found.push(num); - } - return true; - }, LL.form.error.invalid()) - .refine((value: string) => { - if (value === '') return true; - // check if ranges in input are valid means follow pattern - - const matches = value.match(/\b\d+-\d+\b/g); - if (Array.isArray(matches)) { - for (const match of matches) { - const split = match.split('-'); - if (split.length !== 2) { - return false; - } - const start = split[0]; - const end = split[1]; - if (start >= end) { - return false; - } - } - } - return true; - }, LL.form.error.invalid()); - -function dottedMaskToPrefix(mask: string): number | null { - if (!mask.includes('.')) return Number(mask); - const maskTest = - /^(?:255\.255\.255\.(?:0|128|192|224|240|248|252|254|255)|255\.255\.(?:0|128|192|224|240|248|252|254|255)\.0|255\.(?:0|128|192|224|240|248|252|254|255)\.0\.0|(?:0|128|192|224|240|248|252|254|255)\.0\.0\.0)$/; - if (!maskTest.test(mask)) return null; - if (mask.split('.').length !== 4) return null; - const parts = mask.split('.').map(Number); - if (parts.length !== 4 || parts.some((part) => part < 0 || part > 255)) return null; - - const binary = parts.map((part) => part.toString(2).padStart(8, '0')).join(''); - if (!/^1*0*$/.test(binary)) return null; - - return binary.indexOf('0') === -1 ? 32 : binary.indexOf('0'); -} - -const validateIpPart = (input: string): ipaddr.IPv4 | ipaddr.IPv6 | null => { - if (!ipaddr.isValid(input)) return null; - const ip = ipaddr.parse(input); - if (ip.kind() === 'ipv6') { - return ip; - } - if (!patternStrictIpV4.test(input)) return null; - return ip; -}; - -function parseSubnet(input: string): [ipaddr.IPv4 | ipaddr.IPv6, number] | null { - const [ipPart, maskPart] = input.split('/'); - if (!ipaddr.isValid(ipPart) || !maskPart) return null; - const ip = ipaddr.parse(ipPart); - const kind = ip.kind(); - - if (kind === 'ipv6') { - const prefix = parseInt(maskPart, 10); - if (typeof prefix !== 'number' || Number.isNaN(prefix)) { - return null; - } - return [ip, prefix]; - } - if (!patternStrictIpV4.test(ipPart)) return null; - - const prefix = dottedMaskToPrefix(maskPart); - if (prefix === null) return null; - - return [ip, prefix]; -} - -function isValidIpOrCidr(input: string): boolean { - try { - if (input.includes('/')) { - const parsed = parseSubnet(input); - if (!parsed) return false; - const [ip, mask] = parsed; - const cidr = ipaddr.parseCIDR(`${ip.toString()}/${mask}`); - return cidr[0] !== undefined && typeof cidr[1] === 'number'; - } else { - return validateIpPart(input) !== null; - } - } catch { - return false; - } -} - -export const aclDestinationValidator = (LL: TranslationFunctions) => - z.string().refine((value: string) => { - if (value === '') return true; - - const entries = value.split(',').map((s) => s.trim()); - - for (const entry of entries) { - if (entry.includes('-')) { - const [start, end] = entry.split('-').map((s) => s.trim()); - - // reject CIDR notation used in ranges - if (start.includes('/') || end.includes('/')) return false; - - if (!ipaddr.isValid(start) || !ipaddr.isValid(end)) return false; - - const startAddr = ipaddr.parse(start); - const endAddr = ipaddr.parse(end); - - // reject different ip versions in ranges - if (startAddr.kind() !== endAddr.kind()) return false; - - // reject invalid order in ranges - if (startAddr.toByteArray().join('.') > endAddr.toByteArray().join('.')) { - return false; - } - } else { - if (!isValidIpOrCidr(entry)) return false; - } - } - - return true; - }, LL.form.error.invalid()); diff --git a/web/src/pages/activity-log/ActivityLogPage.tsx b/web/src/pages/activity-log/ActivityLogPage.tsx deleted file mode 100644 index 7f1b42b45..000000000 --- a/web/src/pages/activity-log/ActivityLogPage.tsx +++ /dev/null @@ -1,319 +0,0 @@ -import './style.scss'; - -import { type QueryKey, useInfiniteQuery, useQuery } from '@tanstack/react-query'; -import dayjs from 'dayjs'; -import { range } from 'lodash-es'; -import { useMemo, useState } from 'react'; -import Skeleton from 'react-loading-skeleton'; - -import { useI18nContext } from '../../i18n/i18n-react'; -import { FilterButton } from '../../shared/components/Layout/buttons/FilterButton/FilterButton'; -import { PageContainer } from '../../shared/components/Layout/PageContainer/PageContainer'; -import { FilterGroupsModal } from '../../shared/components/modals/FilterGroupsModal/FilterGroupsModal'; -import type { FilterGroupsModalFilter } from '../../shared/components/modals/FilterGroupsModal/types'; -import { Button } from '../../shared/defguard-ui/components/Layout/Button/Button'; -import { ButtonSize } from '../../shared/defguard-ui/components/Layout/Button/types'; -import { Card } from '../../shared/defguard-ui/components/Layout/Card/Card'; -import { ListItemCount } from '../../shared/defguard-ui/components/Layout/ListItemCount/ListItemCount'; -import { NoData } from '../../shared/defguard-ui/components/Layout/NoData/NoData'; -import { Search } from '../../shared/defguard-ui/components/Layout/Search/Search'; -import { ListSortDirection } from '../../shared/defguard-ui/components/Layout/VirtualizedList/types'; -import { isPresent } from '../../shared/defguard-ui/utils/isPresent'; -import { useAuthStore } from '../../shared/hooks/store/useAuthStore'; -import useApi from '../../shared/hooks/useApi'; -import type { ActivityLogSortKey } from '../../shared/types'; -import { ActivityList } from './components/ActivityList'; -import { ActivityTimeRangeModal } from './components/ActivityTimeRangeModal'; -import { - type ActivityLogEventType, - type ActivityLogModule, - activityLogEventTypeValues, - activityLogModuleValues, -} from './types'; - -export const ActivityLogPage = () => { - return ( - - - - ); -}; - -const applyFilterArray = (val: Array | undefined | T): undefined | Array => { - if (val && Array.isArray(val) && val.length > 0) { - return val; - } -}; - -const applyFilter = (val: T | undefined | null): T | undefined => { - if (isPresent(val)) { - return val; - } -}; - -const applySearch = (val: string): string | undefined => { - if (val.length > 0) return val; - return undefined; -}; - -type Filters = 'event' | 'username' | 'module' | 'location'; - -const PageContent = () => { - const [activeFilters, setActiveFilters] = useState< - Record> - >({ - event: [], - module: [], - username: [], - location: [], - }); - const [searchValue, setSearchValue] = useState(''); - const [filtersModalOpen, setFiltersModalOpen] = useState(false); - const [from, setForm] = useState(dayjs.utc().startOf('M').toISOString()); - const [until, setUntil] = useState(null); - const [timeSelectionModalOpen, setTimeSelectionModal] = useState(false); - const [sortKey, setSortKey] = useState('timestamp'); - const [sortDirection, setSortDirection] = useState( - ListSortDirection.DESC, - ); - const isAdmin = useAuthStore((s) => s.user?.is_admin ?? false); - - const activeFiltersCount = useMemo( - () => Object.values(activeFilters).flat().length, - [activeFilters], - ); - - const { LL } = useI18nContext(); - const localLL = LL.activity; - - const { - activityLog: { getActivityLog }, - user: { getUsers }, - network: { getNetworks }, - } = useApi(); - - const { data: users } = useQuery({ - queryFn: getUsers, - queryKey: ['user'], - enabled: isAdmin, - }); - - const { data: locations } = useQuery({ - queryFn: getNetworks, - queryKey: ['location'], - }); - - const queryKey = useMemo( - (): QueryKey => [ - 'activity_log', - { - sortDirection, - sortKey, - from, - until, - searchValue, - filters: activeFilters, - }, - ], - [activeFilters, from, searchValue, sortDirection, sortKey, until], - ); - - const { - data, - hasNextPage, - isFetchingNextPage, - fetchNextPage, - isLoading, - // hasPreviousPage, - // fetchPreviousPage, - } = useInfiniteQuery({ - queryKey, - initialPageParam: 1, - queryFn: ({ pageParam }) => - getActivityLog({ - page: pageParam, - event: applyFilterArray(activeFilters.event as ActivityLogEventType[]), - module: applyFilterArray(activeFilters.module as ActivityLogModule[]), - username: applyFilterArray(activeFilters.username as string[]), - location: applyFilterArray(activeFilters.location as string[]), - sort_order: sortDirection, - sort_by: sortKey, - search: applySearch(searchValue), - from: applyFilter(from), - until: applyFilter(until), - }), - getNextPageParam: (lastPage) => lastPage?.pagination?.next_page, - getPreviousPageParam: (page) => { - if (page.pagination.current_page !== 1) { - return page.pagination.current_page - 1; - } - return undefined; - }, - }); - - const filterOptions = useMemo(() => { - const res: Record = {}; - if (users) { - res.users = { - label: 'Users', - identifier: 'username', - order: 3, - items: users.map((user) => ({ - label: `${user.first_name} ${user.last_name} (${user.username})`, - searchValues: [user.first_name, user.username, user.last_name, user.email], - value: user.username, - })), - }; - } - if (locations) { - res.locations = { - label: 'Locations', - identifier: 'location', - order: 4, - items: locations.map((location) => ({ - label: location.name, - searchValues: [location.name], - value: location.name, - })), - }; - } - res.module = { - identifier: 'module', - label: 'Module', - order: 2, - items: activityLogModuleValues.map((activityLogModule) => { - const translation = LL.enums.activityLogModule[activityLogModule](); - return { - label: translation, - searchValues: [translation], - value: activityLogModule, - }; - }), - }; - res.event = { - identifier: 'event', - label: 'Event', - order: 1, - items: activityLogEventTypeValues.map((eventType) => { - const translation = LL.enums.activityLogEventType[eventType](); - return { - label: translation, - searchValues: [translation], - value: eventType, - }; - }), - }; - return res; - }, [LL.enums, users, locations]); - - const activityData = useMemo(() => { - if (data) { - return data.pages.flatMap((page) => page.data); - } - return undefined; - }, [data]); - - return ( - <> -
-

{localLL.title()}

-
-
-
-

{localLL.list.allLabel()}

- -
- { - setSearchValue(search); - }} - /> - { - setFiltersModalOpen(true); - }} - /> -
-
- - {isPresent(activityData) && activityData.length > 0 && ( - { - setSortDirection(sortDirection); - setSortKey(sortKey as ActivityLogSortKey); - }} - data={activityData} - hasNextPage={hasNextPage} - isFetchingNextPage={isFetchingNextPage} - onNextPage={() => { - void fetchNextPage(); - }} - /> - )} - {!isPresent(activityData) && isLoading && ( -
- {range(10).map((index) => ( - - ))} -
- )} - {(activeFiltersCount > 0 || searchValue.length > 0) && - isPresent(activityData) && - activityData.length === 0 && } -
-
- { - setFiltersModalOpen(false); - }} - onSubmit={(state) => { - setActiveFilters(state as Record); - setFiltersModalOpen(false); - }} - /> - { - setForm(from); - setUntil(until); - }} - /> - - ); -}; diff --git a/web/src/pages/activity-log/components/ActivityList.tsx b/web/src/pages/activity-log/components/ActivityList.tsx deleted file mode 100644 index 552a1a491..000000000 --- a/web/src/pages/activity-log/components/ActivityList.tsx +++ /dev/null @@ -1,173 +0,0 @@ -import { useVirtualizer } from '@tanstack/react-virtual'; -import dayjs from 'dayjs'; -import { useMemo, useRef } from 'react'; -import { useInView } from 'react-intersection-observer'; - -import { useI18nContext } from '../../../i18n/i18n-react'; -import { ListHeader } from '../../../shared/components/Layout/ListHeader/ListHeader'; -import type { ListHeaderColumnConfig } from '../../../shared/components/Layout/ListHeader/types'; -import { ListCellText } from '../../../shared/defguard-ui/components/Layout/ListCellText/ListCellText'; -import { LoaderSpinner } from '../../../shared/defguard-ui/components/Layout/LoaderSpinner/LoaderSpinner'; -import type { ListSortDirection } from '../../../shared/defguard-ui/components/Layout/VirtualizedList/types'; -import type { ActivityLogEvent, ActivityLogSortKey } from '../../../shared/types'; - -type Props = { - data: ActivityLogEvent[]; - hasNextPage: boolean; - isFetchingNextPage: boolean; - sortKey: ActivityLogSortKey; - sortDirection: ListSortDirection; - onNextPage: () => void; - onSortChange: ( - sortKey: keyof ActivityLogEvent, - sortDirection: ListSortDirection, - ) => void; -}; - -export const ActivityList = ({ - data, - isFetchingNextPage, - hasNextPage, - sortDirection, - sortKey, - onSortChange, - onNextPage, -}: Props) => { - const { LL } = useI18nContext(); - const localLL = LL.activity.list; - const headersLL = localLL.headers; - const { ref: infiniteLoadMoreElement } = useInView({ - threshold: 0, - trackVisibility: false, - onChange: (inView) => { - if (inView) { - onNextPage(); - } - }, - }); - const parentRef = useRef(null); - const count = data.length; - const virtualizer = useVirtualizer({ - count, - estimateSize: () => 40, - getScrollElement: () => parentRef.current, - enabled: true, - paddingStart: 45, - paddingEnd: 10, - }); - const items = virtualizer.getVirtualItems(); - const listHeaders = useMemo( - (): ListHeaderColumnConfig[] => [ - { - label: headersLL.date(), - enabled: true, - key: 'date', - sortKey: 'timestamp', - }, - { - label: headersLL.user(), - key: 'user', - }, - { - label: headersLL.ip(), - key: 'ip', - }, - { - label: headersLL.location(), - key: 'location', - }, - { - label: headersLL.event(), - key: 'event', - }, - { - label: headersLL.module(), - key: 'module', - }, - { - label: headersLL.device(), - key: 'device', - }, - { - label: headersLL.description(), - key: 'description', - }, - ], - [headersLL], - ); - return ( -
-
- -
- {items.map((virtualRow) => { - const activity = data[virtualRow.index]; - return ( -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- ); - })} - {hasNextPage && ( -
- {isFetchingNextPage && } -
- )} -
-
-
- ); -}; diff --git a/web/src/pages/activity-log/components/ActivityTimeRangeModal.tsx b/web/src/pages/activity-log/components/ActivityTimeRangeModal.tsx deleted file mode 100644 index 34c9f8323..000000000 --- a/web/src/pages/activity-log/components/ActivityTimeRangeModal.tsx +++ /dev/null @@ -1,106 +0,0 @@ -import { zodResolver } from '@hookform/resolvers/zod'; -import { useMemo } from 'react'; -import { type SubmitHandler, useForm } from 'react-hook-form'; -import { z } from 'zod'; - -import { useI18nContext } from '../../../i18n/i18n-react'; -import { FormDateInput } from '../../../shared/components/Layout/DateInput/FormDateInput'; -import { Button } from '../../../shared/defguard-ui/components/Layout/Button/Button'; -import { - ButtonSize, - ButtonStyleVariant, -} from '../../../shared/defguard-ui/components/Layout/Button/types'; -import { ModalWithTitle } from '../../../shared/defguard-ui/components/Layout/modals/ModalWithTitle/ModalWithTitle'; - -type Props = { - isOpen: boolean; - onOpenChange: (val: boolean) => void; - // Time in UTC ISO without timezone string - activityFrom: string | null; - activityUntil: string | null; - onChange: (from: string | null, until: string | null) => void; -}; - -export const ActivityTimeRangeModal = (props: Props) => { - const { LL } = useI18nContext(); - return ( - { - props.onOpenChange(false); - }} - > - - - ); -}; - -const ModalContent = ({ onOpenChange, activityFrom, activityUntil, onChange }: Props) => { - const { LL } = useI18nContext(); - const schema = useMemo( - () => - z.object({ - from: z.string().nullable(), - until: z.string().nullable(), - }), - [], - ); - - type FormFields = z.infer; - - const defaultValues = useMemo( - (): FormFields => ({ - from: activityFrom, - until: activityUntil, - }), - [activityFrom, activityUntil], - ); - - const { control, handleSubmit } = useForm({ - resolver: zodResolver(schema), - mode: 'all', - defaultValues, - }); - - const handleValidSubmit: SubmitHandler = (values) => { - onChange(values.from, values.until); - onOpenChange(false); - }; - - return ( -
- - -
-
- - ); -}; diff --git a/web/src/pages/activity-log/style.scss b/web/src/pages/activity-log/style.scss deleted file mode 100644 index aecfdc547..000000000 --- a/web/src/pages/activity-log/style.scss +++ /dev/null @@ -1,168 +0,0 @@ -#activity-log-page { - h1 { - @include typography(app-title); - } - - h2 { - @include typography(app-body-1); - } - - .page-header { - display: flex; - flex-flow: row; - gap: var(--spacing-m); - align-items: center; - justify-content: flex-start; - padding-bottom: var(--spacing-l); - - .search { - height: 40px; - width: 100%; - max-width: 350px; - } - } - - .activity-list-skeleton { - width: 100%; - display: flex; - flex-flow: column; - row-gap: var(--spacing-xs); - max-height: 100%; - overflow: hidden; - - .react-loading-skeleton { - height: 40px; - } - } -} - -@mixin list-sizing() { - grid-template-columns: 150px 120px 150px 150px 300px 100px 200px minmax(300px, 1fr); - justify-content: space-between; - column-gap: var(--spacing-xs); - - .cell { - display: flex; - flex-flow: row; - align-items: center; - justify-content: flex-start; - - &.select-cell { - .interaction-box { - width: 18px; - height: 18px; - } - } - } -} - -#activity-list { - & > .top { - display: flex; - flex-flow: row wrap; - align-items: center; - justify-content: flex-start; - row-gap: var(--spacing-xs); - padding-bottom: var(--spacing-m); - - h2 { - padding-right: 10px; - } - - .controls { - display: flex; - flex-flow: row; - column-gap: var(--spacing-xs); - margin-left: auto; - } - } -} - -#activity-list-card { - padding: var(--spacing-s) 15px; - width: 100%; - max-width: 100%; - - @include media-breakpoint-up(lg) { - min-height: min(600px, 75dvh); - } - - .list-headers { - position: sticky; - top: 0; - z-index: 1; - box-sizing: border-box; - background-color: var(--surface-default-modal); - height: 40px; - width: unset; - min-width: 100%; - padding-left: var(--spacing-xs); - - .cell.empty { - display: none; - } - - @include list-sizing(); - } - - .virtual-list { - overflow: auto; - contain: strict; - width: 100%; - max-width: 100%; - max-height: 610px; - height: 600px; - padding-right: 15px; - box-sizing: border-box; - scrollbar-gutter: stable; - - .end-row { - display: flex; - flex-flow: row; - align-items: center; - justify-content: center; - height: 40px; - width: 100%; - } - - .list-row { - display: grid; - max-width: 100%; - @include list-sizing(); - height: 40px; - background-color: var(--surface-default-modal); - padding-left: var(--spacing-xs); - - &:hover { - background-color: var(--surface-button); - } - - .cell { - max-width: 100%; - overflow: hidden; - - p { - color: var(--text-button-primary); - @include typography(app-code); - } - } - } - } -} - -#activity-time-selection-modal-form { - display: flex; - flex-flow: column; - row-gap: var(--spacing-s); - - & > * { - width: 100%; - } - - .controls { - display: grid; - grid-template-columns: 1fr 1fr; - width: 100%; - column-gap: var(--spacing-xs); - } -} diff --git a/web/src/pages/activity-log/types.ts b/web/src/pages/activity-log/types.ts deleted file mode 100644 index 3485b9243..000000000 --- a/web/src/pages/activity-log/types.ts +++ /dev/null @@ -1,163 +0,0 @@ -export type ActivityLogModule = 'defguard' | 'client' | 'vpn' | 'enrollment'; - -export const activityLogModuleValues: ActivityLogModule[] = [ - 'defguard', - 'client', - 'enrollment', - 'vpn', -]; - -export type ActivityLogEventType = - | 'user_login' - | 'user_login_failed' - | 'user_mfa_login' - | 'user_mfa_login_failed' - | 'recovery_code_used' - | 'user_logout' - | 'user_added' - | 'user_modified' - | 'user_removed' - | 'user_groups_modified' - | 'mfa_disabled' - | 'user_mfa_disabled' - | 'mfa_totp_enabled' - | 'mfa_totp_disabled' - | 'mfa_email_enabled' - | 'mfa_email_disabled' - | 'mfa_security_key_added' - | 'mfa_security_key_removed' - | 'device_added' - | 'device_modified' - | 'device_removed' - | 'network_device_added' - | 'network_device_modified' - | 'network_device_removed' - | 'activity_log_stream_created' - | 'activity_log_stream_modified' - | 'activity_log_stream_removed' - | 'vpn_client_connected' - | 'vpn_client_disconnected' - | 'vpn_client_connected_mfa' - | 'vpn_client_disconnected_mfa' - | 'vpn_client_mfa_failed' - | 'enrollment_token_added' - | 'enrollment_started' - | 'enrollment_device_added' - | 'enrollment_completed' - | 'password_reset_requested' - | 'password_reset_started' - | 'password_reset_completed' - | 'vpn_location_added' - | 'vpn_location_removed' - | 'vpn_location_modified' - | 'api_token_added' - | 'api_token_removed' - | 'api_token_renamed' - | 'open_id_app_added' - | 'open_id_app_removed' - | 'open_id_app_modified' - | 'open_id_app_state_changed' - | 'open_id_provider_removed' - | 'open_id_provider_modified' - | 'settings_updated' - | 'settings_updated_partial' - | 'settings_default_branding_restored' - | 'groups_bulk_assigned' - | 'group_added' - | 'group_modified' - | 'group_removed' - | 'group_member_added' - | 'group_member_removed' - | 'group_members_modified' - | 'web_hook_added' - | 'web_hook_modified' - | 'web_hook_removed' - | 'web_hook_state_changed' - | 'authentication_key_added' - | 'authentication_key_removed' - | 'authentication_key_renamed' - | 'password_changed' - | 'password_changed_by_admin' - | 'password_reset' - | 'client_configuration_token_added' - | 'user_snat_binding_added' - | 'user_snat_binding_modified' - | 'user_snat_binding_removed'; - -export const activityLogEventTypeValues: ActivityLogEventType[] = [ - 'user_login', - 'user_login_failed', - 'user_mfa_login', - 'user_mfa_login_failed', - 'user_groups_modified', - 'recovery_code_used', - 'user_logout', - 'user_added', - 'user_modified', - 'user_removed', - 'mfa_disabled', - 'user_mfa_disabled', - 'mfa_totp_enabled', - 'mfa_totp_disabled', - 'mfa_email_enabled', - 'mfa_email_disabled', - 'mfa_security_key_added', - 'mfa_security_key_removed', - 'device_added', - 'device_modified', - 'device_removed', - 'network_device_added', - 'network_device_modified', - 'network_device_removed', - 'activity_log_stream_created', - 'activity_log_stream_modified', - 'activity_log_stream_removed', - 'vpn_client_connected', - 'vpn_client_disconnected', - 'vpn_client_connected_mfa', - 'vpn_client_disconnected_mfa', - 'vpn_client_mfa_failed', - 'enrollment_token_added', - 'enrollment_started', - 'enrollment_device_added', - 'enrollment_completed', - 'password_reset_requested', - 'password_reset_started', - 'password_reset_completed', - 'vpn_location_added', - 'vpn_location_removed', - 'vpn_location_modified', - 'api_token_added', - 'api_token_removed', - 'api_token_renamed', - 'open_id_app_added', - 'open_id_app_removed', - 'open_id_app_modified', - 'open_id_app_state_changed', - 'open_id_provider_removed', - 'open_id_provider_modified', - 'settings_updated', - 'settings_updated_partial', - 'settings_default_branding_restored', - 'groups_bulk_assigned', - 'group_added', - 'group_modified', - 'group_removed', - 'group_member_added', - 'group_member_removed', - 'group_members_modified', - 'web_hook_added', - 'web_hook_modified', - 'web_hook_removed', - 'web_hook_state_changed', - 'authentication_key_added', - 'authentication_key_removed', - 'authentication_key_renamed', - 'password_changed', - 'password_changed_by_admin', - 'password_reset', - 'client_configuration_token_added', - 'user_snat_binding_added', - 'user_snat_binding_modified', - 'user_snat_binding_removed', -]; diff --git a/web/src/pages/addDevice/AddDevicePage.tsx b/web/src/pages/addDevice/AddDevicePage.tsx deleted file mode 100644 index 8366ab960..000000000 --- a/web/src/pages/addDevice/AddDevicePage.tsx +++ /dev/null @@ -1,169 +0,0 @@ -import './style.scss'; - -import { useEffect } from 'react'; -import { useNavigate } from 'react-router'; -import { shallow } from 'zustand/shallow'; - -import { useI18nContext } from '../../i18n/i18n-react'; -import { PageContainer } from '../../shared/components/Layout/PageContainer/PageContainer'; -import { ArrowSingle } from '../../shared/defguard-ui/components/icons/ArrowSingle/ArrowSingle'; -import { - ArrowSingleDirection, - ArrowSingleSize, -} from '../../shared/defguard-ui/components/icons/ArrowSingle/types'; -import { Button } from '../../shared/defguard-ui/components/Layout/Button/Button'; -import { - ButtonSize, - ButtonStyleVariant, -} from '../../shared/defguard-ui/components/Layout/Button/types'; -import { useAppStore } from '../../shared/hooks/store/useAppStore'; -import { useAuthStore } from '../../shared/hooks/store/useAuthStore'; -import { useEnterpriseUpgradeStore } from '../../shared/hooks/store/useEnterpriseUpgradeStore'; -import useApi from '../../shared/hooks/useApi'; -import { useAddDevicePageStore } from './hooks/useAddDevicePageStore'; -import { AddDeviceClientConfigurationStep } from './steps/AddDeviceClientConfigurationStep/AddDeviceClientConfigurationStep'; -import { AddDeviceConfigStep } from './steps/AddDeviceConfigStep/AddDeviceConfigStep'; -import { AddDeviceSetupMethodStep } from './steps/AddDeviceSetupMethodStep/AddDeviceSetupMethodStep'; -import { AddDeviceSetupStep } from './steps/AddDeviceSetupStep/AddDeviceSetupStep'; -import { AddDeviceNavigationEvent, AddDeviceStep } from './types'; - -const finalSteps: AddDeviceStep[] = [ - AddDeviceStep.NATIVE_CONFIGURATION, - AddDeviceStep.CLIENT_CONFIGURATION, -]; - -export const AddDevicePage = () => { - const { LL } = useI18nContext(); - const pageLL = LL.addDevicePage; - const navigate = useNavigate(); - const { getAppInfo } = useApi(); - - const userData = useAddDevicePageStore((state) => state.userData); - const isAdmin = useAuthStore((s) => s.user?.is_admin ?? false); - const setAppStore = useAppStore((s) => s.setState); - const showUpgradeToast = useEnterpriseUpgradeStore((s) => s.show); - const currentStep = useAddDevicePageStore((state) => state.currentStep); - const [navSubject, resetStore, setStep] = useAddDevicePageStore( - (s) => [s.navigationSubject, s.reset, s.setStep], - shallow, - ); - - const isFinalStep = finalSteps.includes(currentStep); - - useEffect(() => { - if (!userData) { - navigate('/', { replace: true }); - } - }, [navigate, userData]); - - useEffect(() => { - const sub = navSubject.subscribe((event) => { - if ( - event === AddDeviceNavigationEvent.NEXT && - [AddDeviceStep.CLIENT_CONFIGURATION, AddDeviceStep.NATIVE_CONFIGURATION].includes( - currentStep, - ) && - userData - ) { - if (isAdmin) { - void getAppInfo().then((resp) => { - setAppStore({ appInfo: resp }); - if (resp.license_info.any_limit_exceeded) { - showUpgradeToast(); - } - }); - } - navigate(userData.originRoutePath, { replace: true }); - setTimeout(() => { - resetStore(); - }, 250); - } - if (event === AddDeviceNavigationEvent.BACK) { - if (currentStep === AddDeviceStep.NATIVE_CHOOSE_METHOD) { - setStep(AddDeviceStep.CHOOSE_METHOD); - } - } - }); - return () => { - sub.unsubscribe(); - }; - }, [ - currentStep, - getAppInfo, - isAdmin, - navSubject, - navigate, - resetStore, - setAppStore, - setStep, - showUpgradeToast, - userData, - ]); - - return ( - -
-
-

{pageLL.title()}

-
-
-
- {steps[currentStep]} -
-
- ); -}; - -const steps = { - [AddDeviceStep.CHOOSE_METHOD]: , - [AddDeviceStep.NATIVE_CHOOSE_METHOD]: , - [AddDeviceStep.NATIVE_CONFIGURATION]: , - [AddDeviceStep.CLIENT_CONFIGURATION]: , -}; diff --git a/web/src/pages/addDevice/hooks/useAddDevicePageStore.tsx b/web/src/pages/addDevice/hooks/useAddDevicePageStore.tsx deleted file mode 100644 index 583a7f3e3..000000000 --- a/web/src/pages/addDevice/hooks/useAddDevicePageStore.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import { omit } from 'lodash-es'; -import { Subject } from 'rxjs'; -import { createJSONStorage, persist } from 'zustand/middleware'; -import { createWithEqualityFn } from 'zustand/traditional'; - -import type { DeviceConfigsCardNetworkInfo } from '../../../shared/components/network/DeviceConfigsCard/types'; -import type { AddDeviceResponseDevice } from '../../../shared/types'; -import { type AddDeviceNavigationEvent, AddDeviceStep } from '../types'; - -const defaultValues: StoreValues = { - navigationSubject: new Subject(), - currentStep: AddDeviceStep.CHOOSE_METHOD, - userData: undefined, - loading: false, - publicKey: undefined, - privateKey: undefined, - device: undefined, - networks: undefined, - clientSetup: undefined, -}; - -export const useAddDevicePageStore = createWithEqualityFn()( - persist( - (set) => ({ - ...defaultValues, - reset: () => set(defaultValues), - init: (userData) => { - set({ ...defaultValues, userData }); - }, - setState: (values) => set({ ...values }), - setStep: (step, values) => { - set({ ...values, currentStep: step }); - }, - }), - { - name: 'add-device-store', - partialize: (store) => omit(store, ['navigationSubject', 'loading']), - storage: createJSONStorage(() => sessionStorage), - }, - ), - Object.is, -); - -type Store = StoreValues & StoreMethods; - -type StoreValues = { - navigationSubject: Subject; - currentStep: AddDeviceStep; - loading: boolean; - privateKey?: string; - publicKey?: string; - device?: AddDeviceResponseDevice; - networks?: DeviceConfigsCardNetworkInfo[]; - userData?: { - id: number; - username: string; - reservedDevices: string[]; - email: string; - // this should be current path that user entered add-device page from, due to brave blocking history relative back doesn't work correctly. - originRoutePath: string; - }; - clientSetup?: { - token: string; - url: string; - }; -}; - -type StoreMethods = { - init: (userData: StoreValues['userData']) => void; - reset: () => void; - setState: (values: Partial) => void; - setStep: (step: AddDeviceStep, values?: Partial) => void; -}; diff --git a/web/src/pages/addDevice/steps/AddDeviceClientConfigurationStep/AddDeviceClientConfigurationStep.tsx b/web/src/pages/addDevice/steps/AddDeviceClientConfigurationStep/AddDeviceClientConfigurationStep.tsx deleted file mode 100644 index eaf9bf391..000000000 --- a/web/src/pages/addDevice/steps/AddDeviceClientConfigurationStep/AddDeviceClientConfigurationStep.tsx +++ /dev/null @@ -1,306 +0,0 @@ -import './style.scss'; - -import { useEffect } from 'react'; -import QRCode from 'react-qr-code'; -import { shallow } from 'zustand/shallow'; - -import { useI18nContext } from '../../../../i18n/i18n-react'; -import { OpenDesktopClientButton } from '../../../../shared/components/Layout/buttons/OpenDesktopClientButton/OpenDesktopClientButton'; -import { RenderMarkdown } from '../../../../shared/components/Layout/RenderMarkdown/RenderMarkdown'; -import { Button } from '../../../../shared/defguard-ui/components/Layout/Button/Button'; -import { - ButtonSize, - ButtonStyleVariant, -} from '../../../../shared/defguard-ui/components/Layout/Button/types'; -import { Card } from '../../../../shared/defguard-ui/components/Layout/Card/Card'; -import { CopyField } from '../../../../shared/defguard-ui/components/Layout/CopyField/CopyField'; -import { MessageBox } from '../../../../shared/defguard-ui/components/Layout/MessageBox/MessageBox'; -import { isPresent } from '../../../../shared/defguard-ui/utils/isPresent'; -import { useClipboard } from '../../../../shared/hooks/useClipboard'; -import { externalLink } from '../../../../shared/links'; -import { useAddDevicePageStore } from '../../hooks/useAddDevicePageStore'; -import { AddDeviceStep } from '../../types'; -import { enrollmentToImportToken } from '../../utils/enrollmentToToken'; - -export const AddDeviceClientConfigurationStep = () => { - const { LL } = useI18nContext(); - const localLL = LL.addDevicePage.steps.client; - const clientData = useAddDevicePageStore((s) => s.clientSetup); - const clientSetup = useAddDevicePageStore((s) => s.clientSetup); - const tokenValue = useAddDevicePageStore((s) => - s.clientSetup - ? enrollmentToImportToken(s.clientSetup.url, s.clientSetup.token) - : null, - ); - const setStep = useAddDevicePageStore((s) => s.setStep, shallow); - const { writeToClipboard } = useClipboard(); - - useEffect(() => { - if (!isPresent(tokenValue)) { - setStep(AddDeviceStep.CHOOSE_METHOD); - } - }, [setStep, tokenValue]); - - if (!isPresent(tokenValue) || !isPresent(clientData)) return null; - - return ( - -

{localLL.title()}

- {isPresent(clientSetup) && ( - <> - -
- -
- - )} - - - - {/* { - void writeToClipboard(value, localLL.tokenCopy()); - }} - /> */} - { - void writeToClipboard(value, localLL.tokenCopy()); - }} - /> - { - void writeToClipboard(value, localLL.tokenCopy()); - }} - /> - -
- -
-
-

{localLL.qrDescription()}

-
- -
- ); -}; diff --git a/web/src/pages/addDevice/steps/AddDeviceClientConfigurationStep/style.scss b/web/src/pages/addDevice/steps/AddDeviceClientConfigurationStep/style.scss deleted file mode 100644 index df62be874..000000000 --- a/web/src/pages/addDevice/steps/AddDeviceClientConfigurationStep/style.scss +++ /dev/null @@ -1,50 +0,0 @@ -#add-device-client-configuration { - .message-box.spacer { - padding-bottom: var(--spacing-s); - } - - .qr-description { - color: var(--text-body-secondary); - text-align: center; - max-width: 480px; - user-select: none; - @include typography(app-input); - } - - .copy-field.spacer { - &:not(:last-of-type) { - padding-bottom: var(--spacing-s); - } - } - - .row { - display: flex; - flex-flow: row; - align-items: center; - justify-content: center; - column-gap: var(--spacing-xs); - - &:not(:last-child) { - padding-bottom: var(--spacing-s); - } - - a { - display: flex; - cursor: pointer; - user-select: none; - } - } - - .links:first-of-type { - padding-bottom: var(--spacing-s); - } - - .qr { - display: flex; - flex-flow: row; - align-items: center; - justify-content: center; - padding: var(--spacing-m) var(--spacing-s) var(--spacing-l); - box-sizing: border-box; - } -} diff --git a/web/src/pages/addDevice/steps/AddDeviceConfigStep/AddDeviceConfigStep.tsx b/web/src/pages/addDevice/steps/AddDeviceConfigStep/AddDeviceConfigStep.tsx deleted file mode 100644 index ca397de7e..000000000 --- a/web/src/pages/addDevice/steps/AddDeviceConfigStep/AddDeviceConfigStep.tsx +++ /dev/null @@ -1,88 +0,0 @@ -import './style.scss'; - -import parse from 'html-react-parser'; -import { isUndefined } from 'lodash-es'; -import { useEffect, useMemo } from 'react'; -import { shallow } from 'zustand/shallow'; - -import { useI18nContext } from '../../../../i18n/i18n-react'; -import { DeviceConfigsCard } from '../../../../shared/components/network/DeviceConfigsCard/DeviceConfigsCard'; -import { Card } from '../../../../shared/defguard-ui/components/Layout/Card/Card'; -import { Input } from '../../../../shared/defguard-ui/components/Layout/Input/Input'; -import { MessageBox } from '../../../../shared/defguard-ui/components/Layout/MessageBox/MessageBox'; -import { MessageBoxType } from '../../../../shared/defguard-ui/components/Layout/MessageBox/types'; -import { useAddDevicePageStore } from '../../hooks/useAddDevicePageStore'; -import { AddDeviceStep } from '../../types'; - -enum SetupMode { - AUTO, - MANUAL, -} - -export const AddDeviceConfigStep = () => { - const { LL } = useI18nContext(); - const localLL = LL.addDevicePage.steps.configDevice; - - const [userData, device, publicKey, privateKey, networks] = useAddDevicePageStore( - (state) => [ - state.userData, - state.device, - state.publicKey, - state.privateKey, - state.networks, - ], - shallow, - ); - - const setStep = useAddDevicePageStore((state) => state.setStep, shallow); - - const setupMode = isUndefined(privateKey) ? SetupMode.MANUAL : SetupMode.AUTO; - - const getWarningMessageContent = useMemo(() => { - if (setupMode === SetupMode.AUTO) { - return parse(localLL.helpers.warningAutoMode()); - } - return parse(localLL.helpers.warningManualMode()); - }, [localLL.helpers, setupMode]); - - useEffect(() => { - if (!device || !userData || !publicKey || !networks) { - setStep(AddDeviceStep.NATIVE_CHOOSE_METHOD); - } - }, [device, networks, publicKey, setStep, userData]); - - if (!device || !userData || !publicKey || !networks) return null; - - return ( - -

{localLL.title()}

- {getWarningMessageContent} - { - return; - }} - disabled={true} - /> -
-

{localLL.qrInfo()}

-
- {networks.length > 0 && ( - - )} - {networks.length === 0 && ( - - {localLL.helpers.warningNoNetworks()} - - )} -
- ); -}; diff --git a/web/src/pages/addDevice/steps/AddDeviceConfigStep/style.scss b/web/src/pages/addDevice/steps/AddDeviceConfigStep/style.scss deleted file mode 100644 index fd2175705..000000000 --- a/web/src/pages/addDevice/steps/AddDeviceConfigStep/style.scss +++ /dev/null @@ -1,21 +0,0 @@ -@use '@scssutils' as *; - -#add-device-page { - #add-device-config-step { - & > .info { - width: 100%; - display: flex; - flex-flow: row nowrap; - align-items: center; - justify-content: center; - padding-bottom: 30px; - - & > p { - @include typography(app-input); - color: var(--text-body-secondary); - text-align: center; - max-width: 410px; - } - } - } -} diff --git a/web/src/pages/addDevice/steps/AddDeviceSetupMethodStep/AddDeviceSetupMethodStep.tsx b/web/src/pages/addDevice/steps/AddDeviceSetupMethodStep/AddDeviceSetupMethodStep.tsx deleted file mode 100644 index 26aa2eb03..000000000 --- a/web/src/pages/addDevice/steps/AddDeviceSetupMethodStep/AddDeviceSetupMethodStep.tsx +++ /dev/null @@ -1,112 +0,0 @@ -import './style.scss'; - -import { useMutation } from '@tanstack/react-query'; -import { useCallback, useEffect, useState } from 'react'; -import { shallow } from 'zustand/shallow'; - -import { useI18nContext } from '../../../../i18n/i18n-react'; -import { Card } from '../../../../shared/defguard-ui/components/Layout/Card/Card'; -import { LoaderSpinner } from '../../../../shared/defguard-ui/components/Layout/LoaderSpinner/LoaderSpinner'; -import { MessageBox } from '../../../../shared/defguard-ui/components/Layout/MessageBox/MessageBox'; -import { useAppStore } from '../../../../shared/hooks/store/useAppStore'; -import useApi from '../../../../shared/hooks/useApi'; -import { useAddDevicePageStore } from '../../hooks/useAddDevicePageStore'; -import { AddDeviceNavigationEvent, AddDeviceStep } from '../../types'; -import { DeviceSetupMethodCard } from './components/DeviceSetupMethodCard/DeviceSetupMethodCard'; -import { DeviceSetupMethod } from './types'; - -export const AddDeviceSetupMethodStep = () => { - const { - user: { startDesktopActivation }, - } = useApi(); - const { LL } = useI18nContext(); - const localLL = LL.addDevicePage.steps.setupMethod; - - const [setupMethod, setSetupMethod] = useState(AddDeviceStep.CLIENT_CONFIGURATION); - const userData = useAddDevicePageStore((state) => state.userData); - - const enterpriseSettings = useAppStore((state) => state.enterprise_settings); - const [navSubject, setPageState, setStep] = useAddDevicePageStore( - (s) => [s.navigationSubject, s.setState, s.setStep], - shallow, - ); - - const { mutate, isPending } = useMutation({ - mutationFn: startDesktopActivation, - onSuccess: (resp) => { - setStep(setupMethod, { - clientSetup: { - url: resp.enrollment_url, - token: resp.enrollment_token, - }, - }); - }, - }); - - const startActivation = useCallback(() => { - mutate({ - username: userData?.username as string, - send_enrollment_notification: true, - email: userData?.email as string, - }); - }, [mutate, userData?.email, userData?.username]); - - useEffect(() => { - const sub = navSubject.subscribe((event) => { - if (event === AddDeviceNavigationEvent.NEXT) { - switch (setupMethod) { - case AddDeviceStep.NATIVE_CHOOSE_METHOD: - setPageState({ currentStep: AddDeviceStep.NATIVE_CHOOSE_METHOD }); - break; - case AddDeviceStep.CLIENT_CONFIGURATION: - startActivation(); - break; - } - } - }); - return () => { - sub.unsubscribe(); - }; - }, [navSubject, setPageState, setupMethod, startActivation]); - - useEffect(() => { - if ( - enterpriseSettings?.only_client_activation && - setupMethod === AddDeviceStep.NATIVE_CHOOSE_METHOD - ) { - setSetupMethod(AddDeviceStep.CLIENT_CONFIGURATION); - } - }, [enterpriseSettings?.only_client_activation, setupMethod]); - - return ( - <> - {!isPending ? ( - -

{localLL.title()}

- -
- { - setSetupMethod(AddDeviceStep.CLIENT_CONFIGURATION); - }} - /> - { - setSetupMethod(AddDeviceStep.NATIVE_CHOOSE_METHOD); - }} - /> -
-
- ) : ( -
- -
- )} - - ); -}; diff --git a/web/src/pages/addDevice/steps/AddDeviceSetupMethodStep/components/DeviceSetupMethodCard/DeviceSetupMethodCard.tsx b/web/src/pages/addDevice/steps/AddDeviceSetupMethodStep/components/DeviceSetupMethodCard/DeviceSetupMethodCard.tsx deleted file mode 100644 index cb1dbecc5..000000000 --- a/web/src/pages/addDevice/steps/AddDeviceSetupMethodStep/components/DeviceSetupMethodCard/DeviceSetupMethodCard.tsx +++ /dev/null @@ -1,365 +0,0 @@ -import './style.scss'; - -import clsx from 'clsx'; -import { type PropsWithChildren, type ReactNode, useId, useMemo } from 'react'; - -import { useI18nContext } from '../../../../../../i18n/i18n-react'; -import { isPresent } from '../../../../../../shared/defguard-ui/utils/isPresent'; -import { DeviceSetupMethod } from '../../types'; - -type StandaloneConfig = { - icon: ReactNode; - title: string; - description: string; - testId: string; - extras?: ReactNode; -}; - -type Props = { - active: boolean; - onClick: () => void; - methodType?: DeviceSetupMethod; - custom?: StandaloneConfig; - disabled?: boolean; -}; - -type ContentConfiguration = { - title: string; - description: string; - testId: string; -} & Pick & - PropsWithChildren; - -export const DeviceSetupMethodCard = ({ - methodType, - active, - onClick, - custom, - disabled = false, -}: Props) => { - const { LL } = useI18nContext(); - const localLL = LL.addDevicePage.steps.setupMethod.methods; - - const [title, description, testId] = useMemo(() => { - if (!isPresent(custom) && methodType) { - const testId = `add-device-method-${methodType.valueOf()}`; - switch (methodType) { - case DeviceSetupMethod.CLIENT: - return [localLL.client.title(), localLL.client.description(), testId]; - case DeviceSetupMethod.NATIVE_WG: - return [localLL.wg.title(), localLL.wg.description(), testId]; - default: - throw Error('Unimplemented setup method supplied to method card.'); - } - } - if (isPresent(custom)) { - return [custom.title, custom.description, custom.testId]; - } - throw Error('Bad props for DeviceSetupMethodCard'); - }, [custom, localLL.client, localLL.wg, methodType]); - - return ( - - {methodType === DeviceSetupMethod.CLIENT && ( - <> -
- - -
-
- -
- - )} - {methodType === DeviceSetupMethod.NATIVE_WG && ( -
- -
- )} - {isPresent(custom) && ( - <> -
{custom.icon}
- {custom.extras} - - )} -
- ); -}; - -const Content = ({ - active, - description, - onClick, - testId, - title, - children, - disabled = false, -}: ContentConfiguration) => { - return ( -
{ - if (!disabled) { - onClick?.(); - } - }} - > -

{title}

-

{description}

- {children} -
- ); -}; - -const PhoneSvg = () => { - return ( - - - - - ); -}; - -const DesktopSvg = () => { - return ( - - - - - - - - - - - - - - - - - - - - - - - - - - - ); -}; - -const DefguardLogo = () => { - const id = useId(); - return ( - - - - - - - - - - - ); -}; - -const WireguardLogo = () => { - return ( - - - - - - - - - - - ); -}; diff --git a/web/src/pages/addDevice/steps/AddDeviceSetupMethodStep/components/DeviceSetupMethodCard/style.scss b/web/src/pages/addDevice/steps/AddDeviceSetupMethodStep/components/DeviceSetupMethodCard/style.scss deleted file mode 100644 index b48c11c67..000000000 --- a/web/src/pages/addDevice/steps/AddDeviceSetupMethodStep/components/DeviceSetupMethodCard/style.scss +++ /dev/null @@ -1,67 +0,0 @@ -.device-method-card { - width: 100%; - display: flex; - flex-flow: column; - box-sizing: border-box; - align-items: center; - justify-content: flex-start; - background-color: var(--surface-frame-bg); - border-radius: 15px; - padding: var(--spacing-l) var(--spacing-s); - box-shadow: var(--box-shadow); - opacity: 1; - cursor: pointer; - outline: 0px solid transparent; - transition-property: outline, opacity; - - @include animate-standard; - - &:not(.active):not(.disabled):hover { - outline: 1px solid var(--surface-main-primary); - } - - &.active { - outline: 3px solid var(--surface-main-primary); - } - - &.disabled { - cursor: not-allowed; - opacity: 0.5; - } - - .title { - text-align: center; - padding-bottom: var(--spacing-s); - @include typography(app-body-1); - } - - .description { - text-align: center; - max-width: 280px; - color: var(--text-body-tertiary); - padding-bottom: var(--spacing-m); - - @include typography(welcome-h2); - } - - & > .icon { - display: flex; - flex-flow: row; - align-items: center; - justify-content: center; - column-gap: var(--spacing-s); - width: 100%; - - &:first-of-type { - padding-bottom: var(--spacing-l); - } - } - - .wg-icon { - display: flex; - flex-flow: column; - align-items: center; - justify-content: center; - min-height: 210px; - } -} diff --git a/web/src/pages/addDevice/steps/AddDeviceSetupMethodStep/style.scss b/web/src/pages/addDevice/steps/AddDeviceSetupMethodStep/style.scss deleted file mode 100644 index 10ed6ff23..000000000 --- a/web/src/pages/addDevice/steps/AddDeviceSetupMethodStep/style.scss +++ /dev/null @@ -1,45 +0,0 @@ -@use '@scssutils' as *; - -#add-device-page { - #setup-method-step { - display: flex; - flex-flow: column; - row-gap: var(--spacing-s); - - .message-box-spacer { - padding-bottom: 0px !important; - } - - & > .title { - @include typography(app-body-1); - user-select: none; - } - - .primary-methods { - display: grid; - grid-template-columns: 1fr 1fr; - grid-template-rows: 1fr; - column-gap: var(--spacing-m); - } - - .native-method { - display: flex; - flex-flow: row; - align-items: center; - justify-content: center; - - label { - @include typography(app-body-2); - color: var(--text-body-primary); - } - } - } - - #spinner-box { - display: flex; - flex-flow: column; - align-items: center; - justify-content: center; - min-height: 500px; - } -} diff --git a/web/src/pages/addDevice/steps/AddDeviceSetupMethodStep/types.ts b/web/src/pages/addDevice/steps/AddDeviceSetupMethodStep/types.ts deleted file mode 100644 index ce07d7633..000000000 --- a/web/src/pages/addDevice/steps/AddDeviceSetupMethodStep/types.ts +++ /dev/null @@ -1,4 +0,0 @@ -export enum DeviceSetupMethod { - CLIENT = 'client', - NATIVE_WG = 'native-wg', -} diff --git a/web/src/pages/addDevice/steps/AddDeviceSetupStep/AddDeviceSetupStep.tsx b/web/src/pages/addDevice/steps/AddDeviceSetupStep/AddDeviceSetupStep.tsx deleted file mode 100644 index f71c05c16..000000000 --- a/web/src/pages/addDevice/steps/AddDeviceSetupStep/AddDeviceSetupStep.tsx +++ /dev/null @@ -1,215 +0,0 @@ -import './style.scss'; - -import { zodResolver } from '@hookform/resolvers/zod'; -import { useMutation, useQueryClient } from '@tanstack/react-query'; -import parser from 'html-react-parser'; -import { useEffect, useMemo, useRef } from 'react'; -import { type SubmitHandler, useController, useForm } from 'react-hook-form'; -import { z } from 'zod'; -import { shallow } from 'zustand/shallow'; - -import { useI18nContext } from '../../../../i18n/i18n-react'; -import { FormInput } from '../../../../shared/defguard-ui/components/Form/FormInput/FormInput'; -import { FormToggle } from '../../../../shared/defguard-ui/components/Form/FormToggle/FormToggle'; -import { Card } from '../../../../shared/defguard-ui/components/Layout/Card/Card'; -import { MessageBox } from '../../../../shared/defguard-ui/components/Layout/MessageBox/MessageBox'; -import { MessageBoxType } from '../../../../shared/defguard-ui/components/Layout/MessageBox/types'; -import type { ToggleOption } from '../../../../shared/defguard-ui/components/Layout/Toggle/types'; -import useApi from '../../../../shared/hooks/useApi'; -import { useToaster } from '../../../../shared/hooks/useToaster'; -import { externalLink } from '../../../../shared/links'; -import { MutationKeys } from '../../../../shared/mutations'; -import { patternValidWireguardKey } from '../../../../shared/patterns'; -import { QueryKeys } from '../../../../shared/queries'; -import { generateWGKeys } from '../../../../shared/utils/generateWGKeys'; -import { trimObjectStrings } from '../../../../shared/utils/trimObjectStrings'; -import { useAddDevicePageStore } from '../../hooks/useAddDevicePageStore'; -import { - AddDeviceNavigationEvent, - AddDeviceStep, - AddNativeWgDeviceMode, -} from '../../types'; - -export const AddDeviceSetupStep = () => { - const { LL } = useI18nContext(); - const localLL = LL.addDevicePage.steps.setupDevice; - const toaster = useToaster(); - const { - device: { addDevice }, - } = useApi(); - const submitRef = useRef(null); - const userData = useAddDevicePageStore((state) => state.userData); - const [setStep, navSubject] = useAddDevicePageStore( - (state) => [state.setStep, state.navigationSubject], - shallow, - ); - - const toggleOptions = useMemo(() => { - const res: ToggleOption[] = [ - { - text: localLL.options.auto(), - value: AddNativeWgDeviceMode.AUTO, - }, - { - text: localLL.options.manual(), - value: AddNativeWgDeviceMode.MANUAL, - }, - ]; - return res; - }, [localLL.options]); - - const zodSchema = useMemo( - () => - z - .object({ - choice: z.nativeEnum(AddNativeWgDeviceMode), - name: z - .string() - .trim() - .min(4, LL.form.error.minimumLength()) - .refine((val) => !userData?.reservedDevices?.includes(val), { - message: localLL.form.errors.name.duplicatedName(), - }), - publicKey: z.string().trim(), - }) - .superRefine((val, ctx) => { - const { publicKey, choice } = val; - if (choice === AddNativeWgDeviceMode.MANUAL) { - const pubKeyRes = z - .string() - .min(44, LL.form.error.minimumLength()) - .max(44, LL.form.error.maximumLength()) - .regex(patternValidWireguardKey, LL.form.error.invalid()) - .safeParse(publicKey); - if (!pubKeyRes.success) { - ctx.addIssue({ - code: 'custom', - message: pubKeyRes.error.message, - path: ['publicKey'], - }); - } - } else { - const pubKeyRes = z.string().safeParse(publicKey); - if (!pubKeyRes.success) { - ctx.addIssue({ - code: 'custom', - path: ['publicKey'], - }); - } - } - }), - [LL.form.error, localLL.form.errors.name, userData?.reservedDevices], - ); - - type FormFields = z.infer; - - const { handleSubmit, control } = useForm({ - defaultValues: { - name: '', - choice: AddNativeWgDeviceMode.AUTO, - publicKey: '', - }, - resolver: zodResolver(zodSchema), - mode: 'all', - }); - - const queryClient = useQueryClient(); - - const { mutateAsync: addDeviceMutation } = useMutation({ - mutationFn: addDevice, - mutationKey: [MutationKeys.ADD_DEVICE], - onSuccess: () => { - void queryClient.invalidateQueries({ - queryKey: [QueryKeys.FETCH_USER_PROFILE], - }); - void queryClient.invalidateQueries({ - queryKey: ['user'], - }); - toaster.success(LL.addDevicePage.messages.deviceAdded()); - }, - onError: (err) => { - toaster.error(LL.messages.error()); - console.error(err); - }, - }); - - const validSubmitHandler: SubmitHandler = (values) => { - if (!userData) return; - values = trimObjectStrings(values); - if (values.choice === AddNativeWgDeviceMode.AUTO) { - const keys = generateWGKeys(); - void addDeviceMutation({ - name: values.name, - wireguard_pubkey: keys.publicKey, - username: userData.username, - }).then((response) => { - setStep(AddDeviceStep.NATIVE_CONFIGURATION, { - device: response.device, - publicKey: keys.publicKey, - privateKey: keys.privateKey, - networks: response.configs.map((c) => ({ - networkName: c.network_name, - networkId: c.network_id, - })), - }); - }); - } else { - void addDeviceMutation({ - name: values.name, - wireguard_pubkey: values.publicKey, - username: userData.username, - }).then((response) => { - setStep(AddDeviceStep.NATIVE_CONFIGURATION, { - device: response.device, - publicKey: values.publicKey, - privateKey: undefined, - networks: response.configs.map((c) => ({ - networkName: c.network_name, - networkId: c.network_id, - })), - }); - }); - } - }; - - const { - field: { value: choiceValue }, - } = useController({ control, name: 'choice' }); - - useEffect(() => { - const sub = navSubject.subscribe((event) => { - if (event === AddDeviceNavigationEvent.NEXT) { - submitRef.current?.click(); - } - }); - return () => { - sub.unsubscribe(); - }; - }, [navSubject]); - - return ( - -

{localLL.title()}

- - {parser( - localLL.infoMessage({ - addDevicesDocs: externalLink.gitbook.wireguard.addDevices, - }), - )} - -
- - - - - -
- ); -}; diff --git a/web/src/pages/addDevice/steps/AddDeviceSetupStep/style.scss b/web/src/pages/addDevice/steps/AddDeviceSetupStep/style.scss deleted file mode 100644 index 41c9e4e89..000000000 --- a/web/src/pages/addDevice/steps/AddDeviceSetupStep/style.scss +++ /dev/null @@ -1,9 +0,0 @@ -@use '@scssutils' as *; - -#add-device-page { - #add-device-setup-step { - h2 { - margin-bottom: 25px; - } - } -} diff --git a/web/src/pages/addDevice/style.scss b/web/src/pages/addDevice/style.scss deleted file mode 100644 index a66e5b930..000000000 --- a/web/src/pages/addDevice/style.scss +++ /dev/null @@ -1,111 +0,0 @@ -@use '@scssutils' as *; - -#add-device-page { - & > .page-content { - box-sizing: border-box; - padding: 100px 40px; - display: flex; - flex-flow: column; - align-items: center; - justify-content: flex-start; - overflow: auto; - - h1 { - color: var(--text-body-primary); - user-select: none; - - @include typography(app-title); - } - - h2 { - color: var(--text-body-primary); - padding-bottom: var(--spacing-s); - user-select: none; - - @include typography(app-body-1); - } - - & > .content-wrapper { - width: 100%; - max-width: 920px; - display: flex; - flex-flow: column; - align-items: center; - justify-content: flex-start; - width: 100%; - row-gap: 40px; - - & > * { - width: 100%; - } - - & > .card { - box-sizing: border-box; - padding: var(--spacing-l); - - .message-box-spacer { - padding-bottom: 20px; - } - - form { - & > * { - width: 100%; - } - } - } - - & > header { - display: flex; - flex-flow: row wrap; - align-items: center; - justify-content: flex-start; - user-select: none; - - & > .controls { - margin-left: auto; - display: flex; - flex-flow: row nowrap; - align-items: center; - justify-content: flex-start; - column-gap: 32px; - - & > .btn { - min-width: 180px; - - &.nav-back { - .arrow-single { - svg { - g { - fill: var(--surface-icon-primary); - } - } - } - } - - .arrow-single { - svg { - g { - fill: var(--surface-icon-secondary); - } - } - } - } - } - } - } - - form { - width: 100%; - display: flex; - flex-flow: column; - align-items: center; - justify-content: flex-start; - - & > * { - &:not(.input, .select) { - padding-bottom: 20px; - } - } - } - } -} diff --git a/web/src/pages/addDevice/types.ts b/web/src/pages/addDevice/types.ts deleted file mode 100644 index f0771818b..000000000 --- a/web/src/pages/addDevice/types.ts +++ /dev/null @@ -1,16 +0,0 @@ -export enum AddDeviceStep { - CHOOSE_METHOD, - NATIVE_CHOOSE_METHOD, - NATIVE_CONFIGURATION, - CLIENT_CONFIGURATION, -} - -export enum AddNativeWgDeviceMode { - AUTO, - MANUAL, -} - -export enum AddDeviceNavigationEvent { - NEXT, - BACK, -} diff --git a/web/src/pages/addDevice/utils/enrollmentToToken.ts b/web/src/pages/addDevice/utils/enrollmentToToken.ts deleted file mode 100644 index 5d3cdf52d..000000000 --- a/web/src/pages/addDevice/utils/enrollmentToToken.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { fromUint8Array } from 'js-base64'; - -export type EnrollmentData = { - url: string; - token: string; -}; - -export const enrollmentToImportToken = (url: string, token: string): string => { - const data: EnrollmentData = { - token, - url, - }; - const jsonString = JSON.stringify(data); - const textEncoder = new TextEncoder(); - const encoded = textEncoder.encode(jsonString); - return fromUint8Array(encoded); -}; diff --git a/web/src/pages/allow/OpenidAllowPage.tsx b/web/src/pages/allow/OpenidAllowPage.tsx deleted file mode 100644 index b600084d4..000000000 --- a/web/src/pages/allow/OpenidAllowPage.tsx +++ /dev/null @@ -1,156 +0,0 @@ -import './style.scss'; - -import type { AxiosError } from 'axios'; -import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; -import { useNavigate, useSearchParams } from 'react-router-dom'; - -import { useI18nContext } from '../../i18n/i18n-react'; -import SvgDefguardLogoLogin from '../../shared/components/svg/DefguardLogoLogin'; -import SvgIconCheckmarkWhite from '../../shared/components/svg/IconCheckmarkWhite'; -import SvgIconDelete from '../../shared/components/svg/IconDelete'; -import { Button } from '../../shared/defguard-ui/components/Layout/Button/Button'; -import { - ButtonSize, - ButtonStyleVariant, -} from '../../shared/defguard-ui/components/Layout/Button/types'; -import { useAuthStore } from '../../shared/hooks/store/useAuthStore'; -import useApi from '../../shared/hooks/useApi'; -import { useToaster } from '../../shared/hooks/useToaster'; -import { LoaderPage } from '../loader/LoaderPage'; - -export const OpenidAllowPage = () => { - const navigate = useNavigate(); - - const [allowLoading, setAllowLoading] = useState(false); - const [cancelLoading, setCancelLoading] = useState(false); - const [params] = useSearchParams(); - const [scope, setScope] = useState(''); - const [responseType, setResponseType] = useState(''); - const [clientId, setClientId] = useState(''); - const [redirectUri, setRedirectUri] = useState(''); - const [state, setState] = useState(''); - const [name, setName] = useState(''); - const inputRef = useRef(null); - const { - openid: { getOpenidClient }, - } = useApi(); - const setAuthStore = useAuthStore((state) => state.setState); - const [loadingInfo, setLoadingInfo] = useState(true); - const toaster = useToaster(); - - const { LL } = useI18nContext(); - - const paramsValid = useMemo(() => { - // nonce is optional in the auth code flow, just pass it as is further if it's in the params - const check = [scope, responseType, clientId, redirectUri, state]; - for (const item of check) { - if (typeof item === 'undefined' || item === null) { - toaster.error('OpenID Params invalid.'); - return false; - } - } - return true; - }, [clientId, redirectUri, responseType, scope, state, toaster]); - - const handleSubmit = useCallback( - (allow: boolean) => { - params.append('allow', String(allow)); - const formAction = `/api/v1/oauth/authorize?${params.toString()}`; - if (inputRef.current) { - inputRef.current.formAction = formAction; - inputRef.current.click(); - } - }, - [params], - ); - - useEffect(() => { - setScope(params.get('scope')); - setResponseType(params.get('response_type')); - setClientId(params.get('client_id')); - setState(params.get('state')); - setRedirectUri(params.get('redirect_uri')); - }, [params]); - - // biome-ignore lint/correctness/useExhaustiveDependencies: migration, checkMeLater - useEffect(() => { - if (paramsValid && clientId) { - getOpenidClient(clientId) - .then((res) => { - setName(res.name); - setLoadingInfo(false); - }) - .catch((error: AxiosError) => { - if (error.response?.status === 401) { - setAuthStore({ openIdParams: params }); - setLoadingInfo(false); - navigate('/auth', { replace: true }); - } else { - navigate('/', { replace: true }); - } - }); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [paramsValid, clientId]); - - const scopes: Record = { - openid: LL.openidAllow.scopes.openid(), - profile: LL.openidAllow.scopes.profile(), - email: LL.openidAllow.scopes.email(), - phone: LL.openidAllow.scopes.phone(), - groups: LL.openidAllow.scopes.groups(), - }; - - if (loadingInfo) return ; - - return ( - - ); -}; diff --git a/web/src/pages/allow/style.scss b/web/src/pages/allow/style.scss deleted file mode 100644 index fe252e197..000000000 --- a/web/src/pages/allow/style.scss +++ /dev/null @@ -1,68 +0,0 @@ -#openid-consent { - background-color: var(--bg-light); - height: 100%; - width: 100%; - display: flex; - flex-direction: row; - align-content: center; - justify-content: center; - align-items: center; - justify-items: center; - min-height: 100dvh; - - & > * { - @include media-breakpoint-up(lg) { - min-height: 100dvh; - } - } - - .logo-container { - display: none; - flex-direction: column; - align-content: center; - align-items: center; - justify-content: center; - background-color: var(--primary); - flex-grow: 1; - flex-shrink: 0; - width: 50%; - height: 100%; - - @include media-breakpoint-up(lg) { - display: flex; - } - } - - & > .consent { - width: 100%; - display: flex; - justify-content: center; - align-items: center; - flex-direction: column; - - ul { - margin: 0; - box-sizing: border-box; - padding: var(--spacing-l) 0; - width: 330px; - } - - .controls { - flex-direction: column; - gap: 1rem; - - button { - width: 330px; - margin-bottom: var(--spacing-s); - - &:last-of-type { - margin: 0; - } - } - } - } - - & > form { - display: none; - } -} diff --git a/web/src/pages/auth/AuthPage.tsx b/web/src/pages/auth/AuthPage.tsx deleted file mode 100644 index 5a81371ae..000000000 --- a/web/src/pages/auth/AuthPage.tsx +++ /dev/null @@ -1,178 +0,0 @@ -import './style.scss'; - -import { useEffect, useMemo, useState } from 'react'; -import { Navigate, Route, Routes, useNavigate, useSearchParams } from 'react-router-dom'; -import { shallow } from 'zustand/shallow'; - -import { useI18nContext } from '../../i18n/i18n-react'; -import SvgDefguardLogoLogin from '../../shared/components/svg/DefguardLogoLogin'; -import { useAppStore } from '../../shared/hooks/store/useAppStore'; -import { useAuthStore } from '../../shared/hooks/store/useAuthStore'; -import useApi from '../../shared/hooks/useApi'; -import { useToaster } from '../../shared/hooks/useToaster'; -import { UserMFAMethod } from '../../shared/types'; -import { RedirectPage } from '../redirect/RedirectPage'; -import { OpenIDCallback } from './Callback/Callback'; -import { Login } from './Login/Login'; -import { MFARoute } from './MFARoute/MFARoute'; -import { useMFAStore } from './shared/hooks/useMFAStore'; - -const VALID_URL_PATTERN = - /^(https?:\/\/[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9](?:\.[a-zA-Z]{2,})+(?::[0-9]{1,5})?(?:\/[a-zA-Z0-9\-._~%!$&'()*+,;=:@/]*)?(?:\?[a-zA-Z0-9\-._~%!$&'()*+,;=:@/?]*)?|\/[a-zA-Z0-9\-._~%!$&'()*+,;=:@/]*(?:\?[a-zA-Z0-9\-._~%!$&'()*+,;=:@/?]*)?)$/gi; - -// Return redirect URL only if it matches a safe pattern: -// - starts with http/https -// - contains only safe characters (no <, >) -// - can include query params -// -// Once a URL matches this pattern we also explicitly check for unsafe elements in case they are a part of redirect URL query params -const sanitizeRedirectUrl = (url: string | null) => { - if (url?.match(VALID_URL_PATTERN) && !/javascript:|data:|\\/.test(url)) return url; - - return null; -}; - -export const AuthPage = () => { - const { - getAppInfo, - settings: { getSettings }, - } = useApi(); - const { LL } = useI18nContext(); - const navigate = useNavigate(); - const [showRedirect, setShowRedirect] = useState(false); - - const loginSubject = useAuthStore((state) => state.loginSubject); - - const setAuthStore = useAuthStore((state) => state.setState); - - const [openIdParams, user] = useAuthStore( - (state) => [state.openIdParams, state.user], - shallow, - ); - - const mfaMethod = useMFAStore((state) => state.mfa_method); - - const [setMFAStore, resetMFAStore] = useMFAStore( - (state) => [state.setState, state.resetState], - shallow, - ); - - const settings = useAppStore((state) => state.settings); - - const toaster = useToaster(); - - const setAppStore = useAppStore((state) => state.setState); - - const [params] = useSearchParams(); - const redirectUrl = useMemo(() => sanitizeRedirectUrl(params.get('r')), [params]); - - useEffect(() => { - if (user && (!mfaMethod || mfaMethod === UserMFAMethod.NONE) && !openIdParams) { - navigate('/', { replace: true }); - } - }, [mfaMethod, navigate, openIdParams, user]); - - // biome-ignore lint/correctness/useExhaustiveDependencies: migration, checkMeLater - useEffect(() => { - // eslint-disable-next-line @typescript-eslint/no-misused-promises - const sub = loginSubject.subscribe(async ({ user, url, mfa }): Promise => { - // handle forward auth redirect - if (redirectUrl && user) { - setShowRedirect(true); - resetMFAStore(); - window.location.replace(redirectUrl); - return; - } - - // handle openid scenarios - - // user authenticated but app needs consent - if (openIdParams && user && !mfa) { - navigate(`/consent?${openIdParams.toString()}`, { replace: true }); - return; - } - - // application already had consent from user - if (url?.length && user) { - setShowRedirect(true); - resetMFAStore(); - window.location.replace(url); - return; - } - - if (mfa) { - setMFAStore(mfa); - let mfaUrl = ''; - switch (mfa.mfa_method) { - case UserMFAMethod.WEB_AUTH_N: - mfaUrl = '/auth/mfa/webauthn'; - break; - case UserMFAMethod.ONE_TIME_PASSWORD: - mfaUrl = '/auth/mfa/totp'; - break; - case UserMFAMethod.EMAIL: - mfaUrl = '/auth/mfa/email'; - break; - default: - toaster.error(LL.messages.error()); - console.error('API did not return any MFA method in MFA flow.'); - return; - } - navigate(mfaUrl, { replace: true }); - return; - } - - // authorization finished - if (user) { - let navigateURL = '/me'; - if (user.is_admin) { - // check where to navigate administrator - const appInfo = await getAppInfo(); - const settings = await getSettings(); - setAppStore({ - appInfo, - settings, - }); - if (settings.wireguard_enabled) { - if (!appInfo?.network_present) { - navigateURL = '/admin/wizard'; - } else { - navigateURL = '/admin/overview'; - } - } else { - navigateURL = '/admin/users'; - } - } - setAuthStore({ user }); - resetMFAStore(); - navigate(navigateURL, { replace: true }); - } - }); - return () => sub?.unsubscribe(); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [loginSubject, openIdParams, redirectUrl]); - - if (showRedirect) return ; - - return ( -
- - - } /> - } /> - } /> - } /> - } /> - } /> - -
- ); -}; diff --git a/web/src/pages/auth/Callback/Callback.tsx b/web/src/pages/auth/Callback/Callback.tsx deleted file mode 100644 index 70c620233..000000000 --- a/web/src/pages/auth/Callback/Callback.tsx +++ /dev/null @@ -1,99 +0,0 @@ -import './style.scss'; - -import { useMutation } from '@tanstack/react-query'; -import type { AxiosError } from 'axios'; -import { useEffect, useState } from 'react'; -import { useNavigate } from 'react-router'; - -import { useI18nContext } from '../../../i18n/i18n-react'; -import { Button } from '../../../shared/defguard-ui/components/Layout/Button/Button'; -import { LoaderSpinner } from '../../../shared/defguard-ui/components/Layout/LoaderSpinner/LoaderSpinner'; -import { useAuthStore } from '../../../shared/hooks/store/useAuthStore'; -import useApi from '../../../shared/hooks/useApi'; -import { useToaster } from '../../../shared/hooks/useToaster'; -import { MutationKeys } from '../../../shared/mutations'; -import type { CallbackData } from '../../../shared/types'; - -type ErrorResponse = { - msg: string; -}; - -export const OpenIDCallback = () => { - const { - auth: { - openid: { callback }, - }, - } = useApi(); - const loginSubject = useAuthStore((state) => state.loginSubject); - const toaster = useToaster(); - const { LL } = useI18nContext(); - const [error, setError] = useState(null); - const navigate = useNavigate(); - - const callbackMutation = useMutation({ - mutationFn: callback, - mutationKey: [MutationKeys.OPENID_CALLBACK], - onSuccess: (data) => loginSubject.next(data), - onError: (error: AxiosError) => { - toaster.error(LL.messages.error()); - console.error(error); - const errorResponse = error.response?.data as ErrorResponse; - if (errorResponse.msg) { - setError(errorResponse.msg); - } else { - setError(JSON.stringify(error)); - } - }, - retry: false, - }); - - // biome-ignore lint/correctness/useExhaustiveDependencies: migration, checkMeLater - useEffect(() => { - if (window.location.search && window.location.search.length > 0) { - // const hashFragment = window.location.search.substring(1); - const params = new URLSearchParams(window.location.search); - - // check if error occurred - const error = params.get('error'); - - if (error) { - setError(error); - toaster.error(LL.messages.error()); - return; - } - - const code = params.get('code'); - const state = params.get('state'); - - if (code && state) { - const data: CallbackData = { - code, - state, - }; - callbackMutation.mutate(data); - } else { - setError('Expected data not returned by the OpenID provider'); - toaster.error(LL.messages.error()); - return; - } - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - // FIXME: make it a bit more user friendly - return error ? ( -
-

{LL.loginPage.callback.error()}:

-

{error}

-
- ) : ( - - ); -}; diff --git a/web/src/pages/auth/Callback/style.scss b/web/src/pages/auth/Callback/style.scss deleted file mode 100644 index 2a75550de..000000000 --- a/web/src/pages/auth/Callback/style.scss +++ /dev/null @@ -1,12 +0,0 @@ -.error-info { - display: flex; - flex-direction: column; - gap: 10px; - text-align: center; - align-items: center; - justify-content: center; -} - -#back-to-login { - width: fit-content; -} diff --git a/web/src/pages/auth/Login/Login.tsx b/web/src/pages/auth/Login/Login.tsx deleted file mode 100644 index 6e55a34df..000000000 --- a/web/src/pages/auth/Login/Login.tsx +++ /dev/null @@ -1,163 +0,0 @@ -import './style.scss'; - -import { zodResolver } from '@hookform/resolvers/zod'; -import { useMutation, useQuery } from '@tanstack/react-query'; -import type { AxiosError } from 'axios'; -import { useMemo } from 'react'; -import { type SubmitHandler, useForm } from 'react-hook-form'; -import { z } from 'zod'; -import { useI18nContext } from '../../../i18n/i18n-react'; -import { FormInput } from '../../../shared/defguard-ui/components/Form/FormInput/FormInput'; -import { Button } from '../../../shared/defguard-ui/components/Layout/Button/Button'; -import { - ButtonSize, - ButtonStyleVariant, -} from '../../../shared/defguard-ui/components/Layout/Button/types'; -import { LoaderSpinner } from '../../../shared/defguard-ui/components/Layout/LoaderSpinner/LoaderSpinner'; -import { isPresent } from '../../../shared/defguard-ui/utils/isPresent'; -import { useAppStore } from '../../../shared/hooks/store/useAppStore'; -import { useAuthStore } from '../../../shared/hooks/store/useAuthStore'; -import useApi from '../../../shared/hooks/useApi'; -import { useToaster } from '../../../shared/hooks/useToaster'; -import { MutationKeys } from '../../../shared/mutations'; -import { patternLoginCharacters } from '../../../shared/patterns'; -import { QueryKeys } from '../../../shared/queries'; -import { trimObjectStrings } from '../../../shared/utils/trimObjectStrings'; -import { OpenIdLoginButton } from './components/OidcButtons'; - -type Inputs = { - username: string; - password: string; -}; - -export const Login = () => { - const { LL } = useI18nContext(); - const { - auth: { - login, - openid: { getOpenIdInfo: getOpenidInfo }, - }, - } = useApi(); - const toaster = useToaster(); - - const enterpriseEnabled = useAppStore((s) => s.appInfo?.license_info.enterprise); - - const { data: openIdInfo, isLoading: openIdLoading } = useQuery({ - enabled: enterpriseEnabled, - queryKey: [QueryKeys.FETCH_OPENID_INFO], - queryFn: getOpenidInfo, - refetchOnMount: true, - refetchOnWindowFocus: false, - retry: false, - }); - - const zodSchema = useMemo( - () => - z.object({ - username: z - .string() - .trim() - .min(1, LL.form.error.minimumLength()) - .max(64) - .regex(patternLoginCharacters, LL.form.error.forbiddenCharacter()), - password: z - .string() - .trim() - .min(1, LL.form.error.required()) - .max(128, LL.form.error.maximumLength()), - }), - [LL.form.error], - ); - - const { handleSubmit, control, setError } = useForm({ - resolver: zodResolver(zodSchema), - mode: 'all', - defaultValues: { - password: '', - username: '', - }, - }); - - const loginSubject = useAuthStore((state) => state.loginSubject); - - const loginMutation = useMutation({ - mutationFn: login, - mutationKey: [MutationKeys.LOG_IN], - onSuccess: (data) => loginSubject.next(data), - onError: (error: AxiosError) => { - const status = error.response?.status; - if (isPresent(status)) { - switch (status) { - case 401: { - setError( - 'password', - { - message: 'username or password is incorrect', - }, - { shouldFocus: true }, - ); - break; - } - case 429: { - toaster.error(LL.form.error.tooManyBadLoginAttempts()); - break; - } - default: { - toaster.error(LL.messages.error()); - } - } - } else { - toaster.error(LL.messages.error()); - } - }, - }); - - const onSubmit: SubmitHandler = (data) => { - if (!loginMutation.isPending) { - loginMutation.mutate(trimObjectStrings(data)); - } - }; - - return ( -
- {!enterpriseEnabled || !openIdLoading ? ( - <> -

{LL.loginPage.pageTitle()}

-
- - -
- ); -}; diff --git a/web/src/pages/auth/Login/components/OidcButtons.tsx b/web/src/pages/auth/Login/components/OidcButtons.tsx deleted file mode 100644 index c3f0c27c0..000000000 --- a/web/src/pages/auth/Login/components/OidcButtons.tsx +++ /dev/null @@ -1,193 +0,0 @@ -import './style.scss'; - -import { useI18nContext } from '../../../../i18n/i18n-react'; -import { Button } from '../../../../shared/defguard-ui/components/Layout/Button/Button'; -import { - ButtonSize, - ButtonStyleVariant, -} from '../../../../shared/defguard-ui/components/Layout/Button/types'; - -export const OpenIdLoginButton = ({ - url, - display_name, -}: { - url: string; - display_name?: string; -}) => { - const { hostname } = new URL(url); - - if (hostname === 'accounts.google.com') { - return ; - } else if (hostname === 'login.microsoftonline.com') { - return ; - } else { - return ; - } -}; - -const GoogleButton = ({ url }: { url: string }) => { - return ( - - ); -}; - -const CustomButton = ({ url, display_name }: { url: string; display_name?: string }) => { - const { LL } = useI18nContext(); - return ( - - ); -}; diff --git a/web/src/pages/auth/Login/components/style.scss b/web/src/pages/auth/Login/components/style.scss deleted file mode 100644 index 422c0fcec..000000000 --- a/web/src/pages/auth/Login/components/style.scss +++ /dev/null @@ -1,120 +0,0 @@ -.gsi-material-button { - -moz-user-select: none; - -webkit-user-select: none; - -ms-user-select: none; - -webkit-appearance: none; - background-color: WHITE; - background-image: none; - border: 1px solid #747775; - -webkit-border-radius: 4px; - border-radius: 4px; - -webkit-box-sizing: border-box; - box-sizing: border-box; - color: #1f1f1f; - cursor: pointer; - font-family: 'Roboto', arial, sans-serif; - font-size: 14px; - height: 40px; - letter-spacing: 0.25px; - outline: none; - overflow: hidden; - padding: 0 12px; - position: relative; - text-align: center; - -webkit-transition: - background-color 0.218s, - border-color 0.218s, - box-shadow 0.218s; - transition: - background-color 0.218s, - border-color 0.218s, - box-shadow 0.218s; - vertical-align: middle; - white-space: nowrap; - width: auto; - max-width: 400px; - min-width: min-content; -} - -.gsi-material-button .gsi-material-button-icon { - height: 20px; - margin-right: 12px; - min-width: 20px; - width: 20px; -} - -.gsi-material-button .gsi-material-button-content-wrapper { - -webkit-align-items: center; - align-items: center; - display: flex; - -webkit-flex-direction: row; - flex-direction: row; - -webkit-flex-wrap: nowrap; - flex-wrap: nowrap; - height: 100%; - justify-content: space-between; - position: relative; - width: 100%; -} - -.gsi-material-button .gsi-material-button-contents { - -webkit-flex-grow: 1; - flex-grow: 1; - font-family: 'Roboto', arial, sans-serif; - font-weight: 500; - overflow: hidden; - text-overflow: ellipsis; - vertical-align: top; -} - -.gsi-material-button .gsi-material-button-state { - -webkit-transition: opacity 0.218s; - transition: opacity 0.218s; - bottom: 0; - left: 0; - opacity: 0; - position: absolute; - right: 0; - top: 0; -} - -.gsi-material-button:disabled { - cursor: default; - background-color: #ffffff61; - border-color: #1f1f1f1f; -} - -.gsi-material-button:disabled .gsi-material-button-contents { - opacity: 38%; -} - -.gsi-material-button:disabled .gsi-material-button-icon { - opacity: 38%; -} - -.gsi-material-button:not(:disabled):active .gsi-material-button-state, -.gsi-material-button:not(:disabled):focus .gsi-material-button-state { - background-color: #303030; - opacity: 12%; -} - -.gsi-material-button:not(:disabled):hover { - -webkit-box-shadow: - 0 1px 2px 0 rgba(60, 64, 67, 0.3), - 0 1px 3px 1px rgba(60, 64, 67, 0.15); - box-shadow: - 0 1px 2px 0 rgba(60, 64, 67, 0.3), - 0 1px 3px 1px rgba(60, 64, 67, 0.15); -} - -.gsi-material-button:not(:disabled):hover .gsi-material-button-state { - background-color: #303030; - opacity: 8%; -} - -.ms-button { - background-color: transparent; - border: none; - cursor: pointer; - padding: 0; -} diff --git a/web/src/pages/auth/Login/style.scss b/web/src/pages/auth/Login/style.scss deleted file mode 100644 index a35ae9200..000000000 --- a/web/src/pages/auth/Login/style.scss +++ /dev/null @@ -1,47 +0,0 @@ -@use '@scssutils' as *; - -#login-container { - min-height: 100vh; - width: 100%; - display: flex; - flex-flow: column; - align-items: center; - justify-content: center; - box-sizing: border-box; - padding: 20px 0; - - @include media-breakpoint-up(lg) { - padding: 40px 0; - } - - h1 { - @include typography(app-welcome-1); - - color: var(--text-body-primary); - text-align: center; - width: 100%; - margin-bottom: 24px; - } - - form { - width: 100%; - max-width: 300px; - box-sizing: border-box; - - @include media-breakpoint-down(lg) { - padding: 10px; - } - - & > .input { - margin-bottom: 5px; - } - - & > .btn { - margin-bottom: 10px; - } - - & > * { - width: 100%; - } - } -} diff --git a/web/src/pages/auth/LoginEmail/LoginEmail.tsx b/web/src/pages/auth/LoginEmail/LoginEmail.tsx new file mode 100644 index 000000000..06c222b7b --- /dev/null +++ b/web/src/pages/auth/LoginEmail/LoginEmail.tsx @@ -0,0 +1,75 @@ +import { useMutation, useQuery } from '@tanstack/react-query'; +import type z from 'zod'; +import { m } from '../../../paraglide/messages'; +import api from '../../../shared/api/api'; +import { LoginPage } from '../../../shared/components/LoginPage/LoginPage'; +import { SizedBox } from '../../../shared/defguard-ui/components/SizedBox/SizedBox'; +import { useAppForm } from '../../../shared/defguard-ui/form'; +import { ThemeSpacing } from '../../../shared/defguard-ui/types'; +import { formChangeLogic } from '../../../shared/form'; +import { useAuth } from '../../../shared/hooks/useAuth'; +import { totpCodeFormSchema } from '../../../shared/schema/totpCode'; +import { MfaLinks } from '../shared/MfaLinks/MfaLinks'; + +const formSchema = totpCodeFormSchema; + +type FormFields = z.infer; + +const defaultValues: FormFields = { + code: '', +}; + +export const LoginEmail = () => { + useQuery({ + queryFn: api.auth.mfa.email.resend, + queryKey: ['auth', 'email'], + refetchOnWindowFocus: false, + refetchOnReconnect: true, + refetchOnMount: true, + }); + + const { mutateAsync } = useMutation({ + mutationFn: api.auth.mfa.email.verify, + meta: { + invalidate: ['me'], + }, + onSuccess: (response) => { + useAuth.getState().setUser(response.data.user); + }, + }); + + const form = useAppForm({ + defaultValues, + validationLogic: formChangeLogic, + validators: { + onSubmit: formSchema, + onChange: formSchema, + }, + onSubmit: async ({ value }) => { + await mutateAsync(value.code); + }, + }); + + return ( + +

{m.login_mfa_title()}

+

{m.login_mfa_email_subtitle()}

+ +
{ + e.stopPropagation(); + e.preventDefault(); + form.handleSubmit(); + }} + > + + + {(field) => } + + + +
+ +
+ ); +}; diff --git a/web/src/pages/auth/LoginMain/LoginMainPage.tsx b/web/src/pages/auth/LoginMain/LoginMainPage.tsx new file mode 100644 index 000000000..bcfc51193 --- /dev/null +++ b/web/src/pages/auth/LoginMain/LoginMainPage.tsx @@ -0,0 +1,156 @@ +import z from 'zod'; +import { m } from '../../../paraglide/messages'; +import { LoginPage } from '../../../shared/components/LoginPage/LoginPage'; +import { Button } from '../../../shared/defguard-ui/components/Button/Button'; +import { SizedBox } from '../../../shared/defguard-ui/components/SizedBox/SizedBox'; +import { useAppForm } from '../../../shared/defguard-ui/form'; +import { ThemeSize, ThemeSpacing } from '../../../shared/defguard-ui/types'; +import './style.scss'; +import { revalidateLogic } from '@tanstack/react-form'; +import { useMutation } from '@tanstack/react-query'; +import { useNavigate } from '@tanstack/react-router'; +import type { AxiosError } from 'axios'; +import { useEffect, useRef, useState } from 'react'; +import api from '../../../shared/api/api'; +import type { LoginMfaResponse, LoginResponseBasic } from '../../../shared/api/types'; +import { InfoBanner } from '../../../shared/defguard-ui/components/InfoBanner/InfoBanner'; +import { isPresent } from '../../../shared/defguard-ui/utils/isPresent'; +import { createZodIssue } from '../../../shared/defguard-ui/utils/zod'; +import { useAuth } from '../../../shared/hooks/useAuth'; + +const formSchema = z.object({ + username: z.string().trim().min(1, m.form_error_required()), + password: z.string().trim().min(1, m.form_error_required()), +}); + +type FormFields = z.infer; + +const defaults: FormFields = { + username: '', + password: '', +}; + +export const LoginMainPage = () => { + const [tooManyAttempts, setTooManyAttempts] = useState(false); + const attemptsTimeoutRef = useRef(null); + const setAuthStore = useAuth((s) => s.setUser); + const navigate = useNavigate(); + const form = useAppForm({ + defaultValues: defaults, + validationLogic: revalidateLogic({ + mode: 'change', + modeAfterSubmission: 'change', + }), + validators: { + onChange: formSchema, + onSubmit: formSchema, + }, + onSubmit: async ({ value }) => { + if (tooManyAttempts) return; + try { + const { data } = await mutateAsync(value); + + // @ts-expect-error + // biome-ignore lint/complexity/useLiteralKeys: needed + if (data['user'] !== undefined) { + const basicResponse = data as LoginResponseBasic; + setAuthStore(basicResponse.user); + navigate({ + to: '/user/$username', + params: { + username: basicResponse.user?.username as string, + }, + }); + } else { + const mfa = data as LoginMfaResponse; + useAuth.setState({ + mfaLogin: mfa, + }); + } + } catch (_) {} + }, + }); + + const { mutateAsync } = useMutation({ + mutationFn: api.auth.login, + onError: (error: AxiosError) => { + const status = error.response?.status; + if (isPresent(status)) { + if (status === 401) { + form.setErrorMap({ + onSubmit: { + fields: { + password: createZodIssue(m.login_error_invalid(), ['password']), + }, + }, + }); + } + if (status === 429) { + setTooManyAttempts(true); + const timeoutId = setTimeout(() => { + setTooManyAttempts(false); + }, 300_000); + attemptsTimeoutRef.current = timeoutId; + } + } + }, + }); + + useEffect(() => { + return () => { + if (attemptsTimeoutRef.current !== null) { + clearTimeout(attemptsTimeoutRef.current); + } + }; + }, []); + + return ( + +

{m.login_main_title()}

+

{m.login_main_subtitle()}

+ + {tooManyAttempts && ( + <> + + + + )} + +
{ + e.preventDefault(); + e.stopPropagation(); + form.handleSubmit(); + }} + > + + {(field) => } + + + {(field) => ( + + )} + +
- } - /> -
-
- -
-
- -
-
- -
-
- {formatDate} -
-
- -
- - ); -}; - -const DeviceRowEditButton = (props: { data: StandaloneDevice }) => { - const { LL } = useI18nContext(); - const { - standaloneDevice: { getDeviceConfig, generateAuthToken }, - } = useApi(); - const toaster = useToaster(); - const { mutateAsync, isPending: deviceConfigLoading } = useMutation({ - mutationFn: getDeviceConfig, - onError: (e) => { - toaster.error(LL.modals.standaloneDeviceConfigModal.toasters.getConfig.error()); - console.error(e); - }, - }); - - const { mutateAsync: mutateTokenGen, isPending: deviceTokenLoading } = useMutation({ - mutationFn: generateAuthToken, - onError: (e) => { - toaster.error(LL.modals.standaloneDeviceEnrollmentModal.toasters.error()); - console.error(e); - }, - }); - - const openDelete = useDeleteStandaloneDeviceModal((s) => s.open, shallow); - const openEdit = useEditStandaloneDeviceModal((s) => s.open, shallow); - const openConfig = useStandaloneDeviceConfigModal((s) => s.open); - const openEnrollment = useStandaloneDeviceEnrollmentModal((s) => s.open); - - const handleOpenConfig = useCallback(() => { - void mutateAsync(props.data.id).then((config) => { - openConfig({ - device: props.data, - config, - }); - }); - }, [mutateAsync, openConfig, props.data]); - - const handleTokenGen = useCallback(() => { - void mutateTokenGen(props.data.id).then((res) => { - openEnrollment({ - device: props.data, - enrollment: res, - }); - }); - }, [mutateTokenGen, openEnrollment, props.data]); - - return ( - - openEdit(props.data)} - /> - handleOpenConfig()} - disabled={!props.data.configured || deviceConfigLoading} - /> - handleTokenGen()} - disabled={deviceTokenLoading} - /> - openDelete(props.data)} - /> - - ); -}; diff --git a/web/src/pages/devices/components/DevicesList/modals/ConfirmDeviceDeleteModal.tsx b/web/src/pages/devices/components/DevicesList/modals/ConfirmDeviceDeleteModal.tsx deleted file mode 100644 index 12e4cc195..000000000 --- a/web/src/pages/devices/components/DevicesList/modals/ConfirmDeviceDeleteModal.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import { useMutation, useQueryClient } from '@tanstack/react-query'; -import { shallow } from 'zustand/shallow'; - -import { useI18nContext } from '../../../../../i18n/i18n-react'; -import { ConfirmModal } from '../../../../../shared/defguard-ui/components/Layout/modals/ConfirmModal/ConfirmModal'; -import useApi from '../../../../../shared/hooks/useApi'; -import { useToaster } from '../../../../../shared/hooks/useToaster'; -import { QueryKeys } from '../../../../../shared/queries'; -import { useDeleteStandaloneDeviceModal } from '../../../hooks/useDeleteStandaloneDeviceModal'; - -export const ConfirmDeviceDeleteModal = () => { - const { LL } = useI18nContext(); - const localLL = LL.modals.deleteStandaloneDevice; - const [visible, device] = useDeleteStandaloneDeviceModal( - (s) => [s.visible, s.device], - shallow, - ); - const queryClient = useQueryClient(); - const [close, reset] = useDeleteStandaloneDeviceModal( - (s) => [s.close, s.reset], - shallow, - ); - - const { - standaloneDevice: { deleteDevice }, - } = useApi(); - - const toaster = useToaster(); - - const { mutate, isPending: isLoading } = useMutation({ - mutationFn: deleteDevice, - onSuccess: () => { - void queryClient.invalidateQueries({ - queryKey: [QueryKeys.FETCH_STANDALONE_DEVICE_LIST], - }); - close(); - toaster.success(localLL.messages.success()); - }, - onError: (e) => { - toaster.error(localLL.messages.error()); - close(); - console.error(e); - }, - }); - - const isOpen = visible && device !== undefined; - - return ( - { - if (device) { - mutate(device.id); - } - }} - onClose={close} - afterClose={reset} - loading={isLoading} - /> - ); -}; diff --git a/web/src/pages/devices/components/DevicesList/style.scss b/web/src/pages/devices/components/DevicesList/style.scss deleted file mode 100644 index a8c0a2131..000000000 --- a/web/src/pages/devices/components/DevicesList/style.scss +++ /dev/null @@ -1,118 +0,0 @@ -@mixin spacing { - display: inline-grid; - grid-template-rows: 1fr; - grid-template-columns: 250px 150px 350px 1fr 180px 200px 50px; - column-gap: 15px; -} - -#devices-page-devices-list { - overflow-x: auto; - min-width: 1540px; - - .scroll-container { - max-height: 650px; - } - - .headers { - @include spacing; - - & > :nth-child(1) { - padding-left: 50px; - } - - & > :nth-child(7) { - align-items: center; - justify-content: center; - - span { - text-align: center; - } - } - } - - .device-row { - width: 100%; - height: 60px; - box-sizing: border-box; - padding: 0 15px; - border: 1px solid transparent; - background-color: var(--surface-default-modal); - border-radius: 15px; - transition-property: border; - transition-duration: 100ms; - transition-timing-function: ease-in-out; - align-items: center; - - &:hover, - &.active { - border-color: var(--border-primary); - } - - @include spacing; - } -} - -.device-item-floating { - font-family: 'Roboto'; - font-size: 15px; - font-weight: 500; -} - -#devices-page-devices-list .device-row { - .avatar-icon { - min-width: 40px; - max-width: 40px; - } - - p, - span { - font-family: 'Roboto'; - font-size: 15px; - font-weight: 500; - } - - [class^='cell-'] { - height: 100%; - width: 100%; - max-width: 100%; - overflow: hidden; - display: flex; - flex-flow: row; - align-items: center; - justify-content: flex-start; - } - - .text-limited { - height: 100%; - } - - .cell-7 { - justify-content: center; - } - - .cell-1 { - display: flex; - flex-flow: row; - align-items: center; - justify-content: flex-start; - column-gap: 10px; - - .avatar-icon { - width: 40px; - height: 40px; - - svg { - width: 30px; - height: 30px; - } - } - } -} - -.copy { - background-color: transparent; - border: 0 solid transparent; - cursor: pointer; - display: flex; - align-items: center; -} diff --git a/web/src/pages/devices/hooks/useDeleteStandaloneDeviceModal.tsx b/web/src/pages/devices/hooks/useDeleteStandaloneDeviceModal.tsx deleted file mode 100644 index ba415d5bc..000000000 --- a/web/src/pages/devices/hooks/useDeleteStandaloneDeviceModal.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { createWithEqualityFn } from 'zustand/traditional'; - -import type { StandaloneDevice } from '../../../shared/types'; - -const defaultValues: StoreValues = { - visible: false, - device: undefined, -}; - -export const useDeleteStandaloneDeviceModal = createWithEqualityFn( - (set) => ({ - ...defaultValues, - open: (device) => set({ visible: true, device }), - close: () => set({ visible: false }), - reset: () => set(defaultValues), - }), - Object.is, -); - -type Store = StoreValues & StoreMethods; - -type StoreValues = { - visible: boolean; - device?: StandaloneDevice; -}; -type StoreMethods = { - open: (device: StandaloneDevice) => void; - close: () => void; - reset: () => void; -}; diff --git a/web/src/pages/devices/hooks/useDevicesPage.tsx b/web/src/pages/devices/hooks/useDevicesPage.tsx deleted file mode 100644 index e1fcc98a0..000000000 --- a/web/src/pages/devices/hooks/useDevicesPage.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { useState } from 'react'; -import { createContainer } from 'react-tracked'; - -import type { StandaloneDevice } from '../../../shared/types'; - -export type DevicesPageContext = { - devices: StandaloneDevice[]; - reservedDeviceNames: string[]; - search: string; -}; - -const initialState: DevicesPageContext = { - devices: [], - reservedDeviceNames: [], - search: '', -}; - -const useValue = () => useState(initialState); - -export const { Provider: DevicesPageProvider, useTracked: useDevicesPage } = - createContainer(useValue); diff --git a/web/src/pages/devices/hooks/useEditStandaloneDeviceModal.tsx b/web/src/pages/devices/hooks/useEditStandaloneDeviceModal.tsx deleted file mode 100644 index 5a17156f9..000000000 --- a/web/src/pages/devices/hooks/useEditStandaloneDeviceModal.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { createWithEqualityFn } from 'zustand/traditional'; - -import type { StandaloneDevice } from '../../../shared/types'; - -const defaults: StoreValues = { - visible: false, - device: undefined, -}; - -export const useEditStandaloneDeviceModal = createWithEqualityFn( - (set) => ({ - ...defaults, - open: (device) => set({ device: device, visible: true }), - close: () => set({ visible: false }), - reset: () => set(defaults), - }), - Object.is, -); - -type Store = StoreValues & StoreMethods; -type StoreValues = { - visible: boolean; - device?: StandaloneDevice; -}; -type StoreMethods = { - open: (device: StandaloneDevice) => void; - close: () => void; - reset: () => void; -}; diff --git a/web/src/pages/devices/modals/AddStandaloneDeviceModal/AddStandaloneDeviceModal.tsx b/web/src/pages/devices/modals/AddStandaloneDeviceModal/AddStandaloneDeviceModal.tsx deleted file mode 100644 index 414d3d545..000000000 --- a/web/src/pages/devices/modals/AddStandaloneDeviceModal/AddStandaloneDeviceModal.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import './style.scss'; - -import { useEffect, useMemo } from 'react'; -import { shallow } from 'zustand/shallow'; - -import { useI18nContext } from '../../../../i18n/i18n-react'; -import { ModalWithTitle } from '../../../../shared/defguard-ui/components/Layout/modals/ModalWithTitle/ModalWithTitle'; -import { FinishCliStep } from './steps/FinishCliStep/FinishCliStep'; -import { FinishManualStep } from './steps/FinishManualStep/FinishManualStep'; -import { MethodStep } from './steps/MethodStep/MethodStep'; -import { SetupCliStep } from './steps/SetupCliStep/SetupCliStep'; -import { SetupManualStep } from './steps/SetupManualStep/SetupManualStep'; -import { useAddStandaloneDeviceModal } from './store'; - -const steps = [ - , - , - , - , - , -]; - -export const AddStandaloneDeviceModal = () => { - const { LL } = useI18nContext(); - const localLL = LL.modals.addStandaloneDevice; - const [currentStep, visible] = useAddStandaloneDeviceModal( - (s) => [s.currentStep, s.visible], - shallow, - ); - const [close, reset] = useAddStandaloneDeviceModal((s) => [s.close, s.reset], shallow); - - const getTitle = useMemo(() => { - switch (currentStep.valueOf()) { - case 0: - return localLL.steps.method.title(); - case 1: - return localLL.steps.cli.title(); - case 2: - return localLL.steps.cli.title(); - case 3: - return localLL.steps.manual.title(); - case 4: - return localLL.steps.manual.title(); - } - }, [currentStep, localLL.steps.cli, localLL.steps.manual, localLL.steps.method]); - - // biome-ignore lint/correctness/useExhaustiveDependencies: migration, checkMeLater - useEffect(() => { - return () => { - reset(); - }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - return ( - close()} - afterClose={() => reset()} - /> - ); -}; diff --git a/web/src/pages/devices/modals/AddStandaloneDeviceModal/steps/FinishCliStep/FinishCliStep.tsx b/web/src/pages/devices/modals/AddStandaloneDeviceModal/steps/FinishCliStep/FinishCliStep.tsx deleted file mode 100644 index b42923e4e..000000000 --- a/web/src/pages/devices/modals/AddStandaloneDeviceModal/steps/FinishCliStep/FinishCliStep.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import './style.scss'; - -import { shallow } from 'zustand/shallow'; - -import { useI18nContext } from '../../../../../../i18n/i18n-react'; -import { Button } from '../../../../../../shared/defguard-ui/components/Layout/Button/Button'; -import { - ButtonSize, - ButtonStyleVariant, -} from '../../../../../../shared/defguard-ui/components/Layout/Button/types'; -import { StandaloneDeviceModalEnrollmentContent } from '../../../components/StandaloneDeviceModalEnrollmentContent/StandaloneDeviceModalEnrollmentContent'; -import { useAddStandaloneDeviceModal } from '../../store'; - -export const FinishCliStep = () => { - const { LL } = useI18nContext(); - const [closeModal] = useAddStandaloneDeviceModal((s) => [s.close], shallow); - const enroll = useAddStandaloneDeviceModal((s) => s.enrollResponse); - - if (!enroll) return null; - return ( -
- -
-
-
- ); -}; diff --git a/web/src/pages/devices/modals/AddStandaloneDeviceModal/steps/FinishCliStep/style.scss b/web/src/pages/devices/modals/AddStandaloneDeviceModal/steps/FinishCliStep/style.scss deleted file mode 100644 index 98058a417..000000000 --- a/web/src/pages/devices/modals/AddStandaloneDeviceModal/steps/FinishCliStep/style.scss +++ /dev/null @@ -1,27 +0,0 @@ -#add-standalone-device-modal { - .finish-cli-step { - .download { - width: 100%; - display: flex; - flex-flow: row; - align-items: center; - justify-content: center; - padding-bottom: 30px; - - a { - text-decoration: none; - cursor: pointer; - } - - .btn { - height: 47px; - } - } - - .expanded-content { - p { - @include typography(app-code); - } - } - } -} diff --git a/web/src/pages/devices/modals/AddStandaloneDeviceModal/steps/FinishManualStep/FinishManualStep.tsx b/web/src/pages/devices/modals/AddStandaloneDeviceModal/steps/FinishManualStep/FinishManualStep.tsx deleted file mode 100644 index 24895d80f..000000000 --- a/web/src/pages/devices/modals/AddStandaloneDeviceModal/steps/FinishManualStep/FinishManualStep.tsx +++ /dev/null @@ -1,164 +0,0 @@ -import './style.scss'; - -import { type ReactNode, useCallback, useMemo, useState } from 'react'; -import QRCode from 'react-qr-code'; -import { shallow } from 'zustand/shallow'; - -import { useI18nContext } from '../../../../../../i18n/i18n-react'; -import { RenderMarkdown } from '../../../../../../shared/components/Layout/RenderMarkdown/RenderMarkdown'; -import { ActionButton } from '../../../../../../shared/defguard-ui/components/Layout/ActionButton/ActionButton'; -import { ActionButtonVariant } from '../../../../../../shared/defguard-ui/components/Layout/ActionButton/types'; -import { Button } from '../../../../../../shared/defguard-ui/components/Layout/Button/Button'; -import { - ButtonSize, - ButtonStyleVariant, -} from '../../../../../../shared/defguard-ui/components/Layout/Button/types'; -import { ExpandableCard } from '../../../../../../shared/defguard-ui/components/Layout/ExpandableCard/ExpandableCard'; -import { MessageBox } from '../../../../../../shared/defguard-ui/components/Layout/MessageBox/MessageBox'; -import { MessageBoxType } from '../../../../../../shared/defguard-ui/components/Layout/MessageBox/types'; -import { useClipboard } from '../../../../../../shared/hooks/useClipboard'; -import { downloadWGConfig } from '../../../../../../shared/utils/downloadWGConfig'; -import { useAddStandaloneDeviceModal } from '../../store'; - -export const FinishManualStep = () => { - const { LL } = useI18nContext(); - const localLL = LL.modals.addStandaloneDevice.steps.manual.finish; - const [closeModal] = useAddStandaloneDeviceModal((s) => [s.close], shallow); - const manual = useAddStandaloneDeviceModal((s) => s.manualResponse); - const generatedKeys = useAddStandaloneDeviceModal((s) => s.genKeys); - - return ( -
- -
-

{LL.modals.addStandaloneDevice.form.labels.deviceName()}:

-

{manual?.device.name}

-
-
-

{localLL.ctaInstruction()}

-
- - - - {manual && ( - - )} -
-
-
- ); -}; - -type ConfigCardProps = { - config: string; - privateKey?: string; - publicKey: string; - deviceName: string; -}; - -enum ConfigCardView { - FILE, - QR, -} - -const DeviceConfigCard = ({ - config, - privateKey, - publicKey, - deviceName, -}: ConfigCardProps) => { - const { LL } = useI18nContext(); - const { writeToClipboard } = useClipboard(); - const localLL = LL.modals.addStandaloneDevice.steps.manual.finish; - const [view, setView] = useState(ConfigCardView.FILE); - - const configForExport = useMemo(() => { - if (privateKey) { - return config.replace('YOUR_PRIVATE_KEY', privateKey); - } - return config; - }, [config, privateKey]); - - const getQRConfig = useMemo((): string => { - if (privateKey) { - return config.replace('YOUR_PRIVATE_KEY', privateKey); - } - return config.replace('YOUR_PRIVATE_KEY', publicKey); - }, [config, privateKey, publicKey]); - - const renderTextConfig = () => { - const content = configForExport.split('\n'); - return ( -

- {content.map((text, index) => ( - <> - {text} - {index !== content.length - 1 &&
} - - ))} -

- ); - }; - - const handleConfigCopy = useCallback(() => { - void writeToClipboard( - configForExport, - LL.components.deviceConfigsCard.messages.copyConfig(), - ); - }, [LL.components.deviceConfigsCard.messages, configForExport, writeToClipboard]); - - const handleConfigDownload = useCallback(() => { - downloadWGConfig(configForExport, deviceName.toLowerCase().replace(' ', '-')); - }, [configForExport, deviceName]); - - const actions = useMemo( - (): ReactNode[] => [ - setView(ConfigCardView.FILE)} - />, - setView(ConfigCardView.QR)} - />, - , - , - ], - [handleConfigCopy, handleConfigDownload, view], - ); - return ( - - {view === ConfigCardView.FILE && renderTextConfig()} - {view === ConfigCardView.QR && } - - ); -}; diff --git a/web/src/pages/devices/modals/AddStandaloneDeviceModal/steps/FinishManualStep/style.scss b/web/src/pages/devices/modals/AddStandaloneDeviceModal/steps/FinishManualStep/style.scss deleted file mode 100644 index 8b4d33261..000000000 --- a/web/src/pages/devices/modals/AddStandaloneDeviceModal/steps/FinishManualStep/style.scss +++ /dev/null @@ -1,37 +0,0 @@ -#add-standalone-device-modal { - .finish-manual-step { - & > .device-name { - padding-bottom: 20px; - - .label { - color: var(--text-body-tertiary); - @include typography(app-wizard-1); - user-select: none; - padding-bottom: 10px; - } - - .name { - color: var(--text-body-primary); - @include typography(app-input); - } - } - - & > .cta { - width: 100%; - display: flex; - flex-flow: row; - align-items: center; - justify-content: center; - padding-bottom: 20px; - - p { - color: var(--text-body-secondary); - text-align: center; - width: 100%; - max-width: 410px; - text-wrap: balance; - @include typography(app-input); - } - } - } -} diff --git a/web/src/pages/devices/modals/AddStandaloneDeviceModal/steps/MethodStep/MethodStep.tsx b/web/src/pages/devices/modals/AddStandaloneDeviceModal/steps/MethodStep/MethodStep.tsx deleted file mode 100644 index a054a9150..000000000 --- a/web/src/pages/devices/modals/AddStandaloneDeviceModal/steps/MethodStep/MethodStep.tsx +++ /dev/null @@ -1,238 +0,0 @@ -import './style.scss'; - -import { useQuery } from '@tanstack/react-query'; -import { useCallback, useEffect, useId } from 'react'; -import { shallow } from 'zustand/shallow'; - -import { useI18nContext } from '../../../../../../i18n/i18n-react'; -import SvgWireguardLogo from '../../../../../../shared/components/svg/WireguardLogo'; -import { Button } from '../../../../../../shared/defguard-ui/components/Layout/Button/Button'; -import { - ButtonSize, - ButtonStyleVariant, -} from '../../../../../../shared/defguard-ui/components/Layout/Button/types'; -import type { SelectOption } from '../../../../../../shared/defguard-ui/components/Layout/Select/types'; -import SvgIconOutsideLink from '../../../../../../shared/defguard-ui/components/svg/IconOutsideLink'; -import useApi from '../../../../../../shared/hooks/useApi'; -import { externalLink } from '../../../../../../shared/links'; -import { QueryKeys } from '../../../../../../shared/queries'; -import type { Network } from '../../../../../../shared/types'; -import { DeviceSetupMethodCard } from '../../../../../addDevice/steps/AddDeviceSetupMethodStep/components/DeviceSetupMethodCard/DeviceSetupMethodCard'; -import { useAddStandaloneDeviceModal } from '../../store'; -import { - AddStandaloneDeviceModalChoice, - AddStandaloneDeviceModalStep, -} from '../../types'; - -export const MethodStep = () => { - // this is needs bcs opening modal again and again would prevent availableIp to refetch - const modalSessionID = useId(); - const { LL } = useI18nContext(); - const localLL = LL.modals.addStandaloneDevice.steps.method; - const choice = useAddStandaloneDeviceModal((s) => s.choice); - const { - network: { getNetworks }, - standaloneDevice: { getAvailableIp }, - } = useApi(); - - const { data: networks } = useQuery({ - queryKey: [QueryKeys.FETCH_NETWORKS], - queryFn: getNetworks, - refetchOnWindowFocus: false, - refetchOnMount: true, - }); - - const { - data: availableIpResponse, - refetch: refetchAvailableIp, - isLoading: availableIpLoading, - } = useQuery({ - queryKey: [ - 'ADD_STANDALONE_DEVICE_MODAL_FETCH_INITIAL_AVAILABLE_IP', - networks, - modalSessionID, - ], - queryFn: () => - getAvailableIp({ - locationId: (networks as Network[])[0].id, - }), - enabled: networks !== undefined && Array.isArray(networks) && networks.length > 0, - refetchOnMount: true, - refetchOnReconnect: true, - refetchOnWindowFocus: false, - }); - - const [setState, close, next] = useAddStandaloneDeviceModal( - (s) => [s.setStore, s.close, s.changeStep], - shallow, - ); - - const handleChange = useCallback( - (choice: AddStandaloneDeviceModalChoice) => { - setState({ choice }); - }, - [setState], - ); - - const handleNext = () => { - switch (choice) { - case AddStandaloneDeviceModalChoice.CLI: - next(AddStandaloneDeviceModalStep.SETUP_CLI); - break; - case AddStandaloneDeviceModalChoice.MANUAL: - next(AddStandaloneDeviceModalStep.SETUP_MANUAL); - break; - } - }; - - // biome-ignore lint/correctness/useExhaustiveDependencies: migration, checkMeLater - useEffect(() => { - if (networks) { - const options: SelectOption[] = networks.map((n) => ({ - key: n.id, - value: n.id, - label: n.name, - })); - setState({ - networks, - networkOptions: options, - }); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [networks]); - - // biome-ignore lint/correctness/useExhaustiveDependencies: migration, checkMeLater - useEffect(() => { - if (availableIpResponse) { - setState({ initLocationIpResponse: availableIpResponse }); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [availableIpResponse]); - - return ( -
-
- , - testId: 'standalone-device-choice-card-cli', - extras: ( - - ), - }} - active={choice === AddStandaloneDeviceModalChoice.CLI} - onClick={() => handleChange(AddStandaloneDeviceModalChoice.CLI)} - /> - , - testId: 'standalone-device-choice-card-manual', - }} - active={choice === AddStandaloneDeviceModalChoice.MANUAL} - onClick={() => handleChange(AddStandaloneDeviceModalChoice.MANUAL)} - /> -
-
-
-
- ); -}; - -const DefguardIcon = () => { - return ( - - - - - - - - - - - - - - - - - - - ); -}; diff --git a/web/src/pages/devices/modals/AddStandaloneDeviceModal/steps/MethodStep/style.scss b/web/src/pages/devices/modals/AddStandaloneDeviceModal/steps/MethodStep/style.scss deleted file mode 100644 index 43f3d1125..000000000 --- a/web/src/pages/devices/modals/AddStandaloneDeviceModal/steps/MethodStep/style.scss +++ /dev/null @@ -1,43 +0,0 @@ -#add-standalone-device-modal .method-step { - box-sizing: border-box; - padding: 0 55px; - - .choices { - display: grid; - grid-template-columns: 1fr 1fr; - padding-bottom: 40px; - column-gap: 50px; - - .device-method-card { - .link-container { - width: 100%; - display: flex; - flex-flow: row; - align-items: center; - justify-content: center; - - .link { - width: 100%; - text-decoration: none; - text-align: center; - @include typography(app-body-2); - - &:hover { - text-decoration: underline; - } - - .spacer { - display: inline-block; - width: var(--spacing-xs); - height: 1px; - } - - svg { - width: 13; - height: 13; - } - } - } - } - } -} diff --git a/web/src/pages/devices/modals/AddStandaloneDeviceModal/steps/SetupCliStep/SetupCliStep.tsx b/web/src/pages/devices/modals/AddStandaloneDeviceModal/steps/SetupCliStep/SetupCliStep.tsx deleted file mode 100644 index 0490ec38c..000000000 --- a/web/src/pages/devices/modals/AddStandaloneDeviceModal/steps/SetupCliStep/SetupCliStep.tsx +++ /dev/null @@ -1,149 +0,0 @@ -import { useMutation, useQueryClient } from '@tanstack/react-query'; -import { useCallback, useMemo, useState } from 'react'; -import { shallow } from 'zustand/shallow'; - -import { useI18nContext } from '../../../../../../i18n/i18n-react'; -import { Button } from '../../../../../../shared/defguard-ui/components/Layout/Button/Button'; -import { - ButtonSize, - ButtonStyleVariant, -} from '../../../../../../shared/defguard-ui/components/Layout/Button/types'; -import { MessageBox } from '../../../../../../shared/defguard-ui/components/Layout/MessageBox/MessageBox'; -import { MessageBoxType } from '../../../../../../shared/defguard-ui/components/Layout/MessageBox/types'; -import { useAppStore } from '../../../../../../shared/hooks/store/useAppStore'; -import { useAuthStore } from '../../../../../../shared/hooks/store/useAuthStore'; -import { useEnterpriseUpgradeStore } from '../../../../../../shared/hooks/store/useEnterpriseUpgradeStore'; -import useApi from '../../../../../../shared/hooks/useApi'; -import { useToaster } from '../../../../../../shared/hooks/useToaster'; -import { QueryKeys } from '../../../../../../shared/queries'; -import { invalidateMultipleQueries } from '../../../../../../shared/utils/invalidateMultipleQueries'; -import { useDevicesPage } from '../../../../hooks/useDevicesPage'; -import { StandaloneDeviceModalForm } from '../../../components/StandaloneDeviceModalForm/StandaloneDeviceModalForm'; -import { StandaloneDeviceModalFormMode } from '../../../components/types'; -import { useAddStandaloneDeviceModal } from '../../store'; -import { - type AddStandaloneDeviceFormFields, - AddStandaloneDeviceModalStep, - WGConfigGenChoice, -} from '../../types'; - -export const SetupCliStep = () => { - const [{ reservedDeviceNames }] = useDevicesPage(); - const { LL } = useI18nContext(); - const localLL = LL.modals.addStandaloneDevice.steps.cli.setup; - const [formLoading, setFormLoading] = useState(false); - const queryClient = useQueryClient(); - const [setState, close, next, submitSubject] = useAddStandaloneDeviceModal( - (s) => [s.setStore, s.close, s.changeStep, s.submitSubject], - shallow, - ); - const currentUserId = useAuthStore((s) => s.user?.id); - - const toast = useToaster(); - - const { - standaloneDevice: { createCliDevice }, - } = useApi(); - - const showUpgradeToast = useEnterpriseUpgradeStore((s) => s.show); - const { getAppInfo } = useApi(); - const setAppStore = useAppStore((s) => s.setState, shallow); - - const { mutateAsync } = useMutation({ - mutationFn: createCliDevice, - onSuccess: () => { - toast.success(LL.modals.addStandaloneDevice.toasts.deviceCreated()); - invalidateMultipleQueries(queryClient, [ - [QueryKeys.FETCH_USER_PROFILE, currentUserId], - [QueryKeys.FETCH_STANDALONE_DEVICE_LIST], - ]); - void getAppInfo().then((response) => { - setAppStore({ - appInfo: response, - }); - if (response.license_info.any_limit_exceeded) { - showUpgradeToast(); - } - }); - }, - onError: (e) => { - toast.error(LL.modals.addStandaloneDevice.toasts.creationFailed()); - console.error(e); - }, - }); - - const [initIpResponse, locationOptions] = useAddStandaloneDeviceModal( - (s) => [s.initLocationIpResponse, s.networkOptions], - shallow, - ); - - const defaultValues = useMemo(() => { - if (initIpResponse && locationOptions) { - const res: AddStandaloneDeviceFormFields = { - modifiableIpParts: initIpResponse.map((ip) => ip.modifiable_part), - generationChoice: WGConfigGenChoice.AUTO, - location_id: locationOptions[0].value, - name: '', - wireguard_pubkey: '', - description: '', - }; - return res; - } - return undefined; - }, [initIpResponse, locationOptions]); - - const handleSubmit = useCallback( - async (values: AddStandaloneDeviceFormFields) => { - const response = await mutateAsync({ - assigned_ips: values.modifiableIpParts, - location_id: values.location_id, - name: values.name, - description: values.description, - }); - setState({ enrollResponse: response }); - next(AddStandaloneDeviceModalStep.FINISH_CLI); - }, - [mutateAsync, next, setState], - ); - - if (initIpResponse === undefined || defaultValues === undefined) return null; - - return ( -
- - -
-
-
- ); -}; diff --git a/web/src/pages/devices/modals/AddStandaloneDeviceModal/steps/SetupManualStep/SetupManualStep.tsx b/web/src/pages/devices/modals/AddStandaloneDeviceModal/steps/SetupManualStep/SetupManualStep.tsx deleted file mode 100644 index a96d93905..000000000 --- a/web/src/pages/devices/modals/AddStandaloneDeviceModal/steps/SetupManualStep/SetupManualStep.tsx +++ /dev/null @@ -1,150 +0,0 @@ -import './style.scss'; - -import { useMutation, useQueryClient } from '@tanstack/react-query'; -import { useCallback, useMemo, useState } from 'react'; -import { shallow } from 'zustand/shallow'; - -import { useI18nContext } from '../../../../../../i18n/i18n-react'; -import { Button } from '../../../../../../shared/defguard-ui/components/Layout/Button/Button'; -import { - ButtonSize, - ButtonStyleVariant, -} from '../../../../../../shared/defguard-ui/components/Layout/Button/types'; -import { useAppStore } from '../../../../../../shared/hooks/store/useAppStore'; -import { useAuthStore } from '../../../../../../shared/hooks/store/useAuthStore'; -import { useEnterpriseUpgradeStore } from '../../../../../../shared/hooks/store/useEnterpriseUpgradeStore'; -import useApi from '../../../../../../shared/hooks/useApi'; -import { QueryKeys } from '../../../../../../shared/queries'; -import { generateWGKeys } from '../../../../../../shared/utils/generateWGKeys'; -import { invalidateMultipleQueries } from '../../../../../../shared/utils/invalidateMultipleQueries'; -import { useDevicesPage } from '../../../../hooks/useDevicesPage'; -import { StandaloneDeviceModalForm } from '../../../components/StandaloneDeviceModalForm/StandaloneDeviceModalForm'; -import { StandaloneDeviceModalFormMode } from '../../../components/types'; -import { useAddStandaloneDeviceModal } from '../../store'; -import { - type AddStandaloneDeviceFormFields, - AddStandaloneDeviceModalStep, - WGConfigGenChoice, -} from '../../types'; - -export const SetupManualStep = () => { - const { LL } = useI18nContext(); - const [formLoading, setFormLoading] = useState(false); - const [setState, next, submitSubject, close] = useAddStandaloneDeviceModal( - (s) => [s.setStore, s.changeStep, s.submitSubject, s.close], - shallow, - ); - const [initialIpResponse, locationOptions] = useAddStandaloneDeviceModal( - (s) => [s.initLocationIpResponse, s.networkOptions], - shallow, - ); - - const queryClient = useQueryClient(); - - const currentUserId = useAuthStore((s) => s.user?.id); - - const [{ reservedDeviceNames }] = useDevicesPage(); - - const { - standaloneDevice: { createManualDevice: createDevice }, - } = useApi(); - - const showUpgradeToast = useEnterpriseUpgradeStore((s) => s.show); - const { getAppInfo } = useApi(); - const setAppStore = useAppStore((s) => s.setState, shallow); - - const { mutateAsync } = useMutation({ - mutationFn: createDevice, - onSuccess: () => { - invalidateMultipleQueries(queryClient, [ - [QueryKeys.FETCH_USER_PROFILE, currentUserId], - [QueryKeys.FETCH_STANDALONE_DEVICE_LIST], - ]); - void getAppInfo().then((response) => { - setAppStore({ - appInfo: response, - }); - if (response.license_info.any_limit_exceeded) { - showUpgradeToast(); - } - }); - }, - }); - - const handleSubmit = useCallback( - async (values: AddStandaloneDeviceFormFields) => { - let pub = values.wireguard_pubkey; - if (values.generationChoice === WGConfigGenChoice.AUTO) { - const keys = generateWGKeys(); - pub = keys.publicKey; - setState({ - genKeys: keys, - }); - } - const response = await mutateAsync({ - assigned_ips: values.modifiableIpParts, - location_id: values.location_id, - name: values.name, - description: values.description, - wireguard_pubkey: pub, - }); - setState({ - genChoice: values.generationChoice, - manualResponse: response, - }); - next(AddStandaloneDeviceModalStep.FINISH_MANUAL); - }, - [mutateAsync, next, setState], - ); - - const defaultFormValues = useMemo(() => { - if (locationOptions && initialIpResponse) { - const res: AddStandaloneDeviceFormFields = { - modifiableIpParts: initialIpResponse.map((ip) => ip.modifiable_part), - generationChoice: WGConfigGenChoice.AUTO, - location_id: locationOptions[0].value, - name: '', - wireguard_pubkey: '', - description: '', - }; - return res; - } - return undefined; - }, [initialIpResponse, locationOptions]); - - if (initialIpResponse === undefined || defaultFormValues === undefined) return null; - - return ( -
- -
-
-
- ); -}; diff --git a/web/src/pages/devices/modals/AddStandaloneDeviceModal/steps/SetupManualStep/style.scss b/web/src/pages/devices/modals/AddStandaloneDeviceModal/steps/SetupManualStep/style.scss deleted file mode 100644 index d6d0f5e91..000000000 --- a/web/src/pages/devices/modals/AddStandaloneDeviceModal/steps/SetupManualStep/style.scss +++ /dev/null @@ -1,7 +0,0 @@ -#add-standalone-device-modal { - .setup-manual { - .toggle { - margin-bottom: 20px; - } - } -} diff --git a/web/src/pages/devices/modals/AddStandaloneDeviceModal/store.tsx b/web/src/pages/devices/modals/AddStandaloneDeviceModal/store.tsx deleted file mode 100644 index e724ba7cd..000000000 --- a/web/src/pages/devices/modals/AddStandaloneDeviceModal/store.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import { isObject } from 'lodash-es'; -import { Subject } from 'rxjs'; -import { createWithEqualityFn } from 'zustand/traditional'; - -import type { SelectOption } from '../../../../shared/defguard-ui/components/Layout/Select/types'; -import type { - CreateStandaloneDeviceResponse, - GetAvailableLocationIpResponse, - Network, - StartEnrollmentResponse, -} from '../../../../shared/types'; -import { - AddStandaloneDeviceModalChoice, - AddStandaloneDeviceModalStep, - WGConfigGenChoice, -} from './types'; - -const defaultValues: StoreValues = { - visible: false, - currentStep: AddStandaloneDeviceModalStep.METHOD_CHOICE, - choice: AddStandaloneDeviceModalChoice.CLI, - networks: undefined, - networkOptions: [], - genChoice: WGConfigGenChoice.AUTO, - submitSubject: new Subject(), - initLocationIpResponse: undefined, - genKeys: undefined, - manualResponse: undefined, - enrollResponse: undefined, -}; - -export const useAddStandaloneDeviceModal = createWithEqualityFn( - (set) => ({ - ...defaultValues, - setStore: (v) => set(v), - reset: () => set(defaultValues), - close: () => set({ visible: false }), - open: () => set({ ...defaultValues, visible: true }), - changeStep: (step) => set({ currentStep: step }), - }), - isObject, -); - -type Store = StoreValues & StoreMethods; - -type StoreValues = { - visible: boolean; - currentStep: AddStandaloneDeviceModalStep; - choice: AddStandaloneDeviceModalChoice; - networkOptions: SelectOption[]; - genChoice: WGConfigGenChoice; - submitSubject: Subject; - initLocationIpResponse?: GetAvailableLocationIpResponse; - networks?: Network[]; - genKeys?: { - publicKey: string; - privateKey: string; - }; - manualResponse?: CreateStandaloneDeviceResponse; - enrollResponse?: StartEnrollmentResponse; -}; - -type StoreMethods = { - setStore: (values: Partial) => void; - reset: () => void; - close: () => void; - open: () => void; - changeStep: (step: AddStandaloneDeviceModalStep) => void; -}; diff --git a/web/src/pages/devices/modals/AddStandaloneDeviceModal/style.scss b/web/src/pages/devices/modals/AddStandaloneDeviceModal/style.scss deleted file mode 100644 index 9e8b7949e..000000000 --- a/web/src/pages/devices/modals/AddStandaloneDeviceModal/style.scss +++ /dev/null @@ -1,59 +0,0 @@ -#add-standalone-device-modal { - width: 100%; - max-width: 920px; - - & > .header { - padding-left: 55px; - } - - .controls { - display: flex; - flex-flow: row; - align-items: center; - justify-content: flex-start; - width: 100%; - column-gap: 20px; - padding-top: 40px; - - .btn { - width: 100%; - height: 47px; - } - - &.solo { - width: 100%; - align-items: center; - justify-content: center; - - .btn { - width: 50%; - } - } - } - - .step-content > div { - box-sizing: border-box; - padding: 0 55px; - - .message-box-spacer { - padding-bottom: 20px; - } - } - - .step-content { - form { - .row { - width: 100%; - display: flex; - flex-flow: column; - row-gap: 0; - - @include media-breakpoint-up(lg) { - display: grid; - grid-template-columns: 1fr 1fr; - column-gap: 20px; - } - } - } - } -} diff --git a/web/src/pages/devices/modals/AddStandaloneDeviceModal/types.ts b/web/src/pages/devices/modals/AddStandaloneDeviceModal/types.ts deleted file mode 100644 index 0c43cda03..000000000 --- a/web/src/pages/devices/modals/AddStandaloneDeviceModal/types.ts +++ /dev/null @@ -1,31 +0,0 @@ -export enum AddStandaloneDeviceModalStep { - METHOD_CHOICE, - SETUP_CLI, - FINISH_CLI, - SETUP_MANUAL, - FINISH_MANUAL, -} - -export enum AddStandaloneDeviceModalChoice { - CLI, - MANUAL, -} - -export enum WGConfigGenChoice { - MANUAL, - AUTO, -} - -export type AddStandaloneDeviceFormFields = { - name: string; - location_id: number; - modifiableIpParts: string[]; - wireguard_pubkey?: string; - generationChoice: WGConfigGenChoice; - description?: string; -}; - -export type AddStandaloneDeviceCLIFormFields = Omit< - AddStandaloneDeviceFormFields, - 'generationChoice' | 'wireguard_pubkey' ->; diff --git a/web/src/pages/devices/modals/EditStandaloneDeviceModal/EditStandaloneModal.tsx b/web/src/pages/devices/modals/EditStandaloneDeviceModal/EditStandaloneModal.tsx deleted file mode 100644 index 1b140e833..000000000 --- a/web/src/pages/devices/modals/EditStandaloneDeviceModal/EditStandaloneModal.tsx +++ /dev/null @@ -1,184 +0,0 @@ -import './style.scss'; - -import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; -import { useCallback, useEffect, useMemo, useState } from 'react'; -import { Subject } from 'rxjs'; -import { shallow } from 'zustand/shallow'; - -import { useI18nContext } from '../../../../i18n/i18n-react'; -import { Button } from '../../../../shared/defguard-ui/components/Layout/Button/Button'; -import { - ButtonSize, - ButtonStyleVariant, -} from '../../../../shared/defguard-ui/components/Layout/Button/types'; -import { LoaderSpinner } from '../../../../shared/defguard-ui/components/Layout/LoaderSpinner/LoaderSpinner'; -import { ModalWithTitle } from '../../../../shared/defguard-ui/components/Layout/modals/ModalWithTitle/ModalWithTitle'; -import { useAuthStore } from '../../../../shared/hooks/store/useAuthStore'; -import useApi from '../../../../shared/hooks/useApi'; -import { useToaster } from '../../../../shared/hooks/useToaster'; -import { QueryKeys } from '../../../../shared/queries'; -import { selectifyNetworks } from '../../../../shared/utils/form/selectifyNetwork'; -import { invalidateMultipleQueries } from '../../../../shared/utils/invalidateMultipleQueries'; -import { useDevicesPage } from '../../hooks/useDevicesPage'; -import { useEditStandaloneDeviceModal } from '../../hooks/useEditStandaloneDeviceModal'; -import { - type AddStandaloneDeviceFormFields, - WGConfigGenChoice, -} from '../AddStandaloneDeviceModal/types'; -import { StandaloneDeviceModalForm } from '../components/StandaloneDeviceModalForm/StandaloneDeviceModalForm'; -import { StandaloneDeviceModalFormMode } from '../components/types'; - -export const EditStandaloneModal = () => { - const { LL } = useI18nContext(); - const localLL = LL.modals.editStandaloneModal; - const [close, reset] = useEditStandaloneDeviceModal((s) => [s.close, s.reset], shallow); - const isOpen = useEditStandaloneDeviceModal((s) => s.visible); - - // biome-ignore lint/correctness/useExhaustiveDependencies: migration, checkMeLater - useEffect(() => { - return () => { - reset(); - }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - return ( - - - - ); -}; - -const ModalContent = () => { - const { LL } = useI18nContext(); - const localLL = LL.modals.editStandaloneModal; - const device = useEditStandaloneDeviceModal((s) => s.device); - const [submitSubject] = useState(new Subject()); - const [formLoading, setFormLoading] = useState(false); - const [closeModal] = useEditStandaloneDeviceModal((s) => [s.close], shallow); - const toaster = useToaster(); - const queryClient = useQueryClient(); - const currentUserId = useAuthStore((s) => s.user?.id); - const [{ reservedDeviceNames }] = useDevicesPage(); - - const { - network: { getNetworks }, - standaloneDevice: { editDevice }, - } = useApi(); - - const { mutateAsync } = useMutation({ - mutationFn: editDevice, - onSuccess: () => { - toaster.success(localLL.toasts.success()); - invalidateMultipleQueries(queryClient, [ - [QueryKeys.FETCH_USER_PROFILE, currentUserId], - [QueryKeys.FETCH_STANDALONE_DEVICE_LIST], - ]); - closeModal(); - }, - onError: (e) => { - toaster.error(localLL.toasts.failure()); - console.error(e); - }, - }); - - const { data: networks } = useQuery({ - queryKey: [QueryKeys.FETCH_NETWORKS], - queryFn: getNetworks, - refetchOnWindowFocus: false, - refetchOnMount: true, - }); - - const locationOptions = useMemo(() => { - if (networks) { - return selectifyNetworks(networks); - } - return []; - }, [networks]); - - const defaultValues = useMemo(() => { - if (locationOptions && device) { - const modifiableParts = device.assigned_ips.map( - (ip, i) => - ip.split(device.split_ips[i].network_part)[1] || - device.split_ips[i].modifiable_part, - ); - - const res: AddStandaloneDeviceFormFields = { - name: device?.name, - modifiableIpParts: modifiableParts, - location_id: device.location.id, - description: device.description, - generationChoice: WGConfigGenChoice.AUTO, - wireguard_pubkey: '', - }; - return res; - } - return undefined; - }, [device, locationOptions]); - - const handleSubmit = useCallback( - async (values: AddStandaloneDeviceFormFields) => { - if (device) { - await mutateAsync({ - assigned_ips: values.modifiableIpParts, - id: device.id, - name: values.name, - description: values.description, - }); - } - }, - [device, mutateAsync], - ); - - if (!device) { - return null; - } - - return ( - <> - {defaultValues && ( - ({ - ip: device.assigned_ips[i], - ...device.split_ips[i], - }))} - /> - )} - {!defaultValues && ( -
- -
- )} -
-
- - ); -}; diff --git a/web/src/pages/devices/modals/EditStandaloneDeviceModal/style.scss b/web/src/pages/devices/modals/EditStandaloneDeviceModal/style.scss deleted file mode 100644 index fd45ad7f6..000000000 --- a/web/src/pages/devices/modals/EditStandaloneDeviceModal/style.scss +++ /dev/null @@ -1,47 +0,0 @@ -#edit-standalone-device-modal { - width: 100%; - max-width: 920px; - - .loader { - width: 100%; - display: flex; - flex-flow: row; - align-items: center; - justify-content: center; - min-height: 300px; - } - - .controls { - display: flex; - width: 100%; - gap: 20px; - padding-top: 40px; - box-sizing: border-box; - padding-left: 30px; - padding-right: 30px; - flex-flow: column; - align-items: center; - justify-content: flex-start; - - @include media-breakpoint-up(lg) { - flex-flow: row; - align-items: center; - justify-content: flex-start; - } - - .btn { - width: 100%; - height: 47px; - } - - &.solo { - width: 100%; - align-items: center; - justify-content: center; - - .btn { - width: 50%; - } - } - } -} diff --git a/web/src/pages/devices/modals/StandaloneDeviceConfigModal/StandaloneDeviceConfigModal.tsx b/web/src/pages/devices/modals/StandaloneDeviceConfigModal/StandaloneDeviceConfigModal.tsx deleted file mode 100644 index 0626cc64e..000000000 --- a/web/src/pages/devices/modals/StandaloneDeviceConfigModal/StandaloneDeviceConfigModal.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import { useEffect } from 'react'; -import { shallow } from 'zustand/shallow'; - -import { useI18nContext } from '../../../../i18n/i18n-react'; -import { WireguardConfigExpandable } from '../../../../shared/components/Layout/WireguardConfigExpandable/WireguardConfigExpandable'; -import { Button } from '../../../../shared/defguard-ui/components/Layout/Button/Button'; -import { ModalWithTitle } from '../../../../shared/defguard-ui/components/Layout/modals/ModalWithTitle/ModalWithTitle'; -import { useStandaloneDeviceConfigModal } from './store'; - -export const StandaloneDeviceConfigModal = () => { - const { LL } = useI18nContext(); - const isOpen = useStandaloneDeviceConfigModal((s) => s.visible); - const [close, reset] = useStandaloneDeviceConfigModal( - (s) => [s.close, s.reset], - shallow, - ); - - // biome-ignore lint/correctness/useExhaustiveDependencies: migration, checkMeLater - useEffect(() => { - return () => { - reset(); - }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - return ( - - - - ); -}; - -const ModalContent = () => { - const { LL } = useI18nContext(); - const data = useStandaloneDeviceConfigModal((s) => s.data); - const close = useStandaloneDeviceConfigModal((s) => s.close, shallow); - - if (!data) return null; - return ( - <> - -
-
- - ); -}; diff --git a/web/src/pages/devices/modals/StandaloneDeviceConfigModal/store.tsx b/web/src/pages/devices/modals/StandaloneDeviceConfigModal/store.tsx deleted file mode 100644 index e02d0481f..000000000 --- a/web/src/pages/devices/modals/StandaloneDeviceConfigModal/store.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { createWithEqualityFn } from 'zustand/traditional'; - -import type { StandaloneDevice } from '../../../../shared/types'; - -const defaults: StoreValues = { - visible: false, -}; - -export const useStandaloneDeviceConfigModal = createWithEqualityFn( - (set) => ({ - ...defaults, - open: (data) => set({ data, visible: true }), - close: () => set({ visible: false }), - reset: () => set(defaults), - }), - Object.is, -); - -type Store = StoreValues & StoreMethods; -type StoreValues = { - visible: boolean; - data?: { - device: StandaloneDevice; - config: string; - }; -}; - -type StoreMethods = { - open: (values: StoreValues['data']) => void; - close: () => void; - reset: () => void; -}; diff --git a/web/src/pages/devices/modals/StandaloneDeviceEnrollmentModal/StandaloneDeviceEnrollmentModal.tsx b/web/src/pages/devices/modals/StandaloneDeviceEnrollmentModal/StandaloneDeviceEnrollmentModal.tsx deleted file mode 100644 index 967dfd872..000000000 --- a/web/src/pages/devices/modals/StandaloneDeviceEnrollmentModal/StandaloneDeviceEnrollmentModal.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import { shallow } from 'zustand/shallow'; - -import { useI18nContext } from '../../../../i18n/i18n-react'; -import { Button } from '../../../../shared/defguard-ui/components/Layout/Button/Button'; -import { ModalWithTitle } from '../../../../shared/defguard-ui/components/Layout/modals/ModalWithTitle/ModalWithTitle'; -import { StandaloneDeviceModalEnrollmentContent } from '../components/StandaloneDeviceModalEnrollmentContent/StandaloneDeviceModalEnrollmentContent'; -import { useStandaloneDeviceEnrollmentModal } from './store'; - -export const StandaloneDeviceEnrollmentModal = () => { - const { LL } = useI18nContext(); - const localLL = LL.modals.standaloneDeviceEnrollmentModal; - const [close, reset] = useStandaloneDeviceEnrollmentModal( - (s) => [s.close, s.reset], - shallow, - ); - const isOpen = useStandaloneDeviceEnrollmentModal((s) => s.visible); - return ( - - - - ); -}; - -const ModalContent = () => { - const { LL } = useI18nContext(); - const modalData = useStandaloneDeviceEnrollmentModal((s) => s.data); - const closeModal = useStandaloneDeviceEnrollmentModal((s) => s.close, shallow); - if (!modalData) return null; - return ( - <> - -
-
- - ); -}; diff --git a/web/src/pages/devices/modals/StandaloneDeviceEnrollmentModal/store.tsx b/web/src/pages/devices/modals/StandaloneDeviceEnrollmentModal/store.tsx deleted file mode 100644 index ee55db40e..000000000 --- a/web/src/pages/devices/modals/StandaloneDeviceEnrollmentModal/store.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { createWithEqualityFn } from 'zustand/traditional'; - -import type { StandaloneDevice, StartEnrollmentResponse } from '../../../../shared/types'; - -const defaults: StoreValues = { - visible: false, - data: undefined, -}; - -export const useStandaloneDeviceEnrollmentModal = createWithEqualityFn( - (set) => ({ - ...defaults, - open: (data) => set({ data: data, visible: true }), - close: () => set({ visible: false }), - reset: () => set(defaults), - }), - Object.is, -); - -type Store = StoreValues & StoreMethods; -type StoreValues = { - visible: boolean; - data?: { - device: StandaloneDevice; - enrollment: StartEnrollmentResponse; - }; -}; -type StoreMethods = { - open: (data: StoreValues['data']) => void; - close: () => void; - reset: () => void; -}; diff --git a/web/src/pages/devices/modals/components/StandaloneDeviceModalEnrollmentContent/StandaloneDeviceModalEnrollmentContent.tsx b/web/src/pages/devices/modals/components/StandaloneDeviceModalEnrollmentContent/StandaloneDeviceModalEnrollmentContent.tsx deleted file mode 100644 index 33348212d..000000000 --- a/web/src/pages/devices/modals/components/StandaloneDeviceModalEnrollmentContent/StandaloneDeviceModalEnrollmentContent.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import './style.scss'; - -import { useMemo } from 'react'; - -import { useI18nContext } from '../../../../../i18n/i18n-react'; -import { ActionButton } from '../../../../../shared/defguard-ui/components/Layout/ActionButton/ActionButton'; -import { ActionButtonVariant } from '../../../../../shared/defguard-ui/components/Layout/ActionButton/types'; -import { Button } from '../../../../../shared/defguard-ui/components/Layout/Button/Button'; -import { - ButtonSize, - ButtonStyleVariant, -} from '../../../../../shared/defguard-ui/components/Layout/Button/types'; -import { ExpandableCard } from '../../../../../shared/defguard-ui/components/Layout/ExpandableCard/ExpandableCard'; -import { MessageBox } from '../../../../../shared/defguard-ui/components/Layout/MessageBox/MessageBox'; -import { MessageBoxType } from '../../../../../shared/defguard-ui/components/Layout/MessageBox/types'; -import { useClipboard } from '../../../../../shared/hooks/useClipboard'; -import { externalLink } from '../../../../../shared/links'; -import type { StartEnrollmentResponse } from '../../../../../shared/types'; - -type Props = { - enrollmentData: StartEnrollmentResponse; -}; -export const StandaloneDeviceModalEnrollmentContent = ({ - enrollmentData: { enrollment_token, enrollment_url }, -}: Props) => { - const { LL } = useI18nContext(); - const localLL = LL.components.standaloneDeviceTokenModalContent; - const { writeToClipboard } = useClipboard(); - const commandToCopy = useMemo(() => { - return `dg enroll -u ${enrollment_url} -t ${enrollment_token}`; - }, [enrollment_token, enrollment_url]); - - return ( -
- - - { - void writeToClipboard(commandToCopy); - }} - key={0} - />, - ]} - expanded={true} - disableExpand={true} - > -

{commandToCopy}

-
-
- ); -}; diff --git a/web/src/pages/devices/modals/components/StandaloneDeviceModalEnrollmentContent/style.scss b/web/src/pages/devices/modals/components/StandaloneDeviceModalEnrollmentContent/style.scss deleted file mode 100644 index 35cbfc50b..000000000 --- a/web/src/pages/devices/modals/components/StandaloneDeviceModalEnrollmentContent/style.scss +++ /dev/null @@ -1,19 +0,0 @@ -.standalone-device-enrollment-content { - .download { - width: 100%; - display: flex; - flex-flow: row; - align-items: center; - justify-content: center; - padding-bottom: 30px; - - a { - text-decoration: none; - cursor: pointer; - } - - .btn { - height: 47px; - } - } -} diff --git a/web/src/pages/devices/modals/components/StandaloneDeviceModalForm/StandaloneDeviceModalForm.tsx b/web/src/pages/devices/modals/components/StandaloneDeviceModalForm/StandaloneDeviceModalForm.tsx deleted file mode 100644 index acc2c31a7..000000000 --- a/web/src/pages/devices/modals/components/StandaloneDeviceModalForm/StandaloneDeviceModalForm.tsx +++ /dev/null @@ -1,267 +0,0 @@ -import './style.scss'; - -import { zodResolver } from '@hookform/resolvers/zod'; -import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; -import { type SubmitHandler, useForm } from 'react-hook-form'; -import type { Subject } from 'rxjs'; - -import { useI18nContext } from '../../../../../i18n/i18n-react'; -import { FormInput } from '../../../../../shared/defguard-ui/components/Form/FormInput/FormInput'; -import { FormLocationIp } from '../../../../../shared/defguard-ui/components/Form/FormLocationIp/FormLocationIp'; -import { FormSelect } from '../../../../../shared/defguard-ui/components/Form/FormSelect/FormSelect'; -import { FormToggle } from '../../../../../shared/defguard-ui/components/Form/FormToggle/FormToggle'; -import type { - SelectOption, - SelectSelectedValue, -} from '../../../../../shared/defguard-ui/components/Layout/Select/types'; -import type { ToggleOption } from '../../../../../shared/defguard-ui/components/Layout/Toggle/types'; -import useApi from '../../../../../shared/hooks/useApi'; -import { useToaster } from '../../../../../shared/hooks/useToaster'; -import type { GetAvailableLocationIpResponse } from '../../../../../shared/types'; -import { - type AddStandaloneDeviceFormFields, - WGConfigGenChoice, -} from '../../AddStandaloneDeviceModal/types'; -import { StandaloneDeviceModalFormMode } from '../types'; -import { - type StandaloneDeviceFormFields, - standaloneDeviceFormSchema, -} from './formSchema'; - -type Props = { - onSubmit: (formValues: AddStandaloneDeviceFormFields) => Promise; - mode: StandaloneDeviceModalFormMode; - onLoadingChange: (value: boolean) => void; - locationOptions: SelectOption[]; - submitSubject: Subject; - defaults: StandaloneDeviceFormFields; - reservedNames: string[]; - initialIpRecommendation: GetAvailableLocationIpResponse; -}; - -export const StandaloneDeviceModalForm = ({ - onSubmit, - mode, - onLoadingChange, - locationOptions, - submitSubject, - defaults, - reservedNames, - initialIpRecommendation, -}: Props) => { - const [internalRecommendedIps, setInternalRecommendedIps] = useState< - GetAvailableLocationIpResponse | undefined - >(); - const { LL } = useI18nContext(); - const { - standaloneDevice: { validateLocationIp, getAvailableIp }, - } = useApi(); - // auto assign upon location change is happening - const [ipIsLoading, setIpIsLoading] = useState(false); - const localLL = LL.modals.addStandaloneDevice.form; - const labels = localLL.labels; - const submitRef = useRef(null); - const toaster = useToaster(); - const renderSelectedOption = useCallback( - (val?: number): SelectSelectedValue => { - const empty: SelectSelectedValue = { - displayValue: '', - key: 'empty', - }; - if (val !== undefined) { - const option = locationOptions.find((n) => n.value === val); - if (option) { - return { - displayValue: option.label, - key: option.key, - }; - } - } - return empty; - }, - [locationOptions], - ); - - const toggleOptions = useMemo( - (): ToggleOption[] => [ - { - text: labels.generation.auto(), - value: WGConfigGenChoice.AUTO, - disabled: false, - }, - { - text: labels.generation.manual(), - value: WGConfigGenChoice.MANUAL, - disabled: false, - }, - ], - [labels.generation], - ); - - const schema = useMemo( - () => - standaloneDeviceFormSchema(LL, { - mode, - reservedNames, - originalName: defaults.name, - }), - [mode, reservedNames, defaults.name, LL], - ); - - const { - handleSubmit, - control, - watch, - formState: { isSubmitting }, - setError, - resetField, - } = useForm({ - defaultValues: defaults, - resolver: zodResolver(schema), - mode: 'all', - }); - - const generationChoiceValue = watch('generationChoice'); - - const submitHandler: SubmitHandler = async (formValues) => { - const values = formValues; - const recommendationResponse = internalRecommendedIps ?? initialIpRecommendation; - let validationList = recommendationResponse.map((recommendation, index) => ({ - ip: recommendation.network_part + formValues.modifiableIpParts[index], - index, - })); - values.modifiableIpParts = validationList.map((item) => item.ip); - // try to validate explicitly chosen IPs before submission - let validationErrors = false; - - // if edit exclude initial ip's from validation as they are reserved already by edited device - if (mode === StandaloneDeviceModalFormMode.EDIT) { - const reservedByDevice = initialIpRecommendation.map( - (item) => item.network_part + item.modifiable_part, - ); - validationList = validationList.filter( - (item) => !reservedByDevice.includes(item.ip), - ); - } - - if (validationList.length) { - try { - const response = await validateLocationIp({ - ips: validationList.map((item) => item.ip), - location: values.location_id, - }); - - response.forEach(({ available, valid }, index) => { - const fieldIndex = validationList[index].index; - if (!available) { - validationErrors = true; - setError(`modifiableIpParts.${fieldIndex}`, { - message: LL.form.error.reservedIp(), - }); - } - if (!valid) { - validationErrors = true; - setError(`modifiableIpParts.${fieldIndex}`, { - message: LL.form.error.invalidIp(), - }); - } - }); - } catch (_) { - validationErrors = true; - toaster.error(LL.messages.error()); - } - } - - // submit form if no validation errors occurred - if (!validationErrors) { - try { - await onSubmit(values); - } catch (_) { - toaster.error(LL.messages.error()); - } - } - }; - - const autoAssignRecommendedIp = useCallback( - (locationId: number | undefined) => { - if (locationId !== undefined && mode !== StandaloneDeviceModalFormMode.EDIT) { - setIpIsLoading(true); - void getAvailableIp({ - locationId, - }) - .then((resp) => { - setInternalRecommendedIps(resp); - resetField('modifiableIpParts', { - defaultValue: resp.map((r) => r.modifiable_part), - }); - }) - .finally(() => { - setIpIsLoading(false); - }); - } - }, - [getAvailableIp, resetField, mode], - ); - - // inform parent that form is processing stuff - useEffect(() => { - const res = isSubmitting; - onLoadingChange(res); - }, [isSubmitting, onLoadingChange]); - - // handle form sub from outside - useEffect(() => { - const sub = submitSubject.subscribe(() => { - if (submitRef.current) { - submitRef.current.click(); - } - }); - return () => sub.unsubscribe(); - }, [submitSubject]); - - const recommendedIps = internalRecommendedIps || initialIpRecommendation; - return ( -
- - - - {recommendedIps.map((ip, i) => ( - - ))} - {mode === StandaloneDeviceModalFormMode.CREATE_MANUAL && ( - <> - - - - )} - - - ); -}; diff --git a/web/src/pages/devices/modals/components/StandaloneDeviceModalForm/formSchema.ts b/web/src/pages/devices/modals/components/StandaloneDeviceModalForm/formSchema.ts deleted file mode 100644 index e43a1aaaf..000000000 --- a/web/src/pages/devices/modals/components/StandaloneDeviceModalForm/formSchema.ts +++ /dev/null @@ -1,64 +0,0 @@ -import z from 'zod'; -import type { TranslationFunctions } from '../../../../../i18n/i18n-types'; -import { isPresent } from '../../../../../shared/defguard-ui/utils/isPresent'; -import { validateWireguardPublicKey } from '../../../../../shared/validators'; -import { WGConfigGenChoice } from '../../AddStandaloneDeviceModal/types'; -import { StandaloneDeviceModalFormMode } from '../types'; - -type SchemaProps = { - mode: StandaloneDeviceModalFormMode; - reservedNames: string[]; - originalName?: string; -}; - -export const standaloneDeviceFormSchema = ( - LL: TranslationFunctions, - { mode, reservedNames, originalName }: SchemaProps, -) => { - const errors = LL.form.error; - - return z - .object({ - name: z - .string() - .trim() - .min(1, LL.form.error.required()) - .refine((value) => { - if (mode === StandaloneDeviceModalFormMode.EDIT && isPresent(originalName)) { - const filtered = reservedNames.filter((n) => n !== originalName.trim()); - return !filtered.includes(value.trim()); - } - return !reservedNames.includes(value.trim()); - }, LL.form.error.reservedName()), - location_id: z.number(), - description: z.string().trim().optional(), - modifiableIpParts: z.array(z.string().trim().min(1, LL.form.error.required())), - generationChoice: z.nativeEnum(WGConfigGenChoice), - wireguard_pubkey: z.string().trim().optional(), - }) - .superRefine((vals, ctx) => { - if (mode === StandaloneDeviceModalFormMode.CREATE_MANUAL) { - if (vals.generationChoice === WGConfigGenChoice.MANUAL) { - const result = validateWireguardPublicKey({ - requiredError: errors.required(), - maxError: errors.maximumLengthOf({ length: 44 }), - minError: errors.minimumLengthOf({ length: 44 }), - validKeyError: errors.invalid(), - }).safeParse(vals.wireguard_pubkey); - if (!result.success) { - result.error.errors.forEach((e) => { - ctx.addIssue({ - path: ['wireguard_pubkey'], - message: e.message, - code: 'custom', - }); - }); - } - } - } - }); -}; - -export type StandaloneDeviceFormFields = z.infer< - ReturnType ->; diff --git a/web/src/pages/devices/modals/components/StandaloneDeviceModalForm/style.scss b/web/src/pages/devices/modals/components/StandaloneDeviceModalForm/style.scss deleted file mode 100644 index ef5f509ce..000000000 --- a/web/src/pages/devices/modals/components/StandaloneDeviceModalForm/style.scss +++ /dev/null @@ -1,14 +0,0 @@ -.standalone-device-modal-form { - .row { - width: 100%; - display: flex; - flex-flow: column; - row-gap: 0; - - @include media-breakpoint-up(lg) { - display: grid; - grid-template-columns: 1fr 1fr; - column-gap: 20px; - } - } -} diff --git a/web/src/pages/devices/modals/components/types.ts b/web/src/pages/devices/modals/components/types.ts deleted file mode 100644 index 408ff8b14..000000000 --- a/web/src/pages/devices/modals/components/types.ts +++ /dev/null @@ -1,5 +0,0 @@ -export enum StandaloneDeviceModalFormMode { - CREATE_CLI, - CREATE_MANUAL, - EDIT, -} diff --git a/web/src/pages/devices/style.scss b/web/src/pages/devices/style.scss deleted file mode 100644 index eb7878e3b..000000000 --- a/web/src/pages/devices/style.scss +++ /dev/null @@ -1,7 +0,0 @@ -#standalone-devices-page { - .list-container { - position: relative; - max-width: 100%; - overflow: auto; - } -} diff --git a/web/src/pages/devices/types.ts b/web/src/pages/devices/types.ts deleted file mode 100644 index e69de29bb..000000000 diff --git a/web/src/pages/enrollment/EnrollmentPage.tsx b/web/src/pages/enrollment/EnrollmentPage.tsx deleted file mode 100644 index 120d198a0..000000000 --- a/web/src/pages/enrollment/EnrollmentPage.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import './style.scss'; - -import { useQuery } from '@tanstack/react-query'; -import { useEffect } from 'react'; - -import { useI18nContext } from '../../i18n/i18n-react'; -import { PageContainer } from '../../shared/components/Layout/PageContainer/PageContainer'; -import { MessageBox } from '../../shared/defguard-ui/components/Layout/MessageBox/MessageBox'; -import { MessageBoxType } from '../../shared/defguard-ui/components/Layout/MessageBox/types'; -import useApi from '../../shared/hooks/useApi'; -import { QueryKeys } from '../../shared/queries'; -import { EnrollmentEmail } from './components/EnrollmentEmail/EnrollmentEmail'; -import { EnrollmentVPN } from './components/EnrollmentVPN/EnrollmentVPN'; -import { EnrollmentWelcomeMessage } from './components/EnrollmentWelcomeMessage/EnrollmentWelcomeMessage'; -import { useEnrollmentStore } from './hooks/useEnrollmentStore'; - -export const EnrollmentPage = () => { - const { - settings: { getSettings }, - } = useApi(); - - const { LL } = useI18nContext(); - - const pageLL = LL.enrollmentPage; - - const setEnrollment = useEnrollmentStore((state) => state.setState); - - const { data: settingsData, isLoading } = useQuery({ - queryFn: getSettings, - queryKey: [QueryKeys.FETCH_SETTINGS], - refetchOnMount: true, - refetchOnWindowFocus: false, - }); - - // biome-ignore lint/correctness/useExhaustiveDependencies: migration, checkMeLater - useEffect(() => { - if (settingsData) { - setEnrollment({ settings: settingsData }); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [settingsData]); - - return ( - -

{pageLL.title()}

- - {!isLoading && ( -
-
- - -
-
- -
-
- )} -
- ); -}; diff --git a/web/src/pages/enrollment/components/EnrollmentEmail/EnrollmentEmail.tsx b/web/src/pages/enrollment/components/EnrollmentEmail/EnrollmentEmail.tsx deleted file mode 100644 index d581f9383..000000000 --- a/web/src/pages/enrollment/components/EnrollmentEmail/EnrollmentEmail.tsx +++ /dev/null @@ -1,117 +0,0 @@ -import './style.scss'; - -import { useMutation, useQueryClient } from '@tanstack/react-query'; -import { isUndefined } from 'lodash-es'; -import { type ChangeEvent, useEffect, useState } from 'react'; - -import { useI18nContext } from '../../../../i18n/i18n-react'; -import SvgIconCheckmark from '../../../../shared/components/svg/IconCheckmark'; -import { Button } from '../../../../shared/defguard-ui/components/Layout/Button/Button'; -import { - ButtonSize, - ButtonStyleVariant, -} from '../../../../shared/defguard-ui/components/Layout/Button/types'; -import { Card } from '../../../../shared/defguard-ui/components/Layout/Card/Card'; -import { CheckBox } from '../../../../shared/defguard-ui/components/Layout/Checkbox/CheckBox'; -import { Input } from '../../../../shared/defguard-ui/components/Layout/Input/Input'; -import { MessageBox } from '../../../../shared/defguard-ui/components/Layout/MessageBox/MessageBox'; -import { MessageBoxType } from '../../../../shared/defguard-ui/components/Layout/MessageBox/types'; -import { TextareaAutoResizable } from '../../../../shared/defguard-ui/components/Layout/TextareaAutoResizable/TextareaAutoResizable'; -import useApi from '../../../../shared/hooks/useApi'; -import { useToaster } from '../../../../shared/hooks/useToaster'; -import { QueryKeys } from '../../../../shared/queries'; -import { useEnrollmentStore } from '../../hooks/useEnrollmentStore'; - -export const EnrollmentEmail = () => { - const { - settings: { editSettings }, - } = useApi(); - const queryClient = useQueryClient(); - const { LL } = useI18nContext(); - const [duplicateMessage, setDuplicateMessage] = useState(false); - const settings = useEnrollmentStore((state) => state.settings); - const [email, setEmail] = useState(settings?.enrollment_welcome_email ?? ''); - const [subject, setSubject] = useState( - settings?.enrollment_welcome_email_subject ?? '', - ); - const componentLL = LL.enrollmentPage.settings.welcomeEmail; - const toaster = useToaster(); - - const { isPending: isLoading, mutate } = useMutation({ - mutationFn: editSettings, - onSuccess: () => { - void queryClient.invalidateQueries({ - queryKey: [QueryKeys.FETCH_SETTINGS], - }); - toaster.success(LL.enrollmentPage.messages.edit.success()); - }, - onError: (e) => { - toaster.error(LL.enrollmentPage.messages.edit.error()); - console.error(e); - }, - }); - - const handleSave = () => { - if (!isLoading && settings) { - mutate({ - ...settings, - enrollment_use_welcome_message_as_email: duplicateMessage, - enrollment_welcome_email: email, - enrollment_welcome_email_subject: subject, - }); - } - }; - - useEffect(() => { - if (settings) { - setDuplicateMessage(settings.enrollment_use_welcome_message_as_email); - setEmail(settings.enrollment_welcome_email); - setSubject(settings.enrollment_welcome_email_subject); - } - }, [settings]); - - return ( -
-
-

{componentLL.title()}

-
- - -
-
- setDuplicateMessage((state) => !state)} - disabled={isLoading} - /> - setDuplicateMessage((state) => !state)}> - {componentLL.controls.duplicateWelcome()} - -
-
- setSubject(e.target.value)} - disabled={isLoading || isUndefined(settings)} - /> -
- ) => setEmail(ev.target.value)} - disabled={isLoading || isUndefined(settings)} - /> -
-
-
- ); -}; diff --git a/web/src/pages/enrollment/components/EnrollmentEmail/style.scss b/web/src/pages/enrollment/components/EnrollmentEmail/style.scss deleted file mode 100644 index d5fa90074..000000000 --- a/web/src/pages/enrollment/components/EnrollmentEmail/style.scss +++ /dev/null @@ -1,82 +0,0 @@ -@use '@scssutils' as *; - -#enrollment-email { - & > .message-box-spacer { - padding-bottom: 26px; - - .message-box { - background-color: var(--surface-tag-modal); - } - } - - & > .card { - @include media-breakpoint-up(lg) { - padding: 17px 15px; - } - - & > .controls { - width: 100%; - display: flex; - flex-flow: row wrap; - column-gap: 10px; - margin-bottom: 16px; - - & > .checkbox-wrap { - display: flex; - flex-flow: row nowrap; - column-gap: 5px; - align-items: center; - justify-content: flex-start; - - & > span { - @include typography(app-modal-1); - - color: var(--text-body-secondary); - user-select: none; - cursor: pointer; - } - } - - & > .btn { - min-width: 140px; - - &:nth-child(2) { - margin-left: auto; - } - } - } - - & > .input { - box-sizing: border-box; - margin: 20px 0; - width: 100%; - padding: 0; - } - - & > .text-wrapper { - border-radius: 15px; - box-sizing: border-box; - padding: 20px; - border: 1px solid var(--border-primary); - width: 100%; - min-height: 700px; - overflow: hidden; - - & > textarea { - min-height: 660px; - width: 100%; - height: inherit; - border: none; - padding: 0; - margin: 0; - resize: none; - overflow: none; - background-color: transparent; - - @include typography(app-welcome-2); - - color: var(--text-body-primary); - } - } - } -} diff --git a/web/src/pages/enrollment/components/EnrollmentVPN/EnrollmentVPN.tsx b/web/src/pages/enrollment/components/EnrollmentVPN/EnrollmentVPN.tsx deleted file mode 100644 index 21023642a..000000000 --- a/web/src/pages/enrollment/components/EnrollmentVPN/EnrollmentVPN.tsx +++ /dev/null @@ -1,111 +0,0 @@ -import './style.scss'; - -import { useMutation, useQueryClient } from '@tanstack/react-query'; -import { isUndefined } from 'lodash-es'; -import { useCallback, useMemo, useState } from 'react'; - -import { useI18nContext } from '../../../../i18n/i18n-react'; -import { Select } from '../../../../shared/defguard-ui/components/Layout/Select/Select'; -import { - type SelectSelectedValue, - SelectSizeVariant, -} from '../../../../shared/defguard-ui/components/Layout/Select/types'; -import useApi from '../../../../shared/hooks/useApi'; -import { useToaster } from '../../../../shared/hooks/useToaster'; -import { QueryKeys } from '../../../../shared/queries'; -import { useEnrollmentStore } from '../../hooks/useEnrollmentStore'; - -export const EnrollmentVPN = () => { - const [isLoading, setLoading] = useState(false); - - const { LL } = useI18nContext(); - - const componentLL = LL.enrollmentPage.settings.vpnOptionality; - - const { - settings: { editSettings }, - } = useApi(); - - const queryClient = useQueryClient(); - - const toaster = useToaster(); - - const settings = useEnrollmentStore((state) => state.settings); - - const { mutate } = useMutation({ - mutationFn: editSettings, - onSuccess: () => { - void queryClient.invalidateQueries({ - queryKey: [QueryKeys.FETCH_SETTINGS], - }); - toaster.success(LL.enrollmentPage.messages.edit.success()); - setLoading(false); - }, - onError: () => { - toaster.error(LL.enrollmentPage.messages.edit.error()); - setLoading(false); - }, - }); - - const vpnOptionalityOptions = useMemo( - () => [ - { - key: 1, - value: true, - label: componentLL.select.options.optional(), - }, - { - key: 2, - value: false, - label: componentLL.select.options.mandatory(), - }, - ], - [componentLL.select.options], - ); - - const renderSelectedVpn = useCallback( - (selected: boolean): SelectSelectedValue => { - const option = vpnOptionalityOptions.find((o) => o.value === selected); - - if (!option) throw Error("Selected value doesn't exist"); - - return { - key: option.key, - displayValue: option.label, - }; - }, - [vpnOptionalityOptions], - ); - - const handleChange = useCallback( - (val: boolean) => { - if (!isLoading && settings) { - setLoading(true); - try { - mutate({ ...settings, enrollment_vpn_step_optional: val }); - } catch (e) { - setLoading(false); - toaster.error(LL.enrollmentPage.messages.edit.error()); - console.error(e); - } - } - }, - [LL.enrollmentPage.messages.edit, isLoading, mutate, settings, toaster], - ); - - return ( -
-
-

{componentLL.title()}

-
- setNetworkState({ selectedNetworkId: res })} - onCreate={() => { - resetWizardState(); - navigate('/admin/wizard', { replace: true }); - }} - /> -
- )} - - - - ); -}; diff --git a/web/src/pages/network/NetworkEditForm/components/DividerHeader.tsx b/web/src/pages/network/NetworkEditForm/components/DividerHeader.tsx deleted file mode 100644 index 86243ea08..000000000 --- a/web/src/pages/network/NetworkEditForm/components/DividerHeader.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import type { PropsWithChildren } from 'react'; - -type DividerHeaderProps = { - text: string; -} & PropsWithChildren; - -export const DividerHeader = ({ text, children }: DividerHeaderProps) => { - return ( -
-
-

{text}

- {children} -
-
- ); -}; diff --git a/web/src/pages/network/NetworkEditForm/style.scss b/web/src/pages/network/NetworkEditForm/style.scss deleted file mode 100644 index c8840e9d8..000000000 --- a/web/src/pages/network/NetworkEditForm/style.scss +++ /dev/null @@ -1,61 +0,0 @@ -#network-page, -.network-setup { - .network-config { - & > .card { - padding: 15px 30px 40px; - - & > form { - justify-content: flex-start; - align-content: flex-start; - align-items: flex-start; - - & > .hidden { - @include visually-hidden; - } - - & > label { - margin-bottom: 12px; - } - - & > .input-container { - width: 100%; - } - - & > .message-box-spacer { - &:not(:last-child) { - padding-bottom: 25px; - } - } - } - } - } - - #location-mfa-mode-explain-message-box { - ul { - list-style-position: inside; - margin-top: 8px; - - li { - p { - display: inline; - } - } - } - } - - .divider-header { - padding-bottom: var(--spacing-s); - - .inner { - display: flex; - flex-flow: row; - align-items: center; - justify-content: flex-start; - border-bottom: 1px solid var(--border-primary); - } - - .header { - @include typography(app-side-bar); - } - } -} diff --git a/web/src/pages/network/NetworkGateway/NetworkGateway.tsx b/web/src/pages/network/NetworkGateway/NetworkGateway.tsx deleted file mode 100644 index 319c86355..000000000 --- a/web/src/pages/network/NetworkGateway/NetworkGateway.tsx +++ /dev/null @@ -1,154 +0,0 @@ -import './style.scss'; - -import { useQuery } from '@tanstack/react-query'; -import { useCallback, useMemo } from 'react'; -import ReactMarkdown from 'react-markdown'; - -import { useI18nContext } from '../../../i18n/i18n-react'; -import { NetworkGatewaysStatus } from '../../../shared/components/network/GatewaysStatus/NetworkGatewaysStatus/NetworkGatewaysStatus'; -import { ActionButton } from '../../../shared/defguard-ui/components/Layout/ActionButton/ActionButton'; -import { ActionButtonVariant } from '../../../shared/defguard-ui/components/Layout/ActionButton/types'; -import { Button } from '../../../shared/defguard-ui/components/Layout/Button/Button'; -import { - ButtonSize, - ButtonStyleVariant, -} from '../../../shared/defguard-ui/components/Layout/Button/types'; -import { ExpandableCard } from '../../../shared/defguard-ui/components/Layout/ExpandableCard/ExpandableCard'; -import { MessageBox } from '../../../shared/defguard-ui/components/Layout/MessageBox/MessageBox'; -import useApi from '../../../shared/hooks/useApi'; -import { useClipboard } from '../../../shared/hooks/useClipboard'; -import { externalLink } from '../../../shared/links'; -import { QueryKeys } from '../../../shared/queries'; -import { useNetworkPageStore } from '../hooks/useNetworkPageStore'; - -export const NetworkGatewaySetup = () => { - const { writeToClipboard } = useClipboard(); - const selectedNetworkId = useNetworkPageStore((state) => state.selectedNetworkId); - const { LL } = useI18nContext(); - const { - network: { getNetworkToken }, - } = useApi(); - - const { data: networkToken } = useQuery({ - queryKey: [QueryKeys.FETCH_NETWORK_TOKEN, selectedNetworkId], - queryFn: () => getNetworkToken(selectedNetworkId as number), - refetchOnMount: true, - refetchOnWindowFocus: false, - enabled: Boolean(selectedNetworkId), - }); - - const command = useCallback(() => { - return `docker run -e DEFGUARD_TOKEN=${networkToken?.token} -e DEFGUARD_GRPC_URL=${networkToken?.grpc_url} --restart unless-stopped --network host --cap-add NET_ADMIN ghcr.io/defguard/gateway:latest`; - }, [networkToken]); - - const returnNetworkToken = useCallback(() => { - return `${networkToken?.token}`; - }, [networkToken]); - - const getActions = useMemo( - () => [ - { - void writeToClipboard(command()); - }} - />, - ], - [command, writeToClipboard], - ); - - const getNetworkTokenActions = useMemo( - () => [ - { - void writeToClipboard(returnNetworkToken()); - }} - />, - ], - [returnNetworkToken, writeToClipboard], - ); - - // TODO: consider a better way to redirect to the gateway releases page - const handleSubmit = () => { - window.location.href = 'https://github.com/DefGuard/gateway/releases'; - }; - - return ( -
-
-

{LL.gatewaySetup.header.main()}

- {/* {parse( - LL.gatewaySetup.messages.runCommand({ - setupGatewayDocs: externalLink.gitbook.setup.gateway, - }), - )} */} - - {LL.gatewaySetup.messages.runCommand({ - setupGatewayDocs: externalLink.gitbook.setup.gateway, - })} - -
- - - {networkToken - ? LL.gatewaySetup.messages.authToken({ - setupGatewayDocs: externalLink.gitbook.setup.gateway, - }) - : LL.gatewaySetup.messages.createNetwork()} - - - {networkToken && ( - -

{returnNetworkToken()}

-
- )} -

{LL.gatewaySetup.header.dockerBasedGatewaySetup()}

- - - {networkToken - ? LL.gatewaySetup.messages.dockerBasedGatewaySetup({ - setupGatewayDocs: externalLink.gitbook.setup.gateway, - }) - : LL.gatewaySetup.messages.createNetwork()} - - - {networkToken && ( - -

{command()}

-
- )} -

{LL.gatewaySetup.header.fromPackage()}

- - - {LL.gatewaySetup.messages.fromPackage({ - setupGatewayDocs: externalLink.gitbook.setup.gateway, - })} - - -
- ); -}; diff --git a/web/src/pages/network/NetworkGateway/style.scss b/web/src/pages/network/NetworkGateway/style.scss deleted file mode 100644 index 9bd3147d0..000000000 --- a/web/src/pages/network/NetworkGateway/style.scss +++ /dev/null @@ -1,74 +0,0 @@ -#network-page { - .gateway { - display: flex; - flex-direction: column; - row-gap: 20px; - - & > section.header-section { - h2, - p { - margin-bottom: 35px; - } - } - - & > .message-box-spacer { - padding-bottom: 20px; - - @include media-breakpoint-up(lg) { - padding-bottom: 40px; - } - } - - & > .expandable-card { - & > .expanded-content { - overflow: hidden; - box-sizing: border-box; - - & > p { - width: 100%; - max-width: 100%; - text-overflow: ellipsis; - text-align: left; - word-break: break-all; - white-space: normal; - @include small-text; - - line-height: 22px; - color: var(--text-body-secondary); - padding: 20px 0px; - } - } - - margin-bottom: 30px; - } - - h2 { - @include typography(app-body-1); - color: var(--text-body-primary); - text-align: center; - } - - h3 { - @include typography(modal-title); - - color: var(--text-body-primary); - } - - p { - & > a { - color: var(--text-body-secondary); - } - - @include typography(app-modal-2); - color: var(--text-body-secondary); - } - - & > p { - text-align: center; - } - - & > .btn { - width: 100%; - } - } -} diff --git a/web/src/pages/network/NetworkPage.tsx b/web/src/pages/network/NetworkPage.tsx deleted file mode 100644 index b6eb19d25..000000000 --- a/web/src/pages/network/NetworkPage.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import './style.scss'; - -import { useQuery } from '@tanstack/react-query'; -import { useEffect } from 'react'; -import { useBreakpoint } from 'use-breakpoint'; - -import { useI18nContext } from '../../i18n/i18n-react'; -import { PageContainer } from '../../shared/components/Layout/PageContainer/PageContainer'; -import { deviceBreakpoints } from '../../shared/constants'; -import { Card } from '../../shared/defguard-ui/components/Layout/Card/Card'; -import useApi from '../../shared/hooks/useApi'; -import { QueryKeys } from '../../shared/queries'; -import { useNetworkPageStore } from './hooks/useNetworkPageStore'; -import { NetworkControls } from './NetworkControls/NetworkControls'; -import { NetworkEditForm } from './NetworkEditForm/NetworkEditForm'; -import { NetworkGatewaySetup } from './NetworkGateway/NetworkGateway'; -import { NetworkTabs } from './NetworkTabs/NetworkTabs'; - -export const NetworkPage = () => { - const { - network: { getNetworks }, - } = useApi(); - const { LL } = useI18nContext(); - const setNetworks = useNetworkPageStore((state) => state.setNetworks); - const { breakpoint } = useBreakpoint(deviceBreakpoints); - - const { data: networksData } = useQuery({ - queryKey: [QueryKeys.FETCH_NETWORKS], - queryFn: getNetworks, - refetchOnWindowFocus: false, - }); - - useEffect(() => { - if (networksData) { - setNetworks(networksData); - } - }, [networksData, setNetworks]); - - return ( - -
-

{LL.networkPage.pageTitle()}

-
- {breakpoint === 'desktop' && } - - - - - -
- ); -}; diff --git a/web/src/pages/network/NetworkTabs/NetworkTabs.tsx b/web/src/pages/network/NetworkTabs/NetworkTabs.tsx deleted file mode 100644 index fce9d2516..000000000 --- a/web/src/pages/network/NetworkTabs/NetworkTabs.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import { useMemo } from 'react'; -import { useNavigate } from 'react-router'; - -import { useI18nContext } from '../../../i18n/i18n-react'; -import { CardTabs } from '../../../shared/defguard-ui/components/Layout/CardTabs/CardTabs'; -import { useWizardStore } from '../../wizard/hooks/useWizardStore'; -import { useNetworkPageStore } from '../hooks/useNetworkPageStore'; - -export const NetworkTabs = () => { - const navigate = useNavigate(); - const { LL } = useI18nContext(); - const networks = useNetworkPageStore((state) => state.networks); - const selectedNetworkId = useNetworkPageStore((state) => state.selectedNetworkId); - const setPageState = useNetworkPageStore((state) => state.setState); - const resetWizardState = useWizardStore((state) => state.resetState); - const tabs = useMemo( - () => - networks.map((n) => ({ - key: n.id, - onClick: () => { - if (n.id !== selectedNetworkId) { - setPageState({ selectedNetworkId: n.id }); - } - }, - content: n.name, - active: n.id === selectedNetworkId, - })), - [networks, selectedNetworkId, setPageState], - ); - - return ( - { - resetWizardState(); - navigate('/admin/wizard', { replace: true }); - }} - loading={!networks || networks.length === 0} - /> - ); -}; diff --git a/web/src/pages/network/hooks/useNetworkPageStore.ts b/web/src/pages/network/hooks/useNetworkPageStore.ts deleted file mode 100644 index edac1fce3..000000000 --- a/web/src/pages/network/hooks/useNetworkPageStore.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { Subject } from 'rxjs'; -import { createWithEqualityFn } from 'zustand/traditional'; - -import type { Network } from '../../../shared/types'; - -type NetworkPageStore = { - saveSubject: Subject; - loading: boolean; - networks: Network[]; - selectedNetworkId?: number; - setState: (data: Partial) => void; - setNetworks: (data: Network[]) => void; -}; - -export const useNetworkPageStore = createWithEqualityFn()( - (set, get) => ({ - saveSubject: new Subject(), - loading: false, - networks: [], - selectedNetworkId: undefined, - setState: (newState) => set(() => newState), - setNetworks: (networks) => { - const sortedNetworks = networks.sort((a, b) => a.name.localeCompare(b.name)); - if (get().selectedNetworkId === undefined) { - set({ selectedNetworkId: sortedNetworks[0]?.id }); - } - set({ networks: sortedNetworks }); - }, - }), - Object.is, -); diff --git a/web/src/pages/network/style.scss b/web/src/pages/network/style.scss deleted file mode 100644 index 78f356703..000000000 --- a/web/src/pages/network/style.scss +++ /dev/null @@ -1,128 +0,0 @@ -#network-page { - & > .page-content { - box-sizing: border-box; - overflow-y: auto; - overflow-x: hidden; - display: block; - padding: 0; - background-color: var(--white); - - @include media-breakpoint-up(lg) { - background-color: var(--bg-light); - padding: 64px 70px 45px; - } - - & > header { - display: none; - height: auto; - margin-bottom: 5px; - width: 100%; - - & > h1 { - user-select: none; - - @include page-header; - } - - @include media-breakpoint-up(lg) { - display: block; - - & > h1 { - line-height: 62px; - } - } - } - - & > .network-card { - border-radius: 0 15px 15px 15px; - width: 100%; - max-width: 100%; - padding: 30px 20px 20px; - box-sizing: border-box; - display: grid; - grid-template-rows: repeat(3, auto); - grid-template-columns: 1fr; - row-gap: 15px; - grid-template-areas: - 'controls' - 'config' - 'gateway'; - - @include media-breakpoint-up(lg) { - padding: 20px; - row-gap: 30px; - column-gap: 30px; - grid-template-rows: 40px repeat(2, auto); - grid-template-columns: 1fr; - grid-template-areas: - 'controls' - 'config' - 'gateway'; - } - - @include media-breakpoint-up(xl) { - padding: 20px; - row-gap: 48px; - column-gap: 57px; - grid-template-rows: 40px auto; - grid-template-columns: 1fr 1fr; - grid-template-areas: - 'controls controls' - 'config gateway'; - } - - & > .network-controls { - grid-area: controls; - } - - & > .network-config { - grid-area: config; - } - - & > .gateway { - grid-area: gateway; - } - - & > * { - width: 100%; - max-width: 100%; - } - - header { - display: flex; - flex-flow: row nowrap; - align-items: center; - justify-content: flex-start; - column-gap: 7px; - user-select: none; - margin-bottom: 20px; - - h2 { - @include typography-legacy(20px, 30px, semiBold, var(--text-main), 'Poppins'); - } - } - - .message-box-spacer { - &:not(:last-child) { - padding-bottom: 20px; - } - } - - form { - & > * { - width: 100%; - } - - & > .message-box-spacer { - &:not(:last-child) { - padding-bottom: 32px; - } - } - - & > .form-checkbox { - margin-bottom: 25px; - } - } - } - } -} diff --git a/web/src/pages/openid/OpenidClientsListPage/OpenidClientsListPage.tsx b/web/src/pages/openid/OpenidClientsListPage/OpenidClientsListPage.tsx deleted file mode 100644 index 91ddb2d7e..000000000 --- a/web/src/pages/openid/OpenidClientsListPage/OpenidClientsListPage.tsx +++ /dev/null @@ -1,372 +0,0 @@ -import './style.scss'; - -import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; -import { isUndefined, orderBy } from 'lodash-es'; -import { useCallback, useEffect, useMemo, useState } from 'react'; -import { useBreakpoint } from 'use-breakpoint'; - -import { useI18nContext } from '../../../i18n/i18n-react'; -import { PageContainer } from '../../../shared/components/Layout/PageContainer/PageContainer'; -import IconCheckmarkGreen from '../../../shared/components/svg/IconCheckmarkGreen'; -import IconDeactivated from '../../../shared/components/svg/IconDeactivated'; -import SvgIconPlusWhite from '../../../shared/components/svg/IconPlusWhite'; -import { deviceBreakpoints } from '../../../shared/constants'; -import { Button } from '../../../shared/defguard-ui/components/Layout/Button/Button'; -import { - ButtonSize, - ButtonStyleVariant, -} from '../../../shared/defguard-ui/components/Layout/Button/types'; -import { EditButton } from '../../../shared/defguard-ui/components/Layout/EditButton/EditButton'; -import { EditButtonOption } from '../../../shared/defguard-ui/components/Layout/EditButton/EditButtonOption'; -import { EditButtonOptionStyleVariant } from '../../../shared/defguard-ui/components/Layout/EditButton/types'; -import { LoaderSpinner } from '../../../shared/defguard-ui/components/Layout/LoaderSpinner/LoaderSpinner'; -import { ConfirmModal } from '../../../shared/defguard-ui/components/Layout/modals/ConfirmModal/ConfirmModal'; -import { ConfirmModalType } from '../../../shared/defguard-ui/components/Layout/modals/ConfirmModal/types'; -import { NoData } from '../../../shared/defguard-ui/components/Layout/NoData/NoData'; -import { Search } from '../../../shared/defguard-ui/components/Layout/Search/Search'; -import { Select } from '../../../shared/defguard-ui/components/Layout/Select/Select'; -import type { - SelectOption, - SelectSelectedValue, -} from '../../../shared/defguard-ui/components/Layout/Select/types'; -import { - type ListHeader, - type ListRowCell, - ListSortDirection, -} from '../../../shared/defguard-ui/components/Layout/VirtualizedList/types'; -import { VirtualizedList } from '../../../shared/defguard-ui/components/Layout/VirtualizedList/VirtualizedList'; -import { useModalStore } from '../../../shared/hooks/store/useModalStore'; -import useApi from '../../../shared/hooks/useApi'; -import { useClipboard } from '../../../shared/hooks/useClipboard'; -import { useToaster } from '../../../shared/hooks/useToaster'; -import { MutationKeys } from '../../../shared/mutations'; -import { QueryKeys } from '../../../shared/queries'; -import type { OpenidClient } from '../../../shared/types'; -import { OpenIdClientModal } from '../modals/OpenIdClientModal/OpenIdClientModal'; - -export const OpenidClientsListPage = () => { - const { writeToClipboard } = useClipboard(); - const { LL } = useI18nContext(); - const toaster = useToaster(); - const queryClient = useQueryClient(); - const { breakpoint } = useBreakpoint(deviceBreakpoints); - const [deleteClientModalOpen, setDeleteClientModalOpen] = useState(false); - const [deleteClient, setDeleteClient] = useState(undefined); - const [searchValue, setSearchValue] = useState(''); - const { - openid: { getOpenidClients, changeOpenidClientState, deleteOpenidClient }, - } = useApi(); - const setOpenIdClientModalState = useModalStore((state) => state.setOpenIdClientModal); - - const selectOptions = useMemo( - (): SelectOption[] => [ - { - key: 1, - label: LL.openidOverview.filterLabels.all(), - value: FilterOption.ALL, - }, - { - key: 3, - label: LL.openidOverview.filterLabels.enabled(), - value: FilterOption.ENABLED, - }, - { - key: 2, - label: LL.openidOverview.filterLabels.disabled(), - value: FilterOption.DISABLED, - }, - ], - [LL], - ); - - const [selectedFilter, setSelectedFilter] = useState(FilterOption.ALL); - - const { mutate: deleteClientMutation, isPending: deleteClientLoading } = useMutation({ - mutationKey: [MutationKeys.DELETE_OPENID_CLIENT], - mutationFn: deleteOpenidClient, - onSuccess: () => { - toaster.success(LL.openidOverview.deleteApp.messages.success()); - void queryClient.invalidateQueries({ - queryKey: [QueryKeys.FETCH_CLIENTS], - }); - setDeleteClientModalOpen(false); - }, - onError: (err) => { - toaster.error(LL.messages.error()); - setDeleteClientModalOpen(false); - console.error(err); - }, - }); - - const { mutate: editClientStatusMutation } = useMutation({ - mutationFn: (client: OpenidClient) => - changeOpenidClientState({ - clientId: client.client_id, - enabled: !client.enabled, - }), - onSuccess: (_, client) => { - if (client.enabled) { - toaster.success(LL.openidOverview.disableApp.messages.success()); - } else { - toaster.success(LL.openidOverview.enableApp.messages.success()); - } - void queryClient.invalidateQueries({ - queryKey: [QueryKeys.FETCH_CLIENTS], - }); - }, - onError: (err) => { - toaster.error(LL.messages.error()); - console.error(err); - }, - }); - - const { data: clients, isLoading } = useQuery({ - queryKey: [QueryKeys.FETCH_CLIENTS], - queryFn: getOpenidClients, - refetchOnWindowFocus: false, - refetchInterval: 15000, - }); - - const filteredClients = useMemo(() => { - if (!clients || (clients && clients.length === 0)) return []; - let res = orderBy(clients, ['name'], ['asc']); - res = res.filter((c) => c.name.toLowerCase().includes(searchValue.toLowerCase())); - switch (selectedFilter) { - case FilterOption.ALL: - break; - case FilterOption.ENABLED: - res = res.filter((c) => c.enabled); - break; - case FilterOption.DISABLED: - res = res.filter((c) => !c.enabled); - break; - } - return res; - }, [clients, searchValue, selectedFilter]); - - const listHeaders = useMemo(() => { - const res: ListHeader[] = [ - { - key: 'name', - text: LL.openidOverview.list.headers.name(), - active: true, - sortDirection: ListSortDirection.ASC, - }, - { - key: 'status', - text: LL.openidOverview.list.headers.status(), - }, - { - key: 'actions', - text: LL.openidOverview.list.headers.actions(), - sortable: false, - }, - ]; - return res; - }, [LL.openidOverview.list.headers]); - - const listCells = useMemo(() => { - const res: ListRowCell[] = [ - { - key: 'name', - render: (client) => {client.name}, - }, - { - key: 'status', - render: (client) => - client.enabled ? ( - <> - {' '} - {LL.openidOverview.list.status.enabled()} - - ) : ( - <> - {LL.openidOverview.list.status.disabled()} - - ), - }, - { - key: 'actions', - render: (client) => ( - - - setOpenIdClientModalState({ - visible: true, - viewMode: false, - client, - }) - } - /> - editClientStatusMutation(client)} - /> - { - void writeToClipboard( - client.client_id, - LL.openidOverview.messages.copySuccess(), - ); - }} - /> - { - setDeleteClient(client); - setDeleteClientModalOpen(true); - }} - /> - - ), - }, - ]; - return res; - }, [ - writeToClipboard, - LL.openidOverview.list.editButton, - LL.openidOverview.list.status, - LL.openidOverview.messages, - editClientStatusMutation, - setOpenIdClientModalState, - ]); - - const getListPadding = useMemo(() => { - if (breakpoint === 'desktop') { - return { - left: 60, - right: 60, - }; - } - return { - left: 20, - right: 20, - }; - }, [breakpoint]); - - const renderSelectedFilter = useCallback( - (selected: FilterOption): SelectSelectedValue => { - const option = selectOptions.find((o) => o.value === selected); - if (!option) throw Error("Selected value doesn't exists"); - return { - key: selected, - displayValue: option.label, - }; - }, - [selectOptions], - ); - - useEffect(() => { - if (breakpoint !== 'desktop' && selectedFilter !== FilterOption.ALL) { - setSelectedFilter(FilterOption.ALL); - } - }, [breakpoint, selectedFilter]); - - return ( - -
-

{LL.openidOverview.pageTitle()}

- setSearchValue(value)} - /> -
-
-
- {LL.openidOverview.clientCount()} -
- {clients && clients.length > 0 ? clients.length : 0} -
-
-
- {breakpoint === 'desktop' && ( - { - if (networkId !== null) { - navigate(`/admin/overview/${networkId}${location.search}`); - } else { - navigate(`/admin/overview${location.search}`); - } - }} - /> - ); -}; diff --git a/web/src/pages/overview-index/components/OverviewTimeSelection/OverviewTimeSelection.tsx b/web/src/pages/overview-index/components/OverviewTimeSelection/OverviewTimeSelection.tsx deleted file mode 100644 index 44c4539fa..000000000 --- a/web/src/pages/overview-index/components/OverviewTimeSelection/OverviewTimeSelection.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { useMemo } from 'react'; - -import { useI18nContext } from '../../../../i18n/i18n-react'; -import { Select } from '../../../../shared/defguard-ui/components/Layout/Select/Select'; -import { - type SelectOption, - SelectSizeVariant, -} from '../../../../shared/defguard-ui/components/Layout/Select/types'; -import { useOverviewTimeSelection } from '../hooks/useOverviewTimeSelection'; - -const availableFilters: number[] = [1, 2, 4, 6, 8, 12, 16, 24]; - -export const OverviewTimeSelection = () => { - const { from, setTimeSelection } = useOverviewTimeSelection(); - const { LL } = useI18nContext(); - const options = useMemo((): SelectOption[] => { - return availableFilters.map((filter) => ({ - key: filter, - label: LL.networkOverview.timeRangeSelectionLabel({ - value: filter, - }), - value: filter, - })); - }, [LL.networkOverview]); - - return ( - { - setOverviewStore({ selectedNetworkId: res }); - }} - /> - ); -}; diff --git a/web/src/pages/overview/OverviewPage.tsx b/web/src/pages/overview/OverviewPage.tsx deleted file mode 100644 index 9def60e10..000000000 --- a/web/src/pages/overview/OverviewPage.tsx +++ /dev/null @@ -1,176 +0,0 @@ -import './style.scss'; - -import { useQuery } from '@tanstack/react-query'; -import { isUndefined, orderBy } from 'lodash-es'; -import { useEffect, useMemo } from 'react'; -import { useLocation, useNavigate, useParams } from 'react-router'; -import { useBreakpoint } from 'use-breakpoint'; - -import { useI18nContext } from '../../i18n/i18n-react'; -import { PageContainer } from '../../shared/components/Layout/PageContainer/PageContainer'; -import { NetworkGatewaysStatus } from '../../shared/components/network/GatewaysStatus/NetworkGatewaysStatus/NetworkGatewaysStatus'; -import { deviceBreakpoints } from '../../shared/constants'; -import { LoaderSpinner } from '../../shared/defguard-ui/components/Layout/LoaderSpinner/LoaderSpinner'; -import { NoData } from '../../shared/defguard-ui/components/Layout/NoData/NoData'; -import useApi from '../../shared/hooks/useApi'; -import { QueryKeys } from '../../shared/queries'; -import { OverviewLayoutType } from '../../shared/types'; -import { sortByDate } from '../../shared/utils/sortByDate'; -import { useOverviewTimeSelection } from '../overview-index/components/hooks/useOverviewTimeSelection'; -import { useWizardStore } from '../wizard/hooks/useWizardStore'; -import { useOverviewStore } from './hooks/store/useOverviewStore'; -import { OverviewConnectedUsers } from './OverviewConnectedUsers/OverviewConnectedUsers'; -import { StandaloneDeviceConnectionCard } from './OverviewConnectedUsers/UserConnectionCard/UserConnectionCard'; -import { OverviewExpandable } from './OverviewExpandable/OverviewExpandable'; -import { OverviewHeader } from './OverviewHeader/OverviewHeader'; -import { OverviewStats } from './OverviewStats/OverviewStats'; - -const STATUS_REFETCH_TIMEOUT = 30 * 1000; - -export const OverviewPage = () => { - const navigate = useNavigate(); - const { breakpoint } = useBreakpoint(deviceBreakpoints); - const setOverViewStore = useOverviewStore((state) => state.setState); - const resetWizard = useWizardStore((state) => state.resetState); - const viewMode = useOverviewStore((state) => state.viewMode); - const { LL } = useI18nContext(); - const { from: statsFilter } = useOverviewTimeSelection(); - const { networkId } = useParams(); - const selectedNetworkId = parseInt(networkId ?? '', 10); - const location = useLocation(); - - const { - network: { getNetworks, getOverviewStats, getNetworkStats }, - } = useApi(); - - const { data: fetchNetworksData } = useQuery({ - queryKey: ['network'], - queryFn: getNetworks, - placeholderData: (perv) => perv, - select: (networks) => - orderBy(networks, (network) => network.name.toLowerCase(), ['asc']), - }); - - const { data: networkStats } = useQuery({ - queryKey: [QueryKeys.FETCH_NETWORK_STATS, statsFilter, selectedNetworkId], - queryFn: () => - getNetworkStats({ - from: statsFilter, - id: selectedNetworkId, - }), - refetchOnWindowFocus: false, - refetchInterval: STATUS_REFETCH_TIMEOUT, - enabled: !isUndefined(selectedNetworkId) && !Number.isNaN(selectedNetworkId), - }); - - const { data: overviewStats, isLoading: userStatsLoading } = useQuery({ - queryKey: [QueryKeys.FETCH_NETWORK_USERS_STATS, statsFilter, selectedNetworkId], - queryFn: () => - getOverviewStats({ - from: statsFilter, - id: selectedNetworkId, - }), - enabled: - !isUndefined(statsFilter) && - !isUndefined(selectedNetworkId) && - !Number.isNaN(selectedNetworkId), - refetchOnWindowFocus: false, - refetchInterval: STATUS_REFETCH_TIMEOUT, - }); - - const getNetworkUsers = useMemo(() => { - if (overviewStats !== undefined) { - const user = sortByDate(overviewStats.user_devices, (s) => { - const fistDevice = sortByDate(s.devices, (d) => d.connected_at, false)[0]; - return fistDevice.connected_at; - }); - const devices = sortByDate( - overviewStats.network_devices.filter((d) => d.connected_at !== undefined), - (d) => d.connected_at as string, - ); - return { - network_devices: devices, - user_devices: user, - }; - } - return undefined; - }, [overviewStats]); - - // FIXME: lock viewMode on grid for now - useEffect(() => { - if (viewMode !== OverviewLayoutType.GRID) { - setOverViewStore({ viewMode: OverviewLayoutType.GRID }); - } - }, [setOverViewStore, viewMode]); - - // biome-ignore lint/correctness/useExhaustiveDependencies: migration, checkMeLater - useEffect(() => { - if (Number.isNaN(selectedNetworkId)) { - navigate(`/admin/overview/${location.search}`, { - replace: true, - }); - } - if (fetchNetworksData) { - if (!fetchNetworksData.length) { - resetWizard(); - navigate('/admin/wizard', { replace: true }); - } else { - setOverViewStore({ networks: fetchNetworksData }); - const ids = fetchNetworksData.map((n) => n.id); - if ( - isUndefined(selectedNetworkId) || - (!isUndefined(selectedNetworkId) && !ids.includes(selectedNetworkId)) - ) { - const oldestNetwork = orderBy(fetchNetworksData, ['id'], ['asc'])[0]; - setOverViewStore({ selectedNetworkId: oldestNetwork.id }); - } - } - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [fetchNetworksData, selectedNetworkId]); - - return ( - <> - - - {breakpoint === 'desktop' && !isUndefined(selectedNetworkId) && ( - - )} - {networkStats && } -
- {userStatsLoading && ( -
- -
- )} - {!getNetworkUsers && !userStatsLoading && } - {!userStatsLoading && - getNetworkUsers && - getNetworkUsers.network_devices.length === 0 && - getNetworkUsers.user_devices.length === 0 && } - {!userStatsLoading && - getNetworkUsers && - getNetworkUsers.user_devices.length > 0 && ( - - - - )} - {!userStatsLoading && - getNetworkUsers && - getNetworkUsers.network_devices.length > 0 && ( - -
-
- {getNetworkUsers.network_devices.map((device) => ( - - ))} -
-
-
- )} -
-
- {/* Modals */} - - ); -}; diff --git a/web/src/pages/overview/OverviewStats/OverviewStats.tsx b/web/src/pages/overview/OverviewStats/OverviewStats.tsx deleted file mode 100644 index f4ebc26d8..000000000 --- a/web/src/pages/overview/OverviewStats/OverviewStats.tsx +++ /dev/null @@ -1,373 +0,0 @@ -import './style.scss'; - -import clsx from 'clsx'; -import { orderBy } from 'lodash-es'; -import millify from 'millify'; -import { forwardRef, type ReactNode, useId, useMemo } from 'react'; -import AutoSizer from 'react-virtualized-auto-sizer'; - -import { useI18nContext } from '../../../i18n/i18n-react'; -import IconPacketsIn from '../../../shared/components/svg/IconPacketsIn'; -import IconPacketsOut from '../../../shared/components/svg/IconPacketsOut'; -import { Card } from '../../../shared/defguard-ui/components/Layout/Card/Card'; -import { NetworkSpeed } from '../../../shared/defguard-ui/components/Layout/NetworkSpeed/NetworkSpeed'; -import { NetworkDirection } from '../../../shared/defguard-ui/components/Layout/NetworkSpeed/types'; -import { isPresent } from '../../../shared/defguard-ui/utils/isPresent'; -import type { WireguardNetworkStats } from '../../../shared/types'; -import { useOverviewTimeSelection } from '../../overview-index/components/hooks/useOverviewTimeSelection'; -import { NetworkUsageChart } from '../OverviewConnectedUsers/shared/components/NetworkUsageChart/NetworkUsageChart'; -import { networkTrafficToChartData } from './utils'; - -interface Props { - networkStats: WireguardNetworkStats; -} - -export const OverviewStats = forwardRef( - ({ networkStats }, ref) => { - const { from: filterValue } = useOverviewTimeSelection(); - const peakDownload = useMemo(() => { - const sorted = orderBy(networkStats.transfer_series, (stats) => stats.download, [ - 'desc', - ]); - return sorted[0]?.download ?? 0; - }, [networkStats.transfer_series]); - const peakUpload = useMemo(() => { - const sorted = orderBy(networkStats.transfer_series, ['upload'], ['desc']); - return sorted[0]?.upload ?? 0; - }, [networkStats.transfer_series]); - const { LL } = useI18nContext(); - const localLL = LL.networkOverview.stats; - - const chartData = useMemo( - () => networkTrafficToChartData(networkStats.transfer_series, filterValue), - [filterValue, networkStats.transfer_series], - ); - - const info = useMemo( - (): InfoProps[] => [ - { - key: 'currently-active-users', - count: networkStats.current_active_users, - icon: , - title: localLL.currentlyActiveUsers(), - subTitle: localLL.totalUserDevices({ - count: networkStats.current_active_users, - }), - }, - { - key: 'current-active-network-devices', - title: localLL.currentlyActiveNetworkDevices(), - icon: , - count: networkStats.current_active_network_devices, - }, - { - key: 'active-users-icon', - title: localLL.activeUsersFilter({ - hour: filterValue, - }), - count: networkStats.active_users, - icon: , - subTitle: localLL.totalUserDevices({ - count: networkStats.active_user_devices, - }), - }, - { - key: 'active-network-devices', - title: localLL.activeNetworkDevices({ - hour: filterValue, - }), - icon: , - count: networkStats.current_active_network_devices, - }, - ], - [ - filterValue, - localLL, - networkStats.active_user_devices, - networkStats.active_users, - networkStats.current_active_network_devices, - networkStats.current_active_users, - ], - ); - - return ( -
- - {info.map((info) => ( - - ))} -
- {LL.networkOverview.stats.networkUsage()} -
-
- - {LL.networkOverview.stats.in()} - - -
-
- - {LL.networkOverview.stats.out()} - - -
-
-
-
- -
-

{LL.networkOverview.stats.activityIn({ hour: filterValue })}

-
- {LL.networkOverview.stats.peak()} -
- - -
-
- - -
-
-
-
- - {({ width, height }) => ( - <> - {networkStats.transfer_series && ( - - )} - - )} - -
-
-
- ); - }, -); - -type InfoProps = { - icon: ReactNode; - title: string; - subTitle?: string; - count: number; - key: string | number; -}; - -const InfoContainer = ({ count, icon, subTitle, title }: InfoProps) => { - return ( -
-

{title}

-
- {icon} -

- {millify(count, { - precision: 0, - })} -

-
- {isPresent(subTitle) &&

{subTitle}

} -
- ); -}; - -const CurrentActiveUsersIcon = () => { - return ( - - - - - ); -}; - -const CurrentActiveNetworkDevicesIcon = () => { - const maskId = useId(); - return ( - - - - - - - - - - - - ); -}; - -const ActiveUsersIcon = () => { - return ( - - - - - - - ); -}; - -const ActiveNetworkDevicesIcon = () => { - const maskId = useId(); - const mask2Id = useId(); - return ( - - - - - - - - - - - - - - - - - - ); -}; diff --git a/web/src/pages/overview/OverviewStats/style.scss b/web/src/pages/overview/OverviewStats/style.scss deleted file mode 100644 index ffdc73c69..000000000 --- a/web/src/pages/overview/OverviewStats/style.scss +++ /dev/null @@ -1,204 +0,0 @@ -.overview-network-stats { - display: grid; - - grid-template-columns: 1fr; - grid-template-rows: 1fr 1fr; - gap: var(--spacing-s); - padding: 0 var(--spacing-xs) var(--spacing-xs); - align-items: stretch; - overflow-x: auto; - - @include media-breakpoint-up(lg) { - overflow-x: unset; - } - - @include media-breakpoint-up(xl) { - grid-template-columns: 850px 1fr; - grid-template-rows: 1fr; - } - - @include media-breakpoint-up(xxl) { - grid-template-columns: auto 1fr; - } - - & > .summary { - position: relative; - width: 100%; - box-shadow: 5px 5px 15px #00000005; - background-color: var(--white); - border-radius: 15px; - display: flex; - align-items: stretch; - align-content: center; - justify-content: flex-start; - flex-flow: row nowrap; - min-height: 120px; - min-width: 800px; - - & > .info { - display: flex; - flex-flow: column; - align-items: center; - justify-content: flex-start; - row-gap: var(--spacing-xs); - padding: var(--spacing-s) 5px; - min-width: 155px; - - &:not(:first-child) { - border-left: 1px solid var(--border-primary); - } - - &.network-usage { - row-gap: var(--spacing-m); - } - - &:not(.network-usage) { - .info-title { - min-height: 29px; - } - } - - .info-title { - color: var(--text-body-tertiary); - text-align: center; - @include typography(app-modal-1); - max-width: 120px; - } - - .info-track { - width: 100%; - display: flex; - flex-flow: row; - align-items: center; - justify-content: center; - column-gap: var(--spacing-xs); - max-height: 42px; - - .info-count { - @include typography(app-title); - } - } - - .info-sub-title { - color: var(--text-body-tertiary); - @include typography(app-modal-3); - } - - .network-usage-track { - display: flex; - flex-flow: row; - align-items: center; - justify-content: center; - column-gap: var(--spacing-xs); - - & > :nth-child(1) { - svg { - transform: rotate(-90deg); - } - } - - & > :nth-child(2) { - svg { - transform: rotate(-90deg); - } - } - - .network-speed { - &.download { - svg { - transform: rotate(90deg); - } - } - } - - .network-usage { - & > span { - @include typography(app-modal-1); - } - } - } - } - } - - & > .activity-graph { - padding: 1.5rem 2rem 1rem; - box-sizing: border-box; - border-radius: 15px; - background-color: var(--white); - box-shadow: 5px 5px 15px #00000005; - min-height: 120px; - display: grid; - grid-template-rows: 21px 1fr; - grid-template-columns: 1fr; - row-gap: 15px; - width: 100%; - - & > .chart { - grid-row: 2; - grid-column: 1 / 2; - } - - & > header { - grid-row: 1; - grid-column: 1 / 2; - display: flex; - flex-direction: row; - align-items: center; - align-content: center; - justify-content: flex-start; - - h3 { - @include typography-legacy(15px, 21px, medium, var(--text-main), 'Poppins'); - - @include media-breakpoint-down(md) { - text-transform: uppercase; - @include text-weight(semiBold); - } - - @include media-breakpoint-up(md) { - } - } - - & > .peaks { - margin-left: auto; - display: flex; - flex-direction: row; - align-items: center; - align-content: center; - justify-content: flex-start; - height: 17px; - - @include media-breakpoint-down(md) { - column-gap: 1rem; - } - - @include media-breakpoint-up(md) { - column-gap: 2rem; - } - - & > span { - @include media-breakpoint-down(md) { - &:first-of-type { - display: none; - } - } - - @include typography-legacy(12px, 17px, medium, var(--gray-light), 'Poppins'); - } - - & > .network-speed { - display: flex; - flex-direction: row; - align-items: center; - align-content: center; - justify-content: flex-start; - column-gap: 0.4rem; - } - } - } - - & > .network-speed { - margin-top: 1rem; - } - } -} diff --git a/web/src/pages/overview/OverviewStats/utils.ts b/web/src/pages/overview/OverviewStats/utils.ts deleted file mode 100644 index fe9e648be..000000000 --- a/web/src/pages/overview/OverviewStats/utils.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { groupBy, map, sortBy } from 'lodash-es'; - -import type { NetworkSpeedStats } from '../../../shared/types'; - -type AggregatedTick = { - collected_at: string; - download: number; - upload: number; - count: number; -}; - -export const networkTrafficToChartData = ( - ticks: NetworkSpeedStats[], - filter: number, -): NetworkSpeedStats[] => { - if (filter >= 2 && filter <= 5 && ticks.length > 60) { - const sorted = sortBy(ticks, (tick) => new Date(tick.collected_at).getTime()); - const first = new Date(sorted[0].collected_at).getTime(); - const last = new Date(sorted[sorted.length - 1].collected_at).getTime(); - - const totalMinutes = Math.max(1, Math.floor((last - first) / (1000 * 60))); - const minutesPerBucket = Math.ceil(totalMinutes / 60); - - const grouped = groupBy(sorted, (tick) => { - const date = new Date(tick.collected_at); - const bucketTime = new Date( - Math.floor(date.getTime() / (minutesPerBucket * 60 * 1000)) * - minutesPerBucket * - 60 * - 1000, - ); - return bucketTime.toISOString(); - }); - - return map(grouped, (group, timestamp): AggregatedTick => { - const totalDownload = group.reduce((sum, t) => sum + t.download, 0); - const totalUpload = group.reduce((sum, t) => sum + t.upload, 0); - const count = group.length; - - return { - collected_at: timestamp, - download: totalDownload, - upload: totalUpload, - count, - }; - }); - } - return ticks; -}; diff --git a/web/src/pages/overview/OverviewViewSelect/OverviewViewSelect.tsx b/web/src/pages/overview/OverviewViewSelect/OverviewViewSelect.tsx deleted file mode 100644 index 6f3ada31f..000000000 --- a/web/src/pages/overview/OverviewViewSelect/OverviewViewSelect.tsx +++ /dev/null @@ -1,94 +0,0 @@ -import { useCallback, useEffect, useMemo } from 'react'; -import { useBreakpoint } from 'use-breakpoint'; - -import { useI18nContext } from '../../../i18n/i18n-react'; -import { deviceBreakpoints } from '../../../shared/constants'; -import { Select } from '../../../shared/defguard-ui/components/Layout/Select/Select'; -import type { - SelectOption, - SelectSelectedValue, -} from '../../../shared/defguard-ui/components/Layout/Select/types'; -import { OverviewLayoutType } from '../../../shared/types'; -import { useOverviewStore } from '../hooks/store/useOverviewStore'; - -export const OverviewViewSelect = () => { - const { breakpoint } = useBreakpoint(deviceBreakpoints); - const defaultViewMode = useOverviewStore((state) => state.defaultViewMode); - const viewMode = useOverviewStore((state) => state.viewMode); - const setOverViewStore = useOverviewStore((state) => state.setState); - const { LL } = useI18nContext(); - - useEffect(() => { - setOverViewStore({ viewMode: defaultViewMode }); - }, [defaultViewMode, setOverViewStore]); - - const getSelectOptions = useMemo((): SelectOption[] => { - if (breakpoint === 'mobile') { - return [ - { - key: 0, - value: OverviewLayoutType.GRID, - label: LL.networkOverview.filterLabels.grid(), - }, - { - key: 1, - value: OverviewLayoutType.LIST, - label: LL.networkOverview.filterLabels.list(), - disabled: true, - }, - ]; - } - if (breakpoint === 'tablet') { - return [ - { - key: 0, - value: OverviewLayoutType.GRID, - label: LL.networkOverview.filterLabels.grid(), - disabled: true, - }, - { - key: 1, - value: OverviewLayoutType.LIST, - label: LL.networkOverview.filterLabels.list(), - disabled: false, - }, - ]; - } - return [ - { - key: 0, - value: OverviewLayoutType.GRID, - label: LL.networkOverview.filterLabels.grid(), - }, - { - key: 1, - value: OverviewLayoutType.LIST, - label: LL.networkOverview.filterLabels.list(), - }, - ]; - }, [LL.networkOverview.filterLabels, breakpoint]); - - const renderSelected = useCallback( - (selected: OverviewLayoutType): SelectSelectedValue => { - const option = getSelectOptions.find((o) => o.value === selected); - if (!option) throw Error("Selected option doesn't exists"); - return { - key: selected, - displayValue: option.label, - }; - }, - [getSelectOptions], - ); - - return ( - setSelectedFilterOption(filter)} - /> - )} -
- {!isLoading && filteredProvisioners && filteredProvisioners.length > 0 && ( - - )} - {!isLoading && - (!filteredProvisioners || !filteredProvisioners.length ? ( - - ) : null)} - {isLoading && ( -
- -
- )} - -
- -
- - - ); -}; - -enum FilterOptions { - ALL = 'all', - AVAILABLE = 'available', - UNAVAILABLE = 'unavailable', -} diff --git a/web/src/pages/provisioners/components/ProvisionersList/ProvisionersList.tsx b/web/src/pages/provisioners/components/ProvisionersList/ProvisionersList.tsx deleted file mode 100644 index 4408ff6ea..000000000 --- a/web/src/pages/provisioners/components/ProvisionersList/ProvisionersList.tsx +++ /dev/null @@ -1,141 +0,0 @@ -import './style.scss'; - -import classNames from 'classnames'; -import { useMemo } from 'react'; -import { useBreakpoint } from 'use-breakpoint'; - -import { useI18nContext } from '../../../../i18n/i18n-react'; -import IconCheckmarkGreen from '../../../../shared/components/svg/IconCheckmarkGreen'; -import IconDeactivated from '../../../../shared/components/svg/IconDeactivated'; -import { deviceBreakpoints } from '../../../../shared/constants'; -import { EditButton } from '../../../../shared/defguard-ui/components/Layout/EditButton/EditButton'; -import { EditButtonOption } from '../../../../shared/defguard-ui/components/Layout/EditButton/EditButtonOption'; -import { EditButtonOptionStyleVariant } from '../../../../shared/defguard-ui/components/Layout/EditButton/types'; -import { - type ListHeader, - ListSortDirection, -} from '../../../../shared/defguard-ui/components/Layout/VirtualizedList/types'; -import { VirtualizedList } from '../../../../shared/defguard-ui/components/Layout/VirtualizedList/VirtualizedList'; -import type { Provisioner } from '../../../../shared/types'; -import { useDeleteProvisionerModal } from '../modals/useDeleteProvisionerModal'; - -interface Props { - provisioners: Provisioner[]; -} - -export const ProvisionersList = ({ provisioners }: Props) => { - const { breakpoint } = useBreakpoint(deviceBreakpoints); - const { LL } = useI18nContext(); - const openDeletionModal = useDeleteProvisionerModal((state) => state.open); - - const listCells = useMemo(() => { - const res = [ - { - key: 'name', - render: (value: Provisioner) => ( - <> - {value.id} - - ), - }, - { - key: 'status', - render: (value: Provisioner) => ( - <> - {value.connected ? ( - <> - - - {LL.provisionersOverview.list.status.available()} - - - ) : ( - <> - - - {LL.provisionersOverview.list.status.unavailable()} - - - )} - - ), - }, - { - key: 'ip', - render: (value: Provisioner) => ( - {value.ip} - ), - }, - { - key: 'edit', - render: (value: Provisioner) => ( - - openDeletionModal({ provisionerId: value.id })} - text={LL.provisionersOverview.list.editButton.delete()} - /> - - ), - }, - ]; - if (breakpoint !== 'desktop') { - res.splice(1, 1); - } - return res; - }, [ - LL.provisionersOverview.list.editButton, - LL.provisionersOverview.list.status, - openDeletionModal, - breakpoint, - ]); - - const getListHeaders = useMemo(() => { - const res: ListHeader[] = [ - { - key: 'name', - text: LL.provisionersOverview.list.headers.name(), - active: true, - sortDirection: ListSortDirection.ASC, - }, - { - key: 'status', - text: LL.provisionersOverview.list.headers.status(), - active: false, - }, - { - key: 'ip', - text: LL.provisionersOverview.list.headers.ip(), - active: false, - }, - { - key: 'actions', - text: LL.provisionersOverview.list.headers.actions(), - active: false, - sortable: false, - }, - ]; - if (breakpoint !== 'desktop') { - res.splice(1, 1); - } - return res; - }, [LL.provisionersOverview.list.headers, breakpoint]); - - return ( - - ); -}; diff --git a/web/src/pages/provisioners/components/ProvisionersList/style.scss b/web/src/pages/provisioners/components/ProvisionersList/style.scss deleted file mode 100644 index 68f32e2fd..000000000 --- a/web/src/pages/provisioners/components/ProvisionersList/style.scss +++ /dev/null @@ -1,83 +0,0 @@ -@mixin list-layout { - @include media-breakpoint-down(lg) { - grid-template-columns: 250px 1fr 60px; - @for $i from 1 through 3 { - & > :nth-child(#{$i}) { - grid-column: $i; - } - } - } - @include media-breakpoint-up(lg) { - grid-template-columns: 250px 200px 1fr 62px; - @for $i from 1 through 4 { - & > :nth-child(#{$i}) { - grid-column: $i; - } - } - } -} - -#provisioners-page { - .provisioners-list { - .headers { - @include list-layout; - - @include media-breakpoint-up(lg) { - & > :nth-child(4) { - justify-content: center; - } - } - } - - .scroll-container { - box-sizing: border-box; - padding-bottom: 1.5rem; - margin-right: 5px; - @include media-breakpoint-up(lg) { - padding-bottom: 4rem; - } - } - - .default-row { - display: inline-grid; - grid-template-rows: 60px; - align-items: center; - box-sizing: border-box; - padding: 0 2rem; - @include list-layout; - - & > * { - display: flex; - flex-flow: row nowrap; - align-items: center; - align-content: center; - justify-content: flex-start; - - & > span { - @include regular-text; - @include text-weight(medium); - - color: var(--gray-dark); - - &.connected { - color: var(--text-main); - } - } - } - - @include media-breakpoint-up(lg) { - & > :nth-child(2) { - column-gap: 5px; - } - - & > :nth-child(4) { - justify-content: center; - - & > button { - width: 100%; - } - } - } - } - } -} diff --git a/web/src/pages/provisioners/components/ProvisioningStationSetupCard/ProvisioningStationSetupCard.tsx b/web/src/pages/provisioners/components/ProvisioningStationSetupCard/ProvisioningStationSetupCard.tsx deleted file mode 100644 index 02fbd9e12..000000000 --- a/web/src/pages/provisioners/components/ProvisioningStationSetupCard/ProvisioningStationSetupCard.tsx +++ /dev/null @@ -1,99 +0,0 @@ -import './style.scss'; - -import { useQuery } from '@tanstack/react-query'; -import { isUndefined } from 'lodash-es'; -import { type ReactNode, useMemo } from 'react'; -import Skeleton from 'react-loading-skeleton'; - -import { useI18nContext } from '../../../../i18n/i18n-react'; -import YubikeyProvisioningGraphic from '../../../../shared/components/svg/YubikeyProvisioningGraphic'; -import { ActionButton } from '../../../../shared/defguard-ui/components/Layout/ActionButton/ActionButton'; -import { ActionButtonVariant } from '../../../../shared/defguard-ui/components/Layout/ActionButton/types'; -import { Card } from '../../../../shared/defguard-ui/components/Layout/Card/Card'; -import { ExpandableCard } from '../../../../shared/defguard-ui/components/Layout/ExpandableCard/ExpandableCard'; -import useApi from '../../../../shared/hooks/useApi'; -import { useClipboard } from '../../../../shared/hooks/useClipboard'; -import { QueryKeys } from '../../../../shared/queries'; - -export const ProvisioningStationSetup = () => { - const { writeToClipboard } = useClipboard(); - const { LL } = useI18nContext(); - const { - provisioning: { getWorkerToken }, - } = useApi(); - - const { data, isLoading: tokenLoading } = useQuery({ - queryKey: [QueryKeys.FETCH_WORKER_TOKEN], - queryFn: getWorkerToken, - refetchOnWindowFocus: false, - refetchOnMount: true, - }); - - const command = useMemo( - () => - `docker run --privileged ghcr.io/defguard/yubikey-provision:main -t ${data?.token} --id --grpc `, - [data?.token], - ); - - const tokenActions = useMemo( - (): ReactNode[] => [ - { - if (data?.token) { - void writeToClipboard( - data.token, - LL.provisionersOverview.messages.copy.token(), - ); - } - }} - />, - ], - [data?.token, LL.provisionersOverview.messages.copy, writeToClipboard], - ); - - const dockerActions = useMemo( - () => [ - { - void writeToClipboard(command, LL.provisionersOverview.messages.copy.command()); - }} - />, - ], - [LL.provisionersOverview.messages, command, writeToClipboard], - ); - - return ( - -

{LL.provisionersOverview.provisioningStation.header()}

-

{LL.provisionersOverview.provisioningStation.content()}

-
- -
- {data && !isUndefined(data.token) && ( - -

{data?.token}

-
- )} - {data && !isUndefined(data.token) && ( - -

{command}

-
- )} - {tokenLoading && !data && } -
- ); -}; diff --git a/web/src/pages/provisioners/components/ProvisioningStationSetupCard/style.scss b/web/src/pages/provisioners/components/ProvisioningStationSetupCard/style.scss deleted file mode 100644 index c3e0e79a1..000000000 --- a/web/src/pages/provisioners/components/ProvisioningStationSetupCard/style.scss +++ /dev/null @@ -1,86 +0,0 @@ -@use '@scssutils' as *; - -#provisioning-setup-card { - box-sizing: border-box; - padding: 15px; - - @include media-breakpoint-down(lg) { - margin: 0 2rem; - width: calc(100% - 40px); - } - - @include media-breakpoint-up(lg) { - padding: 4rem; - margin: 0 40px 25px; - width: calc(100% - 80px); - } - - @include media-breakpoint-up(xxl) { - margin: 0; - width: 100%; - } - - & > h4 { - @include modal-header; - - text-align: center; - margin-bottom: 1.5rem; - @include media-breakpoint-up(lg) { - margin-bottom: 2rem; - } - } - - & > .image-row { - height: 75px; - display: flex; - flex-flow: row; - align-items: center; - justify-content: center; - align-content: center; - } - - & > p { - word-break: normal; - text-align: center; - @include regular-text; - - margin-bottom: 15px; - max-width: 100%; - color: var(--gray-dark); - font-size: 12px; - - @include media-breakpoint-up(lg) { - margin-bottom: 25px; - } - } - - & > .expandable-card { - width: 100%; - margin-top: 20px; - @include media-breakpoint-up(lg) { - margin-top: 40px; - } - - & > .expanded-content { - overflow: hidden; - - & > p { - max-width: 100%; - text-overflow: ellipsis; - text-align: left; - word-break: break-all; - white-space: normal; - @include small-text; - - line-height: 22px; - } - } - } - .command-skeleton { - line-height: 1; - height: 198px; - width: 100%; - margin-top: 20px; - border-radius: 12px; - } -} diff --git a/web/src/pages/provisioners/components/modals/DeleteProvisionerModal.tsx b/web/src/pages/provisioners/components/modals/DeleteProvisionerModal.tsx deleted file mode 100644 index 33da15048..000000000 --- a/web/src/pages/provisioners/components/modals/DeleteProvisionerModal.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import { useMutation, useQueryClient } from '@tanstack/react-query'; -import { shallow } from 'zustand/shallow'; - -import { useI18nContext } from '../../../../i18n/i18n-react'; -import { ConfirmModal } from '../../../../shared/defguard-ui/components/Layout/modals/ConfirmModal/ConfirmModal'; -import { ConfirmModalType } from '../../../../shared/defguard-ui/components/Layout/modals/ConfirmModal/types'; -import useApi from '../../../../shared/hooks/useApi'; -import { useToaster } from '../../../../shared/hooks/useToaster'; -import { MutationKeys } from '../../../../shared/mutations'; -import { QueryKeys } from '../../../../shared/queries'; -import { useDeleteProvisionerModal } from './useDeleteProvisionerModal'; - -export const DeleteProvisionerModal = () => { - const isOpen = useDeleteProvisionerModal((state) => state.visible); - const targetId = useDeleteProvisionerModal((state) => state.provisionerId); - const [close, reset] = useDeleteProvisionerModal( - (state) => [state.close, state.reset], - shallow, - ); - - const { LL } = useI18nContext(); - const { - provisioning: { deleteWorker }, - } = useApi(); - const toaster = useToaster(); - - const queryClient = useQueryClient(); - - const { mutate, isPending: isLoading } = useMutation({ - mutationFn: deleteWorker, - mutationKey: [MutationKeys.DELETE_WORKER], - onSuccess: () => { - void queryClient.invalidateQueries({ - queryKey: [QueryKeys.FETCH_WORKERS], - }); - toaster.success( - LL.modals.deleteProvisioner.messages.success({ - provisioner: targetId ?? '', - }), - ); - close(); - }, - onError: (e) => { - toaster.error(LL.messages.error()); - console.error(e); - }, - }); - - return ( - close()} - afterClose={() => reset()} - onSubmit={() => { - if (targetId) { - mutate(targetId); - } - }} - /> - ); -}; diff --git a/web/src/pages/provisioners/components/modals/useDeleteProvisionerModal.tsx b/web/src/pages/provisioners/components/modals/useDeleteProvisionerModal.tsx deleted file mode 100644 index 90264bce4..000000000 --- a/web/src/pages/provisioners/components/modals/useDeleteProvisionerModal.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { createWithEqualityFn } from 'zustand/traditional'; - -const defaultValues: StoreValues = { - visible: false, - provisionerId: undefined, -}; - -export const useDeleteProvisionerModal = createWithEqualityFn( - (set) => ({ - ...defaultValues, - open: (values) => set({ ...defaultValues, ...values, visible: true }), - close: () => set({ visible: false }), - reset: () => set(defaultValues), - }), - Object.is, -); - -type Store = StoreValues & StoreMethods; - -type StoreValues = { - visible: boolean; - provisionerId?: string; -}; - -type StoreMethods = { - open: (values?: Partial) => void; - close: () => void; - reset: () => void; -}; diff --git a/web/src/pages/provisioners/style.scss b/web/src/pages/provisioners/style.scss deleted file mode 100644 index 58007db08..000000000 --- a/web/src/pages/provisioners/style.scss +++ /dev/null @@ -1,177 +0,0 @@ -#provisioners-page { - & > .page-content { - min-height: 100%; - max-height: 100%; - overflow-y: auto; - display: grid; - grid-template-columns: 1fr; - grid-template-rows: auto 40px 100vh; - padding: 2rem 0; - box-sizing: border-box; - row-gap: 2rem; - - @include media-breakpoint-up(lg) { - padding-top: 2rem; - row-gap: 0; - grid-template-columns: 1fr; - grid-template-rows: 104px auto 100vh; - } - - @include media-breakpoint-up(xxl) { - padding: 4rem 7rem 0 0; - grid-template-columns: minmax(880px, 1fr) minmax(400px, 700px); - grid-template-rows: 105px 1fr; - } - - & > header { - grid-row: 2; - grid-column: 1; - box-sizing: border-box; - display: flex; - flex-flow: row nowrap; - align-items: center; - align-content: center; - padding: 0 2rem; - - @include media-breakpoint-up(lg) { - padding: 0 0 0 6rem; - column-gap: 3rem; - grid-column: 1; - grid-row: 1; - } - - & > h1 { - display: none; - - @include page-header; - - @include media-breakpoint-up(lg) { - display: block; - } - } - - & > .search { - height: 40px; - width: 100%; - - @include media-breakpoint-up(lg) { - width: clamp(200px, 360px, 360px); - } - } - } - - & > .provisioners-container { - grid-column: 1; - grid-row: 3; - display: grid; - row-gap: 1.8rem; - grid-template-rows: 40px 1fr; - grid-template-columns: 1fr; - - & > .top { - grid-row: 1; - grid-column: 1; - box-sizing: border-box; - display: flex; - flex-flow: row nowrap; - width: 100%; - padding: 0 2rem; - - @include media-breakpoint-up(lg) { - padding: 0 4rem 0 6rem; - } - - & > .select { - margin-left: auto; - width: 200px; - - & > .select-container { - min-height: 40px; - } - } - - & > .provisioners-count { - display: flex; - flex-flow: row nowrap; - column-gap: 1.5rem; - align-content: center; - align-items: center; - justify-content: flex-start; - - & > span { - @include modal-header; - } - - & > .count { - display: flex; - flex-flow: row; - height: 30px; - min-width: 30px; - box-sizing: border-box; - padding: 0 5px; - align-items: center; - align-content: center; - justify-content: center; - background-color: var(--gray-light); - border-radius: 10px; - - span { - display: block; - @include typography-legacy(12px, 18px, semiBold, var(--white), Poppins); - } - } - } - } - - & > .provisioners-list, - & > .no-data, - & > .loader { - grid-row: 2; - grid-column: 1; - } - - & > .loader { - width: 100%; - height: 100%; - display: flex; - flex-flow: column; - align-items: center; - justify-content: center; - align-content: center; - } - - & > .no-data { - width: 100%; - text-align: center; - box-sizing: border-box; - padding: 0 4rem 0 6rem; - } - } - - & > .setup-container { - grid-column: 1; - grid-row: 1; - - @include media-breakpoint-up(lg) { - grid-row: 2; - } - } - - @include media-breakpoint-up(xxl) { - & > header { - grid-row: 1; - grid-column: 1/3; - } - - & > .provisioners-container { - grid-column: 1 / 2; - grid-row: 2; - } - - & > .setup-container { - grid-row: 2; - grid-column: 2 / 3; - } - } - } -} diff --git a/web/src/pages/redirect/RedirectPage.tsx b/web/src/pages/redirect/RedirectPage.tsx deleted file mode 100644 index bc3e7e186..000000000 --- a/web/src/pages/redirect/RedirectPage.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import './style.scss'; - -import { useI18nContext } from '../../i18n/i18n-react'; -import SvgIconDfgOpenidRedirect from '../../shared/components/svg/IconDfgOpenidRedirect'; -import { Card } from '../../shared/defguard-ui/components/Layout/Card/Card'; - -// used in auth flow -export const RedirectPage = () => { - const { LL } = useI18nContext(); - return ( -
- -

{LL.redirectPage.title()}

-

{LL.redirectPage.subtitle()}

-
- -
-
-
- ); -}; diff --git a/web/src/pages/redirect/style.scss b/web/src/pages/redirect/style.scss deleted file mode 100644 index 2b9ff17ac..000000000 --- a/web/src/pages/redirect/style.scss +++ /dev/null @@ -1,42 +0,0 @@ -@use '@scssutils' as *; - -#redirect-page { - width: 100%; - height: 100%; - display: flex; - flex-flow: column; - align-items: center; - justify-content: center; - box-sizing: border-box; - padding: 0 20px; - user-select: none; - - & > .card { - display: flex; - flex-flow: column; - align-items: center; - justify-content: flex-start; - box-sizing: border-box; - padding: 30px 20px; - width: 100%; - - @include media-breakpoint-up(md) { - padding: 50px 0; - width: 450px; - } - - h2 { - @include typography-legacy(20px, 30px, semiBold, var(--text-main), 'Poppins'); - margin-bottom: 20px; - width: 100%; - text-align: center; - } - - p { - @include typography-legacy(12px, 1.2, regular, var(--gray-light), 'Roboto'); - margin-bottom: 30px; - width: 100%; - text-align: center; - } - } -} diff --git a/web/src/pages/settings/SettingsPage.tsx b/web/src/pages/settings/SettingsPage.tsx deleted file mode 100644 index e7d819c03..000000000 --- a/web/src/pages/settings/SettingsPage.tsx +++ /dev/null @@ -1,182 +0,0 @@ -import './style.scss'; - -import { useQuery, useQueryClient } from '@tanstack/react-query'; -import { type ReactNode, useCallback, useEffect, useMemo, useState } from 'react'; -import { shallow } from 'zustand/shallow'; - -import { useI18nContext } from '../../i18n/i18n-react'; -import { PageContainer } from '../../shared/components/Layout/PageContainer/PageContainer'; -import { useUpgradeLicenseModal } from '../../shared/components/Layout/UpgradeLicenseModal/store'; -import { UpgradeLicenseModalVariant } from '../../shared/components/Layout/UpgradeLicenseModal/types'; -import { Card } from '../../shared/defguard-ui/components/Layout/Card/Card'; -import { CardTabs } from '../../shared/defguard-ui/components/Layout/CardTabs/CardTabs'; -import type { CardTabsData } from '../../shared/defguard-ui/components/Layout/CardTabs/types'; -import { LoaderSpinner } from '../../shared/defguard-ui/components/Layout/LoaderSpinner/LoaderSpinner'; -import { useAppStore } from '../../shared/hooks/store/useAppStore'; -import useApi from '../../shared/hooks/useApi'; -import { QueryKeys } from '../../shared/queries'; -import { ActivityLogStreamSettings } from './components/ActivityLogStreamSettings/ActivityLogStreamSettings'; -import { EnterpriseSettings } from './components/EnterpriseSettings/EnterpriseSettings'; -import { GlobalSettings } from './components/GlobalSettings/GlobalSettings'; -import { LdapSettings } from './components/LdapSettings/LdapSettings'; -import { NotificationSettings } from './components/NotificationSettings/NotificationSettings'; -import { OpenIdSettings } from './components/OpenIdSettings/OpenIdSettings'; -import { SmtpSettings } from './components/SmtpSettings/SmtpSettings'; -import { useSettingsPage } from './hooks/useSettingsPage'; - -const tabsContent: ReactNode[] = [ - , - , - , - , - , - , - , -]; - -const enterpriseTabs: number[] = [2, 3, 4, 6]; - -export const SettingsPage = () => { - const { LL } = useI18nContext(); - const { - getEnterpriseInfo, - settings: { getSettings }, - } = useApi(); - - const [activeCard, setActiveCard] = useState(0); - const queryClient = useQueryClient(); - const appInfo = useAppStore((s) => s.appInfo); - const openUpgradeLicenseModal = useUpgradeLicenseModal((s) => s.open, shallow); - - const [setPageState, resetPageState] = useSettingsPage( - (state) => [state.setState, state.reset], - shallow, - ); - - const settings = useSettingsPage((state) => state.settings); - - const { data: settingsData, isLoading: settingsLoading } = useQuery({ - queryFn: getSettings, - queryKey: [QueryKeys.FETCH_SETTINGS], - refetchOnMount: true, - refetchOnWindowFocus: false, - }); - - const { data: enterpriseInfo, isLoading: enterpriseInfoLoading } = useQuery({ - queryFn: getEnterpriseInfo, - queryKey: [QueryKeys.FETCH_ENTERPRISE_INFO], - refetchOnWindowFocus: false, - refetchOnMount: true, - }); - - const handleTabClick = useCallback( - (tabIndex: number) => { - if (appInfo) { - if (enterpriseTabs.includes(tabIndex) && !appInfo.license_info.enterprise) { - openUpgradeLicenseModal({ - modalVariant: UpgradeLicenseModalVariant.ENTERPRISE_NOTICE, - }); - } else { - setActiveCard(tabIndex); - } - } - }, - [appInfo, openUpgradeLicenseModal], - ); - - const tabs = useMemo( - (): CardTabsData[] => [ - { - key: 0, - content: LL.settingsPage.tabs.global(), - active: activeCard === 0, - onClick: () => handleTabClick(0), - }, - { - key: 1, - content: LL.settingsPage.tabs.smtp(), - active: activeCard === 1, - onClick: () => handleTabClick(1), - }, - { - key: 2, - content: LL.settingsPage.tabs.ldap(), - active: activeCard === 2, - onClick: () => handleTabClick(2), - }, - { - key: 3, - content: LL.settingsPage.tabs.openid(), - active: activeCard === 3, - onClick: () => handleTabClick(3), - }, - { - key: 4, - content: LL.settingsPage.tabs.enterprise(), - active: activeCard === 4, - onClick: () => handleTabClick(4), - }, - { - key: 5, - content: LL.settingsPage.tabs.gatewayNotifications(), - active: activeCard === 5, - onClick: () => handleTabClick(5), - }, - { - key: 6, - content: LL.settingsPage.tabs.activityLogStream(), - active: activeCard === 6, - onClick: () => handleTabClick(6), - }, - ], - [LL.settingsPage.tabs, activeCard, handleTabClick], - ); - - // set store - useEffect(() => { - setPageState({ - settings: settingsData, - enterpriseInfo: enterpriseInfo?.license_info, - }); - }, [settingsData, setPageState, enterpriseInfo?.license_info]); - - // biome-ignore lint/correctness/useExhaustiveDependencies: on mount - useEffect(() => { - void queryClient.invalidateQueries({ - queryKey: [QueryKeys.FETCH_APP_INFO], - }); - return () => { - resetPageState?.(); - }; - }, []); - - // if appinfo changes and license is not enterprise anymore then change active tab to global - // this can happen when admin is on enterprise tab but limits are exceeded in the mean time - useEffect(() => { - if ( - appInfo && - !appInfo.license_info.enterprise && - enterpriseTabs.includes(activeCard) - ) { - setActiveCard(0); - openUpgradeLicenseModal({ - modalVariant: UpgradeLicenseModalVariant.LICENSE_LIMIT, - }); - } - }, [activeCard, appInfo, openUpgradeLicenseModal]); - - return ( - -

{LL.settingsPage.title()}

- {(settingsLoading || enterpriseInfoLoading) && } - {settings && !enterpriseInfoLoading && !settingsLoading && ( - <> - - - {tabsContent[activeCard]} - - - )} -
- ); -}; diff --git a/web/src/pages/settings/components/ActivityLogStreamSettings/ActivityLogStreamSettings.tsx b/web/src/pages/settings/components/ActivityLogStreamSettings/ActivityLogStreamSettings.tsx deleted file mode 100644 index 426a7a56e..000000000 --- a/web/src/pages/settings/components/ActivityLogStreamSettings/ActivityLogStreamSettings.tsx +++ /dev/null @@ -1,244 +0,0 @@ -import './style.scss'; - -import { useMutation, useQuery } from '@tanstack/react-query'; -import { orderBy, range } from 'lodash-es'; -import { useMemo, useState } from 'react'; -import Skeleton from 'react-loading-skeleton'; -import { shallow } from 'zustand/shallow'; - -import { useI18nContext } from '../../../../i18n/i18n-react'; -import { ListHeader } from '../../../../shared/components/Layout/ListHeader/ListHeader'; -import type { ListHeaderColumnConfig } from '../../../../shared/components/Layout/ListHeader/types'; -import { Button } from '../../../../shared/defguard-ui/components/Layout/Button/Button'; -import { - ButtonSize, - ButtonStyleVariant, -} from '../../../../shared/defguard-ui/components/Layout/Button/types'; -import { EditButton } from '../../../../shared/defguard-ui/components/Layout/EditButton/EditButton'; -import { EditButtonOption } from '../../../../shared/defguard-ui/components/Layout/EditButton/EditButtonOption'; -import { EditButtonOptionStyleVariant } from '../../../../shared/defguard-ui/components/Layout/EditButton/types'; -import { ListCellText } from '../../../../shared/defguard-ui/components/Layout/ListCellText/ListCellText'; -import { NoData } from '../../../../shared/defguard-ui/components/Layout/NoData/NoData'; -import { ListSortDirection } from '../../../../shared/defguard-ui/components/Layout/VirtualizedList/types'; -import SvgIconPlus from '../../../../shared/defguard-ui/components/svg/IconPlus'; -import { isPresent } from '../../../../shared/defguard-ui/utils/isPresent'; -import useApi from '../../../../shared/hooks/useApi'; -import { useToaster } from '../../../../shared/hooks/useToaster'; -import queryClient from '../../../../shared/query-client'; -import type { ActivityLogStream } from '../../../../shared/types'; -import { CreateActivityLogStreamModal } from './modals/CreateActivityLogStreamModal/CreateActivityLogStreamModal'; -import { useCreateActivityLogStreamModalStore } from './modals/CreateActivityLogStreamModal/store'; -import { LogStashHttpStreamCEModal } from './modals/LogStashHttpStreamCEModal/LogStashHttpStreamCEModal'; -import { useLogstashHttpStreamCEModalStore } from './modals/LogStashHttpStreamCEModal/store'; -import { useVectorHttpStreamCEModal } from './modals/VectorHttpStreamCEModal/store'; -import { VectorHttpStreamCEModal } from './modals/VectorHttpStreamCEModal/VectorHttpStreamCEModal'; -import { - activityLogStreamToLabel, - activityLogStreamTypeToLabel, -} from './utils/activityLogStreamToLabel'; - -export const ActivityLogStreamSettings = () => { - const { LL } = useI18nContext(); - const localLL = LL.settingsPage.activityLogStreamSettings; - - return ( - <> -
-
-

{localLL.title()}

-
- -
- - - - - ); -}; - -const ActivityLogStreamList = () => { - const { LL } = useI18nContext(); - const localLL = LL.settingsPage.activityLogStreamSettings; - - const { - activityLogStream: { getActivityLogStreams }, - } = useApi(); - - const openCreateModal = useCreateActivityLogStreamModalStore((s) => s.open, shallow); - - const { data: activityLogStreams, isLoading: streamsLoading } = useQuery({ - queryFn: getActivityLogStreams, - queryKey: ['activity_stream'], - placeholderData: (perv) => perv, - refetchOnMount: true, - refetchOnWindowFocus: true, - select: (data) => orderBy(data, (row) => row.name.toLowerCase(), ['asc']), - }); - - const [activeSortKey] = useState('name'); - const [sortDirection, setSortDirection] = useState( - ListSortDirection.ASC, - ); - - const listHeaders = useMemo( - (): ListHeaderColumnConfig[] => [ - { - key: 'name', - enabled: true, - sortKey: 'name', - label: localLL.list.headers.name(), - }, - { - key: 'destination', - enabled: false, - sortKey: 'stream_type', - label: localLL.list.headers.destination(), - }, - { - key: 'edit', - enabled: false, - label: LL.common.controls.edit(), - }, - ], - [LL.common.controls, localLL.list.headers], - ); - - return ( -
-
-
-
- { - setSortDirection(direction); - }} - /> -
-
- {!isPresent(activityLogStreams) && streamsLoading && ( -
- {range(6).map((index) => ( - - ))} -
- )} - {isPresent(activityLogStreams) && ( -
    - {activityLogStreams.map((stream) => ( -
  • - -
  • - ))} -
- )} - {isPresent(activityLogStreams) && activityLogStreams.length === 0 && ( - - )} -
-
- ); -}; - -type ListItemsProps = { - stream: ActivityLogStream; -}; - -const ListItem = ({ stream }: ListItemsProps) => { - return ( -
-
- -
-
- -
-
- -
-
- ); -}; - -type EditProps = { - stream: ActivityLogStream; -}; - -const EditListItem = ({ stream }: EditProps) => { - const openVectorHttpStreamModal = useVectorHttpStreamCEModal((s) => s.open, shallow); - const openLogstashHttpStreamModal = useLogstashHttpStreamCEModalStore( - (s) => s.open, - shallow, - ); - const { LL } = useI18nContext(); - - const toast = useToaster(); - - const { - activityLogStream: { deleteActivityLogStream }, - } = useApi(); - - const { mutate: deleteStreamMutation, isPending: isDeleting } = useMutation({ - mutationFn: deleteActivityLogStream, - onSuccess: () => { - toast.success( - LL.settingsPage.activityLogStreamSettings.messages.destinationCrud.delete({ - destination: activityLogStreamToLabel(stream), - }), - ); - void queryClient.invalidateQueries({ - queryKey: ['activity_stream'], - }); - }, - onError: (e) => { - toast.error(LL.messages.error()); - console.error(e); - }, - }); - - const handleEdit = () => { - switch (stream.stream_type) { - case 'logstash_http': - openLogstashHttpStreamModal(stream); - break; - case 'vector_http': - openVectorHttpStreamModal(stream); - break; - default: - toast.error('Unimplemented'); - } - }; - - return ( - - - { - deleteStreamMutation(stream.id); - }} - disabled={isDeleting} - /> - - ); -}; diff --git a/web/src/pages/settings/components/ActivityLogStreamSettings/modals/CreateActivityLogStreamModal/CreateActivityLogStreamModal.tsx b/web/src/pages/settings/components/ActivityLogStreamSettings/modals/CreateActivityLogStreamModal/CreateActivityLogStreamModal.tsx deleted file mode 100644 index 014f04356..000000000 --- a/web/src/pages/settings/components/ActivityLogStreamSettings/modals/CreateActivityLogStreamModal/CreateActivityLogStreamModal.tsx +++ /dev/null @@ -1,102 +0,0 @@ -import './style.scss'; - -import clsx from 'clsx'; -import { useState } from 'react'; -import { shallow } from 'zustand/shallow'; - -import { useI18nContext } from '../../../../../../i18n/i18n-react'; -import { Button } from '../../../../../../shared/defguard-ui/components/Layout/Button/Button'; -import { ButtonStyleVariant } from '../../../../../../shared/defguard-ui/components/Layout/Button/types'; -import { ModalWithTitle } from '../../../../../../shared/defguard-ui/components/Layout/modals/ModalWithTitle/ModalWithTitle'; -import { RadioButton } from '../../../../../../shared/defguard-ui/components/Layout/RadioButton/Radiobutton'; -import type { ActivityLogStreamType } from '../../../../../../shared/types'; -import { activityLogStreamTypeToLabel } from '../../utils/activityLogStreamToLabel'; -import { useLogstashHttpStreamCEModalStore } from '../LogStashHttpStreamCEModal/store'; -import { useVectorHttpStreamCEModal } from '../VectorHttpStreamCEModal/store'; -import { useCreateActivityLogStreamModalStore } from './store'; - -export const CreateActivityLogStreamModal = () => { - const { LL } = useI18nContext(); - const localLL = LL.settingsPage.activityLogStreamSettings.modals.selectDestination; - const [close, reset] = useCreateActivityLogStreamModalStore( - (s) => [s.close, s.reset], - shallow, - ); - const isOpen = useCreateActivityLogStreamModalStore((s) => s.visible); - - return ( - { - close(); - }} - afterClose={() => { - reset(); - }} - > - - - ); -}; - -const availableTypes: ActivityLogStreamType[] = ['vector_http', 'logstash_http']; - -const ModalContent = () => { - const { LL } = useI18nContext(); - - const closeModal = useCreateActivityLogStreamModalStore((s) => s.close, shallow); - const openCreateLogstash = useLogstashHttpStreamCEModalStore((s) => s.open, shallow); - const openCreateVector = useVectorHttpStreamCEModal((s) => s.open, shallow); - - const [currentStreamType, setStreamType] = - useState('vector_http'); - - return ( - <> -
- {availableTypes.map((streamType) => { - const active = streamType === currentStreamType; - return ( -
{ - setStreamType(streamType); - }} - > - -

{activityLogStreamTypeToLabel(streamType)}

-
- ); - })} -
-
-
- - ); -}; diff --git a/web/src/pages/settings/components/ActivityLogStreamSettings/modals/CreateActivityLogStreamModal/store.tsx b/web/src/pages/settings/components/ActivityLogStreamSettings/modals/CreateActivityLogStreamModal/store.tsx deleted file mode 100644 index f7ad2092b..000000000 --- a/web/src/pages/settings/components/ActivityLogStreamSettings/modals/CreateActivityLogStreamModal/store.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import { createWithEqualityFn } from 'zustand/traditional'; - -const defaults: StoreValues = { - visible: false, -}; - -export const useCreateActivityLogStreamModalStore = createWithEqualityFn( - (set) => ({ - ...defaults, - open: () => set({ visible: true }), - close: () => set({ visible: false }), - reset: () => set(defaults), - }), - Object.is, -); - -type Store = StoreValues & StoreMethods; - -type StoreValues = { - visible: boolean; -}; - -type StoreMethods = { - open: () => void; - close: () => void; - reset: () => void; -}; diff --git a/web/src/pages/settings/components/ActivityLogStreamSettings/modals/CreateActivityLogStreamModal/style.scss b/web/src/pages/settings/components/ActivityLogStreamSettings/modals/CreateActivityLogStreamModal/style.scss deleted file mode 100644 index 4e1bb2332..000000000 --- a/web/src/pages/settings/components/ActivityLogStreamSettings/modals/CreateActivityLogStreamModal/style.scss +++ /dev/null @@ -1,60 +0,0 @@ -#create-activity-log-stream-modal { - .activity-log-stream-types { - display: flex; - flex-flow: column; - row-gap: var(--spacing-xs); - - .stream-type { - display: flex; - flex-flow: row; - align-items: center; - column-gap: var(--spacing-xs); - min-height: 30px; - border: 1px solid var(--border-primary); - padding: var(--spacing-xs) var(--spacing-s); - border-radius: 10px; - cursor: pointer; - user-select: none; - transition-property: border-color; - - @include animate-standard; - - &:not(.active) { - &:hover { - border-color: var(--border-separator); - } - } - - &.active { - border-color: var(--surface-main-primary); - } - - &.active, - &:hover { - .label { - color: var(--text-body-primary); - } - } - - .label { - color: var(--text-body-secondary); - transition-property: color; - @include typography(app-modal-1); - @include animate-standard; - } - } - } - - .controls { - display: flex; - flex-flow: row; - align-items: center; - justify-content: flex-start; - column-gap: var(--spacing-xs); - padding-top: var(--spacing-m); - - .btn { - width: 100%; - } - } -} diff --git a/web/src/pages/settings/components/ActivityLogStreamSettings/modals/LogStashHttpStreamCEModal/LogStashHttpStreamCEModal.tsx b/web/src/pages/settings/components/ActivityLogStreamSettings/modals/LogStashHttpStreamCEModal/LogStashHttpStreamCEModal.tsx deleted file mode 100644 index b983c4426..000000000 --- a/web/src/pages/settings/components/ActivityLogStreamSettings/modals/LogStashHttpStreamCEModal/LogStashHttpStreamCEModal.tsx +++ /dev/null @@ -1,237 +0,0 @@ -import { zodResolver } from '@hookform/resolvers/zod'; -import { useMutation } from '@tanstack/react-query'; -import type { AxiosError } from 'axios'; -import { useCallback, useMemo } from 'react'; -import { type SubmitHandler, useForm } from 'react-hook-form'; -import { z } from 'zod'; -import { shallow } from 'zustand/shallow'; - -import { useI18nContext } from '../../../../../../i18n/i18n-react'; -import { FormInput } from '../../../../../../shared/defguard-ui/components/Form/FormInput/FormInput'; -import { Button } from '../../../../../../shared/defguard-ui/components/Layout/Button/Button'; -import { ButtonStyleVariant } from '../../../../../../shared/defguard-ui/components/Layout/Button/types'; -import { ModalWithTitle } from '../../../../../../shared/defguard-ui/components/Layout/modals/ModalWithTitle/ModalWithTitle'; -import { isPresent } from '../../../../../../shared/defguard-ui/utils/isPresent'; -import useApi from '../../../../../../shared/hooks/useApi'; -import { useToaster } from '../../../../../../shared/hooks/useToaster'; -import queryClient from '../../../../../../shared/query-client'; -import type { - ActivityLogStreamLogstashHttp, - ActivityLogStreamType, -} from '../../../../../../shared/types'; -import { removeEmptyStrings } from '../../../../../../shared/utils/removeEmptyStrings'; -import { trimObjectStrings } from '../../../../../../shared/utils/trimObjectStrings'; -import { activityLogStreamTypeToLabel } from '../../utils/activityLogStreamToLabel'; -import { useLogstashHttpStreamCEModalStore } from './store'; - -export const LogStashHttpStreamCEModal = () => { - const { LL } = useI18nContext(); - const localLL = LL.settingsPage.activityLogStreamSettings.modals.logstash; - - const [close, reset] = useLogstashHttpStreamCEModalStore( - (s) => [s.close, s.reset], - shallow, - ); - const [isOpen, isEdit] = useLogstashHttpStreamCEModalStore((s) => [ - s.visible, - isPresent(s.initStreamData), - ]); - - const title = isEdit ? localLL.modify() : localLL.create(); - - return ( - { - close(); - }} - afterClose={() => { - reset(); - }} - > - - - ); -}; - -const ModalContent = () => { - const { LL } = useI18nContext(); - const localLL = LL.settingsPage.activityLogStreamSettings; - const formLabels = LL.settingsPage.activityLogStreamSettings.modals.shared.formLabels; - const { - activityLogStream: { createActivityLogStream, modifyActivityLogStream }, - } = useApi(); - const close = useLogstashHttpStreamCEModalStore((s) => s.close, shallow); - const initialData = useLogstashHttpStreamCEModalStore((s) => s.initStreamData); - const toaster = useToaster(); - - const onError = useCallback( - (e: AxiosError) => { - toaster.error(LL.messages.error()); - console.error(e); - }, - [LL.messages, toaster], - ); - - const { mutateAsync: createStreamMutation } = useMutation({ - mutationFn: createActivityLogStream, - onSuccess: () => { - toaster.success( - localLL.messages.destinationCrud.create({ - destination: activityLogStreamTypeToLabel('logstash_http'), - }), - ); - void queryClient.invalidateQueries({ - queryKey: ['activity_stream'], - }); - close(); - }, - onError, - }); - - const { mutateAsync: modifyStreamMutation } = useMutation({ - mutationFn: modifyActivityLogStream, - onSuccess: () => { - toaster.success( - localLL.messages.destinationCrud.modify({ - destination: activityLogStreamTypeToLabel('logstash_http'), - }), - ); - void queryClient.invalidateQueries({ - queryKey: ['activity_stream'], - }); - close(); - }, - onError, - }); - - const isEdit = isPresent(initialData); - - const schema = useMemo( - () => - z.object({ - name: z.string().trim().min(1, LL.form.error.required()), - url: z - .string() - .trim() - .min(1, LL.form.error.required()) - .url(LL.form.error.urlInvalid()), - username: z.string().trim(), - password: z.string().trim(), - cert: z.string().trim(), - }), - [LL.form.error], - ); - - type FormFields = z.infer; - - const defaultValues = useMemo((): FormFields => { - if (isPresent(initialData)) { - const { name, config } = initialData; - const { cert, url, password, username } = config; - return { - name: name, - cert: cert ?? '', - password: password ?? '', - username: username ?? '', - url, - }; - } - - return { - cert: '', - name: '', - url: '', - password: '', - username: '', - }; - }, [initialData]); - - const { - handleSubmit, - control, - resetField, - formState: { isSubmitting }, - } = useForm({ - defaultValues, - mode: 'all', - resolver: zodResolver(schema), - }); - - const handleValidSubmit: SubmitHandler = async (values) => { - const { name, ...config } = removeEmptyStrings(trimObjectStrings(values)); - const streamType: ActivityLogStreamType = 'logstash_http'; - - const logstashConfig: ActivityLogStreamLogstashHttp = config; - - if (isEdit) { - await modifyStreamMutation({ - id: initialData.id, - stream_type: streamType, - stream_config: logstashConfig, - name, - }); - } else { - await createStreamMutation({ - stream_type: streamType, - stream_config: logstashConfig, - name, - }); - } - }; - - return ( -
- - - { - resetField('username', { defaultValue: '' }); - }} - /> - { - resetField('password', { defaultValue: '' }); - }} - /> - { - resetField('cert', { defaultValue: '' }); - }} - /> - -
-
- - ); -}; diff --git a/web/src/pages/settings/components/ActivityLogStreamSettings/modals/LogStashHttpStreamCEModal/store.tsx b/web/src/pages/settings/components/ActivityLogStreamSettings/modals/LogStashHttpStreamCEModal/store.tsx deleted file mode 100644 index 3c0881588..000000000 --- a/web/src/pages/settings/components/ActivityLogStreamSettings/modals/LogStashHttpStreamCEModal/store.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import { createWithEqualityFn } from 'zustand/traditional'; - -import type { - ActivityLogStream, - ActivityLogStreamLogstashHttp, -} from '../../../../../../shared/types'; - -const defaults: StoreValues = { - visible: false, -}; - -export const useLogstashHttpStreamCEModalStore = createWithEqualityFn( - (set) => ({ - ...defaults, - open: (vals) => { - if (vals) { - if (vals?.stream_type !== 'logstash_http') { - throw Error( - 'Opened Logstash Http CE modal with wrong activity log stream type config', - ); - } - const initData: ModifyData = { - config: vals.config as ActivityLogStreamLogstashHttp, - id: vals.id, - name: vals.name, - }; - set({ ...vals, visible: true, initStreamData: initData }); - } - set({ visible: true }); - }, - close: () => set({ visible: false }), - reset: () => set(defaults), - }), - Object.is, -); - -type Store = StoreValues & StoreMethods; - -type ModifyData = { - id: number; - name: string; - config: ActivityLogStreamLogstashHttp; -}; - -type StoreValues = { - visible: boolean; - initStreamData?: ModifyData; -}; - -type StoreMethods = { - open: (activityLogStream?: ActivityLogStream) => void; - close: () => void; - reset: () => void; -}; diff --git a/web/src/pages/settings/components/ActivityLogStreamSettings/modals/VectorHttpStreamCEModal/VectorHttpStreamCEModal.tsx b/web/src/pages/settings/components/ActivityLogStreamSettings/modals/VectorHttpStreamCEModal/VectorHttpStreamCEModal.tsx deleted file mode 100644 index 51acd555b..000000000 --- a/web/src/pages/settings/components/ActivityLogStreamSettings/modals/VectorHttpStreamCEModal/VectorHttpStreamCEModal.tsx +++ /dev/null @@ -1,223 +0,0 @@ -import { zodResolver } from '@hookform/resolvers/zod'; -import { useMutation } from '@tanstack/react-query'; -import type { AxiosError } from 'axios'; -import { useCallback, useMemo } from 'react'; -import { type SubmitHandler, useForm } from 'react-hook-form'; -import { z } from 'zod'; -import { shallow } from 'zustand/shallow'; - -import { useI18nContext } from '../../../../../../i18n/i18n-react'; -import { FormInput } from '../../../../../../shared/defguard-ui/components/Form/FormInput/FormInput'; -import { Button } from '../../../../../../shared/defguard-ui/components/Layout/Button/Button'; -import { ButtonStyleVariant } from '../../../../../../shared/defguard-ui/components/Layout/Button/types'; -import { ModalWithTitle } from '../../../../../../shared/defguard-ui/components/Layout/modals/ModalWithTitle/ModalWithTitle'; -import { isPresent } from '../../../../../../shared/defguard-ui/utils/isPresent'; -import useApi from '../../../../../../shared/hooks/useApi'; -import { useToaster } from '../../../../../../shared/hooks/useToaster'; -import queryClient from '../../../../../../shared/query-client'; -import { removeEmptyStrings } from '../../../../../../shared/utils/removeEmptyStrings'; -import { trimObjectStrings } from '../../../../../../shared/utils/trimObjectStrings'; -import { activityLogStreamTypeToLabel } from '../../utils/activityLogStreamToLabel'; -import { useVectorHttpStreamCEModal } from './store'; - -export const VectorHttpStreamCEModal = () => { - const { LL } = useI18nContext(); - const localLL = LL.settingsPage.activityLogStreamSettings.modals.vector; - const isOpen = useVectorHttpStreamCEModal((s) => s.visible); - const [close, reset] = useVectorHttpStreamCEModal((s) => [s.close, s.reset], shallow); - const isEdit = useVectorHttpStreamCEModal((s) => isPresent(s.initStreamData)); - - const title = isEdit ? localLL.modify() : localLL.create(); - - return ( - { - close(); - }} - afterClose={() => { - reset(); - }} - > - - - ); -}; - -const ModalContent = () => { - const closeModal = useVectorHttpStreamCEModal((s) => s.close, shallow); - const [isEdit, initialData] = useVectorHttpStreamCEModal((s) => [ - s.edit, - s.initStreamData, - ]); - - const { LL } = useI18nContext(); - const localLL = LL.settingsPage.activityLogStreamSettings; - const formLabels = LL.settingsPage.activityLogStreamSettings.modals.shared.formLabels; - const toaster = useToaster(); - - const { - activityLogStream: { createActivityLogStream, modifyActivityLogStream }, - } = useApi(); - - const schema = useMemo( - () => - z.object({ - name: z.string().trim().min(1, LL.form.error.required()), - url: z - .string() - .trim() - .min(1, LL.form.error.required()) - .url(LL.form.error.urlInvalid()), - username: z.string().trim(), - password: z.string().trim(), - cert: z.string().trim(), - }), - [LL.form.error], - ); - - type FormFields = z.infer; - - const defaultValues = useMemo((): FormFields => { - if (isEdit && isPresent(initialData)) { - return { - name: initialData.name ?? '', - url: initialData.config.url, - username: initialData.config.username ?? '', - password: initialData.config.password ?? '', - cert: initialData.config.cert ?? '', - }; - } - return { - name: '', - password: '', - url: '', - username: '', - cert: '', - }; - }, [initialData, isEdit]); - - const { handleSubmit, control, resetField } = useForm({ - defaultValues, - resolver: zodResolver(schema), - mode: 'all', - }); - - const handleSuccess = useCallback(() => { - closeModal(); - void queryClient.invalidateQueries({ - queryKey: ['activity_stream'], - }); - }, [closeModal]); - - const handleError = useCallback( - (e: AxiosError) => { - toaster.error(LL.messages.error()); - console.error(e); - }, - [LL.messages, toaster], - ); - - const { mutateAsync: modifyMutation } = useMutation({ - mutationFn: modifyActivityLogStream, - onError: handleError, - onSuccess: () => { - toaster.success( - localLL.messages.destinationCrud.modify({ - destination: activityLogStreamTypeToLabel('vector_http'), - }), - ); - handleSuccess(); - }, - }); - - const { mutateAsync: createMutation } = useMutation({ - mutationFn: createActivityLogStream, - onError: handleError, - onSuccess: () => { - toaster.success( - localLL.messages.destinationCrud.create({ - destination: activityLogStreamTypeToLabel('vector_http'), - }), - ); - handleSuccess(); - }, - }); - - const handleValidSubmit: SubmitHandler = async (values) => { - // prepare output - const { name, ...config } = removeEmptyStrings(trimObjectStrings(values)); - - if (isPresent(initialData)) { - await modifyMutation({ - id: initialData.id, - name, - stream_type: 'vector_http', - stream_config: config, - }); - } else { - await createMutation({ - name, - stream_config: config, - stream_type: 'vector_http', - }); - } - }; - - return ( -
- - - { - resetField('username', { defaultValue: '' }); - }} - /> - { - resetField('password', { defaultValue: '' }); - }} - /> - { - resetField('cert', { defaultValue: '' }); - }} - /> - -
-
- - ); -}; diff --git a/web/src/pages/settings/components/ActivityLogStreamSettings/modals/VectorHttpStreamCEModal/store.tsx b/web/src/pages/settings/components/ActivityLogStreamSettings/modals/VectorHttpStreamCEModal/store.tsx deleted file mode 100644 index d501a12f6..000000000 --- a/web/src/pages/settings/components/ActivityLogStreamSettings/modals/VectorHttpStreamCEModal/store.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import { createWithEqualityFn } from 'zustand/traditional'; - -import { isPresent } from '../../../../../../shared/defguard-ui/utils/isPresent'; -import type { ActivityLogStreamVectorHttp } from '../../../../../../shared/types'; - -type ModifyData = { - id: number; - name: string; - config: ActivityLogStreamVectorHttp; -}; - -const defaults: StoreValues = { - visible: false, - edit: false, - initStreamData: undefined, -}; - -export const useVectorHttpStreamCEModal = createWithEqualityFn( - (set) => ({ - ...defaults, - open: (initData) => { - if (isPresent(initData)) { - set({ visible: true, edit: true, initStreamData: initData }); - } - set({ visible: true, edit: true }); - }, - close: () => set({ visible: false }), - reset: () => set(defaults), - }), - Object.is, -); - -type Store = StoreValues & StoreMethods; - -type StoreValues = { - visible: boolean; - edit: boolean; - initStreamData?: ModifyData; -}; - -type StoreMethods = { - open: (values?: ModifyData) => void; - close: () => void; - reset: () => void; -}; diff --git a/web/src/pages/settings/components/ActivityLogStreamSettings/style.scss b/web/src/pages/settings/components/ActivityLogStreamSettings/style.scss deleted file mode 100644 index 6de1352a1..000000000 --- a/web/src/pages/settings/components/ActivityLogStreamSettings/style.scss +++ /dev/null @@ -1,82 +0,0 @@ -@mixin row-config { - width: 100%; - display: grid; - grid-template-columns: 1fr 1fr 40px; - grid-template-rows: 1fr; - align-items: center; -} - -#activity-log-stream-settings { - .controls { - display: flex; - flex-flow: row; - align-items: center; - justify-content: flex-end; - padding-bottom: var(--spacing-m); - - column-gap: var(--spacing-s); - - .btn.add { - svg { - path { - fill: var(--surface-icon-secondary); - } - } - } - } - - .list-header { - padding-bottom: var(--spacing-xs); - - .list-headers { - box-sizing: border-box; - padding: 0 var(--spacing-xs); - user-select: none; - @include row-config(); - - & > :nth-child(4) { - justify-self: center; - } - - .cell.empty { - display: none; - } - } - } - - .list { - ul { - list-style: none; - display: flex; - flex-flow: column; - row-gap: var(--spacing-xs); - } - } - - .list-item { - border: 1px solid var(--border-primary); - border-radius: 10px; - min-height: 50px; - box-sizing: border-box; - padding: 5px var(--spacing-xs); - background-color: var(--surface-default-modal); - display: grid; - grid-template-rows: 1fr; - grid-template-columns: 1fr 40px; - align-items: center; - box-shadow: 0px 5px 7.5px 0px rgba(0, 0, 0, 0); - transition-property: box-shadow, border; - - @include animate-standard(); - @include row-config(); - - &:hover { - border-color: var(--border-separator); - box-shadow: 0px 5px 7.5px 0px rgba(0, 0, 0, 0.08); - } - - p { - @include typography(app-input); - } - } -} diff --git a/web/src/pages/settings/components/ActivityLogStreamSettings/utils/activityLogStreamToLabel.ts b/web/src/pages/settings/components/ActivityLogStreamSettings/utils/activityLogStreamToLabel.ts deleted file mode 100644 index 08bd5e8f5..000000000 --- a/web/src/pages/settings/components/ActivityLogStreamSettings/utils/activityLogStreamToLabel.ts +++ /dev/null @@ -1,18 +0,0 @@ -import type { - ActivityLogStream, - ActivityLogStreamType, -} from '../../../../../shared/types'; - -export const activityLogStreamToLabel = (value: ActivityLogStream): string => - activityLogStreamTypeToLabel(value.stream_type); - -export const activityLogStreamTypeToLabel = (value: ActivityLogStreamType): string => { - switch (value) { - case 'vector_http': - return 'Vector'; - case 'logstash_http': - return 'Logstash'; - default: - return 'Unknown'; - } -}; diff --git a/web/src/pages/settings/components/EnterpriseSettings/EnterpriseSettings.tsx b/web/src/pages/settings/components/EnterpriseSettings/EnterpriseSettings.tsx deleted file mode 100644 index 1d26113f8..000000000 --- a/web/src/pages/settings/components/EnterpriseSettings/EnterpriseSettings.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import parse from 'html-react-parser'; - -import { useI18nContext } from '../../../../i18n/i18n-react'; -import { BigInfoBox } from '../../../../shared/defguard-ui/components/Layout/BigInfoBox/BigInfoBox'; -import { useAppStore } from '../../../../shared/hooks/store/useAppStore'; -import { useSettingsPage } from '../../hooks/useSettingsPage'; -import { EnterpriseForm } from './components/EnterpriseForm'; - -export const EnterpriseSettings = () => { - const enterpriseInfo = useSettingsPage((s) => s.enterpriseInfo); - const { LL } = useI18nContext(); - const localLL = LL.settingsPage.enterpriseOnly; - const appInfo = useAppStore((s) => s.appInfo); - - if (!appInfo) return null; - - return ( - <> - {appInfo.license_info.is_enterprise_free && ( -
- -
- )} -
- {!appInfo.license_info.enterprise && ( -
-
-
-

{localLL.title()}

- {enterpriseInfo?.expired &&

{localLL.currentExpired()}

} -

- {localLL.subtitle()}{' '} - - {localLL.website()} - - . -

-
-
-
- )} -
- -
-
-
- - ); -}; diff --git a/web/src/pages/settings/components/EnterpriseSettings/components/EnterpriseForm.tsx b/web/src/pages/settings/components/EnterpriseSettings/components/EnterpriseForm.tsx deleted file mode 100644 index d7df932de..000000000 --- a/web/src/pages/settings/components/EnterpriseSettings/components/EnterpriseForm.tsx +++ /dev/null @@ -1,95 +0,0 @@ -import './styles.scss'; - -import { useMutation, useQueryClient } from '@tanstack/react-query'; -import type { AxiosError } from 'axios'; -import parse from 'html-react-parser'; - -import { useI18nContext } from '../../../../../i18n/i18n-react'; -import { Helper } from '../../../../../shared/defguard-ui/components/Layout/Helper/Helper'; -import { LabeledCheckbox } from '../../../../../shared/defguard-ui/components/Layout/LabeledCheckbox/LabeledCheckbox'; -import { useAppStore } from '../../../../../shared/hooks/store/useAppStore'; -import useApi from '../../../../../shared/hooks/useApi'; -import { useToaster } from '../../../../../shared/hooks/useToaster'; -import { MutationKeys } from '../../../../../shared/mutations'; -import { QueryKeys } from '../../../../../shared/queries'; - -export const EnterpriseForm = () => { - const { LL } = useI18nContext(); - const toaster = useToaster(); - const { - settings: { patchEnterpriseSettings }, - } = useApi(); - - const settings = useAppStore((state) => state.enterprise_settings); - - const queryClient = useQueryClient(); - - const { mutate, isPending: isLoading } = useMutation({ - mutationKey: [MutationKeys.EDIT_SETTINGS], - mutationFn: patchEnterpriseSettings, - onSuccess: () => { - void queryClient.invalidateQueries({ - queryKey: [QueryKeys.FETCH_ENTERPRISE_SETTINGS], - }); - toaster.success(LL.settingsPage.messages.editSuccess()); - }, - onError: (err: AxiosError) => { - toaster.error(LL.messages.error()); - console.error(err); - }, - }); - - if (!settings) return null; - - return ( -
-
-
-

{LL.settingsPage.enterprise.header()}

- {LL.settingsPage.enterprise.helper()} -
-
-
-
- - mutate({ admin_device_management: !settings.admin_device_management }) - } - /> - - {parse(LL.settingsPage.enterprise.fields.deviceManagement.helper())} - -
-
- - mutate({ only_client_activation: !settings.only_client_activation }) - } - /> - - {parse(LL.settingsPage.enterprise.fields.manualConfig.helper())} - -
-
- - mutate({ disable_all_traffic: !settings.disable_all_traffic }) - } - /> - - {parse(LL.settingsPage.enterprise.fields.disableAllTraffic.helper())} - -
-
-
- ); -}; diff --git a/web/src/pages/settings/components/EnterpriseSettings/components/styles.scss b/web/src/pages/settings/components/EnterpriseSettings/components/styles.scss deleted file mode 100644 index bcd4df160..000000000 --- a/web/src/pages/settings/components/EnterpriseSettings/components/styles.scss +++ /dev/null @@ -1,19 +0,0 @@ -@use '@scssutils' as *; - -#enterprise-settings { - & > .card { - display: flex; - flex-flow: column; - row-gap: 16px; - - @include media-breakpoint-up(lg) { - padding: 16px 15px; - } - - & > .helper-row { - display: flex; - align-items: center; - gap: 10px; - } - } -} diff --git a/web/src/pages/settings/components/GlobalSettings/GlobalSettings.tsx b/web/src/pages/settings/components/GlobalSettings/GlobalSettings.tsx deleted file mode 100644 index c70be705c..000000000 --- a/web/src/pages/settings/components/GlobalSettings/GlobalSettings.tsx +++ /dev/null @@ -1,3 +0,0 @@ -import { GlobalSettingsForm } from './components/GlobalSettingsForm/GlobalSettingsForm'; - -export const GlobalSettings = () => ; diff --git a/web/src/pages/settings/components/GlobalSettings/components/GlobalSettingsForm/GlobalSettingsForm.tsx b/web/src/pages/settings/components/GlobalSettings/components/GlobalSettingsForm/GlobalSettingsForm.tsx deleted file mode 100644 index b5b3c2fbc..000000000 --- a/web/src/pages/settings/components/GlobalSettings/components/GlobalSettingsForm/GlobalSettingsForm.tsx +++ /dev/null @@ -1,249 +0,0 @@ -import './styles.scss'; - -import { zodResolver } from '@hookform/resolvers/zod'; -import { useMutation, useQueryClient } from '@tanstack/react-query'; -import parse from 'html-react-parser'; -import { useMemo } from 'react'; -import { type SubmitHandler, useForm } from 'react-hook-form'; -import { useBreakpoint } from 'use-breakpoint'; - -import { useI18nContext } from '../../../../../../i18n/i18n-react'; -import IconCheckmarkWhite from '../../../../../../shared/components/svg/IconCheckmarkWhite'; -import { deviceBreakpoints } from '../../../../../../shared/constants'; -import { FormCheckBox } from '../../../../../../shared/defguard-ui/components/Form/FormCheckBox/FormCheckBox'; -import { FormInput } from '../../../../../../shared/defguard-ui/components/Form/FormInput/FormInput'; -import { Button } from '../../../../../../shared/defguard-ui/components/Layout/Button/Button'; -import { - ButtonSize, - ButtonStyleVariant, -} from '../../../../../../shared/defguard-ui/components/Layout/Button/types'; -import { Helper } from '../../../../../../shared/defguard-ui/components/Layout/Helper/Helper'; -import SvgIconX from '../../../../../../shared/defguard-ui/components/svg/IconX'; -import useApi from '../../../../../../shared/hooks/useApi'; -import { useToaster } from '../../../../../../shared/hooks/useToaster'; -import { externalLink } from '../../../../../../shared/links'; -import { QueryKeys } from '../../../../../../shared/queries'; -import { invalidateMultipleQueries } from '../../../../../../shared/utils/invalidateMultipleQueries'; -import { useSettingsPage } from '../../../../hooks/useSettingsPage'; -import { type GlobalSettingsFormFields, globalSettingsSchema } from '../../types'; -import { LicenseSettings } from '../LicenseSettings/LicenseSettings'; - -const defaultSettings = { - instance_name: 'Defguard', - main_logo_url: '/svg/logo-defguard-white.svg', - nav_logo_url: '/svg/defguard-nav-logo.svg', - openid_enabled: false, - wireguard_enabled: false, - worker_enabled: false, - webhooks_enabled: false, - license: '', -}; - -export const GlobalSettingsForm = () => { - const { LL } = useI18nContext(); - const toaster = useToaster(); - const { - settings: { patchSettings }, - } = useApi(); - - const settings = useSettingsPage((state) => state.settings); - - const queryClient = useQueryClient(); - const { breakpoint } = useBreakpoint(deviceBreakpoints); - - const { mutate, isPending: isLoading } = useMutation({ - mutationFn: patchSettings, - onSuccess: () => { - const keys = [ - [QueryKeys.FETCH_ENTERPRISE_INFO], - [QueryKeys.FETCH_ENTERPRISE_STATUS], - [QueryKeys.FETCH_SETTINGS], - [QueryKeys.FETCH_APP_INFO], - [QueryKeys.FETCH_ESSENTIAL_SETTINGS], - ]; - invalidateMultipleQueries(queryClient, keys); - toaster.success(LL.settingsPage.messages.editSuccess()); - }, - onError: (err) => { - toaster.error(LL.messages.error()); - console.error(err); - }, - }); - - const zodSchema = useMemo(() => globalSettingsSchema(LL), [LL]); - - const defaultValues = useMemo((): GlobalSettingsFormFields => { - return { - instance_name: settings?.instance_name ?? '', - main_logo_url: - settings?.main_logo_url === defaultSettings.main_logo_url - ? '' - : (settings?.main_logo_url ?? ''), - nav_logo_url: - settings?.nav_logo_url === defaultSettings.nav_logo_url - ? '' - : (settings?.nav_logo_url ?? ''), - openid_enabled: settings?.openid_enabled ?? false, - wireguard_enabled: settings?.wireguard_enabled ?? false, - worker_enabled: settings?.worker_enabled ?? false, - webhooks_enabled: settings?.webhooks_enabled ?? false, - license: settings?.license ?? '', - }; - }, [settings]); - - const { control, handleSubmit, setValue } = useForm({ - defaultValues, - mode: 'all', - resolver: zodResolver(zodSchema), - }); - - const onSubmit: SubmitHandler = (submitted) => { - mutate(submitted); - }; - - if (!settings) return null; - - return ( -
-
-

{LL.settingsPage.tabs.global()}

-
-
-
-
-
-
-
-
-
-

{LL.settingsPage.instanceBranding.header()}

- - {parse( - LL.settingsPage.instanceBranding.helper({ - documentationLink: externalLink.gitbook.base, - }), - )} - -
-
- - - {LL.settingsPage.instanceBranding.form.fields.mainLogoUrl.helper()} - - } - label={LL.settingsPage.instanceBranding.form.fields.mainLogoUrl.label()} - controller={{ control, name: 'main_logo_url' }} - placeholder={LL.settingsPage.instanceBranding.form.fields.mainLogoUrl.placeholder()} - required - /> - - {LL.settingsPage.instanceBranding.form.fields.navLogoUrl.helper()} - - } - label={LL.settingsPage.instanceBranding.form.fields.navLogoUrl.label()} - controller={{ control, name: 'nav_logo_url' }} - placeholder={LL.settingsPage.instanceBranding.form.fields.navLogoUrl.placeholder()} - required - /> -
-
-
-

{LL.settingsPage.modulesVisibility.header()}

- - {parse( - LL.settingsPage.modulesVisibility.helper({ - documentationLink: externalLink.gitbook.base, - }), - )} - -
-
- - - - -
-
-
-
- -
-
-
-
- ); -}; diff --git a/web/src/pages/settings/components/GlobalSettings/components/GlobalSettingsForm/styles.scss b/web/src/pages/settings/components/GlobalSettings/components/GlobalSettingsForm/styles.scss deleted file mode 100644 index e69de29bb..000000000 diff --git a/web/src/pages/settings/components/GlobalSettings/components/LicenseSettings/LicenseSettings.tsx b/web/src/pages/settings/components/GlobalSettings/components/LicenseSettings/LicenseSettings.tsx deleted file mode 100644 index 3fa9acfab..000000000 --- a/web/src/pages/settings/components/GlobalSettings/components/LicenseSettings/LicenseSettings.tsx +++ /dev/null @@ -1,122 +0,0 @@ -import './styles.scss'; - -import { useMemo } from 'react'; -import type { Control } from 'react-hook-form'; - -import { useI18nContext } from '../../../../../../i18n/i18n-react'; -import { FormInput } from '../../../../../../shared/defguard-ui/components/Form/FormInput/FormInput'; -import { ActivityIcon } from '../../../../../../shared/defguard-ui/components/icons/ActivityIcon/ActivityIcon'; -import { ActivityIconVariant } from '../../../../../../shared/defguard-ui/components/icons/ActivityIcon/types'; -import { ExpandableCard } from '../../../../../../shared/defguard-ui/components/Layout/ExpandableCard/ExpandableCard'; -import { Helper } from '../../../../../../shared/defguard-ui/components/Layout/Helper/Helper'; -import { Label } from '../../../../../../shared/defguard-ui/components/Layout/Label/Label'; -import { isPresent } from '../../../../../../shared/defguard-ui/utils/isPresent'; -import { useAppStore } from '../../../../../../shared/hooks/store/useAppStore'; -import { useSettingsPage } from '../../../../hooks/useSettingsPage'; -import type { GlobalSettingsFormFields } from '../../types'; - -export const LicenseSettings = ({ - control, -}: { - control: Control; -}) => { - const { LL } = useI18nContext(); - const appInfo = useAppStore((s) => s.appInfo); - const enterpriseInfo = useSettingsPage((s) => s.enterpriseInfo); - - const licenseIconVariant = useMemo(() => { - if ( - isPresent(enterpriseInfo) && - !enterpriseInfo.limits_exceeded && - !enterpriseInfo.expired - ) { - return ActivityIconVariant.CONNECTED; - } - return ActivityIconVariant.ERROR; - }, [enterpriseInfo]); - - const statusText = useMemo(() => { - if (!isPresent(enterpriseInfo)) { - return LL.settingsPage.license.licenseInfo.status.noLicense(); - } - if (enterpriseInfo.expired) { - return LL.settingsPage.license.licenseInfo.status.expired(); - } - if (appInfo?.license_info.any_limit_exceeded) { - return LL.settingsPage.license.licenseInfo.status.limitsExceeded(); - } - return LL.settingsPage.license.licenseInfo.status.active(); - }, [ - LL.settingsPage.license.licenseInfo.status, - appInfo?.license_info.any_limit_exceeded, - enterpriseInfo, - ]); - - return ( -
-
-

{LL.settingsPage.license.header()}

- -

{LL.settingsPage.license.helpers.enterpriseHeader.text()}

- - {LL.settingsPage.license.helpers.enterpriseHeader.link()} - -
-
-
- - - {isPresent(enterpriseInfo) ? ( -
-
- -
- -

{statusText}

- {enterpriseInfo.subscription ? ( - - {LL.settingsPage.license.licenseInfo.fields.status.subscriptionHelper()} - - ) : null} -
-
-
- -
-

- {enterpriseInfo.subscription - ? LL.settingsPage.license.licenseInfo.types.subscription.label() - : LL.settingsPage.license.licenseInfo.types.offline.label()} -

- - {enterpriseInfo.subscription - ? LL.settingsPage.license.licenseInfo.types.subscription.helper() - : LL.settingsPage.license.licenseInfo.types.offline.helper()} - -
-
-
- -

- {enterpriseInfo.valid_until - ? new Date(enterpriseInfo.valid_until).toLocaleString() - : '-'} -

-
-
- ) : ( -

- {LL.settingsPage.license.licenseInfo.status.noLicense()} -

- )} -
-
-
- ); -}; diff --git a/web/src/pages/settings/components/GlobalSettings/components/LicenseSettings/styles.scss b/web/src/pages/settings/components/GlobalSettings/components/LicenseSettings/styles.scss deleted file mode 100644 index 3d1ca3037..000000000 --- a/web/src/pages/settings/components/GlobalSettings/components/LicenseSettings/styles.scss +++ /dev/null @@ -1,56 +0,0 @@ -#license-settings { - & > .card { - display: flex; - flex-flow: column; - row-gap: 16px; - - @include media-breakpoint-up(lg) { - padding: 16px 15px; - } - - .loading-license-info { - display: flex; - justify-content: center; - align-items: center; - } - } -} - -.controls > .header { - display: flex; - justify-content: space-between; - align-items: center; - gap: 10px; -} - -#license-info { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); - gap: 16px; - - & > div { - display: flex; - flex-flow: column; - row-gap: 8px; - align-items: center; - - & > .with-helper { - display: flex; - gap: 5px; - } - } - - .license-status { - display: flex; - align-items: center; - gap: 5px; - } -} - -#no-license { - text-align: center; -} - -#license-not-required { - text-align: center; -} diff --git a/web/src/pages/settings/components/GlobalSettings/types.ts b/web/src/pages/settings/components/GlobalSettings/types.ts deleted file mode 100644 index 85875150b..000000000 --- a/web/src/pages/settings/components/GlobalSettings/types.ts +++ /dev/null @@ -1,20 +0,0 @@ -import z from 'zod'; -import type { TranslationFunctions } from '../../../../i18n/i18n-types'; - -export const globalSettingsSchema = (LL: TranslationFunctions) => - z.object({ - main_logo_url: z.string().trim(), - nav_logo_url: z.string().trim(), - instance_name: z - .string() - .trim() - .min(3, LL.form.error.minimumLength()) - .max(64, LL.form.error.maximumLength()), - openid_enabled: z.boolean(), - wireguard_enabled: z.boolean(), - worker_enabled: z.boolean(), - webhooks_enabled: z.boolean(), - license: z.string().trim().optional(), - }); - -export type GlobalSettingsFormFields = z.infer>; diff --git a/web/src/pages/settings/components/LdapSettings/LdapSettings.tsx b/web/src/pages/settings/components/LdapSettings/LdapSettings.tsx deleted file mode 100644 index 40e1109eb..000000000 --- a/web/src/pages/settings/components/LdapSettings/LdapSettings.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import './style.scss'; - -import parse from 'html-react-parser'; - -import { useI18nContext } from '../../../../i18n/i18n-react'; -import { BigInfoBox } from '../../../../shared/defguard-ui/components/Layout/BigInfoBox/BigInfoBox'; -import { useAppStore } from '../../../../shared/hooks/store/useAppStore'; -import { LdapSettingsForm } from './components/LdapSettingsForm'; - -export const LdapSettings = () => { - const { LL } = useI18nContext(); - const appInfo = useAppStore((s) => s.appInfo); - - if (!appInfo) return null; - return ( - <> - {appInfo.license_info.is_enterprise_free && ( -
- -
- )} - - - ); -}; diff --git a/web/src/pages/settings/components/LdapSettings/components/LdapConnectionTest.tsx b/web/src/pages/settings/components/LdapSettings/components/LdapConnectionTest.tsx deleted file mode 100644 index 4346999e6..000000000 --- a/web/src/pages/settings/components/LdapSettings/components/LdapConnectionTest.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import { useMutation } from '@tanstack/react-query'; - -import { useI18nContext } from '../../../../../i18n/i18n-react'; -import { Button } from '../../../../../shared/defguard-ui/components/Layout/Button/Button'; -import { - ButtonSize, - ButtonStyleVariant, -} from '../../../../../shared/defguard-ui/components/Layout/Button/types'; -import useApi from '../../../../../shared/hooks/useApi'; -import { useToaster } from '../../../../../shared/hooks/useToaster'; - -export const LdapConnectionTest = () => { - const { LL } = useI18nContext(); - const localLL = LL.settingsPage.ldapSettings.test; - const { - settings: { testLdapSettings }, - } = useApi(); - - const toaster = useToaster(); - - const { isPending: isLoading, mutate } = useMutation({ - mutationFn: testLdapSettings, - onSuccess: () => { - toaster.success(localLL.messages.success()); - }, - onError: () => { - toaster.error(localLL.messages.error()); - }, - }); - - return ( -
- ); -}; diff --git a/web/src/pages/settings/components/LdapSettings/style.scss b/web/src/pages/settings/components/LdapSettings/style.scss deleted file mode 100644 index 4c2eff9d8..000000000 --- a/web/src/pages/settings/components/LdapSettings/style.scss +++ /dev/null @@ -1,7 +0,0 @@ -@use '@scssutils' as *; - -#ldap-settings { - .message-box-spacer { - padding-bottom: var(--spacing-s); - } -} diff --git a/web/src/pages/settings/components/NotificationSettings/NotificationSettings.tsx b/web/src/pages/settings/components/NotificationSettings/NotificationSettings.tsx deleted file mode 100644 index 5ef0c5049..000000000 --- a/web/src/pages/settings/components/NotificationSettings/NotificationSettings.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { useAppStore } from '../../../../shared/hooks/store/useAppStore'; -import { NotificationsForm } from './components/NotificationSettingsForm'; - -export const NotificationSettings = () => { - const appInfo = useAppStore((s) => s.appInfo); - - if (!appInfo) return null; - - return ; -}; diff --git a/web/src/pages/settings/components/NotificationSettings/components/GatewayNotificationsForm.tsx b/web/src/pages/settings/components/NotificationSettings/components/GatewayNotificationsForm.tsx deleted file mode 100644 index 68ef4899f..000000000 --- a/web/src/pages/settings/components/NotificationSettings/components/GatewayNotificationsForm.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import parse from 'html-react-parser'; -import type { Control } from 'react-hook-form'; - -import { useI18nContext } from '../../../../../i18n/i18n-react'; -import { FormCheckBox } from '../../../../../shared/defguard-ui/components/Form/FormCheckBox/FormCheckBox'; -import { FormInput } from '../../../../../shared/defguard-ui/components/Form/FormInput/FormInput'; -import { Helper } from '../../../../../shared/defguard-ui/components/Layout/Helper/Helper'; -import { useAppStore } from '../../../../../shared/hooks/store/useAppStore'; -import type { FormFields } from './NotificationSettingsForm'; - -export const GatewayNotificationsForm = ({ - control, - isLoading, -}: { - control: Control; - isLoading: boolean; -}) => { - const { LL } = useI18nContext(); - const localLL = LL.settingsPage.gatewayNotifications; - const smtpConfigured = useAppStore((s) => Boolean(s.appInfo?.smtp_enabled)); - - return ( -
-

{localLL.sections.gateway()}

-
-
- - - {parse(localLL.form.fields.disconnectNotificationsEnabled.help())} - -
-
- - - {parse(localLL.form.fields.reconnectNotificationsEnabled.help())} - -
-
- {parse(localLL.form.fields.inactivityThreshold.help())} - } - disabled={isLoading || !smtpConfigured} - required - /> -
- ); -}; diff --git a/web/src/pages/settings/components/NotificationSettings/components/NotificationSettingsForm.tsx b/web/src/pages/settings/components/NotificationSettings/components/NotificationSettingsForm.tsx deleted file mode 100644 index ed6293251..000000000 --- a/web/src/pages/settings/components/NotificationSettings/components/NotificationSettingsForm.tsx +++ /dev/null @@ -1,132 +0,0 @@ -import { zodResolver } from '@hookform/resolvers/zod'; -import { useMutation, useQueryClient } from '@tanstack/react-query'; -import parse from 'html-react-parser'; -import { useMemo } from 'react'; -import { type SubmitHandler, useForm } from 'react-hook-form'; -import { z } from 'zod'; - -import { useI18nContext } from '../../../../../i18n/i18n-react'; -import IconCheckmarkWhite from '../../../../../shared/components/svg/IconCheckmarkWhite'; -import { Button } from '../../../../../shared/defguard-ui/components/Layout/Button/Button'; -import { - ButtonSize, - ButtonStyleVariant, -} from '../../../../../shared/defguard-ui/components/Layout/Button/types'; -import { Helper } from '../../../../../shared/defguard-ui/components/Layout/Helper/Helper'; -import { MessageBox } from '../../../../../shared/defguard-ui/components/Layout/MessageBox/MessageBox'; -import { useAppStore } from '../../../../../shared/hooks/store/useAppStore'; -import useApi from '../../../../../shared/hooks/useApi'; -import { useToaster } from '../../../../../shared/hooks/useToaster'; -import { QueryKeys } from '../../../../../shared/queries'; -import type { ApiError } from '../../../../../shared/types'; -import { invalidateMultipleQueries } from '../../../../../shared/utils/invalidateMultipleQueries'; -import { useSettingsPage } from '../../../hooks/useSettingsPage'; -import { GatewayNotificationsForm } from './GatewayNotificationsForm'; - -export type FormFields = { - gateway_disconnect_notifications_enabled: boolean; - gateway_disconnect_notifications_inactivity_threshold: number; - gateway_disconnect_notifications_reconnect_notification_enabled: boolean; -}; - -export const NotificationsForm = () => { - const { LL } = useI18nContext(); - const localLL = LL.settingsPage.gatewayNotifications; - const settings = useSettingsPage((state) => state.settings); - - const toaster = useToaster(); - - const { - settings: { patchSettings }, - } = useApi(); - - const smtpConfigured = useAppStore((s) => Boolean(s.appInfo?.smtp_enabled)); - - const queryClient = useQueryClient(); - - const { mutate, isPending: isLoading } = useMutation({ - mutationFn: patchSettings, - onSuccess: () => { - invalidateMultipleQueries(queryClient, [ - [QueryKeys.FETCH_APP_INFO], - [QueryKeys.FETCH_SETTINGS], - ]); - toaster.success(LL.settingsPage.messages.editSuccess()); - }, - onError: (err: ApiError) => { - toaster.error(err.response?.data.msg || LL.messages.error()); - console.error(err); - }, - }); - - const zodSchema = useMemo( - () => - z.object({ - gateway_disconnect_notifications_enabled: z.boolean(), - gateway_disconnect_notifications_inactivity_threshold: z - .number() - .min(0, LL.form.error.minimumValue({ value: 0 })), - gateway_disconnect_notifications_reconnect_notification_enabled: z.boolean(), - }), - [LL.form], - ); - - const defaultValues = useMemo(() => { - const res: FormFields = { - gateway_disconnect_notifications_enabled: - settings?.gateway_disconnect_notifications_enabled ?? false, - gateway_disconnect_notifications_inactivity_threshold: - settings?.gateway_disconnect_notifications_inactivity_threshold ?? 5, - gateway_disconnect_notifications_reconnect_notification_enabled: - settings?.gateway_disconnect_notifications_reconnect_notification_enabled ?? - false, - }; - return res; - }, [settings]); - - const { control, handleSubmit } = useForm({ - defaultValues, - mode: 'all', - resolver: zodResolver(zodSchema), - }); - - const onSubmit: SubmitHandler = (data) => { - mutate(data); - }; - - if (!settings) return null; - - return ( -
-
-
-

{localLL.header()}

- {parse(localLL.helper())} -
-
-
-
-
-
-
- - -
-
-
-
- ); -}; diff --git a/web/src/pages/settings/components/OpenIdSettings/OpenIdSettings.tsx b/web/src/pages/settings/components/OpenIdSettings/OpenIdSettings.tsx deleted file mode 100644 index d20bce2f1..000000000 --- a/web/src/pages/settings/components/OpenIdSettings/OpenIdSettings.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import './style.scss'; - -import parse from 'html-react-parser'; - -import { useI18nContext } from '../../../../i18n/i18n-react'; -import { BigInfoBox } from '../../../../shared/defguard-ui/components/Layout/BigInfoBox/BigInfoBox'; -import { useAppStore } from '../../../../shared/hooks/store/useAppStore'; -import { OpenIdSettingsForm } from './components/OpenIdSettingsForm'; - -export const OpenIdSettings = () => { - const { LL } = useI18nContext(); - const appInfo = useAppStore((s) => s.appInfo); - - if (!appInfo) return null; - - return ( - <> - {appInfo.license_info.is_enterprise_free && ( -
- -
- )} -
- -
- - ); -}; diff --git a/web/src/pages/settings/components/OpenIdSettings/components/DirectorySyncSettings.tsx b/web/src/pages/settings/components/OpenIdSettings/components/DirectorySyncSettings.tsx deleted file mode 100644 index a859fbbfc..000000000 --- a/web/src/pages/settings/components/OpenIdSettings/components/DirectorySyncSettings.tsx +++ /dev/null @@ -1,268 +0,0 @@ -import './style.scss'; - -import parse from 'html-react-parser'; -import { useMemo, useState } from 'react'; -import { useFormContext, useWatch } from 'react-hook-form'; - -import { useI18nContext } from '../../../../../i18n/i18n-react'; -import { FormInput } from '../../../../../shared/defguard-ui/components/Form/FormInput/FormInput'; -import { FormSelect } from '../../../../../shared/defguard-ui/components/Form/FormSelect/FormSelect'; -import { Helper } from '../../../../../shared/defguard-ui/components/Layout/Helper/Helper'; -import { LabeledCheckbox } from '../../../../../shared/defguard-ui/components/Layout/LabeledCheckbox/LabeledCheckbox'; -import SvgIconDownload from '../../../../../shared/defguard-ui/components/svg/IconDownload'; -import { titleCase } from '../../../../../shared/utils/titleCase'; -import { SUPPORTED_SYNC_PROVIDERS } from './SupportedProviders'; - -export const DirsyncSettings = ({ isLoading }: { isLoading: boolean }) => { - const { LL } = useI18nContext(); - const localLL = LL.settingsPage.openIdSettings; - const [googleServiceAccountFileName, setGoogleServiceAccountFileName] = useState< - string | null - >(null); - - const { control, setValue } = useFormContext(); - - const userBehaviorOptions = useMemo( - () => [ - { - value: 'keep', - label: localLL.form.selects.behavior.keep(), - key: 1, - }, - { - value: 'disable', - label: localLL.form.selects.behavior.disable(), - key: 2, - }, - { - value: 'delete', - label: localLL.form.selects.behavior.delete(), - key: 3, - }, - ], - [localLL.form.selects.behavior], - ); - - const syncTarget = useMemo( - () => [ - { - value: 'all', - label: localLL.form.selects.synchronize.all(), - key: 1, - }, - { - value: 'users', - label: localLL.form.selects.synchronize.users(), - key: 2, - }, - { - value: 'groups', - label: localLL.form.selects.synchronize.groups(), - key: 3, - }, - ], - [localLL.form.selects.synchronize], - ); - - const providerName = useWatch({ control, name: 'name' }) as string; - const dirsyncEnabled: boolean = useWatch({ - control, - name: 'directory_sync_enabled', - }) as boolean; - const showDirsync = SUPPORTED_SYNC_PROVIDERS.includes(providerName ?? ''); - - return ( -
-
-

{localLL.form.directory_sync_settings.title()}

- {localLL.form.directory_sync_settings.helper()} -
-
- {showDirsync ? ( - <> -
- {/* FIXME: Really buggy when using the controller, investigate why */} - setValue('directory_sync_enabled', val)} - // controller={{ control, name: 'directory_sync_enabled' }} - /> -
- ({ - key: val, - displayValue: titleCase(val), - })} - labelExtras={ - {parse(localLL.form.labels.sync_target.helper())} - } - disabled={isLoading} - /> - {parse(localLL.form.labels.sync_interval.helper())} - } - disabled={isLoading} - /> - ({ - key: val, - displayValue: titleCase(val), - })} - labelExtras={ - {parse(localLL.form.labels.user_behavior.helper())} - } - disabled={isLoading} - /> - ({ - key: val, - displayValue: titleCase(val), - })} - labelExtras={ - {parse(localLL.form.labels.admin_behavior.helper())} - } - disabled={isLoading} - /> - {providerName === 'Microsoft' ? ( - {parse(localLL.form.labels.group_match.helper())} - } - required={false} - > - ) : null} - {providerName === 'Okta' ? ( - <> - {parse(localLL.form.labels.okta_client_id.helper())} - } - required={dirsyncEnabled} - /> - {parse(localLL.form.labels.okta_client_key.helper())} - } - required={dirsyncEnabled} - /> - - ) : null} - {providerName === 'JumpCloud' ? ( - {parse(localLL.form.labels.jumpcloud_api_key.helper())} - } - required={dirsyncEnabled} - type="password" - /> - ) : null} - {providerName === 'Google' ? ( - <> - {parse(localLL.form.labels.admin_email.helper())} - } - required={dirsyncEnabled} - /> - - {parse(localLL.form.labels.service_account_used.helper())} - - } - disabled={isLoading} - required={dirsyncEnabled} - /> -
-
- - - {localLL.form.labels.service_account_key_file.helper()} - -
-
- { - const file = e.target.files?.[0]; - if (file) { - const reader = new FileReader(); - reader.onload = (e) => { - if (e?.target?.result) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - const key = JSON.parse(e.target?.result as string); - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - setValue('google_service_account_key', key.private_key); - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - setValue('google_service_account_email', key.client_email); - setGoogleServiceAccountFileName(file.name); - } - }; - reader.readAsText(file); - } - }} - disabled={isLoading} - /> -
- {' '} -

- {googleServiceAccountFileName - ? `${localLL.form.labels.service_account_key_file.uploaded()}: ${googleServiceAccountFileName}` - : localLL.form.labels.service_account_key_file.uploadPrompt()} -

-
-
-
- - ) : null} - - ) : ( -

- {localLL.form.directory_sync_settings.notSupported()} -

- )} -
-
- ); -}; diff --git a/web/src/pages/settings/components/OpenIdSettings/components/OpenIdGeneralSettings.tsx b/web/src/pages/settings/components/OpenIdSettings/components/OpenIdGeneralSettings.tsx deleted file mode 100644 index 4722287be..000000000 --- a/web/src/pages/settings/components/OpenIdSettings/components/OpenIdGeneralSettings.tsx +++ /dev/null @@ -1,82 +0,0 @@ -import './style.scss'; - -import parse from 'html-react-parser'; -import { useMemo } from 'react'; -import { useFormContext, useWatch } from 'react-hook-form'; - -import { useI18nContext } from '../../../../../i18n/i18n-react'; -import { FormSelect } from '../../../../../shared/defguard-ui/components/Form/FormSelect/FormSelect'; -import { Helper } from '../../../../../shared/defguard-ui/components/Layout/Helper/Helper'; -import { LabeledCheckbox } from '../../../../../shared/defguard-ui/components/Layout/LabeledCheckbox/LabeledCheckbox'; -import { - type SelectOption, - SelectSizeVariant, -} from '../../../../../shared/defguard-ui/components/Layout/Select/types'; -import type { UsernameHandling } from './OpenIdSettingsForm'; - -export const OpenIdGeneralSettings = ({ isLoading }: { isLoading: boolean }) => { - const { LL } = useI18nContext(); - const localLL = LL.settingsPage.openIdSettings; - const { control, setValue } = useFormContext(); - const create_account = useWatch({ - control, - name: 'create_account', - }) as boolean; - - const options: SelectOption[] = useMemo( - () => [ - { - value: 'RemoveForbidden', - label: localLL.general.usernameHandling.options.remove(), - key: 0, - }, - { - value: 'ReplaceForbidden', - label: localLL.general.usernameHandling.options.replace(), - key: 1, - }, - { - value: 'PruneEmailDomain', - label: localLL.general.usernameHandling.options.prune_email(), - key: 2, - }, - ], - [localLL.general.usernameHandling.options], - ); - - return ( -
-
-

{localLL.general.title()}

- {parse(localLL.general.helper())} -
-
- {/* FIXME: Really buggy when using the controller, investigate why */} - { - setValue('create_account', e); - }} - disabled={isLoading} - /> - {localLL.general.createAccount.helper()} -
- {localLL.general.usernameHandling.helper()}} - disabled={isLoading} - /> -
- ); -}; diff --git a/web/src/pages/settings/components/OpenIdSettings/components/OpenIdProviderSettings.tsx b/web/src/pages/settings/components/OpenIdSettings/components/OpenIdProviderSettings.tsx deleted file mode 100644 index 4fddbe135..000000000 --- a/web/src/pages/settings/components/OpenIdSettings/components/OpenIdProviderSettings.tsx +++ /dev/null @@ -1,167 +0,0 @@ -import './style.scss'; - -import parse from 'html-react-parser'; -import { useCallback, useMemo } from 'react'; -import { useFormContext, useWatch } from 'react-hook-form'; - -import { useI18nContext } from '../../../../../i18n/i18n-react'; -import { FormInput } from '../../../../../shared/defguard-ui/components/Form/FormInput/FormInput'; -import { FormSelect } from '../../../../../shared/defguard-ui/components/Form/FormSelect/FormSelect'; -import { Helper } from '../../../../../shared/defguard-ui/components/Layout/Helper/Helper'; -import { - type SelectOption, - type SelectSelectedValue, - SelectSizeVariant, -} from '../../../../../shared/defguard-ui/components/Layout/Select/types'; - -export const OpenIdProviderSettings = ({ isLoading }: { isLoading: boolean }) => { - const { LL } = useI18nContext(); - const localLL = LL.settingsPage.openIdSettings; - const { control, setValue } = useFormContext(); - - const options: SelectOption[] = useMemo( - () => [ - { - value: '', - label: localLL.form.none(), - key: 0, - }, - { - value: 'Google', - label: 'Google', - key: 1, - }, - { - value: 'Microsoft', - label: 'Microsoft', - key: 2, - }, - { - value: 'Okta', - label: 'Okta', - key: 3, - }, - { - value: 'JumpCloud', - label: 'JumpCloud', - key: 4, - }, - { - value: 'Custom', - label: localLL.form.custom(), - key: 5, - }, - ], - [localLL.form], - ); - - const renderSelected = useCallback( - (selected: string): SelectSelectedValue => { - const option = options.find((o) => o.value === selected); - - if (!option) throw Error("Selected value doesn't exist"); - - return { - key: option.key, - displayValue: option.label, - }; - }, - [options], - ); - - const getProviderUrl = useCallback(({ name }: { name: string }): string | null => { - switch (name) { - case 'Google': - return 'https://accounts.google.com'; - case 'Microsoft': - return `https://login.microsoftonline.com//v2.0`; - case 'Okta': - return ``; - case 'JumpCloud': - return 'https://oauth.id.jumpcloud.com/'; - default: - return null; - } - }, []); - - const getProviderDisplayName = useCallback( - ({ name }: { name: string }): string | null => { - switch (name) { - case 'Google': - return 'Google'; - case 'Microsoft': - return 'Microsoft'; - case 'Okta': - return 'Okta'; - case 'JumpCloud': - return 'JumpCloud'; - default: - return null; - } - }, - [], - ); - - const providerName = useWatch({ - control, - name: 'name', - }) as string; - - const handleProviderChange = useCallback( - (val: string) => { - setValue('base_url', getProviderUrl({ name: val }) ?? ''); - setValue('display_name', getProviderDisplayName({ name: val }) ?? ''); - }, - [getProviderUrl, getProviderDisplayName, setValue], - ); - - return ( -
-
-

{localLL.form.title()}

- {parse(localLL.form.helper())} -
- handleProviderChange(res)} - label={localLL.form.labels.provider.label()} - labelExtras={{parse(localLL.form.labels.provider.helper())}} - disabled={isLoading} - /> - {parse(localLL.form.labels.base_url.helper())}} - disabled={providerName === 'Google' || providerName === 'JumpCloud' || isLoading} - required - /> - {parse(localLL.form.labels.client_id.helper())}} - disabled={isLoading} - required - /> - {parse(localLL.form.labels.client_secret.helper())}} - required - type="password" - disabled={isLoading} - /> - {parse(localLL.form.labels.display_name.helper())}} - disabled={isLoading || providerName !== 'Custom'} - /> -
- ); -}; diff --git a/web/src/pages/settings/components/OpenIdSettings/components/OpenIdSettingsForm.tsx b/web/src/pages/settings/components/OpenIdSettings/components/OpenIdSettingsForm.tsx deleted file mode 100644 index ec3215c2b..000000000 --- a/web/src/pages/settings/components/OpenIdSettings/components/OpenIdSettingsForm.tsx +++ /dev/null @@ -1,312 +0,0 @@ -import './style.scss'; - -import { zodResolver } from '@hookform/resolvers/zod'; -import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; -import { useCallback, useEffect, useMemo } from 'react'; -import { FormProvider, type SubmitHandler, useForm } from 'react-hook-form'; -import ReactMarkdown from 'react-markdown'; -import { z } from 'zod'; - -import { useI18nContext } from '../../../../../i18n/i18n-react'; -import IconCheckmarkWhite from '../../../../../shared/components/svg/IconCheckmarkWhite'; -import SvgIconX from '../../../../../shared/components/svg/IconX'; -import { Button } from '../../../../../shared/defguard-ui/components/Layout/Button/Button'; -import { - ButtonSize, - ButtonStyleVariant, -} from '../../../../../shared/defguard-ui/components/Layout/Button/types'; -import { MessageBox } from '../../../../../shared/defguard-ui/components/Layout/MessageBox/MessageBox'; -import { MessageBoxType } from '../../../../../shared/defguard-ui/components/Layout/MessageBox/types'; -import { useAppStore } from '../../../../../shared/hooks/store/useAppStore'; -import useApi from '../../../../../shared/hooks/useApi'; -import { useToaster } from '../../../../../shared/hooks/useToaster'; -import { QueryKeys } from '../../../../../shared/queries'; -import { invalidateMultipleQueries } from '../../../../../shared/utils/invalidateMultipleQueries'; -import { DirsyncSettings } from './DirectorySyncSettings'; -import { OpenIdGeneralSettings } from './OpenIdGeneralSettings'; -import { OpenIdProviderSettings } from './OpenIdProviderSettings'; -import { SUPPORTED_SYNC_PROVIDERS } from './SupportedProviders'; - -export type UsernameHandling = - | 'RemoveForbidden' - | 'ReplaceForbidden' - | 'PruneEmailDomain'; - -export const OpenIdSettingsForm = () => { - const { LL } = useI18nContext(); - const localLL = LL.settingsPage.openIdSettings; - const queryClient = useQueryClient(); - const enterpriseEnabled = useAppStore((s) => s.appInfo?.license_info.enterprise); - const { - settings: { testDirsync }, - } = useApi(); - - const { - settings: { fetchOpenIdProviders, addOpenIdProvider, deleteOpenIdProvider }, - } = useApi(); - - const { isLoading, data: openidData } = useQuery({ - queryFn: fetchOpenIdProviders, - queryKey: [QueryKeys.FETCH_OPENID_PROVIDERS], - refetchOnMount: true, - refetchOnWindowFocus: false, - retry: false, - enabled: enterpriseEnabled, - }); - - const toaster = useToaster(); - - const { mutate } = useMutation({ - mutationFn: addOpenIdProvider, - onSuccess: () => { - invalidateMultipleQueries(queryClient, [ - [QueryKeys.FETCH_OPENID_PROVIDERS], - [QueryKeys.FETCH_APP_INFO], - ]); - toaster.success(LL.settingsPage.messages.editSuccess()); - }, - onError: (error) => { - toaster.error(LL.messages.error()); - console.error(error); - }, - }); - - const { mutate: deleteProvider } = useMutation({ - mutationFn: deleteOpenIdProvider, - onSuccess: () => { - invalidateMultipleQueries(queryClient, [ - [QueryKeys.FETCH_OPENID_PROVIDERS], - [QueryKeys.FETCH_APP_INFO], - ]); - toaster.success(LL.settingsPage.messages.editSuccess()); - }, - onError: (error) => { - toaster.error(LL.messages.error()); - console.error(error); - }, - }); - - const schema = useMemo( - () => - z - .object({ - name: z.string().optional(), - base_url: z - .string() - .url(LL.form.error.invalid()) - .min(1, LL.form.error.required()), - client_id: z.string().min(1, LL.form.error.required()), - client_secret: z.string().min(1, LL.form.error.required()), - display_name: z.string(), - admin_email: z.string(), - google_service_account_email: z.string(), - google_service_account_key: z.string(), - directory_sync_enabled: z.boolean(), - directory_sync_interval: z.number().min(60, LL.form.error.invalid()), - directory_sync_user_behavior: z.enum(['keep', 'disable', 'delete']), - directory_sync_admin_behavior: z.enum(['keep', 'disable', 'delete']), - directory_sync_target: z.enum(['all', 'users', 'groups']), - create_account: z.boolean(), - username_handling: z.string(), - okta_private_jwk: z.string(), - okta_dirsync_client_id: z.string(), - directory_sync_group_match: z.string(), - jumpcloud_api_key: z.string(), - }) - .superRefine((val, ctx) => { - if (val.name === '') { - ctx.addIssue({ - code: z.ZodIssueCode.custom, - message: LL.form.error.required(), - path: ['name'], - }); - } - - if (val.directory_sync_enabled && val.base_url.includes('okta')) { - if (val.okta_dirsync_client_id.length === 0) { - ctx.addIssue({ - code: z.ZodIssueCode.custom, - message: LL.form.error.required(), - path: ['okta_dirsync_client_id'], - }); - } - } - - if (val.directory_sync_enabled && val.name === 'Google') { - if (val.admin_email.length === 0) { - ctx.addIssue({ - code: z.ZodIssueCode.custom, - message: LL.form.error.required(), - path: ['admin_email'], - }); - } - - if (val.google_service_account_email.length === 0) { - ctx.addIssue({ - code: z.ZodIssueCode.custom, - message: LL.form.error.required(), - path: ['google_service_account_email'], - }); - } - } - - if (val.directory_sync_enabled && val.name === 'JumpCloud') { - if (val.jumpcloud_api_key.length === 0) { - ctx.addIssue({ - code: z.ZodIssueCode.custom, - message: LL.form.error.required(), - }); - } - } - }), - [LL.form.error], - ); - - type FormFields = z.infer; - - const defaultValues = useMemo((): FormFields => { - let defaults: FormFields = { - name: '', - base_url: '', - client_id: '', - client_secret: '', - display_name: '', - admin_email: '', - google_service_account_email: '', - google_service_account_key: '', - directory_sync_enabled: false, - directory_sync_interval: 600, - directory_sync_user_behavior: 'keep', - directory_sync_admin_behavior: 'keep', - directory_sync_target: 'all', - create_account: false, - okta_private_jwk: '', - okta_dirsync_client_id: '', - directory_sync_group_match: '', - username_handling: 'RemoveForbidden', - jumpcloud_api_key: '', - }; - - if (openidData) { - if (openidData.provider) { - defaults = { - ...defaults, - ...openidData.provider, - }; - - if (Array.isArray(openidData.provider.directory_sync_group_match)) { - defaults = { - ...defaults, - - directory_sync_group_match: - openidData.provider.directory_sync_group_match.length > 0 - ? openidData.provider.directory_sync_group_match.join(',') - : '', - }; - } - } - - defaults = { - ...defaults, - ...openidData.settings, - }; - } - - return defaults; - }, [openidData]); - - const formControl = useForm({ - resolver: zodResolver(schema), - defaultValues, - mode: 'all', - }); - - const { handleSubmit, reset } = formControl; - - // Make sure the form data is fresh - useEffect(() => { - reset(defaultValues); - }, [defaultValues, reset]); - - const handleValidSubmit: SubmitHandler = (data) => { - mutate({ id: openidData?.provider?.id ?? 0, ...data, name: data.name ?? '' }); - }; - - const handleDeleteProvider = useCallback(() => { - if (openidData?.provider) { - deleteProvider(openidData?.provider.name); - } - }, [openidData, deleteProvider]); - - const showDirsync = SUPPORTED_SYNC_PROVIDERS.includes(openidData?.provider?.name ?? ''); - - return ( - <> -
-

{localLL.heading()}

-
- {showDirsync && ( - - )} -
-
- -
-
- - {localLL.form.documentation()} - - - -
-
- -
-
-
- - ); -}; diff --git a/web/src/pages/settings/components/OpenIdSettings/components/SupportedProviders.ts b/web/src/pages/settings/components/OpenIdSettings/components/SupportedProviders.ts deleted file mode 100644 index 9bff03ecc..000000000 --- a/web/src/pages/settings/components/OpenIdSettings/components/SupportedProviders.ts +++ /dev/null @@ -1 +0,0 @@ -export const SUPPORTED_SYNC_PROVIDERS = ['Google', 'Microsoft', 'Okta', 'JumpCloud']; diff --git a/web/src/pages/settings/components/OpenIdSettings/components/style.scss b/web/src/pages/settings/components/OpenIdSettings/components/style.scss deleted file mode 100644 index 017b6c9d4..000000000 --- a/web/src/pages/settings/components/OpenIdSettings/components/style.scss +++ /dev/null @@ -1,91 +0,0 @@ -@use '@scssutils' as *; - -#openid-settings, -#dirsync-settings, -#general-settings { - .select { - padding-bottom: var(--spacing-s); - } - - .checkbox-padding { - padding-bottom: var(--spacing-s); - } - - #sync-not-supported { - text-align: center; - margin: var(--spacing-s) 0; - color: var(--text-body-tertiary); - } - - .hidden-input { - display: none; - } - - .file-upload-container { - background-color: var(--surface-frame-bg); - display: flex; - justify-content: center; - align-items: center; - border: 2px dashed var(--border-primary); - border-radius: 10px; - position: relative; - padding: var(--spacing-s); - z-index: 0; - } - - .file-upload-container.dragging { - border-color: var(--border-secondary); - } - - .file-upload { - position: absolute; - top: 0; - left: 0; - opacity: 0; - width: 100%; - height: 100%; - z-index: 1; - cursor: pointer; - } - - .select-container { - margin-bottom: 0; - } - - .upload-label { - display: flex; - align-items: center; - gap: var(--spacing-xs); - cursor: pointer; - - p { - color: var(--text-body-secondary); - font-size: 15px; - } - - & > svg { - width: 20px; - height: 20px; - transform: rotate(180deg); - } - } - - .test-connection { - display: flex; - width: 100%; - justify-content: flex-end; - } - - .labeled-checkbox { - padding-bottom: var(--spacing-s); - } -} - -.loader { - display: flex; - justify-content: center; - align-items: center; - height: 100%; - grid-column: 1 / -1; - min-height: 300px; -} diff --git a/web/src/pages/settings/components/OpenIdSettings/style.scss b/web/src/pages/settings/components/OpenIdSettings/style.scss deleted file mode 100644 index 475fc4d20..000000000 --- a/web/src/pages/settings/components/OpenIdSettings/style.scss +++ /dev/null @@ -1,23 +0,0 @@ -.settings-card { - position: relative; -} - -.enterprise-info { - position: absolute; - inset: 0; - display: flex; - justify-content: center; - align-items: center; - backdrop-filter: blur(1.5px); - z-index: 100; - border-radius: 15px; - border-top-left-radius: 0px; - background-color: rgba(#f9f9f9, 0.75); -} - -.enterprise-info > div { - display: flex; - flex-direction: column; - align-items: center; - gap: 0.5rem; -} diff --git a/web/src/pages/settings/components/SmtpSettings/SmtpSettings.tsx b/web/src/pages/settings/components/SmtpSettings/SmtpSettings.tsx deleted file mode 100644 index ba1fb29ec..000000000 --- a/web/src/pages/settings/components/SmtpSettings/SmtpSettings.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import './styles.scss'; - -import { SmtpSettingsForm } from './components/SmtpSettingsForm/SmtpSettingsForm'; - -export const SmtpSettings = () => { - return ; -}; diff --git a/web/src/pages/settings/components/SmtpSettings/components/SmtpSettingsForm/SmtpSettingsForm.tsx b/web/src/pages/settings/components/SmtpSettings/components/SmtpSettingsForm/SmtpSettingsForm.tsx deleted file mode 100644 index 55db9997b..000000000 --- a/web/src/pages/settings/components/SmtpSettings/components/SmtpSettingsForm/SmtpSettingsForm.tsx +++ /dev/null @@ -1,263 +0,0 @@ -import './style.scss'; - -import { zodResolver } from '@hookform/resolvers/zod'; -import { useMutation, useQueryClient } from '@tanstack/react-query'; -import parse from 'html-react-parser'; -import { useCallback, useMemo } from 'react'; -import { type SubmitHandler, useForm } from 'react-hook-form'; -import { z } from 'zod'; - -import { useI18nContext } from '../../../../../../i18n/i18n-react'; -import IconCheckmarkWhite from '../../../../../../shared/components/svg/IconCheckmarkWhite'; -import { FormInput } from '../../../../../../shared/defguard-ui/components/Form/FormInput/FormInput'; -import { FormSelect } from '../../../../../../shared/defguard-ui/components/Form/FormSelect/FormSelect'; -import { Button } from '../../../../../../shared/defguard-ui/components/Layout/Button/Button'; -import { - ButtonSize, - ButtonStyleVariant, -} from '../../../../../../shared/defguard-ui/components/Layout/Button/types'; -import { Helper } from '../../../../../../shared/defguard-ui/components/Layout/Helper/Helper'; -import type { - SelectOption, - SelectSelectedValue, -} from '../../../../../../shared/defguard-ui/components/Layout/Select/types'; -import useApi from '../../../../../../shared/hooks/useApi'; -import { useToaster } from '../../../../../../shared/hooks/useToaster'; -import { patternValidEmail } from '../../../../../../shared/patterns'; -import { QueryKeys } from '../../../../../../shared/queries'; -import type { SettingsSMTP } from '../../../../../../shared/types'; -import { invalidateMultipleQueries } from '../../../../../../shared/utils/invalidateMultipleQueries'; -import { validateIpOrDomain } from '../../../../../../shared/validators'; -import { useSettingsPage } from '../../../../hooks/useSettingsPage'; -import { SmtpTestModal } from '../SmtpTest/SmtpTestModal'; -import { useSmtpTestModal } from '../SmtpTest/useSmtpTestModal'; - -type FormFields = { - smtp_server: string; - smtp_port: number; - smtp_user: string; - smtp_password: string; - smtp_sender: string; - smtp_encryption: string; -}; - -export const SmtpSettingsForm = () => { - const { LL } = useI18nContext(); - const localLL = LL.settingsPage.smtp; - - const settings = useSettingsPage((state) => state.settings); - const openSmtpTest = useSmtpTestModal((state) => state.open); - - const toaster = useToaster(); - - const { - settings: { patchSettings }, - } = useApi(); - - const queryClient = useQueryClient(); - - const { mutate, isPending: isLoading } = useMutation({ - mutationFn: patchSettings, - onSuccess: () => { - invalidateMultipleQueries(queryClient, [ - [QueryKeys.FETCH_APP_INFO], - [QueryKeys.FETCH_SETTINGS], - ]); - toaster.success(LL.settingsPage.messages.editSuccess()); - }, - onError: (err) => { - toaster.error(LL.messages.error()); - console.error(err); - }, - }); - - const encryptionOptions = useMemo( - (): SelectOption[] => [ - { - key: 1, - value: 'StartTls', - label: 'Start TLS', - }, - { - key: 2, - value: 'None', - label: 'None', - }, - { - key: 3, - value: 'ImplicitTls', - label: 'Implicit TLS', - }, - ], - [], - ); - - const renderSelectedEncryption = useCallback( - (selected: string): SelectSelectedValue => { - const option = encryptionOptions.find((o) => o.value === selected); - if (!option) throw Error("Selected value doesn't exist"); - return { - key: option.key, - displayValue: option.label, - }; - }, - [encryptionOptions], - ); - - const zodSchema = useMemo( - () => - z.object({ - smtp_server: z - .string() - .trim() - .min(1, LL.form.error.required()) - .refine( - (val) => (!val ? true : validateIpOrDomain(val, false, true)), - LL.form.error.endpoint(), - ), - smtp_port: z - .number({ - invalid_type_error: LL.form.error.required(), - }) - .max(65535, LL.form.error.portMax()), - smtp_password: z.string().trim(), - smtp_user: z.string().trim(), - smtp_sender: z - .string() - .trim() - .min(1, LL.form.error.required()) - .regex(patternValidEmail, LL.form.error.invalid()), - smtp_encryption: z.string().trim().min(1, LL.form.error.required()), - }), - [LL.form], - ); - - const defaultValues = useMemo(() => { - const res: FormFields = { - smtp_server: settings?.smtp_server ?? '', - smtp_port: settings?.smtp_port ?? 587, - smtp_password: settings?.smtp_password ?? '', - smtp_sender: settings?.smtp_sender ?? '', - smtp_user: settings?.smtp_user ?? '', - smtp_encryption: settings?.smtp_encryption ?? encryptionOptions[1].value, - }; - return res; - }, [settings, encryptionOptions]); - - const emptyValues: SettingsSMTP = useMemo( - () => ({ - smtp_server: '', - smtp_port: 587, - smtp_password: '', - smtp_sender: '', - smtp_user: '', - smtp_encryption: encryptionOptions[1].value, - }), - [encryptionOptions], - ); - - const { control, reset, handleSubmit } = useForm({ - defaultValues, - mode: 'all', - resolver: zodResolver(zodSchema), - }); - - const onSubmit: SubmitHandler = (data) => { - mutate(data); - }; - - const handleDeleteSubmit = useCallback(() => { - mutate(emptyValues); - reset(emptyValues); - }, [mutate, emptyValues, reset]); - - if (!settings) return null; - - return ( -
- -
-
-

{localLL.form.title()}

- {localLL.helper()} -
-
-
-
-
-
-
-
-

{localLL.form.sections.server()}

-
- - - - - {parse(localLL.form.fields.sender.helper())}} - label={localLL.form.fields.sender.label()} - controller={{ control, name: 'smtp_sender' }} - placeholder={localLL.form.fields.sender.placeholder()} - required - /> - -
-
-
-
- ); -}; diff --git a/web/src/pages/settings/components/SmtpSettings/components/SmtpSettingsForm/style.scss b/web/src/pages/settings/components/SmtpSettings/components/SmtpSettingsForm/style.scss deleted file mode 100644 index 42296ec03..000000000 --- a/web/src/pages/settings/components/SmtpSettings/components/SmtpSettingsForm/style.scss +++ /dev/null @@ -1,24 +0,0 @@ -@use '@scssutils' as *; - -#smtp-settings { - & > header { - & > .btn { - margin-left: auto; - } - - & > .controls { - display: flex; - gap: 10px; - } - } - form { - width: 100%; - display: flex; - flex-flow: column; - row-gap: 5px; - - & > * { - width: 100%; - } - } -} diff --git a/web/src/pages/settings/components/SmtpSettings/components/SmtpTest/SmtpTest.tsx b/web/src/pages/settings/components/SmtpSettings/components/SmtpTest/SmtpTest.tsx deleted file mode 100644 index ad2050366..000000000 --- a/web/src/pages/settings/components/SmtpSettings/components/SmtpTest/SmtpTest.tsx +++ /dev/null @@ -1,95 +0,0 @@ -import './style.scss'; - -import { zodResolver } from '@hookform/resolvers/zod'; -import { useMutation } from '@tanstack/react-query'; -import type { AxiosError } from 'axios'; -import { useMemo, useRef } from 'react'; -import { type SubmitHandler, useForm } from 'react-hook-form'; -import { z } from 'zod'; - -import { useI18nContext } from '../../../../../../i18n/i18n-react'; -import IconCheckmark from '../../../../../../shared/components/svg/IconCheckmark'; -import { FormInput } from '../../../../../../shared/defguard-ui/components/Form/FormInput/FormInput'; -import { Button } from '../../../../../../shared/defguard-ui/components/Layout/Button/Button'; -import { - ButtonSize, - ButtonStyleVariant, -} from '../../../../../../shared/defguard-ui/components/Layout/Button/types'; -import useApi from '../../../../../../shared/hooks/useApi'; -import { useToaster } from '../../../../../../shared/hooks/useToaster'; -import { patternValidEmail } from '../../../../../../shared/patterns'; -import type { TestMail } from '../../../../../../shared/types'; - -type SMTPError = AxiosError<{ error: string }>; - -export const SmtpTest = () => { - const submitRef = useRef(null); - const { LL } = useI18nContext(); - const toaster = useToaster(); - const { - mail: { sendTestMail }, - } = useApi(); - - const { mutate, isPending: isLoading } = useMutation({ - mutationFn: sendTestMail, - onSuccess: () => { - toaster.success(LL.settingsPage.smtp.testForm.controls.success()); - }, - onError: (err: SMTPError) => { - toaster.error(`${LL.settingsPage.smtp.testForm.controls.error()}`, { - subMessage: `${err.response?.data.error}`, - }); - console.error(err); - }, - }); - - const zodSchema = useMemo( - () => - z.object({ - to: z.string().trim().regex(patternValidEmail, LL.form.error.invalid()), - }), - [LL.form.error], - ); - - const { control: testControl, handleSubmit: handleTestSubmit } = useForm({ - defaultValues: { - to: '', - }, - resolver: zodResolver(zodSchema), - mode: 'all', - }); - - const onSubmit: SubmitHandler = (data) => { - mutate(data); - }; - - return ( -
-
-

{LL.settingsPage.smtp.testForm.title()}

-
-
- - - -
- ); -}; diff --git a/web/src/pages/settings/components/SmtpSettings/components/SmtpTest/SmtpTestModal.tsx b/web/src/pages/settings/components/SmtpSettings/components/SmtpTest/SmtpTestModal.tsx deleted file mode 100644 index 9f33fffda..000000000 --- a/web/src/pages/settings/components/SmtpSettings/components/SmtpTest/SmtpTestModal.tsx +++ /dev/null @@ -1,148 +0,0 @@ -import './style.scss'; - -import { zodResolver } from '@hookform/resolvers/zod'; -import { useMutation } from '@tanstack/react-query'; -import type { AxiosError } from 'axios'; -import { useMemo, useState } from 'react'; -import { type SubmitHandler, useForm } from 'react-hook-form'; -import { z } from 'zod'; -import { shallow } from 'zustand/shallow'; - -import { useI18nContext } from '../../../../../../i18n/i18n-react'; -import IconCheckmark from '../../../../../../shared/components/svg/IconCheckmark'; -import { FormInput } from '../../../../../../shared/defguard-ui/components/Form/FormInput/FormInput'; -import { Button } from '../../../../../../shared/defguard-ui/components/Layout/Button/Button'; -import { ButtonStyleVariant } from '../../../../../../shared/defguard-ui/components/Layout/Button/types'; -import { ModalWithTitle } from '../../../../../../shared/defguard-ui/components/Layout/modals/ModalWithTitle/ModalWithTitle'; -import useApi from '../../../../../../shared/hooks/useApi'; -import { patternValidEmail } from '../../../../../../shared/patterns'; -import type { TestMail } from '../../../../../../shared/types'; -import { useSmtpTestModal } from './useSmtpTestModal'; - -type SMTPError = AxiosError<{ error: string }>; - -const ModalContent = () => { - const { LL } = useI18nContext(); - const modal = useSmtpTestModal((s) => s); - const { - mail: { sendTestMail }, - } = useApi(); - - const [errorMessage, setErrorMessage] = useState(null); - - const { - mutate, - isPending: isLoading, - status, - reset: resetMutation, - } = useMutation({ - mutationFn: sendTestMail, - onError: (err: SMTPError) => { - if (err.response?.data.error) { - setErrorMessage(err.response?.data.error); - } - console.error(err); - }, - }); - - const zodSchema = useMemo( - () => - z.object({ - to: z.string().trim().regex(patternValidEmail, LL.form.error.invalid()), - }), - [LL.form.error], - ); - - const { control: testControl, handleSubmit: handleTestSubmit } = useForm({ - defaultValues: { - to: '', - }, - resolver: zodResolver(zodSchema), - mode: 'all', - }); - - const onSubmit: SubmitHandler = (data) => { - mutate(data); - }; - - return ( -
-
-

{LL.settingsPage.smtp.testForm.title()}

-
- {status === 'success' && ( -

{LL.settingsPage.smtp.testForm.success.message()}

- )} - {status === 'error' && ( -
-

{LL.settingsPage.smtp.testForm.error.message()}

-

- {LL.settingsPage.smtp.testForm.error.fullError({ - error: errorMessage || 'Unknown error', - })} -

-
- )} - {(status === 'idle' || status === 'pending') && ( - <> -

{LL.settingsPage.smtp.testForm.subtitle()}

-
- - - - )} -
-
-
- ); -}; - -export const SmtpTestModal = () => { - const isOpen = useSmtpTestModal((s) => s.visible); - const close = useSmtpTestModal((s) => s.close, shallow); - - return ( - { - close(); - }} - backdrop - disableClose - > - - - ); -}; diff --git a/web/src/pages/settings/components/SmtpSettings/components/SmtpTest/style.scss b/web/src/pages/settings/components/SmtpSettings/components/SmtpTest/style.scss deleted file mode 100644 index ac23a5607..000000000 --- a/web/src/pages/settings/components/SmtpSettings/components/SmtpTest/style.scss +++ /dev/null @@ -1,41 +0,0 @@ -@use '@scssutils' as *; - -#smtp-test-mail { - display: flex; - flex-direction: column; - gap: var(--spacing-s); - align-items: center; - - .hidden { - display: none; - } - - h2 { - color: var(--text-button-primary); - @include typography(app-body-1); - } - - p { - color: var(--text-button-primary); - @include typography(app-body-2); - text-align: center; - } - - .button-row { - display: flex; - gap: var(--spacing-xs); - justify-content: center; - width: 100%; - - button { - width: 100%; - } - } - - form { - width: 100%; - & > * { - width: 100%; - } - } -} diff --git a/web/src/pages/settings/components/SmtpSettings/components/SmtpTest/useSmtpTestModal.ts b/web/src/pages/settings/components/SmtpSettings/components/SmtpTest/useSmtpTestModal.ts deleted file mode 100644 index 828428432..000000000 --- a/web/src/pages/settings/components/SmtpSettings/components/SmtpTest/useSmtpTestModal.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { createWithEqualityFn } from 'zustand/traditional'; - -const defaults: StoreValues = { - visible: false, -}; - -export const useSmtpTestModal = createWithEqualityFn( - (set) => ({ - ...defaults, - open: () => set({ visible: true }), - close: () => set({ visible: false }), - reset: () => set(defaults), - }), - Object.is, -); - -type Store = StoreValues & StoreMethods; - -type StoreValues = { - visible: boolean; -}; - -type StoreMethods = { - open: () => void; - close: () => void; - reset: () => void; -}; diff --git a/web/src/pages/settings/components/SmtpSettings/styles.scss b/web/src/pages/settings/components/SmtpSettings/styles.scss deleted file mode 100644 index 007442690..000000000 --- a/web/src/pages/settings/components/SmtpSettings/styles.scss +++ /dev/null @@ -1 +0,0 @@ -@use '@scssutils' as *; diff --git a/web/src/pages/settings/hooks/useSettingsPage.tsx b/web/src/pages/settings/hooks/useSettingsPage.tsx deleted file mode 100644 index b8c4f7c41..000000000 --- a/web/src/pages/settings/hooks/useSettingsPage.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import { createWithEqualityFn } from 'zustand/traditional'; - -import type { EnterpriseInfo, Settings } from '../../../shared/types'; - -const defaultValues: StoreValues = { - settings: undefined, - enterpriseInfo: undefined, - freeLicense: true, -}; - -export const useSettingsPage = createWithEqualityFn( - (set) => ({ - ...defaultValues, - setState: (data) => set(data), - reset: () => set(defaultValues), - }), - Object.is, -); - -type Store = StoreValues & StoreMethods; - -type StoreValues = { - settings?: Settings; - enterpriseInfo?: EnterpriseInfo; - freeLicense: boolean; -}; - -type StoreMethods = { - setState: (data: Partial) => void; - reset: () => void; -}; diff --git a/web/src/pages/settings/style.scss b/web/src/pages/settings/style.scss deleted file mode 100644 index 81d10988a..000000000 --- a/web/src/pages/settings/style.scss +++ /dev/null @@ -1,196 +0,0 @@ -@use '@scssutils' as *; - -#settings-page { - .page-content { - box-sizing: border-box; - padding: 20px; - overflow-x: hidden; - overflow-y: auto; - - @include media-breakpoint-up(lg) { - padding: 64px 70px; - } - - h1 { - display: none; - margin-bottom: var(--spacing-l); - - @include typography(app-title); - - color: var(--text-body-primary); - - @include media-breakpoint-up(lg) { - display: block; - } - } - - h2 { - @include typography(app-body-1); - - color: var(--text-body-primary); - } - - h3 { - @include typography(modal-title); - - color: var(--text-body-primary); - } - - & > .loader-spinner { - padding-top: 10vh; - width: 100% !important; - } - - .helper-row { - display: flex; - align-items: center; - gap: var(--spacing-xs); - .labeled-checkbox { - padding-bottom: 0; - } - } - - section { - & > header { - display: flex; - flex-flow: row nowrap; - align-items: center; - justify-content: space-between; - gap: var(--spacing-s); - - h2 { - display: flex; - align-items: center; - gap: var(--spacing-xs); - @include typography(app-welcome-1); - - .helper-floating { - @include typography(modal-title); - } - } - - & > .controls { - display: flex; - gap: var(--spacing-s); - } - } - - display: flex; - flex-flow: column nowrap; - gap: var(--spacing-m); - } - - .license-not-required-container { - padding-bottom: var(--spacing-s); - } - - .card { - & > .controls { - display: flex; - flex-flow: row wrap; - align-items: center; - justify-content: flex-start; - - column-gap: var(--spacing-xs); - - & > .btn { - &:first-of-type { - margin-left: auto; - } - } - } - } - - & > .settings-card { - padding: 0; - @include media-breakpoint-up(lg) { - padding: var(--spacing-m); - border-top-left-radius: 0; - } - - @include media-breakpoint-down(lg) { - padding: var(--spacing-m) 0; - } - h3 { - display: flex; - align-items: center; - gap: var(--spacing-xs); - - @include typography(app-side-bar); - - .helper-floating { - @include typography(modal-title); - } - } - - .subsection-header { - margin-bottom: var(--spacing-s); - } - - .subsection-header-with-controls { - margin-bottom: var(--spacing-s); - display: flex; - align-items: center; - justify-content: space-between; - } - - .checkbox-column { - display: flex; - flex-direction: column; - gap: var(--spacing-s); - padding-bottom: var(--spacing-s); - } - - .column-layout { - @include media-breakpoint-up(xxl) { - display: grid; - grid-template-rows: auto; - grid-template-columns: 1fr 1fr; - align-items: start; - justify-items: start; - row-gap: 48px; - column-gap: 56px; - } - - & > .left { - grid-column: 1; - } - - & > .right { - grid-column: 2; - } - - & > .left, - & > .right { - width: 100%; - max-width: 800px; - display: flex; - flex-flow: column; - row-gap: var(--spacing-s); - } - } - - .spinner-container { - display: flex; - justify-content: center; - align-items: center; - min-height: 400px; - grid-column: 1 / -1; - width: 100%; - } - } - } - - .license-not-required-container { - grid-column: 1 / -1; - width: 100%; - - @include media-breakpoint-down(xxl) { - margin-bottom: 26px; - } - - @include media-breakpoint-down(xl) { - margin-top: 26px; - } - } -} diff --git a/web/src/pages/support/SupportPage.tsx b/web/src/pages/support/SupportPage.tsx deleted file mode 100644 index 6ac1375ec..000000000 --- a/web/src/pages/support/SupportPage.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import './style.scss'; - -import { useI18nContext } from '../../i18n/i18n-react'; -import { PageContainer } from '../../shared/components/Layout/PageContainer/PageContainer'; -import { BuiltByCard } from './components/BuiltByCard/BuiltByCard'; -import { DebugDataCard } from './components/DebugDataCard/DebugDataCard'; -import { SupportCard } from './components/SupportCard/SupportCard'; - -export const SupportPage = () => { - const { LL } = useI18nContext(); - return ( - -

{LL.supportPage.title()}

-
-
- -
-
- - -
-
-
- ); -}; diff --git a/web/src/pages/support/components/BuiltByCard/BuiltByCard.tsx b/web/src/pages/support/components/BuiltByCard/BuiltByCard.tsx deleted file mode 100644 index 1859b6eb3..000000000 --- a/web/src/pages/support/components/BuiltByCard/BuiltByCard.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import './style.scss'; - -import TeoniteLogoGif from '/src/shared/images/gif/tnt-built.gif'; - -import { Card } from '../../../../shared/defguard-ui/components/Layout/Card/Card'; - -export const BuiltByCard = () => { - return ( - - - logo - - - ); -}; diff --git a/web/src/pages/support/components/BuiltByCard/style.scss b/web/src/pages/support/components/BuiltByCard/style.scss deleted file mode 100644 index aee69f34e..000000000 --- a/web/src/pages/support/components/BuiltByCard/style.scss +++ /dev/null @@ -1,25 +0,0 @@ -@use '@scssutils' as *; - -#built-by-card { - width: 100%; - box-sizing: border-box; - display: flex; - flex-flow: column; - align-items: center; - justify-content: center; - padding: 20px 10px; - height: 150px; - - @include media-breakpoint-up(lg) { - padding: 30px 26px; - height: 180px; - } - - a { - height: 32px; - - img { - height: inherit; - } - } -} diff --git a/web/src/pages/support/components/DebugDataCard/DebugDataCard.tsx b/web/src/pages/support/components/DebugDataCard/DebugDataCard.tsx deleted file mode 100644 index a7a738da7..000000000 --- a/web/src/pages/support/components/DebugDataCard/DebugDataCard.tsx +++ /dev/null @@ -1,114 +0,0 @@ -import './style.scss'; - -import { useMutation, useQuery } from '@tanstack/react-query'; -import { saveAs } from 'file-saver'; -import { useState } from 'react'; -import ReactMarkdown from 'react-markdown'; - -import { useI18nContext } from '../../../../i18n/i18n-react'; -import SvgIconDownload from '../../../../shared/components/svg/IconDownload'; -import { Button } from '../../../../shared/defguard-ui/components/Layout/Button/Button'; -import { - ButtonSize, - ButtonStyleVariant, -} from '../../../../shared/defguard-ui/components/Layout/Button/types'; -import { Card } from '../../../../shared/defguard-ui/components/Layout/Card/Card'; -import { Divider } from '../../../../shared/defguard-ui/components/Layout/Divider/Divider'; -import useApi from '../../../../shared/hooks/useApi'; -import { QueryKeys } from '../../../../shared/queries'; -import { SendSupportDataModal } from './components/SendSupportDataModal'; - -export const DebugDataCard = () => { - const [sendDataOpen, setSendDataOpen] = useState(false); - const { LL } = useI18nContext(); - - const { - support: { downloadSupportData, downloadLogs }, - settings: { getSettings }, - } = useApi(); - - const { isPending: logsLoading, mutate: logsMutate } = useMutation({ - mutationFn: downloadLogs, - onSuccess: (res) => { - const content = new Blob([res], { type: 'text/plain;charset=utf-8' }); - const timestamp = new Date().toISOString().replaceAll(':', ''); - saveAs(content, `defguard-logs-${timestamp}.json`); - }, - }); - - const { isPending: configLoading, mutate: configMutate } = useMutation({ - mutationFn: downloadSupportData, - onSuccess: (res) => { - const content = new Blob([JSON.stringify(res, null, 2)], { - type: 'text/plain;charset=utf-8', - }); - const timestamp = new Date().toISOString().replaceAll(':', ''); - saveAs(content, `defguard-support-data-${timestamp}.json`); - }, - }); - - const { data: settings, isLoading: settingsLoading } = useQuery({ - queryFn: getSettings, - queryKey: [QueryKeys.FETCH_SETTINGS], - refetchOnMount: true, - }); - - const smtp_configured = - settings?.smtp_server && - settings?.smtp_port && - settings?.smtp_user && - settings?.smtp_password && - settings?.smtp_sender; - - return ( - <> - setSendDataOpen(v)} - /> - -
-

{LL.supportPage.debugDataCard.title()}

-
- -
- {LL.supportPage.debugDataCard.body()} -
-
- - ); -}; diff --git a/web/src/pages/support/components/DebugDataCard/components/SendSupportDataModal.tsx b/web/src/pages/support/components/DebugDataCard/components/SendSupportDataModal.tsx deleted file mode 100644 index 393487fb5..000000000 --- a/web/src/pages/support/components/DebugDataCard/components/SendSupportDataModal.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import { useMutation } from '@tanstack/react-query'; - -import { useI18nContext } from '../../../../../i18n/i18n-react'; -import { ConfirmModal } from '../../../../../shared/defguard-ui/components/Layout/modals/ConfirmModal/ConfirmModal'; -import { ConfirmModalType } from '../../../../../shared/defguard-ui/components/Layout/modals/ConfirmModal/types'; -import useApi from '../../../../../shared/hooks/useApi'; -import { useToaster } from '../../../../../shared/hooks/useToaster'; -import type { SMTPError } from '../../../../../shared/types'; - -type Props = { - isOpen: boolean; - onOpenChange: (v: boolean) => void; -}; - -export const SendSupportDataModal = ({ isOpen, onOpenChange }: Props) => { - const { LL } = useI18nContext(); - const { - mail: { sendSupportMail }, - } = useApi(); - const toaster = useToaster(); - - const { mutate: sendMail, isPending: mailLoading } = useMutation({ - mutationFn: sendSupportMail, - onSuccess: () => { - toaster.success(LL.supportPage.debugDataCard.mailSent()); - onOpenChange(false); - }, - onError: (err: SMTPError) => { - toaster.error(`${LL.supportPage.debugDataCard.mailError()}`, { - subMessage: `${err.response?.data.error}`, - }); - console.error(err); - }, - }); - - return ( - onOpenChange(false)} - onSubmit={() => { - sendMail(); - }} - /> - ); -}; diff --git a/web/src/pages/support/components/DebugDataCard/style.scss b/web/src/pages/support/components/DebugDataCard/style.scss deleted file mode 100644 index 424fd537c..000000000 --- a/web/src/pages/support/components/DebugDataCard/style.scss +++ /dev/null @@ -1,82 +0,0 @@ -@use '@scssutils' as *; - -#support-debug-card { - box-sizing: border-box; - - .title { - width: 100%; - - @include typography(app-side-bar); - - color: var(--text-body-primary); - padding: 0; - margin: 0; - - @include media-breakpoint-up(md) { - width: unset; - } - } - - & > .controls { - display: flex; - flex-flow: row wrap; - gap: 10px; - align-items: center; - justify-content: flex-start; - box-sizing: border-box; - padding: 20px 10px; - - @include media-breakpoint-up(lg) { - padding: 16px 15px; - } - - & > .btn { - &:first-of-type { - margin-left: auto; - } - - svg { - path { - fill: var(--surface-icon-secondary); - } - } - } - } - - & > .content { - box-sizing: border-box; - padding: 25px 10px; - display: flex; - flex-flow: column; - row-gap: 15px; - - p, - span, - a, - strong, - li { - @include typography(app-body-2); - - color: var(--text-body-primary); - } - - strong { - font-weight: 600; - } - - p, - ul, - li { - text-align: center; - } - - ul, - ol { - list-style: none; - } - - @include media-breakpoint-up(lg) { - padding: 25px 60px; - } - } -} diff --git a/web/src/pages/support/components/SupportCard/SupportCard.tsx b/web/src/pages/support/components/SupportCard/SupportCard.tsx deleted file mode 100644 index d0fd03876..000000000 --- a/web/src/pages/support/components/SupportCard/SupportCard.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import './style.scss'; - -import ReactMarkdown from 'react-markdown'; - -import { useI18nContext } from '../../../../i18n/i18n-react'; -import { Card } from '../../../../shared/defguard-ui/components/Layout/Card/Card'; -import { Divider } from '../../../../shared/defguard-ui/components/Layout/Divider/Divider'; - -export const SupportCard = () => { - const { LL } = useI18nContext(); - return ( - -
-

{LL.supportPage.supportCard.title()}

-
- -
- {LL.supportPage.supportCard.body()} -
-
- ); -}; diff --git a/web/src/pages/support/components/SupportCard/style.scss b/web/src/pages/support/components/SupportCard/style.scss deleted file mode 100644 index a8b9b7432..000000000 --- a/web/src/pages/support/components/SupportCard/style.scss +++ /dev/null @@ -1,56 +0,0 @@ -@use '@scssutils' as *; - -#support-card { - & > header { - box-sizing: border-box; - display: flex; - flex-flow: row nowrap; - align-items: center; - justify-content: center; - padding: 20px; - - & > .title { - @include typography(modal-title); - - color: var(--text-body-primary); - text-align: center; - } - } - - & > .content { - box-sizing: border-box; - padding: 25px 10px; - display: flex; - flex-flow: column; - row-gap: 15px; - - p, - span, - a, - strong, - li { - @include typography(app-body-2); - - color: var(--text-body-primary); - } - - strong { - font-weight: 600; - } - - p, - ul, - li { - text-align: center; - } - - ul, - ol { - list-style: none; - } - - @include media-breakpoint-up(lg) { - padding: 25px 60px; - } - } -} diff --git a/web/src/pages/support/style.scss b/web/src/pages/support/style.scss deleted file mode 100644 index 1fe5e8d69..000000000 --- a/web/src/pages/support/style.scss +++ /dev/null @@ -1,70 +0,0 @@ -@use '@scssutils' as *; - -#support-page { - & > .page-content { - box-sizing: border-box; - padding: 20px; - overflow-x: hidden; - overflow-y: auto; - - @include media-breakpoint-down(lg) { - background-color: var(--surface-default-modal); - } - - @include media-breakpoint-up(lg) { - padding: 64px 70px; - } - - h1 { - display: none; - margin-bottom: 50px; - - @include typography(app-title); - - color: var(--text-body-primary); - - @include media-breakpoint-up(lg) { - display: block; - } - } - - & > .content { - display: flex; - flex-flow: row wrap; - align-items: center; - justify-content: flex-start; - row-gap: 40px; - - & > .left, - & > .right { - display: flex; - flex-flow: column; - row-gap: 48px; - width: 100%; - } - - @include media-breakpoint-up(xxl) { - display: grid; - grid-template-rows: 1fr; - grid-template-columns: minmax(auto, 1000px) minmax(auto, 1000px); - column-gap: 50px; - align-items: start; - justify-content: center; - - & > .left { - grid-column: 1; - } - - & > .right { - grid-column: 2; - } - - & > .left, - & > .right { - grid-row: 1; - width: 100%; - } - } - } - } -} diff --git a/web/src/pages/user-profile/UserProfilePage/UserProfilePage.tsx b/web/src/pages/user-profile/UserProfilePage/UserProfilePage.tsx new file mode 100644 index 000000000..5bab91cb4 --- /dev/null +++ b/web/src/pages/user-profile/UserProfilePage/UserProfilePage.tsx @@ -0,0 +1,151 @@ +import './style.scss'; +import { useSuspenseQuery } from '@tanstack/react-query'; +import { useNavigate, useParams, useSearch } from '@tanstack/react-router'; +import { trainCase } from 'change-case'; +import { useCallback, useEffect, useMemo, useRef } from 'react'; +import { m } from '../../../paraglide/messages'; +import { Page } from '../../../shared/components/Page/Page'; +import { Tabs } from '../../../shared/defguard-ui/components/Tabs/Tabs'; +import type { TabsItem } from '../../../shared/defguard-ui/components/Tabs/types'; +import { useAuth } from '../../../shared/hooks/useAuth'; +import { + getUserApiTokensQueryOptions, + getUserAuthKeysQueryOptions, + userProfileQueryOptions, +} from '../../../shared/query'; +import { createUserProfileStore, UserProfileContext } from './hooks/useUserProfilePage'; +import { ProfileApiTokensTab } from './tabs/ProfileApiTokensTab/ProfileApiTokensTab'; +import { ProfileAuthKeysTab } from './tabs/ProfileAuthKeysTab/ProfileAuthKeysTab'; +import { ProfileDetailsTab } from './tabs/ProfileDetailsTab/ProfileDetailsTab'; +import { ProfileDevicesTab } from './tabs/ProfileDevicesTab/ProfileDevicesTab'; +import { UserProfileTab, type UserProfileTabValue } from './tabs/types'; + +const defaultTab = UserProfileTab.Details; + +export const UserProfilePage = () => { + const navigate = useNavigate(); + const authUsername = useAuth((s) => s.user?.username as string); + const search = useSearch({ from: '/_authorized/user/$username' }); + const activeTab = search.tab ?? defaultTab; + + const { username } = useParams({ + from: '/_authorized/user/$username', + }); + + const isSelf = useMemo( + (): boolean => authUsername === username, + [authUsername, username], + ); + + const { data: userProfile } = useSuspenseQuery(userProfileQueryOptions(username)); + const { data: userAuthKeys } = useSuspenseQuery(getUserAuthKeysQueryOptions(username)); + const { data: userApiTokens } = useSuspenseQuery( + getUserApiTokensQueryOptions(username), + ); + + const pageTitle = useMemo(() => { + if (isSelf) { + return m.profile_my_profile(); + } + const name = trainCase( + `${userProfile.user.first_name} ${userProfile.user.last_name}`, + ); + return m.profile_title({ + name, + }); + }, [isSelf, userProfile.user.first_name, userProfile.user.last_name]); + + const store = useRef( + createUserProfileStore({ + profile: userProfile, + authKeys: userAuthKeys, + apiTokens: userApiTokens, + }), + ).current; + + const setActiveTab = useCallback( + (tab: UserProfileTabValue) => { + navigate({ + from: '/user/$username', + to: '/user/$username', + search: (perv) => ({ ...perv, tab }), + }); + }, + [navigate], + ); + + const tabsConfiguration = useMemo(() => { + const res: TabsItem[] = [ + { + title: m.profile_tabs_details(), + active: activeTab === UserProfileTab.Details, + onClick: () => setActiveTab(UserProfileTab.Details), + }, + { + title: m.profile_tabs_devices(), + active: activeTab === UserProfileTab.Devices, + onClick: () => setActiveTab(UserProfileTab.Devices), + }, + { + title: m.profile_tabs_auth_keys(), + active: activeTab === UserProfileTab.AuthKeys, + onClick: () => setActiveTab(UserProfileTab.AuthKeys), + }, + { + title: m.profile_tabs_api(), + active: activeTab === UserProfileTab.ApiTokens, + onClick: () => setActiveTab(UserProfileTab.ApiTokens), + }, + ]; + return res; + }, [activeTab, setActiveTab]); + + const RenderActiveTab = useMemo(() => { + switch (activeTab) { + case UserProfileTab.Details: + return ProfileDetailsTab; + case UserProfileTab.Devices: + return ProfileDevicesTab; + case UserProfileTab.AuthKeys: + return ProfileAuthKeysTab; + case UserProfileTab.ApiTokens: + return ProfileApiTokensTab; + } + }, [activeTab]); + + // biome-ignore lint/correctness/useExhaustiveDependencies: side effect + useEffect(() => { + if (store && userProfile) { + store.setState({ + ...userProfile, + }); + } + }, [userProfile]); + + // biome-ignore lint/correctness/useExhaustiveDependencies: side effect + useEffect(() => { + if (store && userAuthKeys) { + store.setState({ + authKeys: userAuthKeys, + }); + } + }, [userAuthKeys]); + + // biome-ignore lint/correctness/useExhaustiveDependencies: side effect + useEffect(() => { + if (store && userApiTokens) { + store.setState({ + apiTokens: userApiTokens, + }); + } + }, [userApiTokens]); + + return ( + + + + + + + ); +}; diff --git a/web/src/pages/user-profile/UserProfilePage/components/ProfileCard/ProfileCard.tsx b/web/src/pages/user-profile/UserProfilePage/components/ProfileCard/ProfileCard.tsx new file mode 100644 index 000000000..076c07d6b --- /dev/null +++ b/web/src/pages/user-profile/UserProfilePage/components/ProfileCard/ProfileCard.tsx @@ -0,0 +1,15 @@ +import type { HtmlHTMLAttributes, PropsWithChildren } from 'react'; +import './style.scss'; +import clsx from 'clsx'; + +export const ProfileCard = ({ + children, + className, + ...props +}: PropsWithChildren & HtmlHTMLAttributes) => { + return ( +
+ {children} +
+ ); +}; diff --git a/web/src/pages/user-profile/UserProfilePage/components/ProfileCard/style.scss b/web/src/pages/user-profile/UserProfilePage/components/ProfileCard/style.scss new file mode 100644 index 000000000..b1471436c --- /dev/null +++ b/web/src/pages/user-profile/UserProfilePage/components/ProfileCard/style.scss @@ -0,0 +1,10 @@ +.profile-card { + box-sizing: border-box; + padding: var(--spacing-xl); + border: var(--border-1) solid var(--border-disabled); + border-radius: var(--radius-lg); + + h2 { + padding-bottom: var(--spacing-2xl); + } +} diff --git a/web/src/pages/user-profile/UserProfilePage/components/ProfileTabHeader/ProfileTabHeader.tsx b/web/src/pages/user-profile/UserProfilePage/components/ProfileTabHeader/ProfileTabHeader.tsx new file mode 100644 index 000000000..48d17800b --- /dev/null +++ b/web/src/pages/user-profile/UserProfilePage/components/ProfileTabHeader/ProfileTabHeader.tsx @@ -0,0 +1,16 @@ +import type { PropsWithChildren } from 'react'; +import './style.scss'; + +export const ProfileTabHeader = ({ + children, + title, +}: PropsWithChildren & { + title: string; +}) => { + return ( +
+

{title}

+
{children}
+
+ ); +}; diff --git a/web/src/pages/user-profile/UserProfilePage/components/ProfileTabHeader/style.scss b/web/src/pages/user-profile/UserProfilePage/components/ProfileTabHeader/style.scss new file mode 100644 index 000000000..7ebf148ef --- /dev/null +++ b/web/src/pages/user-profile/UserProfilePage/components/ProfileTabHeader/style.scss @@ -0,0 +1,23 @@ +#user-profile-page .tab-header { + display: flex; + flex-flow: row; + align-items: center; + justify-content: flex-start; + width: 100%; + grid-column: 1 / 13; + box-sizing: border-box; + padding: 0 var(--spacing-xl) var(--spacing-xl); + + h2 { + font: var(--t-title-h5); + } + + & > .right { + display: flex; + flex-flow: row; + align-items: center; + justify-content: flex-end; + column-gap: var(--spacing-md); + margin-left: auto; + } +} diff --git a/web/src/pages/user-profile/UserProfilePage/hooks/useUserProfilePage.tsx b/web/src/pages/user-profile/UserProfilePage/hooks/useUserProfilePage.tsx new file mode 100644 index 000000000..f10e7f3e0 --- /dev/null +++ b/web/src/pages/user-profile/UserProfilePage/hooks/useUserProfilePage.tsx @@ -0,0 +1,39 @@ +import { createContext, useContext } from 'react'; +import { createStore, useStore } from 'zustand'; +import type { ApiToken, AuthKey, UserProfile } from '../../../../shared/api/types'; + +interface Extras { + authKeys: AuthKey[]; + apiTokens: ApiToken[]; +} + +interface ProfileProps extends Extras { + profile: UserProfile; +} + +interface ProfileState extends UserProfile, Extras { + reset: () => void; +} + +type UserProfileStore = ReturnType; + +export const createUserProfileStore = (initialProps: ProfileProps) => { + return createStore()((set) => ({ + apiTokens: initialProps.apiTokens, + authKeys: initialProps.authKeys, + devices: initialProps.profile.devices, + security_keys: initialProps.profile.security_keys, + user: initialProps.profile.user, + reset: () => set(initialProps.profile), + })); +}; + +export const UserProfileContext = createContext(null); + +export function useUserProfile(selector: (state: ProfileState) => T): T { + const store = useContext(UserProfileContext); + + if (!store) throw new Error('Missing userProfile context'); + + return useStore(store, selector); +} diff --git a/web/src/pages/user-profile/UserProfilePage/style.scss b/web/src/pages/user-profile/UserProfilePage/style.scss new file mode 100644 index 000000000..b17199456 --- /dev/null +++ b/web/src/pages/user-profile/UserProfilePage/style.scss @@ -0,0 +1,14 @@ +#user-profile-page { + h2 { + font: var(--t-title-h4); + color: var(--fg-default); + } + + .tabs { + padding-top: var(--spacing-md); + } + + .table { + grid-column: 1 / 13; + } +} diff --git a/web/src/pages/user-profile/UserProfilePage/tabs/ProfileApiTokensTab/ProfileApiTokensTab.tsx b/web/src/pages/user-profile/UserProfilePage/tabs/ProfileApiTokensTab/ProfileApiTokensTab.tsx new file mode 100644 index 000000000..5d224098c --- /dev/null +++ b/web/src/pages/user-profile/UserProfilePage/tabs/ProfileApiTokensTab/ProfileApiTokensTab.tsx @@ -0,0 +1,57 @@ +import { m } from '../../../../../paraglide/messages'; +import { LayoutGrid } from '../../../../../shared/components/LayoutGrid/LayoutGrid'; +import { Button } from '../../../../../shared/defguard-ui/components/Button/Button'; +import { EmptyStateFlexible } from '../../../../../shared/defguard-ui/components/EmptyStateFlexible/EmptyStateFlexible'; +import { SizedBox } from '../../../../../shared/defguard-ui/components/SizedBox/SizedBox'; +import { ThemeSpacing } from '../../../../../shared/defguard-ui/types'; +import { openModal } from '../../../../../shared/hooks/modalControls/modalsSubjects'; +import { ModalName } from '../../../../../shared/hooks/modalControls/modalTypes'; +import { ProfileTabHeader } from '../../components/ProfileTabHeader/ProfileTabHeader'; +import { useUserProfile } from '../../hooks/useUserProfilePage'; +import { ProfileApiTokensTable } from './components/ProfileApiTokensTable/ProfileApiTokensTable'; +import { AddApiTokenModal } from './modals/AddApiTokenModal/AddApiTokenModal'; +import { RenameApiTokenModal } from './modals/RenameApiTokenModal/RenameApiTokenModal'; + +export const ProfileApiTokensTab = () => { + const username = useUserProfile((s) => s.user.username); + const apiTokens = useUserProfile((s) => s.apiTokens); + return ( + <> + {apiTokens.length === 0 && ( + { + openModal(ModalName.AddApiToken, { + username, + }); + }, + }} + /> + )} + {apiTokens.length > 0 && ( + + + + -

{displayDate()}

- - - ); -}; - -type DisplayProps = { - selected?: string | null; - displayFormat?: string; - clearable?: boolean; - onClear?: () => void; -} & HTMLAttributes; - -const DisplayField = forwardRef( - ({ selected, className, displayFormat, onClear, clearable = false, ...rest }, ref) => { - return ( -
- - {clearable && isPresent(onClear) && selected !== null && ( - { - onClear?.(); - }} - > - - - )} -
- ); - }, -); diff --git a/web/src/shared/components/Layout/DateInput/FormDateInput.tsx b/web/src/shared/components/Layout/DateInput/FormDateInput.tsx deleted file mode 100644 index 7163f889d..000000000 --- a/web/src/shared/components/Layout/DateInput/FormDateInput.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import { isUndefined } from 'lodash-es'; -import { useMemo } from 'react'; -import { - type FieldValues, - type UseControllerProps, - useController, -} from 'react-hook-form'; - -import { DateInput } from './DateInput'; -import type { DateInputProps } from './types'; - -type Props = { - onChange?: (value: string | null) => void; - controller: UseControllerProps; - label?: string; - disabled?: boolean; -} & Pick; - -export const FormDateInput = ({ - onChange, - controller, - label, - disabled = false, - ...dateInputProps -}: Props) => { - const { - field: { value, onChange: fieldChange }, - fieldState: { isDirty, isTouched, error }, - formState: { isSubmitted }, - } = useController(controller); - - const errorMessage = useMemo(() => { - if ( - (!isUndefined(error) && (isDirty || isTouched)) || - (!isUndefined(error) && isSubmitted) - ) { - return error.message; - } - return undefined; - }, [error, isDirty, isSubmitted, isTouched]); - - return ( - { - fieldChange(val); - onChange?.(val); - }} - label={label} - errorMessage={errorMessage} - disabled={disabled} - {...dateInputProps} - /> - ); -}; diff --git a/web/src/shared/components/Layout/DateInput/style.scss b/web/src/shared/components/Layout/DateInput/style.scss deleted file mode 100644 index cb8657511..000000000 --- a/web/src/shared/components/Layout/DateInput/style.scss +++ /dev/null @@ -1,276 +0,0 @@ -.date-input-spacer { - & > .inner { - position: relative; - - &.with-time { - & > .fields-track { - display: grid; - grid-template-columns: 1fr 80px; - column-gap: var(--spacing-s); - - & > .react-datepicker-wrapper { - &:first-child { - grid-row: 1; - grid-column: 1 / 2; - } - - &:last-child { - grid-row: 1; - grid-column: 2 / 3; - } - } - } - } - - & > .label { - user-select: none; - @include typography(app-wizard-1); - color: var(--text-body-tertiary); - padding-bottom: var(--spacing-xs); - } - } -} - -.date-input-container { - position: relative; - - &.clearable { - & > .date-input { - padding: 8px 42px 8px 20px; - } - - &:hover { - & > .interaction-box { - visibility: visible; - } - } - } - - .interaction-box { - visibility: hidden; - width: 24px; - height: 24px; - position: absolute; - right: 10px; - top: 50%; - transform: translateY(-50%); - - &:hover { - svg path { - fill: var(--surface-alert-primary); - } - } - } - - .date-input { - display: flex; - flex-flow: row; - align-items: center; - justify-content: flex-start; - background-color: var(--surface-frame-bg); - border: 1px solid var(--border-primary); - border-radius: 10px; - box-sizing: border-box; - width: 100%; - min-height: 50px; - margin: 0; - padding: 8px 20px; - cursor: pointer; - opacity: 1; - - @include animate-field; - transition-property: border-color, opacity; - - span { - width: 100%; - text-align: left; - @include typography(app-input); - } - } - - &.disabled { - cursor: not-allowed; - - & > .date-input { - cursor: not-allowed; - opacity: var(--disabled-opacity); - } - } - - &.error { - & > .date-input { - border-color: var(--border-alert); - } - } -} - -.react-datepicker-popper { - z-index: 2; -} - -.react-datepicker { - border-radius: 10px; - background-color: var(--surface-default-modal); - border: 1px solid var(--border-primary); - box-sizing: border-box; - padding: var(--spacing-s); - width: 100%; - max-width: 360px; -} - -.react-datepicker__triangle { - color: var(--border-primary); -} - -.react-datepicker__day-names { - display: grid; - grid-template-columns: repeat(7, 1fr); - grid-template-rows: auto; - column-gap: var(--spacing-s); - align-items: center; - border-bottom: 1px solid var(--border-primary); -} - -.react-datepicker__aria-live { - display: none; -} - -.react-datepicker-wrapper { - width: 100%; -} - -.react-datepicker__month { - display: flex; - flex-flow: column; - row-gap: var(--spacing-xs); -} - -.react-datepicker__week { - display: grid; - grid-template-columns: repeat(7, 1fr); - column-gap: var(--spacing-s); -} - -.react-datepicker__day { - width: 100%; - cursor: pointer; - - .custom-day { - display: flex; - flex-flow: row; - align-items: center; - justify-content: center; - width: 20px; - height: 20px; - border-radius: 5px; - - @include typography(app-modal-1); - - &:hover { - background-color: var(--surface-main-primary); - - span { - color: var(--white); - } - } - } -} - -.react-datepicker__day--selected { - & > .custom-day { - background-color: var(--surface-main-primary); - - span { - color: var(--white); - } - } -} - -.react-datepicker__header { - padding: 0 0 var(--spacing-xs) 0; - - .date-picker-custom-header { - display: grid; - width: 100%; - grid-template-rows: auto; - grid-template-columns: 22px 1fr 22px; - align-items: center; - justify-items: start; - column-gap: 10px; - border: 0; - padding-bottom: var(--spacing-xs); - - button { - display: flex; - flex-flow: row; - align-items: center; - justify-content: center; - margin: 0; - padding: 0; - background: none; - border: none; - width: 22px; - height: 22px; - cursor: pointer; - - svg { - width: 100%; - height: 100%; - } - - &:last-child { - svg { - transform: rotateZ(-180deg); - } - } - } - - p { - width: 100%; - text-align: center; - @include typography(modal-title); - user-select: none; - } - - .icon-container { - svg { - path { - fill: var(--surface-icon-primary); - } - } - } - } -} - -.react-datepicker-time__header { - color: var(--text-body-primary); - @include typography(modal-title); - border-bottom: 1px solid var(--border-primary); - padding-bottom: var(--spacing-xs); -} - -.react-datepicker__time-list { - display: flex; - flex-flow: column; - row-gap: var(--spacing-xs); - list-style: none; - padding-right: 10px; - scrollbar-gutter: stable; - max-height: min(250px, 75dvh); - overflow: auto; - - .react-datepicker__time-list-item { - @include typography(app-modal-1); - color: var(--text-body-primary); - padding: 3px; - border-radius: 5px; - cursor: pointer; - background-color: transparent; - - &:hover, - &.react-datepicker__time-list-item--selected { - color: var(--white); - background-color: var(--surface-main-primary); - } - } -} diff --git a/web/src/shared/components/Layout/DateInput/types.ts b/web/src/shared/components/Layout/DateInput/types.ts deleted file mode 100644 index 1fda27111..000000000 --- a/web/src/shared/components/Layout/DateInput/types.ts +++ /dev/null @@ -1,9 +0,0 @@ -export type DateInputProps = { - selected: string; - label?: string; - errorMessage?: string; - disabled?: boolean; - showTimeSelection?: boolean; - clearable?: boolean; - onChange: (value: string | null) => void; -}; diff --git a/web/src/shared/components/Layout/EnterpriseUpgradeToast/EnterpriseUpgradeToast.tsx b/web/src/shared/components/Layout/EnterpriseUpgradeToast/EnterpriseUpgradeToast.tsx deleted file mode 100644 index 5e557bbe9..000000000 --- a/web/src/shared/components/Layout/EnterpriseUpgradeToast/EnterpriseUpgradeToast.tsx +++ /dev/null @@ -1,75 +0,0 @@ -import './style.scss'; - -import { useCallback } from 'react'; - -import { useI18nContext } from '../../../../i18n/i18n-react'; -import { Badge } from '../../../defguard-ui/components/Layout/Badge/Badge'; -import { BadgeStyleVariant } from '../../../defguard-ui/components/Layout/Badge/types'; -import type { ToastOptions } from '../../../defguard-ui/components/Layout/ToastManager/Toast/types'; -import { useToastsStore } from '../../../defguard-ui/hooks/toasts/useToastStore'; -import SvgIconX from '../../svg/IconX'; - -export const EnterpriseUpgradeToast = ({ id }: ToastOptions) => { - const removeToast = useToastsStore((s) => s.removeToast); - const { LL } = useI18nContext(); - - const closeToast = useCallback(() => { - removeToast(id); - }, [id, removeToast]); - - const handleDismiss = () => { - closeToast(); - }; - - return ( -
-
-
- - - - - - } - className="toaster-badge" - /> -

{LL.modals.enterpriseUpgradeToaster.title()}

-
- -
-
-

{LL.modals.enterpriseUpgradeToaster.message()}

- -
-
- ); -}; diff --git a/web/src/shared/components/Layout/EnterpriseUpgradeToast/style.scss b/web/src/shared/components/Layout/EnterpriseUpgradeToast/style.scss deleted file mode 100644 index 1e282663c..000000000 --- a/web/src/shared/components/Layout/EnterpriseUpgradeToast/style.scss +++ /dev/null @@ -1,99 +0,0 @@ -.enterprise-upgrade-toaster { - box-sizing: border-box; - padding: 15px; - box-shadow: 0px 6px 10px 0px rgba(0, 0, 0, 0.1); - border-radius: 10px; - background-color: var(--surface-nav-bg); - border-radius: 15px; - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - gap: 8px; - - min-width: 270px; - max-width: 400px; - - .toaster-badge { - border-radius: 100%; - width: 18px; - height: 18px; - } - - .dismiss { - display: flex; - flex-flow: row; - align-items: center; - justify-content: center; - border: 0 solid transparent; - background-color: transparent; - cursor: pointer; - padding: 2px; - - svg { - & > g { - & > g { - fill: var(--surface-icon-primary); - } - } - height: 14px; - width: 14px; - } - } - - .top { - display: flex; - align-items: center; - flex-flow: row nowrap; - column-gap: 8px; - width: 100%; - justify-content: space-between; - - .heading { - display: flex; - align-items: center; - gap: 5px; - } - - p { - color: var(--text-body-primary); - text-wrap: nowrap; - white-space: none; - @include typography(app-modal-1); - } - - & > a { - margin-left: auto; - border: none; - background-color: transparent; - cursor: pointer; - width: 22px; - height: 22px; - display: inline-flex; - flex-flow: row; - align-content: center; - align-items: center; - justify-content: center; - padding: 4px; - box-sizing: border-box; - text-decoration: none; - } - } - - .bottom { - min-width: 230px; - display: flex; - flex-flow: column; - align-items: left; - justify-content: space-between; - width: 100%; - @include typography(app-copyright); - - .upgrade-link-container { - display: flex; - width: 100%; - margin-top: 8px; - justify-content: right; - } - } -} diff --git a/web/src/shared/components/Layout/EnterpriseUpgradeToast/types.ts b/web/src/shared/components/Layout/EnterpriseUpgradeToast/types.ts deleted file mode 100644 index 5e111a0ca..000000000 --- a/web/src/shared/components/Layout/EnterpriseUpgradeToast/types.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { z } from 'zod'; - -export const enterpriseUpgradeToastMetaSchema = z.object({ - customId: z.string().trim().min(1), -}); - -export type EnterpriseUpgradeToastMeta = z.infer; diff --git a/web/src/shared/components/Layout/ExpandableSection/ExpandableSection.tsx b/web/src/shared/components/Layout/ExpandableSection/ExpandableSection.tsx deleted file mode 100644 index 4178b75c1..000000000 --- a/web/src/shared/components/Layout/ExpandableSection/ExpandableSection.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import './style.scss'; - -import clsx from 'clsx'; -import { type PropsWithChildren, useState } from 'react'; - -import { ArrowSingle } from '../../../defguard-ui/components/icons/ArrowSingle/ArrowSingle'; -import { ArrowSingleDirection } from '../../../defguard-ui/components/icons/ArrowSingle/types'; - -type Props = { - text: string; - initOpen?: boolean; - textAs?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'p'; - className?: string; - id?: string; -} & PropsWithChildren; - -export const ExpandableSection = ({ - children, - text, - className, - id, - textAs: Tag = 'p', - initOpen = true, -}: Props) => { - const [expanded, setExpanded] = useState(initOpen); - - return ( -
-
{ - setExpanded((s) => !s); - }} - > - {text} - -
-
-
{children}
-
-
- ); -}; diff --git a/web/src/shared/components/Layout/ExpandableSection/style.scss b/web/src/shared/components/Layout/ExpandableSection/style.scss deleted file mode 100644 index e2cd65435..000000000 --- a/web/src/shared/components/Layout/ExpandableSection/style.scss +++ /dev/null @@ -1,59 +0,0 @@ -.expandable-section { - & > .track { - width: 100%; - user-select: none; - cursor: pointer; - display: grid; - grid-template-columns: 1fr auto; - column-gap: var(--spacing-xs); - align-items: center; - justify-items: start; - border-bottom: 1px solid var(--border-primary); - - p { - width: 100%; - user-select: none; - } - - .arrow-single { - align-self: end; - width: 22px; - height: 22px; - - svg { - transition-property: transform; - - @include animate-standard; - } - } - } - - & > .expandable { - display: grid; - width: 100%; - transition-property: grid-template-rows; - - @include animate-standard(); - - &:not(.open) { - grid-template-rows: 0fr; - } - - &.open { - grid-template-rows: 1fr; - } - - & > div { - overflow: hidden; - padding-top: var(--spacing-s); - } - } -} - -.expandable-section .track p { - @include typography(app-side-bar); -} - -.expandable-section .track h2 { - @include typography(app-body-1); -} diff --git a/web/src/shared/components/Layout/ListCellTags/ListCellTags.tsx b/web/src/shared/components/Layout/ListCellTags/ListCellTags.tsx deleted file mode 100644 index 2fcbabc29..000000000 --- a/web/src/shared/components/Layout/ListCellTags/ListCellTags.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import './style.scss'; - -import useResizeObserver from '@react-hook/resize-observer'; -import clsx from 'clsx'; -import { useCallback, useRef, useState } from 'react'; - -import type { ListCellTag } from '../../../../pages/acl/AclIndexPage/components/shared/types'; -import { FloatingMenu } from '../../../defguard-ui/components/Layout/FloatingMenu/FloatingMenu'; -import { FloatingMenuProvider } from '../../../defguard-ui/components/Layout/FloatingMenu/FloatingMenuProvider'; -import { FloatingMenuTrigger } from '../../../defguard-ui/components/Layout/FloatingMenu/FloatingMenuTrigger'; -import { Tag } from '../../../defguard-ui/components/Layout/Tag/Tag'; -import { isPresent } from '../../../defguard-ui/utils/isPresent'; - -type RenderTagsProps = { - data: ListCellTag[]; - placeholder?: string; -}; - -export const ListCellTags = ({ data, placeholder }: RenderTagsProps) => { - const containerRef = useRef(null); - const [overflows, setOverflows] = useState(false); - - const handleResize = useCallback(() => { - if (containerRef.current) { - setOverflows(containerRef.current.scrollWidth > containerRef.current.clientWidth); - } - }, []); - - useResizeObserver(containerRef, handleResize); - return ( - - -
- - {data.length === 0 && isPresent(placeholder) && ( -

{placeholder}

- )} -
-
- - - -
- ); -}; - -const FloatingContent = ({ data }: RenderTagsProps) => { - return ( -
    - {data.map((d) => ( -
  • {d.label}
  • - ))} -
- ); -}; - -const TagContent = ({ data }: RenderTagsProps) => { - return ( -
- {data.map((d) => { - if (d.displayAsTag) { - return ; - } - return {d.label}; - })} -
- ); -}; diff --git a/web/src/shared/components/Layout/ListCellTags/style.scss b/web/src/shared/components/Layout/ListCellTags/style.scss deleted file mode 100644 index 90633bb6b..000000000 --- a/web/src/shared/components/Layout/ListCellTags/style.scss +++ /dev/null @@ -1,72 +0,0 @@ -.list-cell-tags-floating { - list-style: none; - display: flex; - flex-flow: column; - row-gap: var(--spacing-xs); - max-height: 250px; - max-width: 100dvh; - min-height: 35px; - overflow: auto; - padding-right: var(--spacing-xs); - min-width: 201px; - - li { - @include typography(app-modal-1); - color: var(--text-body-secondary); - } -} - -.list-cell-tags { - position: relative; - overflow: hidden; - max-width: 100%; - width: 100%; - - &:not(.empty) { - cursor: help; - } - - span, - p { - @include typography(app-modal-2); - } - - & > .track { - display: flex; - flex-flow: row nowrap; - align-items: center; - justify-content: flex-start; - column-gap: var(--spacing-xs); - overflow: visible; - - .tag { - padding: 3px 6px; - height: unset; - border-radius: 6px; - } - } - - &.overflows { - &::after { - position: absolute; - top: 0; - right: 0; - width: 65px; - height: 100%; - content: ' '; - background: linear-gradient( - 90deg, - rgba(0, 0, 0, 0) 0%, - var(--surface-default-modal) 100% - ); - } - } - - .no-data { - cursor: default; - } - - .tag { - user-select: none; - } -} diff --git a/web/src/shared/components/Layout/ListHeader/ListHeader.tsx b/web/src/shared/components/Layout/ListHeader/ListHeader.tsx deleted file mode 100644 index 54a28ab92..000000000 --- a/web/src/shared/components/Layout/ListHeader/ListHeader.tsx +++ /dev/null @@ -1,158 +0,0 @@ -import './style.scss'; - -import clsx from 'clsx'; -import { uniqBy } from 'lodash-es'; -import { useEffect } from 'react'; - -import { CheckBox } from '../../../defguard-ui/components/Layout/Checkbox/CheckBox'; -import { InteractionBox } from '../../../defguard-ui/components/Layout/InteractionBox/InteractionBox'; -import { ListSortDirection } from '../../../defguard-ui/components/Layout/VirtualizedList/types'; -import { isPresent } from '../../../defguard-ui/utils/isPresent'; -import type { ListHeaderColumnConfig } from './types'; - -type ListHeaderColumnProps = { - active: boolean; - sortDirection?: ListSortDirection; - onClick?: () => void; - columnKey?: string; -} & Omit, 'key'>; - -type Props = { - headers: ListHeaderColumnConfig[]; - activeKey?: keyof T; - sortDirection?: ListSortDirection; - className?: string; - id?: string; - selectAll?: boolean; - onSelectAll?: (value: boolean) => void; - onChange?: (key: keyof T, direction: ListSortDirection) => void; -}; - -const ListHeaderColumn = ({ - onClick, - sortDirection, - label, - active, - enabled, - ...props -}: ListHeaderColumnProps) => { - const disabled = !enabled || !isPresent(props.sortKey); - const key = (props.sortKey ?? props.columnKey) as string; - - useEffect(() => { - if (!props.sortKey && !props.columnKey) { - throw Error('ListHeader needs either key or sortKey!'); - } - }, [props.columnKey, props.sortKey]); - - return ( -
- -
- ); -}; - -export const ListHeader = ({ - headers, - activeKey, - sortDirection, - className, - id, - selectAll, - onChange, - onSelectAll, -}: Props) => { - useEffect(() => { - const unq = uniqBy(headers, (h) => h.sortKey ?? h.key); - if (unq.length !== headers.length) { - throw Error('ListHeader component given headers with duplicate identifiers'); - } - }, [headers]); - - const selectEnabled = isPresent(selectAll) && isPresent(onSelectAll); - - return ( -
- {!selectEnabled &&
} - {selectEnabled && ( -
- { - onSelectAll?.(!selectAll); - }} - > - - -
- )} - {headers.map(({ label, sortKey, enabled, key }) => { - const isActive = activeKey === sortKey; - const direction = isActive ? sortDirection : ListSortDirection.ASC; - const componentKey: string = (sortKey ?? key) as string; - return ( - { - if (enabled && isPresent(onChange) && isPresent(sortKey)) { - if (isActive) { - const newDirection = - sortDirection === ListSortDirection.ASC - ? ListSortDirection.DESC - : ListSortDirection.ASC; - onChange(sortKey, newDirection); - } else { - onChange(sortKey, ListSortDirection.ASC); - } - } - }} - /> - ); - })} -
- ); -}; diff --git a/web/src/shared/components/Layout/ListHeader/style.scss b/web/src/shared/components/Layout/ListHeader/style.scss deleted file mode 100644 index ccafe59af..000000000 --- a/web/src/shared/components/Layout/ListHeader/style.scss +++ /dev/null @@ -1,70 +0,0 @@ -.list-headers { - width: 100%; - display: inline-grid; - grid-template-rows: 1fr; - - .list-header-column { - button { - display: flex; - flex-flow: row; - align-items: center; - justify-content: flex-start; - background-color: transparent; - padding: 0; - margin: 0; - border: none; - - & > .label { - @include typography(app-modal-1); - @include animate-standard; - transition-property: font-weight color; - color: var(--text-body-tertiary); - } - - & > .sort-icon { - display: flex; - flex-flow: row; - align-items: center; - justify-content: center; - padding: 0; - margin: 0; - width: 22px; - height: 22px; - user-select: none; - visibility: visible; - @include animate-standard; - transition-property: transform; - transform: rotate(180deg); - - &.asc { - transform: rotate(180deg); - } - - &.desc { - transform: rotate(0deg); - } - } - } - - &.disabled { - & > button { - & > .sort-icon { - visibility: hidden; - } - } - } - - &:not(.disabled) { - &.active { - & > button { - cursor: pointer; - - & > .label { - font-weight: 500; - color: var(--text-body-primary); - } - } - } - } - } -} diff --git a/web/src/shared/components/Layout/ListHeader/types.ts b/web/src/shared/components/Layout/ListHeader/types.ts deleted file mode 100644 index 8a7281522..000000000 --- a/web/src/shared/components/Layout/ListHeader/types.ts +++ /dev/null @@ -1,6 +0,0 @@ -export type ListHeaderColumnConfig = { - label: string; - enabled?: boolean; - sortKey?: keyof T; - key?: string; -}; diff --git a/web/src/shared/components/Layout/ManagementPageLayout/ManagementPageLayout.tsx b/web/src/shared/components/Layout/ManagementPageLayout/ManagementPageLayout.tsx deleted file mode 100644 index 6a8ec0217..000000000 --- a/web/src/shared/components/Layout/ManagementPageLayout/ManagementPageLayout.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import './style.scss'; - -import { clsx } from 'clsx'; - -import { useNavigationStore } from '../../../../components/Navigation/hooks/useNavigationStore'; -import { Search } from '../../../defguard-ui/components/Layout/Search/Search'; -import type { ManagementPageProps } from './types'; - -export const ManagementPageLayout = ({ - children, - title, - actions, - itemsCount, - search, - id, -}: ManagementPageProps) => { - const navOpen = useNavigationStore((s) => s.isOpen); - return ( -
-
-
-

{title}

- {search && ( - - )} -
-
- {itemsCount && ( -
-

{itemsCount.label}

- {itemsCount.itemsCount !== undefined && ( -
- {itemsCount.itemsCount} -
- )} -
- )} -
{actions}
-
-
{children}
-
-
- ); -}; diff --git a/web/src/shared/components/Layout/ManagementPageLayout/style.scss b/web/src/shared/components/Layout/ManagementPageLayout/style.scss deleted file mode 100644 index becdb95a1..000000000 --- a/web/src/shared/components/Layout/ManagementPageLayout/style.scss +++ /dev/null @@ -1,80 +0,0 @@ -.management-page { - box-sizing: border-box; - width: 100%; - padding-top: 64px; - padding-left: 88px; - max-height: 100dvh; - overflow-y: auto; - - &.nav-open { - padding-left: 230px; - } - - & > .page-content { - & > header { - display: flex; - flex-flow: row wrap; - align-items: center; - justify-content: flex-start; - gap: 30px; - width: 100%; - box-sizing: border-box; - padding: 0 70px 48px; - - h1 { - @include typography(app-title); - } - - .search { - min-width: 360px; - max-width: 100%; - } - } - - & > .top-bar { - flex-flow: row; - display: flex; - box-sizing: border-box; - padding: 0 70px 30px; - width: 100%; - - .items-count { - display: flex; - flex-flow: row; - flex-grow: 0; - align-items: center; - justify-content: flex-start; - column-gap: 10px; - - & > p { - @include typography(app-body-1); - } - - .count-box { - display: flex; - align-items: center; - flex-flow: row; - align-items: center; - justify-content: center; - flex-grow: 0; - box-sizing: border-box; - padding: 0 8px; - height: 30px; - min-width: 30px; - background-color: var(--text-body-tertiary); - user-select: none; - border-radius: 35%; - - span { - color: var(--surface-frame-bg); - @include typography(app-number); - } - } - } - - & > .actions { - margin-left: auto; - } - } - } -} diff --git a/web/src/shared/components/Layout/ManagementPageLayout/types.ts b/web/src/shared/components/Layout/ManagementPageLayout/types.ts deleted file mode 100644 index 51d0ac054..000000000 --- a/web/src/shared/components/Layout/ManagementPageLayout/types.ts +++ /dev/null @@ -1,21 +0,0 @@ -import type { ReactNode } from 'react'; - -export type ManagementPageProps = { - children: ReactNode; - title: string; - search?: ManagementPageSearch; - actions?: ReactNode; - itemsCount?: ManagementPageItemsCount; - id?: string; -}; - -export type ManagementPageItemsCount = { - itemsCount?: number; - label: string; -}; - -export type ManagementPageSearch = { - onSearch: (searchValue: string) => void; - placeholder?: string; - loading?: boolean; -}; diff --git a/web/src/shared/components/Layout/PageContainer/PageContainer.tsx b/web/src/shared/components/Layout/PageContainer/PageContainer.tsx deleted file mode 100644 index 523f60366..000000000 --- a/web/src/shared/components/Layout/PageContainer/PageContainer.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import './style.scss'; - -import clsx from 'clsx'; -import type { ComponentProps } from 'react'; -import { useNavigationStore } from '../../../../components/Navigation/hooks/useNavigationStore'; - -type Props = { - withDefaultPadding?: boolean; -} & ComponentProps<'div'>; - -export const PageContainer = ({ - children, - className, - ref, - withDefaultPadding = false, - ...rest -}: Props) => { - const isNavOpen = useNavigationStore((state) => state.isOpen); - return ( -
-
- {children} -
-
- ); -}; diff --git a/web/src/shared/components/Layout/PageContainer/style.scss b/web/src/shared/components/Layout/PageContainer/style.scss deleted file mode 100644 index 2c1b17be2..000000000 --- a/web/src/shared/components/Layout/PageContainer/style.scss +++ /dev/null @@ -1,38 +0,0 @@ -@use '@scssutils' as *; - -.page-container { - min-height: inherit; - - @include media-breakpoint-down(lg) { - height: 100%; - max-height: inherit; - box-sizing: border-box; - position: relative; - } - - @include media-breakpoint-up(lg) { - margin-left: 0; - width: 100%; - height: inherit; - max-height: inherit; - - & > .page-content { - width: calc(100% - 87px); - margin-left: 87px; - - &.nav-open { - margin-left: 230px; - width: calc(100% - 230px); - } - } - } -} - -.page-container > .page-content.default-padding { - box-sizing: border-box; - padding: var(--spacing-m) var(--spacing-s) var(--spacing-m); - - @include media-breakpoint-up(lg) { - padding: var(--spacing-xl) var(--spacing-xl) var(--spacing-m); - } -} diff --git a/web/src/shared/components/Layout/PageLayout/PageLayout.tsx b/web/src/shared/components/Layout/PageLayout/PageLayout.tsx deleted file mode 100644 index b8c4880a5..000000000 --- a/web/src/shared/components/Layout/PageLayout/PageLayout.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import './style.scss'; - -import clsx from 'clsx'; -import type { PropsWithChildren } from 'react'; - -import { PageContainer } from '../PageContainer/PageContainer'; - -type Props = { - id: string; - className?: string; - withDefaultPadding?: boolean; -} & PropsWithChildren; - -export const PageLayout = ({ - id, - className, - children, - withDefaultPadding = false, -}: Props) => { - return ( - - {children} - - ); -}; diff --git a/web/src/shared/components/Layout/PageLayout/style.scss b/web/src/shared/components/Layout/PageLayout/style.scss deleted file mode 100644 index 550dbdeb6..000000000 --- a/web/src/shared/components/Layout/PageLayout/style.scss +++ /dev/null @@ -1,26 +0,0 @@ -.page-layout { - & > .page-content { - box-sizing: border-box; - padding: var(--spacing-s); - - @include media-breakpoint-up(md) { - padding: var(--spacing-m); - } - - @include media-breakpoint-up(lg) { - padding: var(--spacing-xl); - } - - h1 { - @include typography(app-title); - } - - h2 { - @include typography(app-body-1); - } - - h3 { - @include typography(app-side-bar); - } - } -} diff --git a/web/src/shared/components/Layout/PageLimiter/PageLimiter.tsx b/web/src/shared/components/Layout/PageLimiter/PageLimiter.tsx deleted file mode 100644 index c34347a1f..000000000 --- a/web/src/shared/components/Layout/PageLimiter/PageLimiter.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import './style.scss'; - -import clsx from 'clsx'; -import type { HTMLAttributes, PropsWithChildren } from 'react'; - -import { useNavigationStore } from '../../../../components/Navigation/hooks/useNavigationStore'; - -type Props = PropsWithChildren & HTMLAttributes; - -export const PageLimiter = ({ children, className, ...divProps }: Props) => { - const navOpen = useNavigationStore((s) => s.isOpen); - - return ( -
-
{children}
-
- ); -}; diff --git a/web/src/shared/components/Layout/PageLimiter/style.scss b/web/src/shared/components/Layout/PageLimiter/style.scss deleted file mode 100644 index a4ffd1161..000000000 --- a/web/src/shared/components/Layout/PageLimiter/style.scss +++ /dev/null @@ -1,20 +0,0 @@ -.page-limiter { - width: 100%; - display: flex; - flex-flow: row; - align-items: center; - justify-content: center; - box-sizing: border-box; - padding: var(--spacing-l) var(--spacing-s); - - & > .page-limited-content { - width: 100%; - max-width: calc(var(--page-content-limit) + 142px); - } - - &.nav-open { - & > .page-limited-content { - max-width: var(--page-content-limit); - } - } -} diff --git a/web/src/shared/components/Layout/RenderMarkdown/RenderMarkdown.tsx b/web/src/shared/components/Layout/RenderMarkdown/RenderMarkdown.tsx deleted file mode 100644 index 5c9aaa6e8..000000000 --- a/web/src/shared/components/Layout/RenderMarkdown/RenderMarkdown.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import Markdown from 'react-markdown'; -import rehypeExternalLinks from 'rehype-external-links'; -import rehypeRaw from 'rehype-raw'; -import rehypeSanitize, { defaultSchema } from 'rehype-sanitize'; - -type Props = { - content: string; -}; - -const sanitizeSchema = { - ...defaultSchema, - tagNames: [...(defaultSchema.tagNames ?? []), 'div', 'br'], // Allow
tags - attributes: { - ...defaultSchema.attributes, - div: ['style'], // Allow `style` attribute on
- a: ['href', 'target'], // Allow `href` and `target` on - }, -}; - -export const RenderMarkdown = ({ content }: Props) => { - const parse = (): string => { - const lines = content.split(/\r?\n/); - - // Trim all lines and handle empty lines - const processedLines = lines.map((line) => { - const trimmedLine = line.trim(); - return trimmedLine === '' ? '' : trimmedLine; // Replace empty lines with empty strings - }); - - // Remove the first line if it's empty - if (processedLines.length > 0 && processedLines[0] === '') { - processedLines.shift(); - } - - // Remove the last line if it's empty - if (processedLines.length > 0 && processedLines[processedLines.length - 1] === '') { - processedLines.pop(); - } - - return processedLines.join('\n'); - }; - return ( - - {parse()} - - ); -}; diff --git a/web/src/shared/components/Layout/SectionWithCard/SectionWithCard.tsx b/web/src/shared/components/Layout/SectionWithCard/SectionWithCard.tsx deleted file mode 100644 index b49170f7a..000000000 --- a/web/src/shared/components/Layout/SectionWithCard/SectionWithCard.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import './style.scss'; - -import clsx from 'clsx'; -import type { HTMLAttributes, PropsWithChildren } from 'react'; - -import { Card } from '../../../defguard-ui/components/Layout/Card/Card'; - -type Props = { title: string } & HTMLAttributes & PropsWithChildren; - -export const SectionWithCard = ({ - title, - className, - children, - ...containerProps -}: Props) => { - return ( -
-

{title}

- {children} -
- ); -}; diff --git a/web/src/shared/components/Layout/SectionWithCard/style.scss b/web/src/shared/components/Layout/SectionWithCard/style.scss deleted file mode 100644 index e8d45a77b..000000000 --- a/web/src/shared/components/Layout/SectionWithCard/style.scss +++ /dev/null @@ -1,12 +0,0 @@ -.section-with-title { - & > h2 { - @include typography(body-1); - padding-bottom: var(--spacing-s); - } - - & > .card { - box-sizing: border-box; - width: 100%; - padding: var(--spacing-m); - } -} diff --git a/web/src/shared/components/Layout/UpgradeLicenseModal/UpgradeLicenseModal.tsx b/web/src/shared/components/Layout/UpgradeLicenseModal/UpgradeLicenseModal.tsx deleted file mode 100644 index b1347d232..000000000 --- a/web/src/shared/components/Layout/UpgradeLicenseModal/UpgradeLicenseModal.tsx +++ /dev/null @@ -1,135 +0,0 @@ -import './style.scss'; - -import { useId, useMemo } from 'react'; -import { shallow } from 'zustand/shallow'; - -import { useI18nContext } from '../../../../i18n/i18n-react'; -import { Button } from '../../../defguard-ui/components/Layout/Button/Button'; -import { - ButtonSize, - ButtonStyleVariant, -} from '../../../defguard-ui/components/Layout/Button/types'; -import { Modal } from '../../../defguard-ui/components/Layout/modals/Modal/Modal'; -import { useAuthStore } from '../../../hooks/store/useAuthStore'; -import { externalLink } from '../../../links'; -import { RenderMarkdown } from '../RenderMarkdown/RenderMarkdown'; -import checkboxUrl from './checkbox.svg?url'; -import { useUpgradeLicenseModal } from './store'; -import { UpgradeLicenseModalVariant } from './types'; - -export const UpgradeLicenseModal = () => { - const isAdmin = useAuthStore((s) => s.user?.is_admin ?? false); - const isOpen = useUpgradeLicenseModal((s) => s.visible); - const [close, reset] = useUpgradeLicenseModal((s) => [s.close, s.reset], shallow); - - return ( - - - - ); -}; - -const ModalContent = () => { - const variant = useUpgradeLicenseModal((s) => s.modalVariant); - const { LL } = useI18nContext(); - const localLL = LL.modals.upgradeLicenseModal; - const close = useUpgradeLicenseModal((s) => s.close, shallow); - - const [title, subtitle] = useMemo(() => { - switch (variant) { - case UpgradeLicenseModalVariant.ENTERPRISE_NOTICE: - return [localLL.enterprise.title(), localLL.enterprise.subTitle()]; - case UpgradeLicenseModalVariant.LICENSE_LIMIT: - return [localLL.limit.title(), localLL.limit.subTitle()]; - } - }, [localLL.enterprise, localLL.limit, variant]); - - return ( - <> -
-

{title}

-
-
- -
-
- - -
-
- - ); -}; - -const DecoratorSvg = () => { - const id = useId(); - return ( - - - - - - - - - - - - ); -}; diff --git a/web/src/shared/components/Layout/UpgradeLicenseModal/checkbox.svg b/web/src/shared/components/Layout/UpgradeLicenseModal/checkbox.svg deleted file mode 100644 index dfdbc53ab..000000000 --- a/web/src/shared/components/Layout/UpgradeLicenseModal/checkbox.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/web/src/shared/components/Layout/UpgradeLicenseModal/store.tsx b/web/src/shared/components/Layout/UpgradeLicenseModal/store.tsx deleted file mode 100644 index cc6d8fc83..000000000 --- a/web/src/shared/components/Layout/UpgradeLicenseModal/store.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import dayjs from 'dayjs'; -import { persist } from 'zustand/middleware'; -import { createWithEqualityFn } from 'zustand/traditional'; - -import { UpgradeLicenseModalVariant } from './types'; - -//minutes -const modalTimeout = 30; - -const defaults: StoreValues = { - visible: false, - modalVariant: UpgradeLicenseModalVariant.ENTERPRISE_NOTICE, - lastClosed: undefined, -}; - -export const useUpgradeLicenseModal = createWithEqualityFn()( - persist( - (set, get) => ({ - ...defaults, - open: ({ modalVariant }) => { - const { lastClosed } = get(); - if ( - lastClosed !== undefined && - modalVariant === UpgradeLicenseModalVariant.LICENSE_LIMIT - ) { - const past = dayjs(lastClosed).utc(); - const now = dayjs().utc(); - const diff = now.diff(past, 'minutes'); - if (diff >= modalTimeout) { - set({ visible: true, modalVariant }); - } - } else { - set({ visible: true, modalVariant }); - } - }, - close: () => { - const { modalVariant } = get(); - if (modalVariant === UpgradeLicenseModalVariant.LICENSE_LIMIT) { - set({ visible: false, lastClosed: dayjs().utc().toISOString() }); - } else { - set({ visible: false }); - } - }, - reset: () => - set({ visible: defaults.visible, modalVariant: defaults.modalVariant }), - }), - { - name: 'upgrade-license-modal', - version: 1, - partialize: (s) => ({ lastOpened: s.lastClosed }), - }, - ), - Object.is, -); - -type Store = StoreValues & StoreMethods; -type StoreValues = { - visible: boolean; - modalVariant: UpgradeLicenseModalVariant; - lastClosed?: string; -}; -type Open = Pick; - -type StoreMethods = { - open: (modalVariant: Open) => void; - close: () => void; - reset: () => void; -}; diff --git a/web/src/shared/components/Layout/UpgradeLicenseModal/style.scss b/web/src/shared/components/Layout/UpgradeLicenseModal/style.scss deleted file mode 100644 index 0c767f6ee..000000000 --- a/web/src/shared/components/Layout/UpgradeLicenseModal/style.scss +++ /dev/null @@ -1,130 +0,0 @@ -.modal { - &.upgrade-license-modal { - @include media-breakpoint-up(lg) { - padding: 154px 0 40px !important; - align-items: center; - justify-items: center; - } - } -} - -#upgrade-license-modal-content { - background-color: var(--surface-nav-bg); - max-width: 1026px; - width: 100%; - overflow: hidden; - - @include media-breakpoint-down(lg) { - min-height: 100dvh; - } - - @include media-breakpoint-up(lg) { - box-shadow: 0px 12px 24px 0px rgba(0, 0, 0, 0.08); - border-radius: 15px; - } - - & > .title { - display: flex; - flex-flow: row; - align-content: center; - align-items: center; - justify-content: center; - box-sizing: border-box; - - padding: 25px 20px 10px; - - @include media-breakpoint-up(lg) { - padding: 56px 20px 10px; - } - - p { - @include typography(app-title); - } - } - - & > .image-container { - align-items: center; - display: flex; - flex-flow: row; - justify-content: center; - - padding-bottom: 20px; - - @include media-breakpoint-up(lg) { - padding-bottom: 45px; - } - } - - .controls { - width: 100%; - box-sizing: border-box; - display: flex; - align-items: center; - justify-content: center; - padding: 0 20px 20px; - flex-flow: column-reverse; - gap: 18px; - - @include media-breakpoint-up(lg) { - padding: 0 50px 50px; - flex-flow: row; - } - - .btn, - a { - width: 100%; - } - - a { - text-decoration: none; - } - } - - & > .content { - box-sizing: border-box; - padding: 0 20px 40px; - - & > :nth-child(1) { - padding-bottom: 20px; - } - - @include media-breakpoint-up(lg) { - padding: 0 50px 40px; - - & > :nth-child(1) { - padding-bottom: 40px; - } - } - - p { - @include typography(app-welcome-2); - } - - ul { - padding-left: 28px; - list-style: none; - padding-bottom: 32px; - - @include media-breakpoint-up(lg) { - padding-bottom: 40px; - } - - li { - position: relative; - @include typography(app-welcome-2); - - &::before { - user-select: none; - position: absolute; - left: -24px; - top: 50%; - transform: translateY(-50%); - content: ' '; - width: 18px; - height: 18px; - background: var(--list-image); - } - } - } - } -} diff --git a/web/src/shared/components/Layout/UpgradeLicenseModal/types.ts b/web/src/shared/components/Layout/UpgradeLicenseModal/types.ts deleted file mode 100644 index 0f52624b3..000000000 --- a/web/src/shared/components/Layout/UpgradeLicenseModal/types.ts +++ /dev/null @@ -1,6 +0,0 @@ -export enum UpgradeLicenseModalVariant { - // when user try to use feature locked behind enterprise - ENTERPRISE_NOTICE, - // when current limits - LICENSE_LIMIT, -} diff --git a/web/src/shared/components/Layout/VersionUpdateToast/VersionUpdateToast.tsx b/web/src/shared/components/Layout/VersionUpdateToast/VersionUpdateToast.tsx deleted file mode 100644 index 79d40db00..000000000 --- a/web/src/shared/components/Layout/VersionUpdateToast/VersionUpdateToast.tsx +++ /dev/null @@ -1,112 +0,0 @@ -import './style.scss'; - -import dayjs from 'dayjs'; -import { useCallback, useEffect } from 'react'; - -import { useI18nContext } from '../../../../i18n/i18n-react'; -import type { ToastOptions } from '../../../defguard-ui/components/Layout/ToastManager/Toast/types'; -import { useToastsStore } from '../../../defguard-ui/hooks/toasts/useToastStore'; -import { useUpdatesStore } from '../../../hooks/store/useUpdatesStore'; - -export const VersionUpdateToast = ({ id }: ToastOptions) => { - const removeToast = useToastsStore((s) => s.removeToast); - const updateData = useUpdatesStore((s) => s.update); - const dismissal = useUpdatesStore((s) => s.dismissal); - const setUpdateStore = useUpdatesStore((s) => s.setStore); - const { LL } = useI18nContext(); - - const closeToast = useCallback(() => { - removeToast(id); - }, [id, removeToast]); - - const handleOpenModal = () => { - setUpdateStore({ modalVisible: true }); - closeToast(); - }; - - const handleDismiss = () => { - if (updateData) { - setUpdateStore({ - dismissal: { - dismissedAt: dayjs.utc().toISOString(), - version: updateData.version, - }, - }); - closeToast(); - } - }; - - useEffect(() => { - if (dismissal && dismissal.version === updateData?.version) { - closeToast(); - } - }, [closeToast, dismissal, updateData?.version]); - - if (!updateData) return null; - - return ( -
-
-

- {LL.modals.updatesNotificationToaster.title({ - version: updateData.version, - })} -

- {updateData.critical && ( - - - - - )} - - - - - - -
-
- - -
-
- ); -}; diff --git a/web/src/shared/components/Layout/VersionUpdateToast/style.scss b/web/src/shared/components/Layout/VersionUpdateToast/style.scss deleted file mode 100644 index a8f777be7..000000000 --- a/web/src/shared/components/Layout/VersionUpdateToast/style.scss +++ /dev/null @@ -1,63 +0,0 @@ -.update-toaster { - box-sizing: border-box; - padding: 10px 20px 15px; - box-shadow: 0px 6px 10px 0px rgba(0, 0, 0, 0.1); - border-radius: 10px; - background-color: var(--surface-nav-bg); - border-radius: 15px; - - min-width: 270px; - max-width: 100%; - - .top { - padding-bottom: 8px; - display: flex; - align-items: center; - flex-flow: row nowrap; - min-width: 230px; - column-gap: 8px; - - p { - color: var(--text-body-primary); - text-wrap: nowrap; - white-space: none; - @include typography(app-number); - } - - & > a { - margin-left: auto; - border: none; - background-color: transparent; - cursor: pointer; - width: 22px; - height: 22px; - display: inline-flex; - flex-flow: row; - align-content: center; - align-items: center; - justify-content: center; - padding: 4px; - box-sizing: border-box; - text-decoration: none; - } - } - - .bottom { - min-width: 230px; - display: flex; - flex-flow: row; - align-items: center; - justify-content: space-between; - - & > * { - cursor: pointer; - } - - button { - border: none; - background: transparent; - color: var(--text-body-primary); - @include typography(app-copyright); - } - } -} diff --git a/web/src/shared/components/Layout/VersionUpdateToast/types.ts b/web/src/shared/components/Layout/VersionUpdateToast/types.ts deleted file mode 100644 index 8ea128bf9..000000000 --- a/web/src/shared/components/Layout/VersionUpdateToast/types.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { z } from 'zod'; - -export const versionUpdateToastMetaSchema = z.object({ - customId: z.string().trim().min(1), -}); - -export type VersionUpdateToastMeta = z.infer; diff --git a/web/src/shared/components/Layout/WireguardConfigExpandable/WireguardConfigExpandable.tsx b/web/src/shared/components/Layout/WireguardConfigExpandable/WireguardConfigExpandable.tsx deleted file mode 100644 index 998b026f1..000000000 --- a/web/src/shared/components/Layout/WireguardConfigExpandable/WireguardConfigExpandable.tsx +++ /dev/null @@ -1,113 +0,0 @@ -import './style.scss'; - -import { Fragment, type ReactNode, useCallback, useMemo, useState } from 'react'; -import QRCode from 'react-qr-code'; - -import { useI18nContext } from '../../../../i18n/i18n-react'; -import { ActionButton } from '../../../defguard-ui/components/Layout/ActionButton/ActionButton'; -import { ActionButtonVariant } from '../../../defguard-ui/components/Layout/ActionButton/types'; -import { ExpandableCard } from '../../../defguard-ui/components/Layout/ExpandableCard/ExpandableCard'; -import { useClipboard } from '../../../hooks/useClipboard'; -import { downloadWGConfig } from '../../../utils/downloadWGConfig'; - -type Props = { - config: string; - publicKey: string; - deviceName: string; - privateKey?: string; -}; - -enum ConfigCardView { - FILE, - QR, -} - -export const WireguardConfigExpandable = ({ - config, - deviceName, - publicKey, - privateKey, -}: Props) => { - const { LL } = useI18nContext(); - const { writeToClipboard } = useClipboard(); - const localLL = LL.modals.addStandaloneDevice.steps.manual.finish; - const [view, setView] = useState(ConfigCardView.FILE); - - const configForExport = useMemo(() => { - if (privateKey) { - return config.replace('YOUR_PRIVATE_KEY', privateKey); - } - return config; - }, [config, privateKey]); - - const getQRConfig = useMemo((): string => { - if (privateKey) { - return config.replace('YOUR_PRIVATE_KEY', privateKey); - } - return config.replace('YOUR_PRIVATE_KEY', publicKey); - }, [config, privateKey, publicKey]); - - const renderTextConfig = () => { - const content = configForExport.split('\n'); - return ( -

- {content.map((text, index) => ( - - {text} - {index !== content.length - 1 &&
} -
- ))} -

- ); - }; - - const handleConfigCopy = useCallback(() => { - void writeToClipboard( - configForExport, - LL.components.deviceConfigsCard.messages.copyConfig(), - ); - }, [LL.components.deviceConfigsCard.messages, configForExport, writeToClipboard]); - - const handleConfigDownload = useCallback(() => { - downloadWGConfig(configForExport, deviceName.toLowerCase().replace(' ', '-')); - }, [configForExport, deviceName]); - - const actions = useMemo( - (): ReactNode[] => [ - setView(ConfigCardView.FILE)} - />, - setView(ConfigCardView.QR)} - />, - , - , - ], - [handleConfigCopy, handleConfigDownload, view], - ); - return ( - - {view === ConfigCardView.FILE && renderTextConfig()} - {view === ConfigCardView.QR && } - - ); -}; diff --git a/web/src/shared/components/Layout/WireguardConfigExpandable/style.scss b/web/src/shared/components/Layout/WireguardConfigExpandable/style.scss deleted file mode 100644 index 62957360e..000000000 --- a/web/src/shared/components/Layout/WireguardConfigExpandable/style.scss +++ /dev/null @@ -1,18 +0,0 @@ -.wireguard-config-card { - .expanded-content { - display: flex; - flex-flow: column; - align-items: center; - - .config { - width: 100%; - @include typography(app-code); - color: var(--text-button-primary); - - span { - @include typography(app-code); - color: var(--text-button-primary); - } - } - } -} diff --git a/web/src/shared/components/Layout/buttons/AddButton/AddButton.tsx b/web/src/shared/components/Layout/buttons/AddButton/AddButton.tsx deleted file mode 100644 index 66152a268..000000000 --- a/web/src/shared/components/Layout/buttons/AddButton/AddButton.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import './style.scss'; - -import clsx from 'clsx'; - -import { useI18nContext } from '../../../../../i18n/i18n-react'; -import { Button } from '../../../../defguard-ui/components/Layout/Button/Button'; -import { - ButtonSize, - ButtonStyleVariant, -} from '../../../../defguard-ui/components/Layout/Button/types'; -import SvgIconPlusWhite from '../../../svg/IconPlusWhite'; - -type Props = { - onClick?: () => void; - loading?: boolean; - text?: string; - className?: string; -}; - -export const AddButton = ({ loading, onClick, text, className }: Props) => { - const { LL } = useI18nContext(); - const defaultText = LL.common.controls.addNew(); - - return ( -