From b47c9631d1571a3f2014eac81aed930f8d0d9364 Mon Sep 17 00:00:00 2001 From: Andrii Kostenko Date: Thu, 5 Mar 2026 16:13:42 +0000 Subject: [PATCH] biome check --- frontend/scripts/update-version.js | 2 +- .../src/app/animations/toggle.animation.ts | 51 +- frontend/src/app/auth.guard.spec.ts | 23 +- frontend/src/app/auth.guard.ts | 51 +- .../api-keys/api-keys.component.css | 194 +- .../app/components/audit/audit-data-source.ts | 199 +- .../app/components/audit/audit.component.css | 204 +- .../info-dialog/info-dialog.component.css | 55 +- .../info-dialog/info-dialog.component.spec.ts | 347 +- .../info-dialog/info-dialog.component.ts | 119 +- .../branding/branding.component.css | 230 +- .../company-member-invitation.component.css | 42 +- ...ompany-member-invitation.component.spec.ts | 45 +- .../components/company/company.component.css | 372 +- .../delete-domain-dialog.component.spec.ts | 54 +- .../delete-member-dialog.component.spec.ts | 66 +- .../invite-member-dialog.component.css | 41 +- .../invite-member-dialog.component.spec.ts | 58 +- ...revoke-invitation-dialog.component.spec.ts | 56 +- .../connect-db/connect-db.component.css | 194 +- ...db-connection-confirm-dialog.component.css | 56 +- .../db-connection-confirm-dialog.component.ts | 123 +- .../db-connection-delete-dialog.component.css | 14 +- ...-connection-ip-access-dialog.component.css | 20 +- ...nection-ip-access-dialog.component.spec.ts | 46 +- ...b-connection-ip-access-dialog.component.ts | 63 +- .../base-credentials-form.component.css | 42 +- .../base-credentials-form.component.spec.ts | 27 +- .../base-credentials-form.component.ts | 28 +- .../cassandra-credentials-form.component.css | 16 +- ...ssandra-credentials-form.component.spec.ts | 44 +- .../clickhouse-credentials-form.component.css | 16 +- ...ckhouse-credentials-form.component.spec.ts | 54 +- .../db2-credentials-form.component.css | 16 +- .../db2-credentials-form.component.spec.ts | 54 +- .../dynamodb-credentials-form.component.css | 10 +- ...ynamodb-credentials-form.component.spec.ts | 47 +- .../elastic-credentials-form.component.css | 16 +- ...elastic-credentials-form.component.spec.ts | 98 +- .../mongodb-credentials-form.component.css | 16 +- ...mongodb-credentials-form.component.spec.ts | 96 +- .../mssql-credentials-form.component.css | 16 +- .../mssql-credentials-form.component.spec.ts | 54 +- .../mysql-credentials-form.component.css | 16 +- .../mysql-credentials-form.component.spec.ts | 54 +- .../oracledb-credentials-form.component.css | 16 +- ...racledb-credentials-form.component.spec.ts | 54 +- .../postgres-credentials-form.component.css | 16 +- ...ostgres-credentials-form.component.spec.ts | 54 +- .../redis-credentials-form.component.css | 16 +- .../redis-credentials-form.component.spec.ts | 96 +- .../master-encryption-password.component.css | 66 +- ...ster-encryption-password.component.spec.ts | 37 +- .../connection-settings.component.css | 178 +- .../connections-list.component.css | 121 +- .../connections-list.component.spec.ts | 60 +- .../connections-list.component.ts | 138 +- .../demo-connections.component.css | 163 +- .../demo-connections.component.spec.ts | 185 +- .../own-connections.component.css | 95 +- .../own-connections.component.spec.ts | 37 +- .../own-connections.component.ts | 3 +- .../dashboard/dashboard.component.css | 204 +- .../dashboard/dashboard.component.spec.ts | 10 +- .../dashboard/dashboard.component.ts | 4 +- .../db-action-link-dialog.component.css | 2 +- .../db-action-link-dialog.component.spec.ts | 44 +- .../db-action-link-dialog.component.ts | 31 +- ...k-action-confirmation-dialog.component.css | 6 +- ...lk-action-confirmation-dialog.component.ts | 153 +- .../action-delete-dialog.component.spec.ts | 55 +- .../action-delete-dialog.component.ts | 58 +- .../db-table-ai-panel.component.css | 994 +- .../db-table-export-dialog.component.css | 4 +- .../db-table-export-dialog.component.spec.ts | 82 +- .../db-table-filters-dialog.component.css | 49 +- .../db-table-filters-dialog.component.spec.ts | 349 +- .../db-table-import-dialog.component.css | 38 +- .../db-table-import-dialog.component.spec.ts | 78 +- .../db-table-row-view.component.spec.ts | 40 +- .../db-table-settings.component.css | 233 +- .../db-table-settings.component.spec.ts | 2 +- .../db-table-view/db-table-view.component.css | 4 +- .../db-table-view/db-table-view.component.ts | 2 - .../db-table-widgets.component.css | 84 +- .../db-table-widgets.component.ts | 1 - .../widget-delete-dialog.component.spec.ts | 41 +- .../widget-delete-dialog.component.ts | 23 +- .../widget/widget.component.ts | 176 +- .../widgets-empty-state.component.css | 418 +- .../widgets-empty-state.component.ts | 26 +- .../saved-filters-dialog.component.css | 855 +- .../dashboard/db-tables-data-source.ts | 691 +- .../db-folder-delete-dialog.component.css | 54 +- .../db-folder-delete-dialog.component.ts | 45 +- .../db-folder-edit-dialog.component.css | 266 +- .../db-folder-edit-dialog.component.ts | 176 +- .../db-tables-list.component.css | 7 +- .../db-tables-list.component.spec.ts | 52 +- .../db-tables-list.component.ts | 1201 +- .../dashboards-list.component.css | 49 +- .../dashboards-list.component.ts | 2 +- .../dashboards-sidebar.component.css | 2 +- .../dashboards-sidebar.component.ts | 17 +- .../db-table-row-edit.component.css | 2 +- .../email-change/email-change.component.css | 32 +- .../email-change/email-change.component.ts | 90 +- .../email-verification.component.css | 20 +- .../email-verification.component.ts | 70 +- .../feature-notification.component.css | 60 +- .../feature-notification.component.spec.ts | 34 +- .../feature-notification.component.ts | 13 +- .../login/sso-dialog/sso-dialog.component.css | 4 +- .../sso-dialog/sso-dialog.component.spec.ts | 27 +- .../master-password-dialog.component.css | 4 +- .../master-password-dialog.component.spec.ts | 61 +- .../master-password-dialog.component.ts | 52 +- .../page-loader/page-loader.component.spec.ts | 35 +- .../page-loader/page-loader.component.ts | 19 +- .../page-not-found.component.css | 86 +- .../page-not-found.component.spec.ts | 30 +- .../page-not-found.component.ts | 11 +- .../password-change.component.css | 32 +- .../password-request.component.css | 114 +- .../password-request.component.ts | 100 +- .../password-reset.component.css | 62 +- .../password-reset.component.spec.ts | 57 +- .../password-reset.component.ts | 90 +- .../payment-form/payment-form.component.css | 26 +- .../payment-form.component.spec.ts | 63 +- .../payment-form/payment-form.component.ts | 310 +- .../profile-sidebar.component.css | 2 +- .../profile-sidebar.component.ts | 23 +- .../registration/registration.component.css | 10 +- .../audit-log-dialog.component.css | 160 +- .../audit-log-dialog.component.ts | 152 +- .../create-secret-dialog.component.css | 42 +- .../delete-secret-dialog.component.css | 46 +- .../edit-secret-dialog.component.css | 62 +- .../master-password-dialog.component.css | 34 +- .../master-password-dialog.component.ts | 62 +- .../components/secrets/secrets.component.css | 318 +- .../secrets/secrets.component.spec.ts | 8 +- .../placeholder-add-user-dialog.component.css | 38 +- ...ceholder-add-user-dialog.component.spec.ts | 26 +- .../placeholder-add-user-dialog.component.ts | 10 +- .../placeholder-api-keys-list.component.css | 38 +- .../placeholder-api-keys-list.component.ts | 14 +- .../placeholder-audit-log.component.spec.ts | 27 +- .../placeholder-audit-log.component.ts | 10 +- .../placeholder-branding.component.css | 96 +- .../placeholder-branding.component.ts | 10 +- ...aceholder-company-invitation.component.css | 50 +- ...older-company-invitation.component.spec.ts | 27 +- ...laceholder-company-invitation.component.ts | 14 +- .../placeholder-company.component.css | 80 +- .../placeholder-company.component.spec.ts | 27 +- .../placeholder-company.component.ts | 15 +- ...ceholder-connection-settings.component.css | 69 +- ...lder-connection-settings.component.spec.ts | 27 +- ...aceholder-connection-settings.component.ts | 10 +- .../placeholder-connections.component.css | 46 +- .../placeholder-connections.component.spec.ts | 27 +- .../placeholder-connections.component.ts | 10 +- .../placeholder-record-view.component.css | 46 +- .../placeholder-record-view.component.spec.ts | 27 +- .../placeholder-record-view.component.ts | 12 +- .../placeholder-row-edit.component.css | 81 +- .../placeholder-row-edit.component.spec.ts | 27 +- .../placeholder-row-edit.component.ts | 12 +- .../placeholder-saved-filters.component.css | 30 +- ...laceholder-saved-filters.component.spec.ts | 27 +- .../placeholder-saved-filters.component.ts | 10 +- .../placeholder-sso.component.css | 76 +- .../placeholder-sso.component.ts | 10 +- .../placeholder-table-data.component.css | 18 +- .../placeholder-table-data.component.spec.ts | 27 +- .../placeholder-table-data.component.ts | 11 +- .../placeholder-table-settings.component.css | 140 +- ...aceholder-table-settings.component.spec.ts | 27 +- .../placeholder-table-settings.component.ts | 12 +- .../placeholder-table-view.component.css | 50 +- .../placeholder-table-view.component.spec.ts | 27 +- .../placeholder-table-view.component.ts | 12 +- .../placeholder-table-widgets.component.css | 85 +- ...laceholder-table-widgets.component.spec.ts | 27 +- .../placeholder-table-widgets.component.ts | 12 +- .../placeholder-user-group.component.css | 18 +- .../placeholder-user-group.component.spec.ts | 27 +- .../placeholder-user-group.component.ts | 10 +- .../placeholder-user-groups.component.css | 36 +- .../placeholder-user-groups.component.spec.ts | 27 +- .../placeholder-user-groups.component.ts | 10 +- .../src/app/components/sso/sso.component.css | 102 +- .../app/components/sso/sso.component.spec.ts | 5 +- .../src/app/components/sso/sso.component.ts | 17 +- .../ui-components/alert/alert.component.css | 137 +- .../ui-components/alert/alert.component.ts | 95 +- .../ui-components/banner/banner.component.css | 95 +- .../banner/banner.component.spec.ts | 39 +- .../ui-components/banner/banner.component.ts | 11 +- .../breadcrumbs/breadcrumbs.component.css | 20 +- .../breadcrumbs/breadcrumbs.component.spec.ts | 27 +- .../breadcrumbs/breadcrumbs.component.ts | 22 +- .../content-loader.component.css | 34 +- .../content-loader.component.spec.ts | 30 +- .../content-loader.component.ts | 11 +- .../base-filter-field.component.spec.ts | 26 +- .../base-filter-field.component.ts | 34 +- .../binary-data-caption.component.css | 12 +- .../binary-data-caption.component.spec.ts | 30 +- .../binary-data-caption.component.ts | 12 +- .../boolean/boolean.component.spec.ts | 126 +- .../boolean/boolean.component.ts | 144 +- .../country/country.component.css | 20 +- .../country/country.component.ts | 129 +- .../date-time/date-time.component.css | 8 +- .../date-time/date-time.component.ts | 87 +- .../filter-fields/date/date.component.css | 4 +- .../filter-fields/date/date.component.ts | 57 +- .../filter-fields/file/file.component.css | 28 +- .../filter-fields/file/file.component.spec.ts | 38 +- .../filter-fields/file/file.component.ts | 12 +- .../foreign-key/foreign-key.component.css | 32 +- .../filter-fields/id/id.component.css | 4 +- .../filter-fields/id/id.component.spec.ts | 32 +- .../filter-fields/id/id.component.ts | 15 +- .../json-editor/json-editor.component.css | 14 +- .../json-editor/json-editor.component.ts | 47 +- .../long-text/long-text.component.css | 6 +- .../long-text/long-text.component.spec.ts | 33 +- .../long-text/long-text.component.ts | 52 +- .../filter-fields/number/number.component.css | 4 +- .../number/number.component.spec.ts | 33 +- .../filter-fields/number/number.component.ts | 33 +- .../password/password.component.css | 6 +- .../password/password.component.ts | 49 +- .../filter-fields/point/point.component.css | 10 +- .../point/point.component.spec.ts | 33 +- .../filter-fields/point/point.component.ts | 15 +- .../filter-fields/select/select.component.css | 4 +- .../select/select.component.spec.ts | 33 +- .../filter-fields/select/select.component.ts | 56 +- .../static-text/static-text.component.css | 12 +- .../static-text/static-text.component.spec.ts | 30 +- .../static-text/static-text.component.ts | 8 +- .../filter-fields/text/text.component.css | 4 +- .../filter-fields/text/text.component.spec.ts | 33 +- .../filter-fields/text/text.component.ts | 33 +- .../time-interval/time-interval.component.css | 12 +- .../time-interval.component.spec.ts | 33 +- .../time-interval/time-interval.component.ts | 128 +- .../filter-fields/time/time.component.css | 4 +- .../filter-fields/time/time.component.spec.ts | 33 +- .../filter-fields/time/time.component.ts | 17 +- .../timezone/timezone.component.css | 2 +- .../timezone/timezone.component.ts | 122 +- .../icon-picker/icon-picker.component.css | 89 +- .../icon-picker/icon-picker.component.spec.ts | 34 +- .../icon-picker/icon-picker.component.ts | 72 +- .../ip-address-button.component.css | 12 +- .../ip-address-button.component.spec.ts | 27 +- .../ip-address-button.component.ts | 26 +- .../base-row-field.component.spec.ts | 27 +- .../base-row-field.component.ts | 37 +- .../binary-data-caption.component.css | 12 +- .../binary-data-caption.component.spec.ts | 30 +- .../binary-data-caption.component.ts | 12 +- .../boolean/boolean.component.spec.ts | 166 +- .../boolean/boolean.component.ts | 104 +- .../code/code.component.css | 22 +- .../record-edit-fields/code/code.component.ts | 57 +- .../color/color.component.css | 33 +- .../color/color.component.ts | 239 +- .../country/country.component.css | 40 +- .../date-time/date-time.component.css | 8 +- .../date-time/date-time.component.ts | 116 +- .../date/date.component.css | 4 +- .../record-edit-fields/date/date.component.ts | 41 +- .../file/file.component.css | 34 +- .../file/file.component.spec.ts | 38 +- .../record-edit-fields/file/file.component.ts | 284 +- .../foreign-key/foreign-key.component.css | 51 +- .../record-edit-fields/id/id.component.css | 4 +- .../id/id.component.spec.ts | 32 +- .../image/image.component.css | 14 +- .../image/image.component.spec.ts | 32 +- .../image/image.component.ts | 76 +- .../json-editor/json-editor.component.css | 22 +- .../json-editor/json-editor.component.spec.ts | 2 +- .../json-editor/json-editor.component.ts | 57 +- .../language/language.component.css | 46 +- .../long-text/long-text.component.css | 18 +- .../long-text/long-text.component.spec.ts | 33 +- .../long-text/long-text.component.ts | 122 +- .../markdown/markdown.component.css | 18 +- .../markdown/markdown.component.ts | 57 +- .../money/money.component.css | 70 +- .../money/money.component.ts | 389 +- .../number/number.component.css | 4 +- .../number/number.component.spec.ts | 33 +- .../number/number.component.ts | 15 +- .../password/password.component.css | 6 +- .../password/password.component.ts | 54 +- .../phone/phone.component.css | 70 +- .../point/point.component.css | 10 +- .../point/point.component.spec.ts | 33 +- .../range/range.component.css | 154 +- .../range/range.component.ts | 89 +- .../record-edit-fields/s3/s3.component.css | 62 +- .../select/select.component.css | 4 +- .../select/select.component.spec.ts | 33 +- .../select/select.component.ts | 55 +- .../static-text/static-text.component.css | 12 +- .../static-text/static-text.component.spec.ts | 33 +- .../static-text/static-text.component.ts | 13 +- .../text/text.component.css | 6 +- .../text/text.component.spec.ts | 33 +- .../record-edit-fields/text/text.component.ts | 114 +- .../time-interval/time-interval.component.css | 12 +- .../time-interval.component.spec.ts | 33 +- .../time-interval/time-interval.component.ts | 54 +- .../time/time.component.css | 4 +- .../time/time.component.spec.ts | 33 +- .../record-edit-fields/time/time.component.ts | 17 +- .../timezone/timezone.component.css | 2 +- .../timezone/timezone.component.ts | 120 +- .../record-edit-fields/url/url.component.css | 18 +- .../url/url.component.spec.ts | 33 +- .../record-edit-fields/url/url.component.ts | 68 +- .../uuid/uuid.component.css | 62 +- .../record-edit-fields/uuid/uuid.component.ts | 268 +- .../base-record-view-field.component.css | 30 +- .../binary-data-caption.component.ts | 11 +- .../boolean/boolean.component.css | 30 +- .../boolean/boolean.component.ts | 18 +- .../code/code.component.css | 24 +- .../record-view-fields/code/code.component.ts | 53 +- .../color/color.component.css | 20 +- .../color/color.component.ts | 82 +- .../country/country.component.css | 4 +- .../country/country.component.ts | 72 +- .../date-time/date-time.component.css | 1 - .../date-time/date-time.component.ts | 43 +- .../date/date.component.css | 1 - .../record-view-fields/date/date.component.ts | 42 +- .../file/file.component.css | 4 +- .../record-view-fields/file/file.component.ts | 36 +- .../foreign-key/foreign-key.component.css | 20 +- .../foreign-key/foreign-key.component.ts | 29 +- .../record-view-fields/id/id.component.css | 8 +- .../record-view-fields/id/id.component.ts | 11 +- .../image/image.component.css | 4 +- .../image/image.component.ts | 42 +- .../json-editor/json-editor.component.css | 24 +- .../json-editor/json-editor.component.ts | 52 +- .../language/language.component.css | 4 +- .../long-text/long-text.component.ts | 12 +- .../markdown/markdown.component.css | 92 +- .../markdown/markdown.component.spec.ts | 38 +- .../markdown/markdown.component.ts | 20 +- .../money/money.component.css | 1 - .../money/money.component.ts | 97 +- .../number/number.component.css | 14 +- .../number/number.component.ts | 96 +- .../password/password.component.css | 1 - .../password/password.component.ts | 14 +- .../phone/phone.component.css | 16 +- .../phone/phone.component.ts | 88 +- .../point/point.component.css | 1 - .../point/point.component.ts | 63 +- .../range/range.component.ts | 112 +- .../select/select.component.css | 4 +- .../select/select.component.ts | 56 +- .../static-text/static-text.component.css | 1 - .../static-text/static-text.component.ts | 13 +- .../text/text.component.css | 20 +- .../record-view-fields/text/text.component.ts | 138 +- .../time-interval/time-interval.component.ts | 47 +- .../time/time.component.css | 1 - .../record-view-fields/time/time.component.ts | 53 +- .../timezone/timezone.component.spec.ts | 48 +- .../timezone/timezone.component.ts | 84 +- .../record-view-fields/url/url.component.css | 20 +- .../record-view-fields/url/url.component.ts | 30 +- .../uuid/uuid.component.css | 10 +- .../record-view-fields/uuid/uuid.component.ts | 11 +- .../base-table-display-field.component.css | 30 +- .../binary-data-caption.component.ts | 16 +- .../boolean/boolean.component.css | 30 +- .../boolean/boolean.component.ts | 36 +- .../code/code.component.css | 18 +- .../code/code.component.ts | 13 +- .../color/color.component.css | 20 +- .../color/color.component.ts | 76 +- .../country/country.component.css | 26 +- .../country/country.component.ts | 78 +- .../date-time/date-time.component.css | 1 - .../date-time/date-time.component.ts | 116 +- .../date/date.component.css | 1 - .../date/date.component.ts | 116 +- .../file/file.component.css | 4 +- .../file/file.component.ts | 36 +- .../foreign-key/foreign-key.component.ts | 37 +- .../table-display-fields/id/id.component.ts | 16 +- .../image/image.component.css | 10 +- .../image/image.component.ts | 42 +- .../json-editor/json-editor.component.css | 18 +- .../json-editor/json-editor.component.ts | 28 +- .../language/language.component.css | 26 +- .../long-text/long-text.component.css | 4 +- .../long-text/long-text.component.ts | 17 +- .../markdown/markdown.component.css | 50 +- .../markdown/markdown.component.spec.ts | 182 +- .../markdown/markdown.component.ts | 124 +- .../money/money.component.ts | 99 +- .../number/number.component.ts | 89 +- .../password/password.component.css | 1 - .../password/password.component.ts | 13 +- .../phone/phone.component.css | 20 +- .../phone/phone.component.ts | 90 +- .../point/point.component.css | 20 +- .../point/point.component.ts | 65 +- .../range/range.component.ts | 107 +- .../select/select.component.css | 4 +- .../select/select.component.ts | 61 +- .../static-text/static-text.component.css | 1 - .../static-text/static-text.component.ts | 12 +- .../text/text.component.css | 10 +- .../text/text.component.ts | 144 +- .../time-interval/time-interval.component.ts | 52 +- .../time/time.component.css | 1 - .../time/time.component.ts | 55 +- .../timezone/timezone.component.ts | 84 +- .../url/url.component.css | 18 +- .../table-display-fields/url/url.component.ts | 46 +- .../uuid/uuid.component.css | 34 +- .../uuid/uuid.component.ts | 15 +- .../user-password/user-password.component.css | 20 +- .../user-password.component.spec.ts | 34 +- .../upgrade-success.component.css | 4 +- .../upgrade-success.component.spec.ts | 53 +- .../upgrade-success.component.ts | 26 +- .../components/upgrade/upgrade.component.ts | 434 +- .../user-deleted-success.component.spec.ts | 27 +- .../user-deleted-success.component.ts | 16 +- ...ount-delete-confirmation.component.spec.ts | 61 +- .../account-delete-dialog.component.css | 14 +- .../account-delete-dialog.component.ts | 147 +- .../api-key-delete-dialog.component.spec.ts | 50 +- .../api-key-delete-dialog.component.ts | 51 +- .../enable-two-fa-dialog.component.css | 6 +- .../enable-two-fa-dialog.component.spec.ts | 65 +- .../user-settings/user-settings.component.css | 132 +- .../group-add-dialog.component.css | 4 +- .../group-name-edit-dialog.component.css | 4 +- .../group-name-edit-dialog.component.spec.ts | 54 +- .../group-name-edit-dialog.component.ts | 72 +- .../permissions-add-dialog.component.css | 160 +- .../user-add-dialog.component.css | 22 +- .../user-delete-dialog.component.ts | 60 +- .../app/components/users/users.component.css | 54 +- .../components/zapier/zapier.component.css | 30 +- .../zapier/zapier.component.spec.ts | 32 +- frontend/src/app/consts/acronyms.ts | 11654 ++++++++-------- frontend/src/app/consts/code-snippets.ts | 76 +- frontend/src/app/consts/countries.ts | 518 +- frontend/src/app/consts/currencies.ts | 84 +- frontend/src/app/consts/databases.ts | 48 +- frontend/src/app/consts/filter-types.ts | 537 +- .../src/app/consts/google-IP-addresses.ts | 1058 +- frontend/src/app/consts/languages.ts | 386 +- frontend/src/app/consts/plans.ts | 10 +- frontend/src/app/consts/record-edit-types.ts | 654 +- .../directives/base64Validator.directive.ts | 31 +- .../directives/emailValidator.directive.ts | 32 +- .../app/directives/hexValidator.directive.ts | 31 +- .../directives/hostnameValidator.directive.ts | 35 +- .../ngmat-table-query-reflector.directive.ts | 272 +- .../directives/passwordValidator.directive.ts | 31 +- .../directives/text-validator.directive.ts | 32 +- .../url-validator.directive.spec.ts | 8 +- .../app/directives/url-validator.directive.ts | 26 +- frontend/src/app/lib/format-field-value.ts | 52 +- frontend/src/app/lib/normalize.spec.ts | 36 +- .../src/app/lib/parse-filter-params.spec.ts | 54 +- frontend/src/app/lib/parse-filter-params.ts | 22 +- .../app/lib/setup-table-row-structure.spec.ts | 157 +- .../src/app/lib/setup-table-row-structure.ts | 24 +- frontend/src/app/models/alert.ts | 36 +- frontend/src/app/models/connection.ts | 112 +- frontend/src/app/models/logs.ts | 34 +- frontend/src/app/models/plans.ts | 14 +- frontend/src/app/models/secret.ts | 72 +- frontend/src/app/models/table.ts | 210 +- frontend/src/app/models/ui-settings.ts | 30 +- frontend/src/app/modules/config.module.ts | 31 +- .../app/routes/company-invitation.routes.ts | 15 +- .../src/app/routes/password-change.routes.ts | 13 +- .../src/app/routes/password-reset.routes.ts | 13 +- .../src/app/routes/registration.routes.ts | 13 +- .../src/app/services/company.service.spec.ts | 29 +- .../services/configuration.service.spec.ts | 32 +- .../src/app/services/configuration.service.ts | 12 +- .../app/services/master-password.service.ts | 38 +- .../src/app/services/notifications.service.ts | 90 +- .../src/app/services/payment.service.spec.ts | 26 +- frontend/src/app/services/secrets.service.ts | 203 +- .../app/services/table-state.service.spec.ts | 16 +- .../src/app/services/table-state.service.ts | 174 +- frontend/src/app/services/tables.service.ts | 2 +- .../app/services/token.interceptor.spec.ts | 45 +- .../src/app/services/token.interceptor.ts | 117 +- .../app/services/ui-settings.service.spec.ts | 26 +- .../src/app/services/ui-settings.service.ts | 6 +- .../services/url-params.interceptor.spec.ts | 18 +- .../app/services/url-params.interceptor.ts | 60 +- .../src/app/validators/base64.validator.ts | 26 +- .../src/app/validators/email.validator.ts | 21 +- frontend/src/app/validators/hex.validator.ts | 20 +- .../src/app/validators/hostname.validator.ts | 42 +- frontend/src/app/validators/text.validator.ts | 104 +- frontend/src/app/validators/url.validator.ts | 32 +- frontend/src/config.json | 6 +- frontend/src/environments/environment.prod.ts | 12 +- frontend/src/environments/environment.ts | 10 +- frontend/src/polyfills.ts | 3 +- frontend/src/proxy.conf.json | 12 +- frontend/tslint.json | 143 +- 529 files changed, 22534 insertions(+), 22956 deletions(-) diff --git a/frontend/scripts/update-version.js b/frontend/scripts/update-version.js index efb0b39eb..da1e5207e 100644 --- a/frontend/scripts/update-version.js +++ b/frontend/scripts/update-version.js @@ -13,4 +13,4 @@ export const version = '${version}'; `; fs.writeFileSync(versionTsPath, versionTsContent); -console.log(`Updated version.ts with version ${version}`); \ No newline at end of file +console.log(`Updated version.ts with version ${version}`); diff --git a/frontend/src/app/animations/toggle.animation.ts b/frontend/src/app/animations/toggle.animation.ts index e2925db92..64161dbbb 100644 --- a/frontend/src/app/animations/toggle.animation.ts +++ b/frontend/src/app/animations/toggle.animation.ts @@ -1,38 +1,25 @@ -import { - trigger, - transition, - style, - animate, - query, - group, -} from '@angular/animations'; +import { animate, group, query, style, transition, trigger } from '@angular/animations'; const easing = 'cubic-bezier(0.4, 0.0, 0.2, 1)'; const duration = '600ms'; export const toggleAnimation = trigger('toggle', [ - transition(':enter', [ - style({ height: 0, opacity: 0 }), - query('.details', [ - style({ transform: 'translateY(-100%)' }) - ], { optional: true }), - group([ - animate(`${duration} ${easing}`, style({ height: '*', opacity: 1 })), - query('.details', [ - animate(`${duration} ${easing}`, style({ transform: 'translateY(0)' })) - ], { optional: true }) - ]) - ]), - transition(':leave', [ - style({ height: '*', opacity: 1 }), - query('.details', [ - style({ transform: 'translateY(0)' }) - ], { optional: true }), - group([ - animate(`${duration} ${easing}`, style({ height: 0, opacity: 0 })), - query('.details', [ - animate(`${duration} ${easing}`, style({ transform: 'translateY(-100%)' })) - ], { optional: true }) - ]) - ]) + transition(':enter', [ + style({ height: 0, opacity: 0 }), + query('.details', [style({ transform: 'translateY(-100%)' })], { optional: true }), + group([ + animate(`${duration} ${easing}`, style({ height: '*', opacity: 1 })), + query('.details', [animate(`${duration} ${easing}`, style({ transform: 'translateY(0)' }))], { optional: true }), + ]), + ]), + transition(':leave', [ + style({ height: '*', opacity: 1 }), + query('.details', [style({ transform: 'translateY(0)' })], { optional: true }), + group([ + animate(`${duration} ${easing}`, style({ height: 0, opacity: 0 })), + query('.details', [animate(`${duration} ${easing}`, style({ transform: 'translateY(-100%)' }))], { + optional: true, + }), + ]), + ]), ]); diff --git a/frontend/src/app/auth.guard.spec.ts b/frontend/src/app/auth.guard.spec.ts index 0e782a0b2..4c2356a9d 100644 --- a/frontend/src/app/auth.guard.spec.ts +++ b/frontend/src/app/auth.guard.spec.ts @@ -1,17 +1,16 @@ -import { TestBed, inject } from '@angular/core/testing'; - -import { AuthGuard } from './auth.guard'; +import { inject, TestBed } from '@angular/core/testing'; import { RouterTestingModule } from '@angular/router/testing'; +import { AuthGuard } from './auth.guard'; describe('AuthGuard', () => { - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [AuthGuard], - imports: [RouterTestingModule] - }); - }); + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [AuthGuard], + imports: [RouterTestingModule], + }); + }); - it('should ...', inject([AuthGuard], (guard: AuthGuard) => { - expect(guard).toBeTruthy(); - })); + it('should ...', inject([AuthGuard], (guard: AuthGuard) => { + expect(guard).toBeTruthy(); + })); }); diff --git a/frontend/src/app/auth.guard.ts b/frontend/src/app/auth.guard.ts index b54f669d6..4f08977a7 100644 --- a/frontend/src/app/auth.guard.ts +++ b/frontend/src/app/auth.guard.ts @@ -1,37 +1,30 @@ -import { ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot, Router } from '@angular/router'; -import { differenceInMilliseconds } from 'date-fns' - import { Injectable } from '@angular/core'; +import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router'; +import { differenceInMilliseconds } from 'date-fns'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class AuthGuard implements CanActivate { + constructor(private router: Router) {} - constructor( - private router: Router - ) {} - - async canActivate( - _route: ActivatedRouteSnapshot, - _state: RouterStateSnapshot - ) { - try { - const expirationToken = localStorage.getItem('token_expiration'); - let expirationTime = null; - if (expirationToken) expirationTime = new Date(expirationToken); - const currantTime = new Date(); + async canActivate(_route: ActivatedRouteSnapshot, _state: RouterStateSnapshot) { + try { + const expirationToken = localStorage.getItem('token_expiration'); + let expirationTime = null; + if (expirationToken) expirationTime = new Date(expirationToken); + const currantTime = new Date(); - if (expirationTime && currantTime) { - const expirationInterval = differenceInMilliseconds(expirationTime, currantTime); - if (expirationInterval > 0) return true; - } else { - console.log('auth guard, expirationInterval <= 0'); - this.router.navigate(['/login']); - } - return false; - } catch { - return false; - } - } + if (expirationTime && currantTime) { + const expirationInterval = differenceInMilliseconds(expirationTime, currantTime); + if (expirationInterval > 0) return true; + } else { + console.log('auth guard, expirationInterval <= 0'); + this.router.navigate(['/login']); + } + return false; + } catch { + return false; + } + } } diff --git a/frontend/src/app/components/api-keys/api-keys.component.css b/frontend/src/app/components/api-keys/api-keys.component.css index dd8c43d92..e308bf539 100644 --- a/frontend/src/app/components/api-keys/api-keys.component.css +++ b/frontend/src/app/components/api-keys/api-keys.component.css @@ -1,201 +1,201 @@ .profile-layout { - display: flex; - height: calc(100vh - 64px); + display: flex; + height: calc(100vh - 64px); } .profile-main { - flex: 1; - background-color: var(--mat-sidenav-content-background-color); - overflow-y: auto; + flex: 1; + background-color: var(--mat-sidenav-content-background-color); + overflow-y: auto; } ::ng-deep .profile-main > app-alert { - position: relative; - top: 0; - margin: 24px; + position: relative; + top: 0; + margin: 24px; } .api-keys-page { - margin: var(--top-margin) auto; - padding: 0 clamp(40px, 5vw, 100px); - max-width: 800px; + margin: var(--top-margin) auto; + padding: 0 clamp(40px, 5vw, 100px); + max-width: 800px; } @media (width <= 600px) { - .api-keys-page { - padding: 0 16px; - margin: 1.5em auto; - } + .api-keys-page { + padding: 0 16px; + margin: 1.5em auto; + } } .api-keys-description { - color: rgba(0, 0, 0, 0.64); - margin-top: 8px; - margin-bottom: 32px; + color: rgba(0, 0, 0, 0.64); + margin-top: 8px; + margin-bottom: 32px; } @media (prefers-color-scheme: dark) { - .api-keys-description { - color: rgba(255, 255, 255, 0.7); - } + .api-keys-description { + color: rgba(255, 255, 255, 0.7); + } } .api-keys-content { - display: flex; - flex-direction: column; - gap: 24px; + display: flex; + flex-direction: column; + gap: 24px; } .api-key-form { - display: flex; - align-items: flex-start; - gap: 16px; + display: flex; + align-items: flex-start; + gap: 16px; } @media (width <= 600px) { - .api-key-form { - flex-direction: column; - align-items: stretch; - } + .api-key-form { + flex-direction: column; + align-items: stretch; + } } .api-key-form__input { - flex: 1; + flex: 1; } @media (width <= 600px) { - .api-key-form__input { - width: 100%; - } + .api-key-form__input { + width: 100%; + } } .api-key-form__generate-button { - margin-top: 4px; + margin-top: 4px; } @media (width <= 600px) { - .api-key-form__generate-button { - margin-top: 0; - } + .api-key-form__generate-button { + margin-top: 0; + } } .api-key-value { - display: flex; - align-items: flex-start; - gap: 8px; - padding: 16px; - background-color: rgba(76, 175, 80, 0.08); - border-radius: 8px; - border: 1px solid rgba(76, 175, 80, 0.24); + display: flex; + align-items: flex-start; + gap: 8px; + padding: 16px; + background-color: rgba(76, 175, 80, 0.08); + border-radius: 8px; + border: 1px solid rgba(76, 175, 80, 0.24); } @media (prefers-color-scheme: dark) { - .api-key-value { - background-color: rgba(76, 175, 80, 0.12); - border-color: rgba(76, 175, 80, 0.32); - } + .api-key-value { + background-color: rgba(76, 175, 80, 0.12); + border-color: rgba(76, 175, 80, 0.32); + } } .api-key-value__input { - flex: 1; + flex: 1; } .api-key-value__copy-button { - margin-top: 4px; + margin-top: 4px; } .api-keys-list { - margin-top: 16px; + margin-top: 16px; } .api-keys-list__heading { - margin-bottom: 16px; - font-weight: 500; + margin-bottom: 16px; + font-weight: 500; } .api-keys-empty { - display: flex; - flex-direction: column; - align-items: center; - padding: 48px 24px; - text-align: center; - background-color: rgba(0, 0, 0, 0.02); - border-radius: 8px; + display: flex; + flex-direction: column; + align-items: center; + padding: 48px 24px; + text-align: center; + background-color: rgba(0, 0, 0, 0.02); + border-radius: 8px; } @media (prefers-color-scheme: dark) { - .api-keys-empty { - background-color: rgba(255, 255, 255, 0.05); - } + .api-keys-empty { + background-color: rgba(255, 255, 255, 0.05); + } } .api-keys-empty__icon { - font-size: 48px; - width: 48px; - height: 48px; - color: rgba(0, 0, 0, 0.26); - margin-bottom: 16px; + font-size: 48px; + width: 48px; + height: 48px; + color: rgba(0, 0, 0, 0.26); + margin-bottom: 16px; } @media (prefers-color-scheme: dark) { - .api-keys-empty__icon { - color: rgba(255, 255, 255, 0.3); - } + .api-keys-empty__icon { + color: rgba(255, 255, 255, 0.3); + } } .api-keys-empty p { - margin: 0; - color: rgba(0, 0, 0, 0.54); + margin: 0; + color: rgba(0, 0, 0, 0.54); } @media (prefers-color-scheme: dark) { - .api-keys-empty p { - color: rgba(255, 255, 255, 0.54); - } + .api-keys-empty p { + color: rgba(255, 255, 255, 0.54); + } } .api-keys-items { - padding: 0; + padding: 0; } .api-key-list-item { - border-radius: 8px; - margin-bottom: 8px; + border-radius: 8px; + margin-bottom: 8px; } .api-key-list-item:hover { - background-color: rgba(0, 0, 0, 0.04); + background-color: rgba(0, 0, 0, 0.04); } @media (prefers-color-scheme: dark) { - .api-key-list-item:hover { - background-color: rgba(255, 255, 255, 0.08); - } + .api-key-list-item:hover { + background-color: rgba(255, 255, 255, 0.08); + } } .api-key-item { - display: flex; - align-items: center; - justify-content: space-between; - width: 100%; + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; } .api-key-item__info { - display: flex; - align-items: center; - gap: 12px; + display: flex; + align-items: center; + gap: 12px; } .api-key-item__icon { - color: rgba(0, 0, 0, 0.54); + color: rgba(0, 0, 0, 0.54); } @media (prefers-color-scheme: dark) { - .api-key-item__icon { - color: rgba(255, 255, 255, 0.54); - } + .api-key-item__icon { + color: rgba(255, 255, 255, 0.54); + } } .api-key-item__title { - font-weight: 500; + font-weight: 500; } diff --git a/frontend/src/app/components/audit/audit-data-source.ts b/frontend/src/app/components/audit/audit-data-source.ts index df1fa1770..55300c926 100644 --- a/frontend/src/app/components/audit/audit-data-source.ts +++ b/frontend/src/app/components/audit/audit-data-source.ts @@ -1,114 +1,107 @@ -import { BehaviorSubject, Observable, of } from 'rxjs'; -import { catchError, finalize } from 'rxjs/operators'; - import { CollectionViewer } from '@angular/cdk/collections'; -import { ConnectionsService } from 'src/app/services/connections.service'; import { DataSource } from '@angular/cdk/table'; import { MatPaginator } from '@angular/material/paginator'; -import { format } from 'date-fns' +import { format } from 'date-fns'; +import { BehaviorSubject, Observable, of } from 'rxjs'; +import { catchError, finalize } from 'rxjs/operators'; +import { ConnectionsService } from 'src/app/services/connections.service'; interface Column { - title: string, - selected: boolean + title: string; + selected: boolean; } interface RowsParams { - connectionID: string, - tableName?: string, - userEmail: string, - requstedPage?: number, - filters?: object + connectionID: string; + tableName?: string; + userEmail: string; + requstedPage?: number; + filters?: object; } export class AuditDataSource implements DataSource { - - private rowsSubject = new BehaviorSubject([]); - private loadingSubject = new BehaviorSubject(false); - - public loading$ = this.loadingSubject.asObservable(); - public paginator: MatPaginator; - - constructor(private _connections: ConnectionsService) {} - - connect(_collectionViewer: CollectionViewer): Observable { - return this.rowsSubject.asObservable(); - } - - disconnect(_collectionViewer: CollectionViewer): void { - this.rowsSubject.complete(); - this.loadingSubject.complete(); - } - - fetchLogs({ - connectionID, - tableName, - userEmail - }: RowsParams) { - this.loadingSubject.next(true); - const fetchedLogs = this._connections.fetchAuditLog({ - connectionID, - tableName, - userEmail, - requstedPage: this.paginator?.pageIndex + 1 || 1, - chunkSize: this.paginator?.pageSize || 30 - }); - - if (fetchedLogs) { - fetchedLogs - .pipe( - catchError(() => of([])), - finalize(() => this.loadingSubject.next(false)) - ) - .subscribe((res: any) => { - - const actions = { - addRow: 'Created', - deleteRow: 'Deleted', - updateRow: 'Edited', - rowReceived: 'Viewed', - rowsReceived: 'Viewed', - importRows: 'Imported', - exportRows: 'Exported' - } - - const actionIcons = { - addRow: 'add', - deleteRow: 'delete', - updateRow: 'edit', - rowReceived: 'visibility', - rowsReceived: 'visibility', - importRows: 'upload', - exportRows: 'download' - } - - const formattedLogs = res.logs.map(log => { - const date = new Date(log.createdAt); - const formattedDate = format(date, "d MMM yyyy 'at' h:mm a"); - const dateOnly = format(date, "d MMM yyyy"); - const timeOnly = format(date, "h:mm a"); - - return { - "Table": log.table_name, - "User": log.email, - "UserEmail": log.email, - "Action": actions[log.operationType], - "ActionIcon": actionIcons[log.operationType], - "Date": formattedDate, - "DateOnly": dateOnly, - "TimeOnly": timeOnly, - "Status": log.operationStatusResult, - operationType: log.operationType, - createdAt: log.createdAt, - prevValue: log.old_data, - currentValue: log.received_data, - IsAddAction: log.operationType === 'addRow' - } - }); - this.rowsSubject.next(formattedLogs); - - this.paginator.pageSize = res.pagination.perPage; - this.paginator.length = res.pagination.total; - }); - } - } -} \ No newline at end of file + private rowsSubject = new BehaviorSubject([]); + private loadingSubject = new BehaviorSubject(false); + + public loading$ = this.loadingSubject.asObservable(); + public paginator: MatPaginator; + + constructor(private _connections: ConnectionsService) {} + + connect(_collectionViewer: CollectionViewer): Observable { + return this.rowsSubject.asObservable(); + } + + disconnect(_collectionViewer: CollectionViewer): void { + this.rowsSubject.complete(); + this.loadingSubject.complete(); + } + + fetchLogs({ connectionID, tableName, userEmail }: RowsParams) { + this.loadingSubject.next(true); + const fetchedLogs = this._connections.fetchAuditLog({ + connectionID, + tableName, + userEmail, + requstedPage: this.paginator?.pageIndex + 1 || 1, + chunkSize: this.paginator?.pageSize || 30, + }); + + if (fetchedLogs) { + fetchedLogs + .pipe( + catchError(() => of([])), + finalize(() => this.loadingSubject.next(false)), + ) + .subscribe((res: any) => { + const actions = { + addRow: 'Created', + deleteRow: 'Deleted', + updateRow: 'Edited', + rowReceived: 'Viewed', + rowsReceived: 'Viewed', + importRows: 'Imported', + exportRows: 'Exported', + }; + + const actionIcons = { + addRow: 'add', + deleteRow: 'delete', + updateRow: 'edit', + rowReceived: 'visibility', + rowsReceived: 'visibility', + importRows: 'upload', + exportRows: 'download', + }; + + const formattedLogs = res.logs.map((log) => { + const date = new Date(log.createdAt); + const formattedDate = format(date, "d MMM yyyy 'at' h:mm a"); + const dateOnly = format(date, 'd MMM yyyy'); + const timeOnly = format(date, 'h:mm a'); + + return { + Table: log.table_name, + User: log.email, + UserEmail: log.email, + Action: actions[log.operationType], + ActionIcon: actionIcons[log.operationType], + Date: formattedDate, + DateOnly: dateOnly, + TimeOnly: timeOnly, + Status: log.operationStatusResult, + operationType: log.operationType, + createdAt: log.createdAt, + prevValue: log.old_data, + currentValue: log.received_data, + IsAddAction: log.operationType === 'addRow', + }; + }); + this.rowsSubject.next(formattedLogs); + + this.paginator.pageSize = res.pagination.perPage; + this.paginator.length = res.pagination.total; + }); + } + } +} diff --git a/frontend/src/app/components/audit/audit.component.css b/frontend/src/app/components/audit/audit.component.css index cceef52e4..bf7327dfa 100644 --- a/frontend/src/app/components/audit/audit.component.css +++ b/frontend/src/app/components/audit/audit.component.css @@ -1,215 +1,217 @@ .wrapper { - display: flex; - flex-direction: column; - align-items: stretch; - margin: 0 auto; - height: 100%; - width: clamp(300px, 75vw, 1000px); + display: flex; + flex-direction: column; + align-items: stretch; + margin: 0 auto; + height: 100%; + width: clamp(300px, 75vw, 1000px); } header { - align-self: flex-start; - display: flex; - align-items: center; - justify-content: space-between; - margin: 2em 0 1em; - width: 100%; + align-self: flex-start; + display: flex; + align-items: center; + justify-content: space-between; + margin: 2em 0 1em; + width: 100%; } .mat-h1 { - margin: 0; + margin: 0; } .filters { - display: flex; - /* grid-template-columns: 1fr 1fr; */ - gap: 24px; + display: flex; + /* grid-template-columns: 1fr 1fr; */ + gap: 24px; } .table-name { - display: flex; - flex-direction: column; - gap: 4px; + display: flex; + flex-direction: column; + gap: 4px; } .table-name__title { - color: black; - font-size: 16px; - margin-bottom: -6px; + color: black; + font-size: 16px; + margin-bottom: -6px; } @media (prefers-color-scheme: dark) { - .table-name__title { - color: white; - } + .table-name__title { + color: white; + } } .table-name__line { - color: rgba(0, 0, 0, 0.6); - font-size: 12px; + color: rgba(0, 0, 0, 0.6); + font-size: 12px; } @media (prefers-color-scheme: dark) { - .table-name__line { - color: rgba(255, 255, 255, 0.6); - } + .table-name__line { + color: rgba(255, 255, 255, 0.6); + } } .mat-table { - width: 100%; + width: 100%; } .mat-mdc-row:hover { - background-color: rgba(0, 0, 0, 0.02) !important; + background-color: rgba(0, 0, 0, 0.02) !important; } @media (prefers-color-scheme: dark) { - .mat-mdc-row:hover { - background-color: rgba(255, 255, 255, 0.04) !important; - } + .mat-mdc-row:hover { + background-color: rgba(255, 255, 255, 0.04) !important; + } } -th.mat-header-cell, td.mat-cell { - padding-right: 20px; +th.mat-header-cell, +td.mat-cell { + padding-right: 20px; } td.mat-cell { - padding-top: 12px; - padding-bottom: 12px; + padding-top: 12px; + padding-bottom: 12px; } .table-cell-content_break-words { - padding: 16px 0; - word-break: break-all; + padding: 16px 0; + word-break: break-all; } .table-wrapper { - position: relative; - min-height: 200px; + position: relative; + min-height: 200px; } .spinner-wrapper { - position: absolute; - top: 56px; left: 0; - display: flex; - align-items: center; - justify-content: center; - background: rgba(255, 255, 255, 0.9); - height: calc(100% - 112px); - width: 100%; - z-index: 2; + position: absolute; + top: 56px; + left: 0; + display: flex; + align-items: center; + justify-content: center; + background: rgba(255, 255, 255, 0.9); + height: calc(100% - 112px); + width: 100%; + z-index: 2; } .error-wrapper { - display: flex; - align-items: center; - justify-content: center; - margin-top: 100px; - height: 100%; + display: flex; + align-items: center; + justify-content: center; + margin-top: 100px; + height: 100%; } .error-box { - background-color: rgb(253, 237, 237); - border-radius: 4px; - color: rgb(95, 33, 32); - padding: 20px; - width: clamp(300px, 50vw, 450px); + background-color: rgb(253, 237, 237); + border-radius: 4px; + color: rgb(95, 33, 32); + padding: 20px; + width: clamp(300px, 50vw, 450px); } .error-details { - margin-top: 8px; + margin-top: 8px; } .audit-banner { - margin: -60px calc((100vw - clamp(300px, 75vw, 1000px)) / -2) 0; - width: 100vw; + margin: -60px calc((100vw - clamp(300px, 75vw, 1000px)) / -2) 0; + width: 100vw; } .hidden { - display: none; + display: none; } .status-badge { - display: inline-block; - padding: 4px 12px; - border-radius: 4px; + display: inline-block; + padding: 4px 12px; + border-radius: 4px; } .status-succeed { - background-color: #EBF9EB; - color: #1A5E20; + background-color: #ebf9eb; + color: #1a5e20; } .action-cell { - display: flex; - align-items: center; - gap: 8px; + display: flex; + align-items: center; + gap: 8px; } .action-icon { - font-size: 16px; - width: 16px; - height: 16px; + font-size: 16px; + width: 16px; + height: 16px; } .action-icon--add { - color: #1A5E20; + color: #1a5e20; } .action-icon--delete { - color: #C62828; + color: #c62828; } .action-icon--edit { - color: var(--color-accentedPalette-500); + color: var(--color-accentedPalette-500); } .action-icon--visibility { - color: #6A6A6A; + color: #6a6a6a; } .action-icon--upload { - color: #2E7D32; + color: #2e7d32; } .action-icon--download { - color: #1565C0; + color: #1565c0; } .user-cell { - display: flex; - flex-direction: column; - gap: 0; + display: flex; + flex-direction: column; + gap: 0; } .user-name { - font-weight: 500; + font-weight: 500; } .user-email--secondary { - font-size: 12px; - color: rgba(0, 0, 0, 0.6); + font-size: 12px; + color: rgba(0, 0, 0, 0.6); } @media (prefers-color-scheme: dark) { - .user-email--secondary { - color: rgba(255, 255, 255, 0.6); - } + .user-email--secondary { + color: rgba(255, 255, 255, 0.6); + } } .date-cell { - display: flex; - flex-direction: column; - gap: 0; + display: flex; + flex-direction: column; + gap: 0; } .date-time { - font-size: 12px; - color: rgba(0, 0, 0, 0.6); + font-size: 12px; + color: rgba(0, 0, 0, 0.6); } @media (prefers-color-scheme: dark) { - .date-time { - color: rgba(255, 255, 255, 0.6); - } -} \ No newline at end of file + .date-time { + color: rgba(255, 255, 255, 0.6); + } +} diff --git a/frontend/src/app/components/audit/info-dialog/info-dialog.component.css b/frontend/src/app/components/audit/info-dialog/info-dialog.component.css index ebd169765..4498b12cd 100644 --- a/frontend/src/app/components/audit/info-dialog/info-dialog.component.css +++ b/frontend/src/app/components/audit/info-dialog/info-dialog.component.css @@ -1,51 +1,52 @@ .details { - margin-top: 16px; + margin-top: 16px; } table { - border-collapse: collapse; - border-top-width: 1px; - border-top-style: solid; - border-top-color: rgba(0,0,0,.12); - margin-top: 12px; - width: 100%; + border-collapse: collapse; + border-top-width: 1px; + border-top-style: solid; + border-top-color: rgba(0, 0, 0, 0.12); + margin-top: 12px; + width: 100%; } -td, th { - padding: 8px 36px 8px 24px; - border-bottom-width: 1px; - border-bottom-style: solid; - border-bottom-color: rgba(0,0,0,.12); +td, +th { + padding: 8px 36px 8px 24px; + border-bottom-width: 1px; + border-bottom-style: solid; + border-bottom-color: rgba(0, 0, 0, 0.12); } th { - font-weight: 500; - text-align: left; + font-weight: 500; + text-align: left; } .updated-cell { - display: flex; - align-items: center; - justify-content: space-between; + display: flex; + align-items: center; + justify-content: space-between; } .updated-icon { - margin-right: -24px; + margin-right: -24px; } .changed { - background-color: var(--color-primaryPalette-100); + background-color: var(--color-primaryPalette-100); } @media (prefers-color-scheme: dark) { - .changed { - background-color: var(--color-primaryPalette-200); - color: rgba(0, 0, 0, 0.78); - } + .changed { + background-color: var(--color-primaryPalette-200); + color: rgba(0, 0, 0, 0.78); + } } .sensitive { - font-style: italic; - font-family: monospace; - font-size: 0.85em; -} \ No newline at end of file + font-style: italic; + font-family: monospace; + font-size: 0.85em; +} diff --git a/frontend/src/app/components/audit/info-dialog/info-dialog.component.spec.ts b/frontend/src/app/components/audit/info-dialog/info-dialog.component.spec.ts index 204a8879f..aae8044df 100644 --- a/frontend/src/app/components/audit/info-dialog/info-dialog.component.spec.ts +++ b/frontend/src/app/components/audit/info-dialog/info-dialog.component.spec.ts @@ -7,178 +7,177 @@ import { LogAction, LogStatus } from 'src/app/models/logs'; import { InfoDialogComponent } from './info-dialog.component'; describe('InfoDialogComponent', () => { - let component: InfoDialogComponent; - let fixture: ComponentFixture; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [InfoDialogComponent], - providers: [ - { provide: MAT_DIALOG_DATA, useValue: {} }, - { provide: MatDialogRef, useValue: {} }, - ], - schemas: [CUSTOM_ELEMENTS_SCHEMA] -}) - .compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(InfoDialogComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - it('should show block for added row details', () => { - component.log = { - Table: 'south_park_table', - User: 'eric@cartman.ass', - Action: 'added row', - Date: '06/26/2021 7:22 PM', - Status: LogStatus.Successfully, - operationType: LogAction.Add, - createdAt: '2021-06-26T07:22:00.603Z', - prevValue: null, - currentValue: { - firstName: 'Kenny', - secondName: 'Kenny McCormick', - status: 'poor' - }, - } - component.ngOnInit(); - fixture.detectChanges(); - - const oneRowDetails = fixture.debugElement.query(By.css('table[data-block="one-row-details"]')); - expect(oneRowDetails).toBeTruthy(); - }); - - it('should show block for deleted row details', () => { - component.log = { - Table: 'south_park_table', - User: 'eric@cartman.ass', - Action: 'deleted row', - Date: '06/26/2021 7:22 PM', - Status: LogStatus.Successfully, - operationType: LogAction.Delete, - createdAt: '2021-06-26T07:22:00.603Z', - prevValue: null, - currentValue: { - firstName: 'Kenny', - secondName: 'Kenny McCormick', - status: 'poor' - }, - } - component.ngOnInit(); - fixture.detectChanges(); - - const oneRowDetails = fixture.debugElement.query(By.css('table[data-block="one-row-details"]')); - expect(oneRowDetails).toBeTruthy(); - }); - - it('should show block for viewed row details', () => { - component.log = { - Table: 'south_park_table', - User: 'eric@cartman.ass', - Action: 'received row', - Date: '06/26/2021 7:22 PM', - Status: LogStatus.Successfully, - operationType: LogAction.ReceiveRow, - createdAt: '2021-06-26T07:22:00.603Z', - prevValue: null, - currentValue: { - firstName: 'Kenny', - secondName: 'Kenny McCormick', - status: 'poor' - }, - } - component.ngOnInit(); - fixture.detectChanges(); - - const oneRowDetailsNode = fixture.debugElement.query(By.css('table[data-block="one-row-details"]')); - expect(oneRowDetailsNode).toBeTruthy(); - }); - - it('should show block for comparison details of updated row', () => { - component.log = { - Table: 'south_park_table', - User: 'eric@cartman.ass', - Action: 'updated row', - Date: '06/26/2021 7:22 PM', - Status: LogStatus.Successfully, - operationType: LogAction.Update, - createdAt: '2021-06-26T07:22:00.603Z', - prevValue: { - firstName: 'Kenny', - secondName: 'Kenny McCormick', - status: 'poor' - }, - currentValue: { - firstName: 'Kenny', - secondName: 'Kenny McCormick', - status: 'poor and stupid' - }, - } - component.ngOnInit(); - fixture.detectChanges(); - - const comparisonDetails = fixture.debugElement.query(By.css('div[data-block="comparison-details"]')); - expect(comparisonDetails).toBeTruthy(); - }); - - it('should show block with updated value, when prev value in unknown', () => { - component.log = { - Table: 'south_park_table', - User: 'eric@cartman.ass', - Action: 'updated row', - Date: '06/26/2021 7:22 PM', - Status: LogStatus.Successfully, - operationType: LogAction.Update, - createdAt: '2021-06-26T07:22:00.603Z', - prevValue: null, - currentValue: { - firstName: 'Kenny', - secondName: 'Kenny McCormick', - status: 'poor and stupid' - }, - } - component.ngOnInit(); - fixture.detectChanges(); - - const comparisonDetails = fixture.debugElement.query(By.css('table[data-block="one-row-details"]')); - expect(comparisonDetails).toBeTruthy(); - }); - - it('should highlight changed field of updated row', () => { - component.log = { - Table: 'south_park_table', - User: 'eric@cartman.ass', - Action: 'updated row', - Date: '06/26/2021 7:22 PM', - Status: LogStatus.Successfully, - operationType: LogAction.Update, - createdAt: '2021-06-26T07:22:00.603Z', - prevValue: { - firstName: 'Kenny', - secondName: 'McCormick', - status: 'poor' - }, - currentValue: { - firstName: 'Kenny', - secondName: 'McCormick', - status: 'poor and stupid' - }, - } - component.ngOnInit(); - fixture.detectChanges(); - - const changedField = fixture.debugElement.query(By.css('.changed[data-line="status-field"]')); - const unchangedField1 = fixture.debugElement.query(By.css('.changed[data-line="firstName-field"]')); - const unchangedField2 = fixture.debugElement.query(By.css('.changed[data-line="secondName-field"]')); - expect(changedField).toBeTruthy(); - expect(unchangedField1).toBeNull(); - expect(unchangedField2).toBeNull(); - }); + let component: InfoDialogComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [InfoDialogComponent], + providers: [ + { provide: MAT_DIALOG_DATA, useValue: {} }, + { provide: MatDialogRef, useValue: {} }, + ], + schemas: [CUSTOM_ELEMENTS_SCHEMA], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(InfoDialogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should show block for added row details', () => { + component.log = { + Table: 'south_park_table', + User: 'eric@cartman.ass', + Action: 'added row', + Date: '06/26/2021 7:22 PM', + Status: LogStatus.Successfully, + operationType: LogAction.Add, + createdAt: '2021-06-26T07:22:00.603Z', + prevValue: null, + currentValue: { + firstName: 'Kenny', + secondName: 'Kenny McCormick', + status: 'poor', + }, + }; + component.ngOnInit(); + fixture.detectChanges(); + + const oneRowDetails = fixture.debugElement.query(By.css('table[data-block="one-row-details"]')); + expect(oneRowDetails).toBeTruthy(); + }); + + it('should show block for deleted row details', () => { + component.log = { + Table: 'south_park_table', + User: 'eric@cartman.ass', + Action: 'deleted row', + Date: '06/26/2021 7:22 PM', + Status: LogStatus.Successfully, + operationType: LogAction.Delete, + createdAt: '2021-06-26T07:22:00.603Z', + prevValue: null, + currentValue: { + firstName: 'Kenny', + secondName: 'Kenny McCormick', + status: 'poor', + }, + }; + component.ngOnInit(); + fixture.detectChanges(); + + const oneRowDetails = fixture.debugElement.query(By.css('table[data-block="one-row-details"]')); + expect(oneRowDetails).toBeTruthy(); + }); + + it('should show block for viewed row details', () => { + component.log = { + Table: 'south_park_table', + User: 'eric@cartman.ass', + Action: 'received row', + Date: '06/26/2021 7:22 PM', + Status: LogStatus.Successfully, + operationType: LogAction.ReceiveRow, + createdAt: '2021-06-26T07:22:00.603Z', + prevValue: null, + currentValue: { + firstName: 'Kenny', + secondName: 'Kenny McCormick', + status: 'poor', + }, + }; + component.ngOnInit(); + fixture.detectChanges(); + + const oneRowDetailsNode = fixture.debugElement.query(By.css('table[data-block="one-row-details"]')); + expect(oneRowDetailsNode).toBeTruthy(); + }); + + it('should show block for comparison details of updated row', () => { + component.log = { + Table: 'south_park_table', + User: 'eric@cartman.ass', + Action: 'updated row', + Date: '06/26/2021 7:22 PM', + Status: LogStatus.Successfully, + operationType: LogAction.Update, + createdAt: '2021-06-26T07:22:00.603Z', + prevValue: { + firstName: 'Kenny', + secondName: 'Kenny McCormick', + status: 'poor', + }, + currentValue: { + firstName: 'Kenny', + secondName: 'Kenny McCormick', + status: 'poor and stupid', + }, + }; + component.ngOnInit(); + fixture.detectChanges(); + + const comparisonDetails = fixture.debugElement.query(By.css('div[data-block="comparison-details"]')); + expect(comparisonDetails).toBeTruthy(); + }); + + it('should show block with updated value, when prev value in unknown', () => { + component.log = { + Table: 'south_park_table', + User: 'eric@cartman.ass', + Action: 'updated row', + Date: '06/26/2021 7:22 PM', + Status: LogStatus.Successfully, + operationType: LogAction.Update, + createdAt: '2021-06-26T07:22:00.603Z', + prevValue: null, + currentValue: { + firstName: 'Kenny', + secondName: 'Kenny McCormick', + status: 'poor and stupid', + }, + }; + component.ngOnInit(); + fixture.detectChanges(); + + const comparisonDetails = fixture.debugElement.query(By.css('table[data-block="one-row-details"]')); + expect(comparisonDetails).toBeTruthy(); + }); + + it('should highlight changed field of updated row', () => { + component.log = { + Table: 'south_park_table', + User: 'eric@cartman.ass', + Action: 'updated row', + Date: '06/26/2021 7:22 PM', + Status: LogStatus.Successfully, + operationType: LogAction.Update, + createdAt: '2021-06-26T07:22:00.603Z', + prevValue: { + firstName: 'Kenny', + secondName: 'McCormick', + status: 'poor', + }, + currentValue: { + firstName: 'Kenny', + secondName: 'McCormick', + status: 'poor and stupid', + }, + }; + component.ngOnInit(); + fixture.detectChanges(); + + const changedField = fixture.debugElement.query(By.css('.changed[data-line="status-field"]')); + const unchangedField1 = fixture.debugElement.query(By.css('.changed[data-line="firstName-field"]')); + const unchangedField2 = fixture.debugElement.query(By.css('.changed[data-line="secondName-field"]')); + expect(changedField).toBeTruthy(); + expect(unchangedField1).toBeNull(); + expect(unchangedField2).toBeNull(); + }); }); diff --git a/frontend/src/app/components/audit/info-dialog/info-dialog.component.ts b/frontend/src/app/components/audit/info-dialog/info-dialog.component.ts index 32c491eaf..b9c7c9fe4 100644 --- a/frontend/src/app/components/audit/info-dialog/info-dialog.component.ts +++ b/frontend/src/app/components/audit/info-dialog/info-dialog.component.ts @@ -2,81 +2,74 @@ import { CommonModule } from '@angular/common'; import { Component, Inject, OnInit } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog'; -import { format } from 'date-fns' +import { format } from 'date-fns'; import { normalizeFieldName, normalizeTableName } from 'src/app/lib/normalize'; import { Log } from 'src/app/models/logs'; @Component({ - selector: 'app-info-dialog', - templateUrl: './info-dialog.component.html', - styleUrls: ['./info-dialog.component.css'], - imports: [ - CommonModule, - MatDialogModule, - MatButtonModule - ] + selector: 'app-info-dialog', + templateUrl: './info-dialog.component.html', + styleUrls: ['./info-dialog.component.css'], + imports: [CommonModule, MatDialogModule, MatButtonModule], }) export class InfoDialogComponent implements OnInit { + public normalizedTableName: string; + public diffFields: string[] = []; + public fields: string[]; + public filedsNames: object; + public formattedCrreatedAt: string; + public action: string; - public normalizedTableName: string; - public diffFields: string[] = []; - public fields: string[]; - public filedsNames: object; - public formattedCrreatedAt: string; - public action: string; + constructor(@Inject(MAT_DIALOG_DATA) public log: Log) {} - constructor( - @Inject(MAT_DIALOG_DATA) public log: Log, - ) { } + ngOnInit(): void { + const actions = { + successfully: { + addRow: 'added the row', + deleteRow: 'deleted the row', + updateRow: 'edited the row', + rowReceived: 'received the row', + rowsReceived: 'received rows', + }, + unsuccessfully: { + addRow: 'attempted to add the row', + deleteRow: 'attempted to delete the row', + updateRow: 'attempted to edit the row', + rowReceived: 'attempted to receive the row', + rowsReceived: 'attempted to receive rows', + }, + }; - ngOnInit(): void { - const actions = { - successfully: { - addRow: 'added the row', - deleteRow: 'deleted the row', - updateRow: 'edited the row', - rowReceived: 'received the row', - rowsReceived: 'received rows' - }, - unsuccessfully: { - addRow: 'attempted to add the row', - deleteRow: 'attempted to delete the row', - updateRow: 'attempted to edit the row', - rowReceived: 'attempted to receive the row', - rowsReceived: 'attempted to receive rows' - } - }; + if (this.log?.createdAt) { + const datetime = new Date(this.log.createdAt); + this.formattedCrreatedAt = format(datetime, 'PPPpp'); + this.action = actions[this.log.Status][this.log.operationType]; - if (this.log?.createdAt) { - const datetime = new Date(this.log.createdAt); - this.formattedCrreatedAt = format(datetime, 'PPPpp'); - this.action = actions[this.log.Status][this.log.operationType]; + this.normalizedTableName = normalizeTableName(this.log.Table); - this.normalizedTableName = normalizeTableName(this.log.Table); + if (this.log.currentValue) { + this.fields = Object.keys(this.log.currentValue); + if (this.log.operationType === 'updateRow') + this.diffFields = this.getFieldsDiff(this.log.prevValue, this.log.currentValue); - if (this.log.currentValue) { - this.fields = Object.keys(this.log.currentValue); - if (this.log.operationType === 'updateRow') - this.diffFields = this.getFieldsDiff(this.log.prevValue, this.log.currentValue); + this.filedsNames = Object.assign({}, ...this.fields.map((field) => ({ [field]: normalizeFieldName(field) }))); + } + } + } - this.filedsNames = Object.assign({}, ...this.fields.map((field) => ({[field]: normalizeFieldName(field)}))); - } - } - } + isChangedFiled(field: string) { + if (this.diffFields.length) return this.diffFields.includes(field); + } - isChangedFiled(field: string) { - if (this.diffFields.length) return this.diffFields.includes(field); - } - - getFieldsDiff(prevLog, currentLog) { - if (prevLog) { - const diff = Object.entries(prevLog) - .map(([key, value]) => { - if (currentLog[key] !== value) return key; - }) - .filter(key => key); - return diff; - } - return currentLog; - } + getFieldsDiff(prevLog, currentLog) { + if (prevLog) { + const diff = Object.entries(prevLog) + .map(([key, value]) => { + if (currentLog[key] !== value) return key; + }) + .filter((key) => key); + return diff; + } + return currentLog; + } } diff --git a/frontend/src/app/components/branding/branding.component.css b/frontend/src/app/components/branding/branding.component.css index 51667135e..16b8d2bd3 100644 --- a/frontend/src/app/components/branding/branding.component.css +++ b/frontend/src/app/components/branding/branding.component.css @@ -1,216 +1,216 @@ .profile-layout { - display: flex; - height: calc(100vh - 64px); + display: flex; + height: calc(100vh - 64px); } .profile-main { - flex: 1; - background-color: var(--mat-sidenav-content-background-color); - overflow-y: auto; + flex: 1; + background-color: var(--mat-sidenav-content-background-color); + overflow-y: auto; } ::ng-deep .profile-main > app-alert { - position: relative; - top: 0; - margin: 24px; + position: relative; + top: 0; + margin: 24px; } .branding-page { - margin: var(--top-margin) auto; - padding: 0 clamp(40px, 5vw, 100px); - max-width: 1200px; + margin: var(--top-margin) auto; + padding: 0 clamp(40px, 5vw, 100px); + max-width: 1200px; } @media (width <= 600px) { - .branding-page { - padding: 0 16px; - margin: 1.5em auto; - } + .branding-page { + padding: 0 16px; + margin: 1.5em auto; + } } .branding-page-header { - margin-bottom: 8px; + margin-bottom: 8px; } .branding-description { - color: rgba(0, 0, 0, 0.64); - margin-top: 0; - margin-bottom: 32px; + color: rgba(0, 0, 0, 0.64); + margin-top: 0; + margin-bottom: 32px; } @media (prefers-color-scheme: dark) { - .branding-description { - color: rgba(255, 255, 255, 0.7); - } + .branding-description { + color: rgba(255, 255, 255, 0.7); + } } .text-filed-edit { - position: relative; + position: relative; } .text-filed-edit__input { - width: 100%; + width: 100%; } .text-filed-edit__buttons { - position: absolute; - right: 4px; - top: 50%; - transform: translateY(calc(-50% - 10px)); - display: flex; - gap: 4px; + position: absolute; + right: 4px; + top: 50%; + transform: translateY(calc(-50% - 10px)); + display: flex; + gap: 4px; } .white-label { - display: grid; - grid-template-columns: 1fr 1fr; - grid-column-gap: 32px; - margin-bottom: 32px; + display: grid; + grid-template-columns: 1fr 1fr; + grid-column-gap: 32px; + margin-bottom: 32px; } @media (width <= 900px) { - .white-label { - grid-template-columns: 1fr; - grid-row-gap: 32px; - } + .white-label { + grid-template-columns: 1fr; + grid-row-gap: 32px; + } } .white-label-settings { - position: relative; + position: relative; } .white-label-settings-images { - display: flex; - gap: 24px; - margin-top: 16px; - margin-bottom: 24px; + display: flex; + gap: 24px; + margin-top: 16px; + margin-bottom: 24px; } .upload-logo-form { - display: flex; - align-items: center; - gap: 8px; + display: flex; + align-items: center; + gap: 8px; } .white-label-preview { - height: auto; - margin-top: 8px; - width: 100%; + height: auto; + margin-top: 8px; + width: 100%; } .white-label-how-to { - border: 1px solid var(--color-primaryPalette-100); - color: rgba(0,0,0,0.72); - padding: 20px; - height: fit-content; + border: 1px solid var(--color-primaryPalette-100); + color: rgba(0, 0, 0, 0.72); + padding: 20px; + height: fit-content; } @media (prefers-color-scheme: dark) { - .white-label-how-to { - border: 1px solid var(--color-primaryPalette-700); - color: rgba(255,255,255,0.7); - } + .white-label-how-to { + border: 1px solid var(--color-primaryPalette-700); + color: rgba(255, 255, 255, 0.7); + } } .white-label-settings__overlay { - position: absolute; - top: -8px; - left: 0; - right: 0; - bottom: 0; - background-color: rgba(255,255,255,0.64); - z-index: 2; + position: absolute; + top: -8px; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(255, 255, 255, 0.64); + z-index: 2; } @media (prefers-color-scheme: dark) { - .white-label-settings__overlay { - background-color: rgba(25,25,25,0.64); - } + .white-label-settings__overlay { + background-color: rgba(25, 25, 25, 0.64); + } } .cname-instructions { - list-style: none; - padding-left: 0; + list-style: none; + padding-left: 0; } .cname-hint { - margin-top: 2px; + margin-top: 2px; } .cname-hint_dimmed { - color: rgba(0,0,0,0.54); + color: rgba(0, 0, 0, 0.54); } @media (prefers-color-scheme: dark) { - .cname-hint_dimmed { - color: rgba(255,255,255,0.7); - } + .cname-hint_dimmed { + color: rgba(255, 255, 255, 0.7); + } } .cname-value { - position: relative; - padding: 0 4px; + position: relative; + padding: 0 4px; } .cname-value::after { - content: ''; - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - background-color: var(--color-accentedPalette-200); - border-radius: 4px; - height: 100%; - opacity: 0.5; - width: 100%; - z-index: -1; + content: ""; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + background-color: var(--color-accentedPalette-200); + border-radius: 4px; + height: 100%; + opacity: 0.5; + width: 100%; + z-index: -1; } @media (prefers-color-scheme: dark) { - .cname-value::after { - background-color: var(--color-accentedPalette-700); - } + .cname-value::after { + background-color: var(--color-accentedPalette-700); + } } .upgrade-required { - display: flex; - flex-direction: column; - align-items: center; - text-align: center; - padding: 48px 24px; - border: 1px solid var(--color-primaryPalette-100); - height: fit-content; + display: flex; + flex-direction: column; + align-items: center; + text-align: center; + padding: 48px 24px; + border: 1px solid var(--color-primaryPalette-100); + height: fit-content; } @media (prefers-color-scheme: dark) { - .upgrade-required { - border-color: var(--color-primaryPalette-700); - } + .upgrade-required { + border-color: var(--color-primaryPalette-700); + } } .upgrade-required__icon { - font-size: 64px; - width: 64px; - height: 64px; - color: rgba(0, 0, 0, 0.38); - margin-bottom: 24px; + font-size: 64px; + width: 64px; + height: 64px; + color: rgba(0, 0, 0, 0.38); + margin-bottom: 24px; } @media (prefers-color-scheme: dark) { - .upgrade-required__icon { - color: rgba(255, 255, 255, 0.38); - } + .upgrade-required__icon { + color: rgba(255, 255, 255, 0.38); + } } .upgrade-required__description { - color: rgba(0, 0, 0, 0.64); - margin-bottom: 24px; - max-width: 400px; + color: rgba(0, 0, 0, 0.64); + margin-bottom: 24px; + max-width: 400px; } @media (prefers-color-scheme: dark) { - .upgrade-required__description { - color: rgba(255, 255, 255, 0.7); - } + .upgrade-required__description { + color: rgba(255, 255, 255, 0.7); + } } diff --git a/frontend/src/app/components/company-member-invitation/company-member-invitation.component.css b/frontend/src/app/components/company-member-invitation/company-member-invitation.component.css index e67c1974c..a10f35f1f 100644 --- a/frontend/src/app/components/company-member-invitation/company-member-invitation.component.css +++ b/frontend/src/app/components/company-member-invitation/company-member-invitation.component.css @@ -1,41 +1,41 @@ :host app-alert:not(:empty) { - --alert-margin: 24px; + --alert-margin: 24px; - position: absolute; - top: var(--mat-toolbar-standard-height); - width: calc(100% - 48px); + position: absolute; + top: var(--mat-toolbar-standard-height); + width: calc(100% - 48px); } .wrapper { - display: flex; - flex-direction: column; - justify-content: center; - height: 100%; - margin: 0 auto; - padding: 16px; - max-width: 520px; - min-width: 300px; - width: 90%; + display: flex; + flex-direction: column; + justify-content: center; + height: 100%; + margin: 0 auto; + padding: 16px; + max-width: 520px; + min-width: 300px; + width: 90%; } .password-form__title { - margin-bottom: 32px; + margin-bottom: 32px; } .name-input { - width: 100%; + width: 100%; } .form-divider { - margin-top: 48px; - margin-bottom: 20px; + margin-top: 48px; + margin-bottom: 20px; } .actions { - display: flex; - justify-content: flex-end; + display: flex; + justify-content: flex-end; } .password-form__back { - margin-right: 20px; -} \ No newline at end of file + margin-right: 20px; +} diff --git a/frontend/src/app/components/company-member-invitation/company-member-invitation.component.spec.ts b/frontend/src/app/components/company-member-invitation/company-member-invitation.component.spec.ts index 786f8ff95..6078868b2 100644 --- a/frontend/src/app/components/company-member-invitation/company-member-invitation.component.spec.ts +++ b/frontend/src/app/components/company-member-invitation/company-member-invitation.component.spec.ts @@ -1,36 +1,27 @@ +import { provideHttpClient } from '@angular/common/http'; import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { CompanyMemberInvitationComponent } from './company-member-invitation.component'; import { MatSnackBarModule } from '@angular/material/snack-bar'; -import { Angulartics2Module } from 'angulartics2'; -import { IPasswordStrengthMeterService } from 'angular-password-strength-meter'; -import { provideHttpClient } from '@angular/common/http'; import { provideRouter } from '@angular/router'; +import { IPasswordStrengthMeterService } from 'angular-password-strength-meter'; +import { Angulartics2Module } from 'angulartics2'; +import { CompanyMemberInvitationComponent } from './company-member-invitation.component'; describe('CompanyMemberInvitationComponent', () => { - let component: CompanyMemberInvitationComponent; - let fixture: ComponentFixture; + let component: CompanyMemberInvitationComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [ - MatSnackBarModule, - Angulartics2Module.forRoot(), - CompanyMemberInvitationComponent - ], - providers: [ - provideHttpClient(), - provideRouter([]), - { provide: IPasswordStrengthMeterService, useValue: {} } - ] - }).compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [MatSnackBarModule, Angulartics2Module.forRoot(), CompanyMemberInvitationComponent], + providers: [provideHttpClient(), provideRouter([]), { provide: IPasswordStrengthMeterService, useValue: {} }], + }).compileComponents(); - fixture = TestBed.createComponent(CompanyMemberInvitationComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + fixture = TestBed.createComponent(CompanyMemberInvitationComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/company/company.component.css b/frontend/src/app/components/company/company.component.css index f30693070..8f7c25fb5 100644 --- a/frontend/src/app/components/company/company.component.css +++ b/frontend/src/app/components/company/company.component.css @@ -1,330 +1,326 @@ .profile-layout { - display: flex; - height: calc(100vh - 64px); + display: flex; + height: calc(100vh - 64px); } .profile-main { - flex: 1; - background-color: var(--mat-sidenav-content-background-color); - overflow-y: auto; + flex: 1; + background-color: var(--mat-sidenav-content-background-color); + overflow-y: auto; } ::ng-deep .profile-main > app-alert { - position: relative; - top: 0; - margin: 24px; + position: relative; + top: 0; + margin: 24px; } .companyPage { - margin: var(--top-margin) auto; - padding: 0 clamp(40px, 5vw, 100px); - max-width: 1200px; + margin: var(--top-margin) auto; + padding: 0 clamp(40px, 5vw, 100px); + max-width: 1200px; } @media (width <= 600px) { - .companyPage { - padding: 0 16px; - margin: 1.5em auto; - } + .companyPage { + padding: 0 16px; + margin: 1.5em auto; + } } .companyPageHeader { - margin-bottom: 40px; + margin-bottom: 40px; } .text-filed-edit { - position: relative; + position: relative; } .text-filed-edit__input { - width: 100%; + width: 100%; } .text-filed-edit__buttons { - position: absolute; - right: 4px; - top: 50%; - transform: translateY(calc(-50% - 10px)); - display: flex; - gap: 4px; + position: absolute; + right: 4px; + top: 50%; + transform: translateY(calc(-50% - 10px)); + display: flex; + gap: 4px; } .tableHeader { - display: flex; - align-items: center; - justify-content: space-between; - margin-top: 24px; + display: flex; + align-items: center; + justify-content: space-between; + margin-top: 24px; } @media (width <= 600px) { - .tableHeader__heading { - display: flex; - flex-direction: column; - } + .tableHeader__heading { + display: flex; + flex-direction: column; + } } .tableHeader__actions { - display: flex; - gap: 8px; - margin-bottom: 16px; + display: flex; + gap: 8px; + margin-bottom: 16px; } .company-member-cell__label { - display: none; + display: none; } .company-member-cell_email { - max-width: 216px; + max-width: 216px; } @media (width <= 600px) { - .company-member-cell_email { - max-width: 100%; - word-break: break-all; - } + .company-member-cell_email { + max-width: 100%; + word-break: break-all; + } } .company-member-cell_email-value { - display: flex; - align-items: center; - gap: 8px; + display: flex; + align-items: center; + gap: 8px; } .company-member-cell_email-icon { - flex-shrink: 0; - font-size: 16px; - width: 16px; - height: 16px; + flex-shrink: 0; + font-size: 16px; + width: 16px; + height: 16px; } .company-member-cell_name { - max-width: 200px; + max-width: 200px; } @media (width <= 600px) { - .company-member-cell_name { - max-width: 100%; - word-break: break-word; - } + .company-member-cell_name { + max-width: 100%; + word-break: break-word; + } } .company-member-cell_accessed { - color: #1B5E20; + color: #1b5e20; } @media (prefers-color-scheme: dark) { - .company-member-cell_accessed { - color: #4CAF50; - } + .company-member-cell_accessed { + color: #4caf50; + } } .company-member-cell_not-accessed { - color: #B71C1C; + color: #b71c1c; } @media (prefers-color-scheme: dark) { - .company-member-cell_not-accessed { - color: #E53935; - } + .company-member-cell_not-accessed { + color: #e53935; + } } .company-member-cell_content-center { - text-align: center; + text-align: center; } @media (width <= 600px) { - .company-member-cell_content-center { - text-align: initial; - } + .company-member-cell_content-center { + text-align: initial; + } } .company-members-table { - margin-bottom: 32px; + margin-bottom: 32px; } @media (width <= 600px) { - .company-members-table { - display: grid; - grid-template-columns: auto 1fr; - max-width: 100%; - } - - .company-members-table-heading { - display: none; - } - - .company-members-table ::ng-deep tbody { - display: grid; - grid-template-columns: subgrid; - grid-column: 1 / 3; - } - - .company-member-row { - display: grid; - grid-template-columns: subgrid; - grid-column: 1 / 3; - grid-gap: 12px 28px; - border-bottom-color: var(--mat-table-row-item-outline-color, rgba(0, 0, 0, 0.12)); - border-bottom-width: var(--mat-table-row-item-outline-width, 1px); - border-bottom-style: solid; - height: auto; - padding: 20px 0; - } - - .company-member-cell { - display: grid; - grid-template-columns: subgrid; - grid-column: 1 / 3; - border-bottom: none; - } - - .company-member-cell::before { - content: attr(data-label); - display: inline-block; - font-weight: bold; - white-space: nowrap; - } - - .company-member-cell-action { - grid-column: 1 / span 3; - display: flex; - justify-content: flex-end; - border-bottom: none; - } - - .active-status-toggle { - padding-bottom: 4px; - } + .company-members-table { + display: grid; + grid-template-columns: auto 1fr; + max-width: 100%; + } + + .company-members-table-heading { + display: none; + } + + .company-members-table ::ng-deep tbody { + display: grid; + grid-template-columns: subgrid; + grid-column: 1 / 3; + } + + .company-member-row { + display: grid; + grid-template-columns: subgrid; + grid-column: 1 / 3; + grid-gap: 12px 28px; + border-bottom-color: var(--mat-table-row-item-outline-color, rgba(0, 0, 0, 0.12)); + border-bottom-width: var(--mat-table-row-item-outline-width, 1px); + border-bottom-style: solid; + height: auto; + padding: 20px 0; + } + + .company-member-cell { + display: grid; + grid-template-columns: subgrid; + grid-column: 1 / 3; + border-bottom: none; + } + + .company-member-cell::before { + content: attr(data-label); + display: inline-block; + font-weight: bold; + white-space: nowrap; + } + + .company-member-cell-action { + grid-column: 1 / span 3; + display: flex; + justify-content: flex-end; + border-bottom: none; + } + + .active-status-toggle { + padding-bottom: 4px; + } } .table-icon { - margin-bottom: -8px; + margin-bottom: -8px; } .role-option { - padding-top: 4px; - padding-bottom: 4px + padding-top: 4px; + padding-bottom: 4px; } .role-option-header { - display: flex; - align-items: center; + display: flex; + align-items: center; } .role-option-header__mark { - font-size: 14px; - margin-bottom: -10px; - font-weight: 600; - margin-left: 4px; + font-size: 14px; + margin-bottom: -10px; + font-weight: 600; + margin-left: 4px; } .role-option__description { - color: rgba(0,0,0,0.64); - font-size: 13px; - margin-top: 2px; + color: rgba(0, 0, 0, 0.64); + font-size: 13px; + margin-top: 2px; } .test-connections-toggle { - margin-top: 32px; + margin-top: 32px; } .cname-hint { - margin-top: 2px; + margin-top: 2px; } .cname-hint_dimmed { - color: rgba(0,0,0,0.54); + color: rgba(0, 0, 0, 0.54); } @media (prefers-color-scheme: dark) { - .cname-hint_dimmed { - color: rgba(255,255,255,0.7); - } + .cname-hint_dimmed { + color: rgba(255, 255, 255, 0.7); + } } .cname-value { - position: relative; - padding: 0 4px; + position: relative; + padding: 0 4px; } .cname-value::after { - content: ''; - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - background-color: var(--color-accentedPalette-200); - border-radius: 4px; - height: 100%; - opacity: 0.5; - width: 100%; - z-index: -1; + content: ""; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + background-color: var(--color-accentedPalette-200); + border-radius: 4px; + height: 100%; + opacity: 0.5; + width: 100%; + z-index: -1; } @media (prefers-color-scheme: dark) { - .cname-value::after { - background-color: var(--color-accentedPalette-700); - } + .cname-value::after { + background-color: var(--color-accentedPalette-700); + } } - - - - .white-label { - display: grid; - grid-template-columns: 1fr 1fr; - grid-column-gap: 32px; - margin-bottom: 32px; + display: grid; + grid-template-columns: 1fr 1fr; + grid-column-gap: 32px; + margin-bottom: 32px; } .white-label-settings { - position: relative; + position: relative; } .white-label-settings-images { - display: flex; - gap: 24px; - margin-top: 16px; - margin-bottom: 24px; + display: flex; + gap: 24px; + margin-top: 16px; + margin-bottom: 24px; } .upload-logo-form { - display: flex; - align-items: center; - gap: 8px; + display: flex; + align-items: center; + gap: 8px; } .white-label-preview { - height: auto; - margin-top: 8px; - width: 100%; + height: auto; + margin-top: 8px; + width: 100%; } .white-label-how-to { - border: 1px solid var(--color-primaryPalette-100); - color: rgba(0,0,0,0.72); - padding: 20px; + border: 1px solid var(--color-primaryPalette-100); + color: rgba(0, 0, 0, 0.72); + padding: 20px; } @media (prefers-color-scheme: dark) { - .white-label-how-to { - border: 1px solid var(--color-primaryPalette-700); - color: rgba(255,255,255,0.7); - } + .white-label-how-to { + border: 1px solid var(--color-primaryPalette-700); + color: rgba(255, 255, 255, 0.7); + } } .white-label-settings__overlay { - position: absolute; - top: -8px; - left: 0; - right: 0; - bottom: 0; - background-color: rgba(255,255,255,0.64); - z-index: 2; + position: absolute; + top: -8px; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(255, 255, 255, 0.64); + z-index: 2; } .cname-instructions { - list-style: none; -} \ No newline at end of file + list-style: none; +} diff --git a/frontend/src/app/components/company/delete-domain-dialog/delete-domain-dialog.component.spec.ts b/frontend/src/app/components/company/delete-domain-dialog/delete-domain-dialog.component.spec.ts index 90530600d..731903753 100644 --- a/frontend/src/app/components/company/delete-domain-dialog/delete-domain-dialog.component.spec.ts +++ b/frontend/src/app/components/company/delete-domain-dialog/delete-domain-dialog.component.spec.ts @@ -1,37 +1,35 @@ +import { provideHttpClient } from '@angular/common/http'; import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { DeleteDomainDialogComponent } from './delete-domain-dialog.component'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; -import { provideHttpClient } from '@angular/common/http'; import { Angulartics2Module } from 'angulartics2'; +import { DeleteDomainDialogComponent } from './delete-domain-dialog.component'; describe('DeleteDomainDialogComponent', () => { - let component: DeleteDomainDialogComponent; - let fixture: ComponentFixture; + let component: DeleteDomainDialogComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [ - Angulartics2Module.forRoot(), - DeleteDomainDialogComponent - ], - providers: [ - provideHttpClient(), - { provide: MAT_DIALOG_DATA, useValue: { - companyId: '', - domain: '' - }}, - { provide: MatDialogRef, useValue: MatDialogRef } - ] - }) - .compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [Angulartics2Module.forRoot(), DeleteDomainDialogComponent], + providers: [ + provideHttpClient(), + { + provide: MAT_DIALOG_DATA, + useValue: { + companyId: '', + domain: '', + }, + }, + { provide: MatDialogRef, useValue: MatDialogRef }, + ], + }).compileComponents(); - fixture = TestBed.createComponent(DeleteDomainDialogComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + fixture = TestBed.createComponent(DeleteDomainDialogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/company/delete-member-dialog/delete-member-dialog.component.spec.ts b/frontend/src/app/components/company/delete-member-dialog/delete-member-dialog.component.spec.ts index 2d0ea5c50..d720160f3 100644 --- a/frontend/src/app/components/company/delete-member-dialog/delete-member-dialog.component.spec.ts +++ b/frontend/src/app/components/company/delete-member-dialog/delete-member-dialog.component.spec.ts @@ -1,45 +1,41 @@ +import { provideHttpClient } from '@angular/common/http'; import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { FormsModule } from '@angular/forms'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; - +import { MatSnackBarModule } from '@angular/material/snack-bar'; import { Angulartics2Module } from 'angulartics2'; import { DeleteMemberDialogComponent } from './delete-member-dialog.component'; -import { FormsModule } from '@angular/forms'; -import { MatSnackBarModule } from '@angular/material/snack-bar'; -import { provideHttpClient } from '@angular/common/http'; describe('DeleteMemberDialogComponent', () => { - let component: DeleteMemberDialogComponent; - let fixture: ComponentFixture; + let component: DeleteMemberDialogComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [ - MatSnackBarModule, - FormsModule, - Angulartics2Module.forRoot(), - DeleteMemberDialogComponent - ], - providers: [ - provideHttpClient(), - { provide: MAT_DIALOG_DATA, useValue: { - companyId: '', - user: { - id: '', - email: '', - name: '' - } - }}, - { provide: MatDialogRef, useValue: MatDialogRef } - ], -}) - .compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [MatSnackBarModule, FormsModule, Angulartics2Module.forRoot(), DeleteMemberDialogComponent], + providers: [ + provideHttpClient(), + { + provide: MAT_DIALOG_DATA, + useValue: { + companyId: '', + user: { + id: '', + email: '', + name: '', + }, + }, + }, + { provide: MatDialogRef, useValue: MatDialogRef }, + ], + }).compileComponents(); - fixture = TestBed.createComponent(DeleteMemberDialogComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + fixture = TestBed.createComponent(DeleteMemberDialogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/company/invite-member-dialog/invite-member-dialog.component.css b/frontend/src/app/components/company/invite-member-dialog/invite-member-dialog.component.css index 92790cb78..10f62ab32 100644 --- a/frontend/src/app/components/company/invite-member-dialog/invite-member-dialog.component.css +++ b/frontend/src/app/components/company/invite-member-dialog/invite-member-dialog.component.css @@ -1,44 +1,43 @@ .add-member-input { - width: 100%; - margin-bottom: 8px; + width: 100%; + margin-bottom: 8px; } .role-button { - width: 100%; - margin-bottom: 8px; + width: 100%; + margin-bottom: 8px; } .role-button .mdc-button__label { - color: rgba(0,0,0,0.55); - font-weight: 400; + color: rgba(0, 0, 0, 0.55); + font-weight: 400; } .role-option { - padding-top: 4px; - padding-bottom: 4px + padding-top: 4px; + padding-bottom: 4px; } .role-option-header { - display: flex; - align-items: center; + display: flex; + align-items: center; } .role-option-header__mark { - font-size: 14px; - margin-bottom: -10px; - font-weight: 600; - margin-left: 4px; + font-size: 14px; + margin-bottom: -10px; + font-weight: 600; + margin-left: 4px; } .role-option__description { - color: rgba(0,0,0,0.64); - font-size: 13px; - margin-top: 2px; + color: rgba(0, 0, 0, 0.64); + font-size: 13px; + margin-top: 2px; } @media (prefers-color-scheme: dark) { - .role-option__description { - color: rgba(255,255,255,0.7); - } + .role-option__description { + color: rgba(255, 255, 255, 0.7); + } } - diff --git a/frontend/src/app/components/company/invite-member-dialog/invite-member-dialog.component.spec.ts b/frontend/src/app/components/company/invite-member-dialog/invite-member-dialog.component.spec.ts index 3aad7d25a..46b2da222 100644 --- a/frontend/src/app/components/company/invite-member-dialog/invite-member-dialog.component.spec.ts +++ b/frontend/src/app/components/company/invite-member-dialog/invite-member-dialog.component.spec.ts @@ -1,40 +1,38 @@ +import { provideHttpClient } from '@angular/common/http'; import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { FormsModule } from '@angular/forms'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; - -import { Angulartics2Module } from 'angulartics2'; +import { MatSnackBarModule } from '@angular/material/snack-bar'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { FormsModule } from '@angular/forms'; +import { Angulartics2Module } from 'angulartics2'; import { InviteMemberDialogComponent } from './invite-member-dialog.component'; -import { MatSnackBarModule } from '@angular/material/snack-bar'; -import { provideHttpClient } from '@angular/common/http'; describe('AddMemberDialogComponent', () => { - let component: InviteMemberDialogComponent; - let fixture: ComponentFixture; + let component: InviteMemberDialogComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [ - MatSnackBarModule, - FormsModule, - Angulartics2Module.forRoot(), - InviteMemberDialogComponent, - BrowserAnimationsModule - ], - providers: [ - provideHttpClient(), - { provide: MAT_DIALOG_DATA, useValue: { id: '', connections: [] } }, - { provide: MatDialogRef, useValue: MatDialogRef } - ], -}) - .compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + MatSnackBarModule, + FormsModule, + Angulartics2Module.forRoot(), + InviteMemberDialogComponent, + BrowserAnimationsModule, + ], + providers: [ + provideHttpClient(), + { provide: MAT_DIALOG_DATA, useValue: { id: '', connections: [] } }, + { provide: MatDialogRef, useValue: MatDialogRef }, + ], + }).compileComponents(); - fixture = TestBed.createComponent(InviteMemberDialogComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + fixture = TestBed.createComponent(InviteMemberDialogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/company/revoke-invitation-dialog/revoke-invitation-dialog.component.spec.ts b/frontend/src/app/components/company/revoke-invitation-dialog/revoke-invitation-dialog.component.spec.ts index 1fd4e9314..30269255c 100644 --- a/frontend/src/app/components/company/revoke-invitation-dialog/revoke-invitation-dialog.component.spec.ts +++ b/frontend/src/app/components/company/revoke-invitation-dialog/revoke-invitation-dialog.component.spec.ts @@ -1,40 +1,34 @@ +import { provideHttpClient } from '@angular/common/http'; import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { RevokeInvitationDialogComponent } from './revoke-invitation-dialog.component'; -import { Angulartics2Module } from 'angulartics2'; -import { MatSnackBarModule } from '@angular/material/snack-bar'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; -import { provideHttpClient } from '@angular/common/http'; +import { MatSnackBarModule } from '@angular/material/snack-bar'; +import { Angulartics2Module } from 'angulartics2'; +import { RevokeInvitationDialogComponent } from './revoke-invitation-dialog.component'; describe('RevokeInvitationDialogComponent', () => { - let component: RevokeInvitationDialogComponent; - let fixture: ComponentFixture; + let component: RevokeInvitationDialogComponent; + let fixture: ComponentFixture; - const _mockDialogRef = { - close: () => { } - }; + const _mockDialogRef = { + close: () => {}, + }; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [ - MatSnackBarModule, - Angulartics2Module.forRoot(), - RevokeInvitationDialogComponent - ], - providers: [ - provideHttpClient(), - { provide: MAT_DIALOG_DATA, useValue: { companyId: '', userEmail: '' } }, - { provide: MatDialogRef, useValue: MatDialogRef } - ], -}) - .compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [MatSnackBarModule, Angulartics2Module.forRoot(), RevokeInvitationDialogComponent], + providers: [ + provideHttpClient(), + { provide: MAT_DIALOG_DATA, useValue: { companyId: '', userEmail: '' } }, + { provide: MatDialogRef, useValue: MatDialogRef }, + ], + }).compileComponents(); - fixture = TestBed.createComponent(RevokeInvitationDialogComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + fixture = TestBed.createComponent(RevokeInvitationDialogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/connect-db/connect-db.component.css b/frontend/src/app/components/connect-db/connect-db.component.css index d0ce93990..359d9d312 100644 --- a/frontend/src/app/components/connect-db/connect-db.component.css +++ b/frontend/src/app/components/connect-db/connect-db.component.css @@ -1,213 +1,213 @@ :host app-alert:not(:empty) { - --alert-margin: 24px; + --alert-margin: 24px; } .connectPage { - margin-top: var(--top-margin); - margin-bottom: 4em; + margin-top: var(--top-margin); + margin-bottom: 4em; } .form { - display: grid; - grid-template-columns: repeat(4, 1fr); - grid-gap: 0.5em 1.5em; - margin: 0 auto 40px; - width: clamp(18.75em, 96%, 50em); + display: grid; + grid-template-columns: repeat(4, 1fr); + grid-gap: 0.5em 1.5em; + margin: 0 auto 40px; + width: clamp(18.75em, 96%, 50em); } @media screen and (max-width: 600px) { - .form { - display: flex; - flex-direction: column; - padding: 0 9vw; - width: 100%; - } + .form { + display: flex; + flex-direction: column; + padding: 0 9vw; + width: 100%; + } } .credentials-fieldset { - display: grid; - grid-template-columns: subgrid; - grid-template-rows: subgrid; - grid-column: 1 / span 4; - grid-row: 5 / span 4; + display: grid; + grid-template-columns: subgrid; + grid-template-rows: subgrid; + grid-column: 1 / span 4; + grid-row: 5 / span 4; } @media (width <= 600px) { - .credentials-fieldset { - display: flex; - flex-direction: column; - } + .credentials-fieldset { + display: flex; + flex-direction: column; + } } .credentials-fieldset-no-warning { - display: grid; - grid-template-columns: subgrid; - grid-template-rows: subgrid; - grid-column: 1 / span 4; - grid-row: 4 / span 4; + display: grid; + grid-template-columns: subgrid; + grid-template-rows: subgrid; + grid-column: 1 / span 4; + grid-row: 4 / span 4; } @media (width <= 600px) { - .credentials-fieldset-no-warning { - display: flex; - flex-direction: column; - } + .credentials-fieldset-no-warning { + display: flex; + flex-direction: column; + } } .mat-h1 { - margin-top: 2vw; + margin-top: 2vw; } .warningMessage { - display: flex; - align-items: center; - margin-bottom: 8px; - padding: 16px 12px; + display: flex; + align-items: center; + margin-bottom: 8px; + padding: 16px 12px; } @media (prefers-color-scheme: dark) { - .warningMessage { - background: #424242; - } + .warningMessage { + background: #424242; + } } @media (prefers-color-scheme: light) { - .warningMessage { - background: #F5F5F5; - } + .warningMessage { + background: #f5f5f5; + } } .warningMessage__icon { - flex-shrink: 0; - margin-right: 12px; + flex-shrink: 0; + margin-right: 12px; } .warningMessage .mat-body-1 { - margin-bottom: 0; + margin-bottom: 0; } .connectForm__ipAlert { - --alert-margin: 0; + --alert-margin: 0; } .connectForm__title { - grid-column: 1 / span 3; + grid-column: 1 / span 3; } .connectForm__hostname, .connectForm__port { - padding-bottom: 2em; + padding-bottom: 2em; } .connectForm__fullLine { - grid-column: 1 / span 4; + grid-column: 1 / span 4; } .connectForm__typeSwitch { - margin-bottom: 12px; + margin-bottom: 12px; } .connectForm__typeSwitch ::ng-deep .mat-button-toggle-checked { - background-color: rgba(0, 0, 0, 0.08) !important; + background-color: rgba(0, 0, 0, 0.08) !important; } .radio-button { - margin-right: 20px; + margin-right: 20px; } .form-textarea { - resize: none; + resize: none; } .test-connection-actions { - grid-column: 1 / span 4; - display: flex; - flex-direction: column; - align-items: center; - gap: 12px; - margin-top: 16px; + grid-column: 1 / span 4; + display: flex; + flex-direction: column; + align-items: center; + gap: 12px; + margin-top: 16px; } .test-connection-message { - color: rgba(0, 0, 0, 0.54); - margin-bottom: 4px; + color: rgba(0, 0, 0, 0.54); + margin-bottom: 4px; } .actions { - grid-column: 1 / span 4; - display: flex; - align-items: center; - justify-content: flex-end; - margin-top: 12px; + grid-column: 1 / span 4; + display: flex; + align-items: center; + justify-content: flex-end; + margin-top: 12px; } .delete-button { - margin-right: auto; + margin-right: auto; } .test-button { - margin-right: 20px; + margin-right: 20px; } .instruction__docker { - margin-top: 32px; - margin-bottom: auto; + margin-top: 32px; + margin-bottom: auto; } .github-button { - margin-bottom: 20px; + margin-bottom: 20px; } .github-button ::ng-deep .mdc-button__label { - display: flex; - align-items: center; - justify-content: space-between; - width: 100%; + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; } .github-icon { - margin-right: 8px; - margin-left: -4px; - height: 22px; - width: 22px; + margin-right: 8px; + margin-left: -4px; + height: 22px; + width: 22px; } .docker-instruction { - margin-top: 20px; + margin-top: 20px; } .instruction-steps { - list-style-position: inside; + list-style-position: inside; } .instruction-step { - margin: 1em 0; + margin: 1em 0; } .instruction-step__link { - color: var(--color-accentedPalette-500); - text-decoration: none; + color: var(--color-accentedPalette-500); + text-decoration: none; } .instruction-step__link:hover { - color: #FE5E85; - text-decoration: underline; + color: #fe5e85; + text-decoration: underline; } .instruction-command { - background: rgba(0, 0, 0, 0.04); - border-radius: 4px; - padding: 8px 12px; + background: rgba(0, 0, 0, 0.04); + border-radius: 4px; + padding: 8px 12px; } .agent-token { - display: flex; - margin-top: 32px; - margin-bottom: 20px; - width: 100%; + display: flex; + margin-top: 32px; + margin-bottom: 20px; + width: 100%; } .agent-token__copy-button { - margin-top: 4px; - margin-left: 12px; -} \ No newline at end of file + margin-top: 4px; + margin-left: 12px; +} diff --git a/frontend/src/app/components/connect-db/db-connection-confirm-dialog/db-connection-confirm-dialog.component.css b/frontend/src/app/components/connect-db/db-connection-confirm-dialog/db-connection-confirm-dialog.component.css index b315831d1..874b699a4 100644 --- a/frontend/src/app/components/connect-db/db-connection-confirm-dialog/db-connection-confirm-dialog.component.css +++ b/frontend/src/app/components/connect-db/db-connection-confirm-dialog/db-connection-confirm-dialog.component.css @@ -1,53 +1,53 @@ .connection-error-message { - display: block; - color: var(--mat-sidenav-content-text-color); - margin-bottom: 20px; + display: block; + color: var(--mat-sidenav-content-text-color); + margin-bottom: 20px; } .docs-notification { - display: block; - background-color: var(--bg-color); - color: var(--mat-sidenav-content-text-color); - margin-bottom: 20px; - padding: 8px 12px; + display: block; + background-color: var(--bg-color); + color: var(--mat-sidenav-content-text-color); + margin-bottom: 20px; + padding: 8px 12px; } @media (prefers-color-scheme: dark) { - .docs-notification { - --bg-color: #292929; - } + .docs-notification { + --bg-color: #292929; + } } @media (prefers-color-scheme: light) { - .docs-notification { - --bg-color: #F4F4F4; - } + .docs-notification { + --bg-color: #f4f4f4; + } } .docs-link { - color: #EF6C00; - font-weight: 500; - text-decoration: none; + color: #ef6c00; + font-weight: 500; + text-decoration: none; } .connection-error__actions { - display: flex; - justify-content: space-between; + display: flex; + justify-content: space-between; } .main-actions { - display: flex; - gap: 12px; + display: flex; + gap: 12px; } .link { - display: inline-block; - color: var(--color-primaryPalette-500); - margin-top: 15px; + display: inline-block; + color: var(--color-primaryPalette-500); + margin-top: 15px; } @media (prefers-color-scheme: dark) { - .link { - color: var(--color-whitePalette-500); - } -} \ No newline at end of file + .link { + color: var(--color-whitePalette-500); + } +} diff --git a/frontend/src/app/components/connect-db/db-connection-confirm-dialog/db-connection-confirm-dialog.component.ts b/frontend/src/app/components/connect-db/db-connection-confirm-dialog/db-connection-confirm-dialog.component.ts index 55d275c1e..89148a40c 100644 --- a/frontend/src/app/components/connect-db/db-connection-confirm-dialog/db-connection-confirm-dialog.component.ts +++ b/frontend/src/app/components/connect-db/db-connection-confirm-dialog/db-connection-confirm-dialog.component.ts @@ -1,76 +1,77 @@ +import { NgIf } from '@angular/common'; import { Component, Inject, OnInit } from '@angular/core'; -import { MatDialogRef, MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog'; import { MatButtonModule } from '@angular/material/button'; +import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog'; import { Router } from '@angular/router'; import { ConnectionsService } from 'src/app/services/connections.service'; -import { DbConnectionDeleteDialogComponent } from '../db-connection-delete-dialog/db-connection-delete-dialog.component'; import { environment } from 'src/environments/environment'; -import { NgIf } from '@angular/common'; +import { DbConnectionDeleteDialogComponent } from '../db-connection-delete-dialog/db-connection-delete-dialog.component'; @Component({ - selector: 'app-db-connection-confirm-dialog', - templateUrl: './db-connection-confirm-dialog.component.html', - styleUrls: ['./db-connection-confirm-dialog.component.css'], - imports: [ - NgIf, - MatDialogModule, - MatButtonModule - ] + selector: 'app-db-connection-confirm-dialog', + templateUrl: './db-connection-confirm-dialog.component.html', + styleUrls: ['./db-connection-confirm-dialog.component.css'], + imports: [NgIf, MatDialogModule, MatButtonModule], }) export class DbConnectionConfirmDialogComponent implements OnInit { + public isSaas = (environment as any).saas; + public submitting: boolean = false; + public providerNames = { + amazon: 'Amazon Web Services', + azure: 'Microsoft Azure', + google: 'Google Cloud Platform', + mongoatlas: 'MongoDB Atlas', + digitalocean: 'DigitalOcean', + scylladbcloud: 'ScyllaDB Cloud', + amazonDynamoDB: 'Amazon DynamoDB', + }; + public providerDocsLink = { + amazon: 'https://docs.rocketadmin.com/Create%20connections/Direct%20connection/create_aws_mysql', + azure: 'https://learn.microsoft.com/en-us/azure/mysql/flexible-server/how-to-manage-firewall-portal', + google: 'https://docs.rocketadmin.com/Create%20connections/Direct%20connection/create_google_cloud', + mongoatlas: 'https://docs.rocketadmin.com/Create%20connections/Direct%20connection/create_mongo_atlas', + digitalocean: + 'https://docs.rocketadmin.com/Create%20connections/Direct%20connection/create_digitalocean_postgresql', + scylladbcloud: 'https://docs.rocketadmin.com/Create%20connections/Direct%20connection/create_scylladb', + amazonDynamoDB: 'https://docs.rocketadmin.com/Create%20connections/Direct%20connection/create_aws_dynamodb', + }; - public isSaas = (environment as any).saas; - public submitting: boolean = false; - public providerNames = { - amazon: 'Amazon Web Services', - azure: 'Microsoft Azure', - google: 'Google Cloud Platform', - mongoatlas: 'MongoDB Atlas', - digitalocean: 'DigitalOcean', - scylladbcloud: 'ScyllaDB Cloud', - amazonDynamoDB: 'Amazon DynamoDB', - }; - public providerDocsLink = { - amazon: 'https://docs.rocketadmin.com/Create%20connections/Direct%20connection/create_aws_mysql', - azure: 'https://learn.microsoft.com/en-us/azure/mysql/flexible-server/how-to-manage-firewall-portal', - google: 'https://docs.rocketadmin.com/Create%20connections/Direct%20connection/create_google_cloud', - mongoatlas: 'https://docs.rocketadmin.com/Create%20connections/Direct%20connection/create_mongo_atlas', - digitalocean: 'https://docs.rocketadmin.com/Create%20connections/Direct%20connection/create_digitalocean_postgresql', - scylladbcloud: 'https://docs.rocketadmin.com/Create%20connections/Direct%20connection/create_scylladb', - amazonDynamoDB: 'https://docs.rocketadmin.com/Create%20connections/Direct%20connection/create_aws_dynamodb', - } - - constructor( - @Inject(MAT_DIALOG_DATA) public data: any, - private _connections: ConnectionsService, - public dialogRef: MatDialogRef, - public router: Router, - ) { } + constructor( + @Inject(MAT_DIALOG_DATA) public data: any, + private _connections: ConnectionsService, + public dialogRef: MatDialogRef, + public router: Router, + ) {} - ngOnInit(): void {} + ngOnInit(): void {} - editConnection() { - this._connections.updateConnection(this.data.dbCreds, this.data.masterKey) - .subscribe(() => { - this.router.navigate([`/dashboard/${this.data.dbCreds.id}`]); - }, undefined, () => { - this.submitting = false; - }) - } + editConnection() { + this._connections.updateConnection(this.data.dbCreds, this.data.masterKey).subscribe( + () => { + this.router.navigate([`/dashboard/${this.data.dbCreds.id}`]); + }, + undefined, + () => { + this.submitting = false; + }, + ); + } - createConnection() { - this._connections.createConnection(this.data.dbCreds, this.data.masterKey) - .subscribe((res: any) => { - const connectionID = res.id!; - this.router.navigate([`/dashboard/${connectionID}`]); - }, - undefined, - () => {this.submitting = false} - ) - } + createConnection() { + this._connections.createConnection(this.data.dbCreds, this.data.masterKey).subscribe( + (res: any) => { + const connectionID = res.id!; + this.router.navigate([`/dashboard/${connectionID}`]); + }, + undefined, + () => { + this.submitting = false; + }, + ); + } - openIntercome() { - // @ts-expect-error - Intercom('show'); - } + openIntercome() { + // @ts-expect-error + Intercom('show'); + } } diff --git a/frontend/src/app/components/connect-db/db-connection-delete-dialog/db-connection-delete-dialog.component.css b/frontend/src/app/components/connect-db/db-connection-delete-dialog/db-connection-delete-dialog.component.css index 66eaeaf33..716f248ce 100644 --- a/frontend/src/app/components/connect-db/db-connection-delete-dialog/db-connection-delete-dialog.component.css +++ b/frontend/src/app/components/connect-db/db-connection-delete-dialog/db-connection-delete-dialog.component.css @@ -1,20 +1,20 @@ .mat-mdc-dialog-content { - margin-bottom: -20px; + margin-bottom: -20px; } .radio-group { - display: flex; - flex-direction: column; + display: flex; + flex-direction: column; } .radio-button { - margin: 8px 0; + margin: 8px 0; } .message { - width: 100%; + width: 100%; } .message textarea { - resize: none; -} \ No newline at end of file + resize: none; +} diff --git a/frontend/src/app/components/connect-db/db-connection-ip-access-dialog/db-connection-ip-access-dialog.component.css b/frontend/src/app/components/connect-db/db-connection-ip-access-dialog/db-connection-ip-access-dialog.component.css index 8e9bb92c0..19a18582d 100644 --- a/frontend/src/app/components/connect-db/db-connection-ip-access-dialog/db-connection-ip-access-dialog.component.css +++ b/frontend/src/app/components/connect-db/db-connection-ip-access-dialog/db-connection-ip-access-dialog.component.css @@ -1,21 +1,21 @@ .codeSnippet { - display: flex; - align-items: center; - /* justify-content: space-between; */ - margin: 8px 0; + display: flex; + align-items: center; + /* justify-content: space-between; */ + margin: 8px 0; } .code { - background: rgba(0, 0, 0, 0.04); - padding: 8px 12px; - border-radius: 4px; - margin-right: 20px; + background: rgba(0, 0, 0, 0.04); + padding: 8px 12px; + border-radius: 4px; + margin-right: 20px; } .code pre { - white-space: break-spaces; + white-space: break-spaces; } .rules { - padding: 16px; + padding: 16px; } diff --git a/frontend/src/app/components/connect-db/db-connection-ip-access-dialog/db-connection-ip-access-dialog.component.spec.ts b/frontend/src/app/components/connect-db/db-connection-ip-access-dialog/db-connection-ip-access-dialog.component.spec.ts index 58e476ad2..c93aaa12b 100644 --- a/frontend/src/app/components/connect-db/db-connection-ip-access-dialog/db-connection-ip-access-dialog.component.spec.ts +++ b/frontend/src/app/components/connect-db/db-connection-ip-access-dialog/db-connection-ip-access-dialog.component.spec.ts @@ -1,35 +1,29 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog'; - -import { DbConnectionIpAccessDialogComponent } from './db-connection-ip-access-dialog.component'; import { MatSnackBarModule } from '@angular/material/snack-bar'; +import { DbConnectionIpAccessDialogComponent } from './db-connection-ip-access-dialog.component'; describe('DbConnectionIpAccessDialogComponent', () => { - let component: DbConnectionIpAccessDialogComponent; - let fixture: ComponentFixture; + let component: DbConnectionIpAccessDialogComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [ - MatDialogModule, - MatSnackBarModule, - DbConnectionIpAccessDialogComponent - ], - providers: [ - { provide: MAT_DIALOG_DATA, useValue: { host: "database-2.cvfuxe8nltiq.us-east-2.rds.amazonaws.com" } }, - { provide: MatDialogRef, useValue: {} }, - ] -}) - .compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [MatDialogModule, MatSnackBarModule, DbConnectionIpAccessDialogComponent], + providers: [ + { provide: MAT_DIALOG_DATA, useValue: { host: 'database-2.cvfuxe8nltiq.us-east-2.rds.amazonaws.com' } }, + { provide: MatDialogRef, useValue: {} }, + ], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(DbConnectionIpAccessDialogComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(DbConnectionIpAccessDialogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/connect-db/db-connection-ip-access-dialog/db-connection-ip-access-dialog.component.ts b/frontend/src/app/components/connect-db/db-connection-ip-access-dialog/db-connection-ip-access-dialog.component.ts index 55cd71b84..eba096ad5 100644 --- a/frontend/src/app/components/connect-db/db-connection-ip-access-dialog/db-connection-ip-access-dialog.component.ts +++ b/frontend/src/app/components/connect-db/db-connection-ip-access-dialog/db-connection-ip-access-dialog.component.ts @@ -1,47 +1,44 @@ +import { ClipboardModule } from '@angular/cdk/clipboard'; +import { NgIf } from '@angular/common'; import { Component, Inject, OnInit } from '@angular/core'; -import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog'; import { MatButtonModule } from '@angular/material/button'; +import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog'; import { MatIconModule } from '@angular/material/icon'; import { MatTooltipModule } from '@angular/material/tooltip'; -import { ClipboardModule } from '@angular/cdk/clipboard'; -import { NotificationsService } from 'src/app/services/notifications.service'; import { Connection } from 'src/app/models/connection'; +import { NotificationsService } from 'src/app/services/notifications.service'; import { IpAddressButtonComponent } from '../../ui-components/ip-address-button/ip-address-button.component'; -import { NgIf } from '@angular/common'; @Component({ - selector: 'app-db-connection-ip-access-dialog', - templateUrl: './db-connection-ip-access-dialog.component.html', - styleUrls: ['./db-connection-ip-access-dialog.component.css'], - standalone: true, - imports: [ - NgIf, - MatDialogModule, - MatButtonModule, - MatIconModule, - MatTooltipModule, - ClipboardModule, - IpAddressButtonComponent - ] + selector: 'app-db-connection-ip-access-dialog', + templateUrl: './db-connection-ip-access-dialog.component.html', + styleUrls: ['./db-connection-ip-access-dialog.component.css'], + standalone: true, + imports: [ + NgIf, + MatDialogModule, + MatButtonModule, + MatIconModule, + MatTooltipModule, + ClipboardModule, + IpAddressButtonComponent, + ], }) export class DbConnectionIpAccessDialogComponent implements OnInit { + public provider: string = null; - public provider: string = null; - - constructor( - @Inject(MAT_DIALOG_DATA) public data: { - provider: string, - db: Connection, - }, - private _notifications: NotificationsService, - - ) { } - - ngOnInit(): void { + constructor( + @Inject(MAT_DIALOG_DATA) + public data: { + provider: string; + db: Connection; + }, + private _notifications: NotificationsService, + ) {} - } + ngOnInit(): void {} - showCopyNotification(message: string) { - this._notifications.showSuccessSnackbar(message); - } + showCopyNotification(message: string) { + this._notifications.showSuccessSnackbar(message); + } } diff --git a/frontend/src/app/components/connect-db/db-credentials-forms/base-credentials-form/base-credentials-form.component.css b/frontend/src/app/components/connect-db/db-credentials-forms/base-credentials-form/base-credentials-form.component.css index 7f01bfa36..9f76e0077 100644 --- a/frontend/src/app/components/connect-db/db-credentials-forms/base-credentials-form/base-credentials-form.component.css +++ b/frontend/src/app/components/connect-db/db-credentials-forms/base-credentials-form/base-credentials-form.component.css @@ -1,49 +1,49 @@ .credentials-fieldset__1-2-columns { - grid-column: 1 / span 2; + grid-column: 1 / span 2; } .credentials-fieldset__3-4-columns { - grid-column: 3 / span 2; + grid-column: 3 / span 2; } .credentials-fieldset__1-3-columns { - grid-column: 1 / span 3; + grid-column: 1 / span 3; } .credentials-fieldset__1-4-columns { - grid-column: 1 / span 4; + grid-column: 1 / span 4; } .credentials-fieldset__hint-link { - color: inherit; + color: inherit; } .credentials-fieldset__hint-button { - color: inherit; - background: transparent; - border: none; - font-size: inherit; - text-decoration: underline; - cursor: pointer; + color: inherit; + background: transparent; + border: none; + font-size: inherit; + text-decoration: underline; + cursor: pointer; } .advanced-settings { - display: grid; - grid-template-columns: repeat(3, 1fr); - grid-gap: 0.25em 1.5em; + display: grid; + grid-template-columns: repeat(3, 1fr); + grid-gap: 0.25em 1.5em; } @media screen and (max-width: 600px) { - .advanced-settings { - display: flex; - flex-direction: column; - } + .advanced-settings { + display: flex; + flex-direction: column; + } } .advanced-settings__fullLine { - grid-column: 1 / span 3; + grid-column: 1 / span 3; } .checkbox-line { - margin-bottom: 16px; -} \ No newline at end of file + margin-bottom: 16px; +} diff --git a/frontend/src/app/components/connect-db/db-credentials-forms/base-credentials-form/base-credentials-form.component.spec.ts b/frontend/src/app/components/connect-db/db-credentials-forms/base-credentials-form/base-credentials-form.component.spec.ts index e87e9ef61..37d8a12f0 100644 --- a/frontend/src/app/components/connect-db/db-credentials-forms/base-credentials-form/base-credentials-form.component.spec.ts +++ b/frontend/src/app/components/connect-db/db-credentials-forms/base-credentials-form/base-credentials-form.component.spec.ts @@ -3,21 +3,20 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { BaseCredentialsFormComponent } from './base-credentials-form.component'; describe('BaseCredentialsFormComponent', () => { - let component: BaseCredentialsFormComponent; - let fixture: ComponentFixture; + let component: BaseCredentialsFormComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [BaseCredentialsFormComponent] - }) - .compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [BaseCredentialsFormComponent], + }).compileComponents(); - fixture = TestBed.createComponent(BaseCredentialsFormComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + fixture = TestBed.createComponent(BaseCredentialsFormComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/connect-db/db-credentials-forms/base-credentials-form/base-credentials-form.component.ts b/frontend/src/app/components/connect-db/db-credentials-forms/base-credentials-form/base-credentials-form.component.ts index 4da736aa9..de9dda864 100644 --- a/frontend/src/app/components/connect-db/db-credentials-forms/base-credentials-form/base-credentials-form.component.ts +++ b/frontend/src/app/components/connect-db/db-credentials-forms/base-credentials-form/base-credentials-form.component.ts @@ -3,23 +3,23 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; import { Connection } from 'src/app/models/connection'; @Component({ - selector: 'app-base-credentials-form', - templateUrl: './base-credentials-form.component.html', - styleUrl: './base-credentials-form.component.css' + selector: 'app-base-credentials-form', + templateUrl: './base-credentials-form.component.html', + styleUrl: './base-credentials-form.component.css', }) export class BaseCredentialsFormComponent { - @Input() connection: Connection; - @Input() readonly: boolean; - @Input() submitting: boolean; - @Input() masterKey: string; - @Input() accessLevel: string; + @Input() connection: Connection; + @Input() readonly: boolean; + @Input() submitting: boolean; + @Input() masterKey: string; + @Input() accessLevel: string; - @Output() switchToAgent = new EventEmitter(); - @Output() masterKeyChange = new EventEmitter(); + @Output() switchToAgent = new EventEmitter(); + @Output() masterKeyChange = new EventEmitter(); - public tunnelingServiceLink = "https://docs.rocketadmin.com/Create%20connections/Direct%20connection/create_pinggy"; + public tunnelingServiceLink = 'https://docs.rocketadmin.com/Create%20connections/Direct%20connection/create_pinggy'; - handleMasterKeyChange(newMasterKey: string): void { - this.masterKeyChange.emit(newMasterKey); - } + handleMasterKeyChange(newMasterKey: string): void { + this.masterKeyChange.emit(newMasterKey); + } } diff --git a/frontend/src/app/components/connect-db/db-credentials-forms/cassandra-credentials-form/cassandra-credentials-form.component.css b/frontend/src/app/components/connect-db/db-credentials-forms/cassandra-credentials-form/cassandra-credentials-form.component.css index 8b6ed6b0b..6f4923742 100644 --- a/frontend/src/app/components/connect-db/db-credentials-forms/cassandra-credentials-form/cassandra-credentials-form.component.css +++ b/frontend/src/app/components/connect-db/db-credentials-forms/cassandra-credentials-form/cassandra-credentials-form.component.css @@ -1,14 +1,14 @@ .connectForm__hostname, .connectForm__port { - padding-bottom: 20px; + padding-bottom: 20px; } @media (width <= 600px) { - .connectForm__hostname { - padding-bottom: 44px; - } + .connectForm__hostname { + padding-bottom: 44px; + } - .connectForm__port { - padding-bottom: 0; - } -} \ No newline at end of file + .connectForm__port { + padding-bottom: 0; + } +} diff --git a/frontend/src/app/components/connect-db/db-credentials-forms/cassandra-credentials-form/cassandra-credentials-form.component.spec.ts b/frontend/src/app/components/connect-db/db-credentials-forms/cassandra-credentials-form/cassandra-credentials-form.component.spec.ts index 6d664484a..4bb682d39 100644 --- a/frontend/src/app/components/connect-db/db-credentials-forms/cassandra-credentials-form/cassandra-credentials-form.component.spec.ts +++ b/frontend/src/app/components/connect-db/db-credentials-forms/cassandra-credentials-form/cassandra-credentials-form.component.spec.ts @@ -1,36 +1,30 @@ +import { provideHttpClient } from '@angular/common/http'; import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { Angulartics2Module } from 'angulartics2'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { Angulartics2Module } from 'angulartics2'; import { CassandraCredentialsFormComponent } from './cassandra-credentials-form.component'; -import { provideHttpClient } from '@angular/common/http'; describe('CassandraCredentialsFormComponent', () => { - let component: CassandraCredentialsFormComponent; - let fixture: ComponentFixture; + let component: CassandraCredentialsFormComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [ - BrowserAnimationsModule, - Angulartics2Module.forRoot(), - CassandraCredentialsFormComponent - ], - providers: [provideHttpClient()] - }) - .compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [BrowserAnimationsModule, Angulartics2Module.forRoot(), CassandraCredentialsFormComponent], + providers: [provideHttpClient()], + }).compileComponents(); - fixture = TestBed.createComponent(CassandraCredentialsFormComponent); - component = fixture.componentInstance; + fixture = TestBed.createComponent(CassandraCredentialsFormComponent); + component = fixture.componentInstance; - component.connection = { - id: "12345678" - } as any; + component.connection = { + id: '12345678', + } as any; - fixture.detectChanges(); - }); + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/connect-db/db-credentials-forms/clickhouse-credentials-form/clickhouse-credentials-form.component.css b/frontend/src/app/components/connect-db/db-credentials-forms/clickhouse-credentials-form/clickhouse-credentials-form.component.css index 8b6ed6b0b..6f4923742 100644 --- a/frontend/src/app/components/connect-db/db-credentials-forms/clickhouse-credentials-form/clickhouse-credentials-form.component.css +++ b/frontend/src/app/components/connect-db/db-credentials-forms/clickhouse-credentials-form/clickhouse-credentials-form.component.css @@ -1,14 +1,14 @@ .connectForm__hostname, .connectForm__port { - padding-bottom: 20px; + padding-bottom: 20px; } @media (width <= 600px) { - .connectForm__hostname { - padding-bottom: 44px; - } + .connectForm__hostname { + padding-bottom: 44px; + } - .connectForm__port { - padding-bottom: 0; - } -} \ No newline at end of file + .connectForm__port { + padding-bottom: 0; + } +} diff --git a/frontend/src/app/components/connect-db/db-credentials-forms/clickhouse-credentials-form/clickhouse-credentials-form.component.spec.ts b/frontend/src/app/components/connect-db/db-credentials-forms/clickhouse-credentials-form/clickhouse-credentials-form.component.spec.ts index 7b03b62e2..9cd3846d4 100644 --- a/frontend/src/app/components/connect-db/db-credentials-forms/clickhouse-credentials-form/clickhouse-credentials-form.component.spec.ts +++ b/frontend/src/app/components/connect-db/db-credentials-forms/clickhouse-credentials-form/clickhouse-credentials-form.component.spec.ts @@ -1,40 +1,38 @@ +import { provideHttpClient } from '@angular/common/http'; import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { ClickhouseCredentialsFormComponent } from './clickhouse-credentials-form.component'; import { FormsModule } from '@angular/forms'; import { MatCheckboxModule } from '@angular/material/checkbox'; -import { Angulartics2Module } from 'angulartics2'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { provideHttpClient } from '@angular/common/http'; +import { Angulartics2Module } from 'angulartics2'; +import { ClickhouseCredentialsFormComponent } from './clickhouse-credentials-form.component'; describe('ClickhouseCredentialsFormComponent', () => { - let component: ClickhouseCredentialsFormComponent; - let fixture: ComponentFixture; + let component: ClickhouseCredentialsFormComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [ - FormsModule, - MatCheckboxModule, - BrowserAnimationsModule, - Angulartics2Module.forRoot({}), - ClickhouseCredentialsFormComponent - ], - providers: [provideHttpClient()] -}) - .compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + FormsModule, + MatCheckboxModule, + BrowserAnimationsModule, + Angulartics2Module.forRoot({}), + ClickhouseCredentialsFormComponent, + ], + providers: [provideHttpClient()], + }).compileComponents(); - fixture = TestBed.createComponent(ClickhouseCredentialsFormComponent); - component = fixture.componentInstance; + fixture = TestBed.createComponent(ClickhouseCredentialsFormComponent); + component = fixture.componentInstance; - component.connection = { - id: "12345678" - } as any; + component.connection = { + id: '12345678', + } as any; - fixture.detectChanges(); - }); + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/connect-db/db-credentials-forms/db2-credentials-form/db2-credentials-form.component.css b/frontend/src/app/components/connect-db/db-credentials-forms/db2-credentials-form/db2-credentials-form.component.css index 8b6ed6b0b..6f4923742 100644 --- a/frontend/src/app/components/connect-db/db-credentials-forms/db2-credentials-form/db2-credentials-form.component.css +++ b/frontend/src/app/components/connect-db/db-credentials-forms/db2-credentials-form/db2-credentials-form.component.css @@ -1,14 +1,14 @@ .connectForm__hostname, .connectForm__port { - padding-bottom: 20px; + padding-bottom: 20px; } @media (width <= 600px) { - .connectForm__hostname { - padding-bottom: 44px; - } + .connectForm__hostname { + padding-bottom: 44px; + } - .connectForm__port { - padding-bottom: 0; - } -} \ No newline at end of file + .connectForm__port { + padding-bottom: 0; + } +} diff --git a/frontend/src/app/components/connect-db/db-credentials-forms/db2-credentials-form/db2-credentials-form.component.spec.ts b/frontend/src/app/components/connect-db/db-credentials-forms/db2-credentials-form/db2-credentials-form.component.spec.ts index 60458a62e..2d15eefeb 100644 --- a/frontend/src/app/components/connect-db/db-credentials-forms/db2-credentials-form/db2-credentials-form.component.spec.ts +++ b/frontend/src/app/components/connect-db/db-credentials-forms/db2-credentials-form/db2-credentials-form.component.spec.ts @@ -1,40 +1,38 @@ +import { provideHttpClient } from '@angular/common/http'; import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { Db2CredentialsFormComponent } from './db2-credentials-form.component'; import { FormsModule } from '@angular/forms'; import { MatCheckboxModule } from '@angular/material/checkbox'; -import { Angulartics2Module } from 'angulartics2'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { provideHttpClient } from '@angular/common/http'; +import { Angulartics2Module } from 'angulartics2'; +import { Db2CredentialsFormComponent } from './db2-credentials-form.component'; describe('Db2CredentialsFormComponent', () => { - let component: Db2CredentialsFormComponent; - let fixture: ComponentFixture; + let component: Db2CredentialsFormComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [ - FormsModule, - MatCheckboxModule, - Db2CredentialsFormComponent, - BrowserAnimationsModule, - Angulartics2Module.forRoot() - ], - providers: [provideHttpClient()] -}) - .compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + FormsModule, + MatCheckboxModule, + Db2CredentialsFormComponent, + BrowserAnimationsModule, + Angulartics2Module.forRoot(), + ], + providers: [provideHttpClient()], + }).compileComponents(); - fixture = TestBed.createComponent(Db2CredentialsFormComponent); - component = fixture.componentInstance; + fixture = TestBed.createComponent(Db2CredentialsFormComponent); + component = fixture.componentInstance; - component.connection = { - id: "12345678" - } as any; + component.connection = { + id: '12345678', + } as any; - fixture.detectChanges(); - }); + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/connect-db/db-credentials-forms/dynamodb-credentials-form/dynamodb-credentials-form.component.css b/frontend/src/app/components/connect-db/db-credentials-forms/dynamodb-credentials-form/dynamodb-credentials-form.component.css index ce760ecce..2d36b7d8d 100644 --- a/frontend/src/app/components/connect-db/db-credentials-forms/dynamodb-credentials-form/dynamodb-credentials-form.component.css +++ b/frontend/src/app/components/connect-db/db-credentials-forms/dynamodb-credentials-form/dynamodb-credentials-form.component.css @@ -1,9 +1,9 @@ .connectForm__hostname { - padding-bottom: 20px; + padding-bottom: 20px; } @media (width <= 600px) { - .connectForm__hostname { - padding-bottom: 56px; - } -} \ No newline at end of file + .connectForm__hostname { + padding-bottom: 56px; + } +} diff --git a/frontend/src/app/components/connect-db/db-credentials-forms/dynamodb-credentials-form/dynamodb-credentials-form.component.spec.ts b/frontend/src/app/components/connect-db/db-credentials-forms/dynamodb-credentials-form/dynamodb-credentials-form.component.spec.ts index 027aee638..339c6cb49 100644 --- a/frontend/src/app/components/connect-db/db-credentials-forms/dynamodb-credentials-form/dynamodb-credentials-form.component.spec.ts +++ b/frontend/src/app/components/connect-db/db-credentials-forms/dynamodb-credentials-form/dynamodb-credentials-form.component.spec.ts @@ -1,38 +1,31 @@ +import { provideHttpClient } from '@angular/common/http'; import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { DynamodbCredentialsFormComponent } from './dynamodb-credentials-form.component'; import { FormsModule } from '@angular/forms'; -import { Angulartics2Module } from 'angulartics2'; -import { provideHttpClient } from '@angular/common/http'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { Angulartics2Module } from 'angulartics2'; +import { DynamodbCredentialsFormComponent } from './dynamodb-credentials-form.component'; describe('DynamodbCredentialsFormComponent', () => { - let component: DynamodbCredentialsFormComponent; - let fixture: ComponentFixture; + let component: DynamodbCredentialsFormComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [ - FormsModule, - BrowserAnimationsModule, - Angulartics2Module.forRoot({}), - DynamodbCredentialsFormComponent - ], - providers: [provideHttpClient()] -}) - .compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [FormsModule, BrowserAnimationsModule, Angulartics2Module.forRoot({}), DynamodbCredentialsFormComponent], + providers: [provideHttpClient()], + }).compileComponents(); - fixture = TestBed.createComponent(DynamodbCredentialsFormComponent); - component = fixture.componentInstance; + fixture = TestBed.createComponent(DynamodbCredentialsFormComponent); + component = fixture.componentInstance; - component.connection = { - id: "12345678" - } as any; + component.connection = { + id: '12345678', + } as any; - fixture.detectChanges(); - }); + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/connect-db/db-credentials-forms/elastic-credentials-form/elastic-credentials-form.component.css b/frontend/src/app/components/connect-db/db-credentials-forms/elastic-credentials-form/elastic-credentials-form.component.css index 8b6ed6b0b..6f4923742 100644 --- a/frontend/src/app/components/connect-db/db-credentials-forms/elastic-credentials-form/elastic-credentials-form.component.css +++ b/frontend/src/app/components/connect-db/db-credentials-forms/elastic-credentials-form/elastic-credentials-form.component.css @@ -1,14 +1,14 @@ .connectForm__hostname, .connectForm__port { - padding-bottom: 20px; + padding-bottom: 20px; } @media (width <= 600px) { - .connectForm__hostname { - padding-bottom: 44px; - } + .connectForm__hostname { + padding-bottom: 44px; + } - .connectForm__port { - padding-bottom: 0; - } -} \ No newline at end of file + .connectForm__port { + padding-bottom: 0; + } +} diff --git a/frontend/src/app/components/connect-db/db-credentials-forms/elastic-credentials-form/elastic-credentials-form.component.spec.ts b/frontend/src/app/components/connect-db/db-credentials-forms/elastic-credentials-form/elastic-credentials-form.component.spec.ts index 4f24e4172..dbea5a62e 100644 --- a/frontend/src/app/components/connect-db/db-credentials-forms/elastic-credentials-form/elastic-credentials-form.component.spec.ts +++ b/frontend/src/app/components/connect-db/db-credentials-forms/elastic-credentials-form/elastic-credentials-form.component.spec.ts @@ -1,61 +1,59 @@ +import { provideHttpClient } from '@angular/common/http'; import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { Angulartics2Module } from 'angulartics2'; -import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { ElasticCredentialsFormComponent } from './elastic-credentials-form.component'; import { FormsModule } from '@angular/forms'; import { MatCheckboxModule } from '@angular/material/checkbox'; -import { provideHttpClient } from '@angular/common/http'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { Angulartics2Module } from 'angulartics2'; +import { ElasticCredentialsFormComponent } from './elastic-credentials-form.component'; describe('ElasticCredentialsFormComponent', () => { - let component: ElasticCredentialsFormComponent; - let fixture: ComponentFixture; + let component: ElasticCredentialsFormComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [ - FormsModule, - MatCheckboxModule, - BrowserAnimationsModule, - Angulartics2Module.forRoot({}), - ElasticCredentialsFormComponent - ], - providers: [provideHttpClient()] -}) - .compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + FormsModule, + MatCheckboxModule, + BrowserAnimationsModule, + Angulartics2Module.forRoot({}), + ElasticCredentialsFormComponent, + ], + providers: [provideHttpClient()], + }).compileComponents(); - fixture = TestBed.createComponent(ElasticCredentialsFormComponent); - component = fixture.componentInstance; + fixture = TestBed.createComponent(ElasticCredentialsFormComponent); + component = fixture.componentInstance; - component.connection = { - id: "12345678" - } as any; + component.connection = { + id: '12345678', + } as any; - // component.connection = { - // "title": "Test connection via SSH tunnel to mySQL", - // "masterEncryption": false, - // "type": DBtype.MySQL, - // "host": "database-2.cvfuxe8nltiq.us-east-2.rds.amazonaws.com", - // "port": "3306", - // "username": "admin", - // "database": "testDB", - // "schema": null, - // "sid": null, - // "id": "9d5f6d0f-9516-4598-91c4-e4fe6330b4d4", - // "ssh": true, - // "sshHost": "3.134.99.192", - // "sshPort": '22', - // "sshUsername": "ubuntu", - // "ssl": false, - // "cert": null, - // "connectionType": ConnectionType.Direct, - // "azure_encryption": false, - // "signing_key": '' - // }; - fixture.detectChanges(); - }); + // component.connection = { + // "title": "Test connection via SSH tunnel to mySQL", + // "masterEncryption": false, + // "type": DBtype.MySQL, + // "host": "database-2.cvfuxe8nltiq.us-east-2.rds.amazonaws.com", + // "port": "3306", + // "username": "admin", + // "database": "testDB", + // "schema": null, + // "sid": null, + // "id": "9d5f6d0f-9516-4598-91c4-e4fe6330b4d4", + // "ssh": true, + // "sshHost": "3.134.99.192", + // "sshPort": '22', + // "sshUsername": "ubuntu", + // "ssl": false, + // "cert": null, + // "connectionType": ConnectionType.Direct, + // "azure_encryption": false, + // "signing_key": '' + // }; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/connect-db/db-credentials-forms/mongodb-credentials-form/mongodb-credentials-form.component.css b/frontend/src/app/components/connect-db/db-credentials-forms/mongodb-credentials-form/mongodb-credentials-form.component.css index 8b6ed6b0b..6f4923742 100644 --- a/frontend/src/app/components/connect-db/db-credentials-forms/mongodb-credentials-form/mongodb-credentials-form.component.css +++ b/frontend/src/app/components/connect-db/db-credentials-forms/mongodb-credentials-form/mongodb-credentials-form.component.css @@ -1,14 +1,14 @@ .connectForm__hostname, .connectForm__port { - padding-bottom: 20px; + padding-bottom: 20px; } @media (width <= 600px) { - .connectForm__hostname { - padding-bottom: 44px; - } + .connectForm__hostname { + padding-bottom: 44px; + } - .connectForm__port { - padding-bottom: 0; - } -} \ No newline at end of file + .connectForm__port { + padding-bottom: 0; + } +} diff --git a/frontend/src/app/components/connect-db/db-credentials-forms/mongodb-credentials-form/mongodb-credentials-form.component.spec.ts b/frontend/src/app/components/connect-db/db-credentials-forms/mongodb-credentials-form/mongodb-credentials-form.component.spec.ts index f35f20076..81f0bc669 100644 --- a/frontend/src/app/components/connect-db/db-credentials-forms/mongodb-credentials-form/mongodb-credentials-form.component.spec.ts +++ b/frontend/src/app/components/connect-db/db-credentials-forms/mongodb-credentials-form/mongodb-credentials-form.component.spec.ts @@ -1,61 +1,59 @@ +import { provideHttpClient } from '@angular/common/http'; import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { MongodbCredentialsFormComponent } from './mongodb-credentials-form.component'; import { FormsModule } from '@angular/forms'; import { MatCheckboxModule } from '@angular/material/checkbox'; -import { Angulartics2Module } from 'angulartics2'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { provideHttpClient } from '@angular/common/http'; +import { Angulartics2Module } from 'angulartics2'; +import { MongodbCredentialsFormComponent } from './mongodb-credentials-form.component'; describe('MongodbCredentialsFormComponent', () => { - let component: MongodbCredentialsFormComponent; - let fixture: ComponentFixture; + let component: MongodbCredentialsFormComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [ - FormsModule, - MatCheckboxModule, - BrowserAnimationsModule, - Angulartics2Module.forRoot({}), - MongodbCredentialsFormComponent - ], - providers: [provideHttpClient()] -}) - .compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + FormsModule, + MatCheckboxModule, + BrowserAnimationsModule, + Angulartics2Module.forRoot({}), + MongodbCredentialsFormComponent, + ], + providers: [provideHttpClient()], + }).compileComponents(); - fixture = TestBed.createComponent(MongodbCredentialsFormComponent); - component = fixture.componentInstance; + fixture = TestBed.createComponent(MongodbCredentialsFormComponent); + component = fixture.componentInstance; - component.connection = { - id: "12345678" - } as any; + component.connection = { + id: '12345678', + } as any; - // component.connection = { - // "title": "Test connection via SSH tunnel to mySQL", - // "masterEncryption": false, - // "type": DBtype.MySQL, - // "host": "database-2.cvfuxe8nltiq.us-east-2.rds.amazonaws.com", - // "port": "3306", - // "username": "admin", - // "database": "testDB", - // "schema": null, - // "sid": null, - // "id": "9d5f6d0f-9516-4598-91c4-e4fe6330b4d4", - // "ssh": true, - // "sshHost": "3.134.99.192", - // "sshPort": '22', - // "sshUsername": "ubuntu", - // "ssl": false, - // "cert": null, - // "connectionType": ConnectionType.Direct, - // "azure_encryption": false, - // "signing_key": '' - // }; - fixture.detectChanges(); - }); + // component.connection = { + // "title": "Test connection via SSH tunnel to mySQL", + // "masterEncryption": false, + // "type": DBtype.MySQL, + // "host": "database-2.cvfuxe8nltiq.us-east-2.rds.amazonaws.com", + // "port": "3306", + // "username": "admin", + // "database": "testDB", + // "schema": null, + // "sid": null, + // "id": "9d5f6d0f-9516-4598-91c4-e4fe6330b4d4", + // "ssh": true, + // "sshHost": "3.134.99.192", + // "sshPort": '22', + // "sshUsername": "ubuntu", + // "ssl": false, + // "cert": null, + // "connectionType": ConnectionType.Direct, + // "azure_encryption": false, + // "signing_key": '' + // }; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/connect-db/db-credentials-forms/mssql-credentials-form/mssql-credentials-form.component.css b/frontend/src/app/components/connect-db/db-credentials-forms/mssql-credentials-form/mssql-credentials-form.component.css index 8b6ed6b0b..6f4923742 100644 --- a/frontend/src/app/components/connect-db/db-credentials-forms/mssql-credentials-form/mssql-credentials-form.component.css +++ b/frontend/src/app/components/connect-db/db-credentials-forms/mssql-credentials-form/mssql-credentials-form.component.css @@ -1,14 +1,14 @@ .connectForm__hostname, .connectForm__port { - padding-bottom: 20px; + padding-bottom: 20px; } @media (width <= 600px) { - .connectForm__hostname { - padding-bottom: 44px; - } + .connectForm__hostname { + padding-bottom: 44px; + } - .connectForm__port { - padding-bottom: 0; - } -} \ No newline at end of file + .connectForm__port { + padding-bottom: 0; + } +} diff --git a/frontend/src/app/components/connect-db/db-credentials-forms/mssql-credentials-form/mssql-credentials-form.component.spec.ts b/frontend/src/app/components/connect-db/db-credentials-forms/mssql-credentials-form/mssql-credentials-form.component.spec.ts index f39f2d6a3..cd7c35a08 100644 --- a/frontend/src/app/components/connect-db/db-credentials-forms/mssql-credentials-form/mssql-credentials-form.component.spec.ts +++ b/frontend/src/app/components/connect-db/db-credentials-forms/mssql-credentials-form/mssql-credentials-form.component.spec.ts @@ -1,40 +1,38 @@ +import { provideHttpClient } from '@angular/common/http'; import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { MssqlCredentialsFormComponent } from './mssql-credentials-form.component'; import { FormsModule } from '@angular/forms'; import { MatCheckboxModule } from '@angular/material/checkbox'; -import { Angulartics2Module } from 'angulartics2'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { provideHttpClient } from '@angular/common/http'; +import { Angulartics2Module } from 'angulartics2'; +import { MssqlCredentialsFormComponent } from './mssql-credentials-form.component'; describe('MssqlCredentialsFormComponent', () => { - let component: MssqlCredentialsFormComponent; - let fixture: ComponentFixture; + let component: MssqlCredentialsFormComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [ - FormsModule, - MatCheckboxModule, - BrowserAnimationsModule, - Angulartics2Module.forRoot({}), - MssqlCredentialsFormComponent - ], - providers: [provideHttpClient()] -}) - .compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + FormsModule, + MatCheckboxModule, + BrowserAnimationsModule, + Angulartics2Module.forRoot({}), + MssqlCredentialsFormComponent, + ], + providers: [provideHttpClient()], + }).compileComponents(); - fixture = TestBed.createComponent(MssqlCredentialsFormComponent); - component = fixture.componentInstance; + fixture = TestBed.createComponent(MssqlCredentialsFormComponent); + component = fixture.componentInstance; - component.connection = { - id: "12345678" - } as any; + component.connection = { + id: '12345678', + } as any; - fixture.detectChanges(); - }); + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/connect-db/db-credentials-forms/mysql-credentials-form/mysql-credentials-form.component.css b/frontend/src/app/components/connect-db/db-credentials-forms/mysql-credentials-form/mysql-credentials-form.component.css index 8b6ed6b0b..6f4923742 100644 --- a/frontend/src/app/components/connect-db/db-credentials-forms/mysql-credentials-form/mysql-credentials-form.component.css +++ b/frontend/src/app/components/connect-db/db-credentials-forms/mysql-credentials-form/mysql-credentials-form.component.css @@ -1,14 +1,14 @@ .connectForm__hostname, .connectForm__port { - padding-bottom: 20px; + padding-bottom: 20px; } @media (width <= 600px) { - .connectForm__hostname { - padding-bottom: 44px; - } + .connectForm__hostname { + padding-bottom: 44px; + } - .connectForm__port { - padding-bottom: 0; - } -} \ No newline at end of file + .connectForm__port { + padding-bottom: 0; + } +} diff --git a/frontend/src/app/components/connect-db/db-credentials-forms/mysql-credentials-form/mysql-credentials-form.component.spec.ts b/frontend/src/app/components/connect-db/db-credentials-forms/mysql-credentials-form/mysql-credentials-form.component.spec.ts index fb89f010a..8aa5877f4 100644 --- a/frontend/src/app/components/connect-db/db-credentials-forms/mysql-credentials-form/mysql-credentials-form.component.spec.ts +++ b/frontend/src/app/components/connect-db/db-credentials-forms/mysql-credentials-form/mysql-credentials-form.component.spec.ts @@ -1,40 +1,38 @@ +import { provideHttpClient } from '@angular/common/http'; import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { MysqlCredentialsFormComponent } from './mysql-credentials-form.component'; import { FormsModule } from '@angular/forms'; import { MatCheckboxModule } from '@angular/material/checkbox'; -import { Angulartics2Module } from 'angulartics2'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { provideHttpClient } from '@angular/common/http'; +import { Angulartics2Module } from 'angulartics2'; +import { MysqlCredentialsFormComponent } from './mysql-credentials-form.component'; describe('MysqlCredentialsFormComponent', () => { - let component: MysqlCredentialsFormComponent; - let fixture: ComponentFixture; + let component: MysqlCredentialsFormComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [ - FormsModule, - MatCheckboxModule, - BrowserAnimationsModule, - Angulartics2Module.forRoot({}), - MysqlCredentialsFormComponent - ], - providers: [provideHttpClient()] -}) - .compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + FormsModule, + MatCheckboxModule, + BrowserAnimationsModule, + Angulartics2Module.forRoot({}), + MysqlCredentialsFormComponent, + ], + providers: [provideHttpClient()], + }).compileComponents(); - fixture = TestBed.createComponent(MysqlCredentialsFormComponent); - component = fixture.componentInstance; + fixture = TestBed.createComponent(MysqlCredentialsFormComponent); + component = fixture.componentInstance; - component.connection = { - id: "12345678" - } as any; + component.connection = { + id: '12345678', + } as any; - fixture.detectChanges(); - }); + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/connect-db/db-credentials-forms/oracledb-credentials-form/oracledb-credentials-form.component.css b/frontend/src/app/components/connect-db/db-credentials-forms/oracledb-credentials-form/oracledb-credentials-form.component.css index 8b6ed6b0b..6f4923742 100644 --- a/frontend/src/app/components/connect-db/db-credentials-forms/oracledb-credentials-form/oracledb-credentials-form.component.css +++ b/frontend/src/app/components/connect-db/db-credentials-forms/oracledb-credentials-form/oracledb-credentials-form.component.css @@ -1,14 +1,14 @@ .connectForm__hostname, .connectForm__port { - padding-bottom: 20px; + padding-bottom: 20px; } @media (width <= 600px) { - .connectForm__hostname { - padding-bottom: 44px; - } + .connectForm__hostname { + padding-bottom: 44px; + } - .connectForm__port { - padding-bottom: 0; - } -} \ No newline at end of file + .connectForm__port { + padding-bottom: 0; + } +} diff --git a/frontend/src/app/components/connect-db/db-credentials-forms/oracledb-credentials-form/oracledb-credentials-form.component.spec.ts b/frontend/src/app/components/connect-db/db-credentials-forms/oracledb-credentials-form/oracledb-credentials-form.component.spec.ts index 3c6754b96..f5495a15d 100644 --- a/frontend/src/app/components/connect-db/db-credentials-forms/oracledb-credentials-form/oracledb-credentials-form.component.spec.ts +++ b/frontend/src/app/components/connect-db/db-credentials-forms/oracledb-credentials-form/oracledb-credentials-form.component.spec.ts @@ -1,40 +1,38 @@ +import { provideHttpClient } from '@angular/common/http'; import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { OracledbCredentialsFormComponent } from './oracledb-credentials-form.component'; import { FormsModule } from '@angular/forms'; import { MatCheckboxModule } from '@angular/material/checkbox'; -import { Angulartics2Module } from 'angulartics2'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { provideHttpClient } from '@angular/common/http'; +import { Angulartics2Module } from 'angulartics2'; +import { OracledbCredentialsFormComponent } from './oracledb-credentials-form.component'; describe('OracledbCredentialsFormComponent', () => { - let component: OracledbCredentialsFormComponent; - let fixture: ComponentFixture; + let component: OracledbCredentialsFormComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [ - FormsModule, - MatCheckboxModule, - BrowserAnimationsModule, - Angulartics2Module.forRoot({}), - OracledbCredentialsFormComponent - ], - providers: [provideHttpClient()] -}) - .compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + FormsModule, + MatCheckboxModule, + BrowserAnimationsModule, + Angulartics2Module.forRoot({}), + OracledbCredentialsFormComponent, + ], + providers: [provideHttpClient()], + }).compileComponents(); - fixture = TestBed.createComponent(OracledbCredentialsFormComponent); - component = fixture.componentInstance; + fixture = TestBed.createComponent(OracledbCredentialsFormComponent); + component = fixture.componentInstance; - component.connection = { - id: "12345678" - } as any; + component.connection = { + id: '12345678', + } as any; - fixture.detectChanges(); - }); + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/connect-db/db-credentials-forms/postgres-credentials-form/postgres-credentials-form.component.css b/frontend/src/app/components/connect-db/db-credentials-forms/postgres-credentials-form/postgres-credentials-form.component.css index 8b6ed6b0b..6f4923742 100644 --- a/frontend/src/app/components/connect-db/db-credentials-forms/postgres-credentials-form/postgres-credentials-form.component.css +++ b/frontend/src/app/components/connect-db/db-credentials-forms/postgres-credentials-form/postgres-credentials-form.component.css @@ -1,14 +1,14 @@ .connectForm__hostname, .connectForm__port { - padding-bottom: 20px; + padding-bottom: 20px; } @media (width <= 600px) { - .connectForm__hostname { - padding-bottom: 44px; - } + .connectForm__hostname { + padding-bottom: 44px; + } - .connectForm__port { - padding-bottom: 0; - } -} \ No newline at end of file + .connectForm__port { + padding-bottom: 0; + } +} diff --git a/frontend/src/app/components/connect-db/db-credentials-forms/postgres-credentials-form/postgres-credentials-form.component.spec.ts b/frontend/src/app/components/connect-db/db-credentials-forms/postgres-credentials-form/postgres-credentials-form.component.spec.ts index bdbb4baca..95b9f2e3d 100644 --- a/frontend/src/app/components/connect-db/db-credentials-forms/postgres-credentials-form/postgres-credentials-form.component.spec.ts +++ b/frontend/src/app/components/connect-db/db-credentials-forms/postgres-credentials-form/postgres-credentials-form.component.spec.ts @@ -1,40 +1,38 @@ +import { provideHttpClient } from '@angular/common/http'; import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { PostgresCredentialsFormComponent } from './postgres-credentials-form.component'; import { FormsModule } from '@angular/forms'; import { MatCheckboxModule } from '@angular/material/checkbox'; -import { Angulartics2Module } from 'angulartics2'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { provideHttpClient } from '@angular/common/http'; +import { Angulartics2Module } from 'angulartics2'; +import { PostgresCredentialsFormComponent } from './postgres-credentials-form.component'; describe('PostgresCredentialsFormComponent', () => { - let component: PostgresCredentialsFormComponent; - let fixture: ComponentFixture; + let component: PostgresCredentialsFormComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [ - FormsModule, - MatCheckboxModule, - BrowserAnimationsModule, - Angulartics2Module.forRoot({}), - PostgresCredentialsFormComponent - ], - providers: [provideHttpClient()] -}) - .compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + FormsModule, + MatCheckboxModule, + BrowserAnimationsModule, + Angulartics2Module.forRoot({}), + PostgresCredentialsFormComponent, + ], + providers: [provideHttpClient()], + }).compileComponents(); - fixture = TestBed.createComponent(PostgresCredentialsFormComponent); - component = fixture.componentInstance; + fixture = TestBed.createComponent(PostgresCredentialsFormComponent); + component = fixture.componentInstance; - component.connection = { - id: "12345678" - } as any; + component.connection = { + id: '12345678', + } as any; - fixture.detectChanges(); - }); + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/connect-db/db-credentials-forms/redis-credentials-form/redis-credentials-form.component.css b/frontend/src/app/components/connect-db/db-credentials-forms/redis-credentials-form/redis-credentials-form.component.css index 8b6ed6b0b..6f4923742 100644 --- a/frontend/src/app/components/connect-db/db-credentials-forms/redis-credentials-form/redis-credentials-form.component.css +++ b/frontend/src/app/components/connect-db/db-credentials-forms/redis-credentials-form/redis-credentials-form.component.css @@ -1,14 +1,14 @@ .connectForm__hostname, .connectForm__port { - padding-bottom: 20px; + padding-bottom: 20px; } @media (width <= 600px) { - .connectForm__hostname { - padding-bottom: 44px; - } + .connectForm__hostname { + padding-bottom: 44px; + } - .connectForm__port { - padding-bottom: 0; - } -} \ No newline at end of file + .connectForm__port { + padding-bottom: 0; + } +} diff --git a/frontend/src/app/components/connect-db/db-credentials-forms/redis-credentials-form/redis-credentials-form.component.spec.ts b/frontend/src/app/components/connect-db/db-credentials-forms/redis-credentials-form/redis-credentials-form.component.spec.ts index ae83b4871..9bc8dbba9 100644 --- a/frontend/src/app/components/connect-db/db-credentials-forms/redis-credentials-form/redis-credentials-form.component.spec.ts +++ b/frontend/src/app/components/connect-db/db-credentials-forms/redis-credentials-form/redis-credentials-form.component.spec.ts @@ -1,61 +1,59 @@ +import { provideHttpClient } from '@angular/common/http'; import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { Angulartics2Module } from 'angulartics2'; -import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { FormsModule } from '@angular/forms'; import { MatCheckboxModule } from '@angular/material/checkbox'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { Angulartics2Module } from 'angulartics2'; import { RedisCredentialsFormComponent } from './redis-credentials-form.component'; -import { provideHttpClient } from '@angular/common/http'; describe('RedisCredentialsFormComponent', () => { - let component: RedisCredentialsFormComponent; - let fixture: ComponentFixture; + let component: RedisCredentialsFormComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [ - FormsModule, - MatCheckboxModule, - BrowserAnimationsModule, - Angulartics2Module.forRoot({}), - RedisCredentialsFormComponent - ], - providers: [provideHttpClient()] -}) - .compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + FormsModule, + MatCheckboxModule, + BrowserAnimationsModule, + Angulartics2Module.forRoot({}), + RedisCredentialsFormComponent, + ], + providers: [provideHttpClient()], + }).compileComponents(); - fixture = TestBed.createComponent(RedisCredentialsFormComponent); - component = fixture.componentInstance; + fixture = TestBed.createComponent(RedisCredentialsFormComponent); + component = fixture.componentInstance; - component.connection = { - id: "12345678" - } as any; + component.connection = { + id: '12345678', + } as any; - // component.connection = { - // "title": "Test connection via SSH tunnel to mySQL", - // "masterEncryption": false, - // "type": DBtype.MySQL, - // "host": "database-2.cvfuxe8nltiq.us-east-2.rds.amazonaws.com", - // "port": "3306", - // "username": "admin", - // "database": "testDB", - // "schema": null, - // "sid": null, - // "id": "9d5f6d0f-9516-4598-91c4-e4fe6330b4d4", - // "ssh": true, - // "sshHost": "3.134.99.192", - // "sshPort": '22', - // "sshUsername": "ubuntu", - // "ssl": false, - // "cert": null, - // "connectionType": ConnectionType.Direct, - // "azure_encryption": false, - // "signing_key": '' - // }; - fixture.detectChanges(); - }); + // component.connection = { + // "title": "Test connection via SSH tunnel to mySQL", + // "masterEncryption": false, + // "type": DBtype.MySQL, + // "host": "database-2.cvfuxe8nltiq.us-east-2.rds.amazonaws.com", + // "port": "3306", + // "username": "admin", + // "database": "testDB", + // "schema": null, + // "sid": null, + // "id": "9d5f6d0f-9516-4598-91c4-e4fe6330b4d4", + // "ssh": true, + // "sshHost": "3.134.99.192", + // "sshPort": '22', + // "sshUsername": "ubuntu", + // "ssl": false, + // "cert": null, + // "connectionType": ConnectionType.Direct, + // "azure_encryption": false, + // "signing_key": '' + // }; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/connect-db/master-encryption-password/master-encryption-password.component.css b/frontend/src/app/components/connect-db/master-encryption-password/master-encryption-password.component.css index b17d37157..4ef99b6cb 100644 --- a/frontend/src/app/components/connect-db/master-encryption-password/master-encryption-password.component.css +++ b/frontend/src/app/components/connect-db/master-encryption-password/master-encryption-password.component.css @@ -1,58 +1,58 @@ .encription-setting { - display: flex; - justify-content: space-between; - align-items: center; - flex-wrap: wrap; - margin-bottom: 20px; + display: flex; + justify-content: space-between; + align-items: center; + flex-wrap: wrap; + margin-bottom: 20px; } .encription-password { - display: flex; - flex-direction: column; - margin-top: 32px; - width: 100%; + display: flex; + flex-direction: column; + margin-top: 32px; + width: 100%; } .encription-password__field { - display: flex; - align-items: center; - width: 100%; + display: flex; + align-items: center; + width: 100%; } .encription-password__copy-button { - margin-top: -20px; - margin-left: -40px; + margin-top: -20px; + margin-left: -40px; } .docs-notification { - display: flex; - gap: 8px; - background-color: var(--bg-color); - border-left: 12px solid #FF6F00; - color: var(--mat-sidenav-content-text-color); - margin-top: -8px; - padding: 16px 12px; + display: flex; + gap: 8px; + background-color: var(--bg-color); + border-left: 12px solid #ff6f00; + color: var(--mat-sidenav-content-text-color); + margin-top: -8px; + padding: 16px 12px; } @media (prefers-color-scheme: dark) { - .docs-notification { - --bg-color: #292929; - } + .docs-notification { + --bg-color: #292929; + } } @media (prefers-color-scheme: light) { - .docs-notification { - --bg-color: #F4F4F4; - } + .docs-notification { + --bg-color: #f4f4f4; + } } .docs-notification__warning { - line-height: 1.55em; - margin-bottom: 8px; + line-height: 1.55em; + margin-bottom: 8px; } .docs-link { - color: #EF6C00; - font-weight: 500; - text-decoration: none; -} \ No newline at end of file + color: #ef6c00; + font-weight: 500; + text-decoration: none; +} diff --git a/frontend/src/app/components/connect-db/master-encryption-password/master-encryption-password.component.spec.ts b/frontend/src/app/components/connect-db/master-encryption-password/master-encryption-password.component.spec.ts index 5167b6370..aced728d6 100644 --- a/frontend/src/app/components/connect-db/master-encryption-password/master-encryption-password.component.spec.ts +++ b/frontend/src/app/components/connect-db/master-encryption-password/master-encryption-password.component.spec.ts @@ -1,29 +1,24 @@ +import { provideHttpClient } from '@angular/common/http'; import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { MasterEncryptionPasswordComponent } from './master-encryption-password.component'; import { Angulartics2Module } from 'angulartics2'; -import { provideHttpClient } from '@angular/common/http'; +import { MasterEncryptionPasswordComponent } from './master-encryption-password.component'; describe('MasterEncryptionPasswordComponent', () => { - let component: MasterEncryptionPasswordComponent; - let fixture: ComponentFixture; + let component: MasterEncryptionPasswordComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [ - Angulartics2Module.forRoot({}), - MasterEncryptionPasswordComponent - ], - providers: [provideHttpClient()] -}) - .compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [Angulartics2Module.forRoot({}), MasterEncryptionPasswordComponent], + providers: [provideHttpClient()], + }).compileComponents(); - fixture = TestBed.createComponent(MasterEncryptionPasswordComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + fixture = TestBed.createComponent(MasterEncryptionPasswordComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/connection-settings/connection-settings.component.css b/frontend/src/app/components/connection-settings/connection-settings.component.css index 053204cb4..da3d1c426 100644 --- a/frontend/src/app/components/connection-settings/connection-settings.component.css +++ b/frontend/src/app/components/connection-settings/connection-settings.component.css @@ -1,180 +1,180 @@ :host app-alert:not(:empty) { - --alert-margin: 24px; + --alert-margin: 24px; } .settings-page { - height: 100%; - margin: var(--top-margin) 0; - padding: 0 max(calc(50vw - 325px), 10%); + height: 100%; + margin: var(--top-margin) 0; + padding: 0 max(calc(50vw - 325px), 10%); } .mat-h1 { - margin-bottom: 1.5em !important; + margin-bottom: 1.5em !important; } .settings-form { - display: flex; - flex-direction: column; - align-items: stretch; - padding-bottom: 72px; + display: flex; + flex-direction: column; + align-items: stretch; + padding-bottom: 72px; } .form-group-ordering { - display: flex; + display: flex; } .form-group-ordering__ordering-by { - width: 50% + width: 50%; } .form-group-ordering__order-options { - margin-top: 24px; - margin-left: 16px; + margin-top: 24px; + margin-left: 16px; } .form-group-ordering__order-options * { - margin-right: 16px; + margin-right: 16px; } .logo-uploader { - display: flex; - align-items: flex-start; - gap: 16px; - width: 100%; + display: flex; + align-items: flex-start; + gap: 16px; + width: 100%; } .logo-box { - display: flex; - align-items: center; - justify-content: center; - background: #171d39; - border-radius: 4px; - max-width: 160px; - height: 44px; - object-fit: contain; - padding: 4px 8px; + display: flex; + align-items: center; + justify-content: center; + background: #171d39; + border-radius: 4px; + max-width: 160px; + height: 44px; + object-fit: contain; + padding: 4px 8px; } .logo-img { - height: 40px; - max-width: 136px; - object-fit: contain; + height: 40px; + max-width: 136px; + object-fit: contain; } .logo-input { - flex-grow: 1; + flex-grow: 1; } .logo-stub { - margin-top: 12px; + margin-top: 12px; } .color-theme { - display: grid; - grid-template-columns: repeat(2, 1fr); - grid-gap: 24px; - align-items: center; + display: grid; + grid-template-columns: repeat(2, 1fr); + grid-gap: 24px; + align-items: center; } .color-item { - position: relative; + position: relative; } .color-input { - width: 100%; + width: 100%; } .color-input ::ng-deep input.mat-mdc-input-element { - box-sizing: border-box; - padding-top: 4px; - padding-left: 24px; + box-sizing: border-box; + padding-top: 4px; + padding-left: 24px; } .color-sample { - position: absolute; - top: 8px; - left: 8px; - background-color: var(--color); - border-radius: 4px; - height: 24px; - margin-top: 4px; - width: 24px; + position: absolute; + top: 8px; + left: 8px; + background-color: var(--color); + border-radius: 4px; + height: 24px; + margin-top: 4px; + width: 24px; } .table-name { - display: flex; - flex-direction: column; - gap: 4px; + display: flex; + flex-direction: column; + gap: 4px; } .table-name__title { - color: black; - font-size: 16px; - margin-bottom: -6px; + color: black; + font-size: 16px; + margin-bottom: -6px; } @media (prefers-color-scheme: dark) { - .table-name__title { - color: var(--color-whitePalette-500); - } + .table-name__title { + color: var(--color-whitePalette-500); + } } .table-name__line { - color: rgba(0, 0, 0, 0.6); - font-size: 12px; + color: rgba(0, 0, 0, 0.6); + font-size: 12px; } @media (prefers-color-scheme: dark) { - .table-name__line { - color: rgba(255, 255, 255, 0.6); - } + .table-name__line { + color: rgba(255, 255, 255, 0.6); + } } .actions { - position: fixed; - left: 0; - bottom: 0; - display: flex; - align-items: center; - justify-content: space-between; - background-color: var(--background-color); - box-shadow: var(--shadow); - height: 64px; - padding: 0 max(calc(50vw - 325px), 10%); - width: 100vw; + position: fixed; + left: 0; + bottom: 0; + display: flex; + align-items: center; + justify-content: space-between; + background-color: var(--background-color); + box-shadow: var(--shadow); + height: 64px; + padding: 0 max(calc(50vw - 325px), 10%); + width: 100vw; } @media (prefers-color-scheme: dark) { - .actions { - --shadow: 0 3px 1px -2px rgba(0,0,0,.5),0 2px 2px 0 rgba(0,0,0,.64),0 1px 5px 0 rgba(0,0,0,0.85); - --background-color: var(--surface-dark-color); - } + .actions { + --shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.5), 0 2px 2px 0 rgba(0, 0, 0, 0.64), 0 1px 5px 0 rgba(0, 0, 0, 0.85); + --background-color: var(--surface-dark-color); + } } @media (prefers-color-scheme: light) { - .actions { - --shadow: 0 3px 1px -2px rgba(0,0,0,.2),0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12); - --background-color: #fff; - } + .actions { + --shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12); + --background-color: #fff; + } } .settings-form__back-button { - margin-right: auto; + margin-right: auto; } .settings-form__save-button { - margin-left: 20px; + margin-left: 20px; } .settings-banner { - margin: -124px calc(max(calc(50vw - 325px), 10%) * -1) 0; - width: 100vw; + margin: -124px calc(max(calc(50vw - 325px), 10%) * -1) 0; + width: 100vw; } .error-details { - margin-top: 8px; + margin-top: 8px; } .audit-toggle { - margin-top: 4px; -} \ No newline at end of file + margin-top: 4px; +} diff --git a/frontend/src/app/components/connections-list/connections-list.component.css b/frontend/src/app/components/connections-list/connections-list.component.css index 08220e887..fc3527475 100644 --- a/frontend/src/app/components/connections-list/connections-list.component.css +++ b/frontend/src/app/components/connections-list/connections-list.component.css @@ -1,106 +1,107 @@ :host app-alert:not(:empty) { - --alert-margin: 24px; + --alert-margin: 24px; } .wrapper { - display: flex; - flex-direction: column; - align-items: center; - background-image: - url('../../../assets/bg/connections-list-left-decoration.svg'), - url('../../../assets/bg/connections-list-right-decoration.svg'); - background-repeat: no-repeat, no-repeat; - background-position: left 125%, right -26%; - background-size: 17%, 18%; - height: 100%; - padding: 72px max(calc(50vw - 535px), 10%) 72px; + display: flex; + flex-direction: column; + align-items: center; + background-image: + url("../../../assets/bg/connections-list-left-decoration.svg"), + url("../../../assets/bg/connections-list-right-decoration.svg"); + background-repeat: no-repeat, no-repeat; + background-position: + left 125%, + right -26%; + background-size: 17%, 18%; + height: 100%; + padding: 72px max(calc(50vw - 535px), 10%) 72px; } - @media (prefers-color-scheme: dark) { - .wrapper { - background-image: - url('../../../assets/bg/connections-list-left-decoration_dark.svg'), - url('../../../assets/bg/connections-list-right-decoration_dark.svg'); - } + .wrapper { + background-image: + url("../../../assets/bg/connections-list-left-decoration_dark.svg"), + url("../../../assets/bg/connections-list-right-decoration_dark.svg"); + } } @media (width <= 600px) { - .wrapper { - padding: 48px 9vw; - width: 100%; - } + .wrapper { + padding: 48px 9vw; + width: 100%; + } } .pageTitleHeader { - position: relative; - text-align: center; - margin-bottom: 54px; - width: 100%; + position: relative; + text-align: center; + margin-bottom: 54px; + width: 100%; } @media (width <= 600px) { - .pageTitleHeader { - margin-bottom: 32px; - } + .pageTitleHeader { + margin-bottom: 32px; + } } .pageTitleHeader .pageTitle { - position: relative; - display: inline-block; + position: relative; + display: inline-block; } .pageTitleHeader .pageTitle__name { - font-weight: 600; - margin-bottom: 0; + font-weight: 600; + margin-bottom: 0; } .companyEditLink { - position: absolute; - top: 50%; - right: -48px; - transform: translateY(-50%); - opacity: 0; - transition: opacity 200ms; + position: absolute; + top: 50%; + right: -48px; + transform: translateY(-50%); + opacity: 0; + transition: opacity 200ms; } @media (width <= 600px) { - .companyEditLink { - opacity: 1; - } + .companyEditLink { + opacity: 1; + } } .pageTitle:hover .companyEditLink { - opacity: 1; + opacity: 1; } .home { - display: contents; + display: contents; } .demoHome { - display: flex; - flex-direction: column-reverse; - align-items: center; - width: 100%; + display: flex; + flex-direction: column-reverse; + align-items: center; + width: 100%; } .connections-divider { - position: relative; - margin: 24px auto 32px; - width: calc(100% - 16px); + position: relative; + margin: 24px auto 32px; + width: calc(100% - 16px); } .connections-divider_or::after { - content: 'OR'; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - background-color: var(--mat-sidenav-content-background-color); - padding: 0 8px; + content: "OR"; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background-color: var(--mat-sidenav-content-background-color); + padding: 0 8px; } .placeholder { - width: 100%; -} \ No newline at end of file + width: 100%; +} diff --git a/frontend/src/app/components/connections-list/connections-list.component.spec.ts b/frontend/src/app/components/connections-list/connections-list.component.spec.ts index 6c3fbeb93..ec316e3bc 100644 --- a/frontend/src/app/components/connections-list/connections-list.component.spec.ts +++ b/frontend/src/app/components/connections-list/connections-list.component.spec.ts @@ -1,43 +1,37 @@ +import { provideHttpClient } from '@angular/common/http'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { MatDialogModule } from '@angular/material/dialog'; import { MatSnackBarModule } from '@angular/material/snack-bar'; - -import { ConnectionsListComponent } from './connections-list.component'; -import { ConnectionsService } from 'src/app/services/connections.service'; -import { Angulartics2Module } from 'angulartics2'; -import { provideHttpClient } from '@angular/common/http'; import { provideRouter } from '@angular/router'; +import { Angulartics2Module } from 'angulartics2'; +import { ConnectionsService } from 'src/app/services/connections.service'; +import { ConnectionsListComponent } from './connections-list.component'; describe('ConnectionsListComponent', () => { - let component: ConnectionsListComponent; - let fixture: ComponentFixture; + let component: ConnectionsListComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [ - MatSnackBarModule, - MatDialogModule, - Angulartics2Module.forRoot({}), - ConnectionsListComponent - ], - providers: [ - provideHttpClient(), - provideRouter([]), - { - provide: ConnectionsService, - // useClass: ConnectionsServiseStub - } - ], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [MatSnackBarModule, MatDialogModule, Angulartics2Module.forRoot({}), ConnectionsListComponent], + providers: [ + provideHttpClient(), + provideRouter([]), + { + provide: ConnectionsService, + // useClass: ConnectionsServiseStub + }, + ], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(ConnectionsListComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(ConnectionsListComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/connections-list/connections-list.component.ts b/frontend/src/app/components/connections-list/connections-list.component.ts index 552fc0ea9..0bd7df766 100644 --- a/frontend/src/app/components/connections-list/connections-list.component.ts +++ b/frontend/src/app/components/connections-list/connections-list.component.ts @@ -1,89 +1,89 @@ -import { Component, OnInit } from '@angular/core'; -import { Connection, } from 'src/app/models/connection'; - -import { AlertComponent } from '../ui-components/alert/alert.component'; -import { Angulartics2Module } from 'angulartics2'; import { CommonModule } from '@angular/common'; -import { CompanyService } from 'src/app/services/company.service'; -import { ConnectionsService } from 'src/app/services/connections.service'; -import { DemoConnectionsComponent } from './demo-connections/demo-connections.component'; +import { Component, OnInit } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatDialog } from '@angular/material/dialog'; import { MatDividerModule } from '@angular/material/divider'; import { MatIconModule } from '@angular/material/icon'; -import { OwnConnectionsComponent } from './own-connections/own-connections.component'; -import { PlaceholderConnectionsComponent } from '../skeletons/placeholder-connections/placeholder-connections.component'; -import { RouterModule } from '@angular/router'; import { Title } from '@angular/platform-browser'; +import { RouterModule } from '@angular/router'; +import { Angulartics2Module } from 'angulartics2'; +import { take } from 'rxjs/operators'; +import { Connection } from 'src/app/models/connection'; import { User } from 'src/app/models/user'; +import { CompanyService } from 'src/app/services/company.service'; +import { ConnectionsService } from 'src/app/services/connections.service'; import { UserService } from 'src/app/services/user.service'; -import { take } from 'rxjs/operators'; +import { PlaceholderConnectionsComponent } from '../skeletons/placeholder-connections/placeholder-connections.component'; +import { AlertComponent } from '../ui-components/alert/alert.component'; +import { DemoConnectionsComponent } from './demo-connections/demo-connections.component'; +import { OwnConnectionsComponent } from './own-connections/own-connections.component'; @Component({ - selector: 'app-connections-list', - templateUrl: './connections-list.component.html', - styleUrls: ['./connections-list.component.css'], - imports: [ - CommonModule, - RouterModule, - MatIconModule, - MatButtonModule, - MatDividerModule, - AlertComponent, - PlaceholderConnectionsComponent, - Angulartics2Module, - OwnConnectionsComponent, - DemoConnectionsComponent - ] + selector: 'app-connections-list', + templateUrl: './connections-list.component.html', + styleUrls: ['./connections-list.component.css'], + imports: [ + CommonModule, + RouterModule, + MatIconModule, + MatButtonModule, + MatDividerModule, + AlertComponent, + PlaceholderConnectionsComponent, + Angulartics2Module, + OwnConnectionsComponent, + DemoConnectionsComponent, + ], }) export class ConnectionsListComponent implements OnInit { + public testAlert = { + type: 'info' as const, + message: + 'These connections are for demonstration purposes only. To create your own connections, please use the "Add Connection" button above.', + }; - public testAlert = { - type: 'info' as const, - message: 'These connections are for demonstration purposes only. To create your own connections, please use the "Add Connection" button above.', - }; - - public connections: Connection[] = null; - // public testConnections: Connection[] = null; - public titles: Object; - public displayedCardCount: number = 3; - public connectionsListCollapsed: boolean = true; - public companyName: string; - public currentUser: User; + public connections: Connection[] = null; + // public testConnections: Connection[] = null; + public titles: Object; + public displayedCardCount: number = 3; + public connectionsListCollapsed: boolean = true; + public companyName: string; + public currentUser: User; - constructor( - private _connectionsServise: ConnectionsService, - public deleteDialog: MatDialog, - private _userService: UserService, - private _companyService: CompanyService, - private title: Title - ) { } + constructor( + private _connectionsServise: ConnectionsService, + public deleteDialog: MatDialog, + private _userService: UserService, + private _companyService: CompanyService, + private title: Title, + ) {} - get isDemo() { - return this._userService.isDemo; - } + get isDemo() { + return this._userService.isDemo; + } - get ownConnections() { - return this._connectionsServise.ownConnectionsList; - } + get ownConnections() { + return this._connectionsServise.ownConnectionsList; + } - get testConnections() { - return this._connectionsServise.testConnectionsList; - } + get testConnections() { + return this._connectionsServise.testConnectionsList; + } - ngOnInit(): void { - this._companyService.getCurrentTabTitle() - .pipe(take(1)) - .subscribe(tabTitle => { - this.title.setTitle(`Connections | ${tabTitle || 'Rocketadmin'}`); - }); + ngOnInit(): void { + this._companyService + .getCurrentTabTitle() + .pipe(take(1)) + .subscribe((tabTitle) => { + this.title.setTitle(`Connections | ${tabTitle || 'Rocketadmin'}`); + }); - this._userService.cast.subscribe(user => { - this.currentUser = user; - user.id && this._companyService.fetchCompanyName(user.company.id) - .subscribe((res: any) => { - this.companyName = res.name; - }) - }); - } + this._userService.cast.subscribe((user) => { + this.currentUser = user; + user.id && + this._companyService.fetchCompanyName(user.company.id).subscribe((res: any) => { + this.companyName = res.name; + }); + }); + } } diff --git a/frontend/src/app/components/connections-list/demo-connections/demo-connections.component.css b/frontend/src/app/components/connections-list/demo-connections/demo-connections.component.css index 018b1eeba..687354ed7 100644 --- a/frontend/src/app/components/connections-list/demo-connections/demo-connections.component.css +++ b/frontend/src/app/components/connections-list/demo-connections/demo-connections.component.css @@ -1,125 +1,128 @@ :host { - display: flex; - flex-direction: column; - align-items: center; - width: 100%; + display: flex; + flex-direction: column; + align-items: center; + width: 100%; } .testConnections { - list-style: none; - display: grid; - grid-template-columns: repeat(4, minmax(0, 1fr)); - grid-gap: 20px; - margin-top: 24px; - width: 100%; + list-style: none; + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + grid-gap: 20px; + margin-top: 24px; + width: 100%; } @media (width <= 600px) { - .testConnections { - grid-template-columns: repeat(2, minmax(0, 1fr)); - } + .testConnections { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } } .testConnectionLink { - display: flex; - align-items: center; - gap: 12px; - border-radius: 4px; - box-shadow: - 0px 1px 3px 0px rgba(0, 0, 0, 0.2), - 0px 2px 2px 0px rgba(0, 0, 0, 0.12), - 0px 0px 2px 0px rgba(0, 0, 0, 0.14); - color: inherit; - height: 48px; - padding: 0 12px; - text-decoration: none; - transition: box-shadow 200ms, background 200ms, border 200ms; + display: flex; + align-items: center; + gap: 12px; + border-radius: 4px; + box-shadow: + 0px 1px 3px 0px rgba(0, 0, 0, 0.2), + 0px 2px 2px 0px rgba(0, 0, 0, 0.12), + 0px 0px 2px 0px rgba(0, 0, 0, 0.14); + color: inherit; + height: 48px; + padding: 0 12px; + text-decoration: none; + transition: + box-shadow 200ms, + background 200ms, + border 200ms; } @media (width <= 600px) { - .testConnectionLink { - justify-content: flex-start; - } + .testConnectionLink { + justify-content: flex-start; + } } @media (prefers-color-scheme: light) { - .testConnectionLink { - background: var(--mat-sidenav-content-background-color); - border: 1px solid var(--mat-sidenav-content-background-color); - } - - .testConnectionLink:hover { - box-shadow: - 0px 1px 3px 0px rgba(0, 0, 0, 0.2), - 0px 2px 2px 0px rgba(0, 0, 0, 0.12), - 0px 0px 2px 0px rgba(0, 0, 0, 0.14); - } + .testConnectionLink { + background: var(--mat-sidenav-content-background-color); + border: 1px solid var(--mat-sidenav-content-background-color); + } + + .testConnectionLink:hover { + box-shadow: + 0px 1px 3px 0px rgba(0, 0, 0, 0.2), + 0px 2px 2px 0px rgba(0, 0, 0, 0.12), + 0px 0px 2px 0px rgba(0, 0, 0, 0.14); + } } @media (prefers-color-scheme: dark) { - .testConnectionLink { - background: #2a2a2a; - border: 1px solid #2a2a2a; - } - - .testConnectionLink:hover { - background: var(--surface-dark-color); - border: 1px solid rgba(255, 255, 255, 0.75); - box-shadow: - 0px 1px 3px 0px rgba(255, 255, 255, 0.1), - 0px 2px 2px 0px rgba(255, 255, 255, 0.08), - 0px 0px 2px 0px rgba(255, 255, 255, 0.12); - } - - .testConnectionLink:hover .connection__goIcon { - color: var(--color-accentedPalette-500); - } + .testConnectionLink { + background: #2a2a2a; + border: 1px solid #2a2a2a; + } + + .testConnectionLink:hover { + background: var(--surface-dark-color); + border: 1px solid rgba(255, 255, 255, 0.75); + box-shadow: + 0px 1px 3px 0px rgba(255, 255, 255, 0.1), + 0px 2px 2px 0px rgba(255, 255, 255, 0.08), + 0px 0px 2px 0px rgba(255, 255, 255, 0.12); + } + + .testConnectionLink:hover .connection__goIcon { + color: var(--color-accentedPalette-500); + } } .testConnectionLink:hover { - box-shadow: - 0px 1px 5px 0px rgba(0, 0, 0, 0.2), - 0px 3px 4px 0px rgba(0, 0, 0, 0.12), - 0px 2px 4px 0px rgba(0, 0, 0, 0.14); + box-shadow: + 0px 1px 5px 0px rgba(0, 0, 0, 0.2), + 0px 3px 4px 0px rgba(0, 0, 0, 0.12), + 0px 2px 4px 0px rgba(0, 0, 0, 0.14); } .testConnectionLink:active { - background: rgba(0, 0, 0, 0.16); + background: rgba(0, 0, 0, 0.16); } .testConnectionLink__icon { - flex-shrink: 0; - height: 28px; - width: 28px; + flex-shrink: 0; + height: 28px; + width: 28px; } -@media(prefers-color-scheme: dark) { - .testConnectionLink__icon { - filter: brightness(0) invert(1); - } +@media (prefers-color-scheme: dark) { + .testConnectionLink__icon { + filter: brightness(0) invert(1); + } } .connection__goIcon { - margin-left: auto; + margin-left: auto; } @media (width <= 600px) { - .testConnectionLink .connection__goIcon { - display: none; - } + .testConnectionLink .connection__goIcon { + display: none; + } } .testConnectionLink:hover .connection__goIcon, -.connection:hover .connection__goIcon{ - opacity: 1; +.connection:hover .connection__goIcon { + opacity: 1; } .testConnectionLink__info { - display: flex; - flex-direction: column; + display: flex; + flex-direction: column; } .testConnectionLink__type { - font-size: 12px; - margin-top: -2px; -} \ No newline at end of file + font-size: 12px; + margin-top: -2px; +} diff --git a/frontend/src/app/components/connections-list/demo-connections/demo-connections.component.spec.ts b/frontend/src/app/components/connections-list/demo-connections/demo-connections.component.spec.ts index 53d22a18f..c776011ce 100644 --- a/frontend/src/app/components/connections-list/demo-connections/demo-connections.component.spec.ts +++ b/frontend/src/app/components/connections-list/demo-connections/demo-connections.component.spec.ts @@ -1,114 +1,109 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { DemoConnectionsComponent } from './demo-connections.component'; -import { ConnectionItem, DBtype, ConnectionType } from 'src/app/models/connection'; -import { AccessLevel } from 'src/app/models/user'; import { provideRouter } from '@angular/router'; import { Angulartics2Module } from 'angulartics2'; +import { ConnectionItem, ConnectionType, DBtype } from 'src/app/models/connection'; +import { AccessLevel } from 'src/app/models/user'; +import { DemoConnectionsComponent } from './demo-connections.component'; describe('DemoConnectionsComponent', () => { - let component: DemoConnectionsComponent; - let fixture: ComponentFixture; + let component: DemoConnectionsComponent; + let fixture: ComponentFixture; - const mockTestConnections: ConnectionItem[] = [ - { - connection: { - id: '1', - title: 'MySQL Test DB', - type: DBtype.MySQL, - database: 'test_db', - host: 'localhost', - port: '3306', - username: 'root', - password: 'password', - schema: null, - sid: null, - ssl: false, - ssh: false, - connectionType: ConnectionType.Direct, - masterEncryption: false, - azure_encryption: false, - isTestConnection: true, - cert: '' - }, - accessLevel: AccessLevel.None - }, - { - connection: { - id: '2', - title: 'PostgreSQL Test DB', - type: DBtype.Postgres, - database: 'test_db', - host: 'localhost', - port: '5432', - username: 'postgres', - password: 'password', - schema: null, - sid: null, - ssl: false, - ssh: false, - connectionType: ConnectionType.Direct, - masterEncryption: false, - azure_encryption: false, - isTestConnection: true, - cert: '' - }, - accessLevel: AccessLevel.None - } - ]; + const mockTestConnections: ConnectionItem[] = [ + { + connection: { + id: '1', + title: 'MySQL Test DB', + type: DBtype.MySQL, + database: 'test_db', + host: 'localhost', + port: '3306', + username: 'root', + password: 'password', + schema: null, + sid: null, + ssl: false, + ssh: false, + connectionType: ConnectionType.Direct, + masterEncryption: false, + azure_encryption: false, + isTestConnection: true, + cert: '', + }, + accessLevel: AccessLevel.None, + }, + { + connection: { + id: '2', + title: 'PostgreSQL Test DB', + type: DBtype.Postgres, + database: 'test_db', + host: 'localhost', + port: '5432', + username: 'postgres', + password: 'password', + schema: null, + sid: null, + ssl: false, + ssh: false, + connectionType: ConnectionType.Direct, + masterEncryption: false, + azure_encryption: false, + isTestConnection: true, + cert: '', + }, + accessLevel: AccessLevel.None, + }, + ]; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [ - Angulartics2Module.forRoot({}), - DemoConnectionsComponent - ], - providers: [provideRouter([])] - }) - .compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [Angulartics2Module.forRoot({}), DemoConnectionsComponent], + providers: [provideRouter([])], + }).compileComponents(); - fixture = TestBed.createComponent(DemoConnectionsComponent); - component = fixture.componentInstance; - component.testConnections = mockTestConnections; - fixture.detectChanges(); - }); + fixture = TestBed.createComponent(DemoConnectionsComponent); + component = fixture.componentInstance; + component.testConnections = mockTestConnections; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); - it('should order test databases according to supportedOrderedDatabases', () => { - // Call the method directly - const orderedConnections = component.orderTestDatabases(); + it('should order test databases according to supportedOrderedDatabases', () => { + // Call the method directly + const orderedConnections = component.orderTestDatabases(); - // Verify the result is an array - expect(Array.isArray(orderedConnections)).toBe(true); + // Verify the result is an array + expect(Array.isArray(orderedConnections)).toBe(true); - // Verify the connections are in the right order - expect(orderedConnections.length).toBe(2); - expect(orderedConnections[0].connection.type).toBe(DBtype.MySQL); - expect(orderedConnections[1].connection.type).toBe(DBtype.Postgres); - }); + // Verify the connections are in the right order + expect(orderedConnections.length).toBe(2); + expect(orderedConnections[0].connection.type).toBe(DBtype.MySQL); + expect(orderedConnections[1].connection.type).toBe(DBtype.Postgres); + }); - it('should handle empty or null testConnections', () => { - // Set testConnections to null - component.testConnections = null; - fixture.detectChanges(); + it('should handle empty or null testConnections', () => { + // Set testConnections to null + component.testConnections = null; + fixture.detectChanges(); - // Call the method - const result = component.orderTestDatabases(); + // Call the method + const result = component.orderTestDatabases(); - // Verify it returns an empty array - expect(result).toEqual([]); + // Verify it returns an empty array + expect(result).toEqual([]); - // Set testConnections to empty array - component.testConnections = []; - fixture.detectChanges(); + // Set testConnections to empty array + component.testConnections = []; + fixture.detectChanges(); - // Call the method again - const resultForEmpty = component.orderTestDatabases(); + // Call the method again + const resultForEmpty = component.orderTestDatabases(); - // Verify it returns an empty array - expect(resultForEmpty).toEqual([]); - }); + // Verify it returns an empty array + expect(resultForEmpty).toEqual([]); + }); }); diff --git a/frontend/src/app/components/connections-list/own-connections/own-connections.component.css b/frontend/src/app/components/connections-list/own-connections/own-connections.component.css index e042cf447..0325461fc 100644 --- a/frontend/src/app/components/connections-list/own-connections/own-connections.component.css +++ b/frontend/src/app/components/connections-list/own-connections/own-connections.component.css @@ -1,9 +1,9 @@ :host { - flex-grow: 1; - display: flex; - flex-direction: column; - align-items: center; - width: 100%; + flex-grow: 1; + display: flex; + flex-direction: column; + align-items: center; + width: 100%; } .empty-state { @@ -54,23 +54,26 @@ } .addConnectionLink { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - gap: 4px; - border-radius: 4px; - box-shadow: - 0px 1px 3px 0px rgba(0, 0, 0, 0.2), - 0px 2px 2px 0px rgba(0, 0, 0, 0.12), - 0px 0px 2px 0px rgba(0, 0, 0, 0.14); - color: inherit; - font-weight: 600; - padding: 8px; - text-decoration: none; - height: 100%; - min-height: 90px; - transition: box-shadow 200ms, background 200ms, border 200ms; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 4px; + border-radius: 4px; + box-shadow: + 0px 1px 3px 0px rgba(0, 0, 0, 0.2), + 0px 2px 2px 0px rgba(0, 0, 0, 0.12), + 0px 0px 2px 0px rgba(0, 0, 0, 0.14); + color: inherit; + font-weight: 600; + padding: 8px; + text-decoration: none; + height: 100%; + min-height: 90px; + transition: + box-shadow 200ms, + background 200ms, + border 200ms; } .addConnectionLink:hover { @@ -81,10 +84,10 @@ } @media (prefers-color-scheme: dark) { - .addConnectionLink { - background: #2a2a2a; - border: 1px solid #2a2a2a; - } + .addConnectionLink { + background: #2a2a2a; + border: 1px solid #2a2a2a; + } .addConnectionLink:hover { background: var(--surface-dark-color); @@ -97,27 +100,27 @@ } .addConnectionLink__iconBox { - flex-shrink: 0; - display: flex; - align-items: center; - justify-content: center; - height: 32px; - width: 32px; + flex-shrink: 0; + display: flex; + align-items: center; + justify-content: center; + height: 32px; + width: 32px; } .addConnectionLink__icon { - flex-shrink: 0; - height: 28px; - width: 28px; + flex-shrink: 0; + height: 28px; + width: 28px; } @media (prefers-color-scheme: dark) { - .addConnectionLink__icon[data-mat-icon-name="mongodb"], - .addConnectionLink__icon[data-mat-icon-name="mysql"], - .addConnectionLink__icon[data-mat-icon-name="dynamodb"], - .addConnectionLink__icon[data-mat-icon-name="mssql"] { - filter: brightness(2); - } + .addConnectionLink__icon[data-mat-icon-name="mongodb"], + .addConnectionLink__icon[data-mat-icon-name="mysql"], + .addConnectionLink__icon[data-mat-icon-name="dynamodb"], + .addConnectionLink__icon[data-mat-icon-name="mssql"] { + filter: brightness(2); + } } .addConnectionLink__label { @@ -125,11 +128,11 @@ } .addConnectionLink__subLabel { - display: block; - font-weight: 400; - opacity: 0.6; - font-size: 12px; - margin-top: -2px; + display: block; + font-weight: 400; + opacity: 0.6; + font-size: 12px; + margin-top: -2px; } .showAllButton { diff --git a/frontend/src/app/components/connections-list/own-connections/own-connections.component.spec.ts b/frontend/src/app/components/connections-list/own-connections/own-connections.component.spec.ts index 3307a1fa4..a2420789f 100644 --- a/frontend/src/app/components/connections-list/own-connections/own-connections.component.spec.ts +++ b/frontend/src/app/components/connections-list/own-connections/own-connections.component.spec.ts @@ -1,29 +1,24 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { OwnConnectionsComponent } from './own-connections.component'; import { provideHttpClient } from '@angular/common/http'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; import { provideRouter } from '@angular/router'; +import { OwnConnectionsComponent } from './own-connections.component'; describe('OwnConnectionsComponent', () => { - let component: OwnConnectionsComponent; - let fixture: ComponentFixture; + let component: OwnConnectionsComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [OwnConnectionsComponent], - providers: [ - provideHttpClient(), - provideRouter([]) - ] - }) - .compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [OwnConnectionsComponent], + providers: [provideHttpClient(), provideRouter([])], + }).compileComponents(); - fixture = TestBed.createComponent(OwnConnectionsComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + fixture = TestBed.createComponent(OwnConnectionsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/connections-list/own-connections/own-connections.component.ts b/frontend/src/app/components/connections-list/own-connections/own-connections.component.ts index b109521ff..7145ea40d 100644 --- a/frontend/src/app/components/connections-list/own-connections/own-connections.component.ts +++ b/frontend/src/app/components/connections-list/own-connections/own-connections.component.ts @@ -17,7 +17,6 @@ import { UiSettingsService } from 'src/app/services/ui-settings.service'; templateUrl: './own-connections.component.html', styleUrl: './own-connections.component.css', }) - export class OwnConnectionsComponent implements OnInit, OnChanges { protected posthog = posthog; @Input() currentUser: User; @@ -34,7 +33,7 @@ export class OwnConnectionsComponent implements OnInit, OnChanges { constructor( private _uiSettings: UiSettingsService, - private _companyService: CompanyService + private _companyService: CompanyService, ) {} ngOnInit() { diff --git a/frontend/src/app/components/dashboard/dashboard.component.css b/frontend/src/app/components/dashboard/dashboard.component.css index eddfeda94..ae1ccbe98 100644 --- a/frontend/src/app/components/dashboard/dashboard.component.css +++ b/frontend/src/app/components/dashboard/dashboard.component.css @@ -1,202 +1,204 @@ .mat-sidenav-container { - height: 100%; - /* height: calc(100vh - 56px); */ - overflow-x: hidden !important; + height: 100%; + /* height: calc(100vh - 56px); */ + overflow-x: hidden !important; } .table-list-sidenav { - max-width: 240px; + max-width: 240px; } @media (prefers-color-scheme: dark) { - .table-list-sidenav { - background-color: var(--surface-dark-color); - } + .table-list-sidenav { + background-color: var(--surface-dark-color); + } } @media (width <= 600px) { - .table-list-sidenav { - display: none; - } + .table-list-sidenav { + display: none; + } } .side-bar_collapsed { - position: static; - float: left; - display: block; - height: 100%; - width: 65px; - visibility: visible !important; - transform: translate3d(0, 0, 0); + position: static; + float: left; + display: block; + height: 100%; + width: 65px; + visibility: visible !important; + transform: translate3d(0, 0, 0); } @media (width <= 600px) { - .side-bar_collapsed { - display: none; - } + .side-bar_collapsed { + display: none; + } } .table-list-sidenav-container ::ng-deep .mat-drawer-inner-container, .side-bar_collapsed ::ng-deep .mat-drawer-inner-container { - overflow-y: auto !important; - overflow-x: hidden !important; + overflow-y: auto !important; + overflow-x: hidden !important; } -.table-list-sidenav-container ::ng-deep .mat-drawer:not(.mat-drawer-opened):not(.mat-drawer-animating) .mat-drawer-inner-container { - display: block; +.table-list-sidenav-container + ::ng-deep + .mat-drawer:not(.mat-drawer-opened):not(.mat-drawer-animating) + .mat-drawer-inner-container { + display: block; } .mat-list-item { - text-decoration: none; + text-decoration: none; } .toggle-button { - margin-top: 8px; - margin-left: 12px; + margin-top: 8px; + margin-left: 12px; } .toggle-button-position button { - border-radius: 4px; + border-radius: 4px; } .toggle-button-position button mat-icon { - border-radius: 4px; + border-radius: 4px; } .toggle-button-position button:hover { - border-radius: 4px; + border-radius: 4px; } .toggle-button-position button:hover mat-icon { - border-radius: 4px; + border-radius: 4px; } .dashboard { - display: flex; - flex-direction: column; - height: calc(100vh - 60px - 56px); - max-width: 1000px; - margin: 20px auto; - widows: 96%; + display: flex; + flex-direction: column; + height: calc(100vh - 60px - 56px); + max-width: 1000px; + margin: 20px auto; + widows: 96%; } .title { - align-self: center; + align-self: center; } .alerts { - display: flex; - flex-direction: column; - gap: 8px; - margin-top: 24px; - margin-bottom: 16px; + display: flex; + flex-direction: column; + gap: 8px; + margin-top: 24px; + margin-bottom: 16px; } .alerts app-alert { - position: initial; + position: initial; } .alerts app-alert:empty { - display: none; + display: none; } .alerts:has(app-alert:only-child:empty) { - margin: 0; + margin: 0; } .ai-config-alert { - display: flex; - gap: 8px; - background-color: color-mix(in hsl, var(--color-accentedPalette-500), transparent 95%); - backdrop-filter: blur(4px); - border-top: 1px solid var(--color-accentedPalette-500); - border-right: 1px solid var(--color-accentedPalette-500); - border-bottom: 1px solid var(--color-accentedPalette-500); - border-left: 12px solid var(--color-accentedPalette-500); - border-radius: 4px; - padding: 12px 16px 12px 8px; + display: flex; + gap: 8px; + background-color: color-mix(in hsl, var(--color-accentedPalette-500), transparent 95%); + backdrop-filter: blur(4px); + border-top: 1px solid var(--color-accentedPalette-500); + border-right: 1px solid var(--color-accentedPalette-500); + border-bottom: 1px solid var(--color-accentedPalette-500); + border-left: 12px solid var(--color-accentedPalette-500); + border-radius: 4px; + padding: 12px 16px 12px 8px; } @media (prefers-color-scheme: dark) { - .ai-config-alert { - backdrop-filter: blur(2px); - } + .ai-config-alert { + backdrop-filter: blur(2px); + } } @media (width <= 600px) { - .ai-config-alert { - display: grid; - grid-template-columns: auto 1fr; - } + .ai-config-alert { + display: grid; + grid-template-columns: auto 1fr; + } } .ai-config-alert__icon { - flex-shrink: 0; - display: flex; - align-items: center; - justify-content: center; - border-radius: 50%; - color: var(--color-accentedPalette-500); - font-size: 24px; - height: 36px; - opacity: 0.9; - width: 36px; + flex-shrink: 0; + display: flex; + align-items: center; + justify-content: center; + border-radius: 50%; + color: var(--color-accentedPalette-500); + font-size: 24px; + height: 36px; + opacity: 0.9; + width: 36px; } .ai-config-alert__message { - flex: 1; - display: flex; - justify-content: center; - flex-direction: column; - min-height: 36px; - margin: 0 !important; + flex: 1; + display: flex; + justify-content: center; + flex-direction: column; + min-height: 36px; + margin: 0 !important; } .ai-config-alert__title { - color: var(--color-accentedPalette-500); - margin-bottom: 4px; + color: var(--color-accentedPalette-500); + margin-bottom: 4px; } @media (prefers-color-scheme: dark) { - .ai-config-alert__title { - color: inherit; - } + .ai-config-alert__title { + color: inherit; + } } .ai-config-alert__text { - margin: 0 !important; + margin: 0 !important; } .ai-config-alert__actions { - margin-left: auto; + margin-left: auto; } @media (width <= 600px) { - .ai-config-alert__actions { - grid-column: 1 / -1; - } + .ai-config-alert__actions { + grid-column: 1 / -1; + } } .error-details { - margin-top: 8px; + margin-top: 8px; } .table-preview { - display: flex; - overflow-x: hidden; - height: calc(100vh - 44px); + display: flex; + overflow-x: hidden; + height: calc(100vh - 44px); } .table-preview-content { - flex-grow: 1; - padding-bottom: 24px; - height: calc(100vh - 44px); - padding: 0 24px; - overflow-y: scroll; - width: 100%; + flex-grow: 1; + padding-bottom: 24px; + height: calc(100vh - 44px); + padding: 0 24px; + overflow-y: scroll; + width: 100%; } .sidenav-content__toggle-button { - display: none !important; + display: none !important; } - diff --git a/frontend/src/app/components/dashboard/dashboard.component.spec.ts b/frontend/src/app/components/dashboard/dashboard.component.spec.ts index 2200dfb69..a9c869990 100644 --- a/frontend/src/app/components/dashboard/dashboard.component.spec.ts +++ b/frontend/src/app/components/dashboard/dashboard.component.spec.ts @@ -76,7 +76,9 @@ describe('DashboardComponent', () => { fakeTablesService = { fetchTables: vi.fn().mockReturnValue(of(fakeTables)), - fetchTablesFolders: vi.fn().mockReturnValue(of([{ category_id: 'all-tables-kitten', category_name: 'All Tables', tables: fakeTables }])), + fetchTablesFolders: vi + .fn() + .mockReturnValue(of([{ category_id: 'all-tables-kitten', category_name: 'All Tables', tables: fakeTables }])), cast: new BehaviorSubject(''), }; @@ -129,9 +131,11 @@ describe('DashboardComponent', () => { }); it('should call getData and populate tables', () => { - fakeTablesService.fetchTablesFolders.mockReturnValue(of([{ category_id: 'all-tables-kitten', category_name: 'All Tables', tables: fakeTables }])); + fakeTablesService.fetchTablesFolders.mockReturnValue( + of([{ category_id: 'all-tables-kitten', category_name: 'All Tables', tables: fakeTables }]), + ); component.getData(); expect(component.allTables.length).toEqual(fakeTables.length); - expect(component.allTables.map(t => t.table)).toEqual(fakeTables.map(t => t.table)); + expect(component.allTables.map((t) => t.table)).toEqual(fakeTables.map((t) => t.table)); }); }); diff --git a/frontend/src/app/components/dashboard/dashboard.component.ts b/frontend/src/app/components/dashboard/dashboard.component.ts index 9194f9868..492524a44 100644 --- a/frontend/src/app/components/dashboard/dashboard.component.ts +++ b/frontend/src/app/components/dashboard/dashboard.component.ts @@ -160,7 +160,7 @@ export class DashboardComponent implements OnInit, OnDestroy { console.log('getData'); this._tables.fetchTablesFolders(this.connectionID).subscribe((res) => { - console.log('getTables folders') + console.log('getTables folders'); console.log(res); const tables = res.find((item) => item.category_id === 'all-tables-kitten')?.tables || []; @@ -220,7 +220,7 @@ export class DashboardComponent implements OnInit, OnDestroy { formatTableNames() { // Format table names inside tableFolders so all components receive formatted tables - this.tableFolders = this.tableFolders.map(category => ({ + this.tableFolders = this.tableFolders.map((category) => ({ ...category, tables: category.tables.map((tableItem: TableProperties) => this.formatTable(tableItem)), })); diff --git a/frontend/src/app/components/dashboard/db-table-view/db-action-link-dialog/db-action-link-dialog.component.css b/frontend/src/app/components/dashboard/db-table-view/db-action-link-dialog/db-action-link-dialog.component.css index e72d9e047..a89be1948 100644 --- a/frontend/src/app/components/dashboard/db-table-view/db-action-link-dialog/db-action-link-dialog.component.css +++ b/frontend/src/app/components/dashboard/db-table-view/db-action-link-dialog/db-action-link-dialog.component.css @@ -1,3 +1,3 @@ .location-link { - color: var(--color-accentedPalette-500); + color: var(--color-accentedPalette-500); } diff --git a/frontend/src/app/components/dashboard/db-table-view/db-action-link-dialog/db-action-link-dialog.component.spec.ts b/frontend/src/app/components/dashboard/db-table-view/db-action-link-dialog/db-action-link-dialog.component.spec.ts index 4d35f088b..475a3674e 100644 --- a/frontend/src/app/components/dashboard/db-table-view/db-action-link-dialog/db-action-link-dialog.component.spec.ts +++ b/frontend/src/app/components/dashboard/db-table-view/db-action-link-dialog/db-action-link-dialog.component.spec.ts @@ -4,32 +4,28 @@ import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/materia import { DbActionLinkDialogComponent } from './db-action-link-dialog.component'; describe('DbActionLinkDialogComponent', () => { - let component: DbActionLinkDialogComponent; - let fixture: ComponentFixture; + let component: DbActionLinkDialogComponent; + let fixture: ComponentFixture; - const mockDialogRef = { - close: () => { } - }; + const mockDialogRef = { + close: () => {}, + }; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [ - MatDialogModule, - DbActionLinkDialogComponent - ], - providers: [ - { provide: MAT_DIALOG_DATA, useValue: {} }, - { provide: MatDialogRef, useValue: mockDialogRef } - ] -}) - .compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [MatDialogModule, DbActionLinkDialogComponent], + providers: [ + { provide: MAT_DIALOG_DATA, useValue: {} }, + { provide: MatDialogRef, useValue: mockDialogRef }, + ], + }).compileComponents(); - fixture = TestBed.createComponent(DbActionLinkDialogComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + fixture = TestBed.createComponent(DbActionLinkDialogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/dashboard/db-table-view/db-action-link-dialog/db-action-link-dialog.component.ts b/frontend/src/app/components/dashboard/db-table-view/db-action-link-dialog/db-action-link-dialog.component.ts index 0b5ced706..0706ac416 100644 --- a/frontend/src/app/components/dashboard/db-table-view/db-action-link-dialog/db-action-link-dialog.component.ts +++ b/frontend/src/app/components/dashboard/db-table-view/db-action-link-dialog/db-action-link-dialog.component.ts @@ -1,26 +1,23 @@ -import { Component, Inject, OnInit } from '@angular/core'; -import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; import { CommonModule } from '@angular/common'; +import { Component, Inject, OnInit } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; -import { MatDialogModule } from '@angular/material/dialog'; +import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog'; @Component({ - selector: 'app-db-action-link-dialog', - templateUrl: './db-action-link-dialog.component.html', - styleUrls: ['./db-action-link-dialog.component.css'], - imports: [CommonModule, MatButtonModule, MatDialogModule] + selector: 'app-db-action-link-dialog', + templateUrl: './db-action-link-dialog.component.html', + styleUrls: ['./db-action-link-dialog.component.css'], + imports: [CommonModule, MatButtonModule, MatDialogModule], }) export class DbActionLinkDialogComponent implements OnInit { + constructor( + @Inject(MAT_DIALOG_DATA) public data: any, + public dialogRef: MatDialogRef, + ) {} - constructor( - @Inject(MAT_DIALOG_DATA) public data: any, - public dialogRef: MatDialogRef - ) { } - - ngOnInit(): void { - } + ngOnInit(): void {} - closeDialog() { - this.dialogRef.close(); - } + closeDialog() { + this.dialogRef.close(); + } } diff --git a/frontend/src/app/components/dashboard/db-table-view/db-bulk-action-confirmation-dialog/db-bulk-action-confirmation-dialog.component.css b/frontend/src/app/components/dashboard/db-table-view/db-bulk-action-confirmation-dialog/db-bulk-action-confirmation-dialog.component.css index 7cbb175ee..698e8d19b 100644 --- a/frontend/src/app/components/dashboard/db-table-view/db-bulk-action-confirmation-dialog/db-bulk-action-confirmation-dialog.component.css +++ b/frontend/src/app/components/dashboard/db-table-view/db-bulk-action-confirmation-dialog/db-bulk-action-confirmation-dialog.component.css @@ -1,7 +1,7 @@ .listOfPrimaryKeys { - list-style: none; + list-style: none; } .actions-title { - text-transform: lowercase; -} \ No newline at end of file + text-transform: lowercase; +} diff --git a/frontend/src/app/components/dashboard/db-table-view/db-bulk-action-confirmation-dialog/db-bulk-action-confirmation-dialog.component.ts b/frontend/src/app/components/dashboard/db-table-view/db-bulk-action-confirmation-dialog/db-bulk-action-confirmation-dialog.component.ts index f765d052d..8b40049d4 100644 --- a/frontend/src/app/components/dashboard/db-table-view/db-bulk-action-confirmation-dialog/db-bulk-action-confirmation-dialog.component.ts +++ b/frontend/src/app/components/dashboard/db-table-view/db-bulk-action-confirmation-dialog/db-bulk-action-confirmation-dialog.component.ts @@ -1,83 +1,100 @@ -import { Component, OnInit, Inject } from '@angular/core'; -import { MAT_DIALOG_DATA, MatDialog, MatDialogModule, MatDialogRef } from '@angular/material/dialog'; import { CommonModule } from '@angular/common'; +import { Component, Inject, OnInit } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { MatButtonModule } from '@angular/material/button'; -import { TablesService } from 'src/app/services/tables.service'; -import { TableRowService } from 'src/app/services/table-row.service'; +import { MAT_DIALOG_DATA, MatDialog, MatDialogModule, MatDialogRef } from '@angular/material/dialog'; import { ConnectionsService } from 'src/app/services/connections.service'; +import { TableRowService } from 'src/app/services/table-row.service'; +import { TablesService } from 'src/app/services/tables.service'; import { DbActionLinkDialogComponent } from '../db-action-link-dialog/db-action-link-dialog.component'; @Component({ - selector: 'app-db-rows-delete-dialog', - templateUrl: './db-bulk-action-confirmation-dialog.component.html', - styleUrls: ['./db-bulk-action-confirmation-dialog.component.css'], - imports: [ - CommonModule, - FormsModule, - MatButtonModule, - MatDialogModule - ] + selector: 'app-db-rows-delete-dialog', + templateUrl: './db-bulk-action-confirmation-dialog.component.html', + styleUrls: ['./db-bulk-action-confirmation-dialog.component.css'], + imports: [CommonModule, FormsModule, MatButtonModule, MatDialogModule], }) export class BbBulkActionConfirmationDialogComponent implements OnInit { + public selectedTableName; + public submitting: boolean = false; + public connectionID: string; - public selectedTableName; - public submitting: boolean = false; - public connectionID: string; - - constructor( - @Inject(MAT_DIALOG_DATA) public data: any, - private _connections: ConnectionsService, - private _tables: TablesService, - private _tableRow: TableRowService, - public dialog: MatDialog, - public dialogRef: MatDialogRef, - ) { } + constructor( + @Inject(MAT_DIALOG_DATA) public data: any, + private _connections: ConnectionsService, + private _tables: TablesService, + private _tableRow: TableRowService, + public dialog: MatDialog, + public dialogRef: MatDialogRef, + ) {} - ngOnInit(): void { - this.connectionID = this._connections.currentConnectionID; - this.selectedTableName = this._tables.currentTableName; - console.log(this.data); - this._tables.cast.subscribe(); - } + ngOnInit(): void { + this.connectionID = this._connections.currentConnectionID; + this.selectedTableName = this._tables.currentTableName; + console.log(this.data); + this._tables.cast.subscribe(); + } - onActionsComplete() { - this.dialogRef.close(); - this.submitting = false; - } + onActionsComplete() { + this.dialogRef.close(); + this.submitting = false; + } - handleConfirmedActions() { - this.submitting = true; + handleConfirmedActions() { + this.submitting = true; - if (this.data.id) { - this._tables.activateActions(this.connectionID, this.selectedTableName, this.data.id, this.data.title, this.data.primaryKeys, true) - .subscribe( - (res) => { - this.onActionsComplete(); - if (res?.location) this.dialog.open(DbActionLinkDialogComponent, { - width: '25em', - data: {href: res.location, actionName: this.data.title, primaryKeys: this.data.primaryKeys[0]} - }) - }, - () => { this.onActionsComplete() }, - () => { this.onActionsComplete() } - ) - } else { - if (this.data.primaryKeys.length === 1) { - this._tableRow.deleteTableRow(this.connectionID, this.selectedTableName, this.data.primaryKeys[0]) - .subscribe( - () => { this.onActionsComplete() }, - () => { this.onActionsComplete() }, - () => { this.onActionsComplete() } - ) - } else if (this.data.primaryKeys.length > 1) { - this._tables.bulkDelete(this.connectionID, this.selectedTableName, this.data.primaryKeys) - .subscribe( - () => { this.onActionsComplete() }, - () => { this.onActionsComplete() }, - () => { this.onActionsComplete() } - ) - } - } - } + if (this.data.id) { + this._tables + .activateActions( + this.connectionID, + this.selectedTableName, + this.data.id, + this.data.title, + this.data.primaryKeys, + true, + ) + .subscribe( + (res) => { + this.onActionsComplete(); + if (res?.location) + this.dialog.open(DbActionLinkDialogComponent, { + width: '25em', + data: { href: res.location, actionName: this.data.title, primaryKeys: this.data.primaryKeys[0] }, + }); + }, + () => { + this.onActionsComplete(); + }, + () => { + this.onActionsComplete(); + }, + ); + } else { + if (this.data.primaryKeys.length === 1) { + this._tableRow.deleteTableRow(this.connectionID, this.selectedTableName, this.data.primaryKeys[0]).subscribe( + () => { + this.onActionsComplete(); + }, + () => { + this.onActionsComplete(); + }, + () => { + this.onActionsComplete(); + }, + ); + } else if (this.data.primaryKeys.length > 1) { + this._tables.bulkDelete(this.connectionID, this.selectedTableName, this.data.primaryKeys).subscribe( + () => { + this.onActionsComplete(); + }, + () => { + this.onActionsComplete(); + }, + () => { + this.onActionsComplete(); + }, + ); + } + } + } } diff --git a/frontend/src/app/components/dashboard/db-table-view/db-table-actions/action-delete-dialog/action-delete-dialog.component.spec.ts b/frontend/src/app/components/dashboard/db-table-view/db-table-actions/action-delete-dialog/action-delete-dialog.component.spec.ts index 71e8b3930..237a6c6b8 100644 --- a/frontend/src/app/components/dashboard/db-table-view/db-table-actions/action-delete-dialog/action-delete-dialog.component.spec.ts +++ b/frontend/src/app/components/dashboard/db-table-view/db-table-actions/action-delete-dialog/action-delete-dialog.component.spec.ts @@ -1,38 +1,37 @@ +import { provideHttpClient } from '@angular/common/http'; import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { MatDialogModule, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog'; import { MatSnackBarModule } from '@angular/material/snack-bar'; import { RouterTestingModule } from '@angular/router/testing'; - -import { ActionDeleteDialogComponent } from './action-delete-dialog.component'; import { Angulartics2Module } from 'angulartics2'; -import { provideHttpClient } from '@angular/common/http'; +import { ActionDeleteDialogComponent } from './action-delete-dialog.component'; describe('ActionDeleteDialogComponent', () => { - let component: ActionDeleteDialogComponent; - let fixture: ComponentFixture; + let component: ActionDeleteDialogComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [ - RouterTestingModule, - MatDialogModule, - MatSnackBarModule, - Angulartics2Module.forRoot(), - ActionDeleteDialogComponent - ], - providers: [ - provideHttpClient(), - { provide: MAT_DIALOG_DATA, useValue: {} }, - { provide: MatDialogRef, useValue: {} }, - ], - }).compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + RouterTestingModule, + MatDialogModule, + MatSnackBarModule, + Angulartics2Module.forRoot(), + ActionDeleteDialogComponent, + ], + providers: [ + provideHttpClient(), + { provide: MAT_DIALOG_DATA, useValue: {} }, + { provide: MatDialogRef, useValue: {} }, + ], + }).compileComponents(); - fixture = TestBed.createComponent(ActionDeleteDialogComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + fixture = TestBed.createComponent(ActionDeleteDialogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/dashboard/db-table-view/db-table-actions/action-delete-dialog/action-delete-dialog.component.ts b/frontend/src/app/components/dashboard/db-table-view/db-table-actions/action-delete-dialog/action-delete-dialog.component.ts index 3ecf8497c..fb0677533 100644 --- a/frontend/src/app/components/dashboard/db-table-view/db-table-actions/action-delete-dialog/action-delete-dialog.component.ts +++ b/frontend/src/app/components/dashboard/db-table-view/db-table-actions/action-delete-dialog/action-delete-dialog.component.ts @@ -1,40 +1,42 @@ -import { Component, Inject, OnInit } from '@angular/core'; -import { MatDialogRef, MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog'; import { CommonModule } from '@angular/common'; +import { Component, Inject, OnInit } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; +import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog'; import { TablesService } from 'src/app/services/tables.service'; @Component({ - selector: 'app-action-delete-dialog', - templateUrl: './action-delete-dialog.component.html', - styleUrls: ['./action-delete-dialog.component.css'], - imports: [CommonModule, MatButtonModule, MatDialogModule] + selector: 'app-action-delete-dialog', + templateUrl: './action-delete-dialog.component.html', + styleUrls: ['./action-delete-dialog.component.css'], + imports: [CommonModule, MatButtonModule, MatDialogModule], }) export class ActionDeleteDialogComponent implements OnInit { + public submitting: boolean = false; - public submitting: boolean = false; - - constructor( - @Inject(MAT_DIALOG_DATA) public data: any, - private _tables: TablesService, - public dialogRef: MatDialogRef - ) { } - - ngOnInit(): void { - this._tables.cast.subscribe(); - } + constructor( + @Inject(MAT_DIALOG_DATA) public data: any, + private _tables: TablesService, + public dialogRef: MatDialogRef, + ) {} - deleteAction() { - this.submitting = true; - this._tables.deleteRule(this.data.connectionID, this.data.selectedTableName, this.data.rule.id) - .subscribe(() => { - this.dialogRef.close(); - this.submitting = false; - }, - () => { this.submitting = false; }, - () => { this.submitting = false; } - ) - } + ngOnInit(): void { + this._tables.cast.subscribe(); + } + deleteAction() { + this.submitting = true; + this._tables.deleteRule(this.data.connectionID, this.data.selectedTableName, this.data.rule.id).subscribe( + () => { + this.dialogRef.close(); + this.submitting = false; + }, + () => { + this.submitting = false; + }, + () => { + this.submitting = false; + }, + ); + } } diff --git a/frontend/src/app/components/dashboard/db-table-view/db-table-ai-panel/db-table-ai-panel.component.css b/frontend/src/app/components/dashboard/db-table-view/db-table-ai-panel/db-table-ai-panel.component.css index 2e9f1cfba..7d13c206f 100644 --- a/frontend/src/app/components/dashboard/db-table-view/db-table-ai-panel/db-table-ai-panel.component.css +++ b/frontend/src/app/components/dashboard/db-table-view/db-table-ai-panel/db-table-ai-panel.component.css @@ -1,924 +1,936 @@ .ai-panel-sidebar { - position: relative; - opacity: 0; - transform: translateX(100%); - height: calc(100vh - 44px); - width: 0; - overflow-y: auto; - transition: width 500ms cubic-bezier(0.4, 0, 0.2, 1), - transform 500ms cubic-bezier(0.4, 0, 0.2, 1), - opacity 400ms cubic-bezier(0.4, 0, 0.2, 1); + position: relative; + opacity: 0; + transform: translateX(100%); + height: calc(100vh - 44px); + width: 0; + overflow-y: auto; + transition: + width 500ms cubic-bezier(0.4, 0, 0.2, 1), + transform 500ms cubic-bezier(0.4, 0, 0.2, 1), + opacity 400ms cubic-bezier(0.4, 0, 0.2, 1); } @media (width <= 600px) { - .ai-panel-sidebar { - width: 100vw; - z-index: 10; - } + .ai-panel-sidebar { + width: 100vw; + z-index: 10; + } } .ai-panel-sidebar_open { - opacity: 1; - transform: translateX(24px); - width: clamp(200px, 22vw, 400px); - transition: transform 400ms cubic-bezier(0.4, 0, 0.2, 1), - opacity 400ms cubic-bezier(0.4, 0, 0.2, 1), - width 400ms cubic-bezier(0.4, 0, 0.2, 1); + opacity: 1; + transform: translateX(24px); + width: clamp(200px, 22vw, 400px); + transition: + transform 400ms cubic-bezier(0.4, 0, 0.2, 1), + opacity 400ms cubic-bezier(0.4, 0, 0.2, 1), + width 400ms cubic-bezier(0.4, 0, 0.2, 1); } .ai-panel-sidebar_open.ai-panel-sidebar_expanded { - /* Keep the same width as collapsed - don't push the table */ - width: clamp(200px, 22vw, 400px); - transition: transform 400ms cubic-bezier(0.4, 0, 0.2, 1), - opacity 400ms cubic-bezier(0.4, 0, 0.2, 1), - width 400ms cubic-bezier(0.4, 0, 0.2, 1); + /* Keep the same width as collapsed - don't push the table */ + width: clamp(200px, 22vw, 400px); + transition: + transform 400ms cubic-bezier(0.4, 0, 0.2, 1), + opacity 400ms cubic-bezier(0.4, 0, 0.2, 1), + width 400ms cubic-bezier(0.4, 0, 0.2, 1); } @media (width <= 600px) { - .ai-panel-sidebar_open { - width: 0; - } + .ai-panel-sidebar_open { + width: 0; + } } .ai-panel-sidebar-content { - position: fixed; - top: 44px; - left: 100vw; - display: flex; - flex-direction: column; - background-color: var(--mat-sidenav-content-background-color); - border-left: solid 1px rgba(0, 0, 0, 0.12); - height: calc(100vh - 44px); - width: clamp(200px, 22vw, 400px); - z-index: 100; - transition: width 500ms cubic-bezier(0.4, 0, 0.2, 1), - border-left 400ms ease; - visibility: hidden; + position: fixed; + top: 44px; + left: 100vw; + display: flex; + flex-direction: column; + background-color: var(--mat-sidenav-content-background-color); + border-left: solid 1px rgba(0, 0, 0, 0.12); + height: calc(100vh - 44px); + width: clamp(200px, 22vw, 400px); + z-index: 100; + transition: + width 500ms cubic-bezier(0.4, 0, 0.2, 1), + border-left 400ms ease; + visibility: hidden; } .ai-panel-sidebar-content_open { - left: calc(100vw - clamp(200px, 22vw, 400px)); - visibility: visible; - transition: left 400ms cubic-bezier(0.4, 0, 0.2, 1), - width 400ms cubic-bezier(0.4, 0, 0.2, 1), - padding-left 400ms cubic-bezier(0.4, 0, 0.2, 1), - border-left 300ms ease; + left: calc(100vw - clamp(200px, 22vw, 400px)); + visibility: visible; + transition: + left 400ms cubic-bezier(0.4, 0, 0.2, 1), + width 400ms cubic-bezier(0.4, 0, 0.2, 1), + padding-left 400ms cubic-bezier(0.4, 0, 0.2, 1), + border-left 300ms ease; } .ai-panel-sidebar-content_open.ai-panel-sidebar-content_expanded { - left: 65px; - width: calc(100vw - 65px); - padding-left: 175px; - border-left: none; - z-index: 3; - transition: left 400ms cubic-bezier(0.4, 0, 0.2, 1), - width 400ms cubic-bezier(0.4, 0, 0.2, 1), - padding-left 400ms cubic-bezier(0.4, 0, 0.2, 1), - border-left 300ms ease; + left: 65px; + width: calc(100vw - 65px); + padding-left: 175px; + border-left: none; + z-index: 3; + transition: + left 400ms cubic-bezier(0.4, 0, 0.2, 1), + width 400ms cubic-bezier(0.4, 0, 0.2, 1), + padding-left 400ms cubic-bezier(0.4, 0, 0.2, 1), + border-left 300ms ease; } .ai-panel-sidebar-content_open.ai-panel-sidebar-content_expanded.ai-panel-sidebar-content_sidebar-collapsed { - padding-left: 0; + padding-left: 0; } @media (width <= 800px) { - .ai-panel-sidebar-content { - width: 100%; - } + .ai-panel-sidebar-content { + width: 100%; + } - .ai-panel-sidebar-content_open { - left: 0; - } + .ai-panel-sidebar-content_open { + left: 0; + } - .ai-panel-sidebar-content_open.ai-panel-sidebar-content_expanded { - left: 0; - width: 100%; - } + .ai-panel-sidebar-content_open.ai-panel-sidebar-content_expanded { + left: 0; + width: 100%; + } } .ai-panel-sidebar-content ::ng-deep .ai-message { - overflow-x: auto; + overflow-x: auto; } .ai-panel-sidebar-content ::ng-deep .ai-message table { - min-width: 100%; - width: max-content; - border-collapse: collapse; - margin: 8px 0; - font-size: 12px; + min-width: 100%; + width: max-content; + border-collapse: collapse; + margin: 8px 0; + font-size: 12px; } .ai-panel-sidebar-content ::ng-deep .ai-message table thead { - background-color: rgba(0, 0, 0, 0.04); + background-color: rgba(0, 0, 0, 0.04); } @media (prefers-color-scheme: dark) { - .ai-panel-sidebar-content ::ng-deep .ai-message table thead { - background-color: rgba(255, 255, 255, 0.04); - } + .ai-panel-sidebar-content ::ng-deep .ai-message table thead { + background-color: rgba(255, 255, 255, 0.04); + } } .ai-panel-sidebar-content ::ng-deep .ai-message table th, .ai-panel-sidebar-content ::ng-deep .ai-message table td { - padding: 6px 8px; - text-align: left; - border: 1px solid rgba(0, 0, 0, 0.12); - white-space: nowrap; + padding: 6px 8px; + text-align: left; + border: 1px solid rgba(0, 0, 0, 0.12); + white-space: nowrap; } @media (prefers-color-scheme: dark) { - .ai-panel-sidebar-content ::ng-deep .ai-message table th, - .ai-panel-sidebar-content ::ng-deep .ai-message table td { - border: 1px solid rgba(255, 255, 255, 0.12); - } + .ai-panel-sidebar-content ::ng-deep .ai-message table th, + .ai-panel-sidebar-content ::ng-deep .ai-message table td { + border: 1px solid rgba(255, 255, 255, 0.12); + } } .ai-panel-sidebar-content ::ng-deep .ai-message table th { - font-weight: 600; + font-weight: 600; } .ai-panel-sidebar-content ::ng-deep .ai-message table tbody tr:hover { - background-color: rgba(0, 0, 0, 0.02); + background-color: rgba(0, 0, 0, 0.02); } @media (prefers-color-scheme: dark) { - .ai-panel-sidebar-content ::ng-deep .ai-message table tbody tr:hover { - background-color: rgba(255, 255, 255, 0.02); - } + .ai-panel-sidebar-content ::ng-deep .ai-message table tbody tr:hover { + background-color: rgba(255, 255, 255, 0.02); + } } @media (prefers-color-scheme: dark) { - .ai-panel-sidebar-content { - border-left: 1px solid var(--mat-sidenav-container-divider-color); - } + .ai-panel-sidebar-content { + border-left: 1px solid var(--mat-sidenav-container-divider-color); + } } @media (width <= 600px) { - .ai-panel-sidebar-content { - left: 0; - width: 100vw; - z-index: 5; - } + .ai-panel-sidebar-content { + left: 0; + width: 100vw; + z-index: 5; + } } .ai-panel-sidebar__header { - display: flex; - align-items: center; - justify-content: space-between; - padding-top: 20px; - padding-left: 16px; - padding-right: 16px; - flex-shrink: 0; - transition: max-width 350ms cubic-bezier(0.4, 0, 0.2, 1), - padding 300ms cubic-bezier(0.4, 0, 0.2, 1); + display: flex; + align-items: center; + justify-content: space-between; + padding-top: 20px; + padding-left: 16px; + padding-right: 16px; + flex-shrink: 0; + transition: + max-width 350ms cubic-bezier(0.4, 0, 0.2, 1), + padding 300ms cubic-bezier(0.4, 0, 0.2, 1); } .ai-panel-sidebar__actions { - display: flex; - align-items: center; - transition: right 350ms cubic-bezier(0.4, 0, 0.2, 1), - top 350ms cubic-bezier(0.4, 0, 0.2, 1); + display: flex; + align-items: center; + transition: + right 350ms cubic-bezier(0.4, 0, 0.2, 1), + top 350ms cubic-bezier(0.4, 0, 0.2, 1); } @media (prefers-color-scheme: dark) { - .ai-icon ::ng-deep svg path { - fill: #fff; - } + .ai-icon ::ng-deep svg path { + fill: #fff; + } } .ai-icon { - animation: rocket-float 3s ease-in-out infinite; + animation: rocket-float 3s ease-in-out infinite; } .ai-icon ::ng-deep svg path[fill="#C177FC"] { - animation: sparkle-twinkle 1.5s ease-in-out infinite; + animation: sparkle-twinkle 1.5s ease-in-out infinite; } .ai-icon ::ng-deep svg path[fill="#C177FC"]:nth-of-type(2) { - animation-delay: 0.3s; + animation-delay: 0.3s; } .ai-icon ::ng-deep svg path[fill="#C177FC"]:nth-of-type(3) { - animation-delay: 0.6s; + animation-delay: 0.6s; } .ai-icon ::ng-deep svg path[fill="#C177FC"]:nth-of-type(4) { - animation-delay: 0.9s; + animation-delay: 0.9s; } @keyframes rocket-float { - 0%, 100% { - transform: translateY(0) rotate(0deg); - } - 50% { - transform: translateY(-2px) rotate(2deg); - } + 0%, + 100% { + transform: translateY(0) rotate(0deg); + } + 50% { + transform: translateY(-2px) rotate(2deg); + } } @keyframes sparkle-twinkle { - 0%, 100% { - opacity: 1; - transform: scale(1); - } - 50% { - opacity: 0.4; - transform: scale(0.8); - } + 0%, + 100% { + opacity: 1; + transform: scale(1); + } + 50% { + opacity: 0.4; + transform: scale(0.8); + } } .ai-panel-sidebar__title { - display: flex; - align-items: center; - gap: 8px; - margin-bottom: 0 !important; + display: flex; + align-items: center; + gap: 8px; + margin-bottom: 0 !important; } .ai-panel-chat { - display: flex; - flex-direction: column; - flex: 1; - justify-content: flex-end; - padding-left: 16px; - padding-right: 16px; - overflow: hidden; - min-height: 0; + display: flex; + flex-direction: column; + flex: 1; + justify-content: flex-end; + padding-left: 16px; + padding-right: 16px; + overflow: hidden; + min-height: 0; } .ai-message-chain-box { - height: calc(100vh - 44px - 252px - 48px); - overflow-y: auto; - margin-bottom: 24px; - transition: max-width 350ms cubic-bezier(0.4, 0, 0.2, 1); + height: calc(100vh - 44px - 252px - 48px); + overflow-y: auto; + margin-bottom: 24px; + transition: max-width 350ms cubic-bezier(0.4, 0, 0.2, 1); } .ai-message-chain { - display: flex; - flex-direction: column; - justify-content: flex-end; - min-height: 100%; - width: 100%; + display: flex; + flex-direction: column; + justify-content: flex-end; + min-height: 100%; + width: 100%; } .user-message { - align-self: flex-end; - background-color: #f0f4f8; - border-radius: 8px; - padding: 12px 16px 16px; - margin: 4px 0 16px; - max-width: 80%; + align-self: flex-end; + background-color: #f0f4f8; + border-radius: 8px; + padding: 12px 16px 16px; + margin: 4px 0 16px; + max-width: 80%; } @media (prefers-color-scheme: dark) { - .user-message { - background-color: #2d3748; - } + .user-message { + background-color: #2d3748; + } } .ai-message { - align-self: flex-start; - background-color: rgba(99, 132, 255, 0.06); - border-radius: 8px; - padding: 12px 16px 8px; - margin: 4px 0; + align-self: flex-start; + background-color: rgba(99, 132, 255, 0.06); + border-radius: 8px; + padding: 12px 16px 8px; + margin: 4px 0; } @media (prefers-color-scheme: dark) { - .ai-message { - background-color: rgba(99, 132, 255, 0.1); - } + .ai-message { + background-color: rgba(99, 132, 255, 0.1); + } } .ai-error-message { - border-radius: 8px; - padding: 8px 8px 0; - margin: 4px 0; + border-radius: 8px; + padding: 8px 8px 0; + margin: 4px 0; } @media (prefers-color-scheme: light) { - .ai-error-message { - background-color: var(--color-warnPalette-100); - color: var(--color-warnPalette-100-contrast); - } + .ai-error-message { + background-color: var(--color-warnPalette-100); + color: var(--color-warnPalette-100-contrast); + } } @media (prefers-color-scheme: dark) { - .ai-error-message { - background-color: var(--color-warnDarkPalette-200); - color: var(--color-warnDarkPalette-200-contrast); - } + .ai-error-message { + background-color: var(--color-warnDarkPalette-200); + color: var(--color-warnDarkPalette-200-contrast); + } } .ai-message ::ng-deep ol, .ai-message ::ng-deep ul { - padding-left: 20px; - margin: 8px 0; + padding-left: 20px; + margin: 8px 0; } .ai-message ::ng-deep li { - margin: 4px 0; - line-height: 1.5; + margin: 4px 0; + line-height: 1.5; } .ai-message ::ng-deep ol { - list-style-position: outside; + list-style-position: outside; } .ai-message ::ng-deep ul { - list-style-position: outside; + list-style-position: outside; } .ai-message ::ng-deep pre { - white-space: break-spaces; + white-space: break-spaces; } .user-message ::ng-deep p, .ai-message ::ng-deep p { - margin-bottom: 8px; + margin-bottom: 8px; } .ai-welcome { - display: flex; - flex-direction: column; - gap: 16px; - margin-bottom: 24px; - flex-shrink: 0; - transition: max-width 350ms cubic-bezier(0.4, 0, 0.2, 1); + display: flex; + flex-direction: column; + gap: 16px; + margin-bottom: 24px; + flex-shrink: 0; + transition: max-width 350ms cubic-bezier(0.4, 0, 0.2, 1); } .ai-welcome__message { - display: flex; - flex-direction: column; - gap: 4px; + display: flex; + flex-direction: column; + gap: 4px; } .ai-welcome__message p { - margin: 0; - line-height: 1.4; + margin: 0; + line-height: 1.4; } .ai-welcome__section { - display: flex; - flex-direction: column; - gap: 2px; + display: flex; + flex-direction: column; + gap: 2px; } .ai-welcome__section-title { - margin: 0; - font-size: 12px; - color: rgba(0, 0, 0, 0.5); + margin: 0; + font-size: 12px; + color: rgba(0, 0, 0, 0.5); } @media (prefers-color-scheme: dark) { - .ai-welcome__section-title { - color: rgba(255, 255, 255, 0.5); - } + .ai-welcome__section-title { + color: rgba(255, 255, 255, 0.5); + } } .suggestions-container { - display: flex; - flex-wrap: wrap; - gap: 6px; + display: flex; + flex-wrap: wrap; + gap: 6px; } .suggestion-chip { - display: inline-flex; - align-items: center; - background-color: var(--mat-sidenav-content-background-color); - border: 1px solid #d3d3d3; - border-radius: 16px; - padding: 4px 12px; - font-size: 12px; - cursor: pointer; - transition: background-color 0.2s ease; + display: inline-flex; + align-items: center; + background-color: var(--mat-sidenav-content-background-color); + border: 1px solid #d3d3d3; + border-radius: 16px; + padding: 4px 12px; + font-size: 12px; + cursor: pointer; + transition: background-color 0.2s ease; } .suggestion-chip:hover { - background-color: var(--color-primaryPalette-50); + background-color: var(--color-primaryPalette-50); } @media (prefers-color-scheme: dark) { - .suggestion-chip { - border-color: #4a4a4a; - color: rgba(255, 255, 255, 0.9); - } + .suggestion-chip { + border-color: #4a4a4a; + color: rgba(255, 255, 255, 0.9); + } - .suggestion-chip:hover { - background-color: rgba(255, 255, 255, 0.08); - } + .suggestion-chip:hover { + background-color: rgba(255, 255, 255, 0.08); + } } .templates-container { - display: flex; - flex-direction: column; - align-items: flex-start; - gap: 8px; - padding: 0; - transition: gap 300ms cubic-bezier(0.4, 0, 0.2, 1); + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 8px; + padding: 0; + transition: gap 300ms cubic-bezier(0.4, 0, 0.2, 1); } .template-card { - display: flex; - align-items: flex-start; - width: auto; - gap: 8px; - background-color: var(--mat-sidenav-content-background-color); - border: 1px solid #d3d3d3; - border-radius: 6px; - padding: 6px 10px; - text-align: left; - cursor: pointer; - transition: background-color 0.2s ease; + display: flex; + align-items: flex-start; + width: auto; + gap: 8px; + background-color: var(--mat-sidenav-content-background-color); + border: 1px solid #d3d3d3; + border-radius: 6px; + padding: 6px 10px; + text-align: left; + cursor: pointer; + transition: background-color 0.2s ease; } .template-card:hover { - background-color: var(--color-primaryPalette-50); + background-color: var(--color-primaryPalette-50); } .template-card__icon { - color: rgba(0, 0, 0, 0.5); - font-size: 16px; - width: 16px; - height: 16px; - flex-shrink: 0; - margin-top: 1px; + color: rgba(0, 0, 0, 0.5); + font-size: 16px; + width: 16px; + height: 16px; + flex-shrink: 0; + margin-top: 1px; } .template-card__content { - display: flex; - flex-direction: column; - gap: 0; + display: flex; + flex-direction: column; + gap: 0; } .template-card__title { - font-size: 13px; - font-weight: 500; - color: rgba(0, 0, 0, 0.87); - line-height: 1.3; + font-size: 13px; + font-weight: 500; + color: rgba(0, 0, 0, 0.87); + line-height: 1.3; } .template-card__description { - font-size: 11px; - color: rgba(0, 0, 0, 0.5); - line-height: 1.3; + font-size: 11px; + color: rgba(0, 0, 0, 0.5); + line-height: 1.3; } @media (prefers-color-scheme: dark) { - .template-card { - border-color: #4a4a4a; - } + .template-card { + border-color: #4a4a4a; + } - .template-card:hover { - background-color: rgba(255, 255, 255, 0.08); - } + .template-card:hover { + background-color: rgba(255, 255, 255, 0.08); + } - .template-card__icon { - color: rgba(255, 255, 255, 0.5); - } + .template-card__icon { + color: rgba(255, 255, 255, 0.5); + } - .template-card__title { - color: rgba(255, 255, 255, 0.9); - } + .template-card__title { + color: rgba(255, 255, 255, 0.9); + } - .template-card__description { - color: rgba(255, 255, 255, 0.5); - } + .template-card__description { + color: rgba(255, 255, 255, 0.5); + } } .loading { - display: flex; - justify-content: center; - align-items: center; - height: 50px; - flex-shrink: 0; + display: flex; + justify-content: center; + align-items: center; + height: 50px; + flex-shrink: 0; } .loading-content { - display: flex; - align-items: center; - gap: 8px; + display: flex; + align-items: center; + gap: 8px; } .loading-dots { - display: flex; - gap: 6px; - align-items: center; + display: flex; + gap: 6px; + align-items: center; } .loading-text { - font-size: 13px; - color: #9ca3af; + font-size: 13px; + color: #9ca3af; } .loading-dot { - width: 6px; - height: 6px; - border-radius: 50%; - background-color: #9ca3af; - animation: loading-bounce 1.4s ease-in-out infinite; + width: 6px; + height: 6px; + border-radius: 50%; + background-color: #9ca3af; + animation: loading-bounce 1.4s ease-in-out infinite; } .loading-dot:nth-child(1) { - animation-delay: 0s; + animation-delay: 0s; } .loading-dot:nth-child(2) { - animation-delay: 0.15s; + animation-delay: 0.15s; } .loading-dot:nth-child(3) { - animation-delay: 0.3s; + animation-delay: 0.3s; } @keyframes loading-bounce { - 0%, 60%, 100% { - transform: translateY(0); - } - 30% { - transform: translateY(-8px); - } + 0%, + 60%, + 100% { + transform: translateY(0); + } + 30% { + transform: translateY(-8px); + } } .ai-message-form { - position: relative; - width: 100%; - flex-shrink: 0; - transition: max-width 350ms cubic-bezier(0.4, 0, 0.2, 1); + position: relative; + width: 100%; + flex-shrink: 0; + transition: max-width 350ms cubic-bezier(0.4, 0, 0.2, 1); } .ai-message-form__textarea { - width: 100%; + width: 100%; } .ai-message-form__textarea ::ng-deep .mat-mdc-form-field-focus-overlay { - display: none; + display: none; } .ai-message-form__textarea ::ng-deep .mdc-text-field--focused .mdc-notched-outline__leading, .ai-message-form__textarea ::ng-deep .mdc-text-field--focused .mdc-notched-outline__trailing { - border-color: #A855F7 !important; - border-width: 2px !important; + border-color: #a855f7 !important; + border-width: 2px !important; } .ai-message-form__textarea ::ng-deep .mdc-text-field--focused .mdc-notched-outline__notch { - border-color: #A855F7 !important; - border-width: 2px 0 !important; + border-color: #a855f7 !important; + border-width: 2px 0 !important; } .ai-message-form__textarea ::ng-deep .mat-mdc-text-field-wrapper { - padding-bottom: 24px !important; + padding-bottom: 24px !important; } .ai-message-form__footer { - position: absolute; - right: 0; - left: 2px; - bottom: 21px; - display: flex; - justify-content: space-between; - align-items: center; - background-color: var(--mat-sidenav-content-background-color); - padding-left: 16px; - padding-right: 4px; - width: calc(100% - 4px); + position: absolute; + right: 0; + left: 2px; + bottom: 21px; + display: flex; + justify-content: space-between; + align-items: center; + background-color: var(--mat-sidenav-content-background-color); + padding-left: 16px; + padding-right: 4px; + width: calc(100% - 4px); } .ai-message-form__char-count { - color: rgba(0, 0, 0, 0.4); - font-size: 12px; + color: rgba(0, 0, 0, 0.4); + font-size: 12px; } @media (prefers-color-scheme: dark) { - .ai-message-form__char-count { - color: rgba(255, 255, 255, 0.4); - } + .ai-message-form__char-count { + color: rgba(255, 255, 255, 0.4); + } } .ai-message-form__button { - color: #A855F7 !important; + color: #a855f7 !important; } .ai-panel-sidebar__message { - resize: none; - width: 100%; + resize: none; + width: 100%; } .footer { - padding-bottom: 16px; - flex-shrink: 0; - transition: max-width 350ms cubic-bezier(0.4, 0, 0.2, 1); + padding-bottom: 16px; + flex-shrink: 0; + transition: max-width 350ms cubic-bezier(0.4, 0, 0.2, 1); } .footer p { - color: rgba(0, 0, 0, 0.36) !important; - text-align: center; - margin: 0; + color: rgba(0, 0, 0, 0.36) !important; + text-align: center; + margin: 0; } @media (prefers-color-scheme: dark) { - .footer p { - color: rgba(255, 255, 255, 0.36) !important; - } + .footer p { + color: rgba(255, 255, 255, 0.36) !important; + } } .link { - color: var(--color-accentedPalette-500); + color: var(--color-accentedPalette-500); } /* Expanded state styles */ .ai-panel-sidebar-content_expanded .ai-panel-chat { - align-items: center; + align-items: center; } .ai-panel-sidebar-content_expanded .ai-welcome { - width: 100%; - max-width: 800px; + width: 100%; + max-width: 800px; } .ai-panel-sidebar-content_expanded .ai-message-chain-box { - width: 100%; - max-width: 800px; + width: 100%; + max-width: 800px; } .ai-panel-sidebar-content_expanded .ai-message-form { - width: 100%; - max-width: 800px; + width: 100%; + max-width: 800px; } .ai-panel-sidebar-content_expanded .footer { - width: 100%; - max-width: 800px; + width: 100%; + max-width: 800px; } .ai-panel-sidebar-content_expanded .ai-panel-sidebar__header { - position: relative; - width: 100%; - max-width: 800px; - margin: 0 auto; - padding-left: 0; - padding-right: 0; - justify-content: flex-start; + position: relative; + width: 100%; + max-width: 800px; + margin: 0 auto; + padding-left: 0; + padding-right: 0; + justify-content: flex-start; } .ai-panel-sidebar-content_expanded .ai-panel-sidebar__actions { - position: fixed; - right: 16px; - top: 64px; + position: fixed; + right: 16px; + top: 64px; } .ai-panel-sidebar-content_expanded .ai-welcome__section { - gap: 4px !important; + gap: 4px !important; } .ai-panel-sidebar-content_expanded .ai-welcome__section-title { - margin-bottom: 0 !important; + margin-bottom: 0 !important; } .ai-panel-sidebar-content_expanded .templates-container, .ai-panel-sidebar-content_expanded .suggestions-container { - margin-top: 0 !important; + margin-top: 0 !important; } .ai-panel-sidebar-content_expanded .templates-container { - flex-direction: row; - flex-wrap: wrap; - gap: 12px; + flex-direction: row; + flex-wrap: wrap; + gap: 12px; } .ai-panel-sidebar-content_expanded .template-card { - width: auto; + width: auto; } .ai-panel-sidebar-content_expanded .template-card__description { - white-space: nowrap; + white-space: nowrap; } /* Welcome centered layout */ .ai-welcome-centered { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - flex: 1; - padding: 16px; - min-height: 0; - overflow: hidden; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + flex: 1; + padding: 16px; + min-height: 0; + overflow: hidden; } .ai-welcome-centered__content { - display: flex; - flex-direction: column; - align-items: center; - width: 100%; - max-width: 500px; - gap: 24px; + display: flex; + flex-direction: column; + align-items: center; + width: 100%; + max-width: 500px; + gap: 24px; } .ai-welcome-icon { - width: 48px !important; - height: 48px !important; - animation: rocket-float 3s ease-in-out infinite; + width: 48px !important; + height: 48px !important; + animation: rocket-float 3s ease-in-out infinite; } .ai-welcome-icon ::ng-deep svg path:first-of-type { - fill: rgba(0, 0, 0, 0.6); + fill: rgba(0, 0, 0, 0.6); } .ai-welcome-icon ::ng-deep svg path[fill="#C177FC"] { - animation: sparkle-twinkle 1.5s ease-in-out infinite; + animation: sparkle-twinkle 1.5s ease-in-out infinite; } .ai-welcome-icon ::ng-deep svg path[fill="#C177FC"]:nth-of-type(2) { - animation-delay: 0.3s; + animation-delay: 0.3s; } .ai-welcome-icon ::ng-deep svg path[fill="#C177FC"]:nth-of-type(3) { - animation-delay: 0.6s; + animation-delay: 0.6s; } .ai-welcome-icon ::ng-deep svg path[fill="#C177FC"]:nth-of-type(4) { - animation-delay: 0.9s; + animation-delay: 0.9s; } @media (prefers-color-scheme: dark) { - .ai-welcome-icon ::ng-deep svg path:first-of-type { - fill: rgba(255, 255, 255, 0.7); - } + .ai-welcome-icon ::ng-deep svg path:first-of-type { + fill: rgba(255, 255, 255, 0.7); + } } .ai-welcome-centered__title { - font-size: 16px; - font-weight: 500; - color: rgba(0, 0, 0, 0.7); - margin: 0; - text-align: center; + font-size: 16px; + font-weight: 500; + color: rgba(0, 0, 0, 0.7); + margin: 0; + text-align: center; } @media (prefers-color-scheme: dark) { - .ai-welcome-centered__title { - color: rgba(255, 255, 255, 0.7); - } + .ai-welcome-centered__title { + color: rgba(255, 255, 255, 0.7); + } } .ai-input-wrapper { - position: relative; - width: 100%; + position: relative; + width: 100%; } .ai-welcome-form { - width: 100%; + width: 100%; } .ai-welcome-form__field { - width: 100%; + width: 100%; } .ai-welcome-form__field ::ng-deep .mdc-text-field--outlined .mdc-notched-outline__leading, .ai-welcome-form__field ::ng-deep .mdc-text-field--outlined .mdc-notched-outline__trailing { - border-radius: 24px; + border-radius: 24px; } .ai-welcome-form__field ::ng-deep .mdc-text-field--outlined .mdc-notched-outline__leading { - border-radius: 24px 0 0 24px; - width: 24px; + border-radius: 24px 0 0 24px; + width: 24px; } .ai-welcome-form__field ::ng-deep .mdc-text-field--outlined .mdc-notched-outline__trailing { - border-radius: 0 24px 24px 0; + border-radius: 0 24px 24px 0; } .ai-welcome-form__field ::ng-deep .mdc-text-field--focused .mdc-notched-outline__leading, .ai-welcome-form__field ::ng-deep .mdc-text-field--focused .mdc-notched-outline__trailing { - border-color: #6384FF !important; - border-width: 2px !important; + border-color: #6384ff !important; + border-width: 2px !important; } .ai-welcome-form__field ::ng-deep .mdc-text-field--focused .mdc-notched-outline__notch { - border-color: #6384FF !important; - border-width: 2px 0 !important; + border-color: #6384ff !important; + border-width: 2px 0 !important; } .ai-welcome-form__field ::ng-deep .mat-mdc-form-field-subscript-wrapper { - display: none; + display: none; } .ai-welcome-form__button { - color: #6384FF !important; + color: #6384ff !important; } .ai-completions { - position: absolute; - top: 100%; - left: 0; - right: 0; - background-color: var(--mat-sidenav-content-background-color); - border: 1px solid rgba(0, 0, 0, 0.12); - border-radius: 12px; - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); - z-index: 10; - margin-top: 2px; - overflow: hidden; + position: absolute; + top: 100%; + left: 0; + right: 0; + background-color: var(--mat-sidenav-content-background-color); + border: 1px solid rgba(0, 0, 0, 0.12); + border-radius: 12px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); + z-index: 10; + margin-top: 2px; + overflow: hidden; } @media (prefers-color-scheme: dark) { - .ai-completions { - border-color: rgba(255, 255, 255, 0.12); - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); - } + .ai-completions { + border-color: rgba(255, 255, 255, 0.12); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); + } } .ai-completion-item { - display: block; - width: 100%; - padding: 12px 16px; - text-align: left; - background: none; - border: none; - cursor: pointer; - font-size: 13px; - color: rgba(0, 0, 0, 0.87); - transition: background-color 0.15s ease; + display: block; + width: 100%; + padding: 12px 16px; + text-align: left; + background: none; + border: none; + cursor: pointer; + font-size: 13px; + color: rgba(0, 0, 0, 0.87); + transition: background-color 0.15s ease; } .ai-completion-item:hover { - background-color: rgba(99, 132, 255, 0.08); + background-color: rgba(99, 132, 255, 0.08); } .ai-completion-item:not(:last-child) { - border-bottom: 1px solid rgba(0, 0, 0, 0.06); + border-bottom: 1px solid rgba(0, 0, 0, 0.06); } @media (prefers-color-scheme: dark) { - .ai-completion-item { - color: rgba(255, 255, 255, 0.87); - } + .ai-completion-item { + color: rgba(255, 255, 255, 0.87); + } - .ai-completion-item:not(:last-child) { - border-bottom-color: rgba(255, 255, 255, 0.06); - } + .ai-completion-item:not(:last-child) { + border-bottom-color: rgba(255, 255, 255, 0.06); + } } .ai-categories { - display: flex; - flex-direction: column; - gap: 0; - width: 100%; + display: flex; + flex-direction: column; + gap: 0; + width: 100%; } .ai-category { - display: flex; - flex-direction: column; - gap: 0; + display: flex; + flex-direction: column; + gap: 0; } .ai-category__title { - font-size: 11px; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.5px; - color: rgba(0, 0, 0, 0.4); - margin: 12px 0 4px 0 !important; + font-size: 11px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.5px; + color: rgba(0, 0, 0, 0.4); + margin: 12px 0 4px 0 !important; } .ai-category:first-child .ai-category__title { - margin-top: 0; + margin-top: 0; } @media (prefers-color-scheme: dark) { - .ai-category__title { - color: rgba(255, 255, 255, 0.4); - } + .ai-category__title { + color: rgba(255, 255, 255, 0.4); + } } .ai-category__chips { - display: flex; - flex-wrap: wrap; - gap: 6px; + display: flex; + flex-wrap: wrap; + gap: 6px; } .suggestion-chip_active { - background-color: rgba(99, 132, 255, 0.15) !important; - border-color: #6384FF !important; + background-color: rgba(99, 132, 255, 0.15) !important; + border-color: #6384ff !important; } @media (prefers-color-scheme: dark) { - .suggestion-chip_active { - background-color: rgba(99, 132, 255, 0.25) !important; - } + .suggestion-chip_active { + background-color: rgba(99, 132, 255, 0.25) !important; + } } @media (prefers-color-scheme: dark) { - .ai-message ::ng-deep svg text, - .ai-message ::ng-deep svg .legend text, - .ai-message ::ng-deep svg .title text, - .ai-message ::ng-deep svg .label text, - .ai-message ::ng-deep svg .tick text, - .ai-message ::ng-deep svg .axis text, - .ai-message ::ng-deep svg .pieCircle text, - .ai-message ::ng-deep svg .slice text { - fill: rgba(255, 255, 255, 0.87) !important; - } - - .ai-message ::ng-deep svg .title { - fill: rgba(255, 255, 255, 0.87) !important; - } + .ai-message ::ng-deep svg text, + .ai-message ::ng-deep svg .legend text, + .ai-message ::ng-deep svg .title text, + .ai-message ::ng-deep svg .label text, + .ai-message ::ng-deep svg .tick text, + .ai-message ::ng-deep svg .axis text, + .ai-message ::ng-deep svg .pieCircle text, + .ai-message ::ng-deep svg .slice text { + fill: rgba(255, 255, 255, 0.87) !important; + } + + .ai-message ::ng-deep svg .title { + fill: rgba(255, 255, 255, 0.87) !important; + } } diff --git a/frontend/src/app/components/dashboard/db-table-view/db-table-export-dialog/db-table-export-dialog.component.css b/frontend/src/app/components/dashboard/db-table-view/db-table-export-dialog/db-table-export-dialog.component.css index d40661263..2e5ab5ac8 100644 --- a/frontend/src/app/components/dashboard/db-table-view/db-table-export-dialog/db-table-export-dialog.component.css +++ b/frontend/src/app/components/dashboard/db-table-view/db-table-export-dialog/db-table-export-dialog.component.css @@ -1,3 +1,3 @@ .records-number-input { - margin-top: 16px; -} \ No newline at end of file + margin-top: 16px; +} diff --git a/frontend/src/app/components/dashboard/db-table-view/db-table-export-dialog/db-table-export-dialog.component.spec.ts b/frontend/src/app/components/dashboard/db-table-view/db-table-export-dialog/db-table-export-dialog.component.spec.ts index a9ebee5da..b3c39fd34 100644 --- a/frontend/src/app/components/dashboard/db-table-view/db-table-export-dialog/db-table-export-dialog.component.spec.ts +++ b/frontend/src/app/components/dashboard/db-table-view/db-table-export-dialog/db-table-export-dialog.component.spec.ts @@ -1,51 +1,53 @@ +import { provideHttpClient } from '@angular/common/http'; import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { DbTableExportDialogComponent } from './db-table-export-dialog.component'; +import { FormsModule } from '@angular/forms'; import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog'; import { MatSnackBarModule } from '@angular/material/snack-bar'; -import { FormsModule } from '@angular/forms'; -import { Angulartics2Module } from 'angulartics2'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { provideHttpClient } from '@angular/common/http'; +import { Angulartics2Module } from 'angulartics2'; +import { DbTableExportDialogComponent } from './db-table-export-dialog.component'; describe('DbTableExportDialogComponent', () => { - let component: DbTableExportDialogComponent; - let fixture: ComponentFixture; + let component: DbTableExportDialogComponent; + let fixture: ComponentFixture; - const mockDialogRef = { - close: () => { } - }; + const mockDialogRef = { + close: () => {}, + }; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [ - MatDialogModule, - MatSnackBarModule, - FormsModule, - Angulartics2Module.forRoot(), - DbTableExportDialogComponent, - BrowserAnimationsModule - ], - providers: [ - provideHttpClient(), - { provide: MAT_DIALOG_DATA, useValue: { - connectionID: '12345678', - tableName: 'users', - sortColumn: 'first_name', - sortOrder: 'asc', - filters: { first_name: { startswith: 'A' } }, - search: '' - }}, - { provide: MatDialogRef, useValue: mockDialogRef }, - ] - }).compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + MatDialogModule, + MatSnackBarModule, + FormsModule, + Angulartics2Module.forRoot(), + DbTableExportDialogComponent, + BrowserAnimationsModule, + ], + providers: [ + provideHttpClient(), + { + provide: MAT_DIALOG_DATA, + useValue: { + connectionID: '12345678', + tableName: 'users', + sortColumn: 'first_name', + sortOrder: 'asc', + filters: { first_name: { startswith: 'A' } }, + search: '', + }, + }, + { provide: MatDialogRef, useValue: mockDialogRef }, + ], + }).compileComponents(); - fixture = TestBed.createComponent(DbTableExportDialogComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + fixture = TestBed.createComponent(DbTableExportDialogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/dashboard/db-table-view/db-table-filters-dialog/db-table-filters-dialog.component.css b/frontend/src/app/components/dashboard/db-table-view/db-table-filters-dialog/db-table-filters-dialog.component.css index ead289b55..a4fc2d4d9 100644 --- a/frontend/src/app/components/dashboard/db-table-view/db-table-filters-dialog/db-table-filters-dialog.component.css +++ b/frontend/src/app/components/dashboard/db-table-view/db-table-filters-dialog/db-table-filters-dialog.component.css @@ -1,55 +1,54 @@ .filters-header { - display: flex; - align-items: center; - justify-content: space-between; - margin-top: 16px; + display: flex; + align-items: center; + justify-content: space-between; + margin-top: 16px; } .filters-content { - display: grid; - grid-template-columns: auto 228px 0 1fr 32px; - grid-column-gap: 8px; - align-content: flex-start; - align-items: flex-start; + display: grid; + grid-template-columns: auto 228px 0 1fr 32px; + grid-column-gap: 8px; + align-content: flex-start; + align-items: flex-start; } .filters-select { - grid-column: 1 / span 5; + grid-column: 1 / span 5; } .filter-line { - grid-column: 1 / span 4; + grid-column: 1 / span 4; } ::ng-deep .mat-dialog-container > .ng-star-inserted { - display: flex; - flex-direction: column; - width: 100%; - } + display: flex; + flex-direction: column; + width: 100%; +} .filters-form { - flex: 1 0 auto; - display: flex; - flex-direction: column; + flex: 1 0 auto; + display: flex; + flex-direction: column; } ::ng-deep .mat-dialog-container { - display: flex; + display: flex; } - .filters-content { - flex: 1 0 auto; + flex: 1 0 auto; } .column-name { - margin-top: 18px; + margin-top: 18px; } .filter-delete-button { - margin-top: 4px; + margin-top: 4px; } .settings-form__reset-button { - margin-right: auto; -} \ No newline at end of file + margin-right: auto; +} diff --git a/frontend/src/app/components/dashboard/db-table-view/db-table-filters-dialog/db-table-filters-dialog.component.spec.ts b/frontend/src/app/components/dashboard/db-table-view/db-table-filters-dialog/db-table-filters-dialog.component.spec.ts index 970aff59d..9546644ab 100644 --- a/frontend/src/app/components/dashboard/db-table-view/db-table-filters-dialog/db-table-filters-dialog.component.spec.ts +++ b/frontend/src/app/components/dashboard/db-table-view/db-table-filters-dialog/db-table-filters-dialog.component.spec.ts @@ -1,181 +1,182 @@ +import { provideHttpClient } from '@angular/common/http'; import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog'; - -import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { DbTableFiltersDialogComponent } from './db-table-filters-dialog.component'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { MatAutocompleteModule } from '@angular/material/autocomplete'; +import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog'; import { MatSnackBarModule } from '@angular/material/snack-bar'; -import { Angulartics2Module } from 'angulartics2'; -import { provideHttpClient } from '@angular/common/http'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { provideRouter } from '@angular/router'; +import { Angulartics2Module } from 'angulartics2'; +import { DbTableFiltersDialogComponent } from './db-table-filters-dialog.component'; describe('DbTableFiltersDialogComponent', () => { - let component: DbTableFiltersDialogComponent; - let fixture: ComponentFixture; - - const fakeFirstName = { - "column_name": "FirstName", - "column_default": null, - "data_type": "varchar", - "isExcluded": false, - "isSearched": false, - "auto_increment": false, - "allow_null": false, - "character_maximum_length": 30 - }; - const fakeId = { - "column_name": "Id", - "column_default": "auto_increment", - "data_type": "int", - "isExcluded": false, - "isSearched": false, - "auto_increment": true, - "allow_null": false, - "character_maximum_length": null - }; - const fakeBool = { - "column_name": "bool", - "column_default": null, - "data_type": "tinyint", - "isExcluded": false, - "isSearched": true, - "auto_increment": false, - "allow_null": true, - "character_maximum_length": 1 - }; - - const mockTableDataSource_Structure = [fakeFirstName, fakeId, fakeBool]; - - const mockTableDataSource_ForeignKeysList = ["Id"]; - - const mockTableDataSource_ForeignKeys = { - Id: { - "referenced_column_name": "CustomerId", - "referenced_table_name": "Customers", - "constraint_name": "Orders_ibfk_2", - "column_name": "Id" - } - }; - - const mockStructureForFilterDialog = { - structure: mockTableDataSource_Structure, - foreignKeysList: mockTableDataSource_ForeignKeysList, - foreignKeys: mockTableDataSource_ForeignKeys, - widgets: [] - } - - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [ - MatSnackBarModule, - MatDialogModule, - FormsModule, - ReactiveFormsModule, - MatAutocompleteModule, - BrowserAnimationsModule, - Angulartics2Module.forRoot(), - DbTableFiltersDialogComponent - ], - providers: [ - provideHttpClient(), - provideRouter([]), - { provide: MAT_DIALOG_DATA, useValue: { - connectionID: '12345678', - tableName: 'users', - displayTableName: 'Users', - structure: mockStructureForFilterDialog - }}, - { provide: MatDialogRef, useValue: {} }, - // { provide: ActivatedRoute, useValue: {} } - ], - }).compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(DbTableFiltersDialogComponent); - component = fixture.componentInstance; - - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - it('should setup the initial state of fields list and types', async () => { - - component.ngOnInit(); - fixture.detectChanges(); - await fixture.whenStable(); - - expect(component.tableForeignKeys).toEqual(mockStructureForFilterDialog.foreignKeys); - expect(component.fields).toEqual(['FirstName', 'Id', 'bool']); - expect(component.tableRowStructure).toEqual({ - FirstName: fakeFirstName, - Id: fakeId, - bool: fakeBool - }); - }); - - it('should setup the initial state from search params', async () => { - component.route.snapshot.queryParams = { - filters: '(FirstName:(contains:nn))' - }; - - component.ngOnInit(); - fixture.detectChanges(); - await fixture.whenStable(); - - expect(component.tableFilters).toEqual(['FirstName']); - expect(component.tableRowFieldsShown).toEqual({ - FirstName: 'nn' - }); - expect(component.tableRowFieldsComparator).toEqual({ - FirstName: 'contains' - }) - }); - - it('should setup the initial state from isSearched table settings', async () => { - component.ngOnInit(); - fixture.detectChanges(); - await fixture.whenStable(); - - expect(component.tableFilters).toEqual(['bool']); - expect(component.tableRowFieldsShown).toEqual({ - bool: undefined - }); - expect(component.tableRowFieldsComparator).toEqual({ - bool: 'eq' - }) - }); - - it('should update a field in the model from ui component', () => { - component.tableRowFieldsShown = { name: 'John' }; - component.updateField('new user name', 'name'); - - expect(component.tableRowFieldsShown.name).toEqual('new user name'); - }) - - it('should reset filters', () => { - component.tableFilters = ['FirstName', 'Id']; - component.tableRowFieldsShown = { - FirstName: 'John', - Id: '111' - } - component.resetFilters(); - - expect(component.tableFilters).toEqual([]); - expect(component.tableRowFieldsShown).toEqual({}); - }) - - it('should return number type of comparator select if data type is datetime', () => { - const comparatorType = component.getComparatorType('datetime'); - expect(comparatorType).toEqual('number'); - }) - - it('should return nonComparable type of comparator select if data type neither text nor number', () => { - const comparatorType = component.getComparatorType(undefined); - expect(comparatorType).toEqual('nonComparable'); - }) + let component: DbTableFiltersDialogComponent; + let fixture: ComponentFixture; + + const fakeFirstName = { + column_name: 'FirstName', + column_default: null, + data_type: 'varchar', + isExcluded: false, + isSearched: false, + auto_increment: false, + allow_null: false, + character_maximum_length: 30, + }; + const fakeId = { + column_name: 'Id', + column_default: 'auto_increment', + data_type: 'int', + isExcluded: false, + isSearched: false, + auto_increment: true, + allow_null: false, + character_maximum_length: null, + }; + const fakeBool = { + column_name: 'bool', + column_default: null, + data_type: 'tinyint', + isExcluded: false, + isSearched: true, + auto_increment: false, + allow_null: true, + character_maximum_length: 1, + }; + + const mockTableDataSource_Structure = [fakeFirstName, fakeId, fakeBool]; + + const mockTableDataSource_ForeignKeysList = ['Id']; + + const mockTableDataSource_ForeignKeys = { + Id: { + referenced_column_name: 'CustomerId', + referenced_table_name: 'Customers', + constraint_name: 'Orders_ibfk_2', + column_name: 'Id', + }, + }; + + const mockStructureForFilterDialog = { + structure: mockTableDataSource_Structure, + foreignKeysList: mockTableDataSource_ForeignKeysList, + foreignKeys: mockTableDataSource_ForeignKeys, + widgets: [], + }; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + MatSnackBarModule, + MatDialogModule, + FormsModule, + ReactiveFormsModule, + MatAutocompleteModule, + BrowserAnimationsModule, + Angulartics2Module.forRoot(), + DbTableFiltersDialogComponent, + ], + providers: [ + provideHttpClient(), + provideRouter([]), + { + provide: MAT_DIALOG_DATA, + useValue: { + connectionID: '12345678', + tableName: 'users', + displayTableName: 'Users', + structure: mockStructureForFilterDialog, + }, + }, + { provide: MatDialogRef, useValue: {} }, + // { provide: ActivatedRoute, useValue: {} } + ], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(DbTableFiltersDialogComponent); + component = fixture.componentInstance; + + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should setup the initial state of fields list and types', async () => { + component.ngOnInit(); + fixture.detectChanges(); + await fixture.whenStable(); + + expect(component.tableForeignKeys).toEqual(mockStructureForFilterDialog.foreignKeys); + expect(component.fields).toEqual(['FirstName', 'Id', 'bool']); + expect(component.tableRowStructure).toEqual({ + FirstName: fakeFirstName, + Id: fakeId, + bool: fakeBool, + }); + }); + + it('should setup the initial state from search params', async () => { + component.route.snapshot.queryParams = { + filters: '(FirstName:(contains:nn))', + }; + + component.ngOnInit(); + fixture.detectChanges(); + await fixture.whenStable(); + + expect(component.tableFilters).toEqual(['FirstName']); + expect(component.tableRowFieldsShown).toEqual({ + FirstName: 'nn', + }); + expect(component.tableRowFieldsComparator).toEqual({ + FirstName: 'contains', + }); + }); + + it('should setup the initial state from isSearched table settings', async () => { + component.ngOnInit(); + fixture.detectChanges(); + await fixture.whenStable(); + + expect(component.tableFilters).toEqual(['bool']); + expect(component.tableRowFieldsShown).toEqual({ + bool: undefined, + }); + expect(component.tableRowFieldsComparator).toEqual({ + bool: 'eq', + }); + }); + + it('should update a field in the model from ui component', () => { + component.tableRowFieldsShown = { name: 'John' }; + component.updateField('new user name', 'name'); + + expect(component.tableRowFieldsShown.name).toEqual('new user name'); + }); + + it('should reset filters', () => { + component.tableFilters = ['FirstName', 'Id']; + component.tableRowFieldsShown = { + FirstName: 'John', + Id: '111', + }; + component.resetFilters(); + + expect(component.tableFilters).toEqual([]); + expect(component.tableRowFieldsShown).toEqual({}); + }); + + it('should return number type of comparator select if data type is datetime', () => { + const comparatorType = component.getComparatorType('datetime'); + expect(comparatorType).toEqual('number'); + }); + + it('should return nonComparable type of comparator select if data type neither text nor number', () => { + const comparatorType = component.getComparatorType(undefined); + expect(comparatorType).toEqual('nonComparable'); + }); }); diff --git a/frontend/src/app/components/dashboard/db-table-view/db-table-import-dialog/db-table-import-dialog.component.css b/frontend/src/app/components/dashboard/db-table-view/db-table-import-dialog/db-table-import-dialog.component.css index 96862173a..e89768b33 100644 --- a/frontend/src/app/components/dashboard/db-table-view/db-table-import-dialog/db-table-import-dialog.component.css +++ b/frontend/src/app/components/dashboard/db-table-view/db-table-import-dialog/db-table-import-dialog.component.css @@ -1,38 +1,38 @@ .file-uploader-appearance { - display: flex; - align-items: center; - gap: 12px; + display: flex; + align-items: center; + gap: 12px; } .file-name { - color: rgba(0, 0, 0, 0.5); - font-style: italic; - margin-right: 20px; + color: rgba(0, 0, 0, 0.5); + font-style: italic; + margin-right: 20px; } @media (prefers-color-scheme: dark) { - .file-name { - color: rgba(255, 255, 255, 0.5); - } + .file-name { + color: rgba(255, 255, 255, 0.5); + } } .upload-btn { - margin-right: 12px; + margin-right: 12px; } .form__warning { - margin-top: -12px; - margin-bottom: 20px; + margin-top: -12px; + margin-bottom: 20px; } @media (prefers-color-scheme: light) { - .form__warning { - color: var(--color-warnPalette-500); - } + .form__warning { + color: var(--color-warnPalette-500); + } } @media (prefers-color-scheme: dark) { - .form__warning { - color: var(--color-warnDarkPalette-500); - } -} \ No newline at end of file + .form__warning { + color: var(--color-warnDarkPalette-500); + } +} diff --git a/frontend/src/app/components/dashboard/db-table-view/db-table-import-dialog/db-table-import-dialog.component.spec.ts b/frontend/src/app/components/dashboard/db-table-view/db-table-import-dialog/db-table-import-dialog.component.spec.ts index 0812619cb..ebfa7760b 100644 --- a/frontend/src/app/components/dashboard/db-table-view/db-table-import-dialog/db-table-import-dialog.component.spec.ts +++ b/frontend/src/app/components/dashboard/db-table-view/db-table-import-dialog/db-table-import-dialog.component.spec.ts @@ -1,49 +1,51 @@ +import { provideHttpClient } from '@angular/common/http'; import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { DbTableImportDialogComponent } from './db-table-import-dialog.component'; +import { FormsModule } from '@angular/forms'; import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog'; import { MatSnackBarModule } from '@angular/material/snack-bar'; -import { FormsModule } from '@angular/forms'; import { Angulartics2Module } from 'angulartics2'; -import { provideHttpClient } from '@angular/common/http'; +import { DbTableImportDialogComponent } from './db-table-import-dialog.component'; describe('DbTableImportDialogComponent', () => { - let component: DbTableImportDialogComponent; - let fixture: ComponentFixture; + let component: DbTableImportDialogComponent; + let fixture: ComponentFixture; - const mockDialogRef = { - close: () => { } - }; + const mockDialogRef = { + close: () => {}, + }; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [ - MatDialogModule, - MatSnackBarModule, - FormsModule, - Angulartics2Module.forRoot(), - DbTableImportDialogComponent - ], - providers: [ - provideHttpClient(), - { provide: MAT_DIALOG_DATA, useValue: { - connectionID: '12345678', - tableName: 'users', - sortColumn: 'first_name', - sortOrder: 'asc', - filters: { first_name: { startswith: 'A' } }, - search: '' - }}, - { provide: MatDialogRef, useValue: mockDialogRef }, - ] - }).compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + MatDialogModule, + MatSnackBarModule, + FormsModule, + Angulartics2Module.forRoot(), + DbTableImportDialogComponent, + ], + providers: [ + provideHttpClient(), + { + provide: MAT_DIALOG_DATA, + useValue: { + connectionID: '12345678', + tableName: 'users', + sortColumn: 'first_name', + sortOrder: 'asc', + filters: { first_name: { startswith: 'A' } }, + search: '', + }, + }, + { provide: MatDialogRef, useValue: mockDialogRef }, + ], + }).compileComponents(); - fixture = TestBed.createComponent(DbTableImportDialogComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + fixture = TestBed.createComponent(DbTableImportDialogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/dashboard/db-table-view/db-table-row-view/db-table-row-view.component.spec.ts b/frontend/src/app/components/dashboard/db-table-view/db-table-row-view/db-table-row-view.component.spec.ts index 5cb37d748..e7274fbf1 100644 --- a/frontend/src/app/components/dashboard/db-table-view/db-table-row-view/db-table-row-view.component.spec.ts +++ b/frontend/src/app/components/dashboard/db-table-view/db-table-row-view/db-table-row-view.component.spec.ts @@ -1,33 +1,25 @@ +import { provideHttpClient } from '@angular/common/http'; import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { DbTableRowViewComponent } from './db-table-row-view.component'; import { MatSnackBarModule } from '@angular/material/snack-bar'; import { ActivatedRoute } from '@angular/router'; -import { provideHttpClient } from '@angular/common/http'; import { Angulartics2Module } from 'angulartics2'; +import { DbTableRowViewComponent } from './db-table-row-view.component'; describe('DbTableRowViewComponent', () => { - let component: DbTableRowViewComponent; - let fixture: ComponentFixture; + let component: DbTableRowViewComponent; + let fixture: ComponentFixture; - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ - MatSnackBarModule, - DbTableRowViewComponent, - Angulartics2Module.forRoot() - ], - providers: [ - provideHttpClient(), - { provide: ActivatedRoute, useValue: {} } - ] -}); - fixture = TestBed.createComponent(DbTableRowViewComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [MatSnackBarModule, DbTableRowViewComponent, Angulartics2Module.forRoot()], + providers: [provideHttpClient(), { provide: ActivatedRoute, useValue: {} }], + }); + fixture = TestBed.createComponent(DbTableRowViewComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/dashboard/db-table-view/db-table-settings/db-table-settings.component.css b/frontend/src/app/components/dashboard/db-table-view/db-table-settings/db-table-settings.component.css index 72916df55..20082639f 100644 --- a/frontend/src/app/components/dashboard/db-table-view/db-table-settings/db-table-settings.component.css +++ b/frontend/src/app/components/dashboard/db-table-view/db-table-settings/db-table-settings.component.css @@ -1,231 +1,232 @@ :host app-alert:not(:empty) { - --alert-margin: 24px; + --alert-margin: 24px; } .settings-page { - display: flex; - flex-direction: column; - align-items: center; - margin: 2.5em 0 0; + display: flex; + flex-direction: column; + align-items: center; + margin: 2.5em 0 0; } .settings-page .mat-mdc-form-field-bottom-align::before { - margin-left: 0; + margin-left: 0; } .settings-form { - width: 100%; + width: 100%; } .settings-fields { - display: flex; - flex-direction: column; - gap: 20px; - padding: 24px max(calc(50vw - 325px), 10%) 60px; + display: flex; + flex-direction: column; + gap: 20px; + padding: 24px max(calc(50vw - 325px), 10%) 60px; } @media (width <= 600px) { - .settings-fields { - gap: 24px; - padding: 0 9vw 72px; - } + .settings-fields { + gap: 24px; + padding: 0 9vw 72px; + } } .settings-fields__subheading { - margin-top: -4px !important; - margin-bottom: 0 !important; + margin-top: -4px !important; + margin-bottom: 0 !important; } @media (prefers-color-scheme: dark) { - .settings-fields__subheading { - color: rgba(255,255,255,0.64); - } + .settings-fields__subheading { + color: rgba(255, 255, 255, 0.64); + } } @media (prefers-color-scheme: light) { - .settings-fields__subheading { - color: rgba(0,0,0,0.64); - } + .settings-fields__subheading { + color: rgba(0, 0, 0, 0.64); + } } .settings-table-display { - display: flex; - align-items: center; - gap: 12px; - width: 100%; + display: flex; + align-items: center; + gap: 12px; + width: 100%; } .settings-table-display__title { - flex-grow: 1; + flex-grow: 1; } .settings-fields__heading { - margin-top: 20px !important; - margin-bottom: -4px !important; + margin-top: 20px !important; + margin-bottom: -4px !important; } .icon-picker { - margin-left: -4px; - margin-bottom: 20px; + margin-left: -4px; + margin-bottom: 20px; } .order-settings { - display: flex; - gap: 12px; + display: flex; + gap: 12px; } .order-settings__panel { - flex-grow: 1; - position: relative; - margin-top: 16px; - margin-bottom: 8px; + flex-grow: 1; + position: relative; + margin-top: 16px; + margin-bottom: 8px; } .order-settings__expansion-panel { - position: relative; - box-shadow: 0 0 1px 1px rgba(0,0,0,0.15); + position: relative; + box-shadow: 0 0 1px 1px rgba(0, 0, 0, 0.15); } .order-settings ::ng-deep .mat-expansion-panel-header { - height: auto; - min-height: 44px; - padding: 0.75em; + height: auto; + min-height: 44px; + padding: 0.75em; } .order-settings__reset-button { - margin-top: 4px; + margin-top: 4px; } .mat-expansion-panel:not(.mat-expanded):hover { - box-shadow: 0 0 1px 2px rgba(0,0,0,0.87); + box-shadow: 0 0 1px 2px rgba(0, 0, 0, 0.87); } .mat-expansion-panel:not(.mat-expanded) .mat-expansion-panel-header:hover:not([aria-disabled="true"]) { - background: transparent; + background: transparent; } .mat-expansion-panel-header { - position: relative; - height: 60px; - padding: 0 0.75em; + position: relative; + height: 60px; + padding: 0 0.75em; } .order__title { - position: absolute; - top: -12px; - left: 8px; - display: inline-block; - font-size: 0.75em; - padding: 4px; - z-index: 1; + position: absolute; + top: -12px; + left: 8px; + display: inline-block; + font-size: 0.75em; + padding: 4px; + z-index: 1; } @media (prefers-color-scheme: dark) { - .order__title { - background: transparent; - color: rgba(255, 255, 255, 0.6); - } + .order__title { + background: transparent; + color: rgba(255, 255, 255, 0.6); + } } @media (prefers-color-scheme: light) { - .order__title { - background: #fff; - color: rgba(0, 0, 0, 0.6); - } + .order__title { + background: #fff; + color: rgba(0, 0, 0, 0.6); + } } .order-item { - background: var(--mat-expansion-container-background-color); - color: var(--mat-expansion-container-text-color); - box-sizing: border-box; - cursor: move; - padding: 20px 12px; + background: var(--mat-expansion-container-background-color); + color: var(--mat-expansion-container-text-color); + box-sizing: border-box; + cursor: move; + padding: 20px 12px; } .order-item:not(:last-child) { - border-bottom: solid 1px var(--mat-table-row-item-outline-color); + border-bottom: solid 1px var(--mat-table-row-item-outline-color); } .cdk-drag-preview { - box-sizing: border-box; - border-radius: 4px; - box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2), - 0 8px 10px 1px rgba(0, 0, 0, 0.14), - 0 3px 14px 2px rgba(0, 0, 0, 0.12); + box-sizing: border-box; + border-radius: 4px; + box-shadow: + 0 5px 5px -3px rgba(0, 0, 0, 0.2), + 0 8px 10px 1px rgba(0, 0, 0, 0.14), + 0 3px 14px 2px rgba(0, 0, 0, 0.12); } .settings-form__tooltip { - margin-bottom: 20px; + margin-bottom: 20px; } .form-group-ordering { - display: flex; - align-items: center; - gap: 12px; + display: flex; + align-items: center; + gap: 12px; } @media (width <= 600px) { - .form-group-ordering { - flex-direction: column; - align-items: flex-start; - } + .form-group-ordering { + flex-direction: column; + align-items: flex-start; + } } .form-group-ordering__ordering-by { - width: 50% + width: 50%; } @media (width <= 600px) { - .form-group-ordering__ordering-by { - width: 100% - } + .form-group-ordering__ordering-by { + width: 100%; + } } .form-group-ordering__order-options { - flex-shrink: 0; - display: flex; - gap: 12px; - margin-top: -20px; + flex-shrink: 0; + display: flex; + gap: 12px; + margin-top: -20px; } .mat-form-field_excludedFields { - margin-top: 16px; - margin-bottom: 20px; + margin-top: 16px; + margin-bottom: 20px; } .actions { - position: fixed; - left: 0; - bottom: 0; - display: flex; - align-items: center; - justify-content: space-between; - background-color: var(--background-color); - box-shadow: var(--shadow); - height: 64px; - padding: 0 max(calc(50vw - 325px), 10%); - width: 100vw; - z-index: 2; + position: fixed; + left: 0; + bottom: 0; + display: flex; + align-items: center; + justify-content: space-between; + background-color: var(--background-color); + box-shadow: var(--shadow); + height: 64px; + padding: 0 max(calc(50vw - 325px), 10%); + width: 100vw; + z-index: 2; } @media (prefers-color-scheme: dark) { - .actions { - --shadow: 0 3px 1px -2px rgba(0,0,0,.5),0 2px 2px 0 rgba(0,0,0,.64),0 1px 5px 0 rgba(0,0,0,0.85); - --background-color: var(--surface-dark-color); - } + .actions { + --shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.5), 0 2px 2px 0 rgba(0, 0, 0, 0.64), 0 1px 5px 0 rgba(0, 0, 0, 0.85); + --background-color: var(--surface-dark-color); + } } @media (prefers-color-scheme: light) { - .actions { - --shadow: 0 3px 1px -2px rgba(0,0,0,.2),0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12); - --background-color: #fff; - } + .actions { + --shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12); + --background-color: #fff; + } } .settings-form__back-button { - margin-right: auto; + margin-right: auto; } .settings-form__save-button { - margin-left: 20px; -} \ No newline at end of file + margin-left: 20px; +} diff --git a/frontend/src/app/components/dashboard/db-table-view/db-table-settings/db-table-settings.component.spec.ts b/frontend/src/app/components/dashboard/db-table-view/db-table-settings/db-table-settings.component.spec.ts index 04d141a6b..2d5f6482e 100644 --- a/frontend/src/app/components/dashboard/db-table-view/db-table-settings/db-table-settings.component.spec.ts +++ b/frontend/src/app/components/dashboard/db-table-view/db-table-settings/db-table-settings.component.spec.ts @@ -81,7 +81,7 @@ describe('DbTableSettingsComponent', () => { excluded_fields: [], list_fields: [], ordering: TableOrdering.Ascending, - ordering_field: "", + ordering_field: '', columns_view: [], // identification_fields: [], // list_per_page: null, diff --git a/frontend/src/app/components/dashboard/db-table-view/db-table-view.component.css b/frontend/src/app/components/dashboard/db-table-view/db-table-view.component.css index 41247593d..13184e2a3 100644 --- a/frontend/src/app/components/dashboard/db-table-view/db-table-view.component.css +++ b/frontend/src/app/components/dashboard/db-table-view/db-table-view.component.css @@ -163,8 +163,8 @@ } .ai-insights-button { - order: 999; - margin-left: auto; + order: 999; + margin-left: auto; } @media (prefers-color-scheme: light) { diff --git a/frontend/src/app/components/dashboard/db-table-view/db-table-view.component.ts b/frontend/src/app/components/dashboard/db-table-view/db-table-view.component.ts index 4a81fc688..4bda5006e 100644 --- a/frontend/src/app/components/dashboard/db-table-view/db-table-view.component.ts +++ b/frontend/src/app/components/dashboard/db-table-view/db-table-view.component.ts @@ -309,7 +309,6 @@ export class DbTableViewComponent implements OnInit, OnChanges { return this.allTables.filter((t) => !tablesInFolders.has(t.table)); } - loadRowsPage() { this.tableRelatedRecords = null; this.tableData.fetchRows({ @@ -693,7 +692,6 @@ export class DbTableViewComponent implements OnInit, OnChanges { return folder.tables || []; } - onFilterSelected($event) { console.log('table view fiers filterSelected:', $event); this.applyFilter.emit($event); diff --git a/frontend/src/app/components/dashboard/db-table-view/db-table-widgets/db-table-widgets.component.css b/frontend/src/app/components/dashboard/db-table-view/db-table-widgets/db-table-widgets.component.css index 0a53f76b2..916c16fc9 100644 --- a/frontend/src/app/components/dashboard/db-table-view/db-table-widgets/db-table-widgets.component.css +++ b/frontend/src/app/components/dashboard/db-table-view/db-table-widgets/db-table-widgets.component.css @@ -1,73 +1,73 @@ :host app-alert:not(:empty) { - --alert-margin: 24px; + --alert-margin: 24px; } .wrapper { - min-width: 300px; - margin: 0 auto 16px; - width: 90%; + min-width: 300px; + margin: 0 auto 16px; + width: 90%; } .header { - display: flex; - justify-content: space-between; - margin: 2.5em 70px 24px 0; + display: flex; + justify-content: space-between; + margin: 2.5em 70px 24px 0; } .header-actions-box { - display: flex; - flex-direction: column; - align-items: flex-end; - gap: 16px; + display: flex; + flex-direction: column; + align-items: flex-end; + gap: 16px; } .header-actions { - display: flex; - gap: 16px; + display: flex; + gap: 16px; } .header-links { - display: flex; - gap: 16px; + display: flex; + gap: 16px; } .widget-settings { - display: grid; - grid-template-columns: minmax(10%, 130px) 1fr 2fr 1fr 2fr 50px; - align-items: start; - grid-gap: 0 20px; + display: grid; + grid-template-columns: minmax(10%, 130px) 1fr 2fr 1fr 2fr 50px; + align-items: start; + grid-gap: 0 20px; } .widget-item { - display: contents; + display: contents; } .actions { - position: fixed; - left: 0; - bottom: 0; - display: flex; - align-items: center; - justify-content: space-between; - background-color: var(--background-color); - box-shadow: var(--shadow); - height: 64px; - /* padding: 0 max(calc(50vw - 500px), 2%); */ - padding: 0 calc(5% + 70px) 0 5%; - width: 100vw; - z-index: 10; + position: fixed; + left: 0; + bottom: 0; + display: flex; + align-items: center; + justify-content: space-between; + background-color: var(--background-color); + box-shadow: var(--shadow); + height: 64px; + /* padding: 0 max(calc(50vw - 500px), 2%); */ + padding: 0 calc(5% + 70px) 0 5%; + width: 100vw; + z-index: 10; } @media (prefers-color-scheme: dark) { - .actions { - --shadow: 0 3px 1px -2px rgba(0,0,0,.5),0 2px 2px 0 rgba(0,0,0,.64),0 1px 5px 0 rgba(0,0,0,0.85); - --background-color: var(--surface-dark-color); - } + .actions { + --shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.5), 0 2px 2px 0 rgba(0, 0, 0, 0.64), 0 1px 5px 0 rgba(0, 0, 0, 0.85); + --background-color: var(--surface-dark-color); + } } @media (prefers-color-scheme: light) { - .actions { - --shadow: 0 3px 1px -2px rgba(0,0,0,.2),0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12); - --background-color: #fff; - } -} \ No newline at end of file + .actions { + --shadow: 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12); + --background-color: #fff; + } +} diff --git a/frontend/src/app/components/dashboard/db-table-view/db-table-widgets/db-table-widgets.component.ts b/frontend/src/app/components/dashboard/db-table-view/db-table-widgets/db-table-widgets.component.ts index 5fcd2280b..d839578f1 100644 --- a/frontend/src/app/components/dashboard/db-table-view/db-table-widgets/db-table-widgets.component.ts +++ b/frontend/src/app/components/dashboard/db-table-view/db-table-widgets/db-table-widgets.component.ts @@ -425,5 +425,4 @@ export class DbTableWidgetsComponent implements OnInit { }, ); } - } diff --git a/frontend/src/app/components/dashboard/db-table-view/db-table-widgets/widget-delete-dialog/widget-delete-dialog.component.spec.ts b/frontend/src/app/components/dashboard/db-table-view/db-table-widgets/widget-delete-dialog/widget-delete-dialog.component.spec.ts index c7049bf9c..b1ba868e1 100644 --- a/frontend/src/app/components/dashboard/db-table-view/db-table-widgets/widget-delete-dialog/widget-delete-dialog.component.spec.ts +++ b/frontend/src/app/components/dashboard/db-table-view/db-table-widgets/widget-delete-dialog/widget-delete-dialog.component.spec.ts @@ -1,31 +1,30 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { MatSnackBarModule } from '@angular/material/snack-bar'; import { WidgetDeleteDialogComponent } from './widget-delete-dialog.component'; describe('WidgetDeleteDialogComponent', () => { - let component: WidgetDeleteDialogComponent; - let fixture: ComponentFixture; + let component: WidgetDeleteDialogComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [MatSnackBarModule, WidgetDeleteDialogComponent], - providers: [ - { provide: MAT_DIALOG_DATA, useValue: {} }, - { provide: MatDialogRef, useValue: {} }, - ], -}) - .compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [MatSnackBarModule, WidgetDeleteDialogComponent], + providers: [ + { provide: MAT_DIALOG_DATA, useValue: {} }, + { provide: MatDialogRef, useValue: {} }, + ], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(WidgetDeleteDialogComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(WidgetDeleteDialogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/dashboard/db-table-view/db-table-widgets/widget-delete-dialog/widget-delete-dialog.component.ts b/frontend/src/app/components/dashboard/db-table-view/db-table-widgets/widget-delete-dialog/widget-delete-dialog.component.ts index 33ae6c6b2..cc4afe66b 100644 --- a/frontend/src/app/components/dashboard/db-table-view/db-table-widgets/widget-delete-dialog/widget-delete-dialog.component.ts +++ b/frontend/src/app/components/dashboard/db-table-view/db-table-widgets/widget-delete-dialog/widget-delete-dialog.component.ts @@ -1,21 +1,20 @@ -import { Component, Inject, OnInit } from '@angular/core'; -import { MatDialogRef, MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog'; import { CommonModule } from '@angular/common'; +import { Component, Inject, OnInit } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; +import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog'; import { MatIconModule } from '@angular/material/icon'; @Component({ - selector: 'app-widget-delete-dialog', - templateUrl: './widget-delete-dialog.component.html', - styleUrls: ['./widget-delete-dialog.component.css'], - imports: [CommonModule, MatButtonModule, MatIconModule, MatDialogModule] + selector: 'app-widget-delete-dialog', + templateUrl: './widget-delete-dialog.component.html', + styleUrls: ['./widget-delete-dialog.component.css'], + imports: [CommonModule, MatButtonModule, MatIconModule, MatDialogModule], }) export class WidgetDeleteDialogComponent implements OnInit { + constructor( + @Inject(MAT_DIALOG_DATA) public widgetFieldName: any, + _dialogRef: MatDialogRef, + ) {} - constructor( - @Inject(MAT_DIALOG_DATA) public widgetFieldName: any,_dialogRef: MatDialogRef - ) { } - - ngOnInit(): void { - } + ngOnInit(): void {} } diff --git a/frontend/src/app/components/dashboard/db-table-view/db-table-widgets/widget/widget.component.ts b/frontend/src/app/components/dashboard/db-table-view/db-table-widgets/widget/widget.component.ts index e89c706da..20dab57de 100644 --- a/frontend/src/app/components/dashboard/db-table-view/db-table-widgets/widget/widget.component.ts +++ b/frontend/src/app/components/dashboard/db-table-view/db-table-widgets/widget/widget.component.ts @@ -1,102 +1,110 @@ -import { CUSTOM_ELEMENTS_SCHEMA, Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges } from '@angular/core'; - -import { CodeEditorModule } from '@ngstack/code-editor'; import { CommonModule } from '@angular/common'; +import { + Component, + CUSTOM_ELEMENTS_SCHEMA, + EventEmitter, + Input, + OnChanges, + OnInit, + Output, + SimpleChanges, +} from '@angular/core'; import { FormsModule } from '@angular/forms'; import { MatButtonModule } from '@angular/material/button'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatIconModule } from '@angular/material/icon'; import { MatInputModule } from '@angular/material/input'; import { MatSelectModule } from '@angular/material/select'; +import { CodeEditorModule } from '@ngstack/code-editor'; import { Widget } from 'src/app/models/table'; @Component({ - selector: 'app-widget', - imports: [ - CommonModule, - FormsModule, - MatFormFieldModule, - MatInputModule, - MatSelectModule, - MatIconModule, - MatButtonModule, - CodeEditorModule - ], - templateUrl: './widget.component.html', - styleUrl: './widget.component.css', - schemas: [CUSTOM_ELEMENTS_SCHEMA] + selector: 'app-widget', + imports: [ + CommonModule, + FormsModule, + MatFormFieldModule, + MatInputModule, + MatSelectModule, + MatIconModule, + MatButtonModule, + CodeEditorModule, + ], + templateUrl: './widget.component.html', + styleUrl: './widget.component.css', + schemas: [CUSTOM_ELEMENTS_SCHEMA], }) export class WidgetComponent implements OnInit, OnChanges { - @Input() index: number; - @Input() widget: Widget; - @Input() widgetType: string; - @Input() fields: string[]; - @Input() widgetTypes: string[]; - @Input() isReadonlyParams: boolean; - @Input() codeEditorTheme: 'vs' | 'vs-dark'; + @Input() index: number; + @Input() widget: Widget; + @Input() widgetType: string; + @Input() fields: string[]; + @Input() widgetTypes: string[]; + @Input() isReadonlyParams: boolean; + @Input() codeEditorTheme: 'vs' | 'vs-dark'; - @Output() onSelectWidgetField = new EventEmitter(); - @Output() onWidgetTypeChange = new EventEmitter(); - @Output() onWidgetParamsChange = new EventEmitter<{ value: any, fieldName: string }>(); - @Output() onWidgetDelete = new EventEmitter(); + @Output() onSelectWidgetField = new EventEmitter(); + @Output() onWidgetTypeChange = new EventEmitter(); + @Output() onWidgetParamsChange = new EventEmitter<{ value: any; fieldName: string }>(); + @Output() onWidgetDelete = new EventEmitter(); - public mutableWidgetParams: { - language: string; - uri: string; - value: any; - } = { - language: 'json', - uri: '', - value: {} - }; + public mutableWidgetParams: { + language: string; + uri: string; + value: any; + } = { + language: 'json', + uri: '', + value: {}, + }; - public paramsEditorOptions = { - minimap: { enabled: false }, - lineNumbersMinChars: 3, - folding: false, - automaticLayout: true, - scrollBeyondLastLine: false, - wordWrap: 'on', - }; + public paramsEditorOptions = { + minimap: { enabled: false }, + lineNumbersMinChars: 3, + folding: false, + automaticLayout: true, + scrollBeyondLastLine: false, + wordWrap: 'on', + }; - public docsUrls = { - Boolean: 'https://docs.rocketadmin.com/Reference/UI%20Widgets/widgets_management#boolean', - Code: 'https://docs.rocketadmin.com/Reference/UI%20Widgets/widgets_management#code', - Color: 'https://docs.rocketadmin.com/Reference/UI%20Widgets/widgets_management#color', - Country: 'https://docs.rocketadmin.com/Reference/UI%20Widgets/widgets_management#country', - Foreign_key: 'https://docs.rocketadmin.com/Reference/UI%20Widgets/widgets_management#foreign-key', - Image: 'https://docs.rocketadmin.com/Reference/UI%20Widgets/widgets_management#image', - Money: 'https://docs.rocketadmin.com/Reference/UI%20Widgets/widgets_management#money', - Number: 'https://docs.rocketadmin.com/Reference/UI%20Widgets/widgets_management#number', - Password: 'https://docs.rocketadmin.com/Reference/UI%20Widgets/widgets_management#password', - Phone: 'https://docs.rocketadmin.com/Reference/UI%20Widgets/widgets_management#phone', - Range: 'https://docs.rocketadmin.com/Reference/UI%20Widgets/widgets_management#range', - Select: 'https://docs.rocketadmin.com/Reference/UI%20Widgets/widgets_management#select', - String: 'https://docs.rocketadmin.com/Reference/UI%20Widgets/widgets_management#string', - Textarea: 'https://docs.rocketadmin.com/Reference/UI%20Widgets/widgets_management#textarea', - URL: 'https://docs.rocketadmin.com/Reference/UI%20Widgets/widgets_management#url', - UUID: 'https://docs.rocketadmin.com/Reference/UI%20Widgets/widgets_management#uuid', - Date: 'https://docs.rocketadmin.com/Reference/UI%20Widgets/widgets_management#date-time-and-datetime', - DateTime: 'https://docs.rocketadmin.com/Reference/UI%20Widgets/widgets_management#date-time-and-datetime', - Time: 'https://docs.rocketadmin.com/Reference/UI%20Widgets/widgets_management#date-time-and-datetime', - S3: 'https://docs.rocketadmin.com/Reference/UI%20Widgets/widgets_management#s3', - }; + public docsUrls = { + Boolean: 'https://docs.rocketadmin.com/Reference/UI%20Widgets/widgets_management#boolean', + Code: 'https://docs.rocketadmin.com/Reference/UI%20Widgets/widgets_management#code', + Color: 'https://docs.rocketadmin.com/Reference/UI%20Widgets/widgets_management#color', + Country: 'https://docs.rocketadmin.com/Reference/UI%20Widgets/widgets_management#country', + Foreign_key: 'https://docs.rocketadmin.com/Reference/UI%20Widgets/widgets_management#foreign-key', + Image: 'https://docs.rocketadmin.com/Reference/UI%20Widgets/widgets_management#image', + Money: 'https://docs.rocketadmin.com/Reference/UI%20Widgets/widgets_management#money', + Number: 'https://docs.rocketadmin.com/Reference/UI%20Widgets/widgets_management#number', + Password: 'https://docs.rocketadmin.com/Reference/UI%20Widgets/widgets_management#password', + Phone: 'https://docs.rocketadmin.com/Reference/UI%20Widgets/widgets_management#phone', + Range: 'https://docs.rocketadmin.com/Reference/UI%20Widgets/widgets_management#range', + Select: 'https://docs.rocketadmin.com/Reference/UI%20Widgets/widgets_management#select', + String: 'https://docs.rocketadmin.com/Reference/UI%20Widgets/widgets_management#string', + Textarea: 'https://docs.rocketadmin.com/Reference/UI%20Widgets/widgets_management#textarea', + URL: 'https://docs.rocketadmin.com/Reference/UI%20Widgets/widgets_management#url', + UUID: 'https://docs.rocketadmin.com/Reference/UI%20Widgets/widgets_management#uuid', + Date: 'https://docs.rocketadmin.com/Reference/UI%20Widgets/widgets_management#date-time-and-datetime', + DateTime: 'https://docs.rocketadmin.com/Reference/UI%20Widgets/widgets_management#date-time-and-datetime', + Time: 'https://docs.rocketadmin.com/Reference/UI%20Widgets/widgets_management#date-time-and-datetime', + S3: 'https://docs.rocketadmin.com/Reference/UI%20Widgets/widgets_management#s3', + }; - ngOnInit(): void { - this.mutableWidgetParams = { - language: 'json', - uri: `widget-params-${this.index}.json`, - value: this.widget.widget_params - }; - } + ngOnInit(): void { + this.mutableWidgetParams = { + language: 'json', + uri: `widget-params-${this.index}.json`, + value: this.widget.widget_params, + }; + } - ngOnChanges(changes: SimpleChanges): void { - if (changes.widgetType?.currentValue) { - this.mutableWidgetParams = { - ...this.mutableWidgetParams, - value: this.widget.widget_params, - } - console.log(this.mutableWidgetParams); - } - } -}; + ngOnChanges(changes: SimpleChanges): void { + if (changes.widgetType?.currentValue) { + this.mutableWidgetParams = { + ...this.mutableWidgetParams, + value: this.widget.widget_params, + }; + console.log(this.mutableWidgetParams); + } + } +} diff --git a/frontend/src/app/components/dashboard/db-table-view/db-table-widgets/widgets-empty-state/widgets-empty-state.component.css b/frontend/src/app/components/dashboard/db-table-view/db-table-widgets/widgets-empty-state/widgets-empty-state.component.css index 7d9dd4dbd..847de74da 100644 --- a/frontend/src/app/components/dashboard/db-table-view/db-table-widgets/widgets-empty-state/widgets-empty-state.component.css +++ b/frontend/src/app/components/dashboard/db-table-view/db-table-widgets/widgets-empty-state/widgets-empty-state.component.css @@ -1,355 +1,359 @@ .empty-state { - display: flex; - align-items: stretch; - height: calc(100vh - 200px - 16px); - gap: 0; - max-width: 1200px; - margin: 0 auto; + display: flex; + align-items: stretch; + height: calc(100vh - 200px - 16px); + gap: 0; + max-width: 1200px; + margin: 0 auto; } .empty-left { - width: 420px; - flex-shrink: 0; - display: flex; - flex-direction: column; - justify-content: center; - padding: 32px 32px 32px 0; + width: 420px; + flex-shrink: 0; + display: flex; + flex-direction: column; + justify-content: center; + padding: 32px 32px 32px 0; } .empty-icon-box { - width: 40px; - height: 40px; - border-radius: 10px; - background: rgba(99, 102, 241, 0.1); - display: flex; - align-items: center; - justify-content: center; - margin-bottom: 16px; + width: 40px; + height: 40px; + border-radius: 10px; + background: rgba(99, 102, 241, 0.1); + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 16px; } .empty-icon { - color: var(--color-accentedPalette-500); - font-size: 22px; - width: 22px; - height: 22px; + color: var(--color-accentedPalette-500); + font-size: 22px; + width: 22px; + height: 22px; } .empty-title { - font-size: 20px; - font-weight: 600; - margin: 0 0 8px; - line-height: 1.3; + font-size: 20px; + font-weight: 600; + margin: 0 0 8px; + line-height: 1.3; } .empty-subtitle { - font-size: 14px; - font-weight: 300; - color: rgba(0, 0, 0, 0.64); - margin: 0 0 24px !important; - line-height: 1.5; + font-size: 14px; + font-weight: 300; + color: rgba(0, 0, 0, 0.64); + margin: 0 0 24px !important; + line-height: 1.5; } .empty-subtitle strong { - font-weight: 500; + font-weight: 500; } @media (prefers-color-scheme: dark) { - .empty-subtitle { - color: rgba(255, 255, 255, 0.64); - } + .empty-subtitle { + color: rgba(255, 255, 255, 0.64); + } } /* Demo animated table */ .demo-table { - border-radius: 8px; - overflow: hidden; - background: #1e1e1e; - border: 1px solid #2a2a2a; + border-radius: 8px; + overflow: hidden; + background: #1e1e1e; + border: 1px solid #2a2a2a; } .demo-header { - display: flex; - padding: 8px 14px; - border-bottom: 1px solid #2a2a2a; - font-size: 11px; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.05em; - color: rgba(255, 255, 255, 0.35); + display: flex; + padding: 8px 14px; + border-bottom: 1px solid #2a2a2a; + font-size: 11px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.05em; + color: rgba(255, 255, 255, 0.35); } .demo-header-col { - width: 110px; - flex-shrink: 0; + width: 110px; + flex-shrink: 0; } .demo-header-val { - flex: 1; + flex: 1; } .demo-row { - display: flex; - align-items: center; - padding: 10px 14px; - border-bottom: 1px solid #2a2a2a; - position: relative; + display: flex; + align-items: center; + padding: 10px 14px; + border-bottom: 1px solid #2a2a2a; + position: relative; } .demo-row--last { - border-bottom: none; + border-bottom: none; } .demo-col { - width: 110px; - flex-shrink: 0; - font-family: monospace; - font-size: 12px; - color: rgba(255, 255, 255, 0.5); + width: 110px; + flex-shrink: 0; + font-family: monospace; + font-size: 12px; + color: rgba(255, 255, 255, 0.5); } .demo-val-wrap { - flex: 1; - position: relative; - height: 20px; + flex: 1; + position: relative; + height: 20px; } .demo-raw { - position: absolute; - top: 0; - left: 0; - font-family: monospace; - font-size: 12px; - color: rgba(255, 255, 255, 0.4); - white-space: nowrap; - transition: opacity 0.35s, transform 0.35s; - line-height: 20px; + position: absolute; + top: 0; + left: 0; + font-family: monospace; + font-size: 12px; + color: rgba(255, 255, 255, 0.4); + white-space: nowrap; + transition: + opacity 0.35s, + transform 0.35s; + line-height: 20px; } .demo-widget { - position: absolute; - top: 0; - left: 0; - font-family: monospace; - font-size: 12px; - font-weight: 500; - white-space: nowrap; - display: flex; - align-items: center; - gap: 4px; - opacity: 0; - transform: translateY(6px); - transition: opacity 0.35s, transform 0.35s; - line-height: 20px; + position: absolute; + top: 0; + left: 0; + font-family: monospace; + font-size: 12px; + font-weight: 500; + white-space: nowrap; + display: flex; + align-items: center; + gap: 4px; + opacity: 0; + transform: translateY(6px); + transition: + opacity 0.35s, + transform 0.35s; + line-height: 20px; } .show-widget .demo-raw { - opacity: 0; - transform: translateY(-4px); + opacity: 0; + transform: translateY(-4px); } .show-widget .demo-widget { - opacity: 1; - transform: translateY(0); + opacity: 1; + transform: translateY(0); } .demo-widget--green { - color: #22c55e; + color: #22c55e; } .demo-widget--amber { - color: rgba(255, 255, 255, 0.85); + color: rgba(255, 255, 255, 0.85); } .demo-widget--purple { - color: #8b5cf6; + color: #8b5cf6; } .demo-widget--blue { - color: rgba(255, 255, 255, 0.85); + color: rgba(255, 255, 255, 0.85); } .demo-widget-icon { - font-size: 16px; - width: 16px; - height: 16px; + font-size: 16px; + width: 16px; + height: 16px; } .demo-widget-icon--green { - color: #22c55e; + color: #22c55e; } .demo-widget-icon--purple { - color: #8b5cf6; + color: #8b5cf6; } .demo-widget-icon--blue { - color: #3b82f6; + color: #3b82f6; } .widget-badge { - font-size: 10px; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.04em; - color: rgba(255, 255, 255, 0.3); - background: rgba(255, 255, 255, 0.06); - padding: 2px 8px; - border-radius: 4px; - opacity: 0; - transition: opacity 0.35s; - flex-shrink: 0; - margin-left: 8px; + font-size: 10px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.04em; + color: rgba(255, 255, 255, 0.3); + background: rgba(255, 255, 255, 0.06); + padding: 2px 8px; + border-radius: 4px; + opacity: 0; + transition: opacity 0.35s; + flex-shrink: 0; + margin-left: 8px; } .widget-badge--visible { - opacity: 1; + opacity: 1; } @media (prefers-color-scheme: light) { - .demo-table { - background: #f8f8f8; - border-color: rgba(0, 0, 0, 0.1); - } + .demo-table { + background: #f8f8f8; + border-color: rgba(0, 0, 0, 0.1); + } - .demo-header { - border-bottom-color: rgba(0, 0, 0, 0.1); - color: rgba(0, 0, 0, 0.4); - } + .demo-header { + border-bottom-color: rgba(0, 0, 0, 0.1); + color: rgba(0, 0, 0, 0.4); + } - .demo-row { - border-bottom-color: rgba(0, 0, 0, 0.08); - } + .demo-row { + border-bottom-color: rgba(0, 0, 0, 0.08); + } - .demo-col { - color: rgba(0, 0, 0, 0.5); - } + .demo-col { + color: rgba(0, 0, 0, 0.5); + } - .demo-raw { - color: rgba(0, 0, 0, 0.4); - } + .demo-raw { + color: rgba(0, 0, 0, 0.4); + } - .demo-widget--amber { - color: rgba(0, 0, 0, 0.85); - } + .demo-widget--amber { + color: rgba(0, 0, 0, 0.85); + } - .demo-widget--blue { - color: rgba(0, 0, 0, 0.85); - } + .demo-widget--blue { + color: rgba(0, 0, 0, 0.85); + } - .widget-badge { - color: rgba(0, 0, 0, 0.35); - background: rgba(0, 0, 0, 0.06); - } + .widget-badge { + color: rgba(0, 0, 0, 0.35); + background: rgba(0, 0, 0, 0.06); + } } .empty-cta { - margin-top: 24px; - display: flex; - align-items: center; - gap: 16px; + margin-top: 24px; + display: flex; + align-items: center; + gap: 16px; } .empty-docs-link { - display: inline-flex; - align-items: center; - gap: 4px; - font-size: 13px; - color: var(--color-accentedPalette-500); - text-decoration: none; + display: inline-flex; + align-items: center; + gap: 4px; + font-size: 13px; + color: var(--color-accentedPalette-500); + text-decoration: none; } .empty-docs-link:hover { - opacity: 0.8; + opacity: 0.8; } .empty-docs-icon { - font-size: 14px; - width: 14px; - height: 14px; + font-size: 14px; + width: 14px; + height: 14px; } .empty-divider { - width: 1px; - background: rgba(0, 0, 0, 0.1); - flex-shrink: 0; - align-self: center; - height: 60%; + width: 1px; + background: rgba(0, 0, 0, 0.1); + flex-shrink: 0; + align-self: center; + height: 60%; } @media (prefers-color-scheme: dark) { - .empty-divider { - background: rgba(255, 255, 255, 0.1); - } + .empty-divider { + background: rgba(255, 255, 255, 0.1); + } } .empty-right { - flex: 1; - padding: 32px; - overflow: auto; - display: flex; - flex-direction: column; - justify-content: center; + flex: 1; + padding: 32px; + overflow: auto; + display: flex; + flex-direction: column; + justify-content: center; } .widget-grid-label { - text-transform: uppercase; - font-size: 11px; - /* font-weight: 600; */ - letter-spacing: 0.08em; - color: rgba(0, 0, 0, 0.64); - margin-bottom: 16px; + text-transform: uppercase; + font-size: 11px; + /* font-weight: 600; */ + letter-spacing: 0.08em; + color: rgba(0, 0, 0, 0.64); + margin-bottom: 16px; } @media (prefers-color-scheme: dark) { - .widget-grid-label { - color: rgba(255, 255, 255, 0.64); - } + .widget-grid-label { + color: rgba(255, 255, 255, 0.64); + } } .widget-grid { - display: grid; - grid-template-columns: repeat(6, auto); - gap: 10px; + display: grid; + grid-template-columns: repeat(6, auto); + gap: 10px; } .widget-card { - display: flex; - flex-direction: column; - align-items: center; - padding: 10px 2px 8px; - border: 1px solid rgba(0, 0, 0, 0.08); - border-radius: 8px; - text-align: center; + display: flex; + flex-direction: column; + align-items: center; + padding: 10px 2px 8px; + border: 1px solid rgba(0, 0, 0, 0.08); + border-radius: 8px; + text-align: center; } @media (prefers-color-scheme: dark) { - .widget-card { - border-color: rgba(255, 255, 255, 0.08); - } + .widget-card { + border-color: rgba(255, 255, 255, 0.08); + } } .widget-card-icon { - font-size: 24px; - width: 24px; - height: 24px; - color: var(--color-accentedPalette-500); - margin-bottom: 6px; + font-size: 24px; + width: 24px; + height: 24px; + color: var(--color-accentedPalette-500); + margin-bottom: 6px; } .widget-card-name { - font-size: 12px; - font-weight: 500; - line-height: 1.3; + font-size: 12px; + font-weight: 500; + line-height: 1.3; } .widget-card-desc { - font-size: 11px; - color: rgba(0, 0, 0, 0.54); - line-height: 1.3; + font-size: 11px; + color: rgba(0, 0, 0, 0.54); + line-height: 1.3; } @media (prefers-color-scheme: dark) { - .widget-card-desc { - color: rgba(255, 255, 255, 0.54); - } + .widget-card-desc { + color: rgba(255, 255, 255, 0.54); + } } diff --git a/frontend/src/app/components/dashboard/db-table-view/db-table-widgets/widgets-empty-state/widgets-empty-state.component.ts b/frontend/src/app/components/dashboard/db-table-view/db-table-widgets/widgets-empty-state/widgets-empty-state.component.ts index 2540c2da1..30bd22d35 100644 --- a/frontend/src/app/components/dashboard/db-table-view/db-table-widgets/widgets-empty-state/widgets-empty-state.component.ts +++ b/frontend/src/app/components/dashboard/db-table-view/db-table-widgets/widgets-empty-state/widgets-empty-state.component.ts @@ -9,12 +9,7 @@ import posthog from 'posthog-js'; selector: 'app-widgets-empty-state', templateUrl: './widgets-empty-state.component.html', styleUrls: ['./widgets-empty-state.component.css'], - imports: [ - CommonModule, - MatButtonModule, - MatIconModule, - Angulartics2OnModule, - ], + imports: [CommonModule, MatButtonModule, MatIconModule, Angulartics2OnModule], }) export class WidgetsEmptyStateComponent implements OnInit, OnDestroy { protected posthog = posthog; @@ -48,15 +43,18 @@ export class WidgetsEmptyStateComponent implements OnInit, OnDestroy { } this._animationTimers.push( - setTimeout(() => { - this.animatedRows = [false, false, false, false]; + setTimeout( + () => { + this.animatedRows = [false, false, false, false]; - this._animationTimers.push( - setTimeout(() => { - this._runAnimationCycle(); - }, 800), - ); - }, 3 * 600 + 2200), + this._animationTimers.push( + setTimeout(() => { + this._runAnimationCycle(); + }, 800), + ); + }, + 3 * 600 + 2200, + ), ); } diff --git a/frontend/src/app/components/dashboard/db-table-view/saved-filters-panel/saved-filters-dialog/saved-filters-dialog.component.css b/frontend/src/app/components/dashboard/db-table-view/saved-filters-panel/saved-filters-dialog/saved-filters-dialog.component.css index 9824b85cb..7fe705b1a 100644 --- a/frontend/src/app/components/dashboard/db-table-view/saved-filters-panel/saved-filters-dialog/saved-filters-dialog.component.css +++ b/frontend/src/app/components/dashboard/db-table-view/saved-filters-panel/saved-filters-dialog/saved-filters-dialog.component.css @@ -1,690 +1,705 @@ .filters-content { - display: grid; - grid-template-columns: 32px auto 228px 0 1fr 100px; - grid-column-gap: 8px; - align-content: flex-start; - align-items: center; - padding-top: 4px !important; + display: grid; + grid-template-columns: 32px auto 228px 0 1fr 100px; + grid-column-gap: 8px; + align-content: flex-start; + align-items: center; + padding-top: 4px !important; } ::ng-deep mat-dialog-content.filters-content { - padding-left: 24px !important; - padding-right: 24px !important; + padding-left: 24px !important; + padding-right: 24px !important; } .filters-select { - grid-column: 2 / span 5; - margin-bottom: 16px; + grid-column: 2 / span 5; + margin-bottom: 16px; } .filter-line { - grid-column: 2 / span 4; + grid-column: 2 / span 4; } .empty-conditions-container { - grid-column: 1 / span 5; - display: flex; - align-items: center; - gap: 12px; - margin-top: 8px; - margin-bottom: 16px; + grid-column: 1 / span 5; + display: flex; + align-items: center; + gap: 12px; + margin-top: 8px; + margin-bottom: 16px; } .add-condition-container { - grid-column: 1 / span 6; - display: flex; - align-items: center; - gap: 12px; - margin-top: 8px; - margin-bottom: 16px; + grid-column: 1 / span 6; + display: flex; + align-items: center; + gap: 12px; + margin-top: 8px; + margin-bottom: 16px; } .where-label { - font-weight: 500; - color: rgba(0, 0, 0, 0.87); - white-space: nowrap; + font-weight: 500; + color: rgba(0, 0, 0, 0.87); + white-space: nowrap; } @media (prefers-color-scheme: dark) { - .where-label { - color: rgba(255, 255, 255, 0.87); - } + .where-label { + color: rgba(255, 255, 255, 0.87); + } } .empty-conditions-container .column-name-icon { - font-size: 18px; - width: 18px; - height: 18px; - vertical-align: middle; + font-size: 18px; + width: 18px; + height: 18px; + vertical-align: middle; } .empty-condition-input { - width: auto; - min-width: 200px; - max-width: 300px; - margin: 0; - flex-shrink: 0; + width: auto; + min-width: 200px; + max-width: 300px; + margin: 0; + flex-shrink: 0; } .empty-condition-input ::ng-deep .mat-mdc-form-field-infix { - min-height: 36px; - padding-top: 8px; - padding-bottom: 8px; - display: flex; - align-items: center; + min-height: 36px; + padding-top: 8px; + padding-bottom: 8px; + display: flex; + align-items: center; } .empty-condition-input ::ng-deep .mat-mdc-form-field { - margin: 0; + margin: 0; } .empty-condition-input ::ng-deep .mat-mdc-text-field-wrapper { - padding-bottom: 0; - margin-bottom: 0; + padding-bottom: 0; + margin-bottom: 0; } .empty-condition-input ::ng-deep .mat-mdc-form-field-subscript-wrapper { - margin-top: 0; - position: absolute; - top: 100%; + margin-top: 0; + position: absolute; + top: 100%; } .quick-edit-header { - grid-column: 6; - display: flex; - align-items: center; - gap: 4px; - justify-content: center; - margin-top: 8px; - margin-bottom: 16px; + grid-column: 6; + display: flex; + align-items: center; + gap: 4px; + justify-content: center; + margin-top: 8px; + margin-bottom: 16px; } .quick-edit-title { - font-size: 11px; - font-weight: 500; - text-transform: uppercase; - letter-spacing: 0.5px; - color: rgba(0, 0, 0, 0.5); - white-space: nowrap; + font-size: 11px; + font-weight: 500; + text-transform: uppercase; + letter-spacing: 0.5px; + color: rgba(0, 0, 0, 0.5); + white-space: nowrap; } .quick-edit-info-icon { - font-size: 14px; - width: 14px; - height: 14px; - color: rgba(0, 0, 0, 0.35); - cursor: pointer; + font-size: 14px; + width: 14px; + height: 14px; + color: rgba(0, 0, 0, 0.35); + cursor: pointer; } /* Quick edit help menu styles */ ::ng-deep .quick-edit-help-menu { - max-width: none !important; + max-width: none !important; } .quick-edit-help-content { - padding: 16px; - cursor: default; + padding: 16px; + cursor: default; } .quick-edit-help-text { - margin: 0 0 12px 0; - font-size: 13px; - color: rgba(0, 0, 0, 0.7); - line-height: 1.4; + margin: 0 0 12px 0; + font-size: 13px; + color: rgba(0, 0, 0, 0.7); + line-height: 1.4; } /* Animated example */ .quick-edit-animated-example { - display: flex; - align-items: center; - gap: 16px; + display: flex; + align-items: center; + gap: 16px; } .example-toggle-animated { - flex-shrink: 0; + flex-shrink: 0; } .toggle-track { - width: 36px; - height: 20px; - border-radius: 10px; - background-color: rgba(0, 0, 0, 0.25); - position: relative; - animation: toggleTrack 3s ease-in-out infinite; + width: 36px; + height: 20px; + border-radius: 10px; + background-color: rgba(0, 0, 0, 0.25); + position: relative; + animation: toggleTrack 3s ease-in-out infinite; } .toggle-thumb { - width: 16px; - height: 16px; - border-radius: 50%; - background-color: #fff; - position: absolute; - top: 2px; - left: 2px; - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3); - animation: toggleThumb 3s ease-in-out infinite; + width: 16px; + height: 16px; + border-radius: 50%; + background-color: #fff; + position: absolute; + top: 2px; + left: 2px; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3); + animation: toggleThumb 3s ease-in-out infinite; } @keyframes toggleTrack { - 0%, 40% { - background-color: rgba(0, 0, 0, 0.25); - } - 50%, 90% { - background-color: #C177FC; - } - 100% { - background-color: rgba(0, 0, 0, 0.25); - } + 0%, + 40% { + background-color: rgba(0, 0, 0, 0.25); + } + 50%, + 90% { + background-color: #c177fc; + } + 100% { + background-color: rgba(0, 0, 0, 0.25); + } } @keyframes toggleThumb { - 0%, 40% { - left: 2px; - } - 50%, 90% { - left: 18px; - } - 100% { - left: 2px; - } + 0%, + 40% { + left: 2px; + } + 50%, + 90% { + left: 18px; + } + 100% { + left: 2px; + } } .example-filter-animated { - position: relative; - height: 36px; - min-width: 110px; - margin-top: 4px; + position: relative; + height: 36px; + min-width: 110px; + margin-top: 4px; } .example-chip-animated { - position: absolute; - top: 0; - left: 0; - background-color: rgba(0, 0, 0, 0.08); - border-radius: 16px; - padding: 5px 12px; - font-size: 12px; - color: rgba(0, 0, 0, 0.7); - white-space: nowrap; - animation: chipFade 3s ease-in-out infinite; + position: absolute; + top: 0; + left: 0; + background-color: rgba(0, 0, 0, 0.08); + border-radius: 16px; + padding: 5px 12px; + font-size: 12px; + color: rgba(0, 0, 0, 0.7); + white-space: nowrap; + animation: chipFade 3s ease-in-out infinite; } .example-editable-animated { - position: absolute; - top: 0; - left: 0; - display: flex; - align-items: center; - gap: 4px; - background-color: transparent; - border: 1px solid #C177FC; - border-radius: 4px; - padding: 8px 8px 4px 8px; - opacity: 0; - animation: editableFade 3s ease-in-out infinite; + position: absolute; + top: 0; + left: 0; + display: flex; + align-items: center; + gap: 4px; + background-color: transparent; + border: 1px solid #c177fc; + border-radius: 4px; + padding: 8px 8px 4px 8px; + opacity: 0; + animation: editableFade 3s ease-in-out infinite; } .example-editable-label { - position: absolute; - top: -8px; - left: 8px; - font-size: 10px; - color: #C177FC; - background-color: #fff; - padding: 0 4px; + position: absolute; + top: -8px; + left: 8px; + font-size: 10px; + color: #c177fc; + background-color: #fff; + padding: 0 4px; } .example-editable-value { - font-size: 12px; - color: rgba(0, 0, 0, 0.87); - display: flex; - align-items: center; + font-size: 12px; + color: rgba(0, 0, 0, 0.87); + display: flex; + align-items: center; } .typing-text::after { - content: 'active'; - animation: typeText 3s ease-in-out infinite; + content: "active"; + animation: typeText 3s ease-in-out infinite; } @keyframes typeText { - 0%, 40% { - content: ''; - } - 45% { - content: 'p'; - } - 50% { - content: 'pe'; - } - 55% { - content: 'pen'; - } - 60% { - content: 'pend'; - } - 65% { - content: 'pendi'; - } - 70%, 90% { - content: 'pending'; - } - 100% { - content: ''; - } + 0%, + 40% { + content: ""; + } + 45% { + content: "p"; + } + 50% { + content: "pe"; + } + 55% { + content: "pen"; + } + 60% { + content: "pend"; + } + 65% { + content: "pendi"; + } + 70%, + 90% { + content: "pending"; + } + 100% { + content: ""; + } } .example-typing-cursor { - font-size: 12px; - color: #C177FC; - font-weight: 300; - animation: cursorBlink 0.5s step-end infinite; + font-size: 12px; + color: #c177fc; + font-weight: 300; + animation: cursorBlink 0.5s step-end infinite; } @keyframes cursorBlink { - 0%, 50% { - opacity: 1; - } - 51%, 100% { - opacity: 0; - } + 0%, + 50% { + opacity: 1; + } + 51%, + 100% { + opacity: 0; + } } @keyframes chipFade { - 0%, 40% { - opacity: 1; - } - 50%, 90% { - opacity: 0; - } - 100% { - opacity: 1; - } + 0%, + 40% { + opacity: 1; + } + 50%, + 90% { + opacity: 0; + } + 100% { + opacity: 1; + } } @keyframes editableFade { - 0%, 40% { - opacity: 0; - } - 50%, 90% { - opacity: 1; - } - 100% { - opacity: 0; - } + 0%, + 40% { + opacity: 0; + } + 50%, + 90% { + opacity: 1; + } + 100% { + opacity: 0; + } } @media (prefers-color-scheme: dark) { - .quick-edit-help-text { - color: rgba(255, 255, 255, 0.7); - } - - .toggle-track { - animation: toggleTrackDark 3s ease-in-out infinite; - } - - @keyframes toggleTrackDark { - 0%, 40% { - background-color: rgba(255, 255, 255, 0.3); - } - 50%, 90% { - background-color: #C177FC; - } - 100% { - background-color: rgba(255, 255, 255, 0.3); - } - } - - .example-chip-animated { - background-color: rgba(255, 255, 255, 0.12); - color: rgba(255, 255, 255, 0.7); - } - - .example-editable-animated { - background-color: transparent; - } - - .example-editable-label { - color: #C177FC; - background-color: #424242; - } - - .example-editable-value { - color: rgba(255, 255, 255, 0.87); - } - - .example-typing-cursor { - color: #C177FC; - } + .quick-edit-help-text { + color: rgba(255, 255, 255, 0.7); + } + + .toggle-track { + animation: toggleTrackDark 3s ease-in-out infinite; + } + + @keyframes toggleTrackDark { + 0%, + 40% { + background-color: rgba(255, 255, 255, 0.3); + } + 50%, + 90% { + background-color: #c177fc; + } + 100% { + background-color: rgba(255, 255, 255, 0.3); + } + } + + .example-chip-animated { + background-color: rgba(255, 255, 255, 0.12); + color: rgba(255, 255, 255, 0.7); + } + + .example-editable-animated { + background-color: transparent; + } + + .example-editable-label { + color: #c177fc; + background-color: #424242; + } + + .example-editable-value { + color: rgba(255, 255, 255, 0.87); + } + + .example-typing-cursor { + color: #c177fc; + } } .quick-edit-toggle { - grid-column: 6; - margin-top: -16px; - align-self: center; - justify-self: center; + grid-column: 6; + margin-top: -16px; + align-self: center; + justify-self: center; } /* Hide default icons */ .quick-edit-toggle ::ng-deep .mdc-switch__icons { - display: none; + display: none; } /* Style the thumb to contain our icon */ .quick-edit-toggle ::ng-deep .mdc-switch__handle { - display: flex; - align-items: center; - justify-content: center; + display: flex; + align-items: center; + justify-content: center; } /* Add lock icon when unchecked */ .quick-edit-toggle ::ng-deep .mdc-switch__handle::after { - font-family: 'Material Symbols Outlined'; - content: 'lock'; - font-size: 14px; - position: absolute; - color: #fff; + font-family: "Material Symbols Outlined"; + content: "lock"; + font-size: 14px; + position: absolute; + color: #fff; } /* Add edit icon when checked */ .quick-edit-toggle.mat-mdc-slide-toggle-checked ::ng-deep .mdc-switch__handle::after { - font-family: 'Material Symbols Outlined'; - content: 'edit'; - font-size: 14px; - color: #fff; + font-family: "Material Symbols Outlined"; + content: "edit"; + font-size: 14px; + color: #fff; } @media (prefers-color-scheme: dark) { - .quick-edit-title { - color: rgba(255, 255, 255, 0.5); - } + .quick-edit-title { + color: rgba(255, 255, 255, 0.5); + } - .quick-edit-info-icon { - color: rgba(255, 255, 255, 0.35); - } + .quick-edit-info-icon { + color: rgba(255, 255, 255, 0.35); + } } .comparator-select-field { - grid-column: 3; + grid-column: 3; } .add-condition-footer { - grid-column: 1 / span 6; - margin-top: 24px; - display: flex; - flex-direction: column; - justify-content: flex-start; - align-items: flex-start; - gap: 16px; + grid-column: 1 / span 6; + margin-top: 24px; + display: flex; + flex-direction: column; + justify-content: flex-start; + align-items: flex-start; + gap: 16px; } .add-condition-footer button { - width: auto; - min-width: auto; - min-height: 36px; - height: 36px; - display: flex; - align-items: center; - justify-content: center; - text-transform: none; - margin: 0 0 16px 0 !important; - padding: 0 16px; + width: auto; + min-width: auto; + min-height: 36px; + height: 36px; + display: flex; + align-items: center; + justify-content: center; + text-transform: none; + margin: 0 0 16px 0 !important; + padding: 0 16px; } .add-condition-footer .filters-select { - width: auto; - min-width: 200px; - max-width: 300px; - margin: 0 !important; - min-height: 36px; - margin-top: 0 !important; - flex-shrink: 0; + width: auto; + min-width: 200px; + max-width: 300px; + margin: 0 !important; + min-height: 36px; + margin-top: 0 !important; + flex-shrink: 0; } .add-condition-footer .filters-select ::ng-deep .mat-mdc-form-field-infix { - min-height: 36px; - padding-top: 8px; - padding-bottom: 8px; - display: flex; - align-items: center; + min-height: 36px; + padding-top: 8px; + padding-bottom: 8px; + display: flex; + align-items: center; } .add-condition-footer .filters-select ::ng-deep .mat-mdc-text-field-wrapper { - padding-bottom: 0; - margin-bottom: 0; + padding-bottom: 0; + margin-bottom: 0; } .add-condition-footer ::ng-deep .mat-mdc-form-field { - margin-top: 0 !important; - margin-bottom: 0 !important; - margin-left: 0 !important; - margin-right: 0 !important; + margin-top: 0 !important; + margin-bottom: 0 !important; + margin-left: 0 !important; + margin-right: 0 !important; } .add-condition-footer ::ng-deep .mat-mdc-form-field-wrapper { - padding-bottom: 0; + padding-bottom: 0; } - .filter-save-form { - grid-column: 1 / span 6; - margin-bottom: 24px; + grid-column: 1 / span 6; + margin-bottom: 24px; } .section-title { - grid-column: 1 / span 6; - margin: 16px 0 8px 0; + grid-column: 1 / span 6; + margin: 16px 0 8px 0; - font-weight: 500; + font-weight: 500; } @media (prefers-color-scheme: light) { - .section-title { - color: rgba(0, 0, 0, 0.87); - } + .section-title { + color: rgba(0, 0, 0, 0.87); + } } @media (prefers-color-scheme: dark) { - .section-title { - color: rgba(255, 255, 255, 0.87); - } + .section-title { + color: rgba(255, 255, 255, 0.87); + } } .section-description { - grid-column: 1 / span 6; - margin: 0 0 16px 0; - font-size: 14px; - white-space: nowrap; + grid-column: 1 / span 6; + margin: 0 0 16px 0; + font-size: 14px; + white-space: nowrap; } @media (prefers-color-scheme: light) { - .section-description { - color: rgba(0, 0, 0, 0.6); - } + .section-description { + color: rgba(0, 0, 0, 0.6); + } } @media (prefers-color-scheme: dark) { - .section-description { - color: rgba(255, 255, 255, 0.6); - } + .section-description { + color: rgba(255, 255, 255, 0.6); + } } .full-width { - grid-column: 1 / -1; + grid-column: 1 / -1; } .full-width.filter-name-field { - max-width: 400px; + max-width: 400px; } .default-filter-checkbox { - margin-bottom: 16px; + margin-bottom: 16px; } ::ng-deep .mat-dialog-container > .ng-star-inserted { - display: flex; - flex-direction: column; - width: 100%; + display: flex; + flex-direction: column; + width: 100%; } .filters-form { - flex: 1 0 auto; - display: flex; - flex-direction: column; + flex: 1 0 auto; + display: flex; + flex-direction: column; } .filters-header-description { - margin: 0 24px 16px 24px; - font-size: 14px; - line-height: 1.5; + margin: 0 24px 16px 24px; + font-size: 14px; + line-height: 1.5; } @media (prefers-color-scheme: light) { - .filters-header-description { - color: rgba(0, 0, 0, 0.6); - } + .filters-header-description { + color: rgba(0, 0, 0, 0.6); + } } @media (prefers-color-scheme: dark) { - .filters-header-description { - color: rgba(255, 255, 255, 0.6); - } + .filters-header-description { + color: rgba(255, 255, 255, 0.6); + } } ::ng-deep .mat-dialog-container { - display: flex; + display: flex; } .column-name { - grid-column: 2; - margin-top: -8px; - align-self: center; + grid-column: 2; + margin-top: -8px; + align-self: center; } .conditions-vertical-line { - grid-column: 1; - grid-row: 2 / -1; - width: 1px; - background-color: rgba(0, 0, 0, 0.12); - margin-left: 9px; - margin-top: 0; - margin-bottom: 0; - z-index: 0; + grid-column: 1; + grid-row: 2 / -1; + width: 1px; + background-color: rgba(0, 0, 0, 0.12); + margin-left: 9px; + margin-top: 0; + margin-bottom: 0; + z-index: 0; } @media (prefers-color-scheme: dark) { - .conditions-vertical-line { - background-color: rgba(255, 255, 255, 0.12); - } + .conditions-vertical-line { + background-color: rgba(255, 255, 255, 0.12); + } } .filter-delete-button { - grid-column: 1; - margin-top: -20px; - margin-right: 0; - margin-left: 0; - align-self: center; - justify-self: center; - position: relative; - z-index: 1; - width: 18px; - height: 18px; - display: flex; - align-items: center; - justify-content: center; + grid-column: 1; + margin-top: -20px; + margin-right: 0; + margin-left: 0; + align-self: center; + justify-self: center; + position: relative; + z-index: 1; + width: 18px; + height: 18px; + display: flex; + align-items: center; + justify-content: center; } .filter-delete-button ::ng-deep .mat-icon { - font-size: 18px; - width: 18px; - height: 18px; - line-height: 18px; + font-size: 18px; + width: 18px; + height: 18px; + line-height: 18px; } .settings-form__reset-button { - margin-right: auto; + margin-right: auto; } .no-filters-message { - grid-column: 1 / span 6; - color: rgba(0, 0, 0, 0.6); - font-style: italic; - margin: 16px 0; + grid-column: 1 / span 6; + color: rgba(0, 0, 0, 0.6); + font-style: italic; + margin: 16px 0; } .comparator-icon { - font-size: 18px; - width: 18px; - height: 18px; - margin-right: 8px; - vertical-align: middle; + font-size: 18px; + width: 18px; + height: 18px; + margin-right: 8px; + vertical-align: middle; } ::ng-deep .mat-mdc-select-panel .mat-mdc-option { - display: flex; - align-items: center; + display: flex; + align-items: center; } .comparator-select-field { - width: auto; - min-width: 140px; - max-width: none; + width: auto; + min-width: 140px; + max-width: none; } .comparator-select-field ::ng-deep .mat-mdc-form-field-infix { - width: 100% !important; - min-width: fit-content; - padding-left: 0; - position: relative; + width: 100% !important; + min-width: fit-content; + padding-left: 0; + position: relative; } .comparator-select-field ::ng-deep .mat-mdc-select-trigger { - width: 100%; - min-width: fit-content; - display: flex; - justify-content: space-between; - align-items: center; - position: relative; + width: 100%; + min-width: fit-content; + display: flex; + justify-content: space-between; + align-items: center; + position: relative; } .comparator-select-field ::ng-deep .mat-mdc-select-value-text { - width: auto; - min-width: fit-content; - flex: 1; + width: auto; + min-width: fit-content; + flex: 1; } .comparator-select-field ::ng-deep .mat-mdc-select-arrow-wrapper { - position: absolute; - right: 12px; - top: 50%; - transform: translateY(-50%); - pointer-events: none; + position: absolute; + right: 12px; + top: 50%; + transform: translateY(-50%); + pointer-events: none; } .comparator-select-field ::ng-deep .mat-mdc-select-panel { - min-width: max-content !important; - width: max-content !important; + min-width: max-content !important; + width: max-content !important; } .conditions-error-message { - grid-column: 1 / span 6; - color: #f44336; - font-size: 12px; - margin: 8px 0 0 0; - padding-left: 16px; + grid-column: 1 / span 6; + color: #f44336; + font-size: 12px; + margin: 8px 0 0 0; + padding-left: 16px; } /* Add more spacing for multiline textarea inputs (more than 2 rows) */ -.filters-content ::ng-deep .filter-line mat-form-field:has(textarea[rows]):not(:has(textarea[rows="1"])):not(:has(textarea[rows="2"])), +.filters-content ::ng-deep + .filter-line + mat-form-field:has(textarea[rows]):not(:has(textarea[rows="1"])):not(:has(textarea[rows="2"])), .filters-content ::ng-deep .filter-line mat-form-field:has(textarea.long-textarea), .filters-content ::ng-deep .filter-line mat-form-field:has(textarea.form-textarea) { - margin-top: 24px !important; - margin-bottom: 24px !important; + margin-top: 24px !important; + margin-bottom: 24px !important; } /* Add more spacing for foreign key fields */ .filters-content ::ng-deep .filter-line .foreign-key, .filters-content ::ng-deep .filter-line app-edit-foreign-key, .filters-content ::ng-deep .foreign-key { - margin-top: 24px !important; - margin-bottom: 24px !important; + margin-top: 24px !important; + margin-bottom: 24px !important; } diff --git a/frontend/src/app/components/dashboard/db-tables-data-source.ts b/frontend/src/app/components/dashboard/db-tables-data-source.ts index c12d09816..910949816 100644 --- a/frontend/src/app/components/dashboard/db-tables-data-source.ts +++ b/frontend/src/app/components/dashboard/db-tables-data-source.ts @@ -6,6 +6,7 @@ import { filter } from 'lodash-es'; import { BehaviorSubject, Observable, of } from 'rxjs'; import { catchError, finalize } from 'rxjs/operators'; import { formatFieldValue } from 'src/app/lib/format-field-value'; +import { normalizeFieldName } from 'src/app/lib/normalize'; import { getTableTypes } from 'src/app/lib/setup-table-row-structure'; import { Alert, AlertActionType, AlertType } from 'src/app/models/alert'; import { CustomAction, CustomActionType, CustomEvent, TableField, TableForeignKey, Widget } from 'src/app/models/table'; @@ -13,7 +14,6 @@ import { AccessLevel } from 'src/app/models/user'; import { ConnectionsService } from 'src/app/services/connections.service'; import { TableRowService } from 'src/app/services/table-row.service'; import { TablesService } from 'src/app/services/tables.service'; -import { normalizeFieldName } from 'src/app/lib/normalize'; interface Column { title: string; @@ -22,336 +22,367 @@ interface Column { } interface RowsParams { - connectionID: string, - tableName: string, - sortColumn?: string, - requstedPage?: number, - pageSize?: number, - sortOrder?: 'ASC' | 'DESC', - filters?: object, - comparators?: object, - search?: string, - isTablePageSwitched?: boolean + connectionID: string; + tableName: string; + sortColumn?: string; + requstedPage?: number; + pageSize?: number; + sortOrder?: 'ASC' | 'DESC'; + filters?: object; + comparators?: object; + search?: string; + isTablePageSwitched?: boolean; } export class TablesDataSource implements DataSource { - - private rowsSubject = new BehaviorSubject([]); - private loadingSubject = new BehaviorSubject(false); - - public loading$ = this.loadingSubject.asObservable(); - public paginator: MatPaginator; - - public structure; - public keyAttributes; - public columns: Column[] = []; - public dataColumns: string[]; - public dataNormalizedColumns: object; - public displayedColumns: string[]; - public displayedDataColumns: string[]; - public sortByColumns: string[]; - public defaultSort: { - column: string; - direction: 'asc' | 'desc'; - } | null; - public foreignKeysList: string[] = []; - public foreignKeys: TableForeignKey[] = []; - public widgetsList: string[]; - public widgets: { - [key: string]: Widget & { - widget_params: object; - } - } = {}; - public widgetsCount: number = 0; - public selectWidgetsOptions: object; - public tableTypes: object = {}; - public relatedRecords = { - referenced_on_column_name: '', - referenced_by: [] - }; - public permissions; - public isExportAllowed: boolean; - public isImportAllowed: boolean; - public canDelete: boolean; - public isEmptyTable: boolean; - public tableActions: CustomAction[]; - public tableBulkActions: CustomAction[]; - public actionsColumnWidth: string; - public largeDataset: boolean; - public identityColumn: string; - - public alert_primaryKeysInfo: Alert; - public alert_settingsInfo: Alert; - public alert_widgetsWarning: Alert; - - constructor( - private _tables: TablesService, - private _connections: ConnectionsService, - private _tableRow: TableRowService, - ) {} - - connect(_collectionViewer: CollectionViewer): Observable { - return this.rowsSubject.asObservable(); - } - - disconnect(_collectionViewer: CollectionViewer): void { - this.rowsSubject.complete(); - this.loadingSubject.complete(); - } - - formatRow(row, columns) { - const rowToFormat = {}; - for (const [columnName, columnStructute] of columns) { - let type = ''; - if (['number', 'tinyint'].includes(columnStructute.data_type) && (columnStructute.character_maximum_length === 1)) { - type = 'boolean' - } else type = columnStructute.data_type; - rowToFormat[columnName] = formatFieldValue(row[columnName], type); - } - return rowToFormat; - } - - fetchRows({ - connectionID, - tableName, - requstedPage, - pageSize, - sortColumn, - sortOrder, - filters, comparators, - search, - isTablePageSwitched - }: RowsParams) { - this.loadingSubject.next(true); - this.alert_primaryKeysInfo = null; - this.alert_settingsInfo = null; - this.alert_widgetsWarning = null; - - const fetchedTable = this._tables.fetchTable({ - connectionID, - tableName, - requstedPage: requstedPage + 1 || 1, - // chunkSize: this.paginator?.pageSize || 30, - chunkSize: pageSize || 30, - sortColumn, - sortOrder, - filters, - comparators, - search - }); - - if (fetchedTable) { - fetchedTable - .pipe( - catchError(() => of([])), - finalize(() => this.loadingSubject.next(false)) - ) - .subscribe((res: any) => { - if (res.rows?.length) { - const firstRow = res.rows[0]; - - this.foreignKeysList = res.foreignKeys.map((field) => {return field.column_name}); - this.foreignKeys = Object.assign({}, ...res.foreignKeys.map((foreignKey: TableForeignKey) => ({[foreignKey.column_name]: foreignKey}))); - - this._tableRow.fetchTableRow( - connectionID, - tableName, - res.primaryColumns.reduce((keys, column) => { - if (this.foreignKeysList.includes(column.column_name)) { - const referencedColumnNameOfForeignKey = this.foreignKeys[column.column_name].referenced_column_name; - keys[column.column_name] = firstRow[column.column_name][referencedColumnNameOfForeignKey]; - } else { - keys[column.column_name] = firstRow[column.column_name]; - } - return keys; - }, {}) - ).subscribe((res) => this.relatedRecords = res.referenced_table_names_and_columns[0]); - } - this.structure = [...res.structure]; - const columns = res.structure - .reduce((items, item) => { - items.set(item.column_name, item) - return items; - }, new Map()); - - if (res.rows) { - this.isEmptyTable = res.rows.length === 0; - const formattedRows = res.rows.map(row => this.formatRow(row, columns)); - this.rowsSubject.next(formattedRows); - } else { - this.isEmptyTable = true; - } - this.keyAttributes = res.primaryColumns; - this.identityColumn = res.identity_column; - this.tableActions = res.action_events; - this.tableBulkActions = res.action_events.filter((action: CustomEvent) => action.type === CustomActionType.Multiple); - - if (res.widgets) { - this.widgetsList = res.widgets.map((widget: Widget) => {return widget.field_name}); - this.widgetsCount = this.widgetsList.length; - this.widgets = Object.assign({}, ...res.widgets.map((widget: Widget) => { - let parsedParams; - - try { - parsedParams = JSON5.parse(widget.widget_params); - } catch (error) { - parsedParams = {}; - } - - return { - [widget.field_name]: { - ...widget, - widget_params: parsedParams, - }, - }; - }) - ); - } - - this.tableTypes = getTableTypes(res.structure, this.foreignKeysList); - - let orderedColumns: TableField[]; - if (res.table_settings.list_fields.length) { - orderedColumns = res.structure.sort((fieldA: TableField, fieldB: TableField) => res.table_settings.list_fields.indexOf(fieldA.column_name) - res.table_settings.list_fields.indexOf(fieldB.column_name)); - } else { - orderedColumns = [...res.structure]; - }; - - if (isTablePageSwitched === undefined) this.columns = orderedColumns - .filter (item => item.isExcluded === false) - .map((item, index) => { - if (res.table_settings.columns_view && res.table_settings.columns_view.length !== 0) { - return { - title: item.column_name, - normalizedTitle: this.widgets[item.column_name]?.name || normalizeFieldName(item.column_name), - selected: res.table_settings.columns_view.includes(item.column_name) - } - } else if (res.columns_view && res.columns_view.length !== 0) { - return { - title: item.column_name, - normalizedTitle: this.widgets[item.column_name]?.name || normalizeFieldName(item.column_name), - selected: res.columns_view.includes(item.column_name) - } - } else { - if (index < 6) { - return { - title: item.column_name, - normalizedTitle: this.widgets[item.column_name]?.name || normalizeFieldName(item.column_name), - selected: true - } - } - return { - title: item.column_name, - normalizedTitle: this.widgets[item.column_name]?.name || normalizeFieldName(item.column_name), - selected: false - } - } - }); - - this.dataColumns = this.columns.map(column => column.title); - this.dataNormalizedColumns = this.columns - .reduce((normalizedColumns, column) => (normalizedColumns[column.title] = column.normalizedTitle, normalizedColumns), {}) - this.displayedDataColumns = (filter(this.columns, column => column.selected === true)).map(column => column.title); - this.permissions = res.table_permissions.accessLevel; - if (this.keyAttributes.length) { - this.actionsColumnWidth = this.getActionsColumnWidth(this.tableActions, this.permissions); - this.displayedColumns = ['select', ...this.displayedDataColumns, 'actions']; - } else { - this.actionsColumnWidth = '0'; - this.displayedColumns = [...this.displayedDataColumns]; - this.alert_primaryKeysInfo = { - id: 10000, - type: AlertType.Info, - message: 'Add primary keys through your database to be able to work with the table rows.', - actions: [ - { - type: AlertActionType.Anchor, - caption: 'Instruction', - to: 'https://docs.rocketadmin.com/' - } - ] - } - }; - - this.isExportAllowed = res.allow_csv_export; - this.isImportAllowed = res.allow_csv_import; - this.canDelete = res.can_delete; - - this.sortByColumns = res.sortable_by; - - // Only set defaultSort if both ordering_field and ordering are present - if (res.table_settings.ordering_field && res.table_settings.ordering) { - this.defaultSort = { - column: res.table_settings.ordering_field, - direction: res.table_settings.ordering.toLowerCase() as 'asc' | 'desc' - }; - } else { - this.defaultSort = null; - } - - const widgetsConfigured = res.widgets?.length; - if (!res.configured && !widgetsConfigured - && this._connections.connectionAccessLevel !== AccessLevel.None - && this._connections.connectionAccessLevel !== AccessLevel.Readonly) - this.alert_settingsInfo = { - id: 10001, - type: AlertType.Info, - message: 'Configure now to reveal advanced table functionality and features.', - actions: [ - { - type: AlertActionType.Link, - caption: 'Configure all', - to: `/auto-configure/${connectionID}` - }, - { - type: AlertActionType.Link, - caption: 'Settings', - to: 'settings' - }, - { - type: AlertActionType.Link, - caption: 'Fields display', - to: 'widgets' - } - ] - }; - - this.largeDataset = res.large_dataset; - if (this.paginator) this.paginator.pageSize = res.pagination.perPage; - if (this.paginator) this.paginator.length = res.pagination.total; - }); - } - } - - getActionsColumnWidth(actions, permissions) { - const defaultActionsCount = permissions.edit + permissions.add + (!!permissions.delete && !!this.canDelete); - const totalActionsCount = actions.length + defaultActionsCount; - const lengthValue = ((totalActionsCount * 30) + 32); - return totalActionsCount === 0 ? '0' : `${lengthValue}px` - } - - changleColumnList(connectionId: string, tableName: string) { - this.displayedDataColumns = (filter(this.columns, column => column.selected === true)).map(column => column.title); - if (this.keyAttributes.length) { - this.displayedColumns = ['select', ...this.displayedDataColumns, 'actions' ] - } else { - this.displayedColumns = [...this.displayedDataColumns]; - }; - } - - getQueryParams(row, action) { - const params = Object.fromEntries(this.keyAttributes.map((column) => { - if (this.foreignKeysList.includes(column.column_name)) { - const referencedColumnNameOfForeignKey = this.foreignKeys[column.column_name].referenced_column_name; - return [column.column_name, row[column.column_name][referencedColumnNameOfForeignKey]]; - }; - return [column.column_name, row[column.column_name]]; - })); - - if (action === 'dub') { - return { ...params, action: 'dub' }; - } else { - return params; - } - } + private rowsSubject = new BehaviorSubject([]); + private loadingSubject = new BehaviorSubject(false); + + public loading$ = this.loadingSubject.asObservable(); + public paginator: MatPaginator; + + public structure; + public keyAttributes; + public columns: Column[] = []; + public dataColumns: string[]; + public dataNormalizedColumns: object; + public displayedColumns: string[]; + public displayedDataColumns: string[]; + public sortByColumns: string[]; + public defaultSort: { + column: string; + direction: 'asc' | 'desc'; + } | null; + public foreignKeysList: string[] = []; + public foreignKeys: TableForeignKey[] = []; + public widgetsList: string[]; + public widgets: { + [key: string]: Widget & { + widget_params: object; + }; + } = {}; + public widgetsCount: number = 0; + public selectWidgetsOptions: object; + public tableTypes: object = {}; + public relatedRecords = { + referenced_on_column_name: '', + referenced_by: [], + }; + public permissions; + public isExportAllowed: boolean; + public isImportAllowed: boolean; + public canDelete: boolean; + public isEmptyTable: boolean; + public tableActions: CustomAction[]; + public tableBulkActions: CustomAction[]; + public actionsColumnWidth: string; + public largeDataset: boolean; + public identityColumn: string; + + public alert_primaryKeysInfo: Alert; + public alert_settingsInfo: Alert; + public alert_widgetsWarning: Alert; + + constructor( + private _tables: TablesService, + private _connections: ConnectionsService, + private _tableRow: TableRowService, + ) {} + + connect(_collectionViewer: CollectionViewer): Observable { + return this.rowsSubject.asObservable(); + } + + disconnect(_collectionViewer: CollectionViewer): void { + this.rowsSubject.complete(); + this.loadingSubject.complete(); + } + + formatRow(row, columns) { + const rowToFormat = {}; + for (const [columnName, columnStructute] of columns) { + let type = ''; + if (['number', 'tinyint'].includes(columnStructute.data_type) && columnStructute.character_maximum_length === 1) { + type = 'boolean'; + } else type = columnStructute.data_type; + rowToFormat[columnName] = formatFieldValue(row[columnName], type); + } + return rowToFormat; + } + + fetchRows({ + connectionID, + tableName, + requstedPage, + pageSize, + sortColumn, + sortOrder, + filters, + comparators, + search, + isTablePageSwitched, + }: RowsParams) { + this.loadingSubject.next(true); + this.alert_primaryKeysInfo = null; + this.alert_settingsInfo = null; + this.alert_widgetsWarning = null; + + const fetchedTable = this._tables.fetchTable({ + connectionID, + tableName, + requstedPage: requstedPage + 1 || 1, + // chunkSize: this.paginator?.pageSize || 30, + chunkSize: pageSize || 30, + sortColumn, + sortOrder, + filters, + comparators, + search, + }); + + if (fetchedTable) { + fetchedTable + .pipe( + catchError(() => of([])), + finalize(() => this.loadingSubject.next(false)), + ) + .subscribe((res: any) => { + if (res.rows?.length) { + const firstRow = res.rows[0]; + + this.foreignKeysList = res.foreignKeys.map((field) => { + return field.column_name; + }); + this.foreignKeys = Object.assign( + {}, + ...res.foreignKeys.map((foreignKey: TableForeignKey) => ({ [foreignKey.column_name]: foreignKey })), + ); + + this._tableRow + .fetchTableRow( + connectionID, + tableName, + res.primaryColumns.reduce((keys, column) => { + if (this.foreignKeysList.includes(column.column_name)) { + const referencedColumnNameOfForeignKey = + this.foreignKeys[column.column_name].referenced_column_name; + keys[column.column_name] = firstRow[column.column_name][referencedColumnNameOfForeignKey]; + } else { + keys[column.column_name] = firstRow[column.column_name]; + } + return keys; + }, {}), + ) + .subscribe((res) => (this.relatedRecords = res.referenced_table_names_and_columns[0])); + } + this.structure = [...res.structure]; + const columns = res.structure.reduce((items, item) => { + items.set(item.column_name, item); + return items; + }, new Map()); + + if (res.rows) { + this.isEmptyTable = res.rows.length === 0; + const formattedRows = res.rows.map((row) => this.formatRow(row, columns)); + this.rowsSubject.next(formattedRows); + } else { + this.isEmptyTable = true; + } + this.keyAttributes = res.primaryColumns; + this.identityColumn = res.identity_column; + this.tableActions = res.action_events; + this.tableBulkActions = res.action_events.filter( + (action: CustomEvent) => action.type === CustomActionType.Multiple, + ); + + if (res.widgets) { + this.widgetsList = res.widgets.map((widget: Widget) => { + return widget.field_name; + }); + this.widgetsCount = this.widgetsList.length; + this.widgets = Object.assign( + {}, + ...res.widgets.map((widget: Widget) => { + let parsedParams; + + try { + parsedParams = JSON5.parse(widget.widget_params); + } catch (error) { + parsedParams = {}; + } + + return { + [widget.field_name]: { + ...widget, + widget_params: parsedParams, + }, + }; + }), + ); + } + + this.tableTypes = getTableTypes(res.structure, this.foreignKeysList); + + let orderedColumns: TableField[]; + if (res.table_settings.list_fields.length) { + orderedColumns = res.structure.sort( + (fieldA: TableField, fieldB: TableField) => + res.table_settings.list_fields.indexOf(fieldA.column_name) - + res.table_settings.list_fields.indexOf(fieldB.column_name), + ); + } else { + orderedColumns = [...res.structure]; + } + + if (isTablePageSwitched === undefined) + this.columns = orderedColumns + .filter((item) => item.isExcluded === false) + .map((item, index) => { + if (res.table_settings.columns_view && res.table_settings.columns_view.length !== 0) { + return { + title: item.column_name, + normalizedTitle: this.widgets[item.column_name]?.name || normalizeFieldName(item.column_name), + selected: res.table_settings.columns_view.includes(item.column_name), + }; + } else if (res.columns_view && res.columns_view.length !== 0) { + return { + title: item.column_name, + normalizedTitle: this.widgets[item.column_name]?.name || normalizeFieldName(item.column_name), + selected: res.columns_view.includes(item.column_name), + }; + } else { + if (index < 6) { + return { + title: item.column_name, + normalizedTitle: this.widgets[item.column_name]?.name || normalizeFieldName(item.column_name), + selected: true, + }; + } + return { + title: item.column_name, + normalizedTitle: this.widgets[item.column_name]?.name || normalizeFieldName(item.column_name), + selected: false, + }; + } + }); + + this.dataColumns = this.columns.map((column) => column.title); + this.dataNormalizedColumns = this.columns.reduce( + (normalizedColumns, column) => ( + (normalizedColumns[column.title] = column.normalizedTitle), normalizedColumns + ), + {}, + ); + this.displayedDataColumns = filter(this.columns, (column) => column.selected === true).map( + (column) => column.title, + ); + this.permissions = res.table_permissions.accessLevel; + if (this.keyAttributes.length) { + this.actionsColumnWidth = this.getActionsColumnWidth(this.tableActions, this.permissions); + this.displayedColumns = ['select', ...this.displayedDataColumns, 'actions']; + } else { + this.actionsColumnWidth = '0'; + this.displayedColumns = [...this.displayedDataColumns]; + this.alert_primaryKeysInfo = { + id: 10000, + type: AlertType.Info, + message: 'Add primary keys through your database to be able to work with the table rows.', + actions: [ + { + type: AlertActionType.Anchor, + caption: 'Instruction', + to: 'https://docs.rocketadmin.com/', + }, + ], + }; + } + + this.isExportAllowed = res.allow_csv_export; + this.isImportAllowed = res.allow_csv_import; + this.canDelete = res.can_delete; + + this.sortByColumns = res.sortable_by; + + // Only set defaultSort if both ordering_field and ordering are present + if (res.table_settings.ordering_field && res.table_settings.ordering) { + this.defaultSort = { + column: res.table_settings.ordering_field, + direction: res.table_settings.ordering.toLowerCase() as 'asc' | 'desc', + }; + } else { + this.defaultSort = null; + } + + const widgetsConfigured = res.widgets?.length; + if ( + !res.configured && + !widgetsConfigured && + this._connections.connectionAccessLevel !== AccessLevel.None && + this._connections.connectionAccessLevel !== AccessLevel.Readonly + ) + this.alert_settingsInfo = { + id: 10001, + type: AlertType.Info, + message: 'Configure now to reveal advanced table functionality and features.', + actions: [ + { + type: AlertActionType.Link, + caption: 'Configure all', + to: `/auto-configure/${connectionID}`, + }, + { + type: AlertActionType.Link, + caption: 'Settings', + to: 'settings', + }, + { + type: AlertActionType.Link, + caption: 'Fields display', + to: 'widgets', + }, + ], + }; + + this.largeDataset = res.large_dataset; + if (this.paginator) this.paginator.pageSize = res.pagination.perPage; + if (this.paginator) this.paginator.length = res.pagination.total; + }); + } + } + + getActionsColumnWidth(actions, permissions) { + const defaultActionsCount = permissions.edit + permissions.add + (!!permissions.delete && !!this.canDelete); + const totalActionsCount = actions.length + defaultActionsCount; + const lengthValue = totalActionsCount * 30 + 32; + return totalActionsCount === 0 ? '0' : `${lengthValue}px`; + } + + changleColumnList(connectionId: string, tableName: string) { + this.displayedDataColumns = filter(this.columns, (column) => column.selected === true).map( + (column) => column.title, + ); + if (this.keyAttributes.length) { + this.displayedColumns = ['select', ...this.displayedDataColumns, 'actions']; + } else { + this.displayedColumns = [...this.displayedDataColumns]; + } + } + + getQueryParams(row, action) { + const params = Object.fromEntries( + this.keyAttributes.map((column) => { + if (this.foreignKeysList.includes(column.column_name)) { + const referencedColumnNameOfForeignKey = this.foreignKeys[column.column_name].referenced_column_name; + return [column.column_name, row[column.column_name][referencedColumnNameOfForeignKey]]; + } + return [column.column_name, row[column.column_name]]; + }), + ); + + if (action === 'dub') { + return { ...params, action: 'dub' }; + } else { + return params; + } + } } diff --git a/frontend/src/app/components/dashboard/db-tables-list/db-folder-delete-dialog/db-folder-delete-dialog.component.css b/frontend/src/app/components/dashboard/db-tables-list/db-folder-delete-dialog/db-folder-delete-dialog.component.css index 6c028eb26..ea3453502 100644 --- a/frontend/src/app/components/dashboard/db-tables-list/db-folder-delete-dialog/db-folder-delete-dialog.component.css +++ b/frontend/src/app/components/dashboard/db-tables-list/db-folder-delete-dialog/db-folder-delete-dialog.component.css @@ -1,43 +1,43 @@ .confirmation-message { - margin: 0 0 20px 0; + margin: 0 0 20px 0; } .folder-info { - display: flex; - align-items: flex-start; - gap: 12px; - padding: 12px 16px; - background-color: color-mix(in hsl, #296EE9, transparent 95%); - border-radius: 4px; - margin-bottom: 20px; + display: flex; + align-items: flex-start; + gap: 12px; + padding: 12px 16px; + background-color: color-mix(in hsl, #296ee9, transparent 95%); + border-radius: 4px; + margin-bottom: 20px; } .info-icon { - color: #296EE9; - font-size: 20px; - width: 20px; - height: 20px; - margin-top: 2px; + color: #296ee9; + font-size: 20px; + width: 20px; + height: 20px; + margin-top: 2px; } .info-text { - margin: 0; - font-size: 14px; - line-height: 1.5; - flex: 1; + margin: 0; + font-size: 14px; + line-height: 1.5; + flex: 1; } /* Dark mode support */ @media (prefers-color-scheme: dark) { - .folder-info { - background-color: color-mix(in hsl, #296EE9, transparent 75%); - } + .folder-info { + background-color: color-mix(in hsl, #296ee9, transparent 75%); + } - .info-icon { - color: #296EE9; - } + .info-icon { + color: #296ee9; + } - .info-text { - color: #e0e0e0; - } -} \ No newline at end of file + .info-text { + color: #e0e0e0; + } +} diff --git a/frontend/src/app/components/dashboard/db-tables-list/db-folder-delete-dialog/db-folder-delete-dialog.component.ts b/frontend/src/app/components/dashboard/db-tables-list/db-folder-delete-dialog/db-folder-delete-dialog.component.ts index 127251b8c..4f1636b8e 100644 --- a/frontend/src/app/components/dashboard/db-tables-list/db-folder-delete-dialog/db-folder-delete-dialog.component.ts +++ b/frontend/src/app/components/dashboard/db-tables-list/db-folder-delete-dialog/db-folder-delete-dialog.component.ts @@ -1,37 +1,32 @@ -import { Component, Inject } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { MatDialogRef, MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog'; +import { Component, Inject } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; +import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog'; import { MatIconModule } from '@angular/material/icon'; export interface DbFolderDeleteDialogData { - folderName: string; - tableCount: number; + folderName: string; + tableCount: number; } @Component({ - selector: 'app-db-folder-delete-dialog', - templateUrl: './db-folder-delete-dialog.component.html', - styleUrls: ['./db-folder-delete-dialog.component.css'], - standalone: true, - imports: [ - CommonModule, - MatDialogModule, - MatButtonModule, - MatIconModule - ] + selector: 'app-db-folder-delete-dialog', + templateUrl: './db-folder-delete-dialog.component.html', + styleUrls: ['./db-folder-delete-dialog.component.css'], + standalone: true, + imports: [CommonModule, MatDialogModule, MatButtonModule, MatIconModule], }) export class DbFolderDeleteDialogComponent { - constructor( - public dialogRef: MatDialogRef, - @Inject(MAT_DIALOG_DATA) public data: DbFolderDeleteDialogData - ) {} + constructor( + public dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data: DbFolderDeleteDialogData, + ) {} - onCancel(): void { - this.dialogRef.close(false); - } + onCancel(): void { + this.dialogRef.close(false); + } - onDelete(): void { - this.dialogRef.close(true); - } -} \ No newline at end of file + onDelete(): void { + this.dialogRef.close(true); + } +} diff --git a/frontend/src/app/components/dashboard/db-tables-list/db-folder-edit-dialog/db-folder-edit-dialog.component.css b/frontend/src/app/components/dashboard/db-tables-list/db-folder-edit-dialog/db-folder-edit-dialog.component.css index ff3c148d5..8734040a3 100644 --- a/frontend/src/app/components/dashboard/db-tables-list/db-folder-edit-dialog/db-folder-edit-dialog.component.css +++ b/frontend/src/app/components/dashboard/db-tables-list/db-folder-edit-dialog/db-folder-edit-dialog.component.css @@ -1,208 +1,212 @@ .dialog-body { - min-width: 400px; - max-height: 500px; - overflow-y: auto; + min-width: 400px; + max-height: 500px; + overflow-y: auto; } .folder-name-section { - margin-bottom: 20px; - padding-bottom: 16px; - border-bottom: 1px solid #e0e0e0; + margin-bottom: 20px; + padding-bottom: 16px; + border-bottom: 1px solid #e0e0e0; } .folder-name-section h4 { - margin: 0 0 12px 0; - font-size: 14px; - font-weight: 500; - color: #666; + margin: 0 0 12px 0; + font-size: 14px; + font-weight: 500; + color: #666; } .folder-name-field { - width: 100%; + width: 100%; } .folder-color-section { - margin-bottom: 20px; - padding-bottom: 16px; - border-bottom: 1px solid #e0e0e0; + margin-bottom: 20px; + padding-bottom: 16px; + border-bottom: 1px solid #e0e0e0; } .folder-color-section h4 { - margin: 0 0 12px 0; - font-size: 14px; - font-weight: 500; - color: #666; + margin: 0 0 12px 0; + font-size: 14px; + font-weight: 500; + color: #666; } .color-radio-group { - width: 100%; + width: 100%; } .color-row { - display: flex; - gap: 12px; - flex-wrap: wrap; - padding: 8px 0; + display: flex; + gap: 12px; + flex-wrap: wrap; + padding: 8px 0; } .color-radio-label { - display: inline-block; - position: relative; - cursor: pointer; + display: inline-block; + position: relative; + cursor: pointer; } .color-radio-input { - position: absolute; - opacity: 0; - width: 0; - height: 0; + position: absolute; + opacity: 0; + width: 0; + height: 0; } .color-swatch { - width: 40px; - height: 40px; - border-radius: 50%; - border: 2px solid transparent; - cursor: pointer; - transition: all 0.2s ease; - position: relative; - display: flex; - align-items: center; - justify-content: center; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + width: 40px; + height: 40px; + border-radius: 50%; + border: 2px solid transparent; + cursor: pointer; + transition: all 0.2s ease; + position: relative; + display: flex; + align-items: center; + justify-content: center; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); } .color-radio-label:hover .color-swatch { - transform: scale(1.1); - box-shadow: 0 3px 6px rgba(0, 0, 0, 0.2); + transform: scale(1.1); + box-shadow: 0 3px 6px rgba(0, 0, 0, 0.2); } .color-radio-label.selected .color-swatch { - /* border: 2px solid currentColor; */ - box-shadow: 0 0 0 3px rgba(33, 33, 33, 0.1), 0 3px 8px rgba(0, 0, 0, 0.2); - transform: scale(1.05); + /* border: 2px solid currentColor; */ + box-shadow: + 0 0 0 3px rgba(33, 33, 33, 0.1), + 0 3px 8px rgba(0, 0, 0, 0.2); + transform: scale(1.05); } .color-radio-label.selected .color-swatch::after { - content: '✓'; - position: absolute; - /* font-size: 20px; */ - color: white; - /* text-shadow: 0 1px 3px rgba(0, 0, 0, 0.8); */ - /* font-weight: bold; */ + content: "✓"; + position: absolute; + /* font-size: 20px; */ + color: white; + /* text-shadow: 0 1px 3px rgba(0, 0, 0, 0.8); */ + /* font-weight: bold; */ } .tables-section h4 { - margin: 0 0 12px 0; - font-size: 14px; - font-weight: 500; - color: #666; + margin: 0 0 12px 0; + font-size: 14px; + font-weight: 500; + color: #666; } .table-list { - margin-top: 8px; + margin-top: 8px; } .table-item { - display: flex; - justify-content: space-between; - align-items: center; - padding: 12px 8px; - border-bottom: 1px solid #e0e0e0; - transition: background-color 0.2s ease; + display: flex; + justify-content: space-between; + align-items: center; + padding: 12px 8px; + border-bottom: 1px solid #e0e0e0; + transition: background-color 0.2s ease; } .table-item:hover { - background-color: rgba(0, 0, 0, 0.04); + background-color: rgba(0, 0, 0, 0.04); } .table-item:last-child { - border-bottom: none; + border-bottom: none; } .table-name { - flex: 1; - font-size: 14px; - color: #333; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - padding-right: 12px; + flex: 1; + font-size: 14px; + color: #333; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + padding-right: 12px; } .action-button { - min-width: 70px; - height: 32px; - font-size: 12px; - font-weight: 500; - border-radius: 4px; - transition: all 0.2s ease; + min-width: 70px; + height: 32px; + font-size: 12px; + font-weight: 500; + border-radius: 4px; + transition: all 0.2s ease; } .add-button { - color: #4caf50; - background-color: transparent; + color: #4caf50; + background-color: transparent; } .add-button:hover { - background-color: rgba(76, 175, 80, 0.1); + background-color: rgba(76, 175, 80, 0.1); } .remove-button { - color: #f44336; - background-color: transparent; + color: #f44336; + background-color: transparent; } .remove-button:hover { - background-color: rgba(244, 67, 54, 0.1); + background-color: rgba(244, 67, 54, 0.1); } /* Dark mode support */ @media (prefers-color-scheme: dark) { - .folder-name-section { - border-bottom-color: #424242; - } - - .folder-name-section h4 { - color: #999; - } - - .folder-color-section { - border-bottom-color: #424242; - } - - .folder-color-section h4, - .tables-section h4 { - color: #999; - } - - .color-swatch { - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); - } - - .color-radio-label:hover .color-swatch { - box-shadow: 0 3px 6px rgba(0, 0, 0, 0.4); - } - - .color-radio-label.selected .color-swatch { - border-color: currentColor; - box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.1), 0 3px 8px rgba(0, 0, 0, 0.4); - } - - .color-radio-label.selected .color-swatch::before { - border-color: currentColor; - opacity: 0.2; - } - - .table-item { - border-bottom-color: #424242; - } - - .table-item:hover { - background-color: rgba(255, 255, 255, 0.04); - } - - .table-name { - color: #ccc; - } -} \ No newline at end of file + .folder-name-section { + border-bottom-color: #424242; + } + + .folder-name-section h4 { + color: #999; + } + + .folder-color-section { + border-bottom-color: #424242; + } + + .folder-color-section h4, + .tables-section h4 { + color: #999; + } + + .color-swatch { + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); + } + + .color-radio-label:hover .color-swatch { + box-shadow: 0 3px 6px rgba(0, 0, 0, 0.4); + } + + .color-radio-label.selected .color-swatch { + border-color: currentColor; + box-shadow: + 0 0 0 3px rgba(255, 255, 255, 0.1), + 0 3px 8px rgba(0, 0, 0, 0.4); + } + + .color-radio-label.selected .color-swatch::before { + border-color: currentColor; + opacity: 0.2; + } + + .table-item { + border-bottom-color: #424242; + } + + .table-item:hover { + background-color: rgba(255, 255, 255, 0.04); + } + + .table-name { + color: #ccc; + } +} diff --git a/frontend/src/app/components/dashboard/db-tables-list/db-folder-edit-dialog/db-folder-edit-dialog.component.ts b/frontend/src/app/components/dashboard/db-tables-list/db-folder-edit-dialog/db-folder-edit-dialog.component.ts index 7f7711c8a..76b3e6f73 100644 --- a/frontend/src/app/components/dashboard/db-tables-list/db-folder-edit-dialog/db-folder-edit-dialog.component.ts +++ b/frontend/src/app/components/dashboard/db-tables-list/db-folder-edit-dialog/db-folder-edit-dialog.component.ts @@ -1,116 +1,116 @@ -import { Component, Inject } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { MatDialogRef, MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog'; +import { Component, Inject } from '@angular/core'; +import { FormsModule } from '@angular/forms'; import { MatButtonModule } from '@angular/material/button'; -import { MatIconModule } from '@angular/material/icon'; +import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog'; import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatIconModule } from '@angular/material/icon'; import { MatInputModule } from '@angular/material/input'; -import { FormsModule } from '@angular/forms'; import { TableProperties } from 'src/app/models/table'; export interface Folder { - id: string; - name: string; - expanded: boolean; - tableIds: string[]; - iconColor?: string; - isEmpty?: boolean; + id: string; + name: string; + expanded: boolean; + tableIds: string[]; + iconColor?: string; + isEmpty?: boolean; } export interface DbFolderEditDialogData { - folder: Folder; - tables: TableProperties[]; - folderIconColors: { name: string; value: string }[]; + folder: Folder; + tables: TableProperties[]; + folderIconColors: { name: string; value: string }[]; } @Component({ - selector: 'app-db-folder-edit-dialog', - templateUrl: './db-folder-edit-dialog.component.html', - styleUrls: ['./db-folder-edit-dialog.component.css'], - standalone: true, - imports: [ - CommonModule, - MatDialogModule, - MatButtonModule, - MatIconModule, - MatFormFieldModule, - MatInputModule, - FormsModule - ] + selector: 'app-db-folder-edit-dialog', + templateUrl: './db-folder-edit-dialog.component.html', + styleUrls: ['./db-folder-edit-dialog.component.css'], + standalone: true, + imports: [ + CommonModule, + MatDialogModule, + MatButtonModule, + MatIconModule, + MatFormFieldModule, + MatInputModule, + FormsModule, + ], }) export class DbFolderEditDialogComponent { - public folder: Folder; - public tables: TableProperties[]; - public folderIconColors: { name: string; value: string }[]; - originalFolderName: string; + public folder: Folder; + public tables: TableProperties[]; + public folderIconColors: { name: string; value: string }[]; + originalFolderName: string; - constructor( - public dialogRef: MatDialogRef, - @Inject(MAT_DIALOG_DATA) public data: DbFolderEditDialogData - ) { - // Create a copy of the folder to work with - this.folder = { ...data.folder, tableIds: [...data.folder.tableIds] }; - // Store original name for validation - this.originalFolderName = data.folder.name; - // Set default color if not set - if (!this.folder.iconColor) { - this.folder.iconColor = '#757575'; - } - this.tables = data.tables; - this.folderIconColors = data.folderIconColors; - } + constructor( + public dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data: DbFolderEditDialogData, + ) { + // Create a copy of the folder to work with + this.folder = { ...data.folder, tableIds: [...data.folder.tableIds] }; + // Store original name for validation + this.originalFolderName = data.folder.name; + // Set default color if not set + if (!this.folder.iconColor) { + this.folder.iconColor = '#757575'; + } + this.tables = data.tables; + this.folderIconColors = data.folderIconColors; + } - getFolderIconColor(folder: Folder): string { - return folder.iconColor || '#757575'; - } + getFolderIconColor(folder: Folder): string { + return folder.iconColor || '#757575'; + } - onColorChange(color: string) { - console.log('Folder color changed to:', color); - } + onColorChange(color: string) { + console.log('Folder color changed to:', color); + } - isTableInFolder(table: TableProperties): boolean { - return this.folder.tableIds.includes(table.table); - } + isTableInFolder(table: TableProperties): boolean { + return this.folder.tableIds.includes(table.table); + } - getTableName(table: TableProperties): string { - return table.display_name || table.table; - } + getTableName(table: TableProperties): string { + return table.display_name || table.table; + } - toggleTableInFolder(table: TableProperties) { - const tableId = table.table; - const isInFolder = this.folder.tableIds.includes(tableId); + toggleTableInFolder(table: TableProperties) { + const tableId = table.table; + const isInFolder = this.folder.tableIds.includes(tableId); - if (isInFolder) { - // Remove table from folder - this.folder.tableIds = this.folder.tableIds.filter(id => id !== tableId); - } else { - // Add table to folder - this.folder.tableIds.push(tableId); - // If this was an empty folder, mark it as no longer empty - if (this.folder.isEmpty) { - this.folder.isEmpty = false; - } - } + if (isInFolder) { + // Remove table from folder + this.folder.tableIds = this.folder.tableIds.filter((id) => id !== tableId); + } else { + // Add table to folder + this.folder.tableIds.push(tableId); + // If this was an empty folder, mark it as no longer empty + if (this.folder.isEmpty) { + this.folder.isEmpty = false; + } + } - console.log('toggleTableInFolder'); - } + console.log('toggleTableInFolder'); + } - isAllTablesFolder(): boolean { - return this.folder.id === '0' || this.folder.name === 'All Tables'; - } + isAllTablesFolder(): boolean { + return this.folder.id === '0' || this.folder.name === 'All Tables'; + } - onSave() { - // Validate folder name - if (!this.folder.name || this.folder.name.trim() === '') { - return; // Don't save if name is empty - } + onSave() { + // Validate folder name + if (!this.folder.name || this.folder.name.trim() === '') { + return; // Don't save if name is empty + } - // Trim the folder name before saving - this.folder.name = this.folder.name.trim(); - this.dialogRef.close(this.folder); - } + // Trim the folder name before saving + this.folder.name = this.folder.name.trim(); + this.dialogRef.close(this.folder); + } - onCancel() { - this.dialogRef.close(); - } + onCancel() { + this.dialogRef.close(); + } } diff --git a/frontend/src/app/components/dashboard/db-tables-list/db-tables-list.component.css b/frontend/src/app/components/dashboard/db-tables-list/db-tables-list.component.css index 9875b2e7a..9a6d23893 100644 --- a/frontend/src/app/components/dashboard/db-tables-list/db-tables-list.component.css +++ b/frontend/src/app/components/dashboard/db-tables-list/db-tables-list.component.css @@ -1074,9 +1074,10 @@ .cdk-drag-preview.selected-table-item { box-sizing: border-box; border-radius: 4px; - box-shadow: 0 5px 5px -3px rgba(0, 0, 0, 0.2), - 0 8px 10px 1px rgba(0, 0, 0, 0.14), - 0 3px 14px 2px rgba(0, 0, 0, 0.12); + box-shadow: + 0 5px 5px -3px rgba(0, 0, 0, 0.2), + 0 8px 10px 1px rgba(0, 0, 0, 0.14), + 0 3px 14px 2px rgba(0, 0, 0, 0.12); background: #ffffff; opacity: 0.9; } diff --git a/frontend/src/app/components/dashboard/db-tables-list/db-tables-list.component.spec.ts b/frontend/src/app/components/dashboard/db-tables-list/db-tables-list.component.spec.ts index da9612430..5ee455027 100644 --- a/frontend/src/app/components/dashboard/db-tables-list/db-tables-list.component.spec.ts +++ b/frontend/src/app/components/dashboard/db-tables-list/db-tables-list.component.spec.ts @@ -1,37 +1,33 @@ +import { provideHttpClient } from '@angular/common/http'; import { ComponentFixture, TestBed } from '@angular/core/testing'; - import { Angulartics2Module } from 'angulartics2'; import { DbTablesListComponent } from './db-tables-list.component'; -import { provideHttpClient } from '@angular/common/http'; describe('DbTablesListComponent', () => { - let component: DbTablesListComponent; - let fixture: ComponentFixture; + let component: DbTablesListComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - providers: [provideHttpClient()], - imports: [ - Angulartics2Module.forRoot(), - DbTablesListComponent - ] - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + providers: [provideHttpClient()], + imports: [Angulartics2Module.forRoot(), DbTablesListComponent], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(DbTablesListComponent); - component = fixture.componentInstance; - component.tableFolders = [ - { - category_id: 'all-tables-kitten', - category_name: 'All Tables', - tables: [], - }, - ]; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(DbTablesListComponent); + component = fixture.componentInstance; + component.tableFolders = [ + { + category_id: 'all-tables-kitten', + category_name: 'All Tables', + tables: [], + }, + ]; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/dashboard/db-tables-list/db-tables-list.component.ts b/frontend/src/app/components/dashboard/db-tables-list/db-tables-list.component.ts index 79a200fcb..40298136a 100644 --- a/frontend/src/app/components/dashboard/db-tables-list/db-tables-list.component.ts +++ b/frontend/src/app/components/dashboard/db-tables-list/db-tables-list.component.ts @@ -1,16 +1,10 @@ -import { Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges } from '@angular/core'; import { CdkDragDrop, DragDropModule, moveItemInArray } from '@angular/cdk/drag-drop'; -import { DbFolderDeleteDialogComponent, DbFolderDeleteDialogData } from './db-folder-delete-dialog/db-folder-delete-dialog.component'; -import { DbFolderEditDialogComponent, DbFolderEditDialogData } from './db-folder-edit-dialog/db-folder-edit-dialog.component'; -import { MatDialog, MatDialogModule } from '@angular/material/dialog'; -import { TableProperties, } from 'src/app/models/table'; - -import { AccessLevel } from 'src/app/models/user'; import { CommonModule } from '@angular/common'; -import { ContentLoaderComponent } from '../../ui-components/content-loader/content-loader.component'; +import { Component, EventEmitter, Input, OnChanges, OnInit, Output, SimpleChanges } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { MatButtonModule } from '@angular/material/button'; import { MatCheckboxModule } from '@angular/material/checkbox'; +import { MatDialog, MatDialogModule } from '@angular/material/dialog'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatIconModule } from '@angular/material/icon'; import { MatInputModule } from '@angular/material/input'; @@ -18,610 +12,617 @@ import { MatListModule } from '@angular/material/list'; import { MatMenuModule } from '@angular/material/menu'; import { MatTooltipModule } from '@angular/material/tooltip'; import { RouterModule } from '@angular/router'; +import { normalizeTableName } from 'src/app/lib/normalize'; import { TableCategory } from 'src/app/models/connection'; +import { TableProperties } from 'src/app/models/table'; +import { AccessLevel } from 'src/app/models/user'; import { TableStateService } from 'src/app/services/table-state.service'; import { TablesService } from 'src/app/services/tables.service'; import { UiSettingsService } from 'src/app/services/ui-settings.service'; -import { normalizeTableName } from 'src/app/lib/normalize'; +import { ContentLoaderComponent } from '../../ui-components/content-loader/content-loader.component'; +import { + DbFolderDeleteDialogComponent, + DbFolderDeleteDialogData, +} from './db-folder-delete-dialog/db-folder-delete-dialog.component'; +import { + DbFolderEditDialogComponent, + DbFolderEditDialogData, +} from './db-folder-edit-dialog/db-folder-edit-dialog.component'; export interface Folder { - id: string; - name: string; - expanded: boolean; - tableIds: string[]; - iconColor?: string; // Optional color for folder icon - isEmpty?: boolean; // Flag to indicate if folder is newly created and empty + id: string; + name: string; + expanded: boolean; + tableIds: string[]; + iconColor?: string; // Optional color for folder icon + isEmpty?: boolean; // Flag to indicate if folder is newly created and empty } interface Category { - category_id: string; - category_name: string; - category_color?: string; - tables: TableProperties[]; + category_id: string; + category_name: string; + category_color?: string; + tables: TableProperties[]; } @Component({ - selector: 'app-db-tables-list', - templateUrl: './db-tables-list.component.html', - styleUrls: ['./db-tables-list.component.css'], - imports: [ - CommonModule, - FormsModule, - DragDropModule, - MatButtonModule, - MatCheckboxModule, - MatIconModule, - MatFormFieldModule, - MatInputModule, - MatListModule, - MatTooltipModule, - MatMenuModule, - MatDialogModule, - RouterModule, - ContentLoaderComponent - ] + selector: 'app-db-tables-list', + templateUrl: './db-tables-list.component.html', + styleUrls: ['./db-tables-list.component.css'], + imports: [ + CommonModule, + FormsModule, + DragDropModule, + MatButtonModule, + MatCheckboxModule, + MatIconModule, + MatFormFieldModule, + MatInputModule, + MatListModule, + MatTooltipModule, + MatMenuModule, + MatDialogModule, + RouterModule, + ContentLoaderComponent, + ], }) export class DbTablesListComponent implements OnInit, OnChanges { - @Input() connectionID: string; - @Input() connectionTitle: string; - @Input() tableFolders: Category[]; - @Input() selectedTable: string; - @Input() collapsed: boolean; - @Input() uiSettings: any; - @Input() accessLevel: AccessLevel; - - @Output() expandSidebar = new EventEmitter(); - - public tableCategories: TableCategory[] = []; - public substringToSearch: string; - public foundTables: TableProperties[]; - public folders: Folder[] = []; - - // Dialog state - public currentFolder: Folder | null = null; - - // Drag and drop state - public draggedTable: TableProperties | null = null; - public dragOverFolder: string | null = null; - - // Collapsed state - public showCollapsedTableList: boolean = false; - public currentCollapsedFolder: Folder | null = null; - - // State preservation - private preservedFolderStates: { [key: string]: boolean } = {}; - private preservedActiveFolder: string | null = null; - - // Search state preservation - private preSearchFolderStates: { [key: string]: boolean } | null = null; - - // Folder icon colors - public folderIconColors = [ - { name: 'Default', value: 'rgba(0, 0, 0, 0.87)' }, - { name: 'Blue', value: '#2196F3' }, - { name: 'Green', value: '#4CAF50' }, - { name: 'Orange', value: '#FF9800' }, - { name: 'Red', value: '#F44336' }, - { name: 'Purple', value: '#9C27B0' }, - { name: 'Teal', value: '#009688' }, - { name: 'Pink', value: '#E91E63' } - ]; - - constructor( - private _tableState: TableStateService, - private _tablesService: TablesService, - private _uiSettingsService: UiSettingsService, - private dialog: MatDialog - ) { } - - ngOnInit() { - this.foundTables = this.tableFolders - .find((folder: any) => folder.category_id === 'all-tables-kitten')?.tables - .map((table: TableProperties) => { - return { - table: table.table, - display_name: table.display_name, - normalizedTableName: normalizeTableName(table.table), - permissions: table.permissions - }; - }); - this.folders = this.tableFolders.map(category => { - const tableIds = category.tables.map((table: TableProperties) => table.table); - return { - id: category.category_id, - name: category.category_name, - expanded: category.category_id === 'all-tables-kitten' ? true : false, - tableIds, - iconColor: category.category_color - }; - }); - - const expandedFolders = this.uiSettings?.tableFoldersExpanded || ['0']; - if (expandedFolders && expandedFolders.length > 0) { - this.folders.forEach(folder => { - folder.expanded = expandedFolders.includes(folder.id); - }); - } - - // If no custom folders exist, always keep "All tables" expanded - if (!this.hasCustomFolders()) { - const allTablesFolder = this.folders.find(folder => folder.id === 'all-tables-kitten'); - if (allTablesFolder) { - allTablesFolder.expanded = true; - } - } - - console.log('ngOnInit - showCollapsedTableList initialized to:', this.showCollapsedTableList); - } - - ngOnChanges(changes: SimpleChanges) { - if (changes.collapsed) { - if (changes.collapsed.currentValue === true) { - // Sidebar is being collapsed - preserve current state - this.preserveFolderStates(); - } else if (changes.collapsed.currentValue === false) { - // Sidebar is being expanded - restore preserved state - this.restoreFolderStates(); - } - } - } - - searchSubstring() { - if (!this.substringToSearch || this.substringToSearch.trim() === '') { - this.foundTables = this.allTables; - // Restore folder expanded states from before the search - if (this.preSearchFolderStates) { - this.folders.forEach(folder => { - if (Object.hasOwn(this.preSearchFolderStates, folder.id)) { - folder.expanded = this.preSearchFolderStates[folder.id]; - } - }); - this.preSearchFolderStates = null; - } - return; - } - - // Save folder states before the first search modification - if (!this.preSearchFolderStates) { - this.preSearchFolderStates = {}; - this.folders.forEach(folder => { - this.preSearchFolderStates[folder.id] = folder.expanded; - }); - } - - const searchTerm = this.substringToSearch.toLowerCase(); - - // Filter all tables by search term - this.foundTables = this.allTables.filter(tableItem => - tableItem.table.toLowerCase().includes(searchTerm) || - (tableItem.display_name?.toLowerCase().includes(searchTerm)) || - (tableItem.normalizedTableName?.toLowerCase().includes(searchTerm)) - ); - - // Expand all folders that contain matching tables - this.folders.forEach(folder => { - const folderTables = this.getFolderTables(folder); - const hasMatchingTables = folderTables.some(table => - this.foundTables.some(foundTable => foundTable.table === table.table) - ); - - if (hasMatchingTables) { - folder.expanded = true; - } else { - folder.expanded = false; - } - }); - } - - getTableName(table: TableProperties) { - return table.display_name || table.normalizedTableName || table.table - } - - getTableNameLength(tableName: string) { - return tableName.length; - } - - closeSidebar() { - this._tableState.clearSelection(); - this._tableState.closeAIpanel(); - } - - onAddFolder() { - const newFolder: Folder = { - id: this.generateFolderId(), - name: `Folder ${this.folders.length}`, - expanded: true, // Разворачиваем папку сразу после создания - tableIds: [], - isEmpty: true // Mark as empty for special styling - }; - this.folders.push(newFolder); - console.log('onAddFolder'); - this.saveFolders(); - } - - toggleFolder(folderId: string) { - const folder = this.folders.find (f => f.id === folderId); - if (folder) { - // Prevent collapsing "All tables" when no custom folders exist - if (folder.id === 'all-tables-kitten' && folder.expanded && !this.hasCustomFolders()) { - return; - } - folder.expanded = !folder.expanded; - const expandedFolders = this.folders.filter(f => f.expanded).map(f => f.id); - this._uiSettingsService.updateConnectionSetting(this.connectionID, 'tableFoldersExpanded', expandedFolders); - } - } - - onCollapsedFolderClick(folder: Folder) { - console.log('Clicked on folder:', folder.name); - - // If clicking on the same folder that's already open, close it - if (this.currentCollapsedFolder?.id === folder.id) { - this.showCollapsedTableList = false; - this.currentCollapsedFolder = null; - } else { - // If clicking on a different folder, open it immediately - this.showCollapsedTableList = true; - this.currentCollapsedFolder = folder; - } - } - - getCollapsedTableList(): TableProperties[] { - if (!this.currentCollapsedFolder) { - console.log('No current collapsed folder'); - return []; - } - - const tables = this.getFolderTables(this.currentCollapsedFolder); - console.log('getCollapsedTableList - tables:', tables); - return tables; - } - - navigateToTable(_table: TableProperties) { - // This method is called when clicking on a table in collapsed mode - // The actual navigation is handled by routerLink in the template - // We just need to close the sidebar after navigation - this.closeSidebar(); - } - - toggleCollapsedSearch() { - // Open the sidebar and activate search - this.expandSidebar.emit(); - // Focus on search input after sidebar expands - setTimeout(() => { - const input = document.querySelector('.search-input input') as HTMLInputElement; - if (input) { - input.focus(); - input.select(); - } - }, 300); // Wait for sidebar animation to complete - } - - deleteFolder(folder: Folder) { - const dialogData: DbFolderDeleteDialogData = { - folderName: folder.name, - tableCount: folder.tableIds.length - }; - - const dialogRef = this.dialog.open(DbFolderDeleteDialogComponent, { - width: '24em', - data: dialogData, - panelClass: 'db-folder-delete-dialog' - }); - - dialogRef.afterClosed().subscribe((confirmed: boolean) => { - if (confirmed) { - const index = this.folders.findIndex(f => f.id === folder.id); - if (index > -1) { - this.folders.splice(index, 1); - this.saveFolders(); - } - } - }); - } - - getFolderTables(folder: Folder): TableProperties[] { - // Preserve folder.tableIds order by mapping through tableIds - const allTablesMap = new Map(this.allTables.map(t => [t.table, t])); - const folderTables = folder.tableIds - .map(tableId => allTablesMap.get(tableId)) - .filter((table): table is TableProperties => !!table); - - // If there's a search term, filter the collection tables too - if (this.substringToSearch && this.substringToSearch.trim() !== '') { - const searchTerm = this.substringToSearch.toLowerCase(); - return folderTables.filter(table => - table.table.toLowerCase().includes(searchTerm) || - (table.display_name?.toLowerCase().includes(searchTerm)) || - (table.normalizedTableName?.toLowerCase().includes(searchTerm)) - ); - } - - return folderTables; - } - - getAvailableTables(folder: Folder | null): TableProperties[] { - if (!folder) return []; - return this.allTables.filter(table => !folder.tableIds.includes(table.table)); - } - - isTableInAnyFolder(table: TableProperties): boolean { - return this.folders.some(folder => folder.tableIds.includes(table.table)); - } - - shouldShowFolder(folder: Folder): boolean { - // If no search term, show all folders - if (!this.substringToSearch || this.substringToSearch.trim() === '') { - return true; - } - - // Show collection if it has tables matching the search - const matchingTables = this.getFolderTables(folder); - return matchingTables.length > 0; - } - - showEditTablesDialog(folder: Folder) { - // Expand the folder if it's collapsed - folder.expanded = true; - - const dialogData: DbFolderEditDialogData = { - folder: folder, - tables: this.allTables, - folderIconColors: this.folderIconColors - }; - - const dialogRef = this.dialog.open(DbFolderEditDialogComponent, { - width: '32em', - data: dialogData, - panelClass: 'db-folder-edit-dialog' - }); - - dialogRef.afterClosed().subscribe((result: Folder | undefined) => { - if (result) { - // Update the folder with the result from dialog - const index = this.folders.findIndex(f => f.id === result.id); - if (index !== -1) { - this.folders[index] = result; - this.saveFolders(); - } - } - }); - } - - - get allTables(): TableProperties[] { - return this.tableFolders - ?.find((folder: any) => folder.category_id === 'all-tables-kitten')?.tables || []; - } - - isTableInFolder(table: TableProperties): boolean { - return this.currentFolder ? this.currentFolder.tableIds.includes(table.table) : false; - } - - isTableInCurrentDraggedFolder(table: TableProperties, folder: Folder): boolean { - return this.draggedTable && - this.draggedTable.table === table.table && - folder.tableIds.includes(table.table); - } - - toggleTableInFolder(table: TableProperties) { - if (!this.currentFolder) return; - - const tableId = table.table; - const isInFolder = this.currentFolder.tableIds.includes(tableId); - - if (isInFolder) { - // Remove from folder - this.currentFolder.tableIds = this.currentFolder.tableIds.filter(id => id !== tableId); - } else { - // Add to folder - this.currentFolder.tableIds.push(tableId); - // Remove empty flag when adding tables - if (this.currentFolder.isEmpty) { - this.currentFolder.isEmpty = false; - } - } - - console.log('toggleTableInFolder'); - this.saveFolders(); - } - - - removeTableFromFolder(folder: Folder, table: TableProperties, event: Event) { - event.stopPropagation(); - folder.tableIds = folder.tableIds.filter(id => id !== table.table); - console.log('removeTableFromFolder'); - this.saveFolders(); - } - - trackByFolderId(_index: number, folder: Folder): string { - return folder.id; - } - - hasCustomFolders(): boolean { - // Check if there are folders other than "All tables" - return this.folders.some(folder => folder.id !== 'all-tables-kitten'); - } - - private preserveFolderStates() { - // Save expanded states of all folders - this.folders.forEach(folder => { - this.preservedFolderStates[folder.id] = folder.expanded; - }); - - // If no custom folders exist, ensure "All tables" is always expanded - if (!this.hasCustomFolders()) { - const allTablesFolder = this.folders.find(folder => folder.id === 'all-tables-kitten'); - if (allTablesFolder) { - this.preservedFolderStates[allTablesFolder.id] = true; - this.preservedActiveFolder = allTablesFolder.id; - } - } else { - // Find and save the currently active folder (the one that contains selected table) - const activeFolder = this.findActiveFolder(); - this.preservedActiveFolder = activeFolder ? activeFolder.id : null; - } - } - - private restoreFolderStates() { - // If no custom folders exist, always expand "All tables" - if (!this.hasCustomFolders()) { - const allTablesFolder = this.folders.find(folder => folder.id === 'all-tables-kitten'); - if (allTablesFolder) { - allTablesFolder.expanded = true; - this.currentCollapsedFolder = allTablesFolder; - this.showCollapsedTableList = true; - return; - } - } - - // Restore expanded states of all folders - this.folders.forEach(folder => { - if (Object.hasOwn(this.preservedFolderStates, folder.id)) { - folder.expanded = this.preservedFolderStates[folder.id]; - } - }); - - // In collapsed view, show the table list if any folder was expanded - const hasExpandedFolders = Object.values(this.preservedFolderStates).some(expanded => expanded); - if (hasExpandedFolders) { - // If there was an active folder, use it; otherwise use the first expanded folder - let targetFolder = null; - - if (this.preservedActiveFolder) { - targetFolder = this.folders.find(f => f.id === this.preservedActiveFolder); - } - - if (!targetFolder) { - // Find the first expanded folder - targetFolder = this.folders.find(f => this.preservedFolderStates[f.id]); - } - - if (targetFolder) { - this.currentCollapsedFolder = targetFolder; - this.showCollapsedTableList = true; - } - } - } - - private findActiveFolder(): Folder | null { - if (!this.selectedTable) return null; - - return this.folders.find(folder => - folder.tableIds.includes(this.selectedTable) - ) || null; - } - - private generateFolderId(): string { - return Date.now().toString(); - } - - private getDropIndex(event: DragEvent, folder: Folder): number { - const folderEl = (event.currentTarget as HTMLElement); - const tableItems = folderEl.querySelectorAll('.selected-table-item'); - const y = event.clientY; - - for (let i = 0; i < tableItems.length; i++) { - const rect = tableItems[i].getBoundingClientRect(); - const midY = rect.top + rect.height / 2; - if (y < midY) { - return i; - } - } - return folder.tableIds.length; - } - - private saveFolders() { - try { - this.tableCategories = this.folders - .map(folder => ({ - category_id: folder.id, - category_name: folder.name, - category_color: folder.iconColor, - tables: folder.tableIds - })); - this._tablesService.updateTablesFolders(this.connectionID, this.tableCategories).subscribe({ - next: () => { - console.log('Connection settings updated with folders.'); - }, - error: (error) => { - console.error('Error updating connection settings with folders:', error); - } - }); - } catch (e) { - console.error('Error saving folders:', e); - } - } - - // Table reordering within folders - isReorderingEnabled(folder: Folder): boolean { - return this.accessLevel === 'edit' - && !this.collapsed - && (!this.substringToSearch || this.substringToSearch.trim() === ''); - } - - onTableReorder(event: CdkDragDrop, folder: Folder) { - if (event.previousIndex === event.currentIndex) { - return; - } - moveItemInArray(folder.tableIds, event.previousIndex, event.currentIndex); - this.saveFolders(); - } - - // Drag and drop methods - onTableDragStart(event: DragEvent, table: TableProperties) { - this.draggedTable = table; - if (event.dataTransfer) { - event.dataTransfer.effectAllowed = 'move'; - event.dataTransfer.setData('text/plain', table.table); - } - } - - onTableDragEnd(_event: DragEvent) { - this.draggedTable = null; - this.dragOverFolder = null; - } - - onFolderDragOver(event: DragEvent, folderId: string) { - event.preventDefault(); - if (event.dataTransfer) { - event.dataTransfer.dropEffect = 'move'; - } - this.dragOverFolder = folderId; - } - - onFolderDragLeave(event: DragEvent, _folderId: string) { - // Only clear if we're actually leaving the collection (not moving to a child element) - const rect = (event.currentTarget as HTMLElement).getBoundingClientRect(); - const x = event.clientX; - const y = event.clientY; - - if (x < rect.left || x > rect.right || y < rect.top || y > rect.bottom) { - this.dragOverFolder = null; - } - } - - onFolderDrop(event: DragEvent, folder: Folder) { - event.preventDefault(); - - if (this.draggedTable) { - // Check if table is already in this specific folder - if (folder.tableIds.includes(this.draggedTable.table)) { - // Table already exists in this folder, do nothing (no notification) - return; - } else { - // Determine insertion index based on drop position - const insertIndex = this.getDropIndex(event, folder); - folder.tableIds.splice(insertIndex, 0, this.draggedTable.table); - // Remove empty flag when adding tables via drag and drop - if (folder.isEmpty) { - folder.isEmpty = false; - } - this.saveFolders(); - } - } - - this.draggedTable = null; - this.dragOverFolder = null; - } + @Input() connectionID: string; + @Input() connectionTitle: string; + @Input() tableFolders: Category[]; + @Input() selectedTable: string; + @Input() collapsed: boolean; + @Input() uiSettings: any; + @Input() accessLevel: AccessLevel; + + @Output() expandSidebar = new EventEmitter(); + + public tableCategories: TableCategory[] = []; + public substringToSearch: string; + public foundTables: TableProperties[]; + public folders: Folder[] = []; + + // Dialog state + public currentFolder: Folder | null = null; + + // Drag and drop state + public draggedTable: TableProperties | null = null; + public dragOverFolder: string | null = null; + + // Collapsed state + public showCollapsedTableList: boolean = false; + public currentCollapsedFolder: Folder | null = null; + + // State preservation + private preservedFolderStates: { [key: string]: boolean } = {}; + private preservedActiveFolder: string | null = null; + + // Search state preservation + private preSearchFolderStates: { [key: string]: boolean } | null = null; + + // Folder icon colors + public folderIconColors = [ + { name: 'Default', value: 'rgba(0, 0, 0, 0.87)' }, + { name: 'Blue', value: '#2196F3' }, + { name: 'Green', value: '#4CAF50' }, + { name: 'Orange', value: '#FF9800' }, + { name: 'Red', value: '#F44336' }, + { name: 'Purple', value: '#9C27B0' }, + { name: 'Teal', value: '#009688' }, + { name: 'Pink', value: '#E91E63' }, + ]; + + constructor( + private _tableState: TableStateService, + private _tablesService: TablesService, + private _uiSettingsService: UiSettingsService, + private dialog: MatDialog, + ) {} + + ngOnInit() { + this.foundTables = this.tableFolders + .find((folder: any) => folder.category_id === 'all-tables-kitten') + ?.tables.map((table: TableProperties) => { + return { + table: table.table, + display_name: table.display_name, + normalizedTableName: normalizeTableName(table.table), + permissions: table.permissions, + }; + }); + this.folders = this.tableFolders.map((category) => { + const tableIds = category.tables.map((table: TableProperties) => table.table); + return { + id: category.category_id, + name: category.category_name, + expanded: category.category_id === 'all-tables-kitten' ? true : false, + tableIds, + iconColor: category.category_color, + }; + }); + + const expandedFolders = this.uiSettings?.tableFoldersExpanded || ['0']; + if (expandedFolders && expandedFolders.length > 0) { + this.folders.forEach((folder) => { + folder.expanded = expandedFolders.includes(folder.id); + }); + } + + // If no custom folders exist, always keep "All tables" expanded + if (!this.hasCustomFolders()) { + const allTablesFolder = this.folders.find((folder) => folder.id === 'all-tables-kitten'); + if (allTablesFolder) { + allTablesFolder.expanded = true; + } + } + + console.log('ngOnInit - showCollapsedTableList initialized to:', this.showCollapsedTableList); + } + + ngOnChanges(changes: SimpleChanges) { + if (changes.collapsed) { + if (changes.collapsed.currentValue === true) { + // Sidebar is being collapsed - preserve current state + this.preserveFolderStates(); + } else if (changes.collapsed.currentValue === false) { + // Sidebar is being expanded - restore preserved state + this.restoreFolderStates(); + } + } + } + + searchSubstring() { + if (!this.substringToSearch || this.substringToSearch.trim() === '') { + this.foundTables = this.allTables; + // Restore folder expanded states from before the search + if (this.preSearchFolderStates) { + this.folders.forEach((folder) => { + if (Object.hasOwn(this.preSearchFolderStates, folder.id)) { + folder.expanded = this.preSearchFolderStates[folder.id]; + } + }); + this.preSearchFolderStates = null; + } + return; + } + + // Save folder states before the first search modification + if (!this.preSearchFolderStates) { + this.preSearchFolderStates = {}; + this.folders.forEach((folder) => { + this.preSearchFolderStates[folder.id] = folder.expanded; + }); + } + + const searchTerm = this.substringToSearch.toLowerCase(); + + // Filter all tables by search term + this.foundTables = this.allTables.filter( + (tableItem) => + tableItem.table.toLowerCase().includes(searchTerm) || + tableItem.display_name?.toLowerCase().includes(searchTerm) || + tableItem.normalizedTableName?.toLowerCase().includes(searchTerm), + ); + + // Expand all folders that contain matching tables + this.folders.forEach((folder) => { + const folderTables = this.getFolderTables(folder); + const hasMatchingTables = folderTables.some((table) => + this.foundTables.some((foundTable) => foundTable.table === table.table), + ); + + if (hasMatchingTables) { + folder.expanded = true; + } else { + folder.expanded = false; + } + }); + } + + getTableName(table: TableProperties) { + return table.display_name || table.normalizedTableName || table.table; + } + + getTableNameLength(tableName: string) { + return tableName.length; + } + + closeSidebar() { + this._tableState.clearSelection(); + this._tableState.closeAIpanel(); + } + + onAddFolder() { + const newFolder: Folder = { + id: this.generateFolderId(), + name: `Folder ${this.folders.length}`, + expanded: true, // Разворачиваем папку сразу после создания + tableIds: [], + isEmpty: true, // Mark as empty for special styling + }; + this.folders.push(newFolder); + console.log('onAddFolder'); + this.saveFolders(); + } + + toggleFolder(folderId: string) { + const folder = this.folders.find((f) => f.id === folderId); + if (folder) { + // Prevent collapsing "All tables" when no custom folders exist + if (folder.id === 'all-tables-kitten' && folder.expanded && !this.hasCustomFolders()) { + return; + } + folder.expanded = !folder.expanded; + const expandedFolders = this.folders.filter((f) => f.expanded).map((f) => f.id); + this._uiSettingsService.updateConnectionSetting(this.connectionID, 'tableFoldersExpanded', expandedFolders); + } + } + + onCollapsedFolderClick(folder: Folder) { + console.log('Clicked on folder:', folder.name); + + // If clicking on the same folder that's already open, close it + if (this.currentCollapsedFolder?.id === folder.id) { + this.showCollapsedTableList = false; + this.currentCollapsedFolder = null; + } else { + // If clicking on a different folder, open it immediately + this.showCollapsedTableList = true; + this.currentCollapsedFolder = folder; + } + } + + getCollapsedTableList(): TableProperties[] { + if (!this.currentCollapsedFolder) { + console.log('No current collapsed folder'); + return []; + } + + const tables = this.getFolderTables(this.currentCollapsedFolder); + console.log('getCollapsedTableList - tables:', tables); + return tables; + } + + navigateToTable(_table: TableProperties) { + // This method is called when clicking on a table in collapsed mode + // The actual navigation is handled by routerLink in the template + // We just need to close the sidebar after navigation + this.closeSidebar(); + } + + toggleCollapsedSearch() { + // Open the sidebar and activate search + this.expandSidebar.emit(); + // Focus on search input after sidebar expands + setTimeout(() => { + const input = document.querySelector('.search-input input') as HTMLInputElement; + if (input) { + input.focus(); + input.select(); + } + }, 300); // Wait for sidebar animation to complete + } + + deleteFolder(folder: Folder) { + const dialogData: DbFolderDeleteDialogData = { + folderName: folder.name, + tableCount: folder.tableIds.length, + }; + + const dialogRef = this.dialog.open(DbFolderDeleteDialogComponent, { + width: '24em', + data: dialogData, + panelClass: 'db-folder-delete-dialog', + }); + + dialogRef.afterClosed().subscribe((confirmed: boolean) => { + if (confirmed) { + const index = this.folders.findIndex((f) => f.id === folder.id); + if (index > -1) { + this.folders.splice(index, 1); + this.saveFolders(); + } + } + }); + } + + getFolderTables(folder: Folder): TableProperties[] { + // Preserve folder.tableIds order by mapping through tableIds + const allTablesMap = new Map(this.allTables.map((t) => [t.table, t])); + const folderTables = folder.tableIds + .map((tableId) => allTablesMap.get(tableId)) + .filter((table): table is TableProperties => !!table); + + // If there's a search term, filter the collection tables too + if (this.substringToSearch && this.substringToSearch.trim() !== '') { + const searchTerm = this.substringToSearch.toLowerCase(); + return folderTables.filter( + (table) => + table.table.toLowerCase().includes(searchTerm) || + table.display_name?.toLowerCase().includes(searchTerm) || + table.normalizedTableName?.toLowerCase().includes(searchTerm), + ); + } + + return folderTables; + } + + getAvailableTables(folder: Folder | null): TableProperties[] { + if (!folder) return []; + return this.allTables.filter((table) => !folder.tableIds.includes(table.table)); + } + + isTableInAnyFolder(table: TableProperties): boolean { + return this.folders.some((folder) => folder.tableIds.includes(table.table)); + } + + shouldShowFolder(folder: Folder): boolean { + // If no search term, show all folders + if (!this.substringToSearch || this.substringToSearch.trim() === '') { + return true; + } + + // Show collection if it has tables matching the search + const matchingTables = this.getFolderTables(folder); + return matchingTables.length > 0; + } + + showEditTablesDialog(folder: Folder) { + // Expand the folder if it's collapsed + folder.expanded = true; + + const dialogData: DbFolderEditDialogData = { + folder: folder, + tables: this.allTables, + folderIconColors: this.folderIconColors, + }; + + const dialogRef = this.dialog.open(DbFolderEditDialogComponent, { + width: '32em', + data: dialogData, + panelClass: 'db-folder-edit-dialog', + }); + + dialogRef.afterClosed().subscribe((result: Folder | undefined) => { + if (result) { + // Update the folder with the result from dialog + const index = this.folders.findIndex((f) => f.id === result.id); + if (index !== -1) { + this.folders[index] = result; + this.saveFolders(); + } + } + }); + } + + get allTables(): TableProperties[] { + return this.tableFolders?.find((folder: any) => folder.category_id === 'all-tables-kitten')?.tables || []; + } + + isTableInFolder(table: TableProperties): boolean { + return this.currentFolder ? this.currentFolder.tableIds.includes(table.table) : false; + } + + isTableInCurrentDraggedFolder(table: TableProperties, folder: Folder): boolean { + return this.draggedTable && this.draggedTable.table === table.table && folder.tableIds.includes(table.table); + } + + toggleTableInFolder(table: TableProperties) { + if (!this.currentFolder) return; + + const tableId = table.table; + const isInFolder = this.currentFolder.tableIds.includes(tableId); + + if (isInFolder) { + // Remove from folder + this.currentFolder.tableIds = this.currentFolder.tableIds.filter((id) => id !== tableId); + } else { + // Add to folder + this.currentFolder.tableIds.push(tableId); + // Remove empty flag when adding tables + if (this.currentFolder.isEmpty) { + this.currentFolder.isEmpty = false; + } + } + + console.log('toggleTableInFolder'); + this.saveFolders(); + } + + removeTableFromFolder(folder: Folder, table: TableProperties, event: Event) { + event.stopPropagation(); + folder.tableIds = folder.tableIds.filter((id) => id !== table.table); + console.log('removeTableFromFolder'); + this.saveFolders(); + } + + trackByFolderId(_index: number, folder: Folder): string { + return folder.id; + } + + hasCustomFolders(): boolean { + // Check if there are folders other than "All tables" + return this.folders.some((folder) => folder.id !== 'all-tables-kitten'); + } + + private preserveFolderStates() { + // Save expanded states of all folders + this.folders.forEach((folder) => { + this.preservedFolderStates[folder.id] = folder.expanded; + }); + + // If no custom folders exist, ensure "All tables" is always expanded + if (!this.hasCustomFolders()) { + const allTablesFolder = this.folders.find((folder) => folder.id === 'all-tables-kitten'); + if (allTablesFolder) { + this.preservedFolderStates[allTablesFolder.id] = true; + this.preservedActiveFolder = allTablesFolder.id; + } + } else { + // Find and save the currently active folder (the one that contains selected table) + const activeFolder = this.findActiveFolder(); + this.preservedActiveFolder = activeFolder ? activeFolder.id : null; + } + } + + private restoreFolderStates() { + // If no custom folders exist, always expand "All tables" + if (!this.hasCustomFolders()) { + const allTablesFolder = this.folders.find((folder) => folder.id === 'all-tables-kitten'); + if (allTablesFolder) { + allTablesFolder.expanded = true; + this.currentCollapsedFolder = allTablesFolder; + this.showCollapsedTableList = true; + return; + } + } + + // Restore expanded states of all folders + this.folders.forEach((folder) => { + if (Object.hasOwn(this.preservedFolderStates, folder.id)) { + folder.expanded = this.preservedFolderStates[folder.id]; + } + }); + + // In collapsed view, show the table list if any folder was expanded + const hasExpandedFolders = Object.values(this.preservedFolderStates).some((expanded) => expanded); + if (hasExpandedFolders) { + // If there was an active folder, use it; otherwise use the first expanded folder + let targetFolder = null; + + if (this.preservedActiveFolder) { + targetFolder = this.folders.find((f) => f.id === this.preservedActiveFolder); + } + + if (!targetFolder) { + // Find the first expanded folder + targetFolder = this.folders.find((f) => this.preservedFolderStates[f.id]); + } + + if (targetFolder) { + this.currentCollapsedFolder = targetFolder; + this.showCollapsedTableList = true; + } + } + } + + private findActiveFolder(): Folder | null { + if (!this.selectedTable) return null; + + return this.folders.find((folder) => folder.tableIds.includes(this.selectedTable)) || null; + } + + private generateFolderId(): string { + return Date.now().toString(); + } + + private getDropIndex(event: DragEvent, folder: Folder): number { + const folderEl = event.currentTarget as HTMLElement; + const tableItems = folderEl.querySelectorAll('.selected-table-item'); + const y = event.clientY; + + for (let i = 0; i < tableItems.length; i++) { + const rect = tableItems[i].getBoundingClientRect(); + const midY = rect.top + rect.height / 2; + if (y < midY) { + return i; + } + } + return folder.tableIds.length; + } + + private saveFolders() { + try { + this.tableCategories = this.folders.map((folder) => ({ + category_id: folder.id, + category_name: folder.name, + category_color: folder.iconColor, + tables: folder.tableIds, + })); + this._tablesService.updateTablesFolders(this.connectionID, this.tableCategories).subscribe({ + next: () => { + console.log('Connection settings updated with folders.'); + }, + error: (error) => { + console.error('Error updating connection settings with folders:', error); + }, + }); + } catch (e) { + console.error('Error saving folders:', e); + } + } + + // Table reordering within folders + isReorderingEnabled(folder: Folder): boolean { + return ( + this.accessLevel === 'edit' && + !this.collapsed && + (!this.substringToSearch || this.substringToSearch.trim() === '') + ); + } + + onTableReorder(event: CdkDragDrop, folder: Folder) { + if (event.previousIndex === event.currentIndex) { + return; + } + moveItemInArray(folder.tableIds, event.previousIndex, event.currentIndex); + this.saveFolders(); + } + + // Drag and drop methods + onTableDragStart(event: DragEvent, table: TableProperties) { + this.draggedTable = table; + if (event.dataTransfer) { + event.dataTransfer.effectAllowed = 'move'; + event.dataTransfer.setData('text/plain', table.table); + } + } + + onTableDragEnd(_event: DragEvent) { + this.draggedTable = null; + this.dragOverFolder = null; + } + + onFolderDragOver(event: DragEvent, folderId: string) { + event.preventDefault(); + if (event.dataTransfer) { + event.dataTransfer.dropEffect = 'move'; + } + this.dragOverFolder = folderId; + } + + onFolderDragLeave(event: DragEvent, _folderId: string) { + // Only clear if we're actually leaving the collection (not moving to a child element) + const rect = (event.currentTarget as HTMLElement).getBoundingClientRect(); + const x = event.clientX; + const y = event.clientY; + + if (x < rect.left || x > rect.right || y < rect.top || y > rect.bottom) { + this.dragOverFolder = null; + } + } + + onFolderDrop(event: DragEvent, folder: Folder) { + event.preventDefault(); + + if (this.draggedTable) { + // Check if table is already in this specific folder + if (folder.tableIds.includes(this.draggedTable.table)) { + // Table already exists in this folder, do nothing (no notification) + return; + } else { + // Determine insertion index based on drop position + const insertIndex = this.getDropIndex(event, folder); + folder.tableIds.splice(insertIndex, 0, this.draggedTable.table); + // Remove empty flag when adding tables via drag and drop + if (folder.isEmpty) { + folder.isEmpty = false; + } + this.saveFolders(); + } + } + + this.draggedTable = null; + this.dragOverFolder = null; + } } diff --git a/frontend/src/app/components/dashboards/dashboards-list/dashboards-list.component.css b/frontend/src/app/components/dashboards/dashboards-list/dashboards-list.component.css index ce68abd34..818a7e504 100644 --- a/frontend/src/app/components/dashboards/dashboards-list/dashboards-list.component.css +++ b/frontend/src/app/components/dashboards/dashboards-list/dashboards-list.component.css @@ -277,19 +277,48 @@ } /* Illustration — light theme */ -.illust-card-back { fill: #e2e8f0; stroke: #cbd5e1; stroke-width: 1.5; } -.illust-card-mid { fill: #eef2f7; stroke: #cbd5e1; stroke-width: 1.5; } -.illust-card-front { fill: #f8fafc; stroke: #94a3b8; stroke-width: 1.5; } -.illust-detail { fill: #cbd5e1; } -.illust-detail-accent { fill: #94a3b8; } +.illust-card-back { + fill: #e2e8f0; + stroke: #cbd5e1; + stroke-width: 1.5; +} +.illust-card-mid { + fill: #eef2f7; + stroke: #cbd5e1; + stroke-width: 1.5; +} +.illust-card-front { + fill: #f8fafc; + stroke: #94a3b8; + stroke-width: 1.5; +} +.illust-detail { + fill: #cbd5e1; +} +.illust-detail-accent { + fill: #94a3b8; +} /* Illustration — dark theme */ @media (prefers-color-scheme: dark) { - .illust-card-back { fill: #0f1219; stroke: #1e293b; } - .illust-card-mid { fill: #141820; stroke: #1e293b; } - .illust-card-front { fill: #1a1f2e; stroke: #334155; } - .illust-detail { fill: #1e293b; } - .illust-detail-accent { fill: #334155; } + .illust-card-back { + fill: #0f1219; + stroke: #1e293b; + } + .illust-card-mid { + fill: #141820; + stroke: #1e293b; + } + .illust-card-front { + fill: #1a1f2e; + stroke: #334155; + } + .illust-detail { + fill: #1e293b; + } + .illust-detail-accent { + fill: #334155; + } } .no-dashboards h3 { diff --git a/frontend/src/app/components/dashboards/dashboards-list/dashboards-list.component.ts b/frontend/src/app/components/dashboards/dashboards-list/dashboards-list.component.ts index 4c726590f..28550d2ae 100644 --- a/frontend/src/app/components/dashboards/dashboards-list/dashboards-list.component.ts +++ b/frontend/src/app/components/dashboards/dashboards-list/dashboards-list.component.ts @@ -20,10 +20,10 @@ import { ConnectionsService } from 'src/app/services/connections.service'; import { DashboardsService } from 'src/app/services/dashboards.service'; import { PlaceholderDashboardsComponent } from '../../skeletons/placeholder-dashboards/placeholder-dashboards.component'; import { AlertComponent } from '../../ui-components/alert/alert.component'; +import { ChartMiniPreviewComponent } from '../chart-mini-preview/chart-mini-preview.component'; import { DashboardDeleteDialogComponent } from '../dashboard-delete-dialog/dashboard-delete-dialog.component'; import { DashboardEditDialogComponent } from '../dashboard-edit-dialog/dashboard-edit-dialog.component'; import { DashboardsSidebarComponent } from '../dashboards-sidebar/dashboards-sidebar.component'; -import { ChartMiniPreviewComponent } from '../chart-mini-preview/chart-mini-preview.component'; @Component({ selector: 'app-dashboards-list', diff --git a/frontend/src/app/components/dashboards/dashboards-sidebar/dashboards-sidebar.component.css b/frontend/src/app/components/dashboards/dashboards-sidebar/dashboards-sidebar.component.css index f6565b527..437b413ee 100644 --- a/frontend/src/app/components/dashboards/dashboards-sidebar/dashboards-sidebar.component.css +++ b/frontend/src/app/components/dashboards/dashboards-sidebar/dashboards-sidebar.component.css @@ -28,7 +28,7 @@ .sidebar-toggle-button { align-self: flex-start; margin-top: 8px; - margin-left: 12px; + margin-left: 12px; margin-bottom: 8px; } diff --git a/frontend/src/app/components/dashboards/dashboards-sidebar/dashboards-sidebar.component.ts b/frontend/src/app/components/dashboards/dashboards-sidebar/dashboards-sidebar.component.ts index 762ed7b9e..f918151d8 100644 --- a/frontend/src/app/components/dashboards/dashboards-sidebar/dashboards-sidebar.component.ts +++ b/frontend/src/app/components/dashboards/dashboards-sidebar/dashboards-sidebar.component.ts @@ -1,24 +1,17 @@ -import { AfterViewInit, Component, inject, input, OnInit, signal } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { RouterModule } from '@angular/router'; -import { MatListModule } from '@angular/material/list'; -import { MatIconModule } from '@angular/material/icon'; +import { AfterViewInit, Component, inject, input, OnInit, signal } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; +import { MatIconModule } from '@angular/material/icon'; +import { MatListModule } from '@angular/material/list'; import { MatTooltipModule } from '@angular/material/tooltip'; +import { RouterModule } from '@angular/router'; import { UiSettingsService } from '../../../services/ui-settings.service'; @Component({ selector: 'app-dashboards-sidebar', templateUrl: './dashboards-sidebar.component.html', styleUrls: ['./dashboards-sidebar.component.css'], - imports: [ - CommonModule, - RouterModule, - MatListModule, - MatIconModule, - MatButtonModule, - MatTooltipModule, - ], + imports: [CommonModule, RouterModule, MatListModule, MatIconModule, MatButtonModule, MatTooltipModule], }) export class DashboardsSidebarComponent implements OnInit, AfterViewInit { connectionId = input.required(); diff --git a/frontend/src/app/components/db-table-row-edit/db-table-row-edit.component.css b/frontend/src/app/components/db-table-row-edit/db-table-row-edit.component.css index eb0150a2e..163666bf7 100644 --- a/frontend/src/app/components/db-table-row-edit/db-table-row-edit.component.css +++ b/frontend/src/app/components/db-table-row-edit/db-table-row-edit.component.css @@ -1,5 +1,5 @@ :host app-alert:not(:empty) { - --alert-margin: 24px; + --alert-margin: 24px; } .page { diff --git a/frontend/src/app/components/email-change/email-change.component.css b/frontend/src/app/components/email-change/email-change.component.css index 8ac2b23cf..1ff352eac 100644 --- a/frontend/src/app/components/email-change/email-change.component.css +++ b/frontend/src/app/components/email-change/email-change.component.css @@ -1,30 +1,30 @@ :host app-alert:not(:empty) { - --alert-margin: 24px; + --alert-margin: 24px; - position: absolute; - top: var(--mat-toolbar-standard-height); - width: calc(100% - 48px); + position: absolute; + top: var(--mat-toolbar-standard-height); + width: calc(100% - 48px); } .wrapper { - display: flex; - flex-direction: column; - justify-content: center; - height: 100%; - margin: 0 auto; - width: clamp(300px, 33vw, 600px); + display: flex; + flex-direction: column; + justify-content: center; + height: 100%; + margin: 0 auto; + width: clamp(300px, 33vw, 600px); } .new-email-form { - display: flex; - flex-direction: column; + display: flex; + flex-direction: column; } .email-input { - margin-top: 8px; - width: 100%; + margin-top: 8px; + width: 100%; } .new-email-button { - align-self: flex-end; -} \ No newline at end of file + align-self: flex-end; +} diff --git a/frontend/src/app/components/email-change/email-change.component.ts b/frontend/src/app/components/email-change/email-change.component.ts index 3a455d32c..d4d9dd40c 100644 --- a/frontend/src/app/components/email-change/email-change.component.ts +++ b/frontend/src/app/components/email-change/email-change.component.ts @@ -1,60 +1,60 @@ -import { ActivatedRoute, ParamMap, Router } from '@angular/router'; -import { Component, OnInit } from '@angular/core'; - -import { AlertComponent } from '../ui-components/alert/alert.component'; import { CommonModule } from '@angular/common'; -import { EmailValidationDirective } from 'src/app/directives/emailValidator.directive'; +import { Component, OnInit } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { MatButtonModule } from '@angular/material/button'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatIconModule } from '@angular/material/icon'; import { MatInputModule } from '@angular/material/input'; -import { UserService } from 'src/app/services/user.service'; +import { ActivatedRoute, ParamMap, Router } from '@angular/router'; import { map } from 'rxjs/operators'; +import { EmailValidationDirective } from 'src/app/directives/emailValidator.directive'; +import { UserService } from 'src/app/services/user.service'; +import { AlertComponent } from '../ui-components/alert/alert.component'; @Component({ - selector: 'app-email-change', - templateUrl: './email-change.component.html', - styleUrls: ['./email-change.component.css'], - imports: [ - CommonModule, - FormsModule, - MatFormFieldModule, - MatInputModule, - MatButtonModule, - MatIconModule, - EmailValidationDirective, - AlertComponent - ] + selector: 'app-email-change', + templateUrl: './email-change.component.html', + styleUrls: ['./email-change.component.css'], + imports: [ + CommonModule, + FormsModule, + MatFormFieldModule, + MatInputModule, + MatButtonModule, + MatIconModule, + EmailValidationDirective, + AlertComponent, + ], }) export class EmailChangeComponent implements OnInit { + public token: string; + public newEmail: string; + public submitting: boolean; - public token: string; - public newEmail: string; - public submitting: boolean; - - constructor( - private _userService: UserService, - private route: ActivatedRoute, - private router: Router - ) { } + constructor( + private _userService: UserService, + private route: ActivatedRoute, + private router: Router, + ) {} - ngOnInit(): void { - this.route.paramMap - .pipe( - map((params: ParamMap) => { - this.token = params.get('change-token'); - }) - ).subscribe(); - } + ngOnInit(): void { + this.route.paramMap + .pipe( + map((params: ParamMap) => { + this.token = params.get('change-token'); + }), + ) + .subscribe(); + } - updateEmail() { - this.submitting = true; - this._userService.changeEmail(this.token, this.newEmail) - .subscribe(() => { - this._userService.fetchUser().subscribe(() => this.router.navigate(['/user-settings'])) - }, - () => this.submitting = false, - () => this.submitting = false); - } + updateEmail() { + this.submitting = true; + this._userService.changeEmail(this.token, this.newEmail).subscribe( + () => { + this._userService.fetchUser().subscribe(() => this.router.navigate(['/user-settings'])); + }, + () => (this.submitting = false), + () => (this.submitting = false), + ); + } } diff --git a/frontend/src/app/components/email-verification/email-verification.component.css b/frontend/src/app/components/email-verification/email-verification.component.css index 500691aa6..eb1ae07e3 100644 --- a/frontend/src/app/components/email-verification/email-verification.component.css +++ b/frontend/src/app/components/email-verification/email-verification.component.css @@ -1,15 +1,15 @@ :host app-alert:not(:empty) { - --alert-margin: 24px; + --alert-margin: 24px; - position: absolute; - top: var(--mat-toolbar-standard-height); - width: calc(100% - 48px); + position: absolute; + top: var(--mat-toolbar-standard-height); + width: calc(100% - 48px); } .loader { - display: flex; - align-items: center; - justify-content: center; - height: 100%; - width: 100%; -} \ No newline at end of file + display: flex; + align-items: center; + justify-content: center; + height: 100%; + width: 100%; +} diff --git a/frontend/src/app/components/email-verification/email-verification.component.ts b/frontend/src/app/components/email-verification/email-verification.component.ts index 946f253a6..1c46b81af 100644 --- a/frontend/src/app/components/email-verification/email-verification.component.ts +++ b/frontend/src/app/components/email-verification/email-verification.component.ts @@ -1,48 +1,44 @@ -import { ActivatedRoute, ParamMap, Router } from '@angular/router'; -import { Component, OnInit } from '@angular/core'; - -import { AlertComponent } from '../ui-components/alert/alert.component'; -import { AuthService } from 'src/app/services/auth.service'; import { CommonModule } from '@angular/common'; +import { Component, OnInit } from '@angular/core'; import { FormsModule } from '@angular/forms'; -import { RouterModule } from '@angular/router'; +import { ActivatedRoute, ParamMap, Router, RouterModule } from '@angular/router'; import { map } from 'rxjs/operators'; +import { AuthService } from 'src/app/services/auth.service'; +import { AlertComponent } from '../ui-components/alert/alert.component'; @Component({ - selector: 'app-email-verification', - templateUrl: './email-verification.component.html', - styleUrls: ['./email-verification.component.css'], - imports: [CommonModule, FormsModule, RouterModule, AlertComponent] + selector: 'app-email-verification', + templateUrl: './email-verification.component.html', + styleUrls: ['./email-verification.component.css'], + imports: [CommonModule, FormsModule, RouterModule, AlertComponent], }) export class EmailVerificationComponent implements OnInit { + isVerified: boolean = null; + isLoggedin: boolean = null; - isVerified: boolean = null; - isLoggedin: boolean = null; - - constructor( - private _auth: AuthService, - private route: ActivatedRoute, - public router: Router, - ) { } - - ngOnInit(): void { - this.route.paramMap - .pipe( - map((params: ParamMap) => { - const verificationToken = params.get('verification-token'); - const expirationTime = localStorage.getItem('token_expiration'); + constructor( + private _auth: AuthService, + private route: ActivatedRoute, + public router: Router, + ) {} - this._auth.verifyEmail(verificationToken) - .subscribe(() => { - if (expirationTime) { - this.router.navigate(['/user-settings']); - } else { - this.router.navigate(['/login']); - } - this.isVerified = true; - }); - }) - ).subscribe(); - } + ngOnInit(): void { + this.route.paramMap + .pipe( + map((params: ParamMap) => { + const verificationToken = params.get('verification-token'); + const expirationTime = localStorage.getItem('token_expiration'); + this._auth.verifyEmail(verificationToken).subscribe(() => { + if (expirationTime) { + this.router.navigate(['/user-settings']); + } else { + this.router.navigate(['/login']); + } + this.isVerified = true; + }); + }), + ) + .subscribe(); + } } diff --git a/frontend/src/app/components/feature-notification/feature-notification.component.css b/frontend/src/app/components/feature-notification/feature-notification.component.css index 72183fc9a..f5e7c6dcb 100644 --- a/frontend/src/app/components/feature-notification/feature-notification.component.css +++ b/frontend/src/app/components/feature-notification/feature-notification.component.css @@ -1,49 +1,49 @@ .feature-notification { - position: fixed; - bottom: 32px; - left: 32px; - z-index: 1000; - display: flex; - flex-direction: column; - padding: 16px; - border-radius: 8px; - background-color: #fff; - box-shadow: 0px 4px 4px 0px rgba(0, 0, 0, 0.25); - width: clamp(200px, 25vw, 400px); + position: fixed; + bottom: 32px; + left: 32px; + z-index: 1000; + display: flex; + flex-direction: column; + padding: 16px; + border-radius: 8px; + background-color: #fff; + box-shadow: 0px 4px 4px 0px rgba(0, 0, 0, 0.25); + width: clamp(200px, 25vw, 400px); } @media (prefers-color-scheme: dark) { - .feature-notification { - background-color: var(--surface-dark-color); - } + .feature-notification { + background-color: var(--surface-dark-color); + } } .feature-notification__image { - border-radius: 8px; - height: auto; - margin-bottom: 16px; - width: 100%; + border-radius: 8px; + height: auto; + margin-bottom: 16px; + width: 100%; } .feature-notification__title { - font-size: 20px !important; - font-weight: 600 !important; - line-height: 1.15em !important; + font-size: 20px !important; + font-weight: 600 !important; + line-height: 1.15em !important; } .feature-notification__description { - color: rgba(0,0,0,0.64); + color: rgba(0, 0, 0, 0.64); } @media (prefers-color-scheme: dark) { - .feature-notification__description { - color: rgba(255,255,255,0.64); - } + .feature-notification__description { + color: rgba(255, 255, 255, 0.64); + } } .actions { - display: flex; - justify-content: flex-end; - gap: 8px; - margin-top: 16px; -} \ No newline at end of file + display: flex; + justify-content: flex-end; + gap: 8px; + margin-top: 16px; +} diff --git a/frontend/src/app/components/feature-notification/feature-notification.component.spec.ts b/frontend/src/app/components/feature-notification/feature-notification.component.spec.ts index f5c113de9..d3df47328 100644 --- a/frontend/src/app/components/feature-notification/feature-notification.component.spec.ts +++ b/frontend/src/app/components/feature-notification/feature-notification.component.spec.ts @@ -1,27 +1,23 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { FeatureNotificationComponent } from './feature-notification.component'; import { ActivatedRoute } from '@angular/router'; +import { FeatureNotificationComponent } from './feature-notification.component'; describe('FeatureNotificationComponent', () => { - let component: FeatureNotificationComponent; - let fixture: ComponentFixture; + let component: FeatureNotificationComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [FeatureNotificationComponent], - providers: [ - { provide: ActivatedRoute, useValue: {} } - ] - }) - .compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [FeatureNotificationComponent], + providers: [{ provide: ActivatedRoute, useValue: {} }], + }).compileComponents(); - fixture = TestBed.createComponent(FeatureNotificationComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + fixture = TestBed.createComponent(FeatureNotificationComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/feature-notification/feature-notification.component.ts b/frontend/src/app/components/feature-notification/feature-notification.component.ts index 203cfc4ab..889077398 100644 --- a/frontend/src/app/components/feature-notification/feature-notification.component.ts +++ b/frontend/src/app/components/feature-notification/feature-notification.component.ts @@ -4,14 +4,11 @@ import { MatButtonModule } from '@angular/material/button'; import { RouterModule } from '@angular/router'; @Component({ - selector: 'app-feature-notification', - imports: [ - MatButtonModule, - RouterModule - ], - templateUrl: './feature-notification.component.html', - styleUrl: './feature-notification.component.css' + selector: 'app-feature-notification', + imports: [MatButtonModule, RouterModule], + templateUrl: './feature-notification.component.html', + styleUrl: './feature-notification.component.css', }) export class FeatureNotificationComponent { - @Output() dismiss = new EventEmitter(); + @Output() dismiss = new EventEmitter(); } diff --git a/frontend/src/app/components/login/sso-dialog/sso-dialog.component.css b/frontend/src/app/components/login/sso-dialog/sso-dialog.component.css index b321285f7..fcaa8cf92 100644 --- a/frontend/src/app/components/login/sso-dialog/sso-dialog.component.css +++ b/frontend/src/app/components/login/sso-dialog/sso-dialog.component.css @@ -1,3 +1,3 @@ .mat-mdc-form-field { - width: 100%; -} \ No newline at end of file + width: 100%; +} diff --git a/frontend/src/app/components/login/sso-dialog/sso-dialog.component.spec.ts b/frontend/src/app/components/login/sso-dialog/sso-dialog.component.spec.ts index 9d7ff79b9..de38d840b 100644 --- a/frontend/src/app/components/login/sso-dialog/sso-dialog.component.spec.ts +++ b/frontend/src/app/components/login/sso-dialog/sso-dialog.component.spec.ts @@ -3,21 +3,20 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { SsoDialogComponent } from './sso-dialog.component'; describe('SsoDialogComponent', () => { - let component: SsoDialogComponent; - let fixture: ComponentFixture; + let component: SsoDialogComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [SsoDialogComponent] - }) - .compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [SsoDialogComponent], + }).compileComponents(); - fixture = TestBed.createComponent(SsoDialogComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + fixture = TestBed.createComponent(SsoDialogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/master-password-dialog/master-password-dialog.component.css b/frontend/src/app/components/master-password-dialog/master-password-dialog.component.css index b321285f7..fcaa8cf92 100644 --- a/frontend/src/app/components/master-password-dialog/master-password-dialog.component.css +++ b/frontend/src/app/components/master-password-dialog/master-password-dialog.component.css @@ -1,3 +1,3 @@ .mat-mdc-form-field { - width: 100%; -} \ No newline at end of file + width: 100%; +} diff --git a/frontend/src/app/components/master-password-dialog/master-password-dialog.component.spec.ts b/frontend/src/app/components/master-password-dialog/master-password-dialog.component.spec.ts index 41d0b2fd5..80620b280 100644 --- a/frontend/src/app/components/master-password-dialog/master-password-dialog.component.spec.ts +++ b/frontend/src/app/components/master-password-dialog/master-password-dialog.component.spec.ts @@ -1,42 +1,41 @@ +import { provideHttpClient } from '@angular/common/http'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { FormsModule } from '@angular/forms'; -import { MatDialogModule, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog'; import { MatSnackBarModule } from '@angular/material/snack-bar'; - -import { MasterPasswordDialogComponent } from './master-password-dialog.component'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { provideHttpClient } from '@angular/common/http'; import { provideRouter } from '@angular/router'; +import { MasterPasswordDialogComponent } from './master-password-dialog.component'; describe('MasterPasswordDialogComponent', () => { - let component: MasterPasswordDialogComponent; - let fixture: ComponentFixture; + let component: MasterPasswordDialogComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [ - MatSnackBarModule, - MatDialogModule, - FormsModule, - MasterPasswordDialogComponent, - BrowserAnimationsModule - ], - providers: [ - provideHttpClient(), - provideRouter([]), - { provide: MAT_DIALOG_DATA, useValue: {} }, - { provide: MatDialogRef, useValue: {} }, - ], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + MatSnackBarModule, + MatDialogModule, + FormsModule, + MasterPasswordDialogComponent, + BrowserAnimationsModule, + ], + providers: [ + provideHttpClient(), + provideRouter([]), + { provide: MAT_DIALOG_DATA, useValue: {} }, + { provide: MatDialogRef, useValue: {} }, + ], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(MasterPasswordDialogComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(MasterPasswordDialogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/master-password-dialog/master-password-dialog.component.ts b/frontend/src/app/components/master-password-dialog/master-password-dialog.component.ts index 0c403bf8c..f7eeab67e 100644 --- a/frontend/src/app/components/master-password-dialog/master-password-dialog.component.ts +++ b/frontend/src/app/components/master-password-dialog/master-password-dialog.component.ts @@ -1,40 +1,38 @@ import { Component, OnInit } from '@angular/core'; - -import { ConnectionsService } from 'src/app/services/connections.service'; import { FormsModule } from '@angular/forms'; import { MatButtonModule } from '@angular/material/button'; -import { MatDialogModule } from '@angular/material/dialog'; -import { MatDialogRef } from '@angular/material/dialog'; +import { MatDialogModule, MatDialogRef } from '@angular/material/dialog'; +import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; import { Router } from '@angular/router'; -import { MatFormFieldModule } from '@angular/material/form-field'; +import { ConnectionsService } from 'src/app/services/connections.service'; @Component({ - selector: 'app-master-password-dialog', - templateUrl: './master-password-dialog.component.html', - styleUrls: ['./master-password-dialog.component.css'], - imports: [FormsModule, MatFormFieldModule, MatDialogModule, MatInputModule, MatButtonModule] + selector: 'app-master-password-dialog', + templateUrl: './master-password-dialog.component.html', + styleUrls: ['./master-password-dialog.component.css'], + imports: [FormsModule, MatFormFieldModule, MatDialogModule, MatInputModule, MatButtonModule], }) export class MasterPasswordDialogComponent implements OnInit { - public password: string = ''; - public connectionID: string | null = null; + public password: string = ''; + public connectionID: string | null = null; - constructor( - public router: Router, - private dialogRef: MatDialogRef, - private _connections: ConnectionsService, - ) { } + constructor( + public router: Router, + private dialogRef: MatDialogRef, + private _connections: ConnectionsService, + ) {} - ngOnInit(): void { - this.connectionID = this._connections.currentConnectionID; - } + ngOnInit(): void { + this.connectionID = this._connections.currentConnectionID; + } - enterMasterPassword() { - localStorage.setItem(`${this.connectionID}__masterKey`, this.password); - let currentUrl = this.router.url; - this.router.routeReuseStrategy.shouldReuseRoute = () => false; - this.router.onSameUrlNavigation = 'reload'; - this.router.navigate([currentUrl]); - this.dialogRef.close(); - } + enterMasterPassword() { + localStorage.setItem(`${this.connectionID}__masterKey`, this.password); + let currentUrl = this.router.url; + this.router.routeReuseStrategy.shouldReuseRoute = () => false; + this.router.onSameUrlNavigation = 'reload'; + this.router.navigate([currentUrl]); + this.dialogRef.close(); + } } diff --git a/frontend/src/app/components/page-loader/page-loader.component.spec.ts b/frontend/src/app/components/page-loader/page-loader.component.spec.ts index 11d2a65c2..55cac08a1 100644 --- a/frontend/src/app/components/page-loader/page-loader.component.spec.ts +++ b/frontend/src/app/components/page-loader/page-loader.component.spec.ts @@ -1,26 +1,25 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { PageLoaderComponent } from './page-loader.component'; import { provideRouter } from '@angular/router'; +import { PageLoaderComponent } from './page-loader.component'; describe('LoaderComponent', () => { - let component: PageLoaderComponent; - let fixture: ComponentFixture; + let component: PageLoaderComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [PageLoaderComponent], - providers: [provideRouter([])] - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [PageLoaderComponent], + providers: [provideRouter([])], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(PageLoaderComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(PageLoaderComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/page-loader/page-loader.component.ts b/frontend/src/app/components/page-loader/page-loader.component.ts index c1630919f..d4dd37473 100644 --- a/frontend/src/app/components/page-loader/page-loader.component.ts +++ b/frontend/src/app/components/page-loader/page-loader.component.ts @@ -3,18 +3,15 @@ import { Component, NgZone, OnInit } from '@angular/core'; import { Router } from '@angular/router'; @Component({ - selector: 'app-page-loader', - templateUrl: './page-loader.component.html', - styleUrls: ['./page-loader.component.css'] + selector: 'app-page-loader', + templateUrl: './page-loader.component.html', + styleUrls: ['./page-loader.component.css'], }) - export class PageLoaderComponent implements OnInit { + constructor( + public router: Router, + _ngZone: NgZone, + ) {} - constructor( - public router: Router,_ngZone: NgZone - ) { } - - ngOnInit() { - - } + ngOnInit() {} } diff --git a/frontend/src/app/components/page-not-found/page-not-found.component.css b/frontend/src/app/components/page-not-found/page-not-found.component.css index 4b0b88a8a..f59d0d424 100644 --- a/frontend/src/app/components/page-not-found/page-not-found.component.css +++ b/frontend/src/app/components/page-not-found/page-not-found.component.css @@ -1,72 +1,72 @@ .wrapper { - display: flex; - align-items: center; - background-image: url('../../../assets/bg/not-found-bg.svg'); - background-repeat: no-repeat; - background-size: auto 127%; - background-position: 100% 30%; - height: calc(100vh - 56px); - padding: 0 max(calc(20vw - 250px), 15%); - width: 100%; + display: flex; + align-items: center; + background-image: url("../../../assets/bg/not-found-bg.svg"); + background-repeat: no-repeat; + background-size: auto 127%; + background-position: 100% 30%; + height: calc(100vh - 56px); + padding: 0 max(calc(20vw - 250px), 15%); + width: 100%; } @media screen and (max-width: 600px) { - .wrapper { - background: none; - height: 100%; - padding: 0 4.5vw; - } + .wrapper { + background: none; + height: 100%; + padding: 0 4.5vw; + } - .wrapper svg { - width: 55%; - } + .wrapper svg { + width: 55%; + } } .message { - display: flex; - flex-direction: column; - align-items: flex-start; - gap: 36px; - width: clamp(300px, 60%, 600px) + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 36px; + width: clamp(300px, 60%, 600px); } @media screen and (max-width: 600px) { - .message { - gap: 16px; - width: 100%; - } + .message { + gap: 16px; + width: 100%; + } } .message__title { - font-weight: 600; - margin-bottom: 16px; + font-weight: 600; + margin-bottom: 16px; } @media screen and (max-width: 600px) { - .message__title { - font-size: 32px; - line-height: 40px; - } + .message__title { + font-size: 32px; + line-height: 40px; + } } .message__text { - font-size: 20px; - margin-bottom: 8px; + font-size: 20px; + margin-bottom: 8px; } @media screen and (max-width: 600px) { - .message__text { - font-size: 18px; - margin-top: 8px; - } + .message__text { + font-size: 18px; + margin-top: 8px; + } } .message__button { - width: 156px; + width: 156px; } @media screen and (max-width: 600px) { - .message__button { - margin-top: 16px; - } -} \ No newline at end of file + .message__button { + margin-top: 16px; + } +} diff --git a/frontend/src/app/components/page-not-found/page-not-found.component.spec.ts b/frontend/src/app/components/page-not-found/page-not-found.component.spec.ts index 359452d5d..f959d1247 100644 --- a/frontend/src/app/components/page-not-found/page-not-found.component.spec.ts +++ b/frontend/src/app/components/page-not-found/page-not-found.component.spec.ts @@ -3,22 +3,22 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { PageNotFoundComponent } from './page-not-found.component'; describe('PageNotFoundComponent', () => { - let component: PageNotFoundComponent; - let fixture: ComponentFixture; + let component: PageNotFoundComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [PageNotFoundComponent] - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [PageNotFoundComponent], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(PageNotFoundComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(PageNotFoundComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/page-not-found/page-not-found.component.ts b/frontend/src/app/components/page-not-found/page-not-found.component.ts index 7a40bee4a..b8efbeed0 100644 --- a/frontend/src/app/components/page-not-found/page-not-found.component.ts +++ b/frontend/src/app/components/page-not-found/page-not-found.component.ts @@ -1,13 +1,10 @@ import { Component, OnInit } from '@angular/core'; @Component({ - selector: 'app-page-not-found', - templateUrl: './page-not-found.component.html', - styleUrls: ['./page-not-found.component.css'] + selector: 'app-page-not-found', + templateUrl: './page-not-found.component.html', + styleUrls: ['./page-not-found.component.css'], }) export class PageNotFoundComponent implements OnInit { - - ngOnInit() { - } - + ngOnInit() {} } diff --git a/frontend/src/app/components/password-change/password-change.component.css b/frontend/src/app/components/password-change/password-change.component.css index 352cad7e3..9658f2dc7 100644 --- a/frontend/src/app/components/password-change/password-change.component.css +++ b/frontend/src/app/components/password-change/password-change.component.css @@ -1,29 +1,29 @@ :host app-alert:not(:empty) { - --alert-margin: 24px; + --alert-margin: 24px; } .wrapper { - display: flex; - flex-direction: column; - justify-content: center; - height: 100%; - margin: 0 auto; - padding: 16px; - max-width: 420px; - min-width: 300px; - width: 90%; + display: flex; + flex-direction: column; + justify-content: center; + height: 100%; + margin: 0 auto; + padding: 16px; + max-width: 420px; + min-width: 300px; + width: 90%; } .password-input { - width: 100%; + width: 100%; } .actions { - display: flex; - justify-content: flex-end; - margin-top: 48px; + display: flex; + justify-content: flex-end; + margin-top: 48px; } .password-form__back { - margin-right: 20px; -} \ No newline at end of file + margin-right: 20px; +} diff --git a/frontend/src/app/components/password-request/password-request.component.css b/frontend/src/app/components/password-request/password-request.component.css index cad2ee910..e6d4ff606 100644 --- a/frontend/src/app/components/password-request/password-request.component.css +++ b/frontend/src/app/components/password-request/password-request.component.css @@ -1,92 +1,92 @@ :host app-alert:not(:empty) { - --alert-margin: 24px; + --alert-margin: 24px; - position: absolute; - top: var(--mat-toolbar-standard-height); - width: calc(100% - 48px); + position: absolute; + top: var(--mat-toolbar-standard-height); + width: calc(100% - 48px); } .wrapper { - display: flex; - flex-direction: column; - justify-content: center; - height: 100%; - margin: 0 auto; - padding: 16px; - max-width: 420px; - min-width: 300px; - width: 90%; + display: flex; + flex-direction: column; + justify-content: center; + height: 100%; + margin: 0 auto; + padding: 16px; + max-width: 420px; + min-width: 300px; + width: 90%; } @media (width <= 600px) { - .wrapper { - padding: 16px 9vw; - width: 100%; - } + .wrapper { + padding: 16px 9vw; + width: 100%; + } } .password-form { - display: flex; - flex-direction: column; - justify-content: center; - margin-top: -16px; + display: flex; + flex-direction: column; + justify-content: center; + margin-top: -16px; } .request-text { - margin-bottom: 20px; + margin-bottom: 20px; } .email-field { - margin-top: 8px; + margin-top: 8px; } .change-password-button { - margin-top: 20px; + margin-top: 20px; } .password-form__field-loader { - position: relative; - background-color: rgba(0, 0, 0, 0.06); - margin-bottom: 16px; - overflow: hidden; - height: 44px; - width: 100%; + position: relative; + background-color: rgba(0, 0, 0, 0.06); + margin-bottom: 16px; + overflow: hidden; + height: 44px; + width: 100%; } .password-form__field-loader::after { - content: ''; - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - transform: translateX(-100%); - background-image: linear-gradient( - 90deg, - transparent 40%, - var(--mat-sidenav-content-background-color, #fff), - transparent 60% - ); - animation: shimmer 800ms ease-in-out infinite alternate; + content: ""; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + transform: translateX(-100%); + background-image: linear-gradient( + 90deg, + transparent 40%, + var(--mat-sidenav-content-background-color, #fff), + transparent 60% + ); + animation: shimmer 800ms ease-in-out infinite alternate; } @media (prefers-color-scheme: dark) { - .password-form__field-loader::after { - background-image: linear-gradient( - 90deg, - transparent 40%, - var(--mat-sidenav-content-background-color, #303030), - transparent 60% - ); - } + .password-form__field-loader::after { + background-image: linear-gradient( + 90deg, + transparent 40%, + var(--mat-sidenav-content-background-color, #303030), + transparent 60% + ); + } } @keyframes shimmer { - 100% { - transform: translateX(100%); - } + 100% { + transform: translateX(100%); + } } .password-form__reset-button { - margin-top: 8px; -} \ No newline at end of file + margin-top: 8px; +} diff --git a/frontend/src/app/components/password-request/password-request.component.ts b/frontend/src/app/components/password-request/password-request.component.ts index 5370a2dd9..cee3d734c 100644 --- a/frontend/src/app/components/password-request/password-request.component.ts +++ b/frontend/src/app/components/password-request/password-request.component.ts @@ -1,69 +1,65 @@ -import { Component, OnInit } from '@angular/core'; - -import { AlertComponent } from '../ui-components/alert/alert.component'; -import { AuthService } from 'src/app/services/auth.service'; -import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { EmailValidationDirective } from 'src/app/directives/emailValidator.directive'; +import { Component, CUSTOM_ELEMENTS_SCHEMA, OnInit } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { MatButtonModule } from '@angular/material/button'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; import { MatSelectModule } from '@angular/material/select'; +import { EmailValidationDirective } from 'src/app/directives/emailValidator.directive'; +import { AuthService } from 'src/app/services/auth.service'; import { UserService } from 'src/app/services/user.service'; +import { AlertComponent } from '../ui-components/alert/alert.component'; @Component({ - selector: 'app-password-request', - templateUrl: './password-request.component.html', - styleUrls: ['./password-request.component.css'], - imports: [ - CommonModule, - FormsModule, - MatFormFieldModule, - MatInputModule, - MatSelectModule, - MatButtonModule, - EmailValidationDirective, - AlertComponent - ], - schemas: [CUSTOM_ELEMENTS_SCHEMA] + selector: 'app-password-request', + templateUrl: './password-request.component.html', + styleUrls: ['./password-request.component.css'], + imports: [ + CommonModule, + FormsModule, + MatFormFieldModule, + MatInputModule, + MatSelectModule, + MatButtonModule, + EmailValidationDirective, + AlertComponent, + ], + schemas: [CUSTOM_ELEMENTS_SCHEMA], }) export class PasswordRequestComponent implements OnInit { - public userEmail: string; - public companyId: string; - public submitting: boolean; - public isLoadingUserCompanies: boolean = false; - public userCompanies: [] = null; - - constructor( - private _userService: UserService, - private _auth: AuthService, - ) { } + public userEmail: string; + public companyId: string; + public submitting: boolean; + public isLoadingUserCompanies: boolean = false; + public userCompanies: [] = null; - ngOnInit(): void { - } + constructor( + private _userService: UserService, + private _auth: AuthService, + ) {} - requestUserCompanies() { - this.isLoadingUserCompanies = true; - this._auth.fetchUserCompanies(this.userEmail) - .subscribe(companies => { - this.userCompanies = companies; - this.isLoadingUserCompanies = false; + ngOnInit(): void {} - if (companies.length === 1) { - this.companyId = companies[0].id; - } - }); - } + requestUserCompanies() { + this.isLoadingUserCompanies = true; + this._auth.fetchUserCompanies(this.userEmail).subscribe((companies) => { + this.userCompanies = companies; + this.isLoadingUserCompanies = false; - requestPassword() { - this.submitting = true; - this._userService.requestPasswordReset(this.userEmail, this.companyId).subscribe(() => { - this.submitting = false; - }, - () => this.submitting = false, - () => this.submitting = false - ); - } + if (companies.length === 1) { + this.companyId = companies[0].id; + } + }); + } + requestPassword() { + this.submitting = true; + this._userService.requestPasswordReset(this.userEmail, this.companyId).subscribe( + () => { + this.submitting = false; + }, + () => (this.submitting = false), + () => (this.submitting = false), + ); + } } diff --git a/frontend/src/app/components/password-reset/password-reset.component.css b/frontend/src/app/components/password-reset/password-reset.component.css index fb191a75c..d5b408da3 100644 --- a/frontend/src/app/components/password-reset/password-reset.component.css +++ b/frontend/src/app/components/password-reset/password-reset.component.css @@ -1,54 +1,54 @@ :host app-alert:not(:empty) { - --alert-margin: 24px; + --alert-margin: 24px; - position: absolute; - top: var(--mat-toolbar-standard-height); - width: calc(100% - 48px); + position: absolute; + top: var(--mat-toolbar-standard-height); + width: calc(100% - 48px); } .wrapper { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - /* margin: 80px auto; */ - height: 100%; - width: 100%; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + /* margin: 80px auto; */ + height: 100%; + width: 100%; } @media (width <= 600px) { - .wrapper { - padding: 16px 9vw; - } + .wrapper { + padding: 16px 9vw; + } } .new-password-form { - display: flex; - flex-direction: column; - /* align-items: flex-start; */ - margin-top: -16px; - width: clamp(300px, 33vw, 600px); + display: flex; + flex-direction: column; + /* align-items: flex-start; */ + margin-top: -16px; + width: clamp(300px, 33vw, 600px); } @media (width <= 600px) { - .new-password-form { - width: 100%; - } + .new-password-form { + width: 100%; + } } .password-input { - /* align-self: stretch; */ - margin-top: 8px; - width: 100%; + /* align-self: stretch; */ + margin-top: 8px; + width: 100%; } .new-password-button { - align-self: flex-end; - margin-top: 8px; + align-self: flex-end; + margin-top: 8px; } @media screen and (max-width: 1401px) { - .new-password-button { - margin-top: 48px; - } -} \ No newline at end of file + .new-password-button { + margin-top: 48px; + } +} diff --git a/frontend/src/app/components/password-reset/password-reset.component.spec.ts b/frontend/src/app/components/password-reset/password-reset.component.spec.ts index 8fce2c5e3..308097d63 100644 --- a/frontend/src/app/components/password-reset/password-reset.component.spec.ts +++ b/frontend/src/app/components/password-reset/password-reset.component.spec.ts @@ -1,42 +1,37 @@ +import { provideHttpClient } from '@angular/common/http'; import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { PasswordResetComponent } from './password-reset.component'; +import { FormsModule } from '@angular/forms'; import { MatSnackBarModule } from '@angular/material/snack-bar'; -import { FormsModule } from '@angular/forms'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { IPasswordStrengthMeterService } from 'angular-password-strength-meter'; -import { provideHttpClient } from '@angular/common/http'; import { provideRouter } from '@angular/router'; +import { IPasswordStrengthMeterService } from 'angular-password-strength-meter'; import { Angulartics2Module } from 'angulartics2'; +import { PasswordResetComponent } from './password-reset.component'; describe('PasswordResetComponent', () => { - let component: PasswordResetComponent; - let fixture: ComponentFixture; + let component: PasswordResetComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [ - MatSnackBarModule, - FormsModule, - PasswordResetComponent, - BrowserAnimationsModule, - Angulartics2Module.forRoot({}) - ], - providers: [ - provideHttpClient(), - provideRouter([]), - { provide: IPasswordStrengthMeterService, useValue: {} } - ] - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + MatSnackBarModule, + FormsModule, + PasswordResetComponent, + BrowserAnimationsModule, + Angulartics2Module.forRoot({}), + ], + providers: [provideHttpClient(), provideRouter([]), { provide: IPasswordStrengthMeterService, useValue: {} }], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(PasswordResetComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(PasswordResetComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/password-reset/password-reset.component.ts b/frontend/src/app/components/password-reset/password-reset.component.ts index 0eecf6183..6f72e27fb 100644 --- a/frontend/src/app/components/password-reset/password-reset.component.ts +++ b/frontend/src/app/components/password-reset/password-reset.component.ts @@ -1,58 +1,48 @@ -import { ActivatedRoute, ParamMap, Router } from '@angular/router'; -import { Component, OnInit } from '@angular/core'; - -import { AlertComponent } from '../ui-components/alert/alert.component'; -import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { CommonModule } from '@angular/common'; +import { Component, CUSTOM_ELEMENTS_SCHEMA, OnInit } from '@angular/core'; import { FormsModule } from '@angular/forms'; -import { UserPasswordComponent } from '../ui-components/user-password/user-password.component'; -import { UserService } from 'src/app/services/user.service'; -import { map } from 'rxjs/operators'; import { MatButtonModule } from '@angular/material/button'; +import { ActivatedRoute, ParamMap, Router } from '@angular/router'; +import { map } from 'rxjs/operators'; +import { UserService } from 'src/app/services/user.service'; +import { AlertComponent } from '../ui-components/alert/alert.component'; +import { UserPasswordComponent } from '../ui-components/user-password/user-password.component'; @Component({ - selector: 'app-password-reset', - templateUrl: './password-reset.component.html', - styleUrls: ['./password-reset.component.css'], - imports: [ - CommonModule, - FormsModule, - MatButtonModule, - AlertComponent, - UserPasswordComponent - ], - schemas: [CUSTOM_ELEMENTS_SCHEMA] + selector: 'app-password-reset', + templateUrl: './password-reset.component.html', + styleUrls: ['./password-reset.component.css'], + imports: [CommonModule, FormsModule, MatButtonModule, AlertComponent, UserPasswordComponent], + schemas: [CUSTOM_ELEMENTS_SCHEMA], }) export class PasswordResetComponent implements OnInit { - - public token: string; - public newPassword: string = ''; - public submitting: boolean; - - constructor( - private router: Router, - private route: ActivatedRoute, - private _userService: UserService - ) { } - - ngOnInit(): void { - this.route.paramMap - .pipe( - map((params: ParamMap) => { - this.token = params.get('verification-token'); - }) - ).subscribe(); - } - - updatePasswordField(updatedValue: string) { - this.newPassword = updatedValue; - } - - updatePassword() { - this._userService.resetPassword(this.token, this.newPassword) - .subscribe(() => { - this.router.navigate(['/login']) - }); - } - + public token: string; + public newPassword: string = ''; + public submitting: boolean; + + constructor( + private router: Router, + private route: ActivatedRoute, + private _userService: UserService, + ) {} + + ngOnInit(): void { + this.route.paramMap + .pipe( + map((params: ParamMap) => { + this.token = params.get('verification-token'); + }), + ) + .subscribe(); + } + + updatePasswordField(updatedValue: string) { + this.newPassword = updatedValue; + } + + updatePassword() { + this._userService.resetPassword(this.token, this.newPassword).subscribe(() => { + this.router.navigate(['/login']); + }); + } } diff --git a/frontend/src/app/components/payment-form/payment-form.component.css b/frontend/src/app/components/payment-form/payment-form.component.css index 65eae206f..4083470d6 100644 --- a/frontend/src/app/components/payment-form/payment-form.component.css +++ b/frontend/src/app/components/payment-form/payment-form.component.css @@ -1,32 +1,32 @@ :host app-alert:not(:empty) { - --alert-margin: 24px; + --alert-margin: 24px; } .payment-page { - margin: var(--top-margin) auto; - width: clamp(18.75em, 96%, 45em); + margin: var(--top-margin) auto; + width: clamp(18.75em, 96%, 45em); } .payment-page__header { - margin: 2em 0 1em; + margin: 2em 0 1em; } .plan-name { - text-transform: capitalize; + text-transform: capitalize; } .billing-form { - display: grid; - grid-template-columns: 1fr 1fr; - grid-gap: 16px; - gap: 16px; + display: grid; + grid-template-columns: 1fr 1fr; + grid-gap: 16px; + gap: 16px; } .full-width { - grid-column: 1 / span 2; + grid-column: 1 / span 2; } .note { - margin-top: 20px !important; - margin-bottom: 52px !important; -} \ No newline at end of file + margin-top: 20px !important; + margin-bottom: 52px !important; +} diff --git a/frontend/src/app/components/payment-form/payment-form.component.spec.ts b/frontend/src/app/components/payment-form/payment-form.component.spec.ts index 9d3b8fbbf..2bf693578 100644 --- a/frontend/src/app/components/payment-form/payment-form.component.spec.ts +++ b/frontend/src/app/components/payment-form/payment-form.component.spec.ts @@ -1,43 +1,40 @@ +import { provideHttpClient } from '@angular/common/http'; import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { ActivatedRoute } from '@angular/router'; import { MatSnackBarModule } from '@angular/material/snack-bar'; +import { ActivatedRoute } from '@angular/router'; +import { RouterTestingModule } from '@angular/router/testing'; import { NgxStripeModule } from 'ngx-stripe'; import { PaymentFormComponent } from './payment-form.component'; -import { RouterTestingModule } from '@angular/router/testing'; -import { provideHttpClient } from '@angular/common/http'; describe('PaymentFormComponent', () => { - let component: PaymentFormComponent; - let fixture: ComponentFixture; + let component: PaymentFormComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [ - RouterTestingModule, - MatSnackBarModule, - NgxStripeModule.forRoot('asdf'), - PaymentFormComponent - ], - providers: [ - provideHttpClient(), - {provide: ActivatedRoute, useValue: { - snapshot: { - queryParams: { - 'plan': 'team', - 'period': 'monthly' - } - }, - }}, - ] - }).compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [RouterTestingModule, MatSnackBarModule, NgxStripeModule.forRoot('asdf'), PaymentFormComponent], + providers: [ + provideHttpClient(), + { + provide: ActivatedRoute, + useValue: { + snapshot: { + queryParams: { + plan: 'team', + period: 'monthly', + }, + }, + }, + }, + ], + }).compileComponents(); - fixture = TestBed.createComponent(PaymentFormComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + fixture = TestBed.createComponent(PaymentFormComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/payment-form/payment-form.component.ts b/frontend/src/app/components/payment-form/payment-form.component.ts index faab816d0..c5317926b 100644 --- a/frontend/src/app/components/payment-form/payment-form.component.ts +++ b/frontend/src/app/components/payment-form/payment-form.component.ts @@ -1,179 +1,183 @@ -import { ActivatedRoute, Router } from '@angular/router'; -import { AlertActionType, AlertType } from 'src/app/models/alert'; -import { Component, OnInit, ViewChild } from '@angular/core'; -import { FormBuilder, FormGroup, Validators } from '@angular/forms'; -import { - StripeCardElementOptions, - StripeElementsOptions -} from '@stripe/stripe-js'; -import { StripePaymentElementComponent, StripeService } from 'ngx-stripe'; -import { addMonths, format } from 'date-fns' - -import { AlertComponent } from '../ui-components/alert/alert.component'; import { CommonModule } from '@angular/common'; import { HttpClient } from '@angular/common/http'; +import { Component, OnInit, ViewChild } from '@angular/core'; +import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms'; import { MatButtonModule } from '@angular/material/button'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; import { MatTableModule } from '@angular/material/table'; -import { NgxStripeModule } from 'ngx-stripe'; +import { ActivatedRoute, Router } from '@angular/router'; +import { StripeCardElementOptions, StripeElementsOptions } from '@stripe/stripe-js'; +import { addMonths, format } from 'date-fns'; +import { NgxStripeModule, StripePaymentElementComponent, StripeService } from 'ngx-stripe'; +import { AlertActionType, AlertType } from 'src/app/models/alert'; import { NotificationsService } from 'src/app/services/notifications.service'; import { PaymentService } from 'src/app/services/payment.service'; -import { ReactiveFormsModule } from '@angular/forms'; import { UserService } from 'src/app/services/user.service'; import plans from '../../consts/plans'; +import { AlertComponent } from '../ui-components/alert/alert.component'; @Component({ - selector: 'app-payment-form', - templateUrl: './payment-form.component.html', - styleUrls: ['./payment-form.component.css'], - imports: [ - CommonModule, - ReactiveFormsModule, - NgxStripeModule, - MatTableModule, - MatFormFieldModule, - MatInputModule, - MatButtonModule, - AlertComponent, - ], + selector: 'app-payment-form', + templateUrl: './payment-form.component.html', + styleUrls: ['./payment-form.component.css'], + imports: [ + CommonModule, + ReactiveFormsModule, + NgxStripeModule, + MatTableModule, + MatFormFieldModule, + MatInputModule, + MatButtonModule, + AlertComponent, + ], }) export class PaymentFormComponent implements OnInit { + public testAlert = { + type: 'info' as const, + message: 'This is a demo alert for the payment form.', + }; - public testAlert = { - type: 'info' as const, - message: 'This is a demo alert for the payment form.' - }; - - @ViewChild(StripePaymentElementComponent) - paymentElement: StripePaymentElementComponent; + @ViewChild(StripePaymentElementComponent) + paymentElement: StripePaymentElementComponent; - elementsOptions: StripeElementsOptions = { - locale: 'en', - }; + elementsOptions: StripeElementsOptions = { + locale: 'en', + }; - cardOptions: StripeCardElementOptions = { - style: { - base: { - iconColor: '#666EE8', - color: '#31325F', - fontWeight: '300', - fontFamily: 'Noto Sans, sans-serif', - fontSize: '18px', - '::placeholder': { - color: '#CFD7E0', - }, - }, - }, - }; + cardOptions: StripeCardElementOptions = { + style: { + base: { + iconColor: '#666EE8', + color: '#31325F', + fontWeight: '300', + fontFamily: 'Noto Sans, sans-serif', + fontSize: '18px', + '::placeholder': { + color: '#CFD7E0', + }, + }, + }, + }; - // public paymentElementForm; - public paying = false; + // public paymentElementForm; + public paying = false; - constructor( - private _paymentService: PaymentService, - private _userService: UserService, - private _notifications: NotificationsService,_http: HttpClient, - private fb: FormBuilder, - private stripeService: StripeService, - private route: ActivatedRoute, - public router: Router, - ) { } + constructor( + private _paymentService: PaymentService, + private _userService: UserService, + private _notifications: NotificationsService, + _http: HttpClient, + private fb: FormBuilder, + private stripeService: StripeService, + private route: ActivatedRoute, + public router: Router, + ) {} - public paymentElementForm: FormGroup | null = null; - public plan: 'team' | 'enterprise' | 'free'; - public price: number; - public isAnnually: boolean; - public subscriptionLevel: string; - public endOfTrialDate: string; - public companyId: string; + public paymentElementForm: FormGroup | null = null; + public plan: 'team' | 'enterprise' | 'free'; + public price: number; + public isAnnually: boolean; + public subscriptionLevel: string; + public endOfTrialDate: string; + public companyId: string; - public displayedColumns: string[] = ['plan', 'charge-date', 'amount', 'users', 'trial']; - public subscriptionInfo = []; + public displayedColumns: string[] = ['plan', 'charge-date', 'amount', 'users', 'trial']; + public subscriptionInfo = []; - ngOnInit(): void { - const queryParams = this.route.snapshot.queryParams; - this.plan = queryParams.plan; - this.isAnnually = queryParams.period === 'annually'; - const chosenPlan = plans.find(plan => plan.key === this.plan); - this.price = chosenPlan.price; - this.subscriptionLevel = `${ this.isAnnually ? 'ANNUAL_' : '' }${this.plan.toUpperCase()}_PLAN`; - this.endOfTrialDate = format(addMonths(new Date(), 1), 'P'); + ngOnInit(): void { + const queryParams = this.route.snapshot.queryParams; + this.plan = queryParams.plan; + this.isAnnually = queryParams.period === 'annually'; + const chosenPlan = plans.find((plan) => plan.key === this.plan); + this.price = chosenPlan.price; + this.subscriptionLevel = `${this.isAnnually ? 'ANNUAL_' : ''}${this.plan.toUpperCase()}_PLAN`; + this.endOfTrialDate = format(addMonths(new Date(), 1), 'P'); - this.subscriptionInfo = [ - {plan: this.plan, chargeDate: this.endOfTrialDate, amount: `${this.price} * n`, users: 'per each 10 user', trial: '1 month free trial' } - ] + this.subscriptionInfo = [ + { + plan: this.plan, + chargeDate: this.endOfTrialDate, + amount: `${this.price} * n`, + users: 'per each 10 user', + trial: '1 month free trial', + }, + ]; - this._userService.cast.subscribe(user => { - console.log('user'); - console.log(user); - if (user?.company.id) this._paymentService.createIntentToSubscription(user.company.id).subscribe(res => { - this.companyId = user.company.id; - this.paymentElementForm = this.fb.group({ - name: [user.name], - email: [user.email, [Validators.required]], - address: [''], - zipcode: [''], - city: [''] - }); - this.elementsOptions.clientSecret = res.stripeIntentSecret; - }); - }); - } + this._userService.cast.subscribe((user) => { + console.log('user'); + console.log(user); + if (user?.company.id) + this._paymentService.createIntentToSubscription(user.company.id).subscribe((res) => { + this.companyId = user.company.id; + this.paymentElementForm = this.fb.group({ + name: [user.name], + email: [user.email, [Validators.required]], + address: [''], + zipcode: [''], + city: [''], + }); + this.elementsOptions.clientSecret = res.stripeIntentSecret; + }); + }); + } - pay() { - if (this.paymentElementForm.valid) { - this.paying = true; - this.stripeService.confirmSetup({ - elements: this.paymentElement.elements, - confirmParams: { - payment_method_data: { - billing_details: { - name: this.paymentElementForm.get('name').value, - email: this.paymentElementForm.get('email').value, - address: { - line1: this.paymentElementForm.get('address').value || '', - postal_code: this.paymentElementForm.get('zipcode').value || '', - city: this.paymentElementForm.get('city').value || '', - } - } - } - }, - redirect: 'if_required' - }).subscribe(result => { - this.paying = false; - console.log('Result', result); - if (result.error) { - // Show error to your customer (e.g., insufficient funds) - console.log({ success: false, error: result.error.message }); - this._notifications.showAlert(AlertType.Error, result.error.message, [ - { - type: AlertActionType.Button, - caption: 'Dismiss', - action: (_id: number) => this._notifications.dismissAlert() - } - ]); - } else { - // The payment has been processed! - if (result.setupIntent.status === 'succeeded') { - this._paymentService.createSubscription(this.companyId, result.setupIntent.payment_method, this.subscriptionLevel) - .subscribe(_res => { - this._notifications.showSuccessSnackbar('Subscription successfully started.'); - this.router.navigate(['/upgrade']); - }) - } else { - this._notifications.showAlert(AlertType.Error, result.error.message, [ - { - type: AlertActionType.Button, - caption: 'Dismiss', - action: (_id: number) => this._notifications.dismissAlert() - } - ]); - } - } - }); - } else { - console.log(this.paymentElementForm); - } - } + pay() { + if (this.paymentElementForm.valid) { + this.paying = true; + this.stripeService + .confirmSetup({ + elements: this.paymentElement.elements, + confirmParams: { + payment_method_data: { + billing_details: { + name: this.paymentElementForm.get('name').value, + email: this.paymentElementForm.get('email').value, + address: { + line1: this.paymentElementForm.get('address').value || '', + postal_code: this.paymentElementForm.get('zipcode').value || '', + city: this.paymentElementForm.get('city').value || '', + }, + }, + }, + }, + redirect: 'if_required', + }) + .subscribe((result) => { + this.paying = false; + console.log('Result', result); + if (result.error) { + // Show error to your customer (e.g., insufficient funds) + console.log({ success: false, error: result.error.message }); + this._notifications.showAlert(AlertType.Error, result.error.message, [ + { + type: AlertActionType.Button, + caption: 'Dismiss', + action: (_id: number) => this._notifications.dismissAlert(), + }, + ]); + } else { + // The payment has been processed! + if (result.setupIntent.status === 'succeeded') { + this._paymentService + .createSubscription(this.companyId, result.setupIntent.payment_method, this.subscriptionLevel) + .subscribe((_res) => { + this._notifications.showSuccessSnackbar('Subscription successfully started.'); + this.router.navigate(['/upgrade']); + }); + } else { + this._notifications.showAlert(AlertType.Error, result.error.message, [ + { + type: AlertActionType.Button, + caption: 'Dismiss', + action: (_id: number) => this._notifications.dismissAlert(), + }, + ]); + } + } + }); + } else { + console.log(this.paymentElementForm); + } + } } diff --git a/frontend/src/app/components/profile/profile-sidebar/profile-sidebar.component.css b/frontend/src/app/components/profile/profile-sidebar/profile-sidebar.component.css index 0d8c5243f..92a7d456a 100644 --- a/frontend/src/app/components/profile/profile-sidebar/profile-sidebar.component.css +++ b/frontend/src/app/components/profile/profile-sidebar/profile-sidebar.component.css @@ -28,7 +28,7 @@ .sidebar-toggle-button { align-self: flex-start; margin-top: 8px; - margin-left: 12px; + margin-left: 12px; } .sidebar-toggle-button_collapsed { diff --git a/frontend/src/app/components/profile/profile-sidebar/profile-sidebar.component.ts b/frontend/src/app/components/profile/profile-sidebar/profile-sidebar.component.ts index 1dcce0c13..ecffef5d6 100644 --- a/frontend/src/app/components/profile/profile-sidebar/profile-sidebar.component.ts +++ b/frontend/src/app/components/profile/profile-sidebar/profile-sidebar.component.ts @@ -1,28 +1,23 @@ -import { AfterViewInit, Component, inject, input, OnInit, signal } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { RouterModule } from '@angular/router'; -import { MatListModule } from '@angular/material/list'; -import { MatIconModule } from '@angular/material/icon'; +import { AfterViewInit, Component, inject, input, OnInit, signal } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; +import { MatIconModule } from '@angular/material/icon'; +import { MatListModule } from '@angular/material/list'; import { MatTooltipModule } from '@angular/material/tooltip'; -import { UiSettingsService } from '../../../services/ui-settings.service'; +import { RouterModule } from '@angular/router'; import { environment } from 'src/environments/environment'; +import { UiSettingsService } from '../../../services/ui-settings.service'; @Component({ selector: 'app-profile-sidebar', templateUrl: './profile-sidebar.component.html', styleUrls: ['./profile-sidebar.component.css'], - imports: [ - CommonModule, - RouterModule, - MatListModule, - MatIconModule, - MatButtonModule, - MatTooltipModule, - ], + imports: [CommonModule, RouterModule, MatListModule, MatIconModule, MatButtonModule, MatTooltipModule], }) export class ProfileSidebarComponent implements OnInit, AfterViewInit { - activeTab = input<'account' | 'company' | 'subscription' | 'branding' | 'saml' | 'api' | 'secrets' | 'zapier'>('account'); + activeTab = input<'account' | 'company' | 'subscription' | 'branding' | 'saml' | 'api' | 'secrets' | 'zapier'>( + 'account', + ); collapsed = signal(false); initialized = signal(false); diff --git a/frontend/src/app/components/registration/registration.component.css b/frontend/src/app/components/registration/registration.component.css index 48cc35410..58bb920cf 100644 --- a/frontend/src/app/components/registration/registration.component.css +++ b/frontend/src/app/components/registration/registration.component.css @@ -1,9 +1,9 @@ .register-form-box > app-alert { - display: block; - position: sticky; - top: calc(var(--mat-toolbar-standard-height) + 24px); - margin: 0 0 24px; - z-index: 2; + display: block; + position: sticky; + top: calc(var(--mat-toolbar-standard-height) + 24px); + margin: 0 0 24px; + z-index: 2; } .wrapper { diff --git a/frontend/src/app/components/secrets/audit-log-dialog/audit-log-dialog.component.css b/frontend/src/app/components/secrets/audit-log-dialog/audit-log-dialog.component.css index bd4b5d068..2fd279a4c 100644 --- a/frontend/src/app/components/secrets/audit-log-dialog/audit-log-dialog.component.css +++ b/frontend/src/app/components/secrets/audit-log-dialog/audit-log-dialog.component.css @@ -1,176 +1,176 @@ h2[mat-dialog-title] { - display: flex; - align-items: center; - gap: 8px; + display: flex; + align-items: center; + gap: 8px; } .title-icon { - color: rgba(0, 0, 0, 0.54); + color: rgba(0, 0, 0, 0.54); } @media (prefers-color-scheme: dark) { - .title-icon { - color: rgba(255, 255, 255, 0.7); - } + .title-icon { + color: rgba(255, 255, 255, 0.7); + } } mat-dialog-content { - min-width: 600px; - max-height: 60vh; + min-width: 600px; + max-height: 60vh; } @media (width <= 800px) { - mat-dialog-content { - min-width: auto; - } + mat-dialog-content { + min-width: auto; + } } .loading-container { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - padding: 40px; - text-align: center; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 40px; + text-align: center; } .loading-container p { - margin-top: 16px; - color: rgba(0, 0, 0, 0.54); + margin-top: 16px; + color: rgba(0, 0, 0, 0.54); } @media (prefers-color-scheme: dark) { - .loading-container p { - color: rgba(255, 255, 255, 0.54); - } + .loading-container p { + color: rgba(255, 255, 255, 0.54); + } } .empty-state { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - padding: 48px 24px; - text-align: center; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 48px 24px; + text-align: center; } .empty-icon { - font-size: 48px; - width: 48px; - height: 48px; - color: rgba(0, 0, 0, 0.26); - margin-bottom: 16px; + font-size: 48px; + width: 48px; + height: 48px; + color: rgba(0, 0, 0, 0.26); + margin-bottom: 16px; } @media (prefers-color-scheme: dark) { - .empty-icon { - color: rgba(255, 255, 255, 0.3); - } + .empty-icon { + color: rgba(255, 255, 255, 0.3); + } } .empty-state h3 { - margin: 0 0 8px 0; + margin: 0 0 8px 0; } .empty-state p { - margin: 0; - color: rgba(0, 0, 0, 0.54); + margin: 0; + color: rgba(0, 0, 0, 0.54); } @media (prefers-color-scheme: dark) { - .empty-state p { - color: rgba(255, 255, 255, 0.54); - } + .empty-state p { + color: rgba(255, 255, 255, 0.54); + } } .audit-log-table { - width: 100%; + width: 100%; } .action-cell { - display: flex; - align-items: center; - gap: 8px; + display: flex; + align-items: center; + gap: 8px; } .action-cell mat-icon { - font-size: 18px; - width: 18px; - height: 18px; + font-size: 18px; + width: 18px; + height: 18px; } .action-create { - color: #2e7d32; + color: #2e7d32; } @media (prefers-color-scheme: dark) { - .action-create { - color: #81c784; - } + .action-create { + color: #81c784; + } } .action-view, .action-copy { - color: #1976d2; + color: #1976d2; } @media (prefers-color-scheme: dark) { - .action-view, - .action-copy { - color: #64b5f6; - } + .action-view, + .action-copy { + color: #64b5f6; + } } .action-update { - color: #ef6c00; + color: #ef6c00; } @media (prefers-color-scheme: dark) { - .action-update { - color: #ffb74d; - } + .action-update { + color: #ffb74d; + } } .action-delete { - color: #c62828; + color: #c62828; } @media (prefers-color-scheme: dark) { - .action-delete { - color: #ef5350; - } + .action-delete { + color: #ef5350; + } } .user-email { - font-size: 14px; + font-size: 14px; } .success-icon { - color: #2e7d32; + color: #2e7d32; } @media (prefers-color-scheme: dark) { - .success-icon { - color: #81c784; - } + .success-icon { + color: #81c784; + } } .failure-icon { - color: #c62828; + color: #c62828; } @media (prefers-color-scheme: dark) { - .failure-icon { - color: #ef5350; - } + .failure-icon { + color: #ef5350; + } } .failed-row { - background-color: rgba(198, 40, 40, 0.05); + background-color: rgba(198, 40, 40, 0.05); } @media (prefers-color-scheme: dark) { - .failed-row { - background-color: rgba(239, 83, 80, 0.1); - } + .failed-row { + background-color: rgba(239, 83, 80, 0.1); + } } diff --git a/frontend/src/app/components/secrets/audit-log-dialog/audit-log-dialog.component.ts b/frontend/src/app/components/secrets/audit-log-dialog/audit-log-dialog.component.ts index d41a5bc7d..3c31b0b7d 100644 --- a/frontend/src/app/components/secrets/audit-log-dialog/audit-log-dialog.component.ts +++ b/frontend/src/app/components/secrets/audit-log-dialog/audit-log-dialog.component.ts @@ -1,95 +1,93 @@ -import { Component, Inject, OnInit } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { MAT_DIALOG_DATA, MatDialogRef, MatDialogModule } from '@angular/material/dialog'; +import { Component, Inject, OnInit } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; -import { MatTableModule } from '@angular/material/table'; -import { MatPaginatorModule, PageEvent } from '@angular/material/paginator'; -import { MatIconModule } from '@angular/material/icon'; import { MatChipsModule } from '@angular/material/chips'; +import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog'; +import { MatIconModule } from '@angular/material/icon'; +import { MatPaginatorModule, PageEvent } from '@angular/material/paginator'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; +import { MatTableModule } from '@angular/material/table'; import { MatTooltipModule } from '@angular/material/tooltip'; - +import { AuditLogEntry, Secret, SecretPagination } from 'src/app/models/secret'; import { SecretsService } from 'src/app/services/secrets.service'; -import { Secret, AuditLogEntry, SecretPagination } from 'src/app/models/secret'; @Component({ - selector: 'app-audit-log-dialog', - templateUrl: './audit-log-dialog.component.html', - styleUrls: ['./audit-log-dialog.component.css'], - imports: [ - CommonModule, - MatDialogModule, - MatButtonModule, - MatTableModule, - MatPaginatorModule, - MatIconModule, - MatChipsModule, - MatProgressSpinnerModule, - MatTooltipModule, - ] + selector: 'app-audit-log-dialog', + templateUrl: './audit-log-dialog.component.html', + styleUrls: ['./audit-log-dialog.component.css'], + imports: [ + CommonModule, + MatDialogModule, + MatButtonModule, + MatTableModule, + MatPaginatorModule, + MatIconModule, + MatChipsModule, + MatProgressSpinnerModule, + MatTooltipModule, + ], }) export class AuditLogDialogComponent implements OnInit { - public logs: AuditLogEntry[] = []; - public pagination: SecretPagination = { - total: 0, - currentPage: 1, - perPage: 20, - lastPage: 1, - }; - public loading = true; - public displayedColumns = ['action', 'user', 'accessedAt', 'success']; + public logs: AuditLogEntry[] = []; + public pagination: SecretPagination = { + total: 0, + currentPage: 1, + perPage: 20, + lastPage: 1, + }; + public loading = true; + public displayedColumns = ['action', 'user', 'accessedAt', 'success']; - public actionLabels: Record = { - create: 'Created', - view: 'Viewed', - copy: 'Copied', - update: 'Updated', - delete: 'Deleted', - }; + public actionLabels: Record = { + create: 'Created', + view: 'Viewed', + copy: 'Copied', + update: 'Updated', + delete: 'Deleted', + }; - public actionIcons: Record = { - create: 'add_circle', - view: 'visibility', - copy: 'content_copy', - update: 'edit', - delete: 'delete', - }; + public actionIcons: Record = { + create: 'add_circle', + view: 'visibility', + copy: 'content_copy', + update: 'edit', + delete: 'delete', + }; - public actionColors: Record = { - create: 'primary', - view: 'accent', - copy: 'accent', - update: 'primary', - delete: 'warn', - }; + public actionColors: Record = { + create: 'primary', + view: 'accent', + copy: 'accent', + update: 'primary', + delete: 'warn', + }; - constructor( - @Inject(MAT_DIALOG_DATA) public data: { secret: Secret },_dialogRef: MatDialogRef, - private _secrets: SecretsService - ) {} + constructor( + @Inject(MAT_DIALOG_DATA) public data: { secret: Secret }, + _dialogRef: MatDialogRef, + private _secrets: SecretsService, + ) {} - ngOnInit(): void { - this.loadAuditLog(); - } + ngOnInit(): void { + this.loadAuditLog(); + } - loadAuditLog(): void { - this.loading = true; - this._secrets.getAuditLog( - this.data.secret.slug, - this.pagination.currentPage, - this.pagination.perPage - ).subscribe(response => { - if (response) { - this.logs = response.data; - this.pagination = response.pagination; - } - this.loading = false; - }); - } + loadAuditLog(): void { + this.loading = true; + this._secrets + .getAuditLog(this.data.secret.slug, this.pagination.currentPage, this.pagination.perPage) + .subscribe((response) => { + if (response) { + this.logs = response.data; + this.pagination = response.pagination; + } + this.loading = false; + }); + } - onPageChange(event: PageEvent): void { - this.pagination.currentPage = event.pageIndex + 1; - this.pagination.perPage = event.pageSize; - this.loadAuditLog(); - } + onPageChange(event: PageEvent): void { + this.pagination.currentPage = event.pageIndex + 1; + this.pagination.perPage = event.pageSize; + this.loadAuditLog(); + } } diff --git a/frontend/src/app/components/secrets/create-secret-dialog/create-secret-dialog.component.css b/frontend/src/app/components/secrets/create-secret-dialog/create-secret-dialog.component.css index 6052b0629..9c7d52df4 100644 --- a/frontend/src/app/components/secrets/create-secret-dialog/create-secret-dialog.component.css +++ b/frontend/src/app/components/secrets/create-secret-dialog/create-secret-dialog.component.css @@ -1,47 +1,47 @@ .full-width { - width: 100%; - margin-bottom: 16px; + width: 100%; + margin-bottom: 16px; } .full-width:first-child { - margin-top: 12px; + margin-top: 12px; } .master-encryption-section { - margin-top: 8px; - padding: 16px; - background-color: rgba(0, 0, 0, 0.02); - border-radius: 8px; + margin-top: 8px; + padding: 16px; + background-color: rgba(0, 0, 0, 0.02); + border-radius: 8px; } @media (prefers-color-scheme: dark) { - .master-encryption-section { - background-color: rgba(255, 255, 255, 0.05); - } + .master-encryption-section { + background-color: rgba(255, 255, 255, 0.05); + } } .encryption-hint { - font-size: 12px; - color: rgba(0, 0, 0, 0.54); - margin: 8px 0 16px 0; + font-size: 12px; + color: rgba(0, 0, 0, 0.54); + margin: 8px 0 16px 0; } @media (prefers-color-scheme: dark) { - .encryption-hint { - color: rgba(255, 255, 255, 0.54); - } + .encryption-hint { + color: rgba(255, 255, 255, 0.54); + } } mat-dialog-content { - min-width: 400px; + min-width: 400px; } @media (width <= 600px) { - mat-dialog-content { - min-width: auto; - } + mat-dialog-content { + min-width: auto; + } } .secret-masked { - -webkit-text-security: disc; + -webkit-text-security: disc; } diff --git a/frontend/src/app/components/secrets/delete-secret-dialog/delete-secret-dialog.component.css b/frontend/src/app/components/secrets/delete-secret-dialog/delete-secret-dialog.component.css index 88501b0c3..2528b138d 100644 --- a/frontend/src/app/components/secrets/delete-secret-dialog/delete-secret-dialog.component.css +++ b/frontend/src/app/components/secrets/delete-secret-dialog/delete-secret-dialog.component.css @@ -1,46 +1,46 @@ .warning-container { - display: flex; - gap: 16px; - padding: 16px; - background-color: #fff3e0; - border-radius: 8px; + display: flex; + gap: 16px; + padding: 16px; + background-color: #fff3e0; + border-radius: 8px; } @media (prefers-color-scheme: dark) { - .warning-container { - background-color: rgba(255, 152, 0, 0.15); - } + .warning-container { + background-color: rgba(255, 152, 0, 0.15); + } } .warning-icon { - font-size: 32px; - width: 32px; - height: 32px; - color: #ef6c00; - flex-shrink: 0; + font-size: 32px; + width: 32px; + height: 32px; + color: #ef6c00; + flex-shrink: 0; } @media (prefers-color-scheme: dark) { - .warning-icon { - color: #ffb74d; - } + .warning-icon { + color: #ffb74d; + } } .warning-content p { - margin: 0; + margin: 0; } .warning-content p:first-child { - margin-bottom: 8px; + margin-bottom: 8px; } .warning-details { - font-size: 14px; - color: rgba(0, 0, 0, 0.54); + font-size: 14px; + color: rgba(0, 0, 0, 0.54); } @media (prefers-color-scheme: dark) { - .warning-details { - color: rgba(255, 255, 255, 0.54); - } + .warning-details { + color: rgba(255, 255, 255, 0.54); + } } diff --git a/frontend/src/app/components/secrets/edit-secret-dialog/edit-secret-dialog.component.css b/frontend/src/app/components/secrets/edit-secret-dialog/edit-secret-dialog.component.css index 8471b4629..5fb251059 100644 --- a/frontend/src/app/components/secrets/edit-secret-dialog/edit-secret-dialog.component.css +++ b/frontend/src/app/components/secrets/edit-secret-dialog/edit-secret-dialog.component.css @@ -1,60 +1,60 @@ mat-dialog-content { - min-width: 400px; - min-height: 200px; + min-width: 400px; + min-height: 200px; } @media (width <= 600px) { - mat-dialog-content { - min-width: auto; - } + mat-dialog-content { + min-width: auto; + } } .full-width { - width: 100%; - margin-bottom: 16px; + width: 100%; + margin-bottom: 16px; } .info-note { - display: flex; - align-items: flex-start; - gap: 8px; - padding: 12px 16px; - background-color: rgba(25, 118, 210, 0.08); - border-radius: 8px; - color: #1565c0; - font-size: 14px; - margin-bottom: 20px; + display: flex; + align-items: flex-start; + gap: 8px; + padding: 12px 16px; + background-color: rgba(25, 118, 210, 0.08); + border-radius: 8px; + color: #1565c0; + font-size: 14px; + margin-bottom: 20px; } .info-note mat-icon { - font-size: 20px; - width: 20px; - height: 20px; - flex-shrink: 0; + font-size: 20px; + width: 20px; + height: 20px; + flex-shrink: 0; } @media (prefers-color-scheme: dark) { - .info-note { - background-color: rgba(100, 181, 246, 0.15); - color: #64b5f6; - } + .info-note { + background-color: rgba(100, 181, 246, 0.15); + color: #64b5f6; + } } .expiration-section { - margin-bottom: 16px; + margin-bottom: 16px; } .master-password-section { - margin-top: 16px; + margin-top: 16px; } .master-password-error { - color: #f44336; - font-size: 12px; - margin-top: -12px; - margin-bottom: 8px; + color: #f44336; + font-size: 12px; + margin-top: -12px; + margin-bottom: 8px; } .secret-masked { - -webkit-text-security: disc; + -webkit-text-security: disc; } diff --git a/frontend/src/app/components/secrets/master-password-dialog/master-password-dialog.component.css b/frontend/src/app/components/secrets/master-password-dialog/master-password-dialog.component.css index ee5f25435..f6465a85b 100644 --- a/frontend/src/app/components/secrets/master-password-dialog/master-password-dialog.component.css +++ b/frontend/src/app/components/secrets/master-password-dialog/master-password-dialog.component.css @@ -1,40 +1,40 @@ h2[mat-dialog-title] { - display: flex; - align-items: center; - gap: 8px; + display: flex; + align-items: center; + gap: 8px; } .title-icon { - color: #1976d2; + color: #1976d2; } @media (prefers-color-scheme: dark) { - .title-icon { - color: #64b5f6; - } + .title-icon { + color: #64b5f6; + } } mat-dialog-content { - min-width: 350px; + min-width: 350px; } @media (width <= 600px) { - mat-dialog-content { - min-width: auto; - } + mat-dialog-content { + min-width: auto; + } } .description { - color: rgba(0, 0, 0, 0.54); - margin: 0 0 24px 0; + color: rgba(0, 0, 0, 0.54); + margin: 0 0 24px 0; } @media (prefers-color-scheme: dark) { - .description { - color: rgba(255, 255, 255, 0.54); - } + .description { + color: rgba(255, 255, 255, 0.54); + } } .full-width { - width: 100%; + width: 100%; } diff --git a/frontend/src/app/components/secrets/master-password-dialog/master-password-dialog.component.ts b/frontend/src/app/components/secrets/master-password-dialog/master-password-dialog.component.ts index 1c789bddb..f1f5adee2 100644 --- a/frontend/src/app/components/secrets/master-password-dialog/master-password-dialog.component.ts +++ b/frontend/src/app/components/secrets/master-password-dialog/master-password-dialog.component.ts @@ -1,46 +1,44 @@ -import { Component } from '@angular/core'; import { CommonModule } from '@angular/common'; +import { Component } from '@angular/core'; import { FormsModule } from '@angular/forms'; -import { MatDialogRef, MatDialogModule } from '@angular/material/dialog'; import { MatButtonModule } from '@angular/material/button'; -import { MatInputModule } from '@angular/material/input'; +import { MatDialogModule, MatDialogRef } from '@angular/material/dialog'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatIconModule } from '@angular/material/icon'; +import { MatInputModule } from '@angular/material/input'; import { MatTooltipModule } from '@angular/material/tooltip'; @Component({ - selector: 'app-master-password-dialog', - templateUrl: './master-password-dialog.component.html', - styleUrls: ['./master-password-dialog.component.css'], - imports: [ - CommonModule, - FormsModule, - MatDialogModule, - MatButtonModule, - MatInputModule, - MatFormFieldModule, - MatIconModule, - MatTooltipModule, - ] + selector: 'app-master-password-dialog', + templateUrl: './master-password-dialog.component.html', + styleUrls: ['./master-password-dialog.component.css'], + imports: [ + CommonModule, + FormsModule, + MatDialogModule, + MatButtonModule, + MatInputModule, + MatFormFieldModule, + MatIconModule, + MatTooltipModule, + ], }) export class MasterPasswordDialogComponent { - public masterPassword = ''; - public showPassword = false; - public error = ''; + public masterPassword = ''; + public showPassword = false; + public error = ''; - constructor( - private dialogRef: MatDialogRef - ) {} + constructor(private dialogRef: MatDialogRef) {} - togglePasswordVisibility(): void { - this.showPassword = !this.showPassword; - } + togglePasswordVisibility(): void { + this.showPassword = !this.showPassword; + } - onSubmit(): void { - if (!this.masterPassword) { - this.error = 'Please enter the master password'; - return; - } - this.dialogRef.close(this.masterPassword); - } + onSubmit(): void { + if (!this.masterPassword) { + this.error = 'Please enter the master password'; + return; + } + this.dialogRef.close(this.masterPassword); + } } diff --git a/frontend/src/app/components/secrets/secrets.component.css b/frontend/src/app/components/secrets/secrets.component.css index 0ed85cf90..5c90e0b62 100644 --- a/frontend/src/app/components/secrets/secrets.component.css +++ b/frontend/src/app/components/secrets/secrets.component.css @@ -1,276 +1,276 @@ .profile-layout { - display: flex; - height: calc(100vh - 64px); + display: flex; + height: calc(100vh - 64px); } .profile-main { - flex: 1; - background-color: var(--mat-sidenav-content-background-color); - overflow-y: auto; + flex: 1; + background-color: var(--mat-sidenav-content-background-color); + overflow-y: auto; } ::ng-deep .profile-main > app-alert { - position: relative; - top: 0; - margin: 24px; + position: relative; + top: 0; + margin: 24px; } .secrets-page { - margin: var(--top-margin) auto; - padding: 0 clamp(40px, 5vw, 100px); - max-width: 1200px; + margin: var(--top-margin) auto; + padding: 0 clamp(40px, 5vw, 100px); + max-width: 1200px; } @media (width <= 600px) { - .secrets-page { - padding: 0 16px; - margin: 1.5em auto; - } + .secrets-page { + padding: 0 16px; + margin: 1.5em auto; + } } .secrets-header { - margin-bottom: 32px; + margin-bottom: 32px; } .secrets-description { - color: rgba(0, 0, 0, 0.64); - margin-top: 8px; + color: rgba(0, 0, 0, 0.64); + margin-top: 8px; } @media (prefers-color-scheme: dark) { - .secrets-description { - color: rgba(255, 255, 255, 0.7); - } + .secrets-description { + color: rgba(255, 255, 255, 0.7); + } } .secrets-toolbar { - display: flex; - align-items: center; - justify-content: space-between; - gap: 16px; - margin-bottom: 24px; + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; + margin-bottom: 24px; } @media (width <= 600px) { - .secrets-toolbar { - flex-direction: column; - align-items: stretch; - } + .secrets-toolbar { + flex-direction: column; + align-items: stretch; + } } .search-field { - flex: 1; - max-width: 400px; + flex: 1; + max-width: 400px; } @media (width <= 600px) { - .search-field { - max-width: 100%; - } + .search-field { + max-width: 100%; + } } .secrets-table { - width: 100%; - margin-bottom: 16px; + width: 100%; + margin-bottom: 16px; } .secrets-cell_slug { - max-width: 250px; + max-width: 250px; } .slug-text { - font-weight: 500; + font-weight: 500; } .secrets-cell_actions { - text-align: right; + text-align: right; } .encryption-icon { - color: rgba(0, 0, 0, 0.54); + color: rgba(0, 0, 0, 0.54); } @media (prefers-color-scheme: dark) { - .encryption-icon { - color: rgba(255, 255, 255, 0.7); - } + .encryption-icon { + color: rgba(255, 255, 255, 0.7); + } } .encryption-icon_master { - color: #1976d2; + color: #1976d2; } @media (prefers-color-scheme: dark) { - .encryption-icon_master { - color: #64b5f6; - } + .encryption-icon_master { + color: #64b5f6; + } } .no-expiry { - color: rgba(0, 0, 0, 0.54); + color: rgba(0, 0, 0, 0.54); } @media (prefers-color-scheme: dark) { - .no-expiry { - color: rgba(255, 255, 255, 0.5); - } + .no-expiry { + color: rgba(255, 255, 255, 0.5); + } } .expired-badge { - display: inline-flex; - align-items: center; - gap: 4px; - color: #c62828; - font-weight: 500; + display: inline-flex; + align-items: center; + gap: 4px; + color: #c62828; + font-weight: 500; } .expired-badge mat-icon { - font-size: 18px; - width: 18px; - height: 18px; + font-size: 18px; + width: 18px; + height: 18px; } @media (prefers-color-scheme: dark) { - .expired-badge { - color: #ef5350; - } + .expired-badge { + color: #ef5350; + } } .expiring-soon-badge { - display: inline-flex; - align-items: center; - gap: 4px; - color: #ef6c00; - font-weight: 500; + display: inline-flex; + align-items: center; + gap: 4px; + color: #ef6c00; + font-weight: 500; } .expiring-soon-badge mat-icon { - font-size: 18px; - width: 18px; - height: 18px; + font-size: 18px; + width: 18px; + height: 18px; } @media (prefers-color-scheme: dark) { - .expiring-soon-badge { - color: #ffb74d; - } + .expiring-soon-badge { + color: #ffb74d; + } } .no-secrets { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - padding: 64px 24px; - text-align: center; - background-color: rgba(0, 0, 0, 0.02); - border-radius: 8px; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 64px 24px; + text-align: center; + background-color: rgba(0, 0, 0, 0.02); + border-radius: 8px; } @media (prefers-color-scheme: dark) { - .no-secrets { - background-color: rgba(255, 255, 255, 0.05); - } + .no-secrets { + background-color: rgba(255, 255, 255, 0.05); + } } .no-secrets-icon { - font-size: 64px; - width: 64px; - height: 64px; - color: rgba(0, 0, 0, 0.26); - margin-bottom: 16px; + font-size: 64px; + width: 64px; + height: 64px; + color: rgba(0, 0, 0, 0.26); + margin-bottom: 16px; } @media (prefers-color-scheme: dark) { - .no-secrets-icon { - color: rgba(255, 255, 255, 0.3); - } + .no-secrets-icon { + color: rgba(255, 255, 255, 0.3); + } } .no-secrets h3 { - margin: 0 0 8px 0; - color: rgba(0, 0, 0, 0.87); + margin: 0 0 8px 0; + color: rgba(0, 0, 0, 0.87); } @media (prefers-color-scheme: dark) { - .no-secrets h3 { - color: rgba(255, 255, 255, 0.87); - } + .no-secrets h3 { + color: rgba(255, 255, 255, 0.87); + } } .no-secrets p { - margin: 0 0 16px 0; - color: rgba(0, 0, 0, 0.54); + margin: 0 0 16px 0; + color: rgba(0, 0, 0, 0.54); } @media (prefers-color-scheme: dark) { - .no-secrets p { - color: rgba(255, 255, 255, 0.54); - } + .no-secrets p { + color: rgba(255, 255, 255, 0.54); + } } .delete-action { - color: #c62828; + color: #c62828; } @media (prefers-color-scheme: dark) { - .delete-action { - color: #ef5350; - } + .delete-action { + color: #ef5350; + } } /* Responsive table styles */ @media (width <= 600px) { - .secrets-table { - display: grid; - grid-template-columns: auto 1fr; - max-width: 100%; - } - - .secrets-table-heading { - display: none; - } - - .secrets-table ::ng-deep tbody { - display: grid; - grid-template-columns: subgrid; - grid-column: 1 / 3; - } - - .secrets-row { - display: grid; - grid-template-columns: subgrid; - grid-column: 1 / 3; - grid-gap: 12px 28px; - border-bottom-color: var(--mat-table-row-item-outline-color, rgba(0, 0, 0, 0.12)); - border-bottom-width: var(--mat-table-row-item-outline-width, 1px); - border-bottom-style: solid; - height: auto; - padding: 20px 0; - } - - .secrets-cell { - display: grid; - grid-template-columns: subgrid; - grid-column: 1 / 3; - border-bottom: none; - } - - .secrets-cell::before { - content: attr(data-label); - display: inline-block; - font-weight: bold; - white-space: nowrap; - } - - .secrets-cell_actions { - grid-column: 1 / span 3; - display: flex; - justify-content: flex-end; - border-bottom: none; - } - - .secrets-cell_actions::before { - display: none; - } + .secrets-table { + display: grid; + grid-template-columns: auto 1fr; + max-width: 100%; + } + + .secrets-table-heading { + display: none; + } + + .secrets-table ::ng-deep tbody { + display: grid; + grid-template-columns: subgrid; + grid-column: 1 / 3; + } + + .secrets-row { + display: grid; + grid-template-columns: subgrid; + grid-column: 1 / 3; + grid-gap: 12px 28px; + border-bottom-color: var(--mat-table-row-item-outline-color, rgba(0, 0, 0, 0.12)); + border-bottom-width: var(--mat-table-row-item-outline-width, 1px); + border-bottom-style: solid; + height: auto; + padding: 20px 0; + } + + .secrets-cell { + display: grid; + grid-template-columns: subgrid; + grid-column: 1 / 3; + border-bottom: none; + } + + .secrets-cell::before { + content: attr(data-label); + display: inline-block; + font-weight: bold; + white-space: nowrap; + } + + .secrets-cell_actions { + grid-column: 1 / span 3; + display: flex; + justify-content: flex-end; + border-bottom: none; + } + + .secrets-cell_actions::before { + display: none; + } } diff --git a/frontend/src/app/components/secrets/secrets.component.spec.ts b/frontend/src/app/components/secrets/secrets.component.spec.ts index e9647c114..aba928146 100644 --- a/frontend/src/app/components/secrets/secrets.component.spec.ts +++ b/frontend/src/app/components/secrets/secrets.component.spec.ts @@ -58,7 +58,13 @@ describe('SecretsComponent', () => { } as any; await TestBed.configureTestingModule({ - imports: [SecretsComponent, BrowserAnimationsModule, MatSnackBarModule, Angulartics2Module.forRoot(), RouterModule.forRoot([])], + imports: [ + SecretsComponent, + BrowserAnimationsModule, + MatSnackBarModule, + Angulartics2Module.forRoot(), + RouterModule.forRoot([]), + ], providers: [ provideHttpClient(), provideHttpClientTesting(), diff --git a/frontend/src/app/components/skeletons/placeholder-add-user-dialog/placeholder-add-user-dialog.component.css b/frontend/src/app/components/skeletons/placeholder-add-user-dialog/placeholder-add-user-dialog.component.css index b7d8656e8..05ad30347 100644 --- a/frontend/src/app/components/skeletons/placeholder-add-user-dialog/placeholder-add-user-dialog.component.css +++ b/frontend/src/app/components/skeletons/placeholder-add-user-dialog/placeholder-add-user-dialog.component.css @@ -1,35 +1,35 @@ .wrapper { - position: relative; - z-index: 1; + position: relative; + z-index: 1; } .form { - display: flex; - flex-direction: column; - gap: 16px; - padding: 20px 24px 20px 24px; + display: flex; + flex-direction: column; + gap: 16px; + padding: 20px 24px 20px 24px; } .form__field { - height: 44px; - width: 100%; + height: 44px; + width: 100%; } .form__string { - height: 20px; - margin-bottom: -12px; - width: 80%; + height: 20px; + margin-bottom: -12px; + width: 80%; } .actions { - display: flex; - justify-content: flex-end; - gap: 12px; - padding: 8px; + display: flex; + justify-content: flex-end; + gap: 12px; + padding: 8px; } .form__button { - border-radius: 4px; - height: 36px; - width: 72px; -} \ No newline at end of file + border-radius: 4px; + height: 36px; + width: 72px; +} diff --git a/frontend/src/app/components/skeletons/placeholder-add-user-dialog/placeholder-add-user-dialog.component.spec.ts b/frontend/src/app/components/skeletons/placeholder-add-user-dialog/placeholder-add-user-dialog.component.spec.ts index a15e210fd..83da49f29 100644 --- a/frontend/src/app/components/skeletons/placeholder-add-user-dialog/placeholder-add-user-dialog.component.spec.ts +++ b/frontend/src/app/components/skeletons/placeholder-add-user-dialog/placeholder-add-user-dialog.component.spec.ts @@ -3,19 +3,19 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { PlaceholderAddUserDialogComponent } from './placeholder-add-user-dialog.component'; describe('PlaceholderAddUserDialogComponent', () => { - let component: PlaceholderAddUserDialogComponent; - let fixture: ComponentFixture; + let component: PlaceholderAddUserDialogComponent; + let fixture: ComponentFixture; - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [PlaceholderAddUserDialogComponent] -}); - fixture = TestBed.createComponent(PlaceholderAddUserDialogComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [PlaceholderAddUserDialogComponent], + }); + fixture = TestBed.createComponent(PlaceholderAddUserDialogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/skeletons/placeholder-add-user-dialog/placeholder-add-user-dialog.component.ts b/frontend/src/app/components/skeletons/placeholder-add-user-dialog/placeholder-add-user-dialog.component.ts index bb5bc66cd..35a2af67a 100644 --- a/frontend/src/app/components/skeletons/placeholder-add-user-dialog/placeholder-add-user-dialog.component.ts +++ b/frontend/src/app/components/skeletons/placeholder-add-user-dialog/placeholder-add-user-dialog.component.ts @@ -1,10 +1,8 @@ import { Component } from '@angular/core'; @Component({ - selector: 'app-placeholder-add-user-dialog', - templateUrl: './placeholder-add-user-dialog.component.html', - styleUrls: ['./placeholder-add-user-dialog.component.css'] + selector: 'app-placeholder-add-user-dialog', + templateUrl: './placeholder-add-user-dialog.component.html', + styleUrls: ['./placeholder-add-user-dialog.component.css'], }) -export class PlaceholderAddUserDialogComponent { - -} +export class PlaceholderAddUserDialogComponent {} diff --git a/frontend/src/app/components/skeletons/placeholder-api-keys-list/placeholder-api-keys-list.component.css b/frontend/src/app/components/skeletons/placeholder-api-keys-list/placeholder-api-keys-list.component.css index 178e796e2..d79ee9b69 100644 --- a/frontend/src/app/components/skeletons/placeholder-api-keys-list/placeholder-api-keys-list.component.css +++ b/frontend/src/app/components/skeletons/placeholder-api-keys-list/placeholder-api-keys-list.component.css @@ -1,33 +1,33 @@ .list-item { - display: flex; - align-items: center; - justify-content: space-between; - padding: 4px 16px; - border-radius: 8px; - margin-bottom: 0; - min-height: 40px; + display: flex; + align-items: center; + justify-content: space-between; + padding: 4px 16px; + border-radius: 8px; + margin-bottom: 0; + min-height: 40px; } .list-item__info { - display: flex; - align-items: center; - gap: 12px; + display: flex; + align-items: center; + gap: 12px; } .list-item__icon { - height: 24px; - width: 24px; - border-radius: 4px; + height: 24px; + width: 24px; + border-radius: 4px; } .list-item__title { - height: 20px; - width: 160px; - border-radius: 4px; + height: 20px; + width: 160px; + border-radius: 4px; } .list-item__button { - height: 32px; - width: 32px; - border-radius: 50%; + height: 32px; + width: 32px; + border-radius: 50%; } diff --git a/frontend/src/app/components/skeletons/placeholder-api-keys-list/placeholder-api-keys-list.component.ts b/frontend/src/app/components/skeletons/placeholder-api-keys-list/placeholder-api-keys-list.component.ts index 879b5b3af..b0881de5f 100644 --- a/frontend/src/app/components/skeletons/placeholder-api-keys-list/placeholder-api-keys-list.component.ts +++ b/frontend/src/app/components/skeletons/placeholder-api-keys-list/placeholder-api-keys-list.component.ts @@ -1,12 +1,10 @@ -import { Component } from '@angular/core'; import { NgFor } from '@angular/common'; +import { Component } from '@angular/core'; @Component({ - selector: 'app-placeholder-api-keys-list', - templateUrl: './placeholder-api-keys-list.component.html', - styleUrls: ['./placeholder-api-keys-list.component.css'], - imports: [NgFor] + selector: 'app-placeholder-api-keys-list', + templateUrl: './placeholder-api-keys-list.component.html', + styleUrls: ['./placeholder-api-keys-list.component.css'], + imports: [NgFor], }) -export class PlaceholderApiKeysListComponent { - -} +export class PlaceholderApiKeysListComponent {} diff --git a/frontend/src/app/components/skeletons/placeholder-audit-log/placeholder-audit-log.component.spec.ts b/frontend/src/app/components/skeletons/placeholder-audit-log/placeholder-audit-log.component.spec.ts index 73d874cbf..9b4277e92 100644 --- a/frontend/src/app/components/skeletons/placeholder-audit-log/placeholder-audit-log.component.spec.ts +++ b/frontend/src/app/components/skeletons/placeholder-audit-log/placeholder-audit-log.component.spec.ts @@ -3,21 +3,20 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { PlaceholderAuditLogComponent } from './placeholder-audit-log.component'; describe('PlaceholderAuditLogComponent', () => { - let component: PlaceholderAuditLogComponent; - let fixture: ComponentFixture; + let component: PlaceholderAuditLogComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [PlaceholderAuditLogComponent] -}) - .compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [PlaceholderAuditLogComponent], + }).compileComponents(); - fixture = TestBed.createComponent(PlaceholderAuditLogComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + fixture = TestBed.createComponent(PlaceholderAuditLogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/skeletons/placeholder-audit-log/placeholder-audit-log.component.ts b/frontend/src/app/components/skeletons/placeholder-audit-log/placeholder-audit-log.component.ts index 54d8dd632..c8c6cc4f0 100644 --- a/frontend/src/app/components/skeletons/placeholder-audit-log/placeholder-audit-log.component.ts +++ b/frontend/src/app/components/skeletons/placeholder-audit-log/placeholder-audit-log.component.ts @@ -1,10 +1,8 @@ import { Component } from '@angular/core'; @Component({ - selector: 'app-placeholder-audit-log', - templateUrl: './placeholder-audit-log.component.html', - styleUrls: ['./placeholder-audit-log.component.css'] + selector: 'app-placeholder-audit-log', + templateUrl: './placeholder-audit-log.component.html', + styleUrls: ['./placeholder-audit-log.component.css'], }) -export class PlaceholderAuditLogComponent { - -} +export class PlaceholderAuditLogComponent {} diff --git a/frontend/src/app/components/skeletons/placeholder-branding/placeholder-branding.component.css b/frontend/src/app/components/skeletons/placeholder-branding/placeholder-branding.component.css index 9ed138e97..ca6bd590d 100644 --- a/frontend/src/app/components/skeletons/placeholder-branding/placeholder-branding.component.css +++ b/frontend/src/app/components/skeletons/placeholder-branding/placeholder-branding.component.css @@ -1,92 +1,92 @@ .wrapper { - margin: var(--top-margin) auto; - padding: 0 clamp(40px, 5vw, 100px); - max-width: 1200px; + margin: var(--top-margin) auto; + padding: 0 clamp(40px, 5vw, 100px); + max-width: 1200px; } @media (width <= 600px) { - .wrapper { - padding: 0 16px; - margin: 1.5em auto; - } + .wrapper { + padding: 0 16px; + margin: 1.5em auto; + } } .title { - height: 32px; - margin-bottom: 8px; - width: 120px; + height: 32px; + margin-bottom: 8px; + width: 120px; } .description { - height: 20px; - margin-bottom: 32px; - width: 420px; - max-width: 100%; + height: 20px; + margin-bottom: 32px; + width: 420px; + max-width: 100%; } .white-label { - display: grid; - grid-template-columns: 1fr 1fr; - grid-column-gap: 32px; + display: grid; + grid-template-columns: 1fr 1fr; + grid-column-gap: 32px; } @media (width <= 900px) { - .white-label { - grid-template-columns: 1fr; - grid-row-gap: 32px; - } + .white-label { + grid-template-columns: 1fr; + grid-row-gap: 32px; + } } .form__field { - height: 56px; - width: 100%; - border-radius: 4px; - margin-bottom: 16px; + height: 56px; + width: 100%; + border-radius: 4px; + margin-bottom: 16px; } .upload-buttons { - display: flex; - gap: 24px; - margin-top: 16px; - margin-bottom: 24px; + display: flex; + gap: 24px; + margin-top: 16px; + margin-bottom: 24px; } .upload-button { - height: 36px; - width: 140px; - border-radius: 4px; + height: 36px; + width: 140px; + border-radius: 4px; } .preview { - height: 240px; - width: 100%; - border-radius: 4px; - margin-top: 8px; + height: 240px; + width: 100%; + border-radius: 4px; + margin-top: 8px; } .white-label-how-to { - border: 1px solid rgba(0, 0, 0, 0.06); - border-radius: 4px; - padding: 20px; - height: fit-content; + border: 1px solid rgba(0, 0, 0, 0.06); + border-radius: 4px; + padding: 20px; + height: fit-content; } .how-to__title { - height: 24px; - width: 200px; - margin-bottom: 16px; + height: 24px; + width: 200px; + margin-bottom: 16px; } .how-to__line { - height: 16px; - width: 100%; - margin-bottom: 12px; + height: 16px; + width: 100%; + margin-bottom: 12px; } .how-to__line_short { - width: 60%; + width: 60%; } .how-to__line_medium { - width: 80%; + width: 80%; } diff --git a/frontend/src/app/components/skeletons/placeholder-branding/placeholder-branding.component.ts b/frontend/src/app/components/skeletons/placeholder-branding/placeholder-branding.component.ts index a6fa47880..c801887d5 100644 --- a/frontend/src/app/components/skeletons/placeholder-branding/placeholder-branding.component.ts +++ b/frontend/src/app/components/skeletons/placeholder-branding/placeholder-branding.component.ts @@ -1,10 +1,8 @@ import { Component } from '@angular/core'; @Component({ - selector: 'app-placeholder-branding', - templateUrl: './placeholder-branding.component.html', - styleUrls: ['./placeholder-branding.component.css'] + selector: 'app-placeholder-branding', + templateUrl: './placeholder-branding.component.html', + styleUrls: ['./placeholder-branding.component.css'], }) -export class PlaceholderBrandingComponent { - -} +export class PlaceholderBrandingComponent {} diff --git a/frontend/src/app/components/skeletons/placeholder-company-invitation/placeholder-company-invitation.component.css b/frontend/src/app/components/skeletons/placeholder-company-invitation/placeholder-company-invitation.component.css index 6a12dec1d..b261d3fc1 100644 --- a/frontend/src/app/components/skeletons/placeholder-company-invitation/placeholder-company-invitation.component.css +++ b/frontend/src/app/components/skeletons/placeholder-company-invitation/placeholder-company-invitation.component.css @@ -1,40 +1,40 @@ .wrapper { - display: flex; - flex-direction: column; - justify-content: center; - height: 100%; - margin: 0 auto; - padding: 16px; - max-width: 520px; - min-width: 300px; - width: 90%; + display: flex; + flex-direction: column; + justify-content: center; + height: 100%; + margin: 0 auto; + padding: 16px; + max-width: 520px; + min-width: 300px; + width: 90%; } .title { - height: 32px; - margin-bottom: 32px; - width: 100%; + height: 32px; + margin-bottom: 32px; + width: 100%; } .form__field { - height: 44px; - width: 100%; + height: 44px; + width: 100%; } .form__string { - height: 24px; - margin-bottom: 16px; - width: 120px; + height: 24px; + margin-bottom: 16px; + width: 120px; } .form__button { - border-radius: 4px; - height: 36px; - margin-top: 16px; - margin-left: auto; - width: 64px; + border-radius: 4px; + height: 36px; + margin-top: 16px; + margin-left: auto; + width: 64px; } .form-divider { - margin-top: 48px; - margin-bottom: 20px; -} \ No newline at end of file + margin-top: 48px; + margin-bottom: 20px; +} diff --git a/frontend/src/app/components/skeletons/placeholder-company-invitation/placeholder-company-invitation.component.spec.ts b/frontend/src/app/components/skeletons/placeholder-company-invitation/placeholder-company-invitation.component.spec.ts index f6da37b71..96b897533 100644 --- a/frontend/src/app/components/skeletons/placeholder-company-invitation/placeholder-company-invitation.component.spec.ts +++ b/frontend/src/app/components/skeletons/placeholder-company-invitation/placeholder-company-invitation.component.spec.ts @@ -3,21 +3,20 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { PlaceholderCompanyInvitationComponent } from './placeholder-company-invitation.component'; describe('PlaceholderCompanyInvitationComponent', () => { - let component: PlaceholderCompanyInvitationComponent; - let fixture: ComponentFixture; + let component: PlaceholderCompanyInvitationComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [PlaceholderCompanyInvitationComponent] -}) - .compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [PlaceholderCompanyInvitationComponent], + }).compileComponents(); - fixture = TestBed.createComponent(PlaceholderCompanyInvitationComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + fixture = TestBed.createComponent(PlaceholderCompanyInvitationComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/skeletons/placeholder-company-invitation/placeholder-company-invitation.component.ts b/frontend/src/app/components/skeletons/placeholder-company-invitation/placeholder-company-invitation.component.ts index 777e4493c..88664e6df 100644 --- a/frontend/src/app/components/skeletons/placeholder-company-invitation/placeholder-company-invitation.component.ts +++ b/frontend/src/app/components/skeletons/placeholder-company-invitation/placeholder-company-invitation.component.ts @@ -2,13 +2,9 @@ import { Component } from '@angular/core'; import { MatDividerModule } from '@angular/material/divider'; @Component({ - selector: 'app-placeholder-company-invitation', - templateUrl: './placeholder-company-invitation.component.html', - styleUrls: ['./placeholder-company-invitation.component.css'], - imports: [ - MatDividerModule - ] + selector: 'app-placeholder-company-invitation', + templateUrl: './placeholder-company-invitation.component.html', + styleUrls: ['./placeholder-company-invitation.component.css'], + imports: [MatDividerModule], }) -export class PlaceholderCompanyInvitationComponent { - -} +export class PlaceholderCompanyInvitationComponent {} diff --git a/frontend/src/app/components/skeletons/placeholder-company/placeholder-company.component.css b/frontend/src/app/components/skeletons/placeholder-company/placeholder-company.component.css index ebcdc1107..ca898d48d 100644 --- a/frontend/src/app/components/skeletons/placeholder-company/placeholder-company.component.css +++ b/frontend/src/app/components/skeletons/placeholder-company/placeholder-company.component.css @@ -1,86 +1,86 @@ .wrapper { - margin: var(--top-margin) auto; - padding: 0 clamp(40px, 5vw, 100px); - max-width: 1200px; - width: 100%; + margin: var(--top-margin) auto; + padding: 0 clamp(40px, 5vw, 100px); + max-width: 1200px; + width: 100%; } @media (width <= 600px) { - .wrapper { - padding: 0 9vw; - width: 100vw; - } + .wrapper { + padding: 0 9vw; + width: 100vw; + } } .title { - height: 32px; - margin-bottom: 32px; - width: 332px; + height: 32px; + margin-bottom: 32px; + width: 332px; } .form__field { - height: 44px; - width: 100%; + height: 44px; + width: 100%; } .form__line { - display: flex; - justify-content: space-between; - align-items: center; - margin: 8px 0; + display: flex; + justify-content: space-between; + align-items: center; + margin: 8px 0; } .form__line_invite { - margin-bottom: 16px; + margin-bottom: 16px; } .form__title { - height: 32px; - width: 142px; + height: 32px; + width: 142px; } @media (width <= 600px) { - .form__title { - width: 120px; - } + .form__title { + width: 120px; + } } .form__string { - height: 24px; - width: 120px; + height: 24px; + width: 120px; } @media (width <= 600px) { - .form__string { - width: 80px; - } + .form__string { + width: 80px; + } } .form__button { - border-radius: 4px; - height: 36px; + border-radius: 4px; + height: 36px; } .form__button_plan { - width: 190px; + width: 190px; } @media (width <= 600px) { - .form__button_plan { - width: 124px; - } + .form__button_plan { + width: 124px; + } } .form__button_invite { - width: 152px; + width: 152px; } @media (width <= 600px) { - .form__button_plan { - width: 132px; - } + .form__button_plan { + width: 132px; + } } .form__table { - padding: 0 8px; -} \ No newline at end of file + padding: 0 8px; +} diff --git a/frontend/src/app/components/skeletons/placeholder-company/placeholder-company.component.spec.ts b/frontend/src/app/components/skeletons/placeholder-company/placeholder-company.component.spec.ts index 847797095..91e4042e4 100644 --- a/frontend/src/app/components/skeletons/placeholder-company/placeholder-company.component.spec.ts +++ b/frontend/src/app/components/skeletons/placeholder-company/placeholder-company.component.spec.ts @@ -3,21 +3,20 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { PlaceholderCompanyComponent } from './placeholder-company.component'; describe('PlaceholderCompanyComponent', () => { - let component: PlaceholderCompanyComponent; - let fixture: ComponentFixture; + let component: PlaceholderCompanyComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [PlaceholderCompanyComponent] -}) - .compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [PlaceholderCompanyComponent], + }).compileComponents(); - fixture = TestBed.createComponent(PlaceholderCompanyComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + fixture = TestBed.createComponent(PlaceholderCompanyComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/skeletons/placeholder-company/placeholder-company.component.ts b/frontend/src/app/components/skeletons/placeholder-company/placeholder-company.component.ts index 45d77b187..2a82d08b4 100644 --- a/frontend/src/app/components/skeletons/placeholder-company/placeholder-company.component.ts +++ b/frontend/src/app/components/skeletons/placeholder-company/placeholder-company.component.ts @@ -3,14 +3,9 @@ import { Component } from '@angular/core'; import { PlaceholderTableDataComponent } from '../placeholder-table-data/placeholder-table-data.component'; @Component({ - selector: 'app-placeholder-company', - templateUrl: './placeholder-company.component.html', - styleUrls: ['./placeholder-company.component.css'], - imports: [ - CommonModule, - PlaceholderTableDataComponent - ] + selector: 'app-placeholder-company', + templateUrl: './placeholder-company.component.html', + styleUrls: ['./placeholder-company.component.css'], + imports: [CommonModule, PlaceholderTableDataComponent], }) -export class PlaceholderCompanyComponent { - -} +export class PlaceholderCompanyComponent {} diff --git a/frontend/src/app/components/skeletons/placeholder-connection-settings/placeholder-connection-settings.component.css b/frontend/src/app/components/skeletons/placeholder-connection-settings/placeholder-connection-settings.component.css index 1697775a8..742d3a166 100644 --- a/frontend/src/app/components/skeletons/placeholder-connection-settings/placeholder-connection-settings.component.css +++ b/frontend/src/app/components/skeletons/placeholder-connection-settings/placeholder-connection-settings.component.css @@ -1,56 +1,59 @@ .form { - display: grid; - grid-template-columns: 1fr 1fr; - grid-gap: 20px; - margin: 0 auto 40px; - width: 100%; + display: grid; + grid-template-columns: 1fr 1fr; + grid-gap: 20px; + margin: 0 auto 40px; + width: 100%; } .form__line { - display: flex; - align-items: center; - gap: 20px; + display: flex; + align-items: center; + gap: 20px; } .form__field { - height: 44px; - width: 100%; + height: 44px; + width: 100%; } .form__field_full { - grid-column: 1 / span 2; + grid-column: 1 / span 2; } .form__logo { - flex-shrink: 0; - border-radius: 50%; - height: 24px; - width: 24px; + flex-shrink: 0; + border-radius: 50%; + height: 24px; + width: 24px; } .actions { - position: fixed; - left: 0; - bottom: 0; - display: flex; - align-items: center; - justify-content: space-between; - gap: 20px; - background-color: var(--mat-sidenav-content-background-color); - box-shadow: 0 3px 1px -2px rgba(0,0,0,.2),0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12); - height: 64px; - padding: 0 max(calc(50vw - 325px), 2%); - width: 100vw; + position: fixed; + left: 0; + bottom: 0; + display: flex; + align-items: center; + justify-content: space-between; + gap: 20px; + background-color: var(--mat-sidenav-content-background-color); + box-shadow: + 0 3px 1px -2px rgba(0, 0, 0, 0.2), + 0 2px 2px 0 rgba(0, 0, 0, 0.14), + 0 1px 5px 0 rgba(0, 0, 0, 0.12); + height: 64px; + padding: 0 max(calc(50vw - 325px), 2%); + width: 100vw; } .actions__button { - border-radius: 4px; - height: 36px; - width: 72px; - mix-blend-mode: normal !important; - z-index: 2; + border-radius: 4px; + height: 36px; + width: 72px; + mix-blend-mode: normal !important; + z-index: 2; } .actions__button_left { - margin-right: auto; + margin-right: auto; } diff --git a/frontend/src/app/components/skeletons/placeholder-connection-settings/placeholder-connection-settings.component.spec.ts b/frontend/src/app/components/skeletons/placeholder-connection-settings/placeholder-connection-settings.component.spec.ts index db56e906f..dcb376086 100644 --- a/frontend/src/app/components/skeletons/placeholder-connection-settings/placeholder-connection-settings.component.spec.ts +++ b/frontend/src/app/components/skeletons/placeholder-connection-settings/placeholder-connection-settings.component.spec.ts @@ -3,21 +3,20 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { PlaceholderConnectionSettingsComponent } from './placeholder-connection-settings.component'; describe('PlaceholderConnectionSettingsComponent', () => { - let component: PlaceholderConnectionSettingsComponent; - let fixture: ComponentFixture; + let component: PlaceholderConnectionSettingsComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [PlaceholderConnectionSettingsComponent] -}) - .compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [PlaceholderConnectionSettingsComponent], + }).compileComponents(); - fixture = TestBed.createComponent(PlaceholderConnectionSettingsComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + fixture = TestBed.createComponent(PlaceholderConnectionSettingsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/skeletons/placeholder-connection-settings/placeholder-connection-settings.component.ts b/frontend/src/app/components/skeletons/placeholder-connection-settings/placeholder-connection-settings.component.ts index 415545ec9..7689177e8 100644 --- a/frontend/src/app/components/skeletons/placeholder-connection-settings/placeholder-connection-settings.component.ts +++ b/frontend/src/app/components/skeletons/placeholder-connection-settings/placeholder-connection-settings.component.ts @@ -1,10 +1,8 @@ import { Component } from '@angular/core'; @Component({ - selector: 'app-placeholder-connection-settings', - templateUrl: './placeholder-connection-settings.component.html', - styleUrls: ['./placeholder-connection-settings.component.css'] + selector: 'app-placeholder-connection-settings', + templateUrl: './placeholder-connection-settings.component.html', + styleUrls: ['./placeholder-connection-settings.component.css'], }) -export class PlaceholderConnectionSettingsComponent { - -} +export class PlaceholderConnectionSettingsComponent {} diff --git a/frontend/src/app/components/skeletons/placeholder-connections/placeholder-connections.component.css b/frontend/src/app/components/skeletons/placeholder-connections/placeholder-connections.component.css index 052cdcf60..a5d4843b8 100644 --- a/frontend/src/app/components/skeletons/placeholder-connections/placeholder-connections.component.css +++ b/frontend/src/app/components/skeletons/placeholder-connections/placeholder-connections.component.css @@ -1,36 +1,36 @@ .connections { - display: flex; - flex-wrap: wrap; - justify-content: center; - gap: 20px; - width: 100%; + display: flex; + flex-wrap: wrap; + justify-content: center; + gap: 20px; + width: 100%; } @media (width <= 600px) { - .connections { - flex-direction: column; - } + .connections { + flex-direction: column; + } } .connectionCard { - flex: 0 0 calc((100% - 40px)/3); - border-radius: 4px; - height: 148px; - width: 100%; + flex: 0 0 calc((100% - 40px) / 3); + border-radius: 4px; + height: 148px; + width: 100%; } @media (width <= 600px) { - .connectionCard { - flex: initial; - } + .connectionCard { + flex: initial; + } } .connectionAddButton { - align-self: flex-end; - border-radius: 24px; - height: 48px; - margin-top: 36px; - margin-bottom: 32px; - margin-left: auto; - width: 178px; -} \ No newline at end of file + align-self: flex-end; + border-radius: 24px; + height: 48px; + margin-top: 36px; + margin-bottom: 32px; + margin-left: auto; + width: 178px; +} diff --git a/frontend/src/app/components/skeletons/placeholder-connections/placeholder-connections.component.spec.ts b/frontend/src/app/components/skeletons/placeholder-connections/placeholder-connections.component.spec.ts index 91152dfdf..b09e13402 100644 --- a/frontend/src/app/components/skeletons/placeholder-connections/placeholder-connections.component.spec.ts +++ b/frontend/src/app/components/skeletons/placeholder-connections/placeholder-connections.component.spec.ts @@ -3,21 +3,20 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { PlaceholderConnectionsComponent } from './placeholder-connections.component'; describe('PlaceholderConnectionsComponent', () => { - let component: PlaceholderConnectionsComponent; - let fixture: ComponentFixture; + let component: PlaceholderConnectionsComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [PlaceholderConnectionsComponent] -}) - .compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [PlaceholderConnectionsComponent], + }).compileComponents(); - fixture = TestBed.createComponent(PlaceholderConnectionsComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + fixture = TestBed.createComponent(PlaceholderConnectionsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/skeletons/placeholder-connections/placeholder-connections.component.ts b/frontend/src/app/components/skeletons/placeholder-connections/placeholder-connections.component.ts index e62472405..eaf94b7bc 100644 --- a/frontend/src/app/components/skeletons/placeholder-connections/placeholder-connections.component.ts +++ b/frontend/src/app/components/skeletons/placeholder-connections/placeholder-connections.component.ts @@ -1,10 +1,8 @@ import { Component } from '@angular/core'; @Component({ - selector: 'app-placeholder-connections', - templateUrl: './placeholder-connections.component.html', - styleUrls: ['./placeholder-connections.component.css'] + selector: 'app-placeholder-connections', + templateUrl: './placeholder-connections.component.html', + styleUrls: ['./placeholder-connections.component.css'], }) -export class PlaceholderConnectionsComponent { - -} +export class PlaceholderConnectionsComponent {} diff --git a/frontend/src/app/components/skeletons/placeholder-record-view/placeholder-record-view.component.css b/frontend/src/app/components/skeletons/placeholder-record-view/placeholder-record-view.component.css index c25289e8a..8f17fb8be 100644 --- a/frontend/src/app/components/skeletons/placeholder-record-view/placeholder-record-view.component.css +++ b/frontend/src/app/components/skeletons/placeholder-record-view/placeholder-record-view.component.css @@ -1,42 +1,42 @@ .loading-related-records { - display: flex; - align-items: center; - justify-content: space-between; - padding: 0 8px 0 16px; + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 8px 0 16px; } .loading-related-records__title { - mix-blend-mode: normal !important; - height: 28px; - width: 120px; + mix-blend-mode: normal !important; + height: 28px; + width: 120px; } .loading-related-records__button { - mix-blend-mode: normal !important; - border-radius: 50%; - height: 36px; - width: 36px; + mix-blend-mode: normal !important; + border-radius: 50%; + height: 36px; + width: 36px; } .record-field { - display: flex; - flex-direction: column; - gap: 4px; - padding: 12px 16px; + display: flex; + flex-direction: column; + gap: 4px; + padding: 12px 16px; } .record-field:not(:last-child) { - border-bottom: solid 1px rgba(0, 0, 0, 0.12); + border-bottom: solid 1px rgba(0, 0, 0, 0.12); } .record-field__key { - mix-blend-mode: normal !important; - height: 20px; - width: 120px; + mix-blend-mode: normal !important; + height: 20px; + width: 120px; } .record-field__value { - mix-blend-mode: normal !important; - height: 20px; - width: 200px -} \ No newline at end of file + mix-blend-mode: normal !important; + height: 20px; + width: 200px; +} diff --git a/frontend/src/app/components/skeletons/placeholder-record-view/placeholder-record-view.component.spec.ts b/frontend/src/app/components/skeletons/placeholder-record-view/placeholder-record-view.component.spec.ts index 820ba7a0a..6347aeb4c 100644 --- a/frontend/src/app/components/skeletons/placeholder-record-view/placeholder-record-view.component.spec.ts +++ b/frontend/src/app/components/skeletons/placeholder-record-view/placeholder-record-view.component.spec.ts @@ -3,21 +3,20 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { PlaceholderRecordViewComponent } from './placeholder-record-view.component'; describe('PlaceholderRecordViewComponent', () => { - let component: PlaceholderRecordViewComponent; - let fixture: ComponentFixture; + let component: PlaceholderRecordViewComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [PlaceholderRecordViewComponent] - }) - .compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [PlaceholderRecordViewComponent], + }).compileComponents(); - fixture = TestBed.createComponent(PlaceholderRecordViewComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + fixture = TestBed.createComponent(PlaceholderRecordViewComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/skeletons/placeholder-record-view/placeholder-record-view.component.ts b/frontend/src/app/components/skeletons/placeholder-record-view/placeholder-record-view.component.ts index 1f81ab2dd..346edfe96 100644 --- a/frontend/src/app/components/skeletons/placeholder-record-view/placeholder-record-view.component.ts +++ b/frontend/src/app/components/skeletons/placeholder-record-view/placeholder-record-view.component.ts @@ -1,11 +1,9 @@ import { Component } from '@angular/core'; @Component({ - selector: 'app-placeholder-record-view', - imports: [], - templateUrl: './placeholder-record-view.component.html', - styleUrl: './placeholder-record-view.component.css' + selector: 'app-placeholder-record-view', + imports: [], + templateUrl: './placeholder-record-view.component.html', + styleUrl: './placeholder-record-view.component.css', }) -export class PlaceholderRecordViewComponent { - -} +export class PlaceholderRecordViewComponent {} diff --git a/frontend/src/app/components/skeletons/placeholder-row-edit/placeholder-row-edit.component.css b/frontend/src/app/components/skeletons/placeholder-row-edit/placeholder-row-edit.component.css index 1f7748e00..ef863add1 100644 --- a/frontend/src/app/components/skeletons/placeholder-row-edit/placeholder-row-edit.component.css +++ b/frontend/src/app/components/skeletons/placeholder-row-edit/placeholder-row-edit.component.css @@ -1,71 +1,74 @@ .wrapper { - position: relative; - display: flex; - flex-direction: column; - align-items: center; - padding: 36px 0 16px; + position: relative; + display: flex; + flex-direction: column; + align-items: center; + padding: 36px 0 16px; } .row-edit-header { - display: flex; - align-items: center; + display: flex; + align-items: center; } .row-edit-header__breadcrumb { - border-radius: 4px; - height: 36px; + border-radius: 4px; + height: 36px; } .row-edit-header__breadcrumb_1 { - width: 120px; + width: 120px; } .row-edit-header__breadcrumb_2 { - width: 172px; + width: 172px; } .row-edit-header__breadcrumb_3 { - width: 80px; + width: 80px; } .form { - display: flex; - flex-direction: column; - gap: 16px; - margin: 28px auto 40px; - max-width: 520px; - min-width: 300px; - width: 96%; + display: flex; + flex-direction: column; + gap: 16px; + margin: 28px auto 40px; + max-width: 520px; + min-width: 300px; + width: 96%; } .form__field { - height: 44px; - width: 100%; + height: 44px; + width: 100%; } .actions { - position: fixed; - left: 0; - bottom: 0; - display: flex; - align-items: center; - justify-content: space-between; - gap: 20px; - background-color: var(--mat-sidenav-content-background-color); - box-shadow: 0 3px 1px -2px rgba(0,0,0,.2),0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12); - height: 64px; - padding: 0 max(calc(50vw - 260px), 2%); - width: 100vw; + position: fixed; + left: 0; + bottom: 0; + display: flex; + align-items: center; + justify-content: space-between; + gap: 20px; + background-color: var(--mat-sidenav-content-background-color); + box-shadow: + 0 3px 1px -2px rgba(0, 0, 0, 0.2), + 0 2px 2px 0 rgba(0, 0, 0, 0.14), + 0 1px 5px 0 rgba(0, 0, 0, 0.12); + height: 64px; + padding: 0 max(calc(50vw - 260px), 2%); + width: 100vw; } .actions__button { - border-radius: 4px; - height: 36px; - width: 80px; - mix-blend-mode: normal !important; - z-index: 2; + border-radius: 4px; + height: 36px; + width: 80px; + mix-blend-mode: normal !important; + z-index: 2; } .actions__button_left { - margin-right: auto; + margin-right: auto; } diff --git a/frontend/src/app/components/skeletons/placeholder-row-edit/placeholder-row-edit.component.spec.ts b/frontend/src/app/components/skeletons/placeholder-row-edit/placeholder-row-edit.component.spec.ts index fd8dcedca..7769133e6 100644 --- a/frontend/src/app/components/skeletons/placeholder-row-edit/placeholder-row-edit.component.spec.ts +++ b/frontend/src/app/components/skeletons/placeholder-row-edit/placeholder-row-edit.component.spec.ts @@ -3,21 +3,20 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { PlaceholderRowEditComponent } from './placeholder-row-edit.component'; describe('PlaceholderRowEditComponent', () => { - let component: PlaceholderRowEditComponent; - let fixture: ComponentFixture; + let component: PlaceholderRowEditComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [PlaceholderRowEditComponent] -}) - .compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [PlaceholderRowEditComponent], + }).compileComponents(); - fixture = TestBed.createComponent(PlaceholderRowEditComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + fixture = TestBed.createComponent(PlaceholderRowEditComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/skeletons/placeholder-row-edit/placeholder-row-edit.component.ts b/frontend/src/app/components/skeletons/placeholder-row-edit/placeholder-row-edit.component.ts index 37a04a55f..0c0cd17f8 100644 --- a/frontend/src/app/components/skeletons/placeholder-row-edit/placeholder-row-edit.component.ts +++ b/frontend/src/app/components/skeletons/placeholder-row-edit/placeholder-row-edit.component.ts @@ -3,11 +3,9 @@ import { Component } from '@angular/core'; import { MatIconModule } from '@angular/material/icon'; @Component({ - selector: 'app-placeholder-row-edit', - templateUrl: './placeholder-row-edit.component.html', - styleUrls: ['./placeholder-row-edit.component.css'], - imports: [MatIconModule, CommonModule] + selector: 'app-placeholder-row-edit', + templateUrl: './placeholder-row-edit.component.html', + styleUrls: ['./placeholder-row-edit.component.css'], + imports: [MatIconModule, CommonModule], }) -export class PlaceholderRowEditComponent { - -} +export class PlaceholderRowEditComponent {} diff --git a/frontend/src/app/components/skeletons/placeholder-saved-filters/placeholder-saved-filters.component.css b/frontend/src/app/components/skeletons/placeholder-saved-filters/placeholder-saved-filters.component.css index 6c20e81a7..277ae786b 100644 --- a/frontend/src/app/components/skeletons/placeholder-saved-filters/placeholder-saved-filters.component.css +++ b/frontend/src/app/components/skeletons/placeholder-saved-filters/placeholder-saved-filters.component.css @@ -1,31 +1,31 @@ .box { - display: flex; - align-items: center; - gap: 8px; - width: 100%; + display: flex; + align-items: center; + gap: 8px; + width: 100%; } .edit-button { - height: 36px; - width: 36px; - border-radius: 50%; - margin-right: 8px; - /* mix-blend-mode: normal !important; */ + height: 36px; + width: 36px; + border-radius: 50%; + margin-right: 8px; + /* mix-blend-mode: normal !important; */ } .saved-filter { - height: 32px; - border-radius: 4px; + height: 32px; + border-radius: 4px; } .saved-filter_1 { - width: 100px; + width: 100px; } .saved-filter_2 { - width: 150px; + width: 150px; } .saved-filter_3 { - width: 200px; -} \ No newline at end of file + width: 200px; +} diff --git a/frontend/src/app/components/skeletons/placeholder-saved-filters/placeholder-saved-filters.component.spec.ts b/frontend/src/app/components/skeletons/placeholder-saved-filters/placeholder-saved-filters.component.spec.ts index 2094d1a1f..ee3ef5da3 100644 --- a/frontend/src/app/components/skeletons/placeholder-saved-filters/placeholder-saved-filters.component.spec.ts +++ b/frontend/src/app/components/skeletons/placeholder-saved-filters/placeholder-saved-filters.component.spec.ts @@ -3,21 +3,20 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { PlaceholderSavedFiltersComponent } from './placeholder-saved-filters.component'; describe('PlaceholderSavedFiltersComponent', () => { - let component: PlaceholderSavedFiltersComponent; - let fixture: ComponentFixture; + let component: PlaceholderSavedFiltersComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [PlaceholderSavedFiltersComponent] -}) - .compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [PlaceholderSavedFiltersComponent], + }).compileComponents(); - fixture = TestBed.createComponent(PlaceholderSavedFiltersComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + fixture = TestBed.createComponent(PlaceholderSavedFiltersComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/skeletons/placeholder-saved-filters/placeholder-saved-filters.component.ts b/frontend/src/app/components/skeletons/placeholder-saved-filters/placeholder-saved-filters.component.ts index d1fa79aa5..73ef7f401 100644 --- a/frontend/src/app/components/skeletons/placeholder-saved-filters/placeholder-saved-filters.component.ts +++ b/frontend/src/app/components/skeletons/placeholder-saved-filters/placeholder-saved-filters.component.ts @@ -1,10 +1,8 @@ import { Component } from '@angular/core'; @Component({ - selector: 'app-placeholder-saved-filters', - templateUrl: './placeholder-saved-filters.component.html', - styleUrls: ['./placeholder-saved-filters.component.css'] + selector: 'app-placeholder-saved-filters', + templateUrl: './placeholder-saved-filters.component.html', + styleUrls: ['./placeholder-saved-filters.component.css'], }) -export class PlaceholderSavedFiltersComponent { - -} +export class PlaceholderSavedFiltersComponent {} diff --git a/frontend/src/app/components/skeletons/placeholder-sso/placeholder-sso.component.css b/frontend/src/app/components/skeletons/placeholder-sso/placeholder-sso.component.css index b97741ee3..3f1e77248 100644 --- a/frontend/src/app/components/skeletons/placeholder-sso/placeholder-sso.component.css +++ b/frontend/src/app/components/skeletons/placeholder-sso/placeholder-sso.component.css @@ -1,70 +1,70 @@ .wrapper { - margin: var(--top-margin) auto; - padding: 0 clamp(40px, 5vw, 100px); - max-width: 800px; + margin: var(--top-margin) auto; + padding: 0 clamp(40px, 5vw, 100px); + max-width: 800px; } @media (width <= 600px) { - .wrapper { - padding: 0 16px; - margin: 1.5em auto; - } + .wrapper { + padding: 0 16px; + margin: 1.5em auto; + } } .title { - height: 32px; - margin-bottom: 8px; - width: 280px; + height: 32px; + margin-bottom: 8px; + width: 280px; } .description { - height: 20px; - margin-bottom: 32px; - width: 400px; - max-width: 100%; + height: 20px; + margin-bottom: 32px; + width: 400px; + max-width: 100%; } .form { - display: flex; - flex-direction: column; + display: flex; + flex-direction: column; } .form__field { - height: 56px; - width: 100%; - border-radius: 4px; - margin-bottom: 16px; + height: 56px; + width: 100%; + border-radius: 4px; + margin-bottom: 16px; } .form__textarea { - height: 160px; - width: 100%; - border-radius: 4px; - margin-bottom: 16px; + height: 160px; + width: 100%; + border-radius: 4px; + margin-bottom: 16px; } .form__checkboxes { - display: flex; - flex-direction: column; - gap: 12px; - margin-bottom: 24px; + display: flex; + flex-direction: column; + gap: 12px; + margin-bottom: 24px; } .form__checkbox { - height: 20px; - width: 240px; - border-radius: 2px; + height: 20px; + width: 240px; + border-radius: 2px; } .form__actions { - display: flex; - justify-content: flex-end; - margin-top: 16px; - padding-bottom: 32px; + display: flex; + justify-content: flex-end; + margin-top: 16px; + padding-bottom: 32px; } .form__button { - height: 36px; - width: 80px; - border-radius: 4px; + height: 36px; + width: 80px; + border-radius: 4px; } diff --git a/frontend/src/app/components/skeletons/placeholder-sso/placeholder-sso.component.ts b/frontend/src/app/components/skeletons/placeholder-sso/placeholder-sso.component.ts index 513363f8d..58a051998 100644 --- a/frontend/src/app/components/skeletons/placeholder-sso/placeholder-sso.component.ts +++ b/frontend/src/app/components/skeletons/placeholder-sso/placeholder-sso.component.ts @@ -1,10 +1,8 @@ import { Component } from '@angular/core'; @Component({ - selector: 'app-placeholder-sso', - templateUrl: './placeholder-sso.component.html', - styleUrls: ['./placeholder-sso.component.css'] + selector: 'app-placeholder-sso', + templateUrl: './placeholder-sso.component.html', + styleUrls: ['./placeholder-sso.component.css'], }) -export class PlaceholderSsoComponent { - -} +export class PlaceholderSsoComponent {} diff --git a/frontend/src/app/components/skeletons/placeholder-table-data/placeholder-table-data.component.css b/frontend/src/app/components/skeletons/placeholder-table-data/placeholder-table-data.component.css index f58ef9cf6..435716ab7 100644 --- a/frontend/src/app/components/skeletons/placeholder-table-data/placeholder-table-data.component.css +++ b/frontend/src/app/components/skeletons/placeholder-table-data/placeholder-table-data.component.css @@ -1,16 +1,16 @@ .data-table { - display: grid; - grid-template-columns: 56px 20% 30% 7% 8% 15% calc(20% - 56px); - overflow: auto; - width: 100%; + display: grid; + grid-template-columns: 56px 20% 30% 7% 8% 15% calc(20% - 56px); + overflow: auto; + width: 100%; } .table-cell { - border-bottom: 1px solid #0000001f; - box-sizing: border-box; - padding: 8px 16px; + border-bottom: 1px solid #0000001f; + box-sizing: border-box; + padding: 8px 16px; } .table-cell-content { - height: 24px; -} \ No newline at end of file + height: 24px; +} diff --git a/frontend/src/app/components/skeletons/placeholder-table-data/placeholder-table-data.component.spec.ts b/frontend/src/app/components/skeletons/placeholder-table-data/placeholder-table-data.component.spec.ts index 67c3002f2..cdd2d655c 100644 --- a/frontend/src/app/components/skeletons/placeholder-table-data/placeholder-table-data.component.spec.ts +++ b/frontend/src/app/components/skeletons/placeholder-table-data/placeholder-table-data.component.spec.ts @@ -3,21 +3,20 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { PlaceholderTableDataComponent } from './placeholder-table-data.component'; describe('PlaceholderTableDataComponent', () => { - let component: PlaceholderTableDataComponent; - let fixture: ComponentFixture; + let component: PlaceholderTableDataComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [PlaceholderTableDataComponent] -}) - .compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [PlaceholderTableDataComponent], + }).compileComponents(); - fixture = TestBed.createComponent(PlaceholderTableDataComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + fixture = TestBed.createComponent(PlaceholderTableDataComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/skeletons/placeholder-table-data/placeholder-table-data.component.ts b/frontend/src/app/components/skeletons/placeholder-table-data/placeholder-table-data.component.ts index bd58e1002..06ae1ed10 100644 --- a/frontend/src/app/components/skeletons/placeholder-table-data/placeholder-table-data.component.ts +++ b/frontend/src/app/components/skeletons/placeholder-table-data/placeholder-table-data.component.ts @@ -2,12 +2,11 @@ import { NgForOf } from '@angular/common'; import { Component } from '@angular/core'; @Component({ - selector: 'app-placeholder-table-data', - templateUrl: './placeholder-table-data.component.html', - styleUrls: ['./placeholder-table-data.component.css'], - imports: [NgForOf] + selector: 'app-placeholder-table-data', + templateUrl: './placeholder-table-data.component.html', + styleUrls: ['./placeholder-table-data.component.css'], + imports: [NgForOf], }) export class PlaceholderTableDataComponent { - public numberOfDivs = Array.from({ length: 42 }, (_, index) => index); - + public numberOfDivs = Array.from({ length: 42 }, (_, index) => index); } diff --git a/frontend/src/app/components/skeletons/placeholder-table-settings/placeholder-table-settings.component.css b/frontend/src/app/components/skeletons/placeholder-table-settings/placeholder-table-settings.component.css index 04593e7e6..e3e75d2da 100644 --- a/frontend/src/app/components/skeletons/placeholder-table-settings/placeholder-table-settings.component.css +++ b/frontend/src/app/components/skeletons/placeholder-table-settings/placeholder-table-settings.component.css @@ -1,129 +1,129 @@ .wrapper { - position: relative; - display: flex; - flex-direction: column; - align-items: center; - padding: 36px 0 16px; + position: relative; + display: flex; + flex-direction: column; + align-items: center; + padding: 36px 0 16px; } @media (width <= 600px) { - .wrapper { - padding: 36px 9vw 16px; - } + .wrapper { + padding: 36px 9vw 16px; + } } .row-edit-header { - display: flex; - align-items: center; + display: flex; + align-items: center; } .row-edit-header__breadcrumb { - border-radius: 4px; - height: 36px; + border-radius: 4px; + height: 36px; } .row-edit-header__breadcrumb_1 { - width: 120px; + width: 120px; } @media (width <= 600px) { - .row-edit-header__breadcrumb_1 { - width: 60px; - } + .row-edit-header__breadcrumb_1 { + width: 60px; + } } - .row-edit-header__breadcrumb_2 { - width: 172px; + width: 172px; } @media (width <= 600px) { - .row-edit-header__breadcrumb_2 { - width: 120px; - } + .row-edit-header__breadcrumb_2 { + width: 120px; + } } - .row-edit-header__breadcrumb_3 { - width: 80px; + width: 80px; } @media (width <= 600px) { - .row-edit-header__breadcrumb_3 { - width: 52px; - } + .row-edit-header__breadcrumb_3 { + width: 52px; + } } - .form { - display: flex; - flex-direction: column; - margin: 28px auto 40px; - padding: 0 max(calc(50vw - 325px), 10%); - width: 100%; + display: flex; + flex-direction: column; + margin: 28px auto 40px; + padding: 0 max(calc(50vw - 325px), 10%); + width: 100%; } @media (width <= 600px) { - .form { - padding: 0; - } + .form { + padding: 0; + } } .form__heading { - height: 32px; - margin-top: 20px; - margin-bottom: -4px; - width: 142px; + height: 32px; + margin-top: 20px; + margin-bottom: -4px; + width: 142px; } .form__line { - display: flex; - align-items: center; - gap: 12px; + display: flex; + align-items: center; + gap: 12px; } .form__field { - height: 44px; - margin-top: 20px; - width: 100%; + height: 44px; + margin-top: 20px; + width: 100%; } .form__info { - height: 16px; - margin-top: 4px; - width: 63%; + height: 16px; + margin-top: 4px; + width: 63%; } .form__addButton { - border-radius: 4px; - height: 36px; - margin-bottom: -20px; - width: 72px; + border-radius: 4px; + height: 36px; + margin-bottom: -20px; + width: 72px; } .actions { - position: fixed; - left: 0; - bottom: 0; - display: flex; - align-items: center; - justify-content: space-between; - gap: 20px; - background-color: var(--mat-sidenav-content-background-color); - box-shadow: 0 3px 1px -2px rgba(0,0,0,.2),0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12); - height: 64px; - padding: 0 max(calc(50vw - 325px), 2%); - width: 100vw; + position: fixed; + left: 0; + bottom: 0; + display: flex; + align-items: center; + justify-content: space-between; + gap: 20px; + background-color: var(--mat-sidenav-content-background-color); + box-shadow: + 0 3px 1px -2px rgba(0, 0, 0, 0.2), + 0 2px 2px 0 rgba(0, 0, 0, 0.14), + 0 1px 5px 0 rgba(0, 0, 0, 0.12); + height: 64px; + padding: 0 max(calc(50vw - 325px), 2%); + width: 100vw; } .actions__button { - border-radius: 4px; - height: 36px; - width: 72px; - mix-blend-mode: normal !important; - z-index: 2; + border-radius: 4px; + height: 36px; + width: 72px; + mix-blend-mode: normal !important; + z-index: 2; } .actions__button_left { - margin-right: auto; + margin-right: auto; } diff --git a/frontend/src/app/components/skeletons/placeholder-table-settings/placeholder-table-settings.component.spec.ts b/frontend/src/app/components/skeletons/placeholder-table-settings/placeholder-table-settings.component.spec.ts index e36bf5c7f..7eba8eb98 100644 --- a/frontend/src/app/components/skeletons/placeholder-table-settings/placeholder-table-settings.component.spec.ts +++ b/frontend/src/app/components/skeletons/placeholder-table-settings/placeholder-table-settings.component.spec.ts @@ -3,21 +3,20 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { PlaceholderTableSettingsComponent } from './placeholder-table-settings.component'; describe('PlaceholderTableSettingsComponent', () => { - let component: PlaceholderTableSettingsComponent; - let fixture: ComponentFixture; + let component: PlaceholderTableSettingsComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [PlaceholderTableSettingsComponent] -}) - .compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [PlaceholderTableSettingsComponent], + }).compileComponents(); - fixture = TestBed.createComponent(PlaceholderTableSettingsComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + fixture = TestBed.createComponent(PlaceholderTableSettingsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/skeletons/placeholder-table-settings/placeholder-table-settings.component.ts b/frontend/src/app/components/skeletons/placeholder-table-settings/placeholder-table-settings.component.ts index 923c644b3..3ce2dcb75 100644 --- a/frontend/src/app/components/skeletons/placeholder-table-settings/placeholder-table-settings.component.ts +++ b/frontend/src/app/components/skeletons/placeholder-table-settings/placeholder-table-settings.component.ts @@ -3,11 +3,9 @@ import { Component } from '@angular/core'; import { MatIconModule } from '@angular/material/icon'; @Component({ - selector: 'app-placeholder-table-settings', - templateUrl: './placeholder-table-settings.component.html', - styleUrls: ['./placeholder-table-settings.component.css'], - imports: [CommonModule, MatIconModule] + selector: 'app-placeholder-table-settings', + templateUrl: './placeholder-table-settings.component.html', + styleUrls: ['./placeholder-table-settings.component.css'], + imports: [CommonModule, MatIconModule], }) -export class PlaceholderTableSettingsComponent { - -} +export class PlaceholderTableSettingsComponent {} diff --git a/frontend/src/app/components/skeletons/placeholder-table-view/placeholder-table-view.component.css b/frontend/src/app/components/skeletons/placeholder-table-view/placeholder-table-view.component.css index 8117e2bc5..2e215346c 100644 --- a/frontend/src/app/components/skeletons/placeholder-table-view/placeholder-table-view.component.css +++ b/frontend/src/app/components/skeletons/placeholder-table-view/placeholder-table-view.component.css @@ -1,48 +1,48 @@ .placeholder { - display: flex; - height: 100%; + display: flex; + height: 100%; } .sidebar { - border-right: solid 1px rgba(0, 0, 0, 0.12); - height: 100%; - max-width: 240px; - width: 14%;; + border-right: solid 1px rgba(0, 0, 0, 0.12); + height: 100%; + max-width: 240px; + width: 14%; } .table-header { - display: flex; - margin-top: 8px; - padding: 12px 0; - width: 100%; + display: flex; + margin-top: 8px; + padding: 12px 0; + width: 100%; } .table-header__title { - margin-right: auto; - height: 36px; - width: 15vw; + margin-right: auto; + height: 36px; + width: 15vw; } .table-header__search { - border-radius: 4px 4px 0 0; - height: 36px; - width: 200px; + border-radius: 4px 4px 0 0; + height: 36px; + width: 200px; } .table-header__button { - border-radius: 4px; - margin-left: 12px; - height: 36px; - width: 80px; + border-radius: 4px; + margin-left: 12px; + height: 36px; + width: 80px; } .table-header__icon-button { - border-radius: 50%; - margin-left: 12px; - height: 36px; - width: 36px; + border-radius: 50%; + margin-left: 12px; + height: 36px; + width: 36px; } .table-view { - padding: 0 24px 24px; + padding: 0 24px 24px; } diff --git a/frontend/src/app/components/skeletons/placeholder-table-view/placeholder-table-view.component.spec.ts b/frontend/src/app/components/skeletons/placeholder-table-view/placeholder-table-view.component.spec.ts index 02fd58093..6a50ae7e9 100644 --- a/frontend/src/app/components/skeletons/placeholder-table-view/placeholder-table-view.component.spec.ts +++ b/frontend/src/app/components/skeletons/placeholder-table-view/placeholder-table-view.component.spec.ts @@ -3,21 +3,20 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { PlaceholderTableViewComponent } from './placeholder-table-view.component'; describe('PlaceholderTableViewComponent', () => { - let component: PlaceholderTableViewComponent; - let fixture: ComponentFixture; + let component: PlaceholderTableViewComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [PlaceholderTableViewComponent] -}) - .compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [PlaceholderTableViewComponent], + }).compileComponents(); - fixture = TestBed.createComponent(PlaceholderTableViewComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + fixture = TestBed.createComponent(PlaceholderTableViewComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/skeletons/placeholder-table-view/placeholder-table-view.component.ts b/frontend/src/app/components/skeletons/placeholder-table-view/placeholder-table-view.component.ts index 507c2f42e..903103b96 100644 --- a/frontend/src/app/components/skeletons/placeholder-table-view/placeholder-table-view.component.ts +++ b/frontend/src/app/components/skeletons/placeholder-table-view/placeholder-table-view.component.ts @@ -3,11 +3,9 @@ import { Component } from '@angular/core'; import { PlaceholderTableDataComponent } from '../placeholder-table-data/placeholder-table-data.component'; @Component({ - selector: 'app-placeholder-table-view', - templateUrl: './placeholder-table-view.component.html', - styleUrls: ['./placeholder-table-view.component.css'], - imports: [CommonModule, PlaceholderTableDataComponent] + selector: 'app-placeholder-table-view', + templateUrl: './placeholder-table-view.component.html', + styleUrls: ['./placeholder-table-view.component.css'], + imports: [CommonModule, PlaceholderTableDataComponent], }) -export class PlaceholderTableViewComponent { - -} +export class PlaceholderTableViewComponent {} diff --git a/frontend/src/app/components/skeletons/placeholder-table-widgets/placeholder-table-widgets.component.css b/frontend/src/app/components/skeletons/placeholder-table-widgets/placeholder-table-widgets.component.css index 9ab350094..053eab550 100644 --- a/frontend/src/app/components/skeletons/placeholder-table-widgets/placeholder-table-widgets.component.css +++ b/frontend/src/app/components/skeletons/placeholder-table-widgets/placeholder-table-widgets.component.css @@ -1,75 +1,78 @@ .wrapper { - position: relative; - min-width: 300px; - margin: 0 auto 16px; - width: 90%; + position: relative; + min-width: 300px; + margin: 0 auto 16px; + width: 90%; } .row-edit-header { - display: flex; - align-items: flex-start; - margin-top: 36px; - margin-bottom: 28px; + display: flex; + align-items: flex-start; + margin-top: 36px; + margin-bottom: 28px; } .row-edit-header__breadcrumb { - border-radius: 4px; - height: 36px; + border-radius: 4px; + height: 36px; } .row-edit-header__breadcrumb_1 { - width: 120px; + width: 120px; } .row-edit-header__breadcrumb_2 { - width: 172px; + width: 172px; } .row-edit-header__breadcrumb_3 { - width: 80px; + width: 80px; } .widgets { - display: grid; - grid-template-columns: minmax(10%, 130px) 1fr 2fr 1fr 2fr 50px; - align-items: start; - grid-gap: 16px 20px; + display: grid; + grid-template-columns: minmax(10%, 130px) 1fr 2fr 1fr 2fr 50px; + align-items: start; + grid-gap: 16px 20px; } .form__field { - height: 44px; - width: 100%; + height: 44px; + width: 100%; } .form__info { - border-radius: 50%; - height: 24px; - width: 24px; + border-radius: 50%; + height: 24px; + width: 24px; } .actions { - position: fixed; - left: 0; - bottom: 0; - display: flex; - align-items: center; - justify-content: space-between; - gap: 20px; - background-color: var(--mat-sidenav-content-background-color); - box-shadow: 0 3px 1px -2px rgba(0,0,0,.2),0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12); - height: 64px; - padding: 0 calc(5% + 70px) 0 5%; - width: 100vw; + position: fixed; + left: 0; + bottom: 0; + display: flex; + align-items: center; + justify-content: space-between; + gap: 20px; + background-color: var(--mat-sidenav-content-background-color); + box-shadow: + 0 3px 1px -2px rgba(0, 0, 0, 0.2), + 0 2px 2px 0 rgba(0, 0, 0, 0.14), + 0 1px 5px 0 rgba(0, 0, 0, 0.12); + height: 64px; + padding: 0 calc(5% + 70px) 0 5%; + width: 100vw; } .actions__button { - border-radius: 4px; - height: 36px; - width: 80px; - mix-blend-mode: normal !important; - z-index: 2; + border-radius: 4px; + height: 36px; + width: 80px; + mix-blend-mode: normal !important; + z-index: 2; } .actions__button_left { - margin-right: auto; -} \ No newline at end of file + margin-right: auto; +} diff --git a/frontend/src/app/components/skeletons/placeholder-table-widgets/placeholder-table-widgets.component.spec.ts b/frontend/src/app/components/skeletons/placeholder-table-widgets/placeholder-table-widgets.component.spec.ts index 819a683d3..0a0317b87 100644 --- a/frontend/src/app/components/skeletons/placeholder-table-widgets/placeholder-table-widgets.component.spec.ts +++ b/frontend/src/app/components/skeletons/placeholder-table-widgets/placeholder-table-widgets.component.spec.ts @@ -3,21 +3,20 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { PlaceholderTableWidgetsComponent } from './placeholder-table-widgets.component'; describe('PlaceholderTableWidgetsComponent', () => { - let component: PlaceholderTableWidgetsComponent; - let fixture: ComponentFixture; + let component: PlaceholderTableWidgetsComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [PlaceholderTableWidgetsComponent] -}) - .compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [PlaceholderTableWidgetsComponent], + }).compileComponents(); - fixture = TestBed.createComponent(PlaceholderTableWidgetsComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + fixture = TestBed.createComponent(PlaceholderTableWidgetsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/skeletons/placeholder-table-widgets/placeholder-table-widgets.component.ts b/frontend/src/app/components/skeletons/placeholder-table-widgets/placeholder-table-widgets.component.ts index eea6693e3..2456f7ba6 100644 --- a/frontend/src/app/components/skeletons/placeholder-table-widgets/placeholder-table-widgets.component.ts +++ b/frontend/src/app/components/skeletons/placeholder-table-widgets/placeholder-table-widgets.component.ts @@ -2,11 +2,9 @@ import { Component } from '@angular/core'; import { MatIconModule } from '@angular/material/icon'; @Component({ - selector: 'app-placeholder-table-widgets', - templateUrl: './placeholder-table-widgets.component.html', - styleUrls: ['./placeholder-table-widgets.component.css'], - imports: [MatIconModule] + selector: 'app-placeholder-table-widgets', + templateUrl: './placeholder-table-widgets.component.html', + styleUrls: ['./placeholder-table-widgets.component.css'], + imports: [MatIconModule], }) -export class PlaceholderTableWidgetsComponent { - -} +export class PlaceholderTableWidgetsComponent {} diff --git a/frontend/src/app/components/skeletons/placeholder-user-group/placeholder-user-group.component.css b/frontend/src/app/components/skeletons/placeholder-user-group/placeholder-user-group.component.css index b28212831..36f867e11 100644 --- a/frontend/src/app/components/skeletons/placeholder-user-group/placeholder-user-group.component.css +++ b/frontend/src/app/components/skeletons/placeholder-user-group/placeholder-user-group.component.css @@ -1,13 +1,13 @@ .box { - display: flex; - flex-direction: column; - gap: 12px; - margin-top: 20px; - width: 100%; + display: flex; + flex-direction: column; + gap: 12px; + margin-top: 20px; + width: 100%; } .user-email { - height: 20px; - width: 100%; - mix-blend-mode: normal !important; -} \ No newline at end of file + height: 20px; + width: 100%; + mix-blend-mode: normal !important; +} diff --git a/frontend/src/app/components/skeletons/placeholder-user-group/placeholder-user-group.component.spec.ts b/frontend/src/app/components/skeletons/placeholder-user-group/placeholder-user-group.component.spec.ts index 1015104b6..156607785 100644 --- a/frontend/src/app/components/skeletons/placeholder-user-group/placeholder-user-group.component.spec.ts +++ b/frontend/src/app/components/skeletons/placeholder-user-group/placeholder-user-group.component.spec.ts @@ -3,21 +3,20 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { PlaceholderUserGroupComponent } from './placeholder-user-group.component'; describe('PlaceholderUserGroupComponent', () => { - let component: PlaceholderUserGroupComponent; - let fixture: ComponentFixture; + let component: PlaceholderUserGroupComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [PlaceholderUserGroupComponent] -}) - .compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [PlaceholderUserGroupComponent], + }).compileComponents(); - fixture = TestBed.createComponent(PlaceholderUserGroupComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + fixture = TestBed.createComponent(PlaceholderUserGroupComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/skeletons/placeholder-user-group/placeholder-user-group.component.ts b/frontend/src/app/components/skeletons/placeholder-user-group/placeholder-user-group.component.ts index 0b8a88aeb..f47547f98 100644 --- a/frontend/src/app/components/skeletons/placeholder-user-group/placeholder-user-group.component.ts +++ b/frontend/src/app/components/skeletons/placeholder-user-group/placeholder-user-group.component.ts @@ -1,10 +1,8 @@ import { Component } from '@angular/core'; @Component({ - selector: 'app-placeholder-user-group', - templateUrl: './placeholder-user-group.component.html', - styleUrls: ['./placeholder-user-group.component.css'] + selector: 'app-placeholder-user-group', + templateUrl: './placeholder-user-group.component.html', + styleUrls: ['./placeholder-user-group.component.css'], }) -export class PlaceholderUserGroupComponent { - -} +export class PlaceholderUserGroupComponent {} diff --git a/frontend/src/app/components/skeletons/placeholder-user-groups/placeholder-user-groups.component.css b/frontend/src/app/components/skeletons/placeholder-user-groups/placeholder-user-groups.component.css index 382888621..3d739c96c 100644 --- a/frontend/src/app/components/skeletons/placeholder-user-groups/placeholder-user-groups.component.css +++ b/frontend/src/app/components/skeletons/placeholder-user-groups/placeholder-user-groups.component.css @@ -1,28 +1,28 @@ .panels { - border-radius: 4px; - box-shadow: - 0px 3px 1px -2px rgba(0, 0, 0, 0.2), - 0px 2px 2px 0px rgba(0, 0, 0, 0.14), - 0px 1px 5px 0px rgba(0, 0, 0, 0.12); - margin-top: 16px; + border-radius: 4px; + box-shadow: + 0px 3px 1px -2px rgba(0, 0, 0, 0.2), + 0px 2px 2px 0px rgba(0, 0, 0, 0.14), + 0px 1px 5px 0px rgba(0, 0, 0, 0.12); + margin-top: 16px; } .panel { - display: flex; - align-items: center; - justify-content: space-between; - border-bottom: 1px solid #0000001f; - height: 36px; - padding: 0 24px; + display: flex; + align-items: center; + justify-content: space-between; + border-bottom: 1px solid #0000001f; + height: 36px; + padding: 0 24px; } .groupName { - height: 20px; - width: 240px; + height: 20px; + width: 240px; } .groupAction { - border-radius: 50%; - height: 24px; - width: 24px; -} \ No newline at end of file + border-radius: 50%; + height: 24px; + width: 24px; +} diff --git a/frontend/src/app/components/skeletons/placeholder-user-groups/placeholder-user-groups.component.spec.ts b/frontend/src/app/components/skeletons/placeholder-user-groups/placeholder-user-groups.component.spec.ts index fea343b8c..ac44b2417 100644 --- a/frontend/src/app/components/skeletons/placeholder-user-groups/placeholder-user-groups.component.spec.ts +++ b/frontend/src/app/components/skeletons/placeholder-user-groups/placeholder-user-groups.component.spec.ts @@ -3,21 +3,20 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { PlaceholderUserGroupsComponent } from './placeholder-user-groups.component'; describe('PlaceholderUserGroupsComponent', () => { - let component: PlaceholderUserGroupsComponent; - let fixture: ComponentFixture; + let component: PlaceholderUserGroupsComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [PlaceholderUserGroupsComponent] -}) - .compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [PlaceholderUserGroupsComponent], + }).compileComponents(); - fixture = TestBed.createComponent(PlaceholderUserGroupsComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + fixture = TestBed.createComponent(PlaceholderUserGroupsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/skeletons/placeholder-user-groups/placeholder-user-groups.component.ts b/frontend/src/app/components/skeletons/placeholder-user-groups/placeholder-user-groups.component.ts index df7abedb7..cb658b118 100644 --- a/frontend/src/app/components/skeletons/placeholder-user-groups/placeholder-user-groups.component.ts +++ b/frontend/src/app/components/skeletons/placeholder-user-groups/placeholder-user-groups.component.ts @@ -1,10 +1,8 @@ import { Component } from '@angular/core'; @Component({ - selector: 'app-placeholder-user-groups', - templateUrl: './placeholder-user-groups.component.html', - styleUrls: ['./placeholder-user-groups.component.css'] + selector: 'app-placeholder-user-groups', + templateUrl: './placeholder-user-groups.component.html', + styleUrls: ['./placeholder-user-groups.component.css'], }) -export class PlaceholderUserGroupsComponent { - -} +export class PlaceholderUserGroupsComponent {} diff --git a/frontend/src/app/components/sso/sso.component.css b/frontend/src/app/components/sso/sso.component.css index a90036289..bbbab9d1b 100644 --- a/frontend/src/app/components/sso/sso.component.css +++ b/frontend/src/app/components/sso/sso.component.css @@ -1,98 +1,98 @@ .profile-layout { - display: flex; - height: calc(100vh - 64px); + display: flex; + height: calc(100vh - 64px); } .profile-main { - flex: 1; - background-color: var(--mat-sidenav-content-background-color); - overflow-y: auto; + flex: 1; + background-color: var(--mat-sidenav-content-background-color); + overflow-y: auto; } ::ng-deep .profile-main > app-alert { - position: relative; - top: 0; - margin: 24px; + position: relative; + top: 0; + margin: 24px; } .sso-page { - margin: var(--top-margin) auto; - padding: 0 clamp(40px, 5vw, 100px); - max-width: 800px; + margin: var(--top-margin) auto; + padding: 0 clamp(40px, 5vw, 100px); + max-width: 800px; } @media (width <= 600px) { - .sso-page { - padding: 0 16px; - margin: 1.5em auto; - } + .sso-page { + padding: 0 16px; + margin: 1.5em auto; + } } .sso-description { - color: rgba(0, 0, 0, 0.64); - margin-top: 8px; - margin-bottom: 32px; + color: rgba(0, 0, 0, 0.64); + margin-top: 8px; + margin-bottom: 32px; } @media (prefers-color-scheme: dark) { - .sso-description { - color: rgba(255, 255, 255, 0.7); - } + .sso-description { + color: rgba(255, 255, 255, 0.7); + } } .saml-config-form { - display: flex; - flex-direction: column; + display: flex; + flex-direction: column; } .saml-config-form__checkboxes { - display: flex; - flex-direction: column; - gap: 12px; - margin-bottom: 24px; + display: flex; + flex-direction: column; + gap: 12px; + margin-bottom: 24px; } .saml-config-form__checkbox { - margin: 0; + margin: 0; } .saml-config-form__actions { - display: flex; - justify-content: flex-end; - margin-top: 16px; - padding-bottom: 32px; + display: flex; + justify-content: flex-end; + margin-top: 16px; + padding-bottom: 32px; } .upgrade-required { - display: flex; - flex-direction: column; - align-items: center; - text-align: center; - padding: 48px 24px; + display: flex; + flex-direction: column; + align-items: center; + text-align: center; + padding: 48px 24px; } .upgrade-required__icon { - font-size: 64px; - width: 64px; - height: 64px; - color: rgba(0, 0, 0, 0.38); - margin-bottom: 24px; + font-size: 64px; + width: 64px; + height: 64px; + color: rgba(0, 0, 0, 0.38); + margin-bottom: 24px; } @media (prefers-color-scheme: dark) { - .upgrade-required__icon { - color: rgba(255, 255, 255, 0.38); - } + .upgrade-required__icon { + color: rgba(255, 255, 255, 0.38); + } } .upgrade-required__description { - color: rgba(0, 0, 0, 0.64); - margin-bottom: 24px; - max-width: 400px; + color: rgba(0, 0, 0, 0.64); + margin-bottom: 24px; + max-width: 400px; } @media (prefers-color-scheme: dark) { - .upgrade-required__description { - color: rgba(255, 255, 255, 0.7); - } + .upgrade-required__description { + color: rgba(255, 255, 255, 0.7); + } } diff --git a/frontend/src/app/components/sso/sso.component.spec.ts b/frontend/src/app/components/sso/sso.component.spec.ts index 510354fc1..445e27f0d 100644 --- a/frontend/src/app/components/sso/sso.component.spec.ts +++ b/frontend/src/app/components/sso/sso.component.spec.ts @@ -52,10 +52,7 @@ describe('SsoComponent', () => { await TestBed.configureTestingModule({ imports: [SsoComponent, RouterModule.forRoot([])], - providers: [ - provideHttpClient(), - { provide: CompanyService, useValue: companyServiceSpy }, - ], + providers: [provideHttpClient(), { provide: CompanyService, useValue: companyServiceSpy }], }).compileComponents(); fixture = TestBed.createComponent(SsoComponent); diff --git a/frontend/src/app/components/sso/sso.component.ts b/frontend/src/app/components/sso/sso.component.ts index 5f63d544b..9f09d25e0 100644 --- a/frontend/src/app/components/sso/sso.component.ts +++ b/frontend/src/app/components/sso/sso.component.ts @@ -7,14 +7,14 @@ import { MatFormFieldModule } from '@angular/material/form-field'; import { MatIconModule } from '@angular/material/icon'; import { MatInputModule } from '@angular/material/input'; import { MatTooltipModule } from '@angular/material/tooltip'; -import { RouterModule } from '@angular/router'; import { Title } from '@angular/platform-browser'; -import { SamlConfig, Company } from 'src/app/models/company'; +import { RouterModule } from '@angular/router'; +import { Company, SamlConfig } from 'src/app/models/company'; import { SubscriptionPlans } from 'src/app/models/user'; import { CompanyService } from 'src/app/services/company.service'; -import { AlertComponent } from '../ui-components/alert/alert.component'; import { ProfileSidebarComponent } from '../profile/profile-sidebar/profile-sidebar.component'; import { PlaceholderSsoComponent } from '../skeletons/placeholder-sso/placeholder-sso.component'; +import { AlertComponent } from '../ui-components/alert/alert.component'; @Component({ selector: 'app-sso', @@ -67,13 +67,14 @@ export class SsoComponent implements OnInit { ) {} ngOnInit() { - this._company.getCurrentTabTitle().subscribe(tabTitle => { + this._company.getCurrentTabTitle().subscribe((tabTitle) => { this.title.setTitle(`SAML SSO | ${tabTitle || 'Rocketadmin'}`); }); - this._company.fetchCompany().subscribe(res => { + this._company.fetchCompany().subscribe((res) => { this.company = res; - this.hasEnterprisePlan = res.subscriptionLevel === SubscriptionPlans.enterprise || + this.hasEnterprisePlan = + res.subscriptionLevel === SubscriptionPlans.enterprise || res.subscriptionLevel === SubscriptionPlans.enterpriseAnnual; if (this.hasEnterprisePlan) { @@ -90,7 +91,7 @@ export class SsoComponent implements OnInit { () => { this.submitting = false; this.saved = true; - setTimeout(() => this.saved = false, 3000); + setTimeout(() => (this.saved = false), 3000); }, () => { this.submitting = false; @@ -104,7 +105,7 @@ export class SsoComponent implements OnInit { () => { this.submitting = false; this.saved = true; - setTimeout(() => this.saved = false, 3000); + setTimeout(() => (this.saved = false), 3000); }, () => { this.submitting = false; diff --git a/frontend/src/app/components/ui-components/alert/alert.component.css b/frontend/src/app/components/ui-components/alert/alert.component.css index 77a6d34e4..d9262e252 100644 --- a/frontend/src/app/components/ui-components/alert/alert.component.css +++ b/frontend/src/app/components/ui-components/alert/alert.component.css @@ -1,119 +1,118 @@ :host { - position: sticky; - top: calc(var(--mat-toolbar-standard-height) + 24px); - display: block; - margin: var(--alert-margin); - z-index: 2; + position: sticky; + top: calc(var(--mat-toolbar-standard-height) + 24px); + display: block; + margin: var(--alert-margin); + z-index: 2; } .alert { - display: flex; - gap: 8px; - background-color: var(--bg-color); - backdrop-filter: blur(4px); - border-top: 1px solid var(--alert-theme-color); - border-right: 1px solid var(--alert-theme-color); - border-bottom: 1px solid var(--alert-theme-color); - border-left: 12px solid var(--alert-theme-color); - border-radius: 4px; - padding: 12px 16px 12px 8px; + display: flex; + gap: 8px; + background-color: var(--bg-color); + backdrop-filter: blur(4px); + border-top: 1px solid var(--alert-theme-color); + border-right: 1px solid var(--alert-theme-color); + border-bottom: 1px solid var(--alert-theme-color); + border-left: 12px solid var(--alert-theme-color); + border-radius: 4px; + padding: 12px 16px 12px 8px; } @media (prefers-color-scheme: dark) { - .alert { - border-top: 1px solid var(--alert-dark-theme-color); - border-right: 1px solid var(--alert-dark-theme-color); - border-bottom: 1px solid var(--alert-dark-theme-color); - border-left: 12px solid var(--alert-dark-theme-color); - backdrop-filter: blur(2px); - } + .alert { + border-top: 1px solid var(--alert-dark-theme-color); + border-right: 1px solid var(--alert-dark-theme-color); + border-bottom: 1px solid var(--alert-dark-theme-color); + border-left: 12px solid var(--alert-dark-theme-color); + backdrop-filter: blur(2px); + } } @media (width <= 600px) { - .alert { - display: grid; - grid-template-columns: auto 1fr; - } + .alert { + display: grid; + grid-template-columns: auto 1fr; + } } .alert_error { - --alert-theme-color: #B71C1C; - --alert-dark-theme-color: #E53935; - --bg-color: color-mix(in hsl, #B71C1C, transparent 95%); + --alert-theme-color: #b71c1c; + --alert-dark-theme-color: #e53935; + --bg-color: color-mix(in hsl, #b71c1c, transparent 95%); } .alert_warning { - --alert-theme-color: #F79008; - --alert-dark-theme-color: #F79008; - --bg-color: color-mix(in hsl, #F79008, transparent 95%); + --alert-theme-color: #f79008; + --alert-dark-theme-color: #f79008; + --bg-color: color-mix(in hsl, #f79008, transparent 95%); } .alert_info { - --alert-theme-color: #296EE9; - --alert-dark-theme-color: #296EE9; - --bg-color: color-mix(in hsl, #296EE9, transparent 95%); + --alert-theme-color: #296ee9; + --alert-dark-theme-color: #296ee9; + --bg-color: color-mix(in hsl, #296ee9, transparent 95%); } .alert_success { - --alert-theme-color: #1B5E20; - --alert-dark-theme-color: #4CAF50; - --bg-color: color-mix(in hsl, #1B5E20, transparent 95%); + --alert-theme-color: #1b5e20; + --alert-dark-theme-color: #4caf50; + --bg-color: color-mix(in hsl, #1b5e20, transparent 95%); } .alert__icon { - flex-shrink: 0; - display: flex; - align-items: center; - justify-content: center; - border-radius: 50%; - color: var(--alert-theme-color); - font-size: 24px; - height: 36px; - opacity: 0.9; - width: 36px; + flex-shrink: 0; + display: flex; + align-items: center; + justify-content: center; + border-radius: 50%; + color: var(--alert-theme-color); + font-size: 24px; + height: 36px; + opacity: 0.9; + width: 36px; } @media (prefers-color-scheme: dark) { - .alert__icon { - color: var(--alert-dark-theme-color); - } + .alert__icon { + color: var(--alert-dark-theme-color); + } } .alert__message { - flex: 1; - display: flex; - justify-content: center; - flex-direction: column; - min-height: 36px; - margin: 0 !important; + flex: 1; + display: flex; + justify-content: center; + flex-direction: column; + min-height: 36px; + margin: 0 !important; } .alert__title { - color: var(--alert-theme-color); - margin-bottom: 8px; + color: var(--alert-theme-color); + margin-bottom: 8px; } @media (prefers-color-scheme: dark) { - .alert__title { - color: inherit; - } + .alert__title { + color: inherit; + } } .alert__text { - margin: 0 !important; + margin: 0 !important; } .actions { - margin-left: auto; + margin-left: auto; } @media (width <= 600px) { - .actions { - grid-column: 1 / -1; - } + .actions { + grid-column: 1 / -1; + } } .mat-flat-button { - color: var(--text-color); + color: var(--text-color); } - diff --git a/frontend/src/app/components/ui-components/alert/alert.component.ts b/frontend/src/app/components/ui-components/alert/alert.component.ts index fb3115006..1d75e8c26 100644 --- a/frontend/src/app/components/ui-components/alert/alert.component.ts +++ b/frontend/src/app/components/ui-components/alert/alert.component.ts @@ -1,58 +1,55 @@ -import { Component, Input, OnInit } from '@angular/core'; - -import { Alert } from 'src/app/models/alert'; import { CommonModule } from '@angular/common'; +import { Component, Input, OnInit } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; -import { NotificationsService } from 'src/app/services/notifications.service'; import { RouterModule } from '@angular/router'; +import { Alert } from 'src/app/models/alert'; +import { NotificationsService } from 'src/app/services/notifications.service'; @Component({ - selector: 'app-alert', - templateUrl: './alert.component.html', - styleUrls: ['./alert.component.css'], - imports: [CommonModule, MatIconModule, MatButtonModule, RouterModule], + selector: 'app-alert', + templateUrl: './alert.component.html', + styleUrls: ['./alert.component.css'], + imports: [CommonModule, MatIconModule, MatButtonModule, RouterModule], }) export class AlertComponent implements OnInit { - @Input() alert: Alert; - - public messageAbstract: string; - public messageDetails: string; - - public icons = { - error: 'error_outline', - warning: 'warning_amber', - info: 'info', - success: 'task_alt' - } - - constructor( - private _notifications: NotificationsService, - ) { } - - get currentAlert(): Alert { - return this.alert || this._notifications.currentAlert; - } - - ngOnInit(): void { - console.log(this.currentAlert); - } - - onButtonClick(alert: Alert, action) { - action.action(alert.id); - } - - getMessageType() { - if (this.currentAlert.message === null) return; - if (typeof this.currentAlert.message === 'string') return 'plainMessage'; - if (this.currentAlert.message.abstract && !this.currentAlert.message.details) { - this.currentAlert.message = this.currentAlert.message.abstract; - return 'plainMessage' - }; - if (this.currentAlert.message.details) { - this.messageAbstract = this.currentAlert.message.abstract; - this.messageDetails = this.currentAlert.message.details; - return 'complexMessage' - }; - } + @Input() alert: Alert; + + public messageAbstract: string; + public messageDetails: string; + + public icons = { + error: 'error_outline', + warning: 'warning_amber', + info: 'info', + success: 'task_alt', + }; + + constructor(private _notifications: NotificationsService) {} + + get currentAlert(): Alert { + return this.alert || this._notifications.currentAlert; + } + + ngOnInit(): void { + console.log(this.currentAlert); + } + + onButtonClick(alert: Alert, action) { + action.action(alert.id); + } + + getMessageType() { + if (this.currentAlert.message === null) return; + if (typeof this.currentAlert.message === 'string') return 'plainMessage'; + if (this.currentAlert.message.abstract && !this.currentAlert.message.details) { + this.currentAlert.message = this.currentAlert.message.abstract; + return 'plainMessage'; + } + if (this.currentAlert.message.details) { + this.messageAbstract = this.currentAlert.message.abstract; + this.messageDetails = this.currentAlert.message.details; + return 'complexMessage'; + } + } } diff --git a/frontend/src/app/components/ui-components/banner/banner.component.css b/frontend/src/app/components/ui-components/banner/banner.component.css index c23f14165..bc86bce07 100644 --- a/frontend/src/app/components/ui-components/banner/banner.component.css +++ b/frontend/src/app/components/ui-components/banner/banner.component.css @@ -1,77 +1,74 @@ :host { - display: block; - height: 100%; + display: block; + height: 100%; } .banner-wrapper { - display: flex; - align-items: center; - justify-content: center; - background-position: center, center; - background-repeat: no-repeat, no-repeat; - background-size: 100%, cover; - height: calc(100% + 1px); - margin-top: -1px; + display: flex; + align-items: center; + justify-content: center; + background-position: center, center; + background-repeat: no-repeat, no-repeat; + background-size: 100%, cover; + height: calc(100% + 1px); + margin-top: -1px; } @media (prefers-color-scheme: light) { - .banner-wrapper { - background-image: - linear-gradient(rgba(255, 255, 255, 0.4), rgba(255, 255, 255, 0.4)), - url('../../../../assets/bg/banner.svg'); - } + .banner-wrapper { + background-image: + linear-gradient(rgba(255, 255, 255, 0.4), rgba(255, 255, 255, 0.4)), url("../../../../assets/bg/banner.svg"); + } } @media (prefers-color-scheme: dark) { - .banner-wrapper { - background-image: - linear-gradient(rgba(0, 0, 0, 0.4), rgba(0, 0, 0, 0.4)), - url('../../../../assets/bg/banner.svg'); - background-color: #1a1a1a; - } + .banner-wrapper { + background-image: linear-gradient(rgba(0, 0, 0, 0.4), rgba(0, 0, 0, 0.4)), url("../../../../assets/bg/banner.svg"); + background-color: #1a1a1a; + } } .banner_error { - --alert-theme-color: #B71C1C; - --alert-dark-theme-color: #E53935; - --bg-color: color-mix(in hsl, #B71C1C, transparent 95%); + --alert-theme-color: #b71c1c; + --alert-dark-theme-color: #e53935; + --bg-color: color-mix(in hsl, #b71c1c, transparent 95%); } .banner_warning { - --alert-theme-color: #F79008; - --alert-dark-theme-color: #F79008; - --bg-color: color-mix(in hsl, #F79008, transparent 95%); + --alert-theme-color: #f79008; + --alert-dark-theme-color: #f79008; + --bg-color: color-mix(in hsl, #f79008, transparent 95%); } .banner_info { - --alert-theme-color: #296EE9; - --alert-dark-theme-color: #296EE9; - --bg-color: color-mix(in hsl, #296EE9, transparent 95%); + --alert-theme-color: #296ee9; + --alert-dark-theme-color: #296ee9; + --bg-color: color-mix(in hsl, #296ee9, transparent 95%); } .banner_success { - --alert-theme-color: #1B5E20; - --alert-dark-theme-color: #4CAF50; - --bg-color: color-mix(in hsl, #1B5E20, transparent 95%); + --alert-theme-color: #1b5e20; + --alert-dark-theme-color: #4caf50; + --bg-color: color-mix(in hsl, #1b5e20, transparent 95%); } .banner-box { - background-color: var(--bg-color); - backdrop-filter: blur(2px); - border: 1px solid var(--alert-theme-color); - border-left: 12px solid var(--alert-theme-color); - border-radius: 4px; - box-shadow: - 0px 1px 5px 0px rgba(0, 0, 0, 0.2), - 0px 3px 4px 0px rgba(0, 0, 0, 0.12), - 0px 2px 4px 0px rgba(0, 0, 0, 0.14); - padding: 24px 20px 24px 28px; - width: clamp(300px, 50vw, 450px); + background-color: var(--bg-color); + backdrop-filter: blur(2px); + border: 1px solid var(--alert-theme-color); + border-left: 12px solid var(--alert-theme-color); + border-radius: 4px; + box-shadow: + 0px 1px 5px 0px rgba(0, 0, 0, 0.2), + 0px 3px 4px 0px rgba(0, 0, 0, 0.12), + 0px 2px 4px 0px rgba(0, 0, 0, 0.14); + padding: 24px 20px 24px 28px; + width: clamp(300px, 50vw, 450px); } .banner-box ::ng-deep .error-actions { - display: flex; - justify-content: flex-end; - gap: 20px; - margin-top: 32px; -} \ No newline at end of file + display: flex; + justify-content: flex-end; + gap: 20px; + margin-top: 32px; +} diff --git a/frontend/src/app/components/ui-components/banner/banner.component.spec.ts b/frontend/src/app/components/ui-components/banner/banner.component.spec.ts index 2f8b62c63..528240c6f 100644 --- a/frontend/src/app/components/ui-components/banner/banner.component.spec.ts +++ b/frontend/src/app/components/ui-components/banner/banner.component.spec.ts @@ -1,31 +1,24 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { BannerComponent } from './banner.component'; import { MatSnackBarModule } from '@angular/material/snack-bar'; +import { BannerComponent } from './banner.component'; describe('BannerComponent', () => { - let component: BannerComponent; - let fixture: ComponentFixture; + let component: BannerComponent; + let fixture: ComponentFixture; + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [MatSnackBarModule, BannerComponent], + }).compileComponents(); + }); - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [ - MatSnackBarModule, - BannerComponent - ] -}) - .compileComponents(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(BannerComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(BannerComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); - diff --git a/frontend/src/app/components/ui-components/banner/banner.component.ts b/frontend/src/app/components/ui-components/banner/banner.component.ts index f8c1ccd6e..62bcfe917 100644 --- a/frontend/src/app/components/ui-components/banner/banner.component.ts +++ b/frontend/src/app/components/ui-components/banner/banner.component.ts @@ -3,13 +3,12 @@ import { Component, Input, OnInit } from '@angular/core'; import { AlertType } from 'src/app/models/alert'; @Component({ - selector: 'app-banner', - templateUrl: './banner.component.html', - styleUrls: ['./banner.component.css'] + selector: 'app-banner', + templateUrl: './banner.component.html', + styleUrls: ['./banner.component.css'], }) export class BannerComponent implements OnInit { - @Input() type: AlertType; + @Input() type: AlertType; - - ngOnInit(): void {} + ngOnInit(): void {} } diff --git a/frontend/src/app/components/ui-components/breadcrumbs/breadcrumbs.component.css b/frontend/src/app/components/ui-components/breadcrumbs/breadcrumbs.component.css index 40a7e2515..8115c7757 100644 --- a/frontend/src/app/components/ui-components/breadcrumbs/breadcrumbs.component.css +++ b/frontend/src/app/components/ui-components/breadcrumbs/breadcrumbs.component.css @@ -1,17 +1,17 @@ .breadcrumbs { - display: flex; - align-items: center; + display: flex; + align-items: center; } @media (prefers-color-scheme: li) { - .breadcrumb, - .breadcrumbDivider { - color: rgba(0,0,0,0.6); - } + .breadcrumb, + .breadcrumbDivider { + color: rgba(0, 0, 0, 0.6); + } } .static-crumb { - margin-left: 8px; - margin-right: 16px; - margin-bottom: 0 !important; -} \ No newline at end of file + margin-left: 8px; + margin-right: 16px; + margin-bottom: 0 !important; +} diff --git a/frontend/src/app/components/ui-components/breadcrumbs/breadcrumbs.component.spec.ts b/frontend/src/app/components/ui-components/breadcrumbs/breadcrumbs.component.spec.ts index 7ff512bc1..17ea00679 100644 --- a/frontend/src/app/components/ui-components/breadcrumbs/breadcrumbs.component.spec.ts +++ b/frontend/src/app/components/ui-components/breadcrumbs/breadcrumbs.component.spec.ts @@ -3,21 +3,20 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { BreadcrumbsComponent } from './breadcrumbs.component'; describe('BreadcrumbsComponent', () => { - let component: BreadcrumbsComponent; - let fixture: ComponentFixture; + let component: BreadcrumbsComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [BreadcrumbsComponent] -}) - .compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [BreadcrumbsComponent], + }).compileComponents(); - fixture = TestBed.createComponent(BreadcrumbsComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + fixture = TestBed.createComponent(BreadcrumbsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/ui-components/breadcrumbs/breadcrumbs.component.ts b/frontend/src/app/components/ui-components/breadcrumbs/breadcrumbs.component.ts index 78696d02e..7a611a7ea 100644 --- a/frontend/src/app/components/ui-components/breadcrumbs/breadcrumbs.component.ts +++ b/frontend/src/app/components/ui-components/breadcrumbs/breadcrumbs.component.ts @@ -1,22 +1,18 @@ -import { Component, Input, OnInit } from '@angular/core'; - import { CommonModule } from '@angular/common'; +import { Component, Input, OnInit } from '@angular/core'; +import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { RouterModule } from '@angular/router'; -import { MatButtonModule } from '@angular/material/button'; @Component({ - selector: 'app-breadcrumbs', - templateUrl: './breadcrumbs.component.html', - styleUrls: ['./breadcrumbs.component.css'], - standalone: true, - imports: [CommonModule, RouterModule, MatIconModule, MatButtonModule] + selector: 'app-breadcrumbs', + templateUrl: './breadcrumbs.component.html', + styleUrls: ['./breadcrumbs.component.css'], + standalone: true, + imports: [CommonModule, RouterModule, MatIconModule, MatButtonModule], }) export class BreadcrumbsComponent implements OnInit { - @Input() crumbs; - - ngOnInit(): void { - - } + @Input() crumbs; + ngOnInit(): void {} } diff --git a/frontend/src/app/components/ui-components/content-loader/content-loader.component.css b/frontend/src/app/components/ui-components/content-loader/content-loader.component.css index 5cfb2f23a..dbfac9255 100644 --- a/frontend/src/app/components/ui-components/content-loader/content-loader.component.css +++ b/frontend/src/app/components/ui-components/content-loader/content-loader.component.css @@ -1,18 +1,26 @@ .dots { width: 20px; - height: 20px; - background: #171D39; - border-radius: 50%; - position: absolute; - top: calc(50% - 20px); - left: calc(50% - 20px); - box-shadow: #171D39 20px 0, #171D39 40px 0, #171D39 -20px 0, #171D39 -40px 0; - animation: pulse 1.25s infinite ease-in-out; + height: 20px; + background: #171d39; + border-radius: 50%; + position: absolute; + top: calc(50% - 20px); + left: calc(50% - 20px); + box-shadow: + #171d39 20px 0, + #171d39 40px 0, + #171d39 -20px 0, + #171d39 -40px 0; + animation: pulse 1.25s infinite ease-in-out; } @keyframes pulse { - 50% { - box-shadow: #171D39 45px 0, #171D39 90px 0, #171D39 -45px 0, #171D39 -90px 0; - transform: scale(.7) - } -} \ No newline at end of file + 50% { + box-shadow: + #171d39 45px 0, + #171d39 90px 0, + #171d39 -45px 0, + #171d39 -90px 0; + transform: scale(0.7); + } +} diff --git a/frontend/src/app/components/ui-components/content-loader/content-loader.component.spec.ts b/frontend/src/app/components/ui-components/content-loader/content-loader.component.spec.ts index 886c46afc..c675d059c 100644 --- a/frontend/src/app/components/ui-components/content-loader/content-loader.component.spec.ts +++ b/frontend/src/app/components/ui-components/content-loader/content-loader.component.spec.ts @@ -3,22 +3,22 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ContentLoaderComponent } from './content-loader.component'; describe('ContentLoaderComponent', () => { - let component: ContentLoaderComponent; - let fixture: ComponentFixture; + let component: ContentLoaderComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [ContentLoaderComponent] - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ContentLoaderComponent], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(ContentLoaderComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(ContentLoaderComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/ui-components/content-loader/content-loader.component.ts b/frontend/src/app/components/ui-components/content-loader/content-loader.component.ts index 8bc4ea87b..f92b0ae93 100644 --- a/frontend/src/app/components/ui-components/content-loader/content-loader.component.ts +++ b/frontend/src/app/components/ui-components/content-loader/content-loader.component.ts @@ -1,13 +1,10 @@ import { Component, OnInit } from '@angular/core'; @Component({ - selector: 'app-content-loader', - templateUrl: './content-loader.component.html', - styleUrls: ['./content-loader.component.css'] + selector: 'app-content-loader', + templateUrl: './content-loader.component.html', + styleUrls: ['./content-loader.component.css'], }) export class ContentLoaderComponent implements OnInit { - - ngOnInit(): void { - } - + ngOnInit(): void {} } diff --git a/frontend/src/app/components/ui-components/filter-fields/base-filter-field/base-filter-field.component.spec.ts b/frontend/src/app/components/ui-components/filter-fields/base-filter-field/base-filter-field.component.spec.ts index 3696240da..41fef9d85 100644 --- a/frontend/src/app/components/ui-components/filter-fields/base-filter-field/base-filter-field.component.spec.ts +++ b/frontend/src/app/components/ui-components/filter-fields/base-filter-field/base-filter-field.component.spec.ts @@ -3,20 +3,20 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { BaseFilterFieldComponent } from './base-filter-field.component'; describe('BaseFilterFieldComponent', () => { - let component: BaseFilterFieldComponent; - let fixture: ComponentFixture; + let component: BaseFilterFieldComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [BaseFilterFieldComponent] - }).compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [BaseFilterFieldComponent], + }).compileComponents(); - fixture = TestBed.createComponent(BaseFilterFieldComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + fixture = TestBed.createComponent(BaseFilterFieldComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/ui-components/filter-fields/base-filter-field/base-filter-field.component.ts b/frontend/src/app/components/ui-components/filter-fields/base-filter-field/base-filter-field.component.ts index 774c159cd..bc42c2f70 100644 --- a/frontend/src/app/components/ui-components/filter-fields/base-filter-field/base-filter-field.component.ts +++ b/frontend/src/app/components/ui-components/filter-fields/base-filter-field/base-filter-field.component.ts @@ -4,26 +4,26 @@ import { TableField, TableForeignKey, WidgetStructure } from 'src/app/models/tab import { normalizeFieldName } from '../../../../lib/normalize'; @Component({ - selector: 'app-base-filter-field', - templateUrl: './base-filter-field.component.html', - styleUrl: './base-filter-field.component.css' + selector: 'app-base-filter-field', + templateUrl: './base-filter-field.component.html', + styleUrl: './base-filter-field.component.css', }) export class BaseFilterFieldComponent implements OnInit { - @Input() key: string; - @Input() label: string; - @Input() required: boolean; - @Input() readonly: boolean; - @Input() structure: TableField; - @Input() disabled: boolean; - @Input() widgetStructure: WidgetStructure; - @Input() relations: TableForeignKey; - @Input() autofocus: boolean = false; + @Input() key: string; + @Input() label: string; + @Input() required: boolean; + @Input() readonly: boolean; + @Input() structure: TableField; + @Input() disabled: boolean; + @Input() widgetStructure: WidgetStructure; + @Input() relations: TableForeignKey; + @Input() autofocus: boolean = false; - @Output() onFieldChange = new EventEmitter(); + @Output() onFieldChange = new EventEmitter(); - public normalizedLabel: string; + public normalizedLabel: string; - ngOnInit(): void { - this.normalizedLabel = normalizeFieldName(this.label); - } + ngOnInit(): void { + this.normalizedLabel = normalizeFieldName(this.label); + } } diff --git a/frontend/src/app/components/ui-components/filter-fields/binary-data-caption/binary-data-caption.component.css b/frontend/src/app/components/ui-components/filter-fields/binary-data-caption/binary-data-caption.component.css index 86302ca2c..85157a202 100644 --- a/frontend/src/app/components/ui-components/filter-fields/binary-data-caption/binary-data-caption.component.css +++ b/frontend/src/app/components/ui-components/filter-fields/binary-data-caption/binary-data-caption.component.css @@ -1,10 +1,10 @@ .static-text { - margin-bottom: 20px; + margin-bottom: 20px; } .static-text__value { - color: #0a0a0a; - font-style: italic; - opacity: 0.32; - padding-left: 8px; -} \ No newline at end of file + color: #0a0a0a; + font-style: italic; + opacity: 0.32; + padding-left: 8px; +} diff --git a/frontend/src/app/components/ui-components/filter-fields/binary-data-caption/binary-data-caption.component.spec.ts b/frontend/src/app/components/ui-components/filter-fields/binary-data-caption/binary-data-caption.component.spec.ts index 246e7af4d..5e0a23f9e 100644 --- a/frontend/src/app/components/ui-components/filter-fields/binary-data-caption/binary-data-caption.component.spec.ts +++ b/frontend/src/app/components/ui-components/filter-fields/binary-data-caption/binary-data-caption.component.spec.ts @@ -3,22 +3,22 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { BinaryDataCaptionFilterComponent } from './binary-data-caption.component'; describe('BinaryDataCaptionFilterComponent', () => { - let component: BinaryDataCaptionFilterComponent; - let fixture: ComponentFixture; + let component: BinaryDataCaptionFilterComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [BinaryDataCaptionFilterComponent] - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [BinaryDataCaptionFilterComponent], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(BinaryDataCaptionFilterComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(BinaryDataCaptionFilterComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/ui-components/filter-fields/binary-data-caption/binary-data-caption.component.ts b/frontend/src/app/components/ui-components/filter-fields/binary-data-caption/binary-data-caption.component.ts index 7a2300fae..7c6385778 100644 --- a/frontend/src/app/components/ui-components/filter-fields/binary-data-caption/binary-data-caption.component.ts +++ b/frontend/src/app/components/ui-components/filter-fields/binary-data-caption/binary-data-caption.component.ts @@ -1,11 +1,11 @@ -import { BaseFilterFieldComponent } from '../base-filter-field/base-filter-field.component'; import { CommonModule } from '@angular/common'; import { Component } from '@angular/core'; +import { BaseFilterFieldComponent } from '../base-filter-field/base-filter-field.component'; @Component({ - selector: 'app-filter-static-text', - templateUrl: './binary-data-caption.component.html', - styleUrls: ['./binary-data-caption.component.css'], - imports: [CommonModule] + selector: 'app-filter-static-text', + templateUrl: './binary-data-caption.component.html', + styleUrls: ['./binary-data-caption.component.css'], + imports: [CommonModule], }) -export class BinaryDataCaptionFilterComponent extends BaseFilterFieldComponent { } +export class BinaryDataCaptionFilterComponent extends BaseFilterFieldComponent {} diff --git a/frontend/src/app/components/ui-components/filter-fields/boolean/boolean.component.spec.ts b/frontend/src/app/components/ui-components/filter-fields/boolean/boolean.component.spec.ts index 3301f7702..22a27f55e 100644 --- a/frontend/src/app/components/ui-components/filter-fields/boolean/boolean.component.spec.ts +++ b/frontend/src/app/components/ui-components/filter-fields/boolean/boolean.component.spec.ts @@ -1,85 +1,79 @@ +import { provideHttpClient } from '@angular/common/http'; import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { BooleanFilterComponent } from './boolean.component'; import { MatDialogModule } from '@angular/material/dialog'; import { MatSnackBarModule } from '@angular/material/snack-bar'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { provideHttpClient } from '@angular/common/http'; +import { BooleanFilterComponent } from './boolean.component'; describe('BooleanFilterComponent', () => { - let component: BooleanFilterComponent; - let fixture: ComponentFixture; + let component: BooleanFilterComponent; + let fixture: ComponentFixture; - const fakeStructure = { - "column_name": "banned", - "column_default": "0", - "data_type": "tinyint", - "isExcluded": false, - "isSearched": false, - "auto_increment": false, - "allow_null": false, - "character_maximum_length": 1 - } + const fakeStructure = { + column_name: 'banned', + column_default: '0', + data_type: 'tinyint', + isExcluded: false, + isSearched: false, + auto_increment: false, + allow_null: false, + character_maximum_length: 1, + }; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [ - MatSnackBarModule, - MatDialogModule, - BooleanFilterComponent, - BrowserAnimationsModule - ], - providers: [provideHttpClient()] - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [MatSnackBarModule, MatDialogModule, BooleanFilterComponent, BrowserAnimationsModule], + providers: [provideHttpClient()], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(BooleanFilterComponent); - component = fixture.componentInstance; - }); + beforeEach(() => { + fixture = TestBed.createComponent(BooleanFilterComponent); + component = fixture.componentInstance; + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); - it('should set booleanValue in false when input value is 0', () => { - component.value = 0; - component.structure = fakeStructure; - component.ngOnInit(); + it('should set booleanValue in false when input value is 0', () => { + component.value = 0; + component.structure = fakeStructure; + component.ngOnInit(); - expect(component.booleanValue).toEqual(false); - }); + expect(component.booleanValue).toEqual(false); + }); - it('should set booleanValue in unknown when input value is null', () => { - component.value = null; - component.structure = fakeStructure; - component.ngOnInit(); + it('should set booleanValue in unknown when input value is null', () => { + component.value = null; + component.structure = fakeStructure; + component.ngOnInit(); - expect(component.booleanValue).toEqual('unknown'); - }); + expect(component.booleanValue).toEqual('unknown'); + }); - it('should set isRadiogroup in false if allow_null is false', () => { - component.value = undefined; - component.structure = fakeStructure; - component.ngOnInit(); + it('should set isRadiogroup in false if allow_null is false', () => { + component.value = undefined; + component.structure = fakeStructure; + component.ngOnInit(); - expect(component.isRadiogroup).toEqual(false); - }) + expect(component.isRadiogroup).toEqual(false); + }); - it('should set isRadiogroup in true if allow_null is true', () => { - component.value = undefined; - component.structure = { - "column_name": "banned", - "column_default": "0", - "data_type": "tinyint", - "isExcluded": false, - "isSearched": false, - "auto_increment": false, - "allow_null": true, - "character_maximum_length": 1 - }; - component.ngOnInit(); + it('should set isRadiogroup in true if allow_null is true', () => { + component.value = undefined; + component.structure = { + column_name: 'banned', + column_default: '0', + data_type: 'tinyint', + isExcluded: false, + isSearched: false, + auto_increment: false, + allow_null: true, + character_maximum_length: 1, + }; + component.ngOnInit(); - expect(component.isRadiogroup).toEqual(true); - }) + expect(component.isRadiogroup).toEqual(true); + }); }); diff --git a/frontend/src/app/components/ui-components/filter-fields/boolean/boolean.component.ts b/frontend/src/app/components/ui-components/filter-fields/boolean/boolean.component.ts index ca4e4e400..60f620646 100644 --- a/frontend/src/app/components/ui-components/filter-fields/boolean/boolean.component.ts +++ b/frontend/src/app/components/ui-components/filter-fields/boolean/boolean.component.ts @@ -1,87 +1,85 @@ -import { Component, Input } from '@angular/core'; - -import { BaseFilterFieldComponent } from '../base-filter-field/base-filter-field.component'; import { CommonModule } from '@angular/common'; -import { ConnectionsService } from 'src/app/services/connections.service'; -import { DBtype } from 'src/app/models/connection'; +import { Component, Input } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { MatButtonToggleModule } from '@angular/material/button-toggle'; +import { DBtype } from 'src/app/models/connection'; +import { ConnectionsService } from 'src/app/services/connections.service'; +import { BaseFilterFieldComponent } from '../base-filter-field/base-filter-field.component'; @Component({ - selector: 'app-filter-boolean', - templateUrl: './boolean.component.html', - styleUrls: ['./boolean.component.css'], - imports: [CommonModule, FormsModule, MatButtonToggleModule] + selector: 'app-filter-boolean', + templateUrl: './boolean.component.html', + styleUrls: ['./boolean.component.css'], + imports: [CommonModule, FormsModule, MatButtonToggleModule], }) export class BooleanFilterComponent extends BaseFilterFieldComponent { - @Input() value; + @Input() value; + + public isRadiogroup: boolean; + private connectionType: DBtype; + public booleanValue: boolean | 'unknown'; + + constructor(private _connections: ConnectionsService) { + super(); + } - public isRadiogroup: boolean; - private connectionType: DBtype; - public booleanValue: boolean | "unknown"; + ngOnInit(): void { + super.ngOnInit(); + this.connectionType = this._connections.currentConnection.type; + this.setBooleanValue(); - constructor( - private _connections: ConnectionsService, - ) { - super(); - } + // Parse widget parameters if available + let parsedParams = null; + if (this.widgetStructure?.widget_params) { + parsedParams = + typeof this.widgetStructure.widget_params === 'string' + ? JSON.parse(this.widgetStructure.widget_params) + : this.widgetStructure.widget_params; + } - ngOnInit(): void { - super.ngOnInit(); - this.connectionType = this._connections.currentConnection.type; - this.setBooleanValue(); - - // Parse widget parameters if available - let parsedParams = null; - if (this.widgetStructure?.widget_params) { - parsedParams = typeof this.widgetStructure.widget_params === 'string' - ? JSON.parse(this.widgetStructure.widget_params) - : this.widgetStructure.widget_params; - } - - // Check allow_null from either structure or widget params - this.isRadiogroup = (this.structure?.allow_null) || !!(parsedParams?.allow_null); - } + // Check allow_null from either structure or widget params + this.isRadiogroup = this.structure?.allow_null || !!parsedParams?.allow_null; + } - setBooleanValue() { - if (typeof this.value === 'boolean') { - this.booleanValue = this.value; - } else if (this.value === null) { - this.booleanValue = "unknown"; - console.log('i entered condition this.value === null'); - } else { - switch (this.value) { - case 0: - case '0': - case 'F': - case 'N': - case 'false': - this.booleanValue = false; - break; - case 1: - case '1': - case 'T': - case 'Y': - case 'true': - this.booleanValue = true; - break; - } - } - } + setBooleanValue() { + if (typeof this.value === 'boolean') { + this.booleanValue = this.value; + } else if (this.value === null) { + this.booleanValue = 'unknown'; + console.log('i entered condition this.value === null'); + } else { + switch (this.value) { + case 0: + case '0': + case 'F': + case 'N': + case 'false': + this.booleanValue = false; + break; + case 1: + case '1': + case 'T': + case 'Y': + case 'true': + this.booleanValue = true; + break; + } + } + } - onBooleanChange() { - console.log(this.connectionType); - let formattedValue; - switch (this.connectionType) { - case DBtype.MySQL: - case DBtype.MSSQL: - case DBtype.Oracle: - formattedValue = this.booleanValue === "unknown" ? null : Number(this.booleanValue); - break; - default: - formattedValue = this.booleanValue === "unknown" ? null : this.booleanValue; - } + onBooleanChange() { + console.log(this.connectionType); + let formattedValue; + switch (this.connectionType) { + case DBtype.MySQL: + case DBtype.MSSQL: + case DBtype.Oracle: + formattedValue = this.booleanValue === 'unknown' ? null : Number(this.booleanValue); + break; + default: + formattedValue = this.booleanValue === 'unknown' ? null : this.booleanValue; + } - this.onFieldChange.emit(formattedValue); - } + this.onFieldChange.emit(formattedValue); + } } diff --git a/frontend/src/app/components/ui-components/filter-fields/country/country.component.css b/frontend/src/app/components/ui-components/filter-fields/country/country.component.css index 8301b28e1..163ee20ea 100644 --- a/frontend/src/app/components/ui-components/filter-fields/country/country.component.css +++ b/frontend/src/app/components/ui-components/filter-fields/country/country.component.css @@ -1,23 +1,23 @@ .country-form-field { - width: 100%; + width: 100%; } .country-flag { - margin-right: 8px; - font-size: 16px; + margin-right: 8px; + font-size: 16px; } .country-name { - flex: 1; + flex: 1; } .country-code { - margin-left: 8px; - opacity: 0.7; - font-size: 12px; + margin-left: 8px; + opacity: 0.7; + font-size: 12px; } mat-option { - display: flex; - align-items: center; -} \ No newline at end of file + display: flex; + align-items: center; +} diff --git a/frontend/src/app/components/ui-components/filter-fields/country/country.component.ts b/frontend/src/app/components/ui-components/filter-fields/country/country.component.ts index 1774f9585..c859c75a8 100644 --- a/frontend/src/app/components/ui-components/filter-fields/country/country.component.ts +++ b/frontend/src/app/components/ui-components/filter-fields/country/country.component.ts @@ -1,84 +1,83 @@ -import { CUSTOM_ELEMENTS_SCHEMA, Component, Input } from '@angular/core'; - -import { BaseFilterFieldComponent } from '../base-filter-field/base-filter-field.component'; import { CommonModule } from '@angular/common'; -import { FormsModule } from '@angular/forms'; +import { Component, CUSTOM_ELEMENTS_SCHEMA, Input } from '@angular/core'; +import { FormControl, FormsModule, ReactiveFormsModule } from '@angular/forms'; import { MatAutocompleteModule } from '@angular/material/autocomplete'; -import { MatInputModule } from '@angular/material/input'; import { MatFormFieldModule } from '@angular/material/form-field'; -import { COUNTRIES, getCountryFlag } from '../../../../consts/countries'; -import { map, startWith } from 'rxjs/operators'; +import { MatInputModule } from '@angular/material/input'; import { Observable } from 'rxjs'; -import { FormControl } from '@angular/forms'; -import { ReactiveFormsModule } from '@angular/forms'; +import { map, startWith } from 'rxjs/operators'; +import { COUNTRIES, getCountryFlag } from '../../../../consts/countries'; +import { BaseFilterFieldComponent } from '../base-filter-field/base-filter-field.component'; @Component({ - selector: 'app-filter-country', - templateUrl: './country.component.html', - styleUrls: ['./country.component.css'], - imports: [CommonModule, FormsModule, ReactiveFormsModule, MatFormFieldModule, MatAutocompleteModule, MatInputModule], - schemas: [CUSTOM_ELEMENTS_SCHEMA] + selector: 'app-filter-country', + templateUrl: './country.component.html', + styleUrls: ['./country.component.css'], + imports: [CommonModule, FormsModule, ReactiveFormsModule, MatFormFieldModule, MatAutocompleteModule, MatInputModule], + schemas: [CUSTOM_ELEMENTS_SCHEMA], }) export class CountryFilterComponent extends BaseFilterFieldComponent { - @Input() value: string; + @Input() value: string; - public countries: {value: string | null, label: string, flag: string}[] = []; - public countryControl = new FormControl<{value: string | null, label: string, flag: string} | string>(''); - public filteredCountries: Observable<{value: string | null, label: string, flag: string}[]>; + public countries: { value: string | null; label: string; flag: string }[] = []; + public countryControl = new FormControl<{ value: string | null; label: string; flag: string } | string>(''); + public filteredCountries: Observable<{ value: string | null; label: string; flag: string }[]>; - originalOrder = () => { return 0; } + originalOrder = () => { + return 0; + }; - getCountryFlag = getCountryFlag; + getCountryFlag = getCountryFlag; - ngOnInit(): void { - super.ngOnInit(); - this.loadCountries(); - this.setupAutocomplete(); - this.setInitialValue(); - } + ngOnInit(): void { + super.ngOnInit(); + this.loadCountries(); + this.setupAutocomplete(); + this.setInitialValue(); + } - private setupAutocomplete(): void { - this.filteredCountries = this.countryControl.valueChanges.pipe( - startWith(''), - map(value => this._filter(typeof value === 'string' ? value : (value?.label || ''))) - ); - } + private setupAutocomplete(): void { + this.filteredCountries = this.countryControl.valueChanges.pipe( + startWith(''), + map((value) => this._filter(typeof value === 'string' ? value : value?.label || '')), + ); + } - private setInitialValue(): void { - if (this.value) { - const country = this.countries.find(c => c.value === this.value); - if (country) { - this.countryControl.setValue(country); - } - } - } + private setInitialValue(): void { + if (this.value) { + const country = this.countries.find((c) => c.value === this.value); + if (country) { + this.countryControl.setValue(country); + } + } + } - private _filter(value: string): {value: string | null, label: string, flag: string}[] { - const filterValue = value.toLowerCase(); - return this.countries.filter(country => - country.label?.toLowerCase().includes(filterValue) || - (country.value?.toLowerCase().includes(filterValue)) - ); - } + private _filter(value: string): { value: string | null; label: string; flag: string }[] { + const filterValue = value.toLowerCase(); + return this.countries.filter( + (country) => + country.label?.toLowerCase().includes(filterValue) || country.value?.toLowerCase().includes(filterValue), + ); + } - onCountrySelected(selectedCountry: {value: string | null, label: string, flag: string}): void { - this.value = selectedCountry.value; - this.onFieldChange.emit(this.value); - } + onCountrySelected(selectedCountry: { value: string | null; label: string; flag: string }): void { + this.value = selectedCountry.value; + this.onFieldChange.emit(this.value); + } - displayFn(country: any): string { - return country ? country.label : ''; - } + displayFn(country: any): string { + return country ? country.label : ''; + } - private loadCountries(): void { - this.countries = COUNTRIES.map(country => ({ - value: country.code, - label: country.name, - flag: getCountryFlag(country.code) - })); + private loadCountries(): void { + this.countries = COUNTRIES.map((country) => ({ + value: country.code, + label: country.name, + flag: getCountryFlag(country.code), + })); - if (this.widgetStructure?.widget_params?.allow_null || this.structure?.allow_null) { - this.countries = [{ value: null, label: '', flag: '' }, ...this.countries]; - } - } -} \ No newline at end of file + if (this.widgetStructure?.widget_params?.allow_null || this.structure?.allow_null) { + this.countries = [{ value: null, label: '', flag: '' }, ...this.countries]; + } + } +} diff --git a/frontend/src/app/components/ui-components/filter-fields/date-time/date-time.component.css b/frontend/src/app/components/ui-components/filter-fields/date-time/date-time.component.css index 02ce234f5..f63bf4c46 100644 --- a/frontend/src/app/components/ui-components/filter-fields/date-time/date-time.component.css +++ b/frontend/src/app/components/ui-components/filter-fields/date-time/date-time.component.css @@ -1,5 +1,5 @@ .field-couple { - display: grid; - grid-template-columns: 1fr 1fr; - grid-column-gap: 16px; -} \ No newline at end of file + display: grid; + grid-template-columns: 1fr 1fr; + grid-column-gap: 16px; +} diff --git a/frontend/src/app/components/ui-components/filter-fields/date-time/date-time.component.ts b/frontend/src/app/components/ui-components/filter-fields/date-time/date-time.component.ts index a1919bab3..4cd0c7173 100644 --- a/frontend/src/app/components/ui-components/filter-fields/date-time/date-time.component.ts +++ b/frontend/src/app/components/ui-components/filter-fields/date-time/date-time.component.ts @@ -1,53 +1,52 @@ -import { AfterViewInit, Component, ElementRef, EventEmitter, Input, Output, ViewChild } from '@angular/core'; - -import { BaseFilterFieldComponent } from '../base-filter-field/base-filter-field.component'; import { CommonModule } from '@angular/common'; +import { AfterViewInit, Component, ElementRef, EventEmitter, Input, Output, ViewChild } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; -import { format } from 'date-fns' +import { format } from 'date-fns'; +import { BaseFilterFieldComponent } from '../base-filter-field/base-filter-field.component'; @Component({ - selector: 'app-filter-date-time', - templateUrl: './date-time.component.html', - styleUrls: ['./date-time.component.css'], - imports: [CommonModule, FormsModule, MatFormFieldModule, MatInputModule] + selector: 'app-filter-date-time', + templateUrl: './date-time.component.html', + styleUrls: ['./date-time.component.css'], + imports: [CommonModule, FormsModule, MatFormFieldModule, MatInputModule], }) export class DateTimeFilterComponent extends BaseFilterFieldComponent implements AfterViewInit { - @Input() value: string; - @ViewChild('inputElement') inputElement: ElementRef; - - @Output() onFieldChange = new EventEmitter(); - - static type = 'datetime'; - public date: string; - public time: string; - - ngOnInit(): void { - super.ngOnInit(); - if (this.value) { - const datetime = new Date(this.value); - this.date = format(datetime, 'yyyy-MM-dd'); - this.time = format(datetime, 'HH:mm:ss'); - } - } - - onDateChange() { - if (!this.time) this.time = '00:00'; - const datetime = `${this.date}T${this.time}Z`; - this.onFieldChange.emit(datetime); - } - - onTimeChange() { - const datetime = `${this.date}T${this.time}Z`; - this.onFieldChange.emit(datetime); - } - - ngAfterViewInit(): void { - if (this.autofocus && this.inputElement) { - setTimeout(() => { - this.inputElement.nativeElement.focus(); - }, 100); - } - } + @Input() value: string; + @ViewChild('inputElement') inputElement: ElementRef; + + @Output() onFieldChange = new EventEmitter(); + + static type = 'datetime'; + public date: string; + public time: string; + + ngOnInit(): void { + super.ngOnInit(); + if (this.value) { + const datetime = new Date(this.value); + this.date = format(datetime, 'yyyy-MM-dd'); + this.time = format(datetime, 'HH:mm:ss'); + } + } + + onDateChange() { + if (!this.time) this.time = '00:00'; + const datetime = `${this.date}T${this.time}Z`; + this.onFieldChange.emit(datetime); + } + + onTimeChange() { + const datetime = `${this.date}T${this.time}Z`; + this.onFieldChange.emit(datetime); + } + + ngAfterViewInit(): void { + if (this.autofocus && this.inputElement) { + setTimeout(() => { + this.inputElement.nativeElement.focus(); + }, 100); + } + } } diff --git a/frontend/src/app/components/ui-components/filter-fields/date/date.component.css b/frontend/src/app/components/ui-components/filter-fields/date/date.component.css index b8f45cfd5..03fe2b8ac 100644 --- a/frontend/src/app/components/ui-components/filter-fields/date/date.component.css +++ b/frontend/src/app/components/ui-components/filter-fields/date/date.component.css @@ -1,3 +1,3 @@ .date-field { - width: 100%; -} \ No newline at end of file + width: 100%; +} diff --git a/frontend/src/app/components/ui-components/filter-fields/date/date.component.ts b/frontend/src/app/components/ui-components/filter-fields/date/date.component.ts index c583e71f5..9e5ad5b25 100644 --- a/frontend/src/app/components/ui-components/filter-fields/date/date.component.ts +++ b/frontend/src/app/components/ui-components/filter-fields/date/date.component.ts @@ -1,42 +1,41 @@ -import { AfterViewInit, Component, ElementRef, Input, ViewChild } from '@angular/core'; - -import { BaseFilterFieldComponent } from '../base-filter-field/base-filter-field.component'; import { CommonModule } from '@angular/common'; +import { AfterViewInit, Component, ElementRef, Input, ViewChild } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; -import { format } from 'date-fns' +import { format } from 'date-fns'; +import { BaseFilterFieldComponent } from '../base-filter-field/base-filter-field.component'; @Component({ - selector: 'app-filter-date', - templateUrl: './date.component.html', - styleUrls: ['./date.component.css'], - imports: [CommonModule, FormsModule, MatFormFieldModule, MatInputModule] + selector: 'app-filter-date', + templateUrl: './date.component.html', + styleUrls: ['./date.component.css'], + imports: [CommonModule, FormsModule, MatFormFieldModule, MatInputModule], }) export class DateFilterComponent extends BaseFilterFieldComponent implements AfterViewInit { - @Input() value: string; - @ViewChild('inputElement') inputElement: ElementRef; + @Input() value: string; + @ViewChild('inputElement') inputElement: ElementRef; - static type = 'datetime'; - public date: string; + static type = 'datetime'; + public date: string; - ngOnInit(): void { - super.ngOnInit(); - if (this.value) { - const datetime = new Date(this.value); - this.date = format(datetime, 'yyyy-MM-dd'); - } - } + ngOnInit(): void { + super.ngOnInit(); + if (this.value) { + const datetime = new Date(this.value); + this.date = format(datetime, 'yyyy-MM-dd'); + } + } - ngAfterViewInit(): void { - if (this.autofocus && this.inputElement) { - setTimeout(() => { - this.inputElement.nativeElement.focus(); - }, 100); - } - } + ngAfterViewInit(): void { + if (this.autofocus && this.inputElement) { + setTimeout(() => { + this.inputElement.nativeElement.focus(); + }, 100); + } + } - onDateChange() { - this.onFieldChange.emit(this.date); - } + onDateChange() { + this.onFieldChange.emit(this.date); + } } diff --git a/frontend/src/app/components/ui-components/filter-fields/file/file.component.css b/frontend/src/app/components/ui-components/filter-fields/file/file.component.css index 9934db541..d3e76481b 100644 --- a/frontend/src/app/components/ui-components/filter-fields/file/file.component.css +++ b/frontend/src/app/components/ui-components/filter-fields/file/file.component.css @@ -1,39 +1,39 @@ .file { - margin-bottom: 24px; + margin-bottom: 24px; } .header { - display: flex; - align-items: center; - margin-bottom: 20px; + display: flex; + align-items: center; + margin-bottom: 20px; } .file-type { - margin-right: 20px; + margin-right: 20px; } .file-raw-data { - width: 100%; + width: 100%; } .file-input { - display: none; + display: none; } .file-uploader-appearance { - margin-bottom: 20px; + margin-bottom: 20px; } .file-label { - margin-right: 20px; + margin-right: 20px; } .file-name { - color: rgba(0, 0, 0, 0.5); - font-style: italic; - margin-right: 20px; + color: rgba(0, 0, 0, 0.5); + font-style: italic; + margin-right: 20px; } .upload-btn { - margin-right: 12px; -} \ No newline at end of file + margin-right: 12px; +} diff --git a/frontend/src/app/components/ui-components/filter-fields/file/file.component.spec.ts b/frontend/src/app/components/ui-components/filter-fields/file/file.component.spec.ts index 293287599..e6d56f3e2 100644 --- a/frontend/src/app/components/ui-components/filter-fields/file/file.component.spec.ts +++ b/frontend/src/app/components/ui-components/filter-fields/file/file.component.spec.ts @@ -1,32 +1,26 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { FileFilterComponent } from './file.component'; import { FormsModule } from '@angular/forms'; import { MatRadioModule } from '@angular/material/radio'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { FileFilterComponent } from './file.component'; describe('FileFilterComponent', () => { - let component: FileFilterComponent; - let fixture: ComponentFixture; + let component: FileFilterComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [ - FormsModule, - MatRadioModule, - FileFilterComponent, - BrowserAnimationsModule - ] - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [FormsModule, MatRadioModule, FileFilterComponent, BrowserAnimationsModule], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(FileFilterComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(FileFilterComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/ui-components/filter-fields/file/file.component.ts b/frontend/src/app/components/ui-components/filter-fields/file/file.component.ts index e38a34c34..ae16ebc74 100644 --- a/frontend/src/app/components/ui-components/filter-fields/file/file.component.ts +++ b/frontend/src/app/components/ui-components/filter-fields/file/file.component.ts @@ -1,13 +1,13 @@ -import { BaseFilterFieldComponent } from '../base-filter-field/base-filter-field.component'; import { CommonModule } from '@angular/common'; import { Component } from '@angular/core'; +import { BaseFilterFieldComponent } from '../base-filter-field/base-filter-field.component'; @Component({ - selector: 'app-filter-file', - templateUrl: './file.component.html', - styleUrls: ['./file.component.css'], - imports: [CommonModule] + selector: 'app-filter-file', + templateUrl: './file.component.html', + styleUrls: ['./file.component.css'], + imports: [CommonModule], }) export class FileFilterComponent extends BaseFilterFieldComponent { - static type = 'file'; + static type = 'file'; } diff --git a/frontend/src/app/components/ui-components/filter-fields/foreign-key/foreign-key.component.css b/frontend/src/app/components/ui-components/filter-fields/foreign-key/foreign-key.component.css index a692446f5..189e6166a 100644 --- a/frontend/src/app/components/ui-components/filter-fields/foreign-key/foreign-key.component.css +++ b/frontend/src/app/components/ui-components/filter-fields/foreign-key/foreign-key.component.css @@ -1,35 +1,35 @@ .full-width { - position: relative; - width: 100%; + position: relative; + width: 100%; } .loader { - position: absolute; - top: 12px; - right: 12px; + position: absolute; + top: 12px; + right: 12px; } .disabled { - pointer-events: none; + pointer-events: none; } .foreign-key { - display: grid; - grid-template-columns: 1fr 36px; + display: grid; + grid-template-columns: 1fr 36px; } .foreign-key ::ng-deep .mat-mdc-form-field-subscript-wrapper { - margin-bottom: 16px; + margin-bottom: 16px; } .foreign-key__link { - display: flex; - /* align-items: center; */ - justify-content: flex-end; - color: inherit; + display: flex; + /* align-items: center; */ + justify-content: flex-end; + color: inherit; } .foreign-key__link-icon { - margin-left: auto; - margin-top: 12px; -} \ No newline at end of file + margin-left: auto; + margin-top: 12px; +} diff --git a/frontend/src/app/components/ui-components/filter-fields/id/id.component.css b/frontend/src/app/components/ui-components/filter-fields/id/id.component.css index ca0ef768e..bffe35774 100644 --- a/frontend/src/app/components/ui-components/filter-fields/id/id.component.css +++ b/frontend/src/app/components/ui-components/filter-fields/id/id.component.css @@ -1,3 +1,3 @@ .id-form-field { - width: 100%; -} \ No newline at end of file + width: 100%; +} diff --git a/frontend/src/app/components/ui-components/filter-fields/id/id.component.spec.ts b/frontend/src/app/components/ui-components/filter-fields/id/id.component.spec.ts index dadef97b1..ff1383adb 100644 --- a/frontend/src/app/components/ui-components/filter-fields/id/id.component.spec.ts +++ b/frontend/src/app/components/ui-components/filter-fields/id/id.component.spec.ts @@ -1,26 +1,26 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { FormsModule } from '@angular/forms'; -import { IdFilterComponent } from './id.component'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { IdFilterComponent } from './id.component'; describe('IdFilterComponent', () => { - let component: IdFilterComponent; - let fixture: ComponentFixture; + let component: IdFilterComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [FormsModule, IdFilterComponent, BrowserAnimationsModule] - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [FormsModule, IdFilterComponent, BrowserAnimationsModule], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(IdFilterComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(IdFilterComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/ui-components/filter-fields/id/id.component.ts b/frontend/src/app/components/ui-components/filter-fields/id/id.component.ts index f2bb8b136..b605ca9a5 100644 --- a/frontend/src/app/components/ui-components/filter-fields/id/id.component.ts +++ b/frontend/src/app/components/ui-components/filter-fields/id/id.component.ts @@ -1,17 +1,16 @@ -import { Component, Input } from '@angular/core'; - -import { BaseFilterFieldComponent } from '../base-filter-field/base-filter-field.component'; import { CommonModule } from '@angular/common'; +import { Component, Input } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; +import { BaseFilterFieldComponent } from '../base-filter-field/base-filter-field.component'; @Component({ - selector: 'app-filter-id', - templateUrl: './id.component.html', - styleUrls: ['./id.component.css'], - imports: [CommonModule, FormsModule, MatFormFieldModule, MatInputModule] + selector: 'app-filter-id', + templateUrl: './id.component.html', + styleUrls: ['./id.component.css'], + imports: [CommonModule, FormsModule, MatFormFieldModule, MatInputModule], }) export class IdFilterComponent extends BaseFilterFieldComponent { - @Input() value: string; + @Input() value: string; } diff --git a/frontend/src/app/components/ui-components/filter-fields/json-editor/json-editor.component.css b/frontend/src/app/components/ui-components/filter-fields/json-editor/json-editor.component.css index 4f2b933f3..abd26e70e 100644 --- a/frontend/src/app/components/ui-components/filter-fields/json-editor/json-editor.component.css +++ b/frontend/src/app/components/ui-components/filter-fields/json-editor/json-editor.component.css @@ -1,8 +1,8 @@ .codemirror-wrapper { - display: block; - border: 1px solid rgba(0, 0, 0, 0.38); - border-radius: 0; - margin-top: 4px; - margin-bottom: 20px; - overflow: hidden; -} \ No newline at end of file + display: block; + border: 1px solid rgba(0, 0, 0, 0.38); + border-radius: 0; + margin-top: 4px; + margin-bottom: 20px; + overflow: hidden; +} diff --git a/frontend/src/app/components/ui-components/filter-fields/json-editor/json-editor.component.ts b/frontend/src/app/components/ui-components/filter-fields/json-editor/json-editor.component.ts index 15a003dbb..f49543053 100644 --- a/frontend/src/app/components/ui-components/filter-fields/json-editor/json-editor.component.ts +++ b/frontend/src/app/components/ui-components/filter-fields/json-editor/json-editor.component.ts @@ -1,33 +1,32 @@ -import { Component, Input } from '@angular/core'; - -import { BaseFilterFieldComponent } from '../base-filter-field/base-filter-field.component'; -import { CodeEditorModule } from '@ngstack/code-editor'; import { CommonModule } from '@angular/common'; +import { Component, Input } from '@angular/core'; import { FormsModule } from '@angular/forms'; +import { CodeEditorModule } from '@ngstack/code-editor'; +import { BaseFilterFieldComponent } from '../base-filter-field/base-filter-field.component'; @Component({ - selector: 'app-filter-json-editor', - templateUrl: './json-editor.component.html', - styleUrls: ['./json-editor.component.css'], - imports: [CommonModule, FormsModule, CodeEditorModule] + selector: 'app-filter-json-editor', + templateUrl: './json-editor.component.html', + styleUrls: ['./json-editor.component.css'], + imports: [CommonModule, FormsModule, CodeEditorModule], }) export class JsonEditorFilterComponent extends BaseFilterFieldComponent { - @Input() value: Object; + @Input() value: Object; - public mutableCodeModel: Object; - public codeEditorOptions = { - minimap: { enabled: false }, - automaticLayout: true, - scrollBeyondLastLine: false, - wordWrap: 'on', - }; + public mutableCodeModel: Object; + public codeEditorOptions = { + minimap: { enabled: false }, + automaticLayout: true, + scrollBeyondLastLine: false, + wordWrap: 'on', + }; - ngOnInit(): void { - super.ngOnInit(); - this.mutableCodeModel = { - language: 'json', - uri: `${this.label}.json`, - value: JSON.stringify(this.value, undefined, 4) || '{}' - } - } + ngOnInit(): void { + super.ngOnInit(); + this.mutableCodeModel = { + language: 'json', + uri: `${this.label}.json`, + value: JSON.stringify(this.value, undefined, 4) || '{}', + }; + } } diff --git a/frontend/src/app/components/ui-components/filter-fields/long-text/long-text.component.css b/frontend/src/app/components/ui-components/filter-fields/long-text/long-text.component.css index 05120b85c..ea6057220 100644 --- a/frontend/src/app/components/ui-components/filter-fields/long-text/long-text.component.css +++ b/frontend/src/app/components/ui-components/filter-fields/long-text/long-text.component.css @@ -1,7 +1,7 @@ .form-field { - width: 100%; + width: 100%; } .form-textarea { - resize: none; -} \ No newline at end of file + resize: none; +} diff --git a/frontend/src/app/components/ui-components/filter-fields/long-text/long-text.component.spec.ts b/frontend/src/app/components/ui-components/filter-fields/long-text/long-text.component.spec.ts index 9afd41345..840f4901a 100644 --- a/frontend/src/app/components/ui-components/filter-fields/long-text/long-text.component.spec.ts +++ b/frontend/src/app/components/ui-components/filter-fields/long-text/long-text.component.spec.ts @@ -1,25 +1,24 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { LongTextFilterComponent } from './long-text.component'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { LongTextFilterComponent } from './long-text.component'; describe('LongTextFilterComponent', () => { - let component: LongTextFilterComponent; - let fixture: ComponentFixture; + let component: LongTextFilterComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [LongTextFilterComponent, BrowserAnimationsModule] - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [LongTextFilterComponent, BrowserAnimationsModule], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(LongTextFilterComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(LongTextFilterComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/ui-components/filter-fields/long-text/long-text.component.ts b/frontend/src/app/components/ui-components/filter-fields/long-text/long-text.component.ts index 26cd991ce..e54786c33 100644 --- a/frontend/src/app/components/ui-components/filter-fields/long-text/long-text.component.ts +++ b/frontend/src/app/components/ui-components/filter-fields/long-text/long-text.component.ts @@ -1,39 +1,37 @@ -import { AfterViewInit, Component, ElementRef, Input, ViewChild } from '@angular/core'; - -import { BaseFilterFieldComponent } from '../base-filter-field/base-filter-field.component'; import { CommonModule } from '@angular/common'; +import { AfterViewInit, Component, ElementRef, Input, ViewChild } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; +import { BaseFilterFieldComponent } from '../base-filter-field/base-filter-field.component'; @Component({ - selector: 'app-filter-long-text', - templateUrl: './long-text.component.html', - styleUrls: ['./long-text.component.css'], - imports: [CommonModule, FormsModule, MatFormFieldModule, MatInputModule] + selector: 'app-filter-long-text', + templateUrl: './long-text.component.html', + styleUrls: ['./long-text.component.css'], + imports: [CommonModule, FormsModule, MatFormFieldModule, MatInputModule], }) export class LongTextFilterComponent extends BaseFilterFieldComponent implements AfterViewInit { - @Input() value: string; - @ViewChild('inputElement') inputElement: ElementRef; - - static type = 'text'; - public rowsCount: string; + @Input() value: string; + @ViewChild('inputElement') inputElement: ElementRef; - ngOnInit(): void { - super.ngOnInit(); - if (this.widgetStructure?.widget_params) { - this.rowsCount = this.widgetStructure.widget_params.rows - } else { - this.rowsCount = '4' - }; - } + static type = 'text'; + public rowsCount: string; - ngAfterViewInit(): void { - if (this.autofocus && this.inputElement) { - setTimeout(() => { - this.inputElement.nativeElement.focus(); - }, 100); - } - } + ngOnInit(): void { + super.ngOnInit(); + if (this.widgetStructure?.widget_params) { + this.rowsCount = this.widgetStructure.widget_params.rows; + } else { + this.rowsCount = '4'; + } + } + ngAfterViewInit(): void { + if (this.autofocus && this.inputElement) { + setTimeout(() => { + this.inputElement.nativeElement.focus(); + }, 100); + } + } } diff --git a/frontend/src/app/components/ui-components/filter-fields/number/number.component.css b/frontend/src/app/components/ui-components/filter-fields/number/number.component.css index 066c864e6..957c7d47c 100644 --- a/frontend/src/app/components/ui-components/filter-fields/number/number.component.css +++ b/frontend/src/app/components/ui-components/filter-fields/number/number.component.css @@ -1,3 +1,3 @@ .number-form-field { - width: 100%; -} \ No newline at end of file + width: 100%; +} diff --git a/frontend/src/app/components/ui-components/filter-fields/number/number.component.spec.ts b/frontend/src/app/components/ui-components/filter-fields/number/number.component.spec.ts index b5fd7b273..aa837dc76 100644 --- a/frontend/src/app/components/ui-components/filter-fields/number/number.component.spec.ts +++ b/frontend/src/app/components/ui-components/filter-fields/number/number.component.spec.ts @@ -1,25 +1,24 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { NumberFilterComponent } from './number.component'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { NumberFilterComponent } from './number.component'; describe('NumberFilterComponent', () => { - let component: NumberFilterComponent; - let fixture: ComponentFixture; + let component: NumberFilterComponent; + let fixture: ComponentFixture; - beforeEach((() => { - TestBed.configureTestingModule({ - imports: [NumberFilterComponent, BrowserAnimationsModule] - }).compileComponents(); - })); + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [NumberFilterComponent, BrowserAnimationsModule], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(NumberFilterComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(NumberFilterComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/ui-components/filter-fields/number/number.component.ts b/frontend/src/app/components/ui-components/filter-fields/number/number.component.ts index 9505fadc7..eed783886 100644 --- a/frontend/src/app/components/ui-components/filter-fields/number/number.component.ts +++ b/frontend/src/app/components/ui-components/filter-fields/number/number.component.ts @@ -1,28 +1,27 @@ -import { AfterViewInit, Component, ElementRef, Input, ViewChild } from '@angular/core'; - -import { BaseFilterFieldComponent } from '../base-filter-field/base-filter-field.component'; import { CommonModule } from '@angular/common'; +import { AfterViewInit, Component, ElementRef, Input, ViewChild } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; +import { BaseFilterFieldComponent } from '../base-filter-field/base-filter-field.component'; @Component({ - selector: 'app-filter-number', - templateUrl: './number.component.html', - styleUrls: ['./number.component.css'], - imports: [CommonModule, FormsModule, MatFormFieldModule, MatInputModule] + selector: 'app-filter-number', + templateUrl: './number.component.html', + styleUrls: ['./number.component.css'], + imports: [CommonModule, FormsModule, MatFormFieldModule, MatInputModule], }) export class NumberFilterComponent extends BaseFilterFieldComponent implements AfterViewInit { - @Input() value: number; - @ViewChild('inputElement') inputElement: ElementRef; + @Input() value: number; + @ViewChild('inputElement') inputElement: ElementRef; - static type = 'number'; + static type = 'number'; - ngAfterViewInit(): void { - if (this.autofocus && this.inputElement) { - setTimeout(() => { - this.inputElement.nativeElement.focus(); - }, 100); - } - } + ngAfterViewInit(): void { + if (this.autofocus && this.inputElement) { + setTimeout(() => { + this.inputElement.nativeElement.focus(); + }, 100); + } + } } diff --git a/frontend/src/app/components/ui-components/filter-fields/password/password.component.css b/frontend/src/app/components/ui-components/filter-fields/password/password.component.css index e30a2181a..1796f8a97 100644 --- a/frontend/src/app/components/ui-components/filter-fields/password/password.component.css +++ b/frontend/src/app/components/ui-components/filter-fields/password/password.component.css @@ -1,7 +1,7 @@ .password-form-field { - width: 100%; + width: 100%; } .password-checkbox { - margin-bottom: 20px; -} \ No newline at end of file + margin-bottom: 20px; +} diff --git a/frontend/src/app/components/ui-components/filter-fields/password/password.component.ts b/frontend/src/app/components/ui-components/filter-fields/password/password.component.ts index cffe2ed8c..9aa0ad216 100644 --- a/frontend/src/app/components/ui-components/filter-fields/password/password.component.ts +++ b/frontend/src/app/components/ui-components/filter-fields/password/password.component.ts @@ -1,39 +1,38 @@ -import { AfterViewInit, Component, ElementRef, Input, ViewChild } from '@angular/core'; - -import { BaseFilterFieldComponent } from '../base-filter-field/base-filter-field.component'; import { CommonModule } from '@angular/common'; +import { AfterViewInit, Component, ElementRef, Input, ViewChild } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { MatCheckboxModule } from '@angular/material/checkbox'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; +import { BaseFilterFieldComponent } from '../base-filter-field/base-filter-field.component'; @Component({ - selector: 'app-filter-password', - templateUrl: './password.component.html', - styleUrls: ['./password.component.css'], - imports: [CommonModule, FormsModule, MatCheckboxModule, MatFormFieldModule, MatInputModule] + selector: 'app-filter-password', + templateUrl: './password.component.html', + styleUrls: ['./password.component.css'], + imports: [CommonModule, FormsModule, MatCheckboxModule, MatFormFieldModule, MatInputModule], }) export class PasswordFilterComponent extends BaseFilterFieldComponent implements AfterViewInit { - @Input() value: string; - @ViewChild('inputElement') inputElement: ElementRef; + @Input() value: string; + @ViewChild('inputElement') inputElement: ElementRef; - public clearPassword: boolean; + public clearPassword: boolean; - ngOnInit(): void { - super.ngOnInit(); - if (this.value === '***') this.value = ''; - this.onFieldChange.emit(this.value); - } + ngOnInit(): void { + super.ngOnInit(); + if (this.value === '***') this.value = ''; + this.onFieldChange.emit(this.value); + } - ngAfterViewInit(): void { - if (this.autofocus && this.inputElement) { - setTimeout(() => { - this.inputElement.nativeElement.focus(); - }, 100); - } - } + ngAfterViewInit(): void { + if (this.autofocus && this.inputElement) { + setTimeout(() => { + this.inputElement.nativeElement.focus(); + }, 100); + } + } - onClearPasswordChange() { - if (this.clearPassword) this.onFieldChange.emit(null); - } + onClearPasswordChange() { + if (this.clearPassword) this.onFieldChange.emit(null); + } } diff --git a/frontend/src/app/components/ui-components/filter-fields/point/point.component.css b/frontend/src/app/components/ui-components/filter-fields/point/point.component.css index d6858e0b1..7ab52d4e1 100644 --- a/frontend/src/app/components/ui-components/filter-fields/point/point.component.css +++ b/frontend/src/app/components/ui-components/filter-fields/point/point.component.css @@ -1,6 +1,6 @@ .mat-mdc-form-field { - max-width: 260px; - min-width: 150px; - width: 48vw; - display: block; -} \ No newline at end of file + max-width: 260px; + min-width: 150px; + width: 48vw; + display: block; +} diff --git a/frontend/src/app/components/ui-components/filter-fields/point/point.component.spec.ts b/frontend/src/app/components/ui-components/filter-fields/point/point.component.spec.ts index b97364c70..431927de9 100644 --- a/frontend/src/app/components/ui-components/filter-fields/point/point.component.spec.ts +++ b/frontend/src/app/components/ui-components/filter-fields/point/point.component.spec.ts @@ -1,25 +1,24 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { PointFilterComponent } from './point.component'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { PointFilterComponent } from './point.component'; describe('PointFilterComponent', () => { - let component: PointFilterComponent; - let fixture: ComponentFixture; + let component: PointFilterComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [PointFilterComponent, BrowserAnimationsModule] - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [PointFilterComponent, BrowserAnimationsModule], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(PointFilterComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(PointFilterComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/ui-components/filter-fields/point/point.component.ts b/frontend/src/app/components/ui-components/filter-fields/point/point.component.ts index 7db84b215..4d9969d01 100644 --- a/frontend/src/app/components/ui-components/filter-fields/point/point.component.ts +++ b/frontend/src/app/components/ui-components/filter-fields/point/point.component.ts @@ -1,17 +1,16 @@ -import { Component, Input } from '@angular/core'; - -import { BaseFilterFieldComponent } from '../base-filter-field/base-filter-field.component'; import { CommonModule } from '@angular/common'; +import { Component, Input } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; +import { BaseFilterFieldComponent } from '../base-filter-field/base-filter-field.component'; @Component({ - selector: 'app-filter-point', - templateUrl: './point.component.html', - styleUrls: ['./point.component.css'], - imports: [CommonModule, FormsModule, MatFormFieldModule, MatInputModule] + selector: 'app-filter-point', + templateUrl: './point.component.html', + styleUrls: ['./point.component.css'], + imports: [CommonModule, FormsModule, MatFormFieldModule, MatInputModule], }) export class PointFilterComponent extends BaseFilterFieldComponent { - @Input() value; + @Input() value; } diff --git a/frontend/src/app/components/ui-components/filter-fields/select/select.component.css b/frontend/src/app/components/ui-components/filter-fields/select/select.component.css index 7a4696a74..828b119a7 100644 --- a/frontend/src/app/components/ui-components/filter-fields/select/select.component.css +++ b/frontend/src/app/components/ui-components/filter-fields/select/select.component.css @@ -1,3 +1,3 @@ .select-form-field { - width: 100%; -} \ No newline at end of file + width: 100%; +} diff --git a/frontend/src/app/components/ui-components/filter-fields/select/select.component.spec.ts b/frontend/src/app/components/ui-components/filter-fields/select/select.component.spec.ts index 58e3db33b..40cd51102 100644 --- a/frontend/src/app/components/ui-components/filter-fields/select/select.component.spec.ts +++ b/frontend/src/app/components/ui-components/filter-fields/select/select.component.spec.ts @@ -1,25 +1,24 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { SelectFilterComponent } from './select.component'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { SelectFilterComponent } from './select.component'; describe('SelectFilterComponent', () => { - let component: SelectFilterComponent; - let fixture: ComponentFixture; + let component: SelectFilterComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [SelectFilterComponent, BrowserAnimationsModule] - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [SelectFilterComponent, BrowserAnimationsModule], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(SelectFilterComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(SelectFilterComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/ui-components/filter-fields/select/select.component.ts b/frontend/src/app/components/ui-components/filter-fields/select/select.component.ts index 2e2eb6718..aaf92465c 100644 --- a/frontend/src/app/components/ui-components/filter-fields/select/select.component.ts +++ b/frontend/src/app/components/ui-components/filter-fields/select/select.component.ts @@ -1,39 +1,39 @@ -import { CUSTOM_ELEMENTS_SCHEMA, Component, Input } from '@angular/core'; - -import { BaseFilterFieldComponent } from '../base-filter-field/base-filter-field.component'; import { CommonModule } from '@angular/common'; +import { Component, CUSTOM_ELEMENTS_SCHEMA, Input } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { MatSelectModule } from '@angular/material/select'; +import { BaseFilterFieldComponent } from '../base-filter-field/base-filter-field.component'; @Component({ - selector: 'app-filter-select', - templateUrl: './select.component.html', - styleUrls: ['./select.component.css'], - imports: [CommonModule, FormsModule, MatSelectModule], - schemas: [CUSTOM_ELEMENTS_SCHEMA] + selector: 'app-filter-select', + templateUrl: './select.component.html', + styleUrls: ['./select.component.css'], + imports: [CommonModule, FormsModule, MatSelectModule], + schemas: [CUSTOM_ELEMENTS_SCHEMA], }) export class SelectFilterComponent extends BaseFilterFieldComponent { - @Input() value: string; - - public options: {value: string | null, label: string}[] = []; + @Input() value: string; - originalOrder = () => { return 0; } + public options: { value: string | null; label: string }[] = []; + originalOrder = () => { + return 0; + }; - ngOnInit(): void { - super.ngOnInit(); - if (this.widgetStructure) { - this.options = this.widgetStructure.widget_params.options; - if (this.widgetStructure.widget_params.allow_null) { - this.options = [{ value: null, label: '' }, ...this.options]; - } - } else if (this.structure) { - this.options = this.structure.data_type_params.map((option) => { - return { value: option, label: option }; - }); - if (this.structure.allow_null) { - this.options = [{ value: null, label: '' }, ...this.options]; - } - } - } + ngOnInit(): void { + super.ngOnInit(); + if (this.widgetStructure) { + this.options = this.widgetStructure.widget_params.options; + if (this.widgetStructure.widget_params.allow_null) { + this.options = [{ value: null, label: '' }, ...this.options]; + } + } else if (this.structure) { + this.options = this.structure.data_type_params.map((option) => { + return { value: option, label: option }; + }); + if (this.structure.allow_null) { + this.options = [{ value: null, label: '' }, ...this.options]; + } + } + } } diff --git a/frontend/src/app/components/ui-components/filter-fields/static-text/static-text.component.css b/frontend/src/app/components/ui-components/filter-fields/static-text/static-text.component.css index 86302ca2c..85157a202 100644 --- a/frontend/src/app/components/ui-components/filter-fields/static-text/static-text.component.css +++ b/frontend/src/app/components/ui-components/filter-fields/static-text/static-text.component.css @@ -1,10 +1,10 @@ .static-text { - margin-bottom: 20px; + margin-bottom: 20px; } .static-text__value { - color: #0a0a0a; - font-style: italic; - opacity: 0.32; - padding-left: 8px; -} \ No newline at end of file + color: #0a0a0a; + font-style: italic; + opacity: 0.32; + padding-left: 8px; +} diff --git a/frontend/src/app/components/ui-components/filter-fields/static-text/static-text.component.spec.ts b/frontend/src/app/components/ui-components/filter-fields/static-text/static-text.component.spec.ts index 5ca8bbbac..4c082e27f 100644 --- a/frontend/src/app/components/ui-components/filter-fields/static-text/static-text.component.spec.ts +++ b/frontend/src/app/components/ui-components/filter-fields/static-text/static-text.component.spec.ts @@ -3,22 +3,22 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { StaticTextFilterComponent } from './static-text.component'; describe('StaticTextFilterComponent', () => { - let component: StaticTextFilterComponent; - let fixture: ComponentFixture; + let component: StaticTextFilterComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [StaticTextFilterComponent] - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [StaticTextFilterComponent], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(StaticTextFilterComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(StaticTextFilterComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/ui-components/filter-fields/static-text/static-text.component.ts b/frontend/src/app/components/ui-components/filter-fields/static-text/static-text.component.ts index a2bf195f6..d82130d19 100644 --- a/frontend/src/app/components/ui-components/filter-fields/static-text/static-text.component.ts +++ b/frontend/src/app/components/ui-components/filter-fields/static-text/static-text.component.ts @@ -3,10 +3,10 @@ import { Component, Input } from '@angular/core'; import { BaseFilterFieldComponent } from '../base-filter-field/base-filter-field.component'; @Component({ - selector: 'app-filter-static-text', - templateUrl: './static-text.component.html', - styleUrls: ['./static-text.component.css'] + selector: 'app-filter-static-text', + templateUrl: './static-text.component.html', + styleUrls: ['./static-text.component.css'], }) export class StaticTextFilterComponent extends BaseFilterFieldComponent { - @Input() value: string; + @Input() value: string; } diff --git a/frontend/src/app/components/ui-components/filter-fields/text/text.component.css b/frontend/src/app/components/ui-components/filter-fields/text/text.component.css index d1e2ca29c..17b3101cc 100644 --- a/frontend/src/app/components/ui-components/filter-fields/text/text.component.css +++ b/frontend/src/app/components/ui-components/filter-fields/text/text.component.css @@ -1,3 +1,3 @@ .text-form-field { - width: 100%; -} \ No newline at end of file + width: 100%; +} diff --git a/frontend/src/app/components/ui-components/filter-fields/text/text.component.spec.ts b/frontend/src/app/components/ui-components/filter-fields/text/text.component.spec.ts index 96d40fddc..a7755bc05 100644 --- a/frontend/src/app/components/ui-components/filter-fields/text/text.component.spec.ts +++ b/frontend/src/app/components/ui-components/filter-fields/text/text.component.spec.ts @@ -1,25 +1,24 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { TextFilterComponent } from './text.component'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { TextFilterComponent } from './text.component'; describe('TextFilterComponent', () => { - let component: TextFilterComponent; - let fixture: ComponentFixture; + let component: TextFilterComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [TextFilterComponent, BrowserAnimationsModule] - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [TextFilterComponent, BrowserAnimationsModule], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(TextFilterComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(TextFilterComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/ui-components/filter-fields/text/text.component.ts b/frontend/src/app/components/ui-components/filter-fields/text/text.component.ts index 155964683..5ccbdf983 100644 --- a/frontend/src/app/components/ui-components/filter-fields/text/text.component.ts +++ b/frontend/src/app/components/ui-components/filter-fields/text/text.component.ts @@ -1,30 +1,29 @@ -import { AfterViewInit, Component, ElementRef, Injectable, Input, ViewChild } from '@angular/core'; - -import { BaseFilterFieldComponent } from '../base-filter-field/base-filter-field.component'; import { CommonModule } from '@angular/common'; +import { AfterViewInit, Component, ElementRef, Injectable, Input, ViewChild } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; +import { BaseFilterFieldComponent } from '../base-filter-field/base-filter-field.component'; @Injectable() @Component({ - selector: 'app-filter-text', - templateUrl: './text.component.html', - styleUrls: ['./text.component.css'], - imports: [CommonModule, FormsModule, MatFormFieldModule, MatInputModule] + selector: 'app-filter-text', + templateUrl: './text.component.html', + styleUrls: ['./text.component.css'], + imports: [CommonModule, FormsModule, MatFormFieldModule, MatInputModule], }) export class TextFilterComponent extends BaseFilterFieldComponent implements AfterViewInit { - @Input() value: string; - @ViewChild('inputElement') inputElement: ElementRef; + @Input() value: string; + @ViewChild('inputElement') inputElement: ElementRef; - static type = 'text'; + static type = 'text'; - ngAfterViewInit(): void { - if (this.autofocus && this.inputElement) { - setTimeout(() => { - this.inputElement.nativeElement.focus(); - }, 100); - } - } + ngAfterViewInit(): void { + if (this.autofocus && this.inputElement) { + setTimeout(() => { + this.inputElement.nativeElement.focus(); + }, 100); + } + } } diff --git a/frontend/src/app/components/ui-components/filter-fields/time-interval/time-interval.component.css b/frontend/src/app/components/ui-components/filter-fields/time-interval/time-interval.component.css index 7e87dc982..8b6934468 100644 --- a/frontend/src/app/components/ui-components/filter-fields/time-interval/time-interval.component.css +++ b/frontend/src/app/components/ui-components/filter-fields/time-interval/time-interval.component.css @@ -1,13 +1,13 @@ .feild-set-6 { - display: grid; - grid-template-columns: repeat(3, 1fr); - grid-column-gap: 16px; + display: grid; + grid-template-columns: repeat(3, 1fr); + grid-column-gap: 16px; } ::ng-deep .interval-form-field .mat-mdc-form-field-infix { - width: auto !important; + width: auto !important; } .label { - grid-column: 1 / span 3; -} \ No newline at end of file + grid-column: 1 / span 3; +} diff --git a/frontend/src/app/components/ui-components/filter-fields/time-interval/time-interval.component.spec.ts b/frontend/src/app/components/ui-components/filter-fields/time-interval/time-interval.component.spec.ts index a5ea695b7..c63726e00 100644 --- a/frontend/src/app/components/ui-components/filter-fields/time-interval/time-interval.component.spec.ts +++ b/frontend/src/app/components/ui-components/filter-fields/time-interval/time-interval.component.spec.ts @@ -1,25 +1,24 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { TimeIntervalFilterComponent } from './time-interval.component'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { TimeIntervalFilterComponent } from './time-interval.component'; describe('TimeIntervalFilterComponent', () => { - let component: TimeIntervalFilterComponent; - let fixture: ComponentFixture; + let component: TimeIntervalFilterComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [TimeIntervalFilterComponent, BrowserAnimationsModule] - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [TimeIntervalFilterComponent, BrowserAnimationsModule], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(TimeIntervalFilterComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(TimeIntervalFilterComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/ui-components/filter-fields/time-interval/time-interval.component.ts b/frontend/src/app/components/ui-components/filter-fields/time-interval/time-interval.component.ts index 9529af624..290991207 100644 --- a/frontend/src/app/components/ui-components/filter-fields/time-interval/time-interval.component.ts +++ b/frontend/src/app/components/ui-components/filter-fields/time-interval/time-interval.component.ts @@ -1,85 +1,83 @@ -import * as pgInterval from 'postgres-interval' +import { CommonModule } from '@angular/common'; import { Component, Input } from '@angular/core'; - -import { BaseFilterFieldComponent } from '../base-filter-field/base-filter-field.component'; -import { CommonModule } from '@angular/common'; import { FormsModule } from '@angular/forms'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; +import * as pgInterval from 'postgres-interval'; +import { BaseFilterFieldComponent } from '../base-filter-field/base-filter-field.component'; @Component({ - selector: 'app-filter-time-interval', - templateUrl: './time-interval.component.html', - styleUrls: ['./time-interval.component.css'], - imports: [CommonModule, FormsModule, MatFormFieldModule, MatInputModule] + selector: 'app-filter-time-interval', + templateUrl: './time-interval.component.html', + styleUrls: ['./time-interval.component.css'], + imports: [CommonModule, FormsModule, MatFormFieldModule, MatInputModule], }) export class TimeIntervalFilterComponent extends BaseFilterFieldComponent { - @Input() value; - static type = 'number'; - - public interval = { - years: 0, - months: 0, - days: 0, - hours: 0, - minutes: 0, - seconds: 0, - milliseconds: 0 - }; + @Input() value; + static type = 'number'; - ngOnInit(): void { - super.ngOnInit(); + public interval = { + years: 0, + months: 0, + days: 0, + hours: 0, + minutes: 0, + seconds: 0, + milliseconds: 0, + }; - if (this.value) this.interval = this.parseIntervalString(this.value); - } + ngOnInit(): void { + super.ngOnInit(); - onInputChange() { - // @ts-expect-error - const currentInterval = pgInterval.prototype.toPostgres.call(this.interval); - this.onFieldChange.emit(currentInterval); - } + if (this.value) this.interval = this.parseIntervalString(this.value); + } - parseIntervalString(input) { - const units = { - year: 'years', - years: 'years', - month: 'months', - months: 'months', - day: 'days', - days: 'days', - hour: 'hours', - hours: 'hours', - minute: 'minutes', - minutes: 'minutes', - second: 'seconds', - seconds: 'seconds', - millisecond: 'milliseconds', - milliseconds: 'milliseconds', - }; + onInputChange() { + // @ts-expect-error + const currentInterval = pgInterval.prototype.toPostgres.call(this.interval); + this.onFieldChange.emit(currentInterval); + } - const result = { - years: 0, - months: 0, - days: 0, - hours: 0, - minutes: 0, - seconds: 0, - milliseconds: 0, - }; + parseIntervalString(input) { + const units = { + year: 'years', + years: 'years', + month: 'months', + months: 'months', + day: 'days', + days: 'days', + hour: 'hours', + hours: 'hours', + minute: 'minutes', + minutes: 'minutes', + second: 'seconds', + seconds: 'seconds', + millisecond: 'milliseconds', + milliseconds: 'milliseconds', + }; - const regex = /(\d+(?:\.\d+)?)\s*(years?|months?|days?|hours?|minutes?|seconds?|milliseconds?)/gi; + const result = { + years: 0, + months: 0, + days: 0, + hours: 0, + minutes: 0, + seconds: 0, + milliseconds: 0, + }; - let match; - while ((match = regex.exec(input)) !== null) { - const value = parseFloat(match[1]); - const unit = match[2].toLowerCase(); - const key = units[unit]; + const regex = /(\d+(?:\.\d+)?)\s*(years?|months?|days?|hours?|minutes?|seconds?|milliseconds?)/gi; - if (key) result[key] += value; - } + let match; + while ((match = regex.exec(input)) !== null) { + const value = parseFloat(match[1]); + const unit = match[2].toLowerCase(); + const key = units[unit]; - return result; - } + if (key) result[key] += value; + } + return result; + } } diff --git a/frontend/src/app/components/ui-components/filter-fields/time/time.component.css b/frontend/src/app/components/ui-components/filter-fields/time/time.component.css index bf0a25ecd..935f11420 100644 --- a/frontend/src/app/components/ui-components/filter-fields/time/time.component.css +++ b/frontend/src/app/components/ui-components/filter-fields/time/time.component.css @@ -1,3 +1,3 @@ .time-field { - width: 100%; -} \ No newline at end of file + width: 100%; +} diff --git a/frontend/src/app/components/ui-components/filter-fields/time/time.component.spec.ts b/frontend/src/app/components/ui-components/filter-fields/time/time.component.spec.ts index 9502cad1c..529593cec 100644 --- a/frontend/src/app/components/ui-components/filter-fields/time/time.component.spec.ts +++ b/frontend/src/app/components/ui-components/filter-fields/time/time.component.spec.ts @@ -1,25 +1,24 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { TimeFilterComponent } from './time.component'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { TimeFilterComponent } from './time.component'; describe('TimeFilterComponent', () => { - let component: TimeFilterComponent; - let fixture: ComponentFixture; + let component: TimeFilterComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [TimeFilterComponent, BrowserAnimationsModule] - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [TimeFilterComponent, BrowserAnimationsModule], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(TimeFilterComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(TimeFilterComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/ui-components/filter-fields/time/time.component.ts b/frontend/src/app/components/ui-components/filter-fields/time/time.component.ts index bfb90b4a3..e09cc6723 100644 --- a/frontend/src/app/components/ui-components/filter-fields/time/time.component.ts +++ b/frontend/src/app/components/ui-components/filter-fields/time/time.component.ts @@ -1,18 +1,17 @@ -import { Component, Input } from '@angular/core'; - -import { BaseFilterFieldComponent } from '../base-filter-field/base-filter-field.component'; import { CommonModule } from '@angular/common'; +import { Component, Input } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; +import { BaseFilterFieldComponent } from '../base-filter-field/base-filter-field.component'; @Component({ - selector: 'app-filter-time', - templateUrl: './time.component.html', - styleUrls: ['./time.component.css'], - imports: [CommonModule, FormsModule, MatFormFieldModule, MatInputModule] + selector: 'app-filter-time', + templateUrl: './time.component.html', + styleUrls: ['./time.component.css'], + imports: [CommonModule, FormsModule, MatFormFieldModule, MatInputModule], }) export class TimeFilterComponent extends BaseFilterFieldComponent { - @Input() value; - static type = 'datetime'; + @Input() value; + static type = 'datetime'; } diff --git a/frontend/src/app/components/ui-components/filter-fields/timezone/timezone.component.css b/frontend/src/app/components/ui-components/filter-fields/timezone/timezone.component.css index e9739a06c..388e7c15e 100644 --- a/frontend/src/app/components/ui-components/filter-fields/timezone/timezone.component.css +++ b/frontend/src/app/components/ui-components/filter-fields/timezone/timezone.component.css @@ -1,3 +1,3 @@ .timezone-form-field { - width: 100%; + width: 100%; } diff --git a/frontend/src/app/components/ui-components/filter-fields/timezone/timezone.component.ts b/frontend/src/app/components/ui-components/filter-fields/timezone/timezone.component.ts index 8a8543fba..109fcb7d0 100644 --- a/frontend/src/app/components/ui-components/filter-fields/timezone/timezone.component.ts +++ b/frontend/src/app/components/ui-components/filter-fields/timezone/timezone.component.ts @@ -1,5 +1,5 @@ -import { CUSTOM_ELEMENTS_SCHEMA, Component, Injectable, Input } from '@angular/core'; import { CommonModule } from '@angular/common'; +import { Component, CUSTOM_ELEMENTS_SCHEMA, Injectable, Input } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatSelectModule } from '@angular/material/select'; @@ -8,77 +8,79 @@ import { BaseFilterFieldComponent } from '../base-filter-field/base-filter-field @Injectable() @Component({ - selector: 'app-filter-timezone', - imports: [CommonModule, FormsModule, MatFormFieldModule, MatSelectModule], - templateUrl: './timezone.component.html', - styleUrls: ['./timezone.component.css'], - schemas: [CUSTOM_ELEMENTS_SCHEMA] + selector: 'app-filter-timezone', + imports: [CommonModule, FormsModule, MatFormFieldModule, MatSelectModule], + templateUrl: './timezone.component.html', + styleUrls: ['./timezone.component.css'], + schemas: [CUSTOM_ELEMENTS_SCHEMA], }) export class TimezoneFilterComponent extends BaseFilterFieldComponent { - @Input() value: string; + @Input() value: string; - public timezones: { value: string, label: string }[] = []; + public timezones: { value: string; label: string }[] = []; - originalOrder = () => { return 0; } + originalOrder = () => { + return 0; + }; - static type = 'timezone'; + static type = 'timezone'; - ngOnInit(): void { - super.ngOnInit(); - this.initializeTimezones(); - } + ngOnInit(): void { + super.ngOnInit(); + this.initializeTimezones(); + } - private initializeTimezones(): void { - // Get all available timezone identifiers from Intl API - const timezoneList = Intl.supportedValuesOf('timeZone'); + private initializeTimezones(): void { + // Get all available timezone identifiers from Intl API + const timezoneList = Intl.supportedValuesOf('timeZone'); - // Map timezones to format with offset and readable label - this.timezones = timezoneList.map(tz => { - const offset = this.getTimezoneOffset(tz); - return { - value: tz, - label: `${tz} (UTC${offset})` - }; - }); + // Map timezones to format with offset and readable label + this.timezones = timezoneList.map((tz) => { + const offset = this.getTimezoneOffset(tz); + return { + value: tz, + label: `${tz} (UTC${offset})`, + }; + }); - // Sort by timezone name - this.timezones.sort((a, b) => a.value.localeCompare(b.value)); + // Sort by timezone name + this.timezones.sort((a, b) => a.value.localeCompare(b.value)); - // Check widget params for allow_null option - if (this.widgetStructure?.widget_params?.allow_null) { - this.timezones = [{ value: null, label: '' }, ...this.timezones]; - } else if (this.structure?.allow_null) { - this.timezones = [{ value: null, label: '' }, ...this.timezones]; - } - } + // Check widget params for allow_null option + if (this.widgetStructure?.widget_params?.allow_null) { + this.timezones = [{ value: null, label: '' }, ...this.timezones]; + } else if (this.structure?.allow_null) { + this.timezones = [{ value: null, label: '' }, ...this.timezones]; + } + } - private getTimezoneOffset(timezone: string): string { - try { - const now = new Date(); - const formatter = new Intl.DateTimeFormat('en-US', { - timeZone: timezone, - timeZoneName: 'longOffset' - }); + private getTimezoneOffset(timezone: string): string { + try { + const now = new Date(); + const formatter = new Intl.DateTimeFormat('en-US', { + timeZone: timezone, + timeZoneName: 'longOffset', + }); - const parts = formatter.formatToParts(now); - const offsetPart = parts.find(part => part.type === 'timeZoneName'); + const parts = formatter.formatToParts(now); + const offsetPart = parts.find((part) => part.type === 'timeZoneName'); - if (offsetPart?.value.includes('GMT')) { - // Extract offset from "GMT+XX:XX" format - const offset = offsetPart.value.replace('GMT', ''); - return offset === '' ? '+00:00' : offset; - } + if (offsetPart?.value.includes('GMT')) { + // Extract offset from "GMT+XX:XX" format + const offset = offsetPart.value.replace('GMT', ''); + return offset === '' ? '+00:00' : offset; + } - // Fallback: calculate offset manually - const utcDate = new Date(now.toLocaleString('en-US', { timeZone: 'UTC' })); - const tzDate = new Date(now.toLocaleString('en-US', { timeZone: timezone })); - const offsetMinutes = (tzDate.getTime() - utcDate.getTime()) / 60000; - const hours = Math.floor(Math.abs(offsetMinutes) / 60); - const minutes = Math.abs(offsetMinutes) % 60; - const sign = offsetMinutes >= 0 ? '+' : '-'; - return `${sign}${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}`; - } catch (_error) { - return ''; - } - } + // Fallback: calculate offset manually + const utcDate = new Date(now.toLocaleString('en-US', { timeZone: 'UTC' })); + const tzDate = new Date(now.toLocaleString('en-US', { timeZone: timezone })); + const offsetMinutes = (tzDate.getTime() - utcDate.getTime()) / 60000; + const hours = Math.floor(Math.abs(offsetMinutes) / 60); + const minutes = Math.abs(offsetMinutes) % 60; + const sign = offsetMinutes >= 0 ? '+' : '-'; + return `${sign}${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}`; + } catch (_error) { + return ''; + } + } } diff --git a/frontend/src/app/components/ui-components/icon-picker/icon-picker.component.css b/frontend/src/app/components/ui-components/icon-picker/icon-picker.component.css index a65c695f4..5d79bd9c7 100644 --- a/frontend/src/app/components/ui-components/icon-picker/icon-picker.component.css +++ b/frontend/src/app/components/ui-components/icon-picker/icon-picker.component.css @@ -1,82 +1,81 @@ .anchor-icon-button { - background-color: var(--bg-color); + background-color: var(--bg-color); } @media (prefers-color-scheme: dark) { - .anchor-icon-button { - --bg-color: #292929; - } + .anchor-icon-button { + --bg-color: #292929; + } } @media (prefers-color-scheme: light) { - .anchor-icon-button { - --bg-color: var(--color-primaryPalette-100); - } + .anchor-icon-button { + --bg-color: var(--color-primaryPalette-100); + } } .icons-menu { - display: flex; - flex-direction: column; - padding: 0 16px; + display: flex; + flex-direction: column; + padding: 0 16px; } .icons-menu__header { - display: flex; - align-items: center; - justify-content: space-between; - color: var(--mat-sidenav-content-text-color); - margin-top: 8px; + display: flex; + align-items: center; + justify-content: space-between; + color: var(--mat-sidenav-content-text-color); + margin-top: 8px; } .icons-list { - list-style: none; - display: grid; - grid-template-columns: repeat(5, 40px); - grid-gap: 4px; - justify-content: space-between; - margin-top: 8px; - margin-bottom: 12px; + list-style: none; + display: grid; + grid-template-columns: repeat(5, 40px); + grid-gap: 4px; + justify-content: space-between; + margin-top: 8px; + margin-bottom: 12px; } .icon-button { - display: flex; - align-items: center; - justify-content: center; - border-radius: 50%; - color: var(--mat-sidenav-content-text-color); - height: 40px; - width: 40px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 50%; + color: var(--mat-sidenav-content-text-color); + height: 40px; + width: 40px; } .icon-radio-button:checked + ::ng-deep.icon-button { - background-color: rgba(0,0,0,0.16); + background-color: rgba(0, 0, 0, 0.16); } - .custom-icon-box { - position: relative; + position: relative; } .apply-icon-button { - position: absolute; - top: 0; - right: 8px; - align-self: flex-end; - margin-top: 4px; + position: absolute; + top: 0; + right: 8px; + align-self: flex-end; + margin-top: 4px; } .icon-input__hint { - color: rgba(0, 0, 0, 0.64); + color: rgba(0, 0, 0, 0.64); } @media (prefers-color-scheme: dark) { - .icon-input__hint { - color: rgba(255, 255, 255, 0.64); - } + .icon-input__hint { + color: rgba(255, 255, 255, 0.64); + } } .google-icons-link { - color: var(--color-primaryPalette-500); - font-weight: 600; - text-decoration: none; -} \ No newline at end of file + color: var(--color-primaryPalette-500); + font-weight: 600; + text-decoration: none; +} diff --git a/frontend/src/app/components/ui-components/icon-picker/icon-picker.component.spec.ts b/frontend/src/app/components/ui-components/icon-picker/icon-picker.component.spec.ts index 6c1db0302..9f55c4bcc 100644 --- a/frontend/src/app/components/ui-components/icon-picker/icon-picker.component.spec.ts +++ b/frontend/src/app/components/ui-components/icon-picker/icon-picker.component.spec.ts @@ -1,29 +1,23 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { IconPickerComponent } from './icon-picker.component'; import { MatMenuModule } from '@angular/material/menu'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { IconPickerComponent } from './icon-picker.component'; describe('IconPickerComponent', () => { - let component: IconPickerComponent; - let fixture: ComponentFixture; + let component: IconPickerComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [ - MatMenuModule, - IconPickerComponent, - BrowserAnimationsModule - ] -}) - .compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [MatMenuModule, IconPickerComponent, BrowserAnimationsModule], + }).compileComponents(); - fixture = TestBed.createComponent(IconPickerComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + fixture = TestBed.createComponent(IconPickerComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/ui-components/icon-picker/icon-picker.component.ts b/frontend/src/app/components/ui-components/icon-picker/icon-picker.component.ts index 8fc1197f3..a3fa6b771 100644 --- a/frontend/src/app/components/ui-components/icon-picker/icon-picker.component.ts +++ b/frontend/src/app/components/ui-components/icon-picker/icon-picker.component.ts @@ -1,53 +1,53 @@ -import { Component, EventEmitter, Input, Output, ViewChild } from '@angular/core'; - import { CommonModule } from '@angular/common'; +import { Component, EventEmitter, Input, Output, ViewChild } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { MatButtonModule } from '@angular/material/button'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatIconModule } from '@angular/material/icon'; import { MatInputModule } from '@angular/material/input'; -import { MatMenuModule } from '@angular/material/menu'; -import { MatMenuTrigger } from '@angular/material/menu'; +import { MatMenuModule, MatMenuTrigger } from '@angular/material/menu'; import { MatTooltipModule } from '@angular/material/tooltip'; @Component({ - selector: 'app-icon-picker', - templateUrl: './icon-picker.component.html', - styleUrls: ['./icon-picker.component.css'], - imports: [ - CommonModule, - MatMenuModule, - MatIconModule, - FormsModule, - MatTooltipModule, - MatFormFieldModule, - MatInputModule, - MatButtonModule - ] + selector: 'app-icon-picker', + templateUrl: './icon-picker.component.html', + styleUrls: ['./icon-picker.component.css'], + imports: [ + CommonModule, + MatMenuModule, + MatIconModule, + FormsModule, + MatTooltipModule, + MatFormFieldModule, + MatInputModule, + MatButtonModule, + ], }) export class IconPickerComponent { - @Input() resetButtonShown: boolean; - @Input() icon: string; - @Input() defaultIcons: Array; - @Input() tooltip: string; - @Output() onFieldChange = new EventEmitter(); - @ViewChild(MatMenuTrigger) trigger: MatMenuTrigger; + @Input() resetButtonShown: boolean; + @Input() icon: string; + @Input() defaultIcons: Array; + @Input() tooltip: string; + @Output() onFieldChange = new EventEmitter(); + @ViewChild(MatMenuTrigger) trigger: MatMenuTrigger; - public customIcon: string = ''; + public customIcon: string = ''; - constructor() { console.log(this.resetButtonShown, 'resetButtonShown'); } + constructor() { + console.log(this.resetButtonShown, 'resetButtonShown'); + } - ngOnInit() { - this.customIcon = this.icon || ''; - } + ngOnInit() { + this.customIcon = this.icon || ''; + } - applyIcon() { - this.onFieldChange.emit(this.customIcon); - this.trigger.closeMenu(); - } + applyIcon() { + this.onFieldChange.emit(this.customIcon); + this.trigger.closeMenu(); + } - resetIcon() { - this.onFieldChange.emit(null); - this.trigger.closeMenu(); - } + resetIcon() { + this.onFieldChange.emit(null); + this.trigger.closeMenu(); + } } diff --git a/frontend/src/app/components/ui-components/ip-address-button/ip-address-button.component.css b/frontend/src/app/components/ui-components/ip-address-button/ip-address-button.component.css index 3ed6fbd5e..677b50f87 100644 --- a/frontend/src/app/components/ui-components/ip-address-button/ip-address-button.component.css +++ b/frontend/src/app/components/ui-components/ip-address-button/ip-address-button.component.css @@ -1,7 +1,7 @@ .ip-copy-button { - background: transparent; - border: none; - color: var(--mat-sidenav-content-text-color); - font-family: monospace; - padding: 2px 4px; -} \ No newline at end of file + background: transparent; + border: none; + color: var(--mat-sidenav-content-text-color); + font-family: monospace; + padding: 2px 4px; +} diff --git a/frontend/src/app/components/ui-components/ip-address-button/ip-address-button.component.spec.ts b/frontend/src/app/components/ui-components/ip-address-button/ip-address-button.component.spec.ts index 5c3aa85a0..0db053015 100644 --- a/frontend/src/app/components/ui-components/ip-address-button/ip-address-button.component.spec.ts +++ b/frontend/src/app/components/ui-components/ip-address-button/ip-address-button.component.spec.ts @@ -4,21 +4,20 @@ import { MatSnackBarModule } from '@angular/material/snack-bar'; import { IpAddressButtonComponent } from './ip-address-button.component'; describe('IpAddressButtonComponent', () => { - let component: IpAddressButtonComponent; - let fixture: ComponentFixture; + let component: IpAddressButtonComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [MatSnackBarModule, IpAddressButtonComponent] -}) - .compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [MatSnackBarModule, IpAddressButtonComponent], + }).compileComponents(); - fixture = TestBed.createComponent(IpAddressButtonComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + fixture = TestBed.createComponent(IpAddressButtonComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/ui-components/ip-address-button/ip-address-button.component.ts b/frontend/src/app/components/ui-components/ip-address-button/ip-address-button.component.ts index 51985736d..d561dcf7a 100644 --- a/frontend/src/app/components/ui-components/ip-address-button/ip-address-button.component.ts +++ b/frontend/src/app/components/ui-components/ip-address-button/ip-address-button.component.ts @@ -1,25 +1,21 @@ -import { Component, Input, OnInit } from '@angular/core'; - import { CdkCopyToClipboard } from '@angular/cdk/clipboard'; +import { Component, Input, OnInit } from '@angular/core'; import { NotificationsService } from 'src/app/services/notifications.service'; @Component({ - selector: 'app-ip-address-button', - templateUrl: './ip-address-button.component.html', - styleUrls: ['./ip-address-button.component.css'], - imports: [CdkCopyToClipboard] + selector: 'app-ip-address-button', + templateUrl: './ip-address-button.component.html', + styleUrls: ['./ip-address-button.component.css'], + imports: [CdkCopyToClipboard], }) export class IpAddressButtonComponent implements OnInit { - @Input() ip: string; + @Input() ip: string; - constructor( - private _notifications: NotificationsService - ) { } + constructor(private _notifications: NotificationsService) {} - ngOnInit(): void { - } + ngOnInit(): void {} - showCopyNotification(message: string) { - this._notifications.showSuccessSnackbar(message); - } + showCopyNotification(message: string) { + this._notifications.showSuccessSnackbar(message); + } } diff --git a/frontend/src/app/components/ui-components/record-edit-fields/base-row-field/base-row-field.component.spec.ts b/frontend/src/app/components/ui-components/record-edit-fields/base-row-field/base-row-field.component.spec.ts index 47585d69e..245d71db8 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/base-row-field/base-row-field.component.spec.ts +++ b/frontend/src/app/components/ui-components/record-edit-fields/base-row-field/base-row-field.component.spec.ts @@ -3,21 +3,20 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { BaseEditFieldComponent } from './base-row-field.component'; describe('BaseEditFieldComponent', () => { - let component: BaseEditFieldComponent; - let fixture: ComponentFixture; + let component: BaseEditFieldComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [BaseEditFieldComponent] - }).compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [BaseEditFieldComponent], + }).compileComponents(); + fixture = TestBed.createComponent(BaseEditFieldComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - fixture = TestBed.createComponent(BaseEditFieldComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/ui-components/record-edit-fields/base-row-field/base-row-field.component.ts b/frontend/src/app/components/ui-components/record-edit-fields/base-row-field/base-row-field.component.ts index 24f1e7f80..5eb10c9a6 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/base-row-field/base-row-field.component.ts +++ b/frontend/src/app/components/ui-components/record-edit-fields/base-row-field/base-row-field.component.ts @@ -1,30 +1,29 @@ +import { CommonModule } from '@angular/common'; import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { TableField, TableForeignKey, WidgetStructure } from 'src/app/models/table'; - -import { CommonModule } from '@angular/common'; import { normalizeFieldName } from '../../../../lib/normalize'; @Component({ - selector: 'app-base-edit-field', - templateUrl: './base-row-field.component.html', - styleUrl: './base-row-field.component.css', - imports: [CommonModule] + selector: 'app-base-edit-field', + templateUrl: './base-row-field.component.html', + styleUrl: './base-row-field.component.css', + imports: [CommonModule], }) export class BaseEditFieldComponent implements OnInit { - @Input() key: string; - @Input() label: string; - @Input() required: boolean; - @Input() readonly: boolean; - @Input() structure: TableField; - @Input() disabled: boolean; - @Input() widgetStructure: WidgetStructure; - @Input() relations: TableForeignKey; + @Input() key: string; + @Input() label: string; + @Input() required: boolean; + @Input() readonly: boolean; + @Input() structure: TableField; + @Input() disabled: boolean; + @Input() widgetStructure: WidgetStructure; + @Input() relations: TableForeignKey; - @Output() onFieldChange = new EventEmitter(); + @Output() onFieldChange = new EventEmitter(); - public normalizedLabel: string; + public normalizedLabel: string; - ngOnInit(): void { - this.normalizedLabel = normalizeFieldName(this.label); - } + ngOnInit(): void { + this.normalizedLabel = normalizeFieldName(this.label); + } } diff --git a/frontend/src/app/components/ui-components/record-edit-fields/binary-data-caption/binary-data-caption.component.css b/frontend/src/app/components/ui-components/record-edit-fields/binary-data-caption/binary-data-caption.component.css index 86302ca2c..85157a202 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/binary-data-caption/binary-data-caption.component.css +++ b/frontend/src/app/components/ui-components/record-edit-fields/binary-data-caption/binary-data-caption.component.css @@ -1,10 +1,10 @@ .static-text { - margin-bottom: 20px; + margin-bottom: 20px; } .static-text__value { - color: #0a0a0a; - font-style: italic; - opacity: 0.32; - padding-left: 8px; -} \ No newline at end of file + color: #0a0a0a; + font-style: italic; + opacity: 0.32; + padding-left: 8px; +} diff --git a/frontend/src/app/components/ui-components/record-edit-fields/binary-data-caption/binary-data-caption.component.spec.ts b/frontend/src/app/components/ui-components/record-edit-fields/binary-data-caption/binary-data-caption.component.spec.ts index 6144b27d8..55b1542d8 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/binary-data-caption/binary-data-caption.component.spec.ts +++ b/frontend/src/app/components/ui-components/record-edit-fields/binary-data-caption/binary-data-caption.component.spec.ts @@ -3,22 +3,22 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { BinaryDataCaptionEditComponent } from './binary-data-caption.component'; describe('BinaryDataCaptionEditComponent', () => { - let component: BinaryDataCaptionEditComponent; - let fixture: ComponentFixture; + let component: BinaryDataCaptionEditComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [BinaryDataCaptionEditComponent] - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [BinaryDataCaptionEditComponent], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(BinaryDataCaptionEditComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(BinaryDataCaptionEditComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/ui-components/record-edit-fields/binary-data-caption/binary-data-caption.component.ts b/frontend/src/app/components/ui-components/record-edit-fields/binary-data-caption/binary-data-caption.component.ts index 30770f1e3..f1048bfd9 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/binary-data-caption/binary-data-caption.component.ts +++ b/frontend/src/app/components/ui-components/record-edit-fields/binary-data-caption/binary-data-caption.component.ts @@ -1,11 +1,11 @@ -import { BaseEditFieldComponent } from '../base-row-field/base-row-field.component'; import { CommonModule } from '@angular/common'; import { Component } from '@angular/core'; +import { BaseEditFieldComponent } from '../base-row-field/base-row-field.component'; @Component({ - selector: 'app-edit-static-text', - templateUrl: './binary-data-caption.component.html', - styleUrls: ['./binary-data-caption.component.css'], - imports: [CommonModule] + selector: 'app-edit-static-text', + templateUrl: './binary-data-caption.component.html', + styleUrls: ['./binary-data-caption.component.css'], + imports: [CommonModule], }) -export class BinaryDataCaptionEditComponent extends BaseEditFieldComponent { } +export class BinaryDataCaptionEditComponent extends BaseEditFieldComponent {} diff --git a/frontend/src/app/components/ui-components/record-edit-fields/boolean/boolean.component.spec.ts b/frontend/src/app/components/ui-components/record-edit-fields/boolean/boolean.component.spec.ts index 8926e786a..bec93cda4 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/boolean/boolean.component.spec.ts +++ b/frontend/src/app/components/ui-components/record-edit-fields/boolean/boolean.component.spec.ts @@ -1,93 +1,87 @@ +import { provideHttpClient } from '@angular/common/http'; import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { BooleanEditComponent } from './boolean.component'; import { MatDialogModule } from '@angular/material/dialog'; import { MatSnackBarModule } from '@angular/material/snack-bar'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { provideHttpClient } from '@angular/common/http'; +import { BooleanEditComponent } from './boolean.component'; describe('BooleanEditComponent', () => { - let component: BooleanEditComponent; - let fixture: ComponentFixture; - - const fakeStructure = { - "column_name": "banned", - "column_default": "0", - "data_type": "tinyint", - "isExcluded": false, - "isSearched": false, - "auto_increment": false, - "allow_null": false, - "character_maximum_length": 1 - } - - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [ - MatSnackBarModule, - MatDialogModule, - BooleanEditComponent, - BrowserAnimationsModule - ], - providers: [provideHttpClient()] - }).compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(BooleanEditComponent); - component = fixture.componentInstance; - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - it('should set value in true when input value contain anything', () => { - component.value = 'anything'; - component.structure = fakeStructure; - component.ngOnInit(); - - expect(component.value).toBeTruthy(); - }); - - it('should set value in felse when input value is 0', () => { - component.value = 0; - component.structure = fakeStructure; - component.ngOnInit(); - - expect(component.value).toBeFalsy(); - }); - - it('should set value in null when input value is undefined', () => { - component.value = undefined; - component.structure = fakeStructure; - component.ngOnInit(); - - expect(component.value).toEqual(null); - }); - - it('should set isRadiogroup in false if allow_null is false', () => { - component.value = undefined; - component.structure = fakeStructure; - component.ngOnInit(); - - expect(component.isRadiogroup).toEqual(false); - }) - - it('should set isRadiogroup in true if allow_null is true', () => { - component.value = undefined; - component.structure = { - "column_name": "banned", - "column_default": "0", - "data_type": "tinyint", - "isExcluded": false, - "isSearched": false, - "auto_increment": false, - "allow_null": true, - "character_maximum_length": 1 - }; - component.ngOnInit(); - - expect(component.isRadiogroup).toEqual(true); - }) + let component: BooleanEditComponent; + let fixture: ComponentFixture; + + const fakeStructure = { + column_name: 'banned', + column_default: '0', + data_type: 'tinyint', + isExcluded: false, + isSearched: false, + auto_increment: false, + allow_null: false, + character_maximum_length: 1, + }; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [MatSnackBarModule, MatDialogModule, BooleanEditComponent, BrowserAnimationsModule], + providers: [provideHttpClient()], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(BooleanEditComponent); + component = fixture.componentInstance; + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should set value in true when input value contain anything', () => { + component.value = 'anything'; + component.structure = fakeStructure; + component.ngOnInit(); + + expect(component.value).toBeTruthy(); + }); + + it('should set value in felse when input value is 0', () => { + component.value = 0; + component.structure = fakeStructure; + component.ngOnInit(); + + expect(component.value).toBeFalsy(); + }); + + it('should set value in null when input value is undefined', () => { + component.value = undefined; + component.structure = fakeStructure; + component.ngOnInit(); + + expect(component.value).toEqual(null); + }); + + it('should set isRadiogroup in false if allow_null is false', () => { + component.value = undefined; + component.structure = fakeStructure; + component.ngOnInit(); + + expect(component.isRadiogroup).toEqual(false); + }); + + it('should set isRadiogroup in true if allow_null is true', () => { + component.value = undefined; + component.structure = { + column_name: 'banned', + column_default: '0', + data_type: 'tinyint', + isExcluded: false, + isSearched: false, + auto_increment: false, + allow_null: true, + character_maximum_length: 1, + }; + component.ngOnInit(); + + expect(component.isRadiogroup).toEqual(true); + }); }); diff --git a/frontend/src/app/components/ui-components/record-edit-fields/boolean/boolean.component.ts b/frontend/src/app/components/ui-components/record-edit-fields/boolean/boolean.component.ts index 6efc0c0e0..178ff0c08 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/boolean/boolean.component.ts +++ b/frontend/src/app/components/ui-components/record-edit-fields/boolean/boolean.component.ts @@ -1,62 +1,60 @@ -import { Component, Input, } from '@angular/core'; - -import { BaseEditFieldComponent } from '../base-row-field/base-row-field.component'; import { CommonModule } from '@angular/common'; -import { ConnectionsService } from 'src/app/services/connections.service'; +import { Component, Input } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { MatButtonToggleModule } from '@angular/material/button-toggle'; import { DBtype } from 'src/app/models/connection'; +import { ConnectionsService } from 'src/app/services/connections.service'; +import { BaseEditFieldComponent } from '../base-row-field/base-row-field.component'; @Component({ - selector: 'app-edit-boolean', - templateUrl: './boolean.component.html', - styleUrls: ['./boolean.component.css'], - imports: [CommonModule, FormsModule, MatButtonToggleModule] + selector: 'app-edit-boolean', + templateUrl: './boolean.component.html', + styleUrls: ['./boolean.component.css'], + imports: [CommonModule, FormsModule, MatButtonToggleModule], }) export class BooleanEditComponent extends BaseEditFieldComponent { - @Input() value: boolean | number | string | null; - - public isRadiogroup: boolean; - connectionType: DBtype; - - constructor( - private _connections: ConnectionsService, - ) { - super(); - } - - ngOnInit(): void { - super.ngOnInit(); - this.connectionType = this._connections.currentConnection.type; - - if (this.value) { - this.value = true; - } else if (this.value === 0 || this.value === '' || this.value === false) { - this.value = false; - } else { - this.value = null; - } - - this.onFieldChange.emit(this.value); - - // Parse widget parameters if available - let parsedParams = null; - if (this.widgetStructure?.widget_params) { - parsedParams = typeof this.widgetStructure.widget_params === 'string' - ? JSON.parse(this.widgetStructure.widget_params) - : this.widgetStructure.widget_params; - } - - // Check allow_null from either structure or widget params - this.isRadiogroup = (this.structure?.allow_null) || !!(parsedParams?.allow_null); - } - - onToggleChange(optionValue: boolean): void { - if (this.value === optionValue) { - this.value = null; - } else { - this.value = optionValue; - } - this.onFieldChange.emit(this.value); - } + @Input() value: boolean | number | string | null; + + public isRadiogroup: boolean; + connectionType: DBtype; + + constructor(private _connections: ConnectionsService) { + super(); + } + + ngOnInit(): void { + super.ngOnInit(); + this.connectionType = this._connections.currentConnection.type; + + if (this.value) { + this.value = true; + } else if (this.value === 0 || this.value === '' || this.value === false) { + this.value = false; + } else { + this.value = null; + } + + this.onFieldChange.emit(this.value); + + // Parse widget parameters if available + let parsedParams = null; + if (this.widgetStructure?.widget_params) { + parsedParams = + typeof this.widgetStructure.widget_params === 'string' + ? JSON.parse(this.widgetStructure.widget_params) + : this.widgetStructure.widget_params; + } + + // Check allow_null from either structure or widget params + this.isRadiogroup = this.structure?.allow_null || !!parsedParams?.allow_null; + } + + onToggleChange(optionValue: boolean): void { + if (this.value === optionValue) { + this.value = null; + } else { + this.value = optionValue; + } + this.onFieldChange.emit(this.value); + } } diff --git a/frontend/src/app/components/ui-components/record-edit-fields/code/code.component.css b/frontend/src/app/components/ui-components/record-edit-fields/code/code.component.css index 876a91d2e..05e90e456 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/code/code.component.css +++ b/frontend/src/app/components/ui-components/record-edit-fields/code/code.component.css @@ -1,16 +1,16 @@ .code-editor-box { - display: block; - border: 1px solid rgba(0, 0, 0, 0.38); - border-radius: 4px; - margin-top: 4px; - margin-bottom: 20px; - /* overflow-y: auto; + display: block; + border: 1px solid rgba(0, 0, 0, 0.38); + border-radius: 4px; + margin-top: 4px; + margin-bottom: 20px; + /* overflow-y: auto; resize: vertical; */ - overflow: auto; - resize: both; + overflow: auto; + resize: both; } .code-editor-box ::ng-deep .ngs-code-editor { - width: 100%; - height: 100%; -} \ No newline at end of file + width: 100%; + height: 100%; +} diff --git a/frontend/src/app/components/ui-components/record-edit-fields/code/code.component.ts b/frontend/src/app/components/ui-components/record-edit-fields/code/code.component.ts index 1b233628b..461a1a600 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/code/code.component.ts +++ b/frontend/src/app/components/ui-components/record-edit-fields/code/code.component.ts @@ -1,42 +1,39 @@ +import { CommonModule } from '@angular/common'; import { Component, Input } from '@angular/core'; - -import { BaseEditFieldComponent } from '../base-row-field/base-row-field.component'; import { CodeEditorModule } from '@ngstack/code-editor'; -import { CommonModule } from '@angular/common'; import { UiSettingsService } from 'src/app/services/ui-settings.service'; +import { BaseEditFieldComponent } from '../base-row-field/base-row-field.component'; @Component({ - selector: 'app-edit-code', - templateUrl: './code.component.html', - styleUrl: './code.component.css', - imports: [CommonModule, CodeEditorModule], + selector: 'app-edit-code', + templateUrl: './code.component.html', + styleUrl: './code.component.css', + imports: [CommonModule, CodeEditorModule], }) export class CodeEditComponent extends BaseEditFieldComponent { - @Input() value; + @Input() value; - public mutableCodeModel: Object; - public codeEditorOptions = { - minimap: { enabled: false }, - automaticLayout: true, - scrollBeyondLastLine: false, - wordWrap: 'on', - }; - public codeEditorTheme = 'vs-dark'; + public mutableCodeModel: Object; + public codeEditorOptions = { + minimap: { enabled: false }, + automaticLayout: true, + scrollBeyondLastLine: false, + wordWrap: 'on', + }; + public codeEditorTheme = 'vs-dark'; - constructor( - private _uiSettings: UiSettingsService, - ) { - super(); - } + constructor(private _uiSettings: UiSettingsService) { + super(); + } - ngOnInit(): void { - super.ngOnInit(); - this.mutableCodeModel = { - language: `${this.widgetStructure.widget_params.language}`, - uri: `${this.label}.json`, - value: this.value - } + ngOnInit(): void { + super.ngOnInit(); + this.mutableCodeModel = { + language: `${this.widgetStructure.widget_params.language}`, + uri: `${this.label}.json`, + value: this.value, + }; - this.codeEditorTheme = this._uiSettings.isDarkMode ? 'vs-dark' : 'vs'; - } + this.codeEditorTheme = this._uiSettings.isDarkMode ? 'vs-dark' : 'vs'; + } } diff --git a/frontend/src/app/components/ui-components/record-edit-fields/color/color.component.css b/frontend/src/app/components/ui-components/record-edit-fields/color/color.component.css index 4e1b787a7..88dc71654 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/color/color.component.css +++ b/frontend/src/app/components/ui-components/record-edit-fields/color/color.component.css @@ -1,31 +1,30 @@ .color-edit-container { - width: 100%; + width: 100%; } .color-form-field { - width: 100%; + width: 100%; } .color-picker-container { - display: flex; - align-items: center; - margin-left: 8px; + display: flex; + align-items: center; + margin-left: 8px; } .color-picker-input { - width: 32px; - height: 32px; - border: none; - border-radius: 4px; - cursor: pointer; - outline: none; - background: none; - padding: 0; - margin-right: 8px; + width: 32px; + height: 32px; + border: none; + border-radius: 4px; + cursor: pointer; + outline: none; + background: none; + padding: 0; + margin-right: 8px; } .color-picker-input:disabled { - cursor: not-allowed; - opacity: 0.6; + cursor: not-allowed; + opacity: 0.6; } - diff --git a/frontend/src/app/components/ui-components/record-edit-fields/color/color.component.ts b/frontend/src/app/components/ui-components/record-edit-fields/color/color.component.ts index 8f8ee870a..2225c2fc3 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/color/color.component.ts +++ b/frontend/src/app/components/ui-components/record-edit-fields/color/color.component.ts @@ -1,131 +1,130 @@ import { Component, Injectable, Input } from '@angular/core'; - -import { BaseEditFieldComponent } from '../base-row-field/base-row-field.component'; import { FormsModule } from '@angular/forms'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; import colorString from 'color-string'; +import { BaseEditFieldComponent } from '../base-row-field/base-row-field.component'; @Injectable() @Component({ - selector: 'app-edit-color', - templateUrl: './color.component.html', - styleUrls: ['./color.component.css'], - imports: [MatFormFieldModule, MatInputModule, FormsModule] + selector: 'app-edit-color', + templateUrl: './color.component.html', + styleUrls: ['./color.component.css'], + imports: [MatFormFieldModule, MatInputModule, FormsModule], }) export class ColorEditComponent extends BaseEditFieldComponent { - @Input() value: string; - - static type = 'color'; - - get isValidColor(): boolean { - if (!this.value) return false; - return this.parseColor(this.value) !== null; - } - - get normalizedColorForPicker(): string { - const parsed = this.parseColor(this.value); - if (parsed) { - const [r, g, b] = parsed.value; - return `#${this.toHex(r)}${this.toHex(g)}${this.toHex(b)}`; - } - return '#000000'; - } - - get formattedColorValue(): string { - const parsed = this.parseColor(this.value); - if (!parsed) return this.value; - - const format = this.widgetStructure?.widget_params?.format || 'hex_hash'; - const [r, g, b, a] = parsed.value; - - switch (format) { - case 'hex': - return colorString.to.hex(r, g, b, a).slice(1); // Remove # prefix - case 'hex_hash': - return colorString.to.hex(r, g, b, a); - case 'rgb': - return colorString.to.rgb(r, g, b, a); - case 'hsl': { - // Convert RGB to HSL using built-in conversion - const hex = colorString.to.hex(r, g, b, a); - const hslParsed = colorString.get.hsl(hex); - if (hslParsed) { - const [h, s, l, alpha] = hslParsed; - return colorString.to.hsl(h, s, l, alpha); - } - return hex; - } - default: - return colorString.to.hex(r, g, b, a); - } - } - - private parseColor(color: string): any { - if (!color) return null; - - // Try parsing with color-string - const parsed = colorString.get(color); - if (parsed) return parsed; - - // Try hex without hash - if (/^[A-Fa-f0-9]{6}$|^[A-Fa-f0-9]{3}$/.test(color)) { - return colorString.get('#' + color); - } - - return null; - } - - private toHex(n: number): string { - const hex = n.toString(16); - return hex.length === 1 ? '0' + hex : hex; - } - - onColorPickerChange(event: Event) { - const target = event.target as HTMLInputElement; - const pickerValue = target.value; - - // Convert picker value to desired format - const parsed = this.parseColor(pickerValue); - if (parsed) { - const format = this.widgetStructure?.widget_params?.format || 'hex_hash'; - - const [r, g, b, a] = parsed.value; - - switch (format) { - case 'hex': - this.value = colorString.to.hex(r, g, b, a).slice(1); - break; - case 'hex_hash': - this.value = colorString.to.hex(r, g, b, a); - break; - case 'rgb': - this.value = colorString.to.rgb(r, g, b, a); - break; - case 'hsl': { - // Convert RGB to HSL using built-in conversion - const hex = colorString.to.hex(r, g, b, a); - const hslParsed = colorString.get.hsl(hex); - if (hslParsed) { - const [h, s, l, alpha] = hslParsed; - this.value = colorString.to.hsl(h, s, l, alpha); - } else { - this.value = hex; - } - break; - } - default: - this.value = colorString.to.hex(r, g, b, a); - } - } else { - this.value = pickerValue; - } - - this.onFieldChange.emit(this.value); - } - - onTextInputChange() { - this.onFieldChange.emit(this.value); - } -} \ No newline at end of file + @Input() value: string; + + static type = 'color'; + + get isValidColor(): boolean { + if (!this.value) return false; + return this.parseColor(this.value) !== null; + } + + get normalizedColorForPicker(): string { + const parsed = this.parseColor(this.value); + if (parsed) { + const [r, g, b] = parsed.value; + return `#${this.toHex(r)}${this.toHex(g)}${this.toHex(b)}`; + } + return '#000000'; + } + + get formattedColorValue(): string { + const parsed = this.parseColor(this.value); + if (!parsed) return this.value; + + const format = this.widgetStructure?.widget_params?.format || 'hex_hash'; + const [r, g, b, a] = parsed.value; + + switch (format) { + case 'hex': + return colorString.to.hex(r, g, b, a).slice(1); // Remove # prefix + case 'hex_hash': + return colorString.to.hex(r, g, b, a); + case 'rgb': + return colorString.to.rgb(r, g, b, a); + case 'hsl': { + // Convert RGB to HSL using built-in conversion + const hex = colorString.to.hex(r, g, b, a); + const hslParsed = colorString.get.hsl(hex); + if (hslParsed) { + const [h, s, l, alpha] = hslParsed; + return colorString.to.hsl(h, s, l, alpha); + } + return hex; + } + default: + return colorString.to.hex(r, g, b, a); + } + } + + private parseColor(color: string): any { + if (!color) return null; + + // Try parsing with color-string + const parsed = colorString.get(color); + if (parsed) return parsed; + + // Try hex without hash + if (/^[A-Fa-f0-9]{6}$|^[A-Fa-f0-9]{3}$/.test(color)) { + return colorString.get('#' + color); + } + + return null; + } + + private toHex(n: number): string { + const hex = n.toString(16); + return hex.length === 1 ? '0' + hex : hex; + } + + onColorPickerChange(event: Event) { + const target = event.target as HTMLInputElement; + const pickerValue = target.value; + + // Convert picker value to desired format + const parsed = this.parseColor(pickerValue); + if (parsed) { + const format = this.widgetStructure?.widget_params?.format || 'hex_hash'; + + const [r, g, b, a] = parsed.value; + + switch (format) { + case 'hex': + this.value = colorString.to.hex(r, g, b, a).slice(1); + break; + case 'hex_hash': + this.value = colorString.to.hex(r, g, b, a); + break; + case 'rgb': + this.value = colorString.to.rgb(r, g, b, a); + break; + case 'hsl': { + // Convert RGB to HSL using built-in conversion + const hex = colorString.to.hex(r, g, b, a); + const hslParsed = colorString.get.hsl(hex); + if (hslParsed) { + const [h, s, l, alpha] = hslParsed; + this.value = colorString.to.hsl(h, s, l, alpha); + } else { + this.value = hex; + } + break; + } + default: + this.value = colorString.to.hex(r, g, b, a); + } + } else { + this.value = pickerValue; + } + + this.onFieldChange.emit(this.value); + } + + onTextInputChange() { + this.onFieldChange.emit(this.value); + } +} diff --git a/frontend/src/app/components/ui-components/record-edit-fields/country/country.component.css b/frontend/src/app/components/ui-components/record-edit-fields/country/country.component.css index 4a8991d98..cf6260673 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/country/country.component.css +++ b/frontend/src/app/components/ui-components/record-edit-fields/country/country.component.css @@ -1,42 +1,42 @@ .country-form-field { - width: 100%; + width: 100%; } .country-input-container { - display: flex; - align-items: center; - width: 100%; + display: flex; + align-items: center; + width: 100%; } .country-flag-prefix { - font-size: 20px; - margin-right: 8px; - line-height: 1; + font-size: 20px; + margin-right: 8px; + line-height: 1; } .country-input { - flex: 1; - border: none !important; - outline: none !important; - background: transparent !important; + flex: 1; + border: none !important; + outline: none !important; + background: transparent !important; } .country-flag { - margin-right: 8px; - font-size: 16px; + margin-right: 8px; + font-size: 16px; } .country-name { - flex: 1; + flex: 1; } .country-code { - margin-left: 8px; - opacity: 0.7; - font-size: 12px; + margin-left: 8px; + opacity: 0.7; + font-size: 12px; } mat-option { - display: flex; - align-items: center; -} \ No newline at end of file + display: flex; + align-items: center; +} diff --git a/frontend/src/app/components/ui-components/record-edit-fields/date-time/date-time.component.css b/frontend/src/app/components/ui-components/record-edit-fields/date-time/date-time.component.css index 02ce234f5..f63bf4c46 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/date-time/date-time.component.css +++ b/frontend/src/app/components/ui-components/record-edit-fields/date-time/date-time.component.css @@ -1,5 +1,5 @@ .field-couple { - display: grid; - grid-template-columns: 1fr 1fr; - grid-column-gap: 16px; -} \ No newline at end of file + display: grid; + grid-template-columns: 1fr 1fr; + grid-column-gap: 16px; +} diff --git a/frontend/src/app/components/ui-components/record-edit-fields/date-time/date-time.component.ts b/frontend/src/app/components/ui-components/record-edit-fields/date-time/date-time.component.ts index 89ef49a2b..aa13b4e52 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/date-time/date-time.component.ts +++ b/frontend/src/app/components/ui-components/record-edit-fields/date-time/date-time.component.ts @@ -1,71 +1,63 @@ -import { Component, Input } from '@angular/core'; - -import { BaseEditFieldComponent } from '../base-row-field/base-row-field.component'; import { CommonModule } from '@angular/common'; -import { ConnectionsService } from 'src/app/services/connections.service'; -import { DBtype } from 'src/app/models/connection'; +import { Component, Input } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; -import { format } from 'date-fns' +import { format } from 'date-fns'; +import { DBtype } from 'src/app/models/connection'; +import { ConnectionsService } from 'src/app/services/connections.service'; +import { BaseEditFieldComponent } from '../base-row-field/base-row-field.component'; @Component({ - selector: 'app-edit-date-time', - templateUrl: './date-time.component.html', - styleUrls: ['./date-time.component.css'], - imports: [ - CommonModule, - FormsModule, - MatFormFieldModule, - MatInputModule - ] + selector: 'app-edit-date-time', + templateUrl: './date-time.component.html', + styleUrls: ['./date-time.component.css'], + imports: [CommonModule, FormsModule, MatFormFieldModule, MatInputModule], }) export class DateTimeEditComponent extends BaseEditFieldComponent { - @Input() value: string; - - static type = 'datetime'; - public date: string; - public time: string; - public connectionType: DBtype; - - constructor( - private _connections: ConnectionsService - ) { - super(); - } - - ngOnInit(): void { - super.ngOnInit(); - this.connectionType = this._connections.currentConnection.type; - - if (this.value) { - const datetime = new Date(this.value); - this.date = format(datetime, 'yyyy-MM-dd'); - this.time = format(datetime, 'HH:mm:ss'); - } - } - - onDateChange() { - if (!this.time) this.time = '00:00:00'; - - let datetime = ''; - if (this.connectionType === DBtype.MySQL) { - datetime = `${this.date} ${this.time}`; - } else { - datetime = `${this.date}T${this.time}Z`; - } - - this.onFieldChange.emit(datetime); - } - - onTimeChange() { - let datetime = ''; - if (this.connectionType === DBtype.MySQL) { - datetime = `${this.date} ${this.time}`; - } else { - datetime = `${this.date}T${this.time}Z`; - } - - this.onFieldChange.emit(datetime); - } + @Input() value: string; + + static type = 'datetime'; + public date: string; + public time: string; + public connectionType: DBtype; + + constructor(private _connections: ConnectionsService) { + super(); + } + + ngOnInit(): void { + super.ngOnInit(); + this.connectionType = this._connections.currentConnection.type; + + if (this.value) { + const datetime = new Date(this.value); + this.date = format(datetime, 'yyyy-MM-dd'); + this.time = format(datetime, 'HH:mm:ss'); + } + } + + onDateChange() { + if (!this.time) this.time = '00:00:00'; + + let datetime = ''; + if (this.connectionType === DBtype.MySQL) { + datetime = `${this.date} ${this.time}`; + } else { + datetime = `${this.date}T${this.time}Z`; + } + + this.onFieldChange.emit(datetime); + } + + onTimeChange() { + let datetime = ''; + if (this.connectionType === DBtype.MySQL) { + datetime = `${this.date} ${this.time}`; + } else { + datetime = `${this.date}T${this.time}Z`; + } + + this.onFieldChange.emit(datetime); + } } diff --git a/frontend/src/app/components/ui-components/record-edit-fields/date/date.component.css b/frontend/src/app/components/ui-components/record-edit-fields/date/date.component.css index b8f45cfd5..03fe2b8ac 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/date/date.component.css +++ b/frontend/src/app/components/ui-components/record-edit-fields/date/date.component.css @@ -1,3 +1,3 @@ .date-field { - width: 100%; -} \ No newline at end of file + width: 100%; +} diff --git a/frontend/src/app/components/ui-components/record-edit-fields/date/date.component.ts b/frontend/src/app/components/ui-components/record-edit-fields/date/date.component.ts index 9063dfda8..b9313b9a0 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/date/date.component.ts +++ b/frontend/src/app/components/ui-components/record-edit-fields/date/date.component.ts @@ -1,33 +1,32 @@ -import { Component, Input } from '@angular/core'; - -import { BaseEditFieldComponent } from '../base-row-field/base-row-field.component'; import { CommonModule } from '@angular/common'; +import { Component, Input } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; -import { format } from 'date-fns' +import { format } from 'date-fns'; +import { BaseEditFieldComponent } from '../base-row-field/base-row-field.component'; @Component({ - selector: 'app-edit-date', - templateUrl: './date.component.html', - styleUrls: ['./date.component.css'], - imports: [CommonModule, MatFormFieldModule, MatInputModule, FormsModule] + selector: 'app-edit-date', + templateUrl: './date.component.html', + styleUrls: ['./date.component.css'], + imports: [CommonModule, MatFormFieldModule, MatInputModule, FormsModule], }) export class DateEditComponent extends BaseEditFieldComponent { - @Input() value: string; + @Input() value: string; - static type = 'datetime'; - public date: string; + static type = 'datetime'; + public date: string; - ngOnInit(): void { - super.ngOnInit(); - if (this.value) { - const datetime = new Date(this.value); - this.date = format(datetime, 'yyyy-MM-dd'); - } - } + ngOnInit(): void { + super.ngOnInit(); + if (this.value) { + const datetime = new Date(this.value); + this.date = format(datetime, 'yyyy-MM-dd'); + } + } - onDateChange() { - this.onFieldChange.emit(this.date); - } + onDateChange() { + this.onFieldChange.emit(this.date); + } } diff --git a/frontend/src/app/components/ui-components/record-edit-fields/file/file.component.css b/frontend/src/app/components/ui-components/record-edit-fields/file/file.component.css index 982f09e35..af9a259e7 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/file/file.component.css +++ b/frontend/src/app/components/ui-components/record-edit-fields/file/file.component.css @@ -1,45 +1,45 @@ .file { - margin-bottom: 24px; + margin-bottom: 24px; } .header { - display: flex; - align-items: center; - margin-bottom: 20px; + display: flex; + align-items: center; + margin-bottom: 20px; } .file-type { - margin-right: 20px; + margin-right: 20px; } .file-raw-data { - width: 100%; + width: 100%; } .file-input { - display: none; + display: none; } .file-uploader-appearance { - margin-bottom: 20px; + margin-bottom: 20px; } .file-label { - margin-right: 20px; + margin-right: 20px; } .file-name { - color: rgba(0, 0, 0, 0.5); - font-style: italic; - margin-right: 20px; + color: rgba(0, 0, 0, 0.5); + font-style: italic; + margin-right: 20px; } @media (prefers-color-scheme: dark) { - .file-name { - color: rgba(255, 255, 255, 0.5); - } + .file-name { + color: rgba(255, 255, 255, 0.5); + } } .upload-btn { - margin-right: 12px; -} \ No newline at end of file + margin-right: 12px; +} diff --git a/frontend/src/app/components/ui-components/record-edit-fields/file/file.component.spec.ts b/frontend/src/app/components/ui-components/record-edit-fields/file/file.component.spec.ts index 24ce58af9..7371de04c 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/file/file.component.spec.ts +++ b/frontend/src/app/components/ui-components/record-edit-fields/file/file.component.spec.ts @@ -1,32 +1,26 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { FileEditComponent } from './file.component'; import { FormsModule } from '@angular/forms'; import { MatRadioModule } from '@angular/material/radio'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { FileEditComponent } from './file.component'; describe('FileEditComponent', () => { - let component: FileEditComponent; - let fixture: ComponentFixture; + let component: FileEditComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [ - FormsModule, - MatRadioModule, - FileEditComponent, - BrowserAnimationsModule - ] - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [FormsModule, MatRadioModule, FileEditComponent, BrowserAnimationsModule], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(FileEditComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(FileEditComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/ui-components/record-edit-fields/file/file.component.ts b/frontend/src/app/components/ui-components/record-edit-fields/file/file.component.ts index 2298534a5..e3d7a30e6 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/file/file.component.ts +++ b/frontend/src/app/components/ui-components/record-edit-fields/file/file.component.ts @@ -1,160 +1,162 @@ +import { NgIf } from '@angular/common'; import { Component, Input } from '@angular/core'; -import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; - -import { Base64ValidationDirective } from 'src/app/directives/base64Validator.directive'; -import { BaseEditFieldComponent } from '../base-row-field/base-row-field.component'; import { FormsModule } from '@angular/forms'; -import { HexValidationDirective } from 'src/app/directives/hexValidator.directive'; import { MatButtonModule } from '@angular/material/button'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; import { MatRadioModule } from '@angular/material/radio'; -import { NgIf } from '@angular/common'; +import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; +import { Base64ValidationDirective } from 'src/app/directives/base64Validator.directive'; +import { HexValidationDirective } from 'src/app/directives/hexValidator.directive'; import { base64Validation } from 'src/app/validators/base64.validator'; import { hexValidation } from 'src/app/validators/hex.validator'; +import { BaseEditFieldComponent } from '../base-row-field/base-row-field.component'; interface Blob { - type: string, - data: [] + type: string; + data: []; } enum FileType { - Hex = 'hex', - Base64 = 'base64', - File = 'file', + Hex = 'hex', + Base64 = 'base64', + File = 'file', } @Component({ - selector: 'app-edit-file', - templateUrl: './file.component.html', - styleUrls: ['./file.component.css'], - imports: [ - NgIf, - FormsModule, - MatFormFieldModule, - MatInputModule, - MatRadioModule, - MatButtonModule, - HexValidationDirective, - Base64ValidationDirective - ] + selector: 'app-edit-file', + templateUrl: './file.component.html', + styleUrls: ['./file.component.css'], + imports: [ + NgIf, + FormsModule, + MatFormFieldModule, + MatInputModule, + MatRadioModule, + MatButtonModule, + HexValidationDirective, + Base64ValidationDirective, + ], }) export class FileEditComponent extends BaseEditFieldComponent { - @Input() value: Blob; - - static type = 'file'; - public isNotSwitcherActive; - public fileType: FileType = FileType.Hex; - public hexData; - public base64Data; - public fileData; - public fileURL: SafeUrl; - public initError: string | null = null; - - constructor( - private sanitazer: DomSanitizer - ) { - super(); - } - - ngOnInit(): void { - super.ngOnInit(); - if (this.widgetStructure && this.value) { - this.fileType = this.widgetStructure.widget_params.type; - - if (this.fileType === 'hex') { - this.hexData = this.value; - //@ts-expect-error - this.initError = hexValidation()({value: this.hexData}); - this.initError = 'Invalid hex format.'; - }; - - if (this.fileType === 'base64') { - this.base64Data = this.value; - //@ts-expect-error - this.initError = base64Validation()({value: this.hexData}); - this.initError = 'Invalid base64 format.'; - }; - - if (this.fileType === 'file') { - //@ts-expect-error - const blob = new Blob([this.value]); - this.fileURL = this.sanitazer.bypassSecurityTrustUrl(URL.createObjectURL(blob)); - }; - }; - - if (this.value) { - this.hexData = this.value; - } - } - - convertValue(type: FileType) { - if (this.hexData && type === FileType.Base64) { - this.fromHexToBase64(); - } - - if (this.hexData && type === FileType.File) { - this.fromHexToFile(); - } - } - - onHexChange() { - //@ts-expect-error - this.isNotSwitcherActive = hexValidation()({value: this.hexData}); - this.onFieldChange.emit(this.hexData); - } - - onBase64Change() { - this.fromBase64ToHex(); - this.onFieldChange.emit(this.hexData); - } - - onFileSelected(event) { - let reader = new FileReader(); - const file:File = event.target.files[0]; - - reader.addEventListener("load", () => { - this.fromFileToHex(reader); - this.onFieldChange.emit(this.hexData); - }, false); - - reader.readAsArrayBuffer(file); - } - - fromHexToBase64() { - this.base64Data = btoa(this.hexData.match(/\w{2}/g) - .map(a => String.fromCharCode(parseInt(a, 16))) - .join("")); - } - - fromBase64ToHex() { - try { - const raw = atob(this.base64Data); - let result = ''; - for (let i = 0; i < raw.length; i++) { - const hex = raw.charCodeAt(i).toString(16); - result += (hex.length === 2 ? hex : '0' + hex); - }; - this.hexData = result; - this.isNotSwitcherActive = false; - } catch(_e) { - this.isNotSwitcherActive = true; - } - } - - fromHexToFile() { - const fileData = new Uint8Array(this.hexData.match(/[\da-f]{2}/gi).map(h => parseInt(h, 16))); - fileData.buffer; - const blob = new Blob([fileData]); - this.fileURL = this.sanitazer.bypassSecurityTrustUrl(URL.createObjectURL(blob)); - } - - fromFileToHex(reader: FileReader) { - let dataString = reader.result as ArrayBuffer; - // let dataStringArray = new Array(dataString.byteLength); - - this.hexData = [...new Uint8Array(dataString)] - .map(b => b.toString(16).padStart (2, "0")) - .join(""); - } + @Input() value: Blob; + + static type = 'file'; + public isNotSwitcherActive; + public fileType: FileType = FileType.Hex; + public hexData; + public base64Data; + public fileData; + public fileURL: SafeUrl; + public initError: string | null = null; + + constructor(private sanitazer: DomSanitizer) { + super(); + } + + ngOnInit(): void { + super.ngOnInit(); + if (this.widgetStructure && this.value) { + this.fileType = this.widgetStructure.widget_params.type; + + if (this.fileType === 'hex') { + this.hexData = this.value; + //@ts-expect-error + this.initError = hexValidation()({ value: this.hexData }); + this.initError = 'Invalid hex format.'; + } + + if (this.fileType === 'base64') { + this.base64Data = this.value; + //@ts-expect-error + this.initError = base64Validation()({ value: this.hexData }); + this.initError = 'Invalid base64 format.'; + } + + if (this.fileType === 'file') { + //@ts-expect-error + const blob = new Blob([this.value]); + this.fileURL = this.sanitazer.bypassSecurityTrustUrl(URL.createObjectURL(blob)); + } + } + + if (this.value) { + this.hexData = this.value; + } + } + + convertValue(type: FileType) { + if (this.hexData && type === FileType.Base64) { + this.fromHexToBase64(); + } + + if (this.hexData && type === FileType.File) { + this.fromHexToFile(); + } + } + + onHexChange() { + //@ts-expect-error + this.isNotSwitcherActive = hexValidation()({ value: this.hexData }); + this.onFieldChange.emit(this.hexData); + } + + onBase64Change() { + this.fromBase64ToHex(); + this.onFieldChange.emit(this.hexData); + } + + onFileSelected(event) { + let reader = new FileReader(); + const file: File = event.target.files[0]; + + reader.addEventListener( + 'load', + () => { + this.fromFileToHex(reader); + this.onFieldChange.emit(this.hexData); + }, + false, + ); + + reader.readAsArrayBuffer(file); + } + + fromHexToBase64() { + this.base64Data = btoa( + this.hexData + .match(/\w{2}/g) + .map((a) => String.fromCharCode(parseInt(a, 16))) + .join(''), + ); + } + + fromBase64ToHex() { + try { + const raw = atob(this.base64Data); + let result = ''; + for (let i = 0; i < raw.length; i++) { + const hex = raw.charCodeAt(i).toString(16); + result += hex.length === 2 ? hex : '0' + hex; + } + this.hexData = result; + this.isNotSwitcherActive = false; + } catch (_e) { + this.isNotSwitcherActive = true; + } + } + + fromHexToFile() { + const fileData = new Uint8Array(this.hexData.match(/[\da-f]{2}/gi).map((h) => parseInt(h, 16))); + fileData.buffer; + const blob = new Blob([fileData]); + this.fileURL = this.sanitazer.bypassSecurityTrustUrl(URL.createObjectURL(blob)); + } + + fromFileToHex(reader: FileReader) { + let dataString = reader.result as ArrayBuffer; + // let dataStringArray = new Array(dataString.byteLength); + + this.hexData = [...new Uint8Array(dataString)].map((b) => b.toString(16).padStart(2, '0')).join(''); + } } diff --git a/frontend/src/app/components/ui-components/record-edit-fields/foreign-key/foreign-key.component.css b/frontend/src/app/components/ui-components/record-edit-fields/foreign-key/foreign-key.component.css index af4b5f51d..c015b0116 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/foreign-key/foreign-key.component.css +++ b/frontend/src/app/components/ui-components/record-edit-fields/foreign-key/foreign-key.component.css @@ -1,55 +1,54 @@ .full-width { - position: relative; - width: 100%; + position: relative; + width: 100%; } .loader { - position: absolute; - top: 12px; - right: 12px; + position: absolute; + top: 12px; + right: 12px; } .disabled { - pointer-events: none; + pointer-events: none; } .foreign-key { - display: grid; - grid-template-columns: 1fr 36px; + display: grid; + grid-template-columns: 1fr 36px; } .foreign-key ::ng-deep .mat-mdc-form-field-subscript-wrapper { - margin-bottom: 16px; + margin-bottom: 16px; } .foreign-key ::ng-deep .mat-mdc-form-field-hint-wrapper { - padding-right: 0; - width: calc(100% + 8px); + padding-right: 0; + width: calc(100% + 8px); } @media screen and (width <= 600px) { - .foreign-key ::ng-deep .mat-mdc-form-field-subscript-wrapper{ - margin-bottom: 24px; - } - - .foreign-key ::ng-deep .mat-mdc-form-field-bottom-align::before { - margin-left: 0; - } + .foreign-key ::ng-deep .mat-mdc-form-field-subscript-wrapper { + margin-bottom: 24px; + } + .foreign-key ::ng-deep .mat-mdc-form-field-bottom-align::before { + margin-left: 0; + } } .hint-link { - color: var(--color-accentedPalette-500); + color: var(--color-accentedPalette-500); } .foreign-key__link { - display: flex; - /* align-items: center; */ - justify-content: flex-end; - color: inherit; + display: flex; + /* align-items: center; */ + justify-content: flex-end; + color: inherit; } .foreign-key__link-icon { - margin-left: auto; - margin-top: 12px; -} \ No newline at end of file + margin-left: auto; + margin-top: 12px; +} diff --git a/frontend/src/app/components/ui-components/record-edit-fields/id/id.component.css b/frontend/src/app/components/ui-components/record-edit-fields/id/id.component.css index ca0ef768e..bffe35774 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/id/id.component.css +++ b/frontend/src/app/components/ui-components/record-edit-fields/id/id.component.css @@ -1,3 +1,3 @@ .id-form-field { - width: 100%; -} \ No newline at end of file + width: 100%; +} diff --git a/frontend/src/app/components/ui-components/record-edit-fields/id/id.component.spec.ts b/frontend/src/app/components/ui-components/record-edit-fields/id/id.component.spec.ts index ee562aa5d..cbc9a4a9b 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/id/id.component.spec.ts +++ b/frontend/src/app/components/ui-components/record-edit-fields/id/id.component.spec.ts @@ -1,26 +1,26 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { FormsModule } from '@angular/forms'; -import { IdEditComponent } from './id.component'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { IdEditComponent } from './id.component'; describe('IdEditComponent', () => { - let component: IdEditComponent; - let fixture: ComponentFixture; + let component: IdEditComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [FormsModule, IdEditComponent, BrowserAnimationsModule] - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [FormsModule, IdEditComponent, BrowserAnimationsModule], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(IdEditComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(IdEditComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/ui-components/record-edit-fields/image/image.component.css b/frontend/src/app/components/ui-components/record-edit-fields/image/image.component.css index 607725a8b..2a3feec64 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/image/image.component.css +++ b/frontend/src/app/components/ui-components/record-edit-fields/image/image.component.css @@ -1,11 +1,11 @@ .image-box__url { - width: 100%; + width: 100%; } .image-box__image { - /* width: 100%; */ - height: auto; - max-height: 200px; - margin-top: 4px; - margin-bottom: 20px; -} \ No newline at end of file + /* width: 100%; */ + height: auto; + max-height: 200px; + margin-top: 4px; + margin-bottom: 20px; +} diff --git a/frontend/src/app/components/ui-components/record-edit-fields/image/image.component.spec.ts b/frontend/src/app/components/ui-components/record-edit-fields/image/image.component.spec.ts index ce1331767..f07eebb22 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/image/image.component.spec.ts +++ b/frontend/src/app/components/ui-components/record-edit-fields/image/image.component.spec.ts @@ -1,27 +1,23 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { ImageEditComponent } from './image.component'; import { FormsModule } from '@angular/forms'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { ImageEditComponent } from './image.component'; describe('ImageComponent', () => { - let component: ImageEditComponent; - let fixture: ComponentFixture; + let component: ImageEditComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [ - FormsModule, - ImageEditComponent, - BrowserAnimationsModule - ]}).compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [FormsModule, ImageEditComponent, BrowserAnimationsModule], + }).compileComponents(); - fixture = TestBed.createComponent(ImageEditComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + fixture = TestBed.createComponent(ImageEditComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/ui-components/record-edit-fields/image/image.component.ts b/frontend/src/app/components/ui-components/record-edit-fields/image/image.component.ts index 0b78c5220..7b544baea 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/image/image.component.ts +++ b/frontend/src/app/components/ui-components/record-edit-fields/image/image.component.ts @@ -1,55 +1,49 @@ -import { Component, Input, OnInit } from '@angular/core'; - -import { BaseEditFieldComponent } from '../base-row-field/base-row-field.component'; import { CommonModule } from '@angular/common'; +import { Component, Input, OnInit } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; import { UrlValidatorDirective } from 'src/app/directives/url-validator.directive'; +import { BaseEditFieldComponent } from '../base-row-field/base-row-field.component'; @Component({ - selector: 'app-edit-image', - templateUrl: './image.component.html', - styleUrl: './image.component.css', - imports: [ - CommonModule, - FormsModule, - MatFormFieldModule, - MatInputModule, - UrlValidatorDirective - ] + selector: 'app-edit-image', + templateUrl: './image.component.html', + styleUrl: './image.component.css', + imports: [CommonModule, FormsModule, MatFormFieldModule, MatInputModule, UrlValidatorDirective], }) export class ImageEditComponent extends BaseEditFieldComponent implements OnInit { - @Input() value: string; - public prefix: string = ''; + @Input() value: string; + public prefix: string = ''; + + ngOnInit(): void { + super.ngOnInit(); + this._parseWidgetParams(); + } - ngOnInit(): void { - super.ngOnInit(); - this._parseWidgetParams(); - } + ngOnChanges(): void { + this._parseWidgetParams(); + } - ngOnChanges(): void { - this._parseWidgetParams(); - } + private _parseWidgetParams(): void { + if (this.widgetStructure?.widget_params) { + try { + const params = + typeof this.widgetStructure.widget_params === 'string' + ? JSON.parse(this.widgetStructure.widget_params) + : this.widgetStructure.widget_params; - private _parseWidgetParams(): void { - if (this.widgetStructure?.widget_params) { - try { - const params = typeof this.widgetStructure.widget_params === 'string' - ? JSON.parse(this.widgetStructure.widget_params) - : this.widgetStructure.widget_params; - - if (params.prefix !== undefined) { - this.prefix = params.prefix || ''; - } - } catch (e) { - console.error('Error parsing Image widget params:', e); - } - } - } + if (params.prefix !== undefined) { + this.prefix = params.prefix || ''; + } + } catch (e) { + console.error('Error parsing Image widget params:', e); + } + } + } - get imageUrl(): string { - if (!this.value) return ''; - return this.prefix + this.value; - } + get imageUrl(): string { + if (!this.value) return ''; + return this.prefix + this.value; + } } diff --git a/frontend/src/app/components/ui-components/record-edit-fields/json-editor/json-editor.component.css b/frontend/src/app/components/ui-components/record-edit-fields/json-editor/json-editor.component.css index 876a91d2e..05e90e456 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/json-editor/json-editor.component.css +++ b/frontend/src/app/components/ui-components/record-edit-fields/json-editor/json-editor.component.css @@ -1,16 +1,16 @@ .code-editor-box { - display: block; - border: 1px solid rgba(0, 0, 0, 0.38); - border-radius: 4px; - margin-top: 4px; - margin-bottom: 20px; - /* overflow-y: auto; + display: block; + border: 1px solid rgba(0, 0, 0, 0.38); + border-radius: 4px; + margin-top: 4px; + margin-bottom: 20px; + /* overflow-y: auto; resize: vertical; */ - overflow: auto; - resize: both; + overflow: auto; + resize: both; } .code-editor-box ::ng-deep .ngs-code-editor { - width: 100%; - height: 100%; -} \ No newline at end of file + width: 100%; + height: 100%; +} diff --git a/frontend/src/app/components/ui-components/record-edit-fields/json-editor/json-editor.component.spec.ts b/frontend/src/app/components/ui-components/record-edit-fields/json-editor/json-editor.component.spec.ts index 794f05af5..a8970dd7a 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/json-editor/json-editor.component.spec.ts +++ b/frontend/src/app/components/ui-components/record-edit-fields/json-editor/json-editor.component.spec.ts @@ -2,8 +2,8 @@ import { NO_ERRORS_SCHEMA } from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { CodeEditorModule } from '@ngstack/code-editor'; -import { MockCodeEditorComponent } from 'src/app/testing/code-editor.mock'; import { UiSettingsService } from 'src/app/services/ui-settings.service'; +import { MockCodeEditorComponent } from 'src/app/testing/code-editor.mock'; import { JsonEditorEditComponent } from './json-editor.component'; describe('JsonEditorEditComponent', () => { diff --git a/frontend/src/app/components/ui-components/record-edit-fields/json-editor/json-editor.component.ts b/frontend/src/app/components/ui-components/record-edit-fields/json-editor/json-editor.component.ts index 04922a102..c1c1e9caf 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/json-editor/json-editor.component.ts +++ b/frontend/src/app/components/ui-components/record-edit-fields/json-editor/json-editor.component.ts @@ -1,42 +1,39 @@ +import { CommonModule } from '@angular/common'; import { Component, Input } from '@angular/core'; - -import { BaseEditFieldComponent } from '../base-row-field/base-row-field.component'; import { CodeEditorModule } from '@ngstack/code-editor'; -import { CommonModule } from '@angular/common'; import { UiSettingsService } from 'src/app/services/ui-settings.service'; +import { BaseEditFieldComponent } from '../base-row-field/base-row-field.component'; @Component({ - selector: 'app-edit-json-editor', - templateUrl: './json-editor.component.html', - styleUrls: ['./json-editor.component.css'], - imports: [CommonModule, CodeEditorModule], + selector: 'app-edit-json-editor', + templateUrl: './json-editor.component.html', + styleUrls: ['./json-editor.component.css'], + imports: [CommonModule, CodeEditorModule], }) export class JsonEditorEditComponent extends BaseEditFieldComponent { - @Input() value: Object; + @Input() value: Object; - public mutableCodeModel: Object; - public codeEditorOptions = { - minimap: { enabled: false }, - automaticLayout: true, - scrollBeyondLastLine: false, - wordWrap: 'on', - }; - public codeEditorTheme = 'vs-dark'; + public mutableCodeModel: Object; + public codeEditorOptions = { + minimap: { enabled: false }, + automaticLayout: true, + scrollBeyondLastLine: false, + wordWrap: 'on', + }; + public codeEditorTheme = 'vs-dark'; - constructor( - private _uiSettings: UiSettingsService, - ) { - super(); - } + constructor(private _uiSettings: UiSettingsService) { + super(); + } - ngOnInit(): void { - super.ngOnInit(); - this.mutableCodeModel = { - language: 'json', - uri: `${this.label}.json`, - value: JSON.stringify(this.value, undefined, 4) || '{}' - } + ngOnInit(): void { + super.ngOnInit(); + this.mutableCodeModel = { + language: 'json', + uri: `${this.label}.json`, + value: JSON.stringify(this.value, undefined, 4) || '{}', + }; - this.codeEditorTheme = this._uiSettings.isDarkMode ? 'vs-dark' : 'vs'; - } + this.codeEditorTheme = this._uiSettings.isDarkMode ? 'vs-dark' : 'vs'; + } } diff --git a/frontend/src/app/components/ui-components/record-edit-fields/language/language.component.css b/frontend/src/app/components/ui-components/record-edit-fields/language/language.component.css index d37c35d34..60a6679e7 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/language/language.component.css +++ b/frontend/src/app/components/ui-components/record-edit-fields/language/language.component.css @@ -1,49 +1,49 @@ .language-form-field { - width: 100%; + width: 100%; } .language-input-container { - display: flex; - align-items: center; - width: 100%; + display: flex; + align-items: center; + width: 100%; } .language-flag-prefix { - font-size: 20px; - margin-right: 8px; - line-height: 1; + font-size: 20px; + margin-right: 8px; + line-height: 1; } .language-input { - flex: 1; - border: none !important; - outline: none !important; - background: transparent !important; + flex: 1; + border: none !important; + outline: none !important; + background: transparent !important; } .language-flag { - margin-right: 8px; - font-size: 16px; + margin-right: 8px; + font-size: 16px; } .language-name { - flex: 1; + flex: 1; } .language-native { - margin-left: 8px; - opacity: 0.8; - font-size: 13px; - font-style: italic; + margin-left: 8px; + opacity: 0.8; + font-size: 13px; + font-style: italic; } .language-code { - margin-left: 8px; - opacity: 0.7; - font-size: 12px; + margin-left: 8px; + opacity: 0.7; + font-size: 12px; } mat-option { - display: flex; - align-items: center; + display: flex; + align-items: center; } diff --git a/frontend/src/app/components/ui-components/record-edit-fields/long-text/long-text.component.css b/frontend/src/app/components/ui-components/record-edit-fields/long-text/long-text.component.css index c07e4f255..e390d1047 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/long-text/long-text.component.css +++ b/frontend/src/app/components/ui-components/record-edit-fields/long-text/long-text.component.css @@ -1,16 +1,16 @@ .form-field { - position: relative; - width: 100%; + position: relative; + width: 100%; } .long-textarea { - margin-bottom: 20px; + margin-bottom: 20px; } .counter { - position: absolute; - bottom: 8px; - font-size: 12px; - width: 90%; - background: #fff; -} \ No newline at end of file + position: absolute; + bottom: 8px; + font-size: 12px; + width: 90%; + background: #fff; +} diff --git a/frontend/src/app/components/ui-components/record-edit-fields/long-text/long-text.component.spec.ts b/frontend/src/app/components/ui-components/record-edit-fields/long-text/long-text.component.spec.ts index 2247db972..fa17154ce 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/long-text/long-text.component.spec.ts +++ b/frontend/src/app/components/ui-components/record-edit-fields/long-text/long-text.component.spec.ts @@ -1,25 +1,24 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { LongTextEditComponent } from './long-text.component'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { LongTextEditComponent } from './long-text.component'; describe('LongTextEditComponent', () => { - let component: LongTextEditComponent; - let fixture: ComponentFixture; + let component: LongTextEditComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [LongTextEditComponent, BrowserAnimationsModule] - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [LongTextEditComponent, BrowserAnimationsModule], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(LongTextEditComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(LongTextEditComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/ui-components/record-edit-fields/long-text/long-text.component.ts b/frontend/src/app/components/ui-components/record-edit-fields/long-text/long-text.component.ts index 0bebc14a1..de4aac234 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/long-text/long-text.component.ts +++ b/frontend/src/app/components/ui-components/record-edit-fields/long-text/long-text.component.ts @@ -1,78 +1,78 @@ -import { Component, Input, OnInit } from '@angular/core'; - -import { BaseEditFieldComponent } from '../base-row-field/base-row-field.component'; import { CommonModule } from '@angular/common'; +import { Component, Input, OnInit } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; import { TextValidatorDirective } from 'src/app/directives/text-validator.directive'; +import { BaseEditFieldComponent } from '../base-row-field/base-row-field.component'; @Component({ - selector: 'app-edit-long-text', - templateUrl: './long-text.component.html', - styleUrls: ['./long-text.component.css'], - imports: [CommonModule, MatFormFieldModule, MatInputModule, FormsModule, TextValidatorDirective] + selector: 'app-edit-long-text', + templateUrl: './long-text.component.html', + styleUrls: ['./long-text.component.css'], + imports: [CommonModule, MatFormFieldModule, MatInputModule, FormsModule, TextValidatorDirective], }) export class LongTextEditComponent extends BaseEditFieldComponent implements OnInit { - @Input() value: string; + @Input() value: string; + + static type = 'text'; + public rowsCount: string; + maxLength: number | null = null; + validateType: string | null = null; + regexPattern: string | null = null; + + override ngOnInit(): void { + super.ngOnInit(); - static type = 'text'; - public rowsCount: string; - maxLength: number | null = null; - validateType: string | null = null; - regexPattern: string | null = null; + // Use character_maximum_length from the field structure if available + if (this.structure?.character_maximum_length) { + this.maxLength = this.structure.character_maximum_length; + } - override ngOnInit(): void { - super.ngOnInit(); - - // Use character_maximum_length from the field structure if available - if (this.structure?.character_maximum_length) { - this.maxLength = this.structure.character_maximum_length; - } + // Parse widget parameters + if (this.widgetStructure?.widget_params) { + const params = + typeof this.widgetStructure.widget_params === 'string' + ? JSON.parse(this.widgetStructure.widget_params) + : this.widgetStructure.widget_params; - // Parse widget parameters - if (this.widgetStructure?.widget_params) { - const params = typeof this.widgetStructure.widget_params === 'string' - ? JSON.parse(this.widgetStructure.widget_params) - : this.widgetStructure.widget_params; - - this.rowsCount = params.rows || '4'; - this.validateType = params.validate || null; - this.regexPattern = params.regex || null; - } else { - this.rowsCount = '4'; - } - } + this.rowsCount = params.rows || '4'; + this.validateType = params.validate || null; + this.regexPattern = params.regex || null; + } else { + this.rowsCount = '4'; + } + } - getValidationErrorMessage(): string { - if (!this.validateType) { - return ''; - } + getValidationErrorMessage(): string { + if (!this.validateType) { + return ''; + } - if (this.validateType === 'regex') { - return 'Value doesn\'t match the required pattern'; - } + if (this.validateType === 'regex') { + return "Value doesn't match the required pattern"; + } - // Create user-friendly messages for common validators - const messages = { - isEmail: 'Invalid email address', - isURL: 'Invalid URL', - isIP: 'Invalid IP address', - isUUID: 'Invalid UUID', - isJSON: 'Invalid JSON', - isCreditCard: 'Invalid credit card number', - isISBN: 'Invalid ISBN', - isAlpha: 'Should contain only letters', - isNumeric: 'Should contain only numbers', - isAlphanumeric: 'Should contain only letters and numbers', - isHexColor: 'Invalid hex color', - isBase64: 'Invalid Base64 string', - isMobilePhone: 'Invalid mobile phone number', - isMACAddress: 'Invalid MAC address', - isPostalCode: 'Invalid postal code', - isCurrency: 'Invalid currency format' - }; + // Create user-friendly messages for common validators + const messages = { + isEmail: 'Invalid email address', + isURL: 'Invalid URL', + isIP: 'Invalid IP address', + isUUID: 'Invalid UUID', + isJSON: 'Invalid JSON', + isCreditCard: 'Invalid credit card number', + isISBN: 'Invalid ISBN', + isAlpha: 'Should contain only letters', + isNumeric: 'Should contain only numbers', + isAlphanumeric: 'Should contain only letters and numbers', + isHexColor: 'Invalid hex color', + isBase64: 'Invalid Base64 string', + isMobilePhone: 'Invalid mobile phone number', + isMACAddress: 'Invalid MAC address', + isPostalCode: 'Invalid postal code', + isCurrency: 'Invalid currency format', + }; - return messages[this.validateType] || `Invalid ${this.validateType}`; - } + return messages[this.validateType] || `Invalid ${this.validateType}`; + } } diff --git a/frontend/src/app/components/ui-components/record-edit-fields/markdown/markdown.component.css b/frontend/src/app/components/ui-components/record-edit-fields/markdown/markdown.component.css index 7e6b36f97..90c359617 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/markdown/markdown.component.css +++ b/frontend/src/app/components/ui-components/record-edit-fields/markdown/markdown.component.css @@ -1,17 +1,17 @@ .code-editor-box { - min-height: 200px; - border-radius: 4px; - overflow: hidden; + min-height: 200px; + border-radius: 4px; + overflow: hidden; } @media (prefers-color-scheme: light) { - .code-editor-box { - border: 1px solid #ccc; - } + .code-editor-box { + border: 1px solid #ccc; + } } @media (prefers-color-scheme: dark) { - .code-editor-box { - border: 1px solid #404040; - } + .code-editor-box { + border: 1px solid #404040; + } } diff --git a/frontend/src/app/components/ui-components/record-edit-fields/markdown/markdown.component.ts b/frontend/src/app/components/ui-components/record-edit-fields/markdown/markdown.component.ts index 65a6fc722..1e78e7df8 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/markdown/markdown.component.ts +++ b/frontend/src/app/components/ui-components/record-edit-fields/markdown/markdown.component.ts @@ -1,42 +1,39 @@ +import { CommonModule } from '@angular/common'; import { Component, Input } from '@angular/core'; - -import { BaseEditFieldComponent } from '../base-row-field/base-row-field.component'; import { CodeEditorModule } from '@ngstack/code-editor'; -import { CommonModule } from '@angular/common'; import { UiSettingsService } from 'src/app/services/ui-settings.service'; +import { BaseEditFieldComponent } from '../base-row-field/base-row-field.component'; @Component({ - selector: 'app-edit-markdown', - templateUrl: './markdown.component.html', - styleUrl: './markdown.component.css', - imports: [CommonModule, CodeEditorModule], + selector: 'app-edit-markdown', + templateUrl: './markdown.component.html', + styleUrl: './markdown.component.css', + imports: [CommonModule, CodeEditorModule], }) export class MarkdownEditComponent extends BaseEditFieldComponent { - @Input() value; + @Input() value; - public mutableCodeModel: Object; - public codeEditorOptions = { - minimap: { enabled: false }, - automaticLayout: true, - scrollBeyondLastLine: false, - wordWrap: 'on', - }; - public codeEditorTheme = 'vs-dark'; + public mutableCodeModel: Object; + public codeEditorOptions = { + minimap: { enabled: false }, + automaticLayout: true, + scrollBeyondLastLine: false, + wordWrap: 'on', + }; + public codeEditorTheme = 'vs-dark'; - constructor( - private _uiSettings: UiSettingsService, - ) { - super(); - } + constructor(private _uiSettings: UiSettingsService) { + super(); + } - ngOnInit(): void { - super.ngOnInit(); - this.mutableCodeModel = { - language: 'markdown', - uri: `${this.label}.md`, - value: this.value - } + ngOnInit(): void { + super.ngOnInit(); + this.mutableCodeModel = { + language: 'markdown', + uri: `${this.label}.md`, + value: this.value, + }; - this.codeEditorTheme = this._uiSettings.isDarkMode ? 'vs-dark' : 'vs'; - } + this.codeEditorTheme = this._uiSettings.isDarkMode ? 'vs-dark' : 'vs'; + } } diff --git a/frontend/src/app/components/ui-components/record-edit-fields/money/money.component.css b/frontend/src/app/components/ui-components/record-edit-fields/money/money.component.css index fec1e2422..cafa4de54 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/money/money.component.css +++ b/frontend/src/app/components/ui-components/record-edit-fields/money/money.component.css @@ -1,76 +1,74 @@ .money-widget { - width: 100%; - margin-left: 0; - margin-right: 0; + width: 100%; + margin-left: 0; + margin-right: 0; } .money-input-container { - display: flex; - gap: 12px; - align-items: flex-start; + display: flex; + gap: 12px; + align-items: flex-start; } .money-selector { - min-width: 180px; - flex-shrink: 0; - margin-left: 16px; + min-width: 180px; + flex-shrink: 0; + margin-left: 16px; } .money-selector .mat-mdc-form-field { - margin-left: 0; + margin-left: 0; } .amount-input { - flex: 1; - min-width: 120px; + flex: 1; + min-width: 120px; } - - /* Responsive design */ @media (max-width: 768px) { - .money-input-container { - flex-direction: column; - gap: 8px; - } - - .money-selector { - width: 100%; - min-width: unset; - margin-left: 16px; - } + .money-input-container { + flex-direction: column; + gap: 8px; + } + + .money-selector { + width: 100%; + min-width: unset; + margin-left: 16px; + } } /* Ensure proper spacing with Material Design */ .mat-mdc-form-field { - margin-bottom: 0; - margin-left: 0; - margin-right: 0; + margin-bottom: 0; + margin-left: 0; + margin-right: 0; } .mat-mdc-form-field + .mat-mdc-form-field { - margin-left: 0; + margin-left: 0; } /* Prefix/suffix styling */ .mat-mdc-form-field-prefix, .mat-mdc-form-field-suffix { - font-weight: 500; - color: rgba(0, 0, 0, 0.87); + font-weight: 500; + color: rgba(0, 0, 0, 0.87); } [data-theme="dark"] .mat-mdc-form-field-prefix, [data-theme="dark"] .mat-mdc-form-field-suffix { - color: rgba(255, 255, 255, 0.87); + color: rgba(255, 255, 255, 0.87); } /* Specific styling for text prefix */ .mat-mdc-form-field-text-prefix { - font-weight: 500; - color: rgba(0, 0, 0, 0.87); - margin-right: 4px; + font-weight: 500; + color: rgba(0, 0, 0, 0.87); + margin-right: 4px; } [data-theme="dark"] .mat-mdc-form-field-text-prefix { - color: rgba(255, 255, 255, 0.87); -} \ No newline at end of file + color: rgba(255, 255, 255, 0.87); +} diff --git a/frontend/src/app/components/ui-components/record-edit-fields/money/money.component.ts b/frontend/src/app/components/ui-components/record-edit-fields/money/money.component.ts index 62dbe8722..91b626004 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/money/money.component.ts +++ b/frontend/src/app/components/ui-components/record-edit-fields/money/money.component.ts @@ -1,205 +1,204 @@ -import { CURRENCIES, Money, MoneyValue } from 'src/app/consts/currencies'; -import { Component, Input, OnInit } from '@angular/core'; - -import { BaseEditFieldComponent } from '../base-row-field/base-row-field.component'; import { CommonModule } from '@angular/common'; +import { Component, Input, OnInit } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; import { MatSelectModule } from '@angular/material/select'; +import { CURRENCIES, Money, MoneyValue } from 'src/app/consts/currencies'; +import { BaseEditFieldComponent } from '../base-row-field/base-row-field.component'; @Component({ - selector: 'app-edit-money', - templateUrl: './money.component.html', - styleUrls: ['./money.component.css'], - imports: [CommonModule, MatFormFieldModule, MatInputModule, MatSelectModule, FormsModule] + selector: 'app-edit-money', + templateUrl: './money.component.html', + styleUrls: ['./money.component.css'], + imports: [CommonModule, MatFormFieldModule, MatInputModule, MatSelectModule, FormsModule], }) export class MoneyEditComponent extends BaseEditFieldComponent implements OnInit { - @Input() value: string | number | MoneyValue = ''; - - static type = 'money'; - - defaultCurrency: string = 'USD'; - showCurrencySelector: boolean = false; - decimalPlaces: number = 2; - allowNegative: boolean = true; - - selectedCurrency: string = 'USD'; - amount: number | string = ''; - displayAmount: string = ''; - - currencies: Money[] = CURRENCIES; - - ngOnInit(): void { - super.ngOnInit(); - this.configureFromWidgetParams(); - this.initializeMoneyValue(); - } - - configureFromWidgetParams(): void { - if (this.widgetStructure?.widget_params) { - const params = this.widgetStructure.widget_params; - - if (typeof params.default_currency === 'string') { - this.defaultCurrency = params.default_currency; - } - - if (typeof params.show_currency_selector === 'boolean') { - this.showCurrencySelector = params.show_currency_selector; - } - - if (typeof params.decimal_places === 'number') { - this.decimalPlaces = Math.max(0, Math.min(10, params.decimal_places)); - } - - if (typeof params.allow_negative === 'boolean') { - this.allowNegative = params.allow_negative; - } - } - } - - private initializeMoneyValue(): void { - if (this.value) { - if (typeof this.value === 'string') { - this.parseStringValue(this.value); - } else if (typeof this.value === 'object' && this.value.amount !== undefined && this.value.currency) { - this.amount = this.value.amount; - this.selectedCurrency = this.value.currency; - this.displayAmount = this.formatAmount(this.amount); - } else if (typeof this.value === 'number') { - // Handle numeric values when currency selector is disabled - this.amount = this.value; - this.selectedCurrency = this.defaultCurrency; - this.displayAmount = this.formatAmount(this.amount); - } - } else { - this.selectedCurrency = this.defaultCurrency; - this.amount = ''; - this.displayAmount = ''; - } - } - - private parseStringValue(stringValue: string): void { - // Try to parse formats like "100.50 USD", "USD 100.50", "$100.50", "€100,50" - const currencyMatch = stringValue.match(/([A-Z]{3})/); - const numberMatch = stringValue.match(/([\d,.-]+)/); - - if (currencyMatch) { - this.selectedCurrency = currencyMatch[1]; - } else { - // Try to detect currency by symbol - const currency = this.currencies.find(c => stringValue.includes(c.symbol)); - if (currency) { - this.selectedCurrency = currency.code; - } else { - this.selectedCurrency = this.defaultCurrency; - } - } - - if (numberMatch) { - const cleanNumber = numberMatch[1].replace(/,/g, ''); - this.amount = parseFloat(cleanNumber) || ''; - this.displayAmount = this.formatAmount(this.amount); - } else { - this.amount = ''; - this.displayAmount = ''; - } - } - - onCurrencyChange(): void { - this.updateValue(); - } - - onAmountChange(): void { - // Clean and validate the input - let cleanValue = this.displayAmount.replace(/[^\d.-]/g, ''); - - // Handle negative values - if (!this.allowNegative) { - cleanValue = cleanValue.replace(/-/g, ''); - } - - // Parse the number - const numericValue = parseFloat(cleanValue); - - if (!Number.isNaN(numericValue)) { - this.amount = numericValue; - // Don't reformat while user is typing to preserve focus - if (this.displayAmount !== cleanValue) { - this.displayAmount = cleanValue; - } - } else if (cleanValue === '' || cleanValue === '-') { - this.amount = ''; - this.displayAmount = cleanValue; - } else { - // Invalid input, revert to previous value - this.displayAmount = this.formatAmount(this.amount); - } - - this.updateValue(); - } - - onAmountBlur(): void { - // Format the amount when user leaves the field - if (this.amount !== '' && this.amount !== null && this.amount !== undefined) { - this.displayAmount = this.formatAmount(this.amount); - } - } - - public formatAmount(amount: number | string): string { - if (amount === '' || amount === null || amount === undefined) { - return ''; - } - - const numericAmount = typeof amount === 'string' ? parseFloat(amount) : amount; - - if (Number.isNaN(numericAmount)) { - return ''; - } - - return numericAmount.toFixed(this.decimalPlaces); - } - - public updateValue(): void { - if (this.amount === '' || this.amount === null || this.amount === undefined) { - this.value = ''; - } else { - if (this.showCurrencySelector) { - // Store as object with amount and currency when selector is enabled - this.value = { - amount: this.amount, - currency: this.selectedCurrency - }; - } else { - // Store only the numeric amount when currency selector is disabled - this.value = typeof this.amount === 'string' ? parseFloat(this.amount) || 0 : this.amount; - } - } - - this.onFieldChange.emit(this.value); - } - - get selectedCurrencyData(): Money { - return this.currencies.find(c => c.code === this.selectedCurrency) || this.currencies[0]; - } - - get placeholder(): string { - const currency = this.selectedCurrencyData; - return `Enter amount in ${currency.name}`; - } - - get displayValue(): string { - if (!this.amount && this.amount !== 0) { - return ''; - } - - const currency = this.selectedCurrencyData; - const formattedAmount = this.formatAmount(this.amount); - - return `${currency.symbol}${formattedAmount}`; - } - - displayCurrencyFn(currency: Money): string { - return currency ? `${currency.flag || ''} ${currency.code} - ${currency.name}` : ''; - } + @Input() value: string | number | MoneyValue = ''; + + static type = 'money'; + + defaultCurrency: string = 'USD'; + showCurrencySelector: boolean = false; + decimalPlaces: number = 2; + allowNegative: boolean = true; + + selectedCurrency: string = 'USD'; + amount: number | string = ''; + displayAmount: string = ''; + + currencies: Money[] = CURRENCIES; + + ngOnInit(): void { + super.ngOnInit(); + this.configureFromWidgetParams(); + this.initializeMoneyValue(); + } + + configureFromWidgetParams(): void { + if (this.widgetStructure?.widget_params) { + const params = this.widgetStructure.widget_params; + + if (typeof params.default_currency === 'string') { + this.defaultCurrency = params.default_currency; + } + + if (typeof params.show_currency_selector === 'boolean') { + this.showCurrencySelector = params.show_currency_selector; + } + + if (typeof params.decimal_places === 'number') { + this.decimalPlaces = Math.max(0, Math.min(10, params.decimal_places)); + } + + if (typeof params.allow_negative === 'boolean') { + this.allowNegative = params.allow_negative; + } + } + } + + private initializeMoneyValue(): void { + if (this.value) { + if (typeof this.value === 'string') { + this.parseStringValue(this.value); + } else if (typeof this.value === 'object' && this.value.amount !== undefined && this.value.currency) { + this.amount = this.value.amount; + this.selectedCurrency = this.value.currency; + this.displayAmount = this.formatAmount(this.amount); + } else if (typeof this.value === 'number') { + // Handle numeric values when currency selector is disabled + this.amount = this.value; + this.selectedCurrency = this.defaultCurrency; + this.displayAmount = this.formatAmount(this.amount); + } + } else { + this.selectedCurrency = this.defaultCurrency; + this.amount = ''; + this.displayAmount = ''; + } + } + + private parseStringValue(stringValue: string): void { + // Try to parse formats like "100.50 USD", "USD 100.50", "$100.50", "€100,50" + const currencyMatch = stringValue.match(/([A-Z]{3})/); + const numberMatch = stringValue.match(/([\d,.-]+)/); + + if (currencyMatch) { + this.selectedCurrency = currencyMatch[1]; + } else { + // Try to detect currency by symbol + const currency = this.currencies.find((c) => stringValue.includes(c.symbol)); + if (currency) { + this.selectedCurrency = currency.code; + } else { + this.selectedCurrency = this.defaultCurrency; + } + } + + if (numberMatch) { + const cleanNumber = numberMatch[1].replace(/,/g, ''); + this.amount = parseFloat(cleanNumber) || ''; + this.displayAmount = this.formatAmount(this.amount); + } else { + this.amount = ''; + this.displayAmount = ''; + } + } + + onCurrencyChange(): void { + this.updateValue(); + } + + onAmountChange(): void { + // Clean and validate the input + let cleanValue = this.displayAmount.replace(/[^\d.-]/g, ''); + + // Handle negative values + if (!this.allowNegative) { + cleanValue = cleanValue.replace(/-/g, ''); + } + + // Parse the number + const numericValue = parseFloat(cleanValue); + + if (!Number.isNaN(numericValue)) { + this.amount = numericValue; + // Don't reformat while user is typing to preserve focus + if (this.displayAmount !== cleanValue) { + this.displayAmount = cleanValue; + } + } else if (cleanValue === '' || cleanValue === '-') { + this.amount = ''; + this.displayAmount = cleanValue; + } else { + // Invalid input, revert to previous value + this.displayAmount = this.formatAmount(this.amount); + } + + this.updateValue(); + } + + onAmountBlur(): void { + // Format the amount when user leaves the field + if (this.amount !== '' && this.amount !== null && this.amount !== undefined) { + this.displayAmount = this.formatAmount(this.amount); + } + } + + public formatAmount(amount: number | string): string { + if (amount === '' || amount === null || amount === undefined) { + return ''; + } + + const numericAmount = typeof amount === 'string' ? parseFloat(amount) : amount; + + if (Number.isNaN(numericAmount)) { + return ''; + } + + return numericAmount.toFixed(this.decimalPlaces); + } + + public updateValue(): void { + if (this.amount === '' || this.amount === null || this.amount === undefined) { + this.value = ''; + } else { + if (this.showCurrencySelector) { + // Store as object with amount and currency when selector is enabled + this.value = { + amount: this.amount, + currency: this.selectedCurrency, + }; + } else { + // Store only the numeric amount when currency selector is disabled + this.value = typeof this.amount === 'string' ? parseFloat(this.amount) || 0 : this.amount; + } + } + + this.onFieldChange.emit(this.value); + } + + get selectedCurrencyData(): Money { + return this.currencies.find((c) => c.code === this.selectedCurrency) || this.currencies[0]; + } + + get placeholder(): string { + const currency = this.selectedCurrencyData; + return `Enter amount in ${currency.name}`; + } + + get displayValue(): string { + if (!this.amount && this.amount !== 0) { + return ''; + } + + const currency = this.selectedCurrencyData; + const formattedAmount = this.formatAmount(this.amount); + + return `${currency.symbol}${formattedAmount}`; + } + + displayCurrencyFn(currency: Money): string { + return currency ? `${currency.flag || ''} ${currency.code} - ${currency.name}` : ''; + } } diff --git a/frontend/src/app/components/ui-components/record-edit-fields/number/number.component.css b/frontend/src/app/components/ui-components/record-edit-fields/number/number.component.css index 066c864e6..957c7d47c 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/number/number.component.css +++ b/frontend/src/app/components/ui-components/record-edit-fields/number/number.component.css @@ -1,3 +1,3 @@ .number-form-field { - width: 100%; -} \ No newline at end of file + width: 100%; +} diff --git a/frontend/src/app/components/ui-components/record-edit-fields/number/number.component.spec.ts b/frontend/src/app/components/ui-components/record-edit-fields/number/number.component.spec.ts index a2cfa5c03..f65c35168 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/number/number.component.spec.ts +++ b/frontend/src/app/components/ui-components/record-edit-fields/number/number.component.spec.ts @@ -4,25 +4,22 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { NumberEditComponent } from './number.component'; describe('NumberEditComponent', () => { - let component: NumberEditComponent; - let fixture: ComponentFixture; + let component: NumberEditComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [ - BrowserAnimationsModule, - NumberEditComponent - ] - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [BrowserAnimationsModule, NumberEditComponent], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(NumberEditComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(NumberEditComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/ui-components/record-edit-fields/number/number.component.ts b/frontend/src/app/components/ui-components/record-edit-fields/number/number.component.ts index adafd3c39..269406436 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/number/number.component.ts +++ b/frontend/src/app/components/ui-components/record-edit-fields/number/number.component.ts @@ -1,18 +1,17 @@ import { Component, Input } from '@angular/core'; - -import { BaseEditFieldComponent } from '../base-row-field/base-row-field.component'; import { FormsModule } from '@angular/forms'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; +import { BaseEditFieldComponent } from '../base-row-field/base-row-field.component'; @Component({ - selector: 'app-edit-number', - templateUrl: './number.component.html', - styleUrls: ['./number.component.css'], - imports: [MatFormFieldModule, MatInputModule, FormsModule] + selector: 'app-edit-number', + templateUrl: './number.component.html', + styleUrls: ['./number.component.css'], + imports: [MatFormFieldModule, MatInputModule, FormsModule], }) export class NumberEditComponent extends BaseEditFieldComponent { - @Input() value: number; + @Input() value: number; - static type = 'number'; + static type = 'number'; } diff --git a/frontend/src/app/components/ui-components/record-edit-fields/password/password.component.css b/frontend/src/app/components/ui-components/record-edit-fields/password/password.component.css index e30a2181a..1796f8a97 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/password/password.component.css +++ b/frontend/src/app/components/ui-components/record-edit-fields/password/password.component.css @@ -1,7 +1,7 @@ .password-form-field { - width: 100%; + width: 100%; } .password-checkbox { - margin-bottom: 20px; -} \ No newline at end of file + margin-bottom: 20px; +} diff --git a/frontend/src/app/components/ui-components/record-edit-fields/password/password.component.ts b/frontend/src/app/components/ui-components/record-edit-fields/password/password.component.ts index 91f51cd81..634a3a70b 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/password/password.component.ts +++ b/frontend/src/app/components/ui-components/record-edit-fields/password/password.component.ts @@ -1,44 +1,38 @@ import { Component, Input } from '@angular/core'; - -import { BaseEditFieldComponent } from '../base-row-field/base-row-field.component'; import { FormsModule } from '@angular/forms'; import { MatCheckboxModule } from '@angular/material/checkbox'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; +import { BaseEditFieldComponent } from '../base-row-field/base-row-field.component'; @Component({ - selector: 'app-edit-password', - templateUrl: './password.component.html', - styleUrls: ['./password.component.css'], - imports: [ - MatFormFieldModule, - MatInputModule, - MatCheckboxModule, - FormsModule - ] + selector: 'app-edit-password', + templateUrl: './password.component.html', + styleUrls: ['./password.component.css'], + imports: [MatFormFieldModule, MatInputModule, MatCheckboxModule, FormsModule], }) export class PasswordEditComponent extends BaseEditFieldComponent { - @Input() value: string; + @Input() value: string; - public clearPassword: boolean; + public clearPassword: boolean; - ngOnInit(): void { - super.ngOnInit(); - if (this.value === '***') this.value = ''; - // Don't emit empty password value to skip sending it to backend - if (this.value !== '') { - this.onFieldChange.emit(this.value); - } - } + ngOnInit(): void { + super.ngOnInit(); + if (this.value === '***') this.value = ''; + // Don't emit empty password value to skip sending it to backend + if (this.value !== '') { + this.onFieldChange.emit(this.value); + } + } - onPasswordChange(newValue: string) { - // Only emit non-empty values to prevent sending empty strings to backend - if (newValue !== '') { - this.onFieldChange.emit(newValue); - } - } + onPasswordChange(newValue: string) { + // Only emit non-empty values to prevent sending empty strings to backend + if (newValue !== '') { + this.onFieldChange.emit(newValue); + } + } - onClearPasswordChange() { - if (this.clearPassword) this.onFieldChange.emit(null); - } + onClearPasswordChange() { + if (this.clearPassword) this.onFieldChange.emit(null); + } } diff --git a/frontend/src/app/components/ui-components/record-edit-fields/phone/phone.component.css b/frontend/src/app/components/ui-components/record-edit-fields/phone/phone.component.css index 88291a68b..68574469d 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/phone/phone.component.css +++ b/frontend/src/app/components/ui-components/record-edit-fields/phone/phone.component.css @@ -1,67 +1,67 @@ .phone-input-container { - display: flex; - gap: 12px; - align-items: flex-start; - width: 100%; + display: flex; + gap: 12px; + align-items: flex-start; + width: 100%; } .country-select-field { - flex: 0 0 200px; - min-width: 200px; + flex: 0 0 200px; + min-width: 200px; } .phone-number-field { - flex: 1; - min-width: 0; + flex: 1; + min-width: 0; } .country-option { - display: flex; - align-items: center; - gap: 8px; + display: flex; + align-items: center; + gap: 8px; } .flag { - font-size: 16px; - width: 20px; - text-align: center; + font-size: 16px; + width: 20px; + text-align: center; } .dial-code { - font-weight: 500; - color: rgba(0, 0, 0, 0.87); - min-width: 40px; + font-weight: 500; + color: rgba(0, 0, 0, 0.87); + min-width: 40px; } .country-name { - color: rgba(0, 0, 0, 0.6); + color: rgba(0, 0, 0, 0.6); } .phone-number-field input.invalid { - border-color: #f44336; + border-color: #f44336; } @media (max-width: 768px) { - .phone-input-container { - flex-direction: column; - gap: 8px; - } - - .country-select-field { - flex: none; - width: 100%; - } - - .phone-number-field { - width: 100%; - } + .phone-input-container { + flex-direction: column; + gap: 8px; + } + + .country-select-field { + flex: none; + width: 100%; + } + + .phone-number-field { + width: 100%; + } } /* Dark theme support */ :host-context(.dark-theme) .dial-code { - color: rgba(255, 255, 255, 0.87); + color: rgba(255, 255, 255, 0.87); } :host-context(.dark-theme) .country-name { - color: rgba(255, 255, 255, 0.6); -} \ No newline at end of file + color: rgba(255, 255, 255, 0.6); +} diff --git a/frontend/src/app/components/ui-components/record-edit-fields/point/point.component.css b/frontend/src/app/components/ui-components/record-edit-fields/point/point.component.css index d6858e0b1..7ab52d4e1 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/point/point.component.css +++ b/frontend/src/app/components/ui-components/record-edit-fields/point/point.component.css @@ -1,6 +1,6 @@ .mat-mdc-form-field { - max-width: 260px; - min-width: 150px; - width: 48vw; - display: block; -} \ No newline at end of file + max-width: 260px; + min-width: 150px; + width: 48vw; + display: block; +} diff --git a/frontend/src/app/components/ui-components/record-edit-fields/point/point.component.spec.ts b/frontend/src/app/components/ui-components/record-edit-fields/point/point.component.spec.ts index ce7ed2239..0d3b040ec 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/point/point.component.spec.ts +++ b/frontend/src/app/components/ui-components/record-edit-fields/point/point.component.spec.ts @@ -1,25 +1,24 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { PointEditComponent } from './point.component'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { PointEditComponent } from './point.component'; describe('PointEditComponent', () => { - let component: PointEditComponent; - let fixture: ComponentFixture; + let component: PointEditComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [PointEditComponent, BrowserAnimationsModule] - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [PointEditComponent, BrowserAnimationsModule], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(PointEditComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(PointEditComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/ui-components/record-edit-fields/range/range.component.css b/frontend/src/app/components/ui-components/record-edit-fields/range/range.component.css index 74aa047b1..4ccd92ebf 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/range/range.component.css +++ b/frontend/src/app/components/ui-components/record-edit-fields/range/range.component.css @@ -1,120 +1,120 @@ .range-edit-container { - width: 100%; - display: flex; - flex-direction: column; - gap: 4px; - padding-bottom: 28px; + width: 100%; + display: flex; + flex-direction: column; + gap: 4px; + padding-bottom: 28px; } .range-label { - font-size: 14px; - color: rgba(0, 0, 0, 0.87); - margin-bottom: 8px; - display: block; + font-size: 14px; + color: rgba(0, 0, 0, 0.87); + margin-bottom: 8px; + display: block; } .range-labels { - display: flex; - justify-content: space-between; - font-size: 12px; - color: rgba(0, 0, 0, 0.6); - padding: 0 2px; + display: flex; + justify-content: space-between; + font-size: 12px; + color: rgba(0, 0, 0, 0.6); + padding: 0 2px; } .range-current { - font-weight: 600; - color: rgba(0, 0, 0, 0.87); + font-weight: 600; + color: rgba(0, 0, 0, 0.87); } .range-input { - width: 100%; - height: 6px; - -webkit-appearance: none; - appearance: none; - background: transparent; - outline: none; - cursor: pointer; + width: 100%; + height: 6px; + -webkit-appearance: none; + appearance: none; + background: transparent; + outline: none; + cursor: pointer; } .range-input::-webkit-slider-track { - width: 100%; - height: 6px; - background: #e0e0e0; - border-radius: 3px; + width: 100%; + height: 6px; + background: #e0e0e0; + border-radius: 3px; } .range-input::-moz-range-track { - width: 100%; - height: 6px; - background: #e0e0e0; - border-radius: 3px; + width: 100%; + height: 6px; + background: #e0e0e0; + border-radius: 3px; } .range-input::-webkit-slider-thumb { - -webkit-appearance: none; - appearance: none; - width: 18px; - height: 18px; - background: #1976d2; - border-radius: 50%; - cursor: pointer; - margin-top: -6px; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); - transition: all 0.2s ease; + -webkit-appearance: none; + appearance: none; + width: 18px; + height: 18px; + background: #1976d2; + border-radius: 50%; + cursor: pointer; + margin-top: -6px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); + transition: all 0.2s ease; } .range-input::-moz-range-thumb { - width: 18px; - height: 18px; - background: #1976d2; - border-radius: 50%; - cursor: pointer; - border: none; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); - transition: all 0.2s ease; + width: 18px; + height: 18px; + background: #1976d2; + border-radius: 50%; + cursor: pointer; + border: none; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); + transition: all 0.2s ease; } .range-input::-webkit-slider-thumb:hover { - transform: scale(1.1); - box-shadow: 0 3px 6px rgba(0, 0, 0, 0.3); + transform: scale(1.1); + box-shadow: 0 3px 6px rgba(0, 0, 0, 0.3); } .range-input::-moz-range-thumb:hover { - transform: scale(1.1); - box-shadow: 0 3px 6px rgba(0, 0, 0, 0.3); + transform: scale(1.1); + box-shadow: 0 3px 6px rgba(0, 0, 0, 0.3); } .range-input:disabled { - opacity: 0.5; - cursor: not-allowed; + opacity: 0.5; + cursor: not-allowed; } .range-input:disabled::-webkit-slider-thumb { - cursor: not-allowed; + cursor: not-allowed; } .range-input:disabled::-moz-range-thumb { - cursor: not-allowed; + cursor: not-allowed; } @media (prefers-color-scheme: dark) { - .range-label { - color: rgba(255, 255, 255, 0.87); - } - - .range-labels { - color: rgba(255, 255, 255, 0.6); - } - - .range-current { - color: rgba(255, 255, 255, 0.87); - } - - .range-input::-webkit-slider-track { - background: #424242; - } - - .range-input::-moz-range-track { - background: #424242; - } -} \ No newline at end of file + .range-label { + color: rgba(255, 255, 255, 0.87); + } + + .range-labels { + color: rgba(255, 255, 255, 0.6); + } + + .range-current { + color: rgba(255, 255, 255, 0.87); + } + + .range-input::-webkit-slider-track { + background: #424242; + } + + .range-input::-moz-range-track { + background: #424242; + } +} diff --git a/frontend/src/app/components/ui-components/record-edit-fields/range/range.component.ts b/frontend/src/app/components/ui-components/record-edit-fields/range/range.component.ts index 94b00c52c..81bd8b1c0 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/range/range.component.ts +++ b/frontend/src/app/components/ui-components/record-edit-fields/range/range.component.ts @@ -1,60 +1,55 @@ -import { Component, ElementRef, Input, ViewChild } from '@angular/core'; -import { BaseEditFieldComponent } from '../base-row-field/base-row-field.component'; import { CommonModule } from '@angular/common'; +import { Component, ElementRef, Input, ViewChild } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; +import { BaseEditFieldComponent } from '../base-row-field/base-row-field.component'; @Component({ - selector: 'app-range-edit', - templateUrl: './range.component.html', - styleUrls: ['./range.component.css'], - imports: [ - CommonModule, - FormsModule, - MatFormFieldModule, - MatInputModule - ], + selector: 'app-range-edit', + templateUrl: './range.component.html', + styleUrls: ['./range.component.css'], + imports: [CommonModule, FormsModule, MatFormFieldModule, MatInputModule], }) export class RangeEditComponent extends BaseEditFieldComponent { - @ViewChild('rangeInput') rangeInput: ElementRef; - @Input() value: number; - static type = 'range'; + @ViewChild('rangeInput') rangeInput: ElementRef; + @Input() value: number; + static type = 'range'; - public min: number = 0; - public max: number = 100; - public step: number = 1; + public min: number = 0; + public max: number = 100; + public step: number = 1; - override ngOnInit(): void { - super.ngOnInit(); - this._parseWidgetParams(); - } + override ngOnInit(): void { + super.ngOnInit(); + this._parseWidgetParams(); + } - ngOnChanges(): void { - this._parseWidgetParams(); - } + ngOnChanges(): void { + this._parseWidgetParams(); + } - public onValueChange(newValue: number): void { - this.value = newValue; - this.onFieldChange.emit(this.value); - } + public onValueChange(newValue: number): void { + this.value = newValue; + this.onFieldChange.emit(this.value); + } - private _parseWidgetParams(): void { - if (this.widgetStructure?.widget_params) { - try { - const params = this.widgetStructure.widget_params; - if (params.min !== undefined) { - this.min = Number(params.min) || 0; - } - if (params.max !== undefined) { - this.max = Number(params.max) || 100; - } - if (params.step !== undefined) { - this.step = Number(params.step) || 1; - } - } catch (error) { - console.error('Failed to parse widget params:', error); - } - } - } -} \ No newline at end of file + private _parseWidgetParams(): void { + if (this.widgetStructure?.widget_params) { + try { + const params = this.widgetStructure.widget_params; + if (params.min !== undefined) { + this.min = Number(params.min) || 0; + } + if (params.max !== undefined) { + this.max = Number(params.max) || 100; + } + if (params.step !== undefined) { + this.step = Number(params.step) || 1; + } + } catch (error) { + console.error('Failed to parse widget params:', error); + } + } + } +} diff --git a/frontend/src/app/components/ui-components/record-edit-fields/s3/s3.component.css b/frontend/src/app/components/ui-components/record-edit-fields/s3/s3.component.css index 7c0f62d5d..6e8be11c8 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/s3/s3.component.css +++ b/frontend/src/app/components/ui-components/record-edit-fields/s3/s3.component.css @@ -1,57 +1,57 @@ .s3-widget { - display: flex; - flex-direction: column; - gap: 12px; + display: flex; + flex-direction: column; + gap: 12px; } .s3-widget__key { - width: 100%; + width: 100%; } .s3-widget__actions { - display: flex; - gap: 8px; + display: flex; + gap: 8px; } .s3-widget__actions button { - display: flex; - align-items: center; - gap: 4px; + display: flex; + align-items: center; + gap: 4px; } .s3-widget__preview { - display: flex; - align-items: center; - justify-content: center; - min-height: 100px; - border: 1px dashed #ccc; - border-radius: 4px; - padding: 8px; + display: flex; + align-items: center; + justify-content: center; + min-height: 100px; + border: 1px dashed #ccc; + border-radius: 4px; + padding: 8px; } .s3-widget__thumbnail { - max-width: 200px; - max-height: 150px; - object-fit: contain; - border-radius: 4px; + max-width: 200px; + max-height: 150px; + object-fit: contain; + border-radius: 4px; } .s3-widget__file-icon { - display: flex; - flex-direction: column; - align-items: center; - gap: 8px; - color: #666; + display: flex; + flex-direction: column; + align-items: center; + gap: 8px; + color: #666; } .s3-widget__file-icon mat-icon { - font-size: 48px; - width: 48px; - height: 48px; + font-size: 48px; + width: 48px; + height: 48px; } .s3-widget__filename { - font-size: 12px; - word-break: break-all; - text-align: center; + font-size: 12px; + word-break: break-all; + text-align: center; } diff --git a/frontend/src/app/components/ui-components/record-edit-fields/select/select.component.css b/frontend/src/app/components/ui-components/record-edit-fields/select/select.component.css index 7a4696a74..828b119a7 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/select/select.component.css +++ b/frontend/src/app/components/ui-components/record-edit-fields/select/select.component.css @@ -1,3 +1,3 @@ .select-form-field { - width: 100%; -} \ No newline at end of file + width: 100%; +} diff --git a/frontend/src/app/components/ui-components/record-edit-fields/select/select.component.spec.ts b/frontend/src/app/components/ui-components/record-edit-fields/select/select.component.spec.ts index 582b26768..c22baf7ea 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/select/select.component.spec.ts +++ b/frontend/src/app/components/ui-components/record-edit-fields/select/select.component.spec.ts @@ -1,25 +1,24 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { SelectEditComponent } from './select.component'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { SelectEditComponent } from './select.component'; describe('SelectEditComponent', () => { - let component: SelectEditComponent; - let fixture: ComponentFixture; + let component: SelectEditComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [SelectEditComponent, BrowserAnimationsModule] - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [SelectEditComponent, BrowserAnimationsModule], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(SelectEditComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(SelectEditComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/ui-components/record-edit-fields/select/select.component.ts b/frontend/src/app/components/ui-components/record-edit-fields/select/select.component.ts index 3a222c330..6daa91e53 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/select/select.component.ts +++ b/frontend/src/app/components/ui-components/record-edit-fields/select/select.component.ts @@ -1,39 +1,40 @@ -import { CUSTOM_ELEMENTS_SCHEMA, Component, Input } from '@angular/core'; - -import { BaseEditFieldComponent } from '../base-row-field/base-row-field.component'; import { CommonModule } from '@angular/common'; +import { Component, CUSTOM_ELEMENTS_SCHEMA, Input } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatSelectModule } from '@angular/material/select'; +import { BaseEditFieldComponent } from '../base-row-field/base-row-field.component'; @Component({ - selector: 'app-edit-select', - imports: [CommonModule, FormsModule, MatFormFieldModule, MatSelectModule], - templateUrl: './select.component.html', - styleUrls: ['./select.component.css'], - schemas: [CUSTOM_ELEMENTS_SCHEMA] + selector: 'app-edit-select', + imports: [CommonModule, FormsModule, MatFormFieldModule, MatSelectModule], + templateUrl: './select.component.html', + styleUrls: ['./select.component.css'], + schemas: [CUSTOM_ELEMENTS_SCHEMA], }) export class SelectEditComponent extends BaseEditFieldComponent { - @Input() value: string; + @Input() value: string; - public options: {value: string | null, label: string}[] = []; + public options: { value: string | null; label: string }[] = []; - originalOrder = () => { return 0; } + originalOrder = () => { + return 0; + }; - ngOnInit(): void { - super.ngOnInit(); - if (this.widgetStructure) { - this.options = this.widgetStructure.widget_params.options; - if (this.widgetStructure.widget_params.allow_null) { - this.options = [{ value: null, label: '' }, ...this.options]; - } - } else if (this.structure) { - this.options = this.structure.data_type_params.map((option) => { - return { value: option, label: option }; - }); - if (this.structure.allow_null) { - this.options = [{ value: null, label: '' }, ...this.options]; - } - } - } + ngOnInit(): void { + super.ngOnInit(); + if (this.widgetStructure) { + this.options = this.widgetStructure.widget_params.options; + if (this.widgetStructure.widget_params.allow_null) { + this.options = [{ value: null, label: '' }, ...this.options]; + } + } else if (this.structure) { + this.options = this.structure.data_type_params.map((option) => { + return { value: option, label: option }; + }); + if (this.structure.allow_null) { + this.options = [{ value: null, label: '' }, ...this.options]; + } + } + } } diff --git a/frontend/src/app/components/ui-components/record-edit-fields/static-text/static-text.component.css b/frontend/src/app/components/ui-components/record-edit-fields/static-text/static-text.component.css index 86302ca2c..85157a202 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/static-text/static-text.component.css +++ b/frontend/src/app/components/ui-components/record-edit-fields/static-text/static-text.component.css @@ -1,10 +1,10 @@ .static-text { - margin-bottom: 20px; + margin-bottom: 20px; } .static-text__value { - color: #0a0a0a; - font-style: italic; - opacity: 0.32; - padding-left: 8px; -} \ No newline at end of file + color: #0a0a0a; + font-style: italic; + opacity: 0.32; + padding-left: 8px; +} diff --git a/frontend/src/app/components/ui-components/record-edit-fields/static-text/static-text.component.spec.ts b/frontend/src/app/components/ui-components/record-edit-fields/static-text/static-text.component.spec.ts index 2bc3fa6cd..029442c47 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/static-text/static-text.component.spec.ts +++ b/frontend/src/app/components/ui-components/record-edit-fields/static-text/static-text.component.spec.ts @@ -1,25 +1,24 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { StaticTextEditComponent } from './static-text.component'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { StaticTextEditComponent } from './static-text.component'; describe('StaticTextEditComponent', () => { - let component: StaticTextEditComponent; - let fixture: ComponentFixture; + let component: StaticTextEditComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [StaticTextEditComponent, BrowserAnimationsModule] - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [StaticTextEditComponent, BrowserAnimationsModule], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(StaticTextEditComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(StaticTextEditComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/ui-components/record-edit-fields/static-text/static-text.component.ts b/frontend/src/app/components/ui-components/record-edit-fields/static-text/static-text.component.ts index 4d0e89887..8251ee483 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/static-text/static-text.component.ts +++ b/frontend/src/app/components/ui-components/record-edit-fields/static-text/static-text.component.ts @@ -1,14 +1,13 @@ +import { CommonModule } from '@angular/common'; import { Component, Input } from '@angular/core'; - import { BaseEditFieldComponent } from '../base-row-field/base-row-field.component'; -import { CommonModule } from '@angular/common'; @Component({ - selector: 'app-edit-static-text', - imports: [CommonModule], - templateUrl: './static-text.component.html', - styleUrls: ['./static-text.component.css'] + selector: 'app-edit-static-text', + imports: [CommonModule], + templateUrl: './static-text.component.html', + styleUrls: ['./static-text.component.css'], }) export class StaticTextEditComponent extends BaseEditFieldComponent { - @Input() value: string; + @Input() value: string; } diff --git a/frontend/src/app/components/ui-components/record-edit-fields/text/text.component.css b/frontend/src/app/components/ui-components/record-edit-fields/text/text.component.css index f8e826b53..51d757486 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/text/text.component.css +++ b/frontend/src/app/components/ui-components/record-edit-fields/text/text.component.css @@ -1,7 +1,7 @@ .text-form-field { - width: 100%; + width: 100%; } .counter { - margin-right: 16px; -} \ No newline at end of file + margin-right: 16px; +} diff --git a/frontend/src/app/components/ui-components/record-edit-fields/text/text.component.spec.ts b/frontend/src/app/components/ui-components/record-edit-fields/text/text.component.spec.ts index e326ae971..d8f7dc665 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/text/text.component.spec.ts +++ b/frontend/src/app/components/ui-components/record-edit-fields/text/text.component.spec.ts @@ -1,25 +1,24 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { TextEditComponent } from './text.component'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { TextEditComponent } from './text.component'; describe('TextEditComponent', () => { - let component: TextEditComponent; - let fixture: ComponentFixture; + let component: TextEditComponent; + let fixture: ComponentFixture; - beforeEach(async() => { - await TestBed.configureTestingModule({ - imports: [TextEditComponent, BrowserAnimationsModule] - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [TextEditComponent, BrowserAnimationsModule], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(TextEditComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(TextEditComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/ui-components/record-edit-fields/text/text.component.ts b/frontend/src/app/components/ui-components/record-edit-fields/text/text.component.ts index 3d8903526..d5dbc6e64 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/text/text.component.ts +++ b/frontend/src/app/components/ui-components/record-edit-fields/text/text.component.ts @@ -1,77 +1,77 @@ +import { CommonModule } from '@angular/common'; import { Component, Injectable, Input, OnInit } from '@angular/core'; - -import { BaseEditFieldComponent } from '../base-row-field/base-row-field.component'; import { FormsModule } from '@angular/forms'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; -import { CommonModule } from '@angular/common'; import { TextValidatorDirective } from 'src/app/directives/text-validator.directive'; +import { BaseEditFieldComponent } from '../base-row-field/base-row-field.component'; @Injectable() @Component({ - selector: 'app-edit-text', - templateUrl: './text.component.html', - styleUrls: ['./text.component.css'], - imports: [CommonModule, MatFormFieldModule, MatInputModule, FormsModule, TextValidatorDirective] + selector: 'app-edit-text', + templateUrl: './text.component.html', + styleUrls: ['./text.component.css'], + imports: [CommonModule, MatFormFieldModule, MatInputModule, FormsModule, TextValidatorDirective], }) export class TextEditComponent extends BaseEditFieldComponent implements OnInit { - @Input() value: string; + @Input() value: string; + + static type = 'text'; + + maxLength: number | null = null; + validateType: string | null = null; + regexPattern: string | null = null; - static type = 'text'; + override ngOnInit(): void { + super.ngOnInit(); - maxLength: number | null = null; - validateType: string | null = null; - regexPattern: string | null = null; + // Use character_maximum_length from the field structure if available + if (this.structure?.character_maximum_length) { + this.maxLength = this.structure.character_maximum_length; + } - override ngOnInit(): void { - super.ngOnInit(); - - // Use character_maximum_length from the field structure if available - if (this.structure?.character_maximum_length) { - this.maxLength = this.structure.character_maximum_length; - } + // Parse widget parameters for validation + if (this.widgetStructure?.widget_params) { + const params = + typeof this.widgetStructure.widget_params === 'string' + ? JSON.parse(this.widgetStructure.widget_params) + : this.widgetStructure.widget_params; - // Parse widget parameters for validation - if (this.widgetStructure?.widget_params) { - const params = typeof this.widgetStructure.widget_params === 'string' - ? JSON.parse(this.widgetStructure.widget_params) - : this.widgetStructure.widget_params; - - this.validateType = params.validate || null; - this.regexPattern = params.regex || null; - } - } + this.validateType = params.validate || null; + this.regexPattern = params.regex || null; + } + } - getValidationErrorMessage(): string { - if (!this.validateType) { - return ''; - } + getValidationErrorMessage(): string { + if (!this.validateType) { + return ''; + } - if (this.validateType === 'regex') { - return 'Value doesn\'t match the required pattern'; - } + if (this.validateType === 'regex') { + return "Value doesn't match the required pattern"; + } - // Create user-friendly messages for common validators - const messages = { - isEmail: 'Invalid email address', - isURL: 'Invalid URL', - isIP: 'Invalid IP address', - isUUID: 'Invalid UUID', - isJSON: 'Invalid JSON', - isCreditCard: 'Invalid credit card number', - isISBN: 'Invalid ISBN', - isAlpha: 'Should contain only letters', - isNumeric: 'Should contain only numbers', - isAlphanumeric: 'Should contain only letters and numbers', - isHexColor: 'Invalid hex color', - isBase64: 'Invalid Base64 string', - isMobilePhone: 'Invalid mobile phone number', - isMACAddress: 'Invalid MAC address', - isPostalCode: 'Invalid postal code', - isCurrency: 'Invalid currency format' - }; + // Create user-friendly messages for common validators + const messages = { + isEmail: 'Invalid email address', + isURL: 'Invalid URL', + isIP: 'Invalid IP address', + isUUID: 'Invalid UUID', + isJSON: 'Invalid JSON', + isCreditCard: 'Invalid credit card number', + isISBN: 'Invalid ISBN', + isAlpha: 'Should contain only letters', + isNumeric: 'Should contain only numbers', + isAlphanumeric: 'Should contain only letters and numbers', + isHexColor: 'Invalid hex color', + isBase64: 'Invalid Base64 string', + isMobilePhone: 'Invalid mobile phone number', + isMACAddress: 'Invalid MAC address', + isPostalCode: 'Invalid postal code', + isCurrency: 'Invalid currency format', + }; - return messages[this.validateType] || `Invalid ${this.validateType}`; - } + return messages[this.validateType] || `Invalid ${this.validateType}`; + } } diff --git a/frontend/src/app/components/ui-components/record-edit-fields/time-interval/time-interval.component.css b/frontend/src/app/components/ui-components/record-edit-fields/time-interval/time-interval.component.css index 7e87dc982..8b6934468 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/time-interval/time-interval.component.css +++ b/frontend/src/app/components/ui-components/record-edit-fields/time-interval/time-interval.component.css @@ -1,13 +1,13 @@ .feild-set-6 { - display: grid; - grid-template-columns: repeat(3, 1fr); - grid-column-gap: 16px; + display: grid; + grid-template-columns: repeat(3, 1fr); + grid-column-gap: 16px; } ::ng-deep .interval-form-field .mat-mdc-form-field-infix { - width: auto !important; + width: auto !important; } .label { - grid-column: 1 / span 3; -} \ No newline at end of file + grid-column: 1 / span 3; +} diff --git a/frontend/src/app/components/ui-components/record-edit-fields/time-interval/time-interval.component.spec.ts b/frontend/src/app/components/ui-components/record-edit-fields/time-interval/time-interval.component.spec.ts index 2e018cc47..2d1d53d27 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/time-interval/time-interval.component.spec.ts +++ b/frontend/src/app/components/ui-components/record-edit-fields/time-interval/time-interval.component.spec.ts @@ -1,25 +1,24 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { TimeIntervalEditComponent } from './time-interval.component'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { TimeIntervalEditComponent } from './time-interval.component'; describe('TimeIntervalEditComponent', () => { - let component: TimeIntervalEditComponent; - let fixture: ComponentFixture; + let component: TimeIntervalEditComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [TimeIntervalEditComponent, BrowserAnimationsModule] - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [TimeIntervalEditComponent, BrowserAnimationsModule], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(TimeIntervalEditComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(TimeIntervalEditComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/ui-components/record-edit-fields/time-interval/time-interval.component.ts b/frontend/src/app/components/ui-components/record-edit-fields/time-interval/time-interval.component.ts index e5a1113b0..d064675c8 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/time-interval/time-interval.component.ts +++ b/frontend/src/app/components/ui-components/record-edit-fields/time-interval/time-interval.component.ts @@ -1,41 +1,39 @@ -import * as pgInterval from 'postgres-interval'; +import { CommonModule } from '@angular/common'; import { Component, Input } from '@angular/core'; - -import { BaseEditFieldComponent } from '../base-row-field/base-row-field.component'; -import { CommonModule } from '@angular/common'; import { FormsModule } from '@angular/forms'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; +import * as pgInterval from 'postgres-interval'; +import { BaseEditFieldComponent } from '../base-row-field/base-row-field.component'; @Component({ - selector: 'app-edit-time-interval', - imports: [CommonModule, MatFormFieldModule, MatInputModule, FormsModule], - templateUrl: './time-interval.component.html', - styleUrls: ['./time-interval.component.css'] + selector: 'app-edit-time-interval', + imports: [CommonModule, MatFormFieldModule, MatInputModule, FormsModule], + templateUrl: './time-interval.component.html', + styleUrls: ['./time-interval.component.css'], }) export class TimeIntervalEditComponent extends BaseEditFieldComponent { - @Input() value; - - public interval = { - years: '', - months: '', - days: '', - hours: '', - minutes: '', - seconds: '', - milliseconds: '' - }; + @Input() value; + public interval = { + years: '', + months: '', + days: '', + hours: '', + minutes: '', + seconds: '', + milliseconds: '', + }; - ngOnInit(): void { - super.ngOnInit(); - if (this.value) this.interval = this.value; - } + ngOnInit(): void { + super.ngOnInit(); + if (this.value) this.interval = this.value; + } - onInputChange() { - // @ts-expect-error - const currentInterval = pgInterval.prototype.toPostgres.call(this.interval); - this.onFieldChange.emit(currentInterval); - } + onInputChange() { + // @ts-expect-error + const currentInterval = pgInterval.prototype.toPostgres.call(this.interval); + this.onFieldChange.emit(currentInterval); + } } diff --git a/frontend/src/app/components/ui-components/record-edit-fields/time/time.component.css b/frontend/src/app/components/ui-components/record-edit-fields/time/time.component.css index bf0a25ecd..935f11420 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/time/time.component.css +++ b/frontend/src/app/components/ui-components/record-edit-fields/time/time.component.css @@ -1,3 +1,3 @@ .time-field { - width: 100%; -} \ No newline at end of file + width: 100%; +} diff --git a/frontend/src/app/components/ui-components/record-edit-fields/time/time.component.spec.ts b/frontend/src/app/components/ui-components/record-edit-fields/time/time.component.spec.ts index 149214c79..6a4c0cf6a 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/time/time.component.spec.ts +++ b/frontend/src/app/components/ui-components/record-edit-fields/time/time.component.spec.ts @@ -1,25 +1,24 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { TimeEditComponent } from './time.component'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { TimeEditComponent } from './time.component'; describe('TimeEditComponent', () => { - let component: TimeEditComponent; - let fixture: ComponentFixture; + let component: TimeEditComponent; + let fixture: ComponentFixture; - beforeEach(async() => { - await TestBed.configureTestingModule({ - imports: [TimeEditComponent, BrowserAnimationsModule] - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [TimeEditComponent, BrowserAnimationsModule], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(TimeEditComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(TimeEditComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/ui-components/record-edit-fields/time/time.component.ts b/frontend/src/app/components/ui-components/record-edit-fields/time/time.component.ts index 620492ff7..62da2f653 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/time/time.component.ts +++ b/frontend/src/app/components/ui-components/record-edit-fields/time/time.component.ts @@ -1,18 +1,17 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; - -import { BaseEditFieldComponent } from '../base-row-field/base-row-field.component'; import { FormsModule } from '@angular/forms'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; +import { BaseEditFieldComponent } from '../base-row-field/base-row-field.component'; @Component({ - selector: 'app-edit-time', - templateUrl: './time.component.html', - styleUrls: ['./time.component.css'], - imports: [MatFormFieldModule, MatInputModule, FormsModule] + selector: 'app-edit-time', + templateUrl: './time.component.html', + styleUrls: ['./time.component.css'], + imports: [MatFormFieldModule, MatInputModule, FormsModule], }) export class TimeEditComponent extends BaseEditFieldComponent { - @Input() value: string; - @Output() onFieldChange = new EventEmitter(); - static type = 'datetime'; + @Input() value: string; + @Output() onFieldChange = new EventEmitter(); + static type = 'datetime'; } diff --git a/frontend/src/app/components/ui-components/record-edit-fields/timezone/timezone.component.css b/frontend/src/app/components/ui-components/record-edit-fields/timezone/timezone.component.css index e9739a06c..388e7c15e 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/timezone/timezone.component.css +++ b/frontend/src/app/components/ui-components/record-edit-fields/timezone/timezone.component.css @@ -1,3 +1,3 @@ .timezone-form-field { - width: 100%; + width: 100%; } diff --git a/frontend/src/app/components/ui-components/record-edit-fields/timezone/timezone.component.ts b/frontend/src/app/components/ui-components/record-edit-fields/timezone/timezone.component.ts index 6d0593d93..761960671 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/timezone/timezone.component.ts +++ b/frontend/src/app/components/ui-components/record-edit-fields/timezone/timezone.component.ts @@ -1,5 +1,5 @@ -import { CUSTOM_ELEMENTS_SCHEMA, Component, Input } from '@angular/core'; import { CommonModule } from '@angular/common'; +import { Component, CUSTOM_ELEMENTS_SCHEMA, Input } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatSelectModule } from '@angular/material/select'; @@ -7,75 +7,77 @@ import { MatSelectModule } from '@angular/material/select'; import { BaseEditFieldComponent } from '../base-row-field/base-row-field.component'; @Component({ - selector: 'app-edit-timezone', - imports: [CommonModule, FormsModule, MatFormFieldModule, MatSelectModule], - templateUrl: './timezone.component.html', - styleUrls: ['./timezone.component.css'], - schemas: [CUSTOM_ELEMENTS_SCHEMA] + selector: 'app-edit-timezone', + imports: [CommonModule, FormsModule, MatFormFieldModule, MatSelectModule], + templateUrl: './timezone.component.html', + styleUrls: ['./timezone.component.css'], + schemas: [CUSTOM_ELEMENTS_SCHEMA], }) export class TimezoneEditComponent extends BaseEditFieldComponent { - @Input() value: string; + @Input() value: string; - public timezones: { value: string, label: string }[] = []; + public timezones: { value: string; label: string }[] = []; - originalOrder = () => { return 0; } + originalOrder = () => { + return 0; + }; - ngOnInit(): void { - super.ngOnInit(); - this.initializeTimezones(); - } + ngOnInit(): void { + super.ngOnInit(); + this.initializeTimezones(); + } - private initializeTimezones(): void { - // Get all available timezone identifiers from Intl API - const timezoneList = Intl.supportedValuesOf('timeZone'); + private initializeTimezones(): void { + // Get all available timezone identifiers from Intl API + const timezoneList = Intl.supportedValuesOf('timeZone'); - // Map timezones to format with offset and readable label - this.timezones = timezoneList.map(tz => { - const offset = this.getTimezoneOffset(tz); - return { - value: tz, - label: `${tz} (UTC${offset})` - }; - }); + // Map timezones to format with offset and readable label + this.timezones = timezoneList.map((tz) => { + const offset = this.getTimezoneOffset(tz); + return { + value: tz, + label: `${tz} (UTC${offset})`, + }; + }); - // Sort by timezone name - this.timezones.sort((a, b) => a.value.localeCompare(b.value)); + // Sort by timezone name + this.timezones.sort((a, b) => a.value.localeCompare(b.value)); - // Check widget params for allow_null option - if (this.widgetStructure?.widget_params?.allow_null) { - this.timezones = [{ value: null, label: '' }, ...this.timezones]; - } else if (this.structure?.allow_null) { - this.timezones = [{ value: null, label: '' }, ...this.timezones]; - } - } + // Check widget params for allow_null option + if (this.widgetStructure?.widget_params?.allow_null) { + this.timezones = [{ value: null, label: '' }, ...this.timezones]; + } else if (this.structure?.allow_null) { + this.timezones = [{ value: null, label: '' }, ...this.timezones]; + } + } - private getTimezoneOffset(timezone: string): string { - try { - const now = new Date(); - const formatter = new Intl.DateTimeFormat('en-US', { - timeZone: timezone, - timeZoneName: 'longOffset' - }); + private getTimezoneOffset(timezone: string): string { + try { + const now = new Date(); + const formatter = new Intl.DateTimeFormat('en-US', { + timeZone: timezone, + timeZoneName: 'longOffset', + }); - const parts = formatter.formatToParts(now); - const offsetPart = parts.find(part => part.type === 'timeZoneName'); + const parts = formatter.formatToParts(now); + const offsetPart = parts.find((part) => part.type === 'timeZoneName'); - if (offsetPart?.value.includes('GMT')) { - // Extract offset from "GMT+XX:XX" format - const offset = offsetPart.value.replace('GMT', ''); - return offset === '' ? '+00:00' : offset; - } + if (offsetPart?.value.includes('GMT')) { + // Extract offset from "GMT+XX:XX" format + const offset = offsetPart.value.replace('GMT', ''); + return offset === '' ? '+00:00' : offset; + } - // Fallback: calculate offset manually - const utcDate = new Date(now.toLocaleString('en-US', { timeZone: 'UTC' })); - const tzDate = new Date(now.toLocaleString('en-US', { timeZone: timezone })); - const offsetMinutes = (tzDate.getTime() - utcDate.getTime()) / 60000; - const hours = Math.floor(Math.abs(offsetMinutes) / 60); - const minutes = Math.abs(offsetMinutes) % 60; - const sign = offsetMinutes >= 0 ? '+' : '-'; - return `${sign}${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}`; - } catch (_error) { - return ''; - } - } + // Fallback: calculate offset manually + const utcDate = new Date(now.toLocaleString('en-US', { timeZone: 'UTC' })); + const tzDate = new Date(now.toLocaleString('en-US', { timeZone: timezone })); + const offsetMinutes = (tzDate.getTime() - utcDate.getTime()) / 60000; + const hours = Math.floor(Math.abs(offsetMinutes) / 60); + const minutes = Math.abs(offsetMinutes) % 60; + const sign = offsetMinutes >= 0 ? '+' : '-'; + return `${sign}${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}`; + } catch (_error) { + return ''; + } + } } diff --git a/frontend/src/app/components/ui-components/record-edit-fields/url/url.component.css b/frontend/src/app/components/ui-components/record-edit-fields/url/url.component.css index 3f62bbbde..1502b7426 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/url/url.component.css +++ b/frontend/src/app/components/ui-components/record-edit-fields/url/url.component.css @@ -1,14 +1,14 @@ .url-box { - margin-bottom: 12px; - width: 100%; + margin-bottom: 12px; + width: 100%; } /* Handle long URL prefixes with overflow */ ::ng-deep .mat-mdc-form-field-text-prefix { - max-width: 30%; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - display: inline-block; - vertical-align: middle; -} \ No newline at end of file + max-width: 30%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + display: inline-block; + vertical-align: middle; +} diff --git a/frontend/src/app/components/ui-components/record-edit-fields/url/url.component.spec.ts b/frontend/src/app/components/ui-components/record-edit-fields/url/url.component.spec.ts index ace360aef..c52f40c1e 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/url/url.component.spec.ts +++ b/frontend/src/app/components/ui-components/record-edit-fields/url/url.component.spec.ts @@ -1,28 +1,23 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { FormsModule } from '@angular/forms'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; - import { UrlEditComponent } from './url.component'; -import { FormsModule } from '@angular/forms'; describe('UrlComponent', () => { - let component: UrlEditComponent; - let fixture: ComponentFixture; + let component: UrlEditComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [ - FormsModule, - UrlEditComponent, - BrowserAnimationsModule - ] - }).compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [FormsModule, UrlEditComponent, BrowserAnimationsModule], + }).compileComponents(); - fixture = TestBed.createComponent(UrlEditComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + fixture = TestBed.createComponent(UrlEditComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/ui-components/record-edit-fields/url/url.component.ts b/frontend/src/app/components/ui-components/record-edit-fields/url/url.component.ts index d1c8ca488..879b9f08e 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/url/url.component.ts +++ b/frontend/src/app/components/ui-components/record-edit-fields/url/url.component.ts @@ -1,50 +1,44 @@ -import { Component, Input, OnInit } from '@angular/core'; - -import { BaseEditFieldComponent } from '../base-row-field/base-row-field.component'; import { CommonModule } from '@angular/common'; +import { Component, Input, OnInit } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; import { UrlValidatorDirective } from 'src/app/directives/url-validator.directive'; +import { BaseEditFieldComponent } from '../base-row-field/base-row-field.component'; @Component({ - selector: 'app-edit-url', - imports: [ - CommonModule, - FormsModule, - MatFormFieldModule, - MatInputModule, - UrlValidatorDirective - ], - templateUrl: './url.component.html', - styleUrl: './url.component.css' + selector: 'app-edit-url', + imports: [CommonModule, FormsModule, MatFormFieldModule, MatInputModule, UrlValidatorDirective], + templateUrl: './url.component.html', + styleUrl: './url.component.css', }) export class UrlEditComponent extends BaseEditFieldComponent implements OnInit { - @Input() value: string; - public prefix: string = ''; + @Input() value: string; + public prefix: string = ''; + + ngOnInit(): void { + super.ngOnInit(); + this._parseWidgetParams(); + } - ngOnInit(): void { - super.ngOnInit(); - this._parseWidgetParams(); - } + ngOnChanges(): void { + this._parseWidgetParams(); + } - ngOnChanges(): void { - this._parseWidgetParams(); - } + private _parseWidgetParams(): void { + if (this.widgetStructure?.widget_params) { + try { + const params = + typeof this.widgetStructure.widget_params === 'string' + ? JSON.parse(this.widgetStructure.widget_params) + : this.widgetStructure.widget_params; - private _parseWidgetParams(): void { - if (this.widgetStructure?.widget_params) { - try { - const params = typeof this.widgetStructure.widget_params === 'string' - ? JSON.parse(this.widgetStructure.widget_params) - : this.widgetStructure.widget_params; - - if (params.prefix !== undefined) { - this.prefix = params.prefix || ''; - } - } catch (e) { - console.error('Error parsing URL widget params:', e); - } - } - } + if (params.prefix !== undefined) { + this.prefix = params.prefix || ''; + } + } catch (e) { + console.error('Error parsing URL widget params:', e); + } + } + } } diff --git a/frontend/src/app/components/ui-components/record-edit-fields/uuid/uuid.component.css b/frontend/src/app/components/ui-components/record-edit-fields/uuid/uuid.component.css index c796719b5..f5208688c 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/uuid/uuid.component.css +++ b/frontend/src/app/components/ui-components/record-edit-fields/uuid/uuid.component.css @@ -1,54 +1,54 @@ .uuid-field { - width: 100%; + width: 100%; } .uuid-input { - font-family: 'Roboto Mono', monospace; - font-size: 14px; + font-family: "Roboto Mono", monospace; + font-size: 14px; } .regenerate-button { - cursor: pointer; + cursor: pointer; } .regenerate-button:hover { - color: var(--mat-primary); + color: var(--mat-primary); } .uuid-config-info { - display: flex; - align-items: center; - gap: 8px; - margin-top: -16px; - margin-bottom: 16px; - padding: 8px; - background-color: var(--mat-grey-100); - border-radius: 4px; - font-size: 12px; + display: flex; + align-items: center; + gap: 8px; + margin-top: -16px; + margin-bottom: 16px; + padding: 8px; + background-color: var(--mat-grey-100); + border-radius: 4px; + font-size: 12px; } .info-icon { - font-size: 16px; - width: 16px; - height: 16px; - color: var(--mat-grey-600); + font-size: 16px; + width: 16px; + height: 16px; + color: var(--mat-grey-600); } .info-text { - color: var(--mat-grey-700); + color: var(--mat-grey-700); } /* Dark mode support */ @media (prefers-color-scheme: dark) { - .uuid-config-info { - background-color: var(--mat-grey-800); - } - - .info-icon { - color: var(--mat-grey-400); - } - - .info-text { - color: var(--mat-grey-300); - } -} \ No newline at end of file + .uuid-config-info { + background-color: var(--mat-grey-800); + } + + .info-icon { + color: var(--mat-grey-400); + } + + .info-text { + color: var(--mat-grey-300); + } +} diff --git a/frontend/src/app/components/ui-components/record-edit-fields/uuid/uuid.component.ts b/frontend/src/app/components/ui-components/record-edit-fields/uuid/uuid.component.ts index dfb03e859..7cdc58eda 100644 --- a/frontend/src/app/components/ui-components/record-edit-fields/uuid/uuid.component.ts +++ b/frontend/src/app/components/ui-components/record-edit-fields/uuid/uuid.component.ts @@ -1,8 +1,5 @@ -import { Component, Injectable, Input, OnInit } from '@angular/core'; -import { validate as uuidValidate, version as uuidVersion, v1 as uuidv1, v3 as uuidv3, v4 as uuidv4, v5 as uuidv5, v7 as uuidv7 } from 'uuid'; - -import { BaseEditFieldComponent } from '../base-row-field/base-row-field.component'; import { CommonModule } from '@angular/common'; +import { Component, Injectable, Input, OnInit } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { MatButtonModule } from '@angular/material/button'; import { MatFormFieldModule } from '@angular/material/form-field'; @@ -10,135 +7,146 @@ import { MatIconModule } from '@angular/material/icon'; import { MatInputModule } from '@angular/material/input'; import { MatSelectModule } from '@angular/material/select'; import { MatTooltipModule } from '@angular/material/tooltip'; +import { + validate as uuidValidate, + version as uuidVersion, + v1 as uuidv1, + v3 as uuidv3, + v4 as uuidv4, + v5 as uuidv5, + v7 as uuidv7, +} from 'uuid'; +import { BaseEditFieldComponent } from '../base-row-field/base-row-field.component'; @Injectable() @Component({ - selector: 'app-edit-uuid', - templateUrl: './uuid.component.html', - styleUrls: ['./uuid.component.css'], - imports: [ - CommonModule, - MatFormFieldModule, - MatInputModule, - MatButtonModule, - MatIconModule, - MatSelectModule, - MatTooltipModule, - FormsModule - ] + selector: 'app-edit-uuid', + templateUrl: './uuid.component.html', + styleUrls: ['./uuid.component.css'], + imports: [ + CommonModule, + MatFormFieldModule, + MatInputModule, + MatButtonModule, + MatIconModule, + MatSelectModule, + MatTooltipModule, + FormsModule, + ], }) export class UuidEditComponent extends BaseEditFieldComponent implements OnInit { - @Input() value: string; - - static type = 'uuid'; - - public uuidVersion: string = 'v4'; - public isCreateMode: boolean = true; - public namespace: string = '6ba7b810-9dad-11d1-80b4-00c04fd430c8'; // Default DNS namespace - public name: string = ''; - - // Available UUID versions - public availableVersions = [ - { value: 'v1', label: 'v1 (timestamp + MAC)', description: 'Timestamp and MAC address based' }, - { value: 'v3', label: 'v3 (MD5 hash)', description: 'MD5 hash of namespace and name' }, - { value: 'v4', label: 'v4 (random)', description: 'Random UUID (most common)' }, - { value: 'v5', label: 'v5 (SHA-1 hash)', description: 'SHA-1 hash of namespace and name' }, - { value: 'v7', label: 'v7 (timestamp)', description: 'Timestamp-based sortable UUID' } - ]; - - // Standard namespaces for v3/v5 - public namespaces = [ - { value: '6ba7b810-9dad-11d1-80b4-00c04fd430c8', label: 'DNS' }, - { value: '6ba7b811-9dad-11d1-80b4-00c04fd430c8', label: 'URL' }, - { value: '6ba7b812-9dad-11d1-80b4-00c04fd430c8', label: 'OID' }, - { value: '6ba7b814-9dad-11d1-80b4-00c04fd430c8', label: 'X500' } - ]; - - ngOnInit(): void { - super.ngOnInit(); - - // Determine if we're in create or update mode - this.isCreateMode = !this.value || this.value === ''; - - // Parse widget parameters - if (this.widgetStructure?.widget_params) { - try { - const params = typeof this.widgetStructure.widget_params === 'string' - ? JSON.parse(this.widgetStructure.widget_params) - : this.widgetStructure.widget_params; - - if (params.version) { - this.uuidVersion = params.version; - } - if (params.namespace) { - this.namespace = params.namespace; - } - if (params.name) { - this.name = params.name; - } - } catch (e) { - console.error('Error parsing UUID widget params:', e); - } - } - - // Generate UUID on create - if (this.isCreateMode) { - this.generateUuid(); - } - } - - generateUuid(): void { - let uuid = ''; - - try { - switch (this.uuidVersion) { - case 'v1': - uuid = uuidv1(); - break; - case 'v3': - if (this.name) { - uuid = uuidv3(this.name, this.namespace); - } else { - uuid = uuidv3(Date.now().toString(), this.namespace); - } - break; - case 'v4': - uuid = uuidv4(); - break; - case 'v5': - if (this.name) { - uuid = uuidv5(this.name, this.namespace); - } else { - uuid = uuidv5(Date.now().toString(), this.namespace); - } - break; - case 'v7': - uuid = uuidv7(); - break; - default: - uuid = uuidv4(); - } - - this.value = uuid; - this.onFieldChange.emit(this.value); - } catch (error) { - console.error('Error generating UUID:', error); - // Fallback to v4 - this.value = uuidv4(); - this.onFieldChange.emit(this.value); - } - } - - validateUuid(value: string): boolean { - return uuidValidate(value); - } - - getUuidVersion(value: string): number | false { - try { - return uuidVersion(value); - } catch { - return false; - } - } -} \ No newline at end of file + @Input() value: string; + + static type = 'uuid'; + + public uuidVersion: string = 'v4'; + public isCreateMode: boolean = true; + public namespace: string = '6ba7b810-9dad-11d1-80b4-00c04fd430c8'; // Default DNS namespace + public name: string = ''; + + // Available UUID versions + public availableVersions = [ + { value: 'v1', label: 'v1 (timestamp + MAC)', description: 'Timestamp and MAC address based' }, + { value: 'v3', label: 'v3 (MD5 hash)', description: 'MD5 hash of namespace and name' }, + { value: 'v4', label: 'v4 (random)', description: 'Random UUID (most common)' }, + { value: 'v5', label: 'v5 (SHA-1 hash)', description: 'SHA-1 hash of namespace and name' }, + { value: 'v7', label: 'v7 (timestamp)', description: 'Timestamp-based sortable UUID' }, + ]; + + // Standard namespaces for v3/v5 + public namespaces = [ + { value: '6ba7b810-9dad-11d1-80b4-00c04fd430c8', label: 'DNS' }, + { value: '6ba7b811-9dad-11d1-80b4-00c04fd430c8', label: 'URL' }, + { value: '6ba7b812-9dad-11d1-80b4-00c04fd430c8', label: 'OID' }, + { value: '6ba7b814-9dad-11d1-80b4-00c04fd430c8', label: 'X500' }, + ]; + + ngOnInit(): void { + super.ngOnInit(); + + // Determine if we're in create or update mode + this.isCreateMode = !this.value || this.value === ''; + + // Parse widget parameters + if (this.widgetStructure?.widget_params) { + try { + const params = + typeof this.widgetStructure.widget_params === 'string' + ? JSON.parse(this.widgetStructure.widget_params) + : this.widgetStructure.widget_params; + + if (params.version) { + this.uuidVersion = params.version; + } + if (params.namespace) { + this.namespace = params.namespace; + } + if (params.name) { + this.name = params.name; + } + } catch (e) { + console.error('Error parsing UUID widget params:', e); + } + } + + // Generate UUID on create + if (this.isCreateMode) { + this.generateUuid(); + } + } + + generateUuid(): void { + let uuid = ''; + + try { + switch (this.uuidVersion) { + case 'v1': + uuid = uuidv1(); + break; + case 'v3': + if (this.name) { + uuid = uuidv3(this.name, this.namespace); + } else { + uuid = uuidv3(Date.now().toString(), this.namespace); + } + break; + case 'v4': + uuid = uuidv4(); + break; + case 'v5': + if (this.name) { + uuid = uuidv5(this.name, this.namespace); + } else { + uuid = uuidv5(Date.now().toString(), this.namespace); + } + break; + case 'v7': + uuid = uuidv7(); + break; + default: + uuid = uuidv4(); + } + + this.value = uuid; + this.onFieldChange.emit(this.value); + } catch (error) { + console.error('Error generating UUID:', error); + // Fallback to v4 + this.value = uuidv4(); + this.onFieldChange.emit(this.value); + } + } + + validateUuid(value: string): boolean { + return uuidValidate(value); + } + + getUuidVersion(value: string): number | false { + try { + return uuidVersion(value); + } catch { + return false; + } + } +} diff --git a/frontend/src/app/components/ui-components/record-view-fields/base-record-view-field/base-record-view-field.component.css b/frontend/src/app/components/ui-components/record-view-fields/base-record-view-field/base-record-view-field.component.css index 967fc930d..0694f24bb 100644 --- a/frontend/src/app/components/ui-components/record-view-fields/base-record-view-field/base-record-view-field.component.css +++ b/frontend/src/app/components/ui-components/record-view-fields/base-record-view-field/base-record-view-field.component.css @@ -1,25 +1,25 @@ .field-view-value { - word-wrap: anywhere; - word-break: keep-all; - white-space: break-spaces; - max-width: 100%; + word-wrap: anywhere; + word-break: keep-all; + white-space: break-spaces; + max-width: 100%; } @media (prefers-color-scheme: light) { - .field-view-value { - color: rgba(0, 0, 0, 0.64); - } + .field-view-value { + color: rgba(0, 0, 0, 0.64); + } } @media (prefers-color-scheme: dark) { - .field-view-value { - color: rgba(255, 255, 255, 0.64); - } + .field-view-value { + color: rgba(255, 255, 255, 0.64); + } } .field-view-icon { - font-size: 16px; - height: 16px; - margin-left: 8px; - width: 16px; -} \ No newline at end of file + font-size: 16px; + height: 16px; + margin-left: 8px; + width: 16px; +} diff --git a/frontend/src/app/components/ui-components/record-view-fields/binary-data-caption/binary-data-caption.component.ts b/frontend/src/app/components/ui-components/record-view-fields/binary-data-caption/binary-data-caption.component.ts index c377e06d0..ae72360c3 100644 --- a/frontend/src/app/components/ui-components/record-view-fields/binary-data-caption/binary-data-caption.component.ts +++ b/frontend/src/app/components/ui-components/record-view-fields/binary-data-caption/binary-data-caption.component.ts @@ -7,10 +7,9 @@ import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-rec @Injectable() @Component({ - selector: 'app-binary-data-caption-record-view', - templateUrl: './binary-data-caption.component.html', - styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './binary-data-caption.component.css'], - imports: [CommonModule, MatIconModule, MatButtonModule, MatTooltipModule] + selector: 'app-binary-data-caption-record-view', + templateUrl: './binary-data-caption.component.html', + styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './binary-data-caption.component.css'], + imports: [CommonModule, MatIconModule, MatButtonModule, MatTooltipModule], }) -export class BinaryDataCaptionRecordViewComponent extends BaseRecordViewFieldComponent { -} +export class BinaryDataCaptionRecordViewComponent extends BaseRecordViewFieldComponent {} diff --git a/frontend/src/app/components/ui-components/record-view-fields/boolean/boolean.component.css b/frontend/src/app/components/ui-components/record-view-fields/boolean/boolean.component.css index 603046a74..30d4073c1 100644 --- a/frontend/src/app/components/ui-components/record-view-fields/boolean/boolean.component.css +++ b/frontend/src/app/components/ui-components/record-view-fields/boolean/boolean.component.css @@ -1,27 +1,23 @@ .boolean-icon { - transform: scale(0.85); + transform: scale(0.85); } @media (prefers-color-scheme: light) { - .boolean-icon-true { - color: #1B5E20; - } + .boolean-icon-true { + color: #1b5e20; + } - .boolean-icon-false { - color: #B71C1C; - } + .boolean-icon-false { + color: #b71c1c; + } } @media (prefers-color-scheme: dark) { - .boolean-icon-true { - color: #4CAF50; - } + .boolean-icon-true { + color: #4caf50; + } - .boolean-icon-false { - color: #E53935; - } + .boolean-icon-false { + color: #e53935; + } } - - - - diff --git a/frontend/src/app/components/ui-components/record-view-fields/boolean/boolean.component.ts b/frontend/src/app/components/ui-components/record-view-fields/boolean/boolean.component.ts index ef3ce9550..7abc99b3a 100644 --- a/frontend/src/app/components/ui-components/record-view-fields/boolean/boolean.component.ts +++ b/frontend/src/app/components/ui-components/record-view-fields/boolean/boolean.component.ts @@ -1,19 +1,17 @@ -import { Component, Injectable } from '@angular/core'; - - import { CommonModule } from '@angular/common'; +import { Component, Injectable } from '@angular/core'; import { MatIconModule } from '@angular/material/icon'; import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-record-view-field.component'; @Injectable() @Component({ - selector: 'app-boolean-record-view', - templateUrl: './boolean.component.html', - styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './boolean.component.css'], - imports: [MatIconModule, CommonModule] + selector: 'app-boolean-record-view', + templateUrl: './boolean.component.html', + styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './boolean.component.css'], + imports: [MatIconModule, CommonModule], }) export class BooleanRecordViewComponent extends BaseRecordViewFieldComponent { - get invertColors(): boolean { - return this.widgetStructure?.widget_params?.invertColors === true; - } + get invertColors(): boolean { + return this.widgetStructure?.widget_params?.invertColors === true; + } } diff --git a/frontend/src/app/components/ui-components/record-view-fields/code/code.component.css b/frontend/src/app/components/ui-components/record-view-fields/code/code.component.css index 103d0c4a3..8f1be46a3 100644 --- a/frontend/src/app/components/ui-components/record-view-fields/code/code.component.css +++ b/frontend/src/app/components/ui-components/record-view-fields/code/code.component.css @@ -1,20 +1,20 @@ :host { - width: 100%; + width: 100%; } .code-editor-box { - display: block; - border: 1px solid rgba(0, 0, 0, 0.38); - border-radius: 0; - margin-top: 4px; - margin-bottom: 20px; - /* overflow-y: auto; + display: block; + border: 1px solid rgba(0, 0, 0, 0.38); + border-radius: 0; + margin-top: 4px; + margin-bottom: 20px; + /* overflow-y: auto; resize: vertical; */ - overflow: auto; - resize: both; + overflow: auto; + resize: both; } .code-editor-box ::ng-deep .ngs-code-editor { - width: 100%; - height: 100%; -} \ No newline at end of file + width: 100%; + height: 100%; +} diff --git a/frontend/src/app/components/ui-components/record-view-fields/code/code.component.ts b/frontend/src/app/components/ui-components/record-view-fields/code/code.component.ts index 280189ed3..edc7c67bd 100644 --- a/frontend/src/app/components/ui-components/record-view-fields/code/code.component.ts +++ b/frontend/src/app/components/ui-components/record-view-fields/code/code.component.ts @@ -1,40 +1,37 @@ -import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-record-view-field.component'; import { Component, Injectable } from '@angular/core'; import { CodeEditorModule } from '@ngstack/code-editor'; import { UiSettingsService } from 'src/app/services/ui-settings.service'; +import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-record-view-field.component'; @Injectable() @Component({ - selector: 'app-code-record-view', - templateUrl: './code.component.html', - styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './code.component.css'], - imports: [CodeEditorModule] + selector: 'app-code-record-view', + templateUrl: './code.component.html', + styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './code.component.css'], + imports: [CodeEditorModule], }) export class CodeRecordViewComponent extends BaseRecordViewFieldComponent { + public codeModel: Object; + public codeEditorOptions = { + minimap: { enabled: false }, + automaticLayout: true, + scrollBeyondLastLine: false, + wordWrap: 'on', + lineNumbers: 'off', + }; + public codeEditorTheme = 'vs-dark'; - public codeModel: Object; - public codeEditorOptions = { - minimap: { enabled: false }, - automaticLayout: true, - scrollBeyondLastLine: false, - wordWrap: 'on', - lineNumbers: 'off' - }; - public codeEditorTheme = 'vs-dark'; - - constructor( - private _uiSettings: UiSettingsService, - ) { - super(); - } + constructor(private _uiSettings: UiSettingsService) { + super(); + } - ngOnInit(): void { - this.codeModel = { - language: `${this.widgetStructure.widget_params.language}`, - uri: `${this.key}.json`, - value: this.value, - } + ngOnInit(): void { + this.codeModel = { + language: `${this.widgetStructure.widget_params.language}`, + uri: `${this.key}.json`, + value: this.value, + }; - this.codeEditorTheme = this._uiSettings.isDarkMode ? 'vs-dark' : 'vs'; - } + this.codeEditorTheme = this._uiSettings.isDarkMode ? 'vs-dark' : 'vs'; + } } diff --git a/frontend/src/app/components/ui-components/record-view-fields/color/color.component.css b/frontend/src/app/components/ui-components/record-view-fields/color/color.component.css index 273272d64..bd573c4fa 100644 --- a/frontend/src/app/components/ui-components/record-view-fields/color/color.component.css +++ b/frontend/src/app/components/ui-components/record-view-fields/color/color.component.css @@ -1,17 +1,17 @@ .color-display { - display: flex; - align-items: center; - gap: 8px; + display: flex; + align-items: center; + gap: 8px; } .color-swatch { - width: 20px; - height: 20px; - border-radius: 4px; - /* border: 1px solid #ccc; */ - flex-shrink: 0; + width: 20px; + height: 20px; + border-radius: 4px; + /* border: 1px solid #ccc; */ + flex-shrink: 0; } .field-value { - font-family: monospace; -} \ No newline at end of file + font-family: monospace; +} diff --git a/frontend/src/app/components/ui-components/record-view-fields/color/color.component.ts b/frontend/src/app/components/ui-components/record-view-fields/color/color.component.ts index 581351e80..4553fc2c0 100644 --- a/frontend/src/app/components/ui-components/record-view-fields/color/color.component.ts +++ b/frontend/src/app/components/ui-components/record-view-fields/color/color.component.ts @@ -1,49 +1,47 @@ +import { CommonModule } from '@angular/common'; import { Component, Injectable } from '@angular/core'; - -import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-record-view-field.component'; import colorString from 'color-string'; -import { CommonModule } from '@angular/common'; +import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-record-view-field.component'; @Injectable() @Component({ - selector: 'app-color-record-view', - templateUrl: './color.component.html', - styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './color.component.css'], - imports: [CommonModule] + selector: 'app-color-record-view', + templateUrl: './color.component.html', + styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './color.component.css'], + imports: [CommonModule], }) export class ColorRecordViewComponent extends BaseRecordViewFieldComponent { - get isValidColor(): boolean { - if (!this.value) return false; - return this.parseColor(this.value) !== null; - } - - get normalizedColorForDisplay(): string { - const parsed = this.parseColor(this.value); - if (parsed) { - const [r, g, b] = parsed.value; - return `#${this.toHex(r)}${this.toHex(g)}${this.toHex(b)}`; - } - return '#000000'; - } - - - private parseColor(color: string): any { - if (!color) return null; - - // Try parsing with color-string - const parsed = colorString.get(color); - if (parsed) return parsed; - - // Try hex without hash - if (/^[A-Fa-f0-9]{6}$|^[A-Fa-f0-9]{3}$/.test(color)) { - return colorString.get('#' + color); - } - - return null; - } - - private toHex(n: number): string { - const hex = n.toString(16); - return hex.length === 1 ? '0' + hex : hex; - } -} \ No newline at end of file + get isValidColor(): boolean { + if (!this.value) return false; + return this.parseColor(this.value) !== null; + } + + get normalizedColorForDisplay(): string { + const parsed = this.parseColor(this.value); + if (parsed) { + const [r, g, b] = parsed.value; + return `#${this.toHex(r)}${this.toHex(g)}${this.toHex(b)}`; + } + return '#000000'; + } + + private parseColor(color: string): any { + if (!color) return null; + + // Try parsing with color-string + const parsed = colorString.get(color); + if (parsed) return parsed; + + // Try hex without hash + if (/^[A-Fa-f0-9]{6}$|^[A-Fa-f0-9]{3}$/.test(color)) { + return colorString.get('#' + color); + } + + return null; + } + + private toHex(n: number): string { + const hex = n.toString(16); + return hex.length === 1 ? '0' + hex : hex; + } +} diff --git a/frontend/src/app/components/ui-components/record-view-fields/country/country.component.css b/frontend/src/app/components/ui-components/record-view-fields/country/country.component.css index cc2400ea0..e1c4f4d1a 100644 --- a/frontend/src/app/components/ui-components/record-view-fields/country/country.component.css +++ b/frontend/src/app/components/ui-components/record-view-fields/country/country.component.css @@ -1,4 +1,4 @@ .country-flag { - margin-right: 6px; - font-size: 1.2em; + margin-right: 6px; + font-size: 1.2em; } diff --git a/frontend/src/app/components/ui-components/record-view-fields/country/country.component.ts b/frontend/src/app/components/ui-components/record-view-fields/country/country.component.ts index 74c4fa7da..2d2b98a5f 100644 --- a/frontend/src/app/components/ui-components/record-view-fields/country/country.component.ts +++ b/frontend/src/app/components/ui-components/record-view-fields/country/country.component.ts @@ -1,49 +1,49 @@ +import { CommonModule } from '@angular/common'; +import { Component, Injectable, OnInit } from '@angular/core'; import { COUNTRIES, getCountryFlag } from '../../../../consts/countries'; -import { Component, Injectable, OnInit, } from '@angular/core'; - import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-record-view-field.component'; -import { CommonModule } from '@angular/common'; @Injectable() @Component({ - selector: 'app-country-record-view', - templateUrl: './country.component.html', - styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './country.component.css'], - imports: [CommonModule] + selector: 'app-country-record-view', + templateUrl: './country.component.html', + styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './country.component.css'], + imports: [CommonModule], }) export class CountryRecordViewComponent extends BaseRecordViewFieldComponent implements OnInit { - static type = 'country'; + static type = 'country'; - public countryName: string = ''; - public countryFlag: string = ''; - public showFlag: boolean = true; + public countryName: string = ''; + public countryFlag: string = ''; + public showFlag: boolean = true; - ngOnInit(): void { - this.parseWidgetParams(); + ngOnInit(): void { + this.parseWidgetParams(); - if (this.value) { - const country = COUNTRIES.find(c => c.code === this.value); - this.countryName = country ? country.name : this.value; - this.countryFlag = getCountryFlag(this.value); - } else { - this.countryName = '—'; - this.countryFlag = ''; - } - } + if (this.value) { + const country = COUNTRIES.find((c) => c.code === this.value); + this.countryName = country ? country.name : this.value; + this.countryFlag = getCountryFlag(this.value); + } else { + this.countryName = '—'; + this.countryFlag = ''; + } + } - private parseWidgetParams(): void { - if (this.widgetStructure?.widget_params) { - try { - const params = typeof this.widgetStructure.widget_params === 'string' - ? JSON.parse(this.widgetStructure.widget_params) - : this.widgetStructure.widget_params; + private parseWidgetParams(): void { + if (this.widgetStructure?.widget_params) { + try { + const params = + typeof this.widgetStructure.widget_params === 'string' + ? JSON.parse(this.widgetStructure.widget_params) + : this.widgetStructure.widget_params; - if (params.show_flag !== undefined) { - this.showFlag = params.show_flag; - } - } catch (e) { - console.error('Error parsing country widget params:', e); - } - } - } + if (params.show_flag !== undefined) { + this.showFlag = params.show_flag; + } + } catch (e) { + console.error('Error parsing country widget params:', e); + } + } + } } diff --git a/frontend/src/app/components/ui-components/record-view-fields/date-time/date-time.component.css b/frontend/src/app/components/ui-components/record-view-fields/date-time/date-time.component.css index 8b1378917..e69de29bb 100644 --- a/frontend/src/app/components/ui-components/record-view-fields/date-time/date-time.component.css +++ b/frontend/src/app/components/ui-components/record-view-fields/date-time/date-time.component.css @@ -1 +0,0 @@ - diff --git a/frontend/src/app/components/ui-components/record-view-fields/date-time/date-time.component.ts b/frontend/src/app/components/ui-components/record-view-fields/date-time/date-time.component.ts index a36abe6ba..0d734bd9f 100644 --- a/frontend/src/app/components/ui-components/record-view-fields/date-time/date-time.component.ts +++ b/frontend/src/app/components/ui-components/record-view-fields/date-time/date-time.component.ts @@ -1,32 +1,31 @@ import { Component, Injectable, OnInit } from '@angular/core'; - -import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-record-view-field.component'; import { format } from 'date-fns'; +import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-record-view-field.component'; @Injectable() @Component({ - selector: 'app-date-time-record-view', - templateUrl: './date-time.component.html', - styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './date-time.component.css'], - imports: [] + selector: 'app-date-time-record-view', + templateUrl: './date-time.component.html', + styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './date-time.component.css'], + imports: [], }) export class DateTimeRecordViewComponent extends BaseRecordViewFieldComponent implements OnInit { - static type = 'datetime'; + static type = 'datetime'; - public formattedDateTime: string; + public formattedDateTime: string; - ngOnInit(): void { - if (this.value) { - try { - const date = new Date(this.value); - if (!Number.isNaN(date.getTime())) { - this.formattedDateTime = format(date, "P p"); - } else { - this.formattedDateTime = this.value; - } - } catch (_error) { - this.formattedDateTime = this.value; - } - } - } + ngOnInit(): void { + if (this.value) { + try { + const date = new Date(this.value); + if (!Number.isNaN(date.getTime())) { + this.formattedDateTime = format(date, 'P p'); + } else { + this.formattedDateTime = this.value; + } + } catch (_error) { + this.formattedDateTime = this.value; + } + } + } } diff --git a/frontend/src/app/components/ui-components/record-view-fields/date/date.component.css b/frontend/src/app/components/ui-components/record-view-fields/date/date.component.css index 8b1378917..e69de29bb 100644 --- a/frontend/src/app/components/ui-components/record-view-fields/date/date.component.css +++ b/frontend/src/app/components/ui-components/record-view-fields/date/date.component.css @@ -1 +0,0 @@ - diff --git a/frontend/src/app/components/ui-components/record-view-fields/date/date.component.ts b/frontend/src/app/components/ui-components/record-view-fields/date/date.component.ts index 31fb8c8b0..72a7b0290 100644 --- a/frontend/src/app/components/ui-components/record-view-fields/date/date.component.ts +++ b/frontend/src/app/components/ui-components/record-view-fields/date/date.component.ts @@ -1,32 +1,32 @@ import { Component, Injectable, OnInit } from '@angular/core'; -import { format, } from 'date-fns'; +import { format } from 'date-fns'; import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-record-view-field.component'; @Injectable() @Component({ - selector: 'app-date-record-view', - templateUrl: './date.component.html', - styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './date.component.css'], - imports: [] + selector: 'app-date-record-view', + templateUrl: './date.component.html', + styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './date.component.css'], + imports: [], }) export class DateRecordViewComponent extends BaseRecordViewFieldComponent implements OnInit { - static type = 'date'; + static type = 'date'; - public formattedDate: string; + public formattedDate: string; - ngOnInit(): void { - if (this.value) { - try { - const date = new Date(this.value); - if (!Number.isNaN(date.getTime())) { - this.formattedDate = format(date, "P"); - } else { - this.formattedDate = this.value; - } - } catch (_error) { - this.formattedDate = this.value; - } - } - } + ngOnInit(): void { + if (this.value) { + try { + const date = new Date(this.value); + if (!Number.isNaN(date.getTime())) { + this.formattedDate = format(date, 'P'); + } else { + this.formattedDate = this.value; + } + } catch (_error) { + this.formattedDate = this.value; + } + } + } } diff --git a/frontend/src/app/components/ui-components/record-view-fields/file/file.component.css b/frontend/src/app/components/ui-components/record-view-fields/file/file.component.css index 7f667e698..05f5eebc1 100644 --- a/frontend/src/app/components/ui-components/record-view-fields/file/file.component.css +++ b/frontend/src/app/components/ui-components/record-view-fields/file/file.component.css @@ -1,4 +1,4 @@ .field-value { - font-style: italic; - color: #666; + font-style: italic; + color: #666; } diff --git a/frontend/src/app/components/ui-components/record-view-fields/file/file.component.ts b/frontend/src/app/components/ui-components/record-view-fields/file/file.component.ts index 77dfad7d5..87ca8ee33 100644 --- a/frontend/src/app/components/ui-components/record-view-fields/file/file.component.ts +++ b/frontend/src/app/components/ui-components/record-view-fields/file/file.component.ts @@ -1,29 +1,29 @@ -import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-record-view-field.component'; import { Component, Injectable } from '@angular/core'; +import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-record-view-field.component'; interface Blob { - type: string; - data: any[]; + type: string; + data: any[]; } @Injectable() @Component({ - selector: 'app-file-record-view', - templateUrl: './file.component.html', - styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './file.component.css'], - imports: [] + selector: 'app-file-record-view', + templateUrl: './file.component.html', + styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './file.component.css'], + imports: [], }) export class FileRecordViewComponent extends BaseRecordViewFieldComponent { - get isBlob(): boolean { - return typeof this.value === 'object' && this.value !== null && 'type' in this.value && 'data' in this.value; - } + get isBlob(): boolean { + return typeof this.value === 'object' && this.value !== null && 'type' in this.value && 'data' in this.value; + } - get displayText(): string { - if (this.isBlob) { - return 'Binary Data'; - } else if (typeof this.value === 'string' && this.value.length > 20) { - return 'Binary Data'; - } - return this.value ? String(this.value) : '—'; - } + get displayText(): string { + if (this.isBlob) { + return 'Binary Data'; + } else if (typeof this.value === 'string' && this.value.length > 20) { + return 'Binary Data'; + } + return this.value ? String(this.value) : '—'; + } } diff --git a/frontend/src/app/components/ui-components/record-view-fields/foreign-key/foreign-key.component.css b/frontend/src/app/components/ui-components/record-view-fields/foreign-key/foreign-key.component.css index 700cad576..f18e255d5 100644 --- a/frontend/src/app/components/ui-components/record-view-fields/foreign-key/foreign-key.component.css +++ b/frontend/src/app/components/ui-components/record-view-fields/foreign-key/foreign-key.component.css @@ -1,17 +1,17 @@ .foreign-key-link { - display: flex; - align-items: center; - text-decoration: none; + display: flex; + align-items: center; + text-decoration: none; } @media (prefers-color-scheme: light) { - .foreign-key-link { - border-bottom: 1px solid rgba(0, 0, 0, 0.64); - } + .foreign-key-link { + border-bottom: 1px solid rgba(0, 0, 0, 0.64); + } } @media (prefers-color-scheme: dark) { - .foreign-key-link { - border-bottom: 1px solid rgba(255, 255, 255, 0.64); - } -} \ No newline at end of file + .foreign-key-link { + border-bottom: 1px solid rgba(255, 255, 255, 0.64); + } +} diff --git a/frontend/src/app/components/ui-components/record-view-fields/foreign-key/foreign-key.component.ts b/frontend/src/app/components/ui-components/record-view-fields/foreign-key/foreign-key.component.ts index 5dc66fc39..4e1be41fd 100644 --- a/frontend/src/app/components/ui-components/record-view-fields/foreign-key/foreign-key.component.ts +++ b/frontend/src/app/components/ui-components/record-view-fields/foreign-key/foreign-key.component.ts @@ -1,27 +1,26 @@ -import { Component, EventEmitter, Injectable, Input, OnInit, Output } from '@angular/core'; - -import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-record-view-field.component'; import { CommonModule } from '@angular/common'; +import { Component, EventEmitter, Injectable, Input, OnInit, Output } from '@angular/core'; import { MatIconModule } from '@angular/material/icon'; import { RouterModule } from '@angular/router'; +import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-record-view-field.component'; @Injectable() @Component({ - selector: 'app-foreign-key-record-view', - templateUrl: './foreign-key.component.html', - styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './foreign-key.component.css'], - imports: [MatIconModule, RouterModule, CommonModule] + selector: 'app-foreign-key-record-view', + templateUrl: './foreign-key.component.html', + styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './foreign-key.component.css'], + imports: [MatIconModule, RouterModule, CommonModule], }) export class ForeignKeyRecordViewComponent extends BaseRecordViewFieldComponent implements OnInit { - @Input() link: string; - @Input() primaryKeysParams: any; - @Input() displayValue: string; + @Input() link: string; + @Input() primaryKeysParams: any; + @Input() displayValue: string; - @Output() onForeignKeyClick = new EventEmitter<{foreignKey: any, value: string}>(); + @Output() onForeignKeyClick = new EventEmitter<{ foreignKey: any; value: string }>(); - public foreignKeyURLParams: any; + public foreignKeyURLParams: any; - ngOnInit() { - this.foreignKeyURLParams = {...this.primaryKeysParams, mode: 'view'} - } + ngOnInit() { + this.foreignKeyURLParams = { ...this.primaryKeysParams, mode: 'view' }; + } } diff --git a/frontend/src/app/components/ui-components/record-view-fields/id/id.component.css b/frontend/src/app/components/ui-components/record-view-fields/id/id.component.css index 277f14993..405dcba7f 100644 --- a/frontend/src/app/components/ui-components/record-view-fields/id/id.component.css +++ b/frontend/src/app/components/ui-components/record-view-fields/id/id.component.css @@ -1,9 +1,9 @@ .field-display-id { - display: flex; - align-items: center; + display: flex; + align-items: center; } .field-value-id { - font-weight: 500; - flex-grow: 1; + font-weight: 500; + flex-grow: 1; } diff --git a/frontend/src/app/components/ui-components/record-view-fields/id/id.component.ts b/frontend/src/app/components/ui-components/record-view-fields/id/id.component.ts index 0289f4b78..bb01857af 100644 --- a/frontend/src/app/components/ui-components/record-view-fields/id/id.component.ts +++ b/frontend/src/app/components/ui-components/record-view-fields/id/id.component.ts @@ -4,10 +4,9 @@ import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-rec @Injectable() @Component({ - selector: 'app-id-record-view', - templateUrl: './id.component.html', - styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './id.component.css'], - imports: [] + selector: 'app-id-record-view', + templateUrl: './id.component.html', + styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './id.component.css'], + imports: [], }) -export class IdRecordViewComponent extends BaseRecordViewFieldComponent { -} +export class IdRecordViewComponent extends BaseRecordViewFieldComponent {} diff --git a/frontend/src/app/components/ui-components/record-view-fields/image/image.component.css b/frontend/src/app/components/ui-components/record-view-fields/image/image.component.css index 5decf8e34..dab143174 100644 --- a/frontend/src/app/components/ui-components/record-view-fields/image/image.component.css +++ b/frontend/src/app/components/ui-components/record-view-fields/image/image.component.css @@ -1,4 +1,4 @@ .image-preview { - width: 100%; - height: auto; + width: 100%; + height: auto; } diff --git a/frontend/src/app/components/ui-components/record-view-fields/image/image.component.ts b/frontend/src/app/components/ui-components/record-view-fields/image/image.component.ts index 8d5d69db2..36eb14eb9 100644 --- a/frontend/src/app/components/ui-components/record-view-fields/image/image.component.ts +++ b/frontend/src/app/components/ui-components/record-view-fields/image/image.component.ts @@ -1,29 +1,29 @@ import { CommonModule } from '@angular/common'; -import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-record-view-field.component'; import { Component, Injectable } from '@angular/core'; +import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-record-view-field.component'; @Injectable() @Component({ - selector: 'app-image-record-view', - templateUrl: './image.component.html', - styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './image.component.css'], - imports: [CommonModule] + selector: 'app-image-record-view', + templateUrl: './image.component.html', + styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './image.component.css'], + imports: [CommonModule], }) export class ImageRecordViewComponent extends BaseRecordViewFieldComponent { - get srcValue(): string { - if (!this.value) return ''; - const prefix = this.widgetStructure?.widget_params?.prefix || ''; - return prefix + this.value; - } - - get isUrl(): boolean { - if (!this.value) return false; - try { - // Check if the prefixed URL is valid - new URL(this.srcValue); - return true; - } catch { - return false; - } - } + get srcValue(): string { + if (!this.value) return ''; + const prefix = this.widgetStructure?.widget_params?.prefix || ''; + return prefix + this.value; + } + + get isUrl(): boolean { + if (!this.value) return false; + try { + // Check if the prefixed URL is valid + new URL(this.srcValue); + return true; + } catch { + return false; + } + } } diff --git a/frontend/src/app/components/ui-components/record-view-fields/json-editor/json-editor.component.css b/frontend/src/app/components/ui-components/record-view-fields/json-editor/json-editor.component.css index 103d0c4a3..8f1be46a3 100644 --- a/frontend/src/app/components/ui-components/record-view-fields/json-editor/json-editor.component.css +++ b/frontend/src/app/components/ui-components/record-view-fields/json-editor/json-editor.component.css @@ -1,20 +1,20 @@ :host { - width: 100%; + width: 100%; } .code-editor-box { - display: block; - border: 1px solid rgba(0, 0, 0, 0.38); - border-radius: 0; - margin-top: 4px; - margin-bottom: 20px; - /* overflow-y: auto; + display: block; + border: 1px solid rgba(0, 0, 0, 0.38); + border-radius: 0; + margin-top: 4px; + margin-bottom: 20px; + /* overflow-y: auto; resize: vertical; */ - overflow: auto; - resize: both; + overflow: auto; + resize: both; } .code-editor-box ::ng-deep .ngs-code-editor { - width: 100%; - height: 100%; -} \ No newline at end of file + width: 100%; + height: 100%; +} diff --git a/frontend/src/app/components/ui-components/record-view-fields/json-editor/json-editor.component.ts b/frontend/src/app/components/ui-components/record-view-fields/json-editor/json-editor.component.ts index d4f8fa809..98fb2ee25 100644 --- a/frontend/src/app/components/ui-components/record-view-fields/json-editor/json-editor.component.ts +++ b/frontend/src/app/components/ui-components/record-view-fields/json-editor/json-editor.component.ts @@ -1,39 +1,37 @@ -import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-record-view-field.component'; import { Component, Injectable } from '@angular/core'; import { CodeEditorModule } from '@ngstack/code-editor'; import { UiSettingsService } from 'src/app/services/ui-settings.service'; +import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-record-view-field.component'; @Injectable() @Component({ - selector: 'app-json-editor-record-view', - templateUrl: './json-editor.component.html', - styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './json-editor.component.css'], - imports: [CodeEditorModule] + selector: 'app-json-editor-record-view', + templateUrl: './json-editor.component.html', + styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './json-editor.component.css'], + imports: [CodeEditorModule], }) export class JsonEditorRecordViewComponent extends BaseRecordViewFieldComponent { - public codeModel: Object; - public codeEditorOptions = { - minimap: { enabled: false }, - automaticLayout: true, - scrollBeyondLastLine: false, - wordWrap: 'on', - lineNumbers: 'off' - }; - public codeEditorTheme = 'vs-dark'; + public codeModel: Object; + public codeEditorOptions = { + minimap: { enabled: false }, + automaticLayout: true, + scrollBeyondLastLine: false, + wordWrap: 'on', + lineNumbers: 'off', + }; + public codeEditorTheme = 'vs-dark'; - constructor( - private _uiSettings: UiSettingsService, - ) { - super(); - } + constructor(private _uiSettings: UiSettingsService) { + super(); + } - ngOnInit(): void { - this.codeModel = { - language: 'json', - uri: `${this.key}.json`, - value: this.value - } + ngOnInit(): void { + this.codeModel = { + language: 'json', + uri: `${this.key}.json`, + value: this.value, + }; - this.codeEditorTheme = this._uiSettings.isDarkMode ? 'vs-dark' : 'vs'; - } + this.codeEditorTheme = this._uiSettings.isDarkMode ? 'vs-dark' : 'vs'; + } } diff --git a/frontend/src/app/components/ui-components/record-view-fields/language/language.component.css b/frontend/src/app/components/ui-components/record-view-fields/language/language.component.css index 17911d54f..ca86f863b 100644 --- a/frontend/src/app/components/ui-components/record-view-fields/language/language.component.css +++ b/frontend/src/app/components/ui-components/record-view-fields/language/language.component.css @@ -1,4 +1,4 @@ .language-flag { - margin-right: 6px; - font-size: 1.2em; + margin-right: 6px; + font-size: 1.2em; } diff --git a/frontend/src/app/components/ui-components/record-view-fields/long-text/long-text.component.ts b/frontend/src/app/components/ui-components/record-view-fields/long-text/long-text.component.ts index 84c4f8733..991bc8272 100644 --- a/frontend/src/app/components/ui-components/record-view-fields/long-text/long-text.component.ts +++ b/frontend/src/app/components/ui-components/record-view-fields/long-text/long-text.component.ts @@ -4,11 +4,9 @@ import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-rec @Injectable() @Component({ - selector: 'app-long-text-record-view', - templateUrl: './long-text.component.html', - styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './long-text.component.css'], - imports: [] + selector: 'app-long-text-record-view', + templateUrl: './long-text.component.html', + styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './long-text.component.css'], + imports: [], }) -export class LongTextRecordViewComponent extends BaseRecordViewFieldComponent { - -} +export class LongTextRecordViewComponent extends BaseRecordViewFieldComponent {} diff --git a/frontend/src/app/components/ui-components/record-view-fields/markdown/markdown.component.css b/frontend/src/app/components/ui-components/record-view-fields/markdown/markdown.component.css index 5b2ca4ec5..8b4bb1a94 100644 --- a/frontend/src/app/components/ui-components/record-view-fields/markdown/markdown.component.css +++ b/frontend/src/app/components/ui-components/record-view-fields/markdown/markdown.component.css @@ -1,23 +1,23 @@ .markdown-container { - padding: 16px; - border-radius: 4px; - min-height: 100px; + padding: 16px; + border-radius: 4px; + min-height: 100px; } @media (prefers-color-scheme: light) { - .markdown-container { - background: #f9f9f9; - border: 1px solid #e0e0e0; - color: rgba(0, 0, 0, 0.64); - } + .markdown-container { + background: #f9f9f9; + border: 1px solid #e0e0e0; + color: rgba(0, 0, 0, 0.64); + } } @media (prefers-color-scheme: dark) { - .markdown-container { - background: #2a2a2a; - border: 1px solid #404040; - color: rgba(255, 255, 255, 0.64); - } + .markdown-container { + background: #2a2a2a; + border: 1px solid #404040; + color: rgba(255, 255, 255, 0.64); + } } .markdown-container ::ng-deep h1, @@ -26,71 +26,71 @@ .markdown-container ::ng-deep h4, .markdown-container ::ng-deep h5, .markdown-container ::ng-deep h6 { - margin-top: 16px; - margin-bottom: 8px; + margin-top: 16px; + margin-bottom: 8px; } .markdown-container ::ng-deep p { - margin-bottom: 8px; + margin-bottom: 8px; } .markdown-container ::ng-deep ul, .markdown-container ::ng-deep ol { - margin-left: 20px; - margin-bottom: 8px; + margin-left: 20px; + margin-bottom: 8px; } .markdown-container ::ng-deep code { - padding: 2px 4px; - border-radius: 3px; - font-family: monospace; + padding: 2px 4px; + border-radius: 3px; + font-family: monospace; } @media (prefers-color-scheme: light) { - .markdown-container ::ng-deep code { - background: #f5f5f5; - } + .markdown-container ::ng-deep code { + background: #f5f5f5; + } } @media (prefers-color-scheme: dark) { - .markdown-container ::ng-deep code { - background: #3a3a3a; - } + .markdown-container ::ng-deep code { + background: #3a3a3a; + } } .markdown-container ::ng-deep pre { - padding: 12px; - border-radius: 4px; - overflow-x: auto; + padding: 12px; + border-radius: 4px; + overflow-x: auto; } @media (prefers-color-scheme: light) { - .markdown-container ::ng-deep pre { - background: #f5f5f5; - } + .markdown-container ::ng-deep pre { + background: #f5f5f5; + } } @media (prefers-color-scheme: dark) { - .markdown-container ::ng-deep pre { - background: #3a3a3a; - } + .markdown-container ::ng-deep pre { + background: #3a3a3a; + } } .markdown-container ::ng-deep blockquote { - padding-left: 16px; - margin-left: 0; + padding-left: 16px; + margin-left: 0; } @media (prefers-color-scheme: light) { - .markdown-container ::ng-deep blockquote { - border-left: 4px solid #ddd; - color: #666; - } + .markdown-container ::ng-deep blockquote { + border-left: 4px solid #ddd; + color: #666; + } } @media (prefers-color-scheme: dark) { - .markdown-container ::ng-deep blockquote { - border-left: 4px solid #555; - color: #aaa; - } + .markdown-container ::ng-deep blockquote { + border-left: 4px solid #555; + color: #aaa; + } } diff --git a/frontend/src/app/components/ui-components/record-view-fields/markdown/markdown.component.spec.ts b/frontend/src/app/components/ui-components/record-view-fields/markdown/markdown.component.spec.ts index a4262aaca..d7a1af29d 100644 --- a/frontend/src/app/components/ui-components/record-view-fields/markdown/markdown.component.spec.ts +++ b/frontend/src/app/components/ui-components/record-view-fields/markdown/markdown.component.spec.ts @@ -1,30 +1,26 @@ +import { provideHttpClient } from '@angular/common/http'; import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { MarkdownRecordViewComponent } from './markdown.component'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { provideMarkdown } from 'ngx-markdown'; -import { provideHttpClient } from '@angular/common/http'; +import { MarkdownRecordViewComponent } from './markdown.component'; describe('MarkdownRecordViewComponent', () => { - let component: MarkdownRecordViewComponent; - let fixture: ComponentFixture; + let component: MarkdownRecordViewComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [MarkdownRecordViewComponent, BrowserAnimationsModule], - providers: [ - provideHttpClient(), - provideMarkdown() - ] - }).compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [MarkdownRecordViewComponent, BrowserAnimationsModule], + providers: [provideHttpClient(), provideMarkdown()], + }).compileComponents(); - fixture = TestBed.createComponent(MarkdownRecordViewComponent); - component = fixture.componentInstance; - component.value = '# Test Markdown'; - fixture.detectChanges(); - }); + fixture = TestBed.createComponent(MarkdownRecordViewComponent); + component = fixture.componentInstance; + component.value = '# Test Markdown'; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/ui-components/record-view-fields/markdown/markdown.component.ts b/frontend/src/app/components/ui-components/record-view-fields/markdown/markdown.component.ts index 9972dae88..09dda79c5 100644 --- a/frontend/src/app/components/ui-components/record-view-fields/markdown/markdown.component.ts +++ b/frontend/src/app/components/ui-components/record-view-fields/markdown/markdown.component.ts @@ -1,19 +1,19 @@ -import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-record-view-field.component'; -import { Component, Injectable, OnInit } from '@angular/core'; import { CommonModule } from '@angular/common'; +import { Component, Injectable, OnInit } from '@angular/core'; import { MarkdownModule } from 'ngx-markdown'; +import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-record-view-field.component'; @Injectable() @Component({ - selector: 'app-markdown-record-view', - templateUrl: './markdown.component.html', - styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './markdown.component.css'], - imports: [CommonModule, MarkdownModule] + selector: 'app-markdown-record-view', + templateUrl: './markdown.component.html', + styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './markdown.component.css'], + imports: [CommonModule, MarkdownModule], }) export class MarkdownRecordViewComponent extends BaseRecordViewFieldComponent implements OnInit { - public renderedMarkdown: string = ''; + public renderedMarkdown: string = ''; - ngOnInit(): void { - this.renderedMarkdown = this.value || ''; - } + ngOnInit(): void { + this.renderedMarkdown = this.value || ''; + } } diff --git a/frontend/src/app/components/ui-components/record-view-fields/money/money.component.css b/frontend/src/app/components/ui-components/record-view-fields/money/money.component.css index 8b1378917..e69de29bb 100644 --- a/frontend/src/app/components/ui-components/record-view-fields/money/money.component.css +++ b/frontend/src/app/components/ui-components/record-view-fields/money/money.component.css @@ -1 +0,0 @@ - diff --git a/frontend/src/app/components/ui-components/record-view-fields/money/money.component.ts b/frontend/src/app/components/ui-components/record-view-fields/money/money.component.ts index 440e48657..155d917c5 100644 --- a/frontend/src/app/components/ui-components/record-view-fields/money/money.component.ts +++ b/frontend/src/app/components/ui-components/record-view-fields/money/money.component.ts @@ -1,57 +1,56 @@ import { Component, Injectable, OnInit } from '@angular/core'; - -import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-record-view-field.component'; import { getCurrencyByCode } from 'src/app/consts/currencies'; +import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-record-view-field.component'; @Injectable() @Component({ - selector: 'app-money-record-view', - templateUrl: './money.component.html', - styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './money.component.css'], - imports: [] + selector: 'app-money-record-view', + templateUrl: './money.component.html', + styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './money.component.css'], + imports: [], }) export class MoneyRecordViewComponent extends BaseRecordViewFieldComponent implements OnInit { - public displayCurrency: string = ''; - public currencySymbol: string = ''; - - ngOnInit(): void { - // Get currency from widget params - this.displayCurrency = ''; - if (this.widgetStructure?.widget_params?.default_currency) { - this.displayCurrency = this.widgetStructure.widget_params.default_currency; - const currency = getCurrencyByCode(this.displayCurrency); - this.currencySymbol = currency ? currency.symbol : ''; - } - } - - get formattedValue(): string { - if (!this.value) { - return ''; - } - - let amount: number | string; - let currency: string = this.displayCurrency; - - if (typeof this.value === 'object' && this.value.amount !== undefined) { - amount = this.value.amount; - if (this.value.currency) { - currency = this.value.currency; - const currencyObj = getCurrencyByCode(currency); - this.currencySymbol = currencyObj ? currencyObj.symbol : ''; - } - } else { - amount = this.value; - } - - if (typeof amount === 'string') { - amount = parseFloat(amount); - } - - if (Number.isNaN(amount as number)) { - return ''; - } - - const decimalPlaces = this.widgetStructure?.widget_params?.decimal_places ?? 2; - return `${this.currencySymbol}${(amount as number).toFixed(decimalPlaces)}`; - } + public displayCurrency: string = ''; + public currencySymbol: string = ''; + + ngOnInit(): void { + // Get currency from widget params + this.displayCurrency = ''; + if (this.widgetStructure?.widget_params?.default_currency) { + this.displayCurrency = this.widgetStructure.widget_params.default_currency; + const currency = getCurrencyByCode(this.displayCurrency); + this.currencySymbol = currency ? currency.symbol : ''; + } + } + + get formattedValue(): string { + if (!this.value) { + return ''; + } + + let amount: number | string; + let currency: string = this.displayCurrency; + + if (typeof this.value === 'object' && this.value.amount !== undefined) { + amount = this.value.amount; + if (this.value.currency) { + currency = this.value.currency; + const currencyObj = getCurrencyByCode(currency); + this.currencySymbol = currencyObj ? currencyObj.symbol : ''; + } + } else { + amount = this.value; + } + + if (typeof amount === 'string') { + amount = parseFloat(amount); + } + + if (Number.isNaN(amount as number)) { + return ''; + } + + const decimalPlaces = this.widgetStructure?.widget_params?.decimal_places ?? 2; + return `${this.currencySymbol}${(amount as number).toFixed(decimalPlaces)}`; + } } diff --git a/frontend/src/app/components/ui-components/record-view-fields/number/number.component.css b/frontend/src/app/components/ui-components/record-view-fields/number/number.component.css index 893313654..140aa7fbc 100644 --- a/frontend/src/app/components/ui-components/record-view-fields/number/number.component.css +++ b/frontend/src/app/components/ui-components/record-view-fields/number/number.component.css @@ -1,11 +1,11 @@ .out-of-threshold { - display: flex; - align-items: center; - gap: 2px; + display: flex; + align-items: center; + gap: 2px; } .out-of-threshold__icon { - font-size: 16px; - width: 16px; - height: 16px; -} \ No newline at end of file + font-size: 16px; + width: 16px; + height: 16px; +} diff --git a/frontend/src/app/components/ui-components/record-view-fields/number/number.component.ts b/frontend/src/app/components/ui-components/record-view-fields/number/number.component.ts index 1cd47b51a..30b9fdc64 100644 --- a/frontend/src/app/components/ui-components/record-view-fields/number/number.component.ts +++ b/frontend/src/app/components/ui-components/record-view-fields/number/number.component.ts @@ -1,58 +1,56 @@ -import { Component, Injectable } from '@angular/core'; - -import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-record-view-field.component'; import { CommonModule } from '@angular/common'; +import { Component, Injectable } from '@angular/core'; import { MatIconModule } from '@angular/material/icon'; import convert from 'convert'; +import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-record-view-field.component'; @Injectable() @Component({ - selector: 'app-number-record-view', - templateUrl: './number.component.html', - styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './number.component.css'], - imports: [CommonModule, MatIconModule] + selector: 'app-number-record-view', + templateUrl: './number.component.html', + styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './number.component.css'], + imports: [CommonModule, MatIconModule], }) export class NumberRecordViewComponent extends BaseRecordViewFieldComponent { - - get displayValue(): string { - if (this.value == null || this.value === '') { - return '—'; - } - - const unit = this.widgetStructure?.widget_params?.unit; - - if (!unit) { - return this.value.toString(); - } - - try { - const convertedValue = convert(parseFloat(this.value), unit).to('best'); - // Format number to max 2 decimal places without trailing zeros - const formattedQuantity = parseFloat(convertedValue.quantity.toFixed(2)).toString(); - return `${formattedQuantity} ${convertedValue.unit}`; - } catch (error) { - console.warn('Unit conversion failed:', error); - return this.value.toString(); - } - } - - get isOutOfThreshold(): 'up' | 'down' | false { - if (this.value == null || this.value === '') { - return false; - } - - const thresholdMin = this.widgetStructure?.widget_params?.threshold_min; - const thresholdMax = this.widgetStructure?.widget_params?.threshold_max; - const numValue = parseFloat(this.value); - - if (thresholdMin !== undefined && numValue < thresholdMin) { - return 'down'; - } - - if (thresholdMax !== undefined && numValue > thresholdMax) { - return 'up'; - } - - return false; - } + get displayValue(): string { + if (this.value == null || this.value === '') { + return '—'; + } + + const unit = this.widgetStructure?.widget_params?.unit; + + if (!unit) { + return this.value.toString(); + } + + try { + const convertedValue = convert(parseFloat(this.value), unit).to('best'); + // Format number to max 2 decimal places without trailing zeros + const formattedQuantity = parseFloat(convertedValue.quantity.toFixed(2)).toString(); + return `${formattedQuantity} ${convertedValue.unit}`; + } catch (error) { + console.warn('Unit conversion failed:', error); + return this.value.toString(); + } + } + + get isOutOfThreshold(): 'up' | 'down' | false { + if (this.value == null || this.value === '') { + return false; + } + + const thresholdMin = this.widgetStructure?.widget_params?.threshold_min; + const thresholdMax = this.widgetStructure?.widget_params?.threshold_max; + const numValue = parseFloat(this.value); + + if (thresholdMin !== undefined && numValue < thresholdMin) { + return 'down'; + } + + if (thresholdMax !== undefined && numValue > thresholdMax) { + return 'up'; + } + + return false; + } } diff --git a/frontend/src/app/components/ui-components/record-view-fields/password/password.component.css b/frontend/src/app/components/ui-components/record-view-fields/password/password.component.css index 8b1378917..e69de29bb 100644 --- a/frontend/src/app/components/ui-components/record-view-fields/password/password.component.css +++ b/frontend/src/app/components/ui-components/record-view-fields/password/password.component.css @@ -1 +0,0 @@ - diff --git a/frontend/src/app/components/ui-components/record-view-fields/password/password.component.ts b/frontend/src/app/components/ui-components/record-view-fields/password/password.component.ts index a61eac033..eb283a909 100644 --- a/frontend/src/app/components/ui-components/record-view-fields/password/password.component.ts +++ b/frontend/src/app/components/ui-components/record-view-fields/password/password.component.ts @@ -1,13 +1,11 @@ -import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-record-view-field.component'; import { Component, Injectable } from '@angular/core'; - +import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-record-view-field.component'; @Injectable() @Component({ - selector: 'app-password-record-view', - templateUrl: './password.component.html', - styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './password.component.css'], - imports: [] + selector: 'app-password-record-view', + templateUrl: './password.component.html', + styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './password.component.css'], + imports: [], }) -export class PasswordRecordViewComponent extends BaseRecordViewFieldComponent { -} +export class PasswordRecordViewComponent extends BaseRecordViewFieldComponent {} diff --git a/frontend/src/app/components/ui-components/record-view-fields/phone/phone.component.css b/frontend/src/app/components/ui-components/record-view-fields/phone/phone.component.css index 094c8843b..08fffb713 100644 --- a/frontend/src/app/components/ui-components/record-view-fields/phone/phone.component.css +++ b/frontend/src/app/components/ui-components/record-view-fields/phone/phone.component.css @@ -1,15 +1,15 @@ .phone-content { - display: flex; - align-items: center; - gap: 8px; + display: flex; + align-items: center; + gap: 8px; } .country-flag { - font-size: 16px; - cursor: help; - user-select: none; + font-size: 16px; + cursor: help; + user-select: none; } .phone-link { - color: var(--primary-color); -} \ No newline at end of file + color: var(--primary-color); +} diff --git a/frontend/src/app/components/ui-components/record-view-fields/phone/phone.component.ts b/frontend/src/app/components/ui-components/record-view-fields/phone/phone.component.ts index 6024f7e9e..3c3b9c56e 100644 --- a/frontend/src/app/components/ui-components/record-view-fields/phone/phone.component.ts +++ b/frontend/src/app/components/ui-components/record-view-fields/phone/phone.component.ts @@ -1,58 +1,58 @@ -import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-record-view-field.component'; +import { CommonModule } from '@angular/common'; import { Component, Injectable, OnInit } from '@angular/core'; +import { MatTooltipModule } from '@angular/material/tooltip'; import { parsePhoneNumber } from 'libphonenumber-js'; import { COUNTRIES, getCountryFlag } from '../../../../consts/countries'; -import { MatTooltipModule } from '@angular/material/tooltip'; -import { CommonModule } from '@angular/common'; +import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-record-view-field.component'; @Injectable() @Component({ - selector: 'app-phone-record-view', - templateUrl: './phone.component.html', - styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './phone.component.css'], - imports: [MatTooltipModule, CommonModule] + selector: 'app-phone-record-view', + templateUrl: './phone.component.html', + styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './phone.component.css'], + imports: [MatTooltipModule, CommonModule], }) export class PhoneRecordViewComponent extends BaseRecordViewFieldComponent implements OnInit { - public countryFlag: string = ''; - public countryName: string = ''; - public formattedNumber: string = ''; + public countryFlag: string = ''; + public countryName: string = ''; + public formattedNumber: string = ''; - ngOnInit(): void { - this.parsePhoneNumber(); - } + ngOnInit(): void { + this.parsePhoneNumber(); + } - private parsePhoneNumber(): void { - if (!this.value || typeof this.value !== 'string') { - this.countryFlag = ''; - this.countryName = ''; - this.formattedNumber = ''; - return; - } + private parsePhoneNumber(): void { + if (!this.value || typeof this.value !== 'string') { + this.countryFlag = ''; + this.countryName = ''; + this.formattedNumber = ''; + return; + } - try { - const phoneNumber = parsePhoneNumber(this.value); + try { + const phoneNumber = parsePhoneNumber(this.value); - if (phoneNumber?.country) { - const country = COUNTRIES.find(c => c.code === phoneNumber.country); + if (phoneNumber?.country) { + const country = COUNTRIES.find((c) => c.code === phoneNumber.country); - if (country) { - this.countryFlag = getCountryFlag(country.code); - this.countryName = country.name; - this.formattedNumber = phoneNumber.formatInternational(); - } else { - this.countryFlag = ''; - this.countryName = ''; - this.formattedNumber = this.value; - } - } else { - this.countryFlag = ''; - this.countryName = ''; - this.formattedNumber = this.value; - } - } catch (_error) { - this.countryFlag = ''; - this.countryName = ''; - this.formattedNumber = this.value; - } - } + if (country) { + this.countryFlag = getCountryFlag(country.code); + this.countryName = country.name; + this.formattedNumber = phoneNumber.formatInternational(); + } else { + this.countryFlag = ''; + this.countryName = ''; + this.formattedNumber = this.value; + } + } else { + this.countryFlag = ''; + this.countryName = ''; + this.formattedNumber = this.value; + } + } catch (_error) { + this.countryFlag = ''; + this.countryName = ''; + this.formattedNumber = this.value; + } + } } diff --git a/frontend/src/app/components/ui-components/record-view-fields/point/point.component.css b/frontend/src/app/components/ui-components/record-view-fields/point/point.component.css index 8b1378917..e69de29bb 100644 --- a/frontend/src/app/components/ui-components/record-view-fields/point/point.component.css +++ b/frontend/src/app/components/ui-components/record-view-fields/point/point.component.css @@ -1 +0,0 @@ - diff --git a/frontend/src/app/components/ui-components/record-view-fields/point/point.component.ts b/frontend/src/app/components/ui-components/record-view-fields/point/point.component.ts index f36e76667..e017915f2 100644 --- a/frontend/src/app/components/ui-components/record-view-fields/point/point.component.ts +++ b/frontend/src/app/components/ui-components/record-view-fields/point/point.component.ts @@ -1,44 +1,43 @@ import { Component, Injectable, OnInit } from '@angular/core'; - -import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-record-view-field.component'; import { MatTooltipModule } from '@angular/material/tooltip'; +import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-record-view-field.component'; @Injectable() @Component({ - selector: 'app-point-record-view', - templateUrl: './point.component.html', - styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './point.component.css'], - imports: [MatTooltipModule] + selector: 'app-point-record-view', + templateUrl: './point.component.html', + styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './point.component.css'], + imports: [MatTooltipModule], }) export class PointRecordViewComponent extends BaseRecordViewFieldComponent implements OnInit { - static type = 'point'; + static type = 'point'; - formattedPoint: string; + formattedPoint: string; - ngOnInit() { - this.formatPoint(); - } + ngOnInit() { + this.formatPoint(); + } - private formatPoint() { - if (!this.value) { - this.formattedPoint = ''; - return; - } + private formatPoint() { + if (!this.value) { + this.formattedPoint = ''; + return; + } - try { - if (typeof this.value === 'string') { - // Handle string format like "(x,y)" or "x,y" - const pointStr = this.value.trim().replace(/[()]/g, ''); - const [x, y] = pointStr.split(',').map(coord => parseFloat(coord.trim())); - this.formattedPoint = `(${x}, ${y})`; - } else if (typeof this.value === 'object') { - // Handle object format like {x: 1, y: 2} - const x = this.value.x || this.value[0]; - const y = this.value.y || this.value[1]; - this.formattedPoint = `(${x}, ${y})`; - } - } catch (_e) { - this.formattedPoint = String(this.value); - } - } + try { + if (typeof this.value === 'string') { + // Handle string format like "(x,y)" or "x,y" + const pointStr = this.value.trim().replace(/[()]/g, ''); + const [x, y] = pointStr.split(',').map((coord) => parseFloat(coord.trim())); + this.formattedPoint = `(${x}, ${y})`; + } else if (typeof this.value === 'object') { + // Handle object format like {x: 1, y: 2} + const x = this.value.x || this.value[0]; + const y = this.value.y || this.value[1]; + this.formattedPoint = `(${x}, ${y})`; + } + } catch (_e) { + this.formattedPoint = String(this.value); + } + } } diff --git a/frontend/src/app/components/ui-components/record-view-fields/range/range.component.ts b/frontend/src/app/components/ui-components/record-view-fields/range/range.component.ts index e53e04d01..560906e18 100644 --- a/frontend/src/app/components/ui-components/record-view-fields/range/range.component.ts +++ b/frontend/src/app/components/ui-components/record-view-fields/range/range.component.ts @@ -1,71 +1,67 @@ -import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-record-view-field.component'; +import { CommonModule } from '@angular/common'; import { Component, Input, OnChanges, OnInit } from '@angular/core'; - -import { CommonModule } from '@angular/common'; import { MatProgressBarModule } from '@angular/material/progress-bar'; +import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-record-view-field.component'; @Component({ - selector: 'app-range-record-view', - standalone: true, - templateUrl: './range.component.html', - styleUrls: ['./range.component.css'], - imports: [ - CommonModule, - MatProgressBarModule - ], + selector: 'app-range-record-view', + standalone: true, + templateUrl: './range.component.html', + styleUrls: ['./range.component.css'], + imports: [CommonModule, MatProgressBarModule], }) export class RangeRecordViewComponent extends BaseRecordViewFieldComponent implements OnInit, OnChanges { - @Input() declare value: number; - static type = 'range'; + @Input() declare value: number; + static type = 'range'; - public min: number = 0; - public max: number = 100; - public step: number = 1; - public displayValue: string = ''; + public min: number = 0; + public max: number = 100; + public step: number = 1; + public displayValue: string = ''; - ngOnInit(): void { - this._parseWidgetParams(); - this._updateDisplayValue(); - } + ngOnInit(): void { + this._parseWidgetParams(); + this._updateDisplayValue(); + } - ngOnChanges(): void { - this._parseWidgetParams(); - this._updateDisplayValue(); - } + ngOnChanges(): void { + this._parseWidgetParams(); + this._updateDisplayValue(); + } - public getProgressValue(): number { - const numValue = Number(this.value) || 0; - const range = this.max - this.min; - if (range === 0) return 0; - const progress = ((numValue - this.min) / range) * 100; - // Ensure progress is between 0 and 100 - return Math.max(0, Math.min(100, progress)); - } + public getProgressValue(): number { + const numValue = Number(this.value) || 0; + const range = this.max - this.min; + if (range === 0) return 0; + const progress = ((numValue - this.min) / range) * 100; + // Ensure progress is between 0 and 100 + return Math.max(0, Math.min(100, progress)); + } - private _parseWidgetParams(): void { - console.log('Parsing widget params:', this.widgetStructure?.widget_params); - if (this.widgetStructure?.widget_params) { - try { - const params = this.widgetStructure.widget_params; - if (params.min !== undefined) { - this.min = Number(params.min) || 0; - } - if (params.max !== undefined) { - this.max = Number(params.max) || 100; - } - if (params.step !== undefined) { - this.step = Number(params.step) || 1; - } - } catch (error) { - console.error('Failed to parse widget params:', error); - } - } - console.log('Parsed widget params:', { min: this.min, max: this.max, step: this.step }); - } + private _parseWidgetParams(): void { + console.log('Parsing widget params:', this.widgetStructure?.widget_params); + if (this.widgetStructure?.widget_params) { + try { + const params = this.widgetStructure.widget_params; + if (params.min !== undefined) { + this.min = Number(params.min) || 0; + } + if (params.max !== undefined) { + this.max = Number(params.max) || 100; + } + if (params.step !== undefined) { + this.step = Number(params.step) || 1; + } + } catch (error) { + console.error('Failed to parse widget params:', error); + } + } + console.log('Parsed widget params:', { min: this.min, max: this.max, step: this.step }); + } - private _updateDisplayValue(): void { - const numValue = Number(this.value) || 0; - this.displayValue = `${numValue} / ${this.max}`; - } -} \ No newline at end of file + private _updateDisplayValue(): void { + const numValue = Number(this.value) || 0; + this.displayValue = `${numValue} / ${this.max}`; + } +} diff --git a/frontend/src/app/components/ui-components/record-view-fields/select/select.component.css b/frontend/src/app/components/ui-components/record-view-fields/select/select.component.css index 288f5afca..8e5333b9f 100644 --- a/frontend/src/app/components/ui-components/record-view-fields/select/select.component.css +++ b/frontend/src/app/components/ui-components/record-view-fields/select/select.component.css @@ -1,4 +1,4 @@ .field-view-value { - padding: 2px 6px; - border-radius: 4px; + padding: 2px 6px; + border-radius: 4px; } diff --git a/frontend/src/app/components/ui-components/record-view-fields/select/select.component.ts b/frontend/src/app/components/ui-components/record-view-fields/select/select.component.ts index b15c2b623..2d4349071 100644 --- a/frontend/src/app/components/ui-components/record-view-fields/select/select.component.ts +++ b/frontend/src/app/components/ui-components/record-view-fields/select/select.component.ts @@ -4,37 +4,37 @@ import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-rec @Injectable() @Component({ - selector: 'app-select-record-view', - templateUrl: './select.component.html', - styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './select.component.css'], - imports: [] + selector: 'app-select-record-view', + templateUrl: './select.component.html', + styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './select.component.css'], + imports: [], }) export class SelectRecordViewComponent extends BaseRecordViewFieldComponent implements OnInit { - public displayValue: string; - public backgroundColor: string; + public displayValue: string; + public backgroundColor: string; - ngOnInit(): void { - this.setDisplayValue(); - } + ngOnInit(): void { + this.setDisplayValue(); + } - private setDisplayValue(): void { - if (!this.value) { - this.displayValue = '—'; - return; - } + private setDisplayValue(): void { + if (!this.value) { + this.displayValue = '—'; + return; + } - if (this.widgetStructure?.widget_params?.options) { - // Find the matching option based on value and use its label - const option = this.widgetStructure.widget_params.options.find( - (opt: { value: any, label: string }) => opt.value === this.value - ); - this.displayValue = option ? option.label : this.value; - this.backgroundColor = option?.background_color ? option.background_color : 'transparent'; - } else if (this.structure?.data_type_params) { - // If no widget structure but we have data_type_params, just use the value - this.displayValue = this.value; - } else { - this.displayValue = this.value; - } - } + if (this.widgetStructure?.widget_params?.options) { + // Find the matching option based on value and use its label + const option = this.widgetStructure.widget_params.options.find( + (opt: { value: any; label: string }) => opt.value === this.value, + ); + this.displayValue = option ? option.label : this.value; + this.backgroundColor = option?.background_color ? option.background_color : 'transparent'; + } else if (this.structure?.data_type_params) { + // If no widget structure but we have data_type_params, just use the value + this.displayValue = this.value; + } else { + this.displayValue = this.value; + } + } } diff --git a/frontend/src/app/components/ui-components/record-view-fields/static-text/static-text.component.css b/frontend/src/app/components/ui-components/record-view-fields/static-text/static-text.component.css index 8b1378917..e69de29bb 100644 --- a/frontend/src/app/components/ui-components/record-view-fields/static-text/static-text.component.css +++ b/frontend/src/app/components/ui-components/record-view-fields/static-text/static-text.component.css @@ -1 +0,0 @@ - diff --git a/frontend/src/app/components/ui-components/record-view-fields/static-text/static-text.component.ts b/frontend/src/app/components/ui-components/record-view-fields/static-text/static-text.component.ts index 6af39d952..70c4b4bdc 100644 --- a/frontend/src/app/components/ui-components/record-view-fields/static-text/static-text.component.ts +++ b/frontend/src/app/components/ui-components/record-view-fields/static-text/static-text.component.ts @@ -1,12 +1,11 @@ -import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-record-view-field.component'; import { Component, Injectable } from '@angular/core'; +import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-record-view-field.component'; @Injectable() @Component({ - selector: 'app-static-text-record-view', - templateUrl: './static-text.component.html', - styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './static-text.component.css'], - imports: [] + selector: 'app-static-text-record-view', + templateUrl: './static-text.component.html', + styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './static-text.component.css'], + imports: [], }) -export class StaticTextRecordViewComponent extends BaseRecordViewFieldComponent { -} +export class StaticTextRecordViewComponent extends BaseRecordViewFieldComponent {} diff --git a/frontend/src/app/components/ui-components/record-view-fields/text/text.component.css b/frontend/src/app/components/ui-components/record-view-fields/text/text.component.css index f6622792a..9ee904706 100644 --- a/frontend/src/app/components/ui-components/record-view-fields/text/text.component.css +++ b/frontend/src/app/components/ui-components/record-view-fields/text/text.component.css @@ -1,17 +1,17 @@ .validation-error { - text-decoration: underline; - text-decoration-style: wavy; - text-underline-offset: 2px; + text-decoration: underline; + text-decoration-style: wavy; + text-underline-offset: 2px; } @media (prefers-color-scheme: light) { - .validation-error { - color: var(--color-warnPalette-500); - } + .validation-error { + color: var(--color-warnPalette-500); + } } @media (prefers-color-scheme: dark) { - .validation-error { - color: var(--color-warnDarkPalette-500); - } -} \ No newline at end of file + .validation-error { + color: var(--color-warnDarkPalette-500); + } +} diff --git a/frontend/src/app/components/ui-components/record-view-fields/text/text.component.ts b/frontend/src/app/components/ui-components/record-view-fields/text/text.component.ts index de30d490b..09d1b3dd9 100644 --- a/frontend/src/app/components/ui-components/record-view-fields/text/text.component.ts +++ b/frontend/src/app/components/ui-components/record-view-fields/text/text.component.ts @@ -4,84 +4,84 @@ import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-rec @Injectable() @Component({ - selector: 'app-text-record-view', - templateUrl: './text.component.html', - styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './text.component.css'], - imports: [] + selector: 'app-text-record-view', + templateUrl: './text.component.html', + styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './text.component.css'], + imports: [], }) export class TextRecordViewComponent extends BaseRecordViewFieldComponent { - get isInvalid(): boolean { - if (!this.value || this.value === '') { - return false; - } + get isInvalid(): boolean { + if (!this.value || this.value === '') { + return false; + } - const validateType = this.widgetStructure?.widget_params?.validate; - if (!validateType) { - return false; - } + const validateType = this.widgetStructure?.widget_params?.validate; + if (!validateType) { + return false; + } - const stringValue = String(this.value); + const stringValue = String(this.value); - // Special case for regex validation - if (validateType === 'regex') { - const regexPattern = this.widgetStructure?.widget_params?.regex; - if (!regexPattern) { - return false; - } - try { - const regex = new RegExp(regexPattern); - return !regex.test(stringValue); - } catch (error) { - console.warn('Invalid regex pattern:', error); - return false; - } - } + // Special case for regex validation + if (validateType === 'regex') { + const regexPattern = this.widgetStructure?.widget_params?.regex; + if (!regexPattern) { + return false; + } + try { + const regex = new RegExp(regexPattern); + return !regex.test(stringValue); + } catch (error) { + console.warn('Invalid regex pattern:', error); + return false; + } + } - // Check if validator has this method - // biome-ignore lint: it is expected to import all exports of validator - const validatorMethod = validator[validateType]; - if (typeof validatorMethod !== 'function') { - console.warn(`Unknown validator method: ${validateType}`); - return false; - } + // Check if validator has this method + // biome-ignore lint: it is expected to import all exports of validator + const validatorMethod = validator[validateType]; + if (typeof validatorMethod !== 'function') { + console.warn(`Unknown validator method: ${validateType}`); + return false; + } - try { - // Call the validator method and invert result (true if invalid) - return !validatorMethod(stringValue); - } catch (error) { - console.warn(`Validation error for ${validateType}:`, error); - return false; - } - } + try { + // Call the validator method and invert result (true if invalid) + return !validatorMethod(stringValue); + } catch (error) { + console.warn(`Validation error for ${validateType}:`, error); + return false; + } + } - get validationErrorMessage(): string { - const validateType = this.widgetStructure?.widget_params?.validate; - if (!validateType) { - return ''; - } + get validationErrorMessage(): string { + const validateType = this.widgetStructure?.widget_params?.validate; + if (!validateType) { + return ''; + } - if (validateType === 'regex') { - return 'Does not match the required pattern'; - } + if (validateType === 'regex') { + return 'Does not match the required pattern'; + } - // Create user-friendly messages for common validators - const messages = { - isEmail: 'Invalid email address', - isURL: 'Invalid URL', - isIP: 'Invalid IP address', - isUUID: 'Invalid UUID', - isJSON: 'Invalid JSON', - isCreditCard: 'Invalid credit card number', - isISBN: 'Invalid ISBN', - isAlpha: 'Should contain only letters', - isNumeric: 'Should contain only numbers', - isAlphanumeric: 'Should contain only letters and numbers', - isHexColor: 'Invalid hex color', - isBase64: 'Invalid Base64 string', - isMobilePhone: 'Invalid mobile phone number', - isPostalCode: 'Invalid postal code' - }; + // Create user-friendly messages for common validators + const messages = { + isEmail: 'Invalid email address', + isURL: 'Invalid URL', + isIP: 'Invalid IP address', + isUUID: 'Invalid UUID', + isJSON: 'Invalid JSON', + isCreditCard: 'Invalid credit card number', + isISBN: 'Invalid ISBN', + isAlpha: 'Should contain only letters', + isNumeric: 'Should contain only numbers', + isAlphanumeric: 'Should contain only letters and numbers', + isHexColor: 'Invalid hex color', + isBase64: 'Invalid Base64 string', + isMobilePhone: 'Invalid mobile phone number', + isPostalCode: 'Invalid postal code', + }; - return messages[validateType] || `Invalid ${validateType.replace(/^is/, '').toLowerCase()}`; - } + return messages[validateType] || `Invalid ${validateType.replace(/^is/, '').toLowerCase()}`; + } } diff --git a/frontend/src/app/components/ui-components/record-view-fields/time-interval/time-interval.component.ts b/frontend/src/app/components/ui-components/record-view-fields/time-interval/time-interval.component.ts index 4fa47b4ad..5375870b1 100644 --- a/frontend/src/app/components/ui-components/record-view-fields/time-interval/time-interval.component.ts +++ b/frontend/src/app/components/ui-components/record-view-fields/time-interval/time-interval.component.ts @@ -4,34 +4,33 @@ import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-rec @Injectable() @Component({ - selector: 'app-time-interval-record-view', - templateUrl: './time-interval.component.html', - styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './time-interval.component.css'], - imports: [] + selector: 'app-time-interval-record-view', + templateUrl: './time-interval.component.html', + styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './time-interval.component.css'], + imports: [], }) export class TimeIntervalRecordViewComponent extends BaseRecordViewFieldComponent implements OnInit { + formattedInterval: string; - formattedInterval: string; + ngOnInit() { + if (!this.value) { + this.formattedInterval = '—'; + return; + } - ngOnInit() { - if (!this.value) { - this.formattedInterval = '—'; - return; - } + try { + const interval = typeof this.value === 'string' ? JSON.parse(this.value) : this.value; + let parts = []; - try { - const interval = typeof this.value === 'string' ? JSON.parse(this.value) : this.value; - let parts = []; + if (interval.days) parts.push(`${interval.days}d`); + if (interval.hours) parts.push(`${interval.hours}h`); + if (interval.minutes) parts.push(`${interval.minutes}m`); + if (interval.seconds) parts.push(`${interval.seconds}s`); + if (interval.milliseconds) parts.push(`${interval.milliseconds}ms`); - if (interval.days) parts.push(`${interval.days}d`); - if (interval.hours) parts.push(`${interval.hours}h`); - if (interval.minutes) parts.push(`${interval.minutes}m`); - if (interval.seconds) parts.push(`${interval.seconds}s`); - if (interval.milliseconds) parts.push(`${interval.milliseconds}ms`); - - this.formattedInterval = parts.length > 0 ? parts.join(' ') : '0'; - } catch (_e) { - this.formattedInterval = String(this.value); - } - } + this.formattedInterval = parts.length > 0 ? parts.join(' ') : '0'; + } catch (_e) { + this.formattedInterval = String(this.value); + } + } } diff --git a/frontend/src/app/components/ui-components/record-view-fields/time/time.component.css b/frontend/src/app/components/ui-components/record-view-fields/time/time.component.css index 8b1378917..e69de29bb 100644 --- a/frontend/src/app/components/ui-components/record-view-fields/time/time.component.css +++ b/frontend/src/app/components/ui-components/record-view-fields/time/time.component.css @@ -1 +0,0 @@ - diff --git a/frontend/src/app/components/ui-components/record-view-fields/time/time.component.ts b/frontend/src/app/components/ui-components/record-view-fields/time/time.component.ts index f9aeceda4..5a90e0280 100644 --- a/frontend/src/app/components/ui-components/record-view-fields/time/time.component.ts +++ b/frontend/src/app/components/ui-components/record-view-fields/time/time.component.ts @@ -1,37 +1,36 @@ import { Component, Injectable, OnInit } from '@angular/core'; - -import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-record-view-field.component'; import { format } from 'date-fns'; +import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-record-view-field.component'; @Injectable() @Component({ - selector: 'app-time-record-view', - templateUrl: './time.component.html', - styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './time.component.css'], - imports: [] + selector: 'app-time-record-view', + templateUrl: './time.component.html', + styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './time.component.css'], + imports: [], }) export class TimeRecordViewComponent extends BaseRecordViewFieldComponent implements OnInit { - static type = 'time'; + static type = 'time'; - public formattedTime: string; + public formattedTime: string; - ngOnInit(): void { - if (this.value) { - try { - if (this.value.includes(':')) { - // Handle time string format - this.formattedTime = this.value; - } else { - const date = new Date(this.value); - if (!Number.isNaN(date.getTime())) { - this.formattedTime = format(date, 'HH:mm:ss'); - } else { - this.formattedTime = this.value; - } - } - } catch (_error) { - this.formattedTime = this.value; - } - } - } + ngOnInit(): void { + if (this.value) { + try { + if (this.value.includes(':')) { + // Handle time string format + this.formattedTime = this.value; + } else { + const date = new Date(this.value); + if (!Number.isNaN(date.getTime())) { + this.formattedTime = format(date, 'HH:mm:ss'); + } else { + this.formattedTime = this.value; + } + } + } catch (_error) { + this.formattedTime = this.value; + } + } + } } diff --git a/frontend/src/app/components/ui-components/record-view-fields/timezone/timezone.component.spec.ts b/frontend/src/app/components/ui-components/record-view-fields/timezone/timezone.component.spec.ts index 396c2a457..e79620c77 100644 --- a/frontend/src/app/components/ui-components/record-view-fields/timezone/timezone.component.spec.ts +++ b/frontend/src/app/components/ui-components/record-view-fields/timezone/timezone.component.spec.ts @@ -2,33 +2,33 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { TimezoneRecordViewComponent } from './timezone.component'; describe('TimezoneRecordViewComponent', () => { - let component: TimezoneRecordViewComponent; - let fixture: ComponentFixture; + let component: TimezoneRecordViewComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [TimezoneRecordViewComponent] - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [TimezoneRecordViewComponent], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(TimezoneRecordViewComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(TimezoneRecordViewComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); - it('should display formatted timezone with UTC offset', () => { - component.value = 'America/New_York'; - expect(component.formattedTimezone).toContain('America/New_York'); - expect(component.formattedTimezone).toContain('UTC'); - }); + it('should display formatted timezone with UTC offset', () => { + component.value = 'America/New_York'; + expect(component.formattedTimezone).toContain('America/New_York'); + expect(component.formattedTimezone).toContain('UTC'); + }); - it('should display dash for null value', () => { - component.value = null; - expect(component.formattedTimezone).toBe('—'); - }); + it('should display dash for null value', () => { + component.value = null; + expect(component.formattedTimezone).toBe('—'); + }); }); diff --git a/frontend/src/app/components/ui-components/record-view-fields/timezone/timezone.component.ts b/frontend/src/app/components/ui-components/record-view-fields/timezone/timezone.component.ts index 68ea74e59..1f05b6802 100644 --- a/frontend/src/app/components/ui-components/record-view-fields/timezone/timezone.component.ts +++ b/frontend/src/app/components/ui-components/record-view-fields/timezone/timezone.component.ts @@ -3,53 +3,53 @@ import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-rec @Injectable() @Component({ - selector: 'app-timezone-record-view', - templateUrl: './timezone.component.html', - styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './timezone.component.css'], - imports: [] + selector: 'app-timezone-record-view', + templateUrl: './timezone.component.html', + styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './timezone.component.css'], + imports: [], }) export class TimezoneRecordViewComponent extends BaseRecordViewFieldComponent { - get formattedTimezone(): string { - console.log('timezone', this.value) - if (!this.value) { - return '—'; - } + get formattedTimezone(): string { + console.log('timezone', this.value); + if (!this.value) { + return '—'; + } - try { - const offset = this.getTimezoneOffset(this.value); - return `${this.value} (UTC${offset})`; - } catch (_error) { - return this.value; - } - } + try { + const offset = this.getTimezoneOffset(this.value); + return `${this.value} (UTC${offset})`; + } catch (_error) { + return this.value; + } + } - private getTimezoneOffset(timezone: string): string { - try { - const now = new Date(); - const formatter = new Intl.DateTimeFormat('en-US', { - timeZone: timezone, - timeZoneName: 'longOffset' - }); + private getTimezoneOffset(timezone: string): string { + try { + const now = new Date(); + const formatter = new Intl.DateTimeFormat('en-US', { + timeZone: timezone, + timeZoneName: 'longOffset', + }); - const parts = formatter.formatToParts(now); - const offsetPart = parts.find(part => part.type === 'timeZoneName'); + const parts = formatter.formatToParts(now); + const offsetPart = parts.find((part) => part.type === 'timeZoneName'); - if (offsetPart?.value.includes('GMT')) { - const offset = offsetPart.value.replace('GMT', ''); - return offset === '' ? '+00:00' : offset; - } + if (offsetPart?.value.includes('GMT')) { + const offset = offsetPart.value.replace('GMT', ''); + return offset === '' ? '+00:00' : offset; + } - // Fallback: calculate offset manually - const utcDate = new Date(now.toLocaleString('en-US', { timeZone: 'UTC' })); - const tzDate = new Date(now.toLocaleString('en-US', { timeZone: timezone })); - const offsetMinutes = (tzDate.getTime() - utcDate.getTime()) / 60000; - const hours = Math.floor(Math.abs(offsetMinutes) / 60); - const minutes = Math.abs(offsetMinutes) % 60; - const sign = offsetMinutes >= 0 ? '+' : '-'; - return `${sign}${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}`; - } catch (error) { - console.error(error) - return ''; - } - } + // Fallback: calculate offset manually + const utcDate = new Date(now.toLocaleString('en-US', { timeZone: 'UTC' })); + const tzDate = new Date(now.toLocaleString('en-US', { timeZone: timezone })); + const offsetMinutes = (tzDate.getTime() - utcDate.getTime()) / 60000; + const hours = Math.floor(Math.abs(offsetMinutes) / 60); + const minutes = Math.abs(offsetMinutes) % 60; + const sign = offsetMinutes >= 0 ? '+' : '-'; + return `${sign}${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}`; + } catch (error) { + console.error(error); + return ''; + } + } } diff --git a/frontend/src/app/components/ui-components/record-view-fields/url/url.component.css b/frontend/src/app/components/ui-components/record-view-fields/url/url.component.css index d8c64e6f6..c574bce09 100644 --- a/frontend/src/app/components/ui-components/record-view-fields/url/url.component.css +++ b/frontend/src/app/components/ui-components/record-view-fields/url/url.component.css @@ -1,15 +1,15 @@ .url-link { - display: flex; - align-items: center; - gap: 4px; - color: var(--color-primaryPalette-500); - word-break: break-all; + display: flex; + align-items: center; + gap: 4px; + color: var(--color-primaryPalette-500); + word-break: break-all; } .url-link__icon { - flex-shrink: 0; - margin-bottom: -2px; - width: 16px; - height: 16px; - font-size: 16px; + flex-shrink: 0; + margin-bottom: -2px; + width: 16px; + height: 16px; + font-size: 16px; } diff --git a/frontend/src/app/components/ui-components/record-view-fields/url/url.component.ts b/frontend/src/app/components/ui-components/record-view-fields/url/url.component.ts index df4d3187f..abbc1a6ab 100644 --- a/frontend/src/app/components/ui-components/record-view-fields/url/url.component.ts +++ b/frontend/src/app/components/ui-components/record-view-fields/url/url.component.ts @@ -1,25 +1,25 @@ -import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-record-view-field.component'; import { CommonModule } from '@angular/common'; import { Component, Injectable } from '@angular/core'; import { MatIconModule } from '@angular/material/icon'; +import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-record-view-field.component'; @Injectable() @Component({ - selector: 'app-url-record-view', - templateUrl: './url.component.html', - styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './url.component.css'], - imports: [CommonModule, MatIconModule] + selector: 'app-url-record-view', + templateUrl: './url.component.html', + styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './url.component.css'], + imports: [CommonModule, MatIconModule], }) export class UrlRecordViewComponent extends BaseRecordViewFieldComponent { - static type = 'url'; + static type = 'url'; - get isValidUrl(): boolean { - if (!this.value) return false; - try { - new URL(this.value); - return true; - } catch { - return false; - } - } + get isValidUrl(): boolean { + if (!this.value) return false; + try { + new URL(this.value); + return true; + } catch { + return false; + } + } } diff --git a/frontend/src/app/components/ui-components/record-view-fields/uuid/uuid.component.css b/frontend/src/app/components/ui-components/record-view-fields/uuid/uuid.component.css index 50c4ef509..37580a139 100644 --- a/frontend/src/app/components/ui-components/record-view-fields/uuid/uuid.component.css +++ b/frontend/src/app/components/ui-components/record-view-fields/uuid/uuid.component.css @@ -1,7 +1,7 @@ .uuid-text { - font-family: 'Roboto Condensed', 'Arial Narrow', sans-serif; - font-stretch: condensed; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; + font-family: "Roboto Condensed", "Arial Narrow", sans-serif; + font-stretch: condensed; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; } diff --git a/frontend/src/app/components/ui-components/record-view-fields/uuid/uuid.component.ts b/frontend/src/app/components/ui-components/record-view-fields/uuid/uuid.component.ts index 226fcadf2..8f15ec411 100644 --- a/frontend/src/app/components/ui-components/record-view-fields/uuid/uuid.component.ts +++ b/frontend/src/app/components/ui-components/record-view-fields/uuid/uuid.component.ts @@ -3,10 +3,9 @@ import { BaseRecordViewFieldComponent } from '../base-record-view-field/base-rec @Injectable() @Component({ - selector: 'app-uuid-record-view', - templateUrl: './uuid.component.html', - styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './uuid.component.css'], - imports: [] + selector: 'app-uuid-record-view', + templateUrl: './uuid.component.html', + styleUrls: ['../base-record-view-field/base-record-view-field.component.css', './uuid.component.css'], + imports: [], }) -export class UuidRecordViewComponent extends BaseRecordViewFieldComponent { -} \ No newline at end of file +export class UuidRecordViewComponent extends BaseRecordViewFieldComponent {} diff --git a/frontend/src/app/components/ui-components/table-display-fields/base-table-display-field/base-table-display-field.component.css b/frontend/src/app/components/ui-components/table-display-fields/base-table-display-field/base-table-display-field.component.css index eb518d97e..42ce1b697 100644 --- a/frontend/src/app/components/ui-components/table-display-fields/base-table-display-field/base-table-display-field.component.css +++ b/frontend/src/app/components/ui-components/table-display-fields/base-table-display-field/base-table-display-field.component.css @@ -1,26 +1,26 @@ .field-display { - position: relative; - display: flex; - align-items: center; + position: relative; + display: flex; + align-items: center; } .field-value { - flex-grow: 1 0 auto; - text-overflow: ellipsis; - overflow: hidden; - white-space: nowrap; + flex-grow: 1 0 auto; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; } .field-copy-button { - position: absolute; - right: -6px; - top: 50%; - background-color: var(--hover-color); - transform: translate(0, -50%) scale(0.85); - opacity: 0; - transition: opacity 0.1s ease-in-out; + position: absolute; + right: -6px; + top: 50%; + background-color: var(--hover-color); + transform: translate(0, -50%) scale(0.85); + opacity: 0; + transition: opacity 0.1s ease-in-out; } .field-display:hover .field-copy-button { - opacity: 1; + opacity: 1; } diff --git a/frontend/src/app/components/ui-components/table-display-fields/binary-data-caption/binary-data-caption.component.ts b/frontend/src/app/components/ui-components/table-display-fields/binary-data-caption/binary-data-caption.component.ts index 86073def1..5a3647b32 100644 --- a/frontend/src/app/components/ui-components/table-display-fields/binary-data-caption/binary-data-caption.component.ts +++ b/frontend/src/app/components/ui-components/table-display-fields/binary-data-caption/binary-data-caption.component.ts @@ -1,15 +1,17 @@ -import { BaseTableDisplayFieldComponent } from '../base-table-display-field/base-table-display-field.component'; import { CommonModule } from '@angular/common'; import { Component } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { MatTooltipModule } from '@angular/material/tooltip'; +import { BaseTableDisplayFieldComponent } from '../base-table-display-field/base-table-display-field.component'; @Component({ - selector: 'app-binary-data-caption-display', - templateUrl: './binary-data-caption.component.html', - styleUrls: ['../base-table-display-field/base-table-display-field.component.css', './binary-data-caption.component.css'], - imports: [CommonModule, MatIconModule, MatButtonModule, MatTooltipModule] + selector: 'app-binary-data-caption-display', + templateUrl: './binary-data-caption.component.html', + styleUrls: [ + '../base-table-display-field/base-table-display-field.component.css', + './binary-data-caption.component.css', + ], + imports: [CommonModule, MatIconModule, MatButtonModule, MatTooltipModule], }) -export class BinaryDataCaptionDisplayComponent extends BaseTableDisplayFieldComponent { -} +export class BinaryDataCaptionDisplayComponent extends BaseTableDisplayFieldComponent {} diff --git a/frontend/src/app/components/ui-components/table-display-fields/boolean/boolean.component.css b/frontend/src/app/components/ui-components/table-display-fields/boolean/boolean.component.css index 603046a74..30d4073c1 100644 --- a/frontend/src/app/components/ui-components/table-display-fields/boolean/boolean.component.css +++ b/frontend/src/app/components/ui-components/table-display-fields/boolean/boolean.component.css @@ -1,27 +1,23 @@ .boolean-icon { - transform: scale(0.85); + transform: scale(0.85); } @media (prefers-color-scheme: light) { - .boolean-icon-true { - color: #1B5E20; - } + .boolean-icon-true { + color: #1b5e20; + } - .boolean-icon-false { - color: #B71C1C; - } + .boolean-icon-false { + color: #b71c1c; + } } @media (prefers-color-scheme: dark) { - .boolean-icon-true { - color: #4CAF50; - } + .boolean-icon-true { + color: #4caf50; + } - .boolean-icon-false { - color: #E53935; - } + .boolean-icon-false { + color: #e53935; + } } - - - - diff --git a/frontend/src/app/components/ui-components/table-display-fields/boolean/boolean.component.ts b/frontend/src/app/components/ui-components/table-display-fields/boolean/boolean.component.ts index 5270c0239..34e51ac4b 100644 --- a/frontend/src/app/components/ui-components/table-display-fields/boolean/boolean.component.ts +++ b/frontend/src/app/components/ui-components/table-display-fields/boolean/boolean.component.ts @@ -1,29 +1,29 @@ -import { Component, Injectable } from '@angular/core'; - -import { BaseTableDisplayFieldComponent } from '../base-table-display-field/base-table-display-field.component'; import { ClipboardModule } from '@angular/cdk/clipboard'; import { CommonModule } from '@angular/common'; +import { Component, Injectable } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { MatTooltipModule } from '@angular/material/tooltip'; +import { BaseTableDisplayFieldComponent } from '../base-table-display-field/base-table-display-field.component'; @Injectable() @Component({ - selector: 'app-display-boolean', - templateUrl: './boolean.component.html', - styleUrls: ['../base-table-display-field/base-table-display-field.component.css', './boolean.component.css'], - imports: [ClipboardModule, MatIconModule, MatButtonModule, MatTooltipModule, CommonModule] + selector: 'app-display-boolean', + templateUrl: './boolean.component.html', + styleUrls: ['../base-table-display-field/base-table-display-field.component.css', './boolean.component.css'], + imports: [ClipboardModule, MatIconModule, MatButtonModule, MatTooltipModule, CommonModule], }) export class BooleanDisplayComponent extends BaseTableDisplayFieldComponent { - get invertColors(): boolean { - // Parse widget parameters if available - if (this.widgetStructure?.widget_params) { - const params = typeof this.widgetStructure.widget_params === 'string' - ? JSON.parse(this.widgetStructure.widget_params) - : this.widgetStructure.widget_params; - - return params?.invert_colors === true; - } - return false; - } + get invertColors(): boolean { + // Parse widget parameters if available + if (this.widgetStructure?.widget_params) { + const params = + typeof this.widgetStructure.widget_params === 'string' + ? JSON.parse(this.widgetStructure.widget_params) + : this.widgetStructure.widget_params; + + return params?.invert_colors === true; + } + return false; + } } diff --git a/frontend/src/app/components/ui-components/table-display-fields/code/code.component.css b/frontend/src/app/components/ui-components/table-display-fields/code/code.component.css index 9d8ce2a22..d3960a73f 100644 --- a/frontend/src/app/components/ui-components/table-display-fields/code/code.component.css +++ b/frontend/src/app/components/ui-components/table-display-fields/code/code.component.css @@ -1,17 +1,17 @@ .field-value { - border-radius: 4px; - font-family: monospace; - padding: 0 8px; + border-radius: 4px; + font-family: monospace; + padding: 0 8px; } @media (prefers-color-scheme: light) { - .field-value { - background-color: #EAEAFF; - } + .field-value { + background-color: #eaeaff; + } } @media (prefers-color-scheme: dark) { - .field-value { - background-color: #2C2C54; - } + .field-value { + background-color: #2c2c54; + } } diff --git a/frontend/src/app/components/ui-components/table-display-fields/code/code.component.ts b/frontend/src/app/components/ui-components/table-display-fields/code/code.component.ts index 85a949984..609418360 100644 --- a/frontend/src/app/components/ui-components/table-display-fields/code/code.component.ts +++ b/frontend/src/app/components/ui-components/table-display-fields/code/code.component.ts @@ -1,15 +1,14 @@ -import { BaseTableDisplayFieldComponent } from '../base-table-display-field/base-table-display-field.component'; import { ClipboardModule } from '@angular/cdk/clipboard'; import { Component } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { MatTooltipModule } from '@angular/material/tooltip'; +import { BaseTableDisplayFieldComponent } from '../base-table-display-field/base-table-display-field.component'; @Component({ - selector: 'app-code-display', - templateUrl: './code.component.html', - styleUrls: ['../base-table-display-field/base-table-display-field.component.css', './code.component.css'], - imports: [ClipboardModule, MatIconModule, MatButtonModule, MatTooltipModule] + selector: 'app-code-display', + templateUrl: './code.component.html', + styleUrls: ['../base-table-display-field/base-table-display-field.component.css', './code.component.css'], + imports: [ClipboardModule, MatIconModule, MatButtonModule, MatTooltipModule], }) -export class CodeDisplayComponent extends BaseTableDisplayFieldComponent { -} +export class CodeDisplayComponent extends BaseTableDisplayFieldComponent {} diff --git a/frontend/src/app/components/ui-components/table-display-fields/color/color.component.css b/frontend/src/app/components/ui-components/table-display-fields/color/color.component.css index 273272d64..bd573c4fa 100644 --- a/frontend/src/app/components/ui-components/table-display-fields/color/color.component.css +++ b/frontend/src/app/components/ui-components/table-display-fields/color/color.component.css @@ -1,17 +1,17 @@ .color-display { - display: flex; - align-items: center; - gap: 8px; + display: flex; + align-items: center; + gap: 8px; } .color-swatch { - width: 20px; - height: 20px; - border-radius: 4px; - /* border: 1px solid #ccc; */ - flex-shrink: 0; + width: 20px; + height: 20px; + border-radius: 4px; + /* border: 1px solid #ccc; */ + flex-shrink: 0; } .field-value { - font-family: monospace; -} \ No newline at end of file + font-family: monospace; +} diff --git a/frontend/src/app/components/ui-components/table-display-fields/color/color.component.ts b/frontend/src/app/components/ui-components/table-display-fields/color/color.component.ts index 87755852c..ec125a11d 100644 --- a/frontend/src/app/components/ui-components/table-display-fields/color/color.component.ts +++ b/frontend/src/app/components/ui-components/table-display-fields/color/color.component.ts @@ -1,53 +1,51 @@ -import { Component, Injectable } from '@angular/core'; - -import { BaseTableDisplayFieldComponent } from '../base-table-display-field/base-table-display-field.component'; import { ClipboardModule } from '@angular/cdk/clipboard'; +import { NgIf } from '@angular/common'; +import { Component, Injectable } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { MatTooltipModule } from '@angular/material/tooltip'; -import { NgIf } from '@angular/common'; import colorString from 'color-string'; +import { BaseTableDisplayFieldComponent } from '../base-table-display-field/base-table-display-field.component'; @Injectable() @Component({ - selector: 'app-display-color', - templateUrl: './color.component.html', - styleUrls: ['../base-table-display-field/base-table-display-field.component.css', './color.component.css'], - imports: [NgIf, ClipboardModule, MatIconModule, MatButtonModule, MatTooltipModule] + selector: 'app-display-color', + templateUrl: './color.component.html', + styleUrls: ['../base-table-display-field/base-table-display-field.component.css', './color.component.css'], + imports: [NgIf, ClipboardModule, MatIconModule, MatButtonModule, MatTooltipModule], }) export class ColorDisplayComponent extends BaseTableDisplayFieldComponent { - get isValidColor(): boolean { - if (!this.value) return false; - return this.parseColor(this.value) !== null; - } + get isValidColor(): boolean { + if (!this.value) return false; + return this.parseColor(this.value) !== null; + } + + get normalizedColorForDisplay(): string { + const parsed = this.parseColor(this.value); + if (parsed) { + const [r, g, b] = parsed.value; + return `#${this.toHex(r)}${this.toHex(g)}${this.toHex(b)}`; + } + return '#000000'; + } + + private parseColor(color: string): any { + if (!color) return null; - get normalizedColorForDisplay(): string { - const parsed = this.parseColor(this.value); - if (parsed) { - const [r, g, b] = parsed.value; - return `#${this.toHex(r)}${this.toHex(g)}${this.toHex(b)}`; - } - return '#000000'; - } + // Try parsing with color-string + const parsed = colorString.get(color); + if (parsed) return parsed; + // Try hex without hash + if (/^[A-Fa-f0-9]{6}$|^[A-Fa-f0-9]{3}$/.test(color)) { + return colorString.get('#' + color); + } - private parseColor(color: string): any { - if (!color) return null; - - // Try parsing with color-string - const parsed = colorString.get(color); - if (parsed) return parsed; - - // Try hex without hash - if (/^[A-Fa-f0-9]{6}$|^[A-Fa-f0-9]{3}$/.test(color)) { - return colorString.get('#' + color); - } - - return null; - } + return null; + } - private toHex(n: number): string { - const hex = n.toString(16); - return hex.length === 1 ? '0' + hex : hex; - } -} \ No newline at end of file + private toHex(n: number): string { + const hex = n.toString(16); + return hex.length === 1 ? '0' + hex : hex; + } +} diff --git a/frontend/src/app/components/ui-components/table-display-fields/country/country.component.css b/frontend/src/app/components/ui-components/table-display-fields/country/country.component.css index b3e456f15..1190f06e5 100644 --- a/frontend/src/app/components/ui-components/table-display-fields/country/country.component.css +++ b/frontend/src/app/components/ui-components/table-display-fields/country/country.component.css @@ -1,27 +1,27 @@ .field-wrapper { - display: flex; - align-items: center; + display: flex; + align-items: center; } .value-wrapper { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - max-width: 300px; - display: flex; - align-items: center; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + max-width: 300px; + display: flex; + align-items: center; } .country-flag { - margin-right: 6px; - font-size: 1.2em; + margin-right: 6px; + font-size: 1.2em; } .copy-button { - visibility: hidden; - margin-left: 4px; + visibility: hidden; + margin-left: 4px; } .field-wrapper:hover .copy-button { - visibility: visible; + visibility: visible; } diff --git a/frontend/src/app/components/ui-components/table-display-fields/country/country.component.ts b/frontend/src/app/components/ui-components/table-display-fields/country/country.component.ts index a23e5ba94..111d535ca 100644 --- a/frontend/src/app/components/ui-components/table-display-fields/country/country.component.ts +++ b/frontend/src/app/components/ui-components/table-display-fields/country/country.component.ts @@ -1,52 +1,52 @@ -import { COUNTRIES, getCountryFlag } from '../../../../consts/countries'; -import { Component, OnInit, } from '@angular/core'; - -import { BaseTableDisplayFieldComponent } from '../base-table-display-field/base-table-display-field.component'; import { ClipboardModule } from '@angular/cdk/clipboard'; import { CommonModule } from '@angular/common'; +import { Component, OnInit } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { MatTooltipModule } from '@angular/material/tooltip'; +import { COUNTRIES, getCountryFlag } from '../../../../consts/countries'; +import { BaseTableDisplayFieldComponent } from '../base-table-display-field/base-table-display-field.component'; @Component({ - selector: 'app-country-display', - templateUrl: './country.component.html', - styleUrls: ['../base-table-display-field/base-table-display-field.component.css', './country.component.css'], - imports: [ClipboardModule, MatIconModule, MatButtonModule, MatTooltipModule, CommonModule] + selector: 'app-country-display', + templateUrl: './country.component.html', + styleUrls: ['../base-table-display-field/base-table-display-field.component.css', './country.component.css'], + imports: [ClipboardModule, MatIconModule, MatButtonModule, MatTooltipModule, CommonModule], }) export class CountryDisplayComponent extends BaseTableDisplayFieldComponent implements OnInit { - static type = 'country'; + static type = 'country'; + + public countryName: string = ''; + public countryFlag: string = ''; + public showFlag: boolean = true; + + ngOnInit(): void { + this.parseWidgetParams(); - public countryName: string = ''; - public countryFlag: string = ''; - public showFlag: boolean = true; + if (this.value) { + const country = COUNTRIES.find((c) => c.code === this.value); + this.countryName = country ? country.name : this.value; + this.countryFlag = getCountryFlag(this.value); + } else { + this.countryName = '—'; + this.countryFlag = ''; + } + } - ngOnInit(): void { - this.parseWidgetParams(); - - if (this.value) { - const country = COUNTRIES.find(c => c.code === this.value); - this.countryName = country ? country.name : this.value; - this.countryFlag = getCountryFlag(this.value); - } else { - this.countryName = '—'; - this.countryFlag = ''; - } - } + private parseWidgetParams(): void { + if (this.widgetStructure?.widget_params) { + try { + const params = + typeof this.widgetStructure.widget_params === 'string' + ? JSON.parse(this.widgetStructure.widget_params) + : this.widgetStructure.widget_params; - private parseWidgetParams(): void { - if (this.widgetStructure?.widget_params) { - try { - const params = typeof this.widgetStructure.widget_params === 'string' - ? JSON.parse(this.widgetStructure.widget_params) - : this.widgetStructure.widget_params; - - if (params.show_flag !== undefined) { - this.showFlag = params.show_flag; - } - } catch (e) { - console.error('Error parsing country widget params:', e); - } - } - } + if (params.show_flag !== undefined) { + this.showFlag = params.show_flag; + } + } catch (e) { + console.error('Error parsing country widget params:', e); + } + } + } } diff --git a/frontend/src/app/components/ui-components/table-display-fields/date-time/date-time.component.css b/frontend/src/app/components/ui-components/table-display-fields/date-time/date-time.component.css index 8b1378917..e69de29bb 100644 --- a/frontend/src/app/components/ui-components/table-display-fields/date-time/date-time.component.css +++ b/frontend/src/app/components/ui-components/table-display-fields/date-time/date-time.component.css @@ -1 +0,0 @@ - diff --git a/frontend/src/app/components/ui-components/table-display-fields/date-time/date-time.component.ts b/frontend/src/app/components/ui-components/table-display-fields/date-time/date-time.component.ts index e5fdac884..0cb8d7d29 100644 --- a/frontend/src/app/components/ui-components/table-display-fields/date-time/date-time.component.ts +++ b/frontend/src/app/components/ui-components/table-display-fields/date-time/date-time.component.ts @@ -1,71 +1,71 @@ -import { Component, OnInit } from '@angular/core'; - -import { BaseTableDisplayFieldComponent } from '../base-table-display-field/base-table-display-field.component'; import { ClipboardModule } from '@angular/cdk/clipboard'; +import { Component, OnInit } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { MatTooltipModule } from '@angular/material/tooltip'; -import { format, formatDistanceToNow, differenceInHours } from 'date-fns'; +import { differenceInHours, format, formatDistanceToNow } from 'date-fns'; +import { BaseTableDisplayFieldComponent } from '../base-table-display-field/base-table-display-field.component'; @Component({ - selector: 'app-date-time-display', - templateUrl: './date-time.component.html', - styleUrls: ['../base-table-display-field/base-table-display-field.component.css', './date-time.component.css'], - imports: [ClipboardModule, MatIconModule, MatButtonModule, MatTooltipModule] + selector: 'app-date-time-display', + templateUrl: './date-time.component.html', + styleUrls: ['../base-table-display-field/base-table-display-field.component.css', './date-time.component.css'], + imports: [ClipboardModule, MatIconModule, MatButtonModule, MatTooltipModule], }) export class DateTimeDisplayComponent extends BaseTableDisplayFieldComponent implements OnInit { - static type = 'datetime'; + static type = 'datetime'; + + public formattedDateTime: string; + public formatDistanceWithinHours: number = 0; + public fullDateTime: string; + + ngOnInit(): void { + this.parseWidgetParams(); + + if (this.value) { + try { + const date = new Date(this.value); + if (!Number.isNaN(date.getTime())) { + // Always store the full date/time format for tooltip + this.fullDateTime = format(date, 'PPpp'); // e.g., "Apr 29, 2023 at 10:30 AM" - public formattedDateTime: string; - public formatDistanceWithinHours: number = 0; - public fullDateTime: string; + // Check if formatDistanceWithinHours is enabled and date is within specified hours from now + if (this.formatDistanceWithinHours > 0 && this.isWithinHours(date, this.formatDistanceWithinHours)) { + this.formattedDateTime = formatDistanceToNow(date, { addSuffix: true }); + } else { + this.formattedDateTime = format(date, 'P p'); + } + } else { + this.formattedDateTime = this.value; + this.fullDateTime = this.value; + } + } catch (_error) { + this.formattedDateTime = this.value; + this.fullDateTime = this.value; + } + } + } - ngOnInit(): void { - this.parseWidgetParams(); - - if (this.value) { - try { - const date = new Date(this.value); - if (!Number.isNaN(date.getTime())) { - // Always store the full date/time format for tooltip - this.fullDateTime = format(date, "PPpp"); // e.g., "Apr 29, 2023 at 10:30 AM" - - // Check if formatDistanceWithinHours is enabled and date is within specified hours from now - if (this.formatDistanceWithinHours > 0 && this.isWithinHours(date, this.formatDistanceWithinHours)) { - this.formattedDateTime = formatDistanceToNow(date, { addSuffix: true }); - } else { - this.formattedDateTime = format(date, "P p"); - } - } else { - this.formattedDateTime = this.value; - this.fullDateTime = this.value; - } - } catch (_error) { - this.formattedDateTime = this.value; - this.fullDateTime = this.value; - } - } - } + private parseWidgetParams(): void { + if (this.widgetStructure?.widget_params) { + try { + const params = + typeof this.widgetStructure.widget_params === 'string' + ? JSON.parse(this.widgetStructure.widget_params) + : this.widgetStructure.widget_params; - private parseWidgetParams(): void { - if (this.widgetStructure?.widget_params) { - try { - const params = typeof this.widgetStructure.widget_params === 'string' - ? JSON.parse(this.widgetStructure.widget_params) - : this.widgetStructure.widget_params; - - if (params.formatDistanceWithinHours !== undefined) { - this.formatDistanceWithinHours = Number(params.formatDistanceWithinHours) || 0; - } - } catch (e) { - console.error('Error parsing datetime widget params:', e); - } - } - } + if (params.formatDistanceWithinHours !== undefined) { + this.formatDistanceWithinHours = Number(params.formatDistanceWithinHours) || 0; + } + } catch (e) { + console.error('Error parsing datetime widget params:', e); + } + } + } - private isWithinHours(date: Date, hours: number): boolean { - const now = new Date(); - const hoursDifference = Math.abs(differenceInHours(date, now)); - return hoursDifference <= hours; - } + private isWithinHours(date: Date, hours: number): boolean { + const now = new Date(); + const hoursDifference = Math.abs(differenceInHours(date, now)); + return hoursDifference <= hours; + } } diff --git a/frontend/src/app/components/ui-components/table-display-fields/date/date.component.css b/frontend/src/app/components/ui-components/table-display-fields/date/date.component.css index 8b1378917..e69de29bb 100644 --- a/frontend/src/app/components/ui-components/table-display-fields/date/date.component.css +++ b/frontend/src/app/components/ui-components/table-display-fields/date/date.component.css @@ -1 +0,0 @@ - diff --git a/frontend/src/app/components/ui-components/table-display-fields/date/date.component.ts b/frontend/src/app/components/ui-components/table-display-fields/date/date.component.ts index 8601b3f5d..d17c65fb9 100644 --- a/frontend/src/app/components/ui-components/table-display-fields/date/date.component.ts +++ b/frontend/src/app/components/ui-components/table-display-fields/date/date.component.ts @@ -1,71 +1,71 @@ -import { Component, OnInit } from '@angular/core'; -import { format, formatDistanceToNow, differenceInHours } from 'date-fns'; - -import { BaseTableDisplayFieldComponent } from '../base-table-display-field/base-table-display-field.component'; import { ClipboardModule } from '@angular/cdk/clipboard'; +import { Component, OnInit } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { MatTooltipModule } from '@angular/material/tooltip'; +import { differenceInHours, format, formatDistanceToNow } from 'date-fns'; +import { BaseTableDisplayFieldComponent } from '../base-table-display-field/base-table-display-field.component'; @Component({ - selector: 'app-date-display', - templateUrl: './date.component.html', - styleUrls: ['../base-table-display-field/base-table-display-field.component.css', './date.component.css'], - imports: [ClipboardModule, MatIconModule, MatButtonModule, MatTooltipModule] + selector: 'app-date-display', + templateUrl: './date.component.html', + styleUrls: ['../base-table-display-field/base-table-display-field.component.css', './date.component.css'], + imports: [ClipboardModule, MatIconModule, MatButtonModule, MatTooltipModule], }) export class DateDisplayComponent extends BaseTableDisplayFieldComponent implements OnInit { - static type = 'date'; + static type = 'date'; + + public formattedDate: string; + public formatDistanceWithinHours: number = 48; + public fullDate: string; + + ngOnInit(): void { + this.parseWidgetParams(); + + if (this.value) { + try { + const date = new Date(this.value); + if (!Number.isNaN(date.getTime())) { + // Always store the full date format for tooltip + this.fullDate = format(date, 'PPP'); // e.g., "April 29th, 2023" - public formattedDate: string; - public formatDistanceWithinHours: number = 48; - public fullDate: string; + // Check if formatDistanceWithinHours is enabled and date is within specified hours from now + if (this.formatDistanceWithinHours > 0 && this.isWithinHours(date, this.formatDistanceWithinHours)) { + this.formattedDate = formatDistanceToNow(date, { addSuffix: true }); + } else { + this.formattedDate = format(date, 'P'); + } + } else { + this.formattedDate = this.value; + this.fullDate = this.value; + } + } catch (_error) { + this.formattedDate = this.value; + this.fullDate = this.value; + } + } + } - ngOnInit(): void { - this.parseWidgetParams(); - - if (this.value) { - try { - const date = new Date(this.value); - if (!Number.isNaN(date.getTime())) { - // Always store the full date format for tooltip - this.fullDate = format(date, "PPP"); // e.g., "April 29th, 2023" - - // Check if formatDistanceWithinHours is enabled and date is within specified hours from now - if (this.formatDistanceWithinHours > 0 && this.isWithinHours(date, this.formatDistanceWithinHours)) { - this.formattedDate = formatDistanceToNow(date, { addSuffix: true }); - } else { - this.formattedDate = format(date, "P"); - } - } else { - this.formattedDate = this.value; - this.fullDate = this.value; - } - } catch (_error) { - this.formattedDate = this.value; - this.fullDate = this.value; - } - } - } + private parseWidgetParams(): void { + if (this.widgetStructure?.widget_params) { + try { + const params = + typeof this.widgetStructure.widget_params === 'string' + ? JSON.parse(this.widgetStructure.widget_params) + : this.widgetStructure.widget_params; - private parseWidgetParams(): void { - if (this.widgetStructure?.widget_params) { - try { - const params = typeof this.widgetStructure.widget_params === 'string' - ? JSON.parse(this.widgetStructure.widget_params) - : this.widgetStructure.widget_params; - - if (params.formatDistanceWithinHours !== undefined) { - this.formatDistanceWithinHours = Number(params.formatDistanceWithinHours) || 48; - } - } catch (e) { - console.error('Error parsing date widget params:', e); - } - } - } + if (params.formatDistanceWithinHours !== undefined) { + this.formatDistanceWithinHours = Number(params.formatDistanceWithinHours) || 48; + } + } catch (e) { + console.error('Error parsing date widget params:', e); + } + } + } - private isWithinHours(date: Date, hours: number): boolean { - const now = new Date(); - const hoursDifference = Math.abs(differenceInHours(date, now)); - return hoursDifference <= hours; - } + private isWithinHours(date: Date, hours: number): boolean { + const now = new Date(); + const hoursDifference = Math.abs(differenceInHours(date, now)); + return hoursDifference <= hours; + } } diff --git a/frontend/src/app/components/ui-components/table-display-fields/file/file.component.css b/frontend/src/app/components/ui-components/table-display-fields/file/file.component.css index 7f667e698..05f5eebc1 100644 --- a/frontend/src/app/components/ui-components/table-display-fields/file/file.component.css +++ b/frontend/src/app/components/ui-components/table-display-fields/file/file.component.css @@ -1,4 +1,4 @@ .field-value { - font-style: italic; - color: #666; + font-style: italic; + color: #666; } diff --git a/frontend/src/app/components/ui-components/table-display-fields/file/file.component.ts b/frontend/src/app/components/ui-components/table-display-fields/file/file.component.ts index ee2aec579..0a7e6779f 100644 --- a/frontend/src/app/components/ui-components/table-display-fields/file/file.component.ts +++ b/frontend/src/app/components/ui-components/table-display-fields/file/file.component.ts @@ -1,33 +1,33 @@ -import { BaseTableDisplayFieldComponent } from '../base-table-display-field/base-table-display-field.component'; import { ClipboardModule } from '@angular/cdk/clipboard'; import { CommonModule } from '@angular/common'; import { Component } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { MatTooltipModule } from '@angular/material/tooltip'; +import { BaseTableDisplayFieldComponent } from '../base-table-display-field/base-table-display-field.component'; interface Blob { - type: string; - data: any[]; + type: string; + data: any[]; } @Component({ - selector: 'app-file-display', - templateUrl: './file.component.html', - styleUrls: ['../base-table-display-field/base-table-display-field.component.css', './file.component.css'], - imports: [ClipboardModule, MatIconModule, MatButtonModule, MatTooltipModule, CommonModule] + selector: 'app-file-display', + templateUrl: './file.component.html', + styleUrls: ['../base-table-display-field/base-table-display-field.component.css', './file.component.css'], + imports: [ClipboardModule, MatIconModule, MatButtonModule, MatTooltipModule, CommonModule], }) export class FileDisplayComponent extends BaseTableDisplayFieldComponent { - get isBlob(): boolean { - return typeof this.value === 'object' && this.value !== null && 'type' in this.value && 'data' in this.value; - } + get isBlob(): boolean { + return typeof this.value === 'object' && this.value !== null && 'type' in this.value && 'data' in this.value; + } - get displayText(): string { - if (this.isBlob) { - return 'Binary Data'; - } else if (typeof this.value === 'string' && this.value.length > 20) { - return 'Binary Data'; - } - return this.value ? String(this.value) : '—'; - } + get displayText(): string { + if (this.isBlob) { + return 'Binary Data'; + } else if (typeof this.value === 'string' && this.value.length > 20) { + return 'Binary Data'; + } + return this.value ? String(this.value) : '—'; + } } diff --git a/frontend/src/app/components/ui-components/table-display-fields/foreign-key/foreign-key.component.ts b/frontend/src/app/components/ui-components/table-display-fields/foreign-key/foreign-key.component.ts index 1c14baf1b..14ec75458 100644 --- a/frontend/src/app/components/ui-components/table-display-fields/foreign-key/foreign-key.component.ts +++ b/frontend/src/app/components/ui-components/table-display-fields/foreign-key/foreign-key.component.ts @@ -1,33 +1,32 @@ -import { Component, EventEmitter, Injectable, Input, Output } from '@angular/core'; - -import { BaseTableDisplayFieldComponent } from '../base-table-display-field/base-table-display-field.component'; import { ClipboardModule } from '@angular/cdk/clipboard'; import { CommonModule } from '@angular/common'; +import { Component, EventEmitter, Injectable, Input, Output } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { MatTooltipModule } from '@angular/material/tooltip'; import { TableForeignKey } from 'src/app/models/table'; +import { BaseTableDisplayFieldComponent } from '../base-table-display-field/base-table-display-field.component'; @Injectable() @Component({ - selector: 'app-display-foreign-key', - templateUrl: './foreign-key.component.html', - styleUrls: ['../base-table-display-field/base-table-display-field.component.css', './foreign-key.component.css'], - imports: [ClipboardModule, MatIconModule, MatButtonModule, MatTooltipModule, CommonModule] + selector: 'app-display-foreign-key', + templateUrl: './foreign-key.component.html', + styleUrls: ['../base-table-display-field/base-table-display-field.component.css', './foreign-key.component.css'], + imports: [ClipboardModule, MatIconModule, MatButtonModule, MatTooltipModule, CommonModule], }) export class ForeignKeyDisplayComponent extends BaseTableDisplayFieldComponent { - @Input() isSelected: boolean = false; - @Input() relations: TableForeignKey; + @Input() isSelected: boolean = false; + @Input() relations: TableForeignKey; - @Output() onForeignKeyClick = new EventEmitter<{foreignKey: any, value: string}>(); + @Output() onForeignKeyClick = new EventEmitter<{ foreignKey: any; value: string }>(); - handleForeignKeyClick($event): void { - $event.stopPropagation(); - if (this.relations && this.value) { - this.onForeignKeyClick.emit({ - foreignKey: this.relations, - value: this.value - }); - } - } + handleForeignKeyClick($event): void { + $event.stopPropagation(); + if (this.relations && this.value) { + this.onForeignKeyClick.emit({ + foreignKey: this.relations, + value: this.value, + }); + } + } } diff --git a/frontend/src/app/components/ui-components/table-display-fields/id/id.component.ts b/frontend/src/app/components/ui-components/table-display-fields/id/id.component.ts index e219738b4..544018bce 100644 --- a/frontend/src/app/components/ui-components/table-display-fields/id/id.component.ts +++ b/frontend/src/app/components/ui-components/table-display-fields/id/id.component.ts @@ -1,17 +1,15 @@ -import { Component, Injectable } from '@angular/core'; - -import { BaseTableDisplayFieldComponent } from '../base-table-display-field/base-table-display-field.component'; import { ClipboardModule } from '@angular/cdk/clipboard'; +import { Component, Injectable } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { MatTooltipModule } from '@angular/material/tooltip'; +import { BaseTableDisplayFieldComponent } from '../base-table-display-field/base-table-display-field.component'; @Injectable() @Component({ - selector: 'app-display-id', - templateUrl: './id.component.html', - styleUrls: ['../base-table-display-field/base-table-display-field.component.css', './id.component.css'], - imports: [ClipboardModule, MatIconModule, MatButtonModule, MatTooltipModule] + selector: 'app-display-id', + templateUrl: './id.component.html', + styleUrls: ['../base-table-display-field/base-table-display-field.component.css', './id.component.css'], + imports: [ClipboardModule, MatIconModule, MatButtonModule, MatTooltipModule], }) -export class IdDisplayComponent extends BaseTableDisplayFieldComponent { -} +export class IdDisplayComponent extends BaseTableDisplayFieldComponent {} diff --git a/frontend/src/app/components/ui-components/table-display-fields/image/image.component.css b/frontend/src/app/components/ui-components/table-display-fields/image/image.component.css index fad6ddbcf..33e4f9686 100644 --- a/frontend/src/app/components/ui-components/table-display-fields/image/image.component.css +++ b/frontend/src/app/components/ui-components/table-display-fields/image/image.component.css @@ -1,10 +1,10 @@ .field-value { - font-style: italic; - color: #666; + font-style: italic; + color: #666; } .image-thumbnail { - display: block; - height: var(--height); - margin: 12px 0; + display: block; + height: var(--height); + margin: 12px 0; } diff --git a/frontend/src/app/components/ui-components/table-display-fields/image/image.component.ts b/frontend/src/app/components/ui-components/table-display-fields/image/image.component.ts index ba89d83a4..cccff9e7b 100644 --- a/frontend/src/app/components/ui-components/table-display-fields/image/image.component.ts +++ b/frontend/src/app/components/ui-components/table-display-fields/image/image.component.ts @@ -1,32 +1,32 @@ -import { BaseTableDisplayFieldComponent } from '../base-table-display-field/base-table-display-field.component'; import { ClipboardModule } from '@angular/cdk/clipboard'; import { CommonModule } from '@angular/common'; import { Component } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { MatTooltipModule } from '@angular/material/tooltip'; +import { BaseTableDisplayFieldComponent } from '../base-table-display-field/base-table-display-field.component'; @Component({ - selector: 'app-image-display', - templateUrl: './image.component.html', - styleUrls: ['../base-table-display-field/base-table-display-field.component.css', './image.component.css'], - imports: [ClipboardModule, MatIconModule, MatButtonModule, MatTooltipModule, CommonModule] + selector: 'app-image-display', + templateUrl: './image.component.html', + styleUrls: ['../base-table-display-field/base-table-display-field.component.css', './image.component.css'], + imports: [ClipboardModule, MatIconModule, MatButtonModule, MatTooltipModule, CommonModule], }) export class ImageDisplayComponent extends BaseTableDisplayFieldComponent { - get srcValue(): string { - if (!this.value) return ''; - const prefix = this.widgetStructure?.widget_params?.prefix || ''; - return prefix + this.value; - } - - get isUrl(): boolean { - if (!this.value) return false; - try { - // Check if the prefixed URL is valid - new URL(this.srcValue); - return true; - } catch { - return false; - } - } + get srcValue(): string { + if (!this.value) return ''; + const prefix = this.widgetStructure?.widget_params?.prefix || ''; + return prefix + this.value; + } + + get isUrl(): boolean { + if (!this.value) return false; + try { + // Check if the prefixed URL is valid + new URL(this.srcValue); + return true; + } catch { + return false; + } + } } diff --git a/frontend/src/app/components/ui-components/table-display-fields/json-editor/json-editor.component.css b/frontend/src/app/components/ui-components/table-display-fields/json-editor/json-editor.component.css index 559bc7f44..cbdf71e10 100644 --- a/frontend/src/app/components/ui-components/table-display-fields/json-editor/json-editor.component.css +++ b/frontend/src/app/components/ui-components/table-display-fields/json-editor/json-editor.component.css @@ -1,17 +1,17 @@ .json-display { - border-radius: 4px; - font-family: monospace; - padding: 0 8px; + border-radius: 4px; + font-family: monospace; + padding: 0 8px; } @media (prefers-color-scheme: light) { - .json-display { - background-color: #EAEAFF; - } + .json-display { + background-color: #eaeaff; + } } @media (prefers-color-scheme: dark) { - .json-display { - background-color: #2C2C54; - } + .json-display { + background-color: #2c2c54; + } } diff --git a/frontend/src/app/components/ui-components/table-display-fields/json-editor/json-editor.component.ts b/frontend/src/app/components/ui-components/table-display-fields/json-editor/json-editor.component.ts index 9c96e2943..d13b3cd25 100644 --- a/frontend/src/app/components/ui-components/table-display-fields/json-editor/json-editor.component.ts +++ b/frontend/src/app/components/ui-components/table-display-fields/json-editor/json-editor.component.ts @@ -1,26 +1,26 @@ -import { BaseTableDisplayFieldComponent } from '../base-table-display-field/base-table-display-field.component'; import { ClipboardModule } from '@angular/cdk/clipboard'; import { CommonModule } from '@angular/common'; import { Component } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { MatTooltipModule } from '@angular/material/tooltip'; +import { BaseTableDisplayFieldComponent } from '../base-table-display-field/base-table-display-field.component'; @Component({ - selector: 'app-json-editor-display', - templateUrl: './json-editor.component.html', - styleUrls: ['../base-table-display-field/base-table-display-field.component.css', './json-editor.component.css'], - imports: [ClipboardModule, MatIconModule, MatButtonModule, MatTooltipModule, CommonModule] + selector: 'app-json-editor-display', + templateUrl: './json-editor.component.html', + styleUrls: ['../base-table-display-field/base-table-display-field.component.css', './json-editor.component.css'], + imports: [ClipboardModule, MatIconModule, MatButtonModule, MatTooltipModule, CommonModule], }) export class JsonEditorDisplayComponent extends BaseTableDisplayFieldComponent { - get formattedJson(): string { - if (!this.value) return ''; + get formattedJson(): string { + if (!this.value) return ''; - try { - const parsedValue = typeof this.value === 'string' ? JSON.parse(this.value) : this.value; - return JSON.stringify(parsedValue, null, 2); - } catch (_e) { - return String(this.value); - } - } + try { + const parsedValue = typeof this.value === 'string' ? JSON.parse(this.value) : this.value; + return JSON.stringify(parsedValue, null, 2); + } catch (_e) { + return String(this.value); + } + } } diff --git a/frontend/src/app/components/ui-components/table-display-fields/language/language.component.css b/frontend/src/app/components/ui-components/table-display-fields/language/language.component.css index 02b00a9c4..a140789cb 100644 --- a/frontend/src/app/components/ui-components/table-display-fields/language/language.component.css +++ b/frontend/src/app/components/ui-components/table-display-fields/language/language.component.css @@ -1,27 +1,27 @@ .field-wrapper { - display: flex; - align-items: center; + display: flex; + align-items: center; } .value-wrapper { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - max-width: 300px; - display: flex; - align-items: center; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + max-width: 300px; + display: flex; + align-items: center; } .language-flag { - margin-right: 6px; - font-size: 1.2em; + margin-right: 6px; + font-size: 1.2em; } .copy-button { - visibility: hidden; - margin-left: 4px; + visibility: hidden; + margin-left: 4px; } .field-wrapper:hover .copy-button { - visibility: visible; + visibility: visible; } diff --git a/frontend/src/app/components/ui-components/table-display-fields/long-text/long-text.component.css b/frontend/src/app/components/ui-components/table-display-fields/long-text/long-text.component.css index adb834c90..218140c22 100644 --- a/frontend/src/app/components/ui-components/table-display-fields/long-text/long-text.component.css +++ b/frontend/src/app/components/ui-components/table-display-fields/long-text/long-text.component.css @@ -1,4 +1,4 @@ .field-value { - height: 36px; - line-height: 36px; + height: 36px; + line-height: 36px; } diff --git a/frontend/src/app/components/ui-components/table-display-fields/long-text/long-text.component.ts b/frontend/src/app/components/ui-components/table-display-fields/long-text/long-text.component.ts index b472abc3b..a0afa3620 100644 --- a/frontend/src/app/components/ui-components/table-display-fields/long-text/long-text.component.ts +++ b/frontend/src/app/components/ui-components/table-display-fields/long-text/long-text.component.ts @@ -1,19 +1,16 @@ -import { Component, Injectable } from '@angular/core'; - -import { BaseTableDisplayFieldComponent } from '../base-table-display-field/base-table-display-field.component'; import { ClipboardModule } from '@angular/cdk/clipboard'; import { CommonModule } from '@angular/common'; +import { Component, Injectable } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { MatTooltipModule } from '@angular/material/tooltip'; +import { BaseTableDisplayFieldComponent } from '../base-table-display-field/base-table-display-field.component'; @Injectable() @Component({ - selector: 'app-display-long-text', - templateUrl: './long-text.component.html', - styleUrls: ['../base-table-display-field/base-table-display-field.component.css', './long-text.component.css'], - imports: [CommonModule, ClipboardModule, MatIconModule, MatButtonModule, MatTooltipModule] + selector: 'app-display-long-text', + templateUrl: './long-text.component.html', + styleUrls: ['../base-table-display-field/base-table-display-field.component.css', './long-text.component.css'], + imports: [CommonModule, ClipboardModule, MatIconModule, MatButtonModule, MatTooltipModule], }) -export class LongTextDisplayComponent extends BaseTableDisplayFieldComponent { - -} +export class LongTextDisplayComponent extends BaseTableDisplayFieldComponent {} diff --git a/frontend/src/app/components/ui-components/table-display-fields/markdown/markdown.component.css b/frontend/src/app/components/ui-components/table-display-fields/markdown/markdown.component.css index 03a09e89e..ec5f93a9b 100644 --- a/frontend/src/app/components/ui-components/table-display-fields/markdown/markdown.component.css +++ b/frontend/src/app/components/ui-components/table-display-fields/markdown/markdown.component.css @@ -1,14 +1,14 @@ .markdown-preview { - display: inline-block; - max-width: 300px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; + display: inline-block; + max-width: 300px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; } .markdown-preview ::ng-deep p { - margin: 0; - display: inline; + margin: 0; + display: inline; } .markdown-preview ::ng-deep h1, @@ -17,35 +17,35 @@ .markdown-preview ::ng-deep h4, .markdown-preview ::ng-deep h5, .markdown-preview ::ng-deep h6 { - margin: 0; - display: inline; - font-size: inherit; - font-weight: bold; + margin: 0; + display: inline; + font-size: inherit; + font-weight: bold; } .markdown-preview ::ng-deep ul, .markdown-preview ::ng-deep ol { - display: inline; - list-style: none; - margin: 0; - padding: 0; + display: inline; + list-style: none; + margin: 0; + padding: 0; } .markdown-preview ::ng-deep code { - padding: 1px 3px; - border-radius: 2px; - font-family: monospace; - font-size: 0.9em; + padding: 1px 3px; + border-radius: 2px; + font-family: monospace; + font-size: 0.9em; } @media (prefers-color-scheme: light) { - .markdown-preview ::ng-deep code { - background: #f5f5f5; - } + .markdown-preview ::ng-deep code { + background: #f5f5f5; + } } @media (prefers-color-scheme: dark) { - .markdown-preview ::ng-deep code { - background: #3a3a3a; - } + .markdown-preview ::ng-deep code { + background: #3a3a3a; + } } diff --git a/frontend/src/app/components/ui-components/table-display-fields/markdown/markdown.component.spec.ts b/frontend/src/app/components/ui-components/table-display-fields/markdown/markdown.component.spec.ts index e2a18966d..26ee34e10 100644 --- a/frontend/src/app/components/ui-components/table-display-fields/markdown/markdown.component.spec.ts +++ b/frontend/src/app/components/ui-components/table-display-fields/markdown/markdown.component.spec.ts @@ -1,99 +1,95 @@ +import { provideHttpClient } from '@angular/common/http'; import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { MarkdownDisplayComponent } from './markdown.component'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { provideMarkdown } from 'ngx-markdown'; -import { provideHttpClient } from '@angular/common/http'; +import { MarkdownDisplayComponent } from './markdown.component'; describe('MarkdownDisplayComponent', () => { - let component: MarkdownDisplayComponent; - let fixture: ComponentFixture; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [MarkdownDisplayComponent, BrowserAnimationsModule], - providers: [ - provideHttpClient(), - provideMarkdown() - ] - }).compileComponents(); - - fixture = TestBed.createComponent(MarkdownDisplayComponent); - component = fixture.componentInstance; - component.value = '# Test Markdown'; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - describe('Title extraction', () => { - it('should extract H1 heading as title', () => { - component.value = '# Main Title\n\nSome paragraph text here.'; - component.ngOnInit(); - expect(component.displayTitle).toBe('Main Title'); - }); - - it('should extract H2 heading as title', () => { - component.value = '## Secondary Title\n\nContent follows.'; - component.ngOnInit(); - expect(component.displayTitle).toBe('Secondary Title'); - }); - - it('should prioritize heading over paragraph', () => { - component.value = 'Some intro text\n\n# Actual Title\n\nParagraph content.'; - component.ngOnInit(); - expect(component.displayTitle).toBe('Actual Title'); - }); - - it('should use first paragraph if no heading exists', () => { - component.value = 'This is the first paragraph.\n\nThis is the second paragraph.'; - component.ngOnInit(); - expect(component.displayTitle).toBe('This is the first paragraph.'); - }); - - it('should truncate long titles at 100 characters', () => { - const longTitle = 'a'.repeat(150); - component.value = `# ${longTitle}`; - component.ngOnInit(); - expect(component.displayTitle).toBe('a'.repeat(100) + '...'); - }); - - it('should handle markdown with bold and italic formatting', () => { - component.value = '# **Bold** and *italic* title'; - component.ngOnInit(); - expect(component.displayTitle).toBe('**Bold** and *italic* title'); - }); - - it('should handle empty markdown', () => { - component.value = ''; - component.ngOnInit(); - expect(component.displayTitle).toBe('—'); - }); - - it('should handle null/undefined markdown', () => { - component.value = null as any; - component.ngOnInit(); - expect(component.displayTitle).toBe('—'); - }); - - it('should handle markdown with only whitespace', () => { - component.value = ' \n\n '; - component.ngOnInit(); - expect(component.displayTitle).toBe('—'); - }); - - it('should extract title from markdown with code blocks', () => { - component.value = '# API Documentation\n\n```javascript\nconst x = 5;\n```'; - component.ngOnInit(); - expect(component.displayTitle).toBe('API Documentation'); - }); - - it('should extract title from markdown with lists', () => { - component.value = '# Shopping List\n\n- Item 1\n- Item 2'; - component.ngOnInit(); - expect(component.displayTitle).toBe('Shopping List'); - }); - }); + let component: MarkdownDisplayComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [MarkdownDisplayComponent, BrowserAnimationsModule], + providers: [provideHttpClient(), provideMarkdown()], + }).compileComponents(); + + fixture = TestBed.createComponent(MarkdownDisplayComponent); + component = fixture.componentInstance; + component.value = '# Test Markdown'; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('Title extraction', () => { + it('should extract H1 heading as title', () => { + component.value = '# Main Title\n\nSome paragraph text here.'; + component.ngOnInit(); + expect(component.displayTitle).toBe('Main Title'); + }); + + it('should extract H2 heading as title', () => { + component.value = '## Secondary Title\n\nContent follows.'; + component.ngOnInit(); + expect(component.displayTitle).toBe('Secondary Title'); + }); + + it('should prioritize heading over paragraph', () => { + component.value = 'Some intro text\n\n# Actual Title\n\nParagraph content.'; + component.ngOnInit(); + expect(component.displayTitle).toBe('Actual Title'); + }); + + it('should use first paragraph if no heading exists', () => { + component.value = 'This is the first paragraph.\n\nThis is the second paragraph.'; + component.ngOnInit(); + expect(component.displayTitle).toBe('This is the first paragraph.'); + }); + + it('should truncate long titles at 100 characters', () => { + const longTitle = 'a'.repeat(150); + component.value = `# ${longTitle}`; + component.ngOnInit(); + expect(component.displayTitle).toBe('a'.repeat(100) + '...'); + }); + + it('should handle markdown with bold and italic formatting', () => { + component.value = '# **Bold** and *italic* title'; + component.ngOnInit(); + expect(component.displayTitle).toBe('**Bold** and *italic* title'); + }); + + it('should handle empty markdown', () => { + component.value = ''; + component.ngOnInit(); + expect(component.displayTitle).toBe('—'); + }); + + it('should handle null/undefined markdown', () => { + component.value = null as any; + component.ngOnInit(); + expect(component.displayTitle).toBe('—'); + }); + + it('should handle markdown with only whitespace', () => { + component.value = ' \n\n '; + component.ngOnInit(); + expect(component.displayTitle).toBe('—'); + }); + + it('should extract title from markdown with code blocks', () => { + component.value = '# API Documentation\n\n```javascript\nconst x = 5;\n```'; + component.ngOnInit(); + expect(component.displayTitle).toBe('API Documentation'); + }); + + it('should extract title from markdown with lists', () => { + component.value = '# Shopping List\n\n- Item 1\n- Item 2'; + component.ngOnInit(); + expect(component.displayTitle).toBe('Shopping List'); + }); + }); }); diff --git a/frontend/src/app/components/ui-components/table-display-fields/markdown/markdown.component.ts b/frontend/src/app/components/ui-components/table-display-fields/markdown/markdown.component.ts index 9a9b757eb..f1b131db7 100644 --- a/frontend/src/app/components/ui-components/table-display-fields/markdown/markdown.component.ts +++ b/frontend/src/app/components/ui-components/table-display-fields/markdown/markdown.component.ts @@ -1,80 +1,80 @@ -import { BaseTableDisplayFieldComponent } from '../base-table-display-field/base-table-display-field.component'; import { ClipboardModule } from '@angular/cdk/clipboard'; -import { Component, OnInit } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { MarkdownModule } from 'ngx-markdown'; +import { Component, OnInit } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { MatTooltipModule } from '@angular/material/tooltip'; import { marked } from 'marked'; +import { MarkdownModule } from 'ngx-markdown'; +import { BaseTableDisplayFieldComponent } from '../base-table-display-field/base-table-display-field.component'; @Component({ - selector: 'app-markdown-display', - templateUrl: './markdown.component.html', - styleUrls: ['../base-table-display-field/base-table-display-field.component.css', './markdown.component.css'], - imports: [CommonModule, MarkdownModule, ClipboardModule, MatIconModule, MatButtonModule, MatTooltipModule] + selector: 'app-markdown-display', + templateUrl: './markdown.component.html', + styleUrls: ['../base-table-display-field/base-table-display-field.component.css', './markdown.component.css'], + imports: [CommonModule, MarkdownModule, ClipboardModule, MatIconModule, MatButtonModule, MatTooltipModule], }) export class MarkdownDisplayComponent extends BaseTableDisplayFieldComponent implements OnInit { - public renderedMarkdown: string = ''; - public displayTitle: string = ''; + public renderedMarkdown: string = ''; + public displayTitle: string = ''; - ngOnInit(): void { - this.renderedMarkdown = this.value || ''; - this.displayTitle = this._extractTitle(this.value); - } + ngOnInit(): void { + this.renderedMarkdown = this.value || ''; + this.displayTitle = this._extractTitle(this.value); + } - /** - * Extracts a title from markdown content using marked lexer - * Prioritizes headings, then falls back to first paragraph - * @param markdown The markdown content - * @returns Extracted and cleaned title string - */ - private _extractTitle(markdown: string): string { - if (!markdown || markdown.trim() === '') { - return '—'; - } + /** + * Extracts a title from markdown content using marked lexer + * Prioritizes headings, then falls back to first paragraph + * @param markdown The markdown content + * @returns Extracted and cleaned title string + */ + private _extractTitle(markdown: string): string { + if (!markdown || markdown.trim() === '') { + return '—'; + } - try { - const tokens = marked.lexer(markdown); + try { + const tokens = marked.lexer(markdown); - // First pass: look for headings (h1-h6) - for (const token of tokens) { - if (token.type === 'heading') { - return this._truncateText(token.text, 100); - } - } + // First pass: look for headings (h1-h6) + for (const token of tokens) { + if (token.type === 'heading') { + return this._truncateText(token.text, 100); + } + } - // Second pass: look for first paragraph or text - for (const token of tokens) { - if (token.type === 'paragraph') { - return this._truncateText(token.text, 100); - } - if (token.type === 'text' && token.text.trim().length > 0) { - return this._truncateText(token.text, 100); - } - } + // Second pass: look for first paragraph or text + for (const token of tokens) { + if (token.type === 'paragraph') { + return this._truncateText(token.text, 100); + } + if (token.type === 'text' && token.text.trim().length > 0) { + return this._truncateText(token.text, 100); + } + } - // If no heading or paragraph found, return first non-empty line - const firstLine = markdown.trim().split('\n')[0]; - return this._truncateText(firstLine, 100); - } catch (_error) { - // Fallback if parsing fails - const firstLine = markdown.trim().split('\n')[0]; - return this._truncateText(firstLine, 100); - } - } + // If no heading or paragraph found, return first non-empty line + const firstLine = markdown.trim().split('\n')[0]; + return this._truncateText(firstLine, 100); + } catch (_error) { + // Fallback if parsing fails + const firstLine = markdown.trim().split('\n')[0]; + return this._truncateText(firstLine, 100); + } + } - /** - * Truncates text to specified length and adds ellipsis if needed - * @param text Text to truncate - * @param maxLength Maximum length before truncation - * @returns Truncated text with ellipsis if applicable - */ - private _truncateText(text: string, maxLength: number): string { - const cleaned = text.trim(); - if (cleaned.length > maxLength) { - return cleaned.substring(0, maxLength) + '...'; - } - return cleaned || '—'; - } + /** + * Truncates text to specified length and adds ellipsis if needed + * @param text Text to truncate + * @param maxLength Maximum length before truncation + * @returns Truncated text with ellipsis if applicable + */ + private _truncateText(text: string, maxLength: number): string { + const cleaned = text.trim(); + if (cleaned.length > maxLength) { + return cleaned.substring(0, maxLength) + '...'; + } + return cleaned || '—'; + } } diff --git a/frontend/src/app/components/ui-components/table-display-fields/money/money.component.ts b/frontend/src/app/components/ui-components/table-display-fields/money/money.component.ts index 132d47912..52daeb76b 100644 --- a/frontend/src/app/components/ui-components/table-display-fields/money/money.component.ts +++ b/frontend/src/app/components/ui-components/table-display-fields/money/money.component.ts @@ -1,61 +1,60 @@ -import { Component, OnInit } from '@angular/core'; - -import { BaseTableDisplayFieldComponent } from '../base-table-display-field/base-table-display-field.component'; import { ClipboardModule } from '@angular/cdk/clipboard'; import { CommonModule } from '@angular/common'; +import { Component, OnInit } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { MatTooltipModule } from '@angular/material/tooltip'; import { getCurrencyByCode } from 'src/app/consts/currencies'; +import { BaseTableDisplayFieldComponent } from '../base-table-display-field/base-table-display-field.component'; @Component({ - selector: 'app-money-display', - templateUrl: './money.component.html', - styleUrls: ['../base-table-display-field/base-table-display-field.component.css', './money.component.css'], - imports: [ClipboardModule, MatIconModule, MatButtonModule, MatTooltipModule, CommonModule] + selector: 'app-money-display', + templateUrl: './money.component.html', + styleUrls: ['../base-table-display-field/base-table-display-field.component.css', './money.component.css'], + imports: [ClipboardModule, MatIconModule, MatButtonModule, MatTooltipModule, CommonModule], }) export class MoneyDisplayComponent extends BaseTableDisplayFieldComponent implements OnInit { - public displayCurrency: string = ''; - public currencySymbol: string = ''; - - ngOnInit(): void { - // Get currency from widget params - this.displayCurrency = ''; - if (this.widgetStructure?.widget_params?.default_currency) { - this.displayCurrency = this.widgetStructure.widget_params.default_currency; - const currency = getCurrencyByCode(this.displayCurrency); - this.currencySymbol = currency ? currency.symbol : ''; - } - } - - get formattedValue(): string { - if (!this.value) { - return ''; - } - - let amount: number | string; - let currency: string = this.displayCurrency; - - if (typeof this.value === 'object' && this.value.amount !== undefined) { - amount = this.value.amount; - if (this.value.currency) { - currency = this.value.currency; - const currencyObj = getCurrencyByCode(currency); - this.currencySymbol = currencyObj ? currencyObj.symbol : ''; - } - } else { - amount = this.value; - } - - if (typeof amount === 'string') { - amount = parseFloat(amount); - } - - if (Number.isNaN(amount as number)) { - return ''; - } - - const decimalPlaces = this.widgetStructure?.widget_params?.decimal_places ?? 2; - return `${this.currencySymbol}${(amount as number).toFixed(decimalPlaces)}`; - } + public displayCurrency: string = ''; + public currencySymbol: string = ''; + + ngOnInit(): void { + // Get currency from widget params + this.displayCurrency = ''; + if (this.widgetStructure?.widget_params?.default_currency) { + this.displayCurrency = this.widgetStructure.widget_params.default_currency; + const currency = getCurrencyByCode(this.displayCurrency); + this.currencySymbol = currency ? currency.symbol : ''; + } + } + + get formattedValue(): string { + if (!this.value) { + return ''; + } + + let amount: number | string; + let currency: string = this.displayCurrency; + + if (typeof this.value === 'object' && this.value.amount !== undefined) { + amount = this.value.amount; + if (this.value.currency) { + currency = this.value.currency; + const currencyObj = getCurrencyByCode(currency); + this.currencySymbol = currencyObj ? currencyObj.symbol : ''; + } + } else { + amount = this.value; + } + + if (typeof amount === 'string') { + amount = parseFloat(amount); + } + + if (Number.isNaN(amount as number)) { + return ''; + } + + const decimalPlaces = this.widgetStructure?.widget_params?.decimal_places ?? 2; + return `${this.currencySymbol}${(amount as number).toFixed(decimalPlaces)}`; + } } diff --git a/frontend/src/app/components/ui-components/table-display-fields/number/number.component.ts b/frontend/src/app/components/ui-components/table-display-fields/number/number.component.ts index a90dbd0dd..342b3e68d 100644 --- a/frontend/src/app/components/ui-components/table-display-fields/number/number.component.ts +++ b/frontend/src/app/components/ui-components/table-display-fields/number/number.component.ts @@ -1,4 +1,3 @@ -import { BaseTableDisplayFieldComponent } from '../base-table-display-field/base-table-display-field.component'; import { ClipboardModule } from '@angular/cdk/clipboard'; import { CommonModule } from '@angular/common'; import { Component } from '@angular/core'; @@ -6,62 +5,62 @@ import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { MatTooltipModule } from '@angular/material/tooltip'; import convert from 'convert'; +import { BaseTableDisplayFieldComponent } from '../base-table-display-field/base-table-display-field.component'; @Component({ - selector: 'app-display-number', - templateUrl: './number.component.html', - styleUrls: ['../base-table-display-field/base-table-display-field.component.css', './number.component.css'], - imports: [CommonModule, ClipboardModule, MatIconModule, MatButtonModule, MatTooltipModule] + selector: 'app-display-number', + templateUrl: './number.component.html', + styleUrls: ['../base-table-display-field/base-table-display-field.component.css', './number.component.css'], + imports: [CommonModule, ClipboardModule, MatIconModule, MatButtonModule, MatTooltipModule], }) export class NumberDisplayComponent extends BaseTableDisplayFieldComponent { + get displayValue(): string { + if (this.value == null || this.value === '') { + return '—'; + } - get displayValue(): string { - if (this.value == null || this.value === '') { - return '—'; - } - - const unit = this.widgetStructure?.widget_params?.unit; + const unit = this.widgetStructure?.widget_params?.unit; - if (!unit) { - return this.value.toString(); - } + if (!unit) { + return this.value.toString(); + } - try { - const convertedValue = convert(parseFloat(this.value), unit).to('best'); - // Format number to max 2 decimal places without trailing zeros - const formattedQuantity = parseFloat(convertedValue.quantity.toFixed(2)).toString(); - return `${formattedQuantity} ${convertedValue.unit}`; - } catch (error) { - console.warn('Unit conversion failed:', error); - return this.value.toString(); - } - } + try { + const convertedValue = convert(parseFloat(this.value), unit).to('best'); + // Format number to max 2 decimal places without trailing zeros + const formattedQuantity = parseFloat(convertedValue.quantity.toFixed(2)).toString(); + return `${formattedQuantity} ${convertedValue.unit}`; + } catch (error) { + console.warn('Unit conversion failed:', error); + return this.value.toString(); + } + } - get isOutOfThreshold(): 'up' | 'down' | false { - if (this.value == null || this.value === '') { - return false; - } + get isOutOfThreshold(): 'up' | 'down' | false { + if (this.value == null || this.value === '') { + return false; + } - const thresholdMin = this.widgetStructure?.widget_params?.threshold_min; - const thresholdMax = this.widgetStructure?.widget_params?.threshold_max; - const numValue = parseFloat(this.value); + const thresholdMin = this.widgetStructure?.widget_params?.threshold_min; + const thresholdMax = this.widgetStructure?.widget_params?.threshold_max; + const numValue = parseFloat(this.value); - if (thresholdMin !== null && thresholdMax !== null && numValue < thresholdMin) { - return 'down'; - } + if (thresholdMin !== null && thresholdMax !== null && numValue < thresholdMin) { + return 'down'; + } - if (thresholdMin && thresholdMax === null && numValue < thresholdMin) { - return 'down'; - } + if (thresholdMin && thresholdMax === null && numValue < thresholdMin) { + return 'down'; + } - if (thresholdMax !== null && thresholdMin !== null && numValue > thresholdMax) { - return 'up'; - } + if (thresholdMax !== null && thresholdMin !== null && numValue > thresholdMax) { + return 'up'; + } - if (thresholdMax && thresholdMin === null && numValue > thresholdMax) { - return 'up'; - } + if (thresholdMax && thresholdMin === null && numValue > thresholdMax) { + return 'up'; + } - return false; - } + return false; + } } diff --git a/frontend/src/app/components/ui-components/table-display-fields/password/password.component.css b/frontend/src/app/components/ui-components/table-display-fields/password/password.component.css index 8b1378917..e69de29bb 100644 --- a/frontend/src/app/components/ui-components/table-display-fields/password/password.component.css +++ b/frontend/src/app/components/ui-components/table-display-fields/password/password.component.css @@ -1 +0,0 @@ - diff --git a/frontend/src/app/components/ui-components/table-display-fields/password/password.component.ts b/frontend/src/app/components/ui-components/table-display-fields/password/password.component.ts index e852fcf5f..46d3dee98 100644 --- a/frontend/src/app/components/ui-components/table-display-fields/password/password.component.ts +++ b/frontend/src/app/components/ui-components/table-display-fields/password/password.component.ts @@ -1,15 +1,14 @@ -import { BaseTableDisplayFieldComponent } from '../base-table-display-field/base-table-display-field.component'; import { ClipboardModule } from '@angular/cdk/clipboard'; import { Component } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { MatTooltipModule } from '@angular/material/tooltip'; +import { BaseTableDisplayFieldComponent } from '../base-table-display-field/base-table-display-field.component'; @Component({ - selector: 'app-password-display', - templateUrl: './password.component.html', - styleUrls: ['../base-table-display-field/base-table-display-field.component.css', './password.component.css'], - imports: [ClipboardModule, MatIconModule, MatButtonModule, MatTooltipModule] + selector: 'app-password-display', + templateUrl: './password.component.html', + styleUrls: ['../base-table-display-field/base-table-display-field.component.css', './password.component.css'], + imports: [ClipboardModule, MatIconModule, MatButtonModule, MatTooltipModule], }) -export class PasswordDisplayComponent extends BaseTableDisplayFieldComponent { -} +export class PasswordDisplayComponent extends BaseTableDisplayFieldComponent {} diff --git a/frontend/src/app/components/ui-components/table-display-fields/phone/phone.component.css b/frontend/src/app/components/ui-components/table-display-fields/phone/phone.component.css index e325e57dd..d9cd2fb63 100644 --- a/frontend/src/app/components/ui-components/table-display-fields/phone/phone.component.css +++ b/frontend/src/app/components/ui-components/table-display-fields/phone/phone.component.css @@ -1,20 +1,20 @@ .field-display { - padding-right: 24px; + padding-right: 24px; } .phone-content { - display: flex; - align-items: center; - gap: 8px; + display: flex; + align-items: center; + gap: 8px; } .country-flag { - font-size: 16px; - cursor: help; - user-select: none; + font-size: 16px; + cursor: help; + user-select: none; } .phone-link { - text-decoration: none; - color: var(--primary-color); -} \ No newline at end of file + text-decoration: none; + color: var(--primary-color); +} diff --git a/frontend/src/app/components/ui-components/table-display-fields/phone/phone.component.ts b/frontend/src/app/components/ui-components/table-display-fields/phone/phone.component.ts index 6a005952c..1f16898f9 100644 --- a/frontend/src/app/components/ui-components/table-display-fields/phone/phone.component.ts +++ b/frontend/src/app/components/ui-components/table-display-fields/phone/phone.component.ts @@ -1,4 +1,3 @@ -import { BaseTableDisplayFieldComponent } from '../base-table-display-field/base-table-display-field.component'; import { ClipboardModule } from '@angular/cdk/clipboard'; import { CommonModule } from '@angular/common'; import { Component, OnInit } from '@angular/core'; @@ -7,54 +6,55 @@ import { MatIconModule } from '@angular/material/icon'; import { MatTooltipModule } from '@angular/material/tooltip'; import { parsePhoneNumber } from 'libphonenumber-js'; import { COUNTRIES, getCountryFlag } from '../../../../consts/countries'; +import { BaseTableDisplayFieldComponent } from '../base-table-display-field/base-table-display-field.component'; @Component({ - selector: 'app-phone-display', - templateUrl: './phone.component.html', - styleUrls: ['../base-table-display-field/base-table-display-field.component.css', './phone.component.css'], - imports: [ClipboardModule, MatIconModule, MatButtonModule, MatTooltipModule, CommonModule] + selector: 'app-phone-display', + templateUrl: './phone.component.html', + styleUrls: ['../base-table-display-field/base-table-display-field.component.css', './phone.component.css'], + imports: [ClipboardModule, MatIconModule, MatButtonModule, MatTooltipModule, CommonModule], }) export class PhoneDisplayComponent extends BaseTableDisplayFieldComponent implements OnInit { - public countryFlag: string = ''; - public countryName: string = ''; - public formattedNumber: string = ''; - - ngOnInit(): void { - this.parsePhoneNumber(); - } + public countryFlag: string = ''; + public countryName: string = ''; + public formattedNumber: string = ''; + + ngOnInit(): void { + this.parsePhoneNumber(); + } + + private parsePhoneNumber(): void { + if (!this.value || typeof this.value !== 'string') { + this.countryFlag = ''; + this.countryName = ''; + this.formattedNumber = ''; + return; + } + + try { + const phoneNumber = parsePhoneNumber(this.value); - private parsePhoneNumber(): void { - if (!this.value || typeof this.value !== 'string') { - this.countryFlag = ''; - this.countryName = ''; - this.formattedNumber = ''; - return; - } + if (phoneNumber?.country) { + const country = COUNTRIES.find((c) => c.code === phoneNumber.country); - try { - const phoneNumber = parsePhoneNumber(this.value); - - if (phoneNumber?.country) { - const country = COUNTRIES.find(c => c.code === phoneNumber.country); - - if (country) { - this.countryFlag = getCountryFlag(country.code); - this.countryName = country.name; - this.formattedNumber = phoneNumber.formatInternational(); - } else { - this.countryFlag = ''; - this.countryName = ''; - this.formattedNumber = this.value; - } - } else { - this.countryFlag = ''; - this.countryName = ''; - this.formattedNumber = this.value; - } - } catch (_error) { - this.countryFlag = ''; - this.countryName = ''; - this.formattedNumber = this.value; - } - } + if (country) { + this.countryFlag = getCountryFlag(country.code); + this.countryName = country.name; + this.formattedNumber = phoneNumber.formatInternational(); + } else { + this.countryFlag = ''; + this.countryName = ''; + this.formattedNumber = this.value; + } + } else { + this.countryFlag = ''; + this.countryName = ''; + this.formattedNumber = this.value; + } + } catch (_error) { + this.countryFlag = ''; + this.countryName = ''; + this.formattedNumber = this.value; + } + } } diff --git a/frontend/src/app/components/ui-components/table-display-fields/point/point.component.css b/frontend/src/app/components/ui-components/table-display-fields/point/point.component.css index 1296352f2..e6f38b6e6 100644 --- a/frontend/src/app/components/ui-components/table-display-fields/point/point.component.css +++ b/frontend/src/app/components/ui-components/table-display-fields/point/point.component.css @@ -1,21 +1,21 @@ .field-wrapper { - display: flex; - align-items: center; + display: flex; + align-items: center; } .value-wrapper { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - max-width: 300px; - font-family: monospace; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + max-width: 300px; + font-family: monospace; } .copy-button { - visibility: hidden; - margin-left: 4px; + visibility: hidden; + margin-left: 4px; } .field-wrapper:hover .copy-button { - visibility: visible; + visibility: visible; } diff --git a/frontend/src/app/components/ui-components/table-display-fields/point/point.component.ts b/frontend/src/app/components/ui-components/table-display-fields/point/point.component.ts index b42c182d0..bc291ff04 100644 --- a/frontend/src/app/components/ui-components/table-display-fields/point/point.component.ts +++ b/frontend/src/app/components/ui-components/table-display-fields/point/point.component.ts @@ -1,47 +1,46 @@ -import { Component, OnInit } from '@angular/core'; - -import { BaseTableDisplayFieldComponent } from '../base-table-display-field/base-table-display-field.component'; import { ClipboardModule } from '@angular/cdk/clipboard'; import { CommonModule } from '@angular/common'; +import { Component, OnInit } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { MatTooltipModule } from '@angular/material/tooltip'; +import { BaseTableDisplayFieldComponent } from '../base-table-display-field/base-table-display-field.component'; @Component({ - selector: 'app-point-display', - templateUrl: './point.component.html', - styleUrls: ['../base-table-display-field/base-table-display-field.component.css', './point.component.css'], - imports: [ClipboardModule, MatIconModule, MatButtonModule, MatTooltipModule, CommonModule] + selector: 'app-point-display', + templateUrl: './point.component.html', + styleUrls: ['../base-table-display-field/base-table-display-field.component.css', './point.component.css'], + imports: [ClipboardModule, MatIconModule, MatButtonModule, MatTooltipModule, CommonModule], }) export class PointDisplayComponent extends BaseTableDisplayFieldComponent implements OnInit { - static type = 'point'; + static type = 'point'; - formattedPoint: string; + formattedPoint: string; - ngOnInit() { - this.formatPoint(); - } + ngOnInit() { + this.formatPoint(); + } - private formatPoint() { - if (!this.value) { - this.formattedPoint = ''; - return; - } + private formatPoint() { + if (!this.value) { + this.formattedPoint = ''; + return; + } - try { - if (typeof this.value === 'string') { - // Handle string format like "(x,y)" or "x,y" - const pointStr = this.value.trim().replace(/[()]/g, ''); - const [x, y] = pointStr.split(',').map(coord => parseFloat(coord.trim())); - this.formattedPoint = `(${x}, ${y})`; - } else if (typeof this.value === 'object') { - // Handle object format like {x: 1, y: 2} - const x = this.value.x || this.value[0]; - const y = this.value.y || this.value[1]; - this.formattedPoint = `(${x}, ${y})`; - } - } catch (_e) { - this.formattedPoint = String(this.value); - } - } + try { + if (typeof this.value === 'string') { + // Handle string format like "(x,y)" or "x,y" + const pointStr = this.value.trim().replace(/[()]/g, ''); + const [x, y] = pointStr.split(',').map((coord) => parseFloat(coord.trim())); + this.formattedPoint = `(${x}, ${y})`; + } else if (typeof this.value === 'object') { + // Handle object format like {x: 1, y: 2} + const x = this.value.x || this.value[0]; + const y = this.value.y || this.value[1]; + this.formattedPoint = `(${x}, ${y})`; + } + } catch (_e) { + this.formattedPoint = String(this.value); + } + } } diff --git a/frontend/src/app/components/ui-components/table-display-fields/range/range.component.ts b/frontend/src/app/components/ui-components/table-display-fields/range/range.component.ts index a71d8ef81..519a8f428 100644 --- a/frontend/src/app/components/ui-components/table-display-fields/range/range.component.ts +++ b/frontend/src/app/components/ui-components/table-display-fields/range/range.component.ts @@ -1,67 +1,64 @@ -import { Component, Input, OnInit, OnChanges } from '@angular/core'; -import { BaseTableDisplayFieldComponent } from '../base-table-display-field/base-table-display-field.component'; import { CommonModule } from '@angular/common'; +import { Component, Input, OnChanges, OnInit } from '@angular/core'; import { MatProgressBarModule } from '@angular/material/progress-bar'; +import { BaseTableDisplayFieldComponent } from '../base-table-display-field/base-table-display-field.component'; @Component({ - selector: 'app-range-display', - standalone: true, - templateUrl: './range.component.html', - styleUrls: ['./range.component.css'], - imports: [ - CommonModule, - MatProgressBarModule - ], + selector: 'app-range-display', + standalone: true, + templateUrl: './range.component.html', + styleUrls: ['./range.component.css'], + imports: [CommonModule, MatProgressBarModule], }) export class RangeDisplayComponent extends BaseTableDisplayFieldComponent implements OnInit, OnChanges { - @Input() declare value: number; - static type = 'range'; + @Input() declare value: number; + static type = 'range'; - public min: number = 0; - public max: number = 100; - public step: number = 1; - public displayValue: string = ''; + public min: number = 0; + public max: number = 100; + public step: number = 1; + public displayValue: string = ''; - ngOnInit(): void { - this._parseWidgetParams(); - this._updateDisplayValue(); - } + ngOnInit(): void { + this._parseWidgetParams(); + this._updateDisplayValue(); + } - ngOnChanges(): void { - this._parseWidgetParams(); - this._updateDisplayValue(); - } + ngOnChanges(): void { + this._parseWidgetParams(); + this._updateDisplayValue(); + } - public getProgressValue(): number { - const numValue = Number(this.value) || 0; - const range = this.max - this.min; - if (range === 0) return 0; - const progress = ((numValue - this.min) / range) * 100; - // Ensure progress is between 0 and 100 - return Math.max(0, Math.min(100, progress)); - } + public getProgressValue(): number { + const numValue = Number(this.value) || 0; + const range = this.max - this.min; + if (range === 0) return 0; + const progress = ((numValue - this.min) / range) * 100; + // Ensure progress is between 0 and 100 + return Math.max(0, Math.min(100, progress)); + } - private _parseWidgetParams(): void { - if (this.widgetStructure?.widget_params) { - try { - const params = this.widgetStructure.widget_params; - if (params.min !== undefined) { - this.min = Number(params.min) || 0; - } - if (params.max !== undefined) { - this.max = Number(params.max) || 100; - } - if (params.step !== undefined) { - this.step = Number(params.step) || 1; - } - } catch (error) { - console.error('Failed to parse widget params:', error); - } - } - } + private _parseWidgetParams(): void { + if (this.widgetStructure?.widget_params) { + try { + const params = this.widgetStructure.widget_params; + if (params.min !== undefined) { + this.min = Number(params.min) || 0; + } + if (params.max !== undefined) { + this.max = Number(params.max) || 100; + } + if (params.step !== undefined) { + this.step = Number(params.step) || 1; + } + } catch (error) { + console.error('Failed to parse widget params:', error); + } + } + } - private _updateDisplayValue(): void { - const numValue = Number(this.value) || 0; - this.displayValue = `${numValue} / ${this.max}`; - } -} \ No newline at end of file + private _updateDisplayValue(): void { + const numValue = Number(this.value) || 0; + this.displayValue = `${numValue} / ${this.max}`; + } +} diff --git a/frontend/src/app/components/ui-components/table-display-fields/select/select.component.css b/frontend/src/app/components/ui-components/table-display-fields/select/select.component.css index 3015d8368..50c5addee 100644 --- a/frontend/src/app/components/ui-components/table-display-fields/select/select.component.css +++ b/frontend/src/app/components/ui-components/table-display-fields/select/select.component.css @@ -1,4 +1,4 @@ .field-value { - padding: 2px 6px; - border-radius: 4px; + padding: 2px 6px; + border-radius: 4px; } diff --git a/frontend/src/app/components/ui-components/table-display-fields/select/select.component.ts b/frontend/src/app/components/ui-components/table-display-fields/select/select.component.ts index 5888669ef..b0c1022a1 100644 --- a/frontend/src/app/components/ui-components/table-display-fields/select/select.component.ts +++ b/frontend/src/app/components/ui-components/table-display-fields/select/select.component.ts @@ -1,44 +1,43 @@ -import { Component, OnInit } from '@angular/core'; - -import { BaseTableDisplayFieldComponent } from '../base-table-display-field/base-table-display-field.component'; import { ClipboardModule } from '@angular/cdk/clipboard'; import { CommonModule } from '@angular/common'; +import { Component, OnInit } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { MatTooltipModule } from '@angular/material/tooltip'; +import { BaseTableDisplayFieldComponent } from '../base-table-display-field/base-table-display-field.component'; @Component({ - selector: 'app-select-display', - templateUrl: './select.component.html', - styleUrls: ['../base-table-display-field/base-table-display-field.component.css', './select.component.css'], - imports: [CommonModule, ClipboardModule, MatIconModule, MatButtonModule, MatTooltipModule] + selector: 'app-select-display', + templateUrl: './select.component.html', + styleUrls: ['../base-table-display-field/base-table-display-field.component.css', './select.component.css'], + imports: [CommonModule, ClipboardModule, MatIconModule, MatButtonModule, MatTooltipModule], }) export class SelectDisplayComponent extends BaseTableDisplayFieldComponent implements OnInit { - public displayValue: string; - public backgroundColor: string; + public displayValue: string; + public backgroundColor: string; - ngOnInit(): void { - this.setDisplayValue(); - } + ngOnInit(): void { + this.setDisplayValue(); + } - private setDisplayValue(): void { - if (!this.value) { - this.displayValue = '—'; - return; - } + private setDisplayValue(): void { + if (!this.value) { + this.displayValue = '—'; + return; + } - if (this.widgetStructure?.widget_params?.options) { - // Find the matching option based on value and use its label - const option = this.widgetStructure.widget_params.options.find( - (opt: { value: any, label: string }) => opt.value === this.value - ); - this.displayValue = option ? option.label : this.value; - this.backgroundColor = option?.background_color ? option.background_color : 'transparent'; - } else if (this.structure?.data_type_params) { - // If no widget structure but we have data_type_params, just use the value - this.displayValue = this.value; - } else { - this.displayValue = this.value; - } - } + if (this.widgetStructure?.widget_params?.options) { + // Find the matching option based on value and use its label + const option = this.widgetStructure.widget_params.options.find( + (opt: { value: any; label: string }) => opt.value === this.value, + ); + this.displayValue = option ? option.label : this.value; + this.backgroundColor = option?.background_color ? option.background_color : 'transparent'; + } else if (this.structure?.data_type_params) { + // If no widget structure but we have data_type_params, just use the value + this.displayValue = this.value; + } else { + this.displayValue = this.value; + } + } } diff --git a/frontend/src/app/components/ui-components/table-display-fields/static-text/static-text.component.css b/frontend/src/app/components/ui-components/table-display-fields/static-text/static-text.component.css index 8b1378917..e69de29bb 100644 --- a/frontend/src/app/components/ui-components/table-display-fields/static-text/static-text.component.css +++ b/frontend/src/app/components/ui-components/table-display-fields/static-text/static-text.component.css @@ -1 +0,0 @@ - diff --git a/frontend/src/app/components/ui-components/table-display-fields/static-text/static-text.component.ts b/frontend/src/app/components/ui-components/table-display-fields/static-text/static-text.component.ts index a3fc1e86c..483c6c326 100644 --- a/frontend/src/app/components/ui-components/table-display-fields/static-text/static-text.component.ts +++ b/frontend/src/app/components/ui-components/table-display-fields/static-text/static-text.component.ts @@ -1,16 +1,16 @@ -import { BaseTableDisplayFieldComponent } from '../base-table-display-field/base-table-display-field.component'; import { ClipboardModule } from '@angular/cdk/clipboard'; import { Component } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { MatTooltipModule } from '@angular/material/tooltip'; +import { BaseTableDisplayFieldComponent } from '../base-table-display-field/base-table-display-field.component'; @Component({ - selector: 'app-display-static-text', - templateUrl: './static-text.component.html', - styleUrls: ['../base-table-display-field/base-table-display-field.component.css', './static-text.component.css'], - imports: [ClipboardModule, MatIconModule, MatButtonModule, MatTooltipModule] + selector: 'app-display-static-text', + templateUrl: './static-text.component.html', + styleUrls: ['../base-table-display-field/base-table-display-field.component.css', './static-text.component.css'], + imports: [ClipboardModule, MatIconModule, MatButtonModule, MatTooltipModule], }) export class StaticTextDisplayComponent extends BaseTableDisplayFieldComponent { - static type = 'static-text'; + static type = 'static-text'; } diff --git a/frontend/src/app/components/ui-components/table-display-fields/text/text.component.css b/frontend/src/app/components/ui-components/table-display-fields/text/text.component.css index fc157be83..327f9c253 100644 --- a/frontend/src/app/components/ui-components/table-display-fields/text/text.component.css +++ b/frontend/src/app/components/ui-components/table-display-fields/text/text.component.css @@ -1,6 +1,6 @@ .validation-error { - color: var(--color-warnPalette-500); - text-decoration: underline; - text-decoration-style: wavy; - text-underline-offset: 2px; -} \ No newline at end of file + color: var(--color-warnPalette-500); + text-decoration: underline; + text-decoration-style: wavy; + text-underline-offset: 2px; +} diff --git a/frontend/src/app/components/ui-components/table-display-fields/text/text.component.ts b/frontend/src/app/components/ui-components/table-display-fields/text/text.component.ts index 4b42ebe18..745ab63d4 100644 --- a/frontend/src/app/components/ui-components/table-display-fields/text/text.component.ts +++ b/frontend/src/app/components/ui-components/table-display-fields/text/text.component.ts @@ -1,91 +1,91 @@ -import { Component, Injectable } from '@angular/core'; -import * as validator from 'validator'; -import { BaseTableDisplayFieldComponent } from '../base-table-display-field/base-table-display-field.component'; import { ClipboardModule } from '@angular/cdk/clipboard'; +import { Component, Injectable } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { MatTooltipModule } from '@angular/material/tooltip'; +import * as validator from 'validator'; +import { BaseTableDisplayFieldComponent } from '../base-table-display-field/base-table-display-field.component'; @Injectable() @Component({ - selector: 'app-display-text', - templateUrl: './text.component.html', - styleUrls: ['../base-table-display-field/base-table-display-field.component.css', './text.component.css'], - imports: [ClipboardModule, MatIconModule, MatButtonModule, MatTooltipModule] + selector: 'app-display-text', + templateUrl: './text.component.html', + styleUrls: ['../base-table-display-field/base-table-display-field.component.css', './text.component.css'], + imports: [ClipboardModule, MatIconModule, MatButtonModule, MatTooltipModule], }) export class TextDisplayComponent extends BaseTableDisplayFieldComponent { - get isInvalid(): boolean { - if (!this.value || this.value === '') { - return false; - } + get isInvalid(): boolean { + if (!this.value || this.value === '') { + return false; + } - const validateType = this.widgetStructure?.widget_params?.validate; - if (!validateType) { - return false; - } + const validateType = this.widgetStructure?.widget_params?.validate; + if (!validateType) { + return false; + } - const stringValue = String(this.value); + const stringValue = String(this.value); - // Special case for regex validation - if (validateType === 'regex') { - const regexPattern = this.widgetStructure?.widget_params?.regex; - if (!regexPattern) { - return false; - } - try { - const regex = new RegExp(regexPattern); - return !regex.test(stringValue); - } catch (error) { - console.warn('Invalid regex pattern:', error); - return false; - } - } + // Special case for regex validation + if (validateType === 'regex') { + const regexPattern = this.widgetStructure?.widget_params?.regex; + if (!regexPattern) { + return false; + } + try { + const regex = new RegExp(regexPattern); + return !regex.test(stringValue); + } catch (error) { + console.warn('Invalid regex pattern:', error); + return false; + } + } - // Check if validator has this method - // biome-ignore lint: it is expected to import all exports of validator - const validatorMethod = validator[validateType]; - if (typeof validatorMethod !== 'function') { - console.warn(`Unknown validator method: ${validateType}`); - return false; - } + // Check if validator has this method + // biome-ignore lint: it is expected to import all exports of validator + const validatorMethod = validator[validateType]; + if (typeof validatorMethod !== 'function') { + console.warn(`Unknown validator method: ${validateType}`); + return false; + } - try { - // Call the validator method and invert result (true if invalid) - return !validatorMethod(stringValue); - } catch (error) { - console.warn(`Validation error for ${validateType}:`, error); - return false; - } - } + try { + // Call the validator method and invert result (true if invalid) + return !validatorMethod(stringValue); + } catch (error) { + console.warn(`Validation error for ${validateType}:`, error); + return false; + } + } - get validationErrorMessage(): string { - const validateType = this.widgetStructure?.widget_params?.validate; - if (!validateType) { - return ''; - } + get validationErrorMessage(): string { + const validateType = this.widgetStructure?.widget_params?.validate; + if (!validateType) { + return ''; + } - if (validateType === 'regex') { - return 'Does not match the required pattern'; - } + if (validateType === 'regex') { + return 'Does not match the required pattern'; + } - // Create user-friendly messages for common validators - const messages = { - isEmail: 'Invalid email address', - isURL: 'Invalid URL', - isIP: 'Invalid IP address', - isUUID: 'Invalid UUID', - isJSON: 'Invalid JSON', - isCreditCard: 'Invalid credit card number', - isISBN: 'Invalid ISBN', - isAlpha: 'Should contain only letters', - isNumeric: 'Should contain only numbers', - isAlphanumeric: 'Should contain only letters and numbers', - isHexColor: 'Invalid hex color', - isBase64: 'Invalid Base64 string', - isMobilePhone: 'Invalid mobile phone number', - isPostalCode: 'Invalid postal code' - }; + // Create user-friendly messages for common validators + const messages = { + isEmail: 'Invalid email address', + isURL: 'Invalid URL', + isIP: 'Invalid IP address', + isUUID: 'Invalid UUID', + isJSON: 'Invalid JSON', + isCreditCard: 'Invalid credit card number', + isISBN: 'Invalid ISBN', + isAlpha: 'Should contain only letters', + isNumeric: 'Should contain only numbers', + isAlphanumeric: 'Should contain only letters and numbers', + isHexColor: 'Invalid hex color', + isBase64: 'Invalid Base64 string', + isMobilePhone: 'Invalid mobile phone number', + isPostalCode: 'Invalid postal code', + }; - return messages[validateType] || `Invalid ${validateType.replace(/^is/, '').toLowerCase()}`; - } + return messages[validateType] || `Invalid ${validateType.replace(/^is/, '').toLowerCase()}`; + } } diff --git a/frontend/src/app/components/ui-components/table-display-fields/time-interval/time-interval.component.ts b/frontend/src/app/components/ui-components/table-display-fields/time-interval/time-interval.component.ts index 0bc990971..b5a8dd192 100644 --- a/frontend/src/app/components/ui-components/table-display-fields/time-interval/time-interval.component.ts +++ b/frontend/src/app/components/ui-components/table-display-fields/time-interval/time-interval.component.ts @@ -1,41 +1,39 @@ -import { Component, OnInit } from '@angular/core'; - -import { BaseTableDisplayFieldComponent } from '../base-table-display-field/base-table-display-field.component'; import { ClipboardModule } from '@angular/cdk/clipboard'; import { CommonModule } from '@angular/common'; +import { Component, OnInit } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { MatTooltipModule } from '@angular/material/tooltip'; +import { BaseTableDisplayFieldComponent } from '../base-table-display-field/base-table-display-field.component'; @Component({ - selector: 'app-time-interval-display', - templateUrl: './time-interval.component.html', - styleUrls: ['../base-table-display-field/base-table-display-field.component.css', './time-interval.component.css'], - imports: [ClipboardModule, MatIconModule, MatButtonModule, MatTooltipModule, CommonModule] + selector: 'app-time-interval-display', + templateUrl: './time-interval.component.html', + styleUrls: ['../base-table-display-field/base-table-display-field.component.css', './time-interval.component.css'], + imports: [ClipboardModule, MatIconModule, MatButtonModule, MatTooltipModule, CommonModule], }) export class TimeIntervalDisplayComponent extends BaseTableDisplayFieldComponent implements OnInit { + formattedInterval: string; - formattedInterval: string; - - ngOnInit() { - if (!this.value) { - this.formattedInterval = '—'; - return; - } + ngOnInit() { + if (!this.value) { + this.formattedInterval = '—'; + return; + } - try { - const interval = typeof this.value === 'string' ? JSON.parse(this.value) : this.value; - let parts = []; + try { + const interval = typeof this.value === 'string' ? JSON.parse(this.value) : this.value; + let parts = []; - if (interval.days) parts.push(`${interval.days}d`); - if (interval.hours) parts.push(`${interval.hours}h`); - if (interval.minutes) parts.push(`${interval.minutes}m`); - if (interval.seconds) parts.push(`${interval.seconds}s`); - if (interval.milliseconds) parts.push(`${interval.milliseconds}ms`); + if (interval.days) parts.push(`${interval.days}d`); + if (interval.hours) parts.push(`${interval.hours}h`); + if (interval.minutes) parts.push(`${interval.minutes}m`); + if (interval.seconds) parts.push(`${interval.seconds}s`); + if (interval.milliseconds) parts.push(`${interval.milliseconds}ms`); - this.formattedInterval = parts.length > 0 ? parts.join(' ') : '0'; - } catch (_e) { - this.formattedInterval = String(this.value); - } - } + this.formattedInterval = parts.length > 0 ? parts.join(' ') : '0'; + } catch (_e) { + this.formattedInterval = String(this.value); + } + } } diff --git a/frontend/src/app/components/ui-components/table-display-fields/time/time.component.css b/frontend/src/app/components/ui-components/table-display-fields/time/time.component.css index 8b1378917..e69de29bb 100644 --- a/frontend/src/app/components/ui-components/table-display-fields/time/time.component.css +++ b/frontend/src/app/components/ui-components/table-display-fields/time/time.component.css @@ -1 +0,0 @@ - diff --git a/frontend/src/app/components/ui-components/table-display-fields/time/time.component.ts b/frontend/src/app/components/ui-components/table-display-fields/time/time.component.ts index 3f6cbad5b..7ffe777a3 100644 --- a/frontend/src/app/components/ui-components/table-display-fields/time/time.component.ts +++ b/frontend/src/app/components/ui-components/table-display-fields/time/time.component.ts @@ -1,40 +1,39 @@ -import { Component, OnInit } from '@angular/core'; - -import { BaseTableDisplayFieldComponent } from '../base-table-display-field/base-table-display-field.component'; import { ClipboardModule } from '@angular/cdk/clipboard'; +import { Component, OnInit } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { MatTooltipModule } from '@angular/material/tooltip'; import { format } from 'date-fns'; +import { BaseTableDisplayFieldComponent } from '../base-table-display-field/base-table-display-field.component'; @Component({ - selector: 'app-time-display', - templateUrl: './time.component.html', - styleUrls: ['../base-table-display-field/base-table-display-field.component.css', './time.component.css'], - imports: [ClipboardModule, MatIconModule, MatButtonModule, MatTooltipModule] + selector: 'app-time-display', + templateUrl: './time.component.html', + styleUrls: ['../base-table-display-field/base-table-display-field.component.css', './time.component.css'], + imports: [ClipboardModule, MatIconModule, MatButtonModule, MatTooltipModule], }) export class TimeDisplayComponent extends BaseTableDisplayFieldComponent implements OnInit { - static type = 'time'; + static type = 'time'; - public formattedTime: string; + public formattedTime: string; - ngOnInit(): void { - if (this.value) { - try { - if (this.value.includes(':')) { - // Handle time string format - this.formattedTime = this.value; - } else { - const date = new Date(this.value); - if (!Number.isNaN(date.getTime())) { - this.formattedTime = format(date, 'HH:mm:ss'); - } else { - this.formattedTime = this.value; - } - } - } catch (_error) { - this.formattedTime = this.value; - } - } - } + ngOnInit(): void { + if (this.value) { + try { + if (this.value.includes(':')) { + // Handle time string format + this.formattedTime = this.value; + } else { + const date = new Date(this.value); + if (!Number.isNaN(date.getTime())) { + this.formattedTime = format(date, 'HH:mm:ss'); + } else { + this.formattedTime = this.value; + } + } + } catch (_error) { + this.formattedTime = this.value; + } + } + } } diff --git a/frontend/src/app/components/ui-components/table-display-fields/timezone/timezone.component.ts b/frontend/src/app/components/ui-components/table-display-fields/timezone/timezone.component.ts index a867e7584..6ece6c773 100644 --- a/frontend/src/app/components/ui-components/table-display-fields/timezone/timezone.component.ts +++ b/frontend/src/app/components/ui-components/table-display-fields/timezone/timezone.component.ts @@ -1,57 +1,57 @@ -import { Component, Injectable } from '@angular/core'; -import { BaseTableDisplayFieldComponent } from '../base-table-display-field/base-table-display-field.component'; import { ClipboardModule } from '@angular/cdk/clipboard'; +import { Component, Injectable } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { MatTooltipModule } from '@angular/material/tooltip'; +import { BaseTableDisplayFieldComponent } from '../base-table-display-field/base-table-display-field.component'; @Injectable() @Component({ - selector: 'app-display-timezone', - templateUrl: './timezone.component.html', - styleUrls: ['../base-table-display-field/base-table-display-field.component.css', './timezone.component.css'], - imports: [ClipboardModule, MatIconModule, MatButtonModule, MatTooltipModule] + selector: 'app-display-timezone', + templateUrl: './timezone.component.html', + styleUrls: ['../base-table-display-field/base-table-display-field.component.css', './timezone.component.css'], + imports: [ClipboardModule, MatIconModule, MatButtonModule, MatTooltipModule], }) export class TimezoneDisplayComponent extends BaseTableDisplayFieldComponent { - get formattedTimezone(): string { - if (!this.value) { - return '—'; - } + get formattedTimezone(): string { + if (!this.value) { + return '—'; + } - try { - const offset = this.getTimezoneOffset(this.value); - return `${this.value} (UTC${offset})`; - } catch (_error) { - return this.value; - } - } + try { + const offset = this.getTimezoneOffset(this.value); + return `${this.value} (UTC${offset})`; + } catch (_error) { + return this.value; + } + } - private getTimezoneOffset(timezone: string): string { - try { - const now = new Date(); - const formatter = new Intl.DateTimeFormat('en-US', { - timeZone: timezone, - timeZoneName: 'longOffset' - }); + private getTimezoneOffset(timezone: string): string { + try { + const now = new Date(); + const formatter = new Intl.DateTimeFormat('en-US', { + timeZone: timezone, + timeZoneName: 'longOffset', + }); - const parts = formatter.formatToParts(now); - const offsetPart = parts.find(part => part.type === 'timeZoneName'); + const parts = formatter.formatToParts(now); + const offsetPart = parts.find((part) => part.type === 'timeZoneName'); - if (offsetPart?.value.includes('GMT')) { - const offset = offsetPart.value.replace('GMT', ''); - return offset === '' ? '+00:00' : offset; - } + if (offsetPart?.value.includes('GMT')) { + const offset = offsetPart.value.replace('GMT', ''); + return offset === '' ? '+00:00' : offset; + } - // Fallback: calculate offset manually - const utcDate = new Date(now.toLocaleString('en-US', { timeZone: 'UTC' })); - const tzDate = new Date(now.toLocaleString('en-US', { timeZone: timezone })); - const offsetMinutes = (tzDate.getTime() - utcDate.getTime()) / 60000; - const hours = Math.floor(Math.abs(offsetMinutes) / 60); - const minutes = Math.abs(offsetMinutes) % 60; - const sign = offsetMinutes >= 0 ? '+' : '-'; - return `${sign}${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}`; - } catch (_error) { - return ''; - } - } + // Fallback: calculate offset manually + const utcDate = new Date(now.toLocaleString('en-US', { timeZone: 'UTC' })); + const tzDate = new Date(now.toLocaleString('en-US', { timeZone: timezone })); + const offsetMinutes = (tzDate.getTime() - utcDate.getTime()) / 60000; + const hours = Math.floor(Math.abs(offsetMinutes) / 60); + const minutes = Math.abs(offsetMinutes) % 60; + const sign = offsetMinutes >= 0 ? '+' : '-'; + return `${sign}${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}`; + } catch (_error) { + return ''; + } + } } diff --git a/frontend/src/app/components/ui-components/table-display-fields/url/url.component.css b/frontend/src/app/components/ui-components/table-display-fields/url/url.component.css index 78c2d730a..d4a57897b 100644 --- a/frontend/src/app/components/ui-components/table-display-fields/url/url.component.css +++ b/frontend/src/app/components/ui-components/table-display-fields/url/url.component.css @@ -1,14 +1,14 @@ .url-link { - display: flex; - align-items: center; - gap: 4px; - color: var(--color-primaryPalette-500); + display: flex; + align-items: center; + gap: 4px; + color: var(--color-primaryPalette-500); } .url-link__icon { - flex-shrink: 0; - margin-bottom: -2px; - width: 16px; - height: 16px; - font-size: 16px; + flex-shrink: 0; + margin-bottom: -2px; + width: 16px; + height: 16px; + font-size: 16px; } diff --git a/frontend/src/app/components/ui-components/table-display-fields/url/url.component.ts b/frontend/src/app/components/ui-components/table-display-fields/url/url.component.ts index b7c059fd9..23be28862 100644 --- a/frontend/src/app/components/ui-components/table-display-fields/url/url.component.ts +++ b/frontend/src/app/components/ui-components/table-display-fields/url/url.component.ts @@ -1,34 +1,34 @@ -import { BaseTableDisplayFieldComponent } from '../base-table-display-field/base-table-display-field.component'; import { ClipboardModule } from '@angular/cdk/clipboard'; import { CommonModule } from '@angular/common'; import { Component } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { MatTooltipModule } from '@angular/material/tooltip'; +import { BaseTableDisplayFieldComponent } from '../base-table-display-field/base-table-display-field.component'; @Component({ - selector: 'app-url-display', - templateUrl: './url.component.html', - styleUrls: ['../base-table-display-field/base-table-display-field.component.css', './url.component.css'], - imports: [ClipboardModule, MatIconModule, MatButtonModule, MatTooltipModule, CommonModule] + selector: 'app-url-display', + templateUrl: './url.component.html', + styleUrls: ['../base-table-display-field/base-table-display-field.component.css', './url.component.css'], + imports: [ClipboardModule, MatIconModule, MatButtonModule, MatTooltipModule, CommonModule], }) export class UrlDisplayComponent extends BaseTableDisplayFieldComponent { - static type = 'url'; - - get hrefValue(): string { - if (!this.value) return ''; - const prefix = this.widgetStructure?.widget_params?.prefix || ''; - return prefix + this.value; - } - - get isValidUrl(): boolean { - if (!this.value) return false; - try { - // Check if the prefixed URL is valid - new URL(this.hrefValue); - return true; - } catch { - return false; - } - } + static type = 'url'; + + get hrefValue(): string { + if (!this.value) return ''; + const prefix = this.widgetStructure?.widget_params?.prefix || ''; + return prefix + this.value; + } + + get isValidUrl(): boolean { + if (!this.value) return false; + try { + // Check if the prefixed URL is valid + new URL(this.hrefValue); + return true; + } catch { + return false; + } + } } diff --git a/frontend/src/app/components/ui-components/table-display-fields/uuid/uuid.component.css b/frontend/src/app/components/ui-components/table-display-fields/uuid/uuid.component.css index f81799d51..6cd942ea5 100644 --- a/frontend/src/app/components/ui-components/table-display-fields/uuid/uuid.component.css +++ b/frontend/src/app/components/ui-components/table-display-fields/uuid/uuid.component.css @@ -1,37 +1,37 @@ .uuid-display { - display: flex; - align-items: center; - gap: 4px; + display: flex; + align-items: center; + gap: 4px; } .uuid-text { - font-family: 'Roboto Condensed', 'Arial Narrow', sans-serif; - font-stretch: condensed; + font-family: "Roboto Condensed", "Arial Narrow", sans-serif; + font-stretch: condensed; } .copy-button { - width: 24px; - height: 24px; - line-height: 24px; - opacity: 0; - transition: opacity 0.2s; + width: 24px; + height: 24px; + line-height: 24px; + opacity: 0; + transition: opacity 0.2s; } .field-value:hover .copy-button { - opacity: 0.7; + opacity: 0.7; } .copy-button:hover { - opacity: 1 !important; + opacity: 1 !important; } .copy-button mat-icon { - font-size: 16px; - width: 16px; - height: 16px; + font-size: 16px; + width: 16px; + height: 16px; } .null-value { - color: var(--mat-grey-500); - font-style: italic; + color: var(--mat-grey-500); + font-style: italic; } diff --git a/frontend/src/app/components/ui-components/table-display-fields/uuid/uuid.component.ts b/frontend/src/app/components/ui-components/table-display-fields/uuid/uuid.component.ts index d27b70c91..5ac07f600 100644 --- a/frontend/src/app/components/ui-components/table-display-fields/uuid/uuid.component.ts +++ b/frontend/src/app/components/ui-components/table-display-fields/uuid/uuid.component.ts @@ -1,17 +1,16 @@ -import { Component, Injectable } from '@angular/core'; -import { BaseTableDisplayFieldComponent } from '../base-table-display-field/base-table-display-field.component'; import { ClipboardModule } from '@angular/cdk/clipboard'; import { CommonModule } from '@angular/common'; +import { Component, Injectable } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { MatTooltipModule } from '@angular/material/tooltip'; +import { BaseTableDisplayFieldComponent } from '../base-table-display-field/base-table-display-field.component'; @Injectable() @Component({ - selector: 'app-display-uuid', - templateUrl: './uuid.component.html', - styleUrls: ['../base-table-display-field/base-table-display-field.component.css', './uuid.component.css'], - imports: [CommonModule, ClipboardModule, MatIconModule, MatButtonModule, MatTooltipModule] + selector: 'app-display-uuid', + templateUrl: './uuid.component.html', + styleUrls: ['../base-table-display-field/base-table-display-field.component.css', './uuid.component.css'], + imports: [CommonModule, ClipboardModule, MatIconModule, MatButtonModule, MatTooltipModule], }) -export class UuidDisplayComponent extends BaseTableDisplayFieldComponent { -} \ No newline at end of file +export class UuidDisplayComponent extends BaseTableDisplayFieldComponent {} diff --git a/frontend/src/app/components/ui-components/user-password/user-password.component.css b/frontend/src/app/components/ui-components/user-password/user-password.component.css index daafbedbd..431124627 100644 --- a/frontend/src/app/components/ui-components/user-password/user-password.component.css +++ b/frontend/src/app/components/ui-components/user-password/user-password.component.css @@ -1,23 +1,23 @@ .password-field { - margin-bottom: -16px; - width: 100%; + margin-bottom: -16px; + width: 100%; } .password-requirements { - list-style: none; - columns: 2; - gap: 60px; - margin-top: 2px; + list-style: none; + columns: 2; + gap: 60px; + margin-top: 2px; } .password-requirements li::before { - content: '—'; + content: "—"; } .error { - color: #E53935; + color: #e53935; } .success { - color: #43A047; -} \ No newline at end of file + color: #43a047; +} diff --git a/frontend/src/app/components/ui-components/user-password/user-password.component.spec.ts b/frontend/src/app/components/ui-components/user-password/user-password.component.spec.ts index 42f4ca741..856b23357 100644 --- a/frontend/src/app/components/ui-components/user-password/user-password.component.spec.ts +++ b/frontend/src/app/components/ui-components/user-password/user-password.component.spec.ts @@ -1,28 +1,26 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { FormsModule } from '@angular/forms'; - -import { UserPasswordComponent } from './user-password.component'; -import { IPasswordStrengthMeterService } from 'angular-password-strength-meter'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { IPasswordStrengthMeterService } from 'angular-password-strength-meter'; import { Angulartics2Module } from 'angulartics2'; +import { UserPasswordComponent } from './user-password.component'; describe('UserPasswordComponent', () => { - let component: UserPasswordComponent; - let fixture: ComponentFixture; + let component: UserPasswordComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [FormsModule, BrowserAnimationsModule, Angulartics2Module.forRoot({}), UserPasswordComponent], - providers: [{ provide: IPasswordStrengthMeterService, useValue: {} }] - }) - .compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [FormsModule, BrowserAnimationsModule, Angulartics2Module.forRoot({}), UserPasswordComponent], + providers: [{ provide: IPasswordStrengthMeterService, useValue: {} }], + }).compileComponents(); - fixture = TestBed.createComponent(UserPasswordComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + fixture = TestBed.createComponent(UserPasswordComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/upgrade-success/upgrade-success.component.css b/frontend/src/app/components/upgrade-success/upgrade-success.component.css index 9acec1a14..3ff7d9ee1 100644 --- a/frontend/src/app/components/upgrade-success/upgrade-success.component.css +++ b/frontend/src/app/components/upgrade-success/upgrade-success.component.css @@ -1,3 +1,3 @@ .plan-name { - text-transform: capitalize; -} \ No newline at end of file + text-transform: capitalize; +} diff --git a/frontend/src/app/components/upgrade-success/upgrade-success.component.spec.ts b/frontend/src/app/components/upgrade-success/upgrade-success.component.spec.ts index 9332c6726..d7a94f89d 100644 --- a/frontend/src/app/components/upgrade-success/upgrade-success.component.spec.ts +++ b/frontend/src/app/components/upgrade-success/upgrade-success.component.spec.ts @@ -1,33 +1,36 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { ActivatedRoute, } from '@angular/router'; - -import { UpgradeSuccessComponent } from './upgrade-success.component'; import { MatSnackBarModule } from '@angular/material/snack-bar'; +import { ActivatedRoute } from '@angular/router'; +import { UpgradeSuccessComponent } from './upgrade-success.component'; describe('UpgradeSuccessComponent', () => { - let component: UpgradeSuccessComponent; - let fixture: ComponentFixture; + let component: UpgradeSuccessComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [MatSnackBarModule, UpgradeSuccessComponent], - providers: [ - { provide: ActivatedRoute, useValue: { - snapshot: { queryParams: { - plan: 'free' - } }, - } }, - // { provide: Router, useValue: routerSpy }, - ], -}) - .compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [MatSnackBarModule, UpgradeSuccessComponent], + providers: [ + { + provide: ActivatedRoute, + useValue: { + snapshot: { + queryParams: { + plan: 'free', + }, + }, + }, + }, + // { provide: Router, useValue: routerSpy }, + ], + }).compileComponents(); - fixture = TestBed.createComponent(UpgradeSuccessComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + fixture = TestBed.createComponent(UpgradeSuccessComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/upgrade-success/upgrade-success.component.ts b/frontend/src/app/components/upgrade-success/upgrade-success.component.ts index 12b6a8048..78dd2aff6 100644 --- a/frontend/src/app/components/upgrade-success/upgrade-success.component.ts +++ b/frontend/src/app/components/upgrade-success/upgrade-success.component.ts @@ -1,25 +1,21 @@ +import { CommonModule } from '@angular/common'; import { Component, OnInit } from '@angular/core'; - +import { MatButtonModule } from '@angular/material/button'; import { ActivatedRoute } from '@angular/router'; import { BannerComponent } from '../ui-components/banner/banner.component'; -import { CommonModule } from '@angular/common'; -import { MatButtonModule } from '@angular/material/button'; @Component({ - selector: 'app-upgrade-success', - imports: [CommonModule, MatButtonModule, BannerComponent], - templateUrl: './upgrade-success.component.html', - styleUrls: ['./upgrade-success.component.css'] + selector: 'app-upgrade-success', + imports: [CommonModule, MatButtonModule, BannerComponent], + templateUrl: './upgrade-success.component.html', + styleUrls: ['./upgrade-success.component.css'], }) export class UpgradeSuccessComponent implements OnInit { + public newPlan: string; - public newPlan: string; - - constructor( - private route: ActivatedRoute - ) { } + constructor(private route: ActivatedRoute) {} - ngOnInit(): void { - this.newPlan = this.route.snapshot.queryParams.plan; - } + ngOnInit(): void { + this.newPlan = this.route.snapshot.queryParams.plan; + } } diff --git a/frontend/src/app/components/upgrade/upgrade.component.ts b/frontend/src/app/components/upgrade/upgrade.component.ts index 22f7419aa..43d4f8103 100644 --- a/frontend/src/app/components/upgrade/upgrade.component.ts +++ b/frontend/src/app/components/upgrade/upgrade.component.ts @@ -1,242 +1,242 @@ -import { Component, OnInit } from '@angular/core'; - -import { AlertComponent } from '../ui-components/alert/alert.component'; import { CommonModule } from '@angular/common'; -import { CompanyMemberRole } from 'src/app/models/company'; -import { CompanyService } from 'src/app/services/company.service'; +import { Component, OnInit } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatChipsModule } from '@angular/material/chips'; import { MatIconModule } from '@angular/material/icon'; import { MatTableModule } from '@angular/material/table'; -import { PaymentService } from 'src/app/services/payment.service'; -import { PlanKey } from 'src/app/models/plans'; -import { ProfileSidebarComponent } from '../profile/profile-sidebar/profile-sidebar.component'; import { RouterModule } from '@angular/router'; +import { CompanyMemberRole } from 'src/app/models/company'; +import { PlanKey } from 'src/app/models/plans'; +import { CompanyService } from 'src/app/services/company.service'; +import { PaymentService } from 'src/app/services/payment.service'; import { UserService } from 'src/app/services/user.service'; import plans from '../../consts/plans'; +import { ProfileSidebarComponent } from '../profile/profile-sidebar/profile-sidebar.component'; +import { AlertComponent } from '../ui-components/alert/alert.component'; @Component({ - selector: 'app-upgrade', - standalone: true, - imports: [ - CommonModule, - MatButtonModule, - MatIconModule, - MatTableModule, - MatChipsModule, - RouterModule, - AlertComponent, - ProfileSidebarComponent - ], - templateUrl: './upgrade.component.html', - styleUrls: ['./upgrade.component.css'] + selector: 'app-upgrade', + standalone: true, + imports: [ + CommonModule, + MatButtonModule, + MatIconModule, + MatTableModule, + MatChipsModule, + RouterModule, + AlertComponent, + ProfileSidebarComponent, + ], + templateUrl: './upgrade.component.html', + styleUrls: ['./upgrade.component.css'], }) export class UpgradeComponent implements OnInit { - public currentPlan = { - key: PlanKey.Free, - price: 0, - name: 'Free', - }; - public hasPaymentMethod = false; - public portalLink: string = null; - public companyId: string; - public submitting = false; - public isAdmin = false; - - databases = [ - { - title: 'MySQL', - free: '3', - team: '∞', - enterprise: '∞' - }, - { - title: 'PostgreSQL', - free: 3, - team: '∞', - enterprise: '∞' - }, - { - title: 'MongoDB', - free: 3, - team: '∞', - enterprise: '∞' - }, - { - title: 'DynamoDB', - free: 3, - team: '∞', - enterprise: '∞' - }, - { - title: 'IBM DB2', - free: 3, - team: '∞', - enterprise: '∞' - }, - { - title: 'Oracle', - free: 1, - team: 1, - enterprise: '∞' - }, - { - title: 'Microsoft SQL', - free: 1, - team: 1, - enterprise: '∞' - }, - { - title: 'Cassandra', - free: 1, - team: 1, - enterprise: '∞' - } - ] + public currentPlan = { + key: PlanKey.Free, + price: 0, + name: 'Free', + }; + public hasPaymentMethod = false; + public portalLink: string = null; + public companyId: string; + public submitting = false; + public isAdmin = false; - users = [ - { - title: 'Priority support', - free: '', - team: true, - enterprise: true - }, - { - title:'SAML SSO', - free: '', - team: '', - enterprise: true - }, - { - title: '99.9% uptime SLA', - free: '', - team: '', - enterprise: true - } - ] + databases = [ + { + title: 'MySQL', + free: '3', + team: '∞', + enterprise: '∞', + }, + { + title: 'PostgreSQL', + free: 3, + team: '∞', + enterprise: '∞', + }, + { + title: 'MongoDB', + free: 3, + team: '∞', + enterprise: '∞', + }, + { + title: 'DynamoDB', + free: 3, + team: '∞', + enterprise: '∞', + }, + { + title: 'IBM DB2', + free: 3, + team: '∞', + enterprise: '∞', + }, + { + title: 'Oracle', + free: 1, + team: 1, + enterprise: '∞', + }, + { + title: 'Microsoft SQL', + free: 1, + team: 1, + enterprise: '∞', + }, + { + title: 'Cassandra', + free: 1, + team: 1, + enterprise: '∞', + }, + ]; - features = [ - { - title: 'Standard actions (CRUD)', - free: true, - team: true, - enterprise: true - }, - { - title: 'Custom actions and webhooks', - free: true, - team: true, - enterprise: true - }, - { - title: 'Export / Import', - free: true, - team: true, - enterprise: true - }, - { - title: 'Search and filters', - free: true, - team: true, - enterprise: true - }, - { - title: 'Record fields customization', - free: true, - team: true, - enterprise: true - }, - { - title: 'Client encryption', - free: true, - team: true, - enterprise: true - }, - { - title: 'Branding', - free: true, - team: true, - enterprise: true - }, - { - title: 'Custom domain', - free: '', - team: true, - enterprise: true - }, - { - title: 'Log audit', - free: '', - team: true, - enterprise: true - } - ] + users = [ + { + title: 'Priority support', + free: '', + team: true, + enterprise: true, + }, + { + title: 'SAML SSO', + free: '', + team: '', + enterprise: true, + }, + { + title: '99.9% uptime SLA', + free: '', + team: '', + enterprise: true, + }, + ]; - public plans = plans; - public displayedColumns = plans.map(plan => plan.key); + features = [ + { + title: 'Standard actions (CRUD)', + free: true, + team: true, + enterprise: true, + }, + { + title: 'Custom actions and webhooks', + free: true, + team: true, + enterprise: true, + }, + { + title: 'Export / Import', + free: true, + team: true, + enterprise: true, + }, + { + title: 'Search and filters', + free: true, + team: true, + enterprise: true, + }, + { + title: 'Record fields customization', + free: true, + team: true, + enterprise: true, + }, + { + title: 'Client encryption', + free: true, + team: true, + enterprise: true, + }, + { + title: 'Branding', + free: true, + team: true, + enterprise: true, + }, + { + title: 'Custom domain', + free: '', + team: true, + enterprise: true, + }, + { + title: 'Log audit', + free: '', + team: true, + enterprise: true, + }, + ]; - constructor( - private _paymentService: PaymentService, - private _company: CompanyService, - private _userService: UserService, - ) { } + public plans = plans; + public displayedColumns = plans.map((plan) => plan.key); - ngOnInit(): void { - this.getCompanyPaln(); - this._userService.cast.subscribe((user) => { - this.isAdmin = user.role === CompanyMemberRole.CAO; - }); - } + constructor( + private _paymentService: PaymentService, + private _company: CompanyService, + private _userService: UserService, + ) {} - getCompanyPaln() { - this._company.fetchCompany().subscribe(company => { - this.currentPlan = this.getPlan(company.subscriptionLevel); - this.companyId = company.id; - this.hasPaymentMethod = company.is_payment_method_added; - this.portalLink = company.portal_link; - }) - } + ngOnInit(): void { + this.getCompanyPaln(); + this._userService.cast.subscribe((user) => { + this.isAdmin = user.role === CompanyMemberRole.CAO; + }); + } - getPlan(planKey: PlanKey) { - let currentPlanKey = planKey || 'FREE_PLAN'; - if (currentPlanKey.startsWith('ANNUAL_')) { - currentPlanKey = currentPlanKey.substring(7); - } + getCompanyPaln() { + this._company.fetchCompany().subscribe((company) => { + this.currentPlan = this.getPlan(company.subscriptionLevel); + this.companyId = company.id; + this.hasPaymentMethod = company.is_payment_method_added; + this.portalLink = company.portal_link; + }); + } - currentPlanKey = currentPlanKey.slice(0, -5).toLowerCase(); - const planData = plans.find(plan => plan.key === currentPlanKey); + getPlan(planKey: PlanKey) { + let currentPlanKey = planKey || 'FREE_PLAN'; + if (currentPlanKey.startsWith('ANNUAL_')) { + currentPlanKey = currentPlanKey.substring(7); + } - return { - key: currentPlanKey as PlanKey, - price: planData?.price || 0, - name: planData?.name || 'Free', - }; - } + currentPlanKey = currentPlanKey.slice(0, -5).toLowerCase(); + const planData = plans.find((plan) => plan.key === currentPlanKey); - changePlan(planKey: PlanKey) { - this.submitting = true; - const formattedPlanKey = planKey.toUpperCase() + '_PLAN'; - this._paymentService.changeSubscription(this.companyId, formattedPlanKey).subscribe(() => { - this.getCompanyPaln(); - this.submitting = false; - }, - () => this.submitting = false, - () => this.submitting = false); - } + return { + key: currentPlanKey as PlanKey, + price: planData?.price || 0, + name: planData?.name || 'Free', + }; + } + changePlan(planKey: PlanKey) { + this.submitting = true; + const formattedPlanKey = planKey.toUpperCase() + '_PLAN'; + this._paymentService.changeSubscription(this.companyId, formattedPlanKey).subscribe( + () => { + this.getCompanyPaln(); + this.submitting = false; + }, + () => (this.submitting = false), + () => (this.submitting = false), + ); + } - // upgradePlan(plan, isAnnually: boolean) { - // this.submitting = true; - // let subscriptionLevel = `${plan.toUpperCase()}_PLAN`; - // if (isAnnually && plan !== 'free') subscriptionLevel = `ANNUAL_${subscriptionLevel}`; - // this._userService.upgradeUser(subscriptionLevel).subscribe(() => { - // this._userService.fetchUser().subscribe(() => { - // this.submitting = false; - // this.isCurrentAnnually = this.isAnnually - // } - // ); - // }, - // () => this.submitting = false, - // () => this.submitting = false); - // } + // upgradePlan(plan, isAnnually: boolean) { + // this.submitting = true; + // let subscriptionLevel = `${plan.toUpperCase()}_PLAN`; + // if (isAnnually && plan !== 'free') subscriptionLevel = `ANNUAL_${subscriptionLevel}`; + // this._userService.upgradeUser(subscriptionLevel).subscribe(() => { + // this._userService.fetchUser().subscribe(() => { + // this.submitting = false; + // this.isCurrentAnnually = this.isAnnually + // } + // ); + // }, + // () => this.submitting = false, + // () => this.submitting = false); + // } } diff --git a/frontend/src/app/components/user-deleted-success/user-deleted-success.component.spec.ts b/frontend/src/app/components/user-deleted-success/user-deleted-success.component.spec.ts index 52615ad1c..44098bcb6 100644 --- a/frontend/src/app/components/user-deleted-success/user-deleted-success.component.spec.ts +++ b/frontend/src/app/components/user-deleted-success/user-deleted-success.component.spec.ts @@ -3,21 +3,20 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { UserDeletedSuccessComponent } from './user-deleted-success.component'; describe('UserDeletedSuccessComponent', () => { - let component: UserDeletedSuccessComponent; - let fixture: ComponentFixture; + let component: UserDeletedSuccessComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [UserDeletedSuccessComponent] -}) - .compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [UserDeletedSuccessComponent], + }).compileComponents(); - fixture = TestBed.createComponent(UserDeletedSuccessComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + fixture = TestBed.createComponent(UserDeletedSuccessComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/user-deleted-success/user-deleted-success.component.ts b/frontend/src/app/components/user-deleted-success/user-deleted-success.component.ts index 083138966..678a2449d 100644 --- a/frontend/src/app/components/user-deleted-success/user-deleted-success.component.ts +++ b/frontend/src/app/components/user-deleted-success/user-deleted-success.component.ts @@ -1,17 +1,13 @@ +import { CommonModule } from '@angular/common'; import { Component, OnInit } from '@angular/core'; - import { BannerComponent } from '../ui-components/banner/banner.component'; -import { CommonModule } from '@angular/common'; @Component({ - selector: 'app-user-deleted-success', - imports: [CommonModule, BannerComponent], - templateUrl: './user-deleted-success.component.html', - styleUrls: ['./user-deleted-success.component.css'] + selector: 'app-user-deleted-success', + imports: [CommonModule, BannerComponent], + templateUrl: './user-deleted-success.component.html', + styleUrls: ['./user-deleted-success.component.css'], }) export class UserDeletedSuccessComponent implements OnInit { - - ngOnInit(): void { - } - + ngOnInit(): void {} } diff --git a/frontend/src/app/components/user-settings/account-delete-confirmation/account-delete-confirmation.component.spec.ts b/frontend/src/app/components/user-settings/account-delete-confirmation/account-delete-confirmation.component.spec.ts index ab2b9b361..0c2c01605 100644 --- a/frontend/src/app/components/user-settings/account-delete-confirmation/account-delete-confirmation.component.spec.ts +++ b/frontend/src/app/components/user-settings/account-delete-confirmation/account-delete-confirmation.component.spec.ts @@ -1,45 +1,38 @@ +import { provideHttpClient } from '@angular/common/http'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog'; - -import { AccountDeleteConfirmationComponent } from './account-delete-confirmation.component'; import { MatSnackBarModule } from '@angular/material/snack-bar'; -import { Angulartics2Module } from 'angulartics2'; -import { provideHttpClient } from '@angular/common/http'; import { provideRouter } from '@angular/router'; +import { Angulartics2Module } from 'angulartics2'; +import { AccountDeleteConfirmationComponent } from './account-delete-confirmation.component'; describe('AccountDeleteConfirmationComponent', () => { - let component: AccountDeleteConfirmationComponent; - let fixture: ComponentFixture; + let component: AccountDeleteConfirmationComponent; + let fixture: ComponentFixture; - const mockDialogRef = { - close: () => { } - }; + const mockDialogRef = { + close: () => {}, + }; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [ - MatDialogModule, - MatSnackBarModule, - Angulartics2Module.forRoot(), - AccountDeleteConfirmationComponent - ], - providers: [ - provideHttpClient(), - provideRouter([]), - { provide: MAT_DIALOG_DATA, useValue: {} }, - { provide: MatDialogRef, useValue: mockDialogRef } - ] -}) - .compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [MatDialogModule, MatSnackBarModule, Angulartics2Module.forRoot(), AccountDeleteConfirmationComponent], + providers: [ + provideHttpClient(), + provideRouter([]), + { provide: MAT_DIALOG_DATA, useValue: {} }, + { provide: MatDialogRef, useValue: mockDialogRef }, + ], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(AccountDeleteConfirmationComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(AccountDeleteConfirmationComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/user-settings/account-delete-dialog/account-delete-dialog.component.css b/frontend/src/app/components/user-settings/account-delete-dialog/account-delete-dialog.component.css index 9c13a3630..5f6d2d4b8 100644 --- a/frontend/src/app/components/user-settings/account-delete-dialog/account-delete-dialog.component.css +++ b/frontend/src/app/components/user-settings/account-delete-dialog/account-delete-dialog.component.css @@ -1,20 +1,20 @@ .radio-group { - display: flex; - flex-direction: column; + display: flex; + flex-direction: column; } .radio-group__message { - margin-bottom: 8px; + margin-bottom: 8px; } .radio-button { - margin: 8px 0; + margin: 8px 0; } .message { - width: 100%; + width: 100%; } .message textarea { - resize: none; -} \ No newline at end of file + resize: none; +} diff --git a/frontend/src/app/components/user-settings/account-delete-dialog/account-delete-dialog.component.ts b/frontend/src/app/components/user-settings/account-delete-dialog/account-delete-dialog.component.ts index ad98bc55b..54b221950 100644 --- a/frontend/src/app/components/user-settings/account-delete-dialog/account-delete-dialog.component.ts +++ b/frontend/src/app/components/user-settings/account-delete-dialog/account-delete-dialog.component.ts @@ -1,87 +1,90 @@ -import { Component, OnInit, Inject } from '@angular/core'; -import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material/dialog'; +import { CommonModule } from '@angular/common'; +import { Component, Inject, OnInit } from '@angular/core'; +import { FormsModule } from '@angular/forms'; import { MatButtonModule } from '@angular/material/button'; -import { MatDialogModule } from '@angular/material/dialog'; +import { MAT_DIALOG_DATA, MatDialog, MatDialogModule, MatDialogRef } from '@angular/material/dialog'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; import { MatRadioModule } from '@angular/material/radio'; -import { FormsModule } from '@angular/forms'; -import { CommonModule } from '@angular/common'; import { AccountDeleteConfirmationComponent } from '../account-delete-confirmation/account-delete-confirmation.component'; @Component({ - selector: 'app-account-delete-dialog', - imports: [ - CommonModule, - FormsModule, - MatButtonModule, - MatDialogModule, - MatFormFieldModule, - MatInputModule, - MatRadioModule - ], - templateUrl: './account-delete-dialog.component.html', - styleUrls: ['./account-delete-dialog.component.css'] + selector: 'app-account-delete-dialog', + imports: [ + CommonModule, + FormsModule, + MatButtonModule, + MatDialogModule, + MatFormFieldModule, + MatInputModule, + MatRadioModule, + ], + templateUrl: './account-delete-dialog.component.html', + styleUrls: ['./account-delete-dialog.component.css'], }) export class AccountDeleteDialogComponent implements OnInit { + public userEmail: string; + public reason: string; + public message: string; + public reasonsList = [ + { + id: 'missing-features', + caption: 'Missing features I need', + message: + 'Perhaps Rocketadmin has updated the functions you need and they are in a different place. ' + + 'Our Support team will advise you.', + }, + { + id: 'technical-issues', + caption: 'Technical issues', + message: + 'If you have technical problems, please contact our Support team and we will solve your problem.', + }, + { + id: 'another-product', + caption: 'Another product', + message: + 'If you are interested in another product, you can read the article where they are compared.', + }, + { + id: 'privacy-concern', + caption: 'I have privacy concern', + message: + 'If you have any doubts about the security of your data, please contact our Support team.', + }, + { + id: 'money-value', + caption: 'I`m not getting value from my membership.', + message: + 'If you have other membership needs, you can always choose another trial plan.', + }, + { + id: 'other', + caption: 'Other', + }, + ]; - public userEmail: string; - public reason: string; - public message: string; - public reasonsList = [ - { - id: 'missing-features', - caption: 'Missing features I need', - message: 'Perhaps Rocketadmin has updated the functions you need and they are in a different place. ' + - 'Our Support team will advise you.' - }, - { - id: 'technical-issues', - caption: 'Technical issues', - message: 'If you have technical problems, please contact our Support team and we will solve your problem.' - }, - { - id: 'another-product', - caption: 'Another product', - message: 'If you are interested in another product, you can read the article where they are compared.' - }, - { - id: 'privacy-concern', - caption: 'I have privacy concern', - message: 'If you have any doubts about the security of your data, please contact our Support team.' - }, - { - id: 'money-value', - caption: 'I`m not getting value from my membership.', - message: 'If you have other membership needs, you can always choose another trial plan.' - }, - { - id: 'other', - caption: 'Other' - } - ] - - constructor( - @Inject(MAT_DIALOG_DATA) public data: any, - public dialog: MatDialog, - public dialogRef: MatDialogRef, - ) { } + constructor( + @Inject(MAT_DIALOG_DATA) public data: any, + public dialog: MatDialog, + public dialogRef: MatDialogRef, + ) {} - ngOnInit(): void { - this.userEmail = this.data.email; - } + ngOnInit(): void { + this.userEmail = this.data.email; + } - openDeleteConfirmation() { - const metadata = { - reason: this.reason, - message: this.message - }; + openDeleteConfirmation() { + const metadata = { + reason: this.reason, + message: this.message, + }; - this.dialog.open(AccountDeleteConfirmationComponent, { - width: '20em', - data: metadata - }); + this.dialog.open(AccountDeleteConfirmationComponent, { + width: '20em', + data: metadata, + }); - this.dialogRef.close(); - } + this.dialogRef.close(); + } } diff --git a/frontend/src/app/components/user-settings/api-key-delete-dialog/api-key-delete-dialog.component.spec.ts b/frontend/src/app/components/user-settings/api-key-delete-dialog/api-key-delete-dialog.component.spec.ts index 655c3e61c..e744ff7c7 100644 --- a/frontend/src/app/components/user-settings/api-key-delete-dialog/api-key-delete-dialog.component.spec.ts +++ b/frontend/src/app/components/user-settings/api-key-delete-dialog/api-key-delete-dialog.component.spec.ts @@ -1,36 +1,32 @@ +import { provideHttpClient } from '@angular/common/http'; import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { ApiKeyDeleteDialogComponent } from './api-key-delete-dialog.component'; import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog'; -import { provideHttpClient } from '@angular/common/http'; +import { ApiKeyDeleteDialogComponent } from './api-key-delete-dialog.component'; describe('ApiKeyDeleteDialogComponent', () => { - let component: ApiKeyDeleteDialogComponent; - let fixture: ComponentFixture; + let component: ApiKeyDeleteDialogComponent; + let fixture: ComponentFixture; - const mockDialogRef = { - close: () => { } - }; + const mockDialogRef = { + close: () => {}, + }; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [ - MatDialogModule, - ApiKeyDeleteDialogComponent - ], - providers: [ - provideHttpClient(), - { provide: MAT_DIALOG_DATA, useValue: {} }, - { provide: MatDialogRef, useValue: mockDialogRef } - ] - }).compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [MatDialogModule, ApiKeyDeleteDialogComponent], + providers: [ + provideHttpClient(), + { provide: MAT_DIALOG_DATA, useValue: {} }, + { provide: MatDialogRef, useValue: mockDialogRef }, + ], + }).compileComponents(); - fixture = TestBed.createComponent(ApiKeyDeleteDialogComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + fixture = TestBed.createComponent(ApiKeyDeleteDialogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/user-settings/api-key-delete-dialog/api-key-delete-dialog.component.ts b/frontend/src/app/components/user-settings/api-key-delete-dialog/api-key-delete-dialog.component.ts index 797f70943..791329d4b 100644 --- a/frontend/src/app/components/user-settings/api-key-delete-dialog/api-key-delete-dialog.component.ts +++ b/frontend/src/app/components/user-settings/api-key-delete-dialog/api-key-delete-dialog.component.ts @@ -1,35 +1,36 @@ -import { Component, Inject } from '@angular/core'; -import { MAT_DIALOG_DATA, MatDialogRef, MatDialogModule } from '@angular/material/dialog'; import { CommonModule } from '@angular/common'; +import { Component, Inject } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; -import { UserService } from 'src/app/services/user.service'; +import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog'; import { ApiKey } from 'src/app/models/user'; +import { UserService } from 'src/app/services/user.service'; @Component({ - selector: 'app-api-key-delete-dialog', - templateUrl: './api-key-delete-dialog.component.html', - styleUrl: './api-key-delete-dialog.component.css', - imports: [CommonModule, MatDialogModule, MatButtonModule] + selector: 'app-api-key-delete-dialog', + templateUrl: './api-key-delete-dialog.component.html', + styleUrl: './api-key-delete-dialog.component.css', + imports: [CommonModule, MatDialogModule, MatButtonModule], }) export class ApiKeyDeleteDialogComponent { + public submitting: boolean = false; - public submitting: boolean = false; - - constructor( - @Inject(MAT_DIALOG_DATA) public data: ApiKey, - private _userService: UserService, - public dialogRef: MatDialogRef - ) { } + constructor( + @Inject(MAT_DIALOG_DATA) public data: ApiKey, + private _userService: UserService, + public dialogRef: MatDialogRef, + ) {} - deleteAPIkey() { - this.submitting = true; - this._userService.deleteAPIkey(this.data) - .subscribe( () => { - this.dialogRef.close(); - this.submitting = false; - }, - () => { }, - () => { this.submitting = false; } - ) - } + deleteAPIkey() { + this.submitting = true; + this._userService.deleteAPIkey(this.data).subscribe( + () => { + this.dialogRef.close(); + this.submitting = false; + }, + () => {}, + () => { + this.submitting = false; + }, + ); + } } diff --git a/frontend/src/app/components/user-settings/enable-two-fa-dialog/enable-two-fa-dialog.component.css b/frontend/src/app/components/user-settings/enable-two-fa-dialog/enable-two-fa-dialog.component.css index 1a9387725..a32a8def3 100644 --- a/frontend/src/app/components/user-settings/enable-two-fa-dialog/enable-two-fa-dialog.component.css +++ b/frontend/src/app/components/user-settings/enable-two-fa-dialog/enable-two-fa-dialog.component.css @@ -1,4 +1,4 @@ .authenticator-code { - margin-top: 12px; - width: 100%; -} \ No newline at end of file + margin-top: 12px; + width: 100%; +} diff --git a/frontend/src/app/components/user-settings/enable-two-fa-dialog/enable-two-fa-dialog.component.spec.ts b/frontend/src/app/components/user-settings/enable-two-fa-dialog/enable-two-fa-dialog.component.spec.ts index 06885eb7f..693ab4e72 100644 --- a/frontend/src/app/components/user-settings/enable-two-fa-dialog/enable-two-fa-dialog.component.spec.ts +++ b/frontend/src/app/components/user-settings/enable-two-fa-dialog/enable-two-fa-dialog.component.spec.ts @@ -1,44 +1,43 @@ +import { provideHttpClient } from '@angular/common/http'; import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog'; - -import { EnableTwoFADialogComponent } from './enable-two-fa-dialog.component'; import { FormsModule } from '@angular/forms'; +import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog'; import { MatSnackBarModule } from '@angular/material/snack-bar'; -import { Angulartics2Module } from 'angulartics2'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { provideHttpClient } from '@angular/common/http'; +import { Angulartics2Module } from 'angulartics2'; +import { EnableTwoFADialogComponent } from './enable-two-fa-dialog.component'; describe('EnableTwoFADialogComponent', () => { - let component: EnableTwoFADialogComponent; - let fixture: ComponentFixture; + let component: EnableTwoFADialogComponent; + let fixture: ComponentFixture; - const mockDialogRef = { - close: () => { } - }; + const mockDialogRef = { + close: () => {}, + }; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [ - MatSnackBarModule, - FormsModule, - MatDialogModule, - Angulartics2Module.forRoot(), - EnableTwoFADialogComponent, - BrowserAnimationsModule - ], - providers: [ - provideHttpClient(), - { provide: MAT_DIALOG_DATA, useValue: {} }, - { provide: MatDialogRef, useValue: mockDialogRef }, - ], - }).compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + MatSnackBarModule, + FormsModule, + MatDialogModule, + Angulartics2Module.forRoot(), + EnableTwoFADialogComponent, + BrowserAnimationsModule, + ], + providers: [ + provideHttpClient(), + { provide: MAT_DIALOG_DATA, useValue: {} }, + { provide: MatDialogRef, useValue: mockDialogRef }, + ], + }).compileComponents(); - fixture = TestBed.createComponent(EnableTwoFADialogComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + fixture = TestBed.createComponent(EnableTwoFADialogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/user-settings/user-settings.component.css b/frontend/src/app/components/user-settings/user-settings.component.css index 29bd2ffb2..483571be6 100644 --- a/frontend/src/app/components/user-settings/user-settings.component.css +++ b/frontend/src/app/components/user-settings/user-settings.component.css @@ -1,134 +1,134 @@ .profile-layout { - display: flex; - height: calc(100vh - 64px); + display: flex; + height: calc(100vh - 64px); } .profile-main { - flex: 1; - background-color: var(--mat-sidenav-content-background-color); - overflow-y: auto; + flex: 1; + background-color: var(--mat-sidenav-content-background-color); + overflow-y: auto; } ::ng-deep .profile-main > app-alert { - position: relative; - top: 0; - margin: 24px; + position: relative; + top: 0; + margin: 24px; } .page { - display: flex; - flex-direction: column; - margin: var(--top-margin) auto; - max-width: 520px; - min-width: 300px; - width: 96%; - padding: 0 24px; + display: flex; + flex-direction: column; + margin: var(--top-margin) auto; + max-width: 520px; + min-width: 300px; + width: 96%; + padding: 0 24px; } @media (width <= 600px) { - .page { - padding: 0 16px; - margin: 1.5em auto; - } + .page { + padding: 0 16px; + margin: 1.5em auto; + } } .user-settings { - margin-top: 32px; + margin-top: 32px; } .user-settings__name-form { - display: flex; - align-items: center; - margin-bottom: -24px; + display: flex; + align-items: center; + margin-bottom: -24px; } .user-settings__section-heading { - display: flex; - align-items: center; - gap: 12px; - margin-top: 40px; - margin-bottom: 8px; + display: flex; + align-items: center; + gap: 12px; + margin-top: 40px; + margin-bottom: 8px; } .user-settings__text-input { - width: 100% + width: 100%; } .user-settings__save-button { - margin-top: -20px; - margin-left: -68px; + margin-top: -20px; + margin-left: -68px; } .user-settings__value { - margin-bottom: 0 !important; + margin-bottom: 0 !important; } .twoFA-toggle { - display: flex; - align-items: center; - gap: 12px; - margin-top: 40px; + display: flex; + align-items: center; + gap: 12px; + margin-top: 40px; } .twoFA-toggle .twoFAhint { - font-size: 20px; - height: 20px; - width: 20px; + font-size: 20px; + height: 20px; + width: 20px; } .qr-verification { - margin-top: 28px; + margin-top: 28px; } .api-key-form { - display: flex; - align-items: center; + display: flex; + align-items: center; } .api-key-form__generate-button { - margin-top: -20px; - margin-left: -82px; + margin-top: -20px; + margin-left: -82px; } .api-key-value { - display: flex; - margin-bottom: 8px; - width: 100%; + display: flex; + margin-bottom: 8px; + width: 100%; } .api-key-value__copy-button { - margin-top: 4px; - margin-left: 12px; + margin-top: 4px; + margin-left: 12px; } .api-key-list-item:hover { - background: var(--color-primaryPalette-50); + background: var(--color-primaryPalette-50); } @media (prefers-color-scheme: dark) { - .api-key-list-item:hover { - background: var(--color-primaryPalette-500); - } + .api-key-list-item:hover { + background: var(--color-primaryPalette-500); + } - ::ng-deep .user-settings__section-heading mat-icon[svgIcon] svg path { - fill: #ffffff; - } + ::ng-deep .user-settings__section-heading mat-icon[svgIcon] svg path { + fill: #ffffff; + } } @media (prefers-color-scheme: light) { - .api-key-list-item:hover { - background: var(--color-primaryPalette-50); - } + .api-key-list-item:hover { + background: var(--color-primaryPalette-50); + } } .api-key { - display: flex; - align-items: center; - justify-content: space-between; - /* width: 100%; */ + display: flex; + align-items: center; + justify-content: space-between; + /* width: 100%; */ } .delete-button { - align-self: center; - margin-top: 80px; -} \ No newline at end of file + align-self: center; + margin-top: 80px; +} diff --git a/frontend/src/app/components/users/group-add-dialog/group-add-dialog.component.css b/frontend/src/app/components/users/group-add-dialog/group-add-dialog.component.css index b321285f7..fcaa8cf92 100644 --- a/frontend/src/app/components/users/group-add-dialog/group-add-dialog.component.css +++ b/frontend/src/app/components/users/group-add-dialog/group-add-dialog.component.css @@ -1,3 +1,3 @@ .mat-mdc-form-field { - width: 100%; -} \ No newline at end of file + width: 100%; +} diff --git a/frontend/src/app/components/users/group-name-edit-dialog/group-name-edit-dialog.component.css b/frontend/src/app/components/users/group-name-edit-dialog/group-name-edit-dialog.component.css index b321285f7..fcaa8cf92 100644 --- a/frontend/src/app/components/users/group-name-edit-dialog/group-name-edit-dialog.component.css +++ b/frontend/src/app/components/users/group-name-edit-dialog/group-name-edit-dialog.component.css @@ -1,3 +1,3 @@ .mat-mdc-form-field { - width: 100%; -} \ No newline at end of file + width: 100%; +} diff --git a/frontend/src/app/components/users/group-name-edit-dialog/group-name-edit-dialog.component.spec.ts b/frontend/src/app/components/users/group-name-edit-dialog/group-name-edit-dialog.component.spec.ts index 722d34a67..834d4b22e 100644 --- a/frontend/src/app/components/users/group-name-edit-dialog/group-name-edit-dialog.component.spec.ts +++ b/frontend/src/app/components/users/group-name-edit-dialog/group-name-edit-dialog.component.spec.ts @@ -1,41 +1,35 @@ +import { provideHttpClient } from '@angular/common/http'; import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog'; import { FormsModule } from '@angular/forms'; -import { GroupNameEditDialogComponent } from './group-name-edit-dialog.component'; +import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog'; import { MatSnackBarModule } from '@angular/material/snack-bar'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { provideHttpClient } from '@angular/common/http'; +import { GroupNameEditDialogComponent } from './group-name-edit-dialog.component'; describe('GroupNameEditDialogComponent', () => { - let component: GroupNameEditDialogComponent; - let fixture: ComponentFixture; + let component: GroupNameEditDialogComponent; + let fixture: ComponentFixture; - const mockDialogRef = { - close: () => { } - }; + const mockDialogRef = { + close: () => {}, + }; - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ - MatDialogModule, - MatSnackBarModule, - FormsModule, - BrowserAnimationsModule, - GroupNameEditDialogComponent - ], - providers: [ - provideHttpClient(), - { provide: MAT_DIALOG_DATA, useValue: {} }, - { provide: MatDialogRef, useValue: mockDialogRef }, - ], - }).compileComponents(); - fixture = TestBed.createComponent(GroupNameEditDialogComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [MatDialogModule, MatSnackBarModule, FormsModule, BrowserAnimationsModule, GroupNameEditDialogComponent], + providers: [ + provideHttpClient(), + { provide: MAT_DIALOG_DATA, useValue: {} }, + { provide: MatDialogRef, useValue: mockDialogRef }, + ], + }).compileComponents(); + fixture = TestBed.createComponent(GroupNameEditDialogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/components/users/group-name-edit-dialog/group-name-edit-dialog.component.ts b/frontend/src/app/components/users/group-name-edit-dialog/group-name-edit-dialog.component.ts index 94c967a23..9da466ab9 100644 --- a/frontend/src/app/components/users/group-name-edit-dialog/group-name-edit-dialog.component.ts +++ b/frontend/src/app/components/users/group-name-edit-dialog/group-name-edit-dialog.component.ts @@ -1,52 +1,46 @@ +import { NgIf } from '@angular/common'; import { Component, Inject } from '@angular/core'; -import { MAT_DIALOG_DATA, MatDialogRef, MatDialogModule } from '@angular/material/dialog'; +import { FormsModule } from '@angular/forms'; +import { MatButtonModule } from '@angular/material/button'; +import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatInputModule } from '@angular/material/input'; -import { MatButtonModule } from '@angular/material/button'; -import { FormsModule } from '@angular/forms'; import { UsersService } from 'src/app/services/users.service'; import { GroupAddDialogComponent } from '../group-add-dialog/group-add-dialog.component'; -import { NgIf } from '@angular/common'; @Component({ - selector: 'app-group-name-edit-dialog', - templateUrl: './group-name-edit-dialog.component.html', - styleUrls: ['./group-name-edit-dialog.component.css'], - imports: [ - NgIf, - MatDialogModule, - MatFormFieldModule, - MatInputModule, - MatButtonModule, - FormsModule - ] + selector: 'app-group-name-edit-dialog', + templateUrl: './group-name-edit-dialog.component.html', + styleUrls: ['./group-name-edit-dialog.component.css'], + imports: [NgIf, MatDialogModule, MatFormFieldModule, MatInputModule, MatButtonModule, FormsModule], }) export class GroupNameEditDialogComponent { - public connectionID: string; - public groupTitle: string = ''; - public submitting: boolean = false; + public connectionID: string; + public groupTitle: string = ''; + public submitting: boolean = false; - constructor( - @Inject(MAT_DIALOG_DATA) public group: any, - public _usersService: UsersService, - public dialogRef: MatDialogRef - ) { } + constructor( + @Inject(MAT_DIALOG_DATA) public group: any, + public _usersService: UsersService, + public dialogRef: MatDialogRef, + ) {} - ngOnInit(): void { - this.groupTitle = this.group.title; - this._usersService.cast.subscribe(); - } + ngOnInit(): void { + this.groupTitle = this.group.title; + this._usersService.cast.subscribe(); + } - addGroup() { - this.submitting = true; - this._usersService.editUsersGroupName(this.group.id, this.groupTitle) - .subscribe( - () => { - this.submitting = false; - this.dialogRef.close(); - }, - () => { }, - () => { this.submitting = false; } - ); - } + addGroup() { + this.submitting = true; + this._usersService.editUsersGroupName(this.group.id, this.groupTitle).subscribe( + () => { + this.submitting = false; + this.dialogRef.close(); + }, + () => {}, + () => { + this.submitting = false; + }, + ); + } } diff --git a/frontend/src/app/components/users/permissions-add-dialog/permissions-add-dialog.component.css b/frontend/src/app/components/users/permissions-add-dialog/permissions-add-dialog.component.css index 44a869288..420ed8631 100644 --- a/frontend/src/app/components/users/permissions-add-dialog/permissions-add-dialog.component.css +++ b/frontend/src/app/components/users/permissions-add-dialog/permissions-add-dialog.component.css @@ -1,152 +1,152 @@ .permissions-header { - display: flex; - align-items: center; - justify-content: space-between; - margin: 16px 0; + display: flex; + align-items: center; + justify-content: space-between; + margin: 16px 0; } .permissions-header::before { - display: none; + display: none; } .permissions-alert { - --alert-margin: 0 0 20px; - - top: 0; + --alert-margin: 0 0 20px; + + top: 0; } .permissions-form ::ng-deep .mat-mdc-dialog-content { - color: var(--mat-sidenav-content-text-color); + color: var(--mat-sidenav-content-text-color); } .permissions { - display: grid; - grid-template-columns: auto 1fr; - grid-column-gap: 60px; - grid-row-gap: 16px; - align-items: center; - margin-bottom: 8px; + display: grid; + grid-template-columns: auto 1fr; + grid-column-gap: 60px; + grid-row-gap: 16px; + align-items: center; + margin-bottom: 8px; } .permissions__title { - margin-bottom: 0 !important; + margin-bottom: 0 !important; } .permissions-toggle-group { - justify-self: flex-start; + justify-self: flex-start; } .tables-options { - display: flex; - align-items: center; - justify-content: flex-end; + display: flex; + align-items: center; + justify-content: flex-end; } .tables-list { - position: relative; - grid-column: 1 / span 2; - display: grid; - grid-template-columns: 72px 1fr 72px repeat(3, 60px); - grid-row-gap: 8px; - align-items: center; - justify-items: center; - margin: 0 -8px; + position: relative; + grid-column: 1 / span 2; + display: grid; + grid-template-columns: 72px 1fr 72px repeat(3, 60px); + grid-row-gap: 8px; + align-items: center; + justify-items: center; + margin: 0 -8px; } .tables-list__header { - grid-column: 1 / -1; - display: grid; - grid-template-columns: subgrid; - justify-items: center; - background-color: var(--color-accentedPalette-100); - padding: 8px 0; + grid-column: 1 / -1; + display: grid; + grid-template-columns: subgrid; + justify-items: center; + background-color: var(--color-accentedPalette-100); + padding: 8px 0; } @media (prefers-color-scheme: dark) { - .tables-list__header { - background-color: var(--color-accentedPalette-800); - } + .tables-list__header { + background-color: var(--color-accentedPalette-800); + } } .tables-list__item { - display: contents; - border-bottom: 1px solid rgba(0, 0, 0, 0.12); + display: contents; + border-bottom: 1px solid rgba(0, 0, 0, 0.12); } .tables-list__divider { - grid-column: 1 / -1; - width: 100%; + grid-column: 1 / -1; + width: 100%; } .table-name-title { - justify-self: flex-start; - padding-left: 16px; + justify-self: flex-start; + padding-left: 16px; } .table-name { - --normalized-table-name-color: var(--mat-sidenav-content-text-color); - --orginal-table-name-color: rgba(0, 0, 0, 0.6); + --normalized-table-name-color: var(--mat-sidenav-content-text-color); + --orginal-table-name-color: rgba(0, 0, 0, 0.6); - justify-self: flex-start; - display: flex; - flex-direction: column; - gap: 4px; - padding-left: 16px; + justify-self: flex-start; + display: flex; + flex-direction: column; + gap: 4px; + padding-left: 16px; } @media (prefers-color-scheme: dark) { - .table-name { - --orginal-table-name-color: rgba(255, 255, 255, 0.75); - } + .table-name { + --orginal-table-name-color: rgba(255, 255, 255, 0.75); + } } .table-name_disabled { - --normalized-table-name-color: rgba(0, 0, 0, 0.38); - --orginal-table-name-color: rgba(0, 0, 0, 0.38); + --normalized-table-name-color: rgba(0, 0, 0, 0.38); + --orginal-table-name-color: rgba(0, 0, 0, 0.38); } @media (prefers-color-scheme: dark) { - .table-name_disabled { - --normalized-table-name-color: rgba(255, 255, 255, 0.38); - --orginal-table-name-color: rgba(255, 255, 255, 0.38); - } + .table-name_disabled { + --normalized-table-name-color: rgba(255, 255, 255, 0.38); + --orginal-table-name-color: rgba(255, 255, 255, 0.38); + } } .table-name__title { - color: var(--normalized-table-name-color); - font-size: 16px; - margin-bottom: -6px; + color: var(--normalized-table-name-color); + font-size: 16px; + margin-bottom: -6px; } .table-name__line { - color: var(--orginal-table-name-color); - font-size: 12px; + color: var(--orginal-table-name-color); + font-size: 12px; } .tables-overlay { - position: absolute; - background: rgba(255, 255, 255, 0.75); - box-sizing: border-box; - padding-top: 12px; - width: 100%; - height: 100%; - z-index: 2; - text-align: center; + position: absolute; + background: rgba(255, 255, 255, 0.75); + box-sizing: border-box; + padding-top: 12px; + width: 100%; + height: 100%; + z-index: 2; + text-align: center; } .tables-overlay__message { - background: rgba(255, 255, 255, 0.9); - box-shadow: 0 0 8px 8px rgba(255, 255, 255, 0.9); - padding: 8px; - display: block; - max-width: 32%; - margin: 0 auto; + background: rgba(255, 255, 255, 0.9); + box-shadow: 0 0 8px 8px rgba(255, 255, 255, 0.9); + padding: 8px; + display: block; + max-width: 32%; + margin: 0 auto; } .visibilityIcon { - cursor: pointer; + cursor: pointer; } .visibilityIcon_visible { - color: var(--color-accentedPalette-500); + color: var(--color-accentedPalette-500); } diff --git a/frontend/src/app/components/users/user-add-dialog/user-add-dialog.component.css b/frontend/src/app/components/users/user-add-dialog/user-add-dialog.component.css index 1b1e6d729..f6a52d0ce 100644 --- a/frontend/src/app/components/users/user-add-dialog/user-add-dialog.component.css +++ b/frontend/src/app/components/users/user-add-dialog/user-add-dialog.component.css @@ -1,24 +1,24 @@ .mat-mdc-form-field { - width: 100%; + width: 100%; } .message-box { - display: flex; - flex-direction: column; - align-items: center; + display: flex; + flex-direction: column; + align-items: center; } .message-text { - text-align: center; - margin-bottom: 24px; + text-align: center; + margin-bottom: 24px; } .link { - color: var(--color-primaryPalette-500); + color: var(--color-primaryPalette-500); } @media (prefers-color-scheme: dark) { - .link { - color: var(--color-whitePalette-500); - } -} \ No newline at end of file + .link { + color: var(--color-whitePalette-500); + } +} diff --git a/frontend/src/app/components/users/user-delete-dialog/user-delete-dialog.component.ts b/frontend/src/app/components/users/user-delete-dialog/user-delete-dialog.component.ts index f06f927ff..e9619afa4 100644 --- a/frontend/src/app/components/users/user-delete-dialog/user-delete-dialog.component.ts +++ b/frontend/src/app/components/users/user-delete-dialog/user-delete-dialog.component.ts @@ -1,39 +1,39 @@ -import { Component, OnInit, Inject } from '@angular/core'; -import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; -import { UsersService } from 'src/app/services/users.service'; -import { MatButtonModule } from '@angular/material/button'; -import { MatDialogModule } from '@angular/material/dialog'; import { CommonModule } from '@angular/common'; +import { Component, Inject, OnInit } from '@angular/core'; +import { MatButtonModule } from '@angular/material/button'; +import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog'; +import { UsersService } from 'src/app/services/users.service'; @Component({ - selector: 'app-user-delete-dialog', - templateUrl: './user-delete-dialog.component.html', - styleUrls: ['./user-delete-dialog.component.css'], - imports: [CommonModule, MatButtonModule, MatDialogModule] + selector: 'app-user-delete-dialog', + templateUrl: './user-delete-dialog.component.html', + styleUrls: ['./user-delete-dialog.component.css'], + imports: [CommonModule, MatButtonModule, MatDialogModule], }) export class UserDeleteDialogComponent implements OnInit { + public submitting: boolean = false; - public submitting: boolean = false; - - constructor( - @Inject(MAT_DIALOG_DATA) public data: any, - private _usersService: UsersService, - public dialogRef: MatDialogRef - ) { } + constructor( + @Inject(MAT_DIALOG_DATA) public data: any, + private _usersService: UsersService, + public dialogRef: MatDialogRef, + ) {} - ngOnInit(): void { - this._usersService.cast.subscribe(); - } + ngOnInit(): void { + this._usersService.cast.subscribe(); + } - deleteGroupUser() { - this.submitting = true; - this._usersService.deleteGroupUser(this.data.user.email, this.data.group.id) - .subscribe(() => { - this.dialogRef.close(); - this.submitting = false; - }, - () => { }, - () => { this.submitting = false; } - ) - } + deleteGroupUser() { + this.submitting = true; + this._usersService.deleteGroupUser(this.data.user.email, this.data.group.id).subscribe( + () => { + this.dialogRef.close(); + this.submitting = false; + }, + () => {}, + () => { + this.submitting = false; + }, + ); + } } diff --git a/frontend/src/app/components/users/users.component.css b/frontend/src/app/components/users/users.component.css index ba352d78c..212c5e825 100644 --- a/frontend/src/app/components/users/users.component.css +++ b/frontend/src/app/components/users/users.component.css @@ -1,63 +1,63 @@ .wrapper { - margin: 0 auto; - width: clamp(18.75em, 96%, 42em); + margin: 0 auto; + width: clamp(18.75em, 96%, 42em); } header { - display: flex; - align-items: center; - justify-content: space-between; - margin: 2em 0 1em; + display: flex; + align-items: center; + justify-content: space-between; + margin: 2em 0 1em; } .mat-h1 { - margin: 0; + margin: 0; } .expansion-panel_admin { - margin-bottom: 20px; + margin-bottom: 20px; } -.add-group-button .mat-icon{ - font-size: 2em; +.add-group-button .mat-icon { + font-size: 2em; } .mat-expansion-panel-header-title { - align-items: center; + align-items: center; } .title-edit-button { - display: none; - color: var(--mat-expansion-header-description-color); - margin-left: 12px; + display: none; + color: var(--mat-expansion-header-description-color); + margin-left: 12px; } .group-actions { - display: none; - flex-grow: 0 !important; - justify-content: flex-end; - align-items: center; + display: none; + flex-grow: 0 !important; + justify-content: flex-end; + align-items: center; } .group-item:hover .title-edit-button, .mat-expanded .title-edit-button { - display: block; + display: block; } .group-item:hover .group-actions, .mat-expanded .group-actions { - display: flex; + display: flex; } .user { - display: flex; - align-items: center; - justify-content: space-between; - box-sizing: border-box; - padding-right: 1.5em; - width: calc(100% + 1em); + display: flex; + align-items: center; + justify-content: space-between; + box-sizing: border-box; + padding-right: 1.5em; + width: calc(100% + 1em); } .no-access { - margin-top: 32px !important; + margin-top: 32px !important; } diff --git a/frontend/src/app/components/zapier/zapier.component.css b/frontend/src/app/components/zapier/zapier.component.css index d0458fe0b..0921729bc 100644 --- a/frontend/src/app/components/zapier/zapier.component.css +++ b/frontend/src/app/components/zapier/zapier.component.css @@ -1,29 +1,29 @@ .profile-layout { - display: flex; - height: calc(100vh - 64px); + display: flex; + height: calc(100vh - 64px); } .profile-main { - flex: 1; - background-color: var(--mat-sidenav-content-background-color); - overflow-y: auto; + flex: 1; + background-color: var(--mat-sidenav-content-background-color); + overflow-y: auto; } ::ng-deep .profile-main > app-alert { - position: relative; - top: 0; - margin: 24px; + position: relative; + top: 0; + margin: 24px; } .zapier-page { - display: flex; - flex-direction: column; - margin: var(--top-margin) auto; - padding: 0 clamp(40px, 5vw, 100px); - max-width: 1200px; + display: flex; + flex-direction: column; + margin: var(--top-margin) auto; + padding: 0 clamp(40px, 5vw, 100px); + max-width: 1200px; } .zapier-docs-button { - align-self: flex-end; - margin-bottom: -36px; + align-self: flex-end; + margin-bottom: -36px; } diff --git a/frontend/src/app/components/zapier/zapier.component.spec.ts b/frontend/src/app/components/zapier/zapier.component.spec.ts index 75e427ffd..d4f23678c 100644 --- a/frontend/src/app/components/zapier/zapier.component.spec.ts +++ b/frontend/src/app/components/zapier/zapier.component.spec.ts @@ -1,26 +1,24 @@ +import { provideHttpClient } from '@angular/common/http'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { RouterModule } from '@angular/router'; - import { ZapierComponent } from './zapier.component'; -import { provideHttpClient } from '@angular/common/http'; describe('ZapierComponent', () => { - let component: ZapierComponent; - let fixture: ComponentFixture; + let component: ZapierComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [ZapierComponent, RouterModule.forRoot([])], - providers: [provideHttpClient()] - }) - .compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ZapierComponent, RouterModule.forRoot([])], + providers: [provideHttpClient()], + }).compileComponents(); - fixture = TestBed.createComponent(ZapierComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + fixture = TestBed.createComponent(ZapierComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/frontend/src/app/consts/acronyms.ts b/frontend/src/app/consts/acronyms.ts index b4e113a5a..990f4c316 100644 --- a/frontend/src/app/consts/acronyms.ts +++ b/frontend/src/app/consts/acronyms.ts @@ -1,5828 +1,5828 @@ export default [ -'0D', -'1AM', -'1D', -'2AM', -'2D', -'2G', -'2LA', -'2Lt', -'3AM', -'3D', -'3G', -'3i', -'3M', -'4D', -'4G', -'4GL', -'4WD', -'5D', -'6D', -'7D', -'8D', -'9D', -'A1C', -'A4', -'A10', -'aa', -'aA', -'AA', -'AAA', -'AAAI', -'AAAS', -'AAAV', -'AAC', -'AAD', -'AADD', -'AADT', -'AADAC', -'AADC', -'AAFC', -'AAFES', -'AAI', -'AAIS', -'AAM', -'AAMOF', -'AAN', -'AANEAS', -'AAODL', -'AAP', -'AAPC', -'aar', -'AAR', -'AARP', -'AAS', -'AASPEM', -'AAT', -'AAV', -'AAWC', -'ab', -'AB', -'ABA', -'ABB', -'ABBA', -'ABC', -'ABCA', -'ABCS', -'ABET', -'ABFIST', -'ABGI', -'ABIN', -'abk', -'ABL', -'ABLE', -'ABM', -'ABMC', -'ABN', -'ABPI', -'ABRSM', -'ABS', -'ABSA', -'ABSTRIPS', -'ABV', -'ABW', -'aC', -'Ac', -'AC', -'AC1', -'AC2', -'ACA', -'ACARS', -'ACAS', -'ACB', -'ACC', -'ACCA', -'ACCAC', -'ACCD', -'ACCS', -'ACDA', -'ACDC', -'ACDT', -'ACE', -'ACES', -'ACF', -'ACHT', -'ACID', -'ACINT', -'ACK', -'ACL', -'ACLJ', -'ACLOS', -'ACLS', -'ACLU', -'ACM', -'ACMC', -'ACN', -'ACNA', -'ACO', -'ACOBA', -'ACOG', -'ACOM', -'ACORN', -'ACP', -'ACR', -'ACRI', -'ACRT', -'ACS', -'ACSC', -'ACST', -'ACST', -'ACT', -'ACTD', -'ACTF', -'ACTU', -'ACU', -'ACUF', -'ACV', -'AD', -'ADA', -'ADAC', -'ADAGE', -'ADAP', -'ADAS', -'ADAT', -'ADatP', -'ADATS', -'ADB', -'ADC', -'ADCTS', -'ADD', -'ADDIE', -'ADDNS', -'ADF', -'ADGB', -'ADHD', -'ADHS', -'ADI', -'ADIZ', -'ADL', -'ADM', -'ADOA', -'ADP', -'ADRG', -'ADS', -'ADSR', -'ADT', -'ADTB', -'ADW', -'ae', -'AE', -'AED', -'AEDT', -'AEF', -'AEGIS', -'AESR', -'AEST', -'aet', -'AEW', -'af', -'aF', -'AF', -'AFA', -'AFAIC', -'AFAICT', -'AFAIK', -'AFAIR', -'AFAM', -'AFATDS', -'AFC', -'AFCEA', -'AFDB', -'AFES', -'AFG', -'AFI', -'AFIS', -'AFIST', -'AFJ', -'AFK', -'AFL', -'AFLAC', -'AFM', -'AFN', -'AFO', -'AFOSR', -'AFP', -'AFQT', -'afr', -'AFRC', -'AFRL', -'AFROTC', -'AFRRI', -'AFRTS', -'AFS', -'AFSC', -'AFSCN', -'AFSPC', -'AFT', -'AFV', -'AFWA', -'ag', -'Ag', -'AG', -'AGA', -'AGARD', -'AGARDograph', -'AGASA', -'AGB', -'AGC', -'AGC', -'AGDC', -'AGI', -'AGL', -'AGM', -'AGN', -'AGO', -'AGP', -'AGRICOLA', -'AGS', -'AGTS', -'AGU', -'AGW', -'aH', -'Ah', -'AH', -'AHA', -'AHCPR', -'AHEC', -'AHPCRC', -'AHQ', -'AHRQ', -'AHS', -'AI', -'AIA', -'AIAA', -'AIC', -'AICD', -'AICMFP', -'AID', -'AIDS', -'AIM', -'AIMP', -'AIPAC', -'AIR', -'AIRFA', -'AIRS', -'AIS', -'AISB', -'aJ', -'AJ', -'Ajax', -'AJD', -'AJP', -'AJPW', -'ak', -'aK', -'AK', -'aka', -'AKA', -'AKDT', -'AKST', -'AKP', -'aL', -'Al', -'AL', -'ALA', -'ALAP', -'ALARM', -'ALB', -'ALD', -'ALERT', -'ALF', -'ALG', -'ALGOL', -'ALL', -'ALOA', -'ALOC', -'ALPA', -'ALS', -'ALSHS', -'ALSP', -'ALTE', -'ALTACS', -'am', -'Am', -'AM', -'AM1', -'AMA', -'AMAPS', -'AMC', -'AMCB', -'AMD', -'AMF', -'AMFOM', -'AMFR', -'AMFRS', -'amh', -'AML', -'AMLCD', -'AMLO', -'AMO', -'AMOLED', -'AMOS', -'AMP', -'AMRAAM', -'AMRDEC', -'AMS', -'AMSAA', -'AMSO', -'AMSU', -'AMTE', -'an', -'aN', -'AN', -'ANA', -'ANC', -'AND', -'ANFO', -'ANG', -'ANGLiCo', -'ANL', -'ANM', -'ANOVA', -'ANS', -'ANSI', -'ANTM', -'ANT', -'ANTY', -'ANWR', -'ANZ', -'ANZAC', -'AO', -'AOA', -'AOC', -'AOD', -'AOI', -'AOL', -'AOR', -'AOSTH', -'AOTS', -'AOU', -'AP', -'APA', -'APB', -'APC', -'APEC', -'APCNR', -'APCR', -'APDS', -'APEDS', -'APEX', -'APFSDS', -'APHA', -'APHE', -'APHIS', -'API', -'APL', -'APM', -'APOD', -'APOE', -'APOLLO', -'APP', -'APR', -'APS', -'AQ', -'AQIM', -'ar', -'Ar', -'AR', -'ara', -'ARAG', -'ARB', -'ARBA', -'ARC', -'ARCA', -'ARCENT', -'ARDA', -'ARE', -'ARF', -'arg', -'ARG', -'ARH', -'ARI', -'ARL', -'ARM', -'ARNet', -'ARP', -'ARPA', -'ARRC', -'ARRF', -'ARRL', -'ARS', -'ARSC', -'ARSPACE', -'ART', -'ARTCC', -'ARWG', -'as', -'aS', -'As', -'AS', -'AS', -'ASA', -'ASAP', -'ASARS', -'ASB', -'asbl', -'ASBO', -'ASCAP', -'ASCC', -'ASCIET', -'ASCII', -'ASD', -'ASDIC', -'ASEAN', -'ASFAR', -'ASG', -'ASIC', -'ASICS', -'ASIO', -'ASIP', -'ASL', -'asm', -'ASM', -'ASMB', -'ASME', -'ASMP', -'ASN', -'ASOC', -'ASP', -'ASPI', -'ASR', -'ASRAAM', -'ASSC', -'AST', -'ASTIA', -'ASTM', -'ASTREA', -'ASUW', -'ASVAB', -'ASVEL', -'ASW', -'ASWORG', -'ASX', -'aT', -'At', -'ATA', -'ATAC', -'ATACMS', -'ATAPI', -'ATAS', -'ATAT', -'ATB', -'ATC', -'ATCCIS', -'ATESS', -'ATF', -'ATG', -'ATGM', -'ATGW', -'ATI', -'Atisbo', -'ATK', -'ATL', -'ATLAS', -'ATM', -'ATN', -'ATO', -'ATP', -'ATSD', -'ATSDR', -'ATV', -'ATWA', -'Au', -'AU', -'AUC', -'AUD', -'AUS', -'AUSCANNZUKUS', -'Auslan', -'AUT', -'AUTL', -'av', -'aV', -'Av', -'AV', -'ava', -'AVA', -'AVB', -'AVDLR', -'ave', -'AVGP', -'AVI', -'AVIM', -'AVLB', -'AVM', -'AVO', -'AVP', -'AVP', -'AVRE', -'AVTB', -'AVUM', -'AVVO', -'aW', -'AW', -'AWA', -'AWACS', -'AWC', -'AWD', -'AWE', -'AWG', -'AWOL', -'AWP', -'AWSIM', -'AWST', -'AWW', -'AX', -'AXP', -'ay', -'AY', -'AYBABTU', -'aym', -'AYU', -'az', -'AZ', -'aze', -'AZE', -'AZN', -'AZUR', -'BAR', -'BB', -'BBB', -'BBC', -'BBD', -'BBL', -'BBS', -'BBYO', -'BC', -'BCA', -'BCCI', -'BCE', -'BCS', -'BEA', -'BES', -'BH', -'BHS', -'BNF', -'BNP', -'BO', -'BoAML', -'BOC', -'BOGs', -'BOI', -'BOM', -'BOQ', -'BOT', -'BP', -'BPV', -'BQ', -'BR', -'BRB', -'BRN', -'BS', -'BSA', -'BSatCoP', -'BSD', -'BSE', -'BSN', -'BSNL', -'BSP', -'BST', -'Bt', -'BT', -'BTN', -'C2C', -'Ca', -'CA', -'CAA', -'CAAT', -'CAC', -'CACM', -'CAD', -'CADCAM', -'CADPAT', -'CADMID', -'CAE', -'CAEn', -'CAF', -'CAFAD', -'CAFDE', -'CAI', -'CAIR', -'CALABARZON', -'CalPERS', -'Caltrans', -'CAM', -'CAMRA', -'CANA', -'CANDU', -'Canola', -'CAP', -'CAPES', -'CARE', -'CARMONETTE', -'CARP', -'CART', -'CAS', -'CASA', -'CASE', -'CASS', -'CASTFOREM', -'CASTOR', -'cat', -'CAT', -'CATK', -'CATS', -'CATT', -'CAV', -'CAVOK', -'CAW', -'CB', -'CBASSE', -'CBC', -'CBE', -'CBF', -'CBGB', -'CBLA', -'CBN', -'CBM', -'CBML', -'CBOL', -'CBOT', -'CBR', -'CBRN', -'CBRNE', -'CBS', -'CBT', -'CBUS', -'CC', -'CCA', -'CCC', -'CCCP', -'CCD', -'CCF', -'CCG', -'CCGC', -'CCGH', -'CCGS', -'CCH', -'CCIF', -'CCII', -'CCIR', -'CCIT', -'CCITT', -'CCK', -'CCM', -'CCOC', -'CCOP', -'CCP', -'CCPA', -'CCR', -'CCRAp', -'CCRP', -'CCRTS', -'CCSCS', -'CCSCS', -'CCSIL', -'CCSK', -'CCT', -'CCTT', -'cd', -'Cd', -'CD', -'CD1', -'CDA', -'CDC', -'CDD', -'CDE', -'CDEC', -'CDF', -'CDIAC', -'CDipAF', -'CDL', -'CDM', -'CDMA', -'CDMSII', -'Cdr', -'CDS', -'Cdt', -'CDT', -'ce', -'Ce', -'CE', -'CEA', -'CECOM', -'CEDEX', -'CEGEP', -'CEI', -'CENTCOM', -'CENTO', -'CENZUB', -'CEO', -'CEP', -'CEPT', -'CERCLA', -'CERDEC', -'CERGA', -'CERN', -'CERT', -'ces', -'CESS', -'CEV', -'CEWC', -'Cf', -'CF', -'CFA', -'CFAR', -'CFC', -'CFES', -'CFFZ', -'CFG', -'CFHT', -'CFI', -'CFL', -'CFLCC', -'CFM', -'CFO', -'CFP', -'CFR', -'CFRP', -'CFS', -'CFSP', -'CFU', -'CFV', -'CFZ', -'CG', -'CGDC', -'CGF', -'CGH', -'CGIG', -'CGM', -'CGO', -'CGS', -'CGT', -'ch', -'ch', -'CH', -'cha', -'CHAMPUS', -'CHAOS', -'CHAPS', -'CHDK', -'che', -'CHE', -'CHF', -'CHI', -'CHI', -'CHiP', -'CHIPS', -'CHL', -'CHLA', -'CHN', -'CHOGM', -'CHP', -'CHPSO', -'CHS', -'CHT', -'chu', -'chv', -'CREN', -'Ci', -'CI', -'CIA', -'CIAO', -'CIB', -'CIBC', -'CICS', -'CID', -'CID', -'CIEF', -'CIF', -'CIMIC', -'CIMMYT', -'CINC', -'CINEOS', -'CIO', -'CIP', -'CIPE', -'CIR', -'CIR', -'CIS', -'CISA', -'CISB', -'CISPR', -'CISSP', -'CITES', -'CITS', -'CIV', -'CIVETS', -'CIVPOP', -'CIWS', -'CHRS', -'CJ', -'CJC', -'CJD', -'CJCS', -'CJO', -'CJTF', -'CK', -'CKA', -'CKND', -'Cl', -'CL', -'CLA', -'CLEC', -'CLEMARS', -'CLI', -'CLIC', -'CLO', -'CLOB', -'CLOS', -'CLOS', -'CLP', -'CLPFC', -'CLRP', -'Cm', -'CM', -'CMA', -'CMB', -'CMBR', -'CME', -'CMHC', -'CMIIAW', -'CMIS', -'CMJ', -'CML', -'CMLL', -'CMM', -'CMMC', -'CMML', -'CMO', -'CMOC', -'CMOS', -'CMP', -'CMP', -'CMPD', -'CMPD', -'CMR', -'CMS', -'CMT', -'CMU', -'CMW', -'CMYK', -'CN', -'CNA', -'CNBC', -'CND', -'CNE', -'CNF', -'CNI', -'CNI', -'CNN', -'CNO', -'CNR', -'CNV', -'CNY', -'co', -'Co', -'CO', -'COA', -'COB', -'COBRA', -'COCOM', -'COD', -'CoDA', -'codec', -'COE', -'COEA', -'COEIA', -'COFT', -'COG', -'COGENT', -'COIL', -'COIN', -'COK', -'COL', -'COLA', -'COLT', -'COM', -'COMECON', -'COMMZ', -'COMSEC', -'CONAD', -'CONCACAF', -'CONMEBOL', -'CONOPS', -'CONUS', -'COO', -'COP', -'COPE', -'COPUS', -'cor', -'CORBA', -'CORD', -'CORE', -'CORG', -'COROT', -'cos', -'COS', -'COSCOM', -'COSER', -'CoT', -'COTS', -'Coy', -'CP', -'CPA', -'CPAC', -'CPCU', -'CPD', -'CPE', -'CPE', -'CPF', -'CPG', -'CPI', -'CPIM', -'CPIR', -'CPON', -'CPPCC', -'CPR', -'CPR', -'CPSM', -'CPSU', -'CPT', -'CPU', -'CPV', -'CPX', -'CQ', -'CQD', -'cr', -'Cr', -'CR', -'CRAM', -'CRC', -'CRD', -'cre', -'CREB', -'CREEP', -'CRI', -'CRIPL', -'CRM', -'CRO', -'CRP', -'CRT', -'CRTC', -'cs', -'Cs', -'CS', -'CSA', -'CSAR', -'CSC', -'CSEAL', -'CSERIAC', -'CSG', -'CSI', -'CSICOP', -'CSIP', -'CSIS', -'CSK', -'CSL', -'CSPI', -'CSR', -'CSR', -'CSRC', -'CSRF', -'CSS', -'CSSCS', -'CST', -'CT', -'CTA', -'CTBT', -'CTC', -'CTDB', -'CTE', -'CTGF', -'CTI', -'CTIL', -'CTIS', -'CTMS', -'CTO', -'CTS', -'CTSS', -'CTU', -'CTVT', -'cu', -'Cu', -'CU', -'CUB', -'CUCV', -'CUNY', -'CUP', -'CUPE', -'CUPS', -'CUPW', -'CUREA', -'CuW', -'CUW', -'cv', -'CV', -'CVCC', -'CVE', -'CVI', -'CVLL', -'CVR', -'CVS', -'CVSD', -'CVT', -'CW', -'CWC', -'CWS', -'CWRU', -'CX', -'CXR', -'cy', -'CY', -'cym', -'CYM', -'CYP', -'CZ', -'CZE', -'CZK', -'CZF', -'CZMA', -'d', -'D', -'da', -'DA', -'DAB', -'DACT', -'DADVSI', -'DAES', -'DAF', -'DAFIF', -'DAK', -'dan', -'DAO', -'DAQ', -'DARE', -'DARO', -'DARPA', -'DART', -'DAS', -'DASD', -'DASL', -'DAU', -'DAWN', -'dB', -'Db', -'DB', -'DBA', -'DBBL', -'DBCS', -'DBDO', -'DBE', -'DBF', -'DBM', -'DBR', -'DBRA', -'DBS', -'DC', -'DCA', -'DCAA', -'DCAS', -'DCC', -'DCC', -'DCCP', -'DCD', -'DCE', -'DCFC', -'DCI', -'DCIM', -'DCIMS', -'DCL', -'DCN', -'DCOM', -'DCOP', -'DCSOPS', -'DCW', -'DCYSC', -'DD', -'DDA', -'DDD', -'DDL', -'DDN', -'DDoS', -'DDP', -'DDR', -'DDS', -'DDT', -'de', -'DE', -'DEA', -'DEA', -'DEC', -'DeCA', -'DECSIM', -'DECT', -'DEFCON', -'DEFRA', -'DELT', -'DEM', -'DEN', -'DEPSECDEF', -'DERA', -'DES', -'deu', -'DEU', -'DF', -'DFAD', -'DFAS', -'DFB', -'DFCB', -'DfID', -'DFL', -'DFM', -'DFM', -'DFSP', -'DfT', -'DFTBA', -'DG', -'DGA', -'DGPS', -'DGZ', -'DH', -'DHA', -'DHHS', -'DHL', -'DHRA', -'DHS', -'DHSS', -'DHTFYSS', -'DHY', -'DIA', -'DIAC', -'DiC', -'DICASS', -'DILLIGAD', -'DILLIGAF', -'DIF', -'DIFAR', -'DIME', -'DIMM', -'DIN', -'DINA', -'DINKY', -'DIP', -'DIPS', -'DIR', -'DIS', -'DISA', -'DISCOM', -'DISE', -'DISSTAF', -'DITY', -'div', -'DIVAD', -'DIY', -'DJ', -'DJD', -'DJF', -'DJI', -'DJS', -'DK', -'DKK', -'DL', -'DLA', -'DLIC', -'DLIR', -'DLL', -'DLPMC', -'DLR', -'DLRP', -'DLS', -'DLSA', -'DLSc', -'DLSC', -'DLTBGYD', -'DM', -'DMA', -'DMC', -'DMCA', -'DMD', -'DMI', -'DMM', -'DMOC', -'DMOS', -'DMPA', -'DMPFC', -'DMPK', -'DMRB', -'DMS', -'DMSO', -'DMSP', -'DMT', -'DMU', -'DMV', -'DMZ', -'DN', -'DNA', -'DNAW', -'DND', -'DNDi', -'DNDO', -'DNM', -'DNK', -'DNR', -'DNS', -'DO', -'DOA', -'DOB', -'DOD', -'DODAF', -'DoDD', -'DoDEA', -'DOE', -'DOHC', -'DOI', -'DOM', -'DOMS', -'DOP', -'DORA', -'DOS', -'DOT', -'DOTA', -'DotD', -'DOTMLPF', -'DOW', -'DP', -'DP', -'DPA', -'DPCO', -'DPDT', -'DPICM', -'DPKO', -'DPMO', -'DPPDB', -'DPRE', -'DPRK', -'DPST', -'DQ', -'DR', -'DRA', -'DRAGN', -'DRAM', -'DRC', -'DRDC', -'DREA', -'DRES', -'DRET', -'DREV', -'DRI', -'DRIC', -'DRL', -'DRM', -'DRMS', -'DRPR', -'DRT', -'Ds', -'DS', -'DSA', -'DSAA', -'DSAS', -'DSB', -'DSC', -'DSCS', -'DSCSOC', -'DSD', -'DSDS', -'DSE', -'DSG', -'DSID', -'DSL', -'DSLR', -'DSM', -'DSN', -'DSO', -'dsp', -'dspl', -'dspm', -'dspml', -'dspms', -'dsps', -'DSP', -'DSPL', -'DSR', -'DSRV', -'DSS', -'DST', -'DSTL', -'dsvp', -'DSWA', -'DTA', -'DTD', -'DTE', -'DTED', -'DTG', -'DTH', -'DTI', -'DTIC', -'DTL', -'DTM', -'DTMF', -'DTLOMS', -'DTO', -'DTOMLS', -'DTR', -'DTRA', -'DTSA', -'DU', -'DUI', -'DUMBO', -'DUSD', -'dv', -'DV', -'DVD', -'DVLC', -'dvm', -'DVM', -'DVO', -'DVOM', -'DVP', -'dvp', -'dvu', -'DW', -'DWG', -'DWI', -'DWM', -'DWIM', -'DWTS', -'DWW', -'DX', -'DXA', -'DXM', -'Dy', -'DY', -'dz', -'DZ', -'DZA', -'DZD', -'dzo', -'E', -'EA', -'EABU', -'EAC', -'EAFUS', -'EAPC', -'EASA', -'EAST', -'EBCDIC', -'EBD', -'EBE', -'EBO', -'EBSA', -'EBW', -'EBW', -'EC', -'ECB', -'ECBR', -'ECASS', -'ECCM', -'ECFS', -'ECG', -'ECHO', -'ECHR', -'ECJ', -'ECL', -'ECM', -'ECommHR', -'ECS', -'ECSC', -'ECT', -'ECtHR', -'ECU', -'ECW', -'EDA', -'EDAC', -'EDCS', -'EDD', -'EDECSIM', -'EDGAR', -'EDH', -'EDS', -'EDSA', -'EDT', -'EDTA', -'ee', -'EE', -'EEC', -'EEG', -'EEK', -'EENT', -'EEO', -'EEOC', -'EEZ', -'EF', -'EFF', -'EFL', -'EFM', -'EFOA', -'EFSA', -'EFT', -'EFTA', -'EFV', -'EG', -'EGBUS', -'EGCG', -'EGIS', -'EGP', -'EGRET', -'EGY', -'EH', -'EHA', -'EHF', -'EHR', -'Ei', -'EI', -'EIA', -'EIAH', -'EIAH', -'EIC', -'EICS', -'eID', -'EIRP', -'EIS', -'EJ', -'EK', -'EKG', -'EKMS', -'el', -'EL', -'ELF', -'ELINT', -'ell', -'ELL', -'ELM', -'ELO', -'ELP', -'ELSS', -'ELT', -'Em', -'EM', -'EMA', -'Embraer', -'EMC', -'EMCON', -'EMD', -'EMDG', -'EMDR', -'EMEA', -'EMF', -'EMG', -'EMO', -'EMP', -'EMS', -'EMT', -'EMU', -'en', -'EN', -'ENCODE', -'ENE', -'eng', -'ENG', -'ENSATT', -'ENT', -'ENTEC', -'ENVL', -'ENY', -'eo', -'EO', -'EOD', -'EOG', -'EOS', -'EOSAT', -'EOSDIS', -'EOTS', -'EOM', -'EP', -'EP', -'EPA', -'EPCOT', -'EPCR', -'EPFL', -'EPGS', -'EPIRB', -'EPL', -'EPLRS', -'epo', -'EPO', -'EPOS', -'EPPA', -'EPR', -'EPW', -'EQ', -'EQG', -'Er', -'ER', -'ERA', -'ERCIM', -'ERI', -'ERLAWS', -'ERN', -'EROS', -'ERP', -'ERR', -'ERS', -'ERTS', -'ERV', -'es', -'Es', -'ES', -'ESA', -'ESC', -'ESD', -'ESDP', -'ESE', -'ESH', -'ESL', -'ESM', -'ESO', -'ESOL', -'ESP', -'ESPGHAN', -'ESPN', -'est', -'EST', -'ESU', -'ESV', -'et', -'ET', -'Exatesla', -'ETA', -'ETB', -'ETF', -'ETH', -'ETI', -'ETLA', -'ETM', -'ETOPS', -'ETT', -'eu', -'Eu', -'EU', -'EUCOM', -'EULA', -'EUR', -'eus', -'EUUSA', -'EV', -'EVA', -'EVIL', -'EW', -'ewe', -'EWR', -'EWSD', -'EWTN', -'EXFOR', -'EXREP', -'EYD', -'EZ', -'EZLN', -'f', -'F', -'fa', -'FA', -'fA', -'FA', -'FAA', -'FAAD', -'FAADEZ', -'FAAN', -'FAAFP', -'FAAN', -'FAAQ', -'FAASV', -'FAB', -'FAC', -'FACA', -'FACH', -'FACN', -'FACP', -'FACS', -'FACTS', -'FAD', -'FAH', -'FAI', -'FAM', -'fao', -'FAO', -'FAPA', -'FAPHA', -'FAQ', -'FARC', -'FARDC', -'FARP', -'fas', -'FAS', -'FASCAM', -'FASD', -'FASEB', -'FAT', -'FAVS', -'FAW', -'FB', -'FBCS', -'FBI', -'FBIS', -'FBIDST', -'FBM', -'FBR', -'FBS', -'fC', -'FC', -'FCA', -'FCC', -'FCE', -'FCF', -'FCLA', -'FCM', -'FCO', -'FCS', -'FCW', -'FCZ', -'FD', -'FDA', -'FDC', -'FDDI', -'FDBK', -'FDR', -'Fe', -'FE', -'FEAR', -'FEAST', -'FEATS', -'FEB', -'FEBA', -'FED', -'FEDEP', -'FEF', -'FEI', -'FEMA', -'FEMEXFUT', -'FENAFUTH', -'FENS', -'FEP', -'FEPAFUT', -'FES', -'FEST', -'FET', -'FEZ', -'ff', -'fF', -'FF', -'FFA', -'FFB', -'FFC', -'FFE', -'FFF', -'FFG', -'FFOV', -'FFR', -'FFS', -'FFT', -'FG', -'fg', -'FG', -'FGR', -'fH', -'FHFIF', -'FHI', -'fi', -'FI', -'FIA', -'FIAT', -'FIB', -'FIBA', -'FIBUA', -'FIC', -'FID', -'FIE', -'FIFA', -'FIFe', -'FIFO', -'FIFRA', -'FIG', -'FIGJAM', -'fij', -'FILA', -'FILO', -'FIMPT', -'fin', -'FIN', -'FINA', -'FIPS', -'FIR', -'FIRES', -'FIS', -'FISA', -'FISH', -'FiST', -'FITS', -'FIU', -'fj', -'fJ', -'FJ', -'FJD', -'FJI', -'FJN', -'fK', -'FK', -'FKP', -'FKSM', -'FKT', -'fl', -'fL', -'FL', -'FLA', -'FLAB', -'FLAC', -'FLAC', -'FLAK', -'FLB', -'FLET', -'FLG', -'FLIP', -'FLIR', -'FLK', -'FLOT', -'FLSA', -'FLQ', -'FLQ', -'FLTSAT', -'FLTSATCOM', -'FLUBOS', -'FM', -'fm', -'Fm', -'FM', -'FMC', -'FMCA', -'FMCAfam', -'FME', -'FMECA', -'FMJD', -'FMLA', -'Fmr', -'fMRI', -'FMS', -'FMSF', -'FMTV', -'FMCG', -'fN', -'FNA', -'fo', -'FO', -'FOAD', -'FOAF', -'FoB', -'FOB', -'FOC', -'FoE', -'FOGC', -'FOH', -'FOIA', -'FOM', -'FONSI', -'FOO', -'FOR', -'Fortran', -'FOS', -'FoS', -'FOSE', -'FOSE', -'FOT', -'FOTA', -'FOTC', -'FOUO', -'FOV', -'FP', -'FPF', -'fps', -'FPS', -'FOOP', -'FQ', -'FQDN', -'fr', -'Fr', -'FR', -'fra', -'FRA', -'FRAGO', -'FRAND', -'FRCP', -'FRICBM', -'frr', -'FRR', -'FRO', -'FRS', -'FRSA', -'fry', -'FRY', -'fs', -'fS', -'FS', -'FSA', -'FSB', -'FSC', -'FSCL', -'FSE', -'FSF', -'FSH', -'FSI', -'FSK', -'FSM', -'FSO', -'FSS', -'FSSP', -'FST', -'FSTC', -'FSV', -'fT', -'FTC', -'FTE', -'FTL', -'FTP', -'FTSE', -'FTW', -'FTS', -'FTS', -'FU', -'FUBAR', -'FUCK', -'FUD', -'ful', -'fV', -'fW', -'FW', -'FWC', -'FWF', -'FWI', -'FWIW', -'FWS', -'FWT', -'FX', -'FXX', -'fy', -'FY', -'FYA', -'FYDP', -'FYI', -'FYROM', -'FZDZ', -'FZFG', -'FZRA', -'g', -'G', -'G1', -'G2', -'G3', -'G4', -'G5', -'G6', -'G7', -'G8', -'G9', -'G10', -'G15', -'G20', -'G24', -'G77', -'Ga', -'GA', -'GAAR', -'GAB', -'GABA', -'GAD', -'GAF', -'GALCIT', -'GAO', -'GAW', -'GB', -'GBBC', -'GBE', -'GBH', -'GBP', -'GBR', -'GBU', -'GC', -'GCA', -'GCCS', -'GCD', -'GCF', -'GCI', -'GCN', -'GCS', -'GCSE', -'gd', -'Gd', -'GD', -'GDC', -'GDL', -'GDP', -'GDR', -'GDR', -'GDW', -'Ge', -'GE', -'GEBCO', -'GEC', -'GED', -'GEEP', -'GEL', -'GEMA', -'GEN', -'GEO', -'GEOS', -'GEQ', -'GER', -'Gestapo', -'GeV', -'GEV', -'GF', -'GFCF', -'GFCI', -'GFDL', -'GFE', -'GFI', -'GFL', -'GFN', -'GFY', -'GG', -'GGK', -'GGP', -'GH', -'GHA', -'GHB', -'GHC', -'Gi', -'GI', -'GIB', -'GIF', -'GIG', -'GIGO', -'GIMP', -'GIN', -'GINK', -'GINO', -'GIP', -'GIS', -'GISS', -'GJ', -'GJD', -'GK', -'gl', -'GL', -'gla', -'GLAAD', -'GLAC', -'GLAST', -'GLBA', -'GLC', -'gle', -'glg', -'GLINT', -'GLONASS', -'GLP', -'GLUT', -'glv', -'GLV', -'Gm', -'GM', -'GMAC', -'GMB', -'GmbH', -'GMC', -'GMD', -'GMFSC', -'GMI', -'GML', -'GMO', -'GMPCS', -'GMT', -'gn', -'GN', -'GNAS', -'GNB', -'GNC', -'GNF', -'GNOME', -'GNP', -'GNQ', -'GnRH', -'GNU', -'GO', -'GOA', -'GOAT', -'GOC', -'GOES', -'GOP', -'gorp', -'GOSC', -'GOSH', -'GOSP', -'GOTS', -'GOYADIN', -'GP', -'GPA', -'GPC', -'GPG', -'GPIB', -'GPL', -'GPMP', -'GPO', -'GPPC', -'GPR', -'GPS', -'GPT', -'GPU', -'GQ', -'GR', -'GRAIL', -'GRAS', -'GRB', -'GRC', -'GRD', -'GRE', -'grep', -'GRid', -'GRID', -'GriT', -'GRL', -'GRN', -'grn', -'GRP', -'GRU', -'Gs', -'GS', -'GSR', -'GSA', -'GSD', -'GSG', -'GSGB', -'GSK', -'GSM', -'GSN', -'GSO', -'GSOH', -'GSP', -'GSS', -'GST', -'GSW', -'GT', -'GTA', -'GTB', -'GTC', -'GTD', -'GTG', -'GTK', -'GTGN', -'GTM', -'GTQ', -'GTV', -'gu', -'GU', -'GUA', -'GUF', -'GUI', -'GUILT', -'guj', -'GULCR', -'Gulag', -'GUM', -'GUT', -'gv', -'GV', -'GW', -'GWARS', -'GWAS', -'GWOT', -'GWS', -'GXA', -'GXE', -'GXF', -'GXH', -'GXL', -'GXM', -'GXP', -'GxP', -'GXY', -'GY', -'GYD', -'GYY', -'GZ', -'GZK', -'h', -'H', -'ha', -'HA', -'HAARP', -'HAART', -'HAI', -'HAL', -'HALO', -'HALT', -'HAM', -'HANS', -'HARM', -'hat', -'HAT', -'hau', -'HAW', -'HAWK', -'HAZ', -'HAZMAT', -'HBCD', -'HBCI', -'HBO', -'HBR', -'HBT', -'HBV', -'HCCI', -'HCF', -'HCI', -'HCSC', -'HCT', -'HD', -'HDD', -'HDL', -'HDM', -'HDMI', -'HDR', -'HDRA', -'HDTV', -'he', -'He', -'HE', -'HEAT', -'heb', -'HEBA', -'HEER', -'HEIC', -'HEMP', -'HEO', -'HEOSS', -'HEP', -'HEPA', -'her', -'HESH', -'HESV', -'HETT', -'HEU', -'Hewitts', -'Hf', -'HF', -'HFAC', -'HFE', -'HFE', -'HFEA', -'Hg', -'HGC', -'HGF', -'HGTV', -'HHD', -'HHB', -'hi', -'HI', -'HIDACZ', -'HIH', -'HIM', -'HIMAD', -'HIMEZ', -'HIMYM', -'hin', -'HIPAA', -'Hipparcos', -'Hird', -'HiRISE', -'HIT', -'HIV', -'HIWT', -'HJ', -'HJD', -'HK', -'HKD', -'HKG', -'HKS', -'hL', -'HL', -'HLA', -'HLL', -'HLN', -'HLOS', -'HLS', -'HLVW', -'HM', -'HMCE', -'HMCS', -'HMD', -'HMF', -'HMM', -'HMMWV', -'hmo', -'HMO', -'HMP', -'HMRC', -'HMS', -'HMSO', -'HMV', -'HN', -'HND', -'HNL', -'ho', -'Ho', -'HO', -'HOLLAND', -'HOMES', -'HONY', -'HOPI', -'HoReCa', -'HORIZON', -'HOSTAC', -'HOT', -'HOTAS', -'HOV', -'hp', -'HP', -'HPA', -'HPCL', -'HPLC', -'HPT', -'HPV', -'HQ', -'HQDA', -'hr', -'HR', -'HRBL', -'HRD', -'HRED', -'HRF', -'HRH', -'HRK', -'HRM', -'HRM', -'HRT', -'hrv', -'HRV', -'Hs', -'HSBC', -'HSC', -'HSM', -'HSMAD', -'HSN', -'HSPH', -'HSS', -'HSSI', -'HST', -'HSV', -'ht', -'HT', -'HTA', -'HTC', -'HTFU', -'HTG', -'HTH', -'HTI', -'HTML', -'HTR', -'HTS', -'HTTP', -'HU', -'HUD', -'HUF', -'HUHA', -'HUMINT', -'hun', -'HUN', -'HURD', -'HVU', -'HV', -'HVAC', -'HVAP', -'HVHF', -'HVM', -'HVO', -'HVP', -'HW', -'HWM', -'HX', -'HXB', -'hy', -'hye', -'HYS', -'hz', -'Hz', -'HZ', -'I', -'ia', -'IA', -'IAAF', -'IAAL', -'IAB', -'IACREOT', -'IAD', -'IADS', -'IAEA', -'IAF', -'IAFG', -'IAI', -'IANA', -'IANAL', -'IAQ', -'IAS', -'IASIP', -'IATA', -'IATF', -'IATSE', -'IAU', -'IAUC', -'IAW', -'IB', -'IBA', -'IBD', -'IBDM', -'IBDS', -'IBM', -'ibo', -'IBRD', -'IBS', -'IC', -'ICANN', -'ICAO', -'ICBM', -'ICBM', -'ICBN', -'ICC', -'ICD', -'ICE', -'ICES', -'ICF', -'ICHTHYS', -'ICM', -'ICMP', -'ICNAF', -'ICNI', -'ICO', -'ICP', -'ICPE', -'ICRAF', -'ICRISAT', -'ICRL', -'ICRS', -'ICS', -'ICSI', -'ICT', -'ICTH', -'ICV', -'ICW', -'ICYMI', -'ICZN', -'id', -'ID', -'IDA', -'IDC', -'IDDI', -'IDE', -'IDeA', -'IDEA', -'IDF', -'IDGAF', -'IDK', -'IDKIBTI', -'IDL', -'IDN', -'ido', -'IDR', -'ie', -'IE', -'IEA', -'IEC', -'IED', -'IEEE', -'IELTS', -'IER', -'IET', -'IET', -'IETF', -'IEW', -'IFAB', -'IFAH', -'IFAP', -'IFC', -'IFF', -'IFFN', -'IFO', -'IFOR', -'IFOV', -'IFPI', -'IFR', -'IFRB', -'IFV', -'ig', -'IG', -'IGNOU', -'IGS', -'IGY', -'IHH', -'IHO', -'IHOP', -'IHR', -'IHSI', -'ii', -'II', -'IICA', -'IIE', -'iii', -'III', -'IIRC', -'IISS', -'IIT', -'IJN', -'IJWP', -'ik', -'IKEA', -'iku', -'IL', -'ILC', -'ILDS', -'ile', -'ILO', -'ILRI', -'ILS', -'ILY', -'IM', -'IMA', -'IMBD', -'IMDb', -'IME', -'IMF', -'IMHO', -'IML', -'IMLAST', -'IMNM', -'IMNSHO', -'IMPAC', -'IMPACT', -'IMPATT', -'IMPT', -'IMR', -'IMRI', -'IMRO', -'IMRL', -'IMS', -'IMSA', -'IMsL', -'IMT', -'IMU', -'In', -'ina', -'INA', -'INAS', -'INC', -'INCITS', -'ind', -'IND', -'inet', -'INDIGO', -'INFORMS', -'INFOSEC', -'INLA', -'INMARSAT', -'INR', -'INRI', -'INRIA', -'INS', -'InSAR', -'INST', -'INTSUM', -'INTERFET', -'io', -'IO', -'IOC', -'IOLTA', -'IOM', -'IONA', -'IOT', -'IOU', -'IP', -'IPA', -'IPB', -'IPCC', -'ipk', -'IPL', -'IPN', -'IPO', -'IPR', -'IPT', -'IPTS', -'IPTV', -'IPW', -'iq', -'IQ', -'IQD', -'Ir', -'IR', -'IRA', -'IRAD', -'IRAN', -'IRAS', -'IRB', -'IRBM', -'IRC', -'IREA', -'IRL', -'IRM', -'IRN', -'IRINN', -'IRO', -'IRQ', -'IRR', -'IRS', -'IRSN', -'IRST', -'is', -'ISA', -'ISAAA', -'ISAF', -'ISAR', -'ISB', -'ISBN', -'ISCCP', -'ISCII', -'ISDN', -'ISEF', -'ISEN', -'ISF', -'ISI', -'ISIL', -'ISIS', -'ISK', -'isl', -'ISL', -'ISLN', -'ISM', -'ISMB', -'ISMC', -'ISO', -'ISOGP', -'ISP', -'ISPE', -'ISR', -'ISRO', -'ISS', -'ISSCR', -'ISSN', -'IST', -'ISTAR', -'ISTC', -'ISTS', -'ISU', -'it', -'IT', -'ita', -'ITA', -'ITAG', -'ITALY', -'ITAS', -'ITC', -'ITCZ', -'ITEC', -'ITEMS', -'ITER', -'ITF', -'ITK', -'ITN', -'ITS', -'ITT', -'ITTO', -'ITU', -'iu', -'IUCN', -'IUPAC', -'IUSS', -'IV', -'IVD', -'Iveco', -'IVF', -'IVI', -'IVIS', -'IVL', -'IVM', -'IVO', -'IW', -'IWARS', -'IWC', -'IWW', -'IX', -'IXT', -'IY', -'IZ', -'J', -'J1', -'J2', -'J2EE', -'J2ME', -'J2SE', -'J3', -'J4', -'J5', -'J6', -'J7', -'J8', -'J9', -'ja', -'JA', -'JAFT', -'JAG', -'JAL', -'JALLC', -'JAM', -'JAMA', -'JANET', -'JANUS', -'JATO', -'jav', -'JB', -'JB', -'JBFSA', -'JCATS', -'JCDB', -'JCDS', -'JCIDS', -'JCIET', -'JCL', -'JCMOTF', -'JCP', -'JCPM', -'JCS', -'JCW', -'JD', -'JDAM', -'JDBC', -'JDCC', -'JDEM', -'JDK', -'JDL', -'JDM', -'JE', -'JEB', -'JEDP', -'JEE', -'JEFX', -'JEPD', -'JET', -'JEZ', -'JFACC', -'JFACTSU', -'JFC', -'JFCOM', -'JFET', -'JFK', -'JFLCC', -'JFMCC', -'JFO', -'JFSOCC', -'JFTX', -'JH', -'JH', -'JI', -'JIB', -'JIC', -'JINI', -'JITC', -'JIVE', -'JJAP', -'JK', -'JL', -'JLA', -'JM', -'JMD', -'JMS', -'JN', -'JNI', -'JNLP', -'JO', -'JOA', -'JOD', -'Jofa', -'JOPES', -'JOR', -'JOTS', -'JSP', -'JP', -'JPEG', -'JPL', -'jpn', -'JPN', -'JPOW', -'JPY', -'JQ', -'JRA', -'JRDF', -'JRE', -'JREF', -'JROC', -'JROCM', -'JRRF', -'JRSAI', -'JSAF', -'JSAP', -'JSCSC', -'JSD', -'JSEAD', -'JSF', -'JSIMS', -'JSOA', -'JSOTF', -'JSP', -'JSR', -'JSS', -'JSTARS', -'JT', -'JTAG', -'JTAGS', -'JTC', -'JTF', -'JTFEX', -'JTIDS', -'JTLS', -'JTMD', -'JTN', -'JTRS', -'JU', -'JUGFET', -'JUICE', -'JUO', -'jv', -'JV', -'JVM', -'JW', -'JWARS', -'JWP', -'JWST', -'JY', -'JZ', -'k', -'K', -'K9', -'ka', -'kA', -'Ka', -'KA', -'KAF', -'kal', -'kan', -'KANUKOKA', -'KARNAC', -'KARR', -'kas', -'kat', -'KATUSA', -'kau', -'kaz', -'KAZ', -'KBE', -'KBO', -'KBVB', -'kC', -'KCK', -'KCL', -'KCMO', -'KCS', -'KD', -'KE', -'KEDO', -'KEN', -'KERA', -'kerma', -'KES', -'KESR', -'KET', -'kF', -'KFC', -'KFOR', -'KFUPM', -'kg', -'KG', -'KGB', -'KGO', -'KGS', -'KGZ', -'kH', -'KH', -'khm', -'KHL', -'KHM', -'KHR', -'KHz', -'ki', -'Ki', -'KI', -'KIA', -'KIDS', -'kik', -'kin', -'kir', -'KIR', -'KISS', -'KITECH', -'KITT', -'KIT', -'kj', -'kJ', -'kk', -'kK', -'KK', -'KKK', -'kl', -'kL', -'KL', -'KLENOT', -'KLM', -'KLOC', -'KLS', -'km', -'KM', -'KMF', -'KMFDM', -'KMR', -'kn', -'kN', -'KN', -'KNA', -'KNVB', -'ko', -'KO', -'kom', -'kon', -'kor', -'KOR', -'KP', -'KPMG', -'KPNO', -'KPV', -'KPVT', -'KPW', -'KQ', -'kr', -'Kr', -'KR', -'KRW', -'ks', -'kS', -'KS', -'KSC', -'KSI', -'KSM', -'KSU', -'kT', -'KT', -'KTS', -'ku', -'KU', -'kua', -'KUL', -'kur', -'KUW', -'kv', -'kV', -'KV', -'KV', -'KVM', -'KVT', -'kw', -'kW', -'KW', -'KWB', -'KWD', -'KWT', -'KX', -'ky', -'KY', -'KYD', -'KZ', -'KZN', -'KZT', -'L', -'la', -'La', -'LA', -'LAAS', -'LAB', -'LabVIEW', -'LACMA', -'LAD', -'LADW', -'LAESI', -'LAK', -'LAMP', -'LAN', -'LANL', -'lao', -'LAO', -'LAPD', -'LAR', -'LARC', -'LARP', -'LAS', -'Laser', -'lat', -'LAT', -'LATA', -'LATCH', -'lav', -'LAV', -'LAW', -'LAX', -'lb', -'LB', -'LBH', -'LBJ', -'LBN', -'LBNL', -'LBP', -'LBR', -'LBS', -'lbs', -'LBT', -'LBW', -'LBY', -'LC', -'LCA', -'LCC', -'LCCP', -'LCD', -'LCE', -'LCM', -'LCol', -'LCS', -'LD', -'LDA', -'LDAP', -'LDC', -'LDL', -'LDMOS', -'LDS', -'LDV', -'LDW', -'LE', -'LEC', -'LED', -'Legs', -'LEGAD', -'LEMOSS', -'LEN', -'LEO', -'LEP', -'LER', -'LEU', -'LF', -'LFO', -'LFTR', -'lg', -'LG', -'LGB', -'LGBT', -'LGLO', -'LGM', -'LGS', -'LH', -'LHC', -'LHD', -'LHS', -'li', -'Li', -'LI', -'LIBOR', -'LIBS', -'LIE', -'LIFO', -'LIGO', -'lim', -'lin', -'LINEAR', -'LISA', -'lit', -'LITAS', -'LIVE', -'LJ', -'LJJ', -'LK', -'LKA', -'LKL', -'LKR', -'LKR4', -'LL', -'LLAD', -'LLB', -'LLC', -'LLGC', -'LLLTV', -'LLNL', -'LLP', -'LLTI', -'LLTR', -'LMAO', -'LMFAO', -'LMB', -'LMN', -'LMS', -'LMS', -'LMV', -'ln', -'LNB', -'LNER', -'LNG', -'LNO', -'LNR', -'lo', -'LO', -'LOA', -'LOC', -'LOD', -'LoD', -'LOFAR', -'LOGPAC', -'LOL', -'LOMEZ', -'LONEOS', -'LOOGY', -'loq', -'LORTID', -'LOS', -'LOSAT', -'LOTA', -'LOTIS', -'LOTS', -'LP', -'LPC', -'LPD', -'LPG', -'LPI', -'LPS', -'LQ', -'Lr', -'LR', -'LRAD', -'LRC', -'LRD', -'LRF', -'LRGW', -'LRICBM', -'LRIP', -'LRP', -'LRPE', -'LRS', -'LRU', -'LS', -'LSAT', -'LSD', -'LSE', -'LSI', -'LSIF', -'LSL', -'LSND', -'LSO', -'LSST', -'LST', -'LSVW', -'lt', -'Lt', -'LtC', -'LT', -'LTC', -'Ltd', -'LTDP', -'LTG', -'LTI', -'LTM', -'LTL', -'LTP', -'LTU', -'ltz', -'lu', -'Lu', -'LU', -'LU', -'lub', -'LUCA', -'lug', -'LUG', -'LULU', -'LUT', -'LUVW', -'LUX', -'lv', -'LV', -'LVA', -'LVAD', -'LVEF', -'LVL', -'Lw', -'LW', -'LWR', -'LWRC', -'LWS', -'LX', -'LX', -'LY', -'LYAO', -'LYD', -'LYLAS', -'LZ', -'m', -'M', -'mA', -'Ma', -'MA', -'MAA', -'MAAC', -'MAC', -'MACA', -'MACE', -'MACHO', -'MAD', -'MADD', -'MAF', -'MAFF', -'MAGIC', -'MAGNETAR', -'MAGTF', -'mah', -'Mahasz', -'mal', -'MALD', -'MANPADS', -'MANPRINT', -'MAO', -'MAOI', -'MAPS', -'MAQS', -'mar', -'MAR', -'MarBEF', -'MARC', -'MARCENT', -'MARFORCC', -'MARRS', -'MAS', -'MASAS', -'Maser', -'MASH', -'MASINT', -'MATREX', -'MATS', -'MAVS', -'MAW', -'MAX', -'MB', -'MBA', -'MBI', -'MBBL', -'MBCC', -'MBCS', -'MBE', -'MBIDST', -'MBM', -'mbsf', -'MBT', -'MBTA', -'MBTI', -'mC', -'MC', -'MCA', -'MCC', -'MCD', -'MCDL', -'MCFT', -'MCG', -'MCI', -'MCIS', -'MCM', -'MCMV', -'MCNC', -'MCO', -'MCP', -'MCR', -'MCSF', -'MCT', -'MCTIS', -'MCU', -'Md', -'MD', -'MDA', -'MDF', -'MDG', -'MDL', -'MDM', -'MDMA', -'MDNA', -'MDO', -'MDP', -'MDT', -'MDV', -'MDW', -'Me', -'ME', -'MEA', -'MEAC', -'MEC', -'MECC', -'MECCA', -'MECO', -'MEDEVAC', -'MEDLI', -'MEDS', -'MEF', -'MEG', -'MEI', -'MEK', -'MEL', -'MELT', -'MEM', -'MEMS', -'MEO', -'MEP', -'MER', -'MERL', -'MES', -'MESSENGER', -'MET', -'METAR', -'METL', -'METOC', -'MEU', -'MeV', -'MEV', -'MEX', -'MEZ', -'MES', -'mF', -'MF', -'MFSB', -'mg', -'Mg', -'MG', -'MGA', -'MGB', -'MGM', -'MGPAM', -'MGS', -'mh', -'mH', -'MH', -'MHC', -'MHHW', -'MHL', -'MHLW', -'MHRA', -'MHW', -'MHWN', -'MHWS', -'MHz', -'mi', -'Mi', -'MI', -'MIA', -'MIAA', -'MIB', -'MIC', -'MICLIC', -'MICOM', -'MICV', -'MID', -'MIDI', -'MIDAS', -'MIDS', -'MIFC', -'MIFG', -'MIFPC', -'MIKE', -'MIL', -'MILAN', -'MILC', -'MILES', -'MILF', -'MILSATCOM', -'MILSET', -'MILSTRIP', -'MIMAROPA', -'MIMOSA', -'MIMPT', -'MIP', -'MIPC', -'MIRA', -'MIRV', -'MIS', -'MISREP', -'MISSI', -'MIT', -'MITL', -'MITS', -'mJ', -'MJ', -'MJD', -'MJP', -'mk', -'mK', -'MK', -'mkd', -'MKD', -'MKG', -'MKO', -'MKS', -'MKSA', -'MKT', -'ml', -'mL', -'ML', -'MLA', -'MLB', -'MLC', -'MLE', -'mlg', -'MLHW', -'MLI', -'MLK', -'MLL', -'MLLW', -'MLP', -'MLRS', -'MLS', -'MLSE', -'MLSR', -'mlt', -'MLT', -'MLVW', -'MLW', -'MLWN', -'MLWS', -'mm', -'Mm', -'MM', -'MMA', -'MMC', -'MMCM', -'MMCS', -'MMEV', -'MMI', -'MMI', -'MMIC', -'MMIC', -'MMI', -'MMK', -'MMO', -'MMOR', -'MMORPG', -'MMPR', -'MMR', -'MMS', -'MMU', -'MMW', -'mn', -'mN', -'Mn', -'MN', -'MNB', -'MND', -'MNE', -'MNG', -'MNMF', -'MNP', -'MNS', -'MNT', -'mo', -'Mo', -'MO', -'MOA', -'MOAB', -'MOB', -'MOC', -'MOCAS', -'MoD', -'MOD', -'MoDAF', -'modem', -'ModSAF', -'Moe', -'MOE', -'MOF', -'MOFA', -'MOH', -'MOID', -'MOJWA', -'mol', -'MOLO', -'MoM', -'MOM', -'MOM', -'MoMA', -'mon', -'MOND', -'MOO', -'MOOTW', -'MOP', -'MOPE', -'MOPP', -'MORP', -'MORS', -'MOS', -'MOSFET', -'MOSS', -'MOT', -'MOTA', -'MOTESS', -'MOTO', -'MOU', -'MOUT', -'MOVES', -'MOW', -'MOZ', -'MP', -'mPa', -'MPa', -'MPA', -'MPAA', -'MPC', -'MPC', -'MPEC', -'MPEG', -'MPEV', -'MPF', -'MPFC', -'MPG', -'MPMC', -'MPSI', -'MPSoC', -'MPSRON', -'MQ', -'mr', -'MR', -'MRAV', -'MRBM', -'MRC', -'MRD', -'MRE', -'MRGO', -'MRGW', -'mri', -'MRI', -'MRL', -'MRO', -'MRPharmS', -'MRR', -'MRS', -'MRSA', -'MRT', -'MRTFB', -'ms', -'mS', -'Ms', -'MS', -'msa', -'MSB', -'MSD', -'MSDB', -'MSDE', -'MSDL', -'MSE', -'MSEC', -'MSG', -'MSH', -'MSI', -'MSIE', -'MSL', -'MSLP', -'MSM', -'MSN', -'MSNBC', -'MSO', -'MSPA', -'MSPB', -'MSR', -'MSSA', -'MST', -'MSU', -'mt', -'mT', -'Mt', -'MT', -'MTA', -'MTBF', -'MtF', -'MTF', -'MTG', -'MTL', -'MTOE', -'MTP', -'MTQ', -'MTR', -'MTS', -'MTT', -'MTU', -'MTV', -'MTVL', -'MTWS', -'MU', -'MUA', -'MUR', -'MUS', -'mV', -'MV', -'MVC', -'MVFC', -'MVNO', -'MVR', -'mW', -'MW', -'MWC', -'MWESH', -'MWI', -'MWK', -'MWOA', -'MWR', -'MWTB', -'MX', -'MXN', -'my', -'MY', -'mya', -'MYOB', -'MYR', -'MYS', -'MYT', -'MZ', -'MZN', -'n', -'N', -'N3F', -'na', -'nA', -'Na', -'NA', -'NAA', -'NAAD', -'NAAFI', -'NAAK', -'NAACP', -'NAAQS', -'NAAWS', -'NABCI', -'NAC', -'NACA', -'NACC', -'NACISA', -'NACMA', -'NACRA', -'NAD', -'NAEC', -'NAF', -'NAFLD', -'NAFO', -'NAFERA', -'NAFTA', -'NAGPRA', -'NAI', -'NAICS', -'NAIL', -'NAILS', -'NAIRU', -'NAK', -'NAM', -'NAMBLA', -'NAMRL', -'NaN', -'NAO', -'NAPDD', -'NAPS', -'NARA', -'NARAL', -'NARDAC', -'NARDIC', -'NARPV', -'NAS', -'NASA', -'NASCAR', -'NASDAQ', -'NASED', -'NASH', -'NASP', -'NASS', -'NatGeo', -'NATO', -'nau', -'NaUKMA', -'nav', -'NAV', -'NAVCAMS', -'NAVCC', -'NAVCENT', -'NAVDAC', -'NAVEUR', -'NAVFE', -'NAVFOR', -'NAVMACS', -'NAVNET', -'NAVS', -'NAVSAT', -'NAVSPACECOM', -'NAVSPASUR', -'NAVSTAR', -'NAVTACNET', -'NAWC', -'NAWMP', -'nb', -'Nb', -'NB', -'NBA', -'NBACC', -'NBC', -'NBCRS', -'NBIC', -'nbl', -'NBL', -'NBT', -'nC', -'NC', -'NCA', -'NCAA', -'NCAR', -'NCB', -'NCCAM', -'NCCUSL', -'NCDDR', -'NCES', -'NCI', -'NCIS', -'NCL', -'NCLR', -'NCMO', -'NCND', -'NCO', -'NCP', -'NCR', -'NCTR', -'NCW', -'nd', -'Nd', -'ND', -'NDAA', -'NDBP', -'NDCC', -'nde', -'NDH', -'ndo', -'NDP', -'NDS', -'NDU', -'ne', -'Ne', -'NE', -'NEA', -'NEAT', -'NEC', -'NED', -'NECTEC', -'NEI', -'NEMS', -'NEO', -'nep', -'NEPA', -'NER', -'NERICA', -'NESDIS', -'NESP', -'NET', -'NETCU', -'NETL', -'NETWARS', -'NEWS', -'NEX', -'nF', -'NF', -'NFA', -'NFC', -'NFER', -'NFG', -'NFHS', -'NFI', -'NFK', -'NFL', -'Nfld', -'NFPA', -'NFT', -'ng', -'NG', -'NGA', -'NGH', -'NGATS', -'NGIC', -'NGN', -'NGO', -'NGR', -'NGS', -'nH', -'NH', -'NHANES', -'NHB', -'NHB', -'NHL', -'NHO', -'NHRA', -'NHS', -'Ni', -'NI', -'NIAAA', -'NIAID', -'NIAG', -'NIAMD', -'NIB', -'NIBP', -'NIC', -'NicA', -'NICAP', -'NICE', -'NICHD', -'NICP', -'NIDDK', -'NIDRR', -'NIEHS', -'NIG', -'NIH', -'NIIN', -'NIMA', -'NIMBY', -'NIMG', -'NiMH', -'NIMH', -'NIN', -'NIO', -'NIPR', -'NIR', -'NISAC', -'NISCAP', -'NISER', -'NIST', -'NIT', -'NITFS', -'NIU', -'nJ', -'NJ', -'NJHS', -'NJPW', -'nK', -'NKA', -'NKVD', -'nl', -'nL', -'NL', -'NLB', -'nld', -'NLD', -'NLF', -'NLL', -'NLP', -'NLW', -'nm', -'NM', -'NMCI', -'NMCS', -'NMD', -'NMN', -'NMOS', -'NMSG', -'NMR', -'NMHNFG', -'nn', -'nN', -'NNE', -'nno', -'NNSA', -'NNW', -'no', -'No', -'NO', -'NOAA', -'NOAF', -'NOAO', -'nob', -'NOC', -'NODEF', -'NOK', -'NOLA', -'NOGI', -'NOLF', -'NOM', -'NOME', -'NoMBO', -'NOME', -'NOPA', -'nor', -'NOR', -'NORAD', -'NORCECA', -'NORELPREF', -'NORML', -'NORTHAG', -'NOS', -'NOSA', -'NOTAFLOF', -'NOTAM', -'NOW', -'Np', -'NP', -'NP', -'NPB', -'NPC', -'NPCS', -'NPD', -'NPDI', -'NPG', -'NPH', -'NPL', -'NPOV', -'NPR', -'NQ', -'NQOCD', -'NPO', -'nr', -'NR', -'NRA', -'NRAO', -'NRC', -'NRDC', -'NREL', -'NRK', -'NRMP', -'NRO', -'NRT', -'NRTA', -'NRU', -'ns', -'nS', -'NS', -'NSA', -'NSABB', -'NSAID', -'NSB', -'NSC', -'NSDAP', -'NSDL', -'NSG', -'NSN', -'NSNA', -'NSO', -'NSS', -'NST', -'NSTAC', -'NSV', -'NSW', -'nT', -'NT', -'NTC', -'NTDS', -'NTP', -'NTR', -'NTRA', -'NTSB', -'NTSC', -'NTV', -'NTY', -'NTZ', -'NU', -'NUI', -'NUL', -'NUTS', -'NUWC', -'nv', -'nV', -'NV', -'NVEOL', -'NVESD', -'NVG', -'NVL', -'nW', -'NW', -'NW', -'NWA', -'NWF', -'NWFZ', -'NWHN', -'nWo', -'NWOAHM', -'NWOBHM', -'NWRA', -'NWSL', -'NWT', -'NX', -'NXA', -'NXD', -'NXE', -'NXG', -'NXL', -'NXN', -'NXP', -'ny', -'NY', -'nya', -'NYHA', -'NYO', -'NYRA', -'NYSE', -'NZ', -'NZC', -'NZD', -'NZL', -'NzL', -'NZOSS', -'NZRU', -'O', -'OA', -'OAC', -'OAP', -'OAS', -'OARSI', -'OASIS', -'OASIS', -'OASIS', -'OASP', -'OAU', -'ob', -'OB', -'OBE', -'OBJ', -'OBS', -'OBSS', -'OBX', -'oc', -'OC', -'OCA', -'OCD', -'OCFS', -'OCFS2', -'oci', -'OCIDS', -'OCMCC', -'OCN', -'OCO', -'OCOKA', -'OCR', -'OCS', -'OD', -'ODAS', -'ODB', -'ODBC', -'ODI', -'OEA', -'OED', -'OECD', -'OEG', -'OEIS', -'OEM', -'OFA', -'OFDA', -'OGC', -'OGD', -'OGO', -'OGX', -'OH', -'OHC', -'OHHLA', -'OHIP', -'OHMS', -'OHP', -'OHSS', -'OIC', -'OIE', -'OIF', -'OIOC', -'oj', -'OJ', -'oji', -'OJT', -'OK', -'OKC', -'OLED', -'OLS', -'om', -'OM', -'OMA', -'OMB', -'OMD', -'OME', -'OMFG', -'OMG', -'OMGW', -'OMN', -'OMNCS', -'OMP', -'OMR', -'OMS', -'OMT', -'OMW', -'ON', -'Onna', -'ONC', -'ONCE', -'ONG', -'ONH', -'ONI', -'ONS', -'OO', -'OOO', -'OOAD', -'OOBE', -'OOCL', -'OODA', -'OOMF', -'OOP', -'OOS', -'OOTW', -'OP', -'OPA', -'OPAC', -'OPCON', -'OPCW', -'OPEC', -'OPFOR', -'OPI', -'OPINTEL', -'OPLAN', -'OPORD', -'OPP', -'OPS', -'OPSEC', -'OPSI', -'OPTAR', -'OPV', -'OQ', -'or', -'OR', -'ORAU', -'ORB', -'ORBAT', -'ORD', -'ORD', -'ORF', -'ori', -'ORISE', -'orm', -'ORNL', -'ORR', -'ORSA', -'OS', -'os', -'Os', -'OSA', -'OSCE', -'OSD', -'OSDL', -'OSE', -'OSERS', -'OSETI', -'OSHA', -'OSI', -'OSIA', -'OSL', -'OSIRIS', -'OSM', -'OSOTPBOIAS', -'OSPA', -'OSPA', -'OSRH', -'oss', -'OSS', -'OSU', -'OTA', -'OTAC', -'OTB', -'OTC', -'OTD', -'OTEA', -'OTI', -'OTO', -'OTOH', -'OTS', -'OTSBH', -'OTW', -'OTYBL', -'OTYRL', -'OU', -'OVC', -'OVV', -'OW', -'OWN', -'OX', -'OXC', -'OXF', -'OXM', -'OXR', -'OXT', -'Oy', -'Oyj', -'OZ', -'p', -'P', -'P2P', -'pa', -'pA', -'Pa', -'PA', -'PAA', -'PAB', -'PABX', -'PAC', -'PACOM', -'PADD', -'PADI', -'PAEK', -'PAFC', -'PAH', -'PAHO', -'PAI', -'PAK', -'Pakistan', -'PAL', -'PAM', -'Pamida', -'PAMELA', -'pan', -'PAN', -'PANS', -'PAO', -'PAOK', -'PAPI', -'PAR', -'PARC', -'PAROS', -'PARP', -'PARWIG', -'PAS', -'PASA', -'PASGT', -'PASR', -'PATH', -'PAWS', -'PAYD', -'PAYE', -'Pb', -'PB', -'PBA', -'PBB', -'PBCK', -'PBD', -'PBDE', -'PBI', -'PBN', -'PBOH', -'PBR', -'PBS', -'PBRT', -'PBT', -'PBX', -'pC', -'PC', -'PCAS', -'PCB', -'PCC', -'PCCB', -'PCCIP', -'PCE', -'PCI', -'PCIe', -'PCL', -'PCMCIA', -'PCN', -'PCP', -'PCPI', -'PCR', -'PCSSD', -'PCT', -'PCTE', -'PCZ', -'Pd', -'PD', -'PD', -'PDA', -'PDD', -'PDF', -'PDG', -'PDL', -'PDN', -'PDP', -'PDQ', -'PDRY', -'PDT', -'PDU', -'PE', -'PE', -'PEBCAC', -'PEBKAC', -'PEEK', -'PEET', -'PEGIDA', -'PEI', -'PEK', -'PEN', -'PEO', -'PEPFAR', -'perl', -'PES', -'PEST', -'PET', -'PETA', -'PETE', -'PETP', -'pF', -'PF', -'PFD', -'PFE', -'PFLAG', -'PFIY', -'PFO', -'PfP', -'pg', -'Pg', -'PG', -'PGCE', -'PGD', -'PGENI', -'PGH', -'PGK', -'PGM', -'PGP', -'pH', -'PH', -'PHA', -'PHL', -'PHQ', -'PHP', -'pi', -'Pi', -'PI', -'PIB', -'PIC', -'PICNIC', -'PICS', -'PID', -'PIF', -'PIG', -'PIM', -'PIN', -'PINF', -'PIPA', -'PIR', -'PIRA', -'PIREPS', -'PITA', -'pJ', -'PJ', -'PJHQ', -'PJs', -'K', -'PK', -'PKA', -'PKE', -'PKI', -'PKM', -'PKMS', -'PKMT', -'PKR', -'PKT', -'PKTM', -'PKU', -'pl', -'pL', -'PL', -'PLAF', -'PLAID', -'PLB', -'PLC', -'PLCC', -'PLD', -'PLEBS', -'PLF', -'PLGR', -'pli', -'PLI', -'PLIF', -'PLK', -'PLKK', -'PLL', -'PLM', -'PLMBA', -'Pln', -'PLN', -'PLO', -'PLOKTA', -'PLP', -'PLS', -'PLUTO', -'PLW', -'pm', -'Pm', -'PM', -'PMC', -'PMCS', -'PME', -'PMI', -'PMID', -'PMMA', -'PMOS', -'PMS', -'PMSC', -'PMSL', -'PMT', -'pN', -'PN', -'PNAC', -'PNC', -'PNF', -'PNG', -'PNP', -'PNS', -'PNVS', -'PNH', -'Po', -'PO', -'PO', -'PO', -'POCD', -'pod', -'POD', -'POE', -'POETS', -'POFF', -'POG', -'POGO', -'POI', -'POK', -'pol', -'POL', -'POLAD', -'POM', -'POMDP', -'POP', -'por', -'POR', -'POS', -'POSH', -'POSSLQ', -'POT', -'POTS', -'POV', -'POW', -'pp', -'PP', -'PPA', -'PPASC', -'PPASSCCATAG', -'PPCLI', -'PPG', -'PPL', -'PPLI', -'ppm', -'PPM', -'PPM', -'PPO', -'PPP', -'PPQ', -'PPQN', -'PPQT', -'PPS', -'PPV', -'PQ', -'Pr', -'Pr', -'PR', -'Prb', -'PRBO', -'PRC', -'PRD', -'PRFG', -'PRI', -'PRK', -'PRM', -'PRN', -'PRT', -'PRY', -'ps', -'pS', -'Ps', -'PS', -'PooS', -'PSA', -'PSC', -'PSE', -'PSG', -'PSI', -'PSI', -'PSO', -'PSS', -'PST', -'PSU', -'PSV', -'PsyOp', -'pt', -'pT', -'Pt', -'PT', -'PTA', -'PTAL', -'PTAN', -'PTES', -'PTFE', -'PTI', -'PTL', -'PTO', -'PTSD', -'PTUI', -'Pu', -'PU', -'Pub', -'PUG', -'PUN', -'PUP', -'PUR', -'pus', -'PUS', -'pV', -'PV', -'PVC', -'PvE', -'PvP', -'PVR', -'PvZ', -'pW', -'PW', -'petawatt', -'PWA', -'PWBA', -'PwC', -'PWC', -'PWL', -'PWLC', -'PWP', -'PWR', -'PWV', -'PX', -'PY', -'PYF', -'PYG', -'PYMWYMI', -'PZ', -'Q1', -'Q2', -'Q3', -'Q4', -'QA', -'Qantas', -'QAP', -'QAR', -'QAT', -'QB', -'QC', -'QCA', -'QCD', -'QD', -'QDOS', -'QDR', -'QDRO', -'QED', -'QEH', -'QFE', -'QID', -'QIP', -'QK', -'QLD', -'QM', -'QNH', -'QPR', -'QPS', -'QR', -'QS', -'QSCAT', -'QSL', -'QSO', -'QSTAG', -'QT', -'qu', -'Quango', -'Quasar', -'QUB', -'que', -'QUIET', -'qv', -'QVC', -'QWG', -'QWGAOR', -'QZ', -'R', -'R2I', -'R3P', -'Ra', -'RA', -'RA', -'RAA', -'RAAF', -'Rabobank', -'RAC', -'RACK', -'Racon', -'RADA', -'Radar', -'RADB', -'RADb', -'RadB', -'RADC', -'RAE', -'RAF', -'RAG', -'RAGBRAI', -'RAM', -'Ramark', -'RAOC', -'RAP', -'RARDE', -'RARE', -'RASC', -'RATELO', -'Rb', -'RB', -'RBAR', -'RBCI', -'RBI', -'RBOC', -'RC', -'RCA', -'RCAF', -'RCAR', -'RCC', -'RCCO', -'RCHA', -'RCMP', -'RCRA', -'RCS', -'RCT', -'RCZ', -'RD', -'RDA', -'RDD', -'RDDL', -'RDECOM', -'RDM', -'RDO', -'RDS', -'RDT', -'RDV', -'Re', -'RE', -'REACH', -'REACT', -'REAM', -'RECCE', -'REEF', -'REM', -'REME', -'RERC', -'RES', -'RESNA', -'REU', -'Rf', -'RF', -'RFA', -'RFC', -'RFD', -'RFE', -'RFEF', -'RFF', -'RFI', -'RFID', -'RFL', -'RFL', -'RFP', -'RFQ', -'RFT', -'RFU', -'Rg', -'RG', -'RGB', -'RGBI', -'Rh', -'RH', -'RHIC', -'RHLI', -'RHO', -'RHS', -'RHU', -'RHWR', -'RI', -'RI', -'RIAA', -'RIBA', -'RIF', -'RIFA', -'RILA', -'RIM', -'RIMVS', -'RINO', -'RIP', -'RIS', -'RISC', -'RISD', -'RISTA', -'RWH', -'RJD', -'RKBA', -'RKI', -'RKO', -'RL', -'RLP', -'RLS', -'RLY', -'RM', -'rm', -'RMA', -'RMC', -'RMC', -'RMDA', -'RMP', -'RMS', -'rn', -'Rn', -'RN', -'RN', -'RNA', -'RNAD', -'RNPS', -'RNZ', -'RNZI', -'ro', -'RO', -'ROA', -'ROC', -'ROD', -'ROE', -'roh', -'ROH', -'ROI', -'ROFLOL', -'ROK', -'ROL', -'ROM', -'ron', -'RON', -'ROP', -'ROPE', -'ROS', -'ROTC', -'ROU', -'ROV', -'ROW', -'ROWPU', -'ROZ', -'RP', -'RPA', -'RPD', -'RPF', -'RPG', -'RPL', -'rpm', -'RPM', -'RPN', -'RPO', -'rps', -'RPV', -'RQ', -'RRB', -'RRN', -'RRP', -'RRS', -'RRSP', -'RS', -'RSA', -'RSD', -'RSEQ', -'RSI', -'RSJ', -'RSN', -'RSPB', -'RSPCA', -'RSR', -'RSRE', -'RSRI', -'RSS', -'RSTA', -'RSVP', -'RT', -'RTA', -'RTBF', -'RTCA', -'RTF', -'RTFM', -'RTG', -'RTGS', -'RTI', -'RTL', -'RTLS', -'RTP', -'RTS', -'RTT', -'RTV', -'ru', -'Ru', -'RU', -'RUB', -'RUC', -'run', -'rus', -'RUS', -'RUSI', -'RV', -'RVD', -'rw', -'RW', -'RWA', -'RWADA', -'RWF', -'RWR', -'RWS', -'Rx', -'RX', -'South', -'SA', -'SAA', -'SAC', -'SAD', -'SADL', -'SAE', -'SALT', -'SAM', -'Sambo', -'san', -'SAR', -'SARA', -'SART', -'SAS', -'SAT', -'SAW', -'SB', -'SBA', -'SBC', -'SBD', -'SBNO', -'SBP', -'SBS', -'SBW', -'SC', -'SCA', -'SCO', -'SCS', -'SCSI', -'SD', -'SDC', -'SDHC', -'SDXC', -'SE', -'SEC', -'SEM', -'SEN', -'SEP', -'SEU', -'SF', -'SFA', -'SFE', -'SG', -'SGC', -'SGS', -'SH', -'SHAC', -'SHO', -'SI', -'SIG', -'SIIT', -'SIMPLE', -'SIP', -'Siri', -'SIRI', -'SISO', -'SJ', -'SK', -'SL', -'SLA', -'SLOC', -'SLP', -'SLT', -'SM', -'SME', -'SMG', -'SMX', -'SMZ', -'SN', -'SNL', -'SNR', -'SO', -'SOA', -'SOAP', -'SOB', -'SoCon', -'SOD', -'SOE', -'SOF', -'SOGAT', -'SOM', -'SOP', -'SOR', -'SOS', -'Sp', -'SP', -'SPD', -'SPL', -'SR', -'SRAM', -'SRD', -'SRG', -'SRL', -'SS', -'SSA', -'SSC', -'SSD', -'SSDD', -'SSE', -'SSN', -'SSR', -'SSRI', -'SSRN', -'SSS', -'SST', -'ST', -'STA', -'STAR', -'STAT', -'STB', -'STD', -'STFU', -'STI', -'STP', -'STRICOM', -'SU', -'SUI', -'SV', -'SVD', -'SW', -'SWA', -'SWF', -'SWG', -'SWLABR', -'SWP', -'SXM', -'SZ', -'T', -'ta', -'Ta', -'TA', -'TAA', -'TAACOM', -'TAADCOORD', -'TAAR', -'TAC', -'TACAMO', -'TACAN', -'TACC', -'TACCO', -'TACCSF', -'TACDAR', -'TACP', -'TACSOP', -'TADIL', -'TADL', -'TADSS', -'tah', -'TAI', -'TAJ', -'TAJT', -'TALES', -'tam', -'TAN', -'TANJ', -'TANP', -'TANSTAAFL', -'TAPPS', -'TARDIS', -'TARP', -'TAROT', -'TAS', -'TASER', -'TASM', -'tat', -'TAT', -'TATP', -'TATT', -'Tb', -'TB', -'TBA', -'TBBPA', -'TBC', -'TBD', -'TBD', -'TBF', -'TBGRI', -'TBH', -'TBL', -'TBM', -'TBS', -'TBT', -'Tc', -'TC', -'TCA', -'TCAS', -'TCAA', -'TCCCS', -'TCD', -'TCG', -'TCHD', -'TCL', -'TCM', -'TCP', -'TCU', -'TCWMS', -'TCW', -'TD', -'TDA', -'TDL', -'TDM', -'TDMA', -'TDP', -'TDRS', -'te', -'Te', -'TE', -'TEAL', -'Team', -'TEAMS', -'TED', -'TEETH', -'tel', -'TEL', -'TELAR', -'TEMO', -'TENCAP', -'TERS', -'TES', -'TEST', -'TEU', -'TeVeS', -'TF', -'TFMPP', -'TFR', -'TFT', -'tg', -'Tg', -'TG', -'TGA', -'TGB', -'TGIF', -'tgk', -'tgl', -'TGO', -'TGT', -'TGV', -'th', -'Th', -'TH', -'tha', -'THA', -'THAAD', -'THB', -'THEGENTRY', -'THEL', -'THG', -'ti', -'Ti', -'TI', -'TIA', -'TIA', -'TIALD', -'TIBS', -'TIC', -'TID', -'TIGERS', -'TIM', -'TIMS', -'TINC', -'TINY', -'TIP', -'tir', -'tits', -'TJ', -'TJD', -'TJK', -'TJS', -'tk', -'TK', -'TKBL', -'TKL', -'TKM', -'tl', -'Tl', -'TLPM', -'TL', -'TLATOJL', -'TLA', -'TLAM', -'TLAR', -'TLC', -'TLD', -'TLM', -'TLS', -'Tm', -'Thulium', -'TM', -'TMA', -'TMAP', -'TMC', -'TMD', -'TMDK', -'TMI', -'TMJ', -'TMM', -'TMNT', -'TMO', -'TMP', -'TMPG', -'TMR', -'TMS', -'TMT', -'TMTOWTDI', -'TMZ', -'tn', -'TN', -'TNA', -'TND', -'TNO', -'TNT', -'to', -'TOA', -'TOAD', -'TOBS', -'TOC', -'TOG', -'TOGS', -'TOMS', -'ton', -'TON', -'TOP', -'TOS', -'TOT', -'TOW', -'TP', -'TPE', -'TPI', -'TPO', -'TPS', -'TPTB', -'TQ', -'tr', -'TR', -'TRA', -'TRAC', -'TRACON', -'TRADOC', -'TRAIN', -'TRANSCOM', -'TRAP', -'TRASANA', -'TRD', -'TRIGAT', -'TRL', -'Trp', -'TRP', -'TRU', -'TRUEX', -'TRW', -'TRY', -'ts', -'Ts', -'TS', -'TSA', -'TSCA', -'TSEC', -'TSL', -'tsn', -'TSN', -'tso', -'TSO', -'TSOP', -'TSR', -'TSSIA', -'tt', -'TT', -'TTCT', -'TTD', -'TTO', -'TTFN', -'TTL', -'TTP', -'TTTEAF', -'TTY', -'TTYL', -'TTZ', -'TU', -'TUC', -'TUG', -'TUI', -'tuk', -'TUN', -'TUNS', -'Tur', -'TUR', -'TUSK', -'TUV', -'Tv', -'TV', -'TVA', -'TVG', -'TVR', -'TVS', -'tw', -'TW', -'TWAIN', -'TWD', -'TWE', -'twi', -'TWI', -'TWN', -'TWP', -'TWS', -'TWIP', -'TX', -'TXV', -'ty', -'TYT', -'TZ', -'TZA', -'TZC', -'TZS', -'U', -'UA', -'UAAP', -'UAB', -'UAE', -'UAH', -'UAR', -'UAV', -'UBC', -'UBIGEO', -'UBS', -'UCBSA', -'UCAV', -'UCF', -'UCI', -'UCLA', -'UCLES', -'UCR', -'UCS', -'UCSB', -'UCSD', -'UDAS', -'UDDI', -'UDP', -'UDT', -'UDTS', -'UE', -'UEFA', -'UEFI', -'UESAC', -'UFC', -'UFO', -'ug', -'UG', -'UGA', -'UGV', -'UGX', -'UHF', -'UHMW', -'UHRB', -'UIC', -'UID', -'uig', -'UIL', -'UIUC', -'UJTL', -'uk', -'UK', -'UKIP', -'ukr', -'UKR', -'UL', -'ULEB', -'ULF', -'UM', -'UMBC', -'UMC', -'UMCP', -'UMES', -'UMI', -'UMIST', -'UMK', -'UML', -'UMTS', -'UN', -'UNABOM', -'UNAIDS', -'UNaM', -'UNAM', -'UNAMSIL', -'UNCF', -'UNCLOS', -'UNCSGN', -'UNCTAD', -'UNDCP', -'UNDE', -'UNDP', -'UNEP', -'UNESCO', -'UNFCCC', -'UNFICYP', -'UNGEGN', -'UNHCR', -'UNICEF', -'UNICRI', -'UNIDO', -'UNIFEM', -'UNIS', -'UNLV', -'UNMIK', -'UNMOVIC', -'UNOSOM', -'UNPROFOR', -'UNRISD', -'UNSM', -'UNTAC', -'UNTAET', -'UNU', -'UP', -'UPC', -'UPdM', -'UPDM', -'UPF', -'UPI', -'UPN', -'UPS', -'UPTN', -'UPU', -'ur', -'UR', -'URBSFA', -'urd', -'URD', -'URI', -'URL', -'URU', -'URY', -'US', -'USA', -'USAADAC', -'USAARMC', -'USAAVNC', -'USAF', -'USAFSPC', -'USAIC', -'USAID', -'USAP', -'USAPATRIOT', -'USAREUR', -'USB', -'USC', -'USCG', -'USCINCSPACE', -'USD', -'USDA', -'USDI', -'USDOE', -'USDRE', -'USEC', -'USFL', -'USFWS', -'USGS', -'USIA', -'USJFCOM', -'USL', -'USMC', -'USMNT', -'USN', -'USNA', -'USNB', -'USNG', -'USNH', -'USNI', -'USNO', -'USNOFS', -'USNP', -'USNR', -'USNS', -'USNY', -'USNZ', -'USPS', -'USPTO', -'USR', -'USS', -'USSPACECOM', -'USSR', -'UST', -'USTRANSCOM', -'USWNT', -'UT', -'UTEP', -'UTM', -'UU', -'UUA', -'UUID', -'UUV', -'UV', -'UVM', -'UW', -'UWO', -'UWT', -'UWTG', -'UWW', -'UXO', -'UY', -'UYU', -'uz', -'UZ', -'uzb', -'UZB', -'UZS', -'V', -'VA', -'VACIS', -'VACIS', -'VANOS', -'VAO', -'VASIS', -'VAT', -'VB', -'VBA', -'VC', -'VCDS', -'vCJD', -'VCJCS', -'VCNO', -'VCR', -'VCofS', -'VCT', -'VD', -'VDFS', -'VDMOS', -'VDOT', -'VDR', -'ve', -'VE', -'VEB', -'VEEGA', -'ven', -'VEN', -'VERDI', -'VERITAS', -'VFA', -'VfD', -'VFL', -'VFM', -'VFMO', -'VFR', -'VFT', -'VFW', -'VG', -'VGB', -'VH1', -'VHF', -'vi', -'VI', -'VIA', -'VIC', -'VIDS', -'vie', -'VII', -'VIII', -'VIN', -'VIP', -'VIR', -'VIS', -'VISCII', -'VIT', -'VIVO', -'VJ', -'VK', -'VLA', -'VLAD', -'VLBA', -'VLF', -'VLIW', -'VLPFC', -'VLPMC', -'VLSF', -'VLSI', -'VLVS', -'VM', -'VMASC', -'VMF', -'VMF', -'VMN', -'VN', -'VND', -'VNIR', -'VNM', -'VNS', -'VOA', -'vo', -'VOC', -'VOIP', -'vol', -'VOLT', -'Volvo', -'VON', -'VOR', -'VOR', -'VOX', -'VP', -'VPN', -'VQ', -'VR', -'VRSG', -'VS', -'VSAT', -'VSHORAD', -'VSO', -'VSOP', -'VSR', -'VT', -'VTC', -'VTEC', -'VTOL', -'VU', -'VUB', -'VUT', -'VUV', -'VV', -'VVAF', -'VW', -'VZ', -'vzw', -'W', -'W3C', -'wa', -'WA', -'WAAC', -'WAAF', -'WAC', -'WACC', -'WACS', -'WAD', -'WAGGGS', -'WAGs', -'WAK', -'WAL', -'WAN', -'WAP', -'WAPIMA', -'WAR', -'WAsP', -'WASP', -'WAYP', -'Wb', -'WB', -'WBA', -'WBC', -'WBE', -'WBO', -'WC', -'WCBO', -'WCBP', -'WCL', -'WCS', -'WCW', -'WDC', -'WDWR', -'WDIN', -'WEBS', -'WEC', -'WEEE', -'WEF', -'WEM', -'WEU', -'WEZ', -'WF', -'WFC', -'WFD', -'WFF', -'WFP', -'WFZ', -'WFI', -'WG', -'WH', -'WHAM', -'WHI', -'WHIP', -'WHO', -'WHS', -'WI', -'WIA', -'WIIFM', -'WIMP', -'WIPO', -'WIPP', -'WIYN', -'WK', -'WKYP', -'WL', -'WLB', -'WLF', -'wln', -'WLOG', -'WM', -'WMANUS', -'WMD', -'WMI', -'WMO', -'WNA', -'WNF', -'WNL', -'WNIT', -'WNSD', -'WNW', -'wo', -'WO', -'WOF', -'WOFT', -'WOFTAM', -'WOI', -'wol', -'WOM', -'WOS', -'WOSM', -'WOT', -'WP', -'WPB', -'WPE', -'WPK', -'WPT', -'WR', -'WRAC', -'WRAIR', -'WRBWWRM', -'WRC', -'WRC', -'WRNS', -'WRVS', -'WRS', -'WRT', -'WRT', -'WRT', -'WRT', -'WRT', -'WRT', -'WRT', -'WRT', -'WRT', -'WRT', -'WRT', -'WS', -'WSC', -'WSDL', -'WSJ', -'WSL', -'WSM', -'WSMR', -'WSROC', -'WSSM', -'WST', -'WSTC', -'WSW', -'WTB', -'WTB', -'WTC', -'WTF', -'WTFH', -'WTH', -'WTO', -'WTS', -'WU', -'WUU2', -'WUBU2', -'WV', -'WVA', -'WVIAC', -'WVR', -'WVSC', -'WVU', -'WVUIT', -'WW', -'WWE', -'WWF', -'WWI', -'WWII', -'WWIII', -'WWJD', -'WWS', -'WWV', -'WWW', -'WWWF', -'WXO', -'WY', -'WYSIWYG', -'WYSIWYP', -'WZ', -'XAF', -'XAG', -'XAU', -'XBA', -'XBB', -'XBML', -'XC', -'XCBL', -'XCD', -'XD', -'Xe', -'XF', -'XFA', -'XFC', -'XFD', -'Xfe', -'XFF', -'XFG', -'XFH', -'XFN', -'XFR', -'XFT', -'XFW', -'XG', -'XGA', -'XGE', -'XGG', -'XGN', -'XGP', -'XGR', -'xh', -'xho', -'XHTML', -'XHR', -'XI', -'XII', -'XIII', -'XIV', -'XIX', -'XJ', -'XJL', -'XJS', -'XK', -'XKA', -'XKB', -'XKE', -'XKH', -'XKR', -'XKS', -'XKY', -'XL', -'XML', -'XMSF', -'XN', -'XNA', -'XNC', -'XNN', -'XNS', -'XNU', -'XO', -'XOF', -'XP', -'XPD', -'XPF', -'XPT', -'XQJ', -'XQP', -'XQU', -'XRML', -'XS', -'XSL', -'XTMH', -'XUL', -'XV', -'XVI', -'XVII', -'XVIII', -'XWB', -'XWF', -'XWM', -'XWN', -'XWS', -'XX', -'XXX', -'XYA', -'Xyl', -'XZA', -'XZN', -'y', -'Y', -'Y2K', -'yA', -'Ya', -'YA', -'YAC', -'YAFLA', -'YAP', -'YaBPr', -'YABSLAG', -'Yb', -'YB', -'yC', -'YC', -'YD', -'YDS', -'YE', -'YEM', -'YER', -'yF', -'YF', -'yg', -'Yg', -'Ygo', -'YGCO', -'yH', -'YH', -'YHS', -'YHBT', -'YHIH1', -'YHII', -'yi', -'Yi', -'YI', -'yid', -'Yippie', -'yJ', -'YJ', -'yK', -'YK', -'YKYFCW', -'yL', -'YL', -'ym', -'Ym', -'YM', -'YMCA', -'YMD', -'YMMD', -'YMMV', -'yN', -'YN', -'yo', -'YO', -'YOLO', -'yor', -'YPA', -'YPG', -'YPIO', -'YPO', -'ys', -'yS', -'Ys', -'YS', -'YSA', -'YSI', -'yT', -'YT', -'YTD', -'YTM', -'YTS', -'YTT', -'YU', -'YUG', -'Yuppie', -'yV', -'YV', -'yW', -'YW', -'YWCA', -'YWMV', -'YYH', -'z', -'Z', -'za', -'zA', -'ZA', -'ZAF', -'ZAMS', -'ZAR', -'ZAS', -'ZBT', -'zC', -'ZC', -'ZCM', -'ZCMK', -'ZCU' -] + '0D', + '1AM', + '1D', + '2AM', + '2D', + '2G', + '2LA', + '2Lt', + '3AM', + '3D', + '3G', + '3i', + '3M', + '4D', + '4G', + '4GL', + '4WD', + '5D', + '6D', + '7D', + '8D', + '9D', + 'A1C', + 'A4', + 'A10', + 'aa', + 'aA', + 'AA', + 'AAA', + 'AAAI', + 'AAAS', + 'AAAV', + 'AAC', + 'AAD', + 'AADD', + 'AADT', + 'AADAC', + 'AADC', + 'AAFC', + 'AAFES', + 'AAI', + 'AAIS', + 'AAM', + 'AAMOF', + 'AAN', + 'AANEAS', + 'AAODL', + 'AAP', + 'AAPC', + 'aar', + 'AAR', + 'AARP', + 'AAS', + 'AASPEM', + 'AAT', + 'AAV', + 'AAWC', + 'ab', + 'AB', + 'ABA', + 'ABB', + 'ABBA', + 'ABC', + 'ABCA', + 'ABCS', + 'ABET', + 'ABFIST', + 'ABGI', + 'ABIN', + 'abk', + 'ABL', + 'ABLE', + 'ABM', + 'ABMC', + 'ABN', + 'ABPI', + 'ABRSM', + 'ABS', + 'ABSA', + 'ABSTRIPS', + 'ABV', + 'ABW', + 'aC', + 'Ac', + 'AC', + 'AC1', + 'AC2', + 'ACA', + 'ACARS', + 'ACAS', + 'ACB', + 'ACC', + 'ACCA', + 'ACCAC', + 'ACCD', + 'ACCS', + 'ACDA', + 'ACDC', + 'ACDT', + 'ACE', + 'ACES', + 'ACF', + 'ACHT', + 'ACID', + 'ACINT', + 'ACK', + 'ACL', + 'ACLJ', + 'ACLOS', + 'ACLS', + 'ACLU', + 'ACM', + 'ACMC', + 'ACN', + 'ACNA', + 'ACO', + 'ACOBA', + 'ACOG', + 'ACOM', + 'ACORN', + 'ACP', + 'ACR', + 'ACRI', + 'ACRT', + 'ACS', + 'ACSC', + 'ACST', + 'ACST', + 'ACT', + 'ACTD', + 'ACTF', + 'ACTU', + 'ACU', + 'ACUF', + 'ACV', + 'AD', + 'ADA', + 'ADAC', + 'ADAGE', + 'ADAP', + 'ADAS', + 'ADAT', + 'ADatP', + 'ADATS', + 'ADB', + 'ADC', + 'ADCTS', + 'ADD', + 'ADDIE', + 'ADDNS', + 'ADF', + 'ADGB', + 'ADHD', + 'ADHS', + 'ADI', + 'ADIZ', + 'ADL', + 'ADM', + 'ADOA', + 'ADP', + 'ADRG', + 'ADS', + 'ADSR', + 'ADT', + 'ADTB', + 'ADW', + 'ae', + 'AE', + 'AED', + 'AEDT', + 'AEF', + 'AEGIS', + 'AESR', + 'AEST', + 'aet', + 'AEW', + 'af', + 'aF', + 'AF', + 'AFA', + 'AFAIC', + 'AFAICT', + 'AFAIK', + 'AFAIR', + 'AFAM', + 'AFATDS', + 'AFC', + 'AFCEA', + 'AFDB', + 'AFES', + 'AFG', + 'AFI', + 'AFIS', + 'AFIST', + 'AFJ', + 'AFK', + 'AFL', + 'AFLAC', + 'AFM', + 'AFN', + 'AFO', + 'AFOSR', + 'AFP', + 'AFQT', + 'afr', + 'AFRC', + 'AFRL', + 'AFROTC', + 'AFRRI', + 'AFRTS', + 'AFS', + 'AFSC', + 'AFSCN', + 'AFSPC', + 'AFT', + 'AFV', + 'AFWA', + 'ag', + 'Ag', + 'AG', + 'AGA', + 'AGARD', + 'AGARDograph', + 'AGASA', + 'AGB', + 'AGC', + 'AGC', + 'AGDC', + 'AGI', + 'AGL', + 'AGM', + 'AGN', + 'AGO', + 'AGP', + 'AGRICOLA', + 'AGS', + 'AGTS', + 'AGU', + 'AGW', + 'aH', + 'Ah', + 'AH', + 'AHA', + 'AHCPR', + 'AHEC', + 'AHPCRC', + 'AHQ', + 'AHRQ', + 'AHS', + 'AI', + 'AIA', + 'AIAA', + 'AIC', + 'AICD', + 'AICMFP', + 'AID', + 'AIDS', + 'AIM', + 'AIMP', + 'AIPAC', + 'AIR', + 'AIRFA', + 'AIRS', + 'AIS', + 'AISB', + 'aJ', + 'AJ', + 'Ajax', + 'AJD', + 'AJP', + 'AJPW', + 'ak', + 'aK', + 'AK', + 'aka', + 'AKA', + 'AKDT', + 'AKST', + 'AKP', + 'aL', + 'Al', + 'AL', + 'ALA', + 'ALAP', + 'ALARM', + 'ALB', + 'ALD', + 'ALERT', + 'ALF', + 'ALG', + 'ALGOL', + 'ALL', + 'ALOA', + 'ALOC', + 'ALPA', + 'ALS', + 'ALSHS', + 'ALSP', + 'ALTE', + 'ALTACS', + 'am', + 'Am', + 'AM', + 'AM1', + 'AMA', + 'AMAPS', + 'AMC', + 'AMCB', + 'AMD', + 'AMF', + 'AMFOM', + 'AMFR', + 'AMFRS', + 'amh', + 'AML', + 'AMLCD', + 'AMLO', + 'AMO', + 'AMOLED', + 'AMOS', + 'AMP', + 'AMRAAM', + 'AMRDEC', + 'AMS', + 'AMSAA', + 'AMSO', + 'AMSU', + 'AMTE', + 'an', + 'aN', + 'AN', + 'ANA', + 'ANC', + 'AND', + 'ANFO', + 'ANG', + 'ANGLiCo', + 'ANL', + 'ANM', + 'ANOVA', + 'ANS', + 'ANSI', + 'ANTM', + 'ANT', + 'ANTY', + 'ANWR', + 'ANZ', + 'ANZAC', + 'AO', + 'AOA', + 'AOC', + 'AOD', + 'AOI', + 'AOL', + 'AOR', + 'AOSTH', + 'AOTS', + 'AOU', + 'AP', + 'APA', + 'APB', + 'APC', + 'APEC', + 'APCNR', + 'APCR', + 'APDS', + 'APEDS', + 'APEX', + 'APFSDS', + 'APHA', + 'APHE', + 'APHIS', + 'API', + 'APL', + 'APM', + 'APOD', + 'APOE', + 'APOLLO', + 'APP', + 'APR', + 'APS', + 'AQ', + 'AQIM', + 'ar', + 'Ar', + 'AR', + 'ara', + 'ARAG', + 'ARB', + 'ARBA', + 'ARC', + 'ARCA', + 'ARCENT', + 'ARDA', + 'ARE', + 'ARF', + 'arg', + 'ARG', + 'ARH', + 'ARI', + 'ARL', + 'ARM', + 'ARNet', + 'ARP', + 'ARPA', + 'ARRC', + 'ARRF', + 'ARRL', + 'ARS', + 'ARSC', + 'ARSPACE', + 'ART', + 'ARTCC', + 'ARWG', + 'as', + 'aS', + 'As', + 'AS', + 'AS', + 'ASA', + 'ASAP', + 'ASARS', + 'ASB', + 'asbl', + 'ASBO', + 'ASCAP', + 'ASCC', + 'ASCIET', + 'ASCII', + 'ASD', + 'ASDIC', + 'ASEAN', + 'ASFAR', + 'ASG', + 'ASIC', + 'ASICS', + 'ASIO', + 'ASIP', + 'ASL', + 'asm', + 'ASM', + 'ASMB', + 'ASME', + 'ASMP', + 'ASN', + 'ASOC', + 'ASP', + 'ASPI', + 'ASR', + 'ASRAAM', + 'ASSC', + 'AST', + 'ASTIA', + 'ASTM', + 'ASTREA', + 'ASUW', + 'ASVAB', + 'ASVEL', + 'ASW', + 'ASWORG', + 'ASX', + 'aT', + 'At', + 'ATA', + 'ATAC', + 'ATACMS', + 'ATAPI', + 'ATAS', + 'ATAT', + 'ATB', + 'ATC', + 'ATCCIS', + 'ATESS', + 'ATF', + 'ATG', + 'ATGM', + 'ATGW', + 'ATI', + 'Atisbo', + 'ATK', + 'ATL', + 'ATLAS', + 'ATM', + 'ATN', + 'ATO', + 'ATP', + 'ATSD', + 'ATSDR', + 'ATV', + 'ATWA', + 'Au', + 'AU', + 'AUC', + 'AUD', + 'AUS', + 'AUSCANNZUKUS', + 'Auslan', + 'AUT', + 'AUTL', + 'av', + 'aV', + 'Av', + 'AV', + 'ava', + 'AVA', + 'AVB', + 'AVDLR', + 'ave', + 'AVGP', + 'AVI', + 'AVIM', + 'AVLB', + 'AVM', + 'AVO', + 'AVP', + 'AVP', + 'AVRE', + 'AVTB', + 'AVUM', + 'AVVO', + 'aW', + 'AW', + 'AWA', + 'AWACS', + 'AWC', + 'AWD', + 'AWE', + 'AWG', + 'AWOL', + 'AWP', + 'AWSIM', + 'AWST', + 'AWW', + 'AX', + 'AXP', + 'ay', + 'AY', + 'AYBABTU', + 'aym', + 'AYU', + 'az', + 'AZ', + 'aze', + 'AZE', + 'AZN', + 'AZUR', + 'BAR', + 'BB', + 'BBB', + 'BBC', + 'BBD', + 'BBL', + 'BBS', + 'BBYO', + 'BC', + 'BCA', + 'BCCI', + 'BCE', + 'BCS', + 'BEA', + 'BES', + 'BH', + 'BHS', + 'BNF', + 'BNP', + 'BO', + 'BoAML', + 'BOC', + 'BOGs', + 'BOI', + 'BOM', + 'BOQ', + 'BOT', + 'BP', + 'BPV', + 'BQ', + 'BR', + 'BRB', + 'BRN', + 'BS', + 'BSA', + 'BSatCoP', + 'BSD', + 'BSE', + 'BSN', + 'BSNL', + 'BSP', + 'BST', + 'Bt', + 'BT', + 'BTN', + 'C2C', + 'Ca', + 'CA', + 'CAA', + 'CAAT', + 'CAC', + 'CACM', + 'CAD', + 'CADCAM', + 'CADPAT', + 'CADMID', + 'CAE', + 'CAEn', + 'CAF', + 'CAFAD', + 'CAFDE', + 'CAI', + 'CAIR', + 'CALABARZON', + 'CalPERS', + 'Caltrans', + 'CAM', + 'CAMRA', + 'CANA', + 'CANDU', + 'Canola', + 'CAP', + 'CAPES', + 'CARE', + 'CARMONETTE', + 'CARP', + 'CART', + 'CAS', + 'CASA', + 'CASE', + 'CASS', + 'CASTFOREM', + 'CASTOR', + 'cat', + 'CAT', + 'CATK', + 'CATS', + 'CATT', + 'CAV', + 'CAVOK', + 'CAW', + 'CB', + 'CBASSE', + 'CBC', + 'CBE', + 'CBF', + 'CBGB', + 'CBLA', + 'CBN', + 'CBM', + 'CBML', + 'CBOL', + 'CBOT', + 'CBR', + 'CBRN', + 'CBRNE', + 'CBS', + 'CBT', + 'CBUS', + 'CC', + 'CCA', + 'CCC', + 'CCCP', + 'CCD', + 'CCF', + 'CCG', + 'CCGC', + 'CCGH', + 'CCGS', + 'CCH', + 'CCIF', + 'CCII', + 'CCIR', + 'CCIT', + 'CCITT', + 'CCK', + 'CCM', + 'CCOC', + 'CCOP', + 'CCP', + 'CCPA', + 'CCR', + 'CCRAp', + 'CCRP', + 'CCRTS', + 'CCSCS', + 'CCSCS', + 'CCSIL', + 'CCSK', + 'CCT', + 'CCTT', + 'cd', + 'Cd', + 'CD', + 'CD1', + 'CDA', + 'CDC', + 'CDD', + 'CDE', + 'CDEC', + 'CDF', + 'CDIAC', + 'CDipAF', + 'CDL', + 'CDM', + 'CDMA', + 'CDMSII', + 'Cdr', + 'CDS', + 'Cdt', + 'CDT', + 'ce', + 'Ce', + 'CE', + 'CEA', + 'CECOM', + 'CEDEX', + 'CEGEP', + 'CEI', + 'CENTCOM', + 'CENTO', + 'CENZUB', + 'CEO', + 'CEP', + 'CEPT', + 'CERCLA', + 'CERDEC', + 'CERGA', + 'CERN', + 'CERT', + 'ces', + 'CESS', + 'CEV', + 'CEWC', + 'Cf', + 'CF', + 'CFA', + 'CFAR', + 'CFC', + 'CFES', + 'CFFZ', + 'CFG', + 'CFHT', + 'CFI', + 'CFL', + 'CFLCC', + 'CFM', + 'CFO', + 'CFP', + 'CFR', + 'CFRP', + 'CFS', + 'CFSP', + 'CFU', + 'CFV', + 'CFZ', + 'CG', + 'CGDC', + 'CGF', + 'CGH', + 'CGIG', + 'CGM', + 'CGO', + 'CGS', + 'CGT', + 'ch', + 'ch', + 'CH', + 'cha', + 'CHAMPUS', + 'CHAOS', + 'CHAPS', + 'CHDK', + 'che', + 'CHE', + 'CHF', + 'CHI', + 'CHI', + 'CHiP', + 'CHIPS', + 'CHL', + 'CHLA', + 'CHN', + 'CHOGM', + 'CHP', + 'CHPSO', + 'CHS', + 'CHT', + 'chu', + 'chv', + 'CREN', + 'Ci', + 'CI', + 'CIA', + 'CIAO', + 'CIB', + 'CIBC', + 'CICS', + 'CID', + 'CID', + 'CIEF', + 'CIF', + 'CIMIC', + 'CIMMYT', + 'CINC', + 'CINEOS', + 'CIO', + 'CIP', + 'CIPE', + 'CIR', + 'CIR', + 'CIS', + 'CISA', + 'CISB', + 'CISPR', + 'CISSP', + 'CITES', + 'CITS', + 'CIV', + 'CIVETS', + 'CIVPOP', + 'CIWS', + 'CHRS', + 'CJ', + 'CJC', + 'CJD', + 'CJCS', + 'CJO', + 'CJTF', + 'CK', + 'CKA', + 'CKND', + 'Cl', + 'CL', + 'CLA', + 'CLEC', + 'CLEMARS', + 'CLI', + 'CLIC', + 'CLO', + 'CLOB', + 'CLOS', + 'CLOS', + 'CLP', + 'CLPFC', + 'CLRP', + 'Cm', + 'CM', + 'CMA', + 'CMB', + 'CMBR', + 'CME', + 'CMHC', + 'CMIIAW', + 'CMIS', + 'CMJ', + 'CML', + 'CMLL', + 'CMM', + 'CMMC', + 'CMML', + 'CMO', + 'CMOC', + 'CMOS', + 'CMP', + 'CMP', + 'CMPD', + 'CMPD', + 'CMR', + 'CMS', + 'CMT', + 'CMU', + 'CMW', + 'CMYK', + 'CN', + 'CNA', + 'CNBC', + 'CND', + 'CNE', + 'CNF', + 'CNI', + 'CNI', + 'CNN', + 'CNO', + 'CNR', + 'CNV', + 'CNY', + 'co', + 'Co', + 'CO', + 'COA', + 'COB', + 'COBRA', + 'COCOM', + 'COD', + 'CoDA', + 'codec', + 'COE', + 'COEA', + 'COEIA', + 'COFT', + 'COG', + 'COGENT', + 'COIL', + 'COIN', + 'COK', + 'COL', + 'COLA', + 'COLT', + 'COM', + 'COMECON', + 'COMMZ', + 'COMSEC', + 'CONAD', + 'CONCACAF', + 'CONMEBOL', + 'CONOPS', + 'CONUS', + 'COO', + 'COP', + 'COPE', + 'COPUS', + 'cor', + 'CORBA', + 'CORD', + 'CORE', + 'CORG', + 'COROT', + 'cos', + 'COS', + 'COSCOM', + 'COSER', + 'CoT', + 'COTS', + 'Coy', + 'CP', + 'CPA', + 'CPAC', + 'CPCU', + 'CPD', + 'CPE', + 'CPE', + 'CPF', + 'CPG', + 'CPI', + 'CPIM', + 'CPIR', + 'CPON', + 'CPPCC', + 'CPR', + 'CPR', + 'CPSM', + 'CPSU', + 'CPT', + 'CPU', + 'CPV', + 'CPX', + 'CQ', + 'CQD', + 'cr', + 'Cr', + 'CR', + 'CRAM', + 'CRC', + 'CRD', + 'cre', + 'CREB', + 'CREEP', + 'CRI', + 'CRIPL', + 'CRM', + 'CRO', + 'CRP', + 'CRT', + 'CRTC', + 'cs', + 'Cs', + 'CS', + 'CSA', + 'CSAR', + 'CSC', + 'CSEAL', + 'CSERIAC', + 'CSG', + 'CSI', + 'CSICOP', + 'CSIP', + 'CSIS', + 'CSK', + 'CSL', + 'CSPI', + 'CSR', + 'CSR', + 'CSRC', + 'CSRF', + 'CSS', + 'CSSCS', + 'CST', + 'CT', + 'CTA', + 'CTBT', + 'CTC', + 'CTDB', + 'CTE', + 'CTGF', + 'CTI', + 'CTIL', + 'CTIS', + 'CTMS', + 'CTO', + 'CTS', + 'CTSS', + 'CTU', + 'CTVT', + 'cu', + 'Cu', + 'CU', + 'CUB', + 'CUCV', + 'CUNY', + 'CUP', + 'CUPE', + 'CUPS', + 'CUPW', + 'CUREA', + 'CuW', + 'CUW', + 'cv', + 'CV', + 'CVCC', + 'CVE', + 'CVI', + 'CVLL', + 'CVR', + 'CVS', + 'CVSD', + 'CVT', + 'CW', + 'CWC', + 'CWS', + 'CWRU', + 'CX', + 'CXR', + 'cy', + 'CY', + 'cym', + 'CYM', + 'CYP', + 'CZ', + 'CZE', + 'CZK', + 'CZF', + 'CZMA', + 'd', + 'D', + 'da', + 'DA', + 'DAB', + 'DACT', + 'DADVSI', + 'DAES', + 'DAF', + 'DAFIF', + 'DAK', + 'dan', + 'DAO', + 'DAQ', + 'DARE', + 'DARO', + 'DARPA', + 'DART', + 'DAS', + 'DASD', + 'DASL', + 'DAU', + 'DAWN', + 'dB', + 'Db', + 'DB', + 'DBA', + 'DBBL', + 'DBCS', + 'DBDO', + 'DBE', + 'DBF', + 'DBM', + 'DBR', + 'DBRA', + 'DBS', + 'DC', + 'DCA', + 'DCAA', + 'DCAS', + 'DCC', + 'DCC', + 'DCCP', + 'DCD', + 'DCE', + 'DCFC', + 'DCI', + 'DCIM', + 'DCIMS', + 'DCL', + 'DCN', + 'DCOM', + 'DCOP', + 'DCSOPS', + 'DCW', + 'DCYSC', + 'DD', + 'DDA', + 'DDD', + 'DDL', + 'DDN', + 'DDoS', + 'DDP', + 'DDR', + 'DDS', + 'DDT', + 'de', + 'DE', + 'DEA', + 'DEA', + 'DEC', + 'DeCA', + 'DECSIM', + 'DECT', + 'DEFCON', + 'DEFRA', + 'DELT', + 'DEM', + 'DEN', + 'DEPSECDEF', + 'DERA', + 'DES', + 'deu', + 'DEU', + 'DF', + 'DFAD', + 'DFAS', + 'DFB', + 'DFCB', + 'DfID', + 'DFL', + 'DFM', + 'DFM', + 'DFSP', + 'DfT', + 'DFTBA', + 'DG', + 'DGA', + 'DGPS', + 'DGZ', + 'DH', + 'DHA', + 'DHHS', + 'DHL', + 'DHRA', + 'DHS', + 'DHSS', + 'DHTFYSS', + 'DHY', + 'DIA', + 'DIAC', + 'DiC', + 'DICASS', + 'DILLIGAD', + 'DILLIGAF', + 'DIF', + 'DIFAR', + 'DIME', + 'DIMM', + 'DIN', + 'DINA', + 'DINKY', + 'DIP', + 'DIPS', + 'DIR', + 'DIS', + 'DISA', + 'DISCOM', + 'DISE', + 'DISSTAF', + 'DITY', + 'div', + 'DIVAD', + 'DIY', + 'DJ', + 'DJD', + 'DJF', + 'DJI', + 'DJS', + 'DK', + 'DKK', + 'DL', + 'DLA', + 'DLIC', + 'DLIR', + 'DLL', + 'DLPMC', + 'DLR', + 'DLRP', + 'DLS', + 'DLSA', + 'DLSc', + 'DLSC', + 'DLTBGYD', + 'DM', + 'DMA', + 'DMC', + 'DMCA', + 'DMD', + 'DMI', + 'DMM', + 'DMOC', + 'DMOS', + 'DMPA', + 'DMPFC', + 'DMPK', + 'DMRB', + 'DMS', + 'DMSO', + 'DMSP', + 'DMT', + 'DMU', + 'DMV', + 'DMZ', + 'DN', + 'DNA', + 'DNAW', + 'DND', + 'DNDi', + 'DNDO', + 'DNM', + 'DNK', + 'DNR', + 'DNS', + 'DO', + 'DOA', + 'DOB', + 'DOD', + 'DODAF', + 'DoDD', + 'DoDEA', + 'DOE', + 'DOHC', + 'DOI', + 'DOM', + 'DOMS', + 'DOP', + 'DORA', + 'DOS', + 'DOT', + 'DOTA', + 'DotD', + 'DOTMLPF', + 'DOW', + 'DP', + 'DP', + 'DPA', + 'DPCO', + 'DPDT', + 'DPICM', + 'DPKO', + 'DPMO', + 'DPPDB', + 'DPRE', + 'DPRK', + 'DPST', + 'DQ', + 'DR', + 'DRA', + 'DRAGN', + 'DRAM', + 'DRC', + 'DRDC', + 'DREA', + 'DRES', + 'DRET', + 'DREV', + 'DRI', + 'DRIC', + 'DRL', + 'DRM', + 'DRMS', + 'DRPR', + 'DRT', + 'Ds', + 'DS', + 'DSA', + 'DSAA', + 'DSAS', + 'DSB', + 'DSC', + 'DSCS', + 'DSCSOC', + 'DSD', + 'DSDS', + 'DSE', + 'DSG', + 'DSID', + 'DSL', + 'DSLR', + 'DSM', + 'DSN', + 'DSO', + 'dsp', + 'dspl', + 'dspm', + 'dspml', + 'dspms', + 'dsps', + 'DSP', + 'DSPL', + 'DSR', + 'DSRV', + 'DSS', + 'DST', + 'DSTL', + 'dsvp', + 'DSWA', + 'DTA', + 'DTD', + 'DTE', + 'DTED', + 'DTG', + 'DTH', + 'DTI', + 'DTIC', + 'DTL', + 'DTM', + 'DTMF', + 'DTLOMS', + 'DTO', + 'DTOMLS', + 'DTR', + 'DTRA', + 'DTSA', + 'DU', + 'DUI', + 'DUMBO', + 'DUSD', + 'dv', + 'DV', + 'DVD', + 'DVLC', + 'dvm', + 'DVM', + 'DVO', + 'DVOM', + 'DVP', + 'dvp', + 'dvu', + 'DW', + 'DWG', + 'DWI', + 'DWM', + 'DWIM', + 'DWTS', + 'DWW', + 'DX', + 'DXA', + 'DXM', + 'Dy', + 'DY', + 'dz', + 'DZ', + 'DZA', + 'DZD', + 'dzo', + 'E', + 'EA', + 'EABU', + 'EAC', + 'EAFUS', + 'EAPC', + 'EASA', + 'EAST', + 'EBCDIC', + 'EBD', + 'EBE', + 'EBO', + 'EBSA', + 'EBW', + 'EBW', + 'EC', + 'ECB', + 'ECBR', + 'ECASS', + 'ECCM', + 'ECFS', + 'ECG', + 'ECHO', + 'ECHR', + 'ECJ', + 'ECL', + 'ECM', + 'ECommHR', + 'ECS', + 'ECSC', + 'ECT', + 'ECtHR', + 'ECU', + 'ECW', + 'EDA', + 'EDAC', + 'EDCS', + 'EDD', + 'EDECSIM', + 'EDGAR', + 'EDH', + 'EDS', + 'EDSA', + 'EDT', + 'EDTA', + 'ee', + 'EE', + 'EEC', + 'EEG', + 'EEK', + 'EENT', + 'EEO', + 'EEOC', + 'EEZ', + 'EF', + 'EFF', + 'EFL', + 'EFM', + 'EFOA', + 'EFSA', + 'EFT', + 'EFTA', + 'EFV', + 'EG', + 'EGBUS', + 'EGCG', + 'EGIS', + 'EGP', + 'EGRET', + 'EGY', + 'EH', + 'EHA', + 'EHF', + 'EHR', + 'Ei', + 'EI', + 'EIA', + 'EIAH', + 'EIAH', + 'EIC', + 'EICS', + 'eID', + 'EIRP', + 'EIS', + 'EJ', + 'EK', + 'EKG', + 'EKMS', + 'el', + 'EL', + 'ELF', + 'ELINT', + 'ell', + 'ELL', + 'ELM', + 'ELO', + 'ELP', + 'ELSS', + 'ELT', + 'Em', + 'EM', + 'EMA', + 'Embraer', + 'EMC', + 'EMCON', + 'EMD', + 'EMDG', + 'EMDR', + 'EMEA', + 'EMF', + 'EMG', + 'EMO', + 'EMP', + 'EMS', + 'EMT', + 'EMU', + 'en', + 'EN', + 'ENCODE', + 'ENE', + 'eng', + 'ENG', + 'ENSATT', + 'ENT', + 'ENTEC', + 'ENVL', + 'ENY', + 'eo', + 'EO', + 'EOD', + 'EOG', + 'EOS', + 'EOSAT', + 'EOSDIS', + 'EOTS', + 'EOM', + 'EP', + 'EP', + 'EPA', + 'EPCOT', + 'EPCR', + 'EPFL', + 'EPGS', + 'EPIRB', + 'EPL', + 'EPLRS', + 'epo', + 'EPO', + 'EPOS', + 'EPPA', + 'EPR', + 'EPW', + 'EQ', + 'EQG', + 'Er', + 'ER', + 'ERA', + 'ERCIM', + 'ERI', + 'ERLAWS', + 'ERN', + 'EROS', + 'ERP', + 'ERR', + 'ERS', + 'ERTS', + 'ERV', + 'es', + 'Es', + 'ES', + 'ESA', + 'ESC', + 'ESD', + 'ESDP', + 'ESE', + 'ESH', + 'ESL', + 'ESM', + 'ESO', + 'ESOL', + 'ESP', + 'ESPGHAN', + 'ESPN', + 'est', + 'EST', + 'ESU', + 'ESV', + 'et', + 'ET', + 'Exatesla', + 'ETA', + 'ETB', + 'ETF', + 'ETH', + 'ETI', + 'ETLA', + 'ETM', + 'ETOPS', + 'ETT', + 'eu', + 'Eu', + 'EU', + 'EUCOM', + 'EULA', + 'EUR', + 'eus', + 'EUUSA', + 'EV', + 'EVA', + 'EVIL', + 'EW', + 'ewe', + 'EWR', + 'EWSD', + 'EWTN', + 'EXFOR', + 'EXREP', + 'EYD', + 'EZ', + 'EZLN', + 'f', + 'F', + 'fa', + 'FA', + 'fA', + 'FA', + 'FAA', + 'FAAD', + 'FAADEZ', + 'FAAN', + 'FAAFP', + 'FAAN', + 'FAAQ', + 'FAASV', + 'FAB', + 'FAC', + 'FACA', + 'FACH', + 'FACN', + 'FACP', + 'FACS', + 'FACTS', + 'FAD', + 'FAH', + 'FAI', + 'FAM', + 'fao', + 'FAO', + 'FAPA', + 'FAPHA', + 'FAQ', + 'FARC', + 'FARDC', + 'FARP', + 'fas', + 'FAS', + 'FASCAM', + 'FASD', + 'FASEB', + 'FAT', + 'FAVS', + 'FAW', + 'FB', + 'FBCS', + 'FBI', + 'FBIS', + 'FBIDST', + 'FBM', + 'FBR', + 'FBS', + 'fC', + 'FC', + 'FCA', + 'FCC', + 'FCE', + 'FCF', + 'FCLA', + 'FCM', + 'FCO', + 'FCS', + 'FCW', + 'FCZ', + 'FD', + 'FDA', + 'FDC', + 'FDDI', + 'FDBK', + 'FDR', + 'Fe', + 'FE', + 'FEAR', + 'FEAST', + 'FEATS', + 'FEB', + 'FEBA', + 'FED', + 'FEDEP', + 'FEF', + 'FEI', + 'FEMA', + 'FEMEXFUT', + 'FENAFUTH', + 'FENS', + 'FEP', + 'FEPAFUT', + 'FES', + 'FEST', + 'FET', + 'FEZ', + 'ff', + 'fF', + 'FF', + 'FFA', + 'FFB', + 'FFC', + 'FFE', + 'FFF', + 'FFG', + 'FFOV', + 'FFR', + 'FFS', + 'FFT', + 'FG', + 'fg', + 'FG', + 'FGR', + 'fH', + 'FHFIF', + 'FHI', + 'fi', + 'FI', + 'FIA', + 'FIAT', + 'FIB', + 'FIBA', + 'FIBUA', + 'FIC', + 'FID', + 'FIE', + 'FIFA', + 'FIFe', + 'FIFO', + 'FIFRA', + 'FIG', + 'FIGJAM', + 'fij', + 'FILA', + 'FILO', + 'FIMPT', + 'fin', + 'FIN', + 'FINA', + 'FIPS', + 'FIR', + 'FIRES', + 'FIS', + 'FISA', + 'FISH', + 'FiST', + 'FITS', + 'FIU', + 'fj', + 'fJ', + 'FJ', + 'FJD', + 'FJI', + 'FJN', + 'fK', + 'FK', + 'FKP', + 'FKSM', + 'FKT', + 'fl', + 'fL', + 'FL', + 'FLA', + 'FLAB', + 'FLAC', + 'FLAC', + 'FLAK', + 'FLB', + 'FLET', + 'FLG', + 'FLIP', + 'FLIR', + 'FLK', + 'FLOT', + 'FLSA', + 'FLQ', + 'FLQ', + 'FLTSAT', + 'FLTSATCOM', + 'FLUBOS', + 'FM', + 'fm', + 'Fm', + 'FM', + 'FMC', + 'FMCA', + 'FMCAfam', + 'FME', + 'FMECA', + 'FMJD', + 'FMLA', + 'Fmr', + 'fMRI', + 'FMS', + 'FMSF', + 'FMTV', + 'FMCG', + 'fN', + 'FNA', + 'fo', + 'FO', + 'FOAD', + 'FOAF', + 'FoB', + 'FOB', + 'FOC', + 'FoE', + 'FOGC', + 'FOH', + 'FOIA', + 'FOM', + 'FONSI', + 'FOO', + 'FOR', + 'Fortran', + 'FOS', + 'FoS', + 'FOSE', + 'FOSE', + 'FOT', + 'FOTA', + 'FOTC', + 'FOUO', + 'FOV', + 'FP', + 'FPF', + 'fps', + 'FPS', + 'FOOP', + 'FQ', + 'FQDN', + 'fr', + 'Fr', + 'FR', + 'fra', + 'FRA', + 'FRAGO', + 'FRAND', + 'FRCP', + 'FRICBM', + 'frr', + 'FRR', + 'FRO', + 'FRS', + 'FRSA', + 'fry', + 'FRY', + 'fs', + 'fS', + 'FS', + 'FSA', + 'FSB', + 'FSC', + 'FSCL', + 'FSE', + 'FSF', + 'FSH', + 'FSI', + 'FSK', + 'FSM', + 'FSO', + 'FSS', + 'FSSP', + 'FST', + 'FSTC', + 'FSV', + 'fT', + 'FTC', + 'FTE', + 'FTL', + 'FTP', + 'FTSE', + 'FTW', + 'FTS', + 'FTS', + 'FU', + 'FUBAR', + 'FUCK', + 'FUD', + 'ful', + 'fV', + 'fW', + 'FW', + 'FWC', + 'FWF', + 'FWI', + 'FWIW', + 'FWS', + 'FWT', + 'FX', + 'FXX', + 'fy', + 'FY', + 'FYA', + 'FYDP', + 'FYI', + 'FYROM', + 'FZDZ', + 'FZFG', + 'FZRA', + 'g', + 'G', + 'G1', + 'G2', + 'G3', + 'G4', + 'G5', + 'G6', + 'G7', + 'G8', + 'G9', + 'G10', + 'G15', + 'G20', + 'G24', + 'G77', + 'Ga', + 'GA', + 'GAAR', + 'GAB', + 'GABA', + 'GAD', + 'GAF', + 'GALCIT', + 'GAO', + 'GAW', + 'GB', + 'GBBC', + 'GBE', + 'GBH', + 'GBP', + 'GBR', + 'GBU', + 'GC', + 'GCA', + 'GCCS', + 'GCD', + 'GCF', + 'GCI', + 'GCN', + 'GCS', + 'GCSE', + 'gd', + 'Gd', + 'GD', + 'GDC', + 'GDL', + 'GDP', + 'GDR', + 'GDR', + 'GDW', + 'Ge', + 'GE', + 'GEBCO', + 'GEC', + 'GED', + 'GEEP', + 'GEL', + 'GEMA', + 'GEN', + 'GEO', + 'GEOS', + 'GEQ', + 'GER', + 'Gestapo', + 'GeV', + 'GEV', + 'GF', + 'GFCF', + 'GFCI', + 'GFDL', + 'GFE', + 'GFI', + 'GFL', + 'GFN', + 'GFY', + 'GG', + 'GGK', + 'GGP', + 'GH', + 'GHA', + 'GHB', + 'GHC', + 'Gi', + 'GI', + 'GIB', + 'GIF', + 'GIG', + 'GIGO', + 'GIMP', + 'GIN', + 'GINK', + 'GINO', + 'GIP', + 'GIS', + 'GISS', + 'GJ', + 'GJD', + 'GK', + 'gl', + 'GL', + 'gla', + 'GLAAD', + 'GLAC', + 'GLAST', + 'GLBA', + 'GLC', + 'gle', + 'glg', + 'GLINT', + 'GLONASS', + 'GLP', + 'GLUT', + 'glv', + 'GLV', + 'Gm', + 'GM', + 'GMAC', + 'GMB', + 'GmbH', + 'GMC', + 'GMD', + 'GMFSC', + 'GMI', + 'GML', + 'GMO', + 'GMPCS', + 'GMT', + 'gn', + 'GN', + 'GNAS', + 'GNB', + 'GNC', + 'GNF', + 'GNOME', + 'GNP', + 'GNQ', + 'GnRH', + 'GNU', + 'GO', + 'GOA', + 'GOAT', + 'GOC', + 'GOES', + 'GOP', + 'gorp', + 'GOSC', + 'GOSH', + 'GOSP', + 'GOTS', + 'GOYADIN', + 'GP', + 'GPA', + 'GPC', + 'GPG', + 'GPIB', + 'GPL', + 'GPMP', + 'GPO', + 'GPPC', + 'GPR', + 'GPS', + 'GPT', + 'GPU', + 'GQ', + 'GR', + 'GRAIL', + 'GRAS', + 'GRB', + 'GRC', + 'GRD', + 'GRE', + 'grep', + 'GRid', + 'GRID', + 'GriT', + 'GRL', + 'GRN', + 'grn', + 'GRP', + 'GRU', + 'Gs', + 'GS', + 'GSR', + 'GSA', + 'GSD', + 'GSG', + 'GSGB', + 'GSK', + 'GSM', + 'GSN', + 'GSO', + 'GSOH', + 'GSP', + 'GSS', + 'GST', + 'GSW', + 'GT', + 'GTA', + 'GTB', + 'GTC', + 'GTD', + 'GTG', + 'GTK', + 'GTGN', + 'GTM', + 'GTQ', + 'GTV', + 'gu', + 'GU', + 'GUA', + 'GUF', + 'GUI', + 'GUILT', + 'guj', + 'GULCR', + 'Gulag', + 'GUM', + 'GUT', + 'gv', + 'GV', + 'GW', + 'GWARS', + 'GWAS', + 'GWOT', + 'GWS', + 'GXA', + 'GXE', + 'GXF', + 'GXH', + 'GXL', + 'GXM', + 'GXP', + 'GxP', + 'GXY', + 'GY', + 'GYD', + 'GYY', + 'GZ', + 'GZK', + 'h', + 'H', + 'ha', + 'HA', + 'HAARP', + 'HAART', + 'HAI', + 'HAL', + 'HALO', + 'HALT', + 'HAM', + 'HANS', + 'HARM', + 'hat', + 'HAT', + 'hau', + 'HAW', + 'HAWK', + 'HAZ', + 'HAZMAT', + 'HBCD', + 'HBCI', + 'HBO', + 'HBR', + 'HBT', + 'HBV', + 'HCCI', + 'HCF', + 'HCI', + 'HCSC', + 'HCT', + 'HD', + 'HDD', + 'HDL', + 'HDM', + 'HDMI', + 'HDR', + 'HDRA', + 'HDTV', + 'he', + 'He', + 'HE', + 'HEAT', + 'heb', + 'HEBA', + 'HEER', + 'HEIC', + 'HEMP', + 'HEO', + 'HEOSS', + 'HEP', + 'HEPA', + 'her', + 'HESH', + 'HESV', + 'HETT', + 'HEU', + 'Hewitts', + 'Hf', + 'HF', + 'HFAC', + 'HFE', + 'HFE', + 'HFEA', + 'Hg', + 'HGC', + 'HGF', + 'HGTV', + 'HHD', + 'HHB', + 'hi', + 'HI', + 'HIDACZ', + 'HIH', + 'HIM', + 'HIMAD', + 'HIMEZ', + 'HIMYM', + 'hin', + 'HIPAA', + 'Hipparcos', + 'Hird', + 'HiRISE', + 'HIT', + 'HIV', + 'HIWT', + 'HJ', + 'HJD', + 'HK', + 'HKD', + 'HKG', + 'HKS', + 'hL', + 'HL', + 'HLA', + 'HLL', + 'HLN', + 'HLOS', + 'HLS', + 'HLVW', + 'HM', + 'HMCE', + 'HMCS', + 'HMD', + 'HMF', + 'HMM', + 'HMMWV', + 'hmo', + 'HMO', + 'HMP', + 'HMRC', + 'HMS', + 'HMSO', + 'HMV', + 'HN', + 'HND', + 'HNL', + 'ho', + 'Ho', + 'HO', + 'HOLLAND', + 'HOMES', + 'HONY', + 'HOPI', + 'HoReCa', + 'HORIZON', + 'HOSTAC', + 'HOT', + 'HOTAS', + 'HOV', + 'hp', + 'HP', + 'HPA', + 'HPCL', + 'HPLC', + 'HPT', + 'HPV', + 'HQ', + 'HQDA', + 'hr', + 'HR', + 'HRBL', + 'HRD', + 'HRED', + 'HRF', + 'HRH', + 'HRK', + 'HRM', + 'HRM', + 'HRT', + 'hrv', + 'HRV', + 'Hs', + 'HSBC', + 'HSC', + 'HSM', + 'HSMAD', + 'HSN', + 'HSPH', + 'HSS', + 'HSSI', + 'HST', + 'HSV', + 'ht', + 'HT', + 'HTA', + 'HTC', + 'HTFU', + 'HTG', + 'HTH', + 'HTI', + 'HTML', + 'HTR', + 'HTS', + 'HTTP', + 'HU', + 'HUD', + 'HUF', + 'HUHA', + 'HUMINT', + 'hun', + 'HUN', + 'HURD', + 'HVU', + 'HV', + 'HVAC', + 'HVAP', + 'HVHF', + 'HVM', + 'HVO', + 'HVP', + 'HW', + 'HWM', + 'HX', + 'HXB', + 'hy', + 'hye', + 'HYS', + 'hz', + 'Hz', + 'HZ', + 'I', + 'ia', + 'IA', + 'IAAF', + 'IAAL', + 'IAB', + 'IACREOT', + 'IAD', + 'IADS', + 'IAEA', + 'IAF', + 'IAFG', + 'IAI', + 'IANA', + 'IANAL', + 'IAQ', + 'IAS', + 'IASIP', + 'IATA', + 'IATF', + 'IATSE', + 'IAU', + 'IAUC', + 'IAW', + 'IB', + 'IBA', + 'IBD', + 'IBDM', + 'IBDS', + 'IBM', + 'ibo', + 'IBRD', + 'IBS', + 'IC', + 'ICANN', + 'ICAO', + 'ICBM', + 'ICBM', + 'ICBN', + 'ICC', + 'ICD', + 'ICE', + 'ICES', + 'ICF', + 'ICHTHYS', + 'ICM', + 'ICMP', + 'ICNAF', + 'ICNI', + 'ICO', + 'ICP', + 'ICPE', + 'ICRAF', + 'ICRISAT', + 'ICRL', + 'ICRS', + 'ICS', + 'ICSI', + 'ICT', + 'ICTH', + 'ICV', + 'ICW', + 'ICYMI', + 'ICZN', + 'id', + 'ID', + 'IDA', + 'IDC', + 'IDDI', + 'IDE', + 'IDeA', + 'IDEA', + 'IDF', + 'IDGAF', + 'IDK', + 'IDKIBTI', + 'IDL', + 'IDN', + 'ido', + 'IDR', + 'ie', + 'IE', + 'IEA', + 'IEC', + 'IED', + 'IEEE', + 'IELTS', + 'IER', + 'IET', + 'IET', + 'IETF', + 'IEW', + 'IFAB', + 'IFAH', + 'IFAP', + 'IFC', + 'IFF', + 'IFFN', + 'IFO', + 'IFOR', + 'IFOV', + 'IFPI', + 'IFR', + 'IFRB', + 'IFV', + 'ig', + 'IG', + 'IGNOU', + 'IGS', + 'IGY', + 'IHH', + 'IHO', + 'IHOP', + 'IHR', + 'IHSI', + 'ii', + 'II', + 'IICA', + 'IIE', + 'iii', + 'III', + 'IIRC', + 'IISS', + 'IIT', + 'IJN', + 'IJWP', + 'ik', + 'IKEA', + 'iku', + 'IL', + 'ILC', + 'ILDS', + 'ile', + 'ILO', + 'ILRI', + 'ILS', + 'ILY', + 'IM', + 'IMA', + 'IMBD', + 'IMDb', + 'IME', + 'IMF', + 'IMHO', + 'IML', + 'IMLAST', + 'IMNM', + 'IMNSHO', + 'IMPAC', + 'IMPACT', + 'IMPATT', + 'IMPT', + 'IMR', + 'IMRI', + 'IMRO', + 'IMRL', + 'IMS', + 'IMSA', + 'IMsL', + 'IMT', + 'IMU', + 'In', + 'ina', + 'INA', + 'INAS', + 'INC', + 'INCITS', + 'ind', + 'IND', + 'inet', + 'INDIGO', + 'INFORMS', + 'INFOSEC', + 'INLA', + 'INMARSAT', + 'INR', + 'INRI', + 'INRIA', + 'INS', + 'InSAR', + 'INST', + 'INTSUM', + 'INTERFET', + 'io', + 'IO', + 'IOC', + 'IOLTA', + 'IOM', + 'IONA', + 'IOT', + 'IOU', + 'IP', + 'IPA', + 'IPB', + 'IPCC', + 'ipk', + 'IPL', + 'IPN', + 'IPO', + 'IPR', + 'IPT', + 'IPTS', + 'IPTV', + 'IPW', + 'iq', + 'IQ', + 'IQD', + 'Ir', + 'IR', + 'IRA', + 'IRAD', + 'IRAN', + 'IRAS', + 'IRB', + 'IRBM', + 'IRC', + 'IREA', + 'IRL', + 'IRM', + 'IRN', + 'IRINN', + 'IRO', + 'IRQ', + 'IRR', + 'IRS', + 'IRSN', + 'IRST', + 'is', + 'ISA', + 'ISAAA', + 'ISAF', + 'ISAR', + 'ISB', + 'ISBN', + 'ISCCP', + 'ISCII', + 'ISDN', + 'ISEF', + 'ISEN', + 'ISF', + 'ISI', + 'ISIL', + 'ISIS', + 'ISK', + 'isl', + 'ISL', + 'ISLN', + 'ISM', + 'ISMB', + 'ISMC', + 'ISO', + 'ISOGP', + 'ISP', + 'ISPE', + 'ISR', + 'ISRO', + 'ISS', + 'ISSCR', + 'ISSN', + 'IST', + 'ISTAR', + 'ISTC', + 'ISTS', + 'ISU', + 'it', + 'IT', + 'ita', + 'ITA', + 'ITAG', + 'ITALY', + 'ITAS', + 'ITC', + 'ITCZ', + 'ITEC', + 'ITEMS', + 'ITER', + 'ITF', + 'ITK', + 'ITN', + 'ITS', + 'ITT', + 'ITTO', + 'ITU', + 'iu', + 'IUCN', + 'IUPAC', + 'IUSS', + 'IV', + 'IVD', + 'Iveco', + 'IVF', + 'IVI', + 'IVIS', + 'IVL', + 'IVM', + 'IVO', + 'IW', + 'IWARS', + 'IWC', + 'IWW', + 'IX', + 'IXT', + 'IY', + 'IZ', + 'J', + 'J1', + 'J2', + 'J2EE', + 'J2ME', + 'J2SE', + 'J3', + 'J4', + 'J5', + 'J6', + 'J7', + 'J8', + 'J9', + 'ja', + 'JA', + 'JAFT', + 'JAG', + 'JAL', + 'JALLC', + 'JAM', + 'JAMA', + 'JANET', + 'JANUS', + 'JATO', + 'jav', + 'JB', + 'JB', + 'JBFSA', + 'JCATS', + 'JCDB', + 'JCDS', + 'JCIDS', + 'JCIET', + 'JCL', + 'JCMOTF', + 'JCP', + 'JCPM', + 'JCS', + 'JCW', + 'JD', + 'JDAM', + 'JDBC', + 'JDCC', + 'JDEM', + 'JDK', + 'JDL', + 'JDM', + 'JE', + 'JEB', + 'JEDP', + 'JEE', + 'JEFX', + 'JEPD', + 'JET', + 'JEZ', + 'JFACC', + 'JFACTSU', + 'JFC', + 'JFCOM', + 'JFET', + 'JFK', + 'JFLCC', + 'JFMCC', + 'JFO', + 'JFSOCC', + 'JFTX', + 'JH', + 'JH', + 'JI', + 'JIB', + 'JIC', + 'JINI', + 'JITC', + 'JIVE', + 'JJAP', + 'JK', + 'JL', + 'JLA', + 'JM', + 'JMD', + 'JMS', + 'JN', + 'JNI', + 'JNLP', + 'JO', + 'JOA', + 'JOD', + 'Jofa', + 'JOPES', + 'JOR', + 'JOTS', + 'JSP', + 'JP', + 'JPEG', + 'JPL', + 'jpn', + 'JPN', + 'JPOW', + 'JPY', + 'JQ', + 'JRA', + 'JRDF', + 'JRE', + 'JREF', + 'JROC', + 'JROCM', + 'JRRF', + 'JRSAI', + 'JSAF', + 'JSAP', + 'JSCSC', + 'JSD', + 'JSEAD', + 'JSF', + 'JSIMS', + 'JSOA', + 'JSOTF', + 'JSP', + 'JSR', + 'JSS', + 'JSTARS', + 'JT', + 'JTAG', + 'JTAGS', + 'JTC', + 'JTF', + 'JTFEX', + 'JTIDS', + 'JTLS', + 'JTMD', + 'JTN', + 'JTRS', + 'JU', + 'JUGFET', + 'JUICE', + 'JUO', + 'jv', + 'JV', + 'JVM', + 'JW', + 'JWARS', + 'JWP', + 'JWST', + 'JY', + 'JZ', + 'k', + 'K', + 'K9', + 'ka', + 'kA', + 'Ka', + 'KA', + 'KAF', + 'kal', + 'kan', + 'KANUKOKA', + 'KARNAC', + 'KARR', + 'kas', + 'kat', + 'KATUSA', + 'kau', + 'kaz', + 'KAZ', + 'KBE', + 'KBO', + 'KBVB', + 'kC', + 'KCK', + 'KCL', + 'KCMO', + 'KCS', + 'KD', + 'KE', + 'KEDO', + 'KEN', + 'KERA', + 'kerma', + 'KES', + 'KESR', + 'KET', + 'kF', + 'KFC', + 'KFOR', + 'KFUPM', + 'kg', + 'KG', + 'KGB', + 'KGO', + 'KGS', + 'KGZ', + 'kH', + 'KH', + 'khm', + 'KHL', + 'KHM', + 'KHR', + 'KHz', + 'ki', + 'Ki', + 'KI', + 'KIA', + 'KIDS', + 'kik', + 'kin', + 'kir', + 'KIR', + 'KISS', + 'KITECH', + 'KITT', + 'KIT', + 'kj', + 'kJ', + 'kk', + 'kK', + 'KK', + 'KKK', + 'kl', + 'kL', + 'KL', + 'KLENOT', + 'KLM', + 'KLOC', + 'KLS', + 'km', + 'KM', + 'KMF', + 'KMFDM', + 'KMR', + 'kn', + 'kN', + 'KN', + 'KNA', + 'KNVB', + 'ko', + 'KO', + 'kom', + 'kon', + 'kor', + 'KOR', + 'KP', + 'KPMG', + 'KPNO', + 'KPV', + 'KPVT', + 'KPW', + 'KQ', + 'kr', + 'Kr', + 'KR', + 'KRW', + 'ks', + 'kS', + 'KS', + 'KSC', + 'KSI', + 'KSM', + 'KSU', + 'kT', + 'KT', + 'KTS', + 'ku', + 'KU', + 'kua', + 'KUL', + 'kur', + 'KUW', + 'kv', + 'kV', + 'KV', + 'KV', + 'KVM', + 'KVT', + 'kw', + 'kW', + 'KW', + 'KWB', + 'KWD', + 'KWT', + 'KX', + 'ky', + 'KY', + 'KYD', + 'KZ', + 'KZN', + 'KZT', + 'L', + 'la', + 'La', + 'LA', + 'LAAS', + 'LAB', + 'LabVIEW', + 'LACMA', + 'LAD', + 'LADW', + 'LAESI', + 'LAK', + 'LAMP', + 'LAN', + 'LANL', + 'lao', + 'LAO', + 'LAPD', + 'LAR', + 'LARC', + 'LARP', + 'LAS', + 'Laser', + 'lat', + 'LAT', + 'LATA', + 'LATCH', + 'lav', + 'LAV', + 'LAW', + 'LAX', + 'lb', + 'LB', + 'LBH', + 'LBJ', + 'LBN', + 'LBNL', + 'LBP', + 'LBR', + 'LBS', + 'lbs', + 'LBT', + 'LBW', + 'LBY', + 'LC', + 'LCA', + 'LCC', + 'LCCP', + 'LCD', + 'LCE', + 'LCM', + 'LCol', + 'LCS', + 'LD', + 'LDA', + 'LDAP', + 'LDC', + 'LDL', + 'LDMOS', + 'LDS', + 'LDV', + 'LDW', + 'LE', + 'LEC', + 'LED', + 'Legs', + 'LEGAD', + 'LEMOSS', + 'LEN', + 'LEO', + 'LEP', + 'LER', + 'LEU', + 'LF', + 'LFO', + 'LFTR', + 'lg', + 'LG', + 'LGB', + 'LGBT', + 'LGLO', + 'LGM', + 'LGS', + 'LH', + 'LHC', + 'LHD', + 'LHS', + 'li', + 'Li', + 'LI', + 'LIBOR', + 'LIBS', + 'LIE', + 'LIFO', + 'LIGO', + 'lim', + 'lin', + 'LINEAR', + 'LISA', + 'lit', + 'LITAS', + 'LIVE', + 'LJ', + 'LJJ', + 'LK', + 'LKA', + 'LKL', + 'LKR', + 'LKR4', + 'LL', + 'LLAD', + 'LLB', + 'LLC', + 'LLGC', + 'LLLTV', + 'LLNL', + 'LLP', + 'LLTI', + 'LLTR', + 'LMAO', + 'LMFAO', + 'LMB', + 'LMN', + 'LMS', + 'LMS', + 'LMV', + 'ln', + 'LNB', + 'LNER', + 'LNG', + 'LNO', + 'LNR', + 'lo', + 'LO', + 'LOA', + 'LOC', + 'LOD', + 'LoD', + 'LOFAR', + 'LOGPAC', + 'LOL', + 'LOMEZ', + 'LONEOS', + 'LOOGY', + 'loq', + 'LORTID', + 'LOS', + 'LOSAT', + 'LOTA', + 'LOTIS', + 'LOTS', + 'LP', + 'LPC', + 'LPD', + 'LPG', + 'LPI', + 'LPS', + 'LQ', + 'Lr', + 'LR', + 'LRAD', + 'LRC', + 'LRD', + 'LRF', + 'LRGW', + 'LRICBM', + 'LRIP', + 'LRP', + 'LRPE', + 'LRS', + 'LRU', + 'LS', + 'LSAT', + 'LSD', + 'LSE', + 'LSI', + 'LSIF', + 'LSL', + 'LSND', + 'LSO', + 'LSST', + 'LST', + 'LSVW', + 'lt', + 'Lt', + 'LtC', + 'LT', + 'LTC', + 'Ltd', + 'LTDP', + 'LTG', + 'LTI', + 'LTM', + 'LTL', + 'LTP', + 'LTU', + 'ltz', + 'lu', + 'Lu', + 'LU', + 'LU', + 'lub', + 'LUCA', + 'lug', + 'LUG', + 'LULU', + 'LUT', + 'LUVW', + 'LUX', + 'lv', + 'LV', + 'LVA', + 'LVAD', + 'LVEF', + 'LVL', + 'Lw', + 'LW', + 'LWR', + 'LWRC', + 'LWS', + 'LX', + 'LX', + 'LY', + 'LYAO', + 'LYD', + 'LYLAS', + 'LZ', + 'm', + 'M', + 'mA', + 'Ma', + 'MA', + 'MAA', + 'MAAC', + 'MAC', + 'MACA', + 'MACE', + 'MACHO', + 'MAD', + 'MADD', + 'MAF', + 'MAFF', + 'MAGIC', + 'MAGNETAR', + 'MAGTF', + 'mah', + 'Mahasz', + 'mal', + 'MALD', + 'MANPADS', + 'MANPRINT', + 'MAO', + 'MAOI', + 'MAPS', + 'MAQS', + 'mar', + 'MAR', + 'MarBEF', + 'MARC', + 'MARCENT', + 'MARFORCC', + 'MARRS', + 'MAS', + 'MASAS', + 'Maser', + 'MASH', + 'MASINT', + 'MATREX', + 'MATS', + 'MAVS', + 'MAW', + 'MAX', + 'MB', + 'MBA', + 'MBI', + 'MBBL', + 'MBCC', + 'MBCS', + 'MBE', + 'MBIDST', + 'MBM', + 'mbsf', + 'MBT', + 'MBTA', + 'MBTI', + 'mC', + 'MC', + 'MCA', + 'MCC', + 'MCD', + 'MCDL', + 'MCFT', + 'MCG', + 'MCI', + 'MCIS', + 'MCM', + 'MCMV', + 'MCNC', + 'MCO', + 'MCP', + 'MCR', + 'MCSF', + 'MCT', + 'MCTIS', + 'MCU', + 'Md', + 'MD', + 'MDA', + 'MDF', + 'MDG', + 'MDL', + 'MDM', + 'MDMA', + 'MDNA', + 'MDO', + 'MDP', + 'MDT', + 'MDV', + 'MDW', + 'Me', + 'ME', + 'MEA', + 'MEAC', + 'MEC', + 'MECC', + 'MECCA', + 'MECO', + 'MEDEVAC', + 'MEDLI', + 'MEDS', + 'MEF', + 'MEG', + 'MEI', + 'MEK', + 'MEL', + 'MELT', + 'MEM', + 'MEMS', + 'MEO', + 'MEP', + 'MER', + 'MERL', + 'MES', + 'MESSENGER', + 'MET', + 'METAR', + 'METL', + 'METOC', + 'MEU', + 'MeV', + 'MEV', + 'MEX', + 'MEZ', + 'MES', + 'mF', + 'MF', + 'MFSB', + 'mg', + 'Mg', + 'MG', + 'MGA', + 'MGB', + 'MGM', + 'MGPAM', + 'MGS', + 'mh', + 'mH', + 'MH', + 'MHC', + 'MHHW', + 'MHL', + 'MHLW', + 'MHRA', + 'MHW', + 'MHWN', + 'MHWS', + 'MHz', + 'mi', + 'Mi', + 'MI', + 'MIA', + 'MIAA', + 'MIB', + 'MIC', + 'MICLIC', + 'MICOM', + 'MICV', + 'MID', + 'MIDI', + 'MIDAS', + 'MIDS', + 'MIFC', + 'MIFG', + 'MIFPC', + 'MIKE', + 'MIL', + 'MILAN', + 'MILC', + 'MILES', + 'MILF', + 'MILSATCOM', + 'MILSET', + 'MILSTRIP', + 'MIMAROPA', + 'MIMOSA', + 'MIMPT', + 'MIP', + 'MIPC', + 'MIRA', + 'MIRV', + 'MIS', + 'MISREP', + 'MISSI', + 'MIT', + 'MITL', + 'MITS', + 'mJ', + 'MJ', + 'MJD', + 'MJP', + 'mk', + 'mK', + 'MK', + 'mkd', + 'MKD', + 'MKG', + 'MKO', + 'MKS', + 'MKSA', + 'MKT', + 'ml', + 'mL', + 'ML', + 'MLA', + 'MLB', + 'MLC', + 'MLE', + 'mlg', + 'MLHW', + 'MLI', + 'MLK', + 'MLL', + 'MLLW', + 'MLP', + 'MLRS', + 'MLS', + 'MLSE', + 'MLSR', + 'mlt', + 'MLT', + 'MLVW', + 'MLW', + 'MLWN', + 'MLWS', + 'mm', + 'Mm', + 'MM', + 'MMA', + 'MMC', + 'MMCM', + 'MMCS', + 'MMEV', + 'MMI', + 'MMI', + 'MMIC', + 'MMIC', + 'MMI', + 'MMK', + 'MMO', + 'MMOR', + 'MMORPG', + 'MMPR', + 'MMR', + 'MMS', + 'MMU', + 'MMW', + 'mn', + 'mN', + 'Mn', + 'MN', + 'MNB', + 'MND', + 'MNE', + 'MNG', + 'MNMF', + 'MNP', + 'MNS', + 'MNT', + 'mo', + 'Mo', + 'MO', + 'MOA', + 'MOAB', + 'MOB', + 'MOC', + 'MOCAS', + 'MoD', + 'MOD', + 'MoDAF', + 'modem', + 'ModSAF', + 'Moe', + 'MOE', + 'MOF', + 'MOFA', + 'MOH', + 'MOID', + 'MOJWA', + 'mol', + 'MOLO', + 'MoM', + 'MOM', + 'MOM', + 'MoMA', + 'mon', + 'MOND', + 'MOO', + 'MOOTW', + 'MOP', + 'MOPE', + 'MOPP', + 'MORP', + 'MORS', + 'MOS', + 'MOSFET', + 'MOSS', + 'MOT', + 'MOTA', + 'MOTESS', + 'MOTO', + 'MOU', + 'MOUT', + 'MOVES', + 'MOW', + 'MOZ', + 'MP', + 'mPa', + 'MPa', + 'MPA', + 'MPAA', + 'MPC', + 'MPC', + 'MPEC', + 'MPEG', + 'MPEV', + 'MPF', + 'MPFC', + 'MPG', + 'MPMC', + 'MPSI', + 'MPSoC', + 'MPSRON', + 'MQ', + 'mr', + 'MR', + 'MRAV', + 'MRBM', + 'MRC', + 'MRD', + 'MRE', + 'MRGO', + 'MRGW', + 'mri', + 'MRI', + 'MRL', + 'MRO', + 'MRPharmS', + 'MRR', + 'MRS', + 'MRSA', + 'MRT', + 'MRTFB', + 'ms', + 'mS', + 'Ms', + 'MS', + 'msa', + 'MSB', + 'MSD', + 'MSDB', + 'MSDE', + 'MSDL', + 'MSE', + 'MSEC', + 'MSG', + 'MSH', + 'MSI', + 'MSIE', + 'MSL', + 'MSLP', + 'MSM', + 'MSN', + 'MSNBC', + 'MSO', + 'MSPA', + 'MSPB', + 'MSR', + 'MSSA', + 'MST', + 'MSU', + 'mt', + 'mT', + 'Mt', + 'MT', + 'MTA', + 'MTBF', + 'MtF', + 'MTF', + 'MTG', + 'MTL', + 'MTOE', + 'MTP', + 'MTQ', + 'MTR', + 'MTS', + 'MTT', + 'MTU', + 'MTV', + 'MTVL', + 'MTWS', + 'MU', + 'MUA', + 'MUR', + 'MUS', + 'mV', + 'MV', + 'MVC', + 'MVFC', + 'MVNO', + 'MVR', + 'mW', + 'MW', + 'MWC', + 'MWESH', + 'MWI', + 'MWK', + 'MWOA', + 'MWR', + 'MWTB', + 'MX', + 'MXN', + 'my', + 'MY', + 'mya', + 'MYOB', + 'MYR', + 'MYS', + 'MYT', + 'MZ', + 'MZN', + 'n', + 'N', + 'N3F', + 'na', + 'nA', + 'Na', + 'NA', + 'NAA', + 'NAAD', + 'NAAFI', + 'NAAK', + 'NAACP', + 'NAAQS', + 'NAAWS', + 'NABCI', + 'NAC', + 'NACA', + 'NACC', + 'NACISA', + 'NACMA', + 'NACRA', + 'NAD', + 'NAEC', + 'NAF', + 'NAFLD', + 'NAFO', + 'NAFERA', + 'NAFTA', + 'NAGPRA', + 'NAI', + 'NAICS', + 'NAIL', + 'NAILS', + 'NAIRU', + 'NAK', + 'NAM', + 'NAMBLA', + 'NAMRL', + 'NaN', + 'NAO', + 'NAPDD', + 'NAPS', + 'NARA', + 'NARAL', + 'NARDAC', + 'NARDIC', + 'NARPV', + 'NAS', + 'NASA', + 'NASCAR', + 'NASDAQ', + 'NASED', + 'NASH', + 'NASP', + 'NASS', + 'NatGeo', + 'NATO', + 'nau', + 'NaUKMA', + 'nav', + 'NAV', + 'NAVCAMS', + 'NAVCC', + 'NAVCENT', + 'NAVDAC', + 'NAVEUR', + 'NAVFE', + 'NAVFOR', + 'NAVMACS', + 'NAVNET', + 'NAVS', + 'NAVSAT', + 'NAVSPACECOM', + 'NAVSPASUR', + 'NAVSTAR', + 'NAVTACNET', + 'NAWC', + 'NAWMP', + 'nb', + 'Nb', + 'NB', + 'NBA', + 'NBACC', + 'NBC', + 'NBCRS', + 'NBIC', + 'nbl', + 'NBL', + 'NBT', + 'nC', + 'NC', + 'NCA', + 'NCAA', + 'NCAR', + 'NCB', + 'NCCAM', + 'NCCUSL', + 'NCDDR', + 'NCES', + 'NCI', + 'NCIS', + 'NCL', + 'NCLR', + 'NCMO', + 'NCND', + 'NCO', + 'NCP', + 'NCR', + 'NCTR', + 'NCW', + 'nd', + 'Nd', + 'ND', + 'NDAA', + 'NDBP', + 'NDCC', + 'nde', + 'NDH', + 'ndo', + 'NDP', + 'NDS', + 'NDU', + 'ne', + 'Ne', + 'NE', + 'NEA', + 'NEAT', + 'NEC', + 'NED', + 'NECTEC', + 'NEI', + 'NEMS', + 'NEO', + 'nep', + 'NEPA', + 'NER', + 'NERICA', + 'NESDIS', + 'NESP', + 'NET', + 'NETCU', + 'NETL', + 'NETWARS', + 'NEWS', + 'NEX', + 'nF', + 'NF', + 'NFA', + 'NFC', + 'NFER', + 'NFG', + 'NFHS', + 'NFI', + 'NFK', + 'NFL', + 'Nfld', + 'NFPA', + 'NFT', + 'ng', + 'NG', + 'NGA', + 'NGH', + 'NGATS', + 'NGIC', + 'NGN', + 'NGO', + 'NGR', + 'NGS', + 'nH', + 'NH', + 'NHANES', + 'NHB', + 'NHB', + 'NHL', + 'NHO', + 'NHRA', + 'NHS', + 'Ni', + 'NI', + 'NIAAA', + 'NIAID', + 'NIAG', + 'NIAMD', + 'NIB', + 'NIBP', + 'NIC', + 'NicA', + 'NICAP', + 'NICE', + 'NICHD', + 'NICP', + 'NIDDK', + 'NIDRR', + 'NIEHS', + 'NIG', + 'NIH', + 'NIIN', + 'NIMA', + 'NIMBY', + 'NIMG', + 'NiMH', + 'NIMH', + 'NIN', + 'NIO', + 'NIPR', + 'NIR', + 'NISAC', + 'NISCAP', + 'NISER', + 'NIST', + 'NIT', + 'NITFS', + 'NIU', + 'nJ', + 'NJ', + 'NJHS', + 'NJPW', + 'nK', + 'NKA', + 'NKVD', + 'nl', + 'nL', + 'NL', + 'NLB', + 'nld', + 'NLD', + 'NLF', + 'NLL', + 'NLP', + 'NLW', + 'nm', + 'NM', + 'NMCI', + 'NMCS', + 'NMD', + 'NMN', + 'NMOS', + 'NMSG', + 'NMR', + 'NMHNFG', + 'nn', + 'nN', + 'NNE', + 'nno', + 'NNSA', + 'NNW', + 'no', + 'No', + 'NO', + 'NOAA', + 'NOAF', + 'NOAO', + 'nob', + 'NOC', + 'NODEF', + 'NOK', + 'NOLA', + 'NOGI', + 'NOLF', + 'NOM', + 'NOME', + 'NoMBO', + 'NOME', + 'NOPA', + 'nor', + 'NOR', + 'NORAD', + 'NORCECA', + 'NORELPREF', + 'NORML', + 'NORTHAG', + 'NOS', + 'NOSA', + 'NOTAFLOF', + 'NOTAM', + 'NOW', + 'Np', + 'NP', + 'NP', + 'NPB', + 'NPC', + 'NPCS', + 'NPD', + 'NPDI', + 'NPG', + 'NPH', + 'NPL', + 'NPOV', + 'NPR', + 'NQ', + 'NQOCD', + 'NPO', + 'nr', + 'NR', + 'NRA', + 'NRAO', + 'NRC', + 'NRDC', + 'NREL', + 'NRK', + 'NRMP', + 'NRO', + 'NRT', + 'NRTA', + 'NRU', + 'ns', + 'nS', + 'NS', + 'NSA', + 'NSABB', + 'NSAID', + 'NSB', + 'NSC', + 'NSDAP', + 'NSDL', + 'NSG', + 'NSN', + 'NSNA', + 'NSO', + 'NSS', + 'NST', + 'NSTAC', + 'NSV', + 'NSW', + 'nT', + 'NT', + 'NTC', + 'NTDS', + 'NTP', + 'NTR', + 'NTRA', + 'NTSB', + 'NTSC', + 'NTV', + 'NTY', + 'NTZ', + 'NU', + 'NUI', + 'NUL', + 'NUTS', + 'NUWC', + 'nv', + 'nV', + 'NV', + 'NVEOL', + 'NVESD', + 'NVG', + 'NVL', + 'nW', + 'NW', + 'NW', + 'NWA', + 'NWF', + 'NWFZ', + 'NWHN', + 'nWo', + 'NWOAHM', + 'NWOBHM', + 'NWRA', + 'NWSL', + 'NWT', + 'NX', + 'NXA', + 'NXD', + 'NXE', + 'NXG', + 'NXL', + 'NXN', + 'NXP', + 'ny', + 'NY', + 'nya', + 'NYHA', + 'NYO', + 'NYRA', + 'NYSE', + 'NZ', + 'NZC', + 'NZD', + 'NZL', + 'NzL', + 'NZOSS', + 'NZRU', + 'O', + 'OA', + 'OAC', + 'OAP', + 'OAS', + 'OARSI', + 'OASIS', + 'OASIS', + 'OASIS', + 'OASP', + 'OAU', + 'ob', + 'OB', + 'OBE', + 'OBJ', + 'OBS', + 'OBSS', + 'OBX', + 'oc', + 'OC', + 'OCA', + 'OCD', + 'OCFS', + 'OCFS2', + 'oci', + 'OCIDS', + 'OCMCC', + 'OCN', + 'OCO', + 'OCOKA', + 'OCR', + 'OCS', + 'OD', + 'ODAS', + 'ODB', + 'ODBC', + 'ODI', + 'OEA', + 'OED', + 'OECD', + 'OEG', + 'OEIS', + 'OEM', + 'OFA', + 'OFDA', + 'OGC', + 'OGD', + 'OGO', + 'OGX', + 'OH', + 'OHC', + 'OHHLA', + 'OHIP', + 'OHMS', + 'OHP', + 'OHSS', + 'OIC', + 'OIE', + 'OIF', + 'OIOC', + 'oj', + 'OJ', + 'oji', + 'OJT', + 'OK', + 'OKC', + 'OLED', + 'OLS', + 'om', + 'OM', + 'OMA', + 'OMB', + 'OMD', + 'OME', + 'OMFG', + 'OMG', + 'OMGW', + 'OMN', + 'OMNCS', + 'OMP', + 'OMR', + 'OMS', + 'OMT', + 'OMW', + 'ON', + 'Onna', + 'ONC', + 'ONCE', + 'ONG', + 'ONH', + 'ONI', + 'ONS', + 'OO', + 'OOO', + 'OOAD', + 'OOBE', + 'OOCL', + 'OODA', + 'OOMF', + 'OOP', + 'OOS', + 'OOTW', + 'OP', + 'OPA', + 'OPAC', + 'OPCON', + 'OPCW', + 'OPEC', + 'OPFOR', + 'OPI', + 'OPINTEL', + 'OPLAN', + 'OPORD', + 'OPP', + 'OPS', + 'OPSEC', + 'OPSI', + 'OPTAR', + 'OPV', + 'OQ', + 'or', + 'OR', + 'ORAU', + 'ORB', + 'ORBAT', + 'ORD', + 'ORD', + 'ORF', + 'ori', + 'ORISE', + 'orm', + 'ORNL', + 'ORR', + 'ORSA', + 'OS', + 'os', + 'Os', + 'OSA', + 'OSCE', + 'OSD', + 'OSDL', + 'OSE', + 'OSERS', + 'OSETI', + 'OSHA', + 'OSI', + 'OSIA', + 'OSL', + 'OSIRIS', + 'OSM', + 'OSOTPBOIAS', + 'OSPA', + 'OSPA', + 'OSRH', + 'oss', + 'OSS', + 'OSU', + 'OTA', + 'OTAC', + 'OTB', + 'OTC', + 'OTD', + 'OTEA', + 'OTI', + 'OTO', + 'OTOH', + 'OTS', + 'OTSBH', + 'OTW', + 'OTYBL', + 'OTYRL', + 'OU', + 'OVC', + 'OVV', + 'OW', + 'OWN', + 'OX', + 'OXC', + 'OXF', + 'OXM', + 'OXR', + 'OXT', + 'Oy', + 'Oyj', + 'OZ', + 'p', + 'P', + 'P2P', + 'pa', + 'pA', + 'Pa', + 'PA', + 'PAA', + 'PAB', + 'PABX', + 'PAC', + 'PACOM', + 'PADD', + 'PADI', + 'PAEK', + 'PAFC', + 'PAH', + 'PAHO', + 'PAI', + 'PAK', + 'Pakistan', + 'PAL', + 'PAM', + 'Pamida', + 'PAMELA', + 'pan', + 'PAN', + 'PANS', + 'PAO', + 'PAOK', + 'PAPI', + 'PAR', + 'PARC', + 'PAROS', + 'PARP', + 'PARWIG', + 'PAS', + 'PASA', + 'PASGT', + 'PASR', + 'PATH', + 'PAWS', + 'PAYD', + 'PAYE', + 'Pb', + 'PB', + 'PBA', + 'PBB', + 'PBCK', + 'PBD', + 'PBDE', + 'PBI', + 'PBN', + 'PBOH', + 'PBR', + 'PBS', + 'PBRT', + 'PBT', + 'PBX', + 'pC', + 'PC', + 'PCAS', + 'PCB', + 'PCC', + 'PCCB', + 'PCCIP', + 'PCE', + 'PCI', + 'PCIe', + 'PCL', + 'PCMCIA', + 'PCN', + 'PCP', + 'PCPI', + 'PCR', + 'PCSSD', + 'PCT', + 'PCTE', + 'PCZ', + 'Pd', + 'PD', + 'PD', + 'PDA', + 'PDD', + 'PDF', + 'PDG', + 'PDL', + 'PDN', + 'PDP', + 'PDQ', + 'PDRY', + 'PDT', + 'PDU', + 'PE', + 'PE', + 'PEBCAC', + 'PEBKAC', + 'PEEK', + 'PEET', + 'PEGIDA', + 'PEI', + 'PEK', + 'PEN', + 'PEO', + 'PEPFAR', + 'perl', + 'PES', + 'PEST', + 'PET', + 'PETA', + 'PETE', + 'PETP', + 'pF', + 'PF', + 'PFD', + 'PFE', + 'PFLAG', + 'PFIY', + 'PFO', + 'PfP', + 'pg', + 'Pg', + 'PG', + 'PGCE', + 'PGD', + 'PGENI', + 'PGH', + 'PGK', + 'PGM', + 'PGP', + 'pH', + 'PH', + 'PHA', + 'PHL', + 'PHQ', + 'PHP', + 'pi', + 'Pi', + 'PI', + 'PIB', + 'PIC', + 'PICNIC', + 'PICS', + 'PID', + 'PIF', + 'PIG', + 'PIM', + 'PIN', + 'PINF', + 'PIPA', + 'PIR', + 'PIRA', + 'PIREPS', + 'PITA', + 'pJ', + 'PJ', + 'PJHQ', + 'PJs', + 'K', + 'PK', + 'PKA', + 'PKE', + 'PKI', + 'PKM', + 'PKMS', + 'PKMT', + 'PKR', + 'PKT', + 'PKTM', + 'PKU', + 'pl', + 'pL', + 'PL', + 'PLAF', + 'PLAID', + 'PLB', + 'PLC', + 'PLCC', + 'PLD', + 'PLEBS', + 'PLF', + 'PLGR', + 'pli', + 'PLI', + 'PLIF', + 'PLK', + 'PLKK', + 'PLL', + 'PLM', + 'PLMBA', + 'Pln', + 'PLN', + 'PLO', + 'PLOKTA', + 'PLP', + 'PLS', + 'PLUTO', + 'PLW', + 'pm', + 'Pm', + 'PM', + 'PMC', + 'PMCS', + 'PME', + 'PMI', + 'PMID', + 'PMMA', + 'PMOS', + 'PMS', + 'PMSC', + 'PMSL', + 'PMT', + 'pN', + 'PN', + 'PNAC', + 'PNC', + 'PNF', + 'PNG', + 'PNP', + 'PNS', + 'PNVS', + 'PNH', + 'Po', + 'PO', + 'PO', + 'PO', + 'POCD', + 'pod', + 'POD', + 'POE', + 'POETS', + 'POFF', + 'POG', + 'POGO', + 'POI', + 'POK', + 'pol', + 'POL', + 'POLAD', + 'POM', + 'POMDP', + 'POP', + 'por', + 'POR', + 'POS', + 'POSH', + 'POSSLQ', + 'POT', + 'POTS', + 'POV', + 'POW', + 'pp', + 'PP', + 'PPA', + 'PPASC', + 'PPASSCCATAG', + 'PPCLI', + 'PPG', + 'PPL', + 'PPLI', + 'ppm', + 'PPM', + 'PPM', + 'PPO', + 'PPP', + 'PPQ', + 'PPQN', + 'PPQT', + 'PPS', + 'PPV', + 'PQ', + 'Pr', + 'Pr', + 'PR', + 'Prb', + 'PRBO', + 'PRC', + 'PRD', + 'PRFG', + 'PRI', + 'PRK', + 'PRM', + 'PRN', + 'PRT', + 'PRY', + 'ps', + 'pS', + 'Ps', + 'PS', + 'PooS', + 'PSA', + 'PSC', + 'PSE', + 'PSG', + 'PSI', + 'PSI', + 'PSO', + 'PSS', + 'PST', + 'PSU', + 'PSV', + 'PsyOp', + 'pt', + 'pT', + 'Pt', + 'PT', + 'PTA', + 'PTAL', + 'PTAN', + 'PTES', + 'PTFE', + 'PTI', + 'PTL', + 'PTO', + 'PTSD', + 'PTUI', + 'Pu', + 'PU', + 'Pub', + 'PUG', + 'PUN', + 'PUP', + 'PUR', + 'pus', + 'PUS', + 'pV', + 'PV', + 'PVC', + 'PvE', + 'PvP', + 'PVR', + 'PvZ', + 'pW', + 'PW', + 'petawatt', + 'PWA', + 'PWBA', + 'PwC', + 'PWC', + 'PWL', + 'PWLC', + 'PWP', + 'PWR', + 'PWV', + 'PX', + 'PY', + 'PYF', + 'PYG', + 'PYMWYMI', + 'PZ', + 'Q1', + 'Q2', + 'Q3', + 'Q4', + 'QA', + 'Qantas', + 'QAP', + 'QAR', + 'QAT', + 'QB', + 'QC', + 'QCA', + 'QCD', + 'QD', + 'QDOS', + 'QDR', + 'QDRO', + 'QED', + 'QEH', + 'QFE', + 'QID', + 'QIP', + 'QK', + 'QLD', + 'QM', + 'QNH', + 'QPR', + 'QPS', + 'QR', + 'QS', + 'QSCAT', + 'QSL', + 'QSO', + 'QSTAG', + 'QT', + 'qu', + 'Quango', + 'Quasar', + 'QUB', + 'que', + 'QUIET', + 'qv', + 'QVC', + 'QWG', + 'QWGAOR', + 'QZ', + 'R', + 'R2I', + 'R3P', + 'Ra', + 'RA', + 'RA', + 'RAA', + 'RAAF', + 'Rabobank', + 'RAC', + 'RACK', + 'Racon', + 'RADA', + 'Radar', + 'RADB', + 'RADb', + 'RadB', + 'RADC', + 'RAE', + 'RAF', + 'RAG', + 'RAGBRAI', + 'RAM', + 'Ramark', + 'RAOC', + 'RAP', + 'RARDE', + 'RARE', + 'RASC', + 'RATELO', + 'Rb', + 'RB', + 'RBAR', + 'RBCI', + 'RBI', + 'RBOC', + 'RC', + 'RCA', + 'RCAF', + 'RCAR', + 'RCC', + 'RCCO', + 'RCHA', + 'RCMP', + 'RCRA', + 'RCS', + 'RCT', + 'RCZ', + 'RD', + 'RDA', + 'RDD', + 'RDDL', + 'RDECOM', + 'RDM', + 'RDO', + 'RDS', + 'RDT', + 'RDV', + 'Re', + 'RE', + 'REACH', + 'REACT', + 'REAM', + 'RECCE', + 'REEF', + 'REM', + 'REME', + 'RERC', + 'RES', + 'RESNA', + 'REU', + 'Rf', + 'RF', + 'RFA', + 'RFC', + 'RFD', + 'RFE', + 'RFEF', + 'RFF', + 'RFI', + 'RFID', + 'RFL', + 'RFL', + 'RFP', + 'RFQ', + 'RFT', + 'RFU', + 'Rg', + 'RG', + 'RGB', + 'RGBI', + 'Rh', + 'RH', + 'RHIC', + 'RHLI', + 'RHO', + 'RHS', + 'RHU', + 'RHWR', + 'RI', + 'RI', + 'RIAA', + 'RIBA', + 'RIF', + 'RIFA', + 'RILA', + 'RIM', + 'RIMVS', + 'RINO', + 'RIP', + 'RIS', + 'RISC', + 'RISD', + 'RISTA', + 'RWH', + 'RJD', + 'RKBA', + 'RKI', + 'RKO', + 'RL', + 'RLP', + 'RLS', + 'RLY', + 'RM', + 'rm', + 'RMA', + 'RMC', + 'RMC', + 'RMDA', + 'RMP', + 'RMS', + 'rn', + 'Rn', + 'RN', + 'RN', + 'RNA', + 'RNAD', + 'RNPS', + 'RNZ', + 'RNZI', + 'ro', + 'RO', + 'ROA', + 'ROC', + 'ROD', + 'ROE', + 'roh', + 'ROH', + 'ROI', + 'ROFLOL', + 'ROK', + 'ROL', + 'ROM', + 'ron', + 'RON', + 'ROP', + 'ROPE', + 'ROS', + 'ROTC', + 'ROU', + 'ROV', + 'ROW', + 'ROWPU', + 'ROZ', + 'RP', + 'RPA', + 'RPD', + 'RPF', + 'RPG', + 'RPL', + 'rpm', + 'RPM', + 'RPN', + 'RPO', + 'rps', + 'RPV', + 'RQ', + 'RRB', + 'RRN', + 'RRP', + 'RRS', + 'RRSP', + 'RS', + 'RSA', + 'RSD', + 'RSEQ', + 'RSI', + 'RSJ', + 'RSN', + 'RSPB', + 'RSPCA', + 'RSR', + 'RSRE', + 'RSRI', + 'RSS', + 'RSTA', + 'RSVP', + 'RT', + 'RTA', + 'RTBF', + 'RTCA', + 'RTF', + 'RTFM', + 'RTG', + 'RTGS', + 'RTI', + 'RTL', + 'RTLS', + 'RTP', + 'RTS', + 'RTT', + 'RTV', + 'ru', + 'Ru', + 'RU', + 'RUB', + 'RUC', + 'run', + 'rus', + 'RUS', + 'RUSI', + 'RV', + 'RVD', + 'rw', + 'RW', + 'RWA', + 'RWADA', + 'RWF', + 'RWR', + 'RWS', + 'Rx', + 'RX', + 'South', + 'SA', + 'SAA', + 'SAC', + 'SAD', + 'SADL', + 'SAE', + 'SALT', + 'SAM', + 'Sambo', + 'san', + 'SAR', + 'SARA', + 'SART', + 'SAS', + 'SAT', + 'SAW', + 'SB', + 'SBA', + 'SBC', + 'SBD', + 'SBNO', + 'SBP', + 'SBS', + 'SBW', + 'SC', + 'SCA', + 'SCO', + 'SCS', + 'SCSI', + 'SD', + 'SDC', + 'SDHC', + 'SDXC', + 'SE', + 'SEC', + 'SEM', + 'SEN', + 'SEP', + 'SEU', + 'SF', + 'SFA', + 'SFE', + 'SG', + 'SGC', + 'SGS', + 'SH', + 'SHAC', + 'SHO', + 'SI', + 'SIG', + 'SIIT', + 'SIMPLE', + 'SIP', + 'Siri', + 'SIRI', + 'SISO', + 'SJ', + 'SK', + 'SL', + 'SLA', + 'SLOC', + 'SLP', + 'SLT', + 'SM', + 'SME', + 'SMG', + 'SMX', + 'SMZ', + 'SN', + 'SNL', + 'SNR', + 'SO', + 'SOA', + 'SOAP', + 'SOB', + 'SoCon', + 'SOD', + 'SOE', + 'SOF', + 'SOGAT', + 'SOM', + 'SOP', + 'SOR', + 'SOS', + 'Sp', + 'SP', + 'SPD', + 'SPL', + 'SR', + 'SRAM', + 'SRD', + 'SRG', + 'SRL', + 'SS', + 'SSA', + 'SSC', + 'SSD', + 'SSDD', + 'SSE', + 'SSN', + 'SSR', + 'SSRI', + 'SSRN', + 'SSS', + 'SST', + 'ST', + 'STA', + 'STAR', + 'STAT', + 'STB', + 'STD', + 'STFU', + 'STI', + 'STP', + 'STRICOM', + 'SU', + 'SUI', + 'SV', + 'SVD', + 'SW', + 'SWA', + 'SWF', + 'SWG', + 'SWLABR', + 'SWP', + 'SXM', + 'SZ', + 'T', + 'ta', + 'Ta', + 'TA', + 'TAA', + 'TAACOM', + 'TAADCOORD', + 'TAAR', + 'TAC', + 'TACAMO', + 'TACAN', + 'TACC', + 'TACCO', + 'TACCSF', + 'TACDAR', + 'TACP', + 'TACSOP', + 'TADIL', + 'TADL', + 'TADSS', + 'tah', + 'TAI', + 'TAJ', + 'TAJT', + 'TALES', + 'tam', + 'TAN', + 'TANJ', + 'TANP', + 'TANSTAAFL', + 'TAPPS', + 'TARDIS', + 'TARP', + 'TAROT', + 'TAS', + 'TASER', + 'TASM', + 'tat', + 'TAT', + 'TATP', + 'TATT', + 'Tb', + 'TB', + 'TBA', + 'TBBPA', + 'TBC', + 'TBD', + 'TBD', + 'TBF', + 'TBGRI', + 'TBH', + 'TBL', + 'TBM', + 'TBS', + 'TBT', + 'Tc', + 'TC', + 'TCA', + 'TCAS', + 'TCAA', + 'TCCCS', + 'TCD', + 'TCG', + 'TCHD', + 'TCL', + 'TCM', + 'TCP', + 'TCU', + 'TCWMS', + 'TCW', + 'TD', + 'TDA', + 'TDL', + 'TDM', + 'TDMA', + 'TDP', + 'TDRS', + 'te', + 'Te', + 'TE', + 'TEAL', + 'Team', + 'TEAMS', + 'TED', + 'TEETH', + 'tel', + 'TEL', + 'TELAR', + 'TEMO', + 'TENCAP', + 'TERS', + 'TES', + 'TEST', + 'TEU', + 'TeVeS', + 'TF', + 'TFMPP', + 'TFR', + 'TFT', + 'tg', + 'Tg', + 'TG', + 'TGA', + 'TGB', + 'TGIF', + 'tgk', + 'tgl', + 'TGO', + 'TGT', + 'TGV', + 'th', + 'Th', + 'TH', + 'tha', + 'THA', + 'THAAD', + 'THB', + 'THEGENTRY', + 'THEL', + 'THG', + 'ti', + 'Ti', + 'TI', + 'TIA', + 'TIA', + 'TIALD', + 'TIBS', + 'TIC', + 'TID', + 'TIGERS', + 'TIM', + 'TIMS', + 'TINC', + 'TINY', + 'TIP', + 'tir', + 'tits', + 'TJ', + 'TJD', + 'TJK', + 'TJS', + 'tk', + 'TK', + 'TKBL', + 'TKL', + 'TKM', + 'tl', + 'Tl', + 'TLPM', + 'TL', + 'TLATOJL', + 'TLA', + 'TLAM', + 'TLAR', + 'TLC', + 'TLD', + 'TLM', + 'TLS', + 'Tm', + 'Thulium', + 'TM', + 'TMA', + 'TMAP', + 'TMC', + 'TMD', + 'TMDK', + 'TMI', + 'TMJ', + 'TMM', + 'TMNT', + 'TMO', + 'TMP', + 'TMPG', + 'TMR', + 'TMS', + 'TMT', + 'TMTOWTDI', + 'TMZ', + 'tn', + 'TN', + 'TNA', + 'TND', + 'TNO', + 'TNT', + 'to', + 'TOA', + 'TOAD', + 'TOBS', + 'TOC', + 'TOG', + 'TOGS', + 'TOMS', + 'ton', + 'TON', + 'TOP', + 'TOS', + 'TOT', + 'TOW', + 'TP', + 'TPE', + 'TPI', + 'TPO', + 'TPS', + 'TPTB', + 'TQ', + 'tr', + 'TR', + 'TRA', + 'TRAC', + 'TRACON', + 'TRADOC', + 'TRAIN', + 'TRANSCOM', + 'TRAP', + 'TRASANA', + 'TRD', + 'TRIGAT', + 'TRL', + 'Trp', + 'TRP', + 'TRU', + 'TRUEX', + 'TRW', + 'TRY', + 'ts', + 'Ts', + 'TS', + 'TSA', + 'TSCA', + 'TSEC', + 'TSL', + 'tsn', + 'TSN', + 'tso', + 'TSO', + 'TSOP', + 'TSR', + 'TSSIA', + 'tt', + 'TT', + 'TTCT', + 'TTD', + 'TTO', + 'TTFN', + 'TTL', + 'TTP', + 'TTTEAF', + 'TTY', + 'TTYL', + 'TTZ', + 'TU', + 'TUC', + 'TUG', + 'TUI', + 'tuk', + 'TUN', + 'TUNS', + 'Tur', + 'TUR', + 'TUSK', + 'TUV', + 'Tv', + 'TV', + 'TVA', + 'TVG', + 'TVR', + 'TVS', + 'tw', + 'TW', + 'TWAIN', + 'TWD', + 'TWE', + 'twi', + 'TWI', + 'TWN', + 'TWP', + 'TWS', + 'TWIP', + 'TX', + 'TXV', + 'ty', + 'TYT', + 'TZ', + 'TZA', + 'TZC', + 'TZS', + 'U', + 'UA', + 'UAAP', + 'UAB', + 'UAE', + 'UAH', + 'UAR', + 'UAV', + 'UBC', + 'UBIGEO', + 'UBS', + 'UCBSA', + 'UCAV', + 'UCF', + 'UCI', + 'UCLA', + 'UCLES', + 'UCR', + 'UCS', + 'UCSB', + 'UCSD', + 'UDAS', + 'UDDI', + 'UDP', + 'UDT', + 'UDTS', + 'UE', + 'UEFA', + 'UEFI', + 'UESAC', + 'UFC', + 'UFO', + 'ug', + 'UG', + 'UGA', + 'UGV', + 'UGX', + 'UHF', + 'UHMW', + 'UHRB', + 'UIC', + 'UID', + 'uig', + 'UIL', + 'UIUC', + 'UJTL', + 'uk', + 'UK', + 'UKIP', + 'ukr', + 'UKR', + 'UL', + 'ULEB', + 'ULF', + 'UM', + 'UMBC', + 'UMC', + 'UMCP', + 'UMES', + 'UMI', + 'UMIST', + 'UMK', + 'UML', + 'UMTS', + 'UN', + 'UNABOM', + 'UNAIDS', + 'UNaM', + 'UNAM', + 'UNAMSIL', + 'UNCF', + 'UNCLOS', + 'UNCSGN', + 'UNCTAD', + 'UNDCP', + 'UNDE', + 'UNDP', + 'UNEP', + 'UNESCO', + 'UNFCCC', + 'UNFICYP', + 'UNGEGN', + 'UNHCR', + 'UNICEF', + 'UNICRI', + 'UNIDO', + 'UNIFEM', + 'UNIS', + 'UNLV', + 'UNMIK', + 'UNMOVIC', + 'UNOSOM', + 'UNPROFOR', + 'UNRISD', + 'UNSM', + 'UNTAC', + 'UNTAET', + 'UNU', + 'UP', + 'UPC', + 'UPdM', + 'UPDM', + 'UPF', + 'UPI', + 'UPN', + 'UPS', + 'UPTN', + 'UPU', + 'ur', + 'UR', + 'URBSFA', + 'urd', + 'URD', + 'URI', + 'URL', + 'URU', + 'URY', + 'US', + 'USA', + 'USAADAC', + 'USAARMC', + 'USAAVNC', + 'USAF', + 'USAFSPC', + 'USAIC', + 'USAID', + 'USAP', + 'USAPATRIOT', + 'USAREUR', + 'USB', + 'USC', + 'USCG', + 'USCINCSPACE', + 'USD', + 'USDA', + 'USDI', + 'USDOE', + 'USDRE', + 'USEC', + 'USFL', + 'USFWS', + 'USGS', + 'USIA', + 'USJFCOM', + 'USL', + 'USMC', + 'USMNT', + 'USN', + 'USNA', + 'USNB', + 'USNG', + 'USNH', + 'USNI', + 'USNO', + 'USNOFS', + 'USNP', + 'USNR', + 'USNS', + 'USNY', + 'USNZ', + 'USPS', + 'USPTO', + 'USR', + 'USS', + 'USSPACECOM', + 'USSR', + 'UST', + 'USTRANSCOM', + 'USWNT', + 'UT', + 'UTEP', + 'UTM', + 'UU', + 'UUA', + 'UUID', + 'UUV', + 'UV', + 'UVM', + 'UW', + 'UWO', + 'UWT', + 'UWTG', + 'UWW', + 'UXO', + 'UY', + 'UYU', + 'uz', + 'UZ', + 'uzb', + 'UZB', + 'UZS', + 'V', + 'VA', + 'VACIS', + 'VACIS', + 'VANOS', + 'VAO', + 'VASIS', + 'VAT', + 'VB', + 'VBA', + 'VC', + 'VCDS', + 'vCJD', + 'VCJCS', + 'VCNO', + 'VCR', + 'VCofS', + 'VCT', + 'VD', + 'VDFS', + 'VDMOS', + 'VDOT', + 'VDR', + 've', + 'VE', + 'VEB', + 'VEEGA', + 'ven', + 'VEN', + 'VERDI', + 'VERITAS', + 'VFA', + 'VfD', + 'VFL', + 'VFM', + 'VFMO', + 'VFR', + 'VFT', + 'VFW', + 'VG', + 'VGB', + 'VH1', + 'VHF', + 'vi', + 'VI', + 'VIA', + 'VIC', + 'VIDS', + 'vie', + 'VII', + 'VIII', + 'VIN', + 'VIP', + 'VIR', + 'VIS', + 'VISCII', + 'VIT', + 'VIVO', + 'VJ', + 'VK', + 'VLA', + 'VLAD', + 'VLBA', + 'VLF', + 'VLIW', + 'VLPFC', + 'VLPMC', + 'VLSF', + 'VLSI', + 'VLVS', + 'VM', + 'VMASC', + 'VMF', + 'VMF', + 'VMN', + 'VN', + 'VND', + 'VNIR', + 'VNM', + 'VNS', + 'VOA', + 'vo', + 'VOC', + 'VOIP', + 'vol', + 'VOLT', + 'Volvo', + 'VON', + 'VOR', + 'VOR', + 'VOX', + 'VP', + 'VPN', + 'VQ', + 'VR', + 'VRSG', + 'VS', + 'VSAT', + 'VSHORAD', + 'VSO', + 'VSOP', + 'VSR', + 'VT', + 'VTC', + 'VTEC', + 'VTOL', + 'VU', + 'VUB', + 'VUT', + 'VUV', + 'VV', + 'VVAF', + 'VW', + 'VZ', + 'vzw', + 'W', + 'W3C', + 'wa', + 'WA', + 'WAAC', + 'WAAF', + 'WAC', + 'WACC', + 'WACS', + 'WAD', + 'WAGGGS', + 'WAGs', + 'WAK', + 'WAL', + 'WAN', + 'WAP', + 'WAPIMA', + 'WAR', + 'WAsP', + 'WASP', + 'WAYP', + 'Wb', + 'WB', + 'WBA', + 'WBC', + 'WBE', + 'WBO', + 'WC', + 'WCBO', + 'WCBP', + 'WCL', + 'WCS', + 'WCW', + 'WDC', + 'WDWR', + 'WDIN', + 'WEBS', + 'WEC', + 'WEEE', + 'WEF', + 'WEM', + 'WEU', + 'WEZ', + 'WF', + 'WFC', + 'WFD', + 'WFF', + 'WFP', + 'WFZ', + 'WFI', + 'WG', + 'WH', + 'WHAM', + 'WHI', + 'WHIP', + 'WHO', + 'WHS', + 'WI', + 'WIA', + 'WIIFM', + 'WIMP', + 'WIPO', + 'WIPP', + 'WIYN', + 'WK', + 'WKYP', + 'WL', + 'WLB', + 'WLF', + 'wln', + 'WLOG', + 'WM', + 'WMANUS', + 'WMD', + 'WMI', + 'WMO', + 'WNA', + 'WNF', + 'WNL', + 'WNIT', + 'WNSD', + 'WNW', + 'wo', + 'WO', + 'WOF', + 'WOFT', + 'WOFTAM', + 'WOI', + 'wol', + 'WOM', + 'WOS', + 'WOSM', + 'WOT', + 'WP', + 'WPB', + 'WPE', + 'WPK', + 'WPT', + 'WR', + 'WRAC', + 'WRAIR', + 'WRBWWRM', + 'WRC', + 'WRC', + 'WRNS', + 'WRVS', + 'WRS', + 'WRT', + 'WRT', + 'WRT', + 'WRT', + 'WRT', + 'WRT', + 'WRT', + 'WRT', + 'WRT', + 'WRT', + 'WRT', + 'WS', + 'WSC', + 'WSDL', + 'WSJ', + 'WSL', + 'WSM', + 'WSMR', + 'WSROC', + 'WSSM', + 'WST', + 'WSTC', + 'WSW', + 'WTB', + 'WTB', + 'WTC', + 'WTF', + 'WTFH', + 'WTH', + 'WTO', + 'WTS', + 'WU', + 'WUU2', + 'WUBU2', + 'WV', + 'WVA', + 'WVIAC', + 'WVR', + 'WVSC', + 'WVU', + 'WVUIT', + 'WW', + 'WWE', + 'WWF', + 'WWI', + 'WWII', + 'WWIII', + 'WWJD', + 'WWS', + 'WWV', + 'WWW', + 'WWWF', + 'WXO', + 'WY', + 'WYSIWYG', + 'WYSIWYP', + 'WZ', + 'XAF', + 'XAG', + 'XAU', + 'XBA', + 'XBB', + 'XBML', + 'XC', + 'XCBL', + 'XCD', + 'XD', + 'Xe', + 'XF', + 'XFA', + 'XFC', + 'XFD', + 'Xfe', + 'XFF', + 'XFG', + 'XFH', + 'XFN', + 'XFR', + 'XFT', + 'XFW', + 'XG', + 'XGA', + 'XGE', + 'XGG', + 'XGN', + 'XGP', + 'XGR', + 'xh', + 'xho', + 'XHTML', + 'XHR', + 'XI', + 'XII', + 'XIII', + 'XIV', + 'XIX', + 'XJ', + 'XJL', + 'XJS', + 'XK', + 'XKA', + 'XKB', + 'XKE', + 'XKH', + 'XKR', + 'XKS', + 'XKY', + 'XL', + 'XML', + 'XMSF', + 'XN', + 'XNA', + 'XNC', + 'XNN', + 'XNS', + 'XNU', + 'XO', + 'XOF', + 'XP', + 'XPD', + 'XPF', + 'XPT', + 'XQJ', + 'XQP', + 'XQU', + 'XRML', + 'XS', + 'XSL', + 'XTMH', + 'XUL', + 'XV', + 'XVI', + 'XVII', + 'XVIII', + 'XWB', + 'XWF', + 'XWM', + 'XWN', + 'XWS', + 'XX', + 'XXX', + 'XYA', + 'Xyl', + 'XZA', + 'XZN', + 'y', + 'Y', + 'Y2K', + 'yA', + 'Ya', + 'YA', + 'YAC', + 'YAFLA', + 'YAP', + 'YaBPr', + 'YABSLAG', + 'Yb', + 'YB', + 'yC', + 'YC', + 'YD', + 'YDS', + 'YE', + 'YEM', + 'YER', + 'yF', + 'YF', + 'yg', + 'Yg', + 'Ygo', + 'YGCO', + 'yH', + 'YH', + 'YHS', + 'YHBT', + 'YHIH1', + 'YHII', + 'yi', + 'Yi', + 'YI', + 'yid', + 'Yippie', + 'yJ', + 'YJ', + 'yK', + 'YK', + 'YKYFCW', + 'yL', + 'YL', + 'ym', + 'Ym', + 'YM', + 'YMCA', + 'YMD', + 'YMMD', + 'YMMV', + 'yN', + 'YN', + 'yo', + 'YO', + 'YOLO', + 'yor', + 'YPA', + 'YPG', + 'YPIO', + 'YPO', + 'ys', + 'yS', + 'Ys', + 'YS', + 'YSA', + 'YSI', + 'yT', + 'YT', + 'YTD', + 'YTM', + 'YTS', + 'YTT', + 'YU', + 'YUG', + 'Yuppie', + 'yV', + 'YV', + 'yW', + 'YW', + 'YWCA', + 'YWMV', + 'YYH', + 'z', + 'Z', + 'za', + 'zA', + 'ZA', + 'ZAF', + 'ZAMS', + 'ZAR', + 'ZAS', + 'ZBT', + 'zC', + 'ZC', + 'ZCM', + 'ZCMK', + 'ZCU', +]; diff --git a/frontend/src/app/consts/code-snippets.ts b/frontend/src/app/consts/code-snippets.ts index 1c1fc980d..7285deef1 100644 --- a/frontend/src/app/consts/code-snippets.ts +++ b/frontend/src/app/consts/code-snippets.ts @@ -1,9 +1,9 @@ export function codeSnippets(signingKey: string) { - return { - cs: { - langName: 'C#', - mode: 'csharp', - snippet: `using System; + return { + cs: { + langName: 'C#', + mode: 'csharp', + snippet: `using System; using System.Security.Cryptography; using System.Text; using Microsoft.AspNetCore.Mvc; @@ -35,13 +35,13 @@ public class RocketAdminController : ControllerBase } } } -` - }, +`, + }, - java: { - langName: 'Java', - mode: 'java', - snippet: `import org.springframework.http.ResponseEntity; + java: { + langName: 'Java', + mode: 'java', + snippet: `import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; @@ -76,13 +76,13 @@ public class RocketAdminController { } } } -` - }, +`, + }, - php: { - langName: 'PHP', - mode: 'php', - snippet: String.raw`json(['error' => 'Signature invalid'], 400); } } -}` - }, +}`, + }, - go: { - langName: 'Go', - mode: 'go', - snippet: `package main + go: { + langName: 'Go', + mode: 'go', + snippet: `package main import ( "crypto/hmac" "crypto/sha256" @@ -147,13 +147,13 @@ func main() { http.ListenAndServe(":8080", nil) } -` - }, +`, + }, - python: { - langName: 'Python', - mode: 'python', - snippet: `from django.http import JsonResponse + python: { + langName: 'Python', + mode: 'python', + snippet: `from django.http import JsonResponse from django.views.decorators.csrf import csrf_exempt from django.utils.crypto import constant_time_compare import hashlib @@ -176,13 +176,13 @@ def rocketadmin(request): return JsonResponse({}, status=200) else: return JsonResponse({'error': 'Signature invalid'}, status=400) -` - }, +`, + }, - node: { - langName: 'Node.js', - mode: 'javascript', - snippet: `const crypto = require("crypto"); + node: { + langName: 'Node.js', + mode: 'javascript', + snippet: `const crypto = require("crypto"); app.use(express.json()); @@ -205,7 +205,7 @@ router.post("/rocketadmin", (req, res) => { res.status(400).send("Signature invalid"); } }); -` - } - } +`, + }, + }; } diff --git a/frontend/src/app/consts/countries.ts b/frontend/src/app/consts/countries.ts index 05e8c18fa..90b9936ba 100644 --- a/frontend/src/app/consts/countries.ts +++ b/frontend/src/app/consts/countries.ts @@ -1,266 +1,266 @@ export interface Country { - code: string; - name: string; - dialCode?: string; + code: string; + name: string; + dialCode?: string; } export function getCountryFlag(countryCode: string): string { - if (!countryCode || countryCode.length !== 2) return ''; - const codePoints = countryCode - .toUpperCase() - .split('') - .map(char => 127397 + char.charCodeAt(0)); - return String.fromCodePoint(...codePoints); + if (!countryCode || countryCode.length !== 2) return ''; + const codePoints = countryCode + .toUpperCase() + .split('') + .map((char) => 127397 + char.charCodeAt(0)); + return String.fromCodePoint(...codePoints); } export const COUNTRIES: Country[] = [ - { code: 'AD', name: 'Andorra', dialCode: '+376' }, - { code: 'AE', name: 'United Arab Emirates', dialCode: '+971' }, - { code: 'AF', name: 'Afghanistan', dialCode: '+93' }, - { code: 'AG', name: 'Antigua and Barbuda', dialCode: '+1268' }, - { code: 'AI', name: 'Anguilla', dialCode: '+1264' }, - { code: 'AL', name: 'Albania', dialCode: '+355' }, - { code: 'AM', name: 'Armenia', dialCode: '+374' }, - { code: 'AO', name: 'Angola', dialCode: '+244' }, - { code: 'AQ', name: 'Antarctica', dialCode: '+672' }, - { code: 'AR', name: 'Argentina', dialCode: '+54' }, - { code: 'AS', name: 'American Samoa', dialCode: '+1684' }, - { code: 'AT', name: 'Austria', dialCode: '+43' }, - { code: 'AU', name: 'Australia', dialCode: '+61' }, - { code: 'AW', name: 'Aruba', dialCode: '+297' }, - { code: 'AX', name: 'Åland Islands', dialCode: '+358' }, - { code: 'AZ', name: 'Azerbaijan', dialCode: '+994' }, - { code: 'BA', name: 'Bosnia and Herzegovina', dialCode: '+387' }, - { code: 'BB', name: 'Barbados', dialCode: '+1246' }, - { code: 'BD', name: 'Bangladesh', dialCode: '+880' }, - { code: 'BE', name: 'Belgium', dialCode: '+32' }, - { code: 'BF', name: 'Burkina Faso', dialCode: '+226' }, - { code: 'BG', name: 'Bulgaria', dialCode: '+359' }, - { code: 'BH', name: 'Bahrain', dialCode: '+973' }, - { code: 'BI', name: 'Burundi', dialCode: '+257' }, - { code: 'BJ', name: 'Benin', dialCode: '+229' }, - { code: 'BL', name: 'Saint Barthélemy', dialCode: '+590' }, - { code: 'BM', name: 'Bermuda', dialCode: '+1441' }, - { code: 'BN', name: 'Brunei Darussalam', dialCode: '+673' }, - { code: 'BO', name: 'Bolivia', dialCode: '+591' }, - { code: 'BQ', name: 'Bonaire, Sint Eustatius and Saba', dialCode: '+599' }, - { code: 'BR', name: 'Brazil', dialCode: '+55' }, - { code: 'BS', name: 'Bahamas', dialCode: '+1242' }, - { code: 'BT', name: 'Bhutan', dialCode: '+975' }, - { code: 'BV', name: 'Bouvet Island', dialCode: '+47' }, - { code: 'BW', name: 'Botswana', dialCode: '+267' }, - { code: 'BY', name: 'Belarus', dialCode: '+375' }, - { code: 'BZ', name: 'Belize', dialCode: '+501' }, - { code: 'CA', name: 'Canada', dialCode: '+1' }, - { code: 'CC', name: 'Cocos (Keeling) Islands', dialCode: '+61' }, - { code: 'CD', name: 'Congo, Democratic Republic of the', dialCode: '+243' }, - { code: 'CF', name: 'Central African Republic', dialCode: '+236' }, - { code: 'CG', name: 'Congo', dialCode: '+242' }, - { code: 'CH', name: 'Switzerland', dialCode: '+41' }, - { code: 'CI', name: 'Côte d\'Ivoire', dialCode: '+225' }, - { code: 'CK', name: 'Cook Islands', dialCode: '+682' }, - { code: 'CL', name: 'Chile', dialCode: '+56' }, - { code: 'CM', name: 'Cameroon', dialCode: '+237' }, - { code: 'CN', name: 'China', dialCode: '+86' }, - { code: 'CO', name: 'Colombia', dialCode: '+57' }, - { code: 'CR', name: 'Costa Rica', dialCode: '+506' }, - { code: 'CU', name: 'Cuba', dialCode: '+53' }, - { code: 'CV', name: 'Cape Verde', dialCode: '+238' }, - { code: 'CW', name: 'Curaçao', dialCode: '+599' }, - { code: 'CX', name: 'Christmas Island', dialCode: '+61' }, - { code: 'CY', name: 'Cyprus', dialCode: '+357' }, - { code: 'CZ', name: 'Czech Republic', dialCode: '+420' }, - { code: 'DE', name: 'Germany', dialCode: '+49' }, - { code: 'DJ', name: 'Djibouti', dialCode: '+253' }, - { code: 'DK', name: 'Denmark', dialCode: '+45' }, - { code: 'DM', name: 'Dominica', dialCode: '+1767' }, - { code: 'DO', name: 'Dominican Republic', dialCode: '+1' }, - { code: 'DZ', name: 'Algeria', dialCode: '+213' }, - { code: 'EC', name: 'Ecuador', dialCode: '+593' }, - { code: 'EE', name: 'Estonia', dialCode: '+372' }, - { code: 'EG', name: 'Egypt', dialCode: '+20' }, - { code: 'EH', name: 'Western Sahara', dialCode: '+212' }, - { code: 'ER', name: 'Eritrea', dialCode: '+291' }, - { code: 'ES', name: 'Spain', dialCode: '+34' }, - { code: 'ET', name: 'Ethiopia', dialCode: '+251' }, - { code: 'FI', name: 'Finland', dialCode: '+358' }, - { code: 'FJ', name: 'Fiji', dialCode: '+679' }, - { code: 'FK', name: 'Falkland Islands (Malvinas)', dialCode: '+500' }, - { code: 'FM', name: 'Micronesia, Federated States of', dialCode: '+691' }, - { code: 'FO', name: 'Faroe Islands', dialCode: '+298' }, - { code: 'FR', name: 'France', dialCode: '+33' }, - { code: 'GA', name: 'Gabon', dialCode: '+241' }, - { code: 'GB', name: 'United Kingdom', dialCode: '+44' }, - { code: 'GD', name: 'Grenada', dialCode: '+1473' }, - { code: 'GE', name: 'Georgia', dialCode: '+995' }, - { code: 'GF', name: 'French Guiana', dialCode: '+594' }, - { code: 'GG', name: 'Guernsey', dialCode: '+44' }, - { code: 'GH', name: 'Ghana', dialCode: '+233' }, - { code: 'GI', name: 'Gibraltar', dialCode: '+350' }, - { code: 'GL', name: 'Greenland', dialCode: '+299' }, - { code: 'GM', name: 'Gambia', dialCode: '+220' }, - { code: 'GN', name: 'Guinea', dialCode: '+224' }, - { code: 'GP', name: 'Guadeloupe', dialCode: '+590' }, - { code: 'GQ', name: 'Equatorial Guinea', dialCode: '+240' }, - { code: 'GR', name: 'Greece', dialCode: '+30' }, - { code: 'GS', name: 'South Georgia and the South Sandwich Islands', dialCode: '+500' }, - { code: 'GT', name: 'Guatemala', dialCode: '+502' }, - { code: 'GU', name: 'Guam', dialCode: '+1671' }, - { code: 'GW', name: 'Guinea-Bissau', dialCode: '+245' }, - { code: 'GY', name: 'Guyana', dialCode: '+592' }, - { code: 'HK', name: 'Hong Kong', dialCode: '+852' }, - { code: 'HM', name: 'Heard Island and McDonald Islands', dialCode: '+672' }, - { code: 'HN', name: 'Honduras', dialCode: '+504' }, - { code: 'HR', name: 'Croatia', dialCode: '+385' }, - { code: 'HT', name: 'Haiti', dialCode: '+509' }, - { code: 'HU', name: 'Hungary', dialCode: '+36' }, - { code: 'ID', name: 'Indonesia', dialCode: '+62' }, - { code: 'IE', name: 'Ireland', dialCode: '+353' }, - { code: 'IL', name: 'Israel', dialCode: '+972' }, - { code: 'IM', name: 'Isle of Man', dialCode: '+44' }, - { code: 'IN', name: 'India', dialCode: '+91' }, - { code: 'IO', name: 'British Indian Ocean Territory', dialCode: '+246' }, - { code: 'IQ', name: 'Iraq', dialCode: '+964' }, - { code: 'IR', name: 'Iran, Islamic Republic of', dialCode: '+98' }, - { code: 'IS', name: 'Iceland', dialCode: '+354' }, - { code: 'IT', name: 'Italy', dialCode: '+39' }, - { code: 'JE', name: 'Jersey', dialCode: '+44' }, - { code: 'JM', name: 'Jamaica', dialCode: '+1876' }, - { code: 'JO', name: 'Jordan', dialCode: '+962' }, - { code: 'JP', name: 'Japan', dialCode: '+81' }, - { code: 'KE', name: 'Kenya', dialCode: '+254' }, - { code: 'KG', name: 'Kyrgyzstan', dialCode: '+996' }, - { code: 'KH', name: 'Cambodia', dialCode: '+855' }, - { code: 'KI', name: 'Kiribati', dialCode: '+686' }, - { code: 'KM', name: 'Comoros', dialCode: '+269' }, - { code: 'KN', name: 'Saint Kitts and Nevis', dialCode: '+1869' }, - { code: 'KP', name: 'Korea, Democratic People\'s Republic of', dialCode: '+850' }, - { code: 'KR', name: 'Korea, Republic of', dialCode: '+82' }, - { code: 'KW', name: 'Kuwait', dialCode: '+965' }, - { code: 'KY', name: 'Cayman Islands', dialCode: '+1345' }, - { code: 'KZ', name: 'Kazakhstan', dialCode: '+7' }, - { code: 'LA', name: 'Lao People\'s Democratic Republic', dialCode: '+856' }, - { code: 'LB', name: 'Lebanon', dialCode: '+961' }, - { code: 'LC', name: 'Saint Lucia', dialCode: '+1758' }, - { code: 'LI', name: 'Liechtenstein', dialCode: '+423' }, - { code: 'LK', name: 'Sri Lanka', dialCode: '+94' }, - { code: 'LR', name: 'Liberia', dialCode: '+231' }, - { code: 'LS', name: 'Lesotho', dialCode: '+266' }, - { code: 'LT', name: 'Lithuania', dialCode: '+370' }, - { code: 'LU', name: 'Luxembourg', dialCode: '+352' }, - { code: 'LV', name: 'Latvia', dialCode: '+371' }, - { code: 'LY', name: 'Libya', dialCode: '+218' }, - { code: 'MA', name: 'Morocco', dialCode: '+212' }, - { code: 'MC', name: 'Monaco', dialCode: '+377' }, - { code: 'MD', name: 'Moldova, Republic of', dialCode: '+373' }, - { code: 'ME', name: 'Montenegro', dialCode: '+382' }, - { code: 'MF', name: 'Saint Martin (French part)', dialCode: '+590' }, - { code: 'MG', name: 'Madagascar', dialCode: '+261' }, - { code: 'MH', name: 'Marshall Islands', dialCode: '+692' }, - { code: 'MK', name: 'North Macedonia', dialCode: '+389' }, - { code: 'ML', name: 'Mali', dialCode: '+223' }, - { code: 'MM', name: 'Myanmar', dialCode: '+95' }, - { code: 'MN', name: 'Mongolia', dialCode: '+976' }, - { code: 'MO', name: 'Macao', dialCode: '+853' }, - { code: 'MP', name: 'Northern Mariana Islands', dialCode: '+1670' }, - { code: 'MQ', name: 'Martinique', dialCode: '+596' }, - { code: 'MR', name: 'Mauritania', dialCode: '+222' }, - { code: 'MS', name: 'Montserrat', dialCode: '+1664' }, - { code: 'MT', name: 'Malta', dialCode: '+356' }, - { code: 'MU', name: 'Mauritius', dialCode: '+230' }, - { code: 'MV', name: 'Maldives', dialCode: '+960' }, - { code: 'MW', name: 'Malawi', dialCode: '+265' }, - { code: 'MX', name: 'Mexico', dialCode: '+52' }, - { code: 'MY', name: 'Malaysia', dialCode: '+60' }, - { code: 'MZ', name: 'Mozambique', dialCode: '+258' }, - { code: 'NA', name: 'Namibia', dialCode: '+264' }, - { code: 'NC', name: 'New Caledonia', dialCode: '+687' }, - { code: 'NE', name: 'Niger', dialCode: '+227' }, - { code: 'NF', name: 'Norfolk Island', dialCode: '+672' }, - { code: 'NG', name: 'Nigeria', dialCode: '+234' }, - { code: 'NI', name: 'Nicaragua', dialCode: '+505' }, - { code: 'NL', name: 'Netherlands', dialCode: '+31' }, - { code: 'NO', name: 'Norway', dialCode: '+47' }, - { code: 'NP', name: 'Nepal', dialCode: '+977' }, - { code: 'NR', name: 'Nauru', dialCode: '+674' }, - { code: 'NU', name: 'Niue', dialCode: '+683' }, - { code: 'NZ', name: 'New Zealand', dialCode: '+64' }, - { code: 'OM', name: 'Oman', dialCode: '+968' }, - { code: 'PA', name: 'Panama', dialCode: '+507' }, - { code: 'PE', name: 'Peru', dialCode: '+51' }, - { code: 'PF', name: 'French Polynesia', dialCode: '+689' }, - { code: 'PG', name: 'Papua New Guinea', dialCode: '+675' }, - { code: 'PH', name: 'Philippines', dialCode: '+63' }, - { code: 'PK', name: 'Pakistan', dialCode: '+92' }, - { code: 'PL', name: 'Poland', dialCode: '+48' }, - { code: 'PM', name: 'Saint Pierre and Miquelon', dialCode: '+508' }, - { code: 'PN', name: 'Pitcairn', dialCode: '+64' }, - { code: 'PR', name: 'Puerto Rico', dialCode: '+1787' }, - { code: 'PS', name: 'Palestine, State of', dialCode: '+970' }, - { code: 'PT', name: 'Portugal', dialCode: '+351' }, - { code: 'PW', name: 'Palau', dialCode: '+680' }, - { code: 'PY', name: 'Paraguay', dialCode: '+595' }, - { code: 'QA', name: 'Qatar', dialCode: '+974' }, - { code: 'RE', name: 'Réunion', dialCode: '+262' }, - { code: 'RO', name: 'Romania', dialCode: '+40' }, - { code: 'RS', name: 'Serbia', dialCode: '+381' }, - { code: 'RU', name: 'Russian Federation', dialCode: '+7' }, - { code: 'RW', name: 'Rwanda', dialCode: '+250' }, - { code: 'SA', name: 'Saudi Arabia', dialCode: '+966' }, - { code: 'SB', name: 'Solomon Islands', dialCode: '+677' }, - { code: 'SC', name: 'Seychelles', dialCode: '+248' }, - { code: 'SD', name: 'Sudan', dialCode: '+249' }, - { code: 'SE', name: 'Sweden', dialCode: '+46' }, - { code: 'SG', name: 'Singapore', dialCode: '+65' }, - { code: 'SH', name: 'Saint Helena, Ascension and Tristan da Cunha', dialCode: '+290' }, - { code: 'SI', name: 'Slovenia', dialCode: '+386' }, - { code: 'SJ', name: 'Svalbard and Jan Mayen', dialCode: '+47' }, - { code: 'SK', name: 'Slovakia', dialCode: '+421' }, - { code: 'SL', name: 'Sierra Leone', dialCode: '+232' }, - { code: 'SM', name: 'San Marino', dialCode: '+378' }, - { code: 'SN', name: 'Senegal', dialCode: '+221' }, - { code: 'SO', name: 'Somalia', dialCode: '+252' }, - { code: 'SR', name: 'Suriname', dialCode: '+597' }, - { code: 'SS', name: 'South Sudan', dialCode: '+211' }, - { code: 'ST', name: 'Sao Tome and Principe', dialCode: '+239' }, - { code: 'SV', name: 'El Salvador', dialCode: '+503' }, - { code: 'SX', name: 'Sint Maarten (Dutch part)', dialCode: '+1721' }, - { code: 'SY', name: 'Syrian Arab Republic', dialCode: '+963' }, - { code: 'SZ', name: 'Eswatini', dialCode: '+268' }, - { code: 'TC', name: 'Turks and Caicos Islands', dialCode: '+1649' }, - { code: 'TD', name: 'Chad', dialCode: '+235' }, - { code: 'TF', name: 'French Southern Territories', dialCode: '+262' }, - { code: 'TG', name: 'Togo', dialCode: '+228' }, - { code: 'TH', name: 'Thailand', dialCode: '+66' }, - { code: 'TJ', name: 'Tajikistan', dialCode: '+992' }, - { code: 'TK', name: 'Tokelau', dialCode: '+690' }, - { code: 'TL', name: 'Timor-Leste', dialCode: '+670' }, - { code: 'TM', name: 'Turkmenistan', dialCode: '+993' }, - { code: 'TN', name: 'Tunisia', dialCode: '+216' }, - { code: 'TO', name: 'Tonga', dialCode: '+676' }, - { code: 'TR', name: 'Turkey', dialCode: '+90' }, - { code: 'TT', name: 'Trinidad and Tobago', dialCode: '+1868' }, - { code: 'TV', name: 'Tuvalu', dialCode: '+688' }, - { code: 'TW', name: 'Taiwan, Province of China', dialCode: '+886' }, - { code: 'TZ', name: 'Tanzania, United Republic of', dialCode: '+255' }, - { code: 'UA', name: 'Ukraine', dialCode: '+380' }, - { code: 'UG', name: 'Uganda', dialCode: '+256' }, - { code: 'UM', name: 'United States Minor Outlying Islands', dialCode: '+1' }, - { code: 'US', name: 'United States', dialCode: '+1' }, - { code: 'UY', name: 'Uruguay', dialCode: '+598' }, - { code: 'UZ', name: 'Uzbekistan', dialCode: '+998' }, - { code: 'VA', name: 'Holy See (Vatican City State)', dialCode: '+379' }, - { code: 'VC', name: 'Saint Vincent and the Grenadines', dialCode: '+1784' }, - { code: 'VE', name: 'Venezuela', dialCode: '+58' }, - { code: 'VG', name: 'Virgin Islands, British', dialCode: '+1284' }, - { code: 'VI', name: 'Virgin Islands, U.S.', dialCode: '+1340' }, - { code: 'VN', name: 'Viet Nam', dialCode: '+84' }, - { code: 'VU', name: 'Vanuatu', dialCode: '+678' }, - { code: 'WF', name: 'Wallis and Futuna', dialCode: '+681' }, - { code: 'WS', name: 'Samoa', dialCode: '+685' }, - { code: 'YE', name: 'Yemen', dialCode: '+967' }, - { code: 'YT', name: 'Mayotte', dialCode: '+262' }, - { code: 'ZA', name: 'South Africa', dialCode: '+27' }, - { code: 'ZM', name: 'Zambia', dialCode: '+260' }, - { code: 'ZW', name: 'Zimbabwe', dialCode: '+263' } -]; \ No newline at end of file + { code: 'AD', name: 'Andorra', dialCode: '+376' }, + { code: 'AE', name: 'United Arab Emirates', dialCode: '+971' }, + { code: 'AF', name: 'Afghanistan', dialCode: '+93' }, + { code: 'AG', name: 'Antigua and Barbuda', dialCode: '+1268' }, + { code: 'AI', name: 'Anguilla', dialCode: '+1264' }, + { code: 'AL', name: 'Albania', dialCode: '+355' }, + { code: 'AM', name: 'Armenia', dialCode: '+374' }, + { code: 'AO', name: 'Angola', dialCode: '+244' }, + { code: 'AQ', name: 'Antarctica', dialCode: '+672' }, + { code: 'AR', name: 'Argentina', dialCode: '+54' }, + { code: 'AS', name: 'American Samoa', dialCode: '+1684' }, + { code: 'AT', name: 'Austria', dialCode: '+43' }, + { code: 'AU', name: 'Australia', dialCode: '+61' }, + { code: 'AW', name: 'Aruba', dialCode: '+297' }, + { code: 'AX', name: 'Åland Islands', dialCode: '+358' }, + { code: 'AZ', name: 'Azerbaijan', dialCode: '+994' }, + { code: 'BA', name: 'Bosnia and Herzegovina', dialCode: '+387' }, + { code: 'BB', name: 'Barbados', dialCode: '+1246' }, + { code: 'BD', name: 'Bangladesh', dialCode: '+880' }, + { code: 'BE', name: 'Belgium', dialCode: '+32' }, + { code: 'BF', name: 'Burkina Faso', dialCode: '+226' }, + { code: 'BG', name: 'Bulgaria', dialCode: '+359' }, + { code: 'BH', name: 'Bahrain', dialCode: '+973' }, + { code: 'BI', name: 'Burundi', dialCode: '+257' }, + { code: 'BJ', name: 'Benin', dialCode: '+229' }, + { code: 'BL', name: 'Saint Barthélemy', dialCode: '+590' }, + { code: 'BM', name: 'Bermuda', dialCode: '+1441' }, + { code: 'BN', name: 'Brunei Darussalam', dialCode: '+673' }, + { code: 'BO', name: 'Bolivia', dialCode: '+591' }, + { code: 'BQ', name: 'Bonaire, Sint Eustatius and Saba', dialCode: '+599' }, + { code: 'BR', name: 'Brazil', dialCode: '+55' }, + { code: 'BS', name: 'Bahamas', dialCode: '+1242' }, + { code: 'BT', name: 'Bhutan', dialCode: '+975' }, + { code: 'BV', name: 'Bouvet Island', dialCode: '+47' }, + { code: 'BW', name: 'Botswana', dialCode: '+267' }, + { code: 'BY', name: 'Belarus', dialCode: '+375' }, + { code: 'BZ', name: 'Belize', dialCode: '+501' }, + { code: 'CA', name: 'Canada', dialCode: '+1' }, + { code: 'CC', name: 'Cocos (Keeling) Islands', dialCode: '+61' }, + { code: 'CD', name: 'Congo, Democratic Republic of the', dialCode: '+243' }, + { code: 'CF', name: 'Central African Republic', dialCode: '+236' }, + { code: 'CG', name: 'Congo', dialCode: '+242' }, + { code: 'CH', name: 'Switzerland', dialCode: '+41' }, + { code: 'CI', name: "Côte d'Ivoire", dialCode: '+225' }, + { code: 'CK', name: 'Cook Islands', dialCode: '+682' }, + { code: 'CL', name: 'Chile', dialCode: '+56' }, + { code: 'CM', name: 'Cameroon', dialCode: '+237' }, + { code: 'CN', name: 'China', dialCode: '+86' }, + { code: 'CO', name: 'Colombia', dialCode: '+57' }, + { code: 'CR', name: 'Costa Rica', dialCode: '+506' }, + { code: 'CU', name: 'Cuba', dialCode: '+53' }, + { code: 'CV', name: 'Cape Verde', dialCode: '+238' }, + { code: 'CW', name: 'Curaçao', dialCode: '+599' }, + { code: 'CX', name: 'Christmas Island', dialCode: '+61' }, + { code: 'CY', name: 'Cyprus', dialCode: '+357' }, + { code: 'CZ', name: 'Czech Republic', dialCode: '+420' }, + { code: 'DE', name: 'Germany', dialCode: '+49' }, + { code: 'DJ', name: 'Djibouti', dialCode: '+253' }, + { code: 'DK', name: 'Denmark', dialCode: '+45' }, + { code: 'DM', name: 'Dominica', dialCode: '+1767' }, + { code: 'DO', name: 'Dominican Republic', dialCode: '+1' }, + { code: 'DZ', name: 'Algeria', dialCode: '+213' }, + { code: 'EC', name: 'Ecuador', dialCode: '+593' }, + { code: 'EE', name: 'Estonia', dialCode: '+372' }, + { code: 'EG', name: 'Egypt', dialCode: '+20' }, + { code: 'EH', name: 'Western Sahara', dialCode: '+212' }, + { code: 'ER', name: 'Eritrea', dialCode: '+291' }, + { code: 'ES', name: 'Spain', dialCode: '+34' }, + { code: 'ET', name: 'Ethiopia', dialCode: '+251' }, + { code: 'FI', name: 'Finland', dialCode: '+358' }, + { code: 'FJ', name: 'Fiji', dialCode: '+679' }, + { code: 'FK', name: 'Falkland Islands (Malvinas)', dialCode: '+500' }, + { code: 'FM', name: 'Micronesia, Federated States of', dialCode: '+691' }, + { code: 'FO', name: 'Faroe Islands', dialCode: '+298' }, + { code: 'FR', name: 'France', dialCode: '+33' }, + { code: 'GA', name: 'Gabon', dialCode: '+241' }, + { code: 'GB', name: 'United Kingdom', dialCode: '+44' }, + { code: 'GD', name: 'Grenada', dialCode: '+1473' }, + { code: 'GE', name: 'Georgia', dialCode: '+995' }, + { code: 'GF', name: 'French Guiana', dialCode: '+594' }, + { code: 'GG', name: 'Guernsey', dialCode: '+44' }, + { code: 'GH', name: 'Ghana', dialCode: '+233' }, + { code: 'GI', name: 'Gibraltar', dialCode: '+350' }, + { code: 'GL', name: 'Greenland', dialCode: '+299' }, + { code: 'GM', name: 'Gambia', dialCode: '+220' }, + { code: 'GN', name: 'Guinea', dialCode: '+224' }, + { code: 'GP', name: 'Guadeloupe', dialCode: '+590' }, + { code: 'GQ', name: 'Equatorial Guinea', dialCode: '+240' }, + { code: 'GR', name: 'Greece', dialCode: '+30' }, + { code: 'GS', name: 'South Georgia and the South Sandwich Islands', dialCode: '+500' }, + { code: 'GT', name: 'Guatemala', dialCode: '+502' }, + { code: 'GU', name: 'Guam', dialCode: '+1671' }, + { code: 'GW', name: 'Guinea-Bissau', dialCode: '+245' }, + { code: 'GY', name: 'Guyana', dialCode: '+592' }, + { code: 'HK', name: 'Hong Kong', dialCode: '+852' }, + { code: 'HM', name: 'Heard Island and McDonald Islands', dialCode: '+672' }, + { code: 'HN', name: 'Honduras', dialCode: '+504' }, + { code: 'HR', name: 'Croatia', dialCode: '+385' }, + { code: 'HT', name: 'Haiti', dialCode: '+509' }, + { code: 'HU', name: 'Hungary', dialCode: '+36' }, + { code: 'ID', name: 'Indonesia', dialCode: '+62' }, + { code: 'IE', name: 'Ireland', dialCode: '+353' }, + { code: 'IL', name: 'Israel', dialCode: '+972' }, + { code: 'IM', name: 'Isle of Man', dialCode: '+44' }, + { code: 'IN', name: 'India', dialCode: '+91' }, + { code: 'IO', name: 'British Indian Ocean Territory', dialCode: '+246' }, + { code: 'IQ', name: 'Iraq', dialCode: '+964' }, + { code: 'IR', name: 'Iran, Islamic Republic of', dialCode: '+98' }, + { code: 'IS', name: 'Iceland', dialCode: '+354' }, + { code: 'IT', name: 'Italy', dialCode: '+39' }, + { code: 'JE', name: 'Jersey', dialCode: '+44' }, + { code: 'JM', name: 'Jamaica', dialCode: '+1876' }, + { code: 'JO', name: 'Jordan', dialCode: '+962' }, + { code: 'JP', name: 'Japan', dialCode: '+81' }, + { code: 'KE', name: 'Kenya', dialCode: '+254' }, + { code: 'KG', name: 'Kyrgyzstan', dialCode: '+996' }, + { code: 'KH', name: 'Cambodia', dialCode: '+855' }, + { code: 'KI', name: 'Kiribati', dialCode: '+686' }, + { code: 'KM', name: 'Comoros', dialCode: '+269' }, + { code: 'KN', name: 'Saint Kitts and Nevis', dialCode: '+1869' }, + { code: 'KP', name: "Korea, Democratic People's Republic of", dialCode: '+850' }, + { code: 'KR', name: 'Korea, Republic of', dialCode: '+82' }, + { code: 'KW', name: 'Kuwait', dialCode: '+965' }, + { code: 'KY', name: 'Cayman Islands', dialCode: '+1345' }, + { code: 'KZ', name: 'Kazakhstan', dialCode: '+7' }, + { code: 'LA', name: "Lao People's Democratic Republic", dialCode: '+856' }, + { code: 'LB', name: 'Lebanon', dialCode: '+961' }, + { code: 'LC', name: 'Saint Lucia', dialCode: '+1758' }, + { code: 'LI', name: 'Liechtenstein', dialCode: '+423' }, + { code: 'LK', name: 'Sri Lanka', dialCode: '+94' }, + { code: 'LR', name: 'Liberia', dialCode: '+231' }, + { code: 'LS', name: 'Lesotho', dialCode: '+266' }, + { code: 'LT', name: 'Lithuania', dialCode: '+370' }, + { code: 'LU', name: 'Luxembourg', dialCode: '+352' }, + { code: 'LV', name: 'Latvia', dialCode: '+371' }, + { code: 'LY', name: 'Libya', dialCode: '+218' }, + { code: 'MA', name: 'Morocco', dialCode: '+212' }, + { code: 'MC', name: 'Monaco', dialCode: '+377' }, + { code: 'MD', name: 'Moldova, Republic of', dialCode: '+373' }, + { code: 'ME', name: 'Montenegro', dialCode: '+382' }, + { code: 'MF', name: 'Saint Martin (French part)', dialCode: '+590' }, + { code: 'MG', name: 'Madagascar', dialCode: '+261' }, + { code: 'MH', name: 'Marshall Islands', dialCode: '+692' }, + { code: 'MK', name: 'North Macedonia', dialCode: '+389' }, + { code: 'ML', name: 'Mali', dialCode: '+223' }, + { code: 'MM', name: 'Myanmar', dialCode: '+95' }, + { code: 'MN', name: 'Mongolia', dialCode: '+976' }, + { code: 'MO', name: 'Macao', dialCode: '+853' }, + { code: 'MP', name: 'Northern Mariana Islands', dialCode: '+1670' }, + { code: 'MQ', name: 'Martinique', dialCode: '+596' }, + { code: 'MR', name: 'Mauritania', dialCode: '+222' }, + { code: 'MS', name: 'Montserrat', dialCode: '+1664' }, + { code: 'MT', name: 'Malta', dialCode: '+356' }, + { code: 'MU', name: 'Mauritius', dialCode: '+230' }, + { code: 'MV', name: 'Maldives', dialCode: '+960' }, + { code: 'MW', name: 'Malawi', dialCode: '+265' }, + { code: 'MX', name: 'Mexico', dialCode: '+52' }, + { code: 'MY', name: 'Malaysia', dialCode: '+60' }, + { code: 'MZ', name: 'Mozambique', dialCode: '+258' }, + { code: 'NA', name: 'Namibia', dialCode: '+264' }, + { code: 'NC', name: 'New Caledonia', dialCode: '+687' }, + { code: 'NE', name: 'Niger', dialCode: '+227' }, + { code: 'NF', name: 'Norfolk Island', dialCode: '+672' }, + { code: 'NG', name: 'Nigeria', dialCode: '+234' }, + { code: 'NI', name: 'Nicaragua', dialCode: '+505' }, + { code: 'NL', name: 'Netherlands', dialCode: '+31' }, + { code: 'NO', name: 'Norway', dialCode: '+47' }, + { code: 'NP', name: 'Nepal', dialCode: '+977' }, + { code: 'NR', name: 'Nauru', dialCode: '+674' }, + { code: 'NU', name: 'Niue', dialCode: '+683' }, + { code: 'NZ', name: 'New Zealand', dialCode: '+64' }, + { code: 'OM', name: 'Oman', dialCode: '+968' }, + { code: 'PA', name: 'Panama', dialCode: '+507' }, + { code: 'PE', name: 'Peru', dialCode: '+51' }, + { code: 'PF', name: 'French Polynesia', dialCode: '+689' }, + { code: 'PG', name: 'Papua New Guinea', dialCode: '+675' }, + { code: 'PH', name: 'Philippines', dialCode: '+63' }, + { code: 'PK', name: 'Pakistan', dialCode: '+92' }, + { code: 'PL', name: 'Poland', dialCode: '+48' }, + { code: 'PM', name: 'Saint Pierre and Miquelon', dialCode: '+508' }, + { code: 'PN', name: 'Pitcairn', dialCode: '+64' }, + { code: 'PR', name: 'Puerto Rico', dialCode: '+1787' }, + { code: 'PS', name: 'Palestine, State of', dialCode: '+970' }, + { code: 'PT', name: 'Portugal', dialCode: '+351' }, + { code: 'PW', name: 'Palau', dialCode: '+680' }, + { code: 'PY', name: 'Paraguay', dialCode: '+595' }, + { code: 'QA', name: 'Qatar', dialCode: '+974' }, + { code: 'RE', name: 'Réunion', dialCode: '+262' }, + { code: 'RO', name: 'Romania', dialCode: '+40' }, + { code: 'RS', name: 'Serbia', dialCode: '+381' }, + { code: 'RU', name: 'Russian Federation', dialCode: '+7' }, + { code: 'RW', name: 'Rwanda', dialCode: '+250' }, + { code: 'SA', name: 'Saudi Arabia', dialCode: '+966' }, + { code: 'SB', name: 'Solomon Islands', dialCode: '+677' }, + { code: 'SC', name: 'Seychelles', dialCode: '+248' }, + { code: 'SD', name: 'Sudan', dialCode: '+249' }, + { code: 'SE', name: 'Sweden', dialCode: '+46' }, + { code: 'SG', name: 'Singapore', dialCode: '+65' }, + { code: 'SH', name: 'Saint Helena, Ascension and Tristan da Cunha', dialCode: '+290' }, + { code: 'SI', name: 'Slovenia', dialCode: '+386' }, + { code: 'SJ', name: 'Svalbard and Jan Mayen', dialCode: '+47' }, + { code: 'SK', name: 'Slovakia', dialCode: '+421' }, + { code: 'SL', name: 'Sierra Leone', dialCode: '+232' }, + { code: 'SM', name: 'San Marino', dialCode: '+378' }, + { code: 'SN', name: 'Senegal', dialCode: '+221' }, + { code: 'SO', name: 'Somalia', dialCode: '+252' }, + { code: 'SR', name: 'Suriname', dialCode: '+597' }, + { code: 'SS', name: 'South Sudan', dialCode: '+211' }, + { code: 'ST', name: 'Sao Tome and Principe', dialCode: '+239' }, + { code: 'SV', name: 'El Salvador', dialCode: '+503' }, + { code: 'SX', name: 'Sint Maarten (Dutch part)', dialCode: '+1721' }, + { code: 'SY', name: 'Syrian Arab Republic', dialCode: '+963' }, + { code: 'SZ', name: 'Eswatini', dialCode: '+268' }, + { code: 'TC', name: 'Turks and Caicos Islands', dialCode: '+1649' }, + { code: 'TD', name: 'Chad', dialCode: '+235' }, + { code: 'TF', name: 'French Southern Territories', dialCode: '+262' }, + { code: 'TG', name: 'Togo', dialCode: '+228' }, + { code: 'TH', name: 'Thailand', dialCode: '+66' }, + { code: 'TJ', name: 'Tajikistan', dialCode: '+992' }, + { code: 'TK', name: 'Tokelau', dialCode: '+690' }, + { code: 'TL', name: 'Timor-Leste', dialCode: '+670' }, + { code: 'TM', name: 'Turkmenistan', dialCode: '+993' }, + { code: 'TN', name: 'Tunisia', dialCode: '+216' }, + { code: 'TO', name: 'Tonga', dialCode: '+676' }, + { code: 'TR', name: 'Turkey', dialCode: '+90' }, + { code: 'TT', name: 'Trinidad and Tobago', dialCode: '+1868' }, + { code: 'TV', name: 'Tuvalu', dialCode: '+688' }, + { code: 'TW', name: 'Taiwan, Province of China', dialCode: '+886' }, + { code: 'TZ', name: 'Tanzania, United Republic of', dialCode: '+255' }, + { code: 'UA', name: 'Ukraine', dialCode: '+380' }, + { code: 'UG', name: 'Uganda', dialCode: '+256' }, + { code: 'UM', name: 'United States Minor Outlying Islands', dialCode: '+1' }, + { code: 'US', name: 'United States', dialCode: '+1' }, + { code: 'UY', name: 'Uruguay', dialCode: '+598' }, + { code: 'UZ', name: 'Uzbekistan', dialCode: '+998' }, + { code: 'VA', name: 'Holy See (Vatican City State)', dialCode: '+379' }, + { code: 'VC', name: 'Saint Vincent and the Grenadines', dialCode: '+1784' }, + { code: 'VE', name: 'Venezuela', dialCode: '+58' }, + { code: 'VG', name: 'Virgin Islands, British', dialCode: '+1284' }, + { code: 'VI', name: 'Virgin Islands, U.S.', dialCode: '+1340' }, + { code: 'VN', name: 'Viet Nam', dialCode: '+84' }, + { code: 'VU', name: 'Vanuatu', dialCode: '+678' }, + { code: 'WF', name: 'Wallis and Futuna', dialCode: '+681' }, + { code: 'WS', name: 'Samoa', dialCode: '+685' }, + { code: 'YE', name: 'Yemen', dialCode: '+967' }, + { code: 'YT', name: 'Mayotte', dialCode: '+262' }, + { code: 'ZA', name: 'South Africa', dialCode: '+27' }, + { code: 'ZM', name: 'Zambia', dialCode: '+260' }, + { code: 'ZW', name: 'Zimbabwe', dialCode: '+263' }, +]; diff --git a/frontend/src/app/consts/currencies.ts b/frontend/src/app/consts/currencies.ts index 5b48192f2..233dcf2bb 100644 --- a/frontend/src/app/consts/currencies.ts +++ b/frontend/src/app/consts/currencies.ts @@ -1,56 +1,56 @@ export interface Money { - code: string; - name: string; - symbol: string; - flag?: string; + code: string; + name: string; + symbol: string; + flag?: string; } export interface MoneyValue { - amount: number | string; - currency: string; + amount: number | string; + currency: string; } export const CURRENCIES: Money[] = [ - { code: 'USD', name: 'US Dollar', symbol: '$', flag: '🇺🇸' }, - { code: 'EUR', name: 'Euro', symbol: '€', flag: '🇪🇺' }, - { code: 'GBP', name: 'British Pound', symbol: '£', flag: '🇬🇧' }, - { code: 'JPY', name: 'Japanese Yen', symbol: '¥', flag: '🇯🇵' }, - { code: 'CHF', name: 'Swiss Franc', symbol: 'CHF', flag: '🇨🇭' }, - { code: 'CAD', name: 'Canadian Dollar', symbol: 'C$', flag: '🇨🇦' }, - { code: 'AUD', name: 'Australian Dollar', symbol: 'A$', flag: '🇦🇺' }, - { code: 'CNY', name: 'Chinese Yuan', symbol: '¥', flag: '🇨🇳' }, - { code: 'INR', name: 'Indian Rupee', symbol: '₹', flag: '🇮🇳' }, - { code: 'KRW', name: 'South Korean Won', symbol: '₩', flag: '🇰🇷' }, - { code: 'SGD', name: 'Singapore Dollar', symbol: 'S$', flag: '🇸🇬' }, - { code: 'HKD', name: 'Hong Kong Dollar', symbol: 'HK$', flag: '🇭🇰' }, - { code: 'NOK', name: 'Norwegian Krone', symbol: 'kr', flag: '🇳🇴' }, - { code: 'SEK', name: 'Swedish Krona', symbol: 'kr', flag: '🇸🇪' }, - { code: 'DKK', name: 'Danish Krone', symbol: 'kr', flag: '🇩🇰' }, - { code: 'PLN', name: 'Polish Zloty', symbol: 'zł', flag: '🇵🇱' }, - { code: 'CZK', name: 'Czech Koruna', symbol: 'Kč', flag: '🇨🇿' }, - { code: 'HUF', name: 'Hungarian Forint', symbol: 'Ft', flag: '🇭🇺' }, - { code: 'RUB', name: 'Russian Ruble', symbol: '₽', flag: '🇷🇺' }, - { code: 'BRL', name: 'Brazilian Real', symbol: 'R$', flag: '🇧🇷' }, - { code: 'MXN', name: 'Mexican Peso', symbol: '$', flag: '🇲🇽' }, - { code: 'ZAR', name: 'South African Rand', symbol: 'R', flag: '🇿🇦' }, - { code: 'TRY', name: 'Turkish Lira', symbol: '₺', flag: '🇹🇷' }, - { code: 'AED', name: 'UAE Dirham', symbol: 'د.إ', flag: '🇦🇪' }, - { code: 'SAR', name: 'Saudi Riyal', symbol: '﷼', flag: '🇸🇦' }, - { code: 'ILS', name: 'Israeli Shekel', symbol: '₪', flag: '🇮🇱' }, - { code: 'EGP', name: 'Egyptian Pound', symbol: '£', flag: '🇪🇬' }, - { code: 'THB', name: 'Thai Baht', symbol: '฿', flag: '🇹🇭' }, - { code: 'MYR', name: 'Malaysian Ringgit', symbol: 'RM', flag: '🇲🇾' }, - { code: 'IDR', name: 'Indonesian Rupiah', symbol: 'Rp', flag: '🇮🇩' }, - { code: 'PHP', name: 'Philippine Peso', symbol: '₱', flag: '🇵🇭' }, - { code: 'VND', name: 'Vietnamese Dong', symbol: '₫', flag: '🇻🇳' }, - { code: 'UAH', name: 'Ukrainian Hryvnia', symbol: '₴', flag: '🇺🇦' } + { code: 'USD', name: 'US Dollar', symbol: '$', flag: '🇺🇸' }, + { code: 'EUR', name: 'Euro', symbol: '€', flag: '🇪🇺' }, + { code: 'GBP', name: 'British Pound', symbol: '£', flag: '🇬🇧' }, + { code: 'JPY', name: 'Japanese Yen', symbol: '¥', flag: '🇯🇵' }, + { code: 'CHF', name: 'Swiss Franc', symbol: 'CHF', flag: '🇨🇭' }, + { code: 'CAD', name: 'Canadian Dollar', symbol: 'C$', flag: '🇨🇦' }, + { code: 'AUD', name: 'Australian Dollar', symbol: 'A$', flag: '🇦🇺' }, + { code: 'CNY', name: 'Chinese Yuan', symbol: '¥', flag: '🇨🇳' }, + { code: 'INR', name: 'Indian Rupee', symbol: '₹', flag: '🇮🇳' }, + { code: 'KRW', name: 'South Korean Won', symbol: '₩', flag: '🇰🇷' }, + { code: 'SGD', name: 'Singapore Dollar', symbol: 'S$', flag: '🇸🇬' }, + { code: 'HKD', name: 'Hong Kong Dollar', symbol: 'HK$', flag: '🇭🇰' }, + { code: 'NOK', name: 'Norwegian Krone', symbol: 'kr', flag: '🇳🇴' }, + { code: 'SEK', name: 'Swedish Krona', symbol: 'kr', flag: '🇸🇪' }, + { code: 'DKK', name: 'Danish Krone', symbol: 'kr', flag: '🇩🇰' }, + { code: 'PLN', name: 'Polish Zloty', symbol: 'zł', flag: '🇵🇱' }, + { code: 'CZK', name: 'Czech Koruna', symbol: 'Kč', flag: '🇨🇿' }, + { code: 'HUF', name: 'Hungarian Forint', symbol: 'Ft', flag: '🇭🇺' }, + { code: 'RUB', name: 'Russian Ruble', symbol: '₽', flag: '🇷🇺' }, + { code: 'BRL', name: 'Brazilian Real', symbol: 'R$', flag: '🇧🇷' }, + { code: 'MXN', name: 'Mexican Peso', symbol: '$', flag: '🇲🇽' }, + { code: 'ZAR', name: 'South African Rand', symbol: 'R', flag: '🇿🇦' }, + { code: 'TRY', name: 'Turkish Lira', symbol: '₺', flag: '🇹🇷' }, + { code: 'AED', name: 'UAE Dirham', symbol: 'د.إ', flag: '🇦🇪' }, + { code: 'SAR', name: 'Saudi Riyal', symbol: '﷼', flag: '🇸🇦' }, + { code: 'ILS', name: 'Israeli Shekel', symbol: '₪', flag: '🇮🇱' }, + { code: 'EGP', name: 'Egyptian Pound', symbol: '£', flag: '🇪🇬' }, + { code: 'THB', name: 'Thai Baht', symbol: '฿', flag: '🇹🇭' }, + { code: 'MYR', name: 'Malaysian Ringgit', symbol: 'RM', flag: '🇲🇾' }, + { code: 'IDR', name: 'Indonesian Rupiah', symbol: 'Rp', flag: '🇮🇩' }, + { code: 'PHP', name: 'Philippine Peso', symbol: '₱', flag: '🇵🇭' }, + { code: 'VND', name: 'Vietnamese Dong', symbol: '₫', flag: '🇻🇳' }, + { code: 'UAH', name: 'Ukrainian Hryvnia', symbol: '₴', flag: '🇺🇦' }, ]; export function getCurrencyByCode(code: string): Money | undefined { - return CURRENCIES.find(currency => currency.code === code); + return CURRENCIES.find((currency) => currency.code === code); } export function getCurrencySymbol(code: string): string { - const currency = getCurrencyByCode(code); - return currency ? currency.symbol : ''; + const currency = getCurrencyByCode(code); + return currency ? currency.symbol : ''; } diff --git a/frontend/src/app/consts/databases.ts b/frontend/src/app/consts/databases.ts index 988b0ffbc..be1896b3f 100644 --- a/frontend/src/app/consts/databases.ts +++ b/frontend/src/app/consts/databases.ts @@ -1,27 +1,27 @@ export const supportedOrderedDatabases = [ - "mysql", - "postgres", - "mongodb", - "dynamodb", - "cassandra", - "oracledb", - "mssql", - "redis", - "elasticsearch", - "clickhouse", - "ibmdb2" -] + 'mysql', + 'postgres', + 'mongodb', + 'dynamodb', + 'cassandra', + 'oracledb', + 'mssql', + 'redis', + 'elasticsearch', + 'clickhouse', + 'ibmdb2', +]; export const supportedDatabasesTitles = { - mysql: "MySQL (MariaDB)", - postgres: "PostgreSQL", - mongodb: "MongoDB", - dynamodb: "DynamoDB", - cassandra: "Cassandra (ScyllaDB)", - oracledb: "OracleDB", - mssql: "SQL Server", - redis: "Redis", - elasticsearch: "Elasticsearch (OpenSearch)", - clickhouse: "ClickHouse", - ibmdb2: "IBM DB2" - } \ No newline at end of file + mysql: 'MySQL (MariaDB)', + postgres: 'PostgreSQL', + mongodb: 'MongoDB', + dynamodb: 'DynamoDB', + cassandra: 'Cassandra (ScyllaDB)', + oracledb: 'OracleDB', + mssql: 'SQL Server', + redis: 'Redis', + elasticsearch: 'Elasticsearch (OpenSearch)', + clickhouse: 'ClickHouse', + ibmdb2: 'IBM DB2', +}; diff --git a/frontend/src/app/consts/filter-types.ts b/frontend/src/app/consts/filter-types.ts index 88c4856ea..85496397a 100644 --- a/frontend/src/app/consts/filter-types.ts +++ b/frontend/src/app/consts/filter-types.ts @@ -1,5 +1,8 @@ - -import { BooleanFilterComponent } from 'src/app/components/ui-components/filter-fields/boolean/boolean.component' +import { BooleanFilterComponent } from 'src/app/components/ui-components/filter-fields/boolean/boolean.component'; +import { LongTextFilterComponent } from 'src/app/components/ui-components/filter-fields/long-text/long-text.component'; +import { NumberFilterComponent } from 'src/app/components/ui-components/filter-fields/number/number.component'; +import { PointFilterComponent } from 'src/app/components/ui-components/filter-fields/point/point.component'; +import { TextFilterComponent } from 'src/app/components/ui-components/filter-fields/text/text.component'; import { CountryFilterComponent } from '../components/ui-components/filter-fields/country/country.component'; import { DateFilterComponent } from '../components/ui-components/filter-fields/date/date.component'; import { DateTimeFilterComponent } from '../components/ui-components/filter-fields/date-time/date-time.component'; @@ -7,280 +10,276 @@ import { FileFilterComponent } from '../components/ui-components/filter-fields/f import { ForeignKeyFilterComponent } from '../components/ui-components/filter-fields/foreign-key/foreign-key.component'; import { IdFilterComponent } from '../components/ui-components/filter-fields/id/id.component'; import { JsonEditorFilterComponent } from '../components/ui-components/filter-fields/json-editor/json-editor.component'; -import { LongTextFilterComponent } from 'src/app/components/ui-components/filter-fields/long-text/long-text.component' -import { NumberFilterComponent } from 'src/app/components/ui-components/filter-fields/number/number.component'; import { PasswordFilterComponent } from '../components/ui-components/filter-fields/password/password.component'; -import { PointFilterComponent } from 'src/app/components/ui-components/filter-fields/point/point.component'; import { SelectFilterComponent } from '../components/ui-components/filter-fields/select/select.component'; import { StaticTextFilterComponent } from '../components/ui-components/filter-fields/static-text/static-text.component'; -import { TextFilterComponent } from 'src/app/components/ui-components/filter-fields/text/text.component'; import { TimeFilterComponent } from '../components/ui-components/filter-fields/time/time.component'; import { TimeIntervalFilterComponent } from '../components/ui-components/filter-fields/time-interval/time-interval.component'; import { TimezoneFilterComponent } from '../components/ui-components/filter-fields/timezone/timezone.component'; export const UIwidgets = { - Default: '', - Boolean: BooleanFilterComponent, - Country: CountryFilterComponent, - Date: DateFilterComponent, - DateTime: DateTimeFilterComponent, - File: FileFilterComponent, - Foreign_key: ForeignKeyFilterComponent, - JSON: JsonEditorFilterComponent, - Number: NumberFilterComponent, - Password: PasswordFilterComponent, - Readonly: StaticTextFilterComponent, - Select: SelectFilterComponent, - String: TextFilterComponent, - Textarea: LongTextFilterComponent, - Time: TimeFilterComponent, - TimeInterval: TimeIntervalFilterComponent, - Timezone: TimezoneFilterComponent, - Point: PointFilterComponent, - ID: IdFilterComponent, -} + Default: '', + Boolean: BooleanFilterComponent, + Country: CountryFilterComponent, + Date: DateFilterComponent, + DateTime: DateTimeFilterComponent, + File: FileFilterComponent, + Foreign_key: ForeignKeyFilterComponent, + JSON: JsonEditorFilterComponent, + Number: NumberFilterComponent, + Password: PasswordFilterComponent, + Readonly: StaticTextFilterComponent, + Select: SelectFilterComponent, + String: TextFilterComponent, + Textarea: LongTextFilterComponent, + Time: TimeFilterComponent, + TimeInterval: TimeIntervalFilterComponent, + Timezone: TimezoneFilterComponent, + Point: PointFilterComponent, + ID: IdFilterComponent, +}; export const filterTypes = { - postgres: { - // numbers (number) - real: NumberFilterComponent, - "double precision": NumberFilterComponent, - smallint: NumberFilterComponent, - integer: NumberFilterComponent, - bigint: NumberFilterComponent, - numeric: NumberFilterComponent, - - //boolean (checkbox) - boolean: BooleanFilterComponent, - - //datetime (datepicker) - "timestamp without time zone": DateTimeFilterComponent, - "timestamp with time zone": DateTimeFilterComponent, - date: DateFilterComponent, - abstime: DateTimeFilterComponent, - realtime: DateTimeFilterComponent, - interval: TimeIntervalFilterComponent, // number + select(seconds, days, weeks, years) - - // short text (text) - "character varying": TextFilterComponent, - macaddr: TextFilterComponent, //to do regexp - macaddr8: TextFilterComponent, //to do regexp - cidr: TextFilterComponent, //to do regexp - inet: TextFilterComponent, //to do regexp - uuid: TextFilterComponent, //to do regexp - - //long text (textarea) - text: LongTextFilterComponent, - xml: LongTextFilterComponent, - - //select (select) - enum: SelectFilterComponent, - - // json-editor - json: JsonEditorFilterComponent, //json-editor - jsonb: JsonEditorFilterComponent, //json-editor - - //file - bytea: FileFilterComponent, - - //etc - money: TextFilterComponent, - - //mess (math) - point: PointFilterComponent, - line: TextFilterComponent, - circle: TextFilterComponent, - path: TextFilterComponent, - box: TextFilterComponent, - lseg: TextFilterComponent, - - "foreign key": ForeignKeyFilterComponent - }, - - mysql: { - // numbers (number) - tinyint: NumberFilterComponent, - smallint: NumberFilterComponent, - mediumint: NumberFilterComponent, - int: NumberFilterComponent, - bigint: NumberFilterComponent, - decimal: NumberFilterComponent, - float: NumberFilterComponent, - double: NumberFilterComponent, - year: NumberFilterComponent, - - //boolean (radiogroup) - boolean: BooleanFilterComponent, - - //datetime (datepicker) - date: DateFilterComponent, - time: TimeFilterComponent, - datetime: DateTimeFilterComponent, - timestamp: DateTimeFilterComponent, - - // short text (text) - char: TextFilterComponent, - varchar: TextFilterComponent, - - //long text (textarea) - text: LongTextFilterComponent, - tinytext: LongTextFilterComponent, - mediumtext: LongTextFilterComponent, - longtext: LongTextFilterComponent, - - json: JsonEditorFilterComponent, //json-editor - - //select (select) - enum: SelectFilterComponent, - - //file - binary: FileFilterComponent, - varbinary: FileFilterComponent, - blob: FileFilterComponent, - tinyblob: FileFilterComponent, - mediumblob: FileFilterComponent, - longblob: FileFilterComponent, - - //etc - set: TextFilterComponent, //(text) - - "foreign key": ForeignKeyFilterComponent - }, - - oracledb: { - // numbers (number) - NUMBER: NumberFilterComponent, - FLOAT: NumberFilterComponent, - BINARY_FLOAT: NumberFilterComponent, - BINARY_DOUBLE: NumberFilterComponent, - "INTERVAL YEAR": NumberFilterComponent, - "INTERVAL DAY": NumberFilterComponent, - - //datetime (datepicker) - DATE: DateFilterComponent, - TIMESTAMP: DateTimeFilterComponent, - - // short text (text) - CHAR: TextFilterComponent, - NCHAR: TextFilterComponent, - CLOB: TextFilterComponent, - NCLOB: TextFilterComponent, - VARCHAR2: TextFilterComponent, - VARCHAR: TextFilterComponent, - NVARCHAR2: TextFilterComponent, - - //file - BLOB: FileFilterComponent, - BFILE: FileFilterComponent, - RAW: FileFilterComponent, - "LONG RAW" : FileFilterComponent, - LONG : FileFilterComponent, - - "foreign key": ForeignKeyFilterComponent - }, - - mssql: { - // numbers (number) - bigint: NumberFilterComponent, - int: NumberFilterComponent, - smallint: NumberFilterComponent, - tinyint: NumberFilterComponent, - decimal: NumberFilterComponent, - bitdecimal: NumberFilterComponent, - numeric: NumberFilterComponent, - real: NumberFilterComponent, - - // short text (text) - uniqueidentifier: IdFilterComponent, - char: TextFilterComponent, - varchar: TextFilterComponent, - - //long text (textarea) - text: LongTextFilterComponent, - nchar: LongTextFilterComponent, - nvarchar: LongTextFilterComponent, - ntext: LongTextFilterComponent, - - //datetime (datepicker) - date: DateFilterComponent, - datetime: DateTimeFilterComponent, - smalldatetime: DateTimeFilterComponent, - timestamp: DateTimeFilterComponent, - - //file - binary: FileFilterComponent, - varbinary: FileFilterComponent, - image: FileFilterComponent, - - // etc - money: TextFilterComponent, - smallmoney: TextFilterComponent, - - "foreign key": ForeignKeyFilterComponent - }, - mongodb: { - // numbers (number) - number: NumberFilterComponent, - double: NumberFilterComponent, - int32: NumberFilterComponent, - long: NumberFilterComponent, - decimal128: NumberFilterComponent, - - //boolean (radiogroup) - boolean: BooleanFilterComponent, - - //datetime (datepicker) - date: DateFilterComponent, - timestamp: DateTimeFilterComponent, - - // short text (text) - string: TextFilterComponent, - regexp: TextFilterComponent, - objectid: TextFilterComponent, - - //file - binary: FileFilterComponent, - - //json - //json - object: JsonEditorFilterComponent, - array: JsonEditorFilterComponent, - - "foreign key": ForeignKeyFilterComponent - }, - dynamodb: { - string: TextFilterComponent, - number: NumberFilterComponent, - boolean: BooleanFilterComponent, - null: StaticTextFilterComponent, - array: JsonEditorFilterComponent, - json: JsonEditorFilterComponent, - binary: FileFilterComponent, - }, - cassandra: { - int: NumberFilterComponent, - bigint: NumberFilterComponent, - varint: NumberFilterComponent, - decimal: NumberFilterComponent, - float: NumberFilterComponent, - double: NumberFilterComponent, - - boolean: BooleanFilterComponent, - - timeuuid: IdFilterComponent, - - timestamp: DateTimeFilterComponent, - date: DateFilterComponent, - time: TimeFilterComponent, - - uuid: TextFilterComponent, - varchar: TextFilterComponent, - inet: TextFilterComponent, - ascii: TextFilterComponent, - text: TextFilterComponent, - - list: JsonEditorFilterComponent, - map: JsonEditorFilterComponent, - set: JsonEditorFilterComponent, - } -} \ No newline at end of file + postgres: { + // numbers (number) + real: NumberFilterComponent, + 'double precision': NumberFilterComponent, + smallint: NumberFilterComponent, + integer: NumberFilterComponent, + bigint: NumberFilterComponent, + numeric: NumberFilterComponent, + + //boolean (checkbox) + boolean: BooleanFilterComponent, + + //datetime (datepicker) + 'timestamp without time zone': DateTimeFilterComponent, + 'timestamp with time zone': DateTimeFilterComponent, + date: DateFilterComponent, + abstime: DateTimeFilterComponent, + realtime: DateTimeFilterComponent, + interval: TimeIntervalFilterComponent, // number + select(seconds, days, weeks, years) + + // short text (text) + 'character varying': TextFilterComponent, + macaddr: TextFilterComponent, //to do regexp + macaddr8: TextFilterComponent, //to do regexp + cidr: TextFilterComponent, //to do regexp + inet: TextFilterComponent, //to do regexp + uuid: TextFilterComponent, //to do regexp + + //long text (textarea) + text: LongTextFilterComponent, + xml: LongTextFilterComponent, + + //select (select) + enum: SelectFilterComponent, + + // json-editor + json: JsonEditorFilterComponent, //json-editor + jsonb: JsonEditorFilterComponent, //json-editor + + //file + bytea: FileFilterComponent, + + //etc + money: TextFilterComponent, + + //mess (math) + point: PointFilterComponent, + line: TextFilterComponent, + circle: TextFilterComponent, + path: TextFilterComponent, + box: TextFilterComponent, + lseg: TextFilterComponent, + + 'foreign key': ForeignKeyFilterComponent, + }, + + mysql: { + // numbers (number) + tinyint: NumberFilterComponent, + smallint: NumberFilterComponent, + mediumint: NumberFilterComponent, + int: NumberFilterComponent, + bigint: NumberFilterComponent, + decimal: NumberFilterComponent, + float: NumberFilterComponent, + double: NumberFilterComponent, + year: NumberFilterComponent, + + //boolean (radiogroup) + boolean: BooleanFilterComponent, + + //datetime (datepicker) + date: DateFilterComponent, + time: TimeFilterComponent, + datetime: DateTimeFilterComponent, + timestamp: DateTimeFilterComponent, + + // short text (text) + char: TextFilterComponent, + varchar: TextFilterComponent, + + //long text (textarea) + text: LongTextFilterComponent, + tinytext: LongTextFilterComponent, + mediumtext: LongTextFilterComponent, + longtext: LongTextFilterComponent, + + json: JsonEditorFilterComponent, //json-editor + + //select (select) + enum: SelectFilterComponent, + + //file + binary: FileFilterComponent, + varbinary: FileFilterComponent, + blob: FileFilterComponent, + tinyblob: FileFilterComponent, + mediumblob: FileFilterComponent, + longblob: FileFilterComponent, + + //etc + set: TextFilterComponent, //(text) + + 'foreign key': ForeignKeyFilterComponent, + }, + + oracledb: { + // numbers (number) + NUMBER: NumberFilterComponent, + FLOAT: NumberFilterComponent, + BINARY_FLOAT: NumberFilterComponent, + BINARY_DOUBLE: NumberFilterComponent, + 'INTERVAL YEAR': NumberFilterComponent, + 'INTERVAL DAY': NumberFilterComponent, + + //datetime (datepicker) + DATE: DateFilterComponent, + TIMESTAMP: DateTimeFilterComponent, + + // short text (text) + CHAR: TextFilterComponent, + NCHAR: TextFilterComponent, + CLOB: TextFilterComponent, + NCLOB: TextFilterComponent, + VARCHAR2: TextFilterComponent, + VARCHAR: TextFilterComponent, + NVARCHAR2: TextFilterComponent, + + //file + BLOB: FileFilterComponent, + BFILE: FileFilterComponent, + RAW: FileFilterComponent, + 'LONG RAW': FileFilterComponent, + LONG: FileFilterComponent, + + 'foreign key': ForeignKeyFilterComponent, + }, + + mssql: { + // numbers (number) + bigint: NumberFilterComponent, + int: NumberFilterComponent, + smallint: NumberFilterComponent, + tinyint: NumberFilterComponent, + decimal: NumberFilterComponent, + bitdecimal: NumberFilterComponent, + numeric: NumberFilterComponent, + real: NumberFilterComponent, + + // short text (text) + uniqueidentifier: IdFilterComponent, + char: TextFilterComponent, + varchar: TextFilterComponent, + + //long text (textarea) + text: LongTextFilterComponent, + nchar: LongTextFilterComponent, + nvarchar: LongTextFilterComponent, + ntext: LongTextFilterComponent, + + //datetime (datepicker) + date: DateFilterComponent, + datetime: DateTimeFilterComponent, + smalldatetime: DateTimeFilterComponent, + timestamp: DateTimeFilterComponent, + + //file + binary: FileFilterComponent, + varbinary: FileFilterComponent, + image: FileFilterComponent, + + // etc + money: TextFilterComponent, + smallmoney: TextFilterComponent, + + 'foreign key': ForeignKeyFilterComponent, + }, + mongodb: { + // numbers (number) + number: NumberFilterComponent, + double: NumberFilterComponent, + int32: NumberFilterComponent, + long: NumberFilterComponent, + decimal128: NumberFilterComponent, + + //boolean (radiogroup) + boolean: BooleanFilterComponent, + + //datetime (datepicker) + date: DateFilterComponent, + timestamp: DateTimeFilterComponent, + + // short text (text) + string: TextFilterComponent, + regexp: TextFilterComponent, + objectid: TextFilterComponent, + + //file + binary: FileFilterComponent, + + //json + //json + object: JsonEditorFilterComponent, + array: JsonEditorFilterComponent, + + 'foreign key': ForeignKeyFilterComponent, + }, + dynamodb: { + string: TextFilterComponent, + number: NumberFilterComponent, + boolean: BooleanFilterComponent, + null: StaticTextFilterComponent, + array: JsonEditorFilterComponent, + json: JsonEditorFilterComponent, + binary: FileFilterComponent, + }, + cassandra: { + int: NumberFilterComponent, + bigint: NumberFilterComponent, + varint: NumberFilterComponent, + decimal: NumberFilterComponent, + float: NumberFilterComponent, + double: NumberFilterComponent, + + boolean: BooleanFilterComponent, + + timeuuid: IdFilterComponent, + + timestamp: DateTimeFilterComponent, + date: DateFilterComponent, + time: TimeFilterComponent, + + uuid: TextFilterComponent, + varchar: TextFilterComponent, + inet: TextFilterComponent, + ascii: TextFilterComponent, + text: TextFilterComponent, + + list: JsonEditorFilterComponent, + map: JsonEditorFilterComponent, + set: JsonEditorFilterComponent, + }, +}; diff --git a/frontend/src/app/consts/google-IP-addresses.ts b/frontend/src/app/consts/google-IP-addresses.ts index 3fa3d6c3c..d11fffb09 100644 --- a/frontend/src/app/consts/google-IP-addresses.ts +++ b/frontend/src/app/consts/google-IP-addresses.ts @@ -1,530 +1,530 @@ export default [ - "34.80.0.0/15", - "34.137.0.0/16", - "35.185.128.0/19", - "35.185.160.0/20", - "35.187.144.0/20", - "35.189.160.0/19", - "35.194.128.0/17", - "35.201.128.0/17", - "35.206.192.0/18", - "35.220.32.0/21", - "35.221.128.0/17", - "35.229.128.0/17", - "35.234.0.0/18", - "35.235.16.0/20", - "35.236.128.0/18", - "35.242.32.0/21", - "104.155.192.0/19", - "104.155.224.0/20", - "104.199.128.0/18", - "104.199.192.0/19", - "104.199.224.0/20", - "104.199.242.0/23", - "104.199.244.0/22", - "104.199.248.0/21", - "107.167.176.0/20", - "130.211.240.0/20", - "34.92.0.0/16", - "34.96.128.0/17", - "34.104.88.0/21", - "34.124.24.0/21", - "34.150.0.0/17", - "35.215.128.0/18", - "35.220.27.0/24", - "35.220.128.0/17", - "35.241.64.0/18", - "35.242.27.0/24", - "35.243.8.0/21", - "34.84.0.0/16", - "34.85.0.0/17", - "34.104.62.0/23", - "34.104.128.0/17", - "34.127.190.0/23", - "34.146.0.0/16", - "34.157.64.0/20", - "34.157.164.0/22", - "34.157.192.0/20", - "35.187.192.0/19", - "35.189.128.0/19", - "35.190.224.0/20", - "35.194.96.0/19", - "35.200.0.0/17", - "35.213.0.0/17", - "35.220.56.0/22", - "35.221.64.0/18", - "35.230.240.0/20", - "35.242.56.0/22", - "35.243.64.0/18", - "104.198.80.0/20", - "104.198.112.0/20", - "34.97.0.0/16", - "34.104.49.0/24", - "34.127.177.0/24", - "35.217.128.0/17", - "35.220.45.0/24", - "35.242.45.0/24", - "35.243.56.0/21", - "34.64.32.0/19", - "34.64.64.0/22", - "34.64.68.0/22", - "34.64.72.0/21", - "34.64.80.0/20", - "34.64.96.0/19", - "34.64.128.0/22", - "34.64.132.0/22", - "34.64.136.0/21", - "34.64.144.0/20", - "34.64.160.0/19", - "34.64.192.0/18", - "35.216.0.0/17", - "34.93.0.0/16", - "34.100.128.0/17", - "34.104.108.0/23", - "34.124.44.0/23", - "34.157.87.0/24", - "34.157.215.0/24", - "35.200.128.0/17", - "35.201.41.0/24", - "35.207.192.0/18", - "35.220.42.0/24", - "35.234.208.0/20", - "35.242.42.0/24", - "35.244.0.0/18", - "34.104.120.0/23", - "34.124.56.0/23", - "34.126.208.0/20", - "34.131.0.0/16", - "34.21.128.0/17", - "34.87.0.0/17", - "34.87.128.0/18", - "34.104.58.0/23", - "34.104.106.0/23", - "34.124.42.0/23", - "34.124.128.0/17", - "34.126.64.0/18", - "34.126.128.0/18", - "34.142.128.0/17", - "34.143.128.0/17", - "34.157.82.0/23", - "34.157.88.0/23", - "34.157.210.0/23", - "35.185.176.0/20", - "35.186.144.0/20", - "35.187.224.0/19", - "35.197.128.0/19", - "35.198.192.0/18", - "35.213.128.0/18", - "35.220.24.0/23", - "35.234.192.0/20", - "35.240.128.0/17", - "35.242.24.0/23", - "35.247.128.0/18", - "34.101.18.0/24", - "34.101.20.0/22", - "34.101.24.0/22", - "34.101.32.0/19", - "34.101.64.0/18", - "34.101.128.0/17", - "34.128.64.0/18", - "35.219.0.0/17", - "34.87.192.0/18", - "34.104.104.0/23", - "34.116.64.0/18", - "34.124.40.0/23", - "34.151.64.0/18", - "34.151.128.0/18", - "35.189.0.0/18", - "35.197.160.0/19", - "35.201.0.0/19", - "35.213.192.0/18", - "35.220.41.0/24", - "35.234.224.0/20", - "35.242.41.0/24", - "35.244.64.0/18", - "34.104.122.0/23", - "34.124.58.0/23", - "34.126.192.0/20", - "34.129.0.0/16", - "34.104.116.0/22", - "34.116.128.0/17", - "34.118.0.0/17", - "34.124.52.0/22", - "34.88.0.0/16", - "34.104.96.0/21", - "34.124.32.0/21", - "35.203.232.0/21", - "35.217.0.0/18", - "35.220.26.0/24", - "35.228.0.0/16", - "35.242.26.0/24", - "34.157.44.0/23", - "34.157.172.0/23", - "34.164.0.0/16", - "34.175.0.0/16", - "8.34.208.0/23", - "8.34.211.0/24", - "8.34.220.0/22", - "23.251.128.0/20", - "34.76.0.0/14", - "34.118.254.0/23", - "34.140.0.0/16", - "35.187.0.0/17", - "35.187.160.0/19", - "35.189.192.0/18", - "35.190.192.0/19", - "35.195.0.0/16", - "35.205.0.0/16", - "35.206.128.0/18", - "35.210.0.0/16", - "35.220.96.0/19", - "35.233.0.0/17", - "35.240.0.0/17", - "35.241.128.0/17", - "35.242.64.0/19", - "104.155.0.0/17", - "104.199.0.0/18", - "104.199.66.0/23", - "104.199.68.0/22", - "104.199.72.0/21", - "104.199.80.0/20", - "104.199.96.0/20", - "130.211.48.0/20", - "130.211.64.0/19", - "130.211.96.0/20", - "146.148.2.0/23", - "146.148.4.0/22", - "146.148.8.0/21", - "146.148.16.0/20", - "146.148.112.0/20", - "192.158.28.0/22", - "34.89.0.0/17", - "34.105.128.0/17", - "34.127.186.0/23", - "34.142.0.0/17", - "34.147.128.0/17", - "34.157.36.0/22", - "34.157.40.0/22", - "34.157.168.0/22", - "35.189.64.0/18", - "35.197.192.0/18", - "35.203.210.0/23", - "35.203.212.0/22", - "35.203.216.0/22", - "35.214.0.0/17", - "35.220.20.0/22", - "35.230.128.0/19", - "35.234.128.0/19", - "35.235.48.0/20", - "35.242.20.0/22", - "35.242.128.0/18", - "35.246.0.0/17", - "34.89.128.0/17", - "34.104.112.0/23", - "34.107.0.0/17", - "34.118.244.0/22", - "34.124.48.0/23", - "34.141.0.0/17", - "34.157.48.0/20", - "34.157.176.0/20", - "34.159.0.0/16", - "35.198.64.0/18", - "35.198.128.0/18", - "35.207.64.0/18", - "35.207.128.0/18", - "35.220.18.0/23", - "35.234.64.0/18", - "35.235.32.0/20", - "35.242.18.0/23", - "35.242.192.0/18", - "35.246.128.0/17", - "34.90.0.0/15", - "34.104.126.0/23", - "34.124.62.0/23", - "34.141.128.0/17", - "34.147.0.0/17", - "34.157.80.0/23", - "34.157.92.0/22", - "34.157.208.0/23", - "34.157.220.0/22", - "35.204.0.0/16", - "35.214.128.0/17", - "35.220.16.0/23", - "35.234.160.0/20", - "35.242.16.0/23", - "34.65.0.0/16", - "34.104.110.0/23", - "34.124.46.0/23", - "35.216.128.0/17", - "35.220.44.0/24", - "35.235.216.0/21", - "35.242.44.0/24", - "34.154.0.0/16", - "34.157.8.0/23", - "34.157.136.0/23", - "35.219.224.0/19", - "34.155.0.0/16", - "34.157.12.0/22", - "34.157.140.0/22", - "34.163.0.0/16", - "34.95.64.0/18", - "34.96.64.0/18", - "34.98.64.0/18", - "34.102.128.0/17", - "34.104.27.0/24", - "34.107.128.0/17", - "34.110.128.0/17", - "34.111.0.0/16", - "34.116.0.0/21", - "34.117.0.0/16", - "34.120.0.0/16", - "34.128.128.0/18", - "34.144.192.0/18", - "34.149.0.0/16", - "34.160.0.0/16", - "35.186.192.0/18", - "35.190.0.0/18", - "35.190.64.0/19", - "35.190.112.0/20", - "35.201.64.0/18", - "35.227.192.0/18", - "35.241.0.0/18", - "35.244.128.0/17", - "107.178.240.0/20", - "130.211.4.0/22", - "130.211.8.0/21", - "130.211.16.0/20", - "130.211.32.0/20", - "34.157.90.0/23", - "34.157.216.0/23", - "34.165.0.0/16", - "34.19.128.0/17", - "34.20.0.0/17", - "34.95.0.0/18", - "34.104.76.0/22", - "34.118.128.0/18", - "34.124.12.0/22", - "34.152.0.0/18", - "35.203.0.0/17", - "35.215.0.0/18", - "35.220.43.0/24", - "35.234.240.0/20", - "35.242.43.0/24", - "34.104.114.0/23", - "34.124.50.0/23", - "34.124.112.0/20", - "34.130.0.0/16", - "34.95.128.0/17", - "34.104.80.0/21", - "34.124.16.0/21", - "34.151.0.0/18", - "34.151.192.0/18", - "35.198.0.0/18", - "35.199.64.0/18", - "35.215.192.0/18", - "35.220.40.0/24", - "35.235.0.0/20", - "35.242.40.0/24", - "35.247.192.0/18", - "34.104.50.0/23", - "34.127.178.0/23", - "34.176.0.0/16", - "8.34.210.0/24", - "8.34.212.0/22", - "8.34.216.0/22", - "8.35.192.0/21", - "23.236.48.0/20", - "23.251.144.0/20", - "34.16.0.0/17", - "34.27.0.0/16", - "34.28.0.0/15", - "34.30.0.0/16", - "34.66.0.0/15", - "34.68.0.0/14", - "34.72.0.0/16", - "34.118.200.0/21", - "34.121.0.0/16", - "34.122.0.0/15", - "34.132.0.0/14", - "34.136.0.0/16", - "34.157.84.0/23", - "34.157.96.0/20", - "34.157.212.0/23", - "34.157.224.0/20", - "34.170.0.0/15", - "34.172.0.0/15", - "35.184.0.0/16", - "35.188.0.0/17", - "35.188.128.0/18", - "35.188.192.0/19", - "35.192.0.0/15", - "35.194.0.0/18", - "35.202.0.0/16", - "35.206.64.0/18", - "35.208.0.0/15", - "35.220.64.0/19", - "35.222.0.0/15", - "35.224.0.0/15", - "35.226.0.0/16", - "35.232.0.0/16", - "35.238.0.0/15", - "35.242.96.0/19", - "104.154.16.0/20", - "104.154.32.0/19", - "104.154.64.0/19", - "104.154.96.0/20", - "104.154.113.0/24", - "104.154.114.0/23", - "104.154.116.0/22", - "104.154.120.0/23", - "104.154.128.0/17", - "104.155.128.0/18", - "104.197.0.0/16", - "104.198.16.0/20", - "104.198.32.0/19", - "104.198.64.0/20", - "104.198.128.0/17", - "107.178.208.0/20", - "108.59.80.0/21", - "130.211.112.0/20", - "130.211.128.0/18", - "130.211.192.0/19", - "130.211.224.0/20", - "146.148.32.0/19", - "146.148.64.0/19", - "146.148.96.0/20", - "162.222.176.0/21", - "173.255.112.0/21", - "199.192.115.0/24", - "199.223.232.0/22", - "199.223.236.0/24", - "34.22.0.0/19", - "35.186.0.0/17", - "35.186.128.0/20", - "35.206.32.0/19", - "35.220.46.0/24", - "35.242.46.0/24", - "107.167.160.0/20", - "108.59.88.0/21", - "173.255.120.0/21", - "34.23.0.0/16", - "34.24.0.0/15", - "34.26.0.0/16", - "34.73.0.0/16", - "34.74.0.0/15", - "34.98.128.0/21", - "34.118.250.0/23", - "34.138.0.0/15", - "34.148.0.0/16", - "35.185.0.0/17", - "35.190.128.0/18", - "35.196.0.0/16", - "35.207.0.0/18", - "35.211.0.0/16", - "35.220.0.0/20", - "35.227.0.0/17", - "35.229.16.0/20", - "35.229.32.0/19", - "35.229.64.0/18", - "35.231.0.0/16", - "35.237.0.0/16", - "35.242.0.0/20", - "35.243.128.0/17", - "104.196.0.0/18", - "104.196.65.0/24", - "104.196.66.0/23", - "104.196.68.0/22", - "104.196.96.0/19", - "104.196.128.0/18", - "104.196.192.0/19", - "162.216.148.0/22", - "34.21.0.0/17", - "34.85.128.0/17", - "34.86.0.0/16", - "34.104.60.0/23", - "34.104.124.0/23", - "34.118.252.0/23", - "34.124.60.0/23", - "34.127.188.0/23", - "34.145.128.0/17", - "34.150.128.0/17", - "34.157.0.0/21", - "34.157.16.0/20", - "34.157.128.0/21", - "34.157.144.0/20", - "35.186.160.0/19", - "35.188.224.0/19", - "35.194.64.0/19", - "35.199.0.0/18", - "35.212.0.0/17", - "35.220.60.0/22", - "35.221.0.0/18", - "35.230.160.0/19", - "35.234.176.0/20", - "35.236.192.0/18", - "35.242.60.0/22", - "35.243.40.0/21", - "35.245.0.0/16", - "34.157.32.0/22", - "34.157.160.0/22", - "34.162.0.0/16", - "34.104.56.0/23", - "34.127.184.0/23", - "34.161.0.0/16", - "35.206.10.0/23", - "34.157.46.0/23", - "34.157.174.0/23", - "34.174.0.0/16", - "34.19.0.0/17", - "34.82.0.0/15", - "34.105.0.0/17", - "34.118.192.0/21", - "34.127.0.0/17", - "34.145.0.0/17", - "34.157.112.0/21", - "34.157.240.0/21", - "34.168.0.0/15", - "35.185.192.0/18", - "35.197.0.0/17", - "35.199.144.0/20", - "35.199.160.0/19", - "35.203.128.0/18", - "35.212.128.0/17", - "35.220.48.0/21", - "35.227.128.0/18", - "35.230.0.0/17", - "35.233.128.0/17", - "35.242.48.0/21", - "35.243.32.0/21", - "35.247.0.0/17", - "104.196.224.0/19", - "104.198.0.0/20", - "104.198.96.0/20", - "104.199.112.0/20", - "34.20.128.0/17", - "34.94.0.0/16", - "34.102.0.0/17", - "34.104.64.0/21", - "34.108.0.0/16", - "34.118.248.0/23", - "34.124.0.0/21", - "35.215.64.0/18", - "35.220.47.0/24", - "35.235.64.0/18", - "35.236.0.0/17", - "35.242.47.0/24", - "35.243.0.0/21", - "34.22.32.0/19", - "34.104.52.0/24", - "34.106.0.0/16", - "34.127.180.0/24", - "35.217.64.0/18", - "35.220.31.0/24", - "35.242.31.0/24", - "34.16.128.0/17", - "34.104.72.0/22", - "34.118.240.0/22", - "34.124.8.0/22", - "34.125.0.0/16", - "35.219.128.0/18", -] + '34.80.0.0/15', + '34.137.0.0/16', + '35.185.128.0/19', + '35.185.160.0/20', + '35.187.144.0/20', + '35.189.160.0/19', + '35.194.128.0/17', + '35.201.128.0/17', + '35.206.192.0/18', + '35.220.32.0/21', + '35.221.128.0/17', + '35.229.128.0/17', + '35.234.0.0/18', + '35.235.16.0/20', + '35.236.128.0/18', + '35.242.32.0/21', + '104.155.192.0/19', + '104.155.224.0/20', + '104.199.128.0/18', + '104.199.192.0/19', + '104.199.224.0/20', + '104.199.242.0/23', + '104.199.244.0/22', + '104.199.248.0/21', + '107.167.176.0/20', + '130.211.240.0/20', + '34.92.0.0/16', + '34.96.128.0/17', + '34.104.88.0/21', + '34.124.24.0/21', + '34.150.0.0/17', + '35.215.128.0/18', + '35.220.27.0/24', + '35.220.128.0/17', + '35.241.64.0/18', + '35.242.27.0/24', + '35.243.8.0/21', + '34.84.0.0/16', + '34.85.0.0/17', + '34.104.62.0/23', + '34.104.128.0/17', + '34.127.190.0/23', + '34.146.0.0/16', + '34.157.64.0/20', + '34.157.164.0/22', + '34.157.192.0/20', + '35.187.192.0/19', + '35.189.128.0/19', + '35.190.224.0/20', + '35.194.96.0/19', + '35.200.0.0/17', + '35.213.0.0/17', + '35.220.56.0/22', + '35.221.64.0/18', + '35.230.240.0/20', + '35.242.56.0/22', + '35.243.64.0/18', + '104.198.80.0/20', + '104.198.112.0/20', + '34.97.0.0/16', + '34.104.49.0/24', + '34.127.177.0/24', + '35.217.128.0/17', + '35.220.45.0/24', + '35.242.45.0/24', + '35.243.56.0/21', + '34.64.32.0/19', + '34.64.64.0/22', + '34.64.68.0/22', + '34.64.72.0/21', + '34.64.80.0/20', + '34.64.96.0/19', + '34.64.128.0/22', + '34.64.132.0/22', + '34.64.136.0/21', + '34.64.144.0/20', + '34.64.160.0/19', + '34.64.192.0/18', + '35.216.0.0/17', + '34.93.0.0/16', + '34.100.128.0/17', + '34.104.108.0/23', + '34.124.44.0/23', + '34.157.87.0/24', + '34.157.215.0/24', + '35.200.128.0/17', + '35.201.41.0/24', + '35.207.192.0/18', + '35.220.42.0/24', + '35.234.208.0/20', + '35.242.42.0/24', + '35.244.0.0/18', + '34.104.120.0/23', + '34.124.56.0/23', + '34.126.208.0/20', + '34.131.0.0/16', + '34.21.128.0/17', + '34.87.0.0/17', + '34.87.128.0/18', + '34.104.58.0/23', + '34.104.106.0/23', + '34.124.42.0/23', + '34.124.128.0/17', + '34.126.64.0/18', + '34.126.128.0/18', + '34.142.128.0/17', + '34.143.128.0/17', + '34.157.82.0/23', + '34.157.88.0/23', + '34.157.210.0/23', + '35.185.176.0/20', + '35.186.144.0/20', + '35.187.224.0/19', + '35.197.128.0/19', + '35.198.192.0/18', + '35.213.128.0/18', + '35.220.24.0/23', + '35.234.192.0/20', + '35.240.128.0/17', + '35.242.24.0/23', + '35.247.128.0/18', + '34.101.18.0/24', + '34.101.20.0/22', + '34.101.24.0/22', + '34.101.32.0/19', + '34.101.64.0/18', + '34.101.128.0/17', + '34.128.64.0/18', + '35.219.0.0/17', + '34.87.192.0/18', + '34.104.104.0/23', + '34.116.64.0/18', + '34.124.40.0/23', + '34.151.64.0/18', + '34.151.128.0/18', + '35.189.0.0/18', + '35.197.160.0/19', + '35.201.0.0/19', + '35.213.192.0/18', + '35.220.41.0/24', + '35.234.224.0/20', + '35.242.41.0/24', + '35.244.64.0/18', + '34.104.122.0/23', + '34.124.58.0/23', + '34.126.192.0/20', + '34.129.0.0/16', + '34.104.116.0/22', + '34.116.128.0/17', + '34.118.0.0/17', + '34.124.52.0/22', + '34.88.0.0/16', + '34.104.96.0/21', + '34.124.32.0/21', + '35.203.232.0/21', + '35.217.0.0/18', + '35.220.26.0/24', + '35.228.0.0/16', + '35.242.26.0/24', + '34.157.44.0/23', + '34.157.172.0/23', + '34.164.0.0/16', + '34.175.0.0/16', + '8.34.208.0/23', + '8.34.211.0/24', + '8.34.220.0/22', + '23.251.128.0/20', + '34.76.0.0/14', + '34.118.254.0/23', + '34.140.0.0/16', + '35.187.0.0/17', + '35.187.160.0/19', + '35.189.192.0/18', + '35.190.192.0/19', + '35.195.0.0/16', + '35.205.0.0/16', + '35.206.128.0/18', + '35.210.0.0/16', + '35.220.96.0/19', + '35.233.0.0/17', + '35.240.0.0/17', + '35.241.128.0/17', + '35.242.64.0/19', + '104.155.0.0/17', + '104.199.0.0/18', + '104.199.66.0/23', + '104.199.68.0/22', + '104.199.72.0/21', + '104.199.80.0/20', + '104.199.96.0/20', + '130.211.48.0/20', + '130.211.64.0/19', + '130.211.96.0/20', + '146.148.2.0/23', + '146.148.4.0/22', + '146.148.8.0/21', + '146.148.16.0/20', + '146.148.112.0/20', + '192.158.28.0/22', + '34.89.0.0/17', + '34.105.128.0/17', + '34.127.186.0/23', + '34.142.0.0/17', + '34.147.128.0/17', + '34.157.36.0/22', + '34.157.40.0/22', + '34.157.168.0/22', + '35.189.64.0/18', + '35.197.192.0/18', + '35.203.210.0/23', + '35.203.212.0/22', + '35.203.216.0/22', + '35.214.0.0/17', + '35.220.20.0/22', + '35.230.128.0/19', + '35.234.128.0/19', + '35.235.48.0/20', + '35.242.20.0/22', + '35.242.128.0/18', + '35.246.0.0/17', + '34.89.128.0/17', + '34.104.112.0/23', + '34.107.0.0/17', + '34.118.244.0/22', + '34.124.48.0/23', + '34.141.0.0/17', + '34.157.48.0/20', + '34.157.176.0/20', + '34.159.0.0/16', + '35.198.64.0/18', + '35.198.128.0/18', + '35.207.64.0/18', + '35.207.128.0/18', + '35.220.18.0/23', + '35.234.64.0/18', + '35.235.32.0/20', + '35.242.18.0/23', + '35.242.192.0/18', + '35.246.128.0/17', + '34.90.0.0/15', + '34.104.126.0/23', + '34.124.62.0/23', + '34.141.128.0/17', + '34.147.0.0/17', + '34.157.80.0/23', + '34.157.92.0/22', + '34.157.208.0/23', + '34.157.220.0/22', + '35.204.0.0/16', + '35.214.128.0/17', + '35.220.16.0/23', + '35.234.160.0/20', + '35.242.16.0/23', + '34.65.0.0/16', + '34.104.110.0/23', + '34.124.46.0/23', + '35.216.128.0/17', + '35.220.44.0/24', + '35.235.216.0/21', + '35.242.44.0/24', + '34.154.0.0/16', + '34.157.8.0/23', + '34.157.136.0/23', + '35.219.224.0/19', + '34.155.0.0/16', + '34.157.12.0/22', + '34.157.140.0/22', + '34.163.0.0/16', + '34.95.64.0/18', + '34.96.64.0/18', + '34.98.64.0/18', + '34.102.128.0/17', + '34.104.27.0/24', + '34.107.128.0/17', + '34.110.128.0/17', + '34.111.0.0/16', + '34.116.0.0/21', + '34.117.0.0/16', + '34.120.0.0/16', + '34.128.128.0/18', + '34.144.192.0/18', + '34.149.0.0/16', + '34.160.0.0/16', + '35.186.192.0/18', + '35.190.0.0/18', + '35.190.64.0/19', + '35.190.112.0/20', + '35.201.64.0/18', + '35.227.192.0/18', + '35.241.0.0/18', + '35.244.128.0/17', + '107.178.240.0/20', + '130.211.4.0/22', + '130.211.8.0/21', + '130.211.16.0/20', + '130.211.32.0/20', + '34.157.90.0/23', + '34.157.216.0/23', + '34.165.0.0/16', + '34.19.128.0/17', + '34.20.0.0/17', + '34.95.0.0/18', + '34.104.76.0/22', + '34.118.128.0/18', + '34.124.12.0/22', + '34.152.0.0/18', + '35.203.0.0/17', + '35.215.0.0/18', + '35.220.43.0/24', + '35.234.240.0/20', + '35.242.43.0/24', + '34.104.114.0/23', + '34.124.50.0/23', + '34.124.112.0/20', + '34.130.0.0/16', + '34.95.128.0/17', + '34.104.80.0/21', + '34.124.16.0/21', + '34.151.0.0/18', + '34.151.192.0/18', + '35.198.0.0/18', + '35.199.64.0/18', + '35.215.192.0/18', + '35.220.40.0/24', + '35.235.0.0/20', + '35.242.40.0/24', + '35.247.192.0/18', + '34.104.50.0/23', + '34.127.178.0/23', + '34.176.0.0/16', + '8.34.210.0/24', + '8.34.212.0/22', + '8.34.216.0/22', + '8.35.192.0/21', + '23.236.48.0/20', + '23.251.144.0/20', + '34.16.0.0/17', + '34.27.0.0/16', + '34.28.0.0/15', + '34.30.0.0/16', + '34.66.0.0/15', + '34.68.0.0/14', + '34.72.0.0/16', + '34.118.200.0/21', + '34.121.0.0/16', + '34.122.0.0/15', + '34.132.0.0/14', + '34.136.0.0/16', + '34.157.84.0/23', + '34.157.96.0/20', + '34.157.212.0/23', + '34.157.224.0/20', + '34.170.0.0/15', + '34.172.0.0/15', + '35.184.0.0/16', + '35.188.0.0/17', + '35.188.128.0/18', + '35.188.192.0/19', + '35.192.0.0/15', + '35.194.0.0/18', + '35.202.0.0/16', + '35.206.64.0/18', + '35.208.0.0/15', + '35.220.64.0/19', + '35.222.0.0/15', + '35.224.0.0/15', + '35.226.0.0/16', + '35.232.0.0/16', + '35.238.0.0/15', + '35.242.96.0/19', + '104.154.16.0/20', + '104.154.32.0/19', + '104.154.64.0/19', + '104.154.96.0/20', + '104.154.113.0/24', + '104.154.114.0/23', + '104.154.116.0/22', + '104.154.120.0/23', + '104.154.128.0/17', + '104.155.128.0/18', + '104.197.0.0/16', + '104.198.16.0/20', + '104.198.32.0/19', + '104.198.64.0/20', + '104.198.128.0/17', + '107.178.208.0/20', + '108.59.80.0/21', + '130.211.112.0/20', + '130.211.128.0/18', + '130.211.192.0/19', + '130.211.224.0/20', + '146.148.32.0/19', + '146.148.64.0/19', + '146.148.96.0/20', + '162.222.176.0/21', + '173.255.112.0/21', + '199.192.115.0/24', + '199.223.232.0/22', + '199.223.236.0/24', + '34.22.0.0/19', + '35.186.0.0/17', + '35.186.128.0/20', + '35.206.32.0/19', + '35.220.46.0/24', + '35.242.46.0/24', + '107.167.160.0/20', + '108.59.88.0/21', + '173.255.120.0/21', + '34.23.0.0/16', + '34.24.0.0/15', + '34.26.0.0/16', + '34.73.0.0/16', + '34.74.0.0/15', + '34.98.128.0/21', + '34.118.250.0/23', + '34.138.0.0/15', + '34.148.0.0/16', + '35.185.0.0/17', + '35.190.128.0/18', + '35.196.0.0/16', + '35.207.0.0/18', + '35.211.0.0/16', + '35.220.0.0/20', + '35.227.0.0/17', + '35.229.16.0/20', + '35.229.32.0/19', + '35.229.64.0/18', + '35.231.0.0/16', + '35.237.0.0/16', + '35.242.0.0/20', + '35.243.128.0/17', + '104.196.0.0/18', + '104.196.65.0/24', + '104.196.66.0/23', + '104.196.68.0/22', + '104.196.96.0/19', + '104.196.128.0/18', + '104.196.192.0/19', + '162.216.148.0/22', + '34.21.0.0/17', + '34.85.128.0/17', + '34.86.0.0/16', + '34.104.60.0/23', + '34.104.124.0/23', + '34.118.252.0/23', + '34.124.60.0/23', + '34.127.188.0/23', + '34.145.128.0/17', + '34.150.128.0/17', + '34.157.0.0/21', + '34.157.16.0/20', + '34.157.128.0/21', + '34.157.144.0/20', + '35.186.160.0/19', + '35.188.224.0/19', + '35.194.64.0/19', + '35.199.0.0/18', + '35.212.0.0/17', + '35.220.60.0/22', + '35.221.0.0/18', + '35.230.160.0/19', + '35.234.176.0/20', + '35.236.192.0/18', + '35.242.60.0/22', + '35.243.40.0/21', + '35.245.0.0/16', + '34.157.32.0/22', + '34.157.160.0/22', + '34.162.0.0/16', + '34.104.56.0/23', + '34.127.184.0/23', + '34.161.0.0/16', + '35.206.10.0/23', + '34.157.46.0/23', + '34.157.174.0/23', + '34.174.0.0/16', + '34.19.0.0/17', + '34.82.0.0/15', + '34.105.0.0/17', + '34.118.192.0/21', + '34.127.0.0/17', + '34.145.0.0/17', + '34.157.112.0/21', + '34.157.240.0/21', + '34.168.0.0/15', + '35.185.192.0/18', + '35.197.0.0/17', + '35.199.144.0/20', + '35.199.160.0/19', + '35.203.128.0/18', + '35.212.128.0/17', + '35.220.48.0/21', + '35.227.128.0/18', + '35.230.0.0/17', + '35.233.128.0/17', + '35.242.48.0/21', + '35.243.32.0/21', + '35.247.0.0/17', + '104.196.224.0/19', + '104.198.0.0/20', + '104.198.96.0/20', + '104.199.112.0/20', + '34.20.128.0/17', + '34.94.0.0/16', + '34.102.0.0/17', + '34.104.64.0/21', + '34.108.0.0/16', + '34.118.248.0/23', + '34.124.0.0/21', + '35.215.64.0/18', + '35.220.47.0/24', + '35.235.64.0/18', + '35.236.0.0/17', + '35.242.47.0/24', + '35.243.0.0/21', + '34.22.32.0/19', + '34.104.52.0/24', + '34.106.0.0/16', + '34.127.180.0/24', + '35.217.64.0/18', + '35.220.31.0/24', + '35.242.31.0/24', + '34.16.128.0/17', + '34.104.72.0/22', + '34.118.240.0/22', + '34.124.8.0/22', + '34.125.0.0/16', + '35.219.128.0/18', +]; diff --git a/frontend/src/app/consts/languages.ts b/frontend/src/app/consts/languages.ts index a198a147f..7db4c4387 100644 --- a/frontend/src/app/consts/languages.ts +++ b/frontend/src/app/consts/languages.ts @@ -1,201 +1,201 @@ export interface Language { - code: string; - name: string; - nativeName?: string; - flagCountryCode?: string; // ISO 3166-1 alpha-2 country code for flag emoji + code: string; + name: string; + nativeName?: string; + flagCountryCode?: string; // ISO 3166-1 alpha-2 country code for flag emoji } export function getLanguageFlag(language: Language): string { - if (!language.flagCountryCode || language.flagCountryCode.length !== 2) return ''; - const codePoints = language.flagCountryCode - .toUpperCase() - .split('') - .map(char => 127397 + char.charCodeAt(0)); - return String.fromCodePoint(...codePoints); + if (!language.flagCountryCode || language.flagCountryCode.length !== 2) return ''; + const codePoints = language.flagCountryCode + .toUpperCase() + .split('') + .map((char) => 127397 + char.charCodeAt(0)); + return String.fromCodePoint(...codePoints); } export const LANGUAGES: Language[] = [ - { code: 'aa', name: 'Afar', nativeName: 'Afaraf', flagCountryCode: 'ET' }, - { code: 'ab', name: 'Abkhazian', nativeName: 'аҧсуа бызшәа', flagCountryCode: 'GE' }, - { code: 'af', name: 'Afrikaans', nativeName: 'Afrikaans', flagCountryCode: 'ZA' }, - { code: 'ak', name: 'Akan', nativeName: 'Akan', flagCountryCode: 'GH' }, - { code: 'am', name: 'Amharic', nativeName: 'አማርኛ', flagCountryCode: 'ET' }, - { code: 'an', name: 'Aragonese', nativeName: 'aragonés', flagCountryCode: 'ES' }, - { code: 'ar', name: 'Arabic', nativeName: 'العربية', flagCountryCode: 'SA' }, - { code: 'as', name: 'Assamese', nativeName: 'অসমীয়া', flagCountryCode: 'IN' }, - { code: 'av', name: 'Avaric', nativeName: 'авар мацӀ', flagCountryCode: 'RU' }, - { code: 'ay', name: 'Aymara', nativeName: 'aymar aru', flagCountryCode: 'BO' }, - { code: 'az', name: 'Azerbaijani', nativeName: 'azərbaycan dili', flagCountryCode: 'AZ' }, - { code: 'ba', name: 'Bashkir', nativeName: 'башҡорт теле', flagCountryCode: 'RU' }, - { code: 'be', name: 'Belarusian', nativeName: 'беларуская мова', flagCountryCode: 'BY' }, - { code: 'bg', name: 'Bulgarian', nativeName: 'български език', flagCountryCode: 'BG' }, - { code: 'bh', name: 'Bihari languages', nativeName: 'भोजपुरी', flagCountryCode: 'IN' }, - { code: 'bi', name: 'Bislama', nativeName: 'Bislama', flagCountryCode: 'VU' }, - { code: 'bm', name: 'Bambara', nativeName: 'bamanankan', flagCountryCode: 'ML' }, - { code: 'bn', name: 'Bengali', nativeName: 'বাংলা', flagCountryCode: 'BD' }, - { code: 'bo', name: 'Tibetan', nativeName: 'བོད་ཡིག', flagCountryCode: 'CN' }, - { code: 'br', name: 'Breton', nativeName: 'brezhoneg', flagCountryCode: 'FR' }, - { code: 'bs', name: 'Bosnian', nativeName: 'bosanski jezik', flagCountryCode: 'BA' }, - { code: 'ca', name: 'Catalan', nativeName: 'català', flagCountryCode: 'ES' }, - { code: 'ce', name: 'Chechen', nativeName: 'нохчийн мott', flagCountryCode: 'RU' }, - { code: 'ch', name: 'Chamorro', nativeName: 'Chamoru', flagCountryCode: 'GU' }, - { code: 'co', name: 'Corsican', nativeName: 'corsu', flagCountryCode: 'FR' }, - { code: 'cr', name: 'Cree', nativeName: 'ᓀᐦᐃᔭᐍᐏᐣ', flagCountryCode: 'CA' }, - { code: 'cs', name: 'Czech', nativeName: 'čeština', flagCountryCode: 'CZ' }, - { code: 'cu', name: 'Church Slavonic', nativeName: 'ѩзыкъ словѣньскъ', flagCountryCode: 'BG' }, - { code: 'cv', name: 'Chuvash', nativeName: 'чӑваш чӗлхи', flagCountryCode: 'RU' }, - { code: 'cy', name: 'Welsh', nativeName: 'Cymraeg', flagCountryCode: 'GB' }, - { code: 'da', name: 'Danish', nativeName: 'dansk', flagCountryCode: 'DK' }, - { code: 'de', name: 'German', nativeName: 'Deutsch', flagCountryCode: 'DE' }, - { code: 'dv', name: 'Divehi', nativeName: 'ދިވެހި', flagCountryCode: 'MV' }, - { code: 'dz', name: 'Dzongkha', nativeName: 'རྫོང་ཁ', flagCountryCode: 'BT' }, - { code: 'ee', name: 'Ewe', nativeName: 'Eʋegbe', flagCountryCode: 'GH' }, - { code: 'el', name: 'Greek', nativeName: 'Ελληνικά', flagCountryCode: 'GR' }, - { code: 'en', name: 'English', nativeName: 'English', flagCountryCode: 'GB' }, - { code: 'eo', name: 'Esperanto', nativeName: 'Esperanto', flagCountryCode: 'EU' }, - { code: 'es', name: 'Spanish', nativeName: 'español', flagCountryCode: 'ES' }, - { code: 'et', name: 'Estonian', nativeName: 'eesti', flagCountryCode: 'EE' }, - { code: 'eu', name: 'Basque', nativeName: 'euskara', flagCountryCode: 'ES' }, - { code: 'fa', name: 'Persian', nativeName: 'فارسی', flagCountryCode: 'IR' }, - { code: 'ff', name: 'Fulah', nativeName: 'Fulfulde', flagCountryCode: 'SN' }, - { code: 'fi', name: 'Finnish', nativeName: 'suomi', flagCountryCode: 'FI' }, - { code: 'fj', name: 'Fijian', nativeName: 'vosa Vakaviti', flagCountryCode: 'FJ' }, - { code: 'fo', name: 'Faroese', nativeName: 'føroyskt', flagCountryCode: 'FO' }, - { code: 'fr', name: 'French', nativeName: 'français', flagCountryCode: 'FR' }, - { code: 'fy', name: 'Western Frisian', nativeName: 'Frysk', flagCountryCode: 'NL' }, - { code: 'ga', name: 'Irish', nativeName: 'Gaeilge', flagCountryCode: 'IE' }, - { code: 'gd', name: 'Scottish Gaelic', nativeName: 'Gàidhlig', flagCountryCode: 'GB' }, - { code: 'gl', name: 'Galician', nativeName: 'galego', flagCountryCode: 'ES' }, - { code: 'gn', name: 'Guarani', nativeName: "Avañe'ẽ", flagCountryCode: 'PY' }, - { code: 'gu', name: 'Gujarati', nativeName: 'ગુજરાતી', flagCountryCode: 'IN' }, - { code: 'gv', name: 'Manx', nativeName: 'Gaelg', flagCountryCode: 'IM' }, - { code: 'ha', name: 'Hausa', nativeName: 'هَوُسَ', flagCountryCode: 'NG' }, - { code: 'he', name: 'Hebrew', nativeName: 'עברית', flagCountryCode: 'IL' }, - { code: 'hi', name: 'Hindi', nativeName: 'हिन्दी', flagCountryCode: 'IN' }, - { code: 'ho', name: 'Hiri Motu', nativeName: 'Hiri Motu', flagCountryCode: 'PG' }, - { code: 'hr', name: 'Croatian', nativeName: 'hrvatski jezik', flagCountryCode: 'HR' }, - { code: 'ht', name: 'Haitian', nativeName: 'Kreyòl ayisyen', flagCountryCode: 'HT' }, - { code: 'hu', name: 'Hungarian', nativeName: 'magyar', flagCountryCode: 'HU' }, - { code: 'hy', name: 'Armenian', nativeName: 'Հայերեն', flagCountryCode: 'AM' }, - { code: 'hz', name: 'Herero', nativeName: 'Otjiherero', flagCountryCode: 'NA' }, - { code: 'ia', name: 'Interlingua', nativeName: 'Interlingua', flagCountryCode: 'EU' }, - { code: 'id', name: 'Indonesian', nativeName: 'Bahasa Indonesia', flagCountryCode: 'ID' }, - { code: 'ie', name: 'Interlingue', nativeName: 'Interlingue', flagCountryCode: 'EU' }, - { code: 'ig', name: 'Igbo', nativeName: 'Asụsụ Igbo', flagCountryCode: 'NG' }, - { code: 'ii', name: 'Sichuan Yi', nativeName: 'ꆈꌠ꒿', flagCountryCode: 'CN' }, - { code: 'ik', name: 'Inupiaq', nativeName: 'Iñupiaq', flagCountryCode: 'US' }, - { code: 'io', name: 'Ido', nativeName: 'Ido', flagCountryCode: 'EU' }, - { code: 'is', name: 'Icelandic', nativeName: 'Íslenska', flagCountryCode: 'IS' }, - { code: 'it', name: 'Italian', nativeName: 'italiano', flagCountryCode: 'IT' }, - { code: 'iu', name: 'Inuktitut', nativeName: 'ᐃᓄᒃᑎᑐᑦ', flagCountryCode: 'CA' }, - { code: 'ja', name: 'Japanese', nativeName: '日本語', flagCountryCode: 'JP' }, - { code: 'jv', name: 'Javanese', nativeName: 'ꦧꦱꦗꦮ', flagCountryCode: 'ID' }, - { code: 'ka', name: 'Georgian', nativeName: 'ქართული', flagCountryCode: 'GE' }, - { code: 'kg', name: 'Kongo', nativeName: 'Kikongo', flagCountryCode: 'CD' }, - { code: 'ki', name: 'Kikuyu', nativeName: 'Gĩkũyũ', flagCountryCode: 'KE' }, - { code: 'kj', name: 'Kuanyama', nativeName: 'Kuanyama', flagCountryCode: 'AO' }, - { code: 'kk', name: 'Kazakh', nativeName: 'қазақ тілі', flagCountryCode: 'KZ' }, - { code: 'kl', name: 'Kalaallisut', nativeName: 'kalaallisut', flagCountryCode: 'GL' }, - { code: 'km', name: 'Central Khmer', nativeName: 'ខេមរភាសា', flagCountryCode: 'KH' }, - { code: 'kn', name: 'Kannada', nativeName: 'ಕನ್ನಡ', flagCountryCode: 'IN' }, - { code: 'ko', name: 'Korean', nativeName: '한국어', flagCountryCode: 'KR' }, - { code: 'kr', name: 'Kanuri', nativeName: 'Kanuri', flagCountryCode: 'NG' }, - { code: 'ks', name: 'Kashmiri', nativeName: 'कश्मीरी', flagCountryCode: 'IN' }, - { code: 'ku', name: 'Kurdish', nativeName: 'Kurdî', flagCountryCode: 'IQ' }, - { code: 'kv', name: 'Komi', nativeName: 'коми кыв', flagCountryCode: 'RU' }, - { code: 'kw', name: 'Cornish', nativeName: 'Kernewek', flagCountryCode: 'GB' }, - { code: 'ky', name: 'Kirghiz', nativeName: 'Кыргызча', flagCountryCode: 'KG' }, - { code: 'la', name: 'Latin', nativeName: 'latine', flagCountryCode: 'VA' }, - { code: 'lb', name: 'Luxembourgish', nativeName: 'Lëtzebuergesch', flagCountryCode: 'LU' }, - { code: 'lg', name: 'Ganda', nativeName: 'Luganda', flagCountryCode: 'UG' }, - { code: 'li', name: 'Limburgan', nativeName: 'Limburgs', flagCountryCode: 'NL' }, - { code: 'ln', name: 'Lingala', nativeName: 'Lingála', flagCountryCode: 'CD' }, - { code: 'lo', name: 'Lao', nativeName: 'ພາສາລາວ', flagCountryCode: 'LA' }, - { code: 'lt', name: 'Lithuanian', nativeName: 'lietuvių kalba', flagCountryCode: 'LT' }, - { code: 'lu', name: 'Luba-Katanga', nativeName: 'Tshiluba', flagCountryCode: 'CD' }, - { code: 'lv', name: 'Latvian', nativeName: 'latviešu valoda', flagCountryCode: 'LV' }, - { code: 'mg', name: 'Malagasy', nativeName: 'fiteny malagasy', flagCountryCode: 'MG' }, - { code: 'mh', name: 'Marshallese', nativeName: 'Kajin M̧ajeļ', flagCountryCode: 'MH' }, - { code: 'mi', name: 'Maori', nativeName: 'te reo Māori', flagCountryCode: 'NZ' }, - { code: 'mk', name: 'Macedonian', nativeName: 'македонски јазик', flagCountryCode: 'MK' }, - { code: 'ml', name: 'Malayalam', nativeName: 'മലയാളം', flagCountryCode: 'IN' }, - { code: 'mn', name: 'Mongolian', nativeName: 'Монгол хэл', flagCountryCode: 'MN' }, - { code: 'mr', name: 'Marathi', nativeName: 'मराठी', flagCountryCode: 'IN' }, - { code: 'ms', name: 'Malay', nativeName: 'Bahasa Melayu', flagCountryCode: 'MY' }, - { code: 'mt', name: 'Maltese', nativeName: 'Malti', flagCountryCode: 'MT' }, - { code: 'my', name: 'Burmese', nativeName: 'ဗမာစာ', flagCountryCode: 'MM' }, - { code: 'na', name: 'Nauru', nativeName: 'Dorerin Naoero', flagCountryCode: 'NR' }, - { code: 'nb', name: 'Norwegian Bokmål', nativeName: 'Norsk bokmål', flagCountryCode: 'NO' }, - { code: 'nd', name: 'North Ndebele', nativeName: 'isiNdebele', flagCountryCode: 'ZW' }, - { code: 'ne', name: 'Nepali', nativeName: 'नेपाली', flagCountryCode: 'NP' }, - { code: 'ng', name: 'Ndonga', nativeName: 'Owambo', flagCountryCode: 'NA' }, - { code: 'nl', name: 'Dutch', nativeName: 'Nederlands', flagCountryCode: 'NL' }, - { code: 'nn', name: 'Norwegian Nynorsk', nativeName: 'Norsk nynorsk', flagCountryCode: 'NO' }, - { code: 'no', name: 'Norwegian', nativeName: 'Norsk', flagCountryCode: 'NO' }, - { code: 'nr', name: 'South Ndebele', nativeName: 'isiNdebele', flagCountryCode: 'ZA' }, - { code: 'nv', name: 'Navajo', nativeName: 'Diné bizaad', flagCountryCode: 'US' }, - { code: 'ny', name: 'Chichewa', nativeName: 'chiCheŵa', flagCountryCode: 'MW' }, - { code: 'oc', name: 'Occitan', nativeName: 'occitan', flagCountryCode: 'FR' }, - { code: 'oj', name: 'Ojibwa', nativeName: 'ᐊᓂᔑᓈᐯᒧᐎᓐ', flagCountryCode: 'CA' }, - { code: 'om', name: 'Oromo', nativeName: 'Afaan Oromoo', flagCountryCode: 'ET' }, - { code: 'or', name: 'Oriya', nativeName: 'ଓଡ଼ିଆ', flagCountryCode: 'IN' }, - { code: 'os', name: 'Ossetian', nativeName: 'ирон æвзаг', flagCountryCode: 'GE' }, - { code: 'pa', name: 'Punjabi', nativeName: 'ਪੰਜਾਬੀ', flagCountryCode: 'IN' }, - { code: 'pi', name: 'Pali', nativeName: 'पाऴि', flagCountryCode: 'IN' }, - { code: 'pl', name: 'Polish', nativeName: 'polski', flagCountryCode: 'PL' }, - { code: 'ps', name: 'Pashto', nativeName: 'پښتو', flagCountryCode: 'AF' }, - { code: 'pt', name: 'Portuguese', nativeName: 'português', flagCountryCode: 'PT' }, - { code: 'qu', name: 'Quechua', nativeName: 'Runa Simi', flagCountryCode: 'PE' }, - { code: 'rm', name: 'Romansh', nativeName: 'rumantsch grischun', flagCountryCode: 'CH' }, - { code: 'rn', name: 'Rundi', nativeName: 'Ikirundi', flagCountryCode: 'BI' }, - { code: 'ro', name: 'Romanian', nativeName: 'română', flagCountryCode: 'RO' }, - { code: 'ru', name: 'Russian', nativeName: 'русский', flagCountryCode: 'RU' }, - { code: 'rw', name: 'Kinyarwanda', nativeName: 'Ikinyarwanda', flagCountryCode: 'RW' }, - { code: 'sa', name: 'Sanskrit', nativeName: 'संस्कृतम्', flagCountryCode: 'IN' }, - { code: 'sc', name: 'Sardinian', nativeName: 'sardu', flagCountryCode: 'IT' }, - { code: 'sd', name: 'Sindhi', nativeName: 'सिन्धी', flagCountryCode: 'PK' }, - { code: 'se', name: 'Northern Sami', nativeName: 'Davvisámegiella', flagCountryCode: 'NO' }, - { code: 'sg', name: 'Sango', nativeName: 'yângâ tî sängö', flagCountryCode: 'CF' }, - { code: 'si', name: 'Sinhala', nativeName: 'සිංහල', flagCountryCode: 'LK' }, - { code: 'sk', name: 'Slovak', nativeName: 'slovenčina', flagCountryCode: 'SK' }, - { code: 'sl', name: 'Slovenian', nativeName: 'slovenski jezik', flagCountryCode: 'SI' }, - { code: 'sm', name: 'Samoan', nativeName: 'gagana fa\'a Samoa', flagCountryCode: 'WS' }, - { code: 'sn', name: 'Shona', nativeName: 'chiShona', flagCountryCode: 'ZW' }, - { code: 'so', name: 'Somali', nativeName: 'Soomaaliga', flagCountryCode: 'SO' }, - { code: 'sq', name: 'Albanian', nativeName: 'Shqip', flagCountryCode: 'AL' }, - { code: 'sr', name: 'Serbian', nativeName: 'српски језик', flagCountryCode: 'RS' }, - { code: 'ss', name: 'Swati', nativeName: 'SiSwati', flagCountryCode: 'SZ' }, - { code: 'st', name: 'Southern Sotho', nativeName: 'Sesotho', flagCountryCode: 'LS' }, - { code: 'su', name: 'Sundanese', nativeName: 'Basa Sunda', flagCountryCode: 'ID' }, - { code: 'sv', name: 'Swedish', nativeName: 'svenska', flagCountryCode: 'SE' }, - { code: 'sw', name: 'Swahili', nativeName: 'Kiswahili', flagCountryCode: 'TZ' }, - { code: 'ta', name: 'Tamil', nativeName: 'தமிழ்', flagCountryCode: 'IN' }, - { code: 'te', name: 'Telugu', nativeName: 'తెలుగు', flagCountryCode: 'IN' }, - { code: 'tg', name: 'Tajik', nativeName: 'тоҷикӣ', flagCountryCode: 'TJ' }, - { code: 'th', name: 'Thai', nativeName: 'ไทย', flagCountryCode: 'TH' }, - { code: 'ti', name: 'Tigrinya', nativeName: 'ትግርኛ', flagCountryCode: 'ER' }, - { code: 'tk', name: 'Turkmen', nativeName: 'Türkmen', flagCountryCode: 'TM' }, - { code: 'tl', name: 'Tagalog', nativeName: 'Wikang Tagalog', flagCountryCode: 'PH' }, - { code: 'tn', name: 'Tswana', nativeName: 'Setswana', flagCountryCode: 'BW' }, - { code: 'to', name: 'Tonga', nativeName: 'faka Tonga', flagCountryCode: 'TO' }, - { code: 'tr', name: 'Turkish', nativeName: 'Türkçe', flagCountryCode: 'TR' }, - { code: 'ts', name: 'Tsonga', nativeName: 'Xitsonga', flagCountryCode: 'ZA' }, - { code: 'tt', name: 'Tatar', nativeName: 'татар теле', flagCountryCode: 'RU' }, - { code: 'tw', name: 'Twi', nativeName: 'Twi', flagCountryCode: 'GH' }, - { code: 'ty', name: 'Tahitian', nativeName: 'Reo Tahiti', flagCountryCode: 'PF' }, - { code: 'ug', name: 'Uighur', nativeName: 'ئۇيغۇرچە', flagCountryCode: 'CN' }, - { code: 'uk', name: 'Ukrainian', nativeName: 'Українська', flagCountryCode: 'UA' }, - { code: 'ur', name: 'Urdu', nativeName: 'اردو', flagCountryCode: 'PK' }, - { code: 'uz', name: 'Uzbek', nativeName: 'Oʻzbek', flagCountryCode: 'UZ' }, - { code: 've', name: 'Venda', nativeName: 'Tshivenḓa', flagCountryCode: 'ZA' }, - { code: 'vi', name: 'Vietnamese', nativeName: 'Tiếng Việt', flagCountryCode: 'VN' }, - { code: 'vo', name: 'Volapük', nativeName: 'Volapük', flagCountryCode: 'EU' }, - { code: 'wa', name: 'Walloon', nativeName: 'walon', flagCountryCode: 'BE' }, - { code: 'wo', name: 'Wolof', nativeName: 'Wollof', flagCountryCode: 'SN' }, - { code: 'xh', name: 'Xhosa', nativeName: 'isiXhosa', flagCountryCode: 'ZA' }, - { code: 'yi', name: 'Yiddish', nativeName: 'ייִדיש', flagCountryCode: 'IL' }, - { code: 'yo', name: 'Yoruba', nativeName: 'Yorùbá', flagCountryCode: 'NG' }, - { code: 'za', name: 'Zhuang', nativeName: 'Saɯ cueŋƅ', flagCountryCode: 'CN' }, - { code: 'zh', name: 'Chinese', nativeName: '中文', flagCountryCode: 'CN' }, - { code: 'zu', name: 'Zulu', nativeName: 'isiZulu', flagCountryCode: 'ZA' } + { code: 'aa', name: 'Afar', nativeName: 'Afaraf', flagCountryCode: 'ET' }, + { code: 'ab', name: 'Abkhazian', nativeName: 'аҧсуа бызшәа', flagCountryCode: 'GE' }, + { code: 'af', name: 'Afrikaans', nativeName: 'Afrikaans', flagCountryCode: 'ZA' }, + { code: 'ak', name: 'Akan', nativeName: 'Akan', flagCountryCode: 'GH' }, + { code: 'am', name: 'Amharic', nativeName: 'አማርኛ', flagCountryCode: 'ET' }, + { code: 'an', name: 'Aragonese', nativeName: 'aragonés', flagCountryCode: 'ES' }, + { code: 'ar', name: 'Arabic', nativeName: 'العربية', flagCountryCode: 'SA' }, + { code: 'as', name: 'Assamese', nativeName: 'অসমীয়া', flagCountryCode: 'IN' }, + { code: 'av', name: 'Avaric', nativeName: 'авар мацӀ', flagCountryCode: 'RU' }, + { code: 'ay', name: 'Aymara', nativeName: 'aymar aru', flagCountryCode: 'BO' }, + { code: 'az', name: 'Azerbaijani', nativeName: 'azərbaycan dili', flagCountryCode: 'AZ' }, + { code: 'ba', name: 'Bashkir', nativeName: 'башҡорт теле', flagCountryCode: 'RU' }, + { code: 'be', name: 'Belarusian', nativeName: 'беларуская мова', flagCountryCode: 'BY' }, + { code: 'bg', name: 'Bulgarian', nativeName: 'български език', flagCountryCode: 'BG' }, + { code: 'bh', name: 'Bihari languages', nativeName: 'भोजपुरी', flagCountryCode: 'IN' }, + { code: 'bi', name: 'Bislama', nativeName: 'Bislama', flagCountryCode: 'VU' }, + { code: 'bm', name: 'Bambara', nativeName: 'bamanankan', flagCountryCode: 'ML' }, + { code: 'bn', name: 'Bengali', nativeName: 'বাংলা', flagCountryCode: 'BD' }, + { code: 'bo', name: 'Tibetan', nativeName: 'བོད་ཡིག', flagCountryCode: 'CN' }, + { code: 'br', name: 'Breton', nativeName: 'brezhoneg', flagCountryCode: 'FR' }, + { code: 'bs', name: 'Bosnian', nativeName: 'bosanski jezik', flagCountryCode: 'BA' }, + { code: 'ca', name: 'Catalan', nativeName: 'català', flagCountryCode: 'ES' }, + { code: 'ce', name: 'Chechen', nativeName: 'нохчийн мott', flagCountryCode: 'RU' }, + { code: 'ch', name: 'Chamorro', nativeName: 'Chamoru', flagCountryCode: 'GU' }, + { code: 'co', name: 'Corsican', nativeName: 'corsu', flagCountryCode: 'FR' }, + { code: 'cr', name: 'Cree', nativeName: 'ᓀᐦᐃᔭᐍᐏᐣ', flagCountryCode: 'CA' }, + { code: 'cs', name: 'Czech', nativeName: 'čeština', flagCountryCode: 'CZ' }, + { code: 'cu', name: 'Church Slavonic', nativeName: 'ѩзыкъ словѣньскъ', flagCountryCode: 'BG' }, + { code: 'cv', name: 'Chuvash', nativeName: 'чӑваш чӗлхи', flagCountryCode: 'RU' }, + { code: 'cy', name: 'Welsh', nativeName: 'Cymraeg', flagCountryCode: 'GB' }, + { code: 'da', name: 'Danish', nativeName: 'dansk', flagCountryCode: 'DK' }, + { code: 'de', name: 'German', nativeName: 'Deutsch', flagCountryCode: 'DE' }, + { code: 'dv', name: 'Divehi', nativeName: 'ދިވެހި', flagCountryCode: 'MV' }, + { code: 'dz', name: 'Dzongkha', nativeName: 'རྫོང་ཁ', flagCountryCode: 'BT' }, + { code: 'ee', name: 'Ewe', nativeName: 'Eʋegbe', flagCountryCode: 'GH' }, + { code: 'el', name: 'Greek', nativeName: 'Ελληνικά', flagCountryCode: 'GR' }, + { code: 'en', name: 'English', nativeName: 'English', flagCountryCode: 'GB' }, + { code: 'eo', name: 'Esperanto', nativeName: 'Esperanto', flagCountryCode: 'EU' }, + { code: 'es', name: 'Spanish', nativeName: 'español', flagCountryCode: 'ES' }, + { code: 'et', name: 'Estonian', nativeName: 'eesti', flagCountryCode: 'EE' }, + { code: 'eu', name: 'Basque', nativeName: 'euskara', flagCountryCode: 'ES' }, + { code: 'fa', name: 'Persian', nativeName: 'فارسی', flagCountryCode: 'IR' }, + { code: 'ff', name: 'Fulah', nativeName: 'Fulfulde', flagCountryCode: 'SN' }, + { code: 'fi', name: 'Finnish', nativeName: 'suomi', flagCountryCode: 'FI' }, + { code: 'fj', name: 'Fijian', nativeName: 'vosa Vakaviti', flagCountryCode: 'FJ' }, + { code: 'fo', name: 'Faroese', nativeName: 'føroyskt', flagCountryCode: 'FO' }, + { code: 'fr', name: 'French', nativeName: 'français', flagCountryCode: 'FR' }, + { code: 'fy', name: 'Western Frisian', nativeName: 'Frysk', flagCountryCode: 'NL' }, + { code: 'ga', name: 'Irish', nativeName: 'Gaeilge', flagCountryCode: 'IE' }, + { code: 'gd', name: 'Scottish Gaelic', nativeName: 'Gàidhlig', flagCountryCode: 'GB' }, + { code: 'gl', name: 'Galician', nativeName: 'galego', flagCountryCode: 'ES' }, + { code: 'gn', name: 'Guarani', nativeName: "Avañe'ẽ", flagCountryCode: 'PY' }, + { code: 'gu', name: 'Gujarati', nativeName: 'ગુજરાતી', flagCountryCode: 'IN' }, + { code: 'gv', name: 'Manx', nativeName: 'Gaelg', flagCountryCode: 'IM' }, + { code: 'ha', name: 'Hausa', nativeName: 'هَوُسَ', flagCountryCode: 'NG' }, + { code: 'he', name: 'Hebrew', nativeName: 'עברית', flagCountryCode: 'IL' }, + { code: 'hi', name: 'Hindi', nativeName: 'हिन्दी', flagCountryCode: 'IN' }, + { code: 'ho', name: 'Hiri Motu', nativeName: 'Hiri Motu', flagCountryCode: 'PG' }, + { code: 'hr', name: 'Croatian', nativeName: 'hrvatski jezik', flagCountryCode: 'HR' }, + { code: 'ht', name: 'Haitian', nativeName: 'Kreyòl ayisyen', flagCountryCode: 'HT' }, + { code: 'hu', name: 'Hungarian', nativeName: 'magyar', flagCountryCode: 'HU' }, + { code: 'hy', name: 'Armenian', nativeName: 'Հայերեն', flagCountryCode: 'AM' }, + { code: 'hz', name: 'Herero', nativeName: 'Otjiherero', flagCountryCode: 'NA' }, + { code: 'ia', name: 'Interlingua', nativeName: 'Interlingua', flagCountryCode: 'EU' }, + { code: 'id', name: 'Indonesian', nativeName: 'Bahasa Indonesia', flagCountryCode: 'ID' }, + { code: 'ie', name: 'Interlingue', nativeName: 'Interlingue', flagCountryCode: 'EU' }, + { code: 'ig', name: 'Igbo', nativeName: 'Asụsụ Igbo', flagCountryCode: 'NG' }, + { code: 'ii', name: 'Sichuan Yi', nativeName: 'ꆈꌠ꒿', flagCountryCode: 'CN' }, + { code: 'ik', name: 'Inupiaq', nativeName: 'Iñupiaq', flagCountryCode: 'US' }, + { code: 'io', name: 'Ido', nativeName: 'Ido', flagCountryCode: 'EU' }, + { code: 'is', name: 'Icelandic', nativeName: 'Íslenska', flagCountryCode: 'IS' }, + { code: 'it', name: 'Italian', nativeName: 'italiano', flagCountryCode: 'IT' }, + { code: 'iu', name: 'Inuktitut', nativeName: 'ᐃᓄᒃᑎᑐᑦ', flagCountryCode: 'CA' }, + { code: 'ja', name: 'Japanese', nativeName: '日本語', flagCountryCode: 'JP' }, + { code: 'jv', name: 'Javanese', nativeName: 'ꦧꦱꦗꦮ', flagCountryCode: 'ID' }, + { code: 'ka', name: 'Georgian', nativeName: 'ქართული', flagCountryCode: 'GE' }, + { code: 'kg', name: 'Kongo', nativeName: 'Kikongo', flagCountryCode: 'CD' }, + { code: 'ki', name: 'Kikuyu', nativeName: 'Gĩkũyũ', flagCountryCode: 'KE' }, + { code: 'kj', name: 'Kuanyama', nativeName: 'Kuanyama', flagCountryCode: 'AO' }, + { code: 'kk', name: 'Kazakh', nativeName: 'қазақ тілі', flagCountryCode: 'KZ' }, + { code: 'kl', name: 'Kalaallisut', nativeName: 'kalaallisut', flagCountryCode: 'GL' }, + { code: 'km', name: 'Central Khmer', nativeName: 'ខេមរភាសា', flagCountryCode: 'KH' }, + { code: 'kn', name: 'Kannada', nativeName: 'ಕನ್ನಡ', flagCountryCode: 'IN' }, + { code: 'ko', name: 'Korean', nativeName: '한국어', flagCountryCode: 'KR' }, + { code: 'kr', name: 'Kanuri', nativeName: 'Kanuri', flagCountryCode: 'NG' }, + { code: 'ks', name: 'Kashmiri', nativeName: 'कश्मीरी', flagCountryCode: 'IN' }, + { code: 'ku', name: 'Kurdish', nativeName: 'Kurdî', flagCountryCode: 'IQ' }, + { code: 'kv', name: 'Komi', nativeName: 'коми кыв', flagCountryCode: 'RU' }, + { code: 'kw', name: 'Cornish', nativeName: 'Kernewek', flagCountryCode: 'GB' }, + { code: 'ky', name: 'Kirghiz', nativeName: 'Кыргызча', flagCountryCode: 'KG' }, + { code: 'la', name: 'Latin', nativeName: 'latine', flagCountryCode: 'VA' }, + { code: 'lb', name: 'Luxembourgish', nativeName: 'Lëtzebuergesch', flagCountryCode: 'LU' }, + { code: 'lg', name: 'Ganda', nativeName: 'Luganda', flagCountryCode: 'UG' }, + { code: 'li', name: 'Limburgan', nativeName: 'Limburgs', flagCountryCode: 'NL' }, + { code: 'ln', name: 'Lingala', nativeName: 'Lingála', flagCountryCode: 'CD' }, + { code: 'lo', name: 'Lao', nativeName: 'ພາສາລາວ', flagCountryCode: 'LA' }, + { code: 'lt', name: 'Lithuanian', nativeName: 'lietuvių kalba', flagCountryCode: 'LT' }, + { code: 'lu', name: 'Luba-Katanga', nativeName: 'Tshiluba', flagCountryCode: 'CD' }, + { code: 'lv', name: 'Latvian', nativeName: 'latviešu valoda', flagCountryCode: 'LV' }, + { code: 'mg', name: 'Malagasy', nativeName: 'fiteny malagasy', flagCountryCode: 'MG' }, + { code: 'mh', name: 'Marshallese', nativeName: 'Kajin M̧ajeļ', flagCountryCode: 'MH' }, + { code: 'mi', name: 'Maori', nativeName: 'te reo Māori', flagCountryCode: 'NZ' }, + { code: 'mk', name: 'Macedonian', nativeName: 'македонски јазик', flagCountryCode: 'MK' }, + { code: 'ml', name: 'Malayalam', nativeName: 'മലയാളം', flagCountryCode: 'IN' }, + { code: 'mn', name: 'Mongolian', nativeName: 'Монгол хэл', flagCountryCode: 'MN' }, + { code: 'mr', name: 'Marathi', nativeName: 'मराठी', flagCountryCode: 'IN' }, + { code: 'ms', name: 'Malay', nativeName: 'Bahasa Melayu', flagCountryCode: 'MY' }, + { code: 'mt', name: 'Maltese', nativeName: 'Malti', flagCountryCode: 'MT' }, + { code: 'my', name: 'Burmese', nativeName: 'ဗမာစာ', flagCountryCode: 'MM' }, + { code: 'na', name: 'Nauru', nativeName: 'Dorerin Naoero', flagCountryCode: 'NR' }, + { code: 'nb', name: 'Norwegian Bokmål', nativeName: 'Norsk bokmål', flagCountryCode: 'NO' }, + { code: 'nd', name: 'North Ndebele', nativeName: 'isiNdebele', flagCountryCode: 'ZW' }, + { code: 'ne', name: 'Nepali', nativeName: 'नेपाली', flagCountryCode: 'NP' }, + { code: 'ng', name: 'Ndonga', nativeName: 'Owambo', flagCountryCode: 'NA' }, + { code: 'nl', name: 'Dutch', nativeName: 'Nederlands', flagCountryCode: 'NL' }, + { code: 'nn', name: 'Norwegian Nynorsk', nativeName: 'Norsk nynorsk', flagCountryCode: 'NO' }, + { code: 'no', name: 'Norwegian', nativeName: 'Norsk', flagCountryCode: 'NO' }, + { code: 'nr', name: 'South Ndebele', nativeName: 'isiNdebele', flagCountryCode: 'ZA' }, + { code: 'nv', name: 'Navajo', nativeName: 'Diné bizaad', flagCountryCode: 'US' }, + { code: 'ny', name: 'Chichewa', nativeName: 'chiCheŵa', flagCountryCode: 'MW' }, + { code: 'oc', name: 'Occitan', nativeName: 'occitan', flagCountryCode: 'FR' }, + { code: 'oj', name: 'Ojibwa', nativeName: 'ᐊᓂᔑᓈᐯᒧᐎᓐ', flagCountryCode: 'CA' }, + { code: 'om', name: 'Oromo', nativeName: 'Afaan Oromoo', flagCountryCode: 'ET' }, + { code: 'or', name: 'Oriya', nativeName: 'ଓଡ଼ିଆ', flagCountryCode: 'IN' }, + { code: 'os', name: 'Ossetian', nativeName: 'ирон æвзаг', flagCountryCode: 'GE' }, + { code: 'pa', name: 'Punjabi', nativeName: 'ਪੰਜਾਬੀ', flagCountryCode: 'IN' }, + { code: 'pi', name: 'Pali', nativeName: 'पाऴि', flagCountryCode: 'IN' }, + { code: 'pl', name: 'Polish', nativeName: 'polski', flagCountryCode: 'PL' }, + { code: 'ps', name: 'Pashto', nativeName: 'پښتو', flagCountryCode: 'AF' }, + { code: 'pt', name: 'Portuguese', nativeName: 'português', flagCountryCode: 'PT' }, + { code: 'qu', name: 'Quechua', nativeName: 'Runa Simi', flagCountryCode: 'PE' }, + { code: 'rm', name: 'Romansh', nativeName: 'rumantsch grischun', flagCountryCode: 'CH' }, + { code: 'rn', name: 'Rundi', nativeName: 'Ikirundi', flagCountryCode: 'BI' }, + { code: 'ro', name: 'Romanian', nativeName: 'română', flagCountryCode: 'RO' }, + { code: 'ru', name: 'Russian', nativeName: 'русский', flagCountryCode: 'RU' }, + { code: 'rw', name: 'Kinyarwanda', nativeName: 'Ikinyarwanda', flagCountryCode: 'RW' }, + { code: 'sa', name: 'Sanskrit', nativeName: 'संस्कृतम्', flagCountryCode: 'IN' }, + { code: 'sc', name: 'Sardinian', nativeName: 'sardu', flagCountryCode: 'IT' }, + { code: 'sd', name: 'Sindhi', nativeName: 'सिन्धी', flagCountryCode: 'PK' }, + { code: 'se', name: 'Northern Sami', nativeName: 'Davvisámegiella', flagCountryCode: 'NO' }, + { code: 'sg', name: 'Sango', nativeName: 'yângâ tî sängö', flagCountryCode: 'CF' }, + { code: 'si', name: 'Sinhala', nativeName: 'සිංහල', flagCountryCode: 'LK' }, + { code: 'sk', name: 'Slovak', nativeName: 'slovenčina', flagCountryCode: 'SK' }, + { code: 'sl', name: 'Slovenian', nativeName: 'slovenski jezik', flagCountryCode: 'SI' }, + { code: 'sm', name: 'Samoan', nativeName: "gagana fa'a Samoa", flagCountryCode: 'WS' }, + { code: 'sn', name: 'Shona', nativeName: 'chiShona', flagCountryCode: 'ZW' }, + { code: 'so', name: 'Somali', nativeName: 'Soomaaliga', flagCountryCode: 'SO' }, + { code: 'sq', name: 'Albanian', nativeName: 'Shqip', flagCountryCode: 'AL' }, + { code: 'sr', name: 'Serbian', nativeName: 'српски језик', flagCountryCode: 'RS' }, + { code: 'ss', name: 'Swati', nativeName: 'SiSwati', flagCountryCode: 'SZ' }, + { code: 'st', name: 'Southern Sotho', nativeName: 'Sesotho', flagCountryCode: 'LS' }, + { code: 'su', name: 'Sundanese', nativeName: 'Basa Sunda', flagCountryCode: 'ID' }, + { code: 'sv', name: 'Swedish', nativeName: 'svenska', flagCountryCode: 'SE' }, + { code: 'sw', name: 'Swahili', nativeName: 'Kiswahili', flagCountryCode: 'TZ' }, + { code: 'ta', name: 'Tamil', nativeName: 'தமிழ்', flagCountryCode: 'IN' }, + { code: 'te', name: 'Telugu', nativeName: 'తెలుగు', flagCountryCode: 'IN' }, + { code: 'tg', name: 'Tajik', nativeName: 'тоҷикӣ', flagCountryCode: 'TJ' }, + { code: 'th', name: 'Thai', nativeName: 'ไทย', flagCountryCode: 'TH' }, + { code: 'ti', name: 'Tigrinya', nativeName: 'ትግርኛ', flagCountryCode: 'ER' }, + { code: 'tk', name: 'Turkmen', nativeName: 'Türkmen', flagCountryCode: 'TM' }, + { code: 'tl', name: 'Tagalog', nativeName: 'Wikang Tagalog', flagCountryCode: 'PH' }, + { code: 'tn', name: 'Tswana', nativeName: 'Setswana', flagCountryCode: 'BW' }, + { code: 'to', name: 'Tonga', nativeName: 'faka Tonga', flagCountryCode: 'TO' }, + { code: 'tr', name: 'Turkish', nativeName: 'Türkçe', flagCountryCode: 'TR' }, + { code: 'ts', name: 'Tsonga', nativeName: 'Xitsonga', flagCountryCode: 'ZA' }, + { code: 'tt', name: 'Tatar', nativeName: 'татар теле', flagCountryCode: 'RU' }, + { code: 'tw', name: 'Twi', nativeName: 'Twi', flagCountryCode: 'GH' }, + { code: 'ty', name: 'Tahitian', nativeName: 'Reo Tahiti', flagCountryCode: 'PF' }, + { code: 'ug', name: 'Uighur', nativeName: 'ئۇيغۇرچە', flagCountryCode: 'CN' }, + { code: 'uk', name: 'Ukrainian', nativeName: 'Українська', flagCountryCode: 'UA' }, + { code: 'ur', name: 'Urdu', nativeName: 'اردو', flagCountryCode: 'PK' }, + { code: 'uz', name: 'Uzbek', nativeName: 'Oʻzbek', flagCountryCode: 'UZ' }, + { code: 've', name: 'Venda', nativeName: 'Tshivenḓa', flagCountryCode: 'ZA' }, + { code: 'vi', name: 'Vietnamese', nativeName: 'Tiếng Việt', flagCountryCode: 'VN' }, + { code: 'vo', name: 'Volapük', nativeName: 'Volapük', flagCountryCode: 'EU' }, + { code: 'wa', name: 'Walloon', nativeName: 'walon', flagCountryCode: 'BE' }, + { code: 'wo', name: 'Wolof', nativeName: 'Wollof', flagCountryCode: 'SN' }, + { code: 'xh', name: 'Xhosa', nativeName: 'isiXhosa', flagCountryCode: 'ZA' }, + { code: 'yi', name: 'Yiddish', nativeName: 'ייִדיש', flagCountryCode: 'IL' }, + { code: 'yo', name: 'Yoruba', nativeName: 'Yorùbá', flagCountryCode: 'NG' }, + { code: 'za', name: 'Zhuang', nativeName: 'Saɯ cueŋƅ', flagCountryCode: 'CN' }, + { code: 'zh', name: 'Chinese', nativeName: '中文', flagCountryCode: 'CN' }, + { code: 'zu', name: 'Zulu', nativeName: 'isiZulu', flagCountryCode: 'ZA' }, ]; diff --git a/frontend/src/app/consts/plans.ts b/frontend/src/app/consts/plans.ts index ce713d220..4054c3429 100644 --- a/frontend/src/app/consts/plans.ts +++ b/frontend/src/app/consts/plans.ts @@ -1,6 +1,6 @@ export default [ - {key: 'title'}, - {key: 'free', name: 'Free', price: 0}, - {key: 'team', name: 'Team', price: 20}, - {key: 'enterprise', name: 'Enterprise', price: 50}, -] \ No newline at end of file + { key: 'title' }, + { key: 'free', name: 'Free', price: 0 }, + { key: 'team', name: 'Team', price: 20 }, + { key: 'enterprise', name: 'Enterprise', price: 50 }, +]; diff --git a/frontend/src/app/consts/record-edit-types.ts b/frontend/src/app/consts/record-edit-types.ts index 568d384e7..e5dfc2e35 100644 --- a/frontend/src/app/consts/record-edit-types.ts +++ b/frontend/src/app/consts/record-edit-types.ts @@ -1,5 +1,8 @@ - -import { BooleanEditComponent } from 'src/app/components/ui-components/record-edit-fields/boolean/boolean.component' +import { BooleanEditComponent } from 'src/app/components/ui-components/record-edit-fields/boolean/boolean.component'; +import { LongTextEditComponent } from 'src/app/components/ui-components/record-edit-fields/long-text/long-text.component'; +import { NumberEditComponent } from 'src/app/components/ui-components/record-edit-fields/number/number.component'; +import { PointEditComponent } from 'src/app/components/ui-components/record-edit-fields/point/point.component'; +import { TextEditComponent } from 'src/app/components/ui-components/record-edit-fields/text/text.component'; import { CodeEditComponent } from '../components/ui-components/record-edit-fields/code/code.component'; import { ColorEditComponent } from '../components/ui-components/record-edit-fields/color/color.component'; import { CountryEditComponent } from '../components/ui-components/record-edit-fields/country/country.component'; @@ -11,338 +14,351 @@ import { IdEditComponent } from '../components/ui-components/record-edit-fields/ import { ImageEditComponent } from '../components/ui-components/record-edit-fields/image/image.component'; import { JsonEditorEditComponent } from '../components/ui-components/record-edit-fields/json-editor/json-editor.component'; import { LanguageEditComponent } from '../components/ui-components/record-edit-fields/language/language.component'; -import { LongTextEditComponent } from 'src/app/components/ui-components/record-edit-fields/long-text/long-text.component' import { MarkdownEditComponent } from '../components/ui-components/record-edit-fields/markdown/markdown.component'; import { MoneyEditComponent } from '../components/ui-components/record-edit-fields/money/money.component'; -import { NumberEditComponent } from 'src/app/components/ui-components/record-edit-fields/number/number.component'; import { PasswordEditComponent } from '../components/ui-components/record-edit-fields/password/password.component'; import { PhoneEditComponent } from '../components/ui-components/record-edit-fields/phone/phone.component'; -import { PointEditComponent } from 'src/app/components/ui-components/record-edit-fields/point/point.component'; import { RangeEditComponent } from '../components/ui-components/record-edit-fields/range/range.component'; +import { S3EditComponent } from '../components/ui-components/record-edit-fields/s3/s3.component'; import { SelectEditComponent } from '../components/ui-components/record-edit-fields/select/select.component'; import { StaticTextEditComponent } from '../components/ui-components/record-edit-fields/static-text/static-text.component'; -import { TextEditComponent } from 'src/app/components/ui-components/record-edit-fields/text/text.component'; import { TimeEditComponent } from '../components/ui-components/record-edit-fields/time/time.component'; import { TimeIntervalEditComponent } from '../components/ui-components/record-edit-fields/time-interval/time-interval.component'; import { TimezoneEditComponent } from '../components/ui-components/record-edit-fields/timezone/timezone.component'; import { UrlEditComponent } from '../components/ui-components/record-edit-fields/url/url.component'; import { UuidEditComponent } from '../components/ui-components/record-edit-fields/uuid/uuid.component'; -import { S3EditComponent } from '../components/ui-components/record-edit-fields/s3/s3.component'; -export const timestampTypes = ['timestamp without time zone', 'timestamp with time zone', 'timestamp', 'date', 'time without time zone', 'time with time zone' , 'time', 'datetime', 'date time', 'datetime2', 'datetimeoffset', 'curdate', 'curtime', 'now', 'localtime', 'localtimestamp']; +export const timestampTypes = [ + 'timestamp without time zone', + 'timestamp with time zone', + 'timestamp', + 'date', + 'time without time zone', + 'time with time zone', + 'time', + 'datetime', + 'date time', + 'datetime2', + 'datetimeoffset', + 'curdate', + 'curtime', + 'now', + 'localtime', + 'localtimestamp', +]; export const defaultTimestampValues = { - postgres: ['current_date', 'current_time', 'current_timestamp', 'localtime', 'localtimestamp', 'now'], - mysql: ['curdate', 'curtime', 'now'], -} + postgres: ['current_date', 'current_time', 'current_timestamp', 'localtime', 'localtimestamp', 'now'], + mysql: ['curdate', 'curtime', 'now'], +}; export const UIwidgets = { - Default: '', - Boolean: BooleanEditComponent, - Code: CodeEditComponent, - Color: ColorEditComponent, - Country: CountryEditComponent, - Date: DateEditComponent, - DateTime: DateTimeEditComponent, - File: FileEditComponent, - Foreign_key: ForeignKeyEditComponent, - Image: ImageEditComponent, - JSON: JsonEditorEditComponent, - Language: LanguageEditComponent, - Markdown: MarkdownEditComponent, - Money: MoneyEditComponent, - Number: NumberEditComponent, - Password: PasswordEditComponent, - Phone: PhoneEditComponent, - Range: RangeEditComponent, - Readonly: StaticTextEditComponent, - Select: SelectEditComponent, - String: TextEditComponent, - Textarea: LongTextEditComponent, - Time: TimeEditComponent, - Timezone: TimezoneEditComponent, - URL: UrlEditComponent, - UUID: UuidEditComponent, - S3: S3EditComponent, -} + Default: '', + Boolean: BooleanEditComponent, + Code: CodeEditComponent, + Color: ColorEditComponent, + Country: CountryEditComponent, + Date: DateEditComponent, + DateTime: DateTimeEditComponent, + File: FileEditComponent, + Foreign_key: ForeignKeyEditComponent, + Image: ImageEditComponent, + JSON: JsonEditorEditComponent, + Language: LanguageEditComponent, + Markdown: MarkdownEditComponent, + Money: MoneyEditComponent, + Number: NumberEditComponent, + Password: PasswordEditComponent, + Phone: PhoneEditComponent, + Range: RangeEditComponent, + Readonly: StaticTextEditComponent, + Select: SelectEditComponent, + String: TextEditComponent, + Textarea: LongTextEditComponent, + Time: TimeEditComponent, + Timezone: TimezoneEditComponent, + URL: UrlEditComponent, + UUID: UuidEditComponent, + S3: S3EditComponent, +}; export const recordEditTypes = { - postgres: { - // numbers (number) - real: NumberEditComponent, - "double precision": NumberEditComponent, - smallint: NumberEditComponent, - integer: NumberEditComponent, - bigint: NumberEditComponent, - numeric: NumberEditComponent, - - //boolean (checkbox) - boolean: BooleanEditComponent, - - //datetime (datepicker) - "timestamp without time zone": DateTimeEditComponent, - "timestamp with time zone": DateTimeEditComponent, - "time without time zone": TimeEditComponent, - "time with time zone": TimeEditComponent, - date: DateEditComponent, - abstime: DateTimeEditComponent, - realtime: DateTimeEditComponent, - interval: TimeIntervalEditComponent, // number + select(seconds, days, weeks, years) - - // short text (text) - "character varying": TextEditComponent, - macaddr: TextEditComponent, //to do regexp - macaddr8: TextEditComponent, //to do regexp - cidr: TextEditComponent, //to do regexp - inet: TextEditComponent, //to do regexp - uuid: UuidEditComponent, - - //long text (textarea) - text: LongTextEditComponent, - xml: LongTextEditComponent, - - //select (select) - enum: SelectEditComponent, - - // json-editor - json: JsonEditorEditComponent, //json-editor - jsonb: JsonEditorEditComponent, //json-editor - ARRAY: JsonEditorEditComponent, - - //file - bytea: FileEditComponent, - - //etc - money: MoneyEditComponent, - - //mess (math) - point: PointEditComponent, - line: TextEditComponent, - circle: TextEditComponent, - path: TextEditComponent, - box: TextEditComponent, - lseg: TextEditComponent, - - "foreign key": ForeignKeyEditComponent - }, - - mysql: { - // numbers (number) - tinyint: NumberEditComponent, - smallint: NumberEditComponent, - mediumint: NumberEditComponent, - int: NumberEditComponent, - bigint: NumberEditComponent, - decimal: NumberEditComponent, - float: NumberEditComponent, - double: NumberEditComponent, - year: NumberEditComponent, - - //boolean (radiogroup) - boolean: BooleanEditComponent, - - //datetime (datepicker) - date: DateEditComponent, - time: TimeEditComponent, - datetime: DateTimeEditComponent, - timestamp: DateTimeEditComponent, - - // short text (text) - char: TextEditComponent, - varchar: TextEditComponent, - - //long text (textarea) - text: LongTextEditComponent, - tinytext: LongTextEditComponent, - mediumtext: LongTextEditComponent, - longtext: LongTextEditComponent, - - json: JsonEditorEditComponent, //json-editor - - //select (select) - enum: SelectEditComponent, - - //file - binary: FileEditComponent, - varbinary: FileEditComponent, - blob: FileEditComponent, - tinyblob: FileEditComponent, - mediumblob: FileEditComponent, - longblob: FileEditComponent, - - //etc - set: TextEditComponent, //(text) - - "foreign key": ForeignKeyEditComponent - }, - - oracledb: { - // numbers (number) - NUMBER: NumberEditComponent, - FLOAT: NumberEditComponent, - BINARY_FLOAT: NumberEditComponent, - BINARY_DOUBLE: NumberEditComponent, - "INTERVAL YEAR": NumberEditComponent, - "INTERVAL DAY": NumberEditComponent, - - //datetime (datepicker) - DATE: DateEditComponent, - TIMESTAMP: DateTimeEditComponent, - - // short text (text) - CHAR: TextEditComponent, - NCHAR: TextEditComponent, - CLOB: TextEditComponent, - NCLOB: TextEditComponent, - VARCHAR2: TextEditComponent, - VARCHAR: TextEditComponent, - NVARCHAR2: TextEditComponent, - - //file - BLOB: FileEditComponent, - BFILE: FileEditComponent, - RAW: FileEditComponent, - "LONG RAW" : FileEditComponent, - LONG : FileEditComponent, - - "foreign key": ForeignKeyEditComponent - }, - - mssql: { - // numbers (number) - bigint: NumberEditComponent, - int: NumberEditComponent, - smallint: NumberEditComponent, - tinyint: NumberEditComponent, - decimal: NumberEditComponent, - bitdecimal: NumberEditComponent, - numeric: NumberEditComponent, - real: NumberEditComponent, - - // short text (text) - uniqueidentifier: UuidEditComponent, - char: TextEditComponent, - varchar: TextEditComponent, - - //long text (textarea) - text: LongTextEditComponent, - nchar: LongTextEditComponent, - nvarchar: LongTextEditComponent, - ntext: LongTextEditComponent, - - //datetime (datepicker) - date: DateEditComponent, - datetime: DateTimeEditComponent, - smalldatetime: DateTimeEditComponent, - timestamp: DateTimeEditComponent, - - //file - binary: FileEditComponent, - varbinary: FileEditComponent, - image: FileEditComponent, - - // etc - money: MoneyEditComponent, - smallmoney: MoneyEditComponent, - - "foreign key": ForeignKeyEditComponent - }, - mongodb: { - // numbers (number) - number: NumberEditComponent, - double: NumberEditComponent, - int32: NumberEditComponent, - long: NumberEditComponent, - decimal128: NumberEditComponent, - - //boolean (radiogroup) - boolean: BooleanEditComponent, - - //datetime (datepicker) - date: DateEditComponent, - timestamp: DateTimeEditComponent, - - // short text (text) - string: TextEditComponent, - regexp: TextEditComponent, - objectid: TextEditComponent, - - //file - binary: FileEditComponent, - - //json - object: JsonEditorEditComponent, - array: JsonEditorEditComponent, - - //etc - unknown: TextEditComponent, - - "foreign key": ForeignKeyEditComponent - }, - dynamodb: { - string: TextEditComponent, - number: NumberEditComponent, - boolean: BooleanEditComponent, - null: StaticTextEditComponent, - array: JsonEditorEditComponent, - json: JsonEditorEditComponent, - binary: FileEditComponent, - }, - cassandra: { - int: NumberEditComponent, - bigint: NumberEditComponent, - varint: NumberEditComponent, - decimal: NumberEditComponent, - float: NumberEditComponent, - double: NumberEditComponent, - - boolean: BooleanEditComponent, - - timeuuid: IdEditComponent, - - timestamp: DateTimeEditComponent, - date: DateEditComponent, - time: TimeEditComponent, - - uuid: UuidEditComponent, - varchar: TextEditComponent, - inet: TextEditComponent, - ascii: TextEditComponent, - text: TextEditComponent, - - list: JsonEditorEditComponent, - map: JsonEditorEditComponent, - set: JsonEditorEditComponent, - }, - redis: { - string: TextEditComponent, - integer: NumberEditComponent, - decimal: NumberEditComponent, - boolean: BooleanEditComponent, - array: JsonEditorEditComponent, - json: JsonEditorEditComponent, - }, - elasticsearch: { - string: TextEditComponent, - number: NumberEditComponent, - boolean: BooleanEditComponent, - date: DateEditComponent, - object: JsonEditorEditComponent, - array: JsonEditorEditComponent, - binary: FileEditComponent, - }, - clickhouse: { - string: TextEditComponent, - uuid: UuidEditComponent, - boolean: BooleanEditComponent, - integer: NumberEditComponent, - bigint: NumberEditComponent, - float: NumberEditComponent, - double: NumberEditComponent, - decimal: NumberEditComponent, - date: DateEditComponent, - datetime: DateTimeEditComponent, - json: JsonEditorEditComponent, - object: JsonEditorEditComponent, - array: JsonEditorEditComponent, - } -} + postgres: { + // numbers (number) + real: NumberEditComponent, + 'double precision': NumberEditComponent, + smallint: NumberEditComponent, + integer: NumberEditComponent, + bigint: NumberEditComponent, + numeric: NumberEditComponent, + + //boolean (checkbox) + boolean: BooleanEditComponent, + + //datetime (datepicker) + 'timestamp without time zone': DateTimeEditComponent, + 'timestamp with time zone': DateTimeEditComponent, + 'time without time zone': TimeEditComponent, + 'time with time zone': TimeEditComponent, + date: DateEditComponent, + abstime: DateTimeEditComponent, + realtime: DateTimeEditComponent, + interval: TimeIntervalEditComponent, // number + select(seconds, days, weeks, years) + + // short text (text) + 'character varying': TextEditComponent, + macaddr: TextEditComponent, //to do regexp + macaddr8: TextEditComponent, //to do regexp + cidr: TextEditComponent, //to do regexp + inet: TextEditComponent, //to do regexp + uuid: UuidEditComponent, + + //long text (textarea) + text: LongTextEditComponent, + xml: LongTextEditComponent, + + //select (select) + enum: SelectEditComponent, + + // json-editor + json: JsonEditorEditComponent, //json-editor + jsonb: JsonEditorEditComponent, //json-editor + ARRAY: JsonEditorEditComponent, + + //file + bytea: FileEditComponent, + + //etc + money: MoneyEditComponent, + + //mess (math) + point: PointEditComponent, + line: TextEditComponent, + circle: TextEditComponent, + path: TextEditComponent, + box: TextEditComponent, + lseg: TextEditComponent, + + 'foreign key': ForeignKeyEditComponent, + }, + + mysql: { + // numbers (number) + tinyint: NumberEditComponent, + smallint: NumberEditComponent, + mediumint: NumberEditComponent, + int: NumberEditComponent, + bigint: NumberEditComponent, + decimal: NumberEditComponent, + float: NumberEditComponent, + double: NumberEditComponent, + year: NumberEditComponent, + + //boolean (radiogroup) + boolean: BooleanEditComponent, + + //datetime (datepicker) + date: DateEditComponent, + time: TimeEditComponent, + datetime: DateTimeEditComponent, + timestamp: DateTimeEditComponent, + + // short text (text) + char: TextEditComponent, + varchar: TextEditComponent, + + //long text (textarea) + text: LongTextEditComponent, + tinytext: LongTextEditComponent, + mediumtext: LongTextEditComponent, + longtext: LongTextEditComponent, + + json: JsonEditorEditComponent, //json-editor + + //select (select) + enum: SelectEditComponent, + + //file + binary: FileEditComponent, + varbinary: FileEditComponent, + blob: FileEditComponent, + tinyblob: FileEditComponent, + mediumblob: FileEditComponent, + longblob: FileEditComponent, + + //etc + set: TextEditComponent, //(text) + + 'foreign key': ForeignKeyEditComponent, + }, + + oracledb: { + // numbers (number) + NUMBER: NumberEditComponent, + FLOAT: NumberEditComponent, + BINARY_FLOAT: NumberEditComponent, + BINARY_DOUBLE: NumberEditComponent, + 'INTERVAL YEAR': NumberEditComponent, + 'INTERVAL DAY': NumberEditComponent, + + //datetime (datepicker) + DATE: DateEditComponent, + TIMESTAMP: DateTimeEditComponent, + + // short text (text) + CHAR: TextEditComponent, + NCHAR: TextEditComponent, + CLOB: TextEditComponent, + NCLOB: TextEditComponent, + VARCHAR2: TextEditComponent, + VARCHAR: TextEditComponent, + NVARCHAR2: TextEditComponent, + + //file + BLOB: FileEditComponent, + BFILE: FileEditComponent, + RAW: FileEditComponent, + 'LONG RAW': FileEditComponent, + LONG: FileEditComponent, + + 'foreign key': ForeignKeyEditComponent, + }, + + mssql: { + // numbers (number) + bigint: NumberEditComponent, + int: NumberEditComponent, + smallint: NumberEditComponent, + tinyint: NumberEditComponent, + decimal: NumberEditComponent, + bitdecimal: NumberEditComponent, + numeric: NumberEditComponent, + real: NumberEditComponent, + + // short text (text) + uniqueidentifier: UuidEditComponent, + char: TextEditComponent, + varchar: TextEditComponent, + + //long text (textarea) + text: LongTextEditComponent, + nchar: LongTextEditComponent, + nvarchar: LongTextEditComponent, + ntext: LongTextEditComponent, + + //datetime (datepicker) + date: DateEditComponent, + datetime: DateTimeEditComponent, + smalldatetime: DateTimeEditComponent, + timestamp: DateTimeEditComponent, + + //file + binary: FileEditComponent, + varbinary: FileEditComponent, + image: FileEditComponent, + + // etc + money: MoneyEditComponent, + smallmoney: MoneyEditComponent, + + 'foreign key': ForeignKeyEditComponent, + }, + mongodb: { + // numbers (number) + number: NumberEditComponent, + double: NumberEditComponent, + int32: NumberEditComponent, + long: NumberEditComponent, + decimal128: NumberEditComponent, + + //boolean (radiogroup) + boolean: BooleanEditComponent, + + //datetime (datepicker) + date: DateEditComponent, + timestamp: DateTimeEditComponent, + + // short text (text) + string: TextEditComponent, + regexp: TextEditComponent, + objectid: TextEditComponent, + + //file + binary: FileEditComponent, + + //json + object: JsonEditorEditComponent, + array: JsonEditorEditComponent, + + //etc + unknown: TextEditComponent, + + 'foreign key': ForeignKeyEditComponent, + }, + dynamodb: { + string: TextEditComponent, + number: NumberEditComponent, + boolean: BooleanEditComponent, + null: StaticTextEditComponent, + array: JsonEditorEditComponent, + json: JsonEditorEditComponent, + binary: FileEditComponent, + }, + cassandra: { + int: NumberEditComponent, + bigint: NumberEditComponent, + varint: NumberEditComponent, + decimal: NumberEditComponent, + float: NumberEditComponent, + double: NumberEditComponent, + + boolean: BooleanEditComponent, + + timeuuid: IdEditComponent, + + timestamp: DateTimeEditComponent, + date: DateEditComponent, + time: TimeEditComponent, + + uuid: UuidEditComponent, + varchar: TextEditComponent, + inet: TextEditComponent, + ascii: TextEditComponent, + text: TextEditComponent, + + list: JsonEditorEditComponent, + map: JsonEditorEditComponent, + set: JsonEditorEditComponent, + }, + redis: { + string: TextEditComponent, + integer: NumberEditComponent, + decimal: NumberEditComponent, + boolean: BooleanEditComponent, + array: JsonEditorEditComponent, + json: JsonEditorEditComponent, + }, + elasticsearch: { + string: TextEditComponent, + number: NumberEditComponent, + boolean: BooleanEditComponent, + date: DateEditComponent, + object: JsonEditorEditComponent, + array: JsonEditorEditComponent, + binary: FileEditComponent, + }, + clickhouse: { + string: TextEditComponent, + uuid: UuidEditComponent, + boolean: BooleanEditComponent, + integer: NumberEditComponent, + bigint: NumberEditComponent, + float: NumberEditComponent, + double: NumberEditComponent, + decimal: NumberEditComponent, + date: DateEditComponent, + datetime: DateTimeEditComponent, + json: JsonEditorEditComponent, + object: JsonEditorEditComponent, + array: JsonEditorEditComponent, + }, +}; diff --git a/frontend/src/app/directives/base64Validator.directive.ts b/frontend/src/app/directives/base64Validator.directive.ts index 0a1f6dd49..c18839050 100644 --- a/frontend/src/app/directives/base64Validator.directive.ts +++ b/frontend/src/app/directives/base64Validator.directive.ts @@ -1,20 +1,19 @@ -import { Directive } from "@angular/core"; -import { AbstractControl, NG_VALIDATORS, ValidationErrors, Validator } from "@angular/forms"; -import { base64Validation } from "../validators/base64.validator"; +import { Directive } from '@angular/core'; +import { AbstractControl, NG_VALIDATORS, ValidationErrors, Validator } from '@angular/forms'; +import { base64Validation } from '../validators/base64.validator'; @Directive({ - selector: '[base64Validator][ngModel]', - providers: [ - { - provide: NG_VALIDATORS, - useExisting: Base64ValidationDirective, - multi: true - } - ] + selector: '[base64Validator][ngModel]', + providers: [ + { + provide: NG_VALIDATORS, + useExisting: Base64ValidationDirective, + multi: true, + }, + ], }) - export class Base64ValidationDirective implements Validator { - validate(control: AbstractControl): ValidationErrors | null { - return base64Validation()(control); - } -} \ No newline at end of file + validate(control: AbstractControl): ValidationErrors | null { + return base64Validation()(control); + } +} diff --git a/frontend/src/app/directives/emailValidator.directive.ts b/frontend/src/app/directives/emailValidator.directive.ts index 6d03f5cd0..42e44e36e 100644 --- a/frontend/src/app/directives/emailValidator.directive.ts +++ b/frontend/src/app/directives/emailValidator.directive.ts @@ -1,21 +1,19 @@ -import { AbstractControl, NG_VALIDATORS, ValidationErrors, Validator } from "@angular/forms"; - -import { Directive } from "@angular/core"; -import { emailValidation } from "../validators/email.validator"; +import { Directive } from '@angular/core'; +import { AbstractControl, NG_VALIDATORS, ValidationErrors, Validator } from '@angular/forms'; +import { emailValidation } from '../validators/email.validator'; @Directive({ - selector: '[emailValidator][ngModel]', - providers: [ - { - provide: NG_VALIDATORS, - useExisting: EmailValidationDirective, - multi: true - } - ] + selector: '[emailValidator][ngModel]', + providers: [ + { + provide: NG_VALIDATORS, + useExisting: EmailValidationDirective, + multi: true, + }, + ], }) - export class EmailValidationDirective implements Validator { - validate(control: AbstractControl): ValidationErrors | null { - return emailValidation()(control); - } -} \ No newline at end of file + validate(control: AbstractControl): ValidationErrors | null { + return emailValidation()(control); + } +} diff --git a/frontend/src/app/directives/hexValidator.directive.ts b/frontend/src/app/directives/hexValidator.directive.ts index c469c8e1f..8bc14133a 100644 --- a/frontend/src/app/directives/hexValidator.directive.ts +++ b/frontend/src/app/directives/hexValidator.directive.ts @@ -1,20 +1,19 @@ -import { Directive } from "@angular/core"; -import { AbstractControl, NG_VALIDATORS, ValidationErrors, Validator } from "@angular/forms"; -import { hexValidation } from "../validators/hex.validator"; +import { Directive } from '@angular/core'; +import { AbstractControl, NG_VALIDATORS, ValidationErrors, Validator } from '@angular/forms'; +import { hexValidation } from '../validators/hex.validator'; @Directive({ - selector: '[hexValidator][ngModel]', - providers: [ - { - provide: NG_VALIDATORS, - useExisting: HexValidationDirective, - multi: true - } - ] + selector: '[hexValidator][ngModel]', + providers: [ + { + provide: NG_VALIDATORS, + useExisting: HexValidationDirective, + multi: true, + }, + ], }) - export class HexValidationDirective implements Validator { - validate(control: AbstractControl): ValidationErrors | null { - return hexValidation()(control); - } -} \ No newline at end of file + validate(control: AbstractControl): ValidationErrors | null { + return hexValidation()(control); + } +} diff --git a/frontend/src/app/directives/hostnameValidator.directive.ts b/frontend/src/app/directives/hostnameValidator.directive.ts index de223e93a..0df2e0dd3 100644 --- a/frontend/src/app/directives/hostnameValidator.directive.ts +++ b/frontend/src/app/directives/hostnameValidator.directive.ts @@ -1,23 +1,22 @@ -import { Directive, Input } from "@angular/core"; -import { AbstractControl, NG_VALIDATORS, ValidationErrors, Validator } from "@angular/forms"; -import { hostnameValidation } from "../validators/hostname.validator"; -import { DBtype } from "../models/connection"; +import { Directive, Input } from '@angular/core'; +import { AbstractControl, NG_VALIDATORS, ValidationErrors, Validator } from '@angular/forms'; +import { DBtype } from '../models/connection'; +import { hostnameValidation } from '../validators/hostname.validator'; @Directive({ - selector: '[hostnameValidator][ngModel]', - providers: [ - { - provide: NG_VALIDATORS, - useExisting: HostnameValidationDirective, - multi: true - } - ] + selector: '[hostnameValidator][ngModel]', + providers: [ + { + provide: NG_VALIDATORS, + useExisting: HostnameValidationDirective, + multi: true, + }, + ], }) - export class HostnameValidationDirective implements Validator { - @Input() hostnameValidator: DBtype; + @Input() hostnameValidator: DBtype; - validate(control: AbstractControl): ValidationErrors | null { - return hostnameValidation(this.hostnameValidator)(control); - } -} \ No newline at end of file + validate(control: AbstractControl): ValidationErrors | null { + return hostnameValidation(this.hostnameValidator)(control); + } +} diff --git a/frontend/src/app/directives/ngmat-table-query-reflector.directive.ts b/frontend/src/app/directives/ngmat-table-query-reflector.directive.ts index e4b585e73..5e2e3a5e1 100644 --- a/frontend/src/app/directives/ngmat-table-query-reflector.directive.ts +++ b/frontend/src/app/directives/ngmat-table-query-reflector.directive.ts @@ -20,150 +20,140 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -import { Directive, Input, OnInit, OnDestroy } from '@angular/core'; -import { Sort, MatSortable } from '@angular/material/sort'; -import { Router, ActivatedRoute } from '@angular/router'; -import { Subject, interval, Subscription } from 'rxjs'; -import { takeUntil } from 'rxjs/operators'; -import { MatTableDataSource } from '@angular/material/table'; +import { Directive, Input, OnDestroy, OnInit } from '@angular/core'; import { PageEvent } from '@angular/material/paginator'; +import { MatSortable, Sort } from '@angular/material/sort'; +import { MatTableDataSource } from '@angular/material/table'; +import { ActivatedRoute, Router } from '@angular/router'; +import { interval, Subject, Subscription } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; @Directive({ - selector: '[NgMatTableQueryReflector]' + selector: '[NgMatTableQueryReflector]', }) export class NgMatTableQueryReflectorDirective implements OnInit, OnDestroy { - - private unsubscribeAll$: Subject = new Subject(); - - @Input() matSortActive: string; - @Input() matSortDirection: 'asc' | 'desc'; - @Input() dataSource: MatTableDataSource; - private _dataSourceChecker$: Subscription; - - constructor( - private _router: Router, - private _activatedRoute: ActivatedRoute - ) { } - - async ngOnInit(): Promise { - await this.waitForDatasourceToLoad(); - this._initialSetup(); - this.listenToStateChangeEvents(); - } - - private _initialSetup(): void { - - const activePageQuery = this.isPageQueryActive(); - - if (activePageQuery) { - this.dataSource.paginator.pageIndex = activePageQuery.page_index; - this.dataSource.paginator.pageSize = activePageQuery.page_size; - } - - // Activating initial Sort - const activeSortQuery = this.isSortQueryActive(); - if (activeSortQuery) { - const sortActiveColumn = activeSortQuery ? (activeSortQuery.sort_direction ? activeSortQuery.sort_active : null) : this.matSortActive; - const sortable: MatSortable = { - id: sortActiveColumn, - start: activeSortQuery ? (activeSortQuery.sort_direction || null) : this.matSortDirection, - disableClear: true - }; - this.dataSource.sort.sort(sortable); - - if (!sortActiveColumn) { return; } - // Material Sort Issue: https://github.com/angular/components/issues/10242 - // Picked a hack from: https://github.com/angular/components/issues/10242#issuecomment-421490991 - const activeSortHeader = this.dataSource.sort.sortables.get(sortActiveColumn); - if (!activeSortHeader) { return; } - } - - } - - private isSortQueryActive(): { sort_active: string, sort_direction: 'asc' | 'desc' } { - - const queryParams = this._activatedRoute.snapshot.queryParams; - - if (Object.hasOwn(queryParams, 'sort_active') || Object.hasOwn(queryParams, 'sort_direction')) { - return { - sort_active: queryParams.sort_active, - sort_direction: queryParams.sort_direction - }; - } - - return; - } - - private isPageQueryActive(): { page_size: number, page_index: number } { - - const queryParams = this._activatedRoute.snapshot.queryParams; - - if (Object.hasOwn(queryParams, 'page_size') || Object.hasOwn(queryParams, 'page_index')) { - return { - page_size: queryParams.page_size, - page_index: queryParams.page_index - }; - } - - return; - } - - private listenToStateChangeEvents(): void { - this.dataSource.sort.sortChange - .pipe( - takeUntil(this.unsubscribeAll$) - ) - .subscribe((sortChange: Sort) => { - this._applySortChangesToUrlQueryParams(sortChange); - }); - - this.dataSource.paginator.page - .pipe( - takeUntil(this.unsubscribeAll$) - ) - .subscribe((pageChange: PageEvent) => { - this._applyPageStateChangesToUrlQueryParams(pageChange); - }); - } - - private _applySortChangesToUrlQueryParams(sortChange: Sort): void { - - const sortingAndPaginationQueryParams = { - sort_active: sortChange.active, - sort_direction: sortChange.direction, - }; - - this._router.navigate([], { queryParams: sortingAndPaginationQueryParams, queryParamsHandling: 'merge' }); - } - - private _applyPageStateChangesToUrlQueryParams(pageChange: PageEvent): void { - - const sortingAndPaginationQueryParams = { - page_size: pageChange.pageSize, - page_index: pageChange.pageIndex, - }; - - this._router.navigate([], { queryParams: sortingAndPaginationQueryParams, queryParamsHandling: 'merge' }); - } - - private waitForDatasourceToLoad(): Promise { - - const titleCheckingInterval$ = interval(500); - - return new Promise((resolve) => { - this._dataSourceChecker$ = titleCheckingInterval$.subscribe(_val => { - if (this.dataSource?.sort && this.dataSource?.paginator) { - this._dataSourceChecker$.unsubscribe(); - return resolve(); - } - }); - }); - - } - - ngOnDestroy(): void { - this.unsubscribeAll$.next(null); - this.unsubscribeAll$.complete(); - } - + private unsubscribeAll$: Subject = new Subject(); + + @Input() matSortActive: string; + @Input() matSortDirection: 'asc' | 'desc'; + @Input() dataSource: MatTableDataSource; + private _dataSourceChecker$: Subscription; + + constructor( + private _router: Router, + private _activatedRoute: ActivatedRoute, + ) {} + + async ngOnInit(): Promise { + await this.waitForDatasourceToLoad(); + this._initialSetup(); + this.listenToStateChangeEvents(); + } + + private _initialSetup(): void { + const activePageQuery = this.isPageQueryActive(); + + if (activePageQuery) { + this.dataSource.paginator.pageIndex = activePageQuery.page_index; + this.dataSource.paginator.pageSize = activePageQuery.page_size; + } + + // Activating initial Sort + const activeSortQuery = this.isSortQueryActive(); + if (activeSortQuery) { + const sortActiveColumn = activeSortQuery + ? activeSortQuery.sort_direction + ? activeSortQuery.sort_active + : null + : this.matSortActive; + const sortable: MatSortable = { + id: sortActiveColumn, + start: activeSortQuery ? activeSortQuery.sort_direction || null : this.matSortDirection, + disableClear: true, + }; + this.dataSource.sort.sort(sortable); + + if (!sortActiveColumn) { + return; + } + // Material Sort Issue: https://github.com/angular/components/issues/10242 + // Picked a hack from: https://github.com/angular/components/issues/10242#issuecomment-421490991 + const activeSortHeader = this.dataSource.sort.sortables.get(sortActiveColumn); + if (!activeSortHeader) { + return; + } + } + } + + private isSortQueryActive(): { sort_active: string; sort_direction: 'asc' | 'desc' } { + const queryParams = this._activatedRoute.snapshot.queryParams; + + if (Object.hasOwn(queryParams, 'sort_active') || Object.hasOwn(queryParams, 'sort_direction')) { + return { + sort_active: queryParams.sort_active, + sort_direction: queryParams.sort_direction, + }; + } + + return; + } + + private isPageQueryActive(): { page_size: number; page_index: number } { + const queryParams = this._activatedRoute.snapshot.queryParams; + + if (Object.hasOwn(queryParams, 'page_size') || Object.hasOwn(queryParams, 'page_index')) { + return { + page_size: queryParams.page_size, + page_index: queryParams.page_index, + }; + } + + return; + } + + private listenToStateChangeEvents(): void { + this.dataSource.sort.sortChange.pipe(takeUntil(this.unsubscribeAll$)).subscribe((sortChange: Sort) => { + this._applySortChangesToUrlQueryParams(sortChange); + }); + + this.dataSource.paginator.page.pipe(takeUntil(this.unsubscribeAll$)).subscribe((pageChange: PageEvent) => { + this._applyPageStateChangesToUrlQueryParams(pageChange); + }); + } + + private _applySortChangesToUrlQueryParams(sortChange: Sort): void { + const sortingAndPaginationQueryParams = { + sort_active: sortChange.active, + sort_direction: sortChange.direction, + }; + + this._router.navigate([], { queryParams: sortingAndPaginationQueryParams, queryParamsHandling: 'merge' }); + } + + private _applyPageStateChangesToUrlQueryParams(pageChange: PageEvent): void { + const sortingAndPaginationQueryParams = { + page_size: pageChange.pageSize, + page_index: pageChange.pageIndex, + }; + + this._router.navigate([], { queryParams: sortingAndPaginationQueryParams, queryParamsHandling: 'merge' }); + } + + private waitForDatasourceToLoad(): Promise { + const titleCheckingInterval$ = interval(500); + + return new Promise((resolve) => { + this._dataSourceChecker$ = titleCheckingInterval$.subscribe((_val) => { + if (this.dataSource?.sort && this.dataSource?.paginator) { + this._dataSourceChecker$.unsubscribe(); + return resolve(); + } + }); + }); + } + + ngOnDestroy(): void { + this.unsubscribeAll$.next(null); + this.unsubscribeAll$.complete(); + } } diff --git a/frontend/src/app/directives/passwordValidator.directive.ts b/frontend/src/app/directives/passwordValidator.directive.ts index 524eca059..6bcdad858 100644 --- a/frontend/src/app/directives/passwordValidator.directive.ts +++ b/frontend/src/app/directives/passwordValidator.directive.ts @@ -1,20 +1,19 @@ -import { Directive } from "@angular/core"; -import { AbstractControl, NG_VALIDATORS, ValidationErrors, Validator } from "@angular/forms"; -import { passwordValidation } from "../validators/password.validator"; +import { Directive } from '@angular/core'; +import { AbstractControl, NG_VALIDATORS, ValidationErrors, Validator } from '@angular/forms'; +import { passwordValidation } from '../validators/password.validator'; @Directive({ - selector: '[passwordValidator][ngModel]', - providers: [ - { - provide: NG_VALIDATORS, - useExisting: PasswordValidationDirective, - multi: true - } - ] + selector: '[passwordValidator][ngModel]', + providers: [ + { + provide: NG_VALIDATORS, + useExisting: PasswordValidationDirective, + multi: true, + }, + ], }) - export class PasswordValidationDirective implements Validator { - validate(control: AbstractControl): ValidationErrors | null { - return passwordValidation()(control); - } -} \ No newline at end of file + validate(control: AbstractControl): ValidationErrors | null { + return passwordValidation()(control); + } +} diff --git a/frontend/src/app/directives/text-validator.directive.ts b/frontend/src/app/directives/text-validator.directive.ts index 5ec067e47..e4dea3f50 100644 --- a/frontend/src/app/directives/text-validator.directive.ts +++ b/frontend/src/app/directives/text-validator.directive.ts @@ -1,25 +1,23 @@ - - -import { AbstractControl, NG_VALIDATORS, ValidationErrors, Validator } from '@angular/forms'; import { Directive, Input } from '@angular/core'; +import { AbstractControl, NG_VALIDATORS, ValidationErrors, Validator } from '@angular/forms'; import { textValidation } from '../validators/text.validator'; @Directive({ - selector: '[textValidator][ngModel]', - providers: [ - { - provide: NG_VALIDATORS, - useExisting: TextValidatorDirective, - multi: true - } - ] + selector: '[textValidator][ngModel]', + providers: [ + { + provide: NG_VALIDATORS, + useExisting: TextValidatorDirective, + multi: true, + }, + ], }) export class TextValidatorDirective implements Validator { - @Input('validateType') validateType: string | null = null; - @Input('regexPattern') regexPattern: string | null = null; + @Input('validateType') validateType: string | null = null; + @Input('regexPattern') regexPattern: string | null = null; - validate(control: AbstractControl): ValidationErrors | null { - return textValidation(this.validateType, this.regexPattern)(control); - } -} \ No newline at end of file + validate(control: AbstractControl): ValidationErrors | null { + return textValidation(this.validateType, this.regexPattern)(control); + } +} diff --git a/frontend/src/app/directives/url-validator.directive.spec.ts b/frontend/src/app/directives/url-validator.directive.spec.ts index 6750e4c09..770229021 100644 --- a/frontend/src/app/directives/url-validator.directive.spec.ts +++ b/frontend/src/app/directives/url-validator.directive.spec.ts @@ -1,8 +1,8 @@ import { UrlValidatorDirective } from './url-validator.directive'; describe('UrlValidatorDirectiveTsDirective', () => { - it('should create an instance', () => { - const directive = new UrlValidatorDirective(); - expect(directive).toBeTruthy(); - }); + it('should create an instance', () => { + const directive = new UrlValidatorDirective(); + expect(directive).toBeTruthy(); + }); }); diff --git a/frontend/src/app/directives/url-validator.directive.ts b/frontend/src/app/directives/url-validator.directive.ts index d1b28e645..ea0f7d6d8 100644 --- a/frontend/src/app/directives/url-validator.directive.ts +++ b/frontend/src/app/directives/url-validator.directive.ts @@ -3,19 +3,19 @@ import { AbstractControl, NG_VALIDATORS, ValidationErrors, Validator } from '@an import { urlValidation } from '../validators/url.validator'; @Directive({ - selector: '[urlValidator][ngModel]', - providers: [ - { - provide: NG_VALIDATORS, - useExisting: UrlValidatorDirective, - multi: true - } - ] + selector: '[urlValidator][ngModel]', + providers: [ + { + provide: NG_VALIDATORS, + useExisting: UrlValidatorDirective, + multi: true, + }, + ], }) export class UrlValidatorDirective implements Validator { - @Input('urlPrefix') prefix: string = ''; - - validate(control: AbstractControl): ValidationErrors | null { - return urlValidation(this.prefix)(control); - } + @Input('urlPrefix') prefix: string = ''; + + validate(control: AbstractControl): ValidationErrors | null { + return urlValidation(this.prefix)(control); + } } diff --git a/frontend/src/app/lib/format-field-value.ts b/frontend/src/app/lib/format-field-value.ts index 0d652b513..127485017 100644 --- a/frontend/src/app/lib/format-field-value.ts +++ b/frontend/src/app/lib/format-field-value.ts @@ -1,30 +1,30 @@ -import { format } from 'date-fns' +import { format } from 'date-fns'; export function formatFieldValue(value, type) { - const dateTimeTypes = [ - 'timestamp without time zone', - 'timestamp with time zone', - 'abstime', - 'realtime', - 'datetime', - 'timestamp' - ] + const dateTimeTypes = [ + 'timestamp without time zone', + 'timestamp with time zone', + 'abstime', + 'realtime', + 'datetime', + 'timestamp', + ]; - if (value && type === 'time') { - return value - } else if (value && type === 'date') { - const datetimeValue = new Date(value); - return format(datetimeValue, "P") - } else if (value && dateTimeTypes.includes(type)) { - const datetimeValue = new Date(value); - return format(datetimeValue, "P p") - } else if (type === 'boolean') { - if (value || value === 1) return true - else if (value === false || value === 0) return false - else return '—' - } else if (type === 'json' || type === 'jsonb' || type === 'object' || type === 'array' || type === 'interval') { - return JSON.stringify(value) - } + if (value && type === 'time') { + return value; + } else if (value && type === 'date') { + const datetimeValue = new Date(value); + return format(datetimeValue, 'P'); + } else if (value && dateTimeTypes.includes(type)) { + const datetimeValue = new Date(value); + return format(datetimeValue, 'P p'); + } else if (type === 'boolean') { + if (value || value === 1) return true; + else if (value === false || value === 0) return false; + else return '—'; + } else if (type === 'json' || type === 'jsonb' || type === 'object' || type === 'array' || type === 'interval') { + return JSON.stringify(value); + } - return value; - } + return value; +} diff --git a/frontend/src/app/lib/normalize.spec.ts b/frontend/src/app/lib/normalize.spec.ts index f8df4e2c2..1e5a1a642 100644 --- a/frontend/src/app/lib/normalize.spec.ts +++ b/frontend/src/app/lib/normalize.spec.ts @@ -1,23 +1,23 @@ -import { normalizeTableName, normalizeFieldName } from './normalize'; +import { normalizeFieldName, normalizeTableName } from './normalize'; describe('Normalize function', () => { - it('should modify name', () => { - const normalizedName = normalizeTableName('TABLE_USER'); - expect(normalizedName).toEqual('Table Users'); - }); + it('should modify name', () => { + const normalizedName = normalizeTableName('TABLE_USER'); + expect(normalizedName).toEqual('Table Users'); + }); - it('should pluralise name', () => { - const normalizedName = normalizeTableName('USER'); - expect(normalizedName).toEqual('Users'); - }); + it('should pluralise name', () => { + const normalizedName = normalizeTableName('USER'); + expect(normalizedName).toEqual('Users'); + }); - it('normalize camel case and capitalize', () => { - const normalizedName = normalizeFieldName('affiliateStripeNumber'); - expect(normalizedName).toEqual('Affiliate stripe number'); - }); + it('normalize camel case and capitalize', () => { + const normalizedName = normalizeFieldName('affiliateStripeNumber'); + expect(normalizedName).toEqual('Affiliate stripe number'); + }); - it('normalize camel case, capitalize and uppercase acronyms', () => { - const normalizedName = normalizeFieldName('affiliateStripeId'); - expect(normalizedName).toEqual('Affiliate stripe ID'); - }); -}); \ No newline at end of file + it('normalize camel case, capitalize and uppercase acronyms', () => { + const normalizedName = normalizeFieldName('affiliateStripeId'); + expect(normalizedName).toEqual('Affiliate stripe ID'); + }); +}); diff --git a/frontend/src/app/lib/parse-filter-params.spec.ts b/frontend/src/app/lib/parse-filter-params.spec.ts index 7538c7151..b041f8436 100644 --- a/frontend/src/app/lib/parse-filter-params.spec.ts +++ b/frontend/src/app/lib/parse-filter-params.spec.ts @@ -1,35 +1,35 @@ import { getComparatorsFromUrl, getFiltersFromUrl } from './parse-filter-params'; const queryParams = { - first_name: { - contains: 'sh' - }, - age: { - gt: 25 - }, - city: { - startswith: 'A' - } -} + first_name: { + contains: 'sh', + }, + age: { + gt: 25, + }, + city: { + startswith: 'A', + }, +}; describe('Parse filter params', () => { - it('should return filters from queryParams', () => { - const filters = getFiltersFromUrl(queryParams); + it('should return filters from queryParams', () => { + const filters = getFiltersFromUrl(queryParams); - expect(filters).toEqual({ - first_name: 'sh', - age: 25, - city: 'A' - }); - }); + expect(filters).toEqual({ + first_name: 'sh', + age: 25, + city: 'A', + }); + }); - it('should return comparators from queryParams', () => { - const comparators = getComparatorsFromUrl(queryParams); + it('should return comparators from queryParams', () => { + const comparators = getComparatorsFromUrl(queryParams); - expect(comparators).toEqual({ - first_name: 'contains', - age: 'gt', - city: 'startswith' - }); - }) -}); \ No newline at end of file + expect(comparators).toEqual({ + first_name: 'contains', + age: 'gt', + city: 'startswith', + }); + }); +}); diff --git a/frontend/src/app/lib/parse-filter-params.ts b/frontend/src/app/lib/parse-filter-params.ts index 161ffafab..453ae579e 100644 --- a/frontend/src/app/lib/parse-filter-params.ts +++ b/frontend/src/app/lib/parse-filter-params.ts @@ -1,15 +1,15 @@ export function getFiltersFromUrl(queryParams) { - let filters = {}; - for (const key in queryParams) { - filters[key] = Object.values(queryParams[key])[0]; - } - return filters; + let filters = {}; + for (const key in queryParams) { + filters[key] = Object.values(queryParams[key])[0]; + } + return filters; } export function getComparatorsFromUrl(queryParams) { - let comparators = {}; - for (const key in queryParams) { - comparators[key] = Object.keys(queryParams[key])[0]; - } - return comparators; -} \ No newline at end of file + let comparators = {}; + for (const key in queryParams) { + comparators[key] = Object.keys(queryParams[key])[0]; + } + return comparators; +} diff --git a/frontend/src/app/lib/setup-table-row-structure.spec.ts b/frontend/src/app/lib/setup-table-row-structure.spec.ts index 7323f17b5..bffeb7878 100644 --- a/frontend/src/app/lib/setup-table-row-structure.spec.ts +++ b/frontend/src/app/lib/setup-table-row-structure.spec.ts @@ -1,102 +1,95 @@ import { getTableTypes } from './setup-table-row-structure'; const fakeProduct_categories = { - "column_name": "product_categories", - "column_default": null, - "data_type": "enum", - "data_type_params": [ - "food", - "drinks", - "cleaning" - ], - "isExcluded": false, - "isSearched": false, - "auto_increment": false, - "allow_null": true, - "character_maximum_length": 1 -} + column_name: 'product_categories', + column_default: null, + data_type: 'enum', + data_type_params: ['food', 'drinks', 'cleaning'], + isExcluded: false, + isSearched: false, + auto_increment: false, + allow_null: true, + character_maximum_length: 1, +}; const fakeCustomer_categories = { - "column_name": "customer_categories", - "column_default": null, - "data_type": "enum", - "data_type_params": [ - "manager", - "seller" - ], - "isExcluded": false, - "isSearched": false, - "auto_increment": false, - "allow_null": true, - "character_maximum_length": 1 -} + column_name: 'customer_categories', + column_default: null, + data_type: 'enum', + data_type_params: ['manager', 'seller'], + isExcluded: false, + isSearched: false, + auto_increment: false, + allow_null: true, + character_maximum_length: 1, +}; const fakeCustomerId = { - "column_name": "CustomerId", - "column_default": null, - "data_type": "int", - "isExcluded": false, - "isSearched": true, - "auto_increment": false, - "allow_null": false, - "character_maximum_length": null -} + column_name: 'CustomerId', + column_default: null, + data_type: 'int', + isExcluded: false, + isSearched: true, + auto_increment: false, + allow_null: false, + character_maximum_length: null, +}; const fakeProductId = { - "column_name": "ProductId", - "column_default": null, - "data_type": "int", - "isExcluded": false, - "isSearched": true, - "auto_increment": false, - "allow_null": false, - "character_maximum_length": null -} + column_name: 'ProductId', + column_default: null, + data_type: 'int', + isExcluded: false, + isSearched: true, + auto_increment: false, + allow_null: false, + character_maximum_length: null, +}; const fakeBool = { - "column_name": "bool", - "column_default": null, - "data_type": "tinyint", - "isExcluded": false, - "isSearched": false, - "auto_increment": false, - "allow_null": true, - "character_maximum_length": 1 -} + column_name: 'bool', + column_default: null, + data_type: 'tinyint', + isExcluded: false, + isSearched: false, + auto_increment: false, + allow_null: true, + character_maximum_length: 1, +}; const fakeFloat = { - "column_name": "float", - "column_default": null, - "data_type": "float", - "isExcluded": false, - "isSearched": false, - "auto_increment": false, - "allow_null": true, - "character_maximum_length": 102 -} + column_name: 'float', + column_default: null, + data_type: 'float', + isExcluded: false, + isSearched: false, + auto_increment: false, + allow_null: true, + character_maximum_length: 102, +}; const fakeStructure = [ - fakeProduct_categories, - fakeCustomer_categories, - fakeCustomerId, - fakeProductId, - fakeBool, - fakeFloat, -] + fakeProduct_categories, + fakeCustomer_categories, + fakeCustomerId, + fakeProductId, + fakeBool, + fakeFloat, +]; const fakeForeignKeysList = ['CustomerId', 'ProductId']; describe('Normalize function', () => { - it('should return comparator of filter', () => { - const tableTypes = getTableTypes(fakeStructure, fakeForeignKeysList); + it('should return comparator of filter', () => { + const tableTypes = getTableTypes(fakeStructure, fakeForeignKeysList); - expect(tableTypes).toEqual({ - product_categories: "enum", - customer_categories: "enum", - CustomerId: "foreign key", - ProductId: "foreign key", - bool: "boolean", - float: "float" - }); - }); -}); \ No newline at end of file + expect(tableTypes).toEqual({ + product_categories: 'enum', + customer_categories: 'enum', + CustomerId: 'foreign key', + ProductId: 'foreign key', + bool: 'boolean', + float: 'float', + }); + }); +}); diff --git a/frontend/src/app/lib/setup-table-row-structure.ts b/frontend/src/app/lib/setup-table-row-structure.ts index 860817ffb..2ff53e113 100644 --- a/frontend/src/app/lib/setup-table-row-structure.ts +++ b/frontend/src/app/lib/setup-table-row-structure.ts @@ -1,16 +1,18 @@ import { TableField } from '../models/table'; export function getTableTypes(structure: TableField[], foreignKeysList: string[]) { - if (!structure || !Array.isArray(structure) || structure.length === 0) { - return {}; - } + if (!structure || !Array.isArray(structure) || structure.length === 0) { + return {}; + } - const tableTypes = Object.assign({}, ...structure.map((field: TableField) => { - if (field.data_type === 'tinyint' && field.character_maximum_length === 1 ) - return {[field.column_name]: 'boolean'} - if (foreignKeysList.includes(field.column_name)) - return {[field.column_name]: 'foreign key'} - return {[field.column_name]: field.data_type} - })) - return tableTypes; + const tableTypes = Object.assign( + {}, + ...structure.map((field: TableField) => { + if (field.data_type === 'tinyint' && field.character_maximum_length === 1) + return { [field.column_name]: 'boolean' }; + if (foreignKeysList.includes(field.column_name)) return { [field.column_name]: 'foreign key' }; + return { [field.column_name]: field.data_type }; + }), + ); + return tableTypes; } diff --git a/frontend/src/app/models/alert.ts b/frontend/src/app/models/alert.ts index 530a97aab..58ec049cb 100644 --- a/frontend/src/app/models/alert.ts +++ b/frontend/src/app/models/alert.ts @@ -1,31 +1,31 @@ export enum AlertType { - Warning = 'warning', - Error = 'error', - Info = 'info', - Success = 'success' + Warning = 'warning', + Error = 'error', + Info = 'info', + Success = 'success', } export enum AlertActionType { - Anchor = 'external link', - Link = 'internal link', - Button = 'button', + Anchor = 'external link', + Link = 'internal link', + Button = 'button', } export interface AlertAction { - type: AlertActionType, - caption: string, - to?: string, - action?: (id: number) => void + type: AlertActionType; + caption: string; + to?: string; + action?: (id: number) => void; } export interface Alert { - id: number, - type: AlertType, - message: ServerError | string, - actions?: AlertAction[] + id: number; + type: AlertType; + message: ServerError | string; + actions?: AlertAction[]; } export interface ServerError { - abstract: string, - details: string -} \ No newline at end of file + abstract: string; + details: string; +} diff --git a/frontend/src/app/models/connection.ts b/frontend/src/app/models/connection.ts index a9fd268af..87e4104a1 100644 --- a/frontend/src/app/models/connection.ts +++ b/frontend/src/app/models/connection.ts @@ -1,77 +1,77 @@ -import { AccessLevel } from "./user"; +import { AccessLevel } from './user'; export enum DBtype { - Postgres = 'postgres', - MySQL = 'mysql', - Oracle = 'oracledb', - MSSQL = 'mssql', - Mongo = 'mongodb', - Dynamo = 'dynamodb', - Cassandra = 'cassandra', - Redis = 'redis', - Elasticsearch = 'elasticsearch', - ClickHouse = 'clickhouse', - DB2 = 'ibmdb2' + Postgres = 'postgres', + MySQL = 'mysql', + Oracle = 'oracledb', + MSSQL = 'mssql', + Mongo = 'mongodb', + Dynamo = 'dynamodb', + Cassandra = 'cassandra', + Redis = 'redis', + Elasticsearch = 'elasticsearch', + ClickHouse = 'clickhouse', + DB2 = 'ibmdb2', } export enum ConnectionType { - Agent = 'agent', - Direct = 'direct' + Agent = 'agent', + Direct = 'direct', } export interface Connection { - id: string | null, - database: string, - authSource?: string, - title: string, - host: string, - port: string, - sid: string | null, - type: DBtype, - createdAt?: Date, - updatedAt?: Date, - username: string - password?: string, - ssh: boolean, - privateSSHKey?: string, - sshHost?: string, - sshPort?: string, - sshUsername?: string, - ssl: boolean, - cert: string, - masterEncryption: boolean, - azure_encryption: boolean, - connectionType: ConnectionType, - schema?: string, - signing_key?: string, - isTestConnection?: boolean, - dataCenter?: string + id: string | null; + database: string; + authSource?: string; + title: string; + host: string; + port: string; + sid: string | null; + type: DBtype; + createdAt?: Date; + updatedAt?: Date; + username: string; + password?: string; + ssh: boolean; + privateSSHKey?: string; + sshHost?: string; + sshPort?: string; + sshUsername?: string; + ssl: boolean; + cert: string; + masterEncryption: boolean; + azure_encryption: boolean; + connectionType: ConnectionType; + schema?: string; + signing_key?: string; + isTestConnection?: boolean; + dataCenter?: string; } export interface ConnectionItem { - connection: Connection, - accessLevel: AccessLevel + connection: Connection; + accessLevel: AccessLevel; } export interface TestConnection { - result: boolean, - message: string + result: boolean; + message: string; } export interface TableCategory { - category_id: string, - category_name: string, - tables: string[], - category_color?: string + category_id: string; + category_name: string; + tables: string[]; + category_color?: string; } export interface ConnectionSettings { - hidden_tables?: string[], - default_showing_table?: string, - primary_color?: string, - secondary_color?: string, - logo_url?: string, - company_name?: string, - tables_audit?: boolean, - table_categories?: TableCategory[] + hidden_tables?: string[]; + default_showing_table?: string; + primary_color?: string; + secondary_color?: string; + logo_url?: string; + company_name?: string; + tables_audit?: boolean; + table_categories?: TableCategory[]; } diff --git a/frontend/src/app/models/logs.ts b/frontend/src/app/models/logs.ts index 060776414..d100ea727 100644 --- a/frontend/src/app/models/logs.ts +++ b/frontend/src/app/models/logs.ts @@ -1,24 +1,24 @@ export enum LogAction { - Add = 'addRow', - Delete = 'deleteRow', - Update = 'updateRow', - ReceiveRow = "rowReceived", - ReceiveRows = "rowsReceived", + Add = 'addRow', + Delete = 'deleteRow', + Update = 'updateRow', + ReceiveRow = 'rowReceived', + ReceiveRows = 'rowsReceived', } export enum LogStatus { - Successfully = 'successfully', - Unsuccessfully = 'unsuccessfully' + Successfully = 'successfully', + Unsuccessfully = 'unsuccessfully', } export interface Log { - Table: string, - User: string, - Action: string, - Date: string, - Status: LogStatus, - operationType: LogAction, - createdAt: string, - prevValue: Object, - currentValue: Object, -} \ No newline at end of file + Table: string; + User: string; + Action: string; + Date: string; + Status: LogStatus; + operationType: LogAction; + createdAt: string; + prevValue: Object; + currentValue: Object; +} diff --git a/frontend/src/app/models/plans.ts b/frontend/src/app/models/plans.ts index 676512e7c..1619e0e5e 100644 --- a/frontend/src/app/models/plans.ts +++ b/frontend/src/app/models/plans.ts @@ -1,11 +1,11 @@ export enum PlanKey { - Free = 'free', - Tean = 'team', - Enterprise = 'enterprise', + Free = 'free', + Tean = 'team', + Enterprise = 'enterprise', } export interface PricePlan { - key: PlanKey, - name: string, - price: number, -} \ No newline at end of file + key: PlanKey; + name: string; + price: number; +} diff --git a/frontend/src/app/models/secret.ts b/frontend/src/app/models/secret.ts index 4bdff7ca7..4fefb948a 100644 --- a/frontend/src/app/models/secret.ts +++ b/frontend/src/app/models/secret.ts @@ -1,61 +1,61 @@ export interface Secret { - id: string; - slug: string; - companyId: string; - createdAt: string; - updatedAt: string; - lastAccessedAt?: string; - expiresAt?: string; - masterEncryption: boolean; + id: string; + slug: string; + companyId: string; + createdAt: string; + updatedAt: string; + lastAccessedAt?: string; + expiresAt?: string; + masterEncryption: boolean; } export interface SecretListResponse { - data: Secret[]; - pagination: SecretPagination; + data: Secret[]; + pagination: SecretPagination; } export interface SecretPagination { - total: number; - currentPage: number; - perPage: number; - lastPage: number; + total: number; + currentPage: number; + perPage: number; + lastPage: number; } export interface AuditLogEntry { - id: string; - action: SecretAction; - user: { - id: string; - email: string; - }; - accessedAt: string; - ipAddress?: string; - userAgent?: string; - success: boolean; - errorMessage?: string; + id: string; + action: SecretAction; + user: { + id: string; + email: string; + }; + accessedAt: string; + ipAddress?: string; + userAgent?: string; + success: boolean; + errorMessage?: string; } export interface AuditLogResponse { - data: AuditLogEntry[]; - pagination: SecretPagination; + data: AuditLogEntry[]; + pagination: SecretPagination; } export type SecretAction = 'create' | 'view' | 'copy' | 'update' | 'delete'; export interface CreateSecretPayload { - slug: string; - value: string; - expiresAt?: string; - masterEncryption?: boolean; - masterPassword?: string; + slug: string; + value: string; + expiresAt?: string; + masterEncryption?: boolean; + masterPassword?: string; } export interface UpdateSecretPayload { - value: string; - expiresAt?: string | null; + value: string; + expiresAt?: string | null; } export interface DeleteSecretResponse { - message: string; - deletedAt: string; + message: string; + deletedAt: string; } diff --git a/frontend/src/app/models/table.ts b/frontend/src/app/models/table.ts index 248f193f0..d4ec10a1a 100644 --- a/frontend/src/app/models/table.ts +++ b/frontend/src/app/models/table.ts @@ -1,155 +1,155 @@ export interface TablePermissions { - visibility: boolean, - readonly: boolean, - add: boolean, - delete: boolean, - edit: boolean + visibility: boolean; + readonly: boolean; + add: boolean; + delete: boolean; + edit: boolean; } export interface TableProperties { - table: string, - display_name?: string, - normalizedTableName?: string, - initials?: string, - icon?: string, - permissions: TablePermissions, + table: string; + display_name?: string; + normalizedTableName?: string; + initials?: string; + icon?: string; + permissions: TablePermissions; } export enum TableOrdering { - Ascending = 'ASC', - Descending = 'DESC' + Ascending = 'ASC', + Descending = 'DESC', } export interface TableSettings { - connection_id: string, - table_name: string, - - icon: string, - display_name: string, - autocomplete_columns: string[], - identity_column: string, - ordering: TableOrdering, - ordering_field: string, - list_fields: string[], - columns_view: string[], - search_fields: string[], - excluded_fields: string[], - readonly_fields: string[], - sortable_by: string[], - sensitive_fields: string[], - allow_csv_export: boolean, - allow_csv_import: boolean, - can_delete: boolean, + connection_id: string; + table_name: string; + + icon: string; + display_name: string; + autocomplete_columns: string[]; + identity_column: string; + ordering: TableOrdering; + ordering_field: string; + list_fields: string[]; + columns_view: string[]; + search_fields: string[]; + excluded_fields: string[]; + readonly_fields: string[]; + sortable_by: string[]; + sensitive_fields: string[]; + allow_csv_export: boolean; + allow_csv_import: boolean; + can_delete: boolean; } export interface PersonalTableViewSettings { - ordering?: TableOrdering, - ordering_field?: string, - list_per_page?: number, - list_fields?: string[], - columns_view?: string[], - original_names?: boolean, + ordering?: TableOrdering; + ordering_field?: string; + list_per_page?: number; + list_fields?: string[]; + columns_view?: string[]; + original_names?: boolean; - // sortable_by: string[], + // sortable_by: string[], } export interface TableRow { - connectionID: string, - tableName: string, - record: object, - columnsOrder: string[], - primaryKeys: object, - foreignKeys: TableForeignKey[], - foreignKeysList: string[], - widgets: Widget[], - widgetsList: string[], - fieldsTypes: { [key: string]: string }, - relatedRecords: { - referenced_on_column_name: string, - referenced_by: [] - }[], - link?: string + connectionID: string; + tableName: string; + record: object; + columnsOrder: string[]; + primaryKeys: object; + foreignKeys: TableForeignKey[]; + foreignKeysList: string[]; + widgets: Widget[]; + widgetsList: string[]; + fieldsTypes: { [key: string]: string }; + relatedRecords: { + referenced_on_column_name: string; + referenced_by: []; + }[]; + link?: string; } export interface TableField { - column_name: string, - column_default: string, - data_type: string, - data_type_params?: string[], - isExcluded: boolean, - isSearched: boolean, - allow_null: boolean, - auto_increment: boolean, - character_maximum_length: number + column_name: string; + column_default: string; + data_type: string; + data_type_params?: string[]; + isExcluded: boolean; + isSearched: boolean; + allow_null: boolean; + auto_increment: boolean; + character_maximum_length: number; } export interface TableForeignKey { - autocomplete_columns?: string[], - column_name: string, - constraint_name: string, - referenced_column_name: string, - referenced_table_name: string, - column_default?: string, + autocomplete_columns?: string[]; + column_name: string; + constraint_name: string; + referenced_column_name: string; + referenced_table_name: string; + column_default?: string; } export interface WidgetStructure { - field_name: string, - widget_type: string, - widget_params: Record, - name: string, - description: string + field_name: string; + widget_type: string; + widget_params: Record; + name: string; + description: string; } export interface Widget { - field_name: string, - widget_type: string, - widget_params: string, - name: string, - description: string + field_name: string; + widget_type: string; + widget_params: string; + name: string; + description: string; } export enum CustomActionType { - Single = "single", - Multiple = "multiple" + Single = 'single', + Multiple = 'multiple', } export enum CustomActionMethod { - Slack = "slack", - Email = "email", - URL = "URL", - Zapier = "zapier" + Slack = 'slack', + Email = 'email', + URL = 'URL', + Zapier = 'zapier', } export interface CustomAction { - method: CustomActionMethod, - emails: string[], - url: string, + method: CustomActionMethod; + emails: string[]; + url: string; } export enum EventType { - AddRow = "ADD_ROW", - UpdateRow = "UPDATE_ROW", - DeleteRow = "DELETE_ROW", - Custom = "CUSTOM" + AddRow = 'ADD_ROW', + UpdateRow = 'UPDATE_ROW', + DeleteRow = 'DELETE_ROW', + Custom = 'CUSTOM', } export interface Event { - event: EventType, + event: EventType; } export interface CustomEvent { - id?: string, - event: EventType, - title: string, - type: CustomActionType, - icon: string, - require_confirmation: boolean + id?: string; + event: EventType; + title: string; + type: CustomActionType; + icon: string; + require_confirmation: boolean; } export interface Rule { - id?: string, - title: string, - table_name: string, - events: (Event | CustomEvent)[], - table_actions: CustomAction[] -}; \ No newline at end of file + id?: string; + title: string; + table_name: string; + events: (Event | CustomEvent)[]; + table_actions: CustomAction[]; +} diff --git a/frontend/src/app/models/ui-settings.ts b/frontend/src/app/models/ui-settings.ts index 40a36187e..aec329e6e 100644 --- a/frontend/src/app/models/ui-settings.ts +++ b/frontend/src/app/models/ui-settings.ts @@ -1,25 +1,25 @@ export interface GlobalSettingsUI { - connectionsListCollapsed: boolean; - lastFeatureNotificationId: string; - profileSidebarCollapsed?: boolean; + connectionsListCollapsed: boolean; + lastFeatureNotificationId: string; + profileSidebarCollapsed?: boolean; } export interface TableSettingsUI { - shownColumns?: string[]; - defaultSort?: { - column: string; - direction: 'asc' | 'desc'; - }; + shownColumns?: string[]; + defaultSort?: { + column: string; + direction: 'asc' | 'desc'; + }; } export interface ConnectionSettingsUI { - shownTableTitles: boolean; - tables: { [tableName: string]: TableSettingsUI }; - tableFoldersExpanded?: string[]; - dashboardsSidebarCollapsed?: boolean; + shownTableTitles: boolean; + tables: { [tableName: string]: TableSettingsUI }; + tableFoldersExpanded?: string[]; + dashboardsSidebarCollapsed?: boolean; } export interface UiSettings { - globalSettings: GlobalSettingsUI; - connections: { [connectionId: string]: ConnectionSettingsUI }; -} \ No newline at end of file + globalSettings: GlobalSettingsUI; + connections: { [connectionId: string]: ConnectionSettingsUI }; +} diff --git a/frontend/src/app/modules/config.module.ts b/frontend/src/app/modules/config.module.ts index 98f69d3ec..0d0dd555e 100644 --- a/frontend/src/app/modules/config.module.ts +++ b/frontend/src/app/modules/config.module.ts @@ -1,24 +1,21 @@ +import { HttpClientModule } from '@angular/common/http'; import { ModuleWithProviders, NgModule } from '@angular/core'; - import { ConfigurationService } from '../services/configuration.service'; -import { HttpClientModule } from '@angular/common/http'; @NgModule({ - providers: [ConfigurationService], - imports: [ - HttpClientModule - ] + providers: [ConfigurationService], + imports: [HttpClientModule], }) export class ConfigModule { - static buildForConfigUrl(configUrl: string): ModuleWithProviders { - return { - ngModule: ConfigModule, - providers: [ - { - provide: 'CONFIG_URL', - useValue: configUrl - } - ] - }; - } + static buildForConfigUrl(configUrl: string): ModuleWithProviders { + return { + ngModule: ConfigModule, + providers: [ + { + provide: 'CONFIG_URL', + useValue: configUrl, + }, + ], + }; + } } diff --git a/frontend/src/app/routes/company-invitation.routes.ts b/frontend/src/app/routes/company-invitation.routes.ts index 0b135aeba..c9210d966 100644 --- a/frontend/src/app/routes/company-invitation.routes.ts +++ b/frontend/src/app/routes/company-invitation.routes.ts @@ -2,10 +2,13 @@ import { Routes } from '@angular/router'; import { provideZxvbnServiceForPSM } from 'angular-password-strength-meter/zxcvbn'; export const COMPANY_INVITATION_ROUTES: Routes = [ - { - path: '', - loadComponent: () => import('../components/company-member-invitation/company-member-invitation.component').then(m => m.CompanyMemberInvitationComponent), - title: 'Invitation | Rocketadmin', - providers: [provideZxvbnServiceForPSM()] - } + { + path: '', + loadComponent: () => + import('../components/company-member-invitation/company-member-invitation.component').then( + (m) => m.CompanyMemberInvitationComponent, + ), + title: 'Invitation | Rocketadmin', + providers: [provideZxvbnServiceForPSM()], + }, ]; diff --git a/frontend/src/app/routes/password-change.routes.ts b/frontend/src/app/routes/password-change.routes.ts index a37597715..edc45950e 100644 --- a/frontend/src/app/routes/password-change.routes.ts +++ b/frontend/src/app/routes/password-change.routes.ts @@ -3,10 +3,11 @@ import { provideZxvbnServiceForPSM } from 'angular-password-strength-meter/zxcvb import { AuthGuard } from '../auth.guard'; export const PASSWORD_CHANGE_ROUTES: Routes = [ - { - path: '', - loadComponent: () => import('../components/password-change/password-change.component').then(m => m.PasswordChangeComponent), - canActivate: [AuthGuard], - providers: [provideZxvbnServiceForPSM()] - } + { + path: '', + loadComponent: () => + import('../components/password-change/password-change.component').then((m) => m.PasswordChangeComponent), + canActivate: [AuthGuard], + providers: [provideZxvbnServiceForPSM()], + }, ]; diff --git a/frontend/src/app/routes/password-reset.routes.ts b/frontend/src/app/routes/password-reset.routes.ts index 8cc370cbd..2b1f66c58 100644 --- a/frontend/src/app/routes/password-reset.routes.ts +++ b/frontend/src/app/routes/password-reset.routes.ts @@ -2,10 +2,11 @@ import { Routes } from '@angular/router'; import { provideZxvbnServiceForPSM } from 'angular-password-strength-meter/zxcvbn'; export const PASSWORD_RESET_ROUTES: Routes = [ - { - path: '', - loadComponent: () => import('../components/password-reset/password-reset.component').then(m => m.PasswordResetComponent), - title: 'Reset password | Rocketadmin', - providers: [provideZxvbnServiceForPSM()] - } + { + path: '', + loadComponent: () => + import('../components/password-reset/password-reset.component').then((m) => m.PasswordResetComponent), + title: 'Reset password | Rocketadmin', + providers: [provideZxvbnServiceForPSM()], + }, ]; diff --git a/frontend/src/app/routes/registration.routes.ts b/frontend/src/app/routes/registration.routes.ts index b83cf5d27..4ad6679d2 100644 --- a/frontend/src/app/routes/registration.routes.ts +++ b/frontend/src/app/routes/registration.routes.ts @@ -2,10 +2,11 @@ import { Routes } from '@angular/router'; import { provideZxvbnServiceForPSM } from 'angular-password-strength-meter/zxcvbn'; export const REGISTRATION_ROUTES: Routes = [ - { - path: '', - loadComponent: () => import('../components/registration/registration.component').then(m => m.RegistrationComponent), - title: 'Sign up | Rocketadmin', - providers: [provideZxvbnServiceForPSM()] - } + { + path: '', + loadComponent: () => + import('../components/registration/registration.component').then((m) => m.RegistrationComponent), + title: 'Sign up | Rocketadmin', + providers: [provideZxvbnServiceForPSM()], + }, ]; diff --git a/frontend/src/app/services/company.service.spec.ts b/frontend/src/app/services/company.service.spec.ts index efc6d68cd..6e6f143c8 100644 --- a/frontend/src/app/services/company.service.spec.ts +++ b/frontend/src/app/services/company.service.spec.ts @@ -1,23 +1,20 @@ +import { provideHttpClient } from '@angular/common/http'; import { TestBed } from '@angular/core/testing'; - -import { CompanyService } from './company.service'; import { MatSnackBarModule } from '@angular/material/snack-bar'; -import { provideHttpClient } from '@angular/common/http'; +import { CompanyService } from './company.service'; describe('CompanyService', () => { - let service: CompanyService; + let service: CompanyService; - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [ - MatSnackBarModule - ], - providers: [provideHttpClient()] - }); - service = TestBed.inject(CompanyService); - }); + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [MatSnackBarModule], + providers: [provideHttpClient()], + }); + service = TestBed.inject(CompanyService); + }); - it('should be created', () => { - expect(service).toBeTruthy(); - }); + it('should be created', () => { + expect(service).toBeTruthy(); + }); }); diff --git a/frontend/src/app/services/configuration.service.spec.ts b/frontend/src/app/services/configuration.service.spec.ts index 38dcc9293..5e5b1c928 100644 --- a/frontend/src/app/services/configuration.service.spec.ts +++ b/frontend/src/app/services/configuration.service.spec.ts @@ -1,22 +1,22 @@ -import { ConfigurationService } from './configuration.service'; import { TestBed } from '@angular/core/testing'; +import { ConfigurationService } from './configuration.service'; describe('ConfigurationService', () => { - let service: ConfigurationService; + let service: ConfigurationService; - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [ - { - provide: ConfigurationService, - useValue: { CONFIG_URL: 'https://app.rocketadmin.com/api' } - } - ] - }); - service = TestBed.inject(ConfigurationService); - }); + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + { + provide: ConfigurationService, + useValue: { CONFIG_URL: 'https://app.rocketadmin.com/api' }, + }, + ], + }); + service = TestBed.inject(ConfigurationService); + }); - it('should be created', () => { - expect(service).toBeTruthy(); - }); + it('should be created', () => { + expect(service).toBeTruthy(); + }); }); diff --git a/frontend/src/app/services/configuration.service.ts b/frontend/src/app/services/configuration.service.ts index 0817366d8..5945d0f90 100644 --- a/frontend/src/app/services/configuration.service.ts +++ b/frontend/src/app/services/configuration.service.ts @@ -1,11 +1,11 @@ -import { Injectable, } from '@angular/core'; -import config from "../../config.json"; +import { Injectable } from '@angular/core'; +import config from '../../config.json'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class ConfigurationService { - getConfig() { - return config; - } + getConfig() { + return config; + } } diff --git a/frontend/src/app/services/master-password.service.ts b/frontend/src/app/services/master-password.service.ts index 12a447536..e646829dd 100644 --- a/frontend/src/app/services/master-password.service.ts +++ b/frontend/src/app/services/master-password.service.ts @@ -1,31 +1,27 @@ -import { MatDialog, } from '@angular/material/dialog'; - import { Injectable } from '@angular/core'; +import { MatDialog } from '@angular/material/dialog'; import { MasterPasswordDialogComponent } from '../components/master-password-dialog/master-password-dialog.component'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class MasterPasswordService { + // private showMasterPasswordDialogRef: MatDialogRef; - // private showMasterPasswordDialogRef: MatDialogRef; - - constructor( - public dialog: MatDialog, - ) { } + constructor(public dialog: MatDialog) {} - showMasterPasswordDialog() { - this.dialog.open(MasterPasswordDialogComponent, { - width: '24em', - disableClose: true - }) - } + showMasterPasswordDialog() { + this.dialog.open(MasterPasswordDialogComponent, { + width: '24em', + disableClose: true, + }); + } - checkMasterPassword(isEncryptionEnabled: boolean, connectionID: string, masterKey: string) { - if (isEncryptionEnabled) { - localStorage.setItem(`${connectionID}__masterKey`, masterKey); - } else { - localStorage.removeItem(`${connectionID}__masterKey`); - } - } + checkMasterPassword(isEncryptionEnabled: boolean, connectionID: string, masterKey: string) { + if (isEncryptionEnabled) { + localStorage.setItem(`${connectionID}__masterKey`, masterKey); + } else { + localStorage.removeItem(`${connectionID}__masterKey`); + } + } } diff --git a/frontend/src/app/services/notifications.service.ts b/frontend/src/app/services/notifications.service.ts index c04d31c48..8925e670f 100644 --- a/frontend/src/app/services/notifications.service.ts +++ b/frontend/src/app/services/notifications.service.ts @@ -1,54 +1,50 @@ -import { Alert, AlertAction, AlertType, ServerError } from '../models/alert'; - import { Injectable } from '@angular/core'; import { MatSnackBar } from '@angular/material/snack-bar'; +import { Alert, AlertAction, AlertType, ServerError } from '../models/alert'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class NotificationsService { - - public alert: Alert; - public idCounter = 0; - - constructor( - private snackBar: MatSnackBar, - ) { } - - showErrorSnackbar(message: string) { - this.snackBar.open(message, 'Dismiss', { - duration: 10000, - horizontalPosition: 'left' - }); - } - - showSuccessSnackbar(message: string) { - this.snackBar.open(message, null, { - duration: 2500, - horizontalPosition: 'left' - }); - } - - get currentAlert() { - return this.alert; - } - - showAlert(type: AlertType, message: ServerError | string, actions: AlertAction[]) { - this.idCounter++; - this.alert = { - id: this.idCounter, - type: type, - message: message, - actions: actions - }; - // window.scrollTo(0, 0); - } - - dismissAlert() { - this.alert = null; - } - - resetAlert() { - this.alert = null; - } + public alert: Alert; + public idCounter = 0; + + constructor(private snackBar: MatSnackBar) {} + + showErrorSnackbar(message: string) { + this.snackBar.open(message, 'Dismiss', { + duration: 10000, + horizontalPosition: 'left', + }); + } + + showSuccessSnackbar(message: string) { + this.snackBar.open(message, null, { + duration: 2500, + horizontalPosition: 'left', + }); + } + + get currentAlert() { + return this.alert; + } + + showAlert(type: AlertType, message: ServerError | string, actions: AlertAction[]) { + this.idCounter++; + this.alert = { + id: this.idCounter, + type: type, + message: message, + actions: actions, + }; + // window.scrollTo(0, 0); + } + + dismissAlert() { + this.alert = null; + } + + resetAlert() { + this.alert = null; + } } diff --git a/frontend/src/app/services/payment.service.spec.ts b/frontend/src/app/services/payment.service.spec.ts index 33ace11d3..28a2bdca4 100644 --- a/frontend/src/app/services/payment.service.spec.ts +++ b/frontend/src/app/services/payment.service.spec.ts @@ -1,20 +1,20 @@ +import { provideHttpClient } from '@angular/common/http'; +import { TestBed } from '@angular/core/testing'; import { MatSnackBarModule } from '@angular/material/snack-bar'; import { PaymentService } from './payment.service'; -import { TestBed } from '@angular/core/testing'; -import { provideHttpClient } from '@angular/common/http'; describe('PaymentService', () => { - let service: PaymentService; + let service: PaymentService; - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [MatSnackBarModule], - providers: [provideHttpClient()] - }).compileComponents(); - service = TestBed.inject(PaymentService); - }); + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [MatSnackBarModule], + providers: [provideHttpClient()], + }).compileComponents(); + service = TestBed.inject(PaymentService); + }); - it('should be created', () => { - expect(service).toBeTruthy(); - }); + it('should be created', () => { + expect(service).toBeTruthy(); + }); }); diff --git a/frontend/src/app/services/secrets.service.ts b/frontend/src/app/services/secrets.service.ts index 1cce0ff2a..0e697440d 100644 --- a/frontend/src/app/services/secrets.service.ts +++ b/frontend/src/app/services/secrets.service.ts @@ -1,126 +1,117 @@ -import { Injectable } from '@angular/core'; import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'; +import { Injectable } from '@angular/core'; import { BehaviorSubject, EMPTY, Observable, throwError } from 'rxjs'; import { catchError, map } from 'rxjs/operators'; -import { NotificationsService } from './notifications.service'; import { - Secret, - SecretListResponse, - AuditLogResponse, - CreateSecretPayload, - UpdateSecretPayload, - DeleteSecretResponse, + AuditLogResponse, + CreateSecretPayload, + DeleteSecretResponse, + Secret, + SecretListResponse, + UpdateSecretPayload, } from '../models/secret'; +import { NotificationsService } from './notifications.service'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class SecretsService { - private secretsUpdated = new BehaviorSubject(''); - public cast = this.secretsUpdated.asObservable(); + private secretsUpdated = new BehaviorSubject(''); + public cast = this.secretsUpdated.asObservable(); - constructor( - private _http: HttpClient, - private _notifications: NotificationsService - ) {} + constructor( + private _http: HttpClient, + private _notifications: NotificationsService, + ) {} - fetchSecrets(page: number = 1, limit: number = 20, search?: string): Observable { - let params = new HttpParams() - .set('page', page.toString()) - .set('limit', limit.toString()); + fetchSecrets(page: number = 1, limit: number = 20, search?: string): Observable { + let params = new HttpParams().set('page', page.toString()).set('limit', limit.toString()); - if (search) { - params = params.set('search', search); - } + if (search) { + params = params.set('search', search); + } - return this._http.get('/secrets', { params }) - .pipe( - map(res => res), - catchError((err) => { - console.log(err); - this._notifications.showErrorSnackbar(err.error?.message || 'Failed to fetch secrets'); - return EMPTY; - }) - ); - } + return this._http.get('/secrets', { params }).pipe( + map((res) => res), + catchError((err) => { + console.log(err); + this._notifications.showErrorSnackbar(err.error?.message || 'Failed to fetch secrets'); + return EMPTY; + }), + ); + } - createSecret(payload: CreateSecretPayload): Observable { - return this._http.post('/secrets', payload) - .pipe( - map(res => { - this._notifications.showSuccessSnackbar('Secret created successfully'); - this.secretsUpdated.next('created'); - return res; - }), - catchError((err) => { - console.log(err); - if (err.status === 409) { - this._notifications.showErrorSnackbar('A secret with this slug already exists'); - } else { - this._notifications.showErrorSnackbar(err.error?.message || 'Failed to create secret'); - } - return EMPTY; - }) - ); - } + createSecret(payload: CreateSecretPayload): Observable { + return this._http.post('/secrets', payload).pipe( + map((res) => { + this._notifications.showSuccessSnackbar('Secret created successfully'); + this.secretsUpdated.next('created'); + return res; + }), + catchError((err) => { + console.log(err); + if (err.status === 409) { + this._notifications.showErrorSnackbar('A secret with this slug already exists'); + } else { + this._notifications.showErrorSnackbar(err.error?.message || 'Failed to create secret'); + } + return EMPTY; + }), + ); + } - updateSecret(slug: string, payload: UpdateSecretPayload, masterPassword?: string): Observable { - let headers = new HttpHeaders(); - if (masterPassword) { - headers = headers.set('masterpwd', masterPassword); - } + updateSecret(slug: string, payload: UpdateSecretPayload, masterPassword?: string): Observable { + let headers = new HttpHeaders(); + if (masterPassword) { + headers = headers.set('masterpwd', masterPassword); + } - return this._http.put(`/secrets/${slug}`, payload, { headers }) - .pipe( - map(res => { - this._notifications.showSuccessSnackbar('Secret updated successfully'); - this.secretsUpdated.next('updated'); - return res; - }), - catchError((err) => { - console.log(err); - if (err.status === 403) { - return throwError(() => err); - } - if (err.status === 410) { - this._notifications.showErrorSnackbar('Cannot update an expired secret'); - } else { - this._notifications.showErrorSnackbar(err.error?.message || 'Failed to update secret'); - } - return EMPTY; - }) - ); - } + return this._http.put(`/secrets/${slug}`, payload, { headers }).pipe( + map((res) => { + this._notifications.showSuccessSnackbar('Secret updated successfully'); + this.secretsUpdated.next('updated'); + return res; + }), + catchError((err) => { + console.log(err); + if (err.status === 403) { + return throwError(() => err); + } + if (err.status === 410) { + this._notifications.showErrorSnackbar('Cannot update an expired secret'); + } else { + this._notifications.showErrorSnackbar(err.error?.message || 'Failed to update secret'); + } + return EMPTY; + }), + ); + } - deleteSecret(slug: string): Observable { - return this._http.delete(`/secrets/${slug}`) - .pipe( - map(res => { - this._notifications.showSuccessSnackbar('Secret deleted successfully'); - this.secretsUpdated.next('deleted'); - return res; - }), - catchError((err) => { - console.log(err); - this._notifications.showErrorSnackbar(err.error?.message || 'Failed to delete secret'); - return EMPTY; - }) - ); - } + deleteSecret(slug: string): Observable { + return this._http.delete(`/secrets/${slug}`).pipe( + map((res) => { + this._notifications.showSuccessSnackbar('Secret deleted successfully'); + this.secretsUpdated.next('deleted'); + return res; + }), + catchError((err) => { + console.log(err); + this._notifications.showErrorSnackbar(err.error?.message || 'Failed to delete secret'); + return EMPTY; + }), + ); + } - getAuditLog(slug: string, page: number = 1, limit: number = 50): Observable { - const params = new HttpParams() - .set('page', page.toString()) - .set('limit', limit.toString()); + getAuditLog(slug: string, page: number = 1, limit: number = 50): Observable { + const params = new HttpParams().set('page', page.toString()).set('limit', limit.toString()); - return this._http.get(`/secrets/${slug}/audit-log`, { params }) - .pipe( - map(res => res), - catchError((err) => { - console.log(err); - this._notifications.showErrorSnackbar(err.error?.message || 'Failed to fetch audit log'); - return EMPTY; - }) - ); - } + return this._http.get(`/secrets/${slug}/audit-log`, { params }).pipe( + map((res) => res), + catchError((err) => { + console.log(err); + this._notifications.showErrorSnackbar(err.error?.message || 'Failed to fetch audit log'); + return EMPTY; + }), + ); + } } diff --git a/frontend/src/app/services/table-state.service.spec.ts b/frontend/src/app/services/table-state.service.spec.ts index 998d2bc1f..6ddb67e15 100644 --- a/frontend/src/app/services/table-state.service.spec.ts +++ b/frontend/src/app/services/table-state.service.spec.ts @@ -3,14 +3,14 @@ import { TestBed } from '@angular/core/testing'; import { TableStateService } from './table-state.service'; describe('TableStateService', () => { - let service: TableStateService; + let service: TableStateService; - beforeEach(() => { - TestBed.configureTestingModule({}); - service = TestBed.inject(TableStateService); - }); + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(TableStateService); + }); - it('should be created', () => { - expect(service).toBeTruthy(); - }); + it('should be created', () => { + expect(service).toBeTruthy(); + }); }); diff --git a/frontend/src/app/services/table-state.service.ts b/frontend/src/app/services/table-state.service.ts index c4441a3b0..bd318f3d7 100644 --- a/frontend/src/app/services/table-state.service.ts +++ b/frontend/src/app/services/table-state.service.ts @@ -1,93 +1,95 @@ -import { BehaviorSubject } from 'rxjs'; import { Injectable } from '@angular/core'; +import { BehaviorSubject } from 'rxjs'; import { TableRow } from '../models/table'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) - export class TableStateService { - private selectedRowSubject = new BehaviorSubject(null); - cast = this.selectedRowSubject.asObservable(); - - private aiPanelSubject = new BehaviorSubject(false); - aiPanelCast = this.aiPanelSubject.asObservable(); - - private aiPanelExpandedSubject = new BehaviorSubject(false); - aiPanelExpandedCast = this.aiPanelExpandedSubject.asObservable(); - - // private backUrlFilters: any; - // private backUrlParams: any; - - private getSessionStorageItem(key: string) { - const item = sessionStorage.getItem(key); - if (item) { - try { - return JSON.parse(item); - } catch (e) { - console.error(`Error parsing JSON from sessionStorage for key "${key}":`, e); - return null; - } - } - return null; - } - - private setSessionStorageItem(key: string, value: any) { - sessionStorage.setItem(key, JSON.stringify(value)); - } - - setBackUrlParams(pageIndex: number, pageSize: number, sortField: string, sortDirection: string) { - console.log('setBackUrlParams', pageIndex, pageSize, sortField, sortDirection); - const params = { - page_index: pageIndex, - page_size: pageSize, - ...(sortField !== undefined && { sort_active: sortField }), - ...(sortDirection !== undefined && { sort_direction: sortDirection.toUpperCase() }) - }; - this.setSessionStorageItem('backUrlParams', params); - } - - getBackUrlParams() { - return this.getSessionStorageItem('backUrlParams'); - } - - setBackUrlFilters(filters: any) { - this.setSessionStorageItem('backUrlFilters', filters); - } - - getBackUrlFilters() { - return this.getSessionStorageItem('backUrlFilters'); - } - - selectRow(row: TableRow) { - if (this.selectedRowSubject.value && JSON.stringify(this.selectedRowSubject.value.primaryKeys) === JSON.stringify(row.primaryKeys)) { - this.selectedRowSubject.next(null); - } else { - this.selectedRowSubject.next(row); - this.closeAIpanel(); - } - } - - handleViewAIpanel() { - this.clearSelection(); - const isOpening = !this.aiPanelSubject.value; - this.aiPanelSubject.next(isOpening); - if (!isOpening) { - this.aiPanelExpandedSubject.next(false); - } - } - - clearSelection() { - this.selectedRowSubject.next(null); - } - - closeAIpanel() { - this.aiPanelSubject.next(false); - this.aiPanelExpandedSubject.next(false); - } - - toggleAIPanelExpanded() { - const newValue = !this.aiPanelExpandedSubject.value; - this.aiPanelExpandedSubject.next(newValue); - } + private selectedRowSubject = new BehaviorSubject(null); + cast = this.selectedRowSubject.asObservable(); + + private aiPanelSubject = new BehaviorSubject(false); + aiPanelCast = this.aiPanelSubject.asObservable(); + + private aiPanelExpandedSubject = new BehaviorSubject(false); + aiPanelExpandedCast = this.aiPanelExpandedSubject.asObservable(); + + // private backUrlFilters: any; + // private backUrlParams: any; + + private getSessionStorageItem(key: string) { + const item = sessionStorage.getItem(key); + if (item) { + try { + return JSON.parse(item); + } catch (e) { + console.error(`Error parsing JSON from sessionStorage for key "${key}":`, e); + return null; + } + } + return null; + } + + private setSessionStorageItem(key: string, value: any) { + sessionStorage.setItem(key, JSON.stringify(value)); + } + + setBackUrlParams(pageIndex: number, pageSize: number, sortField: string, sortDirection: string) { + console.log('setBackUrlParams', pageIndex, pageSize, sortField, sortDirection); + const params = { + page_index: pageIndex, + page_size: pageSize, + ...(sortField !== undefined && { sort_active: sortField }), + ...(sortDirection !== undefined && { sort_direction: sortDirection.toUpperCase() }), + }; + this.setSessionStorageItem('backUrlParams', params); + } + + getBackUrlParams() { + return this.getSessionStorageItem('backUrlParams'); + } + + setBackUrlFilters(filters: any) { + this.setSessionStorageItem('backUrlFilters', filters); + } + + getBackUrlFilters() { + return this.getSessionStorageItem('backUrlFilters'); + } + + selectRow(row: TableRow) { + if ( + this.selectedRowSubject.value && + JSON.stringify(this.selectedRowSubject.value.primaryKeys) === JSON.stringify(row.primaryKeys) + ) { + this.selectedRowSubject.next(null); + } else { + this.selectedRowSubject.next(row); + this.closeAIpanel(); + } + } + + handleViewAIpanel() { + this.clearSelection(); + const isOpening = !this.aiPanelSubject.value; + this.aiPanelSubject.next(isOpening); + if (!isOpening) { + this.aiPanelExpandedSubject.next(false); + } + } + + clearSelection() { + this.selectedRowSubject.next(null); + } + + closeAIpanel() { + this.aiPanelSubject.next(false); + this.aiPanelExpandedSubject.next(false); + } + + toggleAIPanelExpanded() { + const newValue = !this.aiPanelExpandedSubject.value; + this.aiPanelExpandedSubject.next(newValue); + } } diff --git a/frontend/src/app/services/tables.service.ts b/frontend/src/app/services/tables.service.ts index 2df9e2598..55f797430 100644 --- a/frontend/src/app/services/tables.service.ts +++ b/frontend/src/app/services/tables.service.ts @@ -80,7 +80,7 @@ export class TablesService { } fetchTablesFolders(connectionID: string, hidden?: boolean) { - console.log('fetchTablesFolders service') + console.log('fetchTablesFolders service'); return this._http .get(`/table-categories/v2/${connectionID}`, { params: { diff --git a/frontend/src/app/services/token.interceptor.spec.ts b/frontend/src/app/services/token.interceptor.spec.ts index 366e64c0d..00e89fcc1 100644 --- a/frontend/src/app/services/token.interceptor.spec.ts +++ b/frontend/src/app/services/token.interceptor.spec.ts @@ -1,30 +1,29 @@ -import { ConfigurationService } from './configuration.service'; +import { provideHttpClient } from '@angular/common/http'; +import { TestBed } from '@angular/core/testing'; import { MatDialogModule } from '@angular/material/dialog'; import { MatSnackBarModule } from '@angular/material/snack-bar'; -import { TestBed } from '@angular/core/testing'; -import { TokenInterceptor } from './token.interceptor'; -import { provideHttpClient } from '@angular/common/http'; import { provideRouter } from '@angular/router'; +import { ConfigurationService } from './configuration.service'; +import { TokenInterceptor } from './token.interceptor'; describe('TokenInterceptor', () => { - beforeEach(() => TestBed.configureTestingModule({ - imports: [ - MatSnackBarModule, - MatDialogModule - ], - providers: [ - provideHttpClient(), - provideRouter([]), - TokenInterceptor, - { - provide: ConfigurationService, - useValue: { CONFIG_URL: 'https://app.rocketadmin.com/api' } - } - ] - })); + beforeEach(() => + TestBed.configureTestingModule({ + imports: [MatSnackBarModule, MatDialogModule], + providers: [ + provideHttpClient(), + provideRouter([]), + TokenInterceptor, + { + provide: ConfigurationService, + useValue: { CONFIG_URL: 'https://app.rocketadmin.com/api' }, + }, + ], + }), + ); - it('should be created', () => { - const interceptor: TokenInterceptor = TestBed.inject(TokenInterceptor); - expect(interceptor).toBeTruthy(); - }); + it('should be created', () => { + const interceptor: TokenInterceptor = TestBed.inject(TokenInterceptor); + expect(interceptor).toBeTruthy(); + }); }); diff --git a/frontend/src/app/services/token.interceptor.ts b/frontend/src/app/services/token.interceptor.ts index 61e32db81..dfdc63ec3 100644 --- a/frontend/src/app/services/token.interceptor.ts +++ b/frontend/src/app/services/token.interceptor.ts @@ -1,74 +1,67 @@ -import { - HttpErrorResponse, - HttpEvent, - HttpHandler, - HttpInterceptor, - HttpRequest -} from '@angular/common/http'; +import { HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { CookieService } from 'ngx-cookie-service'; import { Observable, throwError } from 'rxjs'; - +import { catchError } from 'rxjs/operators'; +import { environment } from '../../environments/environment'; import { ConfigurationService } from './configuration.service'; import { ConnectionsService } from './connections.service'; -import { CookieService } from 'ngx-cookie-service'; -import { Injectable } from '@angular/core'; -import { catchError } from 'rxjs/operators'; -import { environment } from "../../environments/environment"; @Injectable() export class TokenInterceptor implements HttpInterceptor { - private config; + private config; - constructor( - private cookieService: CookieService, - private _connections: ConnectionsService, - private _configuration: ConfigurationService - ) {} + constructor( + private cookieService: CookieService, + private _connections: ConnectionsService, + private _configuration: ConfigurationService, + ) {} - normalizeURL(url: string, baseURL: string, saasURL: string): string { - if (url.startsWith('/assets')) { - return undefined; - } - if (url.startsWith('/saas')) { - return `${saasURL}${url}`; - } - if (url.startsWith('http://')) { - return url; - } - if (url.startsWith('https://')) { - return url; - } - return `${baseURL}${url}`; - } + normalizeURL(url: string, baseURL: string, saasURL: string): string { + if (url.startsWith('/assets')) { + return undefined; + } + if (url.startsWith('/saas')) { + return `${saasURL}${url}`; + } + if (url.startsWith('http://')) { + return url; + } + if (url.startsWith('https://')) { + return url; + } + return `${baseURL}${url}`; + } - intercept(request: HttpRequest, next: HttpHandler): Observable> { - this.config = this._configuration.getConfig(); - const autoadmin_gclid_cookie = this.cookieService.get('autoadmin_gclid'); - const connectionID = this._connections.currentConnectionID; + intercept(request: HttpRequest, next: HttpHandler): Observable> { + this.config = this._configuration.getConfig(); + const autoadmin_gclid_cookie = this.cookieService.get('autoadmin_gclid'); + const connectionID = this._connections.currentConnectionID; - request = request.clone({ - url: this.normalizeURL(request.url, environment.apiRoot || this.config.baseURL, environment.saasURL), - setHeaders: { - GCLID: autoadmin_gclid_cookie - }, - withCredentials: true - }); + request = request.clone({ + url: this.normalizeURL(request.url, environment.apiRoot || this.config.baseURL, environment.saasURL), + setHeaders: { + GCLID: autoadmin_gclid_cookie, + }, + withCredentials: true, + }); - if (connectionID && !request.headers.has('masterpwd')) { - const masterKey = localStorage.getItem(`${connectionID}__masterKey`) || ''; - request = request.clone({ - setHeaders: { - masterpwd: masterKey - }, - }); - }; - return next.handle(request).pipe( - catchError((error: HttpErrorResponse) => { - if (error.status === 401) { - localStorage.removeItem('token_expiration') - location.href = '/login'; - } - return throwError(error); - }) - ); - } + if (connectionID && !request.headers.has('masterpwd')) { + const masterKey = localStorage.getItem(`${connectionID}__masterKey`) || ''; + request = request.clone({ + setHeaders: { + masterpwd: masterKey, + }, + }); + } + return next.handle(request).pipe( + catchError((error: HttpErrorResponse) => { + if (error.status === 401) { + localStorage.removeItem('token_expiration'); + location.href = '/login'; + } + return throwError(error); + }), + ); + } } diff --git a/frontend/src/app/services/ui-settings.service.spec.ts b/frontend/src/app/services/ui-settings.service.spec.ts index c174e5b66..8d725a2e3 100644 --- a/frontend/src/app/services/ui-settings.service.spec.ts +++ b/frontend/src/app/services/ui-settings.service.spec.ts @@ -1,20 +1,20 @@ -import { MatSnackBarModule } from '@angular/material/snack-bar'; +import { provideHttpClient } from '@angular/common/http'; import { TestBed } from '@angular/core/testing'; +import { MatSnackBarModule } from '@angular/material/snack-bar'; import { UiSettingsService } from './ui-settings.service'; -import { provideHttpClient } from '@angular/common/http'; describe('UiSettingsService', () => { - let service: UiSettingsService; + let service: UiSettingsService; - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [MatSnackBarModule], - providers: [provideHttpClient()] - }); - service = TestBed.inject(UiSettingsService); - }); + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [MatSnackBarModule], + providers: [provideHttpClient()], + }); + service = TestBed.inject(UiSettingsService); + }); - it('should be created', () => { - expect(service).toBeTruthy(); - }); + it('should be created', () => { + expect(service).toBeTruthy(); + }); }); diff --git a/frontend/src/app/services/ui-settings.service.ts b/frontend/src/app/services/ui-settings.service.ts index 5fa8ca761..3e386f9d4 100644 --- a/frontend/src/app/services/ui-settings.service.ts +++ b/frontend/src/app/services/ui-settings.service.ts @@ -28,7 +28,7 @@ export class UiSettingsService { return this._isDarkMode; } -updateGlobalSetting(key: string, value: any) { + updateGlobalSetting(key: string, value: any) { this.settings.globalSettings[key] = value; this.syncUiSettings().subscribe(); } @@ -42,9 +42,9 @@ updateGlobalSetting(key: string, value: any) { } updateTableSetting(connectionId: string, tableName: string, key: string, value: any) { - console.log('updateTableSetting') + console.log('updateTableSetting'); if (!this.settings.connections[connectionId]) { - this.settings.connections[connectionId] = { shownTableTitles: false, tables: {} }; + this.settings.connections[connectionId] = { shownTableTitles: false, tables: {} }; } // if (!this.settings.connections[connectionId].tables[tableName]) { // this.settings.connections[connectionId].tables[tableName] = { shownColumns: [] }; diff --git a/frontend/src/app/services/url-params.interceptor.spec.ts b/frontend/src/app/services/url-params.interceptor.spec.ts index 8690b2a9b..e8730722d 100644 --- a/frontend/src/app/services/url-params.interceptor.spec.ts +++ b/frontend/src/app/services/url-params.interceptor.spec.ts @@ -3,14 +3,14 @@ import { TestBed } from '@angular/core/testing'; import { EncodeUrlParamsSafelyInterceptor } from './url-params.interceptor'; describe('ServicesInterceptor', () => { - beforeEach(() => TestBed.configureTestingModule({ - providers: [ - EncodeUrlParamsSafelyInterceptor - ] - })); + beforeEach(() => + TestBed.configureTestingModule({ + providers: [EncodeUrlParamsSafelyInterceptor], + }), + ); - it('should be created', () => { - const interceptor: EncodeUrlParamsSafelyInterceptor = TestBed.inject(EncodeUrlParamsSafelyInterceptor); - expect(interceptor).toBeTruthy(); - }); + it('should be created', () => { + const interceptor: EncodeUrlParamsSafelyInterceptor = TestBed.inject(EncodeUrlParamsSafelyInterceptor); + expect(interceptor).toBeTruthy(); + }); }); diff --git a/frontend/src/app/services/url-params.interceptor.ts b/frontend/src/app/services/url-params.interceptor.ts index b835a28c1..0ea7524f4 100644 --- a/frontend/src/app/services/url-params.interceptor.ts +++ b/frontend/src/app/services/url-params.interceptor.ts @@ -1,40 +1,42 @@ -import { Injectable } from '@angular/core'; import { - HttpRequest, - HttpHandler, - HttpEvent, - HttpInterceptor, - HttpParams, - HttpParameterCodec + HttpEvent, + HttpHandler, + HttpInterceptor, + HttpParameterCodec, + HttpParams, + HttpRequest, } from '@angular/common/http'; +import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; @Injectable() export class EncodeUrlParamsSafelyInterceptor implements HttpInterceptor, HttpParameterCodec { - intercept(req: HttpRequest, next: HttpHandler): Observable> { - const newParams = new HttpParams({ - fromString: req.params.toString(), - encoder: this, - }); + intercept(req: HttpRequest, next: HttpHandler): Observable> { + const newParams = new HttpParams({ + fromString: req.params.toString(), + encoder: this, + }); - return next.handle(req.clone({ - params: newParams, - })); - } + return next.handle( + req.clone({ + params: newParams, + }), + ); + } - encodeKey(key: string): string { - return encodeURIComponent(key); - } + encodeKey(key: string): string { + return encodeURIComponent(key); + } - encodeValue(value: string): string { - return encodeURIComponent(value); - } + encodeValue(value: string): string { + return encodeURIComponent(value); + } - decodeKey(key: string): string { - return decodeURIComponent(key); - } + decodeKey(key: string): string { + return decodeURIComponent(key); + } - decodeValue(value: string): string { - return decodeURIComponent(value); - } -} \ No newline at end of file + decodeValue(value: string): string { + return decodeURIComponent(value); + } +} diff --git a/frontend/src/app/validators/base64.validator.ts b/frontend/src/app/validators/base64.validator.ts index 20bc5694e..4f218b03c 100644 --- a/frontend/src/app/validators/base64.validator.ts +++ b/frontend/src/app/validators/base64.validator.ts @@ -1,14 +1,14 @@ -import { AbstractControl, ValidationErrors, ValidatorFn } from "@angular/forms"; +import { AbstractControl, ValidationErrors, ValidatorFn } from '@angular/forms'; -export function base64Validation():ValidatorFn { - return (control: AbstractControl) : ValidationErrors | null=> { - if (control.value) { - try { - atob(control.value); - } catch(e) { - console.log(e); - return { 'isInvalidBase64': true } - }; - } - } -} \ No newline at end of file +export function base64Validation(): ValidatorFn { + return (control: AbstractControl): ValidationErrors | null => { + if (control.value) { + try { + atob(control.value); + } catch (e) { + console.log(e); + return { isInvalidBase64: true }; + } + } + }; +} diff --git a/frontend/src/app/validators/email.validator.ts b/frontend/src/app/validators/email.validator.ts index c08bf54d3..1f1e90c99 100644 --- a/frontend/src/app/validators/email.validator.ts +++ b/frontend/src/app/validators/email.validator.ts @@ -1,11 +1,12 @@ -import { AbstractControl, ValidationErrors, ValidatorFn } from "@angular/forms"; +import { AbstractControl, ValidationErrors, ValidatorFn } from '@angular/forms'; -export function emailValidation():ValidatorFn { - return (control: AbstractControl) : ValidationErrors | null=> { - if (control.value) { - const emailRegex = /(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])/; - if (!emailRegex.test(control.value)) return { 'isInvalidEmail': true }; - // don't make return if valid! or return null - } - } -} \ No newline at end of file +export function emailValidation(): ValidatorFn { + return (control: AbstractControl): ValidationErrors | null => { + if (control.value) { + const emailRegex = + /(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])/; + if (!emailRegex.test(control.value)) return { isInvalidEmail: true }; + // don't make return if valid! or return null + } + }; +} diff --git a/frontend/src/app/validators/hex.validator.ts b/frontend/src/app/validators/hex.validator.ts index 2515e61dd..8f6d49048 100644 --- a/frontend/src/app/validators/hex.validator.ts +++ b/frontend/src/app/validators/hex.validator.ts @@ -1,11 +1,11 @@ -import { AbstractControl, ValidationErrors, ValidatorFn } from "@angular/forms"; +import { AbstractControl, ValidationErrors, ValidatorFn } from '@angular/forms'; -export function hexValidation():ValidatorFn { - return (control: AbstractControl) : ValidationErrors | null=> { - if (control.value) { - const hexRegex = /^[-+]?[0-9A-Fa-f]+\.?[0-9A-Fa-f]*?$/; - if (!hexRegex.test(control.value) || !(control.value.length % 2 === 0)) return { 'isInvalidHex': true }; - // don't make return if valid! or return null - } - } -} \ No newline at end of file +export function hexValidation(): ValidatorFn { + return (control: AbstractControl): ValidationErrors | null => { + if (control.value) { + const hexRegex = /^[-+]?[0-9A-Fa-f]+\.?[0-9A-Fa-f]*?$/; + if (!hexRegex.test(control.value) || !(control.value.length % 2 === 0)) return { isInvalidHex: true }; + // don't make return if valid! or return null + } + }; +} diff --git a/frontend/src/app/validators/hostname.validator.ts b/frontend/src/app/validators/hostname.validator.ts index 19ec23e44..d4c34b07c 100644 --- a/frontend/src/app/validators/hostname.validator.ts +++ b/frontend/src/app/validators/hostname.validator.ts @@ -1,29 +1,29 @@ -import { AbstractControl, ValidationErrors, ValidatorFn } from "@angular/forms"; - +import { AbstractControl, ValidationErrors, ValidatorFn } from '@angular/forms'; +import is_ip_private from 'private-ip'; import isFQDN from 'validator/es/lib/isFQDN'; import isIP from 'validator/es/lib/isIP'; -import is_ip_private from 'private-ip'; -import { DBtype } from "../models/connection"; +import { DBtype } from '../models/connection'; -export function hostnameValidation(dbType: DBtype):ValidatorFn { - return (control: AbstractControl) : ValidationErrors | null=> { - if (control.value) { - let hostname = (control.value as string); +export function hostnameValidation(dbType: DBtype): ValidatorFn { + return (control: AbstractControl): ValidationErrors | null => { + if (control.value) { + let hostname = control.value as string; - if (dbType === 'dynamodb') { - if (!hostname.startsWith("https://")) { - return { 'missingHttps': true }; - } + if (dbType === 'dynamodb') { + if (!hostname.startsWith('https://')) { + return { missingHttps: true }; + } - hostname = hostname.replace(/^https:\/\//, ''); - } + hostname = hostname.replace(/^https:\/\//, ''); + } - if (dbType === 'mongodb') { - hostname = hostname.replace(/^mongodb\+srv:\/\//, ''); - } + if (dbType === 'mongodb') { + hostname = hostname.replace(/^mongodb\+srv:\/\//, ''); + } - if (control.value === 'localhost' || isIP(control.value) && is_ip_private(control.value)) return { 'isLocalhost': true } - if (!(isIP(hostname) || isFQDN(hostname))) return { 'isInvalidHostname': true } - } - } + if (control.value === 'localhost' || (isIP(control.value) && is_ip_private(control.value))) + return { isLocalhost: true }; + if (!(isIP(hostname) || isFQDN(hostname))) return { isInvalidHostname: true }; + } + }; } diff --git a/frontend/src/app/validators/text.validator.ts b/frontend/src/app/validators/text.validator.ts index 3618d8285..a6ed2db71 100644 --- a/frontend/src/app/validators/text.validator.ts +++ b/frontend/src/app/validators/text.validator.ts @@ -1,56 +1,54 @@ +import { AbstractControl, ValidationErrors, ValidatorFn } from '@angular/forms'; import * as validator from 'validator'; -import { AbstractControl, ValidationErrors, ValidatorFn } from "@angular/forms"; - -export function textValidation(validateType: string, regexPattern: string):ValidatorFn { - return (control: AbstractControl) : ValidationErrors | null=> { - - if (!control.value || control.value === '') { - return null; - } - - if (!validateType) { - return null; - } - - const stringValue = String(control.value); - - // Special case for regex validation - if (validateType === 'regex') { - if (!regexPattern) { - return null; - } - try { - const regex = new RegExp(regexPattern); - if (!regex.test(stringValue)) { - return { invalidPattern: true }; - } - } catch (error) { - console.warn('Invalid regex pattern:', error); - return null; - } - return null; - } - - // Check if validator has this method - // biome-ignore lint: it is expected to import all exports of validator - const validatorMethod = validator[validateType]; - if (typeof validatorMethod !== 'function') { - console.warn(`Unknown validator method: ${validateType}`); - return null; - } - - try { - // Call the validator method - const isValid = validatorMethod(stringValue); - if (!isValid) { - return { [`invalid${validateType}`]: true }; - } - } catch (error) { - console.warn(`Validation error for ${validateType}:`, error); - return null; - } - - return null; - } +export function textValidation(validateType: string, regexPattern: string): ValidatorFn { + return (control: AbstractControl): ValidationErrors | null => { + if (!control.value || control.value === '') { + return null; + } + + if (!validateType) { + return null; + } + + const stringValue = String(control.value); + + // Special case for regex validation + if (validateType === 'regex') { + if (!regexPattern) { + return null; + } + try { + const regex = new RegExp(regexPattern); + if (!regex.test(stringValue)) { + return { invalidPattern: true }; + } + } catch (error) { + console.warn('Invalid regex pattern:', error); + return null; + } + return null; + } + + // Check if validator has this method + // biome-ignore lint: it is expected to import all exports of validator + const validatorMethod = validator[validateType]; + if (typeof validatorMethod !== 'function') { + console.warn(`Unknown validator method: ${validateType}`); + return null; + } + + try { + // Call the validator method + const isValid = validatorMethod(stringValue); + if (!isValid) { + return { [`invalid${validateType}`]: true }; + } + } catch (error) { + console.warn(`Validation error for ${validateType}:`, error); + return null; + } + + return null; + }; } diff --git a/frontend/src/app/validators/url.validator.ts b/frontend/src/app/validators/url.validator.ts index 9fd00eab6..49a5cd7b5 100644 --- a/frontend/src/app/validators/url.validator.ts +++ b/frontend/src/app/validators/url.validator.ts @@ -1,21 +1,19 @@ -import { AbstractControl, ValidationErrors, ValidatorFn } from "@angular/forms"; +import { AbstractControl, ValidationErrors, ValidatorFn } from '@angular/forms'; -export function urlValidation(prefix: string = ''):ValidatorFn { - return (control: AbstractControl) : ValidationErrors | null=> { +export function urlValidation(prefix: string = ''): ValidatorFn { + return (control: AbstractControl): ValidationErrors | null => { + if (control.value) { + let url = control.value as string; - if (control.value) { - let url = (control.value as string); + // If there's a prefix, prepend it to the URL for validation + const fullUrl = prefix ? prefix + url : url; - // If there's a prefix, prepend it to the URL for validation - const fullUrl = prefix ? prefix + url : url; - - try { - new URL(fullUrl); - } - catch (_e) { - return { 'isInvalidURL': true }; - } - } - return null; - } + try { + new URL(fullUrl); + } catch (_e) { + return { isInvalidURL: true }; + } + } + return null; + }; } diff --git a/frontend/src/config.json b/frontend/src/config.json index 0df8f307f..343eddd45 100644 --- a/frontend/src/config.json +++ b/frontend/src/config.json @@ -1,5 +1,5 @@ { - "#important to keep it /api for prod": true, - "baseURL": "/api", - "saasURL": "" + "#important to keep it /api for prod": true, + "baseURL": "/api", + "saasURL": "" } diff --git a/frontend/src/environments/environment.prod.ts b/frontend/src/environments/environment.prod.ts index 662d75906..7ee4d9505 100644 --- a/frontend/src/environments/environment.prod.ts +++ b/frontend/src/environments/environment.prod.ts @@ -1,8 +1,8 @@ export const environment = { - production: true, - saas: false, - apiRoot: "/api", - saasURL: "", - stagingHost: "rocketadmin-dev.tail9f8b2.ts.net", // Tailscale host - version: '0.0.0' + production: true, + saas: false, + apiRoot: '/api', + saasURL: '', + stagingHost: 'rocketadmin-dev.tail9f8b2.ts.net', // Tailscale host + version: '0.0.0', }; diff --git a/frontend/src/environments/environment.ts b/frontend/src/environments/environment.ts index ab2f5ad78..8964888f0 100644 --- a/frontend/src/environments/environment.ts +++ b/frontend/src/environments/environment.ts @@ -3,10 +3,10 @@ // The list of file replacements can be found in `angular.json`. export const environment = { - production: false, - apiRoot: "/api", - stagingHost: "rocketadmin-dev.tail9f8b2.ts.net", // Tailscale host - version: '0.0.0' + production: false, + apiRoot: '/api', + stagingHost: 'rocketadmin-dev.tail9f8b2.ts.net', // Tailscale host + version: '0.0.0', } as Record; /* @@ -16,4 +16,4 @@ export const environment = { * This import should be commented out in production mode because it will have a negative impact * on performance if an error is thrown. */ -// import 'zone.js/plugins/zone-error'; // Included with Angular CLI. \ No newline at end of file +// import 'zone.js/plugins/zone-error'; // Included with Angular CLI. diff --git a/frontend/src/polyfills.ts b/frontend/src/polyfills.ts index f2ba27fe3..43c98ea7f 100644 --- a/frontend/src/polyfills.ts +++ b/frontend/src/polyfills.ts @@ -59,12 +59,11 @@ import 'zone.js'; // Included with Angular CLI. - /*************************************************************************************************** * APPLICATION IMPORTS */ (window as any).global = window; (window as any).process = { - env: { DEBUG: undefined }, + env: { DEBUG: undefined }, }; diff --git a/frontend/src/proxy.conf.json b/frontend/src/proxy.conf.json index 1252f8479..01c99fe49 100644 --- a/frontend/src/proxy.conf.json +++ b/frontend/src/proxy.conf.json @@ -1,6 +1,6 @@ -{ - "/api/**": { - "target": "https://app.rocketadmin.com/api/", - "secure": false - } -} \ No newline at end of file +{ + "/api/**": { + "target": "https://app.rocketadmin.com/api/", + "secure": false + } +} diff --git a/frontend/tslint.json b/frontend/tslint.json index f0adae980..8ac49059f 100644 --- a/frontend/tslint.json +++ b/frontend/tslint.json @@ -1,92 +1,53 @@ { - "extends": "tslint:recommended", - "rules": { - "array-type": false, - "arrow-parens": false, - "deprecation": { - "severity": "warning" - }, - "component-class-suffix": true, - "contextual-lifecycle": true, - "directive-class-suffix": true, - "directive-selector": [ - true, - "attribute", - "app", - "camelCase" - ], - "component-selector": [ - true, - "element", - "app", - "kebab-case" - ], - "import-blacklist": [ - true, - "rxjs/Rx" - ], - "interface-name": false, - "max-classes-per-file": false, - "max-line-length": [ - true, - 140 - ], - "member-access": false, - "member-ordering": [ - true, - { - "order": [ - "static-field", - "instance-field", - "static-method", - "instance-method" - ] - } - ], - "no-consecutive-blank-lines": false, - "no-console": [ - true, - "debug", - "info", - "time", - "timeEnd", - "trace" - ], - "no-empty": false, - "no-inferrable-types": [ - true, - "ignore-params" - ], - "no-non-null-assertion": true, - "no-redundant-jsdoc": true, - "no-switch-case-fall-through": true, - "no-use-before-declare": true, - "no-var-requires": false, - "object-literal-key-quotes": [ - true, - "as-needed" - ], - "object-literal-sort-keys": false, - "ordered-imports": false, - "quotemark": [ - true, - "single" - ], - "trailing-comma": false, - "no-conflicting-lifecycle": true, - "no-host-metadata-property": true, - "no-input-rename": true, - "no-inputs-metadata-property": true, - "no-output-native": true, - "no-output-on-prefix": true, - "no-output-rename": true, - "no-outputs-metadata-property": true, - "template-banana-in-box": true, - "template-no-negated-async": true, - "use-lifecycle-interface": true, - "use-pipe-transform-interface": true - }, - "rulesDirectory": [ - "codelyzer" - ] -} \ No newline at end of file + "extends": "tslint:recommended", + "rules": { + "array-type": false, + "arrow-parens": false, + "deprecation": { + "severity": "warning" + }, + "component-class-suffix": true, + "contextual-lifecycle": true, + "directive-class-suffix": true, + "directive-selector": [true, "attribute", "app", "camelCase"], + "component-selector": [true, "element", "app", "kebab-case"], + "import-blacklist": [true, "rxjs/Rx"], + "interface-name": false, + "max-classes-per-file": false, + "max-line-length": [true, 140], + "member-access": false, + "member-ordering": [ + true, + { + "order": ["static-field", "instance-field", "static-method", "instance-method"] + } + ], + "no-consecutive-blank-lines": false, + "no-console": [true, "debug", "info", "time", "timeEnd", "trace"], + "no-empty": false, + "no-inferrable-types": [true, "ignore-params"], + "no-non-null-assertion": true, + "no-redundant-jsdoc": true, + "no-switch-case-fall-through": true, + "no-use-before-declare": true, + "no-var-requires": false, + "object-literal-key-quotes": [true, "as-needed"], + "object-literal-sort-keys": false, + "ordered-imports": false, + "quotemark": [true, "single"], + "trailing-comma": false, + "no-conflicting-lifecycle": true, + "no-host-metadata-property": true, + "no-input-rename": true, + "no-inputs-metadata-property": true, + "no-output-native": true, + "no-output-on-prefix": true, + "no-output-rename": true, + "no-outputs-metadata-property": true, + "template-banana-in-box": true, + "template-no-negated-async": true, + "use-lifecycle-interface": true, + "use-pipe-transform-interface": true + }, + "rulesDirectory": ["codelyzer"] +}