From 5dc005e5955cf75889f921786281fc470eb26aed Mon Sep 17 00:00:00 2001 From: Geoffroy Empain Date: Wed, 24 Mar 2021 14:22:34 +0100 Subject: [PATCH] feat: merge ui and server repos (closes #203) (#214) --- .dockerignore | 11 +- .github/pull_request_template.md | 4 +- .github/workflows/main.yml | 19 +- .gitignore | 4 +- Dockerfile | 40 + README.md | 52 +- docker/Dockerfile | 16 - docker/{files => }/caddy-config.json | 0 docker/{files => }/entrypoint.sh | 0 docker/unified.Dockerfile | 35 - docs/generate-env-docs.ts | 2 +- package-lock.json | 22491 ++++------------ package.json | 144 +- publish.sh | 5 + .env.example => server/.env.example | 1 - .eslintignore => server/.eslintignore | 0 .eslintrc.js => server/.eslintrc.js | 0 jest.config.js => server/jest.config.js | 0 .../migrate-mongo-config.js | 0 .../migrations/.gitkeep | 0 server/package-lock.json | 18007 +++++++++++++ server/package.json | 123 + setupTests.js => server/setupTests.js | 0 {src => server/src}/auth/auth.ts | 0 {src => server/src}/auth/authenticate.ts | 0 .../src}/auth/create-or-update-user.ts | 0 .../src}/auth/guards/api-guard.spec.ts | 0 {src => server/src}/auth/guards/api-guard.ts | 0 .../src}/auth/guards/auth-guard.spec.ts | 0 {src => server/src}/auth/guards/auth-guard.ts | 0 .../guards/is-admin-or-owner-guard.spec.ts | 0 .../auth/guards/is-admin-or-owner-guard.ts | 0 .../auth/guards/is-admin-or-owner.spec.ts | 0 .../src}/auth/guards/is-admin-or-owner.ts | 0 {src => server/src}/auth/guards/is-admin.ts | 0 {src => server/src}/auth/guards/is-owner.ts | 0 .../auth/handlers/authorize-api-req.spec.ts | 0 .../src}/auth/handlers/authorize-api-req.ts | 0 .../src}/auth/handlers/authorize-req.spec.ts | 0 .../src}/auth/handlers/authorize-req.ts | 0 .../auth/handlers/get-auth-methods.spec.ts | 0 .../src}/auth/handlers/get-auth-methods.ts | 0 .../src}/auth/handlers/redirect-to-ui.ts | 4 +- .../src}/auth/handlers/sign-out.spec.ts | 0 {src => server/src}/auth/handlers/sign-out.ts | 0 {src => server/src}/auth/passport.ts | 0 .../src}/auth/passport/auth-methods.ts | 0 {src => server/src}/auth/passport/gitea.ts | 0 {src => server/src}/auth/passport/github.ts | 0 {src => server/src}/auth/passport/gitlab.ts | 0 {src => server/src}/auth/passport/google.ts | 0 .../src}/auth/passport/in-memory.ts | 0 .../auth/passport/providers/gitea/gitea.ts | 0 .../gitea/types/gitea-oauth-token-grant.ts | 0 .../providers/gitea/types/gitea-org.ts | 0 .../providers/gitea/types/gitea-user.ts | 0 .../auth/passport/providers/github/github.ts | 0 .../providers/github/types/github-email.ts | 0 .../providers/github/types/github-org.ts | 0 .../providers/github/types/github-user.ts | 0 .../auth/passport/providers/gitlab/gitlab.ts | 0 .../providers/gitlab/types/gitlab-group.ts | 0 .../gitlab/types/gitlab-oauth-token-grant.ts | 0 .../providers/gitlab/types/gitlab-user.ts | 0 {src => server/src}/auth/routes.ts | 0 {src => server/src}/auth/serialize-user.ts | 0 .../auth/utils/get-user-from-socket.spec.ts | 0 .../src}/auth/utils/get-user-from-socket.ts | 0 .../src}/auth/utils/get-user.spec.ts | 0 {src => server/src}/auth/utils/get-user.ts | 0 .../src}/auth/utils/verify-token.spec.ts | 0 .../src}/auth/utils/verify-token.ts | 0 {src => server/src}/caddy/config/api-route.ts | 0 {src => server/src}/caddy/config/fallback.ts | 0 .../src}/caddy/config/get-error-routes.ts | 0 .../config/sites/generate-site-routes.ts | 0 .../caddy/config/sites/get-auth-handler.ts | 0 .../sites/get-branch-404-error-route.ts | 0 .../caddy/config/sites/get-redirect-route.ts | 0 {src => server/src}/caddy/config/ssl.ts | 2 - {src => server/src}/caddy/config/ui-route.ts | 6 +- {src => server/src}/caddy/configuration.ts | 0 .../src}/caddy/definitions/admin.d.ts | 0 .../src}/caddy/definitions/apps.d.ts | 0 .../src}/caddy/definitions/apps/http.d.ts | 0 .../caddy/definitions/apps/http/route.d.ts | 0 .../src}/caddy/definitions/apps/pki.d.ts | 0 .../src}/caddy/definitions/apps/tls.d.ts | 0 .../src}/caddy/definitions/config.d.ts | 0 .../src}/caddy/definitions/handlers.d.ts | 0 .../src}/caddy/definitions/logging.d.ts | 0 .../src}/caddy/definitions/matchers.d.ts | 0 .../src}/caddy/definitions/storage.d.ts | 0 {src => server/src}/caddy/generate-config.ts | 0 .../caddy/utils/get-reverse-proxy-dial.ts | 0 .../src}/commons/allowed-hosts.spec.ts | 0 {src => server/src}/commons/allowed-hosts.ts | 0 .../src}/commons/axios/axios-error.spec.ts | 0 .../src}/commons/axios/axios-error.ts | 0 {src => server/src}/commons/axios/axios.ts | 0 .../src}/commons/axios/ensure-stack-trace.ts | 0 {src => server/src}/commons/enum-to-array.ts | 0 {src => server/src}/commons/env/parse-env.ts | 0 .../src}/commons/env/transformers.ts | 0 .../src}/commons/errors/app-error.ts | 0 .../src}/commons/errors/bad-request-error.ts | 0 .../src}/commons/errors/forbidden-error.ts | 0 .../src}/commons/errors/http-error.ts | 0 .../errors/invalid-environment-error.ts | 0 .../src}/commons/errors/not-found-error.ts | 0 .../src}/commons/errors/unauthorized-error.ts | 0 .../src}/commons/express-joi/body.spec.ts | 0 .../src}/commons/express-joi/body.ts | 0 .../src}/commons/express-joi/params.spec.ts | 0 .../src}/commons/express-joi/params.ts | 0 .../src}/commons/express-joi/query.spec.ts | 0 .../src}/commons/express-joi/query.ts | 0 {src => server/src}/commons/express/guard.ts | 0 .../commons/express/handlers/no-content.ts | 0 .../src}/commons/force-chalk-colors.ts | 0 .../commons/logger/build-winston-logger.ts | 0 {src => server/src}/commons/logger/logger.ts | 0 {src => server/src}/commons/multer/types.ts | 0 {src => server/src}/commons/nv-i18n.ts | 0 .../src}/commons/types/constructor.ts | 0 {src => server/src}/commons/types/duration.ts | 0 .../src}/commons/utils/array-set.ts | 0 {src => server/src}/commons/utils/base64.ts | 0 .../commons/utils/ensure-empty-directory.ts | 0 .../src}/commons/utils/exec-async.ts | 0 .../src}/commons/utils/handle-error.ts | 0 .../src}/commons/utils/month-diff.ts | 0 .../src}/commons/utils/set-replacer.ts | 0 .../commons/utils/wrap-async-middleware.ts | 0 .../src}/commons/validators/is-certificate.ts | 0 .../commons/validators/is-cron-expression.ts | 0 .../commons/validators/is-moment-timezone.ts | 0 .../commons/validators/is-rsa-private-key.ts | 0 .../src}/commons/validators/is-url.ts | 0 .../src}/commons/validators/to-url.ts | 0 {src => server/src}/constants.ts | 0 {src => server/src}/db/db.ts | 0 .../src}/db/indexes/configure-indexes.spec.ts | 0 .../src}/db/indexes/configure-indexes.ts | 0 .../create-indexes-for-collection.spec.ts | 0 .../indexes/create-indexes-for-collection.ts | 0 .../src}/db/indexes/mongo-error-code.ts | 0 .../src}/db/migrate/migrate.spec.ts | 0 {src => server/src}/db/migrate/migrate.ts | 0 .../src}/db/migrate/roll-backwards.spec.ts | 0 .../src}/db/migrate/roll-backwards.ts | 0 .../src}/db/migrate/roll-forward.spec.ts | 0 .../src}/db/migrate/roll-forward.ts | 0 {src => server/src}/db/setup-db-indexes.ts | 0 {src => server/src}/emails/emails.ts | 0 .../src}/emails/methods/send-invite.ts | 0 {src => server/src}/emails/send-email.ts | 0 .../src}/emails/templates/README.md | 0 .../src}/emails/templates/form-submission.hbs | 0 .../src}/emails/templates/invite.hbs | 0 .../src}/entities/api/api-endpoint.ts | 0 {src => server/src}/entities/api/api-scope.ts | 0 {src => server/src}/entities/api/api-token.ts | 0 .../api/guards/api-token-exists-guard.spec.ts | 0 .../api/guards/api-token-exists-guard.ts | 0 .../guards/can-admin-api-token-guard.spec.ts | 0 .../api/guards/can-admin-api-token-guard.ts | 0 .../handlers/endpoints/list-api-endpoints.ts | 0 .../api/handlers/tokens/create-api-token.ts | 0 .../api/handlers/tokens/delete-api-token.ts | 0 .../api/handlers/tokens/get-api-token.ts | 0 .../api/handlers/tokens/list-api-tokens.ts | 0 .../api/handlers/tokens/update-api-token.ts | 0 {src => server/src}/entities/api/routes.ts | 0 .../src}/entities/api/serialize-api-token.ts | 0 .../src}/entities/api/serialize-endpoint.ts | 0 {src => server/src}/entities/forms/form.ts | 0 .../src}/entities/forms/serialize-form.ts | 0 .../src}/entities/forms/submit-email-form.ts | 0 .../invites/handlers/accept-invite.ts | 0 .../invites/handlers/decline-invite.ts | 0 .../entities/invites/handlers/get-invite.ts | 0 .../src}/entities/invites/routes.ts | 0 .../src}/entities/invites/serialize-invite.ts | 0 .../members/guards/can-admin-member-guard.ts | 0 .../members/guards/member-exists-guard.ts | 0 .../members/handlers/delete-member.ts | 0 .../members/handlers/update-member.ts | 0 .../src}/entities/members/member.ts | 0 .../src}/entities/members/routes.ts | 0 .../src}/entities/members/serialize-member.ts | 0 .../orgs/guards/can-get-org-logo-guard.ts | 0 .../orgs/guards/can-write-org-guard.ts | 0 .../orgs/guards/invite-exists-guard.ts | 0 .../orgs/guards/is-org-member-guard.ts | 0 .../entities/orgs/guards/is-org-member.ts | 0 .../entities/orgs/guards/max-org-guard.ts | 0 .../entities/orgs/guards/org-exists-guard.ts | 0 .../entities/orgs/handlers/create-org.spec.ts | 0 .../src}/entities/orgs/handlers/create-org.ts | 0 .../entities/orgs/handlers/get-org-logo.ts | 0 .../src}/entities/orgs/handlers/get-org.ts | 0 .../orgs/handlers/invites/add-invite.ts | 2 +- .../orgs/handlers/invites/delete-invite.ts | 0 .../orgs/handlers/invites/list-invites.ts | 0 .../src}/entities/orgs/handlers/list-orgs.ts | 0 .../members/get-current-org-member.ts | 0 .../orgs/handlers/members/list-members.ts | 0 .../entities/orgs/handlers/remove-org-logo.ts | 0 .../entities/orgs/handlers/set-org-logo.ts | 0 .../orgs/handlers/sites/list-sites.ts | 0 .../orgs/handlers/teams/create-team.spec.ts | 0 .../orgs/handlers/teams/create-team.ts | 0 .../orgs/handlers/teams/list-teams.ts | 0 .../src}/entities/orgs/handlers/update-org.ts | 0 {src => server/src}/entities/orgs/invite.ts | 0 {src => server/src}/entities/orgs/org.ts | 0 {src => server/src}/entities/orgs/routes.ts | 0 .../src}/entities/orgs/serialize-invite.ts | 0 .../src}/entities/orgs/serialize-org.ts | 0 .../src}/entities/orgs/serialize-user-org.ts | 0 .../guards/can-admin-release-guard.ts | 0 .../releases/guards/can-admin-release.ts | 0 .../releases/guards/validate-captcha.ts | 0 .../releases/handlers/delete-release.ts | 0 .../entities/releases/handlers/get-release.ts | 0 .../entities/releases/handlers/submit-form.ts | 0 .../releases/handlers/update-forms.ts | 0 .../releases/handlers/update-release.ts | 0 .../src}/entities/releases/release.ts | 0 .../src}/entities/releases/routes.ts | 0 .../entities/releases/serialize-release.ts | 0 {src => server/src}/entities/sites/branch.ts | 0 .../src}/entities/sites/get-branch-domain.ts | 0 .../src}/entities/sites/get-branch-url.ts | 0 .../src}/entities/sites/get-redirect-url.ts | 0 .../src}/entities/sites/get-site-dir.ts | 0 .../entities/sites/get-site-main-domain.ts | 0 .../src}/entities/sites/get-site-url.ts | 0 .../sites/guards/branch-exists-guard.spec.ts | 0 .../sites/guards/branch-exists-guard.ts | 0 .../sites/guards/can-admin-site-guard.ts | 0 .../entities/sites/guards/can-admin-site.ts | 0 .../sites/guards/can-upload-release-guard.ts | 0 .../sites/guards/redirect-exists-guard.ts | 0 .../sites/guards/site-exists-guard.ts | 0 .../sites/guards/site-hook-exists-guard.ts | 0 .../handlers/branches/add-branch.spec.ts | 0 .../sites/handlers/branches/add-branch.ts | 0 .../handlers/branches/delete-branch.spec.ts | 0 .../sites/handlers/branches/delete-branch.ts | 0 .../sites/handlers/branches/get-branch.ts | 0 .../branches/list-branch-redirects.ts | 0 .../sites/handlers/branches/list-branches.ts | 0 .../branches/remove-branch-password.ts | 0 .../sites/handlers/branches/rename-branch.ts | 0 .../handlers/branches/set-branch-headers.ts | 0 .../handlers/branches/set-branch-password.ts | 0 .../handlers/branches/set-branch-redirects.ts | 0 .../handlers/branches/set-branch-release.ts | 0 .../handlers/branches/validate-branch-name.ts | 0 .../entities/sites/handlers/delete-site.ts | 0 .../entities/sites/handlers/get-site-logo.ts | 0 .../src}/entities/sites/handlers/get-site.ts | 0 .../sites/handlers/hooks/create-site-hook.ts | 0 .../sites/handlers/hooks/delete-site-hook.ts | 0 .../sites/handlers/hooks/get-site-hook.ts | 0 .../sites/handlers/hooks/list-site-events.ts | 0 .../hooks/list-site-hook-deliveries.ts | 0 .../sites/handlers/hooks/list-site-hooks.ts | 0 .../sites/handlers/hooks/site-hook.ts | 0 .../sites/handlers/hooks/update-site-hook.ts | 0 .../handlers/releases/list-site-releases.ts | 0 .../sites/handlers/releases/upload-release.ts | 8 +- .../sites/handlers/remove-site-logo.ts | 0 .../sites/handlers/remove-site-password.ts | 0 .../entities/sites/handlers/set-site-logo.ts | 0 .../sites/handlers/set-site-password.ts | 0 .../sites/handlers/tokens/add-token.ts | 0 .../sites/handlers/tokens/delete-token.ts | 0 .../sites/handlers/tokens/list-tokens.ts | 0 .../entities/sites/handlers/update-site.ts | 0 .../sites/handlers/validate-site-name.ts | 0 .../src}/entities/sites/hash-password.ts | 0 {src => server/src}/entities/sites/header.ts | 0 .../entities/sites/link-branch-to-release.ts | 0 .../src}/entities/sites/password.ts | 0 .../src}/entities/sites/redirect.ts | 0 {src => server/src}/entities/sites/routes.ts | 0 .../src}/entities/sites/serialize-branch.ts | 0 .../src}/entities/sites/serialize-redirect.ts | 0 .../entities/sites/serialize-site-token.ts | 0 .../src}/entities/sites/serialize-site.ts | 0 .../src}/entities/sites/set-site-headers.ts | 0 {src => server/src}/entities/sites/site.ts | 0 .../sites/yaml-config/parse-config.ts | 0 .../entities/sites/yaml-config/site-config.ts | 0 .../teams/guards/can-admin-team-guard.ts | 0 .../teams/guards/can-read-team-guard.ts | 0 .../entities/teams/guards/can-read-team.ts | 0 .../teams/guards/org-member-exists-guard.ts | 0 .../teams/guards/team-exists-guard.ts | 0 .../entities/teams/handlers/delete-team.ts | 0 .../entities/teams/handlers/get-team-logo.ts | 0 .../src}/entities/teams/handlers/get-team.ts | 0 .../teams/handlers/members/add-member.ts | 0 .../teams/handlers/members/delete-member.ts | 0 .../teams/handlers/members/list-members.ts | 0 .../teams/handlers/remove-team-logo.ts | 0 .../entities/teams/handlers/set-team-logo.ts | 0 .../teams/handlers/sites/create-site.spec.ts | 0 .../teams/handlers/sites/create-site.ts | 0 .../handlers/sites/list-team-sites.spec.ts | 0 .../teams/handlers/sites/list-team-sites.ts | 0 .../entities/teams/handlers/update-team.ts | 0 {src => server/src}/entities/teams/routes.ts | 0 .../entities/teams/serialize-team-member.ts | 0 .../src}/entities/teams/serialize-team.ts | 0 {src => server/src}/entities/teams/team.ts | 0 .../users/guards/user-exists-guard.ts | 0 .../users/handlers/get-user-handler.ts | 0 .../users/handlers/invalidate-tokens.ts | 0 {src => server/src}/entities/users/routes.ts | 0 {src => server/src}/entities/users/user.ts | 0 {src => server/src}/env/env-spec.ts | 18 +- {src => server/src}/env/env.ts | 1 - {src => server/src}/events/emit-event.ts | 0 {src => server/src}/events/event-data.ts | 0 {src => server/src}/events/event-type.ts | 0 {src => server/src}/hooks/deliver-hook.ts | 0 .../src}/hooks/get-hooks-for-event.ts | 0 .../handlers/email/get-message-for-event.ts | 0 .../hooks/handlers/email/handle-email-hook.ts | 0 .../hooks/handlers/email/send-email-hook.ts | 0 .../src}/hooks/handlers/get-hook-handler.ts | 0 .../mattermost/get-mattermost-message.ts | 0 .../mattermost/get-message-for-event.ts | 0 .../mattermost/handle-mattermost-hook.ts | 0 .../mattermost/send-mattermost-message.ts | 0 .../handlers/slack/get-message-for-event.ts | 0 .../hooks/handlers/slack/get-slack-message.ts | 0 .../hooks/handlers/slack/handle-slack-hook.ts | 0 .../handlers/slack/send-slack-message.ts | 0 .../hooks/handlers/web/deliver-web-hook.ts | 0 .../src}/hooks/handlers/web/get-payload.ts | 0 .../hooks/handlers/web/handle-web-hook.ts | 0 {src => server/src}/hooks/hook-delivery.ts | 0 .../src}/hooks/hook-event-handler.ts | 0 {src => server/src}/hooks/hook.ts | 0 .../src}/hooks/serialize-hook-delivery.ts | 0 {src => server/src}/hooks/serialize-hook.ts | 0 {src => server/src}/index.ts | 0 {src => server/src}/prometheus/metrics/up.ts | 0 .../src}/prometheus/metrics/user-count.ts | 0 .../src}/prometheus/setup-prometheus.ts | 0 {src => server/src}/routes.ts | 0 {src => server/src}/server.ts | 2 +- .../src}/socket/create-io-server.ts | 2 +- .../src}/socket/handle-socket-event.ts | 0 .../src}/socket/message-builders.ts | 0 {src => server/src}/socket/socket-rooms.ts | 0 {src => server/src}/storage/delete-file.ts | 0 {src => server/src}/storage/get-file-path.ts | 0 {src => server/src}/storage/store-file.ts | 0 .../src}/system/handlers/system-env.ts | 2 +- .../src}/system/handlers/system-info.ts | 0 {src => server/src}/system/routes.ts | 0 {src => server/src}/typings.d.ts | 0 {src => server/src}/upload.ts | 0 {src => server/src}/utils/arrays-utils.ts | 0 {src => server/src}/utils/basic-auth.spec.ts | 0 {src => server/src}/utils/basic-auth.ts | 0 .../src}/utils/generate-token-value.ts | 0 {src => server/src}/utils/get-logo-url.ts | 0 {src => server/src}/utils/get-pagination.ts | 0 {src => server/src}/utils/id.ts | 0 {src => server/src}/utils/slugify.ts | 0 {src => server/src}/utils/uuid.ts | 0 {tests => server/tests}/build-mock.ts | 0 .../tests}/express-request-logger.ts | 0 {tests => server/tests}/nock.ts | 0 {tests => server/tests}/test-server.ts | 2 +- {tests => server/tests}/utils/matchers.ts | 0 {tests => server/tests}/utils/next-wait.ts | 0 .../tests}/utils/spyon-collection.ts | 0 .../tests}/utils/spyon-isadmin.ts | 0 .../tests}/utils/spyon-isadminorowner.ts | 0 .../tests}/utils/spyon-isowner.ts | 0 .../tests}/utils/spyon-verifytoken.ts | 0 server/tsconfig.json | 19 + webpack.config.js => server/webpack.config.js | 2 +- tsconfig.json | 31 - ui/package-lock.json | 18660 +++++++++++++ ui/package.json | 91 + ui/public/android-chrome-192x192.png | Bin 0 -> 4512 bytes ui/public/android-chrome-256x256.png | Bin 0 -> 6408 bytes ui/public/apple-touch-icon.png | Bin 0 -> 4235 bytes ui/public/assets/ads.js | 5 + ui/public/assets/fonts/Graphik-Bold.ttf | Bin 0 -> 146948 bytes ui/public/assets/fonts/Graphik-Medium.ttf | Bin 0 -> 147368 bytes ui/public/assets/fonts/Graphik-Regular.ttf | Bin 0 -> 144968 bytes ui/public/assets/fonts/Graphik-Semibold.ttf | Bin 0 -> 147012 bytes ui/public/assets/fonts/OxygenMono-Regular.ttf | Bin 0 -> 67340 bytes ui/public/assets/fonts/ocraext.ttf | Bin 0 -> 55400 bytes ui/public/assets/get-slack-webhook-url.mp4 | Bin 0 -> 715908 bytes ui/public/browserconfig.xml | 9 + ui/public/favicon-16x16.png | Bin 0 -> 599 bytes ui/public/favicon-32x32.png | Bin 0 -> 881 bytes ui/public/favicon.ico | Bin 0 -> 15086 bytes ui/public/index.html | 24 + ui/public/mstile-150x150.png | Bin 0 -> 3401 bytes ui/public/robots.txt | 3 + ui/public/safari-pinned-tab.svg | 25 + ui/public/site.webmanifest | 19 + ui/scripts/build-info.js | 12 + ui/src/App.module.scss | 32 + ui/src/App.tsx | 153 + ui/src/Hello.module.scss | 3 + ui/src/Hello.tsx | 29 + ui/src/assets/images/bg/bg-bottom-light.svg | 9 + ui/src/assets/images/bg/bg-top-light.svg | 9 + ui/src/assets/images/bg/nav-bg.svg | 10 + .../assets/images/git-servers/gitea-white.svg | 5 + ui/src/assets/images/git-servers/gitea.svg | 5 + .../images/git-servers/github-white.svg | 5 + ui/src/assets/images/git-servers/github.svg | 5 + .../images/git-servers/gitlab-white.svg | 9 + ui/src/assets/images/git-servers/gitlab.svg | 10 + ui/src/assets/images/logo.svg | 6 + ui/src/assets/images/notifications/email.svg | 18 + .../images/notifications/mattermost.svg | 11 + ui/src/assets/images/notifications/slack.svg | 60 + ui/src/commons/components/AdBlockWarning.tsx | 20 + ui/src/commons/components/Alert.module.scss | 44 + ui/src/commons/components/Alert.tsx | 40 + ui/src/commons/components/AlertError.tsx | 16 + ui/src/commons/components/Bubble.module.scss | 8 + ui/src/commons/components/Bubble.tsx | 24 + ui/src/commons/components/Button.tsx | 36 + .../commons/components/ButtonIcon.module.scss | 26 + ui/src/commons/components/ButtonIcon.tsx | 28 + ui/src/commons/components/CenteredLoader.tsx | 10 + .../components/CodeSnippet.module.scss | 100 + ui/src/commons/components/CodeSnippet.tsx | 51 + ui/src/commons/components/Confirm.tsx | 66 + .../components/CopyToClipboard.module.scss | 9 + ui/src/commons/components/CopyToClipboard.tsx | 49 + ui/src/commons/components/Currency.tsx | 20 + .../components/CustomSelect.module.scss | 77 + ui/src/commons/components/CustomSelect.tsx | 90 + ui/src/commons/components/DocsLink.scss | 4 + ui/src/commons/components/DocsLink.tsx | 22 + .../commons/components/EmptyList.module.scss | 26 + ui/src/commons/components/EmptyList.tsx | 20 + ui/src/commons/components/ErrorIcon.tsx | 24 + ui/src/commons/components/ExternalLink.tsx | 22 + ui/src/commons/components/FromNow.tsx | 27 + .../commons/components/FullPageCentered.tsx | 5 + ui/src/commons/components/FullPageLoader.tsx | 10 + ui/src/commons/components/Gauge.module.scss | 24 + ui/src/commons/components/Gauge.tsx | 60 + ui/src/commons/components/Hint.module.scss | 18 + ui/src/commons/components/Hint.tsx | 31 + .../components/KeyboardShortcut.module.scss | 14 + .../commons/components/KeyboardShortcut.tsx | 18 + .../commons/components/LoadMore.module.scss | 27 + ui/src/commons/components/LoadMore.tsx | 29 + ui/src/commons/components/Loader.tsx | 14 + .../commons/components/NavPills.module.scss | 45 + ui/src/commons/components/NavPills.tsx | 50 + ui/src/commons/components/NotFound.tsx | 5 + .../commons/components/Pagination.module.scss | 3 + ui/src/commons/components/Pagination.tsx | 123 + ui/src/commons/components/PrivateRoute.tsx | 30 + .../components/ProgressBar.module.scss | 20 + ui/src/commons/components/ProgressBar.tsx | 36 + ui/src/commons/components/Toasts.scss | 34 + ui/src/commons/components/Toasts.tsx | 11 + ui/src/commons/components/Tooltip.module.scss | 38 + ui/src/commons/components/Tooltip.tsx | 22 + .../components/dropdown/DropDown.module.scss | 41 + .../commons/components/dropdown/Dropdown.tsx | 47 + .../dropdown/DropdownLink.module.scss | 29 + .../components/dropdown/DropdownLink.tsx | 53 + .../dropdown/DropdownSeparator.module.scss | 8 + .../components/dropdown/DropdownSeparator.tsx | 6 + .../commons/components/forms/InputError.tsx | 17 + .../components/forms/RadioInput.module.scss | 66 + .../commons/components/forms/RadioInputs.tsx | 47 + .../components/forms/Toggle.module.scss | 84 + ui/src/commons/components/forms/Toggle.tsx | 80 + .../components/forms/form-constants.ts | 37 + .../components/modals/AppModal.module.scss | 51 + ui/src/commons/components/modals/AppModal.tsx | 61 + .../components/modals/CardModal.module.scss | 15 + .../commons/components/modals/CardModal.tsx | 44 + .../components/modals/CloseModal.module.scss | 9 + .../commons/components/modals/CloseModal.tsx | 16 + .../status/StatusIndicator.module.scss | 42 + .../components/status/StatusIndicator.tsx | 30 + .../components/status/get-status-icon.ts | 65 + ui/src/commons/hooks/use-mounted-state.ts | 21 + ui/src/commons/keyboard/shortcuts-keys.ts | 6 + ui/src/commons/keyboard/use-shortcut.ts | 22 + ui/src/commons/nv-i18n.ts | 982 + ui/src/commons/sentry/SentryIcon.tsx | 30 + .../commons/sentry/SentryProvider.module.scss | 27 + ui/src/commons/sentry/SentryProvider.tsx | 70 + ui/src/commons/types/page.ts | 4 + ui/src/commons/types/react-state.ts | 3 + ui/src/commons/utils/enum-to-array.ts | 3 + ui/src/commons/utils/os.ts | 9 + ui/src/commons/utils/random-string.ts | 9 + ui/src/commons/utils/route-up.ts | 3 + ui/src/components/AppLogo.tsx | 14 + ui/src/components/BuildInfo.tsx | 74 + ui/src/components/Footer.module.scss | 37 + ui/src/components/Footer.tsx | 33 + ui/src/components/Home.module.scss | 33 + ui/src/components/Home.tsx | 31 + ui/src/components/Logo.module.scss | 5 + ui/src/components/SubHeader.module.scss | 5 + ui/src/components/SubHeader.tsx | 11 + ui/src/components/UserHome.tsx | 7 + ui/src/components/auth/IsAdmin.tsx | 13 + ui/src/components/auth/IsOwner.tsx | 13 + ui/src/components/auth/Orgs.module.scss | 30 + ui/src/components/auth/Orgs.tsx | 107 + ui/src/components/auth/SignIn.module.scss | 25 + ui/src/components/auth/SignIn.tsx | 66 + ui/src/components/auth/UserInfo.module.scss | 32 + ui/src/components/auth/UserInfo.tsx | 83 + .../auth/methods/SignInButton.module.scss | 51 + .../components/auth/methods/SignInButton.tsx | 28 + .../auth/methods/SignInWithGitHub.module.scss | 5 + .../auth/methods/SignInWithGitea.module.scss | 12 + .../auth/methods/SignInWithGitea.tsx | 27 + .../auth/methods/SignInWithGithub.tsx | 27 + .../auth/methods/SignInWithGitlab.module.scss | 5 + .../auth/methods/SignInWithGitlab.tsx | 27 + .../auth/methods/SignInWithGoogle.module.scss | 11 + .../auth/methods/SignInWithGoogle.tsx | 26 + .../auth/methods/SignInWithUserPassword.tsx | 93 + ui/src/components/auth/user-org.ts | 9 + ui/src/components/commons/Logo.module.scss | 18 + ui/src/components/commons/Logo.tsx | 103 + ui/src/components/hooks/AddHook.tsx | 33 + ui/src/components/hooks/DeleteHook.tsx | 65 + ui/src/components/hooks/HookList.module.scss | 15 + ui/src/components/hooks/HookList.tsx | 85 + ui/src/components/hooks/HookProvider.tsx | 23 + .../components/hooks/HookTypeIcon.module.scss | 5 + ui/src/components/hooks/HookTypeIcon.tsx | 35 + ui/src/components/hooks/HookView.tsx | 112 + ui/src/components/hooks/Hooks.tsx | 18 + ui/src/components/hooks/TestHook.tsx | 36 + .../hooks/deliveries/HookDeliveries.tsx | 87 + .../hooks/deliveries/HookDeliveryView.tsx | 43 + .../hooks/deliveries/hook-delivery.ts | 11 + ui/src/components/hooks/form/HookEvents.tsx | 61 + .../hooks/form/HookForm.module.scss | 24 + ui/src/components/hooks/form/HookForm.tsx | 146 + .../components/hooks/form/configs/Email.tsx | 34 + .../hooks/form/configs/Mattermost.tsx | 39 + .../hooks/form/configs/Slack.module.scss | 11 + .../components/hooks/form/configs/Slack.tsx | 71 + ui/src/components/hooks/form/configs/Web.tsx | 57 + ui/src/components/hooks/hook.ts | 16 + ui/src/components/icons/BranchIcon.tsx | 13 + ui/src/components/icons/FormIcon.tsx | 13 + ui/src/components/icons/HeaderIcon.tsx | 13 + ui/src/components/icons/HookDeliveryIcon.tsx | 13 + ui/src/components/icons/HookIcon.tsx | 13 + ui/src/components/icons/InviteIcon.tsx | 13 + ui/src/components/icons/OrgIcon.tsx | 13 + ui/src/components/icons/OrgMemberIcon.tsx | 13 + ui/src/components/icons/RedirectIcon.tsx | 13 + ui/src/components/icons/ReleaseIcon.tsx | 13 + ui/src/components/icons/SecurityIcon.tsx | 13 + ui/src/components/icons/SettingsIcon.tsx | 13 + ui/src/components/icons/SiteIcon.tsx | 13 + ui/src/components/icons/TeamIcon.tsx | 13 + ui/src/components/icons/TeamMemberIcon.tsx | 13 + ui/src/components/icons/TokenIcon.tsx | 13 + ui/src/components/icons/UserIcon.tsx | 13 + ui/src/components/invites/AcceptInvite.tsx | 46 + ui/src/components/invites/DeclineInvite.tsx | 45 + .../invites/UserInviteView.module.scss | 11 + ui/src/components/invites/UserInviteView.tsx | 91 + ui/src/components/invites/UserInvites.tsx | 58 + ui/src/components/invites/user-invite.ts | 12 + ui/src/components/legals/CookiePolicy.tsx | 30 + ui/src/components/legals/Legals.tsx | 38 + ui/src/components/legals/PrivacyPolicy.tsx | 30 + ui/src/components/legals/TermsOfService.tsx | 30 + ui/src/components/legals/cookie-policy.md | 82 + ui/src/components/legals/privacy-policy.md | 192 + ui/src/components/legals/terms-of-service.md | 175 + ui/src/components/orgs/AddOrg.tsx | 102 + ui/src/components/orgs/OrgView.module.scss | 0 ui/src/components/orgs/OrgView.tsx | 104 + ui/src/components/orgs/org.ts | 8 + .../orgs/settings/OrgGeneralSettings.tsx | 104 + .../orgs/settings/OrgLogo.module.scss | 18 + ui/src/components/orgs/settings/OrgLogo.tsx | 29 + .../components/orgs/settings/OrgNameInput.tsx | 36 + .../orgs/settings/OrgSettings.module.scss | 45 + .../components/orgs/settings/OrgSettings.tsx | 12 + ui/src/components/orgs/staff/Staff.tsx | 12 + .../orgs/staff/invites/AddInvite.module.scss | 4 + .../orgs/staff/invites/AddInvite.tsx | 119 + .../orgs/staff/invites/DeleteInvite.tsx | 65 + .../orgs/staff/invites/InviteView.tsx | 45 + .../orgs/staff/invites/Invites.module.scss | 15 + .../components/orgs/staff/invites/Invites.tsx | 79 + .../components/orgs/staff/invites/invite.ts | 8 + .../orgs/staff/members/DeleteMember.tsx | 63 + .../orgs/staff/members/MemberView.tsx | 99 + .../orgs/staff/members/Members.module.scss | 15 + .../components/orgs/staff/members/Members.tsx | 150 + .../orgs/staff/members/get-members.ts | 22 + .../orgs/staff/members/org-member.ts | 7 + ui/src/components/sidebar/SideBar.module.scss | 6 + ui/src/components/sidebar/SideBar.tsx | 18 + ui/src/components/sidebar/Sites.module.scss | 39 + ui/src/components/sidebar/Sites.tsx | 96 + ui/src/components/sidebar/Teams.module.scss | 34 + ui/src/components/sidebar/Teams.tsx | 110 + ui/src/components/sites/AddSite.tsx | 100 + ui/src/components/sites/DeleteSite.tsx | 64 + ui/src/components/sites/SiteCard.module.scss | 5 + ui/src/components/sites/SiteCard.tsx | 19 + ui/src/components/sites/SiteList.module.scss | 6 + ui/src/components/sites/SiteList.tsx | 68 + ui/src/components/sites/SiteView.module.scss | 0 ui/src/components/sites/SiteView.tsx | 171 + .../components/sites/branches/AddBranch.tsx | 98 + .../sites/branches/BranchList.module.scss | 15 + .../components/sites/branches/BranchList.tsx | 81 + .../sites/branches/BranchListItem.tsx | 70 + .../sites/branches/BranchNameInput.tsx | 57 + .../sites/branches/BranchPassword.tsx | 121 + .../sites/branches/BranchRelease.tsx | 58 + .../components/sites/branches/BranchView.tsx | 208 + ui/src/components/sites/branches/Branches.tsx | 16 + .../sites/branches/DeleteBranch.tsx | 64 + .../sites/branches/RenameBranch.tsx | 93 + .../sites/branches/branch-redirect.ts | 22 + ui/src/components/sites/branches/branch.ts | 10 + .../sites/branches/forms/CustomFields.tsx | 50 + .../sites/branches/forms/FormForm.tsx | 78 + .../sites/branches/forms/FormList.tsx | 76 + .../components/sites/branches/forms/Forms.tsx | 92 + .../components/sites/branches/get-branches.ts | 12 + ui/src/components/sites/branches/header.ts | 4 + .../sites/branches/headers/BranchHeaders.tsx | 134 + .../branches/redirects/BranchRedirectForm.tsx | 127 + .../branches/redirects/BranchRedirects.tsx | 148 + .../redirects/branch-redirects-form-data.tsx | 5 + .../branches/redirects/configs/FileConfig.tsx | 27 + .../redirects/configs/ReverseProxy.tsx | 59 + .../branches/release/SetBranchRelease.tsx | 80 + .../settings/BranchGeneralSettings.tsx | 58 + .../branches/settings/BranchSettings.tsx | 25 + ui/src/components/sites/get-team-sites.ts | 10 + .../components/sites/headers/HeaderForm.tsx | 71 + .../components/sites/headers/HeaderList.tsx | 71 + .../components/sites/headers/SiteHeaders.tsx | 61 + .../sites/headers/use-site-headers.ts | 29 + .../sites/releases/DeleteRelease.tsx | 63 + .../sites/releases/ReleaseNameInput.tsx | 36 + .../components/sites/releases/ReleaseView.tsx | 101 + ui/src/components/sites/releases/Releases.tsx | 211 + .../sites/releases/RenameRelease.tsx | 91 + .../sites/releases/Search.module.scss | 6 + .../components/sites/releases/SearchInput.tsx | 54 + .../components/sites/releases/get-releases.ts | 24 + ui/src/components/sites/releases/release.ts | 30 + ui/src/components/sites/search/Search.tsx | 55 + .../sites/search/SearchModal.module.scss | 14 + .../components/sites/search/SearchModal.tsx | 102 + .../sites/settings/DomainForm.module.scss | 28 + .../components/sites/settings/DomainForm.tsx | 141 + .../sites/settings/GeneralSettingsForm.tsx | 175 + .../sites/settings/SecuritySettings.tsx | 18 + .../settings/SelectMainBranch.module.scss | 0 .../sites/settings/SelectMainBranch.tsx | 71 + ui/src/components/sites/settings/SiteLogo.tsx | 16 + .../sites/settings/SiteNameInput.tsx | 61 + .../sites/settings/SitePassword.tsx | 120 + .../sites/settings/SiteSettings.module.scss | 45 + .../sites/settings/SiteSettings.tsx | 68 + ui/src/components/sites/site.ts | 39 + .../sites/tokens/AddToken.module.scss | 4 + ui/src/components/sites/tokens/AddToken.tsx | 96 + .../components/sites/tokens/DeleteToken.tsx | 64 + .../sites/tokens/TokenList.module.scss | 15 + ui/src/components/sites/tokens/TokenList.tsx | 88 + ui/src/components/sites/tokens/TokenView.tsx | 39 + ui/src/components/sites/tokens/Tokens.tsx | 16 + ui/src/components/sites/tokens/get-tokens.ts | 18 + ui/src/components/sites/tokens/token.ts | 6 + ui/src/components/sites/use-sites.ts | 44 + ui/src/components/teams/AddTeam.tsx | 129 + ui/src/components/teams/DeleteTeam.tsx | 66 + ui/src/components/teams/TeamList.module.scss | 6 + ui/src/components/teams/TeamList.tsx | 107 + ui/src/components/teams/TeamView.module.scss | 0 ui/src/components/teams/TeamView.tsx | 135 + ui/src/components/teams/live-teams.ts | 31 + .../components/teams/members/DeleteMember.tsx | 68 + .../components/teams/members/MemberView.tsx | 39 + .../teams/members/Members.module.scss | 15 + ui/src/components/teams/members/Members.tsx | 90 + .../teams/members/add/AddMember.module.scss | 26 + .../teams/members/add/AddMember.tsx | 120 + .../components/teams/members/add/ListItem.tsx | 49 + .../components/teams/members/get-members.ts | 18 + .../components/teams/members/team-member.ts | 6 + .../settings/TeamGeneralSettings.module.scss | 45 + .../teams/settings/TeamGeneralSettings.tsx | 106 + .../teams/settings/TeamNameInput.tsx | 36 + .../teams/settings/TeamSettings.tsx | 24 + ui/src/components/teams/team.ts | 9 + ui/src/components/user/UserSecurity.tsx | 43 + ui/src/components/user/UserView.module.scss | 0 ui/src/components/user/UserView.tsx | 77 + .../user/api-tokens/AddApiToken.tsx | 25 + .../user/api-tokens/ApiScopes.module.scss | 3 + .../components/user/api-tokens/ApiScopes.tsx | 109 + .../api-tokens/ApiTokenActivationPeriod.tsx | 43 + .../user/api-tokens/ApiTokenForm.tsx | 141 + .../user/api-tokens/ApiTokenList.module.scss | 15 + .../user/api-tokens/ApiTokenList.tsx | 86 + .../user/api-tokens/ApiTokenView.tsx | 113 + .../user/api-tokens/DeleteApiToken.tsx | 63 + .../user/api-tokens/HttpMethodBadge.tsx | 37 + .../user/api-tokens/api-endpoint.ts | 9 + .../user/api-tokens/api-scope-groups.ts | 97 + .../components/user/api-tokens/api-scope.ts | 82 + .../components/user/api-tokens/api-token.ts | 12 + ui/src/events.ts | 35 + ui/src/index.scss | 37 + ui/src/index.tsx | 89 + ui/src/providers/AuthProvider.tsx | 86 + ui/src/providers/EnvProvider.tsx | 37 + ui/src/providers/OrgProvider.tsx | 106 + ui/src/providers/axios.ts | 7 + ui/src/providers/history.ts | 3 + ui/src/react-app-env.d.ts | 19 + ui/src/setupProxy.js | 14 + ui/src/setupTests.ts | 5 + ui/src/styles/animations.scss | 30 + ui/src/styles/branding.scss | 246 + ui/src/styles/commons.scss | 173 + ui/src/styles/fonts.scss | 2 + ui/src/styles/forms.scss | 150 + ui/src/styles/mixins.scss | 112 + ui/src/styles/variables.scss | 154 + ui/src/utils/debounce-time.ts | 24 + ui/src/utils/dedup-slashes.ts | 3 + ui/src/utils/extract-exrror-message.ts | 8 + ui/src/utils/format-duration.ts | 14 + ui/src/utils/query-params.ts | 7 + ui/src/utils/suffix-big-number.ts | 27 + ui/src/utils/suffix-time.ts | 27 + ui/src/utils/text-search.ts | 22 + ui/src/websockets/SocketProvider.tsx | 23 + .../websockets/emit-now-and-on-reconnect.ts | 16 + ui/src/websockets/event-type.ts | 51 + ui/src/websockets/listen-many.ts | 18 + ui/src/websockets/listen.ts | 8 + ui/src/websockets/use-room.ts | 67 + ui/tsconfig.json | 19 + 774 files changed, 59112 insertions(+), 17587 deletions(-) create mode 100644 Dockerfile delete mode 100644 docker/Dockerfile rename docker/{files => }/caddy-config.json (100%) rename docker/{files => }/entrypoint.sh (100%) delete mode 100644 docker/unified.Dockerfile create mode 100755 publish.sh rename .env.example => server/.env.example (91%) rename .eslintignore => server/.eslintignore (100%) rename .eslintrc.js => server/.eslintrc.js (100%) rename jest.config.js => server/jest.config.js (100%) rename migrate-mongo-config.js => server/migrate-mongo-config.js (100%) rename migrations/.placeholder => server/migrations/.gitkeep (100%) create mode 100644 server/package-lock.json create mode 100644 server/package.json rename setupTests.js => server/setupTests.js (100%) rename {src => server/src}/auth/auth.ts (100%) rename {src => server/src}/auth/authenticate.ts (100%) rename {src => server/src}/auth/create-or-update-user.ts (100%) rename {src => server/src}/auth/guards/api-guard.spec.ts (100%) rename {src => server/src}/auth/guards/api-guard.ts (100%) rename {src => server/src}/auth/guards/auth-guard.spec.ts (100%) rename {src => server/src}/auth/guards/auth-guard.ts (100%) rename {src => server/src}/auth/guards/is-admin-or-owner-guard.spec.ts (100%) rename {src => server/src}/auth/guards/is-admin-or-owner-guard.ts (100%) rename {src => server/src}/auth/guards/is-admin-or-owner.spec.ts (100%) rename {src => server/src}/auth/guards/is-admin-or-owner.ts (100%) rename {src => server/src}/auth/guards/is-admin.ts (100%) rename {src => server/src}/auth/guards/is-owner.ts (100%) rename {src => server/src}/auth/handlers/authorize-api-req.spec.ts (100%) rename {src => server/src}/auth/handlers/authorize-api-req.ts (100%) rename {src => server/src}/auth/handlers/authorize-req.spec.ts (100%) rename {src => server/src}/auth/handlers/authorize-req.ts (100%) rename {src => server/src}/auth/handlers/get-auth-methods.spec.ts (100%) rename {src => server/src}/auth/handlers/get-auth-methods.ts (100%) rename {src => server/src}/auth/handlers/redirect-to-ui.ts (75%) rename {src => server/src}/auth/handlers/sign-out.spec.ts (100%) rename {src => server/src}/auth/handlers/sign-out.ts (100%) rename {src => server/src}/auth/passport.ts (100%) rename {src => server/src}/auth/passport/auth-methods.ts (100%) rename {src => server/src}/auth/passport/gitea.ts (100%) rename {src => server/src}/auth/passport/github.ts (100%) rename {src => server/src}/auth/passport/gitlab.ts (100%) rename {src => server/src}/auth/passport/google.ts (100%) rename {src => server/src}/auth/passport/in-memory.ts (100%) rename {src => server/src}/auth/passport/providers/gitea/gitea.ts (100%) rename {src => server/src}/auth/passport/providers/gitea/types/gitea-oauth-token-grant.ts (100%) rename {src => server/src}/auth/passport/providers/gitea/types/gitea-org.ts (100%) rename {src => server/src}/auth/passport/providers/gitea/types/gitea-user.ts (100%) rename {src => server/src}/auth/passport/providers/github/github.ts (100%) rename {src => server/src}/auth/passport/providers/github/types/github-email.ts (100%) rename {src => server/src}/auth/passport/providers/github/types/github-org.ts (100%) rename {src => server/src}/auth/passport/providers/github/types/github-user.ts (100%) rename {src => server/src}/auth/passport/providers/gitlab/gitlab.ts (100%) rename {src => server/src}/auth/passport/providers/gitlab/types/gitlab-group.ts (100%) rename {src => server/src}/auth/passport/providers/gitlab/types/gitlab-oauth-token-grant.ts (100%) rename {src => server/src}/auth/passport/providers/gitlab/types/gitlab-user.ts (100%) rename {src => server/src}/auth/routes.ts (100%) rename {src => server/src}/auth/serialize-user.ts (100%) rename {src => server/src}/auth/utils/get-user-from-socket.spec.ts (100%) rename {src => server/src}/auth/utils/get-user-from-socket.ts (100%) rename {src => server/src}/auth/utils/get-user.spec.ts (100%) rename {src => server/src}/auth/utils/get-user.ts (100%) rename {src => server/src}/auth/utils/verify-token.spec.ts (100%) rename {src => server/src}/auth/utils/verify-token.ts (100%) rename {src => server/src}/caddy/config/api-route.ts (100%) rename {src => server/src}/caddy/config/fallback.ts (100%) rename {src => server/src}/caddy/config/get-error-routes.ts (100%) rename {src => server/src}/caddy/config/sites/generate-site-routes.ts (100%) rename {src => server/src}/caddy/config/sites/get-auth-handler.ts (100%) rename {src => server/src}/caddy/config/sites/get-branch-404-error-route.ts (100%) rename {src => server/src}/caddy/config/sites/get-redirect-route.ts (100%) rename {src => server/src}/caddy/config/ssl.ts (97%) rename {src => server/src}/caddy/config/ui-route.ts (89%) rename {src => server/src}/caddy/configuration.ts (100%) rename {src => server/src}/caddy/definitions/admin.d.ts (100%) rename {src => server/src}/caddy/definitions/apps.d.ts (100%) rename {src => server/src}/caddy/definitions/apps/http.d.ts (100%) rename {src => server/src}/caddy/definitions/apps/http/route.d.ts (100%) rename {src => server/src}/caddy/definitions/apps/pki.d.ts (100%) rename {src => server/src}/caddy/definitions/apps/tls.d.ts (100%) rename {src => server/src}/caddy/definitions/config.d.ts (100%) rename {src => server/src}/caddy/definitions/handlers.d.ts (100%) rename {src => server/src}/caddy/definitions/logging.d.ts (100%) rename {src => server/src}/caddy/definitions/matchers.d.ts (100%) rename {src => server/src}/caddy/definitions/storage.d.ts (100%) rename {src => server/src}/caddy/generate-config.ts (100%) rename {src => server/src}/caddy/utils/get-reverse-proxy-dial.ts (100%) rename {src => server/src}/commons/allowed-hosts.spec.ts (100%) rename {src => server/src}/commons/allowed-hosts.ts (100%) rename {src => server/src}/commons/axios/axios-error.spec.ts (100%) rename {src => server/src}/commons/axios/axios-error.ts (100%) rename {src => server/src}/commons/axios/axios.ts (100%) rename {src => server/src}/commons/axios/ensure-stack-trace.ts (100%) rename {src => server/src}/commons/enum-to-array.ts (100%) rename {src => server/src}/commons/env/parse-env.ts (100%) rename {src => server/src}/commons/env/transformers.ts (100%) rename {src => server/src}/commons/errors/app-error.ts (100%) rename {src => server/src}/commons/errors/bad-request-error.ts (100%) rename {src => server/src}/commons/errors/forbidden-error.ts (100%) rename {src => server/src}/commons/errors/http-error.ts (100%) rename {src => server/src}/commons/errors/invalid-environment-error.ts (100%) rename {src => server/src}/commons/errors/not-found-error.ts (100%) rename {src => server/src}/commons/errors/unauthorized-error.ts (100%) rename {src => server/src}/commons/express-joi/body.spec.ts (100%) rename {src => server/src}/commons/express-joi/body.ts (100%) rename {src => server/src}/commons/express-joi/params.spec.ts (100%) rename {src => server/src}/commons/express-joi/params.ts (100%) rename {src => server/src}/commons/express-joi/query.spec.ts (100%) rename {src => server/src}/commons/express-joi/query.ts (100%) rename {src => server/src}/commons/express/guard.ts (100%) rename {src => server/src}/commons/express/handlers/no-content.ts (100%) rename {src => server/src}/commons/force-chalk-colors.ts (100%) rename {src => server/src}/commons/logger/build-winston-logger.ts (100%) rename {src => server/src}/commons/logger/logger.ts (100%) rename {src => server/src}/commons/multer/types.ts (100%) rename {src => server/src}/commons/nv-i18n.ts (100%) rename {src => server/src}/commons/types/constructor.ts (100%) rename {src => server/src}/commons/types/duration.ts (100%) rename {src => server/src}/commons/utils/array-set.ts (100%) rename {src => server/src}/commons/utils/base64.ts (100%) rename {src => server/src}/commons/utils/ensure-empty-directory.ts (100%) rename {src => server/src}/commons/utils/exec-async.ts (100%) rename {src => server/src}/commons/utils/handle-error.ts (100%) rename {src => server/src}/commons/utils/month-diff.ts (100%) rename {src => server/src}/commons/utils/set-replacer.ts (100%) rename {src => server/src}/commons/utils/wrap-async-middleware.ts (100%) rename {src => server/src}/commons/validators/is-certificate.ts (100%) rename {src => server/src}/commons/validators/is-cron-expression.ts (100%) rename {src => server/src}/commons/validators/is-moment-timezone.ts (100%) rename {src => server/src}/commons/validators/is-rsa-private-key.ts (100%) rename {src => server/src}/commons/validators/is-url.ts (100%) rename {src => server/src}/commons/validators/to-url.ts (100%) rename {src => server/src}/constants.ts (100%) rename {src => server/src}/db/db.ts (100%) rename {src => server/src}/db/indexes/configure-indexes.spec.ts (100%) rename {src => server/src}/db/indexes/configure-indexes.ts (100%) rename {src => server/src}/db/indexes/create-indexes-for-collection.spec.ts (100%) rename {src => server/src}/db/indexes/create-indexes-for-collection.ts (100%) rename {src => server/src}/db/indexes/mongo-error-code.ts (100%) rename {src => server/src}/db/migrate/migrate.spec.ts (100%) rename {src => server/src}/db/migrate/migrate.ts (100%) rename {src => server/src}/db/migrate/roll-backwards.spec.ts (100%) rename {src => server/src}/db/migrate/roll-backwards.ts (100%) rename {src => server/src}/db/migrate/roll-forward.spec.ts (100%) rename {src => server/src}/db/migrate/roll-forward.ts (100%) rename {src => server/src}/db/setup-db-indexes.ts (100%) rename {src => server/src}/emails/emails.ts (100%) rename {src => server/src}/emails/methods/send-invite.ts (100%) rename {src => server/src}/emails/send-email.ts (100%) rename {src => server/src}/emails/templates/README.md (100%) rename {src => server/src}/emails/templates/form-submission.hbs (100%) rename {src => server/src}/emails/templates/invite.hbs (100%) rename {src => server/src}/entities/api/api-endpoint.ts (100%) rename {src => server/src}/entities/api/api-scope.ts (100%) rename {src => server/src}/entities/api/api-token.ts (100%) rename {src => server/src}/entities/api/guards/api-token-exists-guard.spec.ts (100%) rename {src => server/src}/entities/api/guards/api-token-exists-guard.ts (100%) rename {src => server/src}/entities/api/guards/can-admin-api-token-guard.spec.ts (100%) rename {src => server/src}/entities/api/guards/can-admin-api-token-guard.ts (100%) rename {src => server/src}/entities/api/handlers/endpoints/list-api-endpoints.ts (100%) rename {src => server/src}/entities/api/handlers/tokens/create-api-token.ts (100%) rename {src => server/src}/entities/api/handlers/tokens/delete-api-token.ts (100%) rename {src => server/src}/entities/api/handlers/tokens/get-api-token.ts (100%) rename {src => server/src}/entities/api/handlers/tokens/list-api-tokens.ts (100%) rename {src => server/src}/entities/api/handlers/tokens/update-api-token.ts (100%) rename {src => server/src}/entities/api/routes.ts (100%) rename {src => server/src}/entities/api/serialize-api-token.ts (100%) rename {src => server/src}/entities/api/serialize-endpoint.ts (100%) rename {src => server/src}/entities/forms/form.ts (100%) rename {src => server/src}/entities/forms/serialize-form.ts (100%) rename {src => server/src}/entities/forms/submit-email-form.ts (100%) rename {src => server/src}/entities/invites/handlers/accept-invite.ts (100%) rename {src => server/src}/entities/invites/handlers/decline-invite.ts (100%) rename {src => server/src}/entities/invites/handlers/get-invite.ts (100%) rename {src => server/src}/entities/invites/routes.ts (100%) rename {src => server/src}/entities/invites/serialize-invite.ts (100%) rename {src => server/src}/entities/members/guards/can-admin-member-guard.ts (100%) rename {src => server/src}/entities/members/guards/member-exists-guard.ts (100%) rename {src => server/src}/entities/members/handlers/delete-member.ts (100%) rename {src => server/src}/entities/members/handlers/update-member.ts (100%) rename {src => server/src}/entities/members/member.ts (100%) rename {src => server/src}/entities/members/routes.ts (100%) rename {src => server/src}/entities/members/serialize-member.ts (100%) rename {src => server/src}/entities/orgs/guards/can-get-org-logo-guard.ts (100%) rename {src => server/src}/entities/orgs/guards/can-write-org-guard.ts (100%) rename {src => server/src}/entities/orgs/guards/invite-exists-guard.ts (100%) rename {src => server/src}/entities/orgs/guards/is-org-member-guard.ts (100%) rename {src => server/src}/entities/orgs/guards/is-org-member.ts (100%) rename {src => server/src}/entities/orgs/guards/max-org-guard.ts (100%) rename {src => server/src}/entities/orgs/guards/org-exists-guard.ts (100%) rename {src => server/src}/entities/orgs/handlers/create-org.spec.ts (100%) rename {src => server/src}/entities/orgs/handlers/create-org.ts (100%) rename {src => server/src}/entities/orgs/handlers/get-org-logo.ts (100%) rename {src => server/src}/entities/orgs/handlers/get-org.ts (100%) rename {src => server/src}/entities/orgs/handlers/invites/add-invite.ts (96%) rename {src => server/src}/entities/orgs/handlers/invites/delete-invite.ts (100%) rename {src => server/src}/entities/orgs/handlers/invites/list-invites.ts (100%) rename {src => server/src}/entities/orgs/handlers/list-orgs.ts (100%) rename {src => server/src}/entities/orgs/handlers/members/get-current-org-member.ts (100%) rename {src => server/src}/entities/orgs/handlers/members/list-members.ts (100%) rename {src => server/src}/entities/orgs/handlers/remove-org-logo.ts (100%) rename {src => server/src}/entities/orgs/handlers/set-org-logo.ts (100%) rename {src => server/src}/entities/orgs/handlers/sites/list-sites.ts (100%) rename {src => server/src}/entities/orgs/handlers/teams/create-team.spec.ts (100%) rename {src => server/src}/entities/orgs/handlers/teams/create-team.ts (100%) rename {src => server/src}/entities/orgs/handlers/teams/list-teams.ts (100%) rename {src => server/src}/entities/orgs/handlers/update-org.ts (100%) rename {src => server/src}/entities/orgs/invite.ts (100%) rename {src => server/src}/entities/orgs/org.ts (100%) rename {src => server/src}/entities/orgs/routes.ts (100%) rename {src => server/src}/entities/orgs/serialize-invite.ts (100%) rename {src => server/src}/entities/orgs/serialize-org.ts (100%) rename {src => server/src}/entities/orgs/serialize-user-org.ts (100%) rename {src => server/src}/entities/releases/guards/can-admin-release-guard.ts (100%) rename {src => server/src}/entities/releases/guards/can-admin-release.ts (100%) rename {src => server/src}/entities/releases/guards/validate-captcha.ts (100%) rename {src => server/src}/entities/releases/handlers/delete-release.ts (100%) rename {src => server/src}/entities/releases/handlers/get-release.ts (100%) rename {src => server/src}/entities/releases/handlers/submit-form.ts (100%) rename {src => server/src}/entities/releases/handlers/update-forms.ts (100%) rename {src => server/src}/entities/releases/handlers/update-release.ts (100%) rename {src => server/src}/entities/releases/release.ts (100%) rename {src => server/src}/entities/releases/routes.ts (100%) rename {src => server/src}/entities/releases/serialize-release.ts (100%) rename {src => server/src}/entities/sites/branch.ts (100%) rename {src => server/src}/entities/sites/get-branch-domain.ts (100%) rename {src => server/src}/entities/sites/get-branch-url.ts (100%) rename {src => server/src}/entities/sites/get-redirect-url.ts (100%) rename {src => server/src}/entities/sites/get-site-dir.ts (100%) rename {src => server/src}/entities/sites/get-site-main-domain.ts (100%) rename {src => server/src}/entities/sites/get-site-url.ts (100%) rename {src => server/src}/entities/sites/guards/branch-exists-guard.spec.ts (100%) rename {src => server/src}/entities/sites/guards/branch-exists-guard.ts (100%) rename {src => server/src}/entities/sites/guards/can-admin-site-guard.ts (100%) rename {src => server/src}/entities/sites/guards/can-admin-site.ts (100%) rename {src => server/src}/entities/sites/guards/can-upload-release-guard.ts (100%) rename {src => server/src}/entities/sites/guards/redirect-exists-guard.ts (100%) rename {src => server/src}/entities/sites/guards/site-exists-guard.ts (100%) rename {src => server/src}/entities/sites/guards/site-hook-exists-guard.ts (100%) rename {src => server/src}/entities/sites/handlers/branches/add-branch.spec.ts (100%) rename {src => server/src}/entities/sites/handlers/branches/add-branch.ts (100%) rename {src => server/src}/entities/sites/handlers/branches/delete-branch.spec.ts (100%) rename {src => server/src}/entities/sites/handlers/branches/delete-branch.ts (100%) rename {src => server/src}/entities/sites/handlers/branches/get-branch.ts (100%) rename {src => server/src}/entities/sites/handlers/branches/list-branch-redirects.ts (100%) rename {src => server/src}/entities/sites/handlers/branches/list-branches.ts (100%) rename {src => server/src}/entities/sites/handlers/branches/remove-branch-password.ts (100%) rename {src => server/src}/entities/sites/handlers/branches/rename-branch.ts (100%) rename {src => server/src}/entities/sites/handlers/branches/set-branch-headers.ts (100%) rename {src => server/src}/entities/sites/handlers/branches/set-branch-password.ts (100%) rename {src => server/src}/entities/sites/handlers/branches/set-branch-redirects.ts (100%) rename {src => server/src}/entities/sites/handlers/branches/set-branch-release.ts (100%) rename {src => server/src}/entities/sites/handlers/branches/validate-branch-name.ts (100%) rename {src => server/src}/entities/sites/handlers/delete-site.ts (100%) rename {src => server/src}/entities/sites/handlers/get-site-logo.ts (100%) rename {src => server/src}/entities/sites/handlers/get-site.ts (100%) rename {src => server/src}/entities/sites/handlers/hooks/create-site-hook.ts (100%) rename {src => server/src}/entities/sites/handlers/hooks/delete-site-hook.ts (100%) rename {src => server/src}/entities/sites/handlers/hooks/get-site-hook.ts (100%) rename {src => server/src}/entities/sites/handlers/hooks/list-site-events.ts (100%) rename {src => server/src}/entities/sites/handlers/hooks/list-site-hook-deliveries.ts (100%) rename {src => server/src}/entities/sites/handlers/hooks/list-site-hooks.ts (100%) rename {src => server/src}/entities/sites/handlers/hooks/site-hook.ts (100%) rename {src => server/src}/entities/sites/handlers/hooks/update-site-hook.ts (100%) rename {src => server/src}/entities/sites/handlers/releases/list-site-releases.ts (100%) rename {src => server/src}/entities/sites/handlers/releases/upload-release.ts (96%) rename {src => server/src}/entities/sites/handlers/remove-site-logo.ts (100%) rename {src => server/src}/entities/sites/handlers/remove-site-password.ts (100%) rename {src => server/src}/entities/sites/handlers/set-site-logo.ts (100%) rename {src => server/src}/entities/sites/handlers/set-site-password.ts (100%) rename {src => server/src}/entities/sites/handlers/tokens/add-token.ts (100%) rename {src => server/src}/entities/sites/handlers/tokens/delete-token.ts (100%) rename {src => server/src}/entities/sites/handlers/tokens/list-tokens.ts (100%) rename {src => server/src}/entities/sites/handlers/update-site.ts (100%) rename {src => server/src}/entities/sites/handlers/validate-site-name.ts (100%) rename {src => server/src}/entities/sites/hash-password.ts (100%) rename {src => server/src}/entities/sites/header.ts (100%) rename {src => server/src}/entities/sites/link-branch-to-release.ts (100%) rename {src => server/src}/entities/sites/password.ts (100%) rename {src => server/src}/entities/sites/redirect.ts (100%) rename {src => server/src}/entities/sites/routes.ts (100%) rename {src => server/src}/entities/sites/serialize-branch.ts (100%) rename {src => server/src}/entities/sites/serialize-redirect.ts (100%) rename {src => server/src}/entities/sites/serialize-site-token.ts (100%) rename {src => server/src}/entities/sites/serialize-site.ts (100%) rename {src => server/src}/entities/sites/set-site-headers.ts (100%) rename {src => server/src}/entities/sites/site.ts (100%) rename {src => server/src}/entities/sites/yaml-config/parse-config.ts (100%) rename {src => server/src}/entities/sites/yaml-config/site-config.ts (100%) rename {src => server/src}/entities/teams/guards/can-admin-team-guard.ts (100%) rename {src => server/src}/entities/teams/guards/can-read-team-guard.ts (100%) rename {src => server/src}/entities/teams/guards/can-read-team.ts (100%) rename {src => server/src}/entities/teams/guards/org-member-exists-guard.ts (100%) rename {src => server/src}/entities/teams/guards/team-exists-guard.ts (100%) rename {src => server/src}/entities/teams/handlers/delete-team.ts (100%) rename {src => server/src}/entities/teams/handlers/get-team-logo.ts (100%) rename {src => server/src}/entities/teams/handlers/get-team.ts (100%) rename {src => server/src}/entities/teams/handlers/members/add-member.ts (100%) rename {src => server/src}/entities/teams/handlers/members/delete-member.ts (100%) rename {src => server/src}/entities/teams/handlers/members/list-members.ts (100%) rename {src => server/src}/entities/teams/handlers/remove-team-logo.ts (100%) rename {src => server/src}/entities/teams/handlers/set-team-logo.ts (100%) rename {src => server/src}/entities/teams/handlers/sites/create-site.spec.ts (100%) rename {src => server/src}/entities/teams/handlers/sites/create-site.ts (100%) rename {src => server/src}/entities/teams/handlers/sites/list-team-sites.spec.ts (100%) rename {src => server/src}/entities/teams/handlers/sites/list-team-sites.ts (100%) rename {src => server/src}/entities/teams/handlers/update-team.ts (100%) rename {src => server/src}/entities/teams/routes.ts (100%) rename {src => server/src}/entities/teams/serialize-team-member.ts (100%) rename {src => server/src}/entities/teams/serialize-team.ts (100%) rename {src => server/src}/entities/teams/team.ts (100%) rename {src => server/src}/entities/users/guards/user-exists-guard.ts (100%) rename {src => server/src}/entities/users/handlers/get-user-handler.ts (100%) rename {src => server/src}/entities/users/handlers/invalidate-tokens.ts (100%) rename {src => server/src}/entities/users/routes.ts (100%) rename {src => server/src}/entities/users/user.ts (100%) rename {src => server/src}/env/env-spec.ts (93%) rename {src => server/src}/env/env.ts (99%) rename {src => server/src}/events/emit-event.ts (100%) rename {src => server/src}/events/event-data.ts (100%) rename {src => server/src}/events/event-type.ts (100%) rename {src => server/src}/hooks/deliver-hook.ts (100%) rename {src => server/src}/hooks/get-hooks-for-event.ts (100%) rename {src => server/src}/hooks/handlers/email/get-message-for-event.ts (100%) rename {src => server/src}/hooks/handlers/email/handle-email-hook.ts (100%) rename {src => server/src}/hooks/handlers/email/send-email-hook.ts (100%) rename {src => server/src}/hooks/handlers/get-hook-handler.ts (100%) rename {src => server/src}/hooks/handlers/mattermost/get-mattermost-message.ts (100%) rename {src => server/src}/hooks/handlers/mattermost/get-message-for-event.ts (100%) rename {src => server/src}/hooks/handlers/mattermost/handle-mattermost-hook.ts (100%) rename {src => server/src}/hooks/handlers/mattermost/send-mattermost-message.ts (100%) rename {src => server/src}/hooks/handlers/slack/get-message-for-event.ts (100%) rename {src => server/src}/hooks/handlers/slack/get-slack-message.ts (100%) rename {src => server/src}/hooks/handlers/slack/handle-slack-hook.ts (100%) rename {src => server/src}/hooks/handlers/slack/send-slack-message.ts (100%) rename {src => server/src}/hooks/handlers/web/deliver-web-hook.ts (100%) rename {src => server/src}/hooks/handlers/web/get-payload.ts (100%) rename {src => server/src}/hooks/handlers/web/handle-web-hook.ts (100%) rename {src => server/src}/hooks/hook-delivery.ts (100%) rename {src => server/src}/hooks/hook-event-handler.ts (100%) rename {src => server/src}/hooks/hook.ts (100%) rename {src => server/src}/hooks/serialize-hook-delivery.ts (100%) rename {src => server/src}/hooks/serialize-hook.ts (100%) rename {src => server/src}/index.ts (100%) rename {src => server/src}/prometheus/metrics/up.ts (100%) rename {src => server/src}/prometheus/metrics/user-count.ts (100%) rename {src => server/src}/prometheus/setup-prometheus.ts (100%) rename {src => server/src}/routes.ts (100%) rename {src => server/src}/server.ts (99%) rename {src => server/src}/socket/create-io-server.ts (95%) rename {src => server/src}/socket/handle-socket-event.ts (100%) rename {src => server/src}/socket/message-builders.ts (100%) rename {src => server/src}/socket/socket-rooms.ts (100%) rename {src => server/src}/storage/delete-file.ts (100%) rename {src => server/src}/storage/get-file-path.ts (100%) rename {src => server/src}/storage/store-file.ts (100%) rename {src => server/src}/system/handlers/system-env.ts (70%) rename {src => server/src}/system/handlers/system-info.ts (100%) rename {src => server/src}/system/routes.ts (100%) rename {src => server/src}/typings.d.ts (100%) rename {src => server/src}/upload.ts (100%) rename {src => server/src}/utils/arrays-utils.ts (100%) rename {src => server/src}/utils/basic-auth.spec.ts (100%) rename {src => server/src}/utils/basic-auth.ts (100%) rename {src => server/src}/utils/generate-token-value.ts (100%) rename {src => server/src}/utils/get-logo-url.ts (100%) rename {src => server/src}/utils/get-pagination.ts (100%) rename {src => server/src}/utils/id.ts (100%) rename {src => server/src}/utils/slugify.ts (100%) rename {src => server/src}/utils/uuid.ts (100%) rename {tests => server/tests}/build-mock.ts (100%) rename {tests => server/tests}/express-request-logger.ts (100%) rename {tests => server/tests}/nock.ts (100%) rename {tests => server/tests}/test-server.ts (98%) rename {tests => server/tests}/utils/matchers.ts (100%) rename {tests => server/tests}/utils/next-wait.ts (100%) rename {tests => server/tests}/utils/spyon-collection.ts (100%) rename {tests => server/tests}/utils/spyon-isadmin.ts (100%) rename {tests => server/tests}/utils/spyon-isadminorowner.ts (100%) rename {tests => server/tests}/utils/spyon-isowner.ts (100%) rename {tests => server/tests}/utils/spyon-verifytoken.ts (100%) create mode 100644 server/tsconfig.json rename webpack.config.js => server/webpack.config.js (98%) delete mode 100644 tsconfig.json create mode 100644 ui/package-lock.json create mode 100644 ui/package.json create mode 100644 ui/public/android-chrome-192x192.png create mode 100644 ui/public/android-chrome-256x256.png create mode 100644 ui/public/apple-touch-icon.png create mode 100644 ui/public/assets/ads.js create mode 100644 ui/public/assets/fonts/Graphik-Bold.ttf create mode 100644 ui/public/assets/fonts/Graphik-Medium.ttf create mode 100644 ui/public/assets/fonts/Graphik-Regular.ttf create mode 100644 ui/public/assets/fonts/Graphik-Semibold.ttf create mode 100644 ui/public/assets/fonts/OxygenMono-Regular.ttf create mode 100644 ui/public/assets/fonts/ocraext.ttf create mode 100644 ui/public/assets/get-slack-webhook-url.mp4 create mode 100644 ui/public/browserconfig.xml create mode 100644 ui/public/favicon-16x16.png create mode 100644 ui/public/favicon-32x32.png create mode 100644 ui/public/favicon.ico create mode 100644 ui/public/index.html create mode 100644 ui/public/mstile-150x150.png create mode 100644 ui/public/robots.txt create mode 100644 ui/public/safari-pinned-tab.svg create mode 100644 ui/public/site.webmanifest create mode 100644 ui/scripts/build-info.js create mode 100644 ui/src/App.module.scss create mode 100644 ui/src/App.tsx create mode 100644 ui/src/Hello.module.scss create mode 100644 ui/src/Hello.tsx create mode 100644 ui/src/assets/images/bg/bg-bottom-light.svg create mode 100644 ui/src/assets/images/bg/bg-top-light.svg create mode 100644 ui/src/assets/images/bg/nav-bg.svg create mode 100644 ui/src/assets/images/git-servers/gitea-white.svg create mode 100644 ui/src/assets/images/git-servers/gitea.svg create mode 100644 ui/src/assets/images/git-servers/github-white.svg create mode 100644 ui/src/assets/images/git-servers/github.svg create mode 100644 ui/src/assets/images/git-servers/gitlab-white.svg create mode 100644 ui/src/assets/images/git-servers/gitlab.svg create mode 100644 ui/src/assets/images/logo.svg create mode 100644 ui/src/assets/images/notifications/email.svg create mode 100644 ui/src/assets/images/notifications/mattermost.svg create mode 100644 ui/src/assets/images/notifications/slack.svg create mode 100644 ui/src/commons/components/AdBlockWarning.tsx create mode 100644 ui/src/commons/components/Alert.module.scss create mode 100644 ui/src/commons/components/Alert.tsx create mode 100644 ui/src/commons/components/AlertError.tsx create mode 100644 ui/src/commons/components/Bubble.module.scss create mode 100644 ui/src/commons/components/Bubble.tsx create mode 100644 ui/src/commons/components/Button.tsx create mode 100644 ui/src/commons/components/ButtonIcon.module.scss create mode 100644 ui/src/commons/components/ButtonIcon.tsx create mode 100644 ui/src/commons/components/CenteredLoader.tsx create mode 100644 ui/src/commons/components/CodeSnippet.module.scss create mode 100644 ui/src/commons/components/CodeSnippet.tsx create mode 100644 ui/src/commons/components/Confirm.tsx create mode 100644 ui/src/commons/components/CopyToClipboard.module.scss create mode 100644 ui/src/commons/components/CopyToClipboard.tsx create mode 100644 ui/src/commons/components/Currency.tsx create mode 100644 ui/src/commons/components/CustomSelect.module.scss create mode 100644 ui/src/commons/components/CustomSelect.tsx create mode 100644 ui/src/commons/components/DocsLink.scss create mode 100644 ui/src/commons/components/DocsLink.tsx create mode 100644 ui/src/commons/components/EmptyList.module.scss create mode 100644 ui/src/commons/components/EmptyList.tsx create mode 100644 ui/src/commons/components/ErrorIcon.tsx create mode 100644 ui/src/commons/components/ExternalLink.tsx create mode 100644 ui/src/commons/components/FromNow.tsx create mode 100644 ui/src/commons/components/FullPageCentered.tsx create mode 100644 ui/src/commons/components/FullPageLoader.tsx create mode 100644 ui/src/commons/components/Gauge.module.scss create mode 100644 ui/src/commons/components/Gauge.tsx create mode 100644 ui/src/commons/components/Hint.module.scss create mode 100644 ui/src/commons/components/Hint.tsx create mode 100644 ui/src/commons/components/KeyboardShortcut.module.scss create mode 100644 ui/src/commons/components/KeyboardShortcut.tsx create mode 100644 ui/src/commons/components/LoadMore.module.scss create mode 100644 ui/src/commons/components/LoadMore.tsx create mode 100644 ui/src/commons/components/Loader.tsx create mode 100644 ui/src/commons/components/NavPills.module.scss create mode 100644 ui/src/commons/components/NavPills.tsx create mode 100644 ui/src/commons/components/NotFound.tsx create mode 100644 ui/src/commons/components/Pagination.module.scss create mode 100644 ui/src/commons/components/Pagination.tsx create mode 100644 ui/src/commons/components/PrivateRoute.tsx create mode 100644 ui/src/commons/components/ProgressBar.module.scss create mode 100644 ui/src/commons/components/ProgressBar.tsx create mode 100644 ui/src/commons/components/Toasts.scss create mode 100644 ui/src/commons/components/Toasts.tsx create mode 100644 ui/src/commons/components/Tooltip.module.scss create mode 100644 ui/src/commons/components/Tooltip.tsx create mode 100644 ui/src/commons/components/dropdown/DropDown.module.scss create mode 100644 ui/src/commons/components/dropdown/Dropdown.tsx create mode 100644 ui/src/commons/components/dropdown/DropdownLink.module.scss create mode 100644 ui/src/commons/components/dropdown/DropdownLink.tsx create mode 100644 ui/src/commons/components/dropdown/DropdownSeparator.module.scss create mode 100644 ui/src/commons/components/dropdown/DropdownSeparator.tsx create mode 100644 ui/src/commons/components/forms/InputError.tsx create mode 100644 ui/src/commons/components/forms/RadioInput.module.scss create mode 100644 ui/src/commons/components/forms/RadioInputs.tsx create mode 100644 ui/src/commons/components/forms/Toggle.module.scss create mode 100644 ui/src/commons/components/forms/Toggle.tsx create mode 100644 ui/src/commons/components/forms/form-constants.ts create mode 100644 ui/src/commons/components/modals/AppModal.module.scss create mode 100644 ui/src/commons/components/modals/AppModal.tsx create mode 100644 ui/src/commons/components/modals/CardModal.module.scss create mode 100644 ui/src/commons/components/modals/CardModal.tsx create mode 100644 ui/src/commons/components/modals/CloseModal.module.scss create mode 100644 ui/src/commons/components/modals/CloseModal.tsx create mode 100644 ui/src/commons/components/status/StatusIndicator.module.scss create mode 100644 ui/src/commons/components/status/StatusIndicator.tsx create mode 100644 ui/src/commons/components/status/get-status-icon.ts create mode 100644 ui/src/commons/hooks/use-mounted-state.ts create mode 100644 ui/src/commons/keyboard/shortcuts-keys.ts create mode 100644 ui/src/commons/keyboard/use-shortcut.ts create mode 100644 ui/src/commons/nv-i18n.ts create mode 100644 ui/src/commons/sentry/SentryIcon.tsx create mode 100644 ui/src/commons/sentry/SentryProvider.module.scss create mode 100644 ui/src/commons/sentry/SentryProvider.tsx create mode 100644 ui/src/commons/types/page.ts create mode 100644 ui/src/commons/types/react-state.ts create mode 100644 ui/src/commons/utils/enum-to-array.ts create mode 100644 ui/src/commons/utils/os.ts create mode 100644 ui/src/commons/utils/random-string.ts create mode 100644 ui/src/commons/utils/route-up.ts create mode 100644 ui/src/components/AppLogo.tsx create mode 100644 ui/src/components/BuildInfo.tsx create mode 100644 ui/src/components/Footer.module.scss create mode 100644 ui/src/components/Footer.tsx create mode 100644 ui/src/components/Home.module.scss create mode 100644 ui/src/components/Home.tsx create mode 100644 ui/src/components/Logo.module.scss create mode 100644 ui/src/components/SubHeader.module.scss create mode 100644 ui/src/components/SubHeader.tsx create mode 100644 ui/src/components/UserHome.tsx create mode 100644 ui/src/components/auth/IsAdmin.tsx create mode 100644 ui/src/components/auth/IsOwner.tsx create mode 100644 ui/src/components/auth/Orgs.module.scss create mode 100644 ui/src/components/auth/Orgs.tsx create mode 100644 ui/src/components/auth/SignIn.module.scss create mode 100644 ui/src/components/auth/SignIn.tsx create mode 100644 ui/src/components/auth/UserInfo.module.scss create mode 100644 ui/src/components/auth/UserInfo.tsx create mode 100644 ui/src/components/auth/methods/SignInButton.module.scss create mode 100644 ui/src/components/auth/methods/SignInButton.tsx create mode 100644 ui/src/components/auth/methods/SignInWithGitHub.module.scss create mode 100644 ui/src/components/auth/methods/SignInWithGitea.module.scss create mode 100644 ui/src/components/auth/methods/SignInWithGitea.tsx create mode 100644 ui/src/components/auth/methods/SignInWithGithub.tsx create mode 100644 ui/src/components/auth/methods/SignInWithGitlab.module.scss create mode 100644 ui/src/components/auth/methods/SignInWithGitlab.tsx create mode 100644 ui/src/components/auth/methods/SignInWithGoogle.module.scss create mode 100644 ui/src/components/auth/methods/SignInWithGoogle.tsx create mode 100644 ui/src/components/auth/methods/SignInWithUserPassword.tsx create mode 100644 ui/src/components/auth/user-org.ts create mode 100644 ui/src/components/commons/Logo.module.scss create mode 100644 ui/src/components/commons/Logo.tsx create mode 100644 ui/src/components/hooks/AddHook.tsx create mode 100644 ui/src/components/hooks/DeleteHook.tsx create mode 100644 ui/src/components/hooks/HookList.module.scss create mode 100644 ui/src/components/hooks/HookList.tsx create mode 100644 ui/src/components/hooks/HookProvider.tsx create mode 100644 ui/src/components/hooks/HookTypeIcon.module.scss create mode 100644 ui/src/components/hooks/HookTypeIcon.tsx create mode 100644 ui/src/components/hooks/HookView.tsx create mode 100644 ui/src/components/hooks/Hooks.tsx create mode 100644 ui/src/components/hooks/TestHook.tsx create mode 100644 ui/src/components/hooks/deliveries/HookDeliveries.tsx create mode 100644 ui/src/components/hooks/deliveries/HookDeliveryView.tsx create mode 100644 ui/src/components/hooks/deliveries/hook-delivery.ts create mode 100644 ui/src/components/hooks/form/HookEvents.tsx create mode 100644 ui/src/components/hooks/form/HookForm.module.scss create mode 100644 ui/src/components/hooks/form/HookForm.tsx create mode 100644 ui/src/components/hooks/form/configs/Email.tsx create mode 100644 ui/src/components/hooks/form/configs/Mattermost.tsx create mode 100644 ui/src/components/hooks/form/configs/Slack.module.scss create mode 100644 ui/src/components/hooks/form/configs/Slack.tsx create mode 100644 ui/src/components/hooks/form/configs/Web.tsx create mode 100644 ui/src/components/hooks/hook.ts create mode 100644 ui/src/components/icons/BranchIcon.tsx create mode 100644 ui/src/components/icons/FormIcon.tsx create mode 100644 ui/src/components/icons/HeaderIcon.tsx create mode 100644 ui/src/components/icons/HookDeliveryIcon.tsx create mode 100644 ui/src/components/icons/HookIcon.tsx create mode 100644 ui/src/components/icons/InviteIcon.tsx create mode 100644 ui/src/components/icons/OrgIcon.tsx create mode 100644 ui/src/components/icons/OrgMemberIcon.tsx create mode 100644 ui/src/components/icons/RedirectIcon.tsx create mode 100644 ui/src/components/icons/ReleaseIcon.tsx create mode 100644 ui/src/components/icons/SecurityIcon.tsx create mode 100644 ui/src/components/icons/SettingsIcon.tsx create mode 100644 ui/src/components/icons/SiteIcon.tsx create mode 100644 ui/src/components/icons/TeamIcon.tsx create mode 100644 ui/src/components/icons/TeamMemberIcon.tsx create mode 100644 ui/src/components/icons/TokenIcon.tsx create mode 100644 ui/src/components/icons/UserIcon.tsx create mode 100644 ui/src/components/invites/AcceptInvite.tsx create mode 100644 ui/src/components/invites/DeclineInvite.tsx create mode 100644 ui/src/components/invites/UserInviteView.module.scss create mode 100644 ui/src/components/invites/UserInviteView.tsx create mode 100644 ui/src/components/invites/UserInvites.tsx create mode 100644 ui/src/components/invites/user-invite.ts create mode 100644 ui/src/components/legals/CookiePolicy.tsx create mode 100644 ui/src/components/legals/Legals.tsx create mode 100644 ui/src/components/legals/PrivacyPolicy.tsx create mode 100644 ui/src/components/legals/TermsOfService.tsx create mode 100644 ui/src/components/legals/cookie-policy.md create mode 100644 ui/src/components/legals/privacy-policy.md create mode 100644 ui/src/components/legals/terms-of-service.md create mode 100644 ui/src/components/orgs/AddOrg.tsx create mode 100644 ui/src/components/orgs/OrgView.module.scss create mode 100644 ui/src/components/orgs/OrgView.tsx create mode 100644 ui/src/components/orgs/org.ts create mode 100644 ui/src/components/orgs/settings/OrgGeneralSettings.tsx create mode 100644 ui/src/components/orgs/settings/OrgLogo.module.scss create mode 100644 ui/src/components/orgs/settings/OrgLogo.tsx create mode 100644 ui/src/components/orgs/settings/OrgNameInput.tsx create mode 100644 ui/src/components/orgs/settings/OrgSettings.module.scss create mode 100644 ui/src/components/orgs/settings/OrgSettings.tsx create mode 100644 ui/src/components/orgs/staff/Staff.tsx create mode 100644 ui/src/components/orgs/staff/invites/AddInvite.module.scss create mode 100644 ui/src/components/orgs/staff/invites/AddInvite.tsx create mode 100644 ui/src/components/orgs/staff/invites/DeleteInvite.tsx create mode 100644 ui/src/components/orgs/staff/invites/InviteView.tsx create mode 100644 ui/src/components/orgs/staff/invites/Invites.module.scss create mode 100644 ui/src/components/orgs/staff/invites/Invites.tsx create mode 100644 ui/src/components/orgs/staff/invites/invite.ts create mode 100644 ui/src/components/orgs/staff/members/DeleteMember.tsx create mode 100644 ui/src/components/orgs/staff/members/MemberView.tsx create mode 100644 ui/src/components/orgs/staff/members/Members.module.scss create mode 100644 ui/src/components/orgs/staff/members/Members.tsx create mode 100644 ui/src/components/orgs/staff/members/get-members.ts create mode 100644 ui/src/components/orgs/staff/members/org-member.ts create mode 100644 ui/src/components/sidebar/SideBar.module.scss create mode 100644 ui/src/components/sidebar/SideBar.tsx create mode 100644 ui/src/components/sidebar/Sites.module.scss create mode 100644 ui/src/components/sidebar/Sites.tsx create mode 100644 ui/src/components/sidebar/Teams.module.scss create mode 100644 ui/src/components/sidebar/Teams.tsx create mode 100644 ui/src/components/sites/AddSite.tsx create mode 100644 ui/src/components/sites/DeleteSite.tsx create mode 100644 ui/src/components/sites/SiteCard.module.scss create mode 100644 ui/src/components/sites/SiteCard.tsx create mode 100644 ui/src/components/sites/SiteList.module.scss create mode 100644 ui/src/components/sites/SiteList.tsx create mode 100644 ui/src/components/sites/SiteView.module.scss create mode 100644 ui/src/components/sites/SiteView.tsx create mode 100644 ui/src/components/sites/branches/AddBranch.tsx create mode 100644 ui/src/components/sites/branches/BranchList.module.scss create mode 100644 ui/src/components/sites/branches/BranchList.tsx create mode 100644 ui/src/components/sites/branches/BranchListItem.tsx create mode 100644 ui/src/components/sites/branches/BranchNameInput.tsx create mode 100644 ui/src/components/sites/branches/BranchPassword.tsx create mode 100644 ui/src/components/sites/branches/BranchRelease.tsx create mode 100644 ui/src/components/sites/branches/BranchView.tsx create mode 100644 ui/src/components/sites/branches/Branches.tsx create mode 100644 ui/src/components/sites/branches/DeleteBranch.tsx create mode 100644 ui/src/components/sites/branches/RenameBranch.tsx create mode 100644 ui/src/components/sites/branches/branch-redirect.ts create mode 100644 ui/src/components/sites/branches/branch.ts create mode 100644 ui/src/components/sites/branches/forms/CustomFields.tsx create mode 100644 ui/src/components/sites/branches/forms/FormForm.tsx create mode 100644 ui/src/components/sites/branches/forms/FormList.tsx create mode 100644 ui/src/components/sites/branches/forms/Forms.tsx create mode 100644 ui/src/components/sites/branches/get-branches.ts create mode 100644 ui/src/components/sites/branches/header.ts create mode 100644 ui/src/components/sites/branches/headers/BranchHeaders.tsx create mode 100644 ui/src/components/sites/branches/redirects/BranchRedirectForm.tsx create mode 100644 ui/src/components/sites/branches/redirects/BranchRedirects.tsx create mode 100644 ui/src/components/sites/branches/redirects/branch-redirects-form-data.tsx create mode 100644 ui/src/components/sites/branches/redirects/configs/FileConfig.tsx create mode 100644 ui/src/components/sites/branches/redirects/configs/ReverseProxy.tsx create mode 100644 ui/src/components/sites/branches/release/SetBranchRelease.tsx create mode 100644 ui/src/components/sites/branches/settings/BranchGeneralSettings.tsx create mode 100644 ui/src/components/sites/branches/settings/BranchSettings.tsx create mode 100644 ui/src/components/sites/get-team-sites.ts create mode 100644 ui/src/components/sites/headers/HeaderForm.tsx create mode 100644 ui/src/components/sites/headers/HeaderList.tsx create mode 100644 ui/src/components/sites/headers/SiteHeaders.tsx create mode 100644 ui/src/components/sites/headers/use-site-headers.ts create mode 100644 ui/src/components/sites/releases/DeleteRelease.tsx create mode 100644 ui/src/components/sites/releases/ReleaseNameInput.tsx create mode 100644 ui/src/components/sites/releases/ReleaseView.tsx create mode 100644 ui/src/components/sites/releases/Releases.tsx create mode 100644 ui/src/components/sites/releases/RenameRelease.tsx create mode 100644 ui/src/components/sites/releases/Search.module.scss create mode 100644 ui/src/components/sites/releases/SearchInput.tsx create mode 100644 ui/src/components/sites/releases/get-releases.ts create mode 100644 ui/src/components/sites/releases/release.ts create mode 100644 ui/src/components/sites/search/Search.tsx create mode 100644 ui/src/components/sites/search/SearchModal.module.scss create mode 100644 ui/src/components/sites/search/SearchModal.tsx create mode 100644 ui/src/components/sites/settings/DomainForm.module.scss create mode 100644 ui/src/components/sites/settings/DomainForm.tsx create mode 100644 ui/src/components/sites/settings/GeneralSettingsForm.tsx create mode 100644 ui/src/components/sites/settings/SecuritySettings.tsx create mode 100644 ui/src/components/sites/settings/SelectMainBranch.module.scss create mode 100644 ui/src/components/sites/settings/SelectMainBranch.tsx create mode 100644 ui/src/components/sites/settings/SiteLogo.tsx create mode 100644 ui/src/components/sites/settings/SiteNameInput.tsx create mode 100644 ui/src/components/sites/settings/SitePassword.tsx create mode 100644 ui/src/components/sites/settings/SiteSettings.module.scss create mode 100644 ui/src/components/sites/settings/SiteSettings.tsx create mode 100644 ui/src/components/sites/site.ts create mode 100644 ui/src/components/sites/tokens/AddToken.module.scss create mode 100644 ui/src/components/sites/tokens/AddToken.tsx create mode 100644 ui/src/components/sites/tokens/DeleteToken.tsx create mode 100644 ui/src/components/sites/tokens/TokenList.module.scss create mode 100644 ui/src/components/sites/tokens/TokenList.tsx create mode 100644 ui/src/components/sites/tokens/TokenView.tsx create mode 100644 ui/src/components/sites/tokens/Tokens.tsx create mode 100644 ui/src/components/sites/tokens/get-tokens.ts create mode 100644 ui/src/components/sites/tokens/token.ts create mode 100644 ui/src/components/sites/use-sites.ts create mode 100644 ui/src/components/teams/AddTeam.tsx create mode 100644 ui/src/components/teams/DeleteTeam.tsx create mode 100644 ui/src/components/teams/TeamList.module.scss create mode 100644 ui/src/components/teams/TeamList.tsx create mode 100644 ui/src/components/teams/TeamView.module.scss create mode 100644 ui/src/components/teams/TeamView.tsx create mode 100644 ui/src/components/teams/live-teams.ts create mode 100644 ui/src/components/teams/members/DeleteMember.tsx create mode 100644 ui/src/components/teams/members/MemberView.tsx create mode 100644 ui/src/components/teams/members/Members.module.scss create mode 100644 ui/src/components/teams/members/Members.tsx create mode 100644 ui/src/components/teams/members/add/AddMember.module.scss create mode 100644 ui/src/components/teams/members/add/AddMember.tsx create mode 100644 ui/src/components/teams/members/add/ListItem.tsx create mode 100644 ui/src/components/teams/members/get-members.ts create mode 100644 ui/src/components/teams/members/team-member.ts create mode 100644 ui/src/components/teams/settings/TeamGeneralSettings.module.scss create mode 100644 ui/src/components/teams/settings/TeamGeneralSettings.tsx create mode 100644 ui/src/components/teams/settings/TeamNameInput.tsx create mode 100644 ui/src/components/teams/settings/TeamSettings.tsx create mode 100644 ui/src/components/teams/team.ts create mode 100644 ui/src/components/user/UserSecurity.tsx create mode 100644 ui/src/components/user/UserView.module.scss create mode 100644 ui/src/components/user/UserView.tsx create mode 100644 ui/src/components/user/api-tokens/AddApiToken.tsx create mode 100644 ui/src/components/user/api-tokens/ApiScopes.module.scss create mode 100644 ui/src/components/user/api-tokens/ApiScopes.tsx create mode 100644 ui/src/components/user/api-tokens/ApiTokenActivationPeriod.tsx create mode 100644 ui/src/components/user/api-tokens/ApiTokenForm.tsx create mode 100644 ui/src/components/user/api-tokens/ApiTokenList.module.scss create mode 100644 ui/src/components/user/api-tokens/ApiTokenList.tsx create mode 100644 ui/src/components/user/api-tokens/ApiTokenView.tsx create mode 100644 ui/src/components/user/api-tokens/DeleteApiToken.tsx create mode 100644 ui/src/components/user/api-tokens/HttpMethodBadge.tsx create mode 100644 ui/src/components/user/api-tokens/api-endpoint.ts create mode 100644 ui/src/components/user/api-tokens/api-scope-groups.ts create mode 100644 ui/src/components/user/api-tokens/api-scope.ts create mode 100644 ui/src/components/user/api-tokens/api-token.ts create mode 100644 ui/src/events.ts create mode 100644 ui/src/index.scss create mode 100644 ui/src/index.tsx create mode 100644 ui/src/providers/AuthProvider.tsx create mode 100644 ui/src/providers/EnvProvider.tsx create mode 100644 ui/src/providers/OrgProvider.tsx create mode 100644 ui/src/providers/axios.ts create mode 100644 ui/src/providers/history.ts create mode 100644 ui/src/react-app-env.d.ts create mode 100644 ui/src/setupProxy.js create mode 100644 ui/src/setupTests.ts create mode 100644 ui/src/styles/animations.scss create mode 100644 ui/src/styles/branding.scss create mode 100644 ui/src/styles/commons.scss create mode 100644 ui/src/styles/fonts.scss create mode 100644 ui/src/styles/forms.scss create mode 100644 ui/src/styles/mixins.scss create mode 100644 ui/src/styles/variables.scss create mode 100644 ui/src/utils/debounce-time.ts create mode 100644 ui/src/utils/dedup-slashes.ts create mode 100644 ui/src/utils/extract-exrror-message.ts create mode 100644 ui/src/utils/format-duration.ts create mode 100644 ui/src/utils/query-params.ts create mode 100644 ui/src/utils/suffix-big-number.ts create mode 100644 ui/src/utils/suffix-time.ts create mode 100644 ui/src/utils/text-search.ts create mode 100644 ui/src/websockets/SocketProvider.tsx create mode 100644 ui/src/websockets/emit-now-and-on-reconnect.ts create mode 100644 ui/src/websockets/event-type.ts create mode 100644 ui/src/websockets/listen-many.ts create mode 100644 ui/src/websockets/listen.ts create mode 100644 ui/src/websockets/use-room.ts create mode 100644 ui/tsconfig.json diff --git a/.dockerignore b/.dockerignore index 32f02eb..168824c 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,6 +1,7 @@ * -!node_modules -!migrate-mongo-config.js -!migrations -!build -!docker/files +!server/node_modules +!server/migrate-mongo-config.js +!server/migrations +!server/build +!ui/build +!docker diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 017f37f..60967ec 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,8 +1,8 @@ -^QXHq_`Td&XRv8KuYb5|WXtD@Xkm3I;jiP(s1 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

vvRm)N0d=;+gv3(XEoWC( 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 eY?F-HQwl* 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%0AdPzKw(Cb+Htn~F8Q04= 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℘GMN1UuhC2)Au3@10fmf;VcNZu^XMuR1J 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�W>e)^AqlN|Ld{*M_2*q~++;eXAaFA(nN!kz7Ik>=5^rbt(a|0G@S9qT0j}Zl z5Ljv0)Fe;vJheyFVOFEZKG6D2f-I(Z=>mN6osFB>>+;m8l^2t7zOHuE6!Gr(XLbiL zunG*D95O)<<|I$o&%zywfQz$_Sipl?t2)dnu()*4jfq=-I|1n{AGsH^@TKE&E~&&U_X?+{*#(nMUVgCn%u#VG`XgpJ=Te z?QQZMALdfwO>lS(#fA6}xI1%*Wlqhrg$qdKHH)pdR=BPnZ?1hwEU}_8Q>q!cm#_ig zoP}b2CTd5Xyd z3ig08OM)*krV}p*t+sG)j6O-TP>c{1%TsUPFqoF$xx9hl zT%2n->up3FqNeGOw@|0tWLw$JvN^e%GbP6?gmFiP;~4msXs1FV#wnOzYtY>?M2OtA z-zs16>0?^SY(>*$)66F~@sS(LV|2CHTH>jA{^Ns8w;bXchno50jN(IGMxV!(sQyC* zX-9A*a@9%hkRHX?!szEWQD5iCik!W@2tNQB`h10cU>gcbya^PGArj92mHTR2w`T)s zO@BXz_J5^tiC5ws#TKM!-1J3BYCOYcVoTvK8>+HmL93zqu#;9FTtdi$WWaOSR+BVN z#5d~;#60;HJu6PaIOT{k5i|&}Mn#wW?9-DCTU?2SQ<0T|lMn$IvJ(M6ReJL<{^fyD z&3gZT6I9BkrGnwV@Qo=B&8eo^dI7ukn-fp{L=wq6=-D^Ec+cMl@5MAdS!;S?-}9Nm zgmPfVTH0izAhOTN9CFjGq>D|?^eo_*OlV6m{H0ID`W`ask77DgZijEOCSitV+w{bD z3D4bqM~(Eg`8=UAu{@y2p}_d|)2x^ey>=?wV zJ^K1qzHjtokBh&e-8*{Q#1guc3;g!kdVZ>gnN|vSe7i*Pw=SvV>kAdi+yc8M8x$qS zCiR#Y#OX3XQk9lDm=~4>({#M>L$LR~t>zYK?VD`--j(2h@Y3QVJS2c@3(9iI1M**k zOG#D6s4hjpQNEi~o)HgBFA6kBfnniUKoB0u4o5gUP&^NEbN zof6Ye{%503E!Tc_+jM!)u%+%4k~HozvY8$4MkC)&uFy3vo*n3`d3;H&7ZLf^*t#dS&$hb_RR0ya`AY%PuOvs>%v83&GFvN?d7#S~( zt9%nGHJ<2C%gYD+e*Y7tFumyH&0D&Iv@jS<|A}W`ckboBuzySm;YBrpmFyX6a|>Vv ztTFM{>^E>;iDj`vf=CFJJD24OeDe{(rSbaSc%EY1VcBd)kj=`!{M|Q(+RO#8ctWh zt3X+Y_$ffRCCo!wj<_<^+OlGFP*zMI{ap7XS%>X&3m5usmEoCWe(7Ep z;}iZ1eWg8-Dxrt#JmYK1m!fu?#X}#Mk*j8^SUKXkoi*CPc)j>eq9p^jH=myi@cs#W z;6>k4Bm5H$t`^trPfbvb171wRtOs%~D0rQ}G7INzU*{vvi!|ji@1)C>*TdFI`~?o? zu}Ql+dq*P3*!=b}Iy^1JjvW#pS#&x`cpM!Gy=i&eZ>>5Hd7;QFUb2qhwvJ`~SiIcM z@jVB>S9c=lC7Nql3{vRh%)V`(Ko#)wI=WBFGs8^Rlsb4a3)|6ML)~v!@I4%_#fRrO ziqq+MCY+nK!T(tuH3gP?G-Uz;cbRXD5jH&98=e^3Zb}v)Rp=pQ9RD>p2%6R(EL$8Q zWEVS&!UO5*%CC+eVrUI@p{+vpOY?6$*>W8hql;b)DlbseKb?8{-XqZY&yx@>MnVwa zdrEg9d^y53r)|mYR~!t3K+4#7xnUzTpR;D>AJSN6G+a3{91v=WIJ%+Htz^*bfsY$r zR#$*R5P)r$s~21BrT5JXf_`4CD~c`!asE0=IVL^$M=03CCrpwjAn5zH>IMQ?AHDU+ z%6zTQan4>J+I8BBkn!O0y9#5~ERKN8VM#hEjSB`)!1*t9Y83;%o}tYRj&wd-D<|JI z_vJ4+O=?b%_i!_R*}@_N#DNyfBMKkCZr74Bsebdnhcj8=@6euFIu;uE*w)9G=zBJo>mK5u-TO zn38ha4>B#7&@9olM*%W^0ZJrIdSLPf-@;Q(4WE?e*BYf;ry2lpsmH)UfBzy`9_Ayw z8Hn(G>z!d?3IAGWP-VF&>oBtgZ0{4u%F4AJex0vOT9J6%NfTaRLR3a$A!cEF*ldvAf7d~AP!G*$|inI0VMkBl@QhqSC)6D`yE*# z%)m5Tg3a;mnCG_*gA%sVn?bZRtSnFT0|kV$JK_THF*ELeS00YQfcJ0l-%rloxO z87LBsALV-~lJV`#7v4^|))pLTccs}8v~`I$5&zLV`{J8(8@zR>wH`~A5QkY~-?T&B zO2YIx4@85LjBG}EEeYwIA~3N5n;J`z(k#8B z{PrjV+uq%k*ZxO4l+T7~6s2sd?1z--gw(if(Y)EC3&B@5miP-^7}!1J5hd`U`fTX} z*Dg1384atd+K+{Gwd)dHBEg^Iu2`jmL(8nsIQICcsT&Jt13?3Ug?_CPXHPYQ%hC2? z)1P9gO;Gvyci&I*r03B@8fP&`qlaF*_j$l2&ZNq@$B-?ju0R-RrzKz z4tuG0#U;*i$&ra%i-Cq;kgFro$~j)z3gOE_6Y+*>9*bUckLXGOb1Aj7LWQ?y{O3b4 zJ|>u@_EO*4l`@2!v2rr>nE$WP`KP1O>{ND{1x`Yl@FGf$gkSFJ=W-9smgbITpfydh zONRL^OOibiB-6zDaDgzFL9_5tW$; z-^q*>)dD+W27I?n+1eI$ozluO+l_@f)2TL@d-7L4?pQSf=hP85a*i5!rQjU5eA0=L z${(7bpkpv(_;rkT+Eld@U)ynlFAZ}ecLEfv>{mxslgGqPu5@)suPg#892%2TO8DB3 z?Vs=*uqeYpaLWlDY#yiyiNg^_m}F`q6h$TP^W*D?JJqXZSDm1KLCno0e!s#s^b`h# z4<9Sp&4OoeW(Y^~4eP41i^9#UfQ7m8g&I09`Jd6uyZI$4tkdc5+^z%6HZD+|boxP3 zb6C;L(C=WSb=>8RdX#=h}n{LZjX_o z6=NgDFSR&SEK*{?aQLhAr6rXL9!l)&BV8`-oQ$P@hB<466b|yyMG;;RfM**A+ShFp zyGKw#N}kNa_;~#Iqult4%L|R8SJO#X8qxl$IRc+&9K+navvmys(M$Z4YqFFUv@9QIa@0>?*%Z3Wx-gHVKdP&qDt zSD|*!{B366=9t6a56^82e%fCxB(nCzq;?U!UCc0n3ceuO9{5t%y(=2DMlJ(*w(?Uw zn2FpyF;5M(WaUW1QB!DtISA3%eSM{uiW7)!i_jD-Z9`!JW<`czyrpj9`P|WedM9 z!+)K)ygNBwL~AD8o=+rQiNXe~i0(uy&WZ{Z{+O%%oA49LwN3(nDClOpB~QVXeblO} z;37X(o@ApQ&YXBg{UN&Y<$2;Y1XnsO=ugpgmAFnmv{vQHiArWbqep`j997ln(`M?j2bi@;<5{1=(%kE z4T?_ZAfvNU3W@dwI{^duA)!SWrT5`-!u&BoeQrBpPrM$KJOue9I>xa^&A&2|Mez@u z5JR5JsbgGKWfpP48#H3l(YutKf-1Y+$T|1pn0-}Co{d9%USWGnJy`XW0BTZxbQ>G0 zx1u7>;h0~(O$YVYlW|_7=V|hwyakXS!O0BzK|NK)o-{$lH;{1Qp;#xBZRtrGAQ}z_tJn;7ti(hygs9Ko> z79yC`TKd`)$fDTQ2n^P1`#}ES@JdIL)Kxe%U(Rt+Hq)222`ppzaEFrS=++uapt$E< zyT(}QI?_30NNOiKM5T7KZfE|DAs=ihY9x{kGYhhEKkcCT(_0%C+YO<1`pvUd83|iuqAqN)Q%ba8 zDLSBm3gF{CV)B-Vu?Fk++P%u&R2&s_gdkbGLX@ESxcFcRFZJ%VrwR z>e-QnQyY}?TT(UZ*p)ZP|3zf+(H!@ifu`mrm;JGt9e*7O?`{aDtQ-LIUfHsrDC-ep3KGA0AqA4IY|MaNfawBOY1E9j#e-x15=v&3XSp&>Y za8uNlyr)08%x!-AJm2~OWTN^S3*)1~1Jl1~a4|9B^ZY~XW~ViO_a_l#buJL>(GBwJ z&?2!2g#UN);4d&{417AsqDh-gkv|PzT{!s!Bel(qujgR(;0J<#@4{sZqY_9oEAL!L zxk79uU+gCZ#FNn=ugK^84cq$={sxV^&?~mlL zt!smUL$UrYXCi@(2{=$8nVFoT+w1q}vl-|8^+c!Bc5kq*3X<=$vt~zAddU@$ug{{? zz<|x^5w#MSX0yhu1gy)*;j_F(Xg+OWc15?M5IKpWaV-j2N`2#_QKCc>=G9pk-{1zt zm*t;EIYTs$y#>{CucN{~$g4M&-K@=Yo5H4Dx00 z+49cEH{gLa{R*TjTPWq@q&*P-lbn8UWtCAst*Ne)*GR@sT5dZ=o*;0X3-yStNEK$} z&+v&;K66Z3rqBkzltb$u+DoJ<*v@#`F1KK(rfJ}`8F6d}A69n-r+=VMi?D&W!gr~s zS7wSf3@^Xb#G*Lzty>=_dba5187_7^b*B-{`t<3JS&UIHfl6`pW6b*nkA3DL@ADpa%i5SYPZtBhC_VS^lOkU-$jR0dKEisV5S&cXoS;h6(Jj zmTI6PJX3e9*@G*Q8+0=&rhjTUA5tGrN4{W2`V#_Ktkw||u#4<1Zv;P44g;fq0Pxd| zG>L#)xIKIe0y@^!&($A7X6K!AA>b$(78Am6bRiN4ZR^-wdf`VLeI8WOboj6qoy}Mz z(%!svB>uOHZs9%Tg+{ zX(I`;mnwFUBt+|%VOn(Z)N}gaKaG+VGceVPHMBO>MA>(Z znLcLfElWict-VO=C|kFt*Ad1ckst&>ru>&UFXvV8S~fWFPW<(B>K_xGnL};h1s{Qd zC13-dIADPlwBg|TU)`FS$O3KuFSDh(Y3mcrzii^EAqX$S{EE@(JjyY@X;>h1Cq75A zhKPDISE4&oM_@|rgG4<5P;aEyofed}U3-cZFD^=A=Llaov`MH}F49m#>`yR8t!v^m zO=!gLn}8~gXEZGmM_!7ycjAmZLr7c#qt+`b8_5g6nU^9jLDeqE_g)xXWL=KbR(MmXu8x}ELJbBw#l+@yF_|snT#k@{4 zH3b8e8Wkb#H@dhlBaKN0h{N;%MWySSgAnqTeph{d#K{LxXipqD6`|gLS*GS#%Nw1s zqC6dbso18QbRu2V8%%4%4SI8Z`m~l_l{#72}TEQ#V6~csi<8XF58J0w=iMoN8`CQ%^B4H)GMQ=zQ?V!YQ zWLyrHJ$%xWc~(!8K1552zZ;pG0^z>XK%Z$vi@hsAtCUt7_xsP4b^(H(vSMmS%74R8 zflv137e1vBmd9{-6+^7`YKzt|ndemAp6{33%Qgdc0cGC7Sr|9_EYJX)54^LT_>>I! z=};u8)Qhd7#Ipjgzx;qY(wB|w0P$d~a}IA5*jeW&>TcJt`)TC~%rn6H#Kc%&E8kLu zC}>}Cu&1+gC(R7LHF1}QWYPu*Vw%1iX};h|_@uOHcaqEtcCnTGPhHHl3lot(6N9|O z*)VwyPEYHh*g_JhSUuxSxRXKr2FS)6e;Bp(W7VssBMYBZ`{KuEL9p^26i;tPB0~JU zXvkeR*1OH%V5C=k`aYCO5m=XaPG@poX2$7iNE7)KY7&G&Nxd5N26PScsQ3rU;%-KO zVOlF+yqPQQtDQ=ahj_asSbNA2uU#Y#-0-E(F%KUyJ+B)ZUFn$lnqya0SXCrVJvAA% z))I<+1*1}riD8b>+mB_|1QEp3OZ<-7z~&udR#$EGXe=Zc?tayeDRZRl-Nrr=W$cJJ zr090;SsA^VN2^wicZUjLhz6SDmIcdHdcI^{xwt&nA$%7E#?e=iR06%u0rm(&D}!Pb zVh#{uO^zj4c6LuhXrCv0Jbjpi1SpF15uKE+=uLu;rq%(!E%vzJ^6v;zjoH(VgR_{R z`nqgDrS99oVT%=t%R+Nv<5eRfA9%IkzHrko3ZJA#?^Duv(o4_5X=n;kZ;f3RS{y^>WfG8YduhP37InM3s7aSC<{cP9AnFhh}?%dQiOX8=A!0 z-K$uS$ABm=)IL*S)n>asjnz1(eu>*KBYN6#*QD|$hKk7l4p+yo4jOAar)H&`o^wzA zx9OMV3WXIOaGk#XES0NRvt`d(0JDL#p@W{lS3522%)W_Ak|XSrO>lfSd0T&; zLa`WEaX_x74jXOzb%;CBW@+5u)TH~lY|4|kr|hl_#QB(T4nhnes4)mX2!<)+%r|kF z_5vubMMJ-?tnoI0GH+^aKzqIQyWp_gkNs8M#DzmkBbf0yZ5~dsKHCSH2>l|$0{(Ts z(1%}BL0NcNG+u$l>QenG;g_Xl4i`6Ye7L^>IHLBg?%C;M1QgcymUP*D2os{G*MMtlW&(c7`U?&mZxb0FQtx;?HHI^qN? zX%<2X4s+f2TS<^lyIbSQVRuTlT++?!}iy zO0TZG=cnxvQ%;n(@vv^+{Juou)CC}RR{+NHNBMORM0POmI;m#GGpc9(h?QM!mBd*M zxf-)X8IUlMVsBS;E;%?T)#C-U`^nEqyLZ8?>Br0h6oNsPURGBmXDiin1K8AMPmsU_ z=S8nAxw+4s?EN<2y9GFd1!c0;dG@f}u9L~YN|L2{<*QeV)AsDCClm-YO`byC$s1A6n>yG>vpgavPql3$QSqI3H;@)j68_=*?#VEEp6Q*8^sQ~T0!l3@vU z_+m`f%}1`)S#naWCVg=MQ!5B$@Yh*~lyCZbKAvueUhQJo&mZr#k@VtmxN{tBas z-pUmQ4F6BO@s{O!a>k0@hnM(aPumobr1COqs7rq6 zFhVO2P3Q_bM`*1iSADM=rv_NEdht)iyGWFU`mx9e@UK9xD!e~_lC~~Q8s1RKQ$_!& zq9~M;w$KvfNs zxVr#NjV=};&3dS!I|M?_oPZsZ)NEatg0|&IrtImNR!7S;j%AhSh8$iDtL;jg(&^Z_ z8dSCfGG1NDNmrUQmHcyv9ebb%QHO?ooDx?2JV;X)!QyDhv8-3*MWh0k!X;nZaz46T4j0#ciY*f1AG~Xb zi8cQoqd)cnVH(*ut8;OlNPXDt^7dRk0om-0$eEwWbI7|qE#$Q1S@ACOPCD511l0$r! z*YdhN)>n#`KOf1dU~|pFVo8?TfB7=lPqP{{xrM~;gwIFpFleh zUw}X7xw>WzRyEmfE*M>hi3-_(-vZMNKFxeIX-!(m@)400*M@a z*J7h6`Js56RD|-$LB32T&^I4cXdxcH5g>WkeM+EQ{bk7Y?VEgDpqQ&jm`hn})BAI%$typEZQ@ zh|AgQ@OP2tei%!pP_(C>{-2dPDy3Var&#j8sgcLBU-SeLlnf?Psf!{B>JQOnl3M$A z5eBEnZUk~s3kPYn)f_!ECR;4pVvb&o5f~7h16c{I)AA&&@b$PBB#Amr9)z`F8}8lC zR;F2XVhUoA@Dr#8*AVCiftAG*>C69a|%Mwkxcsx27NC8`lm zx?WHCopYC|H`-{&69oj-^>tuUBp;v7$HVlMxW*H-r5N$><0oTg}!X{_GTta?v{@+ z@dMNWJ1`vns}@Ns!jhQ{5|Gs>E{A|Vtfe%Pj_3+xxG{jlX|;-QI>{%b-pQLA%*+IA zUZ<56i7k@zWI`|i-#ZkRf`6$G!)>Y0I5VBEm3o*bSiHJ}7QG*~_qtlC(jkAB3fYF8 zJ3rm^v>i)!f&=#TeA4d%(n(^Z)aeN#K0kGe67`>=iHfq9?3e!4gNxdFVK$ zBF965_;g`0mJthS-xOR8fTJl3XWJ&6Gee+MG;p{&cP<~;1Bpk9UT5eFJf2?%YuChk z26Ss^azU3$Y6jqg0=AJX`RO?=sbcNIuf*jv2_ea0KEkb>s1?euQGp@Pw)hzENSb6~ zO5esW@4;~s#v3JR#3~P^XxQk+JM8vk83x(@KkMG>?_Vj0LwY~7FRoIT;)sje>J^tu zfQl*%%lPJD20xM&Y3wEDBQSYHEsRB>UIHX_gZsrZ*0F5M?N35Hp>@p zjmwSAFgkX=@v0b_HI&gk%LN!k^-=D-)=V^w>`XKv@GyL@_EG6{mq8vS$V3DsBNFWU z-N*D`@JO=%Y~Q1DW%OH74|}koMnHc+K0sM$O~wO^3B=Kz(a9O8Sssb|8ad&TCDi;o zja9Yj_3K`Jn5EcxEvuy76aMtbwoYQZL`}=XN1nfW-zbr`QGTNWe>!rB9Ebfaa?0n1jA^XXsOrF*6@mBL(p@j(Ja%*$u_)H+SwjS8oCvDgy0cxTW zVOy^30Rntfgz3ot5dWxVTu^O=9g9;f*^xSCl8oh@Q|IWiMBvZWph^juDbukD1h=7gRT zPOv1~Qv`B3{%|*?8L)cYL|tueEi&2j5rzgRUsF*5!5d&7!@nqfpM$5XWzpHtGdC8S zTo5^BHT2%;keVCH<(FnNk_Ts>!r9-q3V#X)qVdB9L6$b0?eWrWm!imtrs2{rGbuq4 z5BPM%6J<7IqB}kSVGqm=&$R^KA2mjXLag;xKiaITP+L-W2Y?5@Tt%Ms3?tf<61Pqw zc$l9^UtiVLq@>L`)GZPeb4xH@4kXxoe@bMTR2?LEW#(qK7%-XbOs$?U_ zs*(BL8Yw|EamrS#IzkKLNjQqCa($6Z)44-n1r-Uu3ZgOi4pAMW3*)ebVLpe-_Rz8{ zH2)spVDpJ5g|w@{;kUq>?*?S{0yGFh8iR9NCL+vLO;iLO(-u-52_d2`h>oQ%L8M^6aRYVB>IF{pZ1jml8hcj z?DZrZ=xU-?RIN6(*F`wA;@m^Z=^j=>votgL2N?44mZxvE_GQ7_noYzuO|>s)(hAb= zhQaUT9!FlRt3-?-H`gzN&v>=xhI*G>yBEE+8S(kVo@Eu<8|>gO+MNvd(L1rd|H}xo zA(unQ8owvjx}5D6wnXf-yjaFGz!nvV^HPB-jiq73my%1?eJV&M)RXW>()H&4Vm>KZ z_ZiQ`*y0TJWH20HMP5*yv=-7==E~18)B!Q>PCLufb5E!GkPJ3d2lwg^>dq~oz$8IT zLt8McNtIr{R~`~IkmIyyo6)EIb{`<*(sf>==DvUGO}XQW+KjNY_6nPzE}#ItO0y%y zE^1btE}b=y34BpO=4~UjXTc3vNZoX28pPlH*P#*cmjA~(vp=!O##268$ucaFONC38 zm!r>0cWpD+)_A!pv&`Bx-)upMq`NO*lmj5{DPoOL%@?}sG42c6+BAi#C-$$cgQ!dX zZTvt(S%QH9^ieabI7kCYNpY8eB75~<4RcG4mgp6dMG4QE8@D)U$)=sLJh;20wRqP)*Dg$bxf~O0Z+%$520g#~^SCfFJdVtix0L0a#{+$`D zdN;)AuSZ{r4sVuQwy(Wm_f`UpN6vOH`Yl9yy}wDc1?4ZIf(6HW14Yr(oyIF>0Hk=yyDAcEm0p&Vm*~pW3|bML|tjtf4t@PTv89J$yFP7iY_Ca@kaV{jd)zX^kt~3}52# zJ7O_T(;XSCB{I}#I$|fT#QO80TVZ_>xgiyq1vt%EM;Gb97c6EA|-n>GMx`Hau zA)9&4ypB8WtM95NZkaY?72KhYzjbu|K%wREUiTFZQTNywTD^{ONJ%~m9+ZjNLRJfB z%M{_;@Ifmin$F_W74x&@t7PohL3^x(yaiee(cK^O|2^RD%RXQ8$JKD9dd7xsjD(aC zD8iU>$yEH58vVt`S@x8p#-}Na>o#qIY0sw$rCKOmMx4ZOOZn0*`HF`Ol-rZCeVVaF ztE%jNp&Rvw6C|u#YvPW%ZSDL_hdjiurR^f9npu5!oo4*rCB)PojZrkM@F-_bwj;@41GZ57hdB;v* z59bdNqP+zv=k1g|RUR@Cd+Ov&lgK!AeStm_=^ChG8Xtl#RmV~O@Hu;Me zqIu;l0a;ddA3GPT2sVuA1#{usfO;&Sf4^2cTT(;arf*FUL-lbg$A5GYK)px0+g(S7 z6C{FyRd&qXY1U`r4H_lI_6er!?sEsPmh%Q|*<_5&xAp(rXUj}rvq*Mkc2EA1LUVsQ zNfV1^l(W_fpA7Fxl%o%EHyCiOUIv)z_)?yu(-f-BF4sbWCyvlyyw1GS@gDT^=tm-1 z9eE)Zt}tnH(#}Re$xdnMlV>(JwZMD5H<3Yyw%dux^ViaZonSp5&7v!9t+HNG_<3C= zkNX?X2k|vk5!P05L}`BHN8FlC8tFh8_THvA5EqX>##=VFcJXg02J6M>;jJ9zsh8Hs zn-76;s`I@v;suvYpd3LID!)@l^{SbP5R1CXS!XT!c!9J`Stu~Z$io+7+fTwy90qN4 zUJS^L!nAWMzt>n_jA3)H$$4<{a~hJs&vDjwmJF*fW2X%nje1{=&}6OV!_$)K5of`r z9$_zw;n_{o)y#pMlo@D5nnMlBM!-3XgTpU zSO{Me=JWZbvQ|izFMjG@vTM#OqjEXqf|QtSGp`M)+@7cB#J5mPp#yu{*2P~5EYEX_ zyVklgt}gO7rnlb|Lxe0OU^56k1Q*KwA4}y7P>ar1HZ642u-cqzT$01}e}Ll<%|Kqx z1D#+HtOGC&%zI%|cJ0q+7*7hD?Fea?qL&bPEgbw=>JCTi`lgYOgeP69gxj7mMfw;p zmfDZPvMfZvqD~WsN+T+QQd6dXD}UgzaC=wRkx5RtvaoE#{M-lWLy;o<1PPqGtlY#% z0A&qVOZgM_8%>;8Ju!y;`vfI*`G{4PY6le}eCmomE4ph+UoVwD-D~PQ?{Za#*A&y? z)uQ@z{OJT;^zDkg19=~B?O>)9z#=&hKo?JmHCfvx+z5Tvr&Bn*^B8)U{Vz8cZw(eg zLxDQZkw)*5s9jn^7ey*Mmn??qN*uNw3kUsi`@n$72 zzg6-X-`5d?%c+6HH6%Crr;>_6!rV}q=hHH@nVd>O7MQ-}1kgNi_%kVk^z+VVE6q7= z@?EEFTdXFJ?So>XbJcfMMqo!tD`qk-kP1U8j(w{O5$h|ioBSx``}~!D7RzkPF?E*3nEH$ z1At|qSxtYcvAFiGs2+|QW>gNbxsKLouC>sd`OXdbsdCXW$w&X0n%J-FqkKnARbk=> zXqlF(*OlV6YjTb~3WQs1V8kw#S@6&K%Jw=@!aE|l=2e`#W7SgjhL#nu_CSnb^WSD4 z7=rHrsb-==hp{JUfwgxB0O)bbL#M(JT)Edaw*3L>+)6$%?SSn@o>cZ36MsOP#OF~4 zwzB+kd?r3p@5t!qK#ll$a4Ww}*7+~CL6neFN_o2If*Kt)eW9!WQS{ExH_u98lQC#L z^@3B}eXqn?QZoM6zAPNkv?g9pVuysIJzf4U7d*=3F;r^=#yV&HQhDz?!~L!suu_2b z&km!-2Gwef#(Z;>g_(IjF=YFejV&ZsjxQE}u@=0xo+u-=)?fz2)E}|lCG`1lWIkz& zj<+5|5z#33G!J}%Vbf^y+A`AOyuc1F)OHz3j#T|ABUX_g3Fuh}K%##KtC_Gsmo=Fl z{rCNVC`7cSwbo+Z7A~>u495F#fw_CuIoh}@W}0iTjLiRD8|LNMOpQNv0CuqfwR0$I z1$STzcbTWR426UxPS(cu6x{xw`Hsx0e2*7|lUTaOpY|p!p9<L$H|sG2;)kW?+V$4lJ=xw||R>Zmx6YRCYxcn-wD zH`%^Rpf+VBp3!D}+G!IXb17U2#lV98D$2H9VbXf%U*%gaOMpn}tHN7J{(?Z%j-vY9 zXI77b{}4ytk+SsTtgPOql?n*l980hJs-uOGrw#TzZ0hr@n1Rll1uTc_9S1wxp2O-8XjLc z7iSO48Db!l{0jU@E+sX@j7k>i8Al?BMzmi|&0R+qH$-pNvG<#qNx=ji={+ z<62}|NMm@)E#~=7%4cj^B#Yl{bK{H~kXODs?3NnP%>LD(`IOL6NWd7|F`5g7%4+3W zJWk(nZ+~=6PY24re5orOtIo>$jwVg#ar4k|{^BbnI%rlgMsXGjft|H>O*54G4JfJ=;!>6MBkAR!CaNY) zrF$#j)4Uxe5s|JK;b%P|rxK6^m>hAiX+jKU-Z2?iY0Y~A1l zQo0O*QEaV94$INDuxEt{0A6nvLMO zE?DHi^X$%iRbUu=&~Grwxt5q-Iog)qOYc%s0n)TW0GF$X5(5sv?Xo+cRwLCqrH1O-GVR;dQuuiPF>lQNKeiNJLiW))9o(7n-E*y&-1!B zaAAwD_0-RF!tEM+S_9y*c0%IEmYQx3IZk-|Eyfo$s+by_j;cOilVg61AWdjtsZxr` z;2XVX?Y^SlSSfWd^?@nj8icqH_HhK_J%<|%cZ$I2^ zdNhTo$=6?!zyUf(NEa>odRPC1?W0zPPAfg#_kSJjN!9z=`isujY`@WO$KMNa7mfkL ziH%1xY#TSg?S^zUk}*)1^5EfyKE^2h`9my19iHe$`*icnH)*QB#afg)`%O@3v{+v8n)*+f z)|AR8bp%QDW41fJdQk z_D$S?@vHK|E@*LNCk~Q_OcJzA{LP$LKUwNV5Hk%KBX#{k$PaB#vqOu^TA-@>D%_gs z=%&Qx1$9OP5ql`5om5J1-i(ej{7nqYUfju|muBmI)10_GbD!8y8_5D%m~`gC@^b^( zch6v9Iu2Zrlh1&TGmw|*2=I2&&+dX*LoL144USVmHEO@N{Sgj@V8M}U6No>Qp12b+ znVedZpDN@Ik)2&rw-N1tckKeNph|7}HmLNOKTYlj-|ErA@G?NW4L+Vi`)_7S*}0K4 zaIwR^U^)i~@f9Eb7}j@_|DKk;QidqMOV2nwh6z3zsr1pE@2tJrA&+nR4NTQ~XKmic z7d5Dhv{L~sQXp1s(j6M3It;FnQna#EKp)8p?nXQi{y4pT<`PO?KEti@@?ppLQABIf3w`*sW5pBXYgpyTZir@K-FISc`SB&(le!wvuS-P0V zJ=i+zR()9Is_Z~|sBZNogC5ae4Q*SYz;Eu5-KkzgRCN1h-~a#JL^77d)djPLkhnSM$lbIz;Jo7PHT=Dt!e zDxbTfHnxNS4SCwGn#1oakga+wtxr%~QxD7KAXrkc2m{ZU=y?WXq z6%WAQnPas;ZKXm$PW6p`4}cQCptfLTbe)30C10`E!uL!qR1Qjss7-f)>we#-+9~-< zWJK>!LRXV=3^8BCp%$~c2CNYkJ|Q)aqGz+?dm!{(p)$9vl}rjx0fWVdrJc22-p^cN zk#Q-lZTk`&6s4ZIA*>7#c&)xgT=ei}l*Aqnr*3d~7&zr?3qhiFm*8ioxl6cKzVo)2 z)m{Brv1%m*m{f0qtDsqyC8H5-;4KSAw_8RzE8G(G1|ES3B$1vCZAESNC~{u-;{lVY zwT_58;uJZL*l{@T5(M6g%`1ct0w^DhBY2ci46(u`ht`^EqQoTSA~a;EstoqwU-vq- z@asg$Kd;RJ$vxB~J-JbC68|=*IX01758E{IHF8Z|6Z;)SvbI>sFSfk365JX8bJBv; zw7z+3*wI^HU)@)z(lt)9_eAMak05pW1U-5ye7C|#ARt^x!ja(GnZ9rNR}ETnxS zgA|eo*r%mRZr5~B{(V@YRQ7_AOYj_g@<-GC?I`PJfxIyRb4^{b$yl-K%#uE347(j? z1MbSsEE}`vRQwcn40qkSxN2c#Lg=3^GT#fB@j&~py=n6bDmuM2L0Nx#SaXss=ECmk z4p}`hyFGXwh*MS02ziIrZoPYkwot%54fS`=^0B)+N1#5m!ElM)zar7k1fcmeE4z5b z?fxJ!aQofClUB4`WkbL^_r#1Q0{LMd`lQ^P{)m^w(2gIZ*Kqj$TU*GrA!7*)xz^H} zU{JMSLX(A%2~eGn18O+BIT#BBpbHZil(+2u$7}HZ&_E;N>nNX#)?XFSH~*4Vh6cXI z-S`dbJ>fq=6oo-FY)FfVLMuj>nSb873%whjx**()X3)XjQ=T>n$M|VDB5)fp+g{CQ z-L}d+?cHU$BFWv;ZDP9Q(YGcS=rB#`%@tzQ3B4cuf2CMmpe9)2`8bB}yQrFAS2~%b z$@&xV5&5eTrYAv|fE+KSD1i@c_(ANBFDz88il;u-t6P1UoUKbw@nB_YV{pj=-Q|F#h&3o~(HvH@mN+|ZW(x$OGjZU)Q2xy@b_W1%#!HRdz)V>Z= zF%T2acr20Yl5b_Gmp#&P4F*PMh#e`-Z3zsF7;Dd_`lMNo%|Vx%BcvS zvSm)e4fXgJWcE1m)K3|C>7EHgRtht%OnPJn+@o5*S^l{JITu@>vqk^fxArg0!U(pF zI$(flwEOS?{N8B&J`=F=77k?gHI)FX9zGD?1GWDR1PJBjwHJJo8q<@ZXA>sfmr)=`Mx6rfaGRMr(`%uqT@MxOkGQuOcL{rVZ*Cs-Hv@)kEt;n4 zm}b0`tgGQ-3>=hkkNSF2B%!dbxSmHuNyOunZvl*h;nPfjJxfmybuYxFWloPb=&sRW zh~9&L`->qgIY`BhWiB+cA!prWp6J!BlM*Y%VxX)AyC2Pq11-*nDifzOCll6Rrq>CI zT0DHt%6Vai+n+cgb{Q4^>rz`!T;j}uzJ9xo&9!qS8XS68ji3eZibz6hmJv3+vp;ENBd568d4JaEP-{%q}1i_)7rv>W2 zFL4^6biN4tpKTEi98#MoLWriY<bTUfd-WrX&Yy2MGOpFB=pz>krk-%>83_G=6$rbs70_ZsPGV} z66%c%q$T1YIIqJDAvZj|Lq2cWr&qm@yp8r+&smJn=x1KJj3d$Voi)->mlVaJ@Z!pE zsEm`gNQ&QkeLHsJRZ;-!td+Q6&v%C^u6q8$oX*W)Ys5q?e82tXkL$R90{95zos16& z>EV0{9d(EE+d7Ph74ih!3QUEm(ubOlWd23_4{24nKAlCD@6G&R$wW(E50*|g(_OyF>po%KTX zEqOvdXitF+Ih89VqpAQP$n9=Z+Q2*iUV|r*z;BpKo{PbnWbe4tLaXlv0Eq&8Yb!JmIywS_Pw+goTw1x4QHu9d4?35m5Vdc#st${Y790}rj$Hg7q2^vde)Y;u zK)>cfkwAZ?T#pRE1k{}K4@_<>d&7iUP}4f`7iq{Ja()*;;> z8niFGCe~LM^)M?6JDzrtNfzZvO-_VT`+|W6o+*AHV-=ZCb~aqam7lOX7(;n!_uTk-G1e8mfZ@H1hZ><-B-urMMekxILcOz*)V z3geDMH<27R5}#!MJ$a9EGK1e=sVIC_0RaJDK^}-L1l$z&w{}PQ!|bJo%d4YzE5509 z6hutFzM$*-*liXdd8BMi)>Yeko%M~q2@}1&s zZl0zzjTV=s4Y9|mVwpM1>i4c5!kV8hI&k5^VBH{z1#+5pe&H3@3@<#70{BTrSD^JP zgB-TSe+~-~Bs<}%wQ!0|_?1(vp`7*r&WK`k0*clgvGJ(l!8aUtxNDA`}dfQrhoCO!ZU@)P57 zF>cGJf-JLtnFaZT80-2)cfM9+Cm51(suC$tNkGHqi;83*bNwKyeZhfo>T?k>?4@zcy|)mA<}8q0mWg3qJ$$*46|pW^SC3=$X# zU8n*kSv092-~jiv#enqqWBbjNnCa}v$Q+7K|KGpGO}ss1IaP}BiI}q47l;9tp(}t- zvF?eI4EV3MarX}fFrB{web2Gcz>{hh;pa$_2|$ltVuGmWjls|!%>60B|B-N>qbyzd zvY~DO3S+ArI~zxP*N7u+?|e8)a?v_sodh6Xkc z<^^`26({**0*crSZBLws$uuq!*m8C-6%8L{)!94}1xwXH=~IcQAFtsf^5}C^_dZ#I zN7*e6yh2-J9>IoN2IqGMYlzNwX;VP`~=tXo8> z40Cm0k^P`^a0&Kt55CCrJS6~N6qSxO{FjT?K-GC@OTodua3_E6dndRPL-)5=M6X7c zL^mTv$FQ-bx=XpATD#Za=zI*^$xE`SM7$=z@FAmsHUXnd9(mVpDgU!FS278nbE|=1=h@#H-PdG z@ZFl&5G=zm0+$q4b%ADk9bY1S5+d7?K=z$clBKatabU;I=H5ysNx=Q;mH_f3mW%7H zl#Oex%P@lraigrySR{Pb9OY=AV^I?$+6l)a*6#{d&>~E~lwB6KTCj27)wqtBPGGYJ zn0&kB6`*NMtfDgonXc6kY($BKwg~koH>K=+_#Le(RAQSQ4A#(~P6l%~GxqDT;)nKp zLsS3-pHrR#54@GoicwFjAbIq;-OSLL-jIb=^zqIxdP-?0oX8h`*L9aH22gLn0;$xO?skH_ zQ31}yLB!77fc<1^8{L1+ z>3e-_LTv|QhRNcBSz4tW9wupzhczR};Q#y?f<~6hp1&t5zOZ&xYLikiF!Lf<%;=l> z=nAi+2ZFE^#&vscCg}#WBVXZhTI;6J@{qY$(k{-m?VtNW5>DZ5tVk)S$&~hk zG!WFMd{4(8Ch%fMaVX$#X3EkbbCH8$*%RPMuA|xWWP~Q_umo8p!VxlSKL<+>8wKiN z3e9-b74K}*!E}K*RK$#)iJoT!1m=Jwp7cS&u_!_^s@GAgCR9PVS}q^+z{N^XAnr}- zMiVL3Ck|kt%0P&bqTPM z9DyCp*M}il<4vcM6*3x4AA~>>DF_%HUZ7nRx|%>wdQ$tY{2wBwOqodD*r}!HKz!nQ z3W48zX91Kf8^#KTRCU?AbK{o~1XYw>zIwHT9nm_nY`$PfqrO1L1Gl1Yv`rZrQ!yJm zE8mV%`NP$XxaTijzN!7ukesA;hpHZO>i>!b(jneFmy1wTY`LofRGP@k395E7&?R4R zyx8mbsy#!ym|%d8rt&gcjG#u~X)=?t+UkK^uVW|K%`{B>GrK|bs5rbK%=6Izx|q0y zbGj9ll2S|NsX=zeD;oS>&pKUUOtyf2$tJUO&sD6P-$zA0kt)g``5DeNDR=we)}0>A z^ww~ZxpbBy9XSj5Ah8_UOj>qw9;@~#_EbukDbcM4gS6yv^?SkvR{54`k;Mm?0D9LD zE0V3&NA%g3s-|1^VwwtXorIAg$rH7X;MaTNV2GX#DI6Sww`C>PGN6V8HfQs_-cxz* zV-~*@T1J4GN=@p!k$qYQlWFQ7!yg@K`Vj5!S{{jo2}AWWa14SrkF8Y5+`a6w^l;eP zpO{x$LJ@TpaPT;~&Ay8Gg4E4Q;NumfiM4g6&%I$}YTfcpPCkrMbc@o1(1ex8ENUIy z#!1>>0h=W@!$+JB>%r==&E*_{b*~!xD^(W5nihKG&I>%re9vc2`|f?8d{;6#1r%NRmKug*pxap z$uj?Afq%MGh9EP?;c0)vWfun^u76Z}RS`wUWh}29-LS%L_aYiCGKv5KJzZq(IDg;} z7jjOyf}@E>3t%MPk1=QoW6&yd&0@58?dYP0Z|%>@B2P~Ya`tmo9S>-1OXfhKfwzpW zsRYMsRht2e+Pz~DEtKA%x$(?x3h&6afrS2n0}CkB!|PZx^i-$t?95XV{&+;;9(t?A zah&|J>WyHAF(J%4?yCf z8}6tPsnnO(IjX}FQ7fLkPrAul+8h48%w2O`%$@b|sSGUWAZaRIMU)W=cpd}BR#)i| zZv^E^iUKnsMFchBYvmr-uzBwdk)D$^_DXF0Q$}3VVk$!h2MI*-JjdT)S6Y9rgBf(ZFvFeMV2@iQViH*`^!?UPde)Y(xs;kkK zYiO`1CnK~lYH8~JV+3W^2z!FdRfevWZj}>DN=*Q zaqnOS1$XbsZRaT$n(hzm$e|5N+8zCEHaj+dL5B^Iv+k>P6{n$#5-mOnr8`ZO2dEJE8t~AL2?4 zOm@+!~@~>6J_O_~6XH6H{A6y0XIk0e9!0TEH4r@R6Y_2kC}G z^v7L@nQ^5-0mT9RYw@De$;-2%*+WDbK`$@La8v=TUx~dKZ1Z#3OtC?iT{nSxb9z^@ zq~^whP?JZU?p(J_rq>?FI1{fIhF<^zX;SN6(leJQ56}&zjm2<0tAWHOJJ7fy(+PSx zkQc3)3VwkhMuW!=)HmZtTwKg!>3y2F26{H!P`1daRV7z;brXQRbTrMj@f`%IzO26| zP^FP}(LpZ5*aRvc{OP4Z5%|rPv{p(^k|&MUT`>IHv>p*G*7yEp=Otz`A1kp#t89ae z>vQrDbNd_K$2Tf|VRTxjDF?rI_b8k40OQ?_jbyF{Wp}!CrB~1FE-8JuGJ12-DH`t^ z-cRJE&qsz|B0k_-BWhd>Le}Pc#EI~d^u3d|z%Yr*Ex4CsUSMpF*O@2x_c~zfTtzXT$ncJ9Yl4?o2vZ7ww9^HVpr)nTSHCc0!w2*bvk@N*(HCp zZjJI&NvpDj862TD9)TuYh;D5h-Q#FcgR>WlJqTbX1SvBMm~R6xQ6Jz9yVh*l*Ogk8Az?7<$51 zg4I1k+P+Cgnql9LA0wZjuaA;%?j<1cpXBr|H;!2h7aXyyP(oyQb$_WiO;{17CAX#@ zHC`lM`C|iyDNofI(>XDpu6hZ&fSKfC=+@3a`jP-U%iC6LN}k}pmC~5yQ%fLKVuIgR zO5mM!zYYL>1;CpLlxR26Lo>aA2m%D`8nm{Pq#!2XS~c@v z0#|ivr#HL&Xq$!-OsrJ;L{TjtsAf2?yEDdIt;$*2tHVd!7VA}BEfmvq23MR(**OyF ze{j5%8SwvWGGQg1s4fwxROds;^zaTF05oGGyI1nT)y!ntz*LFrrse3vKfAVR{FLO_ zVPTd^d#_MbM7df22%vhc^b+5Ev9W)$MFsdS1YKedN%tYEEm?iA0j^7wzUy!Ou00>= zP99)S1qBWf$J3otO!J7$IGfAl5)o`3hXDI`0-GmDI)B0YZR*PtFCRGU#=d25vGW7;osrTLOVYCHW zeGqu1GIBEhl|=p+GUk*g$2t=n4h|z?^tA+1DahXTVbjmunk)liuCqMBNAt9g+oVOS z(J3VLhv^8{gU2f&v`*i$5=eJ)sf*8;e$~fE9o8*+a9j}_)Ql%fmN=!3CU5Ub#s}<2 zo|B|x-ax1UC%e&M`b1n$k7%P!(A=+BH#ww^v9Px2XOewcd2?#f^w9vadYC>8bF|)= zpR0KO<+;tk(k76tgz^-L+HPr6@5mj|=neRrD`MyTa!w2Oh^{HaZH zhhrZ@uT1c%4ns+P*h-{fS@;-2^_xKZNdJqNf!wn2q}ig&M~da@8!uUfGa@{ zB6pKOt;onZ4A;-oh)|A{MP8|e<_R^r_uLEK(2K%hn|h7t0*c-{e2uM~VZGLJYd@TY<54RzaO1q~_N$!`yYtYqbIHZDL#Fo;WJn+JXp%iRt(TGqYmK4YD+{bV{g-^{X?KsP`hD zP%GaC;`VsZzOs}2BM5Juw;@4 zN;rMf7a=4XSENG-KSD&74H9en?`B0Z4eV-c>NLy%X#Lj%p|qHsFEc`9nniF^;)p zdSIAMIqQYH!wtA%IGa1qm7c8B^ortG9*p83nD{`V^Ay?q57MySYTW5er^dMOwFhBy zPEbfejlDsfZ84cDq)h31x(}VlIhXU*Z=ehDfwz2#cSS56I9y)F8zXD4>>Ft3P4N)e zs}xXD#<#{zUSeB3r^Kx6Jp3^__HB8OEFJ9o*#2t50^tQUUm~Fn{pu?ulenX@Nb7Cy z-7@sTyg9}WtIzRc9^dHSu&35lHp%b+C^Sa|K^E`azxXkiw}xrxRZWTc(8K_-weBgj zwQ{(g@R4`w#Q9wr*g(O29*8(C{?5mXnLl{}@)Q~KJk)!!0Mz)!M}%b89s{GpO*lmD zDr)q4^EywEFhlW9xiSl_QFGb}jT<+AOaA+*jJW&dsMd=9)-L-4_R2P$pOub&|6fgX zky`$OjMjZ6M|{lX(H@damOEQdg6jM87E_Ja;UBrWZ<;=OUK#fwFR>R8WE_@;%2UHT zSAzv6n6;iz)a~%EoV^JuKzNTslvQeTN${$gI!`%(fA1I$EIJFsB_&~E>Tf2~g8K+G zM*LH~Hc~u{)8rYv^N!A51{Uo6(b|^)jr%Z$Tz}xrlxvWBi3<;$k2 zhq&(6fCf$pF~?p4n?OghV|I-=lT{+8W-?ii>G8^2#0tDt;RN?WUr*>f4!TeJ+7U51 z(jKlRm^Mird|UECp(gIq{jR_G|NAHOSrHZ)(&_n5h;6v2iATrTj>{oo+4$>yHznvq zo}2rl{<|CG5jt%(Qr>z+iScD=`PD!*lTitXflhRG??-`zpI-5N0Y`>!Uwve2+{rL= z%46Hrc1?_5vn=!eNj%mslTu|czy*=eVL2$pU((4ctYN!TEjSY2IRgg9u>k%7ikiPh z|Ez6cxOOI&Z7-=GU`amBi`FzO;E^OqR``uO2J{oC`$lnGD?s5w!bQ?WXc)$e z^3(7S<*9yVty+=AqKMMCU(Nz2z8u{sKiQP zkB^!^GIg~=K@cKh4gr?M-l*vD1Vas(fJsQ8z9YrUu6|GXs)IHpe|HhdEMJ-xQ!5EF zGG08=;@nT;2G}8sIF2Td%adW|n87@S7*uYb)y^FIM}3k) zg>`yDW6zFNeIUb?aGe44HU8!GL#aB>_#1px&>pjpMG@=5n6E2y`^R|}+tDnJ zZhZphPXIQsv3KZ5zcx$wrU0dSv`(Q*1d=gemvSRb= z*Bz4^#D^rlkS)VVxA!+k>3;snGG=DB4SOp|r*%(1dijcpIm8`nZ8&x@5~0hChBfj` zM-ZJDdVGgz|?NWvWh7Cw21}sr&B9l$aaRsnU>AyKrsL;s$u(RDTgJ+K53JD&{N0HV zY=rJ_K7;fPqXr6kH5Ob1i)lcX73(G+wp=D>O*c_}hqbVA0&!7`X2=HHDgFdN>RLp@{^SXH9&Y(dgPhOw*#HXA zy5IwBmTx%JvdDDP`{ZIIlr5?IrP&tp)5r95nQ(B3iI4na=Gf%>hh^1+m&OQAQ=?Uw zUAZT->ug!}7i;>V5ceUPB!s&1;1}lNKPRm?^?pq5nT2(LE#!M}vPySuLs{H$>ZR1fPF;1%{EG%cn(2m}Dwk4K& zKPPn;kj7&VCFbgd@g6Xn!?!hXfGlK&5>dsf`4$C<_Yn+_Wa$!FR}T zGyof{OP&~dar>`OL<-o$R?sG=cQo?BfI^l$wvO zYXU7GQVQvHCJ}*%xrK1E5hL@aP=6r)X$>T}lZHf~9MI%K%)E7Qln-cuHWx{6(QA@Y zAYkK-)vFXt#gmA^@b-J0njHk?L}^>KPDTIzi;$GQsn$F+DD@@MmgT|FXGzL#Qu7kB zwXM|-zVUs1GYZjQ#UgGP$G^{If%3mh_R!gCQM>qhP+qbIUnH_1)wyBoX1kUSso*Wy zwZ_8^x1Soto|m|uaKsU*%+9hc#89c@z@C0j+03)g^Aepm#;=J>%l=2b`fG%jp0nUd z%3>U2UWYj!uf@M$8ZCr8GGyc6L4`e*epazGUQmkMIOtJK46NGj@dPW_1SWnay4Ge6 ziAOk*TcVYl$+?Ul%M)Rx097U7%E1bs<;wJez=UH4!C)zkA!3*hq&l{D>orl9)-Q!y zO-~e4du}Drqg#YY&sV8K9PfFl*i32r@yq*fS`pC+Fz~-yq%2rb4kRHSsc9>Vjg-i49_~inQ%$JEc6{ zQ@-~9_ln@-&t$U0k#>k{3FKroyegWLx1ZOt1=&UQ=2=95PA>)9FybwmL3J$rfxe&< z>uZg(x{so3x#^ou2#{kIju<60C5cAm9(m@8ClhKyU^r5Nc790u*ift?IOFz_8=&&K zGP#?A-X;7C8p42m$tLhlyeV9VDj7@NJ>H*~e;SXq@d zS=v173a#sJEGC|bWkYm2vnaGe1L)`}JxS`t2fx%-@;1cGU`Lzpp3i7euU1H%o=02Q zm!EetIB44TVoCuiL20-~+(+QoPSMwbg%1_4bplt<#TdO!a5+z2Q(YHD?#QOiM?X6X zCgUeSO;(_f3tW()C%PQ`bNrHScBaobhLt1=bp$w`yc%pXk2_^#M^ zjBaqLWa@AR9TWR3$~rDmSs^>pxZrT}{sFdGWuNkE3iL3+VVmf^D>S1Sr!8HJkeICXjw_vwfL zo;op}64mCWy<^3g+uvH~VcUxBxUSib{c9tJiXNDy;YoP2U!L<1i%A9V6b>XM&&j2& z??)}EpBz`*cg06=yw$yhshFQd+Yd&11=dVu`m;Ms6S5Pn&?vwP!8dN7xJj?!b;VQp zjjum}nc1!=g0~$B>^WA%T+-T4+;zDwiE9(zb3!xERB@=YjP|rHcP^#ubys-4M72 z8BfXSh)Vpsxw$v_M7F*I2j*(z8;P|2vNeDuIqe8XpG#FZr6#>ixYHwIs$bO=cUTj#(>FdvW!N*#vozX0(m2^Qh}nbGV!9o3BlS zpVh#MEE|N9pQ{mf+wdkq)%Yub2NH$F{{Q<4{Xr%#aS5qm)T;A!wV4>;<0a}|BuIeM z+yaHk92tm+;+LLYEE6&(3mziF{p*_gLvF(HBNyK>BW@$V+X-XT0qrdm%?Dab{q&d2 zT{;1z;|^;~T<$;=CGD!|CgJh>R^O#qZGpr>wYl1PZ5QoDfZK;Px~i8mO;`Y0Jjm^P zZ_}>lTZ{;CaK~1nWC$iYIk=)Nj6KLGi0rj`0v*ipi&9(aJ0BbSd0Baqr`eoh{=W1^P;=kJJ1X5`^n3V))dJXgZF0wdq~^_mO4W4JkehM znl|*}(tS{mJWgyBVQ`@;I&`-Kk!&XoqCne@a2(3<^;4~xbjcMqDUXWnk|HOf?^IC9 ziZ{}=a3*=2veqP7&2_n%79tuK^RgYT+V;G8KC%tm; zFS1sR(gqcBg+VT|gf2PCYS}2<6&87^H)9nbzkYR0U_AFym>PZRrSUgvcf;r5!j=~6 zO&S~E9$ipQ5(oUH0|jAOtB{O{fU7x=D@QrkncFkpM@|`EY#^|goI1!_pnL#Dj0jng`0|D;Mvo+?0 zTMsA5zaxWy4xXCaeuZD{v(!Wrg<9Qx(o?LO)vRyj7i@{w>i0+oD%a>`J2U8>akCc( zbFAJ9){kCb7Rza8{I!cZ36>+E=#TLDOm77p@5P#EmpB~(&xteMWN3D>qfFuy+|wj# z3ax1Z{NtyPDPt&#it=(LLXtlf*IQ={JtI%fcQ%hu^U-;{5Cm=S?f-S@DG?ni>uD0P zem#mX(kMP$o^ylHKkSA&H0fjaZKD64$pu(0hK&&)KWW{r?rt$WGLvv^QG-@;y_TUG zSKUjpXj#>1jhTdFsLb3m$peAZIL@7Grkn}Ufe@>V%&UtPA2|neM^JEcfC3K4P{_$? ziZm_-SJ#YLJGDb+{=CoUYq~@^h+bp5O(F;v9?w-~LMhpZUvvzXUBLezoB3}W%7~&F zqc$)|9S0 zY!mnx+ZRX2O(b}lJQul{YpiCH#O_>AG732~oyD1WslzNUDY0& z#|66!%uwd3F(U{7iSg3hwCSY?;;f5DwoodjX#g;PJ}tgCc>tG{%0MsVi0xHQWi3e! znmO&UYPNB5VX5UJdryF`8+ga+;H;@<>iCM$3)6>I7y`Oduz-`vW`zc*Hu)*H)Z?P;>FyjI&pwzh|QVIocZqt4G%|DU z7t%P7*1T^f9v$L(Lh2Po>TKo_Ysj9JVPt-R^T7hgXR)emqp;Rx|6-eS5w0(ai?{Xy z11Jg?lSlF(Y+J{3%XSl-M@moLiHpViMx+^wjo(w%c6(E}%4{Br*|D>?%yT+}oMLgJK#@9n8*9QL&3`=W{z_N|7ccw_o z1pu?t^tv%RaJTL_Fb-LJHwC`Dc7l6Efc;u9=~1nYz^qxYC$*oq-jyefgTUe^Hm2vP z3DtjJ2g*cc_pO?4u4fwEiWRix6^eTAqJgnDJ3am7nTq8hD@#gG(OBPq!VBx}6970^ zgeUUxxNMOvksX6fNuxovpMrNw`z6a4$H^2zZV0^oOwofa=Co_!M}lR zmNk40Cn}b&rW@!x=n@B4M=-J5Yh|Zj<>|y@!vUzIVe@(Ksw8B`c70q zBIBaB$)024;wIX_O05F0Wx3`!i5hryQ0mSLtC59K7R>%T^Jqr5Jcm*mo3hu=G!EHQ~$n$AZkzkcG6v2jh>BU zYC!mI=DCopoJ1hZ#IKT1QMOZ$>$UqDDJza@0^pi0_*J|$j zLZT0#e*?+hfR>8L$XiZ_3S<`W%mWF)hk@$Vq^(b!^PJDy`1CsbRA8AzQNO_yd2YItOmB9&7`AAv84PXci?^AxDroPU-0M;OWPcV zv%vRuFl#mVeIriKd2NjdBE(_R$B@$oB@`Tn?whf_M3u1>MT!HEU-K^NPh`FS0z;sk zPkk4J_HYe^8J36v>>Kh8jxI!N2AM9|7bGus&r+!MYzmrW#%>(nQcZ*P^<8X``gn0< zCP)R?u5Wrw&k`NK=dGoo0mPHHhLPObQJCwoFE`NW( z?J`#o*e;h}WHq%Fms+#($LmH)XWb{05j7!Z z(%muEl_Fs;S(qm`KA|G!%Dr7G6C4NXtED=HC}zLiaUKmEA8{zCI;GG5R=j_NyFx#* zt7qX8kO+;e^VfwSxfwN9cMJf-RBk>dv5U&{Z^%1KJ;Bn`kQM$gsKeboweH*kXQ#G9 z1jpg~T)|6oHrPkVpm(EN=_M+eK}0`EFQ>9RYCa_8x2OI}ofO~fQxTu*f>Z+y(WY{5 zDO=?d*UoGFwa!V1TK&IU4_g~lEZMjp2_CGamuytMzV_CRc(Mj=ZP5s`f{Z*Pz6xaFOCQ%$DQr#hqV{CIDu48TDQc|Cql!Df)S_ zo=}z49pOz4a=adfT#^b zue3K)|E7Z-BkuNFN8HwX;nHz)eUpYYih3k^flV|?Vnv%Wh5u1K63V$jb4&FBygY7g zD+Ry^3?-(g2l@&|6tOfi4l=MRTkw>F*da_T(vqwh|5ErVW~4u5gcmk1{xAMHfn*Oln7Nq80qAT2NHgtlTjVOcrCivY%R!2&5Vrq8|4poX?rFUIzY6CKr@ElC_lPcw;O?&&c=^C(57tRYkg>|$a$LPh~^5c3f z7o~<&hyVbV6+xP;NvJ_=nM?>H|Jcw!;8Rc1!vzs5)`?)92DQ$%{x(edQR2#~$tkZ^ zAD~hCJz54mG41^A;?Uh74mo^}XbZmPwowcLtcWSidz9?9+@@yb4USaO^XN~Z$5%DJ zR8@K7H&$bNY~D`+V2DCbthJ;v)k-30ajXxU2BA@7TioqZLsD{EbWyx1l29c@i|#5` zA4}&69aKN9oyO*gMynvMGjC!j88KV+)L=8Yd3*gu_Jdw%6J(s=eIV~*mbO~V&0sc7i-}^2`>gt9scpf5RG&aIah7Y%9QZL4EyJIZ%mLZD^W5|{cJ>T$Nly}9b%?RG58P2Hn*4n00RI30{{V- zVVtXu6H6e2E0DebXc$w0VIg`r;SK#J2;8d!+I!P@2~DECq@XtK=X#$iD3-e!t)KHu zR^+;&%|6c+aAGrKchA!1jt$D$2LTP-DL&@WKMqRwf`{LHs|97YbE{??tWfh{Q6hE0 zKE;e`Z4!pf?jUppGGXa5`En3=Xgk{AwWKZ!%4r zmWM*f&$Udpn$)}IT)>DK$L>TfU}qVEY^$FoVq~aN)hBY&!d#tZ0?%N3D7!Njw0uL3 zjO-XhL(R6P)5!CwjpCtnzZpQU7MwOL(=FKCmOut*r8Lc0g#28kgXSPko0dtI3=vm5 zd5k=2LxTtRSm`p?x%DVUK{82}axE>xbtoTyJdc#E`4khV>5*p<9vp}uUG|apg}U6X zD52GH6^c7~R#p%$lyj;9h<-3Sytpz}WV9!>EI=d`25T0@jRGYtSj6r=Lz#JP1BAZ^ z2_!|~fHl~b=&+NSY7UiQYdz<>$I|GjacNMOA)&di)Fkd4Ghqnefu39p`-aLSo)+D& zCB3GOe#gS?S{D3*fB-sg-G=~5#A_Gj_x#0rTyEOucR_G!aq0O_sNNt}#%!hIk-B2Xz?R42+re<^06r3Y`c`X!;}Rsu zE;g=wu?{K=65qk$2b}3|e3b(&fPTBTs~(1u{f)ZZtUsXnOnLTi6MuI=n34CT@Za+c z7M${@(-hk%`D7UxHGZx0PjWE5ik9}D$R2aawC7%-RjcD(sPmAIzNHRA zykRiVnt$JQh=26aDQ723#u&o*}0F%%?t!2?*)h}M0ECm)E8)J$SYjq54Hsr3n4 z(|Syyr4PuBfK4lO z0H^O~^u}&)AaoGX+aokjvNmom{}=tmKld>X)+kST_S}4fwgO}Aerz?4Sol^F{qG?e ze(b=QQBAn&65{%edNZ*7=rNhm{sD9RLC-t_#d`5Ny@$qKL9w$Q4NzsA_RblsA=9FWxu1HXZCP;RNr+2PJsf8Xqw!wlCCaG;-c4Nhit}<^!L!g&P#6 z!^dHmx8uP*a-uH;gD8Zn@4G6e@qS-rLF`ie>4e##35q=9+!E(kOBFU$++Pf76rmTy zc?@Dl@@o6!Pz!rEfWbMf$B;vhbzXSSGdK(7hInNhMq-2m{nRv(E6?kR*J_|O1ll*G zegWy{?b!fRFH9&U8)g4;dnC=ait$+bQ8-!Bst1w2UQBm~X1H3FrQbyuQ>(&7yHb1Kf6_gODPP4OST*|i(xgK@iyCAYgH;Tf zy$=24I>Io0CzEjyI`n3y>2s?;S(aTFq4e&6JYS0;mhGIa&?CW1-xBc`sR5}VdoxVz zVT$eKQed)|hAqGs;qyaPu6ucr6A+&){fL&S(YYR^JM;Y7wC5o1O8wkJU|kkD)+4M7Q-fnJJKF<;hj$TIHv(UayhHkN6GacA&2{Yd%H0Rw@< zoLg~B_*ti;S~#ltId_icwTiGV7Qk{OPJnkFmOZfv_=}H*Y{kQ-B1ga&GCu%{s!OqI zXzpMtj7!?+V1p0_@R}e2HPaFTX9n@^#M$T@#}1Nx{0c6q&Uby=RN6EZixzn9Qs*3> z3AX!nTN%L(<}RW!u3GS;jv>KUpm>tlbQ}7FrzEe1EMlZ8MZu5u7fzn!=|~V7-O`sL zKFZ%A`YD_KZEngp-FOlwO%`rukD17J%zHl(^1)pXY9Dee2P0b3gAJDVQA0!llazNH z_eXtJz0RM$Rueo-2~yGXgho)QB}g5a>aSJ*CKQYr$UgNt#+gxGs8lgQPP469RT1mk z)i<>mZRtwMHyIbg)c2jQGU-RdEaB%mHV=FKaQ=>j%-j zq!%-88V`Ad@&y?A2rvtvsqoicVh3m<-`IpxQv1Y@@;@_kpMwMskOHrN`n#=ltV4^F zDo2c**GT0Rw)(CM>zXw;@fB=e4!DjyTeD}Qhyo&O!UTz9UCd_*FQAJm;};Vh8HTVY*UYoB!O$lTu3(!4U|=Td{h!& zfas>6IaZ($kn|&hc=YeYrdR8f$~Go(>GuM@`@jgVtIY$Lt}Puq#)}|Z!B_xi zD!LCx{7|QwQ=kb!G2;+z;U<9cXtr`8+_(SE9WBDECXjDKwA7x(=a4Z`&0YPqwwP$3 zEVfV4d`9?ox0-Aoue2e!BekHdW6JRR^&K3A)REH>(pg6hHzij>Mmuw=-FokJFHIm6rZFpsy~+21)hL+be|>54^?e0_08z%WH)oN zk|0@~^0c9(lmfI2k&~3aXRVuSfc?{{VwmkZ`z`iN|C;s{r$a>3=yxNeN{qd9M`-t* znwqDptLFeMT=&!}pD=3-uvh(8m-5aC41%;nCe29$e7b4yG$2B`!q+rrgg%P5Kk^FN z-!vh&`-n5F002pVe(n!v<&}9OU0Iz)F2^-rg^$qLBmZ%;X6_6WXKj>h39qtX-%uF* zdF2H70l$D4K37BfRKKWM>S1>uCDIF1DqFmzvf}sqQdkA~&Rf696EYvm23Cvsp5(I_ zUz8PQV5FHuWd343(NJR@ivR=?XkTm*z}3PIrc>&U2qIThK1EjPWvQRz@<5|D(<7M5cF1(Q;1eap$Jtax zX4Jk-TNqJ+KTy&tCM0(iM1pHO;zLov6X}t0uOjlJWt$H#nahm#Ew4j}^V1T)@`2>If@`77_!@4_KUJsr z4-JgDhBF2D@_aZh<(ZVT72)rP;!<`Cg4s%RXh%rFCnX%V&4*utoDM9>3z(7Br2%cQ z+=2#l_W8A!du6h&WysN-1r}>vq~=;JFvNeGnE;=)VH*YS&S^9+8?_+*bTu!iJTBsk zT5t!l4_a#>YawQ_k;$hKa=<_n_!X!MbA}tEnVs?qB zhe?uP0aYn8$LVAaH2Im8viQ}iTmQjLBu1`%W27-V>Yn|PmnSQZ+H9LH46qppc`@Dp ztz8%393!5H3@iWARW`Cz3iQ_xRlBF{l9^_|zpG25$ zguLF0YqaToA;C|)1xKA1-+oh2*~-d!7lIC}o^nALqM+@^FN2!|>8Jn?L7G=`xe7+% zBc|t#4svyVAlA8*V8f@1R7jq%f%ksOkm7`|MAXZOK#bb#DJV3Ua=;W@bfB)d*RfXXK>0p0Yu=0P_y0^kUi7biM}KVcQ0DC2ai^ z6Q<{-4C;*F7iGsFxTdHthZ0k3`2;+LO3Hv#05cX zeTJlOJBg>^0Q!{-l9RHEeiXDLJ!tsIg(YRdgL^WLjI>Gg(YGA!xkV=r-M2QqJ_1fT zKSs1XNmbNXaZyY=vn>Hg`cM}0?h0tw)%=$I`QapvP-KK*vRvG9Uu#{GVK z*lSrsEzOyZ8@kS)2JhRZouV|5t0Nq7Qd0l?skFn7^8qWFNoBITVa;>(WAriE`KczLjl9bF1Z#BN#=q z#@wFlx!z<_3kjNz0+`1!^b8d*#Bv$Qniw&QK@9an-~f&gTl>u;O8#(?Id_D<=2HFM zq>~?Y#tOHWAYUJ;yU9m#AhoaYs*7qq-tT?~*gVR5tD+sV@!1!elygplJE^Cb5h+3O z5};l=W9avkzq;Dcxf?~ZUOi9sLK-h)l-=*a|()rCg< zH=B6_yZUqam1r9mgb*3jmSFky@duVn{>rv8^2!=7kkU&wYbM;~< zia2>qYa;{cP$|~kJ*cL1&98zlDt-4v<3D_+DP#q}8V3=dGwUWq*>ym5`%w-%X&Ok&5*9y=4keOZUj^R<#S)RZ}X8leckI4+75_RL&+CSQ>F zQuEHLgKA+EHE!-NBctBm(aa5?Yt1oo84_oRrUTWC!E4W`p#ub~UYfvPz4Z@6U3hVMGnHlE?oodGsM> zM^(nG{14z}S87c3D3&TxC8i9r755nT06mU}3rUu8u;V7`5_Wwgpx}zF`#`&^H*JCR({5^bf3aA|i|+K9-|ilD_vGKml&t{iE4$3O00(zQBiFc63q?9}xUZ7<h2fl4ogcVsqObq%$J*cTRd(&%L>c`AzKZt|S}1w*FDs5ZrgaBu}cI)!z^ z2n%uT6PJ>0!_U3Ae5Ggk7LUdw{&;B(U~fO;dU4QVJ{lPjtn|4Kr;q1bX9=x^*5hwk zy-$U^2!~*`sKaA_D)O^5B`lah{juBVSO2VX=gKw8qjq$Zn;TRA=^|h`tK6r0w-O3% zOgeAqV5uT0ruKAOY>y^F3H2POGYft*-(yJ6#qGWbx)~t}#)VFFI_lYAeNp)3b zH)GOd#T`kBu=xtKrTlIO!hF{0KXNu!+6!QZ5@Lz>q`;1kpZb8 z-(RQbr-mRFQ_xg_Fq3{Ryl*B^zrFJf#j}H5CJ{Ez7r^TKXp>US7R+nYjU`x{?@WDY}=~*5?6X@xg zg08;$OJvyE(febYtZK0MUMvX_OKjrcBMGzGCm>g<#vDMJZwc}fU>YGYRau(ZQX@bz z)2ipOf}ta-XHN>Cw)SpIoWg&k10sNZ`Yxdkc6=GlW~4p{Ql~nH_@h~Lrx}}{giAnQv&P$7H}NnI2LSh zu|jyGRGSHD^aol^lqpA#>2KF`{Xw0v&xnoP(oLmOH<24e;f=UMvIRH z(6TO54T=F!IveTx=h!baKg{8cnd<)4J@ocbOrgExWLpZ59Zb$ZkLri2T>Pgw)#3IL zSo1^eDWN#j?eU@8^%=AErn^e(TRjP$VFWGx??U2EO1dY*AX|7c@_7}kmA9Yar)mW4 zz96(HWr#c4(Hjy{tFS}>00RQh94_mBK=O_k$R5$J&qyDV(87>dzeI|~fuIBeyU1&z zd0uMzmIYaq<$&fRb$IUZWNnKGn;13tC-!gT_b3=}j<_ks%p^&n+S+|abkhaChnX_N zx<%&E-S&l07YO2X5$SV7*|b5<5)71mA`j^x;0est!wBDnnCqGZc_HCn>D!T)It!UI z=KwQ6%)jguFnXIp7hiBp@Q6zqz4MQ$I>vpRrm8~eZml?LiMi;st~>Sy40(Mw=T2^8FTpAP%Qst+YgY&~G-Lur%F6glqt=P4}yRl_Wv?t2Qg->TIck%zRHy>uu7 zVihY&fi%OsoE&1-yxnB+DT}08H|rP`=6!1GYT(2!eXMQliNGAakcs);qc^LGl}JRA z$YHYZuTuAihcqT)(SXx0&z*3Bag9cAR_S>w>i6t`B9b%aO1vKEOZj%4^$c0!Vf+?F z;L*UTTe-k1vr4zKHB)iuLQ`Q(92SZH-U+o0M=6W)2jK)6y4b&ww16_>54+y^rLhg4 zFo3Z-f3M{`Wz5a2)dnM7(Lva=S&S_r)BsjD%q1Vl`M9lbutD(wpf2)xJsynGW~AL_czSCS-$){r`PDi9 zO|0g34gIg`M@BZ2k$oLC(nCB*C8bZ4R%W;>+tFGpFYZE|O2O?5pu1e%nq?2rHJX3_g=EXQnC@Rd8^!r4 z01AZjBV6=7?5k>fZRpEi@hySuGWL-d0=RP9AKny{t;5RF(()_VCRJ)o2P=R{?<7WC zKs4J-Nmx3mFJVJISPQnU)-It}sErR*+~KC4aGu7?W;0ypOAfj3Z+m7dIv>+=SM}m3 zj|!d(%EPY`UEHCry)cpDdt~?9poHH2p z3bsoRceN8tb>v|OvyQ|0AdD|G^Yf%kGY8>UY|Bq@-tTYRU?b***q73Simeuv$p2@8 z9>0665lsfN@sdy_Ih?}xLaii_1+BUsbrxodIiS;MmK18m8~X0BImh^V|7p_uefQ60 zRaHaV3D*R7ngClh4`4TXAx$3J2#eo40nX2_;#88LeylDb@b)22SWcL;nzS7PFbjr3 z9jDQonZmVk-B~Y)TW}P@H^Z@7RTRA7f^#9ORO-5@&;eC(4c9mjonU$^6*pZ7Y8~vf znGAmH5AF>>Rd42;92?XSVV91<*-FqcY6)P)tu##!8le%oz);4xLgCj7Qqt-sELAB;RzZe>e5x*``lVX8^};| z<7${Is7EgXV0}Q68785ZEfi_MuyMOr)^_rZwpp{S-v>lt!Ph=fdLU4Np=aNs!p4p; zxBnTAL&8kImRAg7)%dV;0s~ohR(>e>_ih%woqCxYMO-qlE7LlhExJ6=90>K0|b# zBFLoMC85Lj)8v)g=Xp0@QGHB?zoRRUAxBQ}&jUGQ$L5X;YJeycx}cHjtW65NZk{Hb zDzQd)=2@5DC|_JU=}IsQvs(QF(68zw#Y}*@=vgLx_GhMAwQ|Rxbiiwp*B~L_zMV-l zje3d)KA1ZCSd9M8s;50|l=-?4nJdQ{R$+>Zt~I@4mCBt^o3~Lf<=9d)EORN%e3R{{ zvCF-MfTLH<+lFyB$!+a|0e-=$OH>{*tfWL1#+Dr;EL^AOK63X*U>cT}eA5?3OB zCo8LQETmsCXHX9#5*RyeoDL=b3>3#^31A1E^Ct5L~5O6-@uaZ93z5WJ*iP~CFUfD_+MK4@cKk1?5 zrs^YUIURBA{)kME!&KRuIxzP%5>Es!3~#%imLZZz^4QM*3EO&55RSJaI#d}$(+L~y zC+Uf@@5yLQUB5wbD4Y|cmz{VR6VK{)ug?#82dJlNd$V%s$uHYsdib#`b(_V4A+D!i zi~sNB&Q}&6{nKD6a{0|w-hdkVbT*-8TjWzhGHGk>F*X8QV+G(`eyCyt358L*E%+Gr z$W$3_$F+qZ0w!z7&qmua)Yrhe>wkAwvYD(ORYWPlG^x$~n^Z!0nVPP~SxcQor?1f( zvmh(CsLPR$p1Pi0=vaJfL2MLXka(S z2$q|o(;VH!evDXbTjzH?%41=lE>mjork47l!a-5AY534sGD?^KbeKh2WH+%(R%VPHTK%?rWk+v z=>``@K7&90Ds;582bpUDE2}YR$>m0x1g7oxn!HYy`WOIdcL2J~X?~y-l3QR_u0d=m z9Q&ZAK@b)p-$!PNcIw@xe3C|8VK^ZdwD>c%&&f&FKvyT@KEk*v_Lji_bAsU;-4QV> z$gHd!o5x&YohFawuWcd5N^R}qC(9y-C8(h)7r%L<3TsU9MqV`&+;vkT#>GDXdf>hJ zvGC{KmV`bj`5oC^rAP!UUtD$`y85Qv#4~m7vX2>IdD9T7-Gj;A$}5ve5)hNYXak@o zx0a5Hlj3liC$vd3VOpZFDPqY{`WPfNb^$=02>{#T5@V?vLTUgvn2QbCFwi5H#6)!_ zO@YFMN^i9QbJ-Nqk)BS6vj?Z?Ws=8O_a)?BKr1}mzHjQ*ezVlkO00}jf z#H$iGASJMU_+<8^mZyV!0}(evL~2%Uw6L6mNhH2{aQj)jeUSe~iCq(FBuzzw0oie)2-jc7ZpL`SO+gip0P4Bv=rRwB66OXO6Xh#B2qTLNx((A|(kNWlGra z=ABwVD`_VsJzrI-Fsf*-6x?y~#41}wV|u8W>Z9ojXAHK=m9z2rp{^N~Fw+cS(Rnk( z#foCu^(e@UKU)lB5)M^K#8y@Hx}fb{vnW$67`r7#)laWKhZ${ev&hUTG~9Zgq9wFB z3D&O399s=eAVdXh7H(H5;Rwf&p)cs_Dn_v8*0=q*3yGf--0@jTR= zF_(8WqCZfjvdPiUXuNEkw@_ar2l%MP8;VR9{;YLHUz}AJAcP5w4sPxP`~2Al-`KR2h}Ua76?;SmTPf=KHz%t%a}FCb;WD z@hu0P;RqvDz6m)++ni25huy zR*dLv{9@9c@9TF>1sOmUNK+8`G+QpwGBHe*u<5v_8@Nv^ZFn=a&bCbefz1@Eda}W4Rw|TMp}KB_uxznkJ)4{%L63q3|D~l^?ui(7gXMrHOv`j^&c!3U zEJNJ-S1W6^N?jk??d4=wx1}*Rf0jdx8|RO)BV5ShujwW#?{YU=HdRY#?5{W`{?B@! z=EMOc2Qj2pSwiY_W{)`g7ppS9%%Kvdws_#Rwx+~% zTit|vaCQ}`wqX`!acaVAbU4q%H&!!~?zio^@$$J^Ee|*fysgsKW1fe`p<_VmQg^>Y zM97hOuIZIMtBM*}??*#t-C)BRz)Ze2LIlLIsBj@$934VB?(yMkXkL0Hiuo7AE31ie zDbak`HlnYKX89+Zdphx9*{tEdnT$f-4$eAMelK?+QMK~U&Ub%)#Q29SiMu(jBIxgY zievBLTha=E(;YZR9yQ?~?%YL+jt)U#`a=8Rdl-HMt^OA>xC1WTflDs7vjrVcr@Z4G zphQ5g_Xilu0lK$S;4;eVbhL&n+|$`AoPN@!&YRBNS0ygMD9eos25`tdCs!YZU4iKZ z@kbueBRt(t&kS)?l0Z&{v)slm1R>8@=8X@0#h0#6#kei(06&OS;-u^7fZnZ)cd8w#nI_)WYsx-ibhYZKB(z)g#sHsc8<;a1 z4yf~@Ez8YPNtr7fm#4CK8kB@dU`#?t9OZh~_SstSAUGA9zOXwaHT2D%Iu%Tam zV+tJ>jDkRyS|bs}o%*JL15qNIS1f~J1m#&R_b*Zf#X;bE62&-Uof8vMB z=+Xfk00B39wEq!qy*w8}X&)RjM zGGUPOFingtr2ZX~`UILXuU#zq3hw5+O}JxABXmFB=!mbxIo?!TF<&ys$oB7`e4F`V zeAI39-LjFP8o`CLQVn!sd75;szv%>OEf-D3$F_v7k5b@`Sq0buz8tn;5P)2nV@g_Xz#qpR9L80=>- zKw&mUA{`Cjx>O03{L9|p{6#^X1!WRmF!DN$mZ=5efpLP+7CwG1_!3j3tOCa3YLy>= zNEJQ<-p>q*jv%uX;9vHK{0OMA7?et?*0vH^VR3oUmYI?HS-`Datf!P zQNH6pm-1RtG+OrsbzPH};tbjXrb#XlY2`UU@|v=)ul{IIbk!*9OrHJXdcP?M?g!5| z)(!qAVtMR<+UQJyu3Vc!zbv`-b}~qjjQ0K&8;CEeFQOapWLme4Ma7HEFC@l@K^pB=lh(9A${4HQJ z_0Ip`^+kHwh?Fyh+fVjQ+V=;J9ETncr1){^k~?U?`TcLrF_Af$*$Y%&{%DUh2s6^w zsDHvPU3g?UwboYnBTG5niG6#jaF@>>@yH10)+ltxyXacMAv`lnOG{b*)JNgVZ*f{) z1kiCOQ1fIHc!GX`H}W#Afc-3%QegjrA=Gx0w;oqSb)raw^{|9)cNxKj*)nOWC)vpw zBvdf|P8ezHe%;{!Ul~T=!D{?9|8m7JG4j)_c;Ry=I??N(3L5hLrVPll{>XP z1b8u7MZgpOSPrI3*zNzpYJDd1CesN)PvjNJqHB|m?@kc6ElLg^I`qj;>w(&_p(t#O z85e}!*@G9>5MW0$vCkNmvuXpn+&#*3_V)Xv1?|0Ahr)L+Ub(oai$^)}UY?6#!G>p0 z3?v|s`v(hQq;u>~r}CuWh-ZiBbkKm?7`~+Bk*;OGN8~PD?R=&EX5%Oi=p1-Q?Y=JT z+{P4W@x|G|5Wu5fxhIEj6(_ z@MQkD%j8O}5blKDNdbc$I0`I_LF=`?=_ssj1p1KT$OydMh-usCXtFwYV?bV-^f&Mj z<Pj?b;sn$xAb+1kY z@)-#1t>Q+pZx!2F>^yGh)SGG}Z2f&nTMvK_((UDa5#0Yy39;d{wqxMKE7%JmsR2;| zOxcPkutmD|*Wclm+%3m6f{vRv|0gGq1h-727A_v&(Xw^3N=;Dl5IJ(q3G9E#gYis8 zoNzoo7s`{I)2xo~Gnh=B|0VnS4C*$sraS+F)vF!NV?WbC+`AiE(^8Sf#LtVRhNk(U zl{O{T;)azA+KZONLJvef&R>P=PC(;A@BeK5RA_iHBu@Vym5Sy$EYy}DzGGpxPF4Zg z!;mkE_CU@8;D=@HqNi-jckh*ee!GkHg}r1mS1u+fm)$+bGi0%{d+zhj;~VVvN;yRi zqS+T2Pdm)=vJCqMQhBm+j2r#uGoWq~x8ukH1PpJ8W%N;So|2heu~-~x(Vb^+d-Oj| zhb=xpmUeQ$Y#UHX4p4lU7fAz4gFxc-gj4yoi`%oU$5NX3I9#Nq0jA zsL$VNaH3s zoeB9T|zL zK}%>bsdY~H@gN+zHWf?sAhKg1pcnokF5gIpaA!xxG46iv3MJ}w>nRtTNSPZfq8b<` z=*)-r)S{^gQ@7I5W0ZJx%(sJG4qt7Vib5{iBYVB!EB_m3sW~;Y_i+ERG1Kelzif1F zGkrZU!(0)Avim{pW){w&cNJ0 z-UngbxCzulg~67I;&TT!|1yEg_AZAQkk*5;zO)#T)u=Y$)nFQug-RgtGsLt;lXfV* zOba5pQ6I=O99h|ur{lJCRB8PvY=_VNM3G-6yktD)i$Z^1aRlNcSS4CHqKxH z*S^jkkX;vMX-2y2}Aysgk8Vw1OYjynoMO+eqdxpI#x>2fz8tm zy%0Lr6@ny#&k7xad9Y zSbPJH`PxFVuBc1n0ZEEFfzuyu-TBjbGI6)W_5~v&b#lf?xUFj zP(`?;Qwyshmo8jUZu0B7Hp1{zKF4G;kPq*Uvkk9p>wLN#Z2oBE;}GzEk^ZwNSb9cT zFNqjmSszMVe>9;M<-CL15YjFfaE9hEG1Pw1WWOq*XC?nl=E`n>owI04FYL{HSS?C; z-#-mA^Q2j$mjg;kbgKa>bx1NCf4}4lIr}-vZCZl#Tyi44!amvW&9ew9HSsC0Q~17V zN@N>!QA}NMb;?;nF7Y7?iX7N#0opoNP>ft`0OQ$Hzp;dFE|D6ee=+}`zCE`0i?Z4d zc>+3x>!^1%Rv~-ZLUif9-Eu62cF~Orv|-H~@Jk+v;vg%z2k1CTj_+)pohv{xet?~E z&;M09?=|qUPm^Z%3D3zftXxlf7YH(z%H-`igp!~`=5$Pl3R7LT zkC6x2A?euZ%8Ia=ot3n+#9sa4BGUseW70cRV$9h@ft6vU2*ji2)dxXm@lZ|k877?d zTTw|T$mRW-3sz8z9PDbX#OeAPI_{I>CiCn6;C7}uQcL=Gt$U+xg@k?W7F@#@gz!<3 zS`)3ssAu>a`aGe;C{cls)|i>@dAYW_S*3RVouDjT!ipep5ir?m)jtE1($>b&qu@_)mO_v{Zh>U?_`ULt=T$i%WPb`)md zRC=Dz$93Bl0v9qBUL25wSJ1`wP{aVmw4z~#!$Xujqv~#DtU2)~bTlF@{-{y<3(3bJ zDZf8H@{0~HZ&)9_Ay*Ur zi{J>dtJqeI&Ny9yfBw4~CkuRvi(ObdR1N?QI)|d#Yq<0>oN2MBz9A3=U+KDJbC;xZ zT&TT zP_n1cq@l3fTAj1?U4K7D1M_FqFlP=*-k=KM3rO0kvewS)XX4 zAGa7xxQ~u?u4=$5@xP9>tNX~1U^C9#pG|=fIn+j2kDr3Cv`8KnwlNR*jv9lbA7Kc_ zn;n=RgHUtswrR8&A%f7^o#H2chf)92xkrki;B@aH*Gpc>pkPZ)wKXSK*_^H+gJzWm zampR2*CP#Y!2Kx*ejWR53l@%QinQ7p&58cw{qz(k$YIw8wFN1}kTu>Mpc>nGGKdKg5moW{D zc_q8sx~a8Le)=XlJlaZuUrdUD)X|}P{Tm1GYKOEKqR=tLFE^ALxR1d|0TAC9F(Hrh zZAPbj?^5Z>oz2QJ=EXZ3Cajcef2Kj_&?4zo`NO=w5u@t;MLy__IID(YZi3wC!uEE| z`LW)4L3x9IOJqQ9bs>o{^Yhkm{qmL|5(GYH^yX-!- zR4nN_fUdH6bD8cJy?Z2%0Q`4r+g6M&6mF<4a#0btB1G2}O{dCblqjhT)v?0e--+PyXuG4@jXQ;k=m)t3*XiYpt zUL5`ZPWV@|LjJ^#QSM${NfbrEC;8D_FU+iK$_5TY{KnxjBuUKqhO5Yi)L|G8AV~&q zvqyF~ ztYq;}Um zr5^ex5H~dZ#d52~8&JDAsw)&ZPxD}i!7frGBEkGb(O05?&|=l}6;rq!B&L>s-kjVI zUgZf;%j=i?XF|IJI>1qDawqBGdhqsd4!fSuS`@ifzHYi5;$u85V9>fznn+18mDeo5 z%j!MR?T77muF?bt7uKZ0;~EU}2zg<)(@ZM2#?Gny_L@2i*ynUkenH6b99<|H(ChiN5IJQp}{ zX0$ja_&?U#7KoiK4YE;NJPbEG9pbq0*?Rkj+~Sp8lZsjrI+av%(dUBihW*5g_5h$` z5&WnGEt|&|m;=~VwM&(|Ir5fla!7LZK)$4B_1rFTis7jaMU9^9jSPT-ufocB@(PB8?286} z$qed+iPL*X+$-;6{K%bTv3~GIimZsfKfQj=oVkg}(pb;WS@c+bX&;zp*{>8HCoJ=L#HnYb#+GOW20AZhnhL_oLxrE&FsN~5;xJg2xlTn?-Je_5jb<|AKI+^>yNLJGK%CJQ)eNvp?qj*}*uA^) zXC>G&TJzUKheLit3yNO7 zHLtjEAVyd7SJ)y$e_SNH_CQ2|<(jwP0#ioY zrLMc;nhH><5S3QX}32<)afNJ)b3f5{JEAF4^*gBGgeEyaXQrIw@ua}4(l$YVKn`Q+7WoCZE7Jp1d*_Ik|C`(aTj>BM)5PT~)zK_4himpI zYGp?zR}!VDwX}3UAy|Py;0ehFar*!4F&=er;z6oBIiJkQP^x-q%ef4KiQg{jy#&!m|FG@Q2z6GLV7=U$$8L!4|qsih1B}j-%TpXhEAn|JNke@xwokWWj}nT;wa%~>uiX|Ww71yBMzwC zv#u~bAI^Z-vaWe;5kg+JQsXH{Mwvy%&3=d>sk8WcL2hGyh!Y?{s4#FgcP?|d%10*@0Nk-XuIo{qN zf}c!7rBHERamZSKqCP#Lqtrf+?__QHUuO`zw%;*M@Xn)9R+DgQZCm?-@}so_m+BqQ zL>%!|79(^EO;&8b%@NrR^7}KF7LfbcyS`#b%b-$dbO(l67!#7e^SW*AgGAe4qUwtAae3p&CeE7Ht!|dWSXDudCxedRte$TAy znjaJN%ar#o{t9wi(6Y~89^K=@-vgYfPpvRR`r6?vjkGf%q_q;&1b^wT2OVuM$961( zKR1VJy5wSDO%Bx-t3WtTb#yeZ-WTv$C4Y-qW5=dhjrQ*PKL6R(00!KmmnUCPt86>_ zOKD(y%+_#K$MHl5mvX|VNrvL!sQHq=cG@wUE&T4NceS|_CE8WqR?fY60~!$$&-s<2 z1zq-_o;1d-T1l(d?vt#JRn35)*ZqGIviViv$DSPUWoBPYk$_SCJH2@x2xb?`Gx++N zgVF#X@xmraS3$jtr*1|>ky1|zaiwjiU83+RAfBy}jA}nd=KR%qZGa&liKr&+^91r& z4*=a^?;iU=Xwk0Pz*!~$XobjsGi`AjfjOys(4ue)YYY}A@IRbip;AuHeX7!4Dp=fu zYONH)w@*g;{m3?ba$-m} zrZI=DIJp{+fHkQShPgC$*E8HTD)${u)XC98aK7ttprNkQn!WS-gi(9B|_xNNdVB8wd4Z^Qx;s07p_q zAVWMN$B@~crR9I5)BJckGI5k*kYcJ+w}*d#j6D#sWOyv?u3#|7&Z!t-=DbbTL^b7+H{skLKFM^=6t%rY8!AGHY5gvmiv%PPJ5 zCVvOuM0C+rV%Gm*Ydiz*J{EIA+*$BO1;7^CA08l-3KBt9<=j3DR}4J@$X~eokA1UL z?XLgMHtxI+r&J<`%K3<==9O!$XXld;C`^+T`~KrO6v;Gy6ZWCyVmGJA-w_%#iASSOuzoU5L>cnDCpj)PmUMql^hFt zi~doKr)2&`_(nXeQRpF?EEJxZ9QpihJup(P=koH5(!8$g<(hz%9gM^u@? z8K+Lu*q$kgh{XB_Ymbs(xJXEmc|^xqS&18owy6;*BrBIyjL*x+WTEsBc+Frh&zh^f zSmwvn#@wf;Nxa{&{Ltx!zeb_D@4KizZ{;CV>WKV21mBe8koCyg6U9%YJgsnKGdovw zE0|y0{3eS!FneCz;Ua=wH}NZj69`vdc|1+IBU-cS>toyNkL!pL<6vW|TMK^u`l|0= z8qA5P=GO6F4pIx-#tsfKH5fleppJU>Zpyz0%i#$WyxC%u*^q~m72R~#-(ZqAqTZEO zF=x0VDZSWGO^9wyG^>F|Tdr3By9Ib7FIR}D{MVH|wXfehKe8z%fd-?M0JutLu~F5& zQE_4W4T%Py+wh$yx$P29q5%3NZnG%7#Zl9|*u+EmJ2_vl>g`8wph+1vctAc`28<2t z@_ihf2v!tZ{Yj@S*vsR)fC?&HYtDOv|FzG;9urb{jI|CP`8^mwCe}^{{)R@L-kUb* zWjkto+?aO#K85ESaKPvJ(m3$&>a#!I8vhhy)T@w6=6@6hzx3Bc)amTAo9s2teII!W z2In2|mE1`24t_(#zVbWDi_&6|vBKo`2DF;ZJCBt0yuFHHITFIsG>g0b|9>}tZY6nD z&28DpQj;$kHa%2fBtR(ec7{I^T=}CDHKiFaxT4} znDAek=SVmMzLc-kPASW+$_YeQdCj!-KR}A1q~Vh^fuwZCXPpE8r56mNZt&iOBAe74 z$L9ieqWOv7pDKB>zl47psA2|kAv_+yKy_kh*Er`E#_%j>?Z7hSnM5_yCHQ~pa4Cf@ zSf-1C*|}cmR27h8#r4O6?pYM)Mw|$k>NX7U=hA%CWWje~4gnoOz1Vdxprkh#`!U@w z?oh)j!|>99so8XM-EI;xG{{mRIkg)JX@{Z6Hp93N2n>*B^#8bxjVU5v;xtAj2A^v! z$jKPd_7Es*9OBHeZD@PU?m=zFSdKBF-pgs8q!?Iq%xzI# z;jbiAr9T6jRuG{fNeX_ec(i>B@U0WB%$ z{vXaNJjBSki(pj?Xn`VmPqogZ&pF=m% zQ&_R5{5ZaFY*@?7OZ#Lu%_{vJ19tYmFt1lE<5I|^e$4BGfDDINISxx54y!YCvbAh; zQ{1mH(R_T;$chzzByRJ%^?BSmkNg-l0p(bc7~j-^bft@saohfLv@^6*JB$0+Iq(4$ z0stZUt04!y2M&EB^5J2Z)lT-{m5*zRNMk#th8(d_{~)uSB`3ot0{r;tvVEV#EI${| zqeehOWg5$=qO>M%#qJdkN#%6LNyfSXmE-O+KH*MMzd&&pEUACj{PxbwmCjt^?nG6- zCO139F9;ANi;i0quTCRhli@FAB`A3(D5JJ#6kF3meYe{YP5TRgd^9d9#s55XG;3B4+AEh7#o~Wu`;e4U}`W#0CxWX1sDcg)K z+^qO$=M639yhKEg^>{-hZ`2=ZA?>Q_>~etk26Ki({{ z3T*JD>SD$z<%^T`BFZJLT0#xk##Srh#nImoN#fibfq9Z%_MtNWeK6dJk7u*tz0 z>2510-#YU&xKupikj2wB8ccas`i!mQlw5&|>%<%0xAUIh6quuh(X&W`QARXfsApw` zqUX0XUSaV65`%y>4=jN9C0=qb$>_Ujj(}64r0pid=Ma@VHc#ceBExV=_?!Q~-|d4N zc}_)vN*!!2!wTYny2Z``1v=hr)!^n{*MuPC=Dk$FNxUZ51| z^hO6!fTg~mUFbF3dq9%&?(yD(13Y+}>V$0UzS{d_C(u04y#~&M$4lB8vj0KIBw9uh z&rLWW%_gb|WW4;M;I)v$YGRA;ydCFmj=8N9#sZtue~R$u|AGk2Y1H&V*5!RErbO0| ze=2;9oeN&H=o>5Q$FQEy^Z zo*dpln`wZ03OsEI(SMR-K|U26TC>bYr)w#HJ4Ik7mJQ|J%)sHTS-gU@aNz`HfuRrn zsBBnUli+ghQh}v ze1f^6fjY|kjKQBwO5}zKFPw;C(e1z)(hDPrx`fHBtI{(Nsq_uYsrAg81*(j=^Szbx z#L)eZYhst=Wx(>Sy-!)Y03hJGn$&V7-ypImT6C|yekAWmdV=O~>qKPdrMvI7fU7}L z9r)ss7n#9(kx~(nr?s6&Zhx{rM;V!UQhIOX@Y)l98H^qMfJ7u<2k9N`j5@NgrM@GM z(%##S%hqi40P&A?xA+uIL{qa44aZoQkP|KaZGh{+PrdU8n?%n_2>RJnoD>`|l~xULVfiB@Pyi>l9wIkecwL zpWERT4*F0}{e+PG7n;?tIPH^ky7om9AHVB90I1(^EprrhCC8T7k)di zCby&^k$*0~4UgT+&Umnr;#u2ff3si2-nO}TX*@d>tufiil4I378F<*p^{Mv=^1^G$ zpUpA0pX~<5!x2mV^kq~t(R5A7w^Y3f%Ljg&J(`}qSQSwG1oU_;svH&c=!3Pl-6Ax6$0FW} z6a!suy4Q;_#;5f!tuC)>#=ckm8E~ZUd409-nXHaiz5O`+#|Ejo>D#8UIqJm#~!*aH-HB&k_)utx_ENAP>xx}lyCG;tDeLB?;o3$*tm z=DnT8QPoh+cE*0ADCW3avrchOuC8yd3H}L-i)JFpM5Rg$&b$u;2@_KdomlHgb$sm# zOgr4l^ho+G>>)TSHeM*0FAZ_NMMpAyIbTQ!R5dFGWo}DRag;>{(?VOtj_=7gjxO2P z;F~Nf1X3N)Sm-<8hfSNN`Xu)*n#HE!=2fQnGE4R^{U_;{vnl;&(_lTnz!L(DR?kcg z{bXLMot88!i(h%p_KIB1`iJ_~2v9io@R24dTmYL5FRCy&QPcY;+U9N*tC{*@tbsY&Z})wuUSz!|t2v<2f_h}-X8vRADY+84XnhVuu2yq;WDgGpz1M(xWa)ZVX36%nst z7t7~bg+A7qQPk)NW$KEwdWPj`DS+#AHZ{t$lM0z7p#?@}g{X(1xp??|=xC|G0)yxJ zBV+dxbpZ)qo?}1S-)qX;Yzxp_c6;BF&!CUeU{pM4-Xl;76Q3_S*gYN zi&I$bokVBTdgj;-$oE)V&xiJh=a3r!Geo1TpQ@W0!H7{lq5tV~qShtqlq|Yr54Oft zfJ=h2RnX6|PvIKV-PxbIlxf#yaIN7+FLnA~pJs+`O6kpt5~x(YY>RY1m2kkbyRdw* zZ@Rsm7zB2B+oLz8^9v~_gci60u44T))81J~s5i%LU>wjv{-;T&2mfDiyGf z&U(cc;8aEeR@{!j6GW`?Em>O z>Z(J}gpsU`(wh&?c+lyi8fd)sgGaFVDS{$)6v=$O7h3TkH#qkfiB+fJ{1i^+yJiW3 z>4>@AQ-$>1drrU(TIx^(mA;RhwMq><3AsXk4|dIp@4KZZ*dx%A`3*W*F^(nR$v`)jNt^gN~BFI!O>iC@yjLi zsRtvt8yyEk?YR0KUi0;Pt)zuCu`Up(X<&R3>UT-t>LXm4J;(G=e zY1Uke_IHnF<1x0oB5SYCG2{)mQCH|$$_o_)rKeg-g{@kW; z_sapd(J|a%x;aCY>!Qkl-G%OYK&ZvYgLgut0A?u@ z9FxVh<8eAJNJ2(~Ml(H8jq*~FY<@^5?vvt{LWtrW@r+02_6&m@;0I{HP_^nuLHPQx z1=6mFk7hp#xa{G7I^mcI?zQ3F%QCa+;ad8oaFzlUTsq4;)j6XnW&yjQUHy>6boo5Z z7!mhjBx{o*FU=-wgU_vY$dlvvR@_MZRmL)jMk{?}Q{C112yf7_l6ofYwtXGdokGyh zmOhBjbfS4tgiT1FPwq3jyRFJe02Peb255?G&<^8n+Qa1n5uji6eDNCr6^FSNc?llI zpM>I|q2SvLZ2i9Yet+vvJ7>I?bQ9#e#LHQ^8x-j-Lu5L3v7{q%C^S>bKa1%8FtYtV z1PBnn|8O(QgaA)Ku)oO|c+wzqy|-!_V|FX-opwZT{)ERi_ep&tl4ZpTydDEyVA6R{ zLL1lZqj#F+GOt;H*LD*d7_@3s3X2gc*$td#>6!z@N~GWj+}W>JFAmxKTbi4D7!hbs zPZtlfiKw+7#aF7pii;HPll5li<}HAo?kVmT^z&xHKT(gzPnrYu5Ajg49_nyQv4VSk zR?mP)CZiQ{M=YqSrEd6~R5B*@?EF~eI}LuG5E>gC-n>Lw6x}5#Ye(KRuJ&EWr#Xuw zLN~|Evk(h1Ra}N8=XYkQaI+**5}2vq@C|Ek>DkaN6T|383ThV5K=4+uAdvincYkA zxu<$alyVI^Kgr@w<=>)|y#wMx6A;$^MU7%B$$hl6%-aD3ts@ zsez?DVKH}ec9Daau#m1gW_Gnbb-9(4dqPnT2ygXg{axxjKJHGMfDPXg-R0q+dbb!< zu3n=(HKwj9hn0Qh^pRUg#H7n1G|C0cl999vm27y}>oPv5FzR!)0bdoEfN7ErlCa#^ zpHg-z&ZO_ZU5F4NwES$V)bXUFnp_0bDaRLFD=2$o-j9>F*!FISrcogDG>l&uW6Sr? zVp+c2>0s(U5&W#6i-3{*{%coA5Nm?$@B)`;(8B{VO|CT=Cgx8?mzkOD)I z4$%S?EKmxmJ#lE|#V|xBziJ;rYumAtUa&t>HX&4vK)sr7>Zo>WfTQARsZStn9F2eB z*6;V7QjtK#U>TLt2hb6|H`fbcs+y)Z5jpVm3qS(0-l@%gNcJdl=BBtDO8`p)*qN?z zlOJEQ&^1I0NFxt6a6Uhb^ZJ|_1h(fE8!LR0riGT>G4 zfdq!hXG9DH8smqb#J5@NVmdxw*R1>|idLD}0o2-07RNG@U&`g2rJ&$ZG)Ad%_R+#v z1{^WjH`Q0>-rqbdv(t5znMd_eQ#15di-E<1f5Tcrls`kMZLWiPTfdD$X3xE zsT)=&1{qS50(%?q+9zQr%tV!|*$U~WrNxZcp5_8`tS-E$0V7o%?E}so#xR+hk(#_k z!OBkFHUZUub5Dm$#|RC~XWQ{iIu_@TdUih>eYvbOcev*|?WP=RlqO>RK_8T7UD;BC z?1(x`T~OQCS>mcW0baToUA;sfZ6{R#cXu38XO#$MbOv1e9(QOkgr$16YUV z+|Sts7JWPjw)g6k?X`k%EgdxBCk11siPVv|df|4?hSqd6h0Jm#%Cx^>oKkY3G%idX z4+iUhbVT*VW^7O8X)L(0)TKxgq}vBS7iktyd?nf+;0*WwTJX5$x!ZNb9xX~M>fLAb&BRaIs(yDFJ*A- z-*0%r4PY)rdWkXk&ueVn1WxcBKH*?PLd=ZfIM|etWrq36wV6{cMRkDTyABDWy_bp^=8`W?HY$2fjv+New)R&5FUCIAT z0lTg^J*%GY5C{F=)0^#F!De+$Af;U(6mBPCl))!TkvH<%Ybqx*JfQbWw z>-r@s>b)5OMFmqY1*uM_w0P>3VbdHEK}hLtW`h|lfh8C(>!d^tDf%`7Xocnl6kFwH{p{6FwEj^!5?rC|$7`^9+=2%xQfy7n z)U@^NsqA+?ho|1e_ur@FQFx~xJ44rx{K@n&< zlZ2i?23@!~pz)Y>VYTL%Cj$U&lNRXf(vtWIYfOF8L5Rz_JAwsCP6EVQ-2i-v2;Q1jQ&DCE>1N(7UD8QnN4} z4nua>Vc$Rkk%AJwvpXH9nl4ND{MWrsXX}dFqr_0)LtzN&@LdyXO+3E>w!!j7INx8R z2HrbVBQS?o+GMUi@0`llhJRPp=Mvf84nK;8AVv6X)LJE8qB?Tz(LL)lOcfA>n`;SL zIFa>1I$pFZU|W3kM^vRvG&AxnO+mDExH3HLjLYLn{E?$qLeO2S4Q7myY7}^UKt4^e-%cIy@c%1Y1nfo?e$4T7A$VVS zHOTjS{i8vYh{rY;} z^u(wr5qDq0PIMDnV2` z@Fh@PT~U7=f90+TH z4Jqc*WlcUD>Uu`}o093Hfjt5B%Jxd(?JY;OaB9#$^=YoDu@dy^r*C|Anw!q${%iDp z@QPXu8s_L85od5-RIcEi9Vic+`1jC^+H@{^PtUs8Y&9zULYaXzg2eBWo(HwGFp~$8 zp*cDuyz-(q#ui-7QCkoUljY|{KviQ-RzYgylSfY(o`J+X3)+VFkU_N)!9I!Zr3dv9w)vU-^I=Ly%Ml{x zOI~6+X2#}i^?epqEH`t?kxo4|mX*9WOZ%*tF5^q~Ad;t<@Mb!RtGdJCt$ia*dA_)F zMxJ}y1f>kWG5XW0sItyV>&lw&;Sf%uXc?k z5|wBe&YtA;@m|Z8ivEw2R0jaE`$``j-gt*)LL8VVTaEYqYLigyD$cXXc%hqU$)&S2 z@Uzmn{O+zoMJ`kI-c`vV$Z= z_3t}yNSn8sRA_@28LSDE5J^ThviX29$AwFL44i)xhF&7(-D@{fA#kPw823BkM04W; zcuyT4J0c4?fjCbz(6<@wrJL-(?$>bH+W%iAB{u{GcvPYMX=z<#D+JeJJkya~tV@4u z&30(Zy$rcJXY5lWEIq?QO+RF8U4at{6regN*ZY>eL6uN*qeZ-(3t+@c^G`cev1LZO83joo*4Zzj@MF>Z4mp?P6~E9r z1xHqO8X04_-TEQ8gEtLeo%}Z;5C=S+wAXDzv3OC?)L1oXa5n06_b8FEtU0%C{3sX& z@{;>}ni+j0O=zcfX!_1J82m^$wwcyB-a8X8l>sKV6*q6PepNtt*(O0E^=jfHn(nw6 zbTEYN9zQ}RkBq{#Pkq^#gAe)}PO8a%!fIFxpG$^VhfM4Vr1B+@A2l1nbgw6 zPCur6_n_k#k7OBZq$dF0uRl6fOde@UL8&`R5*(!aTriU_^y!=|pNfXg{U-;4k_6pA zBmxp$LCEma4w+UKKEKtJ5~GY170q&fJpANpXY1Y5fKjkGaID1lI^0L^T6U;XO`X}* ztc7HisWnm6?w{b&^?{WgZeo;qt_kg8K4pOs;-y|f}^pF&Dy()OBGgjcCmnWWpO>wvXm-p^w}3W=I?Kb zSV6xBbw3h8m|4M29qKxgRA=HicIj{JUU5AK#4Q6wL=pHy592cL#C2)rf3hbJ?w{8F z)|d}nA4jM!^xJvklT6aCBfC5KXb9gm$LK~_#lD-~i(5Or5coGO$Iyqe%DWoUZ@N(Jk0fKHm| z{pc}&)OUwPHH)>2N&h>jcTJLBU!Z;_^nTlBo}2SG5GWYnb|}F#Fkiw&7^ckQ6l=0^p^A_uu@UZV4xS3SB$7ZnmLHfL zKcA!3Kt_gWLTOz%sxh0=k%yf`iES$&>lNxiwpeLBpjP2u%5dE5v_@PNm4E*CNy`aW zzr=x%zhcXqE);qjLVTJWc zp_t-OGM-J_wh#&HcdO|lm0a#?o*UOI!FR~=2l}MC1_;j+jUPJEjGYpSZ%W-Flj`2a zMm)F==Tvj_{g*0lfAW}ol1Fj&&`Po@31nx=AAMeO?hD^F8|OPlv#PCd`vf(T=G?>>X(D13tGjT%Jb4U(0j2BUApc1h|0f=aH^}7|6kJUB| zyS}kZHS7Y8I=a;_HufB(yg-#V3C%-l28h1ii@=@_Qd75Uy<=-xmg8#}La&oE^Lu;L zo^%BR*BnV=N&#CG=#!$9LcS6P*4`9gZ#W>wp7u6X46^ddFEE3gu zK<(I2l^ot@uPc|LYn@Ct!-xn25J_aXon?;5x++pFD(OSSZ>kqxG3h$~1j*Yq&~2>~ zWmquo{mH6AVm>x-489W=zdEo^=!Sj=3mpyF)jU5*tCG>51MsQPNGPC;Oc=9BR+BQQ z;D6AiH>~Lo2aCclPGIsi=a;?nCLVY*T3H1JXilDQ#UT*-$3Ve2uNc3})dfa>bb}oH zx4e<1$F4;M?`wJ?CTS1m|1qNn=Osa9UG^#DNcM{(?UH&+kl3*Y=~+ogyEEA|Oq6)F zfGU$s@T_;aWuOkuyJg2$6p6hK?ttLhK-misYjxd6CK@l;*9;-_<1SDpp3csH|J;}E zP%GUrB%5<=xLc62OuzT)(xZzLE=tRACd!I3uV>-M3l~?H6zRP%h80#^^0wvs-chJ} z5^OmoSuP-4UZi%wJ?ljRbg)e~Q4QxV!wpH3VP2~&-smWXEYdyQjY)9l|NVZZ&(*}MfTqAlGq&TPK$QzmAS1oej1H+24LBPxO@KHOOZ)#2-8f@r z)PpYg`a5(NAdsu0@hZy7ql;~jF6ks6Sgs$>`N9=K&hAMq(_f+~B%Y#)OQC4n#8sbt zJFLO-xD9zktU+gp*2P%=0XGZX3PEyE+l?wz^6U{%W%+$usCrO`6_dxQHG=QXRDee7 zk=Qn$gYv=7vC^>eRd-J05gY)hh+FqXha#9oFBZzU9B1R-mV z!McI|%#HI7cF{W#lpJp6GMajCWnEfPh0Kie|AV`Vj$lbuixd?Ta2Ap#mFMiL>%CVw zoO9M$(@2sCp^))8WQ^D5P=SpVwg!qa$;8SMd=A$u4)(z9$8-0|hAjFmGA!)1EJ!3J zt3rYdJ~PRKoi5Fbii{}w@ru1h-V>wo>;)8iyy@lZz@KEOW zt&acjym3$oVDp78ccNhl2(`4Y|! z-+qD|kCMmW3#jr=#kG|ekOM>WIE6v7lcXt}15;2x@h!}jAWZfbox|Gxos4v`2C|7a z>(R8}0qZ-UBdDRCo!!;Q;WsT@UnixV(U7NcD8LvF!LavY0)#(*xPXNW3H{V!T$tN} zm~e1^3jW~^fNx~LpQbJVY;XlMYJjRN&-}c&07%Xom_MVa@VOr>{X1UOlc!%ZcAUn`R{kiyT#Hp7k;T>iBQd4)HnUY}7b0lwLhq@^HU0R$Ro zTqsH4;@a{tzwQ`^oY9ojS=nqk-pH76C&L=Vkd5CSx5rir{=$vaU9U^}9sSfbZ*qVY zz3yZbU;mbnMSXOQ?Ca{5JtaNnvB_36TOe;2cn1h@hOS>G;TZe7O8Cz2=uJg0IIk}~ z6v_ba8#MJK;aKTc7+JWasfH?OV4J(T|#7DiTwLn^7PZCZzI*C$3VsB5Mmz9QaCE>Yy zogJ$%pU%8m(RB`Fvc5aV;6R5w^PwRW395R4k#h>7>+m~#iDMBq%sM+cAhBg2 zvgK^>AAGF$m}E3}0Ivkz;b&;)iE0#)amjk#bE0n7c5*eB2rXQCS`!LcC}(T#QuFTE z{D<>q{Ym`R|-Mb;QeqnzJoJyAoqgc?(5erM_Dw|J+&+#X?`!VmC1TOxV`$<#wA`+v?x2ygvtPTz( zY=s6X_&m#{a7~lSPG9Va`5&uu%uGss^T91pwQI1ZpK<8rSJmm8Y?=_YVjzzy|k{3PQtgU6nw`L$Y5vsjW`@Ni;T&p2lV%y7-= z4kv|2A<>q{|99X(#7nrVum#ZCLYpM$7Tl|ZrbhmUz-hRY7SN>M-)s!cGM-jlWbiFBasH?j>|~!jNe``EsF5)#Ngf`7LbUPoP0vIX$M0d$FEW{1KlWXh_5txX>b}j` z7=&U^&n~n>|2|y@Eh4>m1QYjQ!Gd!3v*jiowavL^(eySJp>xwk4+OrOv#)EVVZvF1 ztP)@z>)FbXMJY0>Vvw~TqEw{NJudC`AV0(ndgrdw>Z5qVAVhmCl&R8YJOKgnty*R3 z?XrK53!P3+LIk&#?1mmtd|qtbk016;alJP7R5pqIG%O>HsNY4PPr+^7HH-9HDqx(t z#l)$>Qa?^dYJ>>ey!Qo0&c&HOl&@mId&k&KSj`1Q-*oKI(6Fv|(u$kZF2t^ceG5dX z6xwm7VDr7z4joWmwv&%vN!x>DK30bvnNt6QIK)|7eij&`z>o`-BT2bhS)7zgKV}9` zC)ueogXdrAbjNQCR2o!7;40*a{n1&k7>*hKRyW-Jd+GT9=e(ze_ndpsV|r)J{2XTo zLxTRVRwbT&ofi&fbSE|#8kNc2IGOupK0N)*P-@LqefqVlhg{q)j4uIXfq$kR zJM51?B^`bRxNT^3BdnpjN)7qRk1g+hD4vvBtid2x<$k!p{*r*2LkI zG>G``SN^S~E4QK`yGy!OwG7*Vz6nCJP!yFlk6^FWm4{^lXhyuwv~ITiHQ54R%adNU z)u8Wx@lz!)R=w@;R$Z!gcR)POS+`>~OtnYnRge7*De@k#Fa8)k02Cyx0Fe?)JeTQ? ze#auJKW$sfF@j{J>3ss997vnFm) z4|E-^TdEBg`<;WNfUS;qy$d4MXwZGGX$)!oeyVvXS2lu6@7_#f@{1zF2o;XXRfW#s zbKLD6)%hAH`~iUkb94uQcKG^l;tQFJir6$w;@zPZZOUIgIVF|!?9V&R_KK-_8eR;enF+lB*cWaUd(d*lDuq=^esJ8U>gXV z2}VyotvJv8c}yJ3dSf&W{K0xk?$=@(&u1z3&khvtR+MoxEpSa_kFykXd-+=4;*h>l zGqK~SXr45Ujsrn)kvy4WlQ)HdA}EfTK~kSPcv(Sz+!pHGCr+eHgw@Fwo%#B=c`bxq zi)zs=>un(kMm!>G`SfM8q!`(LL3(ATn`s3*YP}8!>IE*R>KXK?P z-Z7#|e?{#+<+&M^ z=sUx6;uXq!|JMm;;%Wa~=6z?Mc=cRHOVsBy}%iPZyP&JWeZspucMUjd!!;(>5;3c>o2xTtUk4Avd3#EdALsv^vmH^~7= zim)Q8xt7Y14_%RqKqx^z-Rrw_7y;UiRMkXwlnX}|9a76Vg+5PSlx#0W`Wjq>pY!L! z0C`!Y${6d1gNJZ4nUbupgHip2UZ~DYqe1%xfmVQ75veCbX*m!q*Y7x}5S}mYu`H{$ zA^uLDQQx7Jpo>Dnsf(k-XAdkCJAZ)sI;qAFsZAAac|R^)y@z*%BD9cx3Cv&ki0?m@ zpJzd&kqxWH+7zfTkQMKe zfu$I-Wc1940m3-b*r3r=H`jgD^2Et>Ex^ ztpPx*Y>-CGGviv=Ov+-lrnyaBxfib!O2jJ=9Fn53JW6mWYyjzsZuE}Y0tf=?p=T_) zm5K0UvOd)Z4ZUsnGi`MkJ1_x7a4Usvf(C-Aq*XS07|Ntcq8E~a%)t2Z3{9U9225Fx z#k$~g8VD{+$UiSlsi@U#_JRX^(`0w*I7|r}hqSAIvI&Wp>M6c?ywdD-n2>=77I-~3 zVtad2<8+<+BoQBz6Nn(SseB8_a0RIUl>51g0jhPe%#On)E>QF(> zx*nE<+1HLmmLhfinhhM)wlV1sVV^>5M51c4fZKSqJdo1%j{Pix15-R|_$2;-&E4_H zX1&EQ-a4u8oFg-c4@L;>Ap;xYaJ z9c5!OA^4CTQlm)dza|aoVG6OWBY-><0HdFD>&+m`*&Kle)$`8IgbJ7~iyhC8GG@kw z#`>TL@k1UTD-~CQonYn5VjBm>q$2*Pm;YPsQ-T#_@~CVm7^95##v2#9v;AJvj`KtF zo`WTM^BX=H@In9Ry?7MxzZZuIa-QZ5$ECFA5u5qbjc18DT<*bHu?F5N6Rq0XlQR`P z-W+}aN!0wd=MvXeJ!Z|v1p8g|*Qr>#W5v3F(%}<%SDC2WN;4j`sT6*OY`P#8o|kL? zrWzsNq3ero+&N9Q{KT@oJ8gJ17ScG1B{1#BmmDu`GExW`X4<@E_kD|0K}(^hq=9uV z=ZBds(KCJ*E|eUoN2OozcONHqko`f7O!lW|NlvKbNR)5wINzrf^YMjDL@FR|vaWQv zs&|d*KQ5bsfmvA41m`-f?_}RtU%-h5@55+Y==#~?!Pm|uA;Px-c>j84=@j&o-8el6 z8|no7`*9Gu#YW9Q;Flf|(&O5ZPnT&~*5E`5uNtXkxCSiZ^dP)vMQ+=J-v$-HM!Bl@#V_C<3 z*gz&gE{86U@(eixlFqnPIgi7OR~WY z=)!1q-x8ef0r|k4JMcI5$^~dni)x#ynCon&Pl)^vJ=}<&fX80#xfnt9t<My}OGr#?2k87zg>OF8lgQo7}SmZW?QPf%vIASq~boDhJX^J zsBJ6XujYa?&p**`JTf6M;eu%jwK=oZUs;^6ip3#6o1l-OPvHhs8j@lpuBm4~Sl&uoqa3U%#E`wCaCFas!r>S%Ft+p_it}U3 zVPy2_=89p#hD(-93v2mtu&XDm-wBQ{IJ^ji*Ml($q#*lY+ZXB^+EcW2M&YsBu~{hg zK9K|v>v)4H7OAL3$27=c^4*Pkb-8N2XC6h{`+N%SZt8EH638;3@@^8A$T-yivySKM zr*HGnZ_b8YSZJP8Ow>jqV_9SL!p4X|dJ0~Zx$N2?X;b*Fp?w$>7FgKP6^W`3;UN$I zvoT&G$l9{pT!-xnKn+}Hvo@oftN_z{wir)oiq4@P6h+x-F=cb$lkEr|)WaXSaOy;I z*V7{eTy?Fx(iHQR9g3weAdN-D9^deRSO}&lrU2wr8h3TZg|8CvF?1)et+Vnp9w81{ zRu!EV5K!13hxUGcS6H1^6ZZSPjcE#}+}5f9$d41B1tvyy0+;>Q$3nR)Ng11Y!LMiW zt^0F6pHIWKU;Jn(_x;|{NFB8|ia%;h%67X|o5ze_!vP%Ryqt{a2i+X0@KQeW2lZ$g z)t745P|&r-Bfbz#IV*yErn7$${}LOe1{pZA4`1hx4^8GZ&G_VxN%l`1AUGdEzbp+1 z&1`AX?&emAArJL(`OrDrkWImr0;E_cvK;CFTH&ewFY3pF?rjDoql`Fuh_xMs z@*rP0>hf2HEt-E^Ebrnia|<3Nf6o5FTncA*k2063V`L)$04?Uwz}(1rC;OSUtA%2B zL;9=}vYYeWL6B0ZXMA2yM3gV5+~inm=(5xkNO*E5dR} z%W6CLwqE*hpjckhx0=yR1-cBF_IU8IO_ooxiE8So&MNT_cWEO0kiTx_?2-+Cxt-P) zkepI^iQq|c6cJ??@fDyKO1Z_0B7IIQNjb48lP^QiY|(l)mA#8R33z9r7B%={xoqXH zfG~3lHlv#dv=I^v$3i@#s*%j`=WpZdhTlH52qlEo8-V zyIIcgMfxQ-!53f>)u6nidhsP&kqcX1yefWC{G=ma)EJhd32b2Qas zMYZHgAYN?)X%WC1LDG+bYP3RyK5wQ@3EOw=p(|-o&_j~ctEEh z*MId?d`Ur^RuX3Tel0R3XWHA+iHvZX$9|K!{WteAwa89l;zV&fK1R1FnnGxdcT zh8|TMm&uBtHeK@I1c`4K_IpJ}4kpi^>pbx7JdzfrDbBKBC+Q2D36t)Uc@n{`QX1uJ zr`~OLkHboTTVi!dt?)b+W!*vrjD<?(_=Vv^tElg=J<+A;rp9FU0v!0dBFb<-fC_bL>(+^+j-31Qm(-Ud_m z8kV`0#kD|F8&{^qIBDtP&o_vQP7FlS%`6-Tm?RKYJo_y{S1^eV`MsAy1OI^>raK!3w6_?4z;O+XE5k~VEyCoLGW%rEpo zWYp{?3-*;&dQRG^+wXs~urRj~K?d52)pLLV00RI5y!U^- z$m{-L^YR;hS+vWViUjn2znv7%es4|gO1i$1gP*@K4%kZ5Q{{(6nT%&XoI89BF`LS} za&)+Tl@))ENRP)F!zX?e@}pVYpuaQ#Ct^)#ES=>d+>|1 z!fCQUH#bmHGv!>9njs*6_2;))J?5NQzsKIw7u0PalXWxd134gBGk_{9qu=MsVSOnH zlj_%b&>K^m=H~Qj?D`rdx$C+P-MnW&qh!ef;CS=Y13=K0;A41OyJ|=}W=>#w4&=na z)Oot+kqOnow08RnTm8_8yZ?g`$o!AR8r_pGMPzKZiOz_zos^N|-P?PiZ6RA4KuRIZ z!NU>(ULBjR4Ei0CKkj7`K<>*CtaqcG=w_L%BiNF&w@Ol#q|);B8eTkgZY^6mnP(9) z>^yrwNNZy6Suc4a6bVbxyRmRKM``WxkINd@&)4iHhA;iF<>%LNP5LP7Evx;`hNr6q ztat6IbP@xT(`7sBs0KmIVx{ia|KWyJwPBD2`>#rsD!_HOY@Al$3U_TcgD9$j&F8tL z5z3~Y-$F27w#I-o%*oVwf5)gIaIE6B;-|+#INc>6EJD)9(|30R2@Cv7bpo^>HUIz) zBP;+Vi68<$lmIS`Km&eUDZR7>2C`{qK+gMj>LdEPxig#^bmOB19m7xW*0d)zeQ*E7 zQC(2OQ}v%#nmGBe023q@C%}AMf>L-(U!71rl`;K28-n`{m79v=Sz@6Hvl(;ve5Gb) zA|56%@AmYcsZHt>>K=m|Jhkcp%^!&DKZ6eKbGPM!T?ySuF`Tc>?zmSG=2Tj+@)Lvi zz%Gxx__INVfE`SM<5!lo&h=WOvfUa3GX+4?42MD{Gr4FZbZ2Ovb0@k3q1C_+{5?w6 zY$MCvN;?yVRiUx`H?{0D1RKy>T#HrSlY)U*Gzvvkp5=!CiLED* zOTw7rc2yA;Gt73MO6F2em7`;HM2u6?L%-gW>KkwX6)9-es!J58JNpa?D+Ou88}qQ| zsEJPX&e-~ywRw|_MwWH5lmTp;G9-L}M33y=lzh!55d9<2G(m*!@^DpBB(P^TjlkVi zMKdzt`B45yTta~C`}`2zK7{?UlRb%d;}26V!s$_zsNb@g8Vh7uujEfFWc!qrjZs@U z=N(t#J(EDE8xx=2)%g*K_8l^N%#$coKBJ_F&bZ~x(>yiqzo*P_BV0{!xht&}fi?E& zNwRCg`gfRbHKp|DBKOe85<$aq+E%SJcqQp%W=d2GODm% zpKtxQM_&89{mn>s{=UkZtD_5 z*pB0%$VE5V6y8n4nHrPW20!3#`f^9w%t_(m$ zYF&@zFhM{_bfDQSXhJ_OLmL; zv=+B!cJrB~X_%tDh7c%zubU3MXIhV)Hjnn`B*pNbM)QxEAP-m`R4W)4Am^D@>oj3* z<8oVGPkzA=;RD?nrM}}tcqpYaX#kZzkN|5$_r{D=^40>6ahi~@6c0FG`WJK}07qG% zdwzV?t@d$<&xmuF+xH~~Bm7CY%c{S|IQl#)qc^~wt&%c<@G__Oa z);#d29y{Y)kcQiHY$|tB#$%OEq;Oh{^vY|D62*(_M%evd8ze6hRt$Vhz|901=PHxP z(bOBi_n|N?!%VCG=pfWQFJ!o*(?~G&5`s_XME%=wuC&gs@vSSfT4G@NE*BH239q8s z-hYwL(3Z`^7JT!%J#G_<_6wPqVYfk5^~WV4 z-K#g0uAztzgV%kn@eWv`UI@vOvc)%vjOC!U`AYmKyjATw(L0e33Ye~=eBp3W$dyJV zL~myE^v%j7BAb>_XB*&HGvxjJ74&;W*-Tbs-tUxYt;1KuuYrd{2p1D7qpj{&gy%^7M&~)tB zc04}I{5U89d?(zA0009&L7vJ*6)YkD{if?-Ds24yIh+<0_?xTwD6BA-w0854MTY6S zyILr5G3tv?EhbnBIz9U%nh#HJ6#MA*pGVg6E88>;mL#8+PFKD+6jl>|{>&16g+=Ups0 zWnI}qmMDC}tdEu6Y*~iH^C&l^X-p`-!l*-XBg35flgybAWKfLDQ)aj;T{F5_nHKOG zb2c)=8NX|_!h1LANt4w4BJkm^ZRYRSo`z%B1FA2pdg+E@J&lmm!i(PS`_1nA`nA>h zb`H01>FfMPzJT@!Oq!_=1(FafF{vYz$BgT8(37|*#7@}CEoS3C^as%;D4e}hbS7QY zF8U_vm>t`;ZQHhO+wR!5osMmEoQ`eV=p^sXclLjA&dnZY>@n)1YShJAv(~JMXNo0k z@4hu@NM_o7u|eK$LR;&UDL10Xo_pl*4+=K*DVeUD=RV;^C7+|8hK)8xszdz`x0!&6 zGZ=k?GPKX6hy0I>-5-cxszJi>-Cgid5C*|Zp{K6HU8R=U4^p8`93&idIn$0hwC)w> z9R)^bixV8dU^eWF_87T|_2qc)WXw|}f7x$PVY-CCR8%EKH!TkihU-8_q+&_p5X7s6 zcEzhE4rND!Rdy{_*6%b0hj^=8p3K^iv$f^a}LY_SU72qJw*fO{1Cup#SvkEZWC~RM&Oq9GmZxC=`MQ%+bBFe zo-+O=m0>LECq107(2dn*!Y|3Y005j7NZy&NNWh)L)m(l*3PhRz?a_=Pm?0g2o6Azk z-HxYC#1Rn#7WyG~6nc9X-{4OoVB*;9(D`{fI>Q!3r$9B0!w-Cs6Nn{h2l! ze)A;Sf=BU3NEaI+zyO8QDA6rBH$mp)R^}I)nZPrQ!b?(!i~7gdj~UgnJItww^a}0X z4jt(|QnBY0*E8yHHxi&~yGU^Vu${5d{FD>c$uW9jj`!MvI1_fxfz0`r8d(1|Ri#?t zs&0K!Tt8vf`^Z>hmUYy*6G?JDxey;BZ5an~Lf&tVcwXzJ6gzs~-6JI&t$X2WGUb>7 z#e+YU=?#B)p}UvkL}5I?Qg5g8$$J4uw9_^AaVw8|Ng&8P+X>k?!+6@+{fJ2`5 zASa8+sB(IWC-DcMk7=yNANV@pEejHOj$|60TBK7BfBJ9V@wFgq+Kec;g@hA9o{0=! z$8nBa(A~M-4}|pohFtZp?c*f0k@f%HEwPVirEq;uga~Y2{z`80Jt@eVSG=SUq+(@& ztc~o3PHgVf)!GHfAI7*l<9k*Pw-#ekf5$5&{&fi&wE4S;*pR)gt&GQ%XF{l|yZ!M+e?Y_>9$b==qx->f-*2bF* z1MU4$7#v;?8F7w0|Jcf5nW8jr7iGyIt)LHk>ASvK>7F&xxbhU2`DlGbw@dh5%v6`f zUsR3;3po+J_J?d@dY%h1(T~GmgxDslFyxw+vA95k^Y+hCHz9t&8M(sg6$G$v_tSQb z+?I03E~+Is7?3&voH9WvCJ~7|&Xm2;Hebni&rB@jNexQnac%Lxr)dKJJLNodq*vd~ zUnEJ>NPWdo`&;FwMk`Cymo;_MMs6m63xso5mMS zLqV&F276u66$Y&>!)H*{g#(2<Ahy8jR zVlvucsKF*|YT%-0&n#u0Sz7HTnzTqfEMG0WcuKHweGZM)zf)VAdelV2cS}PaG*OCD z3~;NIulCbWXG&%j!G>3LBa2_Io@DA!*9OUkDD=uFF%TUIAj%ktM6T6L8X?CFl8$Ox zo(-isCDU;c1Fv(2Eqt|?dHkP&COq=F%0G4ow8MJ*(>e?C!PTfX;Du(NTt#`4P;1)V z7RlY4xMI%pmoTlrusLHL8kv7={KSz81U#g(ef@p0Dda0-z);h=7~uNT7BpSS%$rEv zjS2yVr-5E5*|Edzbfea(Ks7~$5RA_V+Othy zy)UumBLT2-Q2JDiUEF6Lgsyd)Dc}m-c2S_L0pc(&%C4_MCIy^Eq5!cZy42T$Y5pz0 z$q$o%h;k_>KMWtukl3hJ}`Q)2zqK6o&3R2Ll<0Qp&31?~`Y^CWh+uS-Vo3>U_a!IFKn3ubO| z0oNIUiLyaaXQi1`XQcc#_6M$LNM6WC!7zy|RHFRNmXl?s!EA>Qq}_WGeOO6AgP)qY zJB?Ftmqp6qR9@X?fhqysS*6Xrd*g*mUnX%lqhws1%kLV%Q9By!*YEAnGE#$w#|*%K zf?=!rFtjj6{j02SR`#Ob{A6$`l&)Mh2%lH3bh3WI+Dv&@Q$}f^xkk)2mIyxw1s{aA zSj~F=#t4V&Y`$7m<-(3sKF#nI`b?YD_0oNVNimd%JZ{XlsV)tDVZX3<2l)Ni>xc0m zc|f4?0U5NGg`PRIA$KD*jwi#5U9~xi5-Vw|-!p!MZZX6ML3$zCuzY1OzXC-sSBqcB z{~py+c93&N1BQ>Skoe#}@P^y~$W zaU9fs&{E&_^R=s-HS;MxDaReqT%`?uog6D%*tTJ6h^KD)+d@{(mT(6mf!9%NmCmN| z&Ta7pS<1!mlN92hDO#ZUH>Z4$ySBZAHLXH?g)=6#o+kYAlL2{QP}o$e@QbqH?JFb< zit0hu4i)wst^6?fvzsxx&<=wY-uvV}8SfSc{`p9O11aOkvy5gfkmzHw^fin-e+}cP zwC(AVD?I{nJ#ENZa=i%9O(O0re*c|2U+HV-CRhLe@l%)wHPS3 zpv|%61#~u1F^@JB+bSZxZ+k&59h{!UdM{TKiu7~rjA~6)imaovE5_;{OYeOjI0^4<}dzXs%+>`vOQ3|`^8ZH z<>1K5#<64Ro_Te!@a#yoV&S;uMrI?@Szel-70T%K_3z~@%NSg?(~dTK)zF-wV>A{hDL{MRmI(*5aOx{#T_Iql9Q{oU@2-phlK z^j=1i!cfqC!wZX*3=Xc(I~ua?a4Yop<5Rm^2@Dp)liOyVPMz=urXSl-n&_8@s?-=N zx5OF$1VS`k%Qi&^zL8151q+`T;dhz)AUQR(dPyDT9sKQ8w$%z`v!AJ{ z$4=FWb|nW)?wNm<`dq|jY2K^#Z|`2aMxCRwT>oXv5gM_au1BxMQA)sn&>z!7M1hv- z(z?Y4I(Fr$f#e9Ye{s_b|N2ulZhqv?6kNuMB>8z`_=k@QfViAJ5OQ7QvbHn9_5)L` zWldt@>VAcrZIVh2`^{QDqJy~Xdgiqce@Hd3Bgb4Y@n~8$_FRGjd&YSAahLW0a-dVU zB@X`AYDPRrHOaqqA}O&9okUJ}IbFtBd$KUAnXJ?&DUAEw(nK~DG_K0pWmsp#rmLT| z-bWSppv}>>JuQkW=Gt{{DY;U2DR~7{Ospl=?s(v2~KJj{i2dJ zii=kY)nJ9SP*u(c7QIjTE5%?V-C%7V`A*t+g|YZ*w4+gRH}nHfhB0czO&0Je=Vd`) zdpid6cRqrLX6YOvS@r4Qp}K>r*IFRIC_SN)w^l#4$fvG8MKCmb@{|}`V^d10?bgF_ za5_L=SEVwj*Y{N{X7!ReY!qL-H(0A>*JIo3o;Lp4Q;NH6eONb5gFY^(`TMw>&=ud! z4RBcr#eNP# z2Wd;u6)eQQ7O}|T7`yGBHv*vi4d?t^u*HRZk4MBKz7!`NEEuRvhY!&`lw?l%Hu+te zw+X84UpdZNsS%JE%xw)??V4!9W46lnqT6Fp;(pb<&gsCcOm5dB?4Nfd)8$V_ z-kb19=Z#}qhIWZxkUu4-V!lo)WHOLVO3C zi%oROz6*NelN;a?7^ifvtUWeGdg}vBFb@6QF)m}UwmlB8e~R#rw729c8?qxOe@y!Iy=? zMY+zf&9-P}$+ThYXGu064pUnV!JJM$!ILf6eDl;z1V#}geObCAU(iF;3a135KaIZ5 znrLWmF%9O#>3)mkNt{Xc=BMOW4bjsy2IT3G$jUl|0!=h3tdf?#$W_qgR z|Gp77vvmJk9W-ve*Gu~nF|N{9rum7NNZnc^b`Ar(Vw)6QYGCN0B=*@El`gA+;M00@ zKHp?_g|3NyDJn~xI%6OW#bA5>4ek0aA!l_}-|;Yfs`--37yCHRC;Z@M=TFVdGxg2o z1c54G?sL{EaMtX!$?6On=f<)7HhB79$byo}snD$!PSAA;PM$Ov&+)YEMeF(Kv{$gZ ztY{umJg~5n!jWl2GOLl}pM%X6O6qpbdCvxrbSHKrnWTW$=qj|C* zEsS5(I?2#kLXF19!p-W)rb-wT&Wur6l^^$pso1Hq9%HzIPQIbXsDV>B-hC=vV&|TZ z3K=ud$So%jIrlr=>P$%&0G|J2v@l|g?pU9Y0JzLxDG6=h_=6xk#w)Yb#%pksJ|oVw z{3@>p|CuRt18`o?2(dj~0Qx3+^TyXI8S?KiO`PkB@DGQdGI(_Qo|bmo#TKs{*ql;c zd%y%3*pfgUn?`C=eK9B}Cw=jkUkuo%osbK4)|bs=rKiJ+DZF1Q;iiZtc%8 z;k9T}aR=LLF-Qr+espcCW*?rcI4? zj}yfC(>SNW%WUul4nToX@^#SJN=9o7X9y8|at6Kr13whIGxtvCh~2#cYVIKL5IA9E^#IR*9vikm1pX?r9uStcZZ9s)VQeZ32t&*kBiaK(cEzJUziB{ z*J-Mvr$uQPe--=^B54x$E&Nt?)vyuyuRy4MqfI208R(jlukd1-^?KkP`{a<|;Md;N z-OZ-D{@omCnu?=dQ*n$lf~m~iSTKjYy?>cv|HJf;kW@gPNA~X zWfPIQc)Z>=_lWwX1`7MUT#r_we*EqgJ=1+n3!r!DmVk*NWWeTtg8gL$fz=sidBWFY zN``IHCal;i1yGhBm~@tW5o}E#O`Lek7>!PuK>rw%K|Cvg@b5$5iZaZLp4iZC@ z!$X^RVIu#S&S2|M*2VRZE_pEkluIM4$p?e|L+rOA+dQ-Dw=ulw*z9}wuS2$B^4Oka z+{qjpG}g)pVd8C#%TQi+5Ghh!n?vQ8Dgvyljpg`4=-*(`KTLHbJP5iBB~r=i2GZRw zj-m3|(t6_AXNj>RNj?5?PpQ#xke~Q53B#ThUU8KG0LY0WR18f+=` zDh|t5ZsPO)Ny$nCyIV=f*62DJU0GcHFEkrr$k-3_?87uFa1Z(rN&Gqv8dWgfy@~r8 zQtLg|QfQ_X2_J8Qt;Kl-=O7Uz*69eF;1%+b;XMyv1Z6D4L8h^iJc&`R8pdk4r7qs=d1${;aTKz`8KU=dQlUDRht{jsm^>>wEk_N$95 z10y2o_L#->F7qSS0&0IMysnT44-36a%iNr3F`bX?>b8(Drh`c%5U>RYDgFuoaI6Ef zSnadkpL)zI2p#p_B*-_N8dx5Yy-Ux+VA(>`v7Y59he6TUcdv@u?MmF!)P2Tt;!MDM zejio}6jT;A#A>b2SoM#rM|@Jd!#^JL`su+x*&Llfw8i!(Vyv~0D>ssAh!29>6bHEO z_ZJp%$ZC8rx8u?tZ(F3;$0JxY3ai~lpWWss^dZ^D-wqKrhtQcTn)3oEOZOv9e1&VR zej~O?LAwP_s;iZkKRScfk?*3s650Y4Khk^=XSiWoazzu@g}HTOhU@(*ClnM@!qik# zN81l}EESxm%Yd&qs;xnCW>~r_EWtgp>~Wfsso%XSZFo2ci)llzQUg#H+Occ^NwOXM}{t{muGfioFYA;%tKOshT{ zC-)6YaR6l+)b6CQQhOU1u|jD<7~?4c@Q&4C01$)dvu+YvD@?xUZ!+pY@)^F(a{vHY z`^jHB&3seb7sIoki;CW&IvF)c7Q#?mQExfB#@7LfNeUAHxv{z&{Op@ppDF*+6E3d zG}4;aoDr#yuFQ+3~HI+Lg&abeL8Hs zytDgiS_Q}Hco$k};QIn*kHI-(AEd9e5x(5avoq}a-%HD;?f$M6Nno|!cfEub=b6yQmDo?|ReJ$Csg z7nVN+Jrl^P-8CT)%#;BhGq}lDtm2}T{@Ly*U2feugYtYhm-q4&Xc8?{a0odPtPvN9U zYT5C)sI}gT3*6pe9($+qWHyJ|Yj`CsKpl3t#oix-!iP+dww>^@gu1qS0N8=*P?}4g zcHsX+f;xKtyGbq6l@Cb%X3l>?`O5!WmI5?z8EPGAGy?|snZIffLzA=<`jf$gve#E_ zs&_|RQFmCQgB0SxB%u_~gMKZC!w}|F0(_I@E5uCgF?Cb#qV0>RCvZAD0@0%_-!5(* zK$zM{$)@Hr+m#NkT6|^S9d#(}D8Nr;e+rlVvgaO6e;aF$tM}lcBTfQw?Y1ADB0Ez;Da5G{g z!C7Ie6v>Npjw4TZ)TouA7Ng!pdxz*$!mOp|0;J8dzk5jo09=`hDI~@N0dW)E#L(cJ z5uzHi&TO9FQ*9O6G7uCKjtJxcYzvyS)1)?~C9I44961|~@j5bIk*-pWAk^u? zf5opGj4iA+%wyK*T!gslJ%7?R|4u~Q4ggs_DlHd=vlzrTI+$_)>27m?y2Wnom6W{S z(+;Cl2T4~?lr&sH1u;b@D3|a?1JJE3nWPc~2)rF(HX=b%5{k=gq4g=v<}D;Adk?7v zEacrpev|VpF{d<;lBM@5xX2}j~c?57ZKo~hY8fCwY(Wdtj`JccgbZiu-SJRY&7^hy|!TcO7COB zL!p;AYjeZBfTf6Zp{;X&_lK-P;oBihPvmKVFA_1z&-tvN*w0`q4Ca8?%k7bj&j1&p0SN?yMslB|8SP#sW z$pe>C|6I?!fqz_GE!?Weq3#MmdiYt3ei;oSmX67~b1xAcXZy_&w|;A+nL^buoN!HT zEbfe}SuK0fJLBsP|J*kIv$rckc#VEf>V#mk27Z7EMmEY50$6mnz=7mmE;B~`jbC3k zMD|UUNKf==%G&${nnPLvPK(>c)hEwtD4-zT>o~IJwW+pbV+wY?Qx+T zf8yT9fl)dIG=^#XM?6GJQCZbB#~ZuNF$~C8+?2F{u=+Jlq!bDJWiUrUW*|z;Iam3!RdWqbhI3dZd z22kR`SQs|=^=5RUwW`|nF(airCm(acY?C3j=m8@M3`-+}U%5P%oGwRgX5 z2oBT}X2MrOcfU{~12GgsV)~j5BptNfh1!JmV4<3Rciv9DV9PwM|79hzV&;-md%Z6Ks;CK1A?q1SXAQ<)CH9 z)Ez33DI2I~K1ExO8xtCHSxob+LkRtO$&+TP!C|(zhigX4Xeyhq@Sst6qAHmN@~Bnz%1t#u;e)T7>6xW$De((xtF|k)y^- z@^^?kx4N)i_W|20W7SFp?6eh#WmRgF6$^xa4<4b2uC9D2HPnD$Ec9y?PI z{`S#Rg6R2KxkVyue~iNCUw{^I#sPp9_my3y*MCG*TDrbm7XC2aJIfiu#S?nt;?_T{ zf=iV3jh->SDwvn5kMDW~>J9uJ3)`$>R@1X$F)+}(n|(^P$$~J+Fj7gOBa3u@R-!4M zK(KOjZh@*q4ap%<4;<*|_?Z@7M1HkWHOSvWD#xG;PXyYMXt(c(A-Epf3_Ey))?0IK z#_M#FKrt-4$CyD4zk}ylh*>uZW=*!kKni#RQNH}yjK${lIC@6i-q^gI&QSk2Sv7l; zkywRTt&jiQ`80x2%okgZQpaWHV3m(FR4~#0W937FbSRvjrH}1trcP4F@AbqfYxTS%7o56=v=7Ctm>8)C7{M;-9e)?weMrGr}xQDEwjxTI5D9qm38_6p5Ar zRpTgQ9-jI5Hg#H$#NSmCgw<5WHR)5`4h6W8aY) zDCpDnUuC`x(A*`3fqswm;&tG=6q|~s+yy|e!K|Plsw|KXd~ebKB){cd8#_s@JhgJTKc~oSC-L!qQ$v}NGRPHzrW^i6gLReP#Q!(!`>CSV`oRMo9EE8UR4Lt$VUgsrc`Ei3pJVw*Rw2>4Vat!<^f~q;) z1`YkG2)3BjaqeWl$3Jm@3+4{%S-e~tsg2bMPkX(3GpuUED)zeX(MRNxK24jS(ZaLJ?C1%R@b3LX=#8}@&1rA#Qh`+t<02k@)%+ba1l zGY3Qn4r_)|xZuAhg{ZxLSa6T@N?ufqwhajW)&L)+05ydQ-PJ9Oalz4JA}6xpQ9gKV zbgXOjEqK7B&$S?gr!Ox<3~Q9c+I7Oustt3`nNL}Yfcy&SA9t~?f}H(Zu@t3B8p`O8 z#%d#=Ej;~q67u&@K?mKnQ~%Bu$j*hE%KxfNhH`*!=zap(CjbDuZ`>GQDU`qd|BrcS zk6PuZ}ytrr50BCJJaa-KICCyWfoG z=-GI{LcHS#XGahFNVS!rq9(`N2}$Ez&9b2tahbo8Ab<|CB7`#-$USELbvN2sZO2qFdpPO!L=ckV@E zPy5z-Q6P8cm_)8%ym?~IoS=du?&>k0-eEwozAuZgJUON&ESi%SK@pfDKK=tb*IT)( z+uz49rllnlp;r@%1sL^i812R9M+E6t4^EsO1$#e?;RnVTFgf+1`r2Hu0w_Z32D#tr zYd$SmlPxB6`D4DF)?p_KGcAD|X_|0+S>}Q%vXc{GHZ(5-B7|YCaZQYw`#PY)Vy=B| zPsGTY(S2+vMeC6wvKOQ`w(xVwol4jXQe!MyV&{-0j6FOx1KWQLr=B0y?tKv*eH+2b zbd5XQg5v(PY0QNt5Q=YE=pV%Jz}dJDm{>f zG?sqXFG`}Fyde*dD#Is1C}nJS9+Dt(8kLy{9w0Pma6D(iu6y=2(68#zyBKFIa!?8s zLxbs?57j*(XefZtBO&#$*hYJ@km+g<)Ba6c?{;!rb?+QxlgR2o$qKd1lt4t!>riZW zW*>O>TWKOYymOq#S<^keCw#dyTvj^)n_qB)K=gQ`1dr56IiLg=R!l$))2_(uU5`rN zGO^AzxW!!(mX(C*W1T8WmTw|&kPK3BgpUs6q&;WE(gMLBEc8>cZw)mhAn#7~OhIU1 zujybaFp78b&ScpUt?mm@|4=4)!r$r|skeGy`?V=ui@;1qQ_w>N3Qt+0clun@NTC0V zi{|TTULN7}os&1mRO|IaIw@^tD~(?WjAB37{P0`eyVATpLV?VsNM%RIMD)8 zDGX=?7OpXt(D^YQ#uDJWlB7!brIyi%vnm+eE(lsognIjl7`dvxfG-PaMip2qJ}v2` zSeFsVVwt)^qSoVC3BC~t%bf=6mfaRdov9TIN7;PIXIBHzoDt5-CnukE2|`8$zDxV~ zvc{*zW57EgV9_o-Kbk|2`KzNN4>hQ2g#yq@Dt@T(`e;2T3`;Z`Z+XWoL-)u0CAJDH zB=r`QTVgX&`>GXdtp7sI$`{+|3N_<%@=nhuxDe*0pJmMpB5v2YayDx^y>a1@s(+07 zC|fsjj%O4Qmiax3#m6&ABkNm}kW|sl2~6ix2ldx4Sg2^J07ly;5%v2DcrRfeWlTl( z4A3&V>4ct{=YbR@3X2&Rk#!j?r}a-nQ*NerMn?+ul7B=C9;%|gb?kdxPKFMr&GWQO z&&%eFN-(7udfO31qdX#_`23L(xeQ?|F>8fRp53{be`~g_V#Q(%=!AYsnO*i z(MANKjMW$_|T!tylmi$ttvwnhrp-xSyPKz3A{%@M1k&;~4?uRdy4WT>eF zpvCr$sI4OU>GMkZ9A#^u1{9ECBP`cwtxGvE72U6Gp8L=W_YCtSZ#?EvL=hbtw}W&$ z)oKlrO3a2|^IG-+%fgTl>~BI74Y^eVEOdT}eAd)%<$Sy6lh5mY^-{F52&?^|+z&Jp zExt`0#cf0aQj{GEd42cl3N5KZ&mL8@nu`9@kAnNx%{XU=B1Kdd%-$>r;}{;H$Ee{n zqzXv| zr>1t%Z&6k{okO2Nq(bW3TfD9#549=FvhFGT8Q{n{H;~xxN&=GjBRRo!{9OcOmv==A zjGjrH#7?45c`iYe?u7Fg`N%ktEoCWC!&p)EczR1Jc|Gsa$JT_L>L+nc3Qn3B+b+M; zhs0CjLT?zlUX|>7b2!KLI;}B}y<-Vp-=BBC_gW084ge&ZRM9dXmthcpYga>uVz-wzG$jwF+ILz{>f>T}l5a z`91C`VVgWMp-qy0NA#mSPDJUv7&ktGtJct+2F5!neAga9v$CSL-M@)|a?~FK+!sl| zvO;mqBqZEc<|zym6rKVjA#g4v^b%Dkris)VXUCWA>P}8?HJ7LGraeskpWkai|C1 zIAA2gQ-Qo~S{+fI84RLY!TOiadNPd?KtCiez{TuF6Y!~5Rvkb6Xg~vtqwLhQE<`3o z`ZN8b@HC$}m3C$gOqyu!)~Mp@CYUM(k8=Wn$MXdUj_?aKoOCX7_cdn*1JR>cv!Ons z6xxZk+@2$l^qkiEhm4`^LFnVPI33njsb4+faZ{MRDP8~6^IX5L5+&m`0|w~;qiM<^T} z5cn!RmPTQ?H$mcf4Odye(Xw%8a7odS6ux{IX7n??+)zsgRuaQ+_)EcF2Fg1_AZDPN z!Bys6)WBplVE~qhy^exC;#$W}WFr2L?gLe_woF{hN7w`hpa^t)@nZ_yW7N9GD7|&Z zgH^x$MJkx*`Eug}1fG@kB+ztr5w!ef{_o{nJ>SLe7zu?teM$6A=Ykcu4E6J0T@zDW zRu86wnp(A=&W-2NcZp3tQ;J*aDHs`mQ5K**wgxK&aB08+!{8>yN)vw0E{2*4#Xd|mn-t-T#M*`OSuBk4}OG$H#zg1IQeF1s3n`=hNRN-!v&@_&b ze>R%k+{Hkv;PhaEi4UDqAw-t}Vtb8Ie?mWxXCC%`{n^B2ooxr@PimabV^j{ZrrAvo zT}>#y)|2*Y93NIJEXP)=W;uduHBcNb7FRYI;C5KG^<83L}gOk)F*Y zDZ5|1AKq$lSo%EBWVOeWGHEahPF`k;Vk3Mpc%w$ic7H;{vs7=CXE4Rs)7*mcyAGX9 zw2$AGbTvUM!723KLn`mYjv3eL*=vRIGtKASELY2I#`vkR?z=(bSPmpL-fYS_x!bOs zzJpBS?i-8s(_?>C)}TkFR6e2^|DR+FDqc5oF8OF@)C|AoO4~Yap6!FL#8Ga9S(?nNu^qHjD@osH zQmyDq2Ko#WAJ*D%z8k!*(<&KgC(!bd2%o%Ly`F1C{HX#Z0E`Ru_lY%{JVhsVLUu>J zw&?lqY2T1-aY4eXb&3$i;^R4!i!atN)m^RJm%3nG*zMhInhos6KDcqj|fB|a;awTg>!UAGq;jsF!Xnm0#C za4TzPCnjF-I?pOv2X)CpAKvYQ%_r-K-Oicl7nU%5Z?iZi*jT_ROO(p0|C8}bauVEh zGwAEu>cAz&H<5fredLm$63tytAl&ViS^pG#M5sTkYBRJAi>-A?o5W5K5!BIj8>uW8eAdZp7SOeKh}Q`{)rBbrYkl zAB$E-T4C@CTDnpA0M(7om70aI7~%5<|60ej#RmN7*HPUUZXaH#ggAc&JEs@S=X2w? z-?Iz{QJQrR5Fa?N{P~AKOoivyT=Tlu7=5H%j7PC8uAZ9)f{7IMq>DU~ORT0vlBZ znc0YNVld-Og?YF~x_pEd$~ql6M|4byo8z;kXS*FECb*+&+MxHXE8PixUn5996y*zC zOCjO_h9TF#FX)K-&!C*SA_y>`51;JV&cznPS#D_-uacbfVsu*#MdM$PrEm}~mbIFm zjj~7PltX{BsoW|I?rU~PnHljV%>^r;%M?vX7Pxe6=dJny`I7}jkw1hlJw5I>e)xd*m$iafG2jeW7GHz#xWz-&&h8_XdagwUq*(&?^RQ$bsQ zunt3t2B%6kads}$lIVsR{1Wn$9}85&txzgQYW_S9ZWALBeUR(CzQ*UaGZUli=E34KxnNuV@OSLwZf(^im|SCYLda z=LPX`DkZB}ud_%svu=NNkQu++_aC^3Me%$ICtVz9;V5zN#_{1R#uk zsveyNgf02PRQqb=4UTG8ua>am@A$pc{9&e&ENb4>S{m*Z2`yD^S$z(H?3Qeloj~0P z8MVkLy0(P?3AAiMPw1^}h=N=sK2?!+STP{-_@}>4#mN|Xxf%SEky5o^WRYySv#k-C z(tlBm2gGQzB*Ga=MWeZbQmu7-{MtUJiRfyp?o=7;EO}RZU&}th!2VM$*m~!^TAcMv z=^^{%Rl(s`*pmBd?r1P+J^!*J*b=^+`FDL7Kj{Pm=hsfY>cvCnDYp=O=*FAFjNzSc z_A{KQNZ_by*BCJj!qfgEsP<}qkwxUoE&zZ+3{~6^H~RLCO#r??^7y{tjXQ_!PLZad zbqG&oN3u!)>yaqM${msFiIYJS2?WLbJVZu8rcdHcr+HIeBW0rmWim4xgR?@oNY3=L zZ@(SyKNMUF+MkF*?z%)$jml59e(OmF7~|fe?qBO=ngFoaCq51{rhR?g5{*N>h>%Dr z%jv_{#y=bYtS8DksCW)Z*dpYdl3Wk@I$h{fj(!-b$22CwvOibQu%r0)Q?MM_+caWDb%gNTA#}7vr zcJKN`QTSg6K{z~?h~R&Q#q*HemX&J*0qjV&$^>Z{65n6o10+wtS^@}KcuI7{1IYzZ zB+nlJl3AmjA()Pf@(SGm68@>nSi0oHWC&ob*Uzr`ZWMqS$Z)$nFQT?~oi!uuvB1|Au;?feAy4B(n6qKc5H_ zIq}A^tQUn9Y9R(-UqkJv$*LTATDOO2@LeEY_(3k?KWtlZY)zUn?-w}CcL|$m8FeHW zjK!{Q6nbx@iprj@wwILk@8EvXwdu}+YXT}k|O^PYQ7%A;drTw%M5J_OmbRxx7;~@lR zkxgS0(%Z*_pyCAq1$?TJKyVCn(`%0aK6T4IWFce^&&gHar`1PFB^iHbDEGfuXBPHT zlyAVYjN-N9(JrP1by~zz&OQjsxKJ=Q$6mN&FAX|U_4bP$4V^70>DJT745^05Gtp45 zBR6FXo=NAP-}71V=#um;i!i6W*&CJz^XUR+Sp+q? zy{2s&nlY3=dGB4@PR14>r=#+U~jq72p=YKmn4dT<5hnL;0H(;K_k0tC6J& zdYWaN7eGM!J6Yy<`KqTiVs=$cL~L?MP=m25N`8-|kwWv)IuFiYC0?Dnr;sQMo%GL8 zi`XtA(bFeaKaCb6NAEE^T2q$-i2I^2%)ZuvMkdI$Eb(R#je$%rBLG5r&{ShGOK>9a zM+Fa0Ku_q3#Kw`>$F|yJ%JCXgzD!v!*AEm@JR<$1xM}Bl>oM=M$-#3*Wuvxt{W)b3 z=ceRAN;jI1TqE((#>l|P#{jCj+9daP4rC0e-10i(B_erQj;Nbph$HxidiuS&; z;6~NS*UW1S>vB`QE&r;+E$-4|rCrVg1Nm00|M2;Z2%($P3rccD@*VzQ6oH2to&TtX z-W#fK+{L_@ByTX-ec=&w*4!r_d9uxq@hLk`!;>rw%cL{W(1s7LaQaO&PHhoPTl+WQ z67e9bxjW92cFw3Z3UY?B+`>}JS^xYtRiFbDk!7I6NzPy@4etj=Y63)t{62w#+!$`D zyz+47a*1ufO|$9>QK}+ROQi=Td%+K34sTm4 z6#!fsegP*m;;*`q(H%12(r6hVw+#*RQqOd_CR51@Fjh7s$bkYTco-uJPJb*CmA&H5 zL|0IITY>UhSvXhNaL!cu#ki7;Dc4cgI`^ygJ?Zx9bcFE-_Um=;HI2Av$>^>xx0~&+ zQ}E~>fh0^EhR^K-kRB@0$_z!v0!4W}mRBkpPa>A+eHy~&445W_=<4{zd_cNiRzU=G z7~L2J!>du`FJU=wU`5|-r}ZkE$mh^BX)H|Ah#3Cw(uchkRH-ryFsJVJmiTE3WhlHx zJLVs4Wm48ERhH0HT1x8wwj^_sM|Eq+K0Q%?uj+1bjuBz0 z|IAC?wIy09GnCKK9^ufHW(t=tdZxL(lLQL=1KFu79%qm^cqRNadHWJEUJ?)r2hQ@^ zpPLiu*e28u*1)wCC}Q;qZEg4Xap3ysvF#qKeoI!tOVE9oK`sCEyB(=zxr>N2qu@<& zM0lNc0uQ;Zgv*Z1Qru#P!f1c?PaytQxPa8UW0hg7&cT3NF~~wAO-gE$NpYv&xA44S zR!jXTM()2cSI0uevRr&z*ZtHd`d~}qYuzj^0ISTKz5&|HkPDAr#MLw03T(y?pVi3+ z+>#cb!@s2v7-NUxJApi+{ce-_rMbQ8c$c!6OYC|?EC@6Byv@~=mO4g?pqCQ;s|4c* z#EYodl?s3zjDtUM=3_W|Zhj!wwbgzq_Y=RdD!Hz2R0^l=nH$zTgeT1&e*gDFs4Z_` znJAi!YLfR{Llkl{SeJ+O&|(2M$qI=iS#&<)qr49+-VKvbO!2*>L5%FQtYrd!MAI4? zt25MKUnBj`m>YI863no1xxah4{4ai2{XbS={e z4tj3(L+!mju}|5b(s>MQ&$+GRc7-OYpqsC%>q%sauR&ly{t>e@{4!R8dvAEXwzZHs zP#^xr=V>fiWO^qu9czSBIyEF0<}0`G9MX7W-|EnNZAkY+cGzGXJILmpYgV~)w~H)Y zP*lRLd5X1Kh54$7eJM9*mQ<- z81v6J4CjxRE%A!{O*f}4*VgYBZA}}lsS2>JPJ(ja+*x}MgK5Flkw=C-KUxO=Bpaj6 zErbLU>UmP^d$dQ>iDLHK+ot*uKA>TzH99l8L?eE&P)-$EAoaHN4}iD9gfy!03L7Bc zl9;ag(8F!#){Yw54a4+n%yQZ!SRGQuB17l6dapA3H;5x(lk?;~G9^yZ9(=8J5Lc(! z^{5Fnwt=!6GWTdD`rcsTQsT4|do01_>NoAb>`e=@t^E^7woM+}dv!3VY zeb-uhz3;o;we7tZdo#zL=vN1ZJ(qJ$ZdMw3H{oQvfmXO@AE#K(=2prB?J*_to8`m8 zHo3)(P7Ms!`&&n5gL12wWt!%*?;=L%Iq~*~9Ga{@Su%d>tEMcnUAL0uBY5kQwM1Lt zg?Woz)h2=I(lqNAn(cPwbB$|iXZP?nmnd)W*;(bJw9v+}=-%on{$iDTmEV>)w!PT( zN$}#zu$^XgTY7lQ^qH)Ryf~xE{0D}4s^(3%UbXFg^|sSqZj7vdmix@cO%AFtHDw2F z%d)IWZm*wxn|&msoa6d+)X5UJk_CeEw|~|UxiS5E80%U@cdqN1pdvrh^AGmxZ0D*T zTi2tnQSD>DJMz{n(_V?#bmzvD8_iEQ-OQew!?sGls&QoQrNWBT?qs9jO0R9rPHTJp zo12|$i(~ICmg)+awL4~c{Y%lZ=lPa5?MnSE)V^-Hl)rBNaegXqQHM<2gu>^xqWUFc z8RM_^bXqRUbrnwz)!4vt^62i^c2PS1i$$|~3euFMteqp0o-A6@m~H*E?8X`O>Mk+Q z?89%@rrIXPrME}M+hnebvR>NaHMRVL`K2o_|5 zz5G!tq8n4^RSB%zbyrtR>KdD_okq^B*J;&_Yb*#FRg)ar_od3M%WP-X$&AU|;^=Zg zL`~@<`-26a7${j0^`KLe+gYrOj7m+d&Bc}Dyh`nxvyJdZr~dpDbL{y0HA*W;-Q)7S z%B7>X--sesH_X>3%r>iakSiXm*p<_I__BOb)%Pxu`vTS5f8M%rdu@w|>x?~yX8Bh6 zjGN-2IW;T$W=ORDurK1YVc(}u($zYfp0eFWsEuDiY${?#ap(14yTBJrtO&j5 zkK=PXw6i**RC`;tSRRjF+f^tPH;-NB=Z3VcZ0TIo*e@ZNskRHj~2UTlV*5bXXG86MBj*!XJv30yR-gk?^iM37^ z_a?q=3p$@`G~r8;M-j<|jX$18mOoZ%S?@#=)(U#U_p>d*e?XIrFC2+jF#5WrLs2*>MTCOplrd<$TkgU`B}X(`d$o zOu-uxWDAqxvCg5NsO2s_WKwT%*z;wPWSVs6WZpKVvwp5Y?%n*&S_impoh2%6_FXNj z%{$56Vw;ANz|&=8dXuhMcIPV0PF2{wuTbNStWunwQ%_KarB>LEJ=Ul06iO{y`%5Ov z?s;fpa+~)9<%Zdz50jj)MYr2xHze8Q+biX3h%J(A9WNk2fJpp!0&foFkRtk+kX7}B z!FfBR_5BJu-s>d9Z0NDS)kW}c_kMmb+ZSEVd|#nYynyj*8MFq!AC?dN{3pL+v%>O4 zP6-EUb2V=jaMRlgtF3kH04rMFCG}cxdR_oPj=dPB51qLksPfSs(1D zIiQcc6T)}*gI*BA4|%sJ^q3U3zyp^NVHs*u*dS(tsoO%O(DIuj2AYTbRO#-60 z7!J|sze_Z{AoCi`EX7mS^jDNtlt$8DNiq6k$sD&TCx%FPx81MSs=zq;Kz0cJxFXUF z?x3y`!WX6-s?hgOgbu`eK$rzx3R=VgjmrVxbzc$>D)Ao;d5?dOZ9yB3!7>S@;-Gw2 zr&Zc7v<>-t`{k)UoR&g+knjA+{Ez+o-Z=m4@qe{^yj$l#JFkCr|L>hQLw>)8oA)2J zKJUec>&b`pkB#HK>&Qo3mp=3}Tt5C=`@`A$*m(_SXSn_k zTbvAiR9pg{puQaP88`$EBUeiye*n9H9sKx01_F#Fj)alUCuAgI6&(jVhG@eZq9VP} zwV>0OQo)a?TSI1mv&hjFNSX&4EAlBEXAgZEz`8KJK_z&yu|U2Ei)c?!oCFB zUxOHM9BuSj=psl1h|>Y%8Ja6^0Dq(dJOS4O=u<$e}pe z#Y+cthUOXTNM-@7mjYc!#3HT?c^}YxAeWMokd3gb0{sm1U!WU6dO+g5Vom}YBgT*L zLhK8m-vQqMTlCF=q%p4m7(>njn)}ZH)`X$+4g)!akwI=5I<^G(1I#Uwk1ouAgB6?3(~H6DE;h)JRzV`v_zU96eVUO)6J18w5wkePt`-T{z*;s+Lk9N0mgMb}}R z2tboG@+dqXT2KiGiXnWD07zQs0fxYRJ%`VRT+QPOB~&tT=oSOpl3;dVAocTWnc_3B gkI~8H2T+6KIc(uzYLwWs1)kGQrcN`SYGi2iFYnR8ga7~l literal 0 HcmV?d00001 diff --git a/ui/public/browserconfig.xml b/ui/public/browserconfig.xml new file mode 100644 index 0000000..d416bc5 --- /dev/null +++ b/ui/public/browserconfig.xml @@ -0,0 +1,9 @@ + + + + + + #ffffff + + + diff --git a/ui/public/favicon-16x16.png b/ui/public/favicon-16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..c1d5410ca5b20ac7fab8679797e17993a2a8f95e GIT binary patch literal 599 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6SkfJR9T^xl_H+M9WCijSl0AZa z85pY67#JE_7#My5g&JNkFq9fFFuY1&V6d9Oz#v{QXIG#NP=YDR+ueoXe|!I#{XiaP zfk$L90|U1(2s1Lwnj--eWH0gbb!C6T!z3=kBhdN76e#rE)5S4F<9zSMy!pq<3 z&e^$fWgv&g=`81(#FZ;llW$B2$yw9Qo;QGdE|SJ+OOImfLp^cl~mK@7~w+k4UEa{HEjtmSN`?>!lvVtU&J%W50 z7^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+10f+@+{-G$+Qd;gjJKptm- zM`SSr1Gg{;GcwGYBLNg-FY)wsWq-oMBrd`u(D}m@sEN_j#WBR=_|mESvxO5Sj@y?{ z-?q)s$s*+bPIo zSuWe!A`P{j>kr>8-1+3zj60wGzTd0e_kG`a|K>XH!ba(s^l6uuFt*Fy`#fuU&7!jm zn>T0%E^e&~>(+A3S#$V4N+D&uvu=+bgpQCO)6@d7iP(k0`a!W0uX_5`PkApFXkt+6_LItUHX|mpry= zxLnNMSgbsKvuuk|qO<3nOA3X-tw(b2S|6>NbnwwnrHdKN`8&i`9^<<%we;UZsc?Pg z-tYC>H#rx{JU#Ldm_AfXTq8@|Ck%5(=k+y-M zm4QL+I>|5;4Y~O#nQ4`{HSm01_zI{YyecH3Bq*_5p`a)~Ei)%op`@}PRUxyWB$Jc1ICAEQ i%n|m}4IT@;^cY@=3zmFxGMx&vg2B_(&t;ucLK6VlWMi=a literal 0 HcmV?d00001 diff --git a/ui/public/favicon.ico b/ui/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..bdca308388e8d0eb56209064117d8204765d8554 GIT binary patch literal 15086 zcmdU#d5jHT7RReK+O7#>OKh33^F!=@v5O$~2qOIK1Ykx#wQTNpVu0 zjvXDnHg$geo#S+K9H(j1=yST`+|#mjz4CrudB^#)3baxgRdJ>%Q*P+DxtAjTt*-XE zEB`_H7v;;!kCopme~a>mXc_O2K^7f!OM+6>DOL0*EUpay%@BEyp%K@KsclqK0}aN0h%R z7Zzh)`uNCF#wWfDLpmBbQ**0o6-BP zaERF5P|7~Dop7v5XePI6f%a+#X&5G4Kx3$*if0iNqU?t|W zL<|_Wu2WJ{q;ccM(zEE-hNL2z+n#YSpSG_3PJM!nX+ZembkWFym%pzNJP@p)8pcaVg)nUSx5XkS{A5Np+W^&wQ7~zyLV55 z==JN@vSY^%>D;-q`Odd*-^Pf2{rXidT(}?u1`Lq7bLUESc6N*m1&?@Ka`Hrc|w3)%dGXqlTP5ecCS~@7}$W{{8#Am=-Ty93w*k z*gY5%-GkY#`4Y*w@5YTACttpNiSc{l#0jZbv7(XVdhFP-7#aSP-o1Ofw&u^DA0tD- z+O=z4nGoOci^^OIA!lsH!+-qvaTh;r?dZ{?G1&b0=gpfJBU1qUcC8)in@b^nnW2_5g0!ukh$S;x!rroESKNojP?&Dp#&-Y}Bq@TP|O|9E0u6n>W(4XHVD9>eZ`bWGGm+Y?&() z0vBUHb3j5JVv%Xnrpb{bM*@8kKQM0&9z59iXRh@V^Y-Dxhh^;8u`+t}Xt{dzs$ay^ z&E(0G6JqgU1v_(a!uX+0n>HpciE$V$pl&~Y{Af66en#x}?b{}%<2|xLMC{YHZQHQ; zZTygD{6_pq-L7B1-o%;y_-yaqy|Q`pX8H8#QxGf@YmOW_QYKBBWMb1mAuZgrX;YZE z*?ymm-|~!~?SIBPfcD+5Uq7M!o;`b3sQZ;GR|?;_Xwf3Me*L<^m#dUr|UBys!EwOoE)^j$e_d)NeTbLY&U2EGlA3Eq3ibS&0M0=#m zIgZnW=hu!?lWRl|ws6hRj?5Kr`U5CuRqn#m>FYSErizZEYO47EYc|hmxgC-dG7+9V z*9@L@gz#S}E=tP4Sb(1hSwcv{C-O@?Vv2EB{BCF%jmTGTtGBEINfH`XlXeyYhYI&&v9h z2^pDe&7XT105Vgp8UCMww<1&vw{yY+wtUVW__f{X;pd z{jhS_#C9SAV={bOjM$D>5Bx`eSGKary5hs%9+=x`C&}{Zw~H@)TI!)U3HLhnz28P* zo^6f`|MXA#YGLdZM3*rI-))rd`~5d)&_HI)m?5)f&2o9poH-_6IDGhU;oiQRJ$tr{ zA3t8Yb?YYF+j4tt(uA2W=KJu8Z^p}*ajkv(_HyOQmG4rDA3l6A`Ms@Mw|k1CsZ&QTUAkn8g*@k3d@}~bj6dZ3`Tm_dcM4+&`!gp`o;3SW zULUVsy)t7Sh!134C8G93r8Z|QYNFB2eIdI@WS+Qb;EMLA{ z=%eM!mlx!^cI_%lmoAlzj0_{sxhHEU;Cf%n@XZ{+T#!gM8Sj|$@C^~C7We$ioNuY5 z;}39@Wc+38p(Nu^i=iarZ;OF?AWkib^@qhkd-z%Tsd7R4AG+A^$J<&Iis+y8P5NrK za?CiXWqxeP+*f8yLD%+iy)7^$ZT&5;Yl>u~i2l*5H22UqN3$)Y7P2)<5mD@LhNciK zJ9&_m;~I(TXR9r2VjCa$!Y97TQ;?Qus_f<|^3EKjsrXRkUzHy!Q|B=El<^LkAgZs0 zTa{la`~N%p%sXU|^%Bp2Q?_z>U6HjmD(XEp^V0FZM+aTL=>_`Cb(T4g>%I3rBOBF0 z_b<_B88c>#?BBm%cJJP8=I%Xv_Q<|{`^>t9^Qs399FPeUCU|k6`@rfG>vO(+&(W^FQP&>*iqx^CYgCLwO%OakYMuU)%l&QB0SbLNyahxbfoR5wtcGbq^M zY;dPeoy-|k^u1@2qWUiW>C>m1xbE@e$L4$o@gw*-YeURM9Mi5{J3oH6{y0m$aN$B( zw{D$@Lx}k}H_F*d&S)-Kvc$wGw!ZX=uKWMla?UTn#4q+NgZDlv$A6A)VcUBoF+N|r)i#7N7IayT(-viSNk4WbkO}D4xlFd literal 0 HcmV?d00001 diff --git a/ui/public/index.html b/ui/public/index.html new file mode 100644 index 0000000..bb28b70 --- /dev/null +++ b/ui/public/index.html @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + Meli + + + +
+ + + diff --git a/ui/public/mstile-150x150.png b/ui/public/mstile-150x150.png new file mode 100644 index 0000000000000000000000000000000000000000..c985dce01e927540fd2f5ca5f798c0e634d2233e GIT binary patch literal 3401 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 + + + +Created by potrace 1.11, written by Peter Selinger 2001-2013 + + + + + diff --git a/ui/public/site.webmanifest b/ui/public/site.webmanifest new file mode 100644 index 0000000..de65106 --- /dev/null +++ b/ui/public/site.webmanifest @@ -0,0 +1,19 @@ +{ + "name": "", + "short_name": "", + "icons": [ + { + "src": "/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/android-chrome-256x256.png", + "sizes": "256x256", + "type": "image/png" + } + ], + "theme_color": "#ffffff", + "background_color": "#ffffff", + "display": "standalone" +} diff --git a/ui/scripts/build-info.js b/ui/scripts/build-info.js new file mode 100644 index 0000000..49a6c75 --- /dev/null +++ b/ui/scripts/build-info.js @@ -0,0 +1,12 @@ +// eval $(node build-info.js) + +const join = require('path').join; +const execSync = require('child_process').execSync; + +const packageJson = require(join(process.cwd(), './package.json')); + +console.log(` +export REACT_APP_VERSION=${packageJson.version}; +export REACT_APP_BUILD_DATE=${new Date().toISOString()}; +export REACT_APP_COMMIT_HASH=${execSync('git rev-parse HEAD').toString().trim()}; +`); diff --git a/ui/src/App.module.scss b/ui/src/App.module.scss new file mode 100644 index 0000000..7462a1f --- /dev/null +++ b/ui/src/App.module.scss @@ -0,0 +1,32 @@ +$sidebar-width: 300px; +.app { + display: flex; + position: relative; +} + +$header-height: 70px; + +.header { + position: absolute; + top: 0; + left: 0; + width: $sidebar-width; + height: $header-height; + display: flex; + align-items: center; +} + +.sidebar { + width: $sidebar-width; + min-height: 100vh; + padding-top: $header-height; +} + +.main { + min-height: 100vh; + //padding-top: 30px; + padding-bottom: 100px; + flex-direction: column; + display: flex; + flex-grow: 1; +} diff --git a/ui/src/App.tsx b/ui/src/App.tsx new file mode 100644 index 0000000..46d5cd7 --- /dev/null +++ b/ui/src/App.tsx @@ -0,0 +1,153 @@ +import React from 'react'; +import { Route, Switch } from 'react-router-dom'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faPlus } from '@fortawesome/free-solid-svg-icons'; +import classNames from 'classnames'; +import { NotFound } from './commons/components/NotFound'; +import { Footer } from './components/Footer'; +import styles from './App.module.scss'; +import { useAuth } from './providers/AuthProvider'; +import { Legals } from './components/legals/Legals'; +import { SignIn } from './components/auth/SignIn'; +import { Home } from './components/Home'; +import { Orgs } from './components/auth/Orgs'; +import { useCurrentOrg } from './providers/OrgProvider'; +import { TeamView } from './components/teams/TeamView'; +import { TeamList } from './components/teams/TeamList'; +import { OrgView } from './components/orgs/OrgView'; +import { SiteView } from './components/sites/SiteView'; +import { SideBar } from './components/sidebar/SideBar'; +import { UserInfo } from './components/auth/UserInfo'; +import { AddTeam } from './components/teams/AddTeam'; +import { ButtonIcon } from './commons/components/ButtonIcon'; +import { Search } from './components/sites/search/Search'; +import { UserInvites } from './components/invites/UserInvites'; +import { UserView } from './components/user/UserView'; +import { PrivateRoute } from './commons/components/PrivateRoute'; +import { FullPageCentered } from './commons/components/FullPageCentered'; +import { Loader } from './commons/components/Loader'; + +function Header() { + const { user } = useAuth(); + const { currentOrg } = useCurrentOrg(); + return ( +
+
+
+
+ {user && ( + <> + + {currentOrg && ( +
+ + + + + + +
+ )} + + )} +
+
+
+
+ ); +} + +export function App() { + const { user, initialized: userInitialized } = useAuth(); + const { currentOrg, initialized: orgInitialized } = useCurrentOrg(); + const initialized = userInitialized || orgInitialized; + return !initialized ? ( + +

+ Initializing + +

+
+ ) : ( +
+
+ {currentOrg && ( + + )} +
+ + {/* public */} + + + {/* user */} + + + + {/* user && !currentOrg */} + + + {/* user && currentOrg */} + + + + + + + + + + +
+
+
+ ); +} diff --git a/ui/src/Hello.module.scss b/ui/src/Hello.module.scss new file mode 100644 index 0000000..4f0b7d3 --- /dev/null +++ b/ui/src/Hello.module.scss @@ -0,0 +1,3 @@ +.button { + transform: scale(2); +} diff --git a/ui/src/Hello.tsx b/ui/src/Hello.tsx new file mode 100644 index 0000000..60ffadf --- /dev/null +++ b/ui/src/Hello.tsx @@ -0,0 +1,29 @@ +import React, { useState } from 'react'; +import classNames from 'classnames'; +import styles from './Hello.module.scss'; + +export function Hello({ label, hello }: { + label: string; + hello: () => void; +}) { + const [counter, setCounter] = useState(0); + + const onLick = () => { + setCounter(counter + 1); + hello(); + }; + + return ( + <> + + + ); +} diff --git a/ui/src/assets/images/bg/bg-bottom-light.svg b/ui/src/assets/images/bg/bg-bottom-light.svg new file mode 100644 index 0000000..a83086c --- /dev/null +++ b/ui/src/assets/images/bg/bg-bottom-light.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/ui/src/assets/images/bg/bg-top-light.svg b/ui/src/assets/images/bg/bg-top-light.svg new file mode 100644 index 0000000..e30371c --- /dev/null +++ b/ui/src/assets/images/bg/bg-top-light.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/ui/src/assets/images/bg/nav-bg.svg b/ui/src/assets/images/bg/nav-bg.svg new file mode 100644 index 0000000..ff79928 --- /dev/null +++ b/ui/src/assets/images/bg/nav-bg.svg @@ -0,0 +1,10 @@ + + + + + + + + + diff --git a/ui/src/assets/images/git-servers/gitea-white.svg b/ui/src/assets/images/git-servers/gitea-white.svg new file mode 100644 index 0000000..b7c9679 --- /dev/null +++ b/ui/src/assets/images/git-servers/gitea-white.svg @@ -0,0 +1,5 @@ + + + diff --git a/ui/src/assets/images/git-servers/gitea.svg b/ui/src/assets/images/git-servers/gitea.svg new file mode 100644 index 0000000..c849d0f --- /dev/null +++ b/ui/src/assets/images/git-servers/gitea.svg @@ -0,0 +1,5 @@ + + + diff --git a/ui/src/assets/images/git-servers/github-white.svg b/ui/src/assets/images/git-servers/github-white.svg new file mode 100644 index 0000000..2e4f966 --- /dev/null +++ b/ui/src/assets/images/git-servers/github-white.svg @@ -0,0 +1,5 @@ + + + diff --git a/ui/src/assets/images/git-servers/github.svg b/ui/src/assets/images/git-servers/github.svg new file mode 100644 index 0000000..1dcdc2e --- /dev/null +++ b/ui/src/assets/images/git-servers/github.svg @@ -0,0 +1,5 @@ + + + diff --git a/ui/src/assets/images/git-servers/gitlab-white.svg b/ui/src/assets/images/git-servers/gitlab-white.svg new file mode 100644 index 0000000..5004a35 --- /dev/null +++ b/ui/src/assets/images/git-servers/gitlab-white.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/ui/src/assets/images/git-servers/gitlab.svg b/ui/src/assets/images/git-servers/gitlab.svg new file mode 100644 index 0000000..8bf1d95 --- /dev/null +++ b/ui/src/assets/images/git-servers/gitlab.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/ui/src/assets/images/logo.svg b/ui/src/assets/images/logo.svg new file mode 100644 index 0000000..03ce235 --- /dev/null +++ b/ui/src/assets/images/logo.svg @@ -0,0 +1,6 @@ + + + + diff --git a/ui/src/assets/images/notifications/email.svg b/ui/src/assets/images/notifications/email.svg new file mode 100644 index 0000000..1f9947b --- /dev/null +++ b/ui/src/assets/images/notifications/email.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + diff --git a/ui/src/assets/images/notifications/mattermost.svg b/ui/src/assets/images/notifications/mattermost.svg new file mode 100644 index 0000000..a069988 --- /dev/null +++ b/ui/src/assets/images/notifications/mattermost.svg @@ -0,0 +1,11 @@ + + + + + + + + + diff --git a/ui/src/assets/images/notifications/slack.svg b/ui/src/assets/images/notifications/slack.svg new file mode 100644 index 0000000..98522fb --- /dev/null +++ b/ui/src/assets/images/notifications/slack.svg @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui/src/commons/components/AdBlockWarning.tsx b/ui/src/commons/components/AdBlockWarning.tsx new file mode 100644 index 0000000..ef72ac8 --- /dev/null +++ b/ui/src/commons/components/AdBlockWarning.tsx @@ -0,0 +1,20 @@ +import React, { useState } from 'react'; +import { Alert } from './Alert'; + +export function adBlockerEnabled(): boolean { + return !document.getElementById('adsjs-wlegKJyqQhLm'); +} + +export function AdBlockerWarning({ className }: { className?: string }) { + const [hasAdBlocker] = useState(adBlockerEnabled()); + return hasAdBlocker ? ( + + You seem to have an ad blocker enabled. You may experience issues with Stripe payments. + + ) : ( + <> + ); +} diff --git a/ui/src/commons/components/Alert.module.scss b/ui/src/commons/components/Alert.module.scss new file mode 100644 index 0000000..493e0f8 --- /dev/null +++ b/ui/src/commons/components/Alert.module.scss @@ -0,0 +1,44 @@ +@import '../../styles/variables'; + +.alert { + display: flex; + padding: 0; + //background: transparent; + margin-bottom: 1rem; +} + +.icon { + width: 42px; + min-height: 42px; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + //color: $light; + font-size: 1.25rem; +} + +.content { + flex: 1; + padding: 0.5rem 1rem; +} + +@mixin alert($type, $color) { + $accentColor: transparentize($color, 0.6); + .alert-#{$type} { + color: $color; + border-color: $accentColor; + background: transparentize($color, 0.85) !important; + + .alert-icon { + background: $accentColor; + } + } +} + +:global { + @include alert(info, #5c85d6); + @include alert(danger, $danger); + @include alert(success, $success); + @include alert(warning, $warning); +} diff --git a/ui/src/commons/components/Alert.tsx b/ui/src/commons/components/Alert.tsx new file mode 100644 index 0000000..5dc3215 --- /dev/null +++ b/ui/src/commons/components/Alert.tsx @@ -0,0 +1,40 @@ +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import React from 'react'; +import classNames from 'classnames'; +import { + faCheck, faExclamation, faInfo, faTimes, +} from '@fortawesome/free-solid-svg-icons'; +import styles from './Alert.module.scss'; + +function getIcon(type: string) { + switch (type) { + case 'info': + return ; + case 'danger': + return ; + case 'success': + return ; + case 'warning': + default: + return ; + } +} + +export function Alert({ + type = 'info', + children, + className, +}: { + type?: 'info' | 'danger' | 'warning' | 'success'; + children: any; + className?: string; +}) { + const icon = getIcon(type); + + return ( +
+
{icon}
+
{children}
+
+ ); +} diff --git a/ui/src/commons/components/AlertError.tsx b/ui/src/commons/components/AlertError.tsx new file mode 100644 index 0000000..be04277 --- /dev/null +++ b/ui/src/commons/components/AlertError.tsx @@ -0,0 +1,16 @@ +import React from 'react'; +import { Alert } from './Alert'; + +export function AlertError({ + error, children, className, +}: { + error?: any; + children?: any; + className?: string; +}) { + return ( + + {children || error.toString()} + + ); +} diff --git a/ui/src/commons/components/Bubble.module.scss b/ui/src/commons/components/Bubble.module.scss new file mode 100644 index 0000000..50ca5c4 --- /dev/null +++ b/ui/src/commons/components/Bubble.module.scss @@ -0,0 +1,8 @@ +@import "src/styles/variables"; + +.bubble { + width: 20px; + height: 20px; + border-radius: 50%; + display: block; +} diff --git a/ui/src/commons/components/Bubble.tsx b/ui/src/commons/components/Bubble.tsx new file mode 100644 index 0000000..4888439 --- /dev/null +++ b/ui/src/commons/components/Bubble.tsx @@ -0,0 +1,24 @@ +import React from 'react'; +import classNames from 'classnames'; +import styles from './Bubble.module.scss'; + +export function Bubble({ color, className, src }: { + color?: string; + src?: string; + className?: any; +}) { + return src ? ( + bubble + ) : ( +
+ ); +} diff --git a/ui/src/commons/components/Button.tsx b/ui/src/commons/components/Button.tsx new file mode 100644 index 0000000..fcc3ce5 --- /dev/null +++ b/ui/src/commons/components/Button.tsx @@ -0,0 +1,36 @@ +import React from 'react'; +import { Loader } from './Loader'; + +export function Button({ + loading, + className, + children, + onClick, + disabled, + type = 'button', +}: { + loading?: boolean; + className?: string; + type?: 'button' | 'submit'; + onClick?: (event) => any; + disabled?: boolean; + children: any; + [key: string]: any; +}) { + const onButtonClick = event => { + if (onClick) { + onClick(event); + event.preventDefault(); + event.stopPropagation(); + } + }; + + return ( + <> + + + ); +} diff --git a/ui/src/commons/components/ButtonIcon.module.scss b/ui/src/commons/components/ButtonIcon.module.scss new file mode 100644 index 0000000..7f94534 --- /dev/null +++ b/ui/src/commons/components/ButtonIcon.module.scss @@ -0,0 +1,26 @@ +@import '../../styles/variables'; + +.container { + cursor: pointer; + background: $gray-100; + color: $dark; + height: 30px; + width: 30px; + display: flex; + justify-content: center; + align-items: center; + border-radius: 50%; + + transition: all $transition-duration $transition-effect; + + &:hover { + font-weight: bold; + background: $gray-200; + text-shadow: $_boxShadow; + } + + &.disabled { + cursor: default; + color: $text-muted; + } +} diff --git a/ui/src/commons/components/ButtonIcon.tsx b/ui/src/commons/components/ButtonIcon.tsx new file mode 100644 index 0000000..a2e1337 --- /dev/null +++ b/ui/src/commons/components/ButtonIcon.tsx @@ -0,0 +1,28 @@ +import React from 'react'; +import classNames from 'classnames'; +import styles from './ButtonIcon.module.scss'; +import { Loader } from './Loader'; + +export function ButtonIcon({ + children, className, onClick, loading, ...props +}: { + children: any; + className?: string; + onClick?: (ev) => void; + loading?: boolean; + [props: string]: any; +}) { + return ( +
+ {loading ? ( + + ) : ( + <>{children} + )} +
+ ); +} diff --git a/ui/src/commons/components/CenteredLoader.tsx b/ui/src/commons/components/CenteredLoader.tsx new file mode 100644 index 0000000..ac3984d --- /dev/null +++ b/ui/src/commons/components/CenteredLoader.tsx @@ -0,0 +1,10 @@ +import React from 'react'; +import { Loader } from './Loader'; + +export function CenteredLoader(props) { + return ( +
+ +
+ ); +} diff --git a/ui/src/commons/components/CodeSnippet.module.scss b/ui/src/commons/components/CodeSnippet.module.scss new file mode 100644 index 0000000..de84a44 --- /dev/null +++ b/ui/src/commons/components/CodeSnippet.module.scss @@ -0,0 +1,100 @@ +//@import "~prismjs/themes/prism-okaidia"; +@import '../../styles/mixins'; + +// copy pasted from docs site + +//Colors +$color-tertiary: #ffffff; +$color-accent-primary: #661aff; +$color-accent-secondary: #ff7f66; + +//Fonts +$font: 'Oxygen Mono'; + +.code { + flex-grow: 1; + height: 100%; + overflow-y: auto; + @include no-scrollbars; + background: transparent; + border: none; + + * { + &::selection { + background-color: $color-accent-primary; + } + } +} + +.container { + :global(.hljs) { + margin: 0; + color: $color-tertiary; + font-size: 14px; + } + + margin: 10px 0; + position: relative; + + pre { + width: 100%; + background-color: $primary; + font-style: normal; + font-weight: 500; + font-size: 14px; + line-height: 160%; + //background-color: #1B0449; + border-radius: 5px; + //padding: 20px 30px; + } + + code { + flex-grow: 1; + display: block; + font-family: $font; + color: $color-tertiary; + padding: 20px 20px 20px 30px; + overflow: visible; + } + + .token { + &.boolean, + &.string { + color: $color-accent-secondary; + } + + &.comment { + color: rgba($color-tertiary, 0.5); + } + } + + .line-numbers-rows { + font-family: $font; + font-size: 1.1rem; + padding-top: 20px; + width: 60px; + border-right: none !important; + background-color: rgba($color-tertiary, 0.05); + border-radius: 5px 0 0 5px; + visibility: visible; + display: block; + height: 100%; + + span { + &::before { + color: rgba($color-tertiary, 0.25); + } + } + } + + .language-yaml { + color: $color-accent-secondary; + + .token { + &.key, + &.punctuation { + color: $color-tertiary; + } + } + } +} diff --git a/ui/src/commons/components/CodeSnippet.tsx b/ui/src/commons/components/CodeSnippet.tsx new file mode 100644 index 0000000..13cea04 --- /dev/null +++ b/ui/src/commons/components/CodeSnippet.tsx @@ -0,0 +1,51 @@ +import { highlightElement } from 'prismjs'; +// languages +import 'prismjs/components/prism-json.min'; +import 'prismjs/components/prism-shell-session.min'; +import 'prismjs/components/prism-typescript.min'; +import 'prismjs/components/prism-yaml.min'; +// plugins +import 'prismjs/plugins/toolbar/prism-toolbar.css'; +import 'prismjs/plugins/toolbar/prism-toolbar'; +import 'prismjs/plugins/copy-to-clipboard/prism-copy-to-clipboard.min'; +import 'prismjs/plugins/show-language/prism-show-language.min'; +import 'prismjs/plugins/line-numbers/prism-line-numbers.min'; +import 'prismjs/plugins/line-numbers/prism-line-numbers.css'; +import React, { useEffect, useState } from 'react'; +import classNames from 'classnames'; +import styles from './CodeSnippet.module.scss'; + +export function CodeSnippet({ + language, + className, + children, + lineNumbers, +}: { + language?: 'json' | 'yaml' | 'typescript' | 'shell'; + className?: string; + children?: any; + lineNumbers?: boolean; +}) { + const [elRef, setElRef] = useState(); + + useEffect(() => { + if (elRef && setElRef) { + highlightElement(elRef); + } + }, [elRef, setElRef, children]); + + return ( +
+
+        
+          {children}
+        
+      
+
+ ); +} diff --git a/ui/src/commons/components/Confirm.tsx b/ui/src/commons/components/Confirm.tsx new file mode 100644 index 0000000..fb373f1 --- /dev/null +++ b/ui/src/commons/components/Confirm.tsx @@ -0,0 +1,66 @@ +import React, { useState } from 'react'; +import { AppModalProps } from './modals/AppModal'; +import { CardModal } from './modals/CardModal'; + +function ConfirmWithInput({ match, onMatched }: { match: string; onMatched: (doesMatch: boolean) => void }) { + const [doesMatch, setDoesMatch] = useState(false); + const onChange = event => { + const { value } = event.target; + const matchesCurrentValue = value === match; + if (matchesCurrentValue !== doesMatch) { + onMatched(matchesCurrentValue); + } + setDoesMatch(matchesCurrentValue); + }; + return ( + <> + + + + ); +} + +export interface ConfirmModalProps extends AppModalProps { + confirmLabel?: string; + onConfirmed: (confirmed: boolean) => void; + requireInput?: string; +} + +export function Confirm({ + children, requireInput, confirmLabel, setOpen, onConfirmed, ...props +}: ConfirmModalProps) { + const [canConfirm, setCanConfirm] = useState(!requireInput); + + const closeModal = (confirmed: boolean) => { + setOpen(false); + // true check is important because the onClose returns an object when the backdrop is clicked + onConfirmed(confirmed === true); + }; + + return ( + closeModal(false)} + {...props} + > + {children} +
+ + {requireInput && ( + + )} +
+
+ ); +} diff --git a/ui/src/commons/components/CopyToClipboard.module.scss b/ui/src/commons/components/CopyToClipboard.module.scss new file mode 100644 index 0000000..8cc3768 --- /dev/null +++ b/ui/src/commons/components/CopyToClipboard.module.scss @@ -0,0 +1,9 @@ +@import "src/styles/mixins"; + +.container { + cursor: pointer; +} + +.blur { + @include blur(8px); +} diff --git a/ui/src/commons/components/CopyToClipboard.tsx b/ui/src/commons/components/CopyToClipboard.tsx new file mode 100644 index 0000000..5b2c21b --- /dev/null +++ b/ui/src/commons/components/CopyToClipboard.tsx @@ -0,0 +1,49 @@ +import React, { useRef, useState } from 'react'; +import { uniqueId } from 'lodash'; +import copy from 'copy-to-clipboard'; +import classNames from 'classnames'; +import { Tooltip, tooltipToggle } from './Tooltip'; +import styles from './CopyToClipboard.module.scss'; + +export function CopyToClipboard({ + value, children, delayHide = 2000, blur, className, +}: { + value; + children; + delayHide?: number; + blur?: boolean; + className?; +}) { + const [uid] = useState(uniqueId()); + const [copied, setCopied] = useState(false); + const timeout = useRef(); + + const copyToClipboard = () => { + copy(value); + setCopied(true); + + if (timeout.current) { + clearTimeout(timeout.current); + } + timeout.current = setTimeout(() => { + setCopied(false); + }, delayHide); + }; + + return ( + <> +
+ {children} +
+ + {copied ? 'Copied' : 'Copy to clipboard'} + + + ); +} diff --git a/ui/src/commons/components/Currency.tsx b/ui/src/commons/components/Currency.tsx new file mode 100644 index 0000000..69340eb --- /dev/null +++ b/ui/src/commons/components/Currency.tsx @@ -0,0 +1,20 @@ +import React from 'react'; + +export function Currency({ + value, currency, symbolOnly, +}: { + value?: number; + currency: string; + symbolOnly?: boolean; +}) { + const text = !symbolOnly + ? new Intl.NumberFormat('en-US', { + style: 'currency', currency, + }).format(value) + : new Intl.NumberFormat('en-US', { + style: 'currency', currency, + }) + .format(0) + .replace(/[0-9.,]/g, ''); + return <>{text}; +} diff --git a/ui/src/commons/components/CustomSelect.module.scss b/ui/src/commons/components/CustomSelect.module.scss new file mode 100644 index 0000000..0f7732d --- /dev/null +++ b/ui/src/commons/components/CustomSelect.module.scss @@ -0,0 +1,77 @@ +@import "../../styles/variables"; + +:global { + // https://react-select.com/styles#using-classnames + .custom-react-select { + text-align: left; + + .react-select__control { + border: $input-border-width solid $input-border-color; + height: $input-height; + + &.react-select__control--is-focused { + border-color: $input-focus-border-color !important; + } + + &.react-select__control--menu-is-open { + + } + } + + .react-select__value-container { + height: 100%; + margin: 0; + padding: $input-padding-y $input-padding-x; + } + + .react-select__placeholder { + color: $input-placeholder-color; + } + + .react-select__input { + + } + + .react-select__indicator-separator { + display: none; + } + + .react-select__clear-indicator { + color: $_blueBell; + } + + .react-select__menu { + border-radius: 5px; + background: linear-gradient(179.21deg, #FFFFFF 0%, #F8F5FF 97.11%); + box-shadow: $_boxShadow; + } + + .react-select__menu-list { + padding: 0; + } + + .react-select__option, .react-select-custom-option { + &:hover { + background: #EEECF9; + } + + &.is-focused { + background: #EEECF9; + } + } + + .react-select__option--is-focused { + background: #EEECF9; + } + + .react-select__option--is-selected { + background: transparent; + color: $primary; + } + + .react-select__option--is-disabled { + background: $input-disabled-bg; + } + + } +} diff --git a/ui/src/commons/components/CustomSelect.tsx b/ui/src/commons/components/CustomSelect.tsx new file mode 100644 index 0000000..a3893b8 --- /dev/null +++ b/ui/src/commons/components/CustomSelect.tsx @@ -0,0 +1,90 @@ +import React, { useEffect, useState } from 'react'; +import Select, { components as compos, createFilter } from 'react-select'; +import './CustomSelect.module.scss'; + +export function SingleValue(props) { + return ; +} + +export function CustomSelectOption({ + innerProps, isFocused, ...otherProps +}: any) { + const { + onMouseMove, onMouseOver, ...otherInnerProps + } = innerProps; + const newProps = { + innerProps: { + ...otherInnerProps, + }, + ...otherProps, + }; + return ( + + ); +} + +const DropdownIndicator = props => ( + + + + + +); + +const ClearIndicator = props => ( + + clear + +); + +export interface Option { + label: string; + value: T; +} + +// https://react-select.com/components#replacing-components +// https://github.com/JedWatson/react-select/issues/3128#issuecomment-521242192 +export function CustomSelect({ + className, components, value, onChange, options, ...props +}: { + options: Option[]; + value?: T; + onChange?: (val: T, option: Option) => void; + className?: string; + components?: any; + [key: string]: any; +}) { + const [valueProxy, setValueProxy] = useState>(); + + useEffect(() => { + const val = options?.find(o => o.value === value); + setValueProxy(val); + }, [value, setValueProxy, options]); + + const onChangeProxy = (val?: any) => { + onChange(val?.value, val); + }; + + return ( + s.value === size)} + onChange={val => setSize((val as any).value)} + className={`${styles['size-select']} ml-3`} + /> + + ); +} diff --git a/ui/src/commons/components/PrivateRoute.tsx b/ui/src/commons/components/PrivateRoute.tsx new file mode 100644 index 0000000..004f3b1 --- /dev/null +++ b/ui/src/commons/components/PrivateRoute.tsx @@ -0,0 +1,30 @@ +import { Redirect, Route } from 'react-router-dom'; +import React from 'react'; + +export function PrivateRoute({ + component: Component, authed, redirectTo, ...rest +}: { + component: any; + authed: any; + redirectTo: string; + [prop: string]: any; +}) { + return ( + ( + authed ? ( + + ) : ( + + ) + )} + /> + ); +} diff --git a/ui/src/commons/components/ProgressBar.module.scss b/ui/src/commons/components/ProgressBar.module.scss new file mode 100644 index 0000000..f4b01c1 --- /dev/null +++ b/ui/src/commons/components/ProgressBar.module.scss @@ -0,0 +1,20 @@ +.progress { + background: #EEECF9; + border-radius: 100px; + height: 10px; + position: relative; + transition: width .2s linear; +} + +.bar { + height: 100%; + border-radius: 100px; + overflow: hidden; +} + +.gradient { + background: linear-gradient(89.33deg, #661AFF 0%, #FF7F66 100%); + width: 100%; + height: 100%; + position: relative; +} diff --git a/ui/src/commons/components/ProgressBar.tsx b/ui/src/commons/components/ProgressBar.tsx new file mode 100644 index 0000000..27275c7 --- /dev/null +++ b/ui/src/commons/components/ProgressBar.tsx @@ -0,0 +1,36 @@ +import React, { useEffect, useState } from 'react'; +import classNames from 'classnames'; +import styles from './ProgressBar.module.scss'; + +export function ProgressBar({ + value, max, className, +}: { value: number; max: number; className?: any }) { + const [progress, setProgress] = useState(0); + + useEffect(() => { + const p = max === null || max === undefined || Number.isNaN(max) + ? 100 + : Math.round((value / max) * 100); + setProgress(p); + }, [value, max]); + + const width = `${progress}%`; + + return ( +
+
+
+
+
+ ); +} diff --git a/ui/src/commons/components/Toasts.scss b/ui/src/commons/components/Toasts.scss new file mode 100644 index 0000000..57cb12b --- /dev/null +++ b/ui/src/commons/components/Toasts.scss @@ -0,0 +1,34 @@ +@import '~react-toastify/dist/ReactToastify.css'; +@import '../../styles/variables'; + +.Toastify__toast { + box-shadow: $_boxShadow !important; + border-radius: $border-radius; + padding: 15px 30px; +} + +.Toastify__toast--dark { + background: $_blackGradient; + color: $light; +} + +.Toastify__toast--default { + background: $_silverGradient; + color: $dark; +} + +.Toastify__toast--info { + background: $_blueGradient; +} + +.Toastify__toast--success { + background: $_greenGradient; +} + +.Toastify__toast--warning { + background: $_orangeGradient; +} + +.Toastify__toast--error { + background: $_redGradient; +} diff --git a/ui/src/commons/components/Toasts.tsx b/ui/src/commons/components/Toasts.tsx new file mode 100644 index 0000000..080a62a --- /dev/null +++ b/ui/src/commons/components/Toasts.tsx @@ -0,0 +1,11 @@ +import React from 'react'; +import { ToastContainer } from 'react-toastify'; +import './Toasts.scss'; + +export function Toasts() { + return ( + <> + + + ); +} diff --git a/ui/src/commons/components/Tooltip.module.scss b/ui/src/commons/components/Tooltip.module.scss new file mode 100644 index 0000000..b90aa28 --- /dev/null +++ b/ui/src/commons/components/Tooltip.module.scss @@ -0,0 +1,38 @@ +@import '../../styles/variables'; + +.tooltip { + background: #1b0449; + opacity: 0.85; + box-shadow: $_boxShadow; + border-radius: 5px; + color: #b0abba; + padding: 10px 16px !important; + + text-transform: none !important; + text-align: left; + line-height: $line-height-base; + + &.place-top { + &:after { + display: none; + } + } + + &.place-left { + &:after { + display: none; + } + } + + &.place-bottom { + &:after { + display: none; + } + } + + &.place-right { + &:after { + display: none; + } + } +} diff --git a/ui/src/commons/components/Tooltip.tsx b/ui/src/commons/components/Tooltip.tsx new file mode 100644 index 0000000..5a2aa19 --- /dev/null +++ b/ui/src/commons/components/Tooltip.tsx @@ -0,0 +1,22 @@ +import ReactTooltip from 'react-tooltip'; +import React from 'react'; +import styles from './Tooltip.module.scss'; + +export function Tooltip({ + children, id, ...props +}: { children: any; id: string; [key: string]: any }) { + return ( + <> + + {children} + + + ); +} + +export function tooltipToggle(id: string) { + return { + 'data-tip': 'tip', + 'data-for': id, + }; +} diff --git a/ui/src/commons/components/dropdown/DropDown.module.scss b/ui/src/commons/components/dropdown/DropDown.module.scss new file mode 100644 index 0000000..bb4e41a --- /dev/null +++ b/ui/src/commons/components/dropdown/DropDown.module.scss @@ -0,0 +1,41 @@ +@import '../../../styles/variables'; + +.dropdown { + opacity: 1 !important; + padding: 0 !important; + border: none !important; + + background: #ffffff !important; + box-shadow: $_boxShadow !important; + border-radius: 5px; + color: $dark !important; +} + +:global { + .__react_component_tooltip { + &.place-top { + &:after { + display: none !important; + } + } + + &.place-left { + &:after { + display: none !important; + } + } + + &.place-bottom { + &:after { + display: none !important; + } + } + + &.place-right { + &:after { + display: none !important; + } + } + } + +} diff --git a/ui/src/commons/components/dropdown/Dropdown.tsx b/ui/src/commons/components/dropdown/Dropdown.tsx new file mode 100644 index 0000000..4de0c5c --- /dev/null +++ b/ui/src/commons/components/dropdown/Dropdown.tsx @@ -0,0 +1,47 @@ +import React from 'react'; +import ReactTooltip from 'react-tooltip'; +import styles from './DropDown.module.scss'; + +const minLeft = 20; +const minRight = 20; + +export function dropdownToggle(id: string) { + return { + 'data-event': 'click', + 'data-for': id, + 'data-tip': 'tip', + }; +} + +export function Dropdown({ + children, id, className, ...props +}: { children: any; id: string; className?: string; [key: string]: any }) { + return ( + <> + { + let newLeft = left; + if (left <= minLeft) { + newLeft = minLeft; + } else if (window.innerWidth - tooltipElement.offsetWidth < minRight) { + newLeft = window.innerWidth - tooltipElement.offsetWidth - minRight; + } + return { + top, + left: newLeft, + }; + }} + {...props} + > + {children} + + + ); +} diff --git a/ui/src/commons/components/dropdown/DropdownLink.module.scss b/ui/src/commons/components/dropdown/DropdownLink.module.scss new file mode 100644 index 0000000..97d892f --- /dev/null +++ b/ui/src/commons/components/dropdown/DropdownLink.module.scss @@ -0,0 +1,29 @@ +@import '../../../styles/variables'; + +.link { + font-weight: 500; + font-size: 14px; + line-height: 130%; + display: flex; + align-items: center; + padding: 0.6rem 1rem; + color: $dark; + + &.disabled { + color: $text-muted; + } + + &:not(.disabled) { + &:hover { + text-decoration: none; + color: $primary; + cursor: pointer; + background: $gray-100; + } + } +} + +.icon { + //width: 30px; + margin-right: 8px; +} diff --git a/ui/src/commons/components/dropdown/DropdownLink.tsx b/ui/src/commons/components/dropdown/DropdownLink.tsx new file mode 100644 index 0000000..9e461c7 --- /dev/null +++ b/ui/src/commons/components/dropdown/DropdownLink.tsx @@ -0,0 +1,53 @@ +import { Link } from 'react-router-dom'; +import React from 'react'; +import classNames from 'classnames'; +import styles from './DropdownLink.module.scss'; + +const defaultIcon = ( + + + +); + +export function DropdownLink({ + to, children, icon, onClick, disabled = false, className, +}: { + to?: string; + onClick?: (e?) => void; + children: any; + icon?: any; + disabled?: boolean; + className?: any; +}) { + const content = ( + <> +
+ {icon || defaultIcon} +
+
+ {children} +
+ + ); + return to ? ( + // TODO not sure you can use disabled prop as done here, might throw error when Link.to is undefined + + {content} + + ) : ( +
{ + if (disabled) { + ev.stopPropagation(); + } else if (onClick) { + onClick(ev); + } + }} + > + {content} +
+ ); +} diff --git a/ui/src/commons/components/dropdown/DropdownSeparator.module.scss b/ui/src/commons/components/dropdown/DropdownSeparator.module.scss new file mode 100644 index 0000000..8d76d2c --- /dev/null +++ b/ui/src/commons/components/dropdown/DropdownSeparator.module.scss @@ -0,0 +1,8 @@ +@import '../../../styles/variables'; + +.separator { + width: 100%; + height: 2px; + background: $_glitter; + opacity: 0.5; +} diff --git a/ui/src/commons/components/dropdown/DropdownSeparator.tsx b/ui/src/commons/components/dropdown/DropdownSeparator.tsx new file mode 100644 index 0000000..bfa5308 --- /dev/null +++ b/ui/src/commons/components/dropdown/DropdownSeparator.tsx @@ -0,0 +1,6 @@ +import React from 'react'; +import styles from './DropdownSeparator.module.scss'; + +export default function DropdownSeparator() { + return
; +} diff --git a/ui/src/commons/components/forms/InputError.tsx b/ui/src/commons/components/forms/InputError.tsx new file mode 100644 index 0000000..52ff3b3 --- /dev/null +++ b/ui/src/commons/components/forms/InputError.tsx @@ -0,0 +1,17 @@ +import React from 'react'; +import get from 'lodash/get'; + +export function InputError({ + error, className, path, +}: { + error: any; + path: string; + className?: string; +}) { + const err = get(error, path); + return err ? ( + + {err.message} + + ) : null; +} diff --git a/ui/src/commons/components/forms/RadioInput.module.scss b/ui/src/commons/components/forms/RadioInput.module.scss new file mode 100644 index 0000000..bb69e03 --- /dev/null +++ b/ui/src/commons/components/forms/RadioInput.module.scss @@ -0,0 +1,66 @@ +@import '../../../styles/variables'; + +$animationSpeed: 0.1s; + +.list { + display: flex; + flex-wrap: wrap; + + &.disabled { + opacity: 0.5; + + .input { + cursor: default !important; + } + } +} + +.bubble { + width: 20px; + height: 20px; + background: #ffffff; + border: 2px solid #eeecf9; + display: flex; + align-items: center; + justify-content: center; + border-radius: 50%; + transition: border-color $animationSpeed linear; +} + +.dot { + width: 8px; + height: 8px; + border-radius: 50%; + background: #ff7f66; + opacity: 0; + visibility: hidden; + transition: opacity $animationSpeed linear; +} + +.label { + font-size: 16px; + line-height: 110%; + color: $dark; +} + +.input { + display: flex; + align-items: center; + cursor: pointer; + margin-right: 1.5rem; + + &:last-child { + margin-right: 0; + } + + &.selected { + .bubble { + border-color: #ff7f66 !important; + } + + .dot { + opacity: 1; + visibility: visible; + } + } +} diff --git a/ui/src/commons/components/forms/RadioInputs.tsx b/ui/src/commons/components/forms/RadioInputs.tsx new file mode 100644 index 0000000..d5f79d7 --- /dev/null +++ b/ui/src/commons/components/forms/RadioInputs.tsx @@ -0,0 +1,47 @@ +import React from 'react'; +import classNames from 'classnames'; +import styles from './RadioInput.module.scss'; + +type RadioInputOption = { value: string; label: string }; + +export function RadioInputs({ + value, + onChange, + choices, + disabled, +}: { + value?: any; + onChange?: (val: any) => void; + choices: RadioInputOption[]; + disabled?: boolean; +}) { + const onSelect = (choice: RadioInputOption) => { + onChange(choice.value); + }; + return ( +
+ {choices.map(choice => { + const isSelected = value === choice.value; + return ( +
!disabled && onSelect(choice)} + className={classNames(styles.input, { + [styles.selected]: isSelected, + })} + > +
+
+
+
+ {choice.label} +
+
+ ); + })} +
+ ); +} diff --git a/ui/src/commons/components/forms/Toggle.module.scss b/ui/src/commons/components/forms/Toggle.module.scss new file mode 100644 index 0000000..0195e3c --- /dev/null +++ b/ui/src/commons/components/forms/Toggle.module.scss @@ -0,0 +1,84 @@ +@import '../../../styles/variables'; + +.container { + &.disabled { + opacity: .5; + } +} + +.icon { + width: 49px; + height: 26px; + border-radius: 40px; + overflow: hidden; + position: relative; + + display: flex; + align-items: center; + justify-content: flex-start; + + &:not(.on) { + .bg-on { + visibility: hidden; + opacity: 0; + } + + .bg-off { + visibility: visible; + opacity: 1; + } + } + + &.on { + .bg-on { + visibility: visible; + opacity: 1; + } + + .bg-off { + visibility: hidden; + opacity: 0; + } + + justify-content: flex-end !important; + } +} + +.knob { + position: relative; + width: 20px; + height: 20px; + margin: 0 3px; + background: #ffffff; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; +} + +.loader { + font-size: 75%; +} + +.label { + font-weight: 500; + font-size: 14px; + line-height: 130%; +} + +.bg { + transition: opacity 0.2s ease-in-out; + width: 100%; + height: 100%; + position: absolute; + left: 0; + top: 0; +} + +.bg-on { + background: linear-gradient(113deg, #7153ac 0%, #1b0449 100%); +} + +.bg-off { + background: rgb(136, 136, 136); +} diff --git a/ui/src/commons/components/forms/Toggle.tsx b/ui/src/commons/components/forms/Toggle.tsx new file mode 100644 index 0000000..2840a77 --- /dev/null +++ b/ui/src/commons/components/forms/Toggle.tsx @@ -0,0 +1,80 @@ +import React from 'react'; +import classNames from 'classnames'; +import styles from './Toggle.module.scss'; +import { Loader } from '../Loader'; + +export function Toggle({ + value, + onChange, + togglePosition = 'right', + disabled, + loading, + children, + disableClick, + className, +}: { + value?: boolean; + onChange?: (isOn: boolean) => void; + togglePosition?: 'left' | 'right'; + disabled?: boolean; + loading?: boolean; + children?: any; + disableClick?: any; + className?: any; +}) { + const toggle = () => { + if (disabled || disableClick) { + return; + } + if (onChange) { + onChange(!value); + } + }; + + const icon = ( +
+
+
+
+ {loading && ( + + )} +
+
+ ); + + return ( +
+ {togglePosition === 'left' ? ( + <> +
+ {icon} +
+ {children} + + ) : ( + <> +
+ {children} +
+
+ {icon} +
+ + )} +
+ ); +} diff --git a/ui/src/commons/components/forms/form-constants.ts b/ui/src/commons/components/forms/form-constants.ts new file mode 100644 index 0000000..72246b5 --- /dev/null +++ b/ui/src/commons/components/forms/form-constants.ts @@ -0,0 +1,37 @@ +export const EMAIL_PATTERN = /^.+@.+$/; +export const STRING_MAX_LENGTH = 1000; +export const LONG_STRING_MAX_LENGTH = 5000; + +// TODO could be loaded from backend or placed in a shared lib. Would be easier if mono repo. +export const SUBDOMAIN_PATTERN = /^[a-z0-9]?[a-z0-9-]*[a-z0-9]{1}$/; +export const COLOR_PATTERN = /^#[a-z0-9]{6}$/; + +export const required = { + value: true, + message: 'Input is required', +}; + +export const isEmail = { + value: EMAIL_PATTERN, + message: 'Invalid email', +}; + +export const isSubdomain = { + value: SUBDOMAIN_PATTERN, + message: `Must match ${SUBDOMAIN_PATTERN}`, +}; + +export function maxLength(l?: number) { + const max = l ?? STRING_MAX_LENGTH; + return { + value: max, + message: `Max length is ${max}`, + }; +} + +export function minLength(l: number) { + return { + value: l, + message: `Min length is ${l}`, + }; +} diff --git a/ui/src/commons/components/modals/AppModal.module.scss b/ui/src/commons/components/modals/AppModal.module.scss new file mode 100644 index 0000000..b9a5172 --- /dev/null +++ b/ui/src/commons/components/modals/AppModal.module.scss @@ -0,0 +1,51 @@ +@import 'src/styles/variables'; +@import 'src/styles/mixins'; + +.overlay { + position: fixed; + top: 0; + bottom: 0; + left: 0; + right: 0; + background: transparentize($_primary, 0.8); +} + +.container { + position: absolute; + top: 50%; + left: 50%; + transform: translateX(-50%) translateY(-50%); + max-width: 90%; + + &:focus { + outline: none; + } + + .card { + margin: 0; + } +} + +.content { + max-height: 80vh; + overflow: auto; +} + +:global { + *[data-blur='true'] { + @include blur(8px); + } + + .ReactModal__Overlay { + opacity: 0; + transition: all $transition-duration $transition-effect; + } + + .ReactModal__Overlay--after-open { + opacity: 1; + } + + .ReactModal__Overlay--before-close { + opacity: 0; + } +} diff --git a/ui/src/commons/components/modals/AppModal.tsx b/ui/src/commons/components/modals/AppModal.tsx new file mode 100644 index 0000000..857fa65 --- /dev/null +++ b/ui/src/commons/components/modals/AppModal.tsx @@ -0,0 +1,61 @@ +import React, { useEffect } from 'react'; +import Modal from 'react-modal'; +import classNames from 'classnames'; +import styles from './AppModal.module.scss'; + +export type AppModalProps = { + isOpen: boolean; + title?: string; + children: any; + footer?: any; + closeModal?: (...args: string[]) => void; + className?: string; + [key: string]: any; +}; + +const blurElId = 'blur-overlay'; + +function blurBackground() { + document.getElementById(blurElId).setAttribute('data-blur', 'true'); +} + +function unblurBackground() { + document.getElementById(blurElId).removeAttribute('data-blur'); +} + +export function AppModal({ + title, + children, + isOpen, + closeModal, + className, + footer, + ...otherProps +}: AppModalProps) { + const close = () => { + unblurBackground(); + if (closeModal) { + closeModal(); + } + }; + + useEffect(() => { + if (isOpen) { + blurBackground(); + } else { + unblurBackground(); + } + }, [isOpen]); + + return ( + + {children} + + ); +} diff --git a/ui/src/commons/components/modals/CardModal.module.scss b/ui/src/commons/components/modals/CardModal.module.scss new file mode 100644 index 0000000..0e8f8a1 --- /dev/null +++ b/ui/src/commons/components/modals/CardModal.module.scss @@ -0,0 +1,15 @@ +@import 'src/styles/variables'; +@import 'src/styles/mixins'; + + +.card { + margin: 0; + background: $white; + border-radius: $border-radius; + height: 100%; +} + +.content { + max-height: 80vh; + overflow: auto; +} diff --git a/ui/src/commons/components/modals/CardModal.tsx b/ui/src/commons/components/modals/CardModal.tsx new file mode 100644 index 0000000..c34f290 --- /dev/null +++ b/ui/src/commons/components/modals/CardModal.tsx @@ -0,0 +1,44 @@ +import React from 'react'; +import classNames from 'classnames'; +import { AppModal } from './AppModal'; +import styles from './CardModal.module.scss'; +import { CloseModal } from './CloseModal'; + +export type AppModalProps = { + isOpen: boolean; + title?: string; + children: any; + footer?: any; + closeModal?: (...args: string[]) => void; + className?: string; + [key: string]: any; +}; + +export function CardModal({ + title, + children, + isOpen, + closeModal, + className, + footer, + ...otherProps +}: AppModalProps) { + return ( + +
+
+ {title} + +
+ +
{children}
+ {footer &&
{footer}
} +
+
+ ); +} diff --git a/ui/src/commons/components/modals/CloseModal.module.scss b/ui/src/commons/components/modals/CloseModal.module.scss new file mode 100644 index 0000000..a496323 --- /dev/null +++ b/ui/src/commons/components/modals/CloseModal.module.scss @@ -0,0 +1,9 @@ +@import 'src/styles/variables'; +@import 'src/styles/mixins'; + +.container { + color: $dark; + cursor: pointer; + display: flex; + align-items: center; +} diff --git a/ui/src/commons/components/modals/CloseModal.tsx b/ui/src/commons/components/modals/CloseModal.tsx new file mode 100644 index 0000000..f5ffdd8 --- /dev/null +++ b/ui/src/commons/components/modals/CloseModal.tsx @@ -0,0 +1,16 @@ +import React from 'react'; +import classNames from 'classnames'; +import styles from './CloseModal.module.scss'; +import { KeyboardShortcut } from '../KeyboardShortcut'; + +export function CloseModal({ onClick, className }: { onClick; className? }) { + return ( +
+
Press
+ + esc + +
to close
+
+ ); +} diff --git a/ui/src/commons/components/status/StatusIndicator.module.scss b/ui/src/commons/components/status/StatusIndicator.module.scss new file mode 100644 index 0000000..ce0be11 --- /dev/null +++ b/ui/src/commons/components/status/StatusIndicator.module.scss @@ -0,0 +1,42 @@ +@import 'src/styles/variables'; + +.status { + $size: 30px; + + width: $size; + height: $size; + border-radius: $size; + color: $white; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + background: $_spaceGradient; + font-size: 1em; + + &.status-created, + &.status-paused, + &.status-running, + &.status-unknown { + background: $_blueGradient; + } + + &.status-success { + background: $_greenGradient; + } + + &.status-partial { + background: $_orangeGradient; + } + + &.status-failure, + &.status-failed, + &.status-failing { + background: $_redGradient; + } + + &.status-cancelled, + &.status-skipped { + background: $_spaceGradient; + } +} diff --git a/ui/src/commons/components/status/StatusIndicator.tsx b/ui/src/commons/components/status/StatusIndicator.tsx new file mode 100644 index 0000000..af30685 --- /dev/null +++ b/ui/src/commons/components/status/StatusIndicator.tsx @@ -0,0 +1,30 @@ +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import React, { useState } from 'react'; +import classNames from 'classnames'; +import { uniqueId } from 'lodash'; +import styles from './StatusIndicator.module.scss'; +import { getStatusIcon } from './get-status-icon'; +import { Tooltip, tooltipToggle } from '../Tooltip'; + +export function StatusIndicator({ status, className }: { + status: string; + className?: string; +}) { + const [uid] = useState(uniqueId()); + const { icon, spin } = getStatusIcon(status); + return ( + <> +
+ {icon && ( + + )} +
+ + {status} + + + ); +} diff --git a/ui/src/commons/components/status/get-status-icon.ts b/ui/src/commons/components/status/get-status-icon.ts new file mode 100644 index 0000000..11d50a1 --- /dev/null +++ b/ui/src/commons/components/status/get-status-icon.ts @@ -0,0 +1,65 @@ +import { + faBan, + faBars, + faCheck, + faCog, + faExclamationTriangle, + faFastForward, + faPause, + faQuestion, + faRocket, + faSpinner, + faTimes, + IconDefinition, +} from '@fortawesome/free-solid-svg-icons'; + +export function getStatusIcon(status: string): { icon: IconDefinition; spin?: boolean } { + switch (status) { + case 'success': + return { + icon: faCheck, + }; + case 'failure': + case 'failed': + case 'failing': + return { + icon: faTimes, + }; + case 'partial': + return { + icon: faExclamationTriangle, + }; + case 'running': + return { + icon: faSpinner, spin: true, + }; + case 'created': + return { + icon: faBars, + }; + case 'cancelled': + return { + icon: faBan, + }; + case 'skipped': + return { + icon: faFastForward, + }; + case 'unknown': + return { + icon: faQuestion, + }; + case 'needs_setup': + return { + icon: faCog, + }; + case 'on_hold': + return { + icon: faPause, + }; + default: + return { + icon: faRocket, + }; + } +} diff --git a/ui/src/commons/hooks/use-mounted-state.ts b/ui/src/commons/hooks/use-mounted-state.ts new file mode 100644 index 0000000..84020a5 --- /dev/null +++ b/ui/src/commons/hooks/use-mounted-state.ts @@ -0,0 +1,21 @@ +import { + useCallback, useEffect, useRef, useState, +} from 'react'; +import { ReactState } from '../types/react-state'; + +export function useMountedState(initialValue?: T): ReactState { + const mounted = useRef(true); + const [loading, _setLoading] = useState(initialValue); + + useEffect(() => () => { + mounted.current = false; + }, []); + + const setLoading = useCallback((val: T) => { + if (mounted && mounted.current) { + _setLoading(val); + } + }, [mounted]); + + return [loading, setLoading]; +} diff --git a/ui/src/commons/keyboard/shortcuts-keys.ts b/ui/src/commons/keyboard/shortcuts-keys.ts new file mode 100644 index 0000000..34d4fbc --- /dev/null +++ b/ui/src/commons/keyboard/shortcuts-keys.ts @@ -0,0 +1,6 @@ +// Keep shortcut keys is this file to avoid collisions +// Either Ctrl or Cmd must be pressed for these shortcuts to work +// Avoid: C, F?, N, Q, R, T, V, W, A, X + +export const SEARCH_SHORTCUT_KEY = 'K'; +export const ADD_TEAM_SHORTCUT_KEY = 'P'; diff --git a/ui/src/commons/keyboard/use-shortcut.ts b/ui/src/commons/keyboard/use-shortcut.ts new file mode 100644 index 0000000..16b458e --- /dev/null +++ b/ui/src/commons/keyboard/use-shortcut.ts @@ -0,0 +1,22 @@ +import { useEffect } from 'react'; + +export function onShortcutKey(letter: string, callback: () => any): (event: KeyboardEvent) => void { + const listener = (ev: KeyboardEvent) => { + if ((ev.ctrlKey || ev.metaKey) && ev.key.toUpperCase() === letter) { + ev.preventDefault(); + callback(); + } + }; + document.addEventListener('keydown', listener); + + return listener; +} + +export function useShortcut(letter: string, callback: () => any) { + useEffect(() => { + const listener = onShortcutKey(letter, callback); + return () => { + document.removeEventListener('keydown', listener); + }; + }, [letter, callback]); +} diff --git a/ui/src/commons/nv-i18n.ts b/ui/src/commons/nv-i18n.ts new file mode 100644 index 0000000..a2caacd --- /dev/null +++ b/ui/src/commons/nv-i18n.ts @@ -0,0 +1,982 @@ +/* eslint-disable @typescript-eslint/camelcase */ + +export enum LanguageCode { + aa, ab, ae, af, ak, am, an, ar, as, av, ay, az, ba, be, bg, bh, bi, bm, bn, bo, br, bs, ca, ce, ch, co, cr, cs, cu, + cv, cy, da, de, dv, dz, ee, el, en, eo, es, et, eu, fa, ff, fi, fj, fo, fr, fy, ga, gd, gl, gn, gu, gv, ha, he, hi, + ho, hr, ht, hu, hy, hz, ia, id, ie, ig, ii, ik, io, is, it, iu, ja, jv, ka, kg, ki, kj, kk, kl, km, kn, ko, kr, ks, + ku, kv, kw, ky, la, lb, lg, li, ln, lo, lt, lu, lv, mg, mh, mi, mk, ml, mn, mr, ms, mt, my, na, nb, nd, ne, ng, nl, + nn, no, nr, nv, ny, oc, oj, om, or, os, pa, pi, pl, ps, pt, qu, rm, rn, ro, ru, rw, sa, sc, sd, se, sg, si, sk, sl, + sm, sn, so, sq, sr, ss, st, su, sv, sw, ta, te, tg, th, ti, tk, tl, tn, to, tr, ts, tt, tw, ty, ug, uk, ur, uz, ve, + vi, vo, wa, wo, xh, yi, yo, za, zh, zu, +} + +export enum CountryCode { + AC = 'AC', AD = 'AD', AE = 'AE', AF = 'AF', AG = 'AG', AI = 'AI', AL = 'AL', AM = 'AM', AN = 'AN', AO = 'AO', + AQ = 'AQ', AR = 'AR', AS = 'AS', AT = 'AT', AU = 'AU', AW = 'AW', AX = 'AX', AZ = 'AZ', BA = 'BA', BB = 'BB', + BD = 'BD', BE = 'BE', BF = 'BF', BG = 'BG', BH = 'BH', BI = 'BI', BJ = 'BJ', BL = 'BL', BM = 'BM', BN = 'BN', + BO = 'BO', BQ = 'BQ', BR = 'BR', BS = 'BS', BT = 'BT', BU = 'BU', BV = 'BV', BW = 'BW', BY = 'BY', BZ = 'BZ', + CA = 'CA', CC = 'CC', CD = 'CD', CF = 'CF', CG = 'CG', CH = 'CH', CI = 'CI', CK = 'CK', CL = 'CL', CM = 'CM', + CN = 'CN', CO = 'CO', CP = 'CP', CR = 'CR', CS = 'CS', CU = 'CU', CV = 'CV', CW = 'CW', CX = 'CX', CY = 'CY', + CZ = 'CZ', DE = 'DE', DG = 'DG', DJ = 'DJ', DK = 'DK', DM = 'DM', DO = 'DO', DZ = 'DZ', EA = 'EA', EC = 'EC', + EE = 'EE', EG = 'EG', EH = 'EH', ER = 'ER', ES = 'ES', ET = 'ET', EU = 'EU', EZ = 'EZ', FI = 'FI', FJ = 'FJ', + FK = 'FK', FM = 'FM', FO = 'FO', FR = 'FR', FX = 'FX', GA = 'GA', GB = 'GB', GD = 'GD', GE = 'GE', GF = 'GF', + GG = 'GG', GH = 'GH', GI = 'GI', GL = 'GL', GM = 'GM', GN = 'GN', GP = 'GP', GQ = 'GQ', GR = 'GR', GS = 'GS', + GT = 'GT', GU = 'GU', GW = 'GW', GY = 'GY', HK = 'HK', HM = 'HM', HN = 'HN', HR = 'HR', HT = 'HT', HU = 'HU', + IC = 'IC', ID = 'ID', IE = 'IE', IL = 'IL', IM = 'IM', IN = 'IN', IO = 'IO', IQ = 'IQ', IR = 'IR', IS = 'IS', + IT = 'IT', JE = 'JE', JM = 'JM', JO = 'JO', JP = 'JP', KE = 'KE', KG = 'KG', KH = 'KH', KI = 'KI', KM = 'KM', + KN = 'KN', KP = 'KP', KR = 'KR', KW = 'KW', KY = 'KY', KZ = 'KZ', LA = 'LA', LB = 'LB', LC = 'LC', LI = 'LI', + LK = 'LK', LR = 'LR', LS = 'LS', LT = 'LT', LU = 'LU', LV = 'LV', LY = 'LY', MA = 'MA', MC = 'MC', MD = 'MD', + ME = 'ME', MF = 'MF', MG = 'MG', MH = 'MH', MK = 'MK', ML = 'ML', MM = 'MM', MN = 'MN', MO = 'MO', MP = 'MP', + MQ = 'MQ', MR = 'MR', MS = 'MS', MT = 'MT', MU = 'MU', MV = 'MV', MW = 'MW', MX = 'MX', MY = 'MY', MZ = 'MZ', + NA = 'NA', NC = 'NC', NE = 'NE', NF = 'NF', NG = 'NG', NI = 'NI', NL = 'NL', NO = 'NO', NP = 'NP', NR = 'NR', + NT = 'NT', NU = 'NU', NZ = 'NZ', OM = 'OM', PA = 'PA', PE = 'PE', PF = 'PF', PG = 'PG', PH = 'PH', PK = 'PK', + PL = 'PL', PM = 'PM', PN = 'PN', PR = 'PR', PS = 'PS', PT = 'PT', PW = 'PW', PY = 'PY', QA = 'QA', RE = 'RE', + RO = 'RO', RS = 'RS', RU = 'RU', RW = 'RW', SA = 'SA', SB = 'SB', SC = 'SC', SD = 'SD', SE = 'SE', SF = 'SF', + SG = 'SG', SH = 'SH', SI = 'SI', SJ = 'SJ', SK = 'SK', SL = 'SL', SM = 'SM', SN = 'SN', SO = 'SO', SR = 'SR', + SS = 'SS', ST = 'ST', SU = 'SU', SV = 'SV', SX = 'SX', SY = 'SY', SZ = 'SZ', TA = 'TA', TC = 'TC', TD = 'TD', + TF = 'TF', TG = 'TG', TH = 'TH', TJ = 'TJ', TK = 'TK', TL = 'TL', TM = 'TM', TN = 'TN', TO = 'TO', TP = 'TP', + TR = 'TR', TT = 'TT', TV = 'TV', TW = 'TW', TZ = 'TZ', UA = 'UA', UG = 'UG', UK = 'UK', UM = 'UM', US = 'US', + UY = 'UY', UZ = 'UZ', VA = 'VA', VC = 'VC', VE = 'VE', VG = 'VG', VI = 'VI', VN = 'VN', VU = 'VU', WF = 'WF', + WS = 'WS', XK = 'XK', YE = 'YE', YT = 'YT', YU = 'YU', ZA = 'ZA', ZM = 'ZM', ZR = 'ZR', ZW = 'ZW', +} + +export enum LocaleCode { + ar = 'ar', ar_AE = 'ar_AE', ar_BH = 'ar_BH', ar_DZ = 'ar_DZ', ar_EG = 'ar_EG', ar_IQ = 'ar_IQ', ar_JO = 'ar_JO', + ar_KW = 'ar_KW', ar_LB = 'ar_LB', ar_LY = 'ar_LY', ar_MA = 'ar_MA', ar_OM = 'ar_OM', ar_QA = 'ar_QA', ar_SA = 'ar_SA', + ar_SD = 'ar_SD', ar_SY = 'ar_SY', ar_TN = 'ar_TN', ar_YE = 'ar_YE', be = 'be', be_BY = 'be_BY', bg = 'bg', + bg_BG = 'bg_BG', ca = 'ca', ca_ES = 'ca_ES', cs = 'cs', cs_CZ = 'cs_CZ', da = 'da', da_DK = 'da_DK', de = 'de', + de_AT = 'de_AT', de_CH = 'de_CH', de_DE = 'de_DE', de_LU = 'de_LU', el = 'el', el_CY = 'el_CY', el_GR = 'el_GR', + en = 'en', en_AU = 'en_AU', en_CA = 'en_CA', en_GB = 'en_GB', en_HK = 'en_HK', en_IE = 'en_IE', en_IN = 'en_IN', + en_MT = 'en_MT', en_NZ = 'en_NZ', en_PH = 'en_PH', en_SG = 'en_SG', en_US = 'en_US', en_ZA = 'en_ZA', es = 'es', + es_AR = 'es_AR', es_BO = 'es_BO', es_CL = 'es_CL', es_CO = 'es_CO', es_CR = 'es_CR', es_DO = 'es_DO', es_EC = 'es_EC', + es_ES = 'es_ES', es_GT = 'es_GT', es_HN = 'es_HN', es_MX = 'es_MX', es_NI = 'es_NI', es_PA = 'es_PA', es_PE = 'es_PE', + es_PR = 'es_PR', es_PY = 'es_PY', es_SV = 'es_SV', es_US = 'es_US', es_UY = 'es_UY', es_VE = 'es_VE', et = 'et', + et_EE = 'et_EE', fa = 'fa', fa_IR = 'fa_IR', fi = 'fi', fi_FI = 'fi_FI', fr = 'fr', fr_BE = 'fr_BE', fr_CA = 'fr_CA', + fr_CH = 'fr_CH', fr_FR = 'fr_FR', fr_LU = 'fr_LU', ga = 'ga', ga_IE = 'ga_IE', he = 'he', he_IL = 'he_IL', + hi_IN = 'hi_IN', hr = 'hr', hr_HR = 'hr_HR', hu = 'hu', hu_HU = 'hu_HU', id = 'id', id_ID = 'id_ID', is = 'is', + is_IS = 'is_IS', it = 'it', it_CH = 'it_CH', it_IT = 'it_IT', ja = 'ja', ja_JP = 'ja_JP', kk_KZ = 'kk_KZ', ko = 'ko', + ko_KR = 'ko_KR', lt = 'lt', lt_LT = 'lt_LT', lv = 'lv', lv_LV = 'lv_LV', mk = 'mk', mk_MK = 'mk_MK', ms = 'ms', + ms_MY = 'ms_MY', mt = 'mt', mt_MT = 'mt_MT', nb = 'nb', nb_NO = 'nb_NO', nl = 'nl', nl_BE = 'nl_BE', nl_NL = 'nl_NL', + nn_NO = 'nn_NO', no = 'no', no_NO = 'no_NO', pl = 'pl', pl_PL = 'pl_PL', pt = 'pt', pt_BR = 'pt_BR', pt_PT = 'pt_PT', + ro = 'ro', ro_RO = 'ro_RO', ru = 'ru', ru_KZ = 'ru_KZ', ru_RU = 'ru_RU', se = 'se', se_NO = 'se_NO', sk = 'sk', + sk_SK = 'sk_SK', sl = 'sl', sl_SI = 'sl_SI', sq = 'sq', sq_AL = 'sq_AL', sr = 'sr', sr_BA = 'sr_BA', sr_CS = 'sr_CS', + sr_ME = 'sr_ME', sr_RS = 'sr_RS', sv = 'sv', sv_SE = 'sv_SE', th = 'th', th_TH = 'th_TH', tr = 'tr', tr_TR = 'tr_TR', + uk = 'uk', uk_UA = 'uk_UA', vi = 'vi', vi_VN = 'vi_VN', zh = 'zh', zh_CN = 'zh_CN', zh_HK = 'zh_HK', zh_SG = 'zh_SG', + zh_TW = 'zh_TW', +} + +export const CountryNames = { + [CountryCode.AC]: 'Ascension Island', + [CountryCode.AD]: 'Andorra', + [CountryCode.AE]: 'United Arab Emirates', + [CountryCode.AF]: 'Afghanistan', + [CountryCode.AG]: 'Antigua and Barbuda', + [CountryCode.AI]: 'Anguilla', + [CountryCode.AL]: 'Albania', + [CountryCode.AM]: 'Armenia', + [CountryCode.AN]: 'Netherlands Antilles', + [CountryCode.AO]: 'Angola', + [CountryCode.AQ]: 'Antarctica', + [CountryCode.AR]: 'Argentina', + [CountryCode.AS]: 'American Samoa', + [CountryCode.AT]: 'Austria', + [CountryCode.AU]: 'Australia', + [CountryCode.AW]: 'Aruba', + [CountryCode.AX]: 'Åland Islands', + [CountryCode.AZ]: 'Azerbaijan', + [CountryCode.BA]: 'Bosnia and Herzegovina', + [CountryCode.BB]: 'Barbados', + [CountryCode.BD]: 'Bangladesh', + [CountryCode.BE]: 'Belgium', + [CountryCode.BF]: 'Burkina Faso', + [CountryCode.BG]: 'Bulgaria', + [CountryCode.BH]: 'Bahrain', + [CountryCode.BI]: 'Burundi', + [CountryCode.BJ]: 'Benin', + [CountryCode.BL]: 'Saint Barthélemy', + [CountryCode.BM]: 'Bermuda', + [CountryCode.BN]: 'Brunei Darussalam', + [CountryCode.BO]: 'Bolivia, Plurinational State of', + [CountryCode.BQ]: 'Bonaire, Sint Eustatius and Saba', + [CountryCode.BR]: 'Brazil', + [CountryCode.BS]: 'Bahamas', + [CountryCode.BT]: 'Bhutan', + [CountryCode.BU]: 'Burma', + [CountryCode.BV]: 'Bouvet Island', + [CountryCode.BW]: 'Botswana', + [CountryCode.BY]: 'Belarus', + [CountryCode.BZ]: 'Belize', + [CountryCode.CA]: 'Canada', + [CountryCode.CC]: 'Cocos (Keeling) Islands', + [CountryCode.CD]: 'Congo, the Democratic Republic of the', + [CountryCode.CF]: 'Central African Republic', + [CountryCode.CG]: 'Congo', + [CountryCode.CH]: 'Switzerland', + [CountryCode.CI]: 'Côte d\'Ivoire', + [CountryCode.CK]: 'Cook Islands', + [CountryCode.CL]: 'Chile', + [CountryCode.CM]: 'Cameroon', + [CountryCode.CN]: 'China', + [CountryCode.CO]: 'Colombia', + [CountryCode.CP]: 'Clipperton Island', + [CountryCode.CR]: 'Costa Rica', + [CountryCode.CS]: 'Serbia and Montenegro', + [CountryCode.CU]: 'Cuba', + [CountryCode.CV]: 'Cape Verde', + [CountryCode.CW]: 'Curaçao', + [CountryCode.CX]: 'Christmas Island', + [CountryCode.CY]: 'Cyprus', + [CountryCode.CZ]: 'Czech Republic', + [CountryCode.DE]: 'Germany', + [CountryCode.DG]: 'Diego Garcia', + [CountryCode.DJ]: 'Djibouti', + [CountryCode.DK]: 'Denmark', + [CountryCode.DM]: 'Dominica', + [CountryCode.DO]: 'Dominican Republic', + [CountryCode.DZ]: 'Algeria', + [CountryCode.EA]: 'Ceuta, Melilla', + [CountryCode.EC]: 'Ecuador', + [CountryCode.EE]: 'Estonia', + [CountryCode.EG]: 'Egypt', + [CountryCode.EH]: 'Western Sahara', + [CountryCode.ER]: 'Eritrea', + [CountryCode.ES]: 'Spain', + [CountryCode.ET]: 'Ethiopia', + [CountryCode.EU]: 'European Union', + [CountryCode.EZ]: 'Eurozone', + [CountryCode.FI]: 'Finland', + [CountryCode.FJ]: 'Fiji', + [CountryCode.FK]: 'Falkland Islands (Malvinas)', + [CountryCode.FM]: 'Micronesia, Federated States of', + [CountryCode.FO]: 'Faroe Islands', + [CountryCode.FR]: 'France', + [CountryCode.FX]: 'France, Metropolitan', + [CountryCode.GA]: 'Gabon', + [CountryCode.GB]: 'United Kingdom', + [CountryCode.GD]: 'Grenada', + [CountryCode.GE]: 'Georgia', + [CountryCode.GF]: 'French Guiana', + [CountryCode.GG]: 'Guernsey', + [CountryCode.GH]: 'Ghana', + [CountryCode.GI]: 'Gibraltar', + [CountryCode.GL]: 'Greenland', + [CountryCode.GM]: 'Gambia', + [CountryCode.GN]: 'Guinea', + [CountryCode.GP]: 'Guadeloupe', + [CountryCode.GQ]: 'Equatorial Guinea', + [CountryCode.GR]: 'Greece', + [CountryCode.GS]: 'South Georgia and the South Sandwich Islands', + [CountryCode.GT]: 'Guatemala', + [CountryCode.GU]: 'Guam', + [CountryCode.GW]: 'Guinea-Bissau', + [CountryCode.GY]: 'Guyana', + [CountryCode.HK]: 'Hong Kong', + [CountryCode.HM]: 'Heard Island and McDonald Islands', + [CountryCode.HN]: 'Honduras', + [CountryCode.HR]: 'Croatia', + [CountryCode.HT]: 'Haiti', + [CountryCode.HU]: 'Hungary', + [CountryCode.IC]: 'Canary Islands', + [CountryCode.ID]: 'Indonesia', + [CountryCode.IE]: 'Ireland', + [CountryCode.IL]: 'Israel', + [CountryCode.IM]: 'Isle of Man', + [CountryCode.IN]: 'India', + [CountryCode.IO]: 'British Indian Ocean Territory', + [CountryCode.IQ]: 'Iraq', + [CountryCode.IR]: 'Iran, Islamic Republic of', + [CountryCode.IS]: 'Iceland', + [CountryCode.IT]: 'Italy', + [CountryCode.JE]: 'Jersey', + [CountryCode.JM]: 'Jamaica', + [CountryCode.JO]: 'Jordan', + [CountryCode.JP]: 'Japan', + [CountryCode.KE]: 'Kenya', + [CountryCode.KG]: 'Kyrgyzstan', + [CountryCode.KH]: 'Cambodia', + [CountryCode.KI]: 'Kiribati', + [CountryCode.KM]: 'Comoros', + [CountryCode.KN]: 'Saint Kitts and Nevis', + [CountryCode.KP]: 'Korea, Democratic People\'s Republic of', + [CountryCode.KR]: 'Korea, Republic of', + [CountryCode.KW]: 'Kuwait', + [CountryCode.KY]: 'Cayman Islands', + [CountryCode.KZ]: 'Kazakhstan', + [CountryCode.LA]: 'Lao People\'s Democratic Republic', + [CountryCode.LB]: 'Lebanon', + [CountryCode.LC]: 'Saint Lucia', + [CountryCode.LI]: 'Liechtenstein', + [CountryCode.LK]: 'Sri Lanka', + [CountryCode.LR]: 'Liberia', + [CountryCode.LS]: 'Lesotho', + [CountryCode.LT]: 'Lithuania', + [CountryCode.LU]: 'Luxembourg', + [CountryCode.LV]: 'Latvia', + [CountryCode.LY]: 'Libya', + [CountryCode.MA]: 'Morocco', + [CountryCode.MC]: 'Monaco', + [CountryCode.MD]: 'Moldova, Republic of', + [CountryCode.ME]: 'Montenegro', + [CountryCode.MF]: 'Saint Martin (French part)', + [CountryCode.MG]: 'Madagascar', + [CountryCode.MH]: 'Marshall Islands', + [CountryCode.MK]: 'Macedonia, the former Yugoslav Republic of', + [CountryCode.ML]: 'Mali', + [CountryCode.MM]: 'Myanmar', + [CountryCode.MN]: 'Mongolia', + [CountryCode.MO]: 'Macao', + [CountryCode.MP]: 'Northern Mariana Islands', + [CountryCode.MQ]: 'Martinique', + [CountryCode.MR]: 'Mauritania', + [CountryCode.MS]: 'Montserrat', + [CountryCode.MT]: 'Malta', + [CountryCode.MU]: 'Mauritius', + [CountryCode.MV]: 'Maldives', + [CountryCode.MW]: 'Malawi', + [CountryCode.MX]: 'Mexico', + [CountryCode.MY]: 'Malaysia', + [CountryCode.MZ]: 'Mozambique', + [CountryCode.NA]: 'Namibia', + [CountryCode.NC]: 'New Caledonia', + [CountryCode.NE]: 'Niger', + [CountryCode.NF]: 'Norfolk Island', + [CountryCode.NG]: 'Nigeria', + [CountryCode.NI]: 'Nicaragua', + [CountryCode.NL]: 'Netherlands', + [CountryCode.NO]: 'Norway', + [CountryCode.NP]: 'Nepal', + [CountryCode.NR]: 'Nauru', + [CountryCode.NT]: 'Neutral Zone', + [CountryCode.NU]: 'Niue', + [CountryCode.NZ]: 'New Zealand', + [CountryCode.OM]: 'Oman', + [CountryCode.PA]: 'Panama', + [CountryCode.PE]: 'Peru', + [CountryCode.PF]: 'French Polynesia', + [CountryCode.PG]: 'Papua New Guinea', + [CountryCode.PH]: 'Philippines', + [CountryCode.PK]: 'Pakistan', + [CountryCode.PL]: 'Poland', + [CountryCode.PM]: 'Saint Pierre and Miquelon', + [CountryCode.PN]: 'Pitcairn', + [CountryCode.PR]: 'Puerto Rico', + [CountryCode.PS]: 'Palestine, State of', + [CountryCode.PT]: 'Portugal', + [CountryCode.PW]: 'Palau', + [CountryCode.PY]: 'Paraguay', + [CountryCode.QA]: 'Qatar', + [CountryCode.RE]: 'Réunion', + [CountryCode.RO]: 'Romania', + [CountryCode.RS]: 'Serbia', + [CountryCode.RU]: 'Russian Federation', + [CountryCode.RW]: 'Rwanda', + [CountryCode.SA]: 'Saudi Arabia', + [CountryCode.SB]: 'Solomon Islands', + [CountryCode.SC]: 'Seychelles', + [CountryCode.SD]: 'Sudan', + [CountryCode.SE]: 'Sweden', + [CountryCode.SF]: 'Finland', + [CountryCode.SG]: 'Singapore', + [CountryCode.SH]: 'Saint Helena, Ascension and Tristan da Cunha', + [CountryCode.SI]: 'Slovenia', + [CountryCode.SJ]: 'Svalbard and Jan Mayen', + [CountryCode.SK]: 'Slovakia', + [CountryCode.SL]: 'Sierra Leone', + [CountryCode.SM]: 'San Marino', + [CountryCode.SN]: 'Senegal', + [CountryCode.SO]: 'Somalia', + [CountryCode.SR]: 'Suriname', + [CountryCode.SS]: 'South Sudan', + [CountryCode.ST]: 'Sao Tome and Principe', + [CountryCode.SU]: 'USSR', + [CountryCode.SV]: 'El Salvador', + [CountryCode.SX]: 'Sint Maarten (Dutch part)', + [CountryCode.SY]: 'Syrian Arab Republic', + [CountryCode.SZ]: 'Swaziland', + [CountryCode.TA]: 'Tristan da Cunha', + [CountryCode.TC]: 'Turks and Caicos Islands', + [CountryCode.TD]: 'Chad', + [CountryCode.TF]: 'French Southern Territories', + [CountryCode.TG]: 'Togo', + [CountryCode.TH]: 'Thailand', + [CountryCode.TJ]: 'Tajikistan', + [CountryCode.TK]: 'Tokelau', + [CountryCode.TL]: 'Timor-Leste', + [CountryCode.TM]: 'Turkmenistan', + [CountryCode.TN]: 'Tunisia', + [CountryCode.TO]: 'Tonga', + [CountryCode.TP]: 'East Timor', + [CountryCode.TR]: 'Turkey', + [CountryCode.TT]: 'Trinidad and Tobago', + [CountryCode.TV]: 'Tuvalu', + [CountryCode.TW]: 'Taiwan, Province of China', + [CountryCode.TZ]: 'Tanzania, United Republic of', + [CountryCode.UA]: 'Ukraine', + [CountryCode.UG]: 'Uganda', + [CountryCode.UK]: 'United Kingdom', + [CountryCode.UM]: 'United States Minor Outlying Islands', + [CountryCode.US]: 'United States', + [CountryCode.UY]: 'Uruguay', + [CountryCode.UZ]: 'Uzbekistan', + [CountryCode.VA]: 'Holy See (Vatican City State)', + [CountryCode.VC]: 'Saint Vincent and the Grenadines', + [CountryCode.VE]: 'Venezuela, Bolivarian Republic of', + [CountryCode.VG]: 'Virgin Islands, British', + [CountryCode.VI]: 'Virgin Islands, U.S.', + [CountryCode.VN]: 'Viet Nam', + [CountryCode.VU]: 'Vanuatu', + [CountryCode.WF]: 'Wallis and Futuna', + [CountryCode.WS]: 'Samoa', + [CountryCode.XK]: 'Kosovo, Republic of', + [CountryCode.YE]: 'Yemen', + [CountryCode.YT]: 'Mayotte', + [CountryCode.YU]: 'Yugoslavia', + [CountryCode.ZA]: 'South Africa', + [CountryCode.ZM]: 'Zambia', + [CountryCode.ZR]: 'Zaire', + [CountryCode.ZW]: 'Zimbabwe', +}; + +export enum CurrencyCode { + AED = 'AED', AFN = 'AFN', ALL = 'ALL', AMD = 'AMD', ANG = 'ANG', AOA = 'AOA', ARS = 'ARS', AUD = 'AUD', AWG = 'AWG', + AZN = 'AZN', BAM = 'BAM', BBD = 'BBD', BDT = 'BDT', BGN = 'BGN', BHD = 'BHD', BIF = 'BIF', BMD = 'BMD', BND = 'BND', + BOB = 'BOB', BOV = 'BOV', BRL = 'BRL', BSD = 'BSD', BTN = 'BTN', BWP = 'BWP', BYN = 'BYN', BYR = 'BYR', BZD = 'BZD', + CAD = 'CAD', CDF = 'CDF', CHE = 'CHE', CHF = 'CHF', CHW = 'CHW', CLF = 'CLF', CLP = 'CLP', CNY = 'CNY', COP = 'COP', + COU = 'COU', CRC = 'CRC', CUC = 'CUC', CUP = 'CUP', CVE = 'CVE', CZK = 'CZK', DJF = 'DJF', DKK = 'DKK', DOP = 'DOP', + DZD = 'DZD', EGP = 'EGP', ERN = 'ERN', ETB = 'ETB', EUR = 'EUR', FJD = 'FJD', FKP = 'FKP', GBP = 'GBP', GEL = 'GEL', + GHS = 'GHS', GIP = 'GIP', GMD = 'GMD', GNF = 'GNF', GTQ = 'GTQ', GYD = 'GYD', HKD = 'HKD', HNL = 'HNL', HRK = 'HRK', + HTG = 'HTG', HUF = 'HUF', IDR = 'IDR', ILS = 'ILS', INR = 'INR', IQD = 'IQD', IRR = 'IRR', ISK = 'ISK', JMD = 'JMD', + JOD = 'JOD', JPY = 'JPY', KES = 'KES', KGS = 'KGS', KHR = 'KHR', KMF = 'KMF', KPW = 'KPW', KRW = 'KRW', KWD = 'KWD', + KYD = 'KYD', KZT = 'KZT', LAK = 'LAK', LBP = 'LBP', LKR = 'LKR', LRD = 'LRD', LSL = 'LSL', LTL = 'LTL', LYD = 'LYD', + MAD = 'MAD', MDL = 'MDL', MGA = 'MGA', MKD = 'MKD', MMK = 'MMK', MNT = 'MNT', MOP = 'MOP', MRO = 'MRO', MRU = 'MRU', + MUR = 'MUR', MVR = 'MVR', MWK = 'MWK', MXN = 'MXN', MXV = 'MXV', MYR = 'MYR', MZN = 'MZN', NAD = 'NAD', NGN = 'NGN', + NIO = 'NIO', NOK = 'NOK', NPR = 'NPR', NZD = 'NZD', OMR = 'OMR', PAB = 'PAB', PEN = 'PEN', PGK = 'PGK', PHP = 'PHP', + PKR = 'PKR', PLN = 'PLN', PYG = 'PYG', QAR = 'QAR', RON = 'RON', RSD = 'RSD', RUB = 'RUB', RUR = 'RUR', RWF = 'RWF', + SAR = 'SAR', SBD = 'SBD', SCR = 'SCR', SDG = 'SDG', SEK = 'SEK', SGD = 'SGD', SHP = 'SHP', SLL = 'SLL', SOS = 'SOS', + SRD = 'SRD', SSP = 'SSP', STD = 'STD', STN = 'STN', SVC = 'SVC', SYP = 'SYP', SZL = 'SZL', THB = 'THB', TJS = 'TJS', + TMT = 'TMT', TND = 'TND', TOP = 'TOP', TRY = 'TRY', TTD = 'TTD', TWD = 'TWD', TZS = 'TZS', UAH = 'UAH', UGX = 'UGX', + USD = 'USD', USN = 'USN', USS = 'USS', UYI = 'UYI', UYU = 'UYU', UZS = 'UZS', VEF = 'VEF', VND = 'VND', VUV = 'VUV', + WST = 'WST', XAF = 'XAF', XAG = 'XAG', XAU = 'XAU', XBA = 'XBA', XBB = 'XBB', XBC = 'XBC', XBD = 'XBD', XCD = 'XCD', + XDR = 'XDR', XOF = 'XOF', XPD = 'XPD', XPF = 'XPF', XPT = 'XPT', XSU = 'XSU', XTS = 'XTS', XUA = 'XUA', XXX = 'XXX', + YER = 'YER', ZAR = 'ZAR', ZMW = 'ZMW', ZWL = 'ZWL', +} + +export const TimeZones: string[] = [ + 'ACT', + 'AET', + 'AGT', + 'ART', + 'AST', + 'Africa/Abidjan', + 'Africa/Accra', + 'Africa/Addis_Ababa', + 'Africa/Algiers', + 'Africa/Asmara', + 'Africa/Asmera', + 'Africa/Bamako', + 'Africa/Bangui', + 'Africa/Banjul', + 'Africa/Bissau', + 'Africa/Blantyre', + 'Africa/Brazzaville', + 'Africa/Bujumbura', + 'Africa/Cairo', + 'Africa/Casablanca', + 'Africa/Ceuta', + 'Africa/Conakry', + 'Africa/Dakar', + 'Africa/Dar_es_Salaam', + 'Africa/Djibouti', + 'Africa/Douala', + 'Africa/El_Aaiun', + 'Africa/Freetown', + 'Africa/Gaborone', + 'Africa/Harare', + 'Africa/Johannesburg', + 'Africa/Juba', + 'Africa/Kampala', + 'Africa/Khartoum', + 'Africa/Kigali', + 'Africa/Kinshasa', + 'Africa/Lagos', + 'Africa/Libreville', + 'Africa/Lome', + 'Africa/Luanda', + 'Africa/Lubumbashi', + 'Africa/Lusaka', + 'Africa/Malabo', + 'Africa/Maputo', + 'Africa/Maseru', + 'Africa/Mbabane', + 'Africa/Mogadishu', + 'Africa/Monrovia', + 'Africa/Nairobi', + 'Africa/Ndjamena', + 'Africa/Niamey', + 'Africa/Nouakchott', + 'Africa/Ouagadougou', + 'Africa/Porto-Novo', + 'Africa/Sao_Tome', + 'Africa/Timbuktu', + 'Africa/Tripoli', + 'Africa/Tunis', + 'Africa/Windhoek', + 'America/Adak', + 'America/Anchorage', + 'America/Anguilla', + 'America/Antigua', + 'America/Araguaina', + 'America/Argentina/Buenos_Aires', + 'America/Argentina/Catamarca', + 'America/Argentina/ComodRivadavia', + 'America/Argentina/Cordoba', + 'America/Argentina/Jujuy', + 'America/Argentina/La_Rioja', + 'America/Argentina/Mendoza', + 'America/Argentina/Rio_Gallegos', + 'America/Argentina/Salta', + 'America/Argentina/San_Juan', + 'America/Argentina/San_Luis', + 'America/Argentina/Tucuman', + 'America/Argentina/Ushuaia', + 'America/Aruba', + 'America/Asuncion', + 'America/Atikokan', + 'America/Atka', + 'America/Bahia', + 'America/Bahia_Banderas', + 'America/Barbados', + 'America/Belem', + 'America/Belize', + 'America/Blanc-Sablon', + 'America/Boa_Vista', + 'America/Bogota', + 'America/Boise', + 'America/Buenos_Aires', + 'America/Cambridge_Bay', + 'America/Campo_Grande', + 'America/Cancun', + 'America/Caracas', + 'America/Catamarca', + 'America/Cayenne', + 'America/Cayman', + 'America/Chicago', + 'America/Chihuahua', + 'America/Coral_Harbour', + 'America/Cordoba', + 'America/Costa_Rica', + 'America/Creston', + 'America/Cuiaba', + 'America/Curacao', + 'America/Danmarkshavn', + 'America/Dawson', + 'America/Dawson_Creek', + 'America/Denver', + 'America/Detroit', + 'America/Dominica', + 'America/Edmonton', + 'America/Eirunepe', + 'America/El_Salvador', + 'America/Ensenada', + 'America/Fort_Wayne', + 'America/Fortaleza', + 'America/Glace_Bay', + 'America/Godthab', + 'America/Goose_Bay', + 'America/Grand_Turk', + 'America/Grenada', + 'America/Guadeloupe', + 'America/Guatemala', + 'America/Guayaquil', + 'America/Guyana', + 'America/Halifax', + 'America/Havana', + 'America/Hermosillo', + 'America/Indiana/Indianapolis', + 'America/Indiana/Knox', + 'America/Indiana/Marengo', + 'America/Indiana/Petersburg', + 'America/Indiana/Tell_City', + 'America/Indiana/Vevay', + 'America/Indiana/Vincennes', + 'America/Indiana/Winamac', + 'America/Indianapolis', + 'America/Inuvik', + 'America/Iqaluit', + 'America/Jamaica', + 'America/Jujuy', + 'America/Juneau', + 'America/Kentucky/Louisville', + 'America/Kentucky/Monticello', + 'America/Knox_IN', + 'America/Kralendijk', + 'America/La_Paz', + 'America/Lima', + 'America/Los_Angeles', + 'America/Louisville', + 'America/Lower_Princes', + 'America/Maceio', + 'America/Managua', + 'America/Manaus', + 'America/Marigot', + 'America/Martinique', + 'America/Matamoros', + 'America/Mazatlan', + 'America/Mendoza', + 'America/Menominee', + 'America/Merida', + 'America/Metlakatla', + 'America/Mexico_City', + 'America/Miquelon', + 'America/Moncton', + 'America/Monterrey', + 'America/Montevideo', + 'America/Montreal', + 'America/Montserrat', + 'America/Nassau', + 'America/New_York', + 'America/Nipigon', + 'America/Nome', + 'America/Noronha', + 'America/North_Dakota/Beulah', + 'America/North_Dakota/Center', + 'America/North_Dakota/New_Salem', + 'America/Ojinaga', + 'America/Panama', + 'America/Pangnirtung', + 'America/Paramaribo', + 'America/Phoenix', + 'America/Port-au-Prince', + 'America/Port_of_Spain', + 'America/Porto_Acre', + 'America/Porto_Velho', + 'America/Puerto_Rico', + 'America/Rainy_River', + 'America/Rankin_Inlet', + 'America/Recife', + 'America/Regina', + 'America/Resolute', + 'America/Rio_Branco', + 'America/Rosario', + 'America/Santa_Isabel', + 'America/Santarem', + 'America/Santiago', + 'America/Santo_Domingo', + 'America/Sao_Paulo', + 'America/Scoresbysund', + 'America/Shiprock', + 'America/Sitka', + 'America/St_Barthelemy', + 'America/St_Johns', + 'America/St_Kitts', + 'America/St_Lucia', + 'America/St_Thomas', + 'America/St_Vincent', + 'America/Swift_Current', + 'America/Tegucigalpa', + 'America/Thule', + 'America/Thunder_Bay', + 'America/Tijuana', + 'America/Toronto', + 'America/Tortola', + 'America/Vancouver', + 'America/Virgin', + 'America/Whitehorse', + 'America/Winnipeg', + 'America/Yakutat', + 'America/Yellowknife', + 'Antarctica/Casey', + 'Antarctica/Davis', + 'Antarctica/DumontDUrville', + 'Antarctica/Macquarie', + 'Antarctica/Mawson', + 'Antarctica/McMurdo', + 'Antarctica/Palmer', + 'Antarctica/Rothera', + 'Antarctica/South_Pole', + 'Antarctica/Syowa', + 'Antarctica/Troll', + 'Antarctica/Vostok', + 'Arctic/Longyearbyen', + 'Asia/Aden', + 'Asia/Almaty', + 'Asia/Amman', + 'Asia/Anadyr', + 'Asia/Aqtau', + 'Asia/Aqtobe', + 'Asia/Ashgabat', + 'Asia/Ashkhabad', + 'Asia/Baghdad', + 'Asia/Bahrain', + 'Asia/Baku', + 'Asia/Bangkok', + 'Asia/Beirut', + 'Asia/Bishkek', + 'Asia/Brunei', + 'Asia/Calcutta', + 'Asia/Chita', + 'Asia/Choibalsan', + 'Asia/Chongqing', + 'Asia/Chungking', + 'Asia/Colombo', + 'Asia/Dacca', + 'Asia/Damascus', + 'Asia/Dhaka', + 'Asia/Dili', + 'Asia/Dubai', + 'Asia/Dushanbe', + 'Asia/Gaza', + 'Asia/Harbin', + 'Asia/Hebron', + 'Asia/Ho_Chi_Minh', + 'Asia/Hong_Kong', + 'Asia/Hovd', + 'Asia/Irkutsk', + 'Asia/Istanbul', + 'Asia/Jakarta', + 'Asia/Jayapura', + 'Asia/Jerusalem', + 'Asia/Kabul', + 'Asia/Kamchatka', + 'Asia/Karachi', + 'Asia/Kashgar', + 'Asia/Kathmandu', + 'Asia/Katmandu', + 'Asia/Khandyga', + 'Asia/Kolkata', + 'Asia/Krasnoyarsk', + 'Asia/Kuala_Lumpur', + 'Asia/Kuching', + 'Asia/Kuwait', + 'Asia/Macao', + 'Asia/Macau', + 'Asia/Magadan', + 'Asia/Makassar', + 'Asia/Manila', + 'Asia/Muscat', + 'Asia/Nicosia', + 'Asia/Novokuznetsk', + 'Asia/Novosibirsk', + 'Asia/Omsk', + 'Asia/Oral', + 'Asia/Phnom_Penh', + 'Asia/Pontianak', + 'Asia/Pyongyang', + 'Asia/Qatar', + 'Asia/Qyzylorda', + 'Asia/Rangoon', + 'Asia/Riyadh', + 'Asia/Saigon', + 'Asia/Sakhalin', + 'Asia/Samarkand', + 'Asia/Seoul', + 'Asia/Shanghai', + 'Asia/Singapore', + 'Asia/Srednekolymsk', + 'Asia/Taipei', + 'Asia/Tashkent', + 'Asia/Tbilisi', + 'Asia/Tehran', + 'Asia/Tel_Aviv', + 'Asia/Thimbu', + 'Asia/Thimphu', + 'Asia/Tokyo', + 'Asia/Ujung_Pandang', + 'Asia/Ulaanbaatar', + 'Asia/Ulan_Bator', + 'Asia/Urumqi', + 'Asia/Ust-Nera', + 'Asia/Vientiane', + 'Asia/Vladivostok', + 'Asia/Yakutsk', + 'Asia/Yekaterinburg', + 'Asia/Yerevan', + 'Atlantic/Azores', + 'Atlantic/Bermuda', + 'Atlantic/Canary', + 'Atlantic/Cape_Verde', + 'Atlantic/Faeroe', + 'Atlantic/Faroe', + 'Atlantic/Jan_Mayen', + 'Atlantic/Madeira', + 'Atlantic/Reykjavik', + 'Atlantic/South_Georgia', + 'Atlantic/St_Helena', + 'Atlantic/Stanley', + 'Australia/ACT', + 'Australia/Adelaide', + 'Australia/Brisbane', + 'Australia/Broken_Hill', + 'Australia/Canberra', + 'Australia/Currie', + 'Australia/Darwin', + 'Australia/Eucla', + 'Australia/Hobart', + 'Australia/LHI', + 'Australia/Lindeman', + 'Australia/Lord_Howe', + 'Australia/Melbourne', + 'Australia/NSW', + 'Australia/North', + 'Australia/Perth', + 'Australia/Queensland', + 'Australia/South', + 'Australia/Sydney', + 'Australia/Tasmania', + 'Australia/Victoria', + 'Australia/West', + 'Australia/Yancowinna', + 'BET', + 'BST', + 'Brazil/Acre', + 'Brazil/DeNoronha', + 'Brazil/East', + 'Brazil/West', + 'CAT', + 'CET', + 'CNT', + 'CST', + 'CST6CDT', + 'CTT', + 'Canada/Atlantic', + 'Canada/Central', + 'Canada/East-Saskatchewan', + 'Canada/Eastern', + 'Canada/Mountain', + 'Canada/Newfoundland', + 'Canada/Pacific', + 'Canada/Saskatchewan', + 'Canada/Yukon', + 'Chile/Continental', + 'Chile/EasterIsland', + 'Cuba', + 'EAT', + 'ECT', + 'EET', + 'EST', + 'EST5EDT', + 'Egypt', + 'Eire', + 'Etc/GMT', + 'Etc/GMT+0', + 'Etc/GMT+1', + 'Etc/GMT+10', + 'Etc/GMT+11', + 'Etc/GMT+12', + 'Etc/GMT+2', + 'Etc/GMT+3', + 'Etc/GMT+4', + 'Etc/GMT+5', + 'Etc/GMT+6', + 'Etc/GMT+7', + 'Etc/GMT+8', + 'Etc/GMT+9', + 'Etc/GMT-0', + 'Etc/GMT-1', + 'Etc/GMT-10', + 'Etc/GMT-11', + 'Etc/GMT-12', + 'Etc/GMT-13', + 'Etc/GMT-14', + 'Etc/GMT-2', + 'Etc/GMT-3', + 'Etc/GMT-4', + 'Etc/GMT-5', + 'Etc/GMT-6', + 'Etc/GMT-7', + 'Etc/GMT-8', + 'Etc/GMT-9', + 'Etc/GMT0', + 'Etc/Greenwich', + 'Etc/UCT', + 'Etc/UTC', + 'Etc/Universal', + 'Etc/Zulu', + 'Europe/Amsterdam', + 'Europe/Andorra', + 'Europe/Athens', + 'Europe/Belfast', + 'Europe/Belgrade', + 'Europe/Berlin', + 'Europe/Bratislava', + 'Europe/Brussels', + 'Europe/Bucharest', + 'Europe/Budapest', + 'Europe/Busingen', + 'Europe/Chisinau', + 'Europe/Copenhagen', + 'Europe/Dublin', + 'Europe/Gibraltar', + 'Europe/Guernsey', + 'Europe/Helsinki', + 'Europe/Isle_of_Man', + 'Europe/Istanbul', + 'Europe/Jersey', + 'Europe/Kaliningrad', + 'Europe/Kiev', + 'Europe/Lisbon', + 'Europe/Ljubljana', + 'Europe/London', + 'Europe/Luxembourg', + 'Europe/Madrid', + 'Europe/Malta', + 'Europe/Mariehamn', + 'Europe/Minsk', + 'Europe/Monaco', + 'Europe/Moscow', + 'Europe/Nicosia', + 'Europe/Oslo', + 'Europe/Paris', + 'Europe/Podgorica', + 'Europe/Prague', + 'Europe/Riga', + 'Europe/Rome', + 'Europe/Samara', + 'Europe/San_Marino', + 'Europe/Sarajevo', + 'Europe/Simferopol', + 'Europe/Skopje', + 'Europe/Sofia', + 'Europe/Stockholm', + 'Europe/Tallinn', + 'Europe/Tirane', + 'Europe/Tiraspol', + 'Europe/Uzhgorod', + 'Europe/Vaduz', + 'Europe/Vatican', + 'Europe/Vienna', + 'Europe/Vilnius', + 'Europe/Volgograd', + 'Europe/Warsaw', + 'Europe/Zagreb', + 'Europe/Zaporozhye', + 'Europe/Zurich', + 'GB', + 'GB-Eire', + 'GMT', + 'GMT0', + 'Greenwich', + 'HST', + 'Hongkong', + 'IET', + 'IST', + 'Iceland', + 'Indian/Antananarivo', + 'Indian/Chagos', + 'Indian/Christmas', + 'Indian/Cocos', + 'Indian/Comoro', + 'Indian/Kerguelen', + 'Indian/Mahe', + 'Indian/Maldives', + 'Indian/Mauritius', + 'Indian/Mayotte', + 'Indian/Reunion', + 'Iran', + 'Israel', + 'JST', + 'Jamaica', + 'Japan', + 'Kwajalein', + 'Libya', + 'MET', + 'MIT', + 'MST', + 'MST7MDT', + 'Mexico/BajaNorte', + 'Mexico/BajaSur', + 'Mexico/General', + 'NET', + 'NST', + 'NZ', + 'NZ-CHAT', + 'Navajo', + 'PLT', + 'PNT', + 'PRC', + 'PRT', + 'PST', + 'PST8PDT', + 'Pacific/Apia', + 'Pacific/Auckland', + 'Pacific/Bougainville', + 'Pacific/Chatham', + 'Pacific/Chuuk', + 'Pacific/Easter', + 'Pacific/Efate', + 'Pacific/Enderbury', + 'Pacific/Fakaofo', + 'Pacific/Fiji', + 'Pacific/Funafuti', + 'Pacific/Galapagos', + 'Pacific/Gambier', + 'Pacific/Guadalcanal', + 'Pacific/Guam', + 'Pacific/Honolulu', + 'Pacific/Johnston', + 'Pacific/Kiritimati', + 'Pacific/Kosrae', + 'Pacific/Kwajalein', + 'Pacific/Majuro', + 'Pacific/Marquesas', + 'Pacific/Midway', + 'Pacific/Nauru', + 'Pacific/Niue', + 'Pacific/Norfolk', + 'Pacific/Noumea', + 'Pacific/Pago_Pago', + 'Pacific/Palau', + 'Pacific/Pitcairn', + 'Pacific/Pohnpei', + 'Pacific/Ponape', + 'Pacific/Port_Moresby', + 'Pacific/Rarotonga', + 'Pacific/Saipan', + 'Pacific/Samoa', + 'Pacific/Tahiti', + 'Pacific/Tarawa', + 'Pacific/Tongatapu', + 'Pacific/Truk', + 'Pacific/Wake', + 'Pacific/Wallis', + 'Pacific/Yap', + 'Poland', + 'Portugal', + 'ROK', + 'SST', + 'Singapore', + 'SystemV/AST4', + 'SystemV/AST4ADT', + 'SystemV/CST6', + 'SystemV/CST6CDT', + 'SystemV/EST5', + 'SystemV/EST5EDT', + 'SystemV/HST10', + 'SystemV/MST7', + 'SystemV/MST7MDT', + 'SystemV/PST8', + 'SystemV/PST8PDT', + 'SystemV/YST9', + 'SystemV/YST9YDT', + 'Turkey', + 'UCT', + 'US/Alaska', + 'US/Aleutian', + 'US/Arizona', + 'US/Central', + 'US/East-Indiana', + 'US/Eastern', + 'US/Hawaii', + 'US/Indiana-Starke', + 'US/Michigan', + 'US/Mountain', + 'US/Pacific', + 'US/Pacific-New', + 'US/Samoa', + 'UTC', + 'Universal', + 'VST', + 'W-SU', + 'WET', + 'Zulu', +]; diff --git a/ui/src/commons/sentry/SentryIcon.tsx b/ui/src/commons/sentry/SentryIcon.tsx new file mode 100644 index 0000000..48ec754 --- /dev/null +++ b/ui/src/commons/sentry/SentryIcon.tsx @@ -0,0 +1,30 @@ +import React, { useState } from 'react'; +import { uniqueId } from 'lodash'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faShieldAlt } from '@fortawesome/free-solid-svg-icons'; +import classNames from 'classnames'; +import { SENTRY_CONFIGURED, useSentry } from './SentryProvider'; +import { Tooltip, tooltipToggle } from '../components/Tooltip'; + +export function SentryIcon() { + const { enabled, openModal } = useSentry(); + const [uid] = useState(uniqueId()); + + return SENTRY_CONFIGURED ? ( + <> + + + Sentry is + {' '} + {enabled ? 'enabled' : 'disabled'} + + + ) : ( + <> + ); +} diff --git a/ui/src/commons/sentry/SentryProvider.module.scss b/ui/src/commons/sentry/SentryProvider.module.scss new file mode 100644 index 0000000..0aab05c --- /dev/null +++ b/ui/src/commons/sentry/SentryProvider.module.scss @@ -0,0 +1,27 @@ +@import "../../styles/variables"; +@import "../../styles/mixins"; + +.modal { + text-align: center; +} + +.buttons { + + button { + margin-left: 1rem; + } + + @include media-breakpoint-down(md) { + display: flex; + flex-direction: column; + + button { + + &:not(:first-child) { + margin-top: 15px; + } + + margin-left: 0; + } + } +} diff --git a/ui/src/commons/sentry/SentryProvider.tsx b/ui/src/commons/sentry/SentryProvider.tsx new file mode 100644 index 0000000..fb555a8 --- /dev/null +++ b/ui/src/commons/sentry/SentryProvider.tsx @@ -0,0 +1,70 @@ +import React, { createContext, useContext, useState } from 'react'; +import classNames from 'classnames'; +import styles from './SentryProvider.module.scss'; +import { CardModal } from '../components/modals/CardModal'; +import { ExternalLink } from '../components/ExternalLink'; + +const sentryLocalStorageKey = 'sentry.enabled'; + +export const SENTRY_CONFIGURED = !!process.env.REACT_APP_SENTRY_RELEASE && !!process.env.REACT_APP_SENTRY_DSN; + +export function isSentryEnabled(): boolean { + return localStorage.getItem(sentryLocalStorageKey) === 'true'; +} + +interface SentryContextType { + enabled: boolean; + openModal: () => void; +} + +const SentryContext = createContext({ + enabled: isSentryEnabled(), + openModal: undefined, +}); + +export const useSentry = () => useContext(SentryContext); + +export function SentryProvider(props) { + const [isOpen, setIsOpen] = useState(!localStorage.getItem(sentryLocalStorageKey)); + const [enabled] = useState(isSentryEnabled()); + + const openModal = () => { + setIsOpen(true); + }; + + const enable = () => { + localStorage.setItem(sentryLocalStorageKey, 'true'); + window.location.reload(); + }; + + const disable = () => { + localStorage.setItem(sentryLocalStorageKey, 'false'); + window.location.reload(); + }; + + return !isOpen ? ( + + ) : ( + +

+ Sentry + {' '} + helps us get crash reports remotely. Enabling it allows us to make this platform better. +

+
+ + +
+
+ ); +} diff --git a/ui/src/commons/types/page.ts b/ui/src/commons/types/page.ts new file mode 100644 index 0000000..04e0f22 --- /dev/null +++ b/ui/src/commons/types/page.ts @@ -0,0 +1,4 @@ +export interface Page { + items: T[]; + count: number; +} diff --git a/ui/src/commons/types/react-state.ts b/ui/src/commons/types/react-state.ts new file mode 100644 index 0000000..91c312b --- /dev/null +++ b/ui/src/commons/types/react-state.ts @@ -0,0 +1,3 @@ +import { Dispatch, SetStateAction } from 'react'; + +export type ReactState = [T, Dispatch>]; diff --git a/ui/src/commons/utils/enum-to-array.ts b/ui/src/commons/utils/enum-to-array.ts new file mode 100644 index 0000000..8c82a36 --- /dev/null +++ b/ui/src/commons/utils/enum-to-array.ts @@ -0,0 +1,3 @@ +export function enumToArray(e): string[] { + return Array.from(new Set(Object.keys(e))); +} diff --git a/ui/src/commons/utils/os.ts b/ui/src/commons/utils/os.ts new file mode 100644 index 0000000..4f30653 --- /dev/null +++ b/ui/src/commons/utils/os.ts @@ -0,0 +1,9 @@ +export function isMac(): boolean { + const isMacLike = /(Mac|iPhone|iPod|iPad)/i.test(navigator.platform); + const isIOS = /(iPhone|iPod|iPad)/i.test(navigator.platform); + return isMacLike || isIOS; +} + +export function isWindows(): boolean { + return /Win/i.test(navigator.platform); +} diff --git a/ui/src/commons/utils/random-string.ts b/ui/src/commons/utils/random-string.ts new file mode 100644 index 0000000..957a9f3 --- /dev/null +++ b/ui/src/commons/utils/random-string.ts @@ -0,0 +1,9 @@ +export function randomString(length) { + let result = ''; + const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + const charactersLength = characters.length; + for (let i = 0; i < length; i++) { + result += characters.charAt(Math.floor(Math.random() * charactersLength)); + } + return result; +} diff --git a/ui/src/commons/utils/route-up.ts b/ui/src/commons/utils/route-up.ts new file mode 100644 index 0000000..8a3e01b --- /dev/null +++ b/ui/src/commons/utils/route-up.ts @@ -0,0 +1,3 @@ +export function routeUp(url: string) { + return url.split('/').slice(0, -1).join('/'); +} diff --git a/ui/src/components/AppLogo.tsx b/ui/src/components/AppLogo.tsx new file mode 100644 index 0000000..2c62543 --- /dev/null +++ b/ui/src/components/AppLogo.tsx @@ -0,0 +1,14 @@ +/* eslint-disable max-len */ +import React from 'react'; +import classNames from 'classnames'; +import styles from './Logo.module.scss'; + +export function AppLogo({ className }: { className? }) { + return ( +
+ + + +
+ ); +} diff --git a/ui/src/components/BuildInfo.tsx b/ui/src/components/BuildInfo.tsx new file mode 100644 index 0000000..e9f250b --- /dev/null +++ b/ui/src/components/BuildInfo.tsx @@ -0,0 +1,74 @@ +import React, { useEffect, useRef, useState } from 'react'; +import { useMountedState } from '../commons/hooks/use-mounted-state'; +import { axios } from '../providers/axios'; +import { CopyToClipboard } from '../commons/components/CopyToClipboard'; + +interface ApiInfo { + version: string; + buildDate: Date; + commitHash: string; +} + +function useApiInfo() { + const [loading, setLoading] = useMountedState(false); + const [error, setError] = useState(); + const [apiInfo, setApiInfo] = useState(); + + useEffect(() => { + setLoading(true); + setError(undefined); + axios + .get(`/system/info`) + .then(({ data }) => data) + .then(setApiInfo) + .catch(setError) + .finally(() => setLoading(false)); + }, [setLoading]); + + return { + apiInfo, + error, + loading, + }; +} + +function getMarkdown(data) { + return `\`\`\`json +${JSON.stringify(data, null, 2)} +\`\`\``; +} + +export function BuildInfo({ className }: { + className?; +}) { + const { apiInfo } = useApiInfo(); + + const jsonRef = useRef({ + ui: { + version: process.env.REACT_APP_VERSION, + commitHash: process.env.REACT_APP_BUILD_DATE, + buildDate: process.env.REACT_APP_BUILD_DATE, + }, + api: undefined, + }); + + const [json, setJson] = useState(getMarkdown(jsonRef.current)); + + useEffect(() => { + if (apiInfo) { + jsonRef.current.api = apiInfo; + } + setJson(getMarkdown(jsonRef.current)); + }, [apiInfo]); + + return ( + <> + + {apiInfo?.version || process.env.REACT_APP_VERSION} + + + ); +} diff --git a/ui/src/components/Footer.module.scss b/ui/src/components/Footer.module.scss new file mode 100644 index 0000000..69a39bd --- /dev/null +++ b/ui/src/components/Footer.module.scss @@ -0,0 +1,37 @@ +@import '../styles/variables'; + +.footer { + position: absolute; + bottom: 0; + left: 0; + width: 100%; + display: flex; + justify-content: space-between; + padding: 0.25rem 1rem; + margin: 0; + z-index: 10; +} + +.link { + font-style: normal; + font-weight: 500; + font-size: 14px; + line-height: 130%; + //color: $_rhythm; + margin-right: 1rem; + + &:last-child { + margin-right: 0; + } +} + +.separator { + $size: 5px; + width: $size; + height: $size; + border-radius: 50%; + background: $_blueBell; + display: inline-block; + margin: 0 0.5rem; + opacity: 0.5; +} diff --git a/ui/src/components/Footer.tsx b/ui/src/components/Footer.tsx new file mode 100644 index 0000000..cd76d46 --- /dev/null +++ b/ui/src/components/Footer.tsx @@ -0,0 +1,33 @@ +import React from 'react'; +import { Link } from 'react-router-dom'; +import styles from './Footer.module.scss'; +import { SentryIcon } from '../commons/sentry/SentryIcon'; +import { ExternalLink } from '../commons/components/ExternalLink'; +import { BuildInfo } from './BuildInfo'; + +export function Footer() { + return ( + <> +
+
+ + Twitter + + + Legal + +
+
+ + +
+
+ + ); +} diff --git a/ui/src/components/Home.module.scss b/ui/src/components/Home.module.scss new file mode 100644 index 0000000..926966b --- /dev/null +++ b/ui/src/components/Home.module.scss @@ -0,0 +1,33 @@ +@import '../styles/variables'; + +.container { + padding-top: 100px; + display: flex; + flex-direction: column; + align-items: center; +} + +.logo { + width: 100px; + position: relative; +} + +.inspire { + max-width: 350px; + text-align: center; +} + +.quoteContainer { + margin-top: 100px; + position: relative; + font-size: .8em; +} + +.quoteIcon { + position: absolute; + font-size: 10rem; + color: transparentize($dark, .98); + left: 50%; + top: 50%; + transform: translate(-50%, -50%); +} diff --git a/ui/src/components/Home.tsx b/ui/src/components/Home.tsx new file mode 100644 index 0000000..4d61c96 --- /dev/null +++ b/ui/src/components/Home.tsx @@ -0,0 +1,31 @@ +import React from 'react'; +import classNames from 'classnames'; +import { faQuoteLeft } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { AppLogo } from './AppLogo'; +import styles from './Home.module.scss'; + +export function Home() { + return ( +
+ +

Deploy, now.

+
+

+ We built + {' '} + Meli + {' '} + because we believe that shipping frontend should not be hard. We truly hope you enjoy using it as much as we do. +

+
+ +

+ Because the people who are crazy enough to think they can change the world, are the ones who do. +

+

Steve Jobs

+
+
+
+ ); +} diff --git a/ui/src/components/Logo.module.scss b/ui/src/components/Logo.module.scss new file mode 100644 index 0000000..0001aac --- /dev/null +++ b/ui/src/components/Logo.module.scss @@ -0,0 +1,5 @@ +@import '../styles/variables'; + +.logo { + +} diff --git a/ui/src/components/SubHeader.module.scss b/ui/src/components/SubHeader.module.scss new file mode 100644 index 0000000..f849de9 --- /dev/null +++ b/ui/src/components/SubHeader.module.scss @@ -0,0 +1,5 @@ +@import "../styles/variables"; + +.header { + margin: 1rem 0; +} diff --git a/ui/src/components/SubHeader.tsx b/ui/src/components/SubHeader.tsx new file mode 100644 index 0000000..75ce6c9 --- /dev/null +++ b/ui/src/components/SubHeader.tsx @@ -0,0 +1,11 @@ +import React from 'react'; +import classNames from 'classnames'; +import styles from './SubHeader.module.scss'; + +export function SubHeader({ children, className }: { children?: any; className: string }) { + return ( +
+ {children} +
+ ); +} diff --git a/ui/src/components/UserHome.tsx b/ui/src/components/UserHome.tsx new file mode 100644 index 0000000..a3077d7 --- /dev/null +++ b/ui/src/components/UserHome.tsx @@ -0,0 +1,7 @@ +import React from 'react'; + +export function UserHome() { + return ( + <>Hello + ); +} diff --git a/ui/src/components/auth/IsAdmin.tsx b/ui/src/components/auth/IsAdmin.tsx new file mode 100644 index 0000000..565f00a --- /dev/null +++ b/ui/src/components/auth/IsAdmin.tsx @@ -0,0 +1,13 @@ +import React from 'react'; +import { useCurrentOrg } from '../../providers/OrgProvider'; + +export function IsAdmin({ children }: { + children; +}) { + const { currentOrg } = useCurrentOrg(); + return currentOrg && currentOrg.isAdminOrOwner ? ( + children + ) : ( + <> + ); +} diff --git a/ui/src/components/auth/IsOwner.tsx b/ui/src/components/auth/IsOwner.tsx new file mode 100644 index 0000000..dfe783c --- /dev/null +++ b/ui/src/components/auth/IsOwner.tsx @@ -0,0 +1,13 @@ +import React from 'react'; +import { useCurrentOrg } from '../../providers/OrgProvider'; + +export function IsOwner({ children }: { + children; +}) { + const { currentOrg } = useCurrentOrg(); + return currentOrg && currentOrg.isOwner ? ( + children + ) : ( + <> + ); +} diff --git a/ui/src/components/auth/Orgs.module.scss b/ui/src/components/auth/Orgs.module.scss new file mode 100644 index 0000000..84fd776 --- /dev/null +++ b/ui/src/components/auth/Orgs.module.scss @@ -0,0 +1,30 @@ +@import '../../styles/variables'; +@import '../../styles/mixins'; + +.container { + display: flex; + align-items: center; + justify-content: center; +} + +.title { + font-size: 3rem; + text-align: center; + font-family: 'Source Serif Pro', serif; + margin-bottom: 0; +} + +.grid { + width: 300px; + + @include media-breakpoint-down(xs) { + width: 100%; + } +} + +.add { + text-transform: uppercase; + font-weight: bold; + text-align: center !important; + //border: 2px solid $gray-200 !important; +} diff --git a/ui/src/components/auth/Orgs.tsx b/ui/src/components/auth/Orgs.tsx new file mode 100644 index 0000000..3ff8b69 --- /dev/null +++ b/ui/src/components/auth/Orgs.tsx @@ -0,0 +1,107 @@ +import React, { useEffect, useState } from 'react'; +import classNames from 'classnames'; +import { toast } from 'react-toastify'; +import { axios } from '../../providers/axios'; +import { Loader } from '../../commons/components/Loader'; +import { AlertError } from '../../commons/components/AlertError'; +import { Bubble } from '../../commons/components/Bubble'; +import styles from './Orgs.module.scss'; +import { useCurrentOrg } from '../../providers/OrgProvider'; +import { UserOrg } from './user-org'; +import { AddOrg } from '../orgs/AddOrg'; +import { useMountedState } from '../../commons/hooks/use-mounted-state'; + +function OrgItem({ item }: { + item: UserOrg; +}) { + const { changeCurrentOrg } = useCurrentOrg(); + const [loading, setLoading] = useMountedState(false); + + const selectOrg = () => { + setLoading(true); + changeCurrentOrg(item.org._id) + .catch(err => { + toast.error(`Could not select org: ${err}`); + }) + .finally(() => { + setLoading(true); + }); + }; + + return ( +
  • +
    + + {item.org.name} +
    + {loading && ( + + )} +
  • + ); +} + +function sortOrgs(a: UserOrg, b: UserOrg): number { + return a.org.name < b.org.name ? -1 : a.org.name > b.org.name ? 1 : 0; +} + +export function Orgs() { + const [loading, setLoading] = useMountedState(true); + const [error, setError] = useState(); + const [items, setItems] = useMountedState(); + + useEffect(() => { + setLoading(true); + setError(undefined); + axios + .get(`/api/v1/orgs`) + .then(({ data }) => data.sort(sortOrgs)) + .then(setItems) + .catch(setError) + .finally(() => setLoading(false)); + }, [setLoading, setItems]); + + const onAdded = (org: UserOrg) => { + setItems([org, ...items].sort(sortOrgs)); + }; + + return loading ? ( + + ) : error ? ( + + ) : ( +
    +
    +
    +
    +
    + +

    Organization

    +

    Select an organization

    + +
      + {items.map(item => ( + + ))} +
    + + + Add org + +
    +
    +
    +
    +
    + ); +} diff --git a/ui/src/components/auth/SignIn.module.scss b/ui/src/components/auth/SignIn.module.scss new file mode 100644 index 0000000..26d610a --- /dev/null +++ b/ui/src/components/auth/SignIn.module.scss @@ -0,0 +1,25 @@ +@import '../../styles/variables'; +@import '../../styles/mixins'; + +.container { + display: flex; + align-items: center; + justify-content: center; + height: 100vh; + width: 100%; +} + +.title { + font-size: 3rem; + text-align: center; + font-family: 'Source Serif Pro', serif; + margin-bottom: 0; +} + +.grid { + width: 300px; + + @include media-breakpoint-down(xs) { + width: 100%; + } +} diff --git a/ui/src/components/auth/SignIn.tsx b/ui/src/components/auth/SignIn.tsx new file mode 100644 index 0000000..e76cd43 --- /dev/null +++ b/ui/src/components/auth/SignIn.tsx @@ -0,0 +1,66 @@ +import React, { useEffect, useState } from 'react'; +import classNames from 'classnames'; +import { Loader } from '../../commons/components/Loader'; +import { axios } from '../../providers/axios'; +import styles from './SignIn.module.scss'; +import { ErrorIcon } from '../../commons/components/ErrorIcon'; +import { useMountedState } from '../../commons/hooks/use-mounted-state'; +import { SignInWithGitea } from './methods/SignInWithGitea'; +import { SignInWithGitlab } from './methods/SignInWithGitlab'; +import { SignInWithGithub } from './methods/SignInWithGithub'; +import { SignInWithGoogle } from './methods/SignInWithGoogle'; +import { SignInWithUserPassword } from './methods/SignInWithUserPassword'; + +export function SignIn() { + const [loading, setLoading] = useMountedState(true); + const [error, setError] = useState(); + const [signInMethods, setSignInMethods] = useState(); + + useEffect(() => { + setLoading(true); + axios + .get(`/auth/methods`) + .then(({ data }) => data) + .then(setSignInMethods) + .catch(setError) + .finally(() => setLoading(false)); + }, [setLoading]); + + return loading ? ( + + ) : error ? ( + + ) : ( +
    +
    +
    +
    +
    +

    + meli +

    +

    + Shipping frontend +

    + {signInMethods.includes('in-memory') && ( + + )} + {signInMethods.includes('gitlab') && ( + + )} + {signInMethods.includes('github') && ( + + )} + {signInMethods.includes('gitea') && ( + + )} + {signInMethods.includes('google') && ( + + )} +
    +
    +
    +
    +
    + ); +} diff --git a/ui/src/components/auth/UserInfo.module.scss b/ui/src/components/auth/UserInfo.module.scss new file mode 100644 index 0000000..aa1c0bd --- /dev/null +++ b/ui/src/components/auth/UserInfo.module.scss @@ -0,0 +1,32 @@ +@import '../../styles/variables'; + +.container { + display: flex; + align-items: center; + cursor: pointer; +} + +.bubble { + width: 40px; + height: 40px; +} + +.user { + position: relative; +} + +//.gitlab { +// background: $gitlab-bg; +//} +// +//.gitea { +// background: $gitea-bg; +//} +// +//.github { +// background: $github-bg; +//} +// +//.google { +// background: $google-bg; +//} diff --git a/ui/src/components/auth/UserInfo.tsx b/ui/src/components/auth/UserInfo.tsx new file mode 100644 index 0000000..2caef55 --- /dev/null +++ b/ui/src/components/auth/UserInfo.tsx @@ -0,0 +1,83 @@ +import React, { useState } from 'react'; +import classNames from 'classnames'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faCog, faDoorOpen } from '@fortawesome/free-solid-svg-icons'; +import { uniqueId } from 'lodash'; +import { useAuth } from '../../providers/AuthProvider'; +import styles from './UserInfo.module.scss'; +import { useCurrentOrg } from '../../providers/OrgProvider'; +import { DropdownLink } from '../../commons/components/dropdown/DropdownLink'; +import DropdownSeparator from '../../commons/components/dropdown/DropdownSeparator'; +import { Dropdown, dropdownToggle } from '../../commons/components/dropdown/Dropdown'; +import { UserIcon } from '../icons/UserIcon'; +import { OrgIcon } from '../icons/OrgIcon'; +import { IsAdmin } from './IsAdmin'; +import { Bubble } from '../../commons/components/Bubble'; + +export function UserInfo({ className }: { + className?: string; +}) { + const [uid] = useState(uniqueId()); + const { signOut, user } = useAuth(); + const { currentOrg, signOutOrg } = useCurrentOrg(); + return ( + <> +
    + {currentOrg && ( + + )} +
    + {currentOrg && ( +
    + {currentOrg.org.name} +
    + )} +
    + {user.name} +
    +
    +
    + + + } + to="/org" + > + Organization Settings + + + } + > + User settings + + + {currentOrg ? ( + } + onClick={signOutOrg} + > + Organizations + + ) : ( + } + > + Organizations + + )} + } + onClick={signOut} + > + Sign out + + + + ); +} diff --git a/ui/src/components/auth/methods/SignInButton.module.scss b/ui/src/components/auth/methods/SignInButton.module.scss new file mode 100644 index 0000000..99116a6 --- /dev/null +++ b/ui/src/components/auth/methods/SignInButton.module.scss @@ -0,0 +1,51 @@ +@import '../../../styles/variables'; +@import '../../../styles/mixins'; + +.container { + border: none; + border-radius: $border-radius; + color: $white; + display: flex; + align-items: center; + justify-content: center; + text-transform: uppercase; + text-align: center; + font-weight: bold; + font-size: 1.25rem; + width: 100%; + height: 50px; + margin-bottom: .5rem; + + transition: box-shadow $transition-duration $transition-effect; + + &:hover { + box-shadow: $box-shadow; + } +} + +.iconContainer { + background: $light; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + $size: 30px; + width: $size; + height: $size; + font-size: $size; +} + +.icon { + display: flex; + height: 70%; + width: auto; + max-width: 80%; + align-items: center; + justify-content: center; + position: relative; +} + +.label { + // hidden for until we find a better style + display: none; +} diff --git a/ui/src/components/auth/methods/SignInButton.tsx b/ui/src/components/auth/methods/SignInButton.tsx new file mode 100644 index 0000000..8125679 --- /dev/null +++ b/ui/src/components/auth/methods/SignInButton.tsx @@ -0,0 +1,28 @@ +import classNames from 'classnames'; +import React from 'react'; +import styles from './SignInButton.module.scss'; + +export function SignInButton({ + onClick, label, icon, className, +}: { + onClick?: any; + label: any; + icon: any; + className?: any; +}) { + return ( + + ); +} diff --git a/ui/src/components/auth/methods/SignInWithGitHub.module.scss b/ui/src/components/auth/methods/SignInWithGitHub.module.scss new file mode 100644 index 0000000..9f3c676 --- /dev/null +++ b/ui/src/components/auth/methods/SignInWithGitHub.module.scss @@ -0,0 +1,5 @@ +@import '../../../styles/variables'; + +.github { + background: $github-bg; +} diff --git a/ui/src/components/auth/methods/SignInWithGitea.module.scss b/ui/src/components/auth/methods/SignInWithGitea.module.scss new file mode 100644 index 0000000..f470244 --- /dev/null +++ b/ui/src/components/auth/methods/SignInWithGitea.module.scss @@ -0,0 +1,12 @@ +@import '../../../styles/variables'; + +.gitea { + background: $gitea-bg; +} + +.icon { + display: block; + width: 100%; + position: relative; + bottom: -2px; +} diff --git a/ui/src/components/auth/methods/SignInWithGitea.tsx b/ui/src/components/auth/methods/SignInWithGitea.tsx new file mode 100644 index 0000000..9d808e2 --- /dev/null +++ b/ui/src/components/auth/methods/SignInWithGitea.tsx @@ -0,0 +1,27 @@ +import React from 'react'; +import styles from './SignInWithGitea.module.scss'; +import giteaLogo from '../../../assets/images/git-servers/gitea.svg'; +import { SignInButton } from './SignInButton'; + +export function SignInWithGitea({ className }: { + className?: any; +}) { + return ( + + + )} + label="Gitea" + className={styles.gitea} + /> + + ); +} diff --git a/ui/src/components/auth/methods/SignInWithGithub.tsx b/ui/src/components/auth/methods/SignInWithGithub.tsx new file mode 100644 index 0000000..cd2c340 --- /dev/null +++ b/ui/src/components/auth/methods/SignInWithGithub.tsx @@ -0,0 +1,27 @@ +import React from 'react'; +import styles from './SignInWithGitHub.module.scss'; +import githubLogo from '../../../assets/images/git-servers/github.svg'; +import { SignInButton } from './SignInButton'; + +export function SignInWithGithub({ className }: { + className?: any; +}) { + return ( + + + )} + label="Github" + className={styles.github} + /> + + ); +} diff --git a/ui/src/components/auth/methods/SignInWithGitlab.module.scss b/ui/src/components/auth/methods/SignInWithGitlab.module.scss new file mode 100644 index 0000000..10c5ef5 --- /dev/null +++ b/ui/src/components/auth/methods/SignInWithGitlab.module.scss @@ -0,0 +1,5 @@ +@import '../../../styles/variables'; + +.gitlab { + background: $gitlab-bg; +} diff --git a/ui/src/components/auth/methods/SignInWithGitlab.tsx b/ui/src/components/auth/methods/SignInWithGitlab.tsx new file mode 100644 index 0000000..90c5180 --- /dev/null +++ b/ui/src/components/auth/methods/SignInWithGitlab.tsx @@ -0,0 +1,27 @@ +import React from 'react'; +import styles from './SignInWithGitlab.module.scss'; +import gitlabLogo from '../../../assets/images/git-servers/gitlab.svg'; +import { SignInButton } from './SignInButton'; + +export function SignInWithGitlab({ className }: { + className?: any; +}) { + return ( + + + )} + label="Gitlab" + className={styles.gitlab} + /> + + ); +} diff --git a/ui/src/components/auth/methods/SignInWithGoogle.module.scss b/ui/src/components/auth/methods/SignInWithGoogle.module.scss new file mode 100644 index 0000000..38b8010 --- /dev/null +++ b/ui/src/components/auth/methods/SignInWithGoogle.module.scss @@ -0,0 +1,11 @@ +@import '../../../styles/variables'; + +.google { + background: $google-bg; +} + +.icon { + color: $google-color; + display: block; + width: 100%; +} diff --git a/ui/src/components/auth/methods/SignInWithGoogle.tsx b/ui/src/components/auth/methods/SignInWithGoogle.tsx new file mode 100644 index 0000000..e4e87ab --- /dev/null +++ b/ui/src/components/auth/methods/SignInWithGoogle.tsx @@ -0,0 +1,26 @@ +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faGoogle } from '@fortawesome/free-brands-svg-icons'; +import React from 'react'; +import styles from './SignInWithGoogle.module.scss'; +import { SignInButton } from './SignInButton'; + +export function SignInWithGoogle({ className }: { + className?: any; +}) { + return ( + + + +
    + )} + label="Google" + className={styles.google} + /> + + ); +} diff --git a/ui/src/components/auth/methods/SignInWithUserPassword.tsx b/ui/src/components/auth/methods/SignInWithUserPassword.tsx new file mode 100644 index 0000000..04f48d9 --- /dev/null +++ b/ui/src/components/auth/methods/SignInWithUserPassword.tsx @@ -0,0 +1,93 @@ +import React from 'react'; +import { useForm } from 'react-hook-form'; +import { toast } from 'react-toastify'; +import classNames from 'classnames'; +import { maxLength, required } from '../../../commons/components/forms/form-constants'; +import { InputError } from '../../../commons/components/forms/InputError'; +import { useMountedState } from '../../../commons/hooks/use-mounted-state'; +import { axios } from '../../../providers/axios'; +import { useAuth } from '../../../providers/AuthProvider'; +import { Button } from '../../../commons/components/Button'; + +interface FormData { + user: string; + password: string; +} + +function useSignIn() { + const [loading, setLoading] = useMountedState(false); + const { fetchUser } = useAuth(); + + const signIn = (formData: FormData) => { + setLoading(true); + axios + .post(`/auth/in-memory`, formData) + .then(() => { + fetchUser(); + }) + .catch(err => { + toast.error(`Could not sign in: ${err}`); + }) + .finally(() => setLoading(false)); + }; + + return { + signIn, + loading, + }; +} + +export function SignInWithUserPassword({ className }: { + className?; +}) { + const { + register, errors, handleSubmit, formState: { isDirty }, + } = useForm(); + const { loading, signIn } = useSignIn(); + + return ( +
    +
    + + + +
    +
    + + + +
    + +
    + ); +} diff --git a/ui/src/components/auth/user-org.ts b/ui/src/components/auth/user-org.ts new file mode 100644 index 0000000..061466d --- /dev/null +++ b/ui/src/components/auth/user-org.ts @@ -0,0 +1,9 @@ +import { Org } from '../orgs/org'; + +export interface UserOrg { + org: Org; + member: { + userId: string; + admin: boolean; + }; +} diff --git a/ui/src/components/commons/Logo.module.scss b/ui/src/components/commons/Logo.module.scss new file mode 100644 index 0000000..81c9ff3 --- /dev/null +++ b/ui/src/components/commons/Logo.module.scss @@ -0,0 +1,18 @@ +@import "src/styles/variables"; + +.dropzone { + &:focus { + outline: 0; + } + + border: 2px dashed transparent; + + &.active { + border: 2px dashed $secondary; + } +} + +.logo { + width: 50px; + height: 50px; +} diff --git a/ui/src/components/commons/Logo.tsx b/ui/src/components/commons/Logo.tsx new file mode 100644 index 0000000..ed83ec1 --- /dev/null +++ b/ui/src/components/commons/Logo.tsx @@ -0,0 +1,103 @@ +import React, { useState } from 'react'; +import { useDropzone } from 'react-dropzone'; +import { toast } from 'react-toastify'; +import { faTrashAlt } from '@fortawesome/free-regular-svg-icons'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faUpload } from '@fortawesome/free-solid-svg-icons'; +import classNames from 'classnames'; +import { ButtonIcon } from '../../commons/components/ButtonIcon'; +import { Bubble } from '../../commons/components/Bubble'; +import { axios } from '../../providers/axios'; +import styles from './Logo.module.scss'; + +interface Value { + _id: string; + logo?: string; +} + +export function Logo({ context, value, setValue, className }: { + context: string; + value: T; + setValue: (value: T) => void; + className?: any; +}) { + const [uploading, setUploading] = useState(false); + const [isDragActive, setIsDragActive] = useState(false); + + const onFileDropped = acceptedFiles => { + setIsDragActive(false); + setUploading(true); + const formData = new FormData(); + formData.append('file', acceptedFiles[0]); + axios + .post(`/api/v1/${context}/${value._id}/logo`, formData, { + headers: { + 'Content-Type': 'multipart/form-data', + }, + }) + .then(({ data }) => { + setValue(data); + }) + .catch(err => { + toast.error(`Could not upload logo: ${err}`); + }) + .finally(() => setUploading(false)); + }; + + const [removing, setRemoving] = useState(false); + + const removeLogo = () => { + setRemoving(true); + axios + .delete(`/api/v1/${context}/${value._id}/logo`) + .then(({ data }) => { + setValue(data); + }) + .catch(err => { + toast.error(`Could not remove logo: ${err}`); + }) + .finally(() => setRemoving(false)); + }; + + const { getRootProps, getInputProps, open } = useDropzone({ + noClick: true, + noKeyboard: true, + multiple: false, + onDragEnter: () => setIsDragActive(true), + onDragLeave: () => setIsDragActive(false), + onDrop: onFileDropped, + }); + + return ( +
    +
    + Logo +
    +
    + {value.logo ? ( + + ) : ( +
    + Drag a file here +
    + )} +
    + + + + + {value.logo && ( + + + + )} +
    +
    +
    + ); +} diff --git a/ui/src/components/hooks/AddHook.tsx b/ui/src/components/hooks/AddHook.tsx new file mode 100644 index 0000000..0197dce --- /dev/null +++ b/ui/src/components/hooks/AddHook.tsx @@ -0,0 +1,33 @@ +import React, { useCallback } from 'react'; +import { toast } from 'react-toastify'; +import { useRouteMatch } from 'react-router-dom'; +import { Hook, HookType } from './hook'; +import { routerHistory } from '../../providers/history'; +import { axios } from '../../providers/axios'; +import { HookForm } from './form/HookForm'; +import { useHookContext } from './HookProvider'; +import { routeUp } from '../../commons/utils/route-up'; + +export function AddHook() { + const { context } = useHookContext(); + const { url } = useRouteMatch(); + const onChange = useCallback( + (data: Hook): Promise => axios + .post(`/api/v1/${context}/hooks`, data) + .then(() => { + routerHistory.push(routeUp(url)); + }) + .catch(err => { + toast.error(`Could not create hook: ${err}`); + }), + [context, url], + ); + return ( + + ); +} diff --git a/ui/src/components/hooks/DeleteHook.tsx b/ui/src/components/hooks/DeleteHook.tsx new file mode 100644 index 0000000..d5c06d0 --- /dev/null +++ b/ui/src/components/hooks/DeleteHook.tsx @@ -0,0 +1,65 @@ +import React from 'react'; +import { toast } from 'react-toastify'; +import { Button } from '../../commons/components/Button'; +import { axios } from '../../providers/axios'; +import { CardModal } from '../../commons/components/modals/CardModal'; +import { useMountedState } from '../../commons/hooks/use-mounted-state'; +import { useHookContext } from './HookProvider'; + +export function DeleteHook({ + hookId, className, children, onDelete, +}: { + hookId: string; + children: any; + className?: string; + onDelete: () => void; +}) { + const [isOpen, setIsOpen] = useMountedState(false); + const [loading, setLoading] = useMountedState(false); + const { context } = useHookContext(); + + const remove = () => { + setLoading(true); + return axios + .delete(`/api/v1/${context}/hooks/${hookId}`) + .then(() => { + setIsOpen(false); + onDelete(); + }) + .catch(err => { + toast.error(`Could not delete hook: ${err}`); + }) + .finally(() => setLoading(false)); + }; + + return ( + <> + setIsOpen(false)} + > +

    Are you sure you want to delete this hook ?

    +
    + + +
    +
    +
    setIsOpen(true)} className={className}> + {children} +
    + + ); +} diff --git a/ui/src/components/hooks/HookList.module.scss b/ui/src/components/hooks/HookList.module.scss new file mode 100644 index 0000000..14c892a --- /dev/null +++ b/ui/src/components/hooks/HookList.module.scss @@ -0,0 +1,15 @@ +@import "../../styles/variables"; + +.loader { + position: absolute; + right: 15px; + top: 50%; + transform: translateY(-50%); +} + +.add { + text-transform: uppercase; + font-weight: bold; + text-align: center !important; + //border: 2px solid $gray-200 !important; +} diff --git a/ui/src/components/hooks/HookList.tsx b/ui/src/components/hooks/HookList.tsx new file mode 100644 index 0000000..eb4ca25 --- /dev/null +++ b/ui/src/components/hooks/HookList.tsx @@ -0,0 +1,85 @@ +import React, { useEffect, useState } from 'react'; +import { toast } from 'react-toastify'; +import classNames from 'classnames'; +import { Link, useRouteMatch } from 'react-router-dom'; +import styles from './HookList.module.scss'; +import { EmptyList } from '../../commons/components/EmptyList'; +import { axios } from '../../providers/axios'; +import { Loader } from '../../commons/components/Loader'; +import { AlertError } from '../../commons/components/AlertError'; +import { useMountedState } from '../../commons/hooks/use-mounted-state'; +import { useHookContext } from './HookProvider'; +import { Hook } from './hook'; +import { HookIcon } from '../icons/HookIcon'; + +function sortHooks(a: Hook, b: Hook): number { + return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(); +} + +export function HookList() { + const { url } = useRouteMatch(); + const { context } = useHookContext(); + + const [loading, setLoading] = useMountedState(true); + const [error, setError] = useState(); + const [hooks, setHooks] = useState(); + + useEffect(() => { + setLoading(true); + setError(undefined); + axios.get(`/api/v1/${context}/hooks`) + .then(({ data }) => data.sort(sortHooks)) + .then(setHooks) + .catch(setError) + .catch(err => toast.error(`Could not list hooks: ${err}`)) + .finally(() => setLoading(false)); + }, [setLoading, context]); + + const emptyList = ( + } + title="No hooks" + > +

    There are no hooks yet

    + + + +
    + ); + + return loading ? ( + + ) : error ? ( + + ) : ( + <> + {hooks.length === 0 ? ( + emptyList + ) : ( +
      + + Add hook + + {hooks.map(hook => ( + +
      + {/* TODO status */} + {hook.type} + {hook.name} +
      + + ))} +
    + )} + + ); +} diff --git a/ui/src/components/hooks/HookProvider.tsx b/ui/src/components/hooks/HookProvider.tsx new file mode 100644 index 0000000..a83cb71 --- /dev/null +++ b/ui/src/components/hooks/HookProvider.tsx @@ -0,0 +1,23 @@ +import React, { createContext, useContext } from 'react'; + +interface HookContext { + context: string; +} + +const Context = createContext(undefined); + +export const useHookContext = () => useContext(Context); + +export function HookProvider({ context, ...props }: { + context: string; + [prop: string]: any; +}) { + return ( + + ); +} diff --git a/ui/src/components/hooks/HookTypeIcon.module.scss b/ui/src/components/hooks/HookTypeIcon.module.scss new file mode 100644 index 0000000..c2b39d4 --- /dev/null +++ b/ui/src/components/hooks/HookTypeIcon.module.scss @@ -0,0 +1,5 @@ +.icon { + height: 1.15em; + display: inline-block; + width: auto; +} diff --git a/ui/src/components/hooks/HookTypeIcon.tsx b/ui/src/components/hooks/HookTypeIcon.tsx new file mode 100644 index 0000000..6b77904 --- /dev/null +++ b/ui/src/components/hooks/HookTypeIcon.tsx @@ -0,0 +1,35 @@ +import React from 'react'; +import classNames from 'classnames'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faLink } from '@fortawesome/free-solid-svg-icons'; +import emailIcon from '../../assets/images/notifications/email.svg'; +import slackIcon from '../../assets/images/notifications/slack.svg'; +import mattermostIcon from '../../assets/images/notifications/mattermost.svg'; +import styles from './HookTypeIcon.module.scss'; +import { HookType } from './hook'; + +export function HookTypeIcon({ type, className }: { + type: HookType; + className?: any; +}) { + switch (type) { + case HookType.email: + return ( + email + ); + case HookType.mattermost: + return ( + mattermost + ); + case HookType.slack: + return ( + slack + ); + case HookType.web: + return ; + default: + return ( + <> + ); + } +} diff --git a/ui/src/components/hooks/HookView.tsx b/ui/src/components/hooks/HookView.tsx new file mode 100644 index 0000000..28b43a1 --- /dev/null +++ b/ui/src/components/hooks/HookView.tsx @@ -0,0 +1,112 @@ +import React, { useCallback, useEffect, useState } from 'react'; +import { Route, Switch, useParams, useRouteMatch } from 'react-router-dom'; +import { toast } from 'react-toastify'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faEllipsisV, faTrashAlt } from '@fortawesome/free-solid-svg-icons'; +import { uniqueId } from 'lodash'; +import { Hook } from './hook'; +import { ButtonIcon } from '../../commons/components/ButtonIcon'; +import { routerHistory } from '../../providers/history'; +import { Dropdown, dropdownToggle } from '../../commons/components/dropdown/Dropdown'; +import { Loader } from '../../commons/components/Loader'; +import { DeleteHook } from './DeleteHook'; +import { DropdownLink } from '../../commons/components/dropdown/DropdownLink'; +import { SubHeader } from '../SubHeader'; +import { axios } from '../../providers/axios'; +import { AlertError } from '../../commons/components/AlertError'; +import { useMountedState } from '../../commons/hooks/use-mounted-state'; +import { useHookContext } from './HookProvider'; +import { HookForm } from './form/HookForm'; +import { routeUp } from '../../commons/utils/route-up'; +import { NavPills } from '../../commons/components/NavPills'; +import { HookDeliveries } from './deliveries/HookDeliveries'; +import { NotFound } from '../../commons/components/NotFound'; + +export function HookView() { + const { hookId } = useParams(); + const [uid] = useState(uniqueId()); + const { url, path } = useRouteMatch(); + const { context } = useHookContext(); + + const [loading, setLoading] = useMountedState(true); + const [error, setError] = useState(); + const [hook, setHook] = useState(); + + useEffect(() => { + setLoading(true); + setError(undefined); + axios + .get(`/api/v1/${context}/hooks/${hookId}`) + .then(({ data }) => setHook(data)) + .catch(setError) + .catch(err => toast.error(`Could not get hook: ${err}`)) + .finally(() => setLoading(false)); + }, [setLoading, hookId, context]); + + const onChange = useCallback( + (formData: Hook): Promise => axios + .put(`/api/v1/${context}/hooks/${hookId}`, formData) + .then(({ data }) => setHook(data)), + [hookId, context], + ); + + const onDelete = () => { + routerHistory.push(routeUp(url)); + }; + + return loading ? ( + + ) : error ? ( + + ) : ( + <> + +
    + {hook.type} +
    + {hook.name} +
    +
    +
    + + + + + + + }> + Delete + + + +
    +
    + + + ( + + )} + /> + + + + + ); +} diff --git a/ui/src/components/hooks/Hooks.tsx b/ui/src/components/hooks/Hooks.tsx new file mode 100644 index 0000000..c419d98 --- /dev/null +++ b/ui/src/components/hooks/Hooks.tsx @@ -0,0 +1,18 @@ +import React from 'react'; +import { + Route, Switch, useRouteMatch, +} from 'react-router-dom'; +import { HookList } from './HookList'; +import { AddHook } from './AddHook'; +import { HookView } from './HookView'; + +export function Hooks() { + const { path } = useRouteMatch(); + return ( + + + + + + ); +} diff --git a/ui/src/components/hooks/TestHook.tsx b/ui/src/components/hooks/TestHook.tsx new file mode 100644 index 0000000..b1ae5bb --- /dev/null +++ b/ui/src/components/hooks/TestHook.tsx @@ -0,0 +1,36 @@ +import React from 'react'; +import { toast } from 'react-toastify'; +import classNames from 'classnames'; +import { Button } from '../../commons/components/Button'; +import { axios } from '../../providers/axios'; +import { useMountedState } from '../../commons/hooks/use-mounted-state'; + +export function TestHook({ + config, className, disabled, +}: { + config: any; + className?: string; + disabled: boolean; +}) { + const [loading, setLoading] = useMountedState(false); + const test = () => { + setLoading(true); + axios + .post(`/api/v1/sites/notifications/test`, config) + .then(() => toast.success('It worked !')) + .catch(err => toast.error(`It didnt work: ${err}`)) + .finally(() => setLoading(false)); + }; + return ( + <> + + + ); +} diff --git a/ui/src/components/hooks/deliveries/HookDeliveries.tsx b/ui/src/components/hooks/deliveries/HookDeliveries.tsx new file mode 100644 index 0000000..eac33d5 --- /dev/null +++ b/ui/src/components/hooks/deliveries/HookDeliveries.tsx @@ -0,0 +1,87 @@ +import React, { useEffect, useRef, useState } from 'react'; +import { useParams } from 'react-router-dom'; +import { toast } from 'react-toastify'; +import { useHookContext } from '../HookProvider'; +import { PaginationData } from '../../../commons/components/Pagination'; +import { LoadMore } from '../../../commons/components/LoadMore'; +import { useMountedState } from '../../../commons/hooks/use-mounted-state'; +import { axios } from '../../../providers/axios'; +import { HookDeliveryView } from './HookDeliveryView'; +import { HookDelivery } from './hook-delivery'; +import { Loader } from '../../../commons/components/Loader'; +import { AlertError } from '../../../commons/components/AlertError'; +import { EmptyList } from '../../../commons/components/EmptyList'; +import { HookDeliveryIcon } from '../../icons/HookDeliveryIcon'; + +export function HookDeliveries() { + const { context } = useHookContext(); + const { hookId } = useParams(); + const [loading, setLoading] = useMountedState(true); + const [error, setError] = useState(); + const [deliveries, setDeliveries] = useState(); + const itemsRef = useRef([]); + const [pagination, setPagination] = useState({ + page: 0, size: 10, + }); + const [hasMore, setHasMore] = useState(false); + + useEffect(() => { + setLoading(true); + setError(undefined); + axios + .get(`/api/v1/${context}/hooks/${hookId}/deliveries`, { + params: pagination, + }) + .then(({ data }) => { + itemsRef.current.push(...data.items); + setDeliveries(itemsRef.current); + setHasMore(data.count > itemsRef.current.length); + }) + .catch(setError) + .catch(err => toast.error(`Could not list hook deliveries: ${err}`)) + .finally(() => setLoading(false)); + }, [pagination, hookId, setLoading, context]); + + const nextPage = () => { + setPagination({ + ...pagination, + page: pagination.page + 1, + }); + }; + + return ( + <> + {deliveries && ( +
    + {deliveries.length === 0 ? ( + } + title="No sites" + /> + ) : ( +
      + {deliveries.map(hookDelivery => ( + + ))} +
    + )} + {loading && ( + + )} + {error && ( + + )} + {hasMore && ( +
    + +
    + )} +
    + )} + + ); +} diff --git a/ui/src/components/hooks/deliveries/HookDeliveryView.tsx b/ui/src/components/hooks/deliveries/HookDeliveryView.tsx new file mode 100644 index 0000000..f194154 --- /dev/null +++ b/ui/src/components/hooks/deliveries/HookDeliveryView.tsx @@ -0,0 +1,43 @@ +import React from 'react'; +import moment from 'moment'; +import { HookDelivery } from './hook-delivery'; +import { CodeSnippet } from '../../../commons/components/CodeSnippet'; +import { StatusIndicator } from '../../../commons/components/status/StatusIndicator'; +import { HookType } from '../hook'; + +function Data({ + data, + type, +}: { + type: HookType; + data: any; +}) { + switch (type) { + default: + return {JSON.stringify(data, null, 2)}; + } +} + +export function HookDeliveryView({ delivery }: { + delivery: HookDelivery; +}) { + return ( +
    +
    +
    + +
    + {moment(delivery.date).format('YYYY-MM-DD')} +
    +
    +
    + {moment(delivery.date).fromNow()} +
    +
    + +
    + ); +} diff --git a/ui/src/components/hooks/deliveries/hook-delivery.ts b/ui/src/components/hooks/deliveries/hook-delivery.ts new file mode 100644 index 0000000..95de041 --- /dev/null +++ b/ui/src/components/hooks/deliveries/hook-delivery.ts @@ -0,0 +1,11 @@ +import { HookType } from '../hook'; + +export interface HookDelivery { + _id: string; + type: HookType; + hookId: string; + date: Date; + data: string; + success: string; + error: string; +} diff --git a/ui/src/components/hooks/form/HookEvents.tsx b/ui/src/components/hooks/form/HookEvents.tsx new file mode 100644 index 0000000..5bd8328 --- /dev/null +++ b/ui/src/components/hooks/form/HookEvents.tsx @@ -0,0 +1,61 @@ +import React, { useEffect, useState } from 'react'; +import { toast } from 'react-toastify'; +import { Controller, useFormContext } from 'react-hook-form'; +import { useHookContext } from '../HookProvider'; +import { useMountedState } from '../../../commons/hooks/use-mounted-state'; +import { axios } from '../../../providers/axios'; +import { Loader } from '../../../commons/components/Loader'; +import { AlertError } from '../../../commons/components/AlertError'; +import { Toggle } from '../../../commons/components/forms/Toggle'; + +export function HookEvents() { + const { context } = useHookContext(); + const { control } = useFormContext(); + + const [loading, setLoading] = useMountedState(true); + const [error, setError] = useState(); + const [events, setEvents] = useState(); + + useEffect(() => { + setLoading(true); + setError(undefined); + axios.get(`/api/v1/${context}/hook-events`) + .then(({ data }) => data) + .then(setEvents) + .catch(setError) + .catch(err => toast.error(`Could not list hook events: ${err}`)) + .finally(() => setLoading(false)); + }, [setLoading, context]); + + return loading ? ( + + ) : error ? ( + + ) : ( + ( +
      + {events.length === 0 ? ( + <>No events to show + ) : ( + events.map(event => ( + { + onChange(isOn ? [event, ...value] : value.filter(ev => ev !== event)); + }} + > + {event} + + )) + )} +
    + )} + /> + ); +} diff --git a/ui/src/components/hooks/form/HookForm.module.scss b/ui/src/components/hooks/form/HookForm.module.scss new file mode 100644 index 0000000..6c6a12a --- /dev/null +++ b/ui/src/components/hooks/form/HookForm.module.scss @@ -0,0 +1,24 @@ +@import "src/styles/variables"; + +.remove { + font-size: 1.5rem; + cursor: pointer; + display: flex; + justify-content: flex-end; +} + +.container { + margin-bottom: .25rem; + + &:last-child { + margin-bottom: 0; + } + + &:hover { + .remove { + visibility: visible; + opacity: 1; + } + } +} + diff --git a/ui/src/components/hooks/form/HookForm.tsx b/ui/src/components/hooks/form/HookForm.tsx new file mode 100644 index 0000000..6a00b22 --- /dev/null +++ b/ui/src/components/hooks/form/HookForm.tsx @@ -0,0 +1,146 @@ +import { FormProvider, useForm } from 'react-hook-form'; +import React, { useEffect } from 'react'; +import classNames from 'classnames'; +import styles from './HookForm.module.scss'; +import { maxLength, required } from '../../../commons/components/forms/form-constants'; +import { InputError } from '../../../commons/components/forms/InputError'; +import { enumToArray } from '../../../commons/utils/enum-to-array'; +import { Email } from './configs/Email'; +import { Mattermost } from './configs/Mattermost'; +import { Slack } from './configs/Slack'; +import { Hook, HookType } from '../hook'; +import { useMountedState } from '../../../commons/hooks/use-mounted-state'; +import { Button } from '../../../commons/components/Button'; +import { Web } from './configs/Web'; +import { HookEvents } from './HookEvents'; + +const types = enumToArray(HookType); + +function Config({ type }: { + type: HookType; +}) { + switch (type) { + case HookType.email: + return ; + case HookType.mattermost: + return ; + case HookType.slack: + return ; + case HookType.web: + return ; + default: + return <>; + } +} + +export function HookForm({ + value, + onChange, +}: { + value?: Hook; + onChange: (hook: Hook) => Promise; +}) { + const methods = useForm(); + const { + register, errors, watch, /* getValues, */ handleSubmit, formState: { isDirty }, reset, + } = methods; + + // const getConfig = () => { + // const formData = getValues(); + // return formData.config ? formData.config : undefined; + // }; + // const config = getConfig(); + + const type = watch('type'); + + const [loading, setLoading] = useMountedState(false); + + const onSubmit = (data: Hook) => { + setLoading(true); + onChange(data).finally(() => setLoading(false)); + }; + + useEffect(() => { + if (value && reset) { + reset(value); + } + }, [value, reset]); + + return ( + +
    + {/*
    */} + {/* */} + {/*
    */} + +
    +
    + General config +
    +
    +
    + + + +
    +
    + + + +
    +
    +
    + +
    +
    + Config +
    +
    + +
    +
    + +
    +
    + Events +
    +
    + +
    +
    + + +
    +
    + ); +} diff --git a/ui/src/components/hooks/form/configs/Email.tsx b/ui/src/components/hooks/form/configs/Email.tsx new file mode 100644 index 0000000..ce20179 --- /dev/null +++ b/ui/src/components/hooks/form/configs/Email.tsx @@ -0,0 +1,34 @@ +import { useFormContext } from 'react-hook-form'; +import React from 'react'; +import { + isEmail, maxLength, required, +} from '../../../../commons/components/forms/form-constants'; +import { InputError } from '../../../../commons/components/forms/InputError'; + +export function Email() { + const { register, errors } = useFormContext(); + const input_to = 'config.to'; + return ( +
    + + + +
    + ); +} diff --git a/ui/src/components/hooks/form/configs/Mattermost.tsx b/ui/src/components/hooks/form/configs/Mattermost.tsx new file mode 100644 index 0000000..39ed8d7 --- /dev/null +++ b/ui/src/components/hooks/form/configs/Mattermost.tsx @@ -0,0 +1,39 @@ +import { useFormContext } from 'react-hook-form'; +import React from 'react'; +import { maxLength, required } from '../../../../commons/components/forms/form-constants'; +import { InputError } from '../../../../commons/components/forms/InputError'; +import { ExternalLink } from '../../../../commons/components/ExternalLink'; + +export function Mattermost() { + const { register, errors } = useFormContext(); + const input_url = 'config.url'; + return ( + <> +
    + + + +
    + + ); +} diff --git a/ui/src/components/hooks/form/configs/Slack.module.scss b/ui/src/components/hooks/form/configs/Slack.module.scss new file mode 100644 index 0000000..faa6f70 --- /dev/null +++ b/ui/src/components/hooks/form/configs/Slack.module.scss @@ -0,0 +1,11 @@ +.modal { + max-width: 1000px; + width: 80%; + max-height: 80%; +} + +.video { + width: 100%; + height: auto; + object-fit: fill; +} diff --git a/ui/src/components/hooks/form/configs/Slack.tsx b/ui/src/components/hooks/form/configs/Slack.tsx new file mode 100644 index 0000000..80f03dc --- /dev/null +++ b/ui/src/components/hooks/form/configs/Slack.tsx @@ -0,0 +1,71 @@ +import { useFormContext } from 'react-hook-form'; +import React, { useState } from 'react'; +import classNames from 'classnames'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faExternalLinkAlt } from '@fortawesome/free-solid-svg-icons'; +import { maxLength, required } from '../../../../commons/components/forms/form-constants'; +import { InputError } from '../../../../commons/components/forms/InputError'; +import { Hint } from '../../../../commons/components/Hint'; +import styles from './Slack.module.scss'; +import { ExternalLink } from '../../../../commons/components/ExternalLink'; +import { CardModal } from '../../../../commons/components/modals/CardModal'; + +function Video({ className }: { className? }) { + const [isOpen, setIsOpen] = useState(false); + return ( + <> + setIsOpen(true)} + className={classNames('cursor-pointer', className)} + > + Click to view how to get this URL + + setIsOpen(false)} + className={styles.modal} + > + + + ); +} + +export function Slack() { + const { register, errors } = useFormContext(); + const input_url = 'config.url'; + return ( +
    +
    + + + Create slack app + + +
    + + +
    +
    + ); +} diff --git a/ui/src/components/hooks/form/configs/Web.tsx b/ui/src/components/hooks/form/configs/Web.tsx new file mode 100644 index 0000000..5dce002 --- /dev/null +++ b/ui/src/components/hooks/form/configs/Web.tsx @@ -0,0 +1,57 @@ +import { useFormContext } from 'react-hook-form'; +import React, { useState } from 'react'; +import { maxLength, required } from '../../../../commons/components/forms/form-constants'; +import { InputError } from '../../../../commons/components/forms/InputError'; +import { randomString } from '../../../../commons/utils/random-string'; + +export function Web() { + const { register, errors } = useFormContext(); + const [randomSecret] = useState(randomString(32)); + const input_url = 'config.url'; + const input_secret = 'config.secret'; + return ( + <> +
    + + + +
    +
    + + + +
    + + ); +} diff --git a/ui/src/components/hooks/hook.ts b/ui/src/components/hooks/hook.ts new file mode 100644 index 0000000..c72114f --- /dev/null +++ b/ui/src/components/hooks/hook.ts @@ -0,0 +1,16 @@ +export enum HookType { + email = 'email', + mattermost = 'mattermost', + slack = 'slack', + web = 'web', +} + +export interface Hook { + _id: string; + name: string; + type: HookType; + createdAt: Date; + updatedAt: Date; + config: any; + events: string[]; +} diff --git a/ui/src/components/icons/BranchIcon.tsx b/ui/src/components/icons/BranchIcon.tsx new file mode 100644 index 0000000..cfc0d2c --- /dev/null +++ b/ui/src/components/icons/BranchIcon.tsx @@ -0,0 +1,13 @@ +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import React from 'react'; +import { faCodeBranch } from '@fortawesome/free-solid-svg-icons'; + +export function BranchIcon({ className }: { className? }) { + return ( + + ); +} diff --git a/ui/src/components/icons/FormIcon.tsx b/ui/src/components/icons/FormIcon.tsx new file mode 100644 index 0000000..4c66a50 --- /dev/null +++ b/ui/src/components/icons/FormIcon.tsx @@ -0,0 +1,13 @@ +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faCode } from '@fortawesome/free-solid-svg-icons'; +import React from 'react'; + +export function FormIcon({ className }: { className? }) { + return ( + + ); +} diff --git a/ui/src/components/icons/HeaderIcon.tsx b/ui/src/components/icons/HeaderIcon.tsx new file mode 100644 index 0000000..40941f8 --- /dev/null +++ b/ui/src/components/icons/HeaderIcon.tsx @@ -0,0 +1,13 @@ +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faWrench } from '@fortawesome/free-solid-svg-icons'; +import React from 'react'; + +export function HeaderIcon({ className }: { className? }) { + return ( + + ); +} diff --git a/ui/src/components/icons/HookDeliveryIcon.tsx b/ui/src/components/icons/HookDeliveryIcon.tsx new file mode 100644 index 0000000..bafa00c --- /dev/null +++ b/ui/src/components/icons/HookDeliveryIcon.tsx @@ -0,0 +1,13 @@ +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faPaperPlane } from '@fortawesome/free-regular-svg-icons'; +import React from 'react'; + +export function HookDeliveryIcon({ className }: { className? }) { + return ( + + ); +} diff --git a/ui/src/components/icons/HookIcon.tsx b/ui/src/components/icons/HookIcon.tsx new file mode 100644 index 0000000..aa7ba5e --- /dev/null +++ b/ui/src/components/icons/HookIcon.tsx @@ -0,0 +1,13 @@ +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faBell } from '@fortawesome/free-regular-svg-icons'; +import React from 'react'; + +export function HookIcon({ className }: { className? }) { + return ( + + ); +} diff --git a/ui/src/components/icons/InviteIcon.tsx b/ui/src/components/icons/InviteIcon.tsx new file mode 100644 index 0000000..82918bd --- /dev/null +++ b/ui/src/components/icons/InviteIcon.tsx @@ -0,0 +1,13 @@ +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faPaperPlane } from '@fortawesome/free-regular-svg-icons'; +import React from 'react'; + +export function InviteIcon({ className }: { className? }) { + return ( + + ); +} diff --git a/ui/src/components/icons/OrgIcon.tsx b/ui/src/components/icons/OrgIcon.tsx new file mode 100644 index 0000000..2f1a17d --- /dev/null +++ b/ui/src/components/icons/OrgIcon.tsx @@ -0,0 +1,13 @@ +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import React from 'react'; +import { faVihara } from '@fortawesome/free-solid-svg-icons'; + +export function OrgIcon({ className }: { className? }) { + return ( + + ); +} diff --git a/ui/src/components/icons/OrgMemberIcon.tsx b/ui/src/components/icons/OrgMemberIcon.tsx new file mode 100644 index 0000000..fbabd94 --- /dev/null +++ b/ui/src/components/icons/OrgMemberIcon.tsx @@ -0,0 +1,13 @@ +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faUserAstronaut } from '@fortawesome/free-solid-svg-icons'; +import React from 'react'; + +export function OrgMemberIcon({ className }: { className? }) { + return ( + + ); +} diff --git a/ui/src/components/icons/RedirectIcon.tsx b/ui/src/components/icons/RedirectIcon.tsx new file mode 100644 index 0000000..c41f670 --- /dev/null +++ b/ui/src/components/icons/RedirectIcon.tsx @@ -0,0 +1,13 @@ +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faDirections } from '@fortawesome/free-solid-svg-icons'; +import React from 'react'; + +export function RedirectIcon({ className }: { className? }) { + return ( + + ); +} diff --git a/ui/src/components/icons/ReleaseIcon.tsx b/ui/src/components/icons/ReleaseIcon.tsx new file mode 100644 index 0000000..927aa73 --- /dev/null +++ b/ui/src/components/icons/ReleaseIcon.tsx @@ -0,0 +1,13 @@ +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faList } from '@fortawesome/free-solid-svg-icons'; +import React from 'react'; + +export function ReleaseIcon({ className }: { className? }) { + return ( + + ); +} diff --git a/ui/src/components/icons/SecurityIcon.tsx b/ui/src/components/icons/SecurityIcon.tsx new file mode 100644 index 0000000..b2b05de --- /dev/null +++ b/ui/src/components/icons/SecurityIcon.tsx @@ -0,0 +1,13 @@ +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import React from 'react'; +import { faLock } from '@fortawesome/free-solid-svg-icons'; + +export function SecurityIcon({ className }: { className? }) { + return ( + + ); +} diff --git a/ui/src/components/icons/SettingsIcon.tsx b/ui/src/components/icons/SettingsIcon.tsx new file mode 100644 index 0000000..f880672 --- /dev/null +++ b/ui/src/components/icons/SettingsIcon.tsx @@ -0,0 +1,13 @@ +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faWrench } from '@fortawesome/free-solid-svg-icons'; +import React from 'react'; + +export function SettingsIcon({ className }: { className? }) { + return ( + + ); +} diff --git a/ui/src/components/icons/SiteIcon.tsx b/ui/src/components/icons/SiteIcon.tsx new file mode 100644 index 0000000..4eac956 --- /dev/null +++ b/ui/src/components/icons/SiteIcon.tsx @@ -0,0 +1,13 @@ +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faRocket } from '@fortawesome/free-solid-svg-icons'; +import React from 'react'; + +export function SiteIcon({ className }: { className? }) { + return ( + + ); +} diff --git a/ui/src/components/icons/TeamIcon.tsx b/ui/src/components/icons/TeamIcon.tsx new file mode 100644 index 0000000..86cb129 --- /dev/null +++ b/ui/src/components/icons/TeamIcon.tsx @@ -0,0 +1,13 @@ +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faSpaceShuttle } from '@fortawesome/free-solid-svg-icons'; +import React from 'react'; + +export function TeamIcon({ className }: { className? }) { + return ( + + ); +} diff --git a/ui/src/components/icons/TeamMemberIcon.tsx b/ui/src/components/icons/TeamMemberIcon.tsx new file mode 100644 index 0000000..e7290e4 --- /dev/null +++ b/ui/src/components/icons/TeamMemberIcon.tsx @@ -0,0 +1,13 @@ +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faUserAstronaut } from '@fortawesome/free-solid-svg-icons'; +import React from 'react'; + +export function TeamMemberIcon({ className }: { className? }) { + return ( + + ); +} diff --git a/ui/src/components/icons/TokenIcon.tsx b/ui/src/components/icons/TokenIcon.tsx new file mode 100644 index 0000000..584ec5f --- /dev/null +++ b/ui/src/components/icons/TokenIcon.tsx @@ -0,0 +1,13 @@ +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faKey } from '@fortawesome/free-solid-svg-icons'; +import React from 'react'; + +export function TokenIcon({ className }: { className? }) { + return ( + + ); +} diff --git a/ui/src/components/icons/UserIcon.tsx b/ui/src/components/icons/UserIcon.tsx new file mode 100644 index 0000000..2b8927f --- /dev/null +++ b/ui/src/components/icons/UserIcon.tsx @@ -0,0 +1,13 @@ +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faUserAstronaut } from '@fortawesome/free-solid-svg-icons'; +import React from 'react'; + +export function UserIcon({ className }: { className? }) { + return ( + + ); +} diff --git a/ui/src/components/invites/AcceptInvite.tsx b/ui/src/components/invites/AcceptInvite.tsx new file mode 100644 index 0000000..f467936 --- /dev/null +++ b/ui/src/components/invites/AcceptInvite.tsx @@ -0,0 +1,46 @@ +import React from 'react'; +import { toast } from 'react-toastify'; +import classNames from 'classnames'; +import { Button } from '../../commons/components/Button'; +import { axios } from '../../providers/axios'; +import { UserOrg } from '../auth/user-org'; +import { useMountedState } from '../../commons/hooks/use-mounted-state'; + +export function AcceptInvite({ + inviteId, className, onAccept, token, disabled, +}: { + token: string; + inviteId: string; + disabled: boolean; + onAccept: (org: UserOrg) => void; + className?: string; +}) { + const [loading, setLoading] = useMountedState(false); + + const accept = () => { + setLoading(true); + return axios + .put(`/api/v1/invites/${inviteId}/accept`, { + token, + }) + .then(({ data }) => data) + .then(onAccept) + .catch(err => { + toast.error(`Could not delete invite: ${err}`); + }) + .finally(() => setLoading(false)); + }; + + return ( + <> + + + ); +} diff --git a/ui/src/components/invites/DeclineInvite.tsx b/ui/src/components/invites/DeclineInvite.tsx new file mode 100644 index 0000000..3c82788 --- /dev/null +++ b/ui/src/components/invites/DeclineInvite.tsx @@ -0,0 +1,45 @@ +import React from 'react'; +import { toast } from 'react-toastify'; +import classNames from 'classnames'; +import { Button } from '../../commons/components/Button'; +import { axios } from '../../providers/axios'; +import { UserOrg } from '../auth/user-org'; +import { useMountedState } from '../../commons/hooks/use-mounted-state'; + +export function DeclineInvite({ + inviteId, className, onIgnore, token, disabled, +}: { + inviteId: string; + token: string; + disabled: boolean; + onIgnore: () => void; + className?: string; +}) { + const [loading, setLoading] = useMountedState(false); + + const accept = () => { + setLoading(true); + return axios + .put(`/api/v1/invites/${inviteId}/decline`, { + token, + }) + .then(() => onIgnore()) + .catch(err => { + toast.error(`Could not delete invite: ${err}`); + }) + .finally(() => setLoading(false)); + }; + + return ( + <> + + + ); +} diff --git a/ui/src/components/invites/UserInviteView.module.scss b/ui/src/components/invites/UserInviteView.module.scss new file mode 100644 index 0000000..9c192e6 --- /dev/null +++ b/ui/src/components/invites/UserInviteView.module.scss @@ -0,0 +1,11 @@ +@import "../../styles/variables"; + +.org { + background: $gray-100; + display: flex; + align-items: center; + padding: 1.5rem; + margin: 2rem 0; + font-size: 1.5rem; + border-radius: $border-radius; +} diff --git a/ui/src/components/invites/UserInviteView.tsx b/ui/src/components/invites/UserInviteView.tsx new file mode 100644 index 0000000..8235b6d --- /dev/null +++ b/ui/src/components/invites/UserInviteView.tsx @@ -0,0 +1,91 @@ +import React, { useState } from 'react'; +import { UserInvite } from './user-invite'; +import { FromNow } from '../../commons/components/FromNow'; +import { DeclineInvite } from './DeclineInvite'; +import { UserOrg } from '../auth/user-org'; +import { AcceptInvite } from './AcceptInvite'; +import { useCurrentOrg } from '../../providers/OrgProvider'; +import { routerHistory } from '../../providers/history'; +import { Loader } from '../../commons/components/Loader'; +import { AlertError } from '../../commons/components/AlertError'; +import { Bubble } from '../../commons/components/Bubble'; +import styles from './UserInviteView.module.scss'; +import { useMountedState } from '../../commons/hooks/use-mounted-state'; + +export function UserInviteView({ invite, token }: { + invite: UserInvite; + token: string; +}) { + const { changeCurrentOrg } = useCurrentOrg(); + const [loading, setLoading] = useMountedState(false); + const [error, setError] = useState(); + + const onIgnore = () => { + routerHistory.push('/'); + }; + + const onAccept = (org: UserOrg) => { + setLoading(true); + changeCurrentOrg(org.org._id) + .then(() => { + routerHistory.push('/'); + }) + .catch(setError) + .finally(() => setLoading(false)); + }; + + const expired = new Date(invite.expiresAt).getTime() < Date.now(); + + return ( +
    +

    You're invited !

    +

    You are invited to join the workspace

    +
    + + {invite.org.name} +
    + {invite.memberOptions.admin && ( +

    + You will have the privilege to be an + admin +

    + )} +

    + + +

    + {loading && ( +

    + +

    + )} + {error && ( +

    + +

    + )} +

    + {expired ? ( +

    + expired +
    + ) : ( +
    + +
    + )} +

    +
    + ); +} diff --git a/ui/src/components/invites/UserInvites.tsx b/ui/src/components/invites/UserInvites.tsx new file mode 100644 index 0000000..01f3d61 --- /dev/null +++ b/ui/src/components/invites/UserInvites.tsx @@ -0,0 +1,58 @@ +import React, { useEffect, useState } from 'react'; +import { axios } from '../../providers/axios'; +import { queryParams } from '../../utils/query-params'; +import { Loader } from '../../commons/components/Loader'; +import { AlertError } from '../../commons/components/AlertError'; +import { UserInvite } from './user-invite'; +import { UserInviteView } from './UserInviteView'; +import { useMountedState } from '../../commons/hooks/use-mounted-state'; + +export function UserInvites() { + const { token } = queryParams(); + + const [loading, setLoading] = useMountedState(!!token); + const [error, setError] = useState(); + const [invite, setInvite] = useState(); + + useEffect(() => { + if (token) { + setLoading(true); + axios + .post(`/api/v1/invites/${token}`, { + token, + }) + .then(({ data }) => data) + .then(setInvite) + .catch(setError) + .finally(() => setLoading(false)); + } + }, [token, setLoading]); + + return ( +
    +
    +
    + {!token ? ( +
    +

    No token found

    +

    + We could not find your invite token. There should be a + ?token=... + {' '} + in the URL +

    +
    + ) : ( + loading ? ( + + ) : error ? ( + + ) : ( + + ) + )} +
    +
    +
    + ); +} diff --git a/ui/src/components/invites/user-invite.ts b/ui/src/components/invites/user-invite.ts new file mode 100644 index 0000000..c15920a --- /dev/null +++ b/ui/src/components/invites/user-invite.ts @@ -0,0 +1,12 @@ +export interface UserInvite { + _id: string; + org: { + name: string; + color: string; + logo?: string; + }; + expiresAt: Date; + memberOptions: { + admin: boolean; + }; +} diff --git a/ui/src/components/legals/CookiePolicy.tsx b/ui/src/components/legals/CookiePolicy.tsx new file mode 100644 index 0000000..68aa678 --- /dev/null +++ b/ui/src/components/legals/CookiePolicy.tsx @@ -0,0 +1,30 @@ +import React, { useEffect, useState } from 'react'; +import ReactMarkdown from 'react-markdown'; +import md from './cookie-policy.md'; +import { axios } from '../../providers/axios'; +import { Loader } from '../../commons/components/Loader'; +import { AlertError } from '../../commons/components/AlertError'; +import { useMountedState } from '../../commons/hooks/use-mounted-state'; + +export default function CookiePolicy() { + const [loading, setLoading] = useMountedState(true); + const [error, setError] = useState(); + const [content, setContent] = useState(); + + useEffect(() => { + axios + .get(md) + .then(({ data }) => data) + .then(setContent) + .catch(setError) + .finally(() => setLoading(false)); + }, [setLoading]); + + return loading ? ( + + ) : error ? ( + + ) : ( + {content} + ); +} diff --git a/ui/src/components/legals/Legals.tsx b/ui/src/components/legals/Legals.tsx new file mode 100644 index 0000000..ac6dac3 --- /dev/null +++ b/ui/src/components/legals/Legals.tsx @@ -0,0 +1,38 @@ +import React from 'react'; +import { + Route, Switch, useRouteMatch, +} from 'react-router-dom'; +import { NotFound } from '../../commons/components/NotFound'; +import TermsOfService from './TermsOfService'; +import PrivacyPolicy from './PrivacyPolicy'; +import { NavPills } from '../../commons/components/NavPills'; + +export function Legals() { + const { url, path } = useRouteMatch(); + const links = [ + { + to: url, label: 'Terms of service', + }, + { + to: `${url}/privacy`, label: 'Privacy policy', + }, + // {to: `${url}/`, label:'Cookie policy'}, + ]; + return ( +
    +
    +
    +
    + +
    + + + + {/* */} + + +
    +
    +
    + ); +} diff --git a/ui/src/components/legals/PrivacyPolicy.tsx b/ui/src/components/legals/PrivacyPolicy.tsx new file mode 100644 index 0000000..1bdc814 --- /dev/null +++ b/ui/src/components/legals/PrivacyPolicy.tsx @@ -0,0 +1,30 @@ +import ReactMarkdown from 'react-markdown'; +import React, { useEffect, useState } from 'react'; +import md from './privacy-policy.md'; +import { axios } from '../../providers/axios'; +import { Loader } from '../../commons/components/Loader'; +import { AlertError } from '../../commons/components/AlertError'; +import { useMountedState } from '../../commons/hooks/use-mounted-state'; + +export default function PrivacyPolicy() { + const [loading, setLoading] = useMountedState(true); + const [error, setError] = useState(); + const [content, setContent] = useState(); + + useEffect(() => { + axios + .get(md) + .then(({ data }) => data) + .then(setContent) + .catch(setError) + .finally(() => setLoading(false)); + }, [setLoading]); + + return loading ? ( + + ) : error ? ( + + ) : ( + {content} + ); +} diff --git a/ui/src/components/legals/TermsOfService.tsx b/ui/src/components/legals/TermsOfService.tsx new file mode 100644 index 0000000..68c6047 --- /dev/null +++ b/ui/src/components/legals/TermsOfService.tsx @@ -0,0 +1,30 @@ +import ReactMarkdown from 'react-markdown'; +import React, { useEffect, useState } from 'react'; +import md from './terms-of-service.md'; +import { axios } from '../../providers/axios'; +import { Loader } from '../../commons/components/Loader'; +import { AlertError } from '../../commons/components/AlertError'; +import { useMountedState } from '../../commons/hooks/use-mounted-state'; + +export default function TermsOfService() { + const [loading, setLoading] = useMountedState(true); + const [error, setError] = useState(); + const [content, setContent] = useState(); + + useEffect(() => { + axios + .get(md) + .then(({ data }) => data) + .then(setContent) + .catch(setError) + .finally(() => setLoading(false)); + }, [setLoading]); + + return loading ? ( + + ) : error ? ( + + ) : ( + {content} + ); +} diff --git a/ui/src/components/legals/cookie-policy.md b/ui/src/components/legals/cookie-policy.md new file mode 100644 index 0000000..091e7a5 --- /dev/null +++ b/ui/src/components/legals/cookie-policy.md @@ -0,0 +1,82 @@ +Last updated July 21, 2020 + +## Introduction + +Charlie Bravo (https://charlie-brzavo.be) (“we” or “us” or “our”) may use [cookies](https://www.cookiesandyou.com/), web beacons, tracking pixels, and other tracking technologies when you visit our websites, including any other media form, media branch, mobile website, or mobile application related or connected thereto (collectively, the “Site”) to help customize the Site and improve your experience. + +We reserve the right to make changes to this Cookie Policy at any time and for any reason. We will alert you about any changes by updating the “Last Updated” date of this Cookie Policy. Any changes or modifications will be effective immediately upon posting the updated Cookie Policy on the Site, and you waive the right to receive specific notice of each such change or modification. + +You are encouraged to periodically review this Cookie Policy to stay informed of updates. You will be deemed to have been made aware of, will be subject to, and will be deemed to have accepted the changes in any revised Cookie Policy by your continued use of the Site after the date such revised Cookie Policy is posted. + +## Use od Cookies + +A “cookie” is a string of information which assigns you a unique identifier that we store on your computer. Your browser then provides that unique identifier to use each time you submit a query to the Site. We use cookies on the Site to, among other things, keep track of services you have used, record registration information, record your user preferences, keep you logged into the Site, facilitate purchase procedures, and track the pages you visit. Cookies help us understand how the Site is being used and improve your user experience. + +## Types of Cookies + +The following types of cookies may be used when you visit the Site: + +### Advertising Cookies + +Advertising cookies are placed on your computer by advertisers and ad servers in order to display advertisements that are most likely to be of interest to you. These cookies allow advertisers and ad servers to gather information about your visits to the Site and other websites, alternate the ads sent to a specific computer, and track how often an ad has been viewed and by whom. These cookies are linked to a computer and do not gather any personal information about you. + +### Analytics Cookies + +Analytics cookies monitor how users reached the Site, and how they interact with and move around once on the Site. These cookies let us know what features on the Site are working the best and what features on the Site can be improved. + +### Our Cookies + +Our cookies are “first-party cookies”, and can be either permanent or temporary. These are necessary cookies, without which the Site won’t work properly or be able to provide certain features and functionalities. Some of these may be manually disabled in your browser, but may affect the functionality of the Site. + +### Personalization Cookies + +Personalization cookies are used to recognize repeat visitors to the Site. We use these cookies to record your browsing history, the pages you have visited, and your settings and preferences each time you visit the Site. + +### Security Cookies + +Security cookies help identify and prevent security risks. We use these cookies to authenticate users and protect user data from unauthorized parties. + +### Site Management Cookies + +Site management cookies are used to maintain your identity or session on the Site so that you are not logged off unexpectedly, and any information you enter is retained from page to page. These cookies cannot be turned off individually, but you can disable all cookies in your browser. + +### Third-Party Cookies + +Third-party cookies may be placed on your computer when you visit the Site by companies that run certain services we offer. These cookies allow the third parties to gather and track certain information about you. These cookies can be manually disabled in your browser. + +- [Google Analytics](https://analytics.google.com/) +- [Algolia](https://www.algolia.com/) +- [Google Recaptcha](https://www.google.com/recaptcha/intro/v3.html) + +## Control of Cookies + +Most browsers are set to accept cookies by default. However, you can remove or reject cookies in your browser’s settings. Please be aware that such action could affect the availability and functionality of the Site. + +For more information on how to control cookies, check your browser or device’s settings for how you can control or reject cookies, or visit the following links: + +- [Apple Safari](https://support.apple.com/kb/ph19214?locale=en_US) +- [Google Chrome](https://support.google.com/chrome/answer/95647?co=GENIE.Platform%3DDesktop&hl=en) +- [Microsoft Edge](https://support.microsoft.com/en-us/help/4468242/microsoft-edge-browsing-data-and-privacy-microsoft-privacy) +- [Microsoft Internet Explorer](https://support.microsoft.com/en-gb/help/17442/windows-internet-explorer-delete-manage-cookies) +- [Mozilla Firefox](https://support.mozilla.org/en-US/kb/enable-and-disable-cookies-website-preferences) +- [Opera](https://help.opera.com/en/latest/) +- [Android (Chrome)](https://support.google.com/chrome/answer/95647?co=GENIE.Platform%3DAndroid&hl=en&oco=1) +- [Blackberry](https://docs.blackberry.com/content/dam/docs-blackberry-com/release-pdfs/en/device-user-guides/BlackBerry-Classic-Smartphone-10.3.3-User-Guide-en.pdf) +- [Iphone or Ipad (Chrome)](https://support.google.com/chrome/answer/95647?co=GENIE.Platform%3DiOS&hl=en&oco=1) +- [Iphone or Ipad (Safari)](https://support.google.com/chrome/answer/95647?co=GENIE.Platform%3DAndroid&hl=en&oco=1) + +In addition, you may opt-out of some third-party cookies through the [Network Advertising Initiative’s Opt-Out Tool](http://optout.networkadvertising.org/?c=1#!%2F). + +## Other Tracking Technologies + +In addition to cookies, we may use web beacons, pixel tags, and other tracking technologies on the Site to help customize the Site and improve your experience. A “web beacon” or “pixel tag” is tiny object or image embedded in a web page or email. They are used to track the number of users who have visited particular pages and viewed emails, and acquire other statistical data. They collect only a limited set of data, such as a cookie number, time and date of page or email view, and a description of the page or email on which they reside. Web beacons and pixel tags cannot be declined. However, you can limit their use by controlling the cookies that interact with them. + +## Privacy Policy + +For more information about how we use information collected by cookies and other tracking technologies, please refer to our Privacy Policy. This Cookie Policy is part of and is incorporated into our [Privacy Policy](/privacy-policy). By using the Site, you agree to be bound by this Cookie Policy and our Privacy Policy. + +## Contact us + +If you have questions or comments about this Cookie Policy, please contact us at: + +[info@meli.sh](mailto:info@meli.sh) diff --git a/ui/src/components/legals/privacy-policy.md b/ui/src/components/legals/privacy-policy.md new file mode 100644 index 0000000..a41c7b8 --- /dev/null +++ b/ui/src/components/legals/privacy-policy.md @@ -0,0 +1,192 @@ +Last updated July 21, 2020 + +## Introduction + +Charlie Bravo (“we” or “us” or “our”) respects the privacy of our users (“user” or “you”). This Privacy Policy explains how we collect, use, disclose, and safeguard your information when you visit our website [name of website.com], including any other media form, media branch, mobile website, or mobile application related or connected thereto (collectively, the “Site”). Please read this privacy policy carefully. If you do not agree with the terms of this privacy policy, please do not access the site. + +We reserve the right to make changes to this Privacy Policy at any time and for any reason. We will alert you about any changes by updating the “Last Updated” date of this Privacy Policy. Any changes or modifications will be effective immediately upon posting the updated Privacy Policy on the Site, and you waive the right to receive specific notice of each such change or modification. + +You are encouraged to periodically review this Privacy Policy to stay informed of updates. You will be deemed to have been made aware of, will be subject to, and will be deemed to have accepted the changes in any revised Privacy Policy by your continued use of the Site after the date such revised Privacy Policy is posted. + +## Collection of your information + +We may collect information about you in a variety of ways. The information we may collect on the Site includes: + +### Personal Data + +Personally identifiable information, such as your name, shipping address, email address, and telephone number, and demographic information, such as your age, gender, hometown, and interests, that you voluntarily give to us [when you register with the Site or] when you choose to participate in various activities related to the Site , such as online chat and message boards. You are under no obligation to provide us with personal information of any kind, however your refusal to do so may prevent you from using certain features of the Site. + +### Derivative Data + +Information our servers automatically collect when you access the Site, such as your IP address, your browser type, your operating system, your access times, and the pages you have viewed directly before and after accessing the Site. [If you are using our mobile application, this information may also include your device name and type, your operating system, your phone number, your country, your likes and replies to a post, and other interactions with the application and other users via server log files, as well as any other information you choose to provide.] + +### Financial Data + +Financial information, such as data related to your payment method (e.g. valid credit card number, card brand, expiration date) that we may collect when you purchase, order, return, exchange, or request information about our services from the Site . [We store only very limited, if any, financial information that we collect. Otherwise, all financial information is stored by our payment processor, [Stripe](https://stripe.com/privacy), and you are encouraged to review their privacy policy and contact them directly for responses to your questions.] + +### Data From Social Networks + +User information from social networking sites, such as [Gitlab, Github, Bitbucket...], including your name, your social network username, location, gender, birth date, email address, profile picture, and public data for contacts, if you connect your account to such social networks. + +### Mobile Device Data + +Device information, such as your mobile device ID, model, and manufacturer, and information about the location of your device, if you access the Site from a mobile device. + +### Third-Party Data + +Information from third parties, such as personal information or network friends, if you connect your account to the third party and grant the Site permission to access this information. + +### Data From Contests, Giveaways, and Surveys + +Personal and other information you may provide when entering contests or giveaways and/or responding to surveys. + +## Use of your information + +Having accurate information about you permits us to provide you with a smooth, efficient, and customized experience. Specifically, we may use information collected about you via the Site to: + +- Administer sweepstakes, promotions, and contests. +- Assist law enforcement and respond to subpoena. +- Compile anonymous statistical data and analysis for use internally or with third parties. +- Create and manage your account. +- Deliver targeted advertising, coupons, newsletters, and other information regarding promotions and the Site to you. +- Email you regarding your account or order. +- Enable user-to-user communications. +- Fulfill and manage purchases, orders, payments, and other transactions related to the Site. +- Generate a personal profile about you to make future visits to the Site more personalized. +- Increase the efficiency and operation of the Site. +- Monitor and analyze usage and trends to improve your experience with the Site. +- Notify you of updates to the Site. +- Offer new products, services, [mobile applications,] and/or recommendations to you. +- Perform other business activities as needed. +- Prevent fraudulent transactions, monitor against theft, and protect against criminal activity. +- Process payments and refunds. +- Request feedback and contact you about your use of the Site. +- Resolve disputes and troubleshoot problems. +- Respond to product and customer service requests. +- Send you a newsletter. +- Solicit support for the Site. + +## Disclosure of your information + +We may share information we have collected about you in certain situations. Your information may be disclosed as follows: + +### By Law or to Protect Rights + +If we believe the release of information about you is necessary to respond to legal process, to investigate or remedy potential violations of our policies, or to protect the rights, property, and safety of others, we may share your information as permitted or required by any applicable law, rule, or regulation. This includes exchanging information with other entities for fraud protection and credit risk reduction. + +### Third-Party Service Providers + +We may share your information with third parties that perform services for us or on our behalf, including payment processing, data analysis, email delivery, hosting services, customer service, and marketing assistance. + +### Marketing Communications + +With your consent, or with an opportunity for you to withdraw consent, we may share your information with third parties for marketing purposes, as permitted by law. + +### Interactions with Other Users + +If you interact with other users of the Site , those users may see your name, profile photo, and descriptions of your activity, including sending invitations to other users, chatting with other users, liking posts, following blogs. + +### Online Postings + +When you post comments, contributions or other content to the Site, your posts may be viewed by all users and may be publicly distributed outside the Site in perpetuity. + +### Third-Party Advertisers + +We may use third-party advertising companies to serve ads when you visit the Site. These companies may use information about your visits to the Site and other websites that are contained in web cookies in order to provide advertisements about goods and services of interest to you. + +### Affiliates + +We may share your information with our affiliates, in which case we will require those affiliates to honor this Privacy Policy. Affiliates include our parent company and any subsidiaries, joint venture partners or other companies that we control or that are under common control with us. + +### Business Partners + +We may share your information with our business partners to offer you certain products, services or promotions. + +### Social Media Contacts + +If you connect to the Site through a social network, your contacts on the social network will see your name, profile photo, and descriptions of your activity. + +### Other Third Parties + +We may share your information with advertisers and investors for the purpose of conducting general business analysis. We may also share your information with such third parties for marketing purposes, as permitted by law. + +### Sale or Bankruptcy + +If we reorganize or sell all or a portion of our assets, undergo a merger, or are acquired by another entity, we may transfer your information to the successor entity. If we go out of business or enter bankruptcy, your information would be an asset transferred or acquired by a third party. You acknowledge that such transfers may occur and that the transferee may decline honor commitments we made in this Privacy Policy. + +We are not responsible for the actions of third parties with whom you share personal or sensitive data, and we have no authority to manage or control third-party solicitations. If you no longer wish to receive correspondence, emails or other communications from third parties, you are responsible for contacting the third party directly. + +## Tracking technologies + +### Cookies and Web Beacons + +We may use cookies, web beacons, tracking pixels, and other tracking technologies on the Site to help customize the Site and improve your experience. For more information on how we use cookies, please refer to our [Cookie Policy](/legals/cookie-policy) posted on the Site, which is incorporated into this Privacy Policy. By using the Site, you agree to be bound by our Cookie Policy. + +### Internet-Based Advertising + +Additionally, we may use third-party software to serve ads on the Site, implement email marketing campaigns, and manage other interactive marketing initiatives. This third-party software may use cookies or similar tracking technology to help manage and optimize your online experience with us. For more information about opting-out of interest-based ads, visit the [Network Advertising Initiative Opt-Out Tool](http://optout.networkadvertising.org/?c=1) or [Digital Advertising Alliance Opt-Out Tool](http://www.aboutads.info/choices/). + +### Website Analytics + +We may also partner with selected third-party vendors, such as [Google Analytics](https://support.google.com/analytics/answer/6004245?hl=en) and [Algolia](https://www.algolia.com/), to allow tracking technologies and remarketing services on the Site through the use of first party cookies and third-party cookies, to, among other things, analyze and track users’ use of the Site , determine the popularity of certain content and better understand online activity. By accessing the Site, you consent to the collection and use of your information by these third-party vendors. You are encouraged to review their privacy policy and contact them directly for responses to your questions. We do not transfer personal information to these third-party vendors. However, if you do not want any information to be collected and used by tracking technologies, you can visit the third-party vendor or the [Network Advertising Initiative Opt-Out Tool](http://optout.networkadvertising.org/?c=1) or [Digital Advertising Alliance Opt-Out Tool](http://www.aboutads.info/choices/). + +You should be aware that getting a new computer, installing a new browser, upgrading an existing browser, or erasing or otherwise altering your browser’s cookies files may also clear certain opt-out cookies, plug-ins, or settings. + +### Crash reporting + +We use the on-premise version [Sentry](https://sentry.io/) as a crash reporting service. + +### Security + +We use [Google Recaptcha](https://www.google.com/recaptcha/intro/v3.html) to protect our sites from bots. + +## Third-party websites + +The Site may contain links to third-party websites and applications of interest, including advertisements and external services, that are not affiliated with us. Once you have used these links to leave the Site, any information you provide to these third parties is not covered by this Privacy Policy, and we cannot guarantee the safety and privacy of your information. Before visiting and providing any information to any third-party websites, you should inform yourself of the privacy policies and practices (if any) of the third party responsible for that website, and should take those steps necessary to, in your discretion, protect the privacy of your information. We are not responsible for the content or privacy and security practices and policies of any third parties, including other sites, services or applications that may be linked to or from the Site. + +## Security of your information + +We use administrative, technical, and physical security measures to help protect your personal information. While we have taken reasonable steps to secure the personal information you provide to us, please be aware that despite our efforts, no security measures are perfect or impenetrable, and no method of data transmission can be guaranteed against any interception or other type of misuse. Any information disclosed online is vulnerable to interception and misuse by unauthorized parties. Therefore, we cannot guarantee complete security if you provide personal information. + +## Policy for children + +We do not knowingly solicit information from or market to children under the age of 13. If you become aware of any data we have collected from children under age 13, please contact us using the contact information provided below. + +## Controls for do-not-track features + +Most web browsers and some mobile operating systems include a Do-Not-Track (“DNT”) feature or setting you can activate to signal your privacy preference not to have data about your online browsing activities monitored and collected. No uniform technology standard for recognizing and implementing DNT signals has been finalized. As such, we do not currently respond to DNT browser signals or any other mechanism that automatically communicates your choice not to be tracked online. If a standard for online tracking is adopted that we must follow in the future, we will inform you about that practice in a revised version of this Privacy Policy. Most web browsers and some mobile operating systems include a Do-Not-Track (“DNT”) feature or setting you can activate to signal your privacy preference not to have data about your online browsing activities monitored and collected. If you set the DNT signal on your browser, we will respond to such DNT browser signals. + +## Options regarding your information + +Most web browsers and some mobile operating systems include a Do-Not-Track (“DNT”) feature or setting you can activate to signal your privacy preference not to have data about your online browsing activities monitored and collected. No uniform technology standard for recognizing and implementing DNT signals has been finalized. As such, we do not currently respond to DNT browser signals or any other mechanism that automatically communicates your choice not to be tracked online. If a standard for online tracking is adopted that we must follow in the future, we will inform you about that practice in a revised version of this Privacy Policy. Most web browsers and some mobile operating systems include a Do-Not-Track (“DNT”) feature or setting you can activate to signal your privacy preference not to have data about your online browsing activities monitored and collected. If you set the DNT signal on your browser, we will respond to such DNT browser signals. + +### Account Information + +You may at any time review or change the information in your account or terminate your account by: + +- Logging into your account settings and updating your account. +- Contacting us using the contact information provided below. + +Upon your request to terminate your account, we will deactivate or delete your account and information from our active databases. However, some information may be retained in our files to prevent fraud, troubleshoot problems, assist with any investigations, enforce our Terms of Use and/or comply with legal requirements. + +### Emails and Communications + +If you no longer wish to receive correspondence, emails, or other communications from us, you may opt-out by: + +- Noting your preferences at the time you register your account with the Site. +- Logging into your account settings and updating your preferences. +- Contacting us using the contact information provided below. + +If you no longer wish to receive correspondence, emails, or other communications from third parties, you are responsible for contacting the third party directly. + +## California privacy rights + +California Civil Code Section 1798.83, also known as the “Shine The Light” law, permits our users who are California residents to request and obtain from us, once a year and free of charge, information about categories of personal information (if any) we disclosed to third parties for direct marketing purposes and the names and addresses of all third parties with which we shared personal information in the immediately preceding calendar year. If you are a California resident and would like to make such a request, please submit your request in writing to us using the contact information provided below. + +If you are under 18 years of age, reside in California, and have a registered account with the Site, you have the right to request removal of unwanted data that you publicly post on the Site. To request removal of such data, please contact us using the contact information provided below, and include the email address associated with your account and a statement that you reside in California. We will make sure the data is not publicly displayed on the Site, but please be aware that the data may not be completely or comprehensively removed from our systems. + +## Contact us + +If you have questions or comments about this Privacy Policy, please contact us at: + +[info@meli.sh](mailto:info@meli.sh) diff --git a/ui/src/components/legals/terms-of-service.md b/ui/src/components/legals/terms-of-service.md new file mode 100644 index 0000000..f4bbd01 --- /dev/null +++ b/ui/src/components/legals/terms-of-service.md @@ -0,0 +1,175 @@ +Last updated July 21, 2020 + +## Agreement to Terms + +These Terms of Use constitute a legally binding agreement made between you, whether personally or on behalf of an entity (“you”) and Charlie Bravo ("Company", “we”, “us”, or “our”), concerning your access to and use of Meli as well as any other media form, media branch, mobile website or mobile application related, linked, or otherwise connected thereto (collectively, the “Site”). You agree that by accessing the Site, you have read, understood, and agreed to be bound by all of these Terms of Use. IF YOU DO NOT AGREE WITH ALL OF THESE TERMS OF USE, THEN YOU ARE EXPRESSLY PROHIBITED FROM USING THE SITE AND YOU MUST DISCONTINUE USE IMMEDIATELY. + +Supplemental terms and conditions or documents that may be posted on the Site from time to time are hereby expressly incorporated herein by reference. We reserve the right, in our sole discretion, to make changes or modifications to these Terms of Use at any time and for any reason. We will alert you about any changes by updating the “Last updated” date of these Terms of Use, and you waive any right to receive specific notice of each such change. It is your responsibility to periodically review these Terms of Use to stay informed of updates. You will be subject to, and will be deemed to have been made aware of and to have accepted, the changes in any revised Terms of Use by your continued use of the Site after the date such revised Terms of Use are posted. + +The information provided on the Site is not intended for distribution to or use by any person or entity in any jurisdiction or country where such distribution or use would be contrary to law or regulation or which would subject us to any registration requirement within such jurisdiction or country. Accordingly, those persons who choose to access the Site from other locations do so on their own initiative and are solely responsible for compliance with local laws, if and to the extent local laws are applicable. + +## Intellectual property rights + +Unless otherwise indicated, the Site is our proprietary property and all source code, databases, functionality, software, website designs, audio, video, text, photographs, and graphics on the Site (collectively, the “Content”) and the trademarks, service marks, and logos contained therein (the “Marks”) are owned or controlled by us or licensed to us, and are protected by copyright and trademark laws and various other intellectual property rights and unfair competition laws of the United States, international copyright laws, and international conventions. The Content and the Marks are provided on the Site “AS IS” for your information and personal use only. Except as expressly provided in these Terms of Use, no part of the Site and no Content or Marks may be copied, reproduced, aggregated, republished, uploaded, posted, publicly displayed, encoded, translated, transmitted, distributed, sold, licensed, or otherwise exploited for any commercial purpose whatsoever, without our express prior written permission. + +Provided that you are eligible to use the Site, you are granted a limited license to access and use the Site and to download or print a copy of any portion of the Content to which you have properly gained access solely for your personal, non-commercial use. We reserve all rights not expressly granted to you in and to the Site, the Content and the Marks. + +## User representations + +By using the Site, you represent and warrant that: (1) all registration information you submit will be true, accurate, current, and complete; (2) you will maintain the accuracy of such information and promptly update such registration information as necessary; (3) you have the legal capacity and you agree to comply with these Terms of Use; (4) you are not a minor in the jurisdiction in which you reside, or if a minor, you have received parental permission to use the Site; (5) you will not access the Site through automated or non-human means, whether through a bot, script, or otherwise; (6) you will not use the Site for any illegal or unauthorized purpose; and (7) your use of the Site will not violate any applicable law or regulation. + +If you provide any information that is untrue, inaccurate, not current, or incomplete, we have the right to suspend or terminate your account and refuse any and all current or future use of the Site (or any portion thereof). + +## User registration + +You may be required to register with the Site. You agree to keep your password confidential and will be responsible for all use of your account and password. We reserve the right to remove, reclaim, or change a username you select if we determine, in our sole discretion, that such username is inappropriate, obscene, or otherwise objectionable. + +## Prohibited activities + +You may not access or use the Site for any purpose other than that for which we make the Site available. The Site may not be used in connection with any commercial endeavors except those that are specifically endorsed or approved by us. + +As a user of the Site, you agree not to: + +1. Systematically retrieve data or other content from the Site to create or compile, directly or indirectly, a collection, compilation, database, or directory without written permission from us. +1. Trick, defraud, or mislead us and other users, especially in any attempt to learn sensitive account information such as user passwords. +1. Circumvent, disable, or otherwise interfere with security-related features of the Site, including features that prevent or restrict the use or copying of any Content or enforce limitations on the use of the Site and/or the Content contained therein. +1. Disparage, tarnish, or otherwise harm, in our opinion, us and/or the Site. +1. Use any information obtained from the Site in order to harass, abuse, or harm another person. +1. Make improper use of our support services or submit false reports of abuse or misconduct. +1. Use the Site in a manner inconsistent with any applicable laws or regulations. +1. Use the Site to advertise or offer to sell goods and services. +1. Engage in unauthorized framing of or linking to the Site. +1. Upload or transmit (or attempt to upload or to transmit) viruses, Trojan horses, or other material, including excessive use of capital letters and spamming (continuous posting of repetitive text), that interferes with any party’s uninterrupted use and enjoyment of the Site or modifies, impairs, disrupts, alters, or interferes with the use, features, functions, operation, or maintenance of the Site. +1. Engage in any automated use of the system, such as using scripts to send comments or messages, or using any data mining, robots, or similar data gathering and extraction tools. +1. Delete the copyright or other proprietary rights notice from any Content. +1. Attempt to impersonate another user or person or use the username of another user. +1. Sell or otherwise transfer your profile. +1. Upload or transmit (or attempt to upload or to transmit) any material that acts as a passive or active information collection or transmission mechanism, including without limitation, clear graphics interchange formats (“gifs”), 1×1 pixels, web bugs, cookies, or other similar devices (sometimes referred to as “spyware” or “passive collection mechanisms” or “pcms”). +1. Interfere with, disrupt, or create an undue burden on the Site or the networks or services connected to the Site. +1. Harass, annoy, intimidate, or threaten any of our employees or agents engaged in providing any portion of the Site to you. +1. Attempt to bypass any measures of the Site designed to prevent or restrict access to the Site, or any portion of the Site. +1. Copy or adapt the Site’s software, including but not limited to Flash, PHP, HTML, JavaScript, or other code. +1. Decipher, decompile, disassemble, or reverse engineer any of the software comprising or in any way making up a part of the Site. +1. Except as may be the result of standard search engine or Internet browser usage, use, launch, develop, or distribute any automated system, including without limitation, any spider, robot, cheat utility, scraper, or offline reader that accesses the Site, or using or launching any unauthorized script or other software. +1. Use a buying agent or purchasing agent to make purchases on the Site. +1. Make any unauthorized use of the Site, including collecting usernames and/or email addresses of users by electronic or other means for the purpose of sending unsolicited email, or creating user accounts by automated means or under false pretenses. +1. Use the Site as part of any effort to compete with us or otherwise use the Site and/or the Content for any revenue-generating endeavor or commercial enterprise. + +## User generated contributions + +The Site may invite you to chat, contribute to, or participate in blogs, message boards, online forums, and other functionality, and may provide you with the opportunity to create, submit, post, display, transmit, perform, publish, distribute, or broadcast content and materials to us or on the Site, including but not limited to text, writings, video, audio, photographs, graphics, comments, suggestions, or personal information or other material (collectively, "Contributions"). Contributions may be viewable by other users of the Site and through third-party websites. As such, any Contributions you transmit may be treated as non-confidential and non-proprietary. When you create or make available any Contributions, you thereby represent and warrant that: + +1. The creation, distribution, transmission, public display, or performance, and the accessing, downloading, or copying of your Contributions do not and will not infringe the proprietary rights, including but not limited to the copyright, patent, trademark, trade secret, or moral rights of any third party. +1. You are the creator and owner of or have the necessary licenses, rights, consents, releases, and permissions to use and to authorize us, the Site, and other users of the Site to use your Contributions in any manner contemplated by the Site and these Terms of Use. +1. You have the written consent, release, and/or permission of each and every identifiable individual person in your Contributions to use the name or likeness of each and every such identifiable individual person to enable inclusion and use of your Contributions in any manner contemplated by the Site and these Terms of Use. +1. Your Contributions are not false, inaccurate, or misleading. +1. Your Contributions are not unsolicited or unauthorized advertising, promotional materials, pyramid schemes, chain letters, spam, mass mailings, or other forms of solicitation. +1. Your Contributions are not obscene, lewd, lascivious, filthy, violent, harassing, libelous, slanderous, or otherwise objectionable (as determined by us). +1. Your Contributions do not ridicule, mock, disparage, intimidate, or abuse anyone. +1. Your Contributions do not advocate the violent overthrow of any government or incite, encourage, or threaten physical harm against another. +1. Your Contributions do not violate any applicable law, regulation, or rule. +1. Your Contributions do not violate the privacy or publicity rights of any third party. +1. Your Contributions do not contain any material that solicits personal information from anyone under the age of 18 or exploits people under the age of 18 in a sexual or violent manner. +1. Your Contributions do not violate any applicable law concerning child pornography, or otherwise intended to protect the health or well-being of minors; +1. Your Contributions do not include any offensive comments that are connected to race, national origin, gender, sexual preference, or physical handicap. +1. Your Contributions do not otherwise violate, or link to material that violates, any provision of these Terms of Use, or any applicable law or regulation. + +Any use of the Site in violation of the foregoing violates these Terms of Use and may result in, among other things, termination or suspension of your rights to use the Site. + +## Contribution license + +By posting your Contributions to any part of the Site or making Contributions accessible to the Site by linking your account from the Site to any of your social networking accounts, you automatically grant, and you represent and warrant that you have the right to grant, to us an unrestricted, unlimited, irrevocable, perpetual, non-exclusive, transferable, royalty-free, fully-paid, worldwide right, and license to host, use, copy, reproduce, disclose, sell, resell, publish, broadcast, retitle, archive, store, cache, publicly perform, publicly display, reformat, translate, transmit, excerpt (in whole or in part), and distribute such Contributions (including, without limitation, your image and voice) for any purpose, commercial, advertising, or otherwise, and to prepare derivative works of, or incorporate into other works, such Contributions, and grant and authorize sublicenses of the foregoing. The use and distribution may occur in any media formats and through any media branches. + +This license will apply to any form, media, or technology now known or hereafter developed, and includes our use of your name, company name, and franchise name, as applicable, and any of the trademarks, service marks, trade names, logos, and personal and commercial images you provide. You waive all moral rights in your Contributions, and you warrant that moral rights have not otherwise been asserted in your Contributions. + +We do not assert any ownership over your Contributions. You retain full ownership of all of your Contributions and any intellectual property rights or other proprietary rights associated with your Contributions. We are not liable for any statements or representations in your Contributions provided by you in any area on the Site. You are solely responsible for your Contributions to the Site and you expressly agree to exonerate us from any and all responsibility and to refrain from any legal action against us regarding your Contributions. + +We have the right, in our sole and absolute discretion, (1) to edit, redact, or otherwise change any Contributions; (2) to re-categorize any Contributions to place them in more appropriate locations on the Site; and (3) to pre-screen or delete any Contributions at any time and for any reason, without notice. We have no obligation to monitor your Contributions. + +## Social media + +As part of the functionality of the Site, you may link your account with online accounts you have with third-party service providers (each such account, a “Third-Party Account”) by either: (1) providing your Third-Party Account login information through the Site; or (2) allowing us to access your Third-Party Account, as is permitted under the applicable terms and conditions that govern your use of each Third-Party Account. You represent and warrant that you are entitled to disclose your Third-Party Account login information to us and/or grant us access to your Third-Party Account, without breach by you of any of the terms and conditions that govern your use of the applicable Third-Party Account, and without obligating us to pay any fees or making us subject to any usage limitations imposed by the third-party service provider of the Third-Party Account. By granting us access to any Third-Party Accounts, you understand that (1) we may access, make available, and store (if applicable) any content that you have provided to and stored in your Third-Party Account (the “Social Network Content”) so that it is available on and through the Site via your account, including without limitation any friend lists and (2) we may submit to and receive from your Third-Party Account additional information to the extent you are notified when you link your account with the Third-Party Account. Depending on the Third-Party Accounts you choose and subject to the privacy settings that you have set in such Third-Party Accounts, personally identifiable information that you post to your Third-Party Accounts may be available on and through your account on the Site. Please note that if a Third-Party Account or associated service becomes unavailable or our access to such Third Party Account is terminated by the third-party service provider, then Social Network Content may no longer be available on and through the Site. You will have the ability to disable the connection between your account on the Site and your Third-Party Accounts at any time. PLEASE NOTE THAT YOUR RELATIONSHIP WITH THE THIRD-PARTY SERVICE PROVIDERS ASSOCIATED WITH YOUR THIRD-PARTY ACCOUNTS IS GOVERNED SOLELY BY YOUR AGREEMENT(S) WITH SUCH THIRD-PARTY SERVICE PROVIDERS. We make no effort to review any Social Network Content for any purpose, including but not limited to, for accuracy, legality, or non-infringement, and we are not responsible for any Social Network Content. You acknowledge and agree that we may access your email address book associated with a Third-Party Account and your contacts list stored on your mobile device or tablet computer solely for purposes of identifying and informing you of those contacts who have also registered to use the Site. You can deactivate the connection between the Site and your Third-Party Account by contacting us using the contact information below or through your account settings (if applicable). We will attempt to delete any information stored on our servers that was obtained through such Third-Party Account, except the username and profile picture that become associated with your account. + +## Submissions + +You acknowledge and agree that any questions, comments, suggestions, ideas, feedback, or other information regarding the Site ("Submissions") provided by you to us are non-confidential and shall become our sole property. We shall own exclusive rights, including all intellectual property rights, and shall be entitled to the unrestricted use and dissemination of these Submissions for any lawful purpose, commercial or otherwise, without acknowledgment or compensation to you. You hereby waive all moral rights to any such Submissions, and you hereby warrant that any such Submissions are original with you or that you have the right to submit such Submissions. You agree there shall be no recourse against us for any alleged or actual infringement or misappropriation of any proprietary right in your Submissions. + +## Third-party Website and Content + +The Site may contain (or you may be sent via the Site) links to other websites ("Third-Party Websites") as well as articles, photographs, text, graphics, pictures, designs, music, sound, video, information, applications, software, and other content or items belonging to or originating from third parties ("Third-Party Content"). Such Third-Party Websites and Third-Party Content are not investigated, monitored, or checked for accuracy, appropriateness, or completeness by us, and we are not responsible for any Third-Party Websites accessed through the Site or any Third-Party Content posted on, available through, or installed from the Site, including the content, accuracy, offensiveness, opinions, reliability, privacy practices, or other policies of or contained in the Third-Party Websites or the Third-Party Content. Inclusion of, linking to, or permitting the use or installation of any Third-Party Websites or any Third-Party Content does not imply approval or endorsement thereof by us. If you decide to leave the Site and access the Third-Party Websites or to use or install any Third-Party Content, you do so at your own risk, and you should be aware these Terms of Use no longer govern. You should review the applicable terms and policies, including privacy and data gathering practices, of any website to which you navigate from the Site or relating to any applications you use or install from the Site. Any purchases you make through Third-Party Websites will be through other websites and from other companies, and we take no responsibility whatsoever in relation to such purchases which are exclusively between you and the applicable third party. You agree and acknowledge that we do not endorse the products or services offered on Third-Party Websites and you shall hold us harmless from any harm caused by your purchase of such products or services. Additionally, you shall hold us harmless from any losses sustained by you or harm caused to you relating to or resulting in any way from any Third-Party Content or any contact with Third-Party Websites. + +## Site management + +We reserve the right, but not the obligation, to: (1) monitor the Site for violations of these Terms of Use; (2) take appropriate legal action against anyone who, in our sole discretion, violates the law or these Terms of Use, including without limitation, reporting such user to law enforcement authorities; (3) in our sole discretion and without limitation, refuse, restrict access to, limit the availability of, or disable (to the extent technologically feasible) any of your Contributions or any portion thereof; (4) in our sole discretion and without limitation, notice, or liability, to remove from the Site or otherwise disable all files and content that are excessive in size or are in any way burdensome to our systems; and (5) otherwise manage the Site in a manner designed to protect our rights and property and to facilitate the proper functioning of the Site. + +## Privacy Policy + +We care about data privacy and security. Please review our [Privacy Policy](/privacy-policy). By using the Site, you agree to be bound by our Privacy Policy, which is incorporated into these Terms of Use. Please be advised the Site is hosted in Belgium. If you access the Site from any other region of the world with laws or other requirements governing personal data collection, use, or disclosure that differ from applicable laws in Belgium, then through your continued use of the Site, you are transferring your data to Belgium, and you agree to have your data transferred to and processed in Belgium. + +## Term and Termination + +These Terms of Use shall remain in full force and effect while you use the Site. WITHOUT LIMITING ANY OTHER PROVISION OF THESE TERMS OF USE, WE RESERVE THE RIGHT TO, IN OUR SOLE DISCRETION AND WITHOUT NOTICE OR LIABILITY, DENY ACCESS TO AND USE OF THE SITE (INCLUDING BLOCKING CERTAIN IP ADDRESSES), TO ANY PERSON FOR ANY REASON OR FOR NO REASON, INCLUDING WITHOUT LIMITATION FOR BREACH OF ANY REPRESENTATION, WARRANTY, OR COVENANT CONTAINED IN THESE TERMS OF USE OR OF ANY APPLICABLE LAW OR REGULATION. WE MAY TERMINATE YOUR USE OR PARTICIPATION IN THE SITE OR DELETE YOUR ACCOUNT AND ANY CONTENT OR INFORMATION THAT YOU POSTED AT ANY TIME, WITHOUT WARNING, IN OUR SOLE DISCRETION. + +If we terminate or suspend your account for any reason, you are prohibited from registering and creating a new account under your name, a fake or borrowed name, or the name of any third party, even if you may be acting on behalf of the third party. In addition to terminating or suspending your account, we reserve the right to take appropriate legal action, including without limitation pursuing civil, criminal, and injunctive redress. + +## Modifications and Interruptions + +We reserve the right to change, modify, or remove the contents of the Site at any time or for any reason at our sole discretion without notice. However, we have no obligation to update any information on our Site. We also reserve the right to modify or discontinue all or part of the Site without notice at any time. We will not be liable to you or any third party for any modification, price change, suspension, or discontinuance of the Site. + +We cannot guarantee the Site will be available at all times. We may experience hardware, software, or other problems or need to perform maintenance related to the Site, resulting in interruptions, delays, or errors. We reserve the right to change, revise, update, suspend, discontinue, or otherwise modify the Site at any time or for any reason without notice to you. You agree that we have no liability whatsoever for any loss, damage, or inconvenience caused by your inability to access or use the Site during any downtime or discontinuance of the Site. Nothing in these Terms of Use will be construed to obligate us to maintain and support the Site or to supply any corrections, updates, or releases in connection therewith. + +## Governing law + +These conditions are governed by and interpreted following the laws of Belgium, and the use of the United Nations Convention of Contracts for the International Sale of Goods is expressly excluded. If your habitual residence is in the EU, and you are a consumer, you additionally possess the protection provided to you by obligatory provisions of the law of your country of residence. Charlie Bravo and yourself both agree to submit to the non-exclusive jurisdiction of the courts of Namur, which means that you may make a claim to defend your consumer protection rights in regards to these Conditions of Use in Belgium, or in the EU country in which you reside. + +## Dispute resolution + +### Informal Negotiations + +To expedite resolution and control the cost of any dispute, controversy, or claim related to these Terms of Use (each a "Dispute" and collectively, the “Disputes”) brought by either you or us (individually, a “Party” and collectively, the “Parties”), the Parties agree to first attempt to negotiate any Dispute (except those Disputes expressly provided below) informally for at least ninety (90) days before initiating arbitration. Such informal negotiations commence upon written notice from one Party to the other Party. + +### Binding Arbitration + +Any dispute arising from the relationships between the Parties to this contract shall be determined by one arbitrator who will be chosen in accordance with the Arbitration and Internal Rules of the European Court of Arbitration being part of the European Centre of Arbitration having its seat in Strasbourg, and which are in force at the time the application for arbitration is filed, and of which adoption of this clause constitutes acceptance. The seat of arbitration shall be Namur, Belgium. The language of the proceedings shall be Français. Applicable rules of substantive law shall be the law of Belgium. + +### Restrictions + +The Parties agree that any arbitration shall be limited to the Dispute between the Parties individually. To the full extent permitted by law, (a) no arbitration shall be joined with any other proceeding; (b) there is no right or authority for any Dispute to be arbitrated on a class-action basis or to utilize class action procedures; and (c) there is no right or authority for any Dispute to be brought in a purported representative capacity on behalf of the general public or any other persons. + +### Exceptions to Informal Negotiations and Arbitration + +The Parties agree that the following Disputes are not subject to the above provisions concerning informal negotiations and binding arbitration: (a) any Disputes seeking to enforce or protect, or concerning the validity of, any of the intellectual property rights of a Party; (b) any Dispute related to, or arising from, allegations of theft, piracy, invasion of privacy, or unauthorized use; and (c) any claim for injunctive relief. If this provision is found to be illegal or unenforceable, then neither Party will elect to arbitrate any Dispute falling within that portion of this provision found to be illegal or unenforceable and such Dispute shall be decided by a court of competent jurisdiction within the courts listed for jurisdiction above, and the Parties agree to submit to the personal jurisdiction of that court. + +## Corrections + +There may be information on the Site that contains typographical errors, inaccuracies, or omissions, including descriptions, pricing, availability, and various other information. We reserve the right to correct any errors, inaccuracies, or omissions and to change or update the information on the Site at any time, without prior notice. + +## Disclaimer + +THE SITE IS PROVIDED ON AN AS-IS AND AS-AVAILABLE BASIS. YOU AGREE THAT YOUR USE OF THE SITE AND OUR SERVICES WILL BE AT YOUR SOLE RISK. TO THE FULLEST EXTENT PERMITTED BY LAW, WE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, IN CONNECTION WITH THE SITE AND YOUR USE THEREOF, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. WE MAKE NO WARRANTIES OR REPRESENTATIONS ABOUT THE ACCURACY OR COMPLETENESS OF THE SITE’S CONTENT OR THE CONTENT OF ANY WEBSITES LINKED TO THE SITE AND WE WILL ASSUME NO LIABILITY OR RESPONSIBILITY FOR ANY (1) ERRORS, MISTAKES, OR INACCURACIES OF CONTENT AND MATERIALS, (2) PERSONAL INJURY OR PROPERTY DAMAGE, OF ANY NATURE WHATSOEVER, RESULTING FROM YOUR ACCESS TO AND USE OF THE SITE, (3) ANY UNAUTHORIZED ACCESS TO OR USE OF OUR SECURE SERVERS AND/OR ANY AND ALL PERSONAL INFORMATION AND/OR FINANCIAL INFORMATION STORED THEREIN, (4) ANY INTERRUPTION OR CESSATION OF TRANSMISSION TO OR FROM THE SITE, (5) ANY BUGS, VIRUSES, TROJAN HORSES, OR THE LIKE WHICH MAY BE TRANSMITTED TO OR THROUGH THE SITE BY ANY THIRD PARTY, AND/OR (6) ANY ERRORS OR OMISSIONS IN ANY CONTENT AND MATERIALS OR FOR ANY LOSS OR DAMAGE OF ANY KIND INCURRED AS A RESULT OF THE USE OF ANY CONTENT POSTED, TRANSMITTED, OR OTHERWISE MADE AVAILABLE VIA THE SITE. WE DO NOT WARRANT, ENDORSE, GUARANTEE, OR ASSUME RESPONSIBILITY FOR ANY PRODUCT OR SERVICE ADVERTISED OR OFFERED BY A THIRD PARTY THROUGH THE SITE, ANY HYPERLINKED WEBSITE, OR ANY WEBSITE OR MOBILE APPLICATION FEATURED IN ANY BANNER OR OTHER ADVERTISING, AND WE WILL NOT BE A PARTY TO OR IN ANY WAY BE RESPONSIBLE FOR MONITORING ANY TRANSACTION BETWEEN YOU AND ANY THIRD-PARTY PROVIDERS OF PRODUCTS OR SERVICES. AS WITH THE PURCHASE OF A PRODUCT OR SERVICE THROUGH ANY MEDIUM OR IN ANY ENVIRONMENT, YOU SHOULD USE YOUR BEST JUDGMENT AND EXERCISE CAUTION WHERE APPROPRIATE. + +## Limitations of Liability + +IN NO EVENT WILL WE OR OUR DIRECTORS, EMPLOYEES, OR AGENTS BE LIABLE TO YOU OR ANY THIRD PARTY FOR ANY DIRECT, INDIRECT, CONSEQUENTIAL, EXEMPLARY, INCIDENTAL, SPECIAL, OR PUNITIVE DAMAGES, INCLUDING LOST PROFIT, LOST REVENUE, LOSS OF DATA, OR OTHER DAMAGES ARISING FROM YOUR USE OF THE SITE, EVEN IF WE HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. NOTWITHSTANDING ANYTHING TO THE CONTRARY CONTAINED HEREIN, OUR LIABILITY TO YOU FOR ANY CAUSE WHATSOEVER AND REGARDLESS OF THE FORM OF THE ACTION, WILL AT ALL TIMES BE LIMITED TO THE AMOUNT PAID, IF ANY, BY YOU TO US DURING THE THREE (3) MONTH PERIOD PRIOR TO ANY CAUSE OF ACTION ARISING. CERTAIN US STATE LAWS AND INTERNATIONAL LAWS DO NOT ALLOW LIMITATIONS ON IMPLIED WARRANTIES OR THE EXCLUSION OR LIMITATION OF CERTAIN DAMAGES. IF THESE LAWS APPLY TO YOU, SOME OR ALL OF THE ABOVE DISCLAIMERS OR LIMITATIONS MAY NOT APPLY TO YOU, AND YOU MAY HAVE ADDITIONAL RIGHTS. + +## Indemnification + +You agree to defend, indemnify, and hold us harmless, including our subsidiaries, affiliates, and all of our respective officers, agents, partners, and employees, from and against any loss, damage, liability, claim, or demand, including reasonable attorneys’ fees and expenses, made by any third party due to or arising out of: (1) your Contributions; (2) use of the Site; (3) breach of these Terms of Use; (4) any breach of your representations and warranties set forth in these Terms of Use; (5) your violation of the rights of a third party, including but not limited to intellectual property rights; or (6) any overt harmful act toward any other user of the Site with whom you connected via the Site. Notwithstanding the foregoing, we reserve the right, at your expense, to assume the exclusive defense and control of any matter for which you are required to indemnify us, and you agree to cooperate, at your expense, with our defense of such claims. We will use reasonable efforts to notify you of any such claim, action, or proceeding which is subject to this indemnification upon becoming aware of it. + +## User data + +We will maintain certain data that you transmit to the Site for the purpose of managing the performance of the Site, as well as data relating to your use of the Site. Although we perform regular routine backups of data, you are solely responsible for all data that you transmit or that relates to any activity you have undertaken using the Site. You agree that we shall have no liability to you for any loss or corruption of any such data, and you hereby waive any right of action against us arising from any such loss or corruption of such data. + +## Electronic communications, transactions, and signatures + +Visiting the Site, sending us emails, and completing online forms constitute electronic communications. You consent to receive electronic communications, and you agree that all agreements, notices, disclosures, and other communications we provide to you electronically, via email and on the Site, satisfy any legal requirement that such communication be in writing. YOU HEREBY AGREE TO THE USE OF ELECTRONIC SIGNATURES, CONTRACTS, ORDERS, AND OTHER RECORDS, AND TO ELECTRONIC DELIVERY OF NOTICES, POLICIES, AND RECORDS OF TRANSACTIONS INITIATED OR COMPLETED BY US OR VIA THE SITE. You hereby waive any rights or requirements under any statutes, regulations, rules, ordinances, or other laws in any jurisdiction which require an original signature or delivery or retention of non-electronic records, or to payments or the granting of credits by any means other than electronic means. + +## Miscellaneous + +These Terms of Use and any policies or operating rules posted by us on the Site or in respect to the Site constitute the entire agreement and understanding between you and us. Our failure to exercise or enforce any right or provision of these Terms of Use shall not operate as a waiver of such right or provision. These Terms of Use operate to the fullest extent permissible by law. We may assign any or all of our rights and obligations to others at any time. We shall not be responsible or liable for any loss, damage, delay, or failure to act caused by any cause beyond our reasonable control. If any provision or part of a provision of these Terms of Use is determined to be unlawful, void, or unenforceable, that provision or part of the provision is deemed severable from these Terms of Use and does not affect the validity and enforceability of any remaining provisions. There is no joint venture, partnership, employment or agency relationship created between you and us as a result of these Terms of Use or use of the Site. You agree that these Terms of Use will not be construed against us by virtue of having drafted them. You hereby waive any and all defenses you may have based on the electronic form of these Terms of Use and the lack of signing by the parties hereto to execute these Terms of Use. + +## Contact us + +In order to resolve a complaint regarding the Site or to receive further information regarding use of the Site, please contact us at: + +[info@meli.sh](mailto:info@meli.sh) diff --git a/ui/src/components/orgs/AddOrg.tsx b/ui/src/components/orgs/AddOrg.tsx new file mode 100644 index 0000000..117c32b --- /dev/null +++ b/ui/src/components/orgs/AddOrg.tsx @@ -0,0 +1,102 @@ +import React, { useEffect, useState } from 'react'; +import { toast } from 'react-toastify'; +import { uniqueId } from 'lodash'; +import { FormProvider, useForm } from 'react-hook-form'; +import { axios } from '../../providers/axios'; +import { Tooltip, tooltipToggle } from '../../commons/components/Tooltip'; +import { OrgNameInput } from './settings/OrgNameInput'; +import { Button } from '../../commons/components/Button'; +import { CardModal } from '../../commons/components/modals/CardModal'; +import { useMountedState } from '../../commons/hooks/use-mounted-state'; +import { UserOrg } from '../auth/user-org'; + +interface Form { + name: string; +} + +function Modal({ closeModal, onAdded }: { + closeModal; + onAdded: (org: UserOrg) => void; +}) { + const methods = useForm
    ({ + mode: 'onChange', + }); + const [loading, setLoading] = useMountedState(false); + const { handleSubmit, formState: { isDirty } } = methods; + + const onSubmit = (form: Form) => { + setLoading(true); + axios + .post(`/api/v1/orgs`, form) + .then(({ data }) => { + onAdded(data); + }) + .finally(() => { + closeModal(); + }) + .catch(err => { + toast.error(`Could not create org: ${err}`); + }) + .finally(() => setLoading(false)); + }; + + const [inputRef, setInputRef] = useState(); + + useEffect(() => { + if (inputRef) { + inputRef.focus(); + } + }, [inputRef]); + + return ( + + + +
    + +
    + +
    + ); +} + +export function AddOrg({ + children, className, tooltip = true, onAdded, +}: { + children; + className?; + tooltip?: boolean; + onAdded: (org: UserOrg) => void; +}) { + const [uid] = useState(uniqueId()); + const [isOpen, setIsOpen] = useMountedState(false); + const openModal = () => setIsOpen(true); + const closeModal = () => setIsOpen(false); + + return ( + <> +
    + {children} +
    + {tooltip && ( + + Create org + + )} + + + + + ); +} diff --git a/ui/src/components/orgs/OrgView.module.scss b/ui/src/components/orgs/OrgView.module.scss new file mode 100644 index 0000000..e69de29 diff --git a/ui/src/components/orgs/OrgView.tsx b/ui/src/components/orgs/OrgView.tsx new file mode 100644 index 0000000..f93a795 --- /dev/null +++ b/ui/src/components/orgs/OrgView.tsx @@ -0,0 +1,104 @@ +import React, { createContext, useContext, useEffect, useState } from 'react'; +import { Redirect, Route, Switch, useRouteMatch } from 'react-router-dom'; +import { SubHeader } from '../SubHeader'; +import { NotFound } from '../../commons/components/NotFound'; +import { NavPills } from '../../commons/components/NavPills'; +import { Bubble } from '../../commons/components/Bubble'; +import { SettingsIcon } from '../icons/SettingsIcon'; +import { OrgMemberIcon } from '../icons/OrgMemberIcon'; +import { axios } from '../../providers/axios'; +import { Org } from './org'; +import { useCurrentOrg } from '../../providers/OrgProvider'; +import { Loader } from '../../commons/components/Loader'; +import { AlertError } from '../../commons/components/AlertError'; +import { Staff } from './staff/Staff'; +import { useMountedState } from '../../commons/hooks/use-mounted-state'; +import { OrgSettings } from './settings/OrgSettings'; + +interface OrgContext { + org: Org; + setOrg: (org: Org) => void; +} + +const Context = createContext(undefined); +export const useOrg = () => useContext(Context); + +export function OrgView() { + // const [uid] = useState(uniqueId()); + const { url, path } = useRouteMatch(); + const { currentOrg: { org: { _id: currentOrgId } } } = useCurrentOrg(); + const [org, setOrg] = useState(); + + const [loading, setLoading] = useMountedState(true); + const [error, setError] = useState(); + useEffect(() => { + setLoading(true); + setError(undefined); + axios + .get(`/api/v1/orgs/${currentOrgId}`) + .then(({ data }) => data) + .then(setOrg) + .catch(setError) + .finally(() => setLoading(false)); + }, [currentOrgId, setLoading]); + + return loading ? ( + + ) : error ? ( + + ) : ( +
    +
    +
    + +
    +
    + + {org.name} +
    +
    +
    + + + {' '} + Settings + + ), + }, + { + to: `${url}/staff`, + label: ( + <> + + {' '} + Staff + + ), + }, + ]} + /> +
    +
    + +
    + + + }/> + + + + + +
    +
    +
    +
    + ); +} diff --git a/ui/src/components/orgs/org.ts b/ui/src/components/orgs/org.ts new file mode 100644 index 0000000..d076d24 --- /dev/null +++ b/ui/src/components/orgs/org.ts @@ -0,0 +1,8 @@ +export interface Org { + _id: string; + name: string; + color: string; + logo?: string; + createdAt: Date; + updatedAt: Date; +} diff --git a/ui/src/components/orgs/settings/OrgGeneralSettings.tsx b/ui/src/components/orgs/settings/OrgGeneralSettings.tsx new file mode 100644 index 0000000..0c31e7e --- /dev/null +++ b/ui/src/components/orgs/settings/OrgGeneralSettings.tsx @@ -0,0 +1,104 @@ +import { FormProvider, useForm } from 'react-hook-form'; +import React, { useEffect } from 'react'; +import { toast } from 'react-toastify'; +import styles from './OrgSettings.module.scss'; +import { Button } from '../../../commons/components/Button'; +import { OrgNameInput } from './OrgNameInput'; +import { axios } from '../../../providers/axios'; +import { InputError } from '../../../commons/components/forms/InputError'; +import { COLOR_PATTERN, required } from '../../../commons/components/forms/form-constants'; +import { useOrg } from '../OrgView'; +import { Org } from '../org'; +import { useMountedState } from '../../../commons/hooks/use-mounted-state'; + +interface Settings { + name: string; + color: string; // TODO color picker +} + +export function OrgGeneralSettings() { + const { org, setOrg } = useOrg(); + + const methods = useForm({ + mode: 'onChange', + }); + const { + errors, register, reset, handleSubmit, formState: { isDirty }, + } = methods; + + useEffect(() => { + if (org && reset) { + reset(org); + } + }, [org, reset]); + + const [loading, setLoading] = useMountedState(false); + + const onSubmit = (settings: Settings) => { + setLoading(true); + axios + .put(`/api/v1/orgs/${org._id}`, settings) + .then(({ data }) => data) + .then(setOrg) + .then(() => toast.success('Org saved')) + .catch(err => toast.error(`Could not update org: ${err}`)) + .finally(() => setLoading(false)); + }; + + return ( + +
    + +
    +
    + General settings +
    +
    + +
    + + + +
    +
    +
    + +
    + {/* TODO use http://reactcommunity.org/react-transition-group/css-transition */} + {isDirty && ( + + )} + +
    + +
    +
    + ); +} diff --git a/ui/src/components/orgs/settings/OrgLogo.module.scss b/ui/src/components/orgs/settings/OrgLogo.module.scss new file mode 100644 index 0000000..81c9ff3 --- /dev/null +++ b/ui/src/components/orgs/settings/OrgLogo.module.scss @@ -0,0 +1,18 @@ +@import "src/styles/variables"; + +.dropzone { + &:focus { + outline: 0; + } + + border: 2px dashed transparent; + + &.active { + border: 2px dashed $secondary; + } +} + +.logo { + width: 50px; + height: 50px; +} diff --git a/ui/src/components/orgs/settings/OrgLogo.tsx b/ui/src/components/orgs/settings/OrgLogo.tsx new file mode 100644 index 0000000..b5dc6af --- /dev/null +++ b/ui/src/components/orgs/settings/OrgLogo.tsx @@ -0,0 +1,29 @@ +import React from 'react'; +import { useOrg } from '../OrgView'; +import { Org } from '../org'; +import { useCurrentOrg } from '../../../providers/OrgProvider'; +import { Logo } from '../../commons/Logo'; + +export function OrgLogo() { + const { org, setOrg } = useOrg(); + const { currentOrg, setCurrentOrg } = useCurrentOrg(); + + const updateOrg = (value: Org) => { + setOrg(value); + if (currentOrg && currentOrg.org?._id === org._id) { + setCurrentOrg({ + ...currentOrg, + org: value, + }); + } + }; + + return ( + + ); +} diff --git a/ui/src/components/orgs/settings/OrgNameInput.tsx b/ui/src/components/orgs/settings/OrgNameInput.tsx new file mode 100644 index 0000000..8fa724b --- /dev/null +++ b/ui/src/components/orgs/settings/OrgNameInput.tsx @@ -0,0 +1,36 @@ +import { useFormContext } from 'react-hook-form'; +import React from 'react'; +import { maxLength, required } from '../../../commons/components/forms/form-constants'; +import { InputError } from '../../../commons/components/forms/InputError'; + +export function OrgNameInput({ setInputRef }: { + setInputRef?: (input: HTMLInputElement) => void; +}) { + const { register, errors } = useFormContext(); + + const ref = input => { + if (setInputRef) { + setInputRef(input); + } + register({ + required, + maxLength: maxLength(), + })(input); + }; + + return ( +
    + + + +
    + ); +} diff --git a/ui/src/components/orgs/settings/OrgSettings.module.scss b/ui/src/components/orgs/settings/OrgSettings.module.scss new file mode 100644 index 0000000..04d3eec --- /dev/null +++ b/ui/src/components/orgs/settings/OrgSettings.module.scss @@ -0,0 +1,45 @@ +@import "../../../styles/variables"; + +.form { + +} + +.add { + border: 2px solid $_blueBell; + border-radius: 50px; + background: none; + padding: 0.5rem 2rem; + color: $_blueBell; + display: block; + width: 100%; + text-transform: uppercase; + + &:active, + &:focus { + outline: none; + } + + transition: border-color $transition-duration $transition-effect, color $transition-duration $transition-effect; + + &:hover { + border-color: $dark; + color: $dark; + } +} + +.remove { + width: $input-height; + height: $input-height; + display: flex; + align-items: center; + justify-content: center; + border-radius: 50%; + font-size: 1.5rem; + cursor: pointer; + + transition: all $transition-duration $transition-effect; + + &:hover { + font-size: 2rem; + } +} diff --git a/ui/src/components/orgs/settings/OrgSettings.tsx b/ui/src/components/orgs/settings/OrgSettings.tsx new file mode 100644 index 0000000..936499f --- /dev/null +++ b/ui/src/components/orgs/settings/OrgSettings.tsx @@ -0,0 +1,12 @@ +import React from 'react'; +import { OrgGeneralSettings } from './OrgGeneralSettings'; +import { OrgLogo } from './OrgLogo'; + +export function OrgSettings() { + return ( + <> + + + + ); +} diff --git a/ui/src/components/orgs/staff/Staff.tsx b/ui/src/components/orgs/staff/Staff.tsx new file mode 100644 index 0000000..62091b0 --- /dev/null +++ b/ui/src/components/orgs/staff/Staff.tsx @@ -0,0 +1,12 @@ +import React from 'react'; +import { Invites } from './invites/Invites'; +import { Members } from './members/Members'; + +export function Staff() { + return ( + <> + + + + ); +} diff --git a/ui/src/components/orgs/staff/invites/AddInvite.module.scss b/ui/src/components/orgs/staff/invites/AddInvite.module.scss new file mode 100644 index 0000000..92a1e6f --- /dev/null +++ b/ui/src/components/orgs/staff/invites/AddInvite.module.scss @@ -0,0 +1,4 @@ +.form { + width: 400px; + max-width: 100%; +} diff --git a/ui/src/components/orgs/staff/invites/AddInvite.tsx b/ui/src/components/orgs/staff/invites/AddInvite.tsx new file mode 100644 index 0000000..04915d6 --- /dev/null +++ b/ui/src/components/orgs/staff/invites/AddInvite.tsx @@ -0,0 +1,119 @@ +import React from 'react'; +import { toast } from 'react-toastify'; +import { Controller, useForm } from 'react-hook-form'; +import { Button } from '../../../../commons/components/Button'; +import { axios } from '../../../../providers/axios'; +import { CardModal } from '../../../../commons/components/modals/CardModal'; +import { isEmail, maxLength, required } from '../../../../commons/components/forms/form-constants'; +import { InputError } from '../../../../commons/components/forms/InputError'; +import styles from './AddInvite.module.scss'; +import { OrgMember } from '../members/org-member'; +import { useCurrentOrg } from '../../../../providers/OrgProvider'; +import { Toggle } from '../../../../commons/components/forms/Toggle'; +import { useMountedState } from '../../../../commons/hooks/use-mounted-state'; + +interface InviteRequest { + email: string; + admin: boolean; +} + +function AddMemberModal({ closeModal, onAdded }: { + closeModal; + onAdded: (member: OrgMember) => void; +}) { + const { currentOrg } = useCurrentOrg(); + const { + register, errors, handleSubmit, formState: { isDirty }, control, + } = useForm({ + mode: 'onChange', + }); + const [loading, setLoading] = useMountedState(false); + + const onChange = (member: InviteRequest) => axios + .post(`/api/v1/orgs/${currentOrg.org._id}/invites`, member) + .then(({ data }) => { + onAdded(data); + }) + .then(closeModal) + .catch(err => { + toast.error(`Could not add invite: ${err}`); + }); + + const onSubmit = data => { + setLoading(true); + onChange(data).finally(() => setLoading(false)); + }; + + return ( +
    +
    + + + +
    +
    + + Admin + + )} + /> + +
    +
    + +
    +
    + ); +} + +export function AddInvite({ + children, className, onAdded, +}: { + children; + className?; + onAdded; +}) { + const [isOpen, setIsOpen] = useMountedState(false); + const openModal = () => setIsOpen(true); + const closeModal = () => setIsOpen(false); + + return ( + <> +
    + {children} +
    + + + + + ); +} diff --git a/ui/src/components/orgs/staff/invites/DeleteInvite.tsx b/ui/src/components/orgs/staff/invites/DeleteInvite.tsx new file mode 100644 index 0000000..ca6bfa8 --- /dev/null +++ b/ui/src/components/orgs/staff/invites/DeleteInvite.tsx @@ -0,0 +1,65 @@ +import React from 'react'; +import { toast } from 'react-toastify'; +import { Button } from '../../../../commons/components/Button'; +import { axios } from '../../../../providers/axios'; +import { CardModal } from '../../../../commons/components/modals/CardModal'; +import { useCurrentOrg } from '../../../../providers/OrgProvider'; +import { useMountedState } from '../../../../commons/hooks/use-mounted-state'; + +export function DeleteInvite({ + inviteId, className, children, onDelete, +}: { + inviteId: string; + children: any; + className?: string; + onDelete: () => void; +}) { + const [isOpen, setIsOpen] = useMountedState(false); + const [loading, setLoading] = useMountedState(false); + const { currentOrg } = useCurrentOrg(); + + const deleteInvite = () => { + setLoading(true); + return axios + .delete(`/api/v1/orgs/${currentOrg.org._id}/invites/${inviteId}`) + .then(() => { + setIsOpen(false); + onDelete(); + }) + .catch(err => { + toast.error(`Could not delete invite: ${err}`); + }) + .finally(() => setLoading(false)); + }; + + return ( + <> + setIsOpen(false)} + > +

    Are you sure you want to delete this invite ?

    +
    + + +
    +
    +
    setIsOpen(true)} className={className}> + {children} +
    + + ); +} diff --git a/ui/src/components/orgs/staff/invites/InviteView.tsx b/ui/src/components/orgs/staff/invites/InviteView.tsx new file mode 100644 index 0000000..11b37fb --- /dev/null +++ b/ui/src/components/orgs/staff/invites/InviteView.tsx @@ -0,0 +1,45 @@ +import React from 'react'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faTimes } from '@fortawesome/free-solid-svg-icons'; +import { Invite } from './invite'; +import { DeleteInvite } from './DeleteInvite'; +import { FromNow } from '../../../../commons/components/FromNow'; +import { ButtonIcon } from '../../../../commons/components/ButtonIcon'; + +export function InviteView({ invite, onDelete }: { + invite: Invite; + onDelete: () => void; +}) { + const expired = new Date(invite.expiresAt).getTime() < Date.now(); + + return ( +
    +
    + {invite.email} + {invite.memberOptions.admin && ( + admin + )} +
    + +
    + {expired ? ( +
    + expired +
    + ) : ( + <> +
    + pending invitation +
    + + + )} + + + + + +
    +
    + ); +} diff --git a/ui/src/components/orgs/staff/invites/Invites.module.scss b/ui/src/components/orgs/staff/invites/Invites.module.scss new file mode 100644 index 0000000..b4db102 --- /dev/null +++ b/ui/src/components/orgs/staff/invites/Invites.module.scss @@ -0,0 +1,15 @@ +@import "src/styles/variables"; + +.loader { + position: absolute; + right: 15px; + top: 50%; + transform: translateY(-50%); +} + +.add { + text-transform: uppercase; + font-weight: bold; + cursor: pointer; + text-align: center !important; +} diff --git a/ui/src/components/orgs/staff/invites/Invites.tsx b/ui/src/components/orgs/staff/invites/Invites.tsx new file mode 100644 index 0000000..e29c515 --- /dev/null +++ b/ui/src/components/orgs/staff/invites/Invites.tsx @@ -0,0 +1,79 @@ +import React, { useEffect, useState } from 'react'; +import { toast } from 'react-toastify'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faPaperPlane } from '@fortawesome/free-regular-svg-icons'; +import { faPlus } from '@fortawesome/free-solid-svg-icons'; +import { Loader } from '../../../../commons/components/Loader'; +import { AlertError } from '../../../../commons/components/AlertError'; +import { Invite } from './invite'; +import { axios } from '../../../../providers/axios'; +import { InviteView } from './InviteView'; +import { AddInvite } from './AddInvite'; +import { useCurrentOrg } from '../../../../providers/OrgProvider'; +import { useMountedState } from '../../../../commons/hooks/use-mounted-state'; +import { ButtonIcon } from '../../../../commons/components/ButtonIcon'; + +function sortInvites(a: Invite, b: Invite): number { + return new Date(b.expiresAt).getTime() - new Date(a.expiresAt).getTime(); +} + +export function Invites() { + const [loading, setLoading] = useMountedState(true); + const [error, setError] = useState(); + const [items, setItems] = useState(); + const { currentOrg } = useCurrentOrg(); + + useEffect(() => { + setLoading(true); + setError(undefined); + axios.get(`/api/v1/orgs/${currentOrg.org._id}/invites`) + .then(({ data }) => data) + .then(setItems) + .catch(setError) + .catch(err => toast.error(`Could not list invites: ${err}`)) + .finally(() => setLoading(false)); + }, [currentOrg, setLoading]); + + const onAdd = invite => { + setItems([invite, ...items].sort(sortInvites)); + }; + + const onDelete = (inviteId: string) => { + setItems(items.filter(({ _id }) => _id !== inviteId)); + }; + + return ( +
    +
    +
    + + Invites +
    + + + + + +
    +
    + {loading ? ( + + ) : error ? ( + + ) : items.length === 0 ? ( +
    No invites to show
    + ) : ( +
      + {items.map(invite => ( + onDelete(invite._id)} + /> + ))} +
    + )} +
    +
    + ); +} diff --git a/ui/src/components/orgs/staff/invites/invite.ts b/ui/src/components/orgs/staff/invites/invite.ts new file mode 100644 index 0000000..386e991 --- /dev/null +++ b/ui/src/components/orgs/staff/invites/invite.ts @@ -0,0 +1,8 @@ +export interface Invite { + _id: string; + email: string; + expiresAt: Date; + memberOptions: { + admin: boolean; + }; +} diff --git a/ui/src/components/orgs/staff/members/DeleteMember.tsx b/ui/src/components/orgs/staff/members/DeleteMember.tsx new file mode 100644 index 0000000..d6bd25e --- /dev/null +++ b/ui/src/components/orgs/staff/members/DeleteMember.tsx @@ -0,0 +1,63 @@ +import React from 'react'; +import { toast } from 'react-toastify'; +import { Button } from '../../../../commons/components/Button'; +import { axios } from '../../../../providers/axios'; +import { CardModal } from '../../../../commons/components/modals/CardModal'; +import { useMountedState } from '../../../../commons/hooks/use-mounted-state'; + +export function DeleteMember({ + memberId, className, children, onDelete, +}: { + memberId: string; + children: any; + className?: string; + onDelete: () => void; +}) { + const [isOpen, setIsOpen] = useMountedState(false); + const [loading, setLoading] = useMountedState(false); + + const deleteMember = () => { + setLoading(true); + return axios + .delete(`/api/v1/members/${memberId}`) + .then(() => { + setIsOpen(false); + onDelete(); + }) + .catch(err => { + toast.error(`Could not delete member: ${err}`); + }) + .finally(() => setLoading(false)); + }; + + return ( + <> + setIsOpen(false)} + > +

    Are you sure you want to delete this member ?

    +
    + + +
    +
    +
    setIsOpen(true)} className={className}> + {children} +
    + + ); +} diff --git a/ui/src/components/orgs/staff/members/MemberView.tsx b/ui/src/components/orgs/staff/members/MemberView.tsx new file mode 100644 index 0000000..6ffe8d9 --- /dev/null +++ b/ui/src/components/orgs/staff/members/MemberView.tsx @@ -0,0 +1,99 @@ +import React, { useState } from 'react'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faTrashAlt } from '@fortawesome/free-regular-svg-icons'; +import { faEllipsisV } from '@fortawesome/free-solid-svg-icons'; +import { uniqueId } from 'lodash'; +import { toast } from 'react-toastify'; +import { OrgMember } from './org-member'; +import { DeleteMember } from './DeleteMember'; +import { ButtonIcon } from '../../../../commons/components/ButtonIcon'; +import { Toggle } from '../../../../commons/components/forms/Toggle'; +import { Dropdown, dropdownToggle } from '../../../../commons/components/dropdown/Dropdown'; +import { axios } from '../../../../providers/axios'; +import { useCurrentOrg } from '../../../../providers/OrgProvider'; +import { Loader } from '../../../../commons/components/Loader'; +import { DropdownLink } from '../../../../commons/components/dropdown/DropdownLink'; +import { useMountedState } from '../../../../commons/hooks/use-mounted-state'; +import { IsOwner } from '../../../auth/IsOwner'; + +function AdminToggle({ member, setMember }: { + member: OrgMember; + setMember: (member: OrgMember) => void; +}) { + const [loading, setLoading] = useMountedState(false); + + const toggle = () => { + setLoading(true); + axios + .put(`/api/v1/members/${member._id}`, { + admin: !member.admin, + }) + .then(({ data }) => data) + .then(setMember) + .catch(err => { + toast.error(`Could not toggle admin: ${err}`); + }) + .finally(() => setLoading(false)); + }; + + return ( + + {loading && ( + + )} + admin + + ); +} + +export function MemberView({ + member, setMember, onDelete, +}: { + member: OrgMember; + setMember: (member: OrgMember) => void; + onDelete: () => void; +}) { + const [uid] = useState(uniqueId()); + const { currentOrg } = useCurrentOrg(); + + return ( +
  • +
    + {member.name} + {member.email} + {member.owner && ( + owner + )} + {currentOrg.member._id === member._id && ( + you + )} +
    + +
    + + {!member.owner && ( + + )} + + + + + + } disabled={member.owner}> + Delete + + + + +
    +
  • + ); +} diff --git a/ui/src/components/orgs/staff/members/Members.module.scss b/ui/src/components/orgs/staff/members/Members.module.scss new file mode 100644 index 0000000..b4db102 --- /dev/null +++ b/ui/src/components/orgs/staff/members/Members.module.scss @@ -0,0 +1,15 @@ +@import "src/styles/variables"; + +.loader { + position: absolute; + right: 15px; + top: 50%; + transform: translateY(-50%); +} + +.add { + text-transform: uppercase; + font-weight: bold; + cursor: pointer; + text-align: center !important; +} diff --git a/ui/src/components/orgs/staff/members/Members.tsx b/ui/src/components/orgs/staff/members/Members.tsx new file mode 100644 index 0000000..e2f648e --- /dev/null +++ b/ui/src/components/orgs/staff/members/Members.tsx @@ -0,0 +1,150 @@ +import React, { useEffect, useRef, useState } from 'react'; +import { toast } from 'react-toastify'; +import { AlertError } from '../../../../commons/components/AlertError'; +import { LoadMore } from '../../../../commons/components/LoadMore'; +import { SearchInput } from '../../../sites/releases/SearchInput'; +import { useOrg } from '../../OrgView'; +import { getMembers, OrgMembersSearchQuery } from './get-members'; +import { useMountedState } from '../../../../commons/hooks/use-mounted-state'; +import { MemberView } from './MemberView'; +import { OrgMember } from './org-member'; +import { OrgMemberIcon } from '../../../icons/OrgMemberIcon'; +import { Loader } from '../../../../commons/components/Loader'; + +export function Members() { + const [loading, setLoading] = useMountedState(true); + const [error, setError] = useState(); + const [items, setItems] = useState([]); + const { org } = useOrg(); + + const itemsRef = useRef([]); + const [canLoadMore, setCanLoadMore] = useState(false); + const [searching, setSearching] = useState(false); + const previousSearch = useRef(''); + const [search, setSearch] = useState(''); + const searchQueryRef = useRef({ + search: '', page: 0, size: 10, + }); + const [searchQuery, setSearchQuery] = useState(searchQueryRef.current); + + useEffect(() => { + // prevents duplicate calls when search input is initialized + if (search === previousSearch.current) { + return; + } + + let query: OrgMembersSearchQuery = { + ...searchQueryRef.current, + search, + }; + + // start to search + if (search && search !== previousSearch.current) { + query = { + ...query, + page: 0, + }; + } + + // stop searching (cleared search) + if (previousSearch.current && !search) { + query = { + ...query, + page: 0, + }; + itemsRef.current = []; + } + + previousSearch.current = search; + + setSearchQuery(query); + }, [search]); + + useEffect(() => { + if (searchQuery.search) { + setSearching(true); + } + + setLoading(true); + setError(undefined); + + getMembers(org._id, searchQuery) + .then(data => { + itemsRef.current = searchQuery.search ? data.items : [...itemsRef.current, ...data.items]; + setItems(itemsRef.current); + setCanLoadMore(itemsRef.current.length !== data.count); + }) + .catch(setError) + .catch(err => toast.error(`Could not list members: ${err}`)) + .finally(() => { + setSearching(false); + setLoading(false); + }); + }, [org, searchQuery, setLoading]); + + const nextPage = () => { + setSearchQuery({ + ...searchQuery, + page: searchQuery.page + 1, + }); + }; + + const onDelete = (memberId: string) => { + setItems(items.filter(({ _id }) => _id !== memberId)); + }; + + const onEdit = (member: OrgMember) => { + setItems(items.map(item => (item._id === member._id ? member : item))); + }; + + return ( + <> +
    +
    +
    + + Members +
    +
    + +
    +
    +
    + {loading ? ( + + ) : error ? ( + + ) : items.length === 0 ? ( + <>No search results found + ) : ( +
      + {items.map(member => ( + onDelete(member._id)} + setMember={onEdit} + /> + ))} + {canLoadMore && ( + + )} + {error && ( + + )} +
    + )} +
    +
    + + ); +} diff --git a/ui/src/components/orgs/staff/members/get-members.ts b/ui/src/components/orgs/staff/members/get-members.ts new file mode 100644 index 0000000..e79ca61 --- /dev/null +++ b/ui/src/components/orgs/staff/members/get-members.ts @@ -0,0 +1,22 @@ +import { axios } from '../../../../providers/axios'; +import { Page } from '../../../../commons/types/page'; +import { OrgMember } from './org-member'; + +export interface OrgMembersSearchQuery { + search: string; + page: number; + size: number; +} + +export function getMembers( + orgId: string, + query?: OrgMembersSearchQuery, +): Promise> { + return axios + .get(`/api/v1/orgs/${orgId}/members`, { + params: { + ...query, + }, + }) + .then(res => res.data); +} diff --git a/ui/src/components/orgs/staff/members/org-member.ts b/ui/src/components/orgs/staff/members/org-member.ts new file mode 100644 index 0000000..0504c19 --- /dev/null +++ b/ui/src/components/orgs/staff/members/org-member.ts @@ -0,0 +1,7 @@ +export interface OrgMember { + _id: string; + name: string; + email: string; + admin: boolean; + owner: boolean; +} diff --git a/ui/src/components/sidebar/SideBar.module.scss b/ui/src/components/sidebar/SideBar.module.scss new file mode 100644 index 0000000..ebd5221 --- /dev/null +++ b/ui/src/components/sidebar/SideBar.module.scss @@ -0,0 +1,6 @@ +@import 'src/styles/variables'; + +.container { + flex-shrink: 0; + background: $gray-100; +} diff --git a/ui/src/components/sidebar/SideBar.tsx b/ui/src/components/sidebar/SideBar.tsx new file mode 100644 index 0000000..3654588 --- /dev/null +++ b/ui/src/components/sidebar/SideBar.tsx @@ -0,0 +1,18 @@ +import React from 'react'; +import classNames from 'classnames'; +import styles from './SideBar.module.scss'; +import { Teams } from './Teams'; + +export function SideBar({ className }: { className? }) { + return ( + + ); +} diff --git a/ui/src/components/sidebar/Sites.module.scss b/ui/src/components/sidebar/Sites.module.scss new file mode 100644 index 0000000..2da8a22 --- /dev/null +++ b/ui/src/components/sidebar/Sites.module.scss @@ -0,0 +1,39 @@ +@import "../../styles/variables"; + +.linkHeader { + text-transform: uppercase; + color: $dark; + display: block; + font-size: .8rem; + font-weight: bold; + margin-bottom: 10px; +} + +.site { + color: inherit; + height: 30px; + display: flex; + align-items: center; + border-radius: $border-radius; + padding: 0 .5rem; + font-weight: 600; + + transition: all $transition-duration $transition-effect; + + &:hover { + background: $gray-200; + text-decoration: none; + color: inherit; + } +} + +.siteIcon { + width: 20px; + height: 20px; + border-radius: 50%; + display: block; +} + +.active { + background: $gray-200; +} diff --git a/ui/src/components/sidebar/Sites.tsx b/ui/src/components/sidebar/Sites.tsx new file mode 100644 index 0000000..dd0f354 --- /dev/null +++ b/ui/src/components/sidebar/Sites.tsx @@ -0,0 +1,96 @@ +import React, { useEffect, useState } from 'react'; +import { NavLink } from 'react-router-dom'; +import { toast } from 'react-toastify'; +import classNames from 'classnames'; +import { Loader } from '../../commons/components/Loader'; +import { AlertError } from '../../commons/components/AlertError'; +import { getTeamSites } from '../sites/get-team-sites'; +import { Site } from '../sites/site'; +import styles from './Sites.module.scss'; +import { Bubble } from '../../commons/components/Bubble'; +import { useMountedState } from '../../commons/hooks/use-mounted-state'; +import { EventType } from '../../websockets/event-type'; +import { useRoom } from '../../websockets/use-room'; + +function sortSites(a: Site, b: Site) { + return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(); +} + +function ListItem({ site, onDeleted }: { + site: Site; + onDeleted: () => void; +}) { + useRoom<{ site: Site }>('site', site._id, [EventType.site_deleted], ({ site: s }) => { + if (site._id === s._id) { + onDeleted(); + } + }); + + return ( + + + {site.name} + + ); +} + +export function Sites({ teamId, className }: { teamId; className? }) { + const [loading, setLoading] = useMountedState(true); + const [error, setError] = useState(); + const [sites, setItems] = useState(); + + useRoom<{ site: Site }>('team', teamId, [EventType.site_added], ({ site }) => { + if (site.teamId === teamId) { + setItems([site, ...sites].sort(sortSites)); + } + }); + + useEffect(() => { + setLoading(true); + setError(undefined); + getTeamSites(teamId) + .then(items => { + setItems(items.sort(sortSites)); + }) + .catch(setError) + .catch(err => toast.error(`Could not list sites: ${err}`)) + .finally(() => setLoading(false)); + }, [teamId, setLoading]); + + const onDelete = (siteId: string) => { + setItems(sites.filter(s => s._id !== siteId)); + }; + + const emptyList = ( +
    + No sites to show +
    + ); + + return loading ? ( + + ) : error ? ( + + ) : ( +
    +
    + {sites.length === 0 ? ( + emptyList + ) : ( + sites.map(site => ( + onDelete(site._id)} + key={site._id} + /> + )) + )} +
    +
    + ); +} diff --git a/ui/src/components/sidebar/Teams.module.scss b/ui/src/components/sidebar/Teams.module.scss new file mode 100644 index 0000000..f76c56f --- /dev/null +++ b/ui/src/components/sidebar/Teams.module.scss @@ -0,0 +1,34 @@ +@import "../../styles/variables"; + +.linkHeader { + color: $dark; + font-size: .8rem; + font-weight: bold; + margin-bottom: 10px; + display: flex; + justify-content: space-between; + align-items: center; + position: relative; +} + +.siteIcon { + width: 20px; + height: 20px; + border-radius: 50%; + display: block; +} + +.active { + &:before { + $size: 8px; + content: ''; + width: $size; + height: $size; + border-radius: 50%; + background: $success; + position: absolute; + top: 50%; + left: -$size - 3px; + transform: translateY(-50%); + } +} diff --git a/ui/src/components/sidebar/Teams.tsx b/ui/src/components/sidebar/Teams.tsx new file mode 100644 index 0000000..25e2106 --- /dev/null +++ b/ui/src/components/sidebar/Teams.tsx @@ -0,0 +1,110 @@ +import React, { useEffect, useRef, useState } from 'react'; +import { NavLink } from 'react-router-dom'; +import { toast } from 'react-toastify'; +import { faPlus } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { Loader } from '../../commons/components/Loader'; +import { AlertError } from '../../commons/components/AlertError'; +import { Team } from '../teams/team'; +import styles from './Teams.module.scss'; +import { Bubble } from '../../commons/components/Bubble'; +import { axios } from '../../providers/axios'; +import { AddSite } from '../sites/AddSite'; +import { ButtonIcon } from '../../commons/components/ButtonIcon'; +import { Sites } from './Sites'; +import { useCurrentOrg } from '../../providers/OrgProvider'; +import { useMountedState } from '../../commons/hooks/use-mounted-state'; +import { useRoom } from '../../websockets/use-room'; +import { EventType } from '../../websockets/event-type'; + +function sortTeams(a: Team, b: Team) { + return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(); +} + +function TeamSection({ team, className, onDelete }: { team: Team; className?; onDelete: () => void }) { + useRoom<{ team: Team }>('team', team._id, [EventType.team_deleted], ({ team: deletedTeam }) => { + if (team._id === deletedTeam._id) { + onDelete(); + } + }); + + return ( +
    +
    + + + {team.name} + +
    + + + + + +
    +
    + +
    + ); +} + +export function Teams({ className }: { className? }) { + const [loading, setLoading] = useMountedState(true); + const [error, setError] = useState(); + const [teams, setTeams] = useState(); + const teamsRef = useRef([]); + const { currentOrg } = useCurrentOrg(); + + useRoom<{ team: Team }>('org', currentOrg.org._id, [EventType.team_added], ({ team }) => { + if (team.orgId === currentOrg.org._id) { + setTeams([team, ...teams]); + } + }); + + useEffect(() => { + setLoading(true); + setError(undefined); + axios + .get(`/api/v1/orgs/${currentOrg.org._id}/teams`) + .then(({ data }) => { + teamsRef.current = data; + setTeams(teamsRef.current.sort(sortTeams)); + }) + .catch(setError) + .catch(err => toast.error(`Could not list teams: ${err}`)) + .finally(() => setLoading(false)); + }, [currentOrg, setLoading]); + + const onTeamDeleted = (team: Team) => { + setTeams(teams.filter(({ _id }) => _id !== team._id)); + }; + + return loading ? ( + + ) : error ? ( + + ) : ( +
    + {teams.length === 0 ? ( + <> + No teams to show + + ) : ( + <> + {teams.map(team => ( + onTeamDeleted(team)} + className="mb-3" + /> + ))} + + )} +
    + ); +} diff --git a/ui/src/components/sites/AddSite.tsx b/ui/src/components/sites/AddSite.tsx new file mode 100644 index 0000000..7574a35 --- /dev/null +++ b/ui/src/components/sites/AddSite.tsx @@ -0,0 +1,100 @@ +import React, { useEffect, useState } from 'react'; +import { toast } from 'react-toastify'; +import { uniqueId } from 'lodash'; +import { FormProvider, useForm } from 'react-hook-form'; +import { axios } from '../../providers/axios'; +import { routerHistory } from '../../providers/history'; +import { Tooltip, tooltipToggle } from '../../commons/components/Tooltip'; +import { SiteNameInput } from './settings/SiteNameInput'; +import { Button } from '../../commons/components/Button'; +import { CardModal } from '../../commons/components/modals/CardModal'; +import { Site } from './site'; +import { useMountedState } from '../../commons/hooks/use-mounted-state'; +import { IsAdmin } from '../auth/IsAdmin'; + +function AddSiteModal({ teamId, closeModal }: { teamId; closeModal }) { + const methods = useForm({ + mode: 'onChange', + }); + const [loading, setLoading] = useMountedState(false); + const { handleSubmit, formState: { isDirty } } = methods; + + const onChange = formData => axios + .post(`/api/v1/teams/${teamId}/sites`, formData) + .then(({ data }) => { + routerHistory.push(`/sites/${data._id}`); + }) + .finally(() => { + closeModal(); + }) + .catch(err => { + toast.error(`Could not create site: ${err}`); + }); + + const onSubmit = data => { + setLoading(true); + onChange(data).finally(() => setLoading(false)); + }; + + const [inputRef, setInputRef] = useState(); + + useEffect(() => { + if (inputRef) { + inputRef.focus(); + } + }, [inputRef]); + + return ( + +
    + +
    + +
    + +
    + ); +} + +export function AddSite({ + teamId, children, className, tooltip = true, +}: { + teamId: string; + children; + className?; + tooltip?: boolean; +}) { + const [uid] = useState(uniqueId()); + const [isOpen, setIsOpen] = useMountedState(false); + const openModal = () => setIsOpen(true); + const closeModal = () => setIsOpen(false); + + return ( + <> + +
    + {children} +
    +
    + {tooltip && ( + + Add site + + )} + + + + + ); +} diff --git a/ui/src/components/sites/DeleteSite.tsx b/ui/src/components/sites/DeleteSite.tsx new file mode 100644 index 0000000..7fc6432 --- /dev/null +++ b/ui/src/components/sites/DeleteSite.tsx @@ -0,0 +1,64 @@ +import React from 'react'; +import { toast } from 'react-toastify'; +import { axios } from '../../providers/axios'; +import { routerHistory } from '../../providers/history'; +import { Button } from '../../commons/components/Button'; +import { CardModal } from '../../commons/components/modals/CardModal'; +import { useMountedState } from '../../commons/hooks/use-mounted-state'; + +export function DeleteSite({ + id, teamId, className, children, +}: { + id: string; + teamId: string; + className?: string; + children: any; +}) { + const [isOpen, setIsOpen] = useMountedState(false); + const [loading, setLoading] = useMountedState(false); + + const deleteSite = () => { + setLoading(true); + return axios + .delete(`/api/v1/sites/${id}`) + .then(() => { + setIsOpen(false); + routerHistory.push(`/teams/${teamId}/sites`); + }) + .catch(err => { + toast.error(`Could not delete site: ${err}`); + }) + .finally(() => setLoading(false)); + }; + + return ( + <> + setIsOpen(false)} + > +

    Are you sure you want to delete this token ?

    +
    + + +
    +
    +
    setIsOpen(true)} className={className}> + {children} +
    + + ); +} diff --git a/ui/src/components/sites/SiteCard.module.scss b/ui/src/components/sites/SiteCard.module.scss new file mode 100644 index 0000000..0980e23 --- /dev/null +++ b/ui/src/components/sites/SiteCard.module.scss @@ -0,0 +1,5 @@ +@import '../../styles/variables'; + +.container { + +} diff --git a/ui/src/components/sites/SiteCard.tsx b/ui/src/components/sites/SiteCard.tsx new file mode 100644 index 0000000..e74480c --- /dev/null +++ b/ui/src/components/sites/SiteCard.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import classNames from 'classnames'; +import styles from './SiteCard.module.scss'; +import { Site } from './site'; +import { Bubble } from '../../commons/components/Bubble'; + +export function SiteCard({ site, className }: { + site: Site; + className?; +}) { + return ( +
    +
    + + {site.name} +
    +
    + ); +} diff --git a/ui/src/components/sites/SiteList.module.scss b/ui/src/components/sites/SiteList.module.scss new file mode 100644 index 0000000..766741c --- /dev/null +++ b/ui/src/components/sites/SiteList.module.scss @@ -0,0 +1,6 @@ +.loader { + position: absolute; + right: 15px; + top: 50%; + transform: translateY(-50%); +} diff --git a/ui/src/components/sites/SiteList.tsx b/ui/src/components/sites/SiteList.tsx new file mode 100644 index 0000000..d8324ef --- /dev/null +++ b/ui/src/components/sites/SiteList.tsx @@ -0,0 +1,68 @@ +import React, { useEffect, useRef, useState } from 'react'; +import { Link, useParams } from 'react-router-dom'; +import { toast } from 'react-toastify'; +import { uniqueId } from 'lodash'; +import { Loader } from '../../commons/components/Loader'; +import { EmptyList } from '../../commons/components/EmptyList'; +import { getTeamSites } from './get-team-sites'; +import { SiteCard } from './SiteCard'; +import { AlertError } from '../../commons/components/AlertError'; +import { Site } from './site'; +import { AddSite } from './AddSite'; +import { SiteIcon } from '../icons/SiteIcon'; +import { useMountedState } from '../../commons/hooks/use-mounted-state'; + +export function SiteList() { + const { teamId } = useParams(); + const [loading, setLoading] = useMountedState(true); + const [error, setError] = useState(); + const [items, setItems] = useState(); + const itemsRef = useRef([]); + + useEffect(() => { + setLoading(true); + setError(undefined); + getTeamSites(teamId) + .then(data => { + itemsRef.current.push(...data); + setItems(itemsRef.current); + }) + .catch(setError) + .catch(err => toast.error(`Could not list repos: ${err}`)) + .finally(() => setLoading(false)); + }, [teamId, setLoading]); + + const emptyList = ( + } + title="No sites" + > + + + + + ); + + return loading ? ( + + ) : error ? ( + + ) : ( +
    + {items.length === 0 ? ( + emptyList + ) : ( + <> +

    Sites

    + {items.map(site => ( + + + + ))} + + )} +
    + ); +} diff --git a/ui/src/components/sites/SiteView.module.scss b/ui/src/components/sites/SiteView.module.scss new file mode 100644 index 0000000..e69de29 diff --git a/ui/src/components/sites/SiteView.tsx b/ui/src/components/sites/SiteView.tsx new file mode 100644 index 0000000..175b44f --- /dev/null +++ b/ui/src/components/sites/SiteView.tsx @@ -0,0 +1,171 @@ +import React, { createContext, useContext, useEffect, useState } from 'react'; +import { Redirect, Route, Switch, useParams, useRouteMatch } from 'react-router-dom'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faTrashAlt } from '@fortawesome/free-regular-svg-icons'; +import { faEllipsisV, faExternalLinkAlt } from '@fortawesome/free-solid-svg-icons'; +import { uniqueId } from 'lodash'; +import { axios } from '../../providers/axios'; +import { Site } from './site'; +import { Loader } from '../../commons/components/Loader'; +import { AlertError } from '../../commons/components/AlertError'; +import { Dropdown, dropdownToggle } from '../../commons/components/dropdown/Dropdown'; +import { DropdownLink } from '../../commons/components/dropdown/DropdownLink'; +import { SubHeader } from '../SubHeader'; +import { NotFound } from '../../commons/components/NotFound'; +import { NavPills } from '../../commons/components/NavPills'; +import { DeleteSite } from './DeleteSite'; +import { ExternalLink } from '../../commons/components/ExternalLink'; +import { Bubble } from '../../commons/components/Bubble'; +import { TokenIcon } from '../icons/TokenIcon'; +import { SettingsIcon } from '../icons/SettingsIcon'; +import { BranchIcon } from '../icons/BranchIcon'; +import { ButtonIcon } from '../../commons/components/ButtonIcon'; +import { useMountedState } from '../../commons/hooks/use-mounted-state'; +import { HookIcon } from '../icons/HookIcon'; +import { HookProvider } from '../hooks/HookProvider'; +import { Hooks } from '../hooks/Hooks'; +import { Branches } from './branches/Branches'; +import { Tokens } from './tokens/Tokens'; +import { Releases } from './releases/Releases'; +import { ReleaseIcon } from '../icons/ReleaseIcon'; +import { SiteSettings } from './settings/SiteSettings'; + +interface SiteContext { + site: Site; + setSite: (site: Site) => void; +} + +const Context = createContext(undefined); +export const useSite = () => useContext(Context); + +export function SiteView() { + const { url, path } = useRouteMatch(); + const { siteId } = useParams(); + const [uid] = useState(uniqueId()); + + const [loading, setLoading] = useMountedState(true); + const [error, setError] = useState(); + const [site, setSite] = useState(); + + useEffect(() => { + setLoading(true); + setError(undefined); + axios + .get(`/api/v1/sites/${siteId}`) + .then(({ data }) => data) + .then(setSite) + .catch(setError) + .finally(() => setLoading(false)); + }, [siteId, setLoading]); + + return loading ? ( + + ) : error ? ( + + ) : ( +
    +
    +
    + +
    +
    + + {site.name} +
    + + + +
    +
    + + + ️ Branches + + ), + }, + { + to: `${url}/releases`, + label: ( + <> + + ️ Releases + + ), + }, + { + to: `${url}/tokens`, + label: ( + <> + + {' '} + Tokens + + ), + }, + { + to: `${url}/hooks`, + label: ( + <> + + {' '} + Hooks + + ), + }, + { + to: `${url}/settings`, + label: ( + <> + + {' '} + Settings + + ), + }, + ]} + /> + + + + + + }> + Delete + + + +
    +
    + +
    + + + }/> + + + + ( + + + + )} + /> + + + + +
    +
    +
    +
    + ); +} diff --git a/ui/src/components/sites/branches/AddBranch.tsx b/ui/src/components/sites/branches/AddBranch.tsx new file mode 100644 index 0000000..48c0880 --- /dev/null +++ b/ui/src/components/sites/branches/AddBranch.tsx @@ -0,0 +1,98 @@ +import React, { useEffect, useState } from 'react'; +import { toast } from 'react-toastify'; +import { FormProvider, useForm } from 'react-hook-form'; +import { Button } from '../../../commons/components/Button'; +import { axios } from '../../../providers/axios'; +import { CardModal } from '../../../commons/components/modals/CardModal'; +import { Branch } from './branch'; +import { BranchNameInput } from './BranchNameInput'; +import { useMountedState } from '../../../commons/hooks/use-mounted-state'; + +function ModalContent({ + siteId, releaseId, onAdded, +}: { + siteId: string; + releaseId?: string; + onAdded: (branch: Branch) => void; +}) { + const methods = useForm({ + mode: 'onChange', + }); + const [loading, setLoading] = useMountedState(false); + const { handleSubmit, formState: { isDirty } } = methods; + + const onChange = formData => axios + .post(`/api/v1/sites/${siteId}/branches`, { + ...formData, + releaseId, + }) + .then(({ data }) => data) + .then(onAdded) + .catch(err => { + toast.error(`Could not create branch: ${err}`); + }); + + const onSubmit = data => { + setLoading(true); + onChange(data).finally(() => setLoading(false)); + }; + + const [inputRef, setInputRef] = useState(); + + useEffect(() => { + if (inputRef) { + inputRef.focus(); + } + }, [inputRef]); + + return ( + +
    + +
    + +
    + +
    + ); +} + +export function AddBranch({ + children, className, siteId, releaseId, onAdded, +}: { + children; + className?; + siteId: string; + releaseId?: string; + onAdded: (branch: Branch) => void; +}) { + const [isOpen, setIsOpen] = useMountedState(false); + const openModal = () => setIsOpen(true); + const closeModal = () => setIsOpen(false); + + const added = val => { + onAdded(val); + closeModal(); + }; + + return ( + <> +
    + {children} +
    + + + + + ); +} diff --git a/ui/src/components/sites/branches/BranchList.module.scss b/ui/src/components/sites/branches/BranchList.module.scss new file mode 100644 index 0000000..e791d25 --- /dev/null +++ b/ui/src/components/sites/branches/BranchList.module.scss @@ -0,0 +1,15 @@ +@import "../../../styles/variables"; + +.loader { + position: absolute; + right: 15px; + top: 50%; + transform: translateY(-50%); +} + +.add { + text-transform: uppercase; + font-weight: bold; + cursor: pointer; + text-align: center !important; +} diff --git a/ui/src/components/sites/branches/BranchList.tsx b/ui/src/components/sites/branches/BranchList.tsx new file mode 100644 index 0000000..1e98383 --- /dev/null +++ b/ui/src/components/sites/branches/BranchList.tsx @@ -0,0 +1,81 @@ +import React, { useEffect, useState } from 'react'; +import { useParams } from 'react-router-dom'; +import { toast } from 'react-toastify'; +import classNames from 'classnames'; +import { EmptyList } from '../../../commons/components/EmptyList'; +import { Loader } from '../../../commons/components/Loader'; +import { AlertError } from '../../../commons/components/AlertError'; +import { axios } from '../../../providers/axios'; +import styles from './BranchList.module.scss'; +import { BranchIcon } from '../../icons/BranchIcon'; +import { AddBranch } from './AddBranch'; +import { Branch } from './branch'; +import { useMountedState } from '../../../commons/hooks/use-mounted-state'; +import { BranchListItemView } from './BranchListItem'; + +function sortBranches(a: Branch, b: Branch): number { + return new Date(b.name).getTime() - new Date(a.name).getTime(); +} + +export function BranchList() { + const { siteId } = useParams(); + const [loading, setLoading] = useMountedState(true); + const [error, setError] = useState(); + const [items, setItems] = useState(); + + useEffect(() => { + setLoading(true); + setError(undefined); + axios.get(`/api/v1/sites/${siteId}/branches`) + .then(({ data }) => setItems(data)) + .catch(setError) + .catch(err => toast.error(`Could not list branches: ${err}`)) + .finally(() => setLoading(false)); + }, [siteId, setLoading]); + + const onAdded = branch => { + setItems([branch, ...items].sort(sortBranches)); + }; + + const emptyList = ( + } + title="No branches" + > +

    There are no branches yet

    + + + +
    + ); + + return loading ? ( + + ) : error ? ( + + ) : ( + <> + {items.length === 0 ? ( + emptyList + ) : ( +
    + + Add branch + + {items.map(branch => ( + + ))} +
    + )} + + ); +} diff --git a/ui/src/components/sites/branches/BranchListItem.tsx b/ui/src/components/sites/branches/BranchListItem.tsx new file mode 100644 index 0000000..2354b6e --- /dev/null +++ b/ui/src/components/sites/branches/BranchListItem.tsx @@ -0,0 +1,70 @@ +import React, { useEffect, useState } from 'react'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faExternalLinkAlt } from '@fortawesome/free-solid-svg-icons'; +import { uniqueId } from 'lodash'; +import classNames from 'classnames'; +import { useRouteMatch } from 'react-router-dom'; +import { Branch } from './branch'; +import { useSite } from '../SiteView'; +import { Tooltip, tooltipToggle } from '../../../commons/components/Tooltip'; +import { ExternalLink } from '../../../commons/components/ExternalLink'; +import { BranchRelease } from './BranchRelease'; +import { BranchIcon } from '../../icons/BranchIcon'; +import { routerHistory } from '../../../providers/history'; + +export function BranchListItemView({ branch, className }: { + branch: Branch; + className?; +}) { + const { url } = useRouteMatch(); + const { site } = useSite(); + const [uid1] = useState(uniqueId()); + const [isMainBranch, setIsMainBranch] = useState(false); + + useEffect(() => { + setIsMainBranch(site.mainBranch === branch._id); + }, [site, branch]); + + return ( +
    { + routerHistory.push(`${url}/${branch._id}`); + }} + > +
    + + {branch.name} +
    + +
    + {branch.release && ( + + )} + {isMainBranch && ( + <> +
    + main +
    + Site main branch + + )} + { + ev.stopPropagation(); + }} + > + + +
    +
    + ); +} diff --git a/ui/src/components/sites/branches/BranchNameInput.tsx b/ui/src/components/sites/branches/BranchNameInput.tsx new file mode 100644 index 0000000..13c4eaf --- /dev/null +++ b/ui/src/components/sites/branches/BranchNameInput.tsx @@ -0,0 +1,57 @@ +import { useFormContext } from 'react-hook-form'; +import React from 'react'; +import { toast } from 'react-toastify'; +import { maxLength, required } from '../../../commons/components/forms/form-constants'; +import { debounceTime } from '../../../utils/debounce-time'; +import { InputError } from '../../../commons/components/forms/InputError'; +import { axios } from '../../../providers/axios'; +import { useSite } from '../SiteView'; + +async function validateName(siteId: string, name: string): Promise { + if (!name) { + return undefined; + } + return axios + .post(`/api/v1/sites/${siteId}/branches.validate/name`, { + name, + }) + .then(({ data }) => data || undefined) + .catch(err => { + toast.error(`Could not validate branch name: ${err}`); + return undefined; + }); +} + +export function BranchNameInput({ setInputRef }: { + setInputRef?: (input: HTMLInputElement) => void; +}) { + const { register, errors } = useFormContext(); + const { site } = useSite(); + + const ref = input => { + if (setInputRef) { + setInputRef(input); + } + register({ + required, + maxLength: maxLength(), + validate: debounceTime(val => validateName(site._id, val), 300), + })(input); + }; + + return ( +
    + + + +
    + ); +} diff --git a/ui/src/components/sites/branches/BranchPassword.tsx b/ui/src/components/sites/branches/BranchPassword.tsx new file mode 100644 index 0000000..e93ad6c --- /dev/null +++ b/ui/src/components/sites/branches/BranchPassword.tsx @@ -0,0 +1,121 @@ +import React, { useEffect, useState } from 'react'; +import { useForm } from 'react-hook-form'; +import { toast } from 'react-toastify'; +import classNames from 'classnames'; +import { Toggle } from '../../../commons/components/forms/Toggle'; +import { CardModal } from '../../../commons/components/modals/CardModal'; +import { Branch } from './branch'; +import { Button } from '../../../commons/components/Button'; +import { InputError } from '../../../commons/components/forms/InputError'; +import { maxLength, required } from '../../../commons/components/forms/form-constants'; +import { axios } from '../../../providers/axios'; +import { randomString } from '../../../commons/utils/random-string'; + +interface FormData { + password: string; +} + +export function BranchPassword({ + siteId, branch, onChange, className, +}: { + siteId: string; + branch: Branch; + onChange: (branch: Branch) => void; + className?; +}) { + const [randomSecret, setRandomSecret] = useState(randomString(16)); + + const [isOpen, setIsOpen] = useState(false); + const openModal = () => setIsOpen(true); + const closeModal = () => setIsOpen(false); + + useEffect(() => { + setRandomSecret(randomString(16)); + }, [isOpen]); + + const { + register, errors, handleSubmit, + } = useForm({ + mode: 'onChange', + }); + + const [loading, setLoading] = useState(false); + + const setPassword = (formData: FormData) => { + setLoading(true); + axios + .put(`/api/v1/sites/${siteId}/branches/${branch._id}/password`, formData) + .then(({ data }) => { + onChange(data); + closeModal(); + }) + .catch(err => { + toast.error(`Could not set branch password: ${err}`); + }) + .finally(() => setLoading(false)); + }; + + const removePassword = () => { + setLoading(true); + axios + .delete(`/api/v1/sites/${siteId}/branches/${branch._id}/password`) + .then(({ data }) => onChange(data)) + .catch(err => { + toast.error(`Could not remove branch password: ${err}`); + }) + .finally(() => setLoading(false)); + }; + + return ( + <> + (branch.hasPassword ? removePassword() : openModal())} + loading={branch.hasPassword && loading} + className={classNames(className, 'w-100 font-weight-bold')} + > + Password protection + {' '} + {branch.hasPassword && ( + <> + {' '} + (user name is + {' '} + user + ) + + )} + + +
    +
    + + + +
    +
    + +
    +
    +
    + + ); +} diff --git a/ui/src/components/sites/branches/BranchRelease.tsx b/ui/src/components/sites/branches/BranchRelease.tsx new file mode 100644 index 0000000..98b9ede --- /dev/null +++ b/ui/src/components/sites/branches/BranchRelease.tsx @@ -0,0 +1,58 @@ +import React, { useEffect, useState } from 'react'; +import { uniqueId } from 'lodash'; +import { useMountedState } from '../../../commons/hooks/use-mounted-state'; +import { Release } from '../releases/release'; +import { axios } from '../../../providers/axios'; +import { Loader } from '../../../commons/components/Loader'; +import { AlertError } from '../../../commons/components/AlertError'; +import { Tooltip, tooltipToggle } from '../../../commons/components/Tooltip'; + +function useRelease(releaseId: string) { + const [loading, setLoading] = useMountedState(!!releaseId); + const [error, setError] = useState(); + const [release, setRelease] = useState(); + + useEffect(() => { + if (releaseId) { + setLoading(true); + setError(undefined); + axios + .get(`/api/v1/releases/${releaseId}`) + .then(({ data }) => data) + .then(setRelease) + .catch(setError) + .finally(() => setLoading(false)); + } + }, [releaseId, setLoading]); + + return { + release, + error, + loading, + }; +} + +export function BranchRelease({ releaseId }: { + releaseId: string; +}) { + const [uid] = useState(uniqueId()); + const { + loading, error, release, + } = useRelease(releaseId); + + return loading ? ( + + ) : error ? ( + + ) : ( + <> +
    + {release.name} +
    + Current release + + ); +} diff --git a/ui/src/components/sites/branches/BranchView.tsx b/ui/src/components/sites/branches/BranchView.tsx new file mode 100644 index 0000000..65afbeb --- /dev/null +++ b/ui/src/components/sites/branches/BranchView.tsx @@ -0,0 +1,208 @@ +import React, { createContext, useContext, useEffect, useState } from 'react'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faEllipsisV, faExternalLinkAlt, faPencilAlt, faTimes } from '@fortawesome/free-solid-svg-icons'; +import { uniqueId } from 'lodash'; +import { Redirect, Route, Switch, useParams, useRouteMatch } from 'react-router-dom'; +import { Branch } from './branch'; +import { DeleteBranch } from './DeleteBranch'; +import { RenameBranch } from './RenameBranch'; +import { Dropdown, dropdownToggle } from '../../../commons/components/dropdown/Dropdown'; +import { DropdownLink } from '../../../commons/components/dropdown/DropdownLink'; +import DropdownSeparator from '../../../commons/components/dropdown/DropdownSeparator'; +import { ButtonIcon } from '../../../commons/components/ButtonIcon'; +import { useSite } from '../SiteView'; +import { Tooltip, tooltipToggle } from '../../../commons/components/Tooltip'; +import { useMountedState } from '../../../commons/hooks/use-mounted-state'; +import { axios } from '../../../providers/axios'; +import { Loader } from '../../../commons/components/Loader'; +import { AlertError } from '../../../commons/components/AlertError'; +import { ExternalLink } from '../../../commons/components/ExternalLink'; +import { BranchRedirects } from './redirects/BranchRedirects'; +import { NotFound } from '../../../commons/components/NotFound'; +import { Releases } from '../releases/Releases'; +import { SubHeader } from '../../SubHeader'; +import { NavPills } from '../../../commons/components/NavPills'; +import { SettingsIcon } from '../../icons/SettingsIcon'; +import { ReleaseIcon } from '../../icons/ReleaseIcon'; +import { RedirectIcon } from '../../icons/RedirectIcon'; +import { routerHistory } from '../../../providers/history'; +import { routeUp } from '../../../commons/utils/route-up'; +import { BranchRelease } from './BranchRelease'; +import { BranchSettings } from './settings/BranchSettings'; +import { BranchIcon } from '../../icons/BranchIcon'; +import { HeaderIcon } from '../../icons/HeaderIcon'; +import { BranchHeaders } from './headers/BranchHeaders'; +import { Forms } from './forms/Forms'; + +function useSiteBranch(siteId: string, branchId: string) { + const [loading, setLoading] = useMountedState(true); + const [error, setError] = useState(); + const [branch, setBranch] = useState(); + + useEffect(() => { + setLoading(true); + setError(undefined); + axios + .get(`/api/v1/sites/${siteId}/branches/${branchId}`) + .then(({ data }) => data) + .then(setBranch) + .catch(setError) + .finally(() => setLoading(false)); + }, [setLoading, siteId, branchId]); + + return { + branch, + setBranch, + error, + loading, + }; +} + +interface BranchContext { + branch: Branch; + setBranch: (chan: Branch) => void; +} + +const Context = createContext(undefined); +export const useBranch = () => useContext(Context); + +export function BranchView() { + const { siteId, branchId } = useParams(); + const { path, url } = useRouteMatch(); + const { site } = useSite(); + const [uid] = useState(uniqueId()); + const [uid1] = useState(uniqueId()); + const [current, setCurrent] = useState(false); + const { + branch, setBranch, loading, error, + } = useSiteBranch(siteId, branchId); + + useEffect(() => { + if (site && branch) { + setCurrent(site.mainBranch === branch._id); + } + }, [site, branch]); + + const onDelete = () => { + routerHistory.push(routeUp(url)); + }; + + return loading ? ( + + ) : error ? ( + + ) : ( + + +
    +
    + + {branch.name} +
    + {current && ( + <> +
    + main +
    + Site main branch + + )} + {branch.release && ( + + )} + + + +
    +
    + + + ️ Releases + + ), + }, + { + to: `${url}/redirects`, + label: ( + <> + + {' '} + Redirects + + ), + }, + { + to: `${url}/headers`, + label: ( + <> + + {' '} + Headers + + ), + }, + { + to: `${url}/forms`, + label: ( + <> + + {' '} + Forms + + ), + }, + { + to: `${url}/settings`, + label: ( + <> + + {' '} + Settings + + ), + }, + ]} + /> + + + + + + }> + Rename + + + + + }> + Delete + + + +
    +
    + +
    + + }/> + + + + + + + +
    +
    + ); +} diff --git a/ui/src/components/sites/branches/Branches.tsx b/ui/src/components/sites/branches/Branches.tsx new file mode 100644 index 0000000..3fb5a9c --- /dev/null +++ b/ui/src/components/sites/branches/Branches.tsx @@ -0,0 +1,16 @@ +import React from 'react'; +import { + Route, Switch, useRouteMatch, +} from 'react-router-dom'; +import { BranchView } from './BranchView'; +import { BranchList } from './BranchList'; + +export function Branches() { + const { path } = useRouteMatch(); + return ( + + + + + ); +} diff --git a/ui/src/components/sites/branches/DeleteBranch.tsx b/ui/src/components/sites/branches/DeleteBranch.tsx new file mode 100644 index 0000000..d9b71dc --- /dev/null +++ b/ui/src/components/sites/branches/DeleteBranch.tsx @@ -0,0 +1,64 @@ +import React from 'react'; +import { toast } from 'react-toastify'; +import { Button } from '../../../commons/components/Button'; +import { axios } from '../../../providers/axios'; +import { CardModal } from '../../../commons/components/modals/CardModal'; +import { useMountedState } from '../../../commons/hooks/use-mounted-state'; + +export function DeleteBranch({ + siteId, branchId, className, children, onDelete, +}: { + siteId: string; + branchId: string; + children: any; + className?: string; + onDelete: () => void; +}) { + const [isOpen, setIsOpen] = useMountedState(false); + const [loading, setLoading] = useMountedState(false); + + const deleteToken = () => { + setLoading(true); + return axios + .delete(`/api/v1/sites/${siteId}/branches/${branchId}`) + .then(() => { + setIsOpen(false); + onDelete(); + }) + .catch(err => { + toast.error(`Could not delete branch: ${err}`); + }) + .finally(() => setLoading(false)); + }; + + return ( + <> + setIsOpen(false)} + > +

    Are you sure you want to delete this branch ?

    +
    + + +
    +
    +
    setIsOpen(true)} className={className}> + {children} +
    + + ); +} diff --git a/ui/src/components/sites/branches/RenameBranch.tsx b/ui/src/components/sites/branches/RenameBranch.tsx new file mode 100644 index 0000000..3cd5f59 --- /dev/null +++ b/ui/src/components/sites/branches/RenameBranch.tsx @@ -0,0 +1,93 @@ +import React, { useEffect, useState } from 'react'; +import { toast } from 'react-toastify'; +import { FormProvider, useForm } from 'react-hook-form'; +import { Button } from '../../../commons/components/Button'; +import { axios } from '../../../providers/axios'; +import { CardModal } from '../../../commons/components/modals/CardModal'; +import { Branch } from './branch'; +import { BranchNameInput } from './BranchNameInput'; +import { useMountedState } from '../../../commons/hooks/use-mounted-state'; + +function ModalContent({ + siteId, branchId, onRenamed, +}: { + siteId: string; + branchId: string; + onRenamed: (branch: Branch) => void; +}) { + const methods = useForm({ + mode: 'onChange', + }); + const [loading, setLoading] = useMountedState(false); + const { handleSubmit, formState: { isDirty } } = methods; + + const onSubmit = formData => { + setLoading(true); + axios + .put(`/api/v1/sites/${siteId}/branches/${branchId}/name`, formData) + .then(({ data }) => onRenamed(data)) + .catch(err => { + toast.error(`Could not rename branch: ${err}`); + }) + .finally(() => setLoading(false)); + }; + + const [inputRef, setInputRef] = useState(); + + useEffect(() => { + if (inputRef) { + inputRef.focus(); + } + }, [inputRef]); + + return ( + +
    + +
    + +
    + +
    + ); +} + +export function RenameBranch({ + children, className, siteId, branchId, onRenamed, +}: { + children; + className?; + siteId: string; + branchId: string; + onRenamed: (branch: Branch) => void; +}) { + const [isOpen, setIsOpen] = useMountedState(false); + const openModal = () => setIsOpen(true); + const closeModal = () => setIsOpen(false); + + const renamed = val => { + onRenamed(val); + closeModal(); + }; + + return ( + <> +
    + {children} +
    + + + + + ); +} diff --git a/ui/src/components/sites/branches/branch-redirect.ts b/ui/src/components/sites/branches/branch-redirect.ts new file mode 100644 index 0000000..0151c6e --- /dev/null +++ b/ui/src/components/sites/branches/branch-redirect.ts @@ -0,0 +1,22 @@ +export enum RedirectType { + file = 'file', + reverse_proxy = 'reverse_proxy', +} + +export interface FileRedirectConfig { + content: string; +} + +export interface ReverseProxyRedirectConfig { + url: string; + stripPathPrefix: string; +} + +export interface BranchRedirect { + _id: string; + type: RedirectType; + // https://caddyserver.com/docs/json/apps/http/servers/routes/match/path/ + path: string; + config: T; + url: string; +} diff --git a/ui/src/components/sites/branches/branch.ts b/ui/src/components/sites/branches/branch.ts new file mode 100644 index 0000000..a377bff --- /dev/null +++ b/ui/src/components/sites/branches/branch.ts @@ -0,0 +1,10 @@ +import { Header } from './header'; + +export interface Branch { + _id: string; + name: string; + release?: string; + hasPassword?: string; + url: string; + headers: Header[]; +} diff --git a/ui/src/components/sites/branches/forms/CustomFields.tsx b/ui/src/components/sites/branches/forms/CustomFields.tsx new file mode 100644 index 0000000..ccf286a --- /dev/null +++ b/ui/src/components/sites/branches/forms/CustomFields.tsx @@ -0,0 +1,50 @@ +import React from 'react'; +import { useFormContext } from 'react-hook-form'; +import { EmailForm, Form, FormType } from '../../releases/release'; +import { EMAIL_PATTERN, maxLength, required } from '../../../../commons/components/forms/form-constants'; +import { InputError } from '../../../../commons/components/forms/InputError'; + +function EmailFormFields({ value, index }: { + value: EmailForm; + index: number; +}) { + const { register, errors } = useFormContext(); + const input = `forms[${index}]`; + const input_recipient = `${input}.recipient`; + + return ( + <> +
    + + + +
    + + ); +} + +export function CustomFields({ type, value, index }: { + type: FormType; + value: Form; + index: number; +}) { + switch (type) { + case FormType.email: + return ; + case FormType.db: + default: + return <>; + } +} diff --git a/ui/src/components/sites/branches/forms/FormForm.tsx b/ui/src/components/sites/branches/forms/FormForm.tsx new file mode 100644 index 0000000..5004764 --- /dev/null +++ b/ui/src/components/sites/branches/forms/FormForm.tsx @@ -0,0 +1,78 @@ +import React from 'react'; +import { useFormContext } from 'react-hook-form'; +import classNames from 'classnames'; +import { Form, FormType } from '../../releases/release'; +import { InputError } from '../../../../commons/components/forms/InputError'; +import { maxLength, required } from '../../../../commons/components/forms/form-constants'; +import { CustomFields } from './CustomFields'; + +const types = Object.values(FormType); + +export function FormForm({ + index, value, remove, className, +}: { + index: number; + value: Form; + remove: () => void; + className?; +}) { + const { register, errors, getValues, watch } = useFormContext(); + const input = `forms[${index}]`; + const input_type = `${input}.type`; + const input_name = `${input}.name`; + + const type = watch(input_type); + + return ( +
    +
    +
    + + + +
    +
    + + { + const count = getValues().forms?.filter(val => val.name.toLowerCase() === currentValue.toLowerCase())?.length || 0; + return count <= 1 ? undefined : 'Form name must be unique'; + }, + pattern: /[a-zA-Z_]+/, + })} + className="form-control" + placeholder="form2" + defaultValue={value?.name} + /> + +
    + +
    + +
    +
    +
    + ); +} diff --git a/ui/src/components/sites/branches/forms/FormList.tsx b/ui/src/components/sites/branches/forms/FormList.tsx new file mode 100644 index 0000000..b88f73e --- /dev/null +++ b/ui/src/components/sites/branches/forms/FormList.tsx @@ -0,0 +1,76 @@ +import React, { useEffect } from 'react'; +import { FormProvider, useFieldArray, useForm } from 'react-hook-form'; +import { Form } from '../../releases/release'; +import { Button } from '../../../../commons/components/Button'; +import { FormForm } from './FormForm'; + +interface FormData { + forms: Form[]; +} + +export function FormList({ forms, onSubmit, submitting }: { + forms: Form[]; + onSubmit: (forms: Form[]) => void; + submitting: boolean; +}) { + const methods = useForm({ mode: 'onChange' }); + const { control, handleSubmit, formState: { isDirty }, reset } = methods; + const formForms = useFieldArray
    ({ control, name: 'forms' }); + + const submit = (formData: FormData) => { + onSubmit(formData.forms); + }; + + useEffect(() => { + if (forms && reset) { + reset({ + forms, + }); + } + }, [forms, reset]); + + return ( + + + {formForms.fields.map((form, index) => ( + formForms.remove(index)} + className="mb-4" + /> + ))} + + +
    + {isDirty && ( + + )} + +
    + +
    + ); +} diff --git a/ui/src/components/sites/branches/forms/Forms.tsx b/ui/src/components/sites/branches/forms/Forms.tsx new file mode 100644 index 0000000..5b092d5 --- /dev/null +++ b/ui/src/components/sites/branches/forms/Forms.tsx @@ -0,0 +1,92 @@ +import React, { useEffect, useState } from 'react'; +import { toast } from 'react-toastify'; +import { useParams } from 'react-router-dom'; +import { useMountedState } from '../../../../commons/hooks/use-mounted-state'; +import { axios } from '../../../../providers/axios'; +import { Loader } from '../../../../commons/components/Loader'; +import { AlertError } from '../../../../commons/components/AlertError'; +import { Branch } from '../branch'; +import { FormList } from './FormList'; +import { Form, Release } from '../../releases/release'; + +function useBranchForms( + siteId: string, + branchId: string, +) { + const [loading, setLoading] = useMountedState(true); + const [error, setError] = useState(); + const [forms, setForms] = useState(); + + useEffect(() => { + setLoading(true); + setError(undefined); + axios + .get(`/api/v1/sites/${siteId}/branches/${branchId}`) + .then(({ data }) => ( + axios.get(`/api/v1/releases/${data.release}`) + )) + .then(({ data }) => data.forms) + .then(setForms) + .catch(setError) + .finally(() => setLoading(false)); + }, [setLoading, siteId, branchId]); + + return { + forms, + setForms, + error, + loading, + }; +} + +function useSetBranchForms( + siteId: string, + branchId: string, + setForms: (forms: Form[]) => void, +) { + const [loading, setLoading] = useMountedState(false); + + const updateForms = (forms: Form[]) => { + setLoading(true); + axios + .get(`/api/v1/sites/${siteId}/branches/${branchId}`) + .then(({ data }) => ( + axios.put(`/api/v1/releases/${data.release}/forms`, { + forms: forms || [], + }) + )) + .then(({ data }) => { + setForms(data); + toast.success('Saved forms'); + }) + .catch(err => { + toast.error(`Could not save forms: ${err}`); + }) + .finally(() => { + setLoading(false); + }); + }; + + return { + updateForms, + loading, + }; +} + +export function Forms() { + const { siteId, branchId } = useParams(); + const { forms, setForms, loading, error } = useBranchForms(siteId, branchId); + const { loading: updating, updateForms } = useSetBranchForms(siteId, branchId, setForms); + + return loading ? ( + + ) : error ? ( + + ) : ( + + ); +} diff --git a/ui/src/components/sites/branches/get-branches.ts b/ui/src/components/sites/branches/get-branches.ts new file mode 100644 index 0000000..55537c0 --- /dev/null +++ b/ui/src/components/sites/branches/get-branches.ts @@ -0,0 +1,12 @@ +import { axios } from '../../../providers/axios'; +import { Env } from '../../../providers/EnvProvider'; +import { Branch } from './branch'; + +export function getBranches( + env: Env, + siteId: string, +): Promise { + return axios + .get(`/api/v1/sites/${siteId}/branches`) + .then(res => res.data); +} diff --git a/ui/src/components/sites/branches/header.ts b/ui/src/components/sites/branches/header.ts new file mode 100644 index 0000000..9597c00 --- /dev/null +++ b/ui/src/components/sites/branches/header.ts @@ -0,0 +1,4 @@ +export interface Header { + name: string; + value: string; +} diff --git a/ui/src/components/sites/branches/headers/BranchHeaders.tsx b/ui/src/components/sites/branches/headers/BranchHeaders.tsx new file mode 100644 index 0000000..e03ba6e --- /dev/null +++ b/ui/src/components/sites/branches/headers/BranchHeaders.tsx @@ -0,0 +1,134 @@ +import React, { useEffect, useState } from 'react'; +import { toast } from 'react-toastify'; +import { Link, useParams } from 'react-router-dom'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faPen } from '@fortawesome/free-solid-svg-icons'; +import { useMountedState } from '../../../../commons/hooks/use-mounted-state'; +import { axios } from '../../../../providers/axios'; +import { Loader } from '../../../../commons/components/Loader'; +import { AlertError } from '../../../../commons/components/AlertError'; +import { Branch } from '../branch'; +import { Header } from '../header'; +import { HeaderList } from '../../headers/HeaderList'; +import { useSiteHeaders } from '../../headers/use-site-headers'; + +function useBranchHeaders( + siteId: string, + branchId: string, +) { + const [loading, setLoading] = useMountedState(true); + const [error, setError] = useState(); + const [headers, setHeaders] = useState(); + + useEffect(() => { + setLoading(true); + setError(undefined); + axios + .get(`/api/v1/sites/${siteId}/branches/${branchId}`) + .then(({ data }) => data.headers) + .then(setHeaders) + .catch(setError) + .finally(() => setLoading(false)); + }, [setLoading, siteId, branchId]); + + return { + headers, + setHeaders, + error, + loading, + }; +} + +function useSetBranchHeaders( + siteId: string, + branchId: string, + setHeaders: (headers: Header[]) => void, +) { + const [loading, setLoading] = useMountedState(false); + + const updateHeaders = (headers: Header[]) => { + setLoading(true); + axios + .put(`/api/v1/sites/${siteId}/branches/${branchId}/headers`, { + headers: headers || [], + }) + .then(({ data }) => { + setHeaders(data.headers); + toast.success('Saved branch headers'); + }) + .catch(err => { + toast.error(`Could not save headers: ${err}`); + }) + .finally(() => { + setLoading(false); + }); + }; + + return { + updateHeaders, + loading, + }; +} + +function SiteHeaders({ siteId }: { + siteId: string; +}) { + const { headers, loading, error } = useSiteHeaders(siteId); + + return loading ? ( + + ) : error ? ( + + ) : ( + <> +
    +

    Headers set at site level

    + + Edit + {' '} + + +
    + + + + + + + + + {headers.map(({ name, value }) => ( + + + + + ))} + +
    NameValue
    {name}{value}
    + + ); +} + +export function BranchHeaders() { + const { siteId, branchId } = useParams(); + const { headers, setHeaders, loading, error } = useBranchHeaders(siteId, branchId); + const { loading: updating, updateHeaders } = useSetBranchHeaders(siteId, branchId, setHeaders); + + return loading ? ( + + ) : error ? ( + + ) : ( + <> +

    Branch headers

    + + + + ); +} diff --git a/ui/src/components/sites/branches/redirects/BranchRedirectForm.tsx b/ui/src/components/sites/branches/redirects/BranchRedirectForm.tsx new file mode 100644 index 0000000..c0653dc --- /dev/null +++ b/ui/src/components/sites/branches/redirects/BranchRedirectForm.tsx @@ -0,0 +1,127 @@ +import React from 'react'; +import { useFormContext } from 'react-hook-form'; +import { faExternalLinkAlt, faTimes } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import classNames from 'classnames'; +import { maxLength, required } from '../../../../commons/components/forms/form-constants'; +import { InputError } from '../../../../commons/components/forms/InputError'; +import { ButtonIcon } from '../../../../commons/components/ButtonIcon'; +import { BranchRedirectsFormData } from './branch-redirects-form-data'; +import { ExternalLink } from '../../../../commons/components/ExternalLink'; +import { BranchRedirect, RedirectType } from '../branch-redirect'; +import { ReverseProxy } from './configs/ReverseProxy'; +import { FileConfig } from './configs/FileConfig'; +import { enumToArray } from '../../../../commons/utils/enum-to-array'; + +function Config({ + type, config, path, +}: { + type: RedirectType; + config; + path: string; +}) { + switch (type) { + case RedirectType.file: + return ; + case RedirectType.reverse_proxy: + return ; + default: + return <>; + } +} + +const types = enumToArray(RedirectType); + +export function BranchRedirectForm({ + index, redirect, remove, className, +}: { + index: number; + redirect: BranchRedirect; + remove: () => void; + className?; +}) { + const { + register, errors, getValues, watch, + } = useFormContext(); + const input = `redirects[${index}]`; + const input_type = `${input}.type`; + const input_name = `${input}.path`; + + const type = watch(input_type); + + return ( +
    +
    +
    + {redirect.url || 'New redirect'} + {redirect.url && ( + + + + )} +
    + + + +
    +
    +
    + + + +
    +
    + + { + const count = getValues().redirects?.filter(val => val.path === value)?.length || 0; + return count <= 1 ? undefined : 'Branch name must be unique'; + }, + })} + className="form-control" + placeholder="/env.json" + defaultValue={redirect?.path} + /> + + You can use wildcards as described + {' '} + here + + +
    + + +
    +
    + ); +} diff --git a/ui/src/components/sites/branches/redirects/BranchRedirects.tsx b/ui/src/components/sites/branches/redirects/BranchRedirects.tsx new file mode 100644 index 0000000..5c4a313 --- /dev/null +++ b/ui/src/components/sites/branches/redirects/BranchRedirects.tsx @@ -0,0 +1,148 @@ +import React, { useEffect, useState } from 'react'; +import { FormProvider, useFieldArray, useForm } from 'react-hook-form'; +import { toast } from 'react-toastify'; +import { useParams } from 'react-router-dom'; +import { useMountedState } from '../../../../commons/hooks/use-mounted-state'; +import { axios } from '../../../../providers/axios'; +import { Loader } from '../../../../commons/components/Loader'; +import { AlertError } from '../../../../commons/components/AlertError'; +import { Button } from '../../../../commons/components/Button'; +import { BranchRedirectsFormData } from './branch-redirects-form-data'; +import { BranchRedirectForm } from './BranchRedirectForm'; +import { BranchRedirect, RedirectType } from '../branch-redirect'; + +function useBranchFiles( + siteId: string, + branchId: string, +) { + const [loading, setLoading] = useMountedState(false); + const [error, setError] = useState(); + const [redirects, setFiles] = useState(); + + useEffect(() => { + setLoading(true); + setError(undefined); + axios + .get(`/api/v1/sites/${siteId}/branches/${branchId}/redirects`) + .then(({ data }) => data) + .then(setFiles) + .catch(setError) + .finally(() => setLoading(false)); + }, [setLoading, siteId, branchId]); + + return { + redirects, + setFiles, + error, + loading, + }; +} + +function useSetFiles( + siteId: string, + branchId: string, + setFiles: (redirects: BranchRedirect[]) => void, +) { + const [loading, setLoading] = useMountedState(false); + + const updateFiles = (formData: BranchRedirectsFormData) => { + setLoading(true); + axios + .put(`/api/v1/sites/${siteId}/branches/${branchId}/redirects`, { + redirects: formData.redirects || [], + }) + .then(({ data }) => { + setFiles(data); + toast.success('Saved branch redirects'); + }) + .catch(err => { + toast.error(`Could not save branch redirects: ${err}`); + }) + .finally(() => setLoading(false)); + }; + + return { + updateFiles, + loading, + }; +} + +export function BranchRedirects() { + const { siteId, branchId } = useParams(); + const methods = useForm({ + mode: 'onChange', + }); + const { + control, handleSubmit, formState: { isDirty }, reset, + } = methods; + const formFiles = useFieldArray({ + control, + name: 'redirects', + }); + + const { + redirects, setFiles, loading, error, + } = useBranchFiles(siteId, branchId); + const { loading: updating, updateFiles } = useSetFiles(siteId, branchId, setFiles); + + useEffect(() => { + if (redirects && reset) { + reset({ + redirects, + }); + } + }, [redirects, reset]); + + return loading ? ( + + ) : error ? ( + + ) : ( + +
    + + {formFiles.fields.map((branchFile, index) => ( + formFiles.remove(index)} + className="mb-4" + /> + ))} + + +
    + {isDirty && ( + + )} + +
    + +
    + ); +} diff --git a/ui/src/components/sites/branches/redirects/branch-redirects-form-data.tsx b/ui/src/components/sites/branches/redirects/branch-redirects-form-data.tsx new file mode 100644 index 0000000..6e72938 --- /dev/null +++ b/ui/src/components/sites/branches/redirects/branch-redirects-form-data.tsx @@ -0,0 +1,5 @@ +import { BranchRedirect } from '../branch-redirect'; + +export interface BranchRedirectsFormData { + redirects: BranchRedirect[]; +} diff --git a/ui/src/components/sites/branches/redirects/configs/FileConfig.tsx b/ui/src/components/sites/branches/redirects/configs/FileConfig.tsx new file mode 100644 index 0000000..42db624 --- /dev/null +++ b/ui/src/components/sites/branches/redirects/configs/FileConfig.tsx @@ -0,0 +1,27 @@ +import { useFormContext } from 'react-hook-form'; +import React from 'react'; +import { InputError } from '../../../../../commons/components/forms/InputError'; +import { FileRedirectConfig } from '../../branch-redirect'; + +export function FileConfig({ config, path }: { + config: FileRedirectConfig; + path: string; +}) { + const { register, errors } = useFormContext(); + const input_content = `${path}.content`; + return ( + <> +
    + +