From c5b4acb09fefa8bea738d2b2accd1a41ffcbca7e Mon Sep 17 00:00:00 2001 From: Jay Varner Date: Fri, 16 Jan 2026 18:01:46 -0500 Subject: [PATCH 01/29] Initial work on React version. --- .circleci/config.yml | 27 - .dockerignore | 4 + .editorconfig | 19 - .eslintignore | 34 +- .eslintrc.cjs | 84 + .eslintrc.js | 51 - .gitignore | 48 +- .nvmrc | 1 - .prettierrc | 5 - .sass-lint.yml | 169 - .template-lintrc.js | 5 - .watchmanconfig | 1 - Dockerfile | 22 + README.md | 87 + app/adapters/application.js | 28 - app/adapters/public.js | 6 - app/adapters/theme.js.bk | 3 - app/adapters/tour-set.js | 10 - app/adapters/user.js | 14 - app/app.css | 20 + app/app.js | 12 - app/choices.ts | 152 + app/components/Account.tsx | 33 + app/components/ClientOnly.tsx | 19 + app/components/List.tsx | 44 + app/components/MediaGrid.tsx | 102 + app/components/Navbar.tsx | 44 + app/components/ThemeSelector.tsx | 71 + app/components/g-map.hbs | 20 - app/components/g-map.js | 4 - app/components/g-map/image-overlay.hbs | 10 - app/components/g-map/image-overlay.js | 74 - app/components/inputs/FileUpload.tsx | 7 + app/components/inputs/InputWrapper.tsx | 13 + app/components/inputs/SelectInput.tsx | 76 + app/components/inputs/TextInput.tsx | 133 + app/components/inputs/Toggle.tsx | 58 + app/components/inputs/ToolTip.tsx | 30 + app/components/language-select/component.js | 81 - app/components/language-select/template.hbs | 14 - app/components/map-icons/component.js | 29 - app/components/map-icons/template.hbs | 74 - .../otb-crud/common-form/component.js | 6 - .../otb-crud/common-form/template.hbs | 48 - .../otb-crud/edit-media-modal/component.js | 81 - .../otb-crud/edit-media-modal/template.hbs | 55 - .../otb-crud/extra-stops/component.js | 38 - .../otb-crud/extra-stops/template.hbs | 57 - .../otb-crud/form-section/component.js | 18 - .../otb-crud/form-section/template.hbs | 5 - .../otb-crud/map-overlay/component.js | 90 - .../otb-crud/map-overlay/template.hbs | 229 - app/components/otb-crud/map-type/component.js | 16 - app/components/otb-crud/map-type/template.hbs | 10 - app/components/otb-crud/map/component.js | 182 - app/components/otb-crud/map/template.hbs | 143 - app/components/otb-crud/media/component.js | 32 - .../otb-crud/media/extra-media/component.js | 74 - .../otb-crud/media/extra-media/template.hbs | 94 - app/components/otb-crud/media/template.hbs | 66 - .../otb-crud/medium-upload/component.js | 12 - .../otb-crud/medium-upload/template.hbs | 37 - app/components/otb-crud/new-button.hbs | 3 - .../otb-crud/pell-editor/component.js | 164 - .../otb-crud/pell-editor/template.hbs | 13 - .../otb-crud/theme-picker/component.js | 14 - .../otb-crud/theme-picker/template.hbs | 15 - .../otb-crud/travel-modes/component.js | 6 - .../otb-crud/travel-modes/template.hbs | 33 - app/components/otb-crud/video/component.js | 80 - app/components/otb-crud/video/template.hbs | 39 - app/components/otb-tooltip.js | 19 - app/components/otb-tooltip/template.hbs | 13 - app/components/preview-button/component.js | 51 - app/components/preview-button/template.hbs | 2 - app/components/uk-accordion/component.js | 24 - app/components/uk-accordion/template.hbs | 3 - app/components/video-embed/template.hbs | 9 - app/context.ts | 30 + app/contexts.ts | 11 + app/controllers/.gitkeep | 0 app/controllers/admin.js | 15 - app/controllers/admin/index.js | 53 - app/controllers/admin/tours/edit.js | 309 - app/controllers/admin/tours/index.js | 70 - app/controllers/admin/users/index.js | 13 - app/controllers/admin/users/user.js | 21 - app/controllers/application.js | 13 - app/controllers/index.js | 16 - app/controllers/login.js | 4 - app/formats.js | 30 - app/helpers/.gitkeep | 0 app/helpers/includes.js | 7 - app/helpers/media-thumbnail.js | 23 - app/helpers/notin.js | 7 - app/helpers/parking-icon.js | 14 - app/helpers/round-float.js | 7 - app/helpers/save.js.bk | 15 - app/hooks.ts | 1 + app/index.html | 24 - app/initializers/currentuser.js | 8 - app/initializers/responsive.js | 7 - app/initializers/tenant.js | 8 - app/initializers/theme.js | 18 - app/lib/base-modal.js | 17 - app/locales/de/config.js | 16 - app/locales/de/translations.js | 32 - app/locales/en/config.js | 16 - app/locales/en/translations.js | 24 - app/locations/trailing-history.js | 12 - app/models/.gitkeep | 0 app/models/flat-page.js | 23 - app/models/login.js | 12 - app/models/map-icon.js | 7 - app/models/map-overlay.js | 35 - app/models/medium.js | 46 - app/models/mode.js | 12 - app/models/stop-medium.js | 12 - app/models/stop.js | 100 - app/models/theme.js | 6 - app/models/token.js | 5 - app/models/tour-author.js | 7 - app/models/tour-collection.js | 6 - app/models/tour-flat-page.js | 12 - app/models/tour-medium.js | 7 - app/models/tour-mode.js | 9 - app/models/tour-set.js | 12 - app/models/tour-stop.js | 36 - app/models/tour.js | 149 - app/models/user.js | 29 - app/root.tsx | 119 + app/router.js | 59 - app/routes.ts | 14 + app/routes/admin.js | 12 - app/routes/admin/index.js | 30 - app/routes/admin/tours.js | 3 - app/routes/admin/tours/edit.js | 40 - app/routes/admin/tours/index.js | 19 - app/routes/admin/tours/new.js | 22 - app/routes/admin/users.js | 14 - app/routes/admin/users/index.js | 14 - app/routes/admin/users/user.js | 14 - app/routes/application.js | 58 - app/routes/home.tsx | 31 + app/routes/index.js | 52 - app/routes/login.js | 11 - app/routes/signin.tsx | 9 + app/routes/tour.tsx | 179 + app/routes/tour_set.tsx | 30 + app/serializers/application.js | 14 - app/serializers/medium.js | 20 - app/services/crud-actions.js | 342 - app/services/current-user.js | 36 - app/services/geocoder.js | 22 - app/services/task-message.js | 27 - app/services/tenant.js | 73 - app/services/theme.js | 47 - app/session-stores/application.js | 3 - app/styles/admin.scss | 144 - app/styles/animate-list.scss | 43 - app/styles/app.css | 0 app/styles/app.scss | 429 - app/styles/desktop.scss | 130 - app/styles/login-form.scss | 20 - app/styles/maps.scss | 56 - app/styles/mixins.scss | 67 - app/styles/themes.scss | 77 - app/styles/variables.scss | 28 - app/templates/admin.hbs | 32 - app/templates/admin/error.hbs | 1 - app/templates/admin/index.hbs | 88 - app/templates/admin/loading.hbs | 1 - app/templates/admin/tour.hbs | 0 app/templates/admin/tours.hbs | 22 - app/templates/admin/tours/edit.hbs | 343 - app/templates/admin/tours/index.hbs | 128 - app/templates/admin/tours/new.hbs | 26 - app/templates/admin/users.hbs | 2 - app/templates/admin/users/index.hbs | 32 - app/templates/admin/users/loading.hbs | 1 - app/templates/admin/users/user.hbs | 33 - app/templates/application.hbs | 8 - app/templates/loading.hbs | 1 - app/templates/login.hbs | 3 - app/types.ts | 188 + app/useIsHydrated.ts | 13 + app/utils/fetchers.ts | 85 + config/deploy.js | 49 - config/ember-intl.js | 125 - config/environment.js | 168 - config/optional-features.json | 6 - config/targets.js | 23 - ember-cli-build.js | 37 - jsconfig.json | 7 - lib/ember-g-map/index.js | 66 - lib/ember-g-map/package.json | 40 - mirage/config.js | 27 - mirage/factories/medium.js | 4 - mirage/factories/theme.js | 5 - mirage/factories/tour-stop.js | 6 - mirage/factories/tour.js | 20 - mirage/fixtures/media.js | 23 - mirage/fixtures/modes.js | 6 - mirage/fixtures/themes.js | 5 - mirage/fixtures/tours.js | 13 - mirage/scenarios/default.js | 7 - mirage/serializers/application.js | 8 - package-lock.json | 69052 ++-------------- package.json | 142 +- public/crossdomain.xml | 15 - public/favicon.ico | Bin 0 -> 15086 bytes public/{assets/images => }/otb-themes/atl.png | Bin .../images => }/otb-themes/austrian.png | Bin .../{assets/images => }/otb-themes/blue.png | Bin .../images => }/otb-themes/bright-blue.png | Bin .../images => }/otb-themes/dark-blue.png | Bin .../images => }/otb-themes/dark-green.png | Bin .../images => }/otb-themes/dark-mode.png | Bin .../images => }/otb-themes/default.png | Bin .../{assets/images => }/otb-themes/emory.png | Bin .../{assets/images => }/otb-themes/gatech.png | Bin .../{assets/images => }/otb-themes/green.png | Bin public/{assets/images => }/otb-themes/gsu.png | Bin public/{assets/images => }/otb-themes/ksu.png | Bin .../{assets/images => }/otb-themes/orange.png | Bin .../{assets/images => }/otb-themes/purple.png | Bin public/{assets/images => }/otb-themes/red.png | Bin public/{assets/images => }/otb-themes/uga.png | Bin public/otblogo.png | Bin 0 -> 4140 bytes public/robots.txt | 3 - react-router.config.ts | 7 + server.js | 126 + testem.js | 27 - tests/.eslintrc.js | 5 - tests/helpers/start-mirage.js | 5 - tests/index.html | 40 - tests/integration/.gitkeep | 0 tests/integration/components/g-map-test.nope | 26 - .../components/g-map/image-overlay-test.nope | 26 - .../language-select/component-test.fix | 26 - .../components/map-icons/component-test.fix | 26 - .../otb-crud/common-form/component-test.js | 46 - .../otb-crud/map-overlay/component-test.js | 22 - .../otb-crud/map-type/component-test.js | 32 - .../otb-crud/media/component-test.js | 50 - .../otb-crud/theme-picker/component-test.js | 39 - .../otb-crud/travel-modes/component-test.js | 85 - .../otb-crud/video/component-test.js | 75 - .../components/otb-tooltip/component-test.js | 26 - .../components/video-embed/component-test.js | 13 - tests/test-helper.js | 12 - tests/unit/models/map-icon-test.js | 13 - tests/unit/models/map-overlay-test.js | 13 - translations/en-us.yaml | 77 - tsconfig.json | 26 + typings/ember/ember.d.ts | 2552 - vendor/.gitkeep | 0 vite.config.ts | 36 + 258 files changed, 8751 insertions(+), 72737 deletions(-) delete mode 100644 .circleci/config.yml create mode 100644 .dockerignore delete mode 100644 .editorconfig create mode 100644 .eslintrc.cjs delete mode 100644 .eslintrc.js delete mode 100644 .nvmrc delete mode 100644 .prettierrc delete mode 100644 .sass-lint.yml delete mode 100644 .template-lintrc.js delete mode 100644 .watchmanconfig create mode 100644 Dockerfile create mode 100644 README.md delete mode 100644 app/adapters/application.js delete mode 100644 app/adapters/public.js delete mode 100644 app/adapters/theme.js.bk delete mode 100644 app/adapters/tour-set.js delete mode 100644 app/adapters/user.js create mode 100644 app/app.css delete mode 100644 app/app.js create mode 100644 app/choices.ts create mode 100644 app/components/Account.tsx create mode 100644 app/components/ClientOnly.tsx create mode 100644 app/components/List.tsx create mode 100644 app/components/MediaGrid.tsx create mode 100644 app/components/Navbar.tsx create mode 100644 app/components/ThemeSelector.tsx delete mode 100644 app/components/g-map.hbs delete mode 100644 app/components/g-map.js delete mode 100644 app/components/g-map/image-overlay.hbs delete mode 100644 app/components/g-map/image-overlay.js create mode 100644 app/components/inputs/FileUpload.tsx create mode 100644 app/components/inputs/InputWrapper.tsx create mode 100644 app/components/inputs/SelectInput.tsx create mode 100644 app/components/inputs/TextInput.tsx create mode 100644 app/components/inputs/Toggle.tsx create mode 100644 app/components/inputs/ToolTip.tsx delete mode 100644 app/components/language-select/component.js delete mode 100644 app/components/language-select/template.hbs delete mode 100644 app/components/map-icons/component.js delete mode 100644 app/components/map-icons/template.hbs delete mode 100644 app/components/otb-crud/common-form/component.js delete mode 100644 app/components/otb-crud/common-form/template.hbs delete mode 100644 app/components/otb-crud/edit-media-modal/component.js delete mode 100644 app/components/otb-crud/edit-media-modal/template.hbs delete mode 100644 app/components/otb-crud/extra-stops/component.js delete mode 100644 app/components/otb-crud/extra-stops/template.hbs delete mode 100644 app/components/otb-crud/form-section/component.js delete mode 100644 app/components/otb-crud/form-section/template.hbs delete mode 100644 app/components/otb-crud/map-overlay/component.js delete mode 100644 app/components/otb-crud/map-overlay/template.hbs delete mode 100644 app/components/otb-crud/map-type/component.js delete mode 100644 app/components/otb-crud/map-type/template.hbs delete mode 100644 app/components/otb-crud/map/component.js delete mode 100644 app/components/otb-crud/map/template.hbs delete mode 100644 app/components/otb-crud/media/component.js delete mode 100644 app/components/otb-crud/media/extra-media/component.js delete mode 100644 app/components/otb-crud/media/extra-media/template.hbs delete mode 100644 app/components/otb-crud/media/template.hbs delete mode 100644 app/components/otb-crud/medium-upload/component.js delete mode 100644 app/components/otb-crud/medium-upload/template.hbs delete mode 100644 app/components/otb-crud/new-button.hbs delete mode 100644 app/components/otb-crud/pell-editor/component.js delete mode 100644 app/components/otb-crud/pell-editor/template.hbs delete mode 100644 app/components/otb-crud/theme-picker/component.js delete mode 100644 app/components/otb-crud/theme-picker/template.hbs delete mode 100644 app/components/otb-crud/travel-modes/component.js delete mode 100644 app/components/otb-crud/travel-modes/template.hbs delete mode 100644 app/components/otb-crud/video/component.js delete mode 100644 app/components/otb-crud/video/template.hbs delete mode 100644 app/components/otb-tooltip.js delete mode 100644 app/components/otb-tooltip/template.hbs delete mode 100644 app/components/preview-button/component.js delete mode 100644 app/components/preview-button/template.hbs delete mode 100644 app/components/uk-accordion/component.js delete mode 100644 app/components/uk-accordion/template.hbs delete mode 100644 app/components/video-embed/template.hbs create mode 100644 app/context.ts create mode 100644 app/contexts.ts delete mode 100644 app/controllers/.gitkeep delete mode 100644 app/controllers/admin.js delete mode 100644 app/controllers/admin/index.js delete mode 100644 app/controllers/admin/tours/edit.js delete mode 100644 app/controllers/admin/tours/index.js delete mode 100644 app/controllers/admin/users/index.js delete mode 100644 app/controllers/admin/users/user.js delete mode 100644 app/controllers/application.js delete mode 100644 app/controllers/index.js delete mode 100644 app/controllers/login.js delete mode 100644 app/formats.js delete mode 100644 app/helpers/.gitkeep delete mode 100644 app/helpers/includes.js delete mode 100644 app/helpers/media-thumbnail.js delete mode 100644 app/helpers/notin.js delete mode 100644 app/helpers/parking-icon.js delete mode 100644 app/helpers/round-float.js delete mode 100644 app/helpers/save.js.bk create mode 100644 app/hooks.ts delete mode 100644 app/index.html delete mode 100644 app/initializers/currentuser.js delete mode 100644 app/initializers/responsive.js delete mode 100644 app/initializers/tenant.js delete mode 100644 app/initializers/theme.js delete mode 100644 app/lib/base-modal.js delete mode 100644 app/locales/de/config.js delete mode 100644 app/locales/de/translations.js delete mode 100644 app/locales/en/config.js delete mode 100644 app/locales/en/translations.js delete mode 100644 app/locations/trailing-history.js delete mode 100644 app/models/.gitkeep delete mode 100644 app/models/flat-page.js delete mode 100644 app/models/login.js delete mode 100644 app/models/map-icon.js delete mode 100644 app/models/map-overlay.js delete mode 100644 app/models/medium.js delete mode 100644 app/models/mode.js delete mode 100644 app/models/stop-medium.js delete mode 100644 app/models/stop.js delete mode 100644 app/models/theme.js delete mode 100644 app/models/token.js delete mode 100644 app/models/tour-author.js delete mode 100644 app/models/tour-collection.js delete mode 100644 app/models/tour-flat-page.js delete mode 100644 app/models/tour-medium.js delete mode 100644 app/models/tour-mode.js delete mode 100644 app/models/tour-set.js delete mode 100644 app/models/tour-stop.js delete mode 100644 app/models/tour.js delete mode 100644 app/models/user.js create mode 100644 app/root.tsx delete mode 100644 app/router.js create mode 100644 app/routes.ts delete mode 100644 app/routes/admin.js delete mode 100644 app/routes/admin/index.js delete mode 100644 app/routes/admin/tours.js delete mode 100644 app/routes/admin/tours/edit.js delete mode 100644 app/routes/admin/tours/index.js delete mode 100644 app/routes/admin/tours/new.js delete mode 100644 app/routes/admin/users.js delete mode 100644 app/routes/admin/users/index.js delete mode 100644 app/routes/admin/users/user.js delete mode 100644 app/routes/application.js create mode 100644 app/routes/home.tsx delete mode 100644 app/routes/index.js delete mode 100644 app/routes/login.js create mode 100644 app/routes/signin.tsx create mode 100644 app/routes/tour.tsx create mode 100644 app/routes/tour_set.tsx delete mode 100644 app/serializers/application.js delete mode 100644 app/serializers/medium.js delete mode 100644 app/services/crud-actions.js delete mode 100644 app/services/current-user.js delete mode 100644 app/services/geocoder.js delete mode 100644 app/services/task-message.js delete mode 100644 app/services/tenant.js delete mode 100644 app/services/theme.js delete mode 100644 app/session-stores/application.js delete mode 100644 app/styles/admin.scss delete mode 100644 app/styles/animate-list.scss delete mode 100644 app/styles/app.css delete mode 100644 app/styles/app.scss delete mode 100644 app/styles/desktop.scss delete mode 100644 app/styles/login-form.scss delete mode 100644 app/styles/maps.scss delete mode 100644 app/styles/mixins.scss delete mode 100644 app/styles/themes.scss delete mode 100644 app/styles/variables.scss delete mode 100644 app/templates/admin.hbs delete mode 100644 app/templates/admin/error.hbs delete mode 100644 app/templates/admin/index.hbs delete mode 100644 app/templates/admin/loading.hbs delete mode 100644 app/templates/admin/tour.hbs delete mode 100644 app/templates/admin/tours.hbs delete mode 100644 app/templates/admin/tours/edit.hbs delete mode 100644 app/templates/admin/tours/index.hbs delete mode 100644 app/templates/admin/tours/new.hbs delete mode 100644 app/templates/admin/users.hbs delete mode 100644 app/templates/admin/users/index.hbs delete mode 100644 app/templates/admin/users/loading.hbs delete mode 100644 app/templates/admin/users/user.hbs delete mode 100644 app/templates/application.hbs delete mode 100644 app/templates/loading.hbs delete mode 100644 app/templates/login.hbs create mode 100644 app/types.ts create mode 100644 app/useIsHydrated.ts create mode 100644 app/utils/fetchers.ts delete mode 100644 config/deploy.js delete mode 100644 config/ember-intl.js delete mode 100644 config/environment.js delete mode 100644 config/optional-features.json delete mode 100644 config/targets.js delete mode 100644 ember-cli-build.js delete mode 100644 jsconfig.json delete mode 100644 lib/ember-g-map/index.js delete mode 100644 lib/ember-g-map/package.json delete mode 100644 mirage/config.js delete mode 100644 mirage/factories/medium.js delete mode 100644 mirage/factories/theme.js delete mode 100644 mirage/factories/tour-stop.js delete mode 100644 mirage/factories/tour.js delete mode 100644 mirage/fixtures/media.js delete mode 100644 mirage/fixtures/modes.js delete mode 100644 mirage/fixtures/themes.js delete mode 100644 mirage/fixtures/tours.js delete mode 100644 mirage/scenarios/default.js delete mode 100644 mirage/serializers/application.js delete mode 100755 public/crossdomain.xml create mode 100644 public/favicon.ico rename public/{assets/images => }/otb-themes/atl.png (100%) rename public/{assets/images => }/otb-themes/austrian.png (100%) rename public/{assets/images => }/otb-themes/blue.png (100%) rename public/{assets/images => }/otb-themes/bright-blue.png (100%) rename public/{assets/images => }/otb-themes/dark-blue.png (100%) rename public/{assets/images => }/otb-themes/dark-green.png (100%) rename public/{assets/images => }/otb-themes/dark-mode.png (100%) rename public/{assets/images => }/otb-themes/default.png (100%) rename public/{assets/images => }/otb-themes/emory.png (100%) rename public/{assets/images => }/otb-themes/gatech.png (100%) rename public/{assets/images => }/otb-themes/green.png (100%) rename public/{assets/images => }/otb-themes/gsu.png (100%) rename public/{assets/images => }/otb-themes/ksu.png (100%) rename public/{assets/images => }/otb-themes/orange.png (100%) rename public/{assets/images => }/otb-themes/purple.png (100%) rename public/{assets/images => }/otb-themes/red.png (100%) rename public/{assets/images => }/otb-themes/uga.png (100%) create mode 100644 public/otblogo.png delete mode 100755 public/robots.txt create mode 100644 react-router.config.ts create mode 100644 server.js delete mode 100644 testem.js delete mode 100644 tests/.eslintrc.js delete mode 100644 tests/helpers/start-mirage.js delete mode 100644 tests/index.html delete mode 100644 tests/integration/.gitkeep delete mode 100644 tests/integration/components/g-map-test.nope delete mode 100644 tests/integration/components/g-map/image-overlay-test.nope delete mode 100644 tests/integration/components/language-select/component-test.fix delete mode 100644 tests/integration/components/map-icons/component-test.fix delete mode 100644 tests/integration/components/otb-crud/common-form/component-test.js delete mode 100644 tests/integration/components/otb-crud/map-overlay/component-test.js delete mode 100644 tests/integration/components/otb-crud/map-type/component-test.js delete mode 100644 tests/integration/components/otb-crud/media/component-test.js delete mode 100644 tests/integration/components/otb-crud/theme-picker/component-test.js delete mode 100644 tests/integration/components/otb-crud/travel-modes/component-test.js delete mode 100644 tests/integration/components/otb-crud/video/component-test.js delete mode 100644 tests/integration/components/otb-tooltip/component-test.js delete mode 100644 tests/integration/components/video-embed/component-test.js delete mode 100644 tests/test-helper.js delete mode 100644 tests/unit/models/map-icon-test.js delete mode 100644 tests/unit/models/map-overlay-test.js delete mode 100644 translations/en-us.yaml create mode 100644 tsconfig.json delete mode 100755 typings/ember/ember.d.ts delete mode 100644 vendor/.gitkeep create mode 100644 vite.config.ts diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index b60d37ec..00000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,27 +0,0 @@ -version: 2.1 -orbs: - browser-tools: circleci/browser-tools@1.1.1 -jobs: - build: - docker: - - image: cimg/node:14.17-browsers - environment: - JOBS: 2 - DBUS_SYSTEM_BUS_ADDRESS: unix:path=/host/run/dbus/system_bus_socket - working_directory: ~/otb-admin-interface - steps: - - browser-tools/install-chrome - - browser-tools/install-chromedriver - - checkout - - run: - name: NPM Install - command: npm install --non-interactive - - run: echo 'export PATH=~/otb-admin-interface/node_modules/.bin:$PATH' >> $BASH_ENV - - run: - name: Run Tests - command: ember t - # - deploy: - # command: | - # if [ "${CIRCLE_BRANCH}" == "master" ]; then - # ember deploy production - # fi \ No newline at end of file diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..9b8d5147 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,4 @@ +.react-router +build +node_modules +README.md \ No newline at end of file diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index c35a0024..00000000 --- a/.editorconfig +++ /dev/null @@ -1,19 +0,0 @@ -# EditorConfig helps developers define and maintain consistent -# coding styles between different editors and IDEs -# editorconfig.org - -root = true - -[*] -end_of_line = lf -charset = utf-8 -trim_trailing_whitespace = true -insert_final_newline = true -indent_style = space -indent_size = 2 - -[*.hbs] -insert_final_newline = false - -[*.{diff,md}] -trim_trailing_whitespace = false diff --git a/.eslintignore b/.eslintignore index 72df3730..e26dc5e8 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,20 +1,18 @@ -# unconventional js -/blueprints/*/files/ -/vendor/ +**/build/ +**/tests/__snapshots/ +**/node_modules/ +.tmp +pnpm-lock.yaml +/playground +**/__tests__/fixtures +**/__tests__/**/*/fixtures +/packages/*/dist/ -# compiled output -/dist/ -/tmp/ +# Deno +integration/helpers/deno-template +packages/remix-deno +templates/deno -# dependencies -/bower_components/ -/node_modules/ - -# misc -/coverage/ -!.* - -# ember-try -/.node_modules.ember-try/ -/bower.json.ember-try -/package.json.ember-try +packages/remix-dev/config/defaults +templates/remix-tutorial/app/data.ts +templates/cloudflare-workers/worker-configuration.d.ts diff --git a/.eslintrc.cjs b/.eslintrc.cjs new file mode 100644 index 00000000..137851af --- /dev/null +++ b/.eslintrc.cjs @@ -0,0 +1,84 @@ +/** + * This is intended to be a basic starting point for linting in your app. + * It relies on recommended configs out of the box for simplicity, but you can + * and should modify this configuration to best suit your team's needs. + */ + +/** @type {import('eslint').Linter.Config} */ +module.exports = { + root: true, + parserOptions: { + ecmaVersion: "latest", + sourceType: "module", + ecmaFeatures: { jsx: true }, + }, + env: { browser: true, commonjs: true, es6: true, node: true }, + ignorePatterns: ["!**/.server", "!**/.client"], + + // Base config + extends: ["eslint:recommended"], + + rules: { + "no-console": ["warn", { allow: ["warn", "error"] }], + "@typescript-eslint/consistent-type-imports": "warn", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-unused-vars": [ + "warn", + { + vars: "all", + args: "after-used", + argsIgnorePattern: "^_", + varsIgnorePattern: "^_", + }, + ], + "import/no-unresolved": [ + "error", + { ignore: ["virtual:react-router/server-build"] }, + ], + }, + + overrides: [ + // React + { + files: ["**/*.{js,jsx,ts,tsx}"], + plugins: ["react", "jsx-a11y"], + extends: [ + "plugin:react/recommended", + "plugin:react/jsx-runtime", + "plugin:react-hooks/recommended", + "plugin:jsx-a11y/recommended", + ], + settings: { + react: { version: "detect" }, + formComponents: ["Form"], + linkComponents: [ + { name: "Link", linkAttribute: "to" }, + { name: "NavLink", linkAttribute: "to" }, + ], + "import/resolver": { typescript: {} }, + }, + }, + + // Typescript + { + files: ["**/*.{ts,tsx}"], + plugins: ["@typescript-eslint", "import"], + parser: "@typescript-eslint/parser", + settings: { + "import/internal-regex": "^~/", + "import/resolver": { + node: { extensions: [".ts", ".tsx"] }, + typescript: { alwaysTryTypes: true }, + }, + }, + extends: [ + "plugin:@typescript-eslint/recommended", + "plugin:import/recommended", + "plugin:import/typescript", + ], + }, + + // Node + { files: [".eslintrc.cjs"], env: { node: true } }, + ], +}; diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index bc0b90f7..00000000 --- a/.eslintrc.js +++ /dev/null @@ -1,51 +0,0 @@ - 'use strict'; - -module.exports = { - root: true, - parser: 'babel-eslint', - parserOptions: { - ecmaVersion: 2018, - sourceType: 'module', - ecmaFeatures: { - legacyDecorators: true - } - }, - plugins: [ - 'ember', - 'prettier' - ], - extends: [ - 'plugin:ember/recommended' - ], - env: { - browser: true - }, - rules: { - 'ember/no-jquery': 'error', - 'no-console': ["error", { allow: ["warn", "error"] }], - 'semi': "error" - }, - overrides: [ - // node files - { - files: [ - '.eslintrc.js', - '.template-lintrc.js', - 'ember-cli-build.js', - 'testem.js', - 'blueprints/*/index.js', - 'config/**/*.js', - 'lib/*/index.js', - 'server/**/*.js' - ], - parserOptions: { - sourceType: 'script' - }, - env: { - browser: false, - node: true - }, - plugins: ['node'] - } - ] -}; diff --git a/.gitignore b/.gitignore index 16b80f31..305a0fb7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,40 +1,8 @@ -*.DS_Store -.coverage -tmp -.vscode -*.env* -*server.js - -# compiled output -dist/ -tmp/ - -# dependencies -bower_components/ -*node_modules* - -# misc -*/.sass-cache -*/connect.lock -*/coverage/ -*/libpeerconnection.log -*/npm-debug.log* -*/testem.log -*/yarn-error.log -/.env* -/.pnp* -/.sass-cache -/connect.lock -/coverage/ -/libpeerconnection.log -/npm-debug.log* -/testem.log -/yarn-error.log - -# ember-try -*/.node_modules.ember-try/ -*/bower.json.ember-try -*/package.json.ember-try -*.log - -.ember-cli.js \ No newline at end of file +.DS_Store +.env +/node_modules/ + +# React Router +/.react-router/ +/build/ +*.pem \ No newline at end of file diff --git a/.nvmrc b/.nvmrc deleted file mode 100644 index 9a0c3d3f..00000000 --- a/.nvmrc +++ /dev/null @@ -1 +0,0 @@ -v14.15.4 diff --git a/.prettierrc b/.prettierrc deleted file mode 100644 index 19c202a5..00000000 --- a/.prettierrc +++ /dev/null @@ -1,5 +0,0 @@ -{ - "singleQuote": true, - "semi": true -} - diff --git a/.sass-lint.yml b/.sass-lint.yml deleted file mode 100644 index 37a11603..00000000 --- a/.sass-lint.yml +++ /dev/null @@ -1,169 +0,0 @@ -linters: - - BangFormat: - enabled: true - space_before_bang: true - space_after_bang: false - - BorderZero: - enabled: true - - ColorKeyword: - enabled: true - - Comment: - enabled: false - - DebugStatement: - enabled: true - - DeclarationOrder: - enabled: true - - DuplicateProperty: - enabled: true - - ElsePlacement: - enabled: true - style: same_line - - EmptyLineBetweenBlocks: - enabled: true - ignore_single_line_blocks: false - - EmptyRule: - enabled: true - - FinalNewline: - enabled: true - present: true - - HexLength: - enabled: true - style: short - - HexNotation: - enabled: true - style: lowercase - - HexValidation: - enabled: true - - IdSelector: - enabled: true - - ImportPath: - enabled: true - leading_underscore: false - filename_extension: false - - Indentation: - enabled: true - character: space - width: 2 - - LeadingZero: - enabled: true - style: include_zero - - MergeableSelector: - enabled: false - - NameFormat: - enabled: true - convention: hyphenated_lowercase - - NestingDepth: - enabled: true - max_depth: 3 - - NoTransitionAll: - enabled: false - - PlaceholderInExtend: - enabled: true - - PropertySortOrder: - enabled: false - - PropertySpelling: - enabled: true - extra_properties: [] - - QualifyingElement: - enabled: true - allow_element_with_attribute: false - allow_element_with_class: false - allow_element_with_id: false - - SelectorDepth: - enabled: true - max_depth: 3 - - SelectorFormat: - enabled: true - convention: hyphenated_lowercase - class_convention: '^(?:u|is|has)\-[a-z][a-zA-Z0-9]*$|^(?!u|is|has)[a-zA-Z][a-zA-Z0-9]*(?:\-[a-z][a-zA-Z0-9]*)?(?:\-\-[a-z][a-zA-Z0-9]*)?$' - - Shorthand: - enabled: true - - SingleLinePerProperty: - enabled: true - allow_single_line_rule_sets: false - - SingleLinePerSelector: - enabled: true - - SpaceAfterComma: - enabled: true - - SpaceAfterPropertyColon: - enabled: true - style: one_space - - SpaceAfterPropertyName: - enabled: true - - SpaceBeforeBrace: - enabled: true - style: space - allow_single_line_padding: true - - SpaceBetweenParens: - enabled: true - spaces: 0 - - StringQuotes: - enabled: true - style: single_quotes - - TrailingSemicolon: - enabled: true - - TrailingZero: - enabled: true - - UnnecessaryMantissa: - enabled: true - - UnnecessaryParentReference: - enabled: false - - UrlFormat: - enabled: false - - UrlQuotes: - enabled: true - - VendorPrefixes: - enabled: true - identifier_list: base - include: [] - exclude: [] - - ZeroUnit: - enabled: true - - Compass::*: - enabled: false diff --git a/.template-lintrc.js b/.template-lintrc.js deleted file mode 100644 index f3873700..00000000 --- a/.template-lintrc.js +++ /dev/null @@ -1,5 +0,0 @@ -'use strict'; - -module.exports = { - extends: 'octane' -}; diff --git a/.watchmanconfig b/.watchmanconfig deleted file mode 100644 index 2b71b98e..00000000 --- a/.watchmanconfig +++ /dev/null @@ -1 +0,0 @@ -{"ignore_dirs":["tmp","dist","ember-cordova","corber"]} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..207bf937 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,22 @@ +FROM node:20-alpine AS development-dependencies-env +COPY . /app +WORKDIR /app +RUN npm ci + +FROM node:20-alpine AS production-dependencies-env +COPY ./package.json package-lock.json /app/ +WORKDIR /app +RUN npm ci --omit=dev + +FROM node:20-alpine AS build-env +COPY . /app/ +COPY --from=development-dependencies-env /app/node_modules /app/node_modules +WORKDIR /app +RUN npm run build + +FROM node:20-alpine +COPY ./package.json package-lock.json /app/ +COPY --from=production-dependencies-env /app/node_modules /app/node_modules +COPY --from=build-env /app/build /app/build +WORKDIR /app +CMD ["npm", "run", "start"] \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 00000000..5c4780a2 --- /dev/null +++ b/README.md @@ -0,0 +1,87 @@ +# Welcome to React Router! + +A modern, production-ready template for building full-stack React applications using React Router. + +[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/remix-run/react-router-templates/tree/main/default) + +## Features + +- 🚀 Server-side rendering +- ⚡️ Hot Module Replacement (HMR) +- 📦 Asset bundling and optimization +- 🔄 Data loading and mutations +- 🔒 TypeScript by default +- 🎉 TailwindCSS for styling +- 📖 [React Router docs](https://reactrouter.com/) + +## Getting Started + +### Installation + +Install the dependencies: + +```bash +npm install +``` + +### Development + +Start the development server with HMR: + +```bash +npm run dev +``` + +Your application will be available at `http://localhost:5173`. + +## Building for Production + +Create a production build: + +```bash +npm run build +``` + +## Deployment + +### Docker Deployment + +To build and run using Docker: + +```bash +docker build -t my-app . + +# Run the container +docker run -p 3000:3000 my-app +``` + +The containerized application can be deployed to any platform that supports Docker, including: + +- AWS ECS +- Google Cloud Run +- Azure Container Apps +- Digital Ocean App Platform +- Fly.io +- Railway + +### DIY Deployment + +If you're familiar with deploying Node applications, the built-in app server is production-ready. + +Make sure to deploy the output of `npm run build` + +``` +├── package.json +├── package-lock.json (or pnpm-lock.yaml, or bun.lockb) +├── build/ +│ ├── client/ # Static assets +│ └── server/ # Server-side code +``` + +## Styling + +This template comes with [Tailwind CSS](https://tailwindcss.com/) already configured for a simple default starting experience. You can use whatever CSS framework you prefer. + +--- + +Built with ❤️ using React Router. diff --git a/app/adapters/application.js b/app/adapters/application.js deleted file mode 100644 index ce1dc5aa..00000000 --- a/app/adapters/application.js +++ /dev/null @@ -1,28 +0,0 @@ -import JSONAPIAdapter from '@ember-data/adapter/json-api'; -import { inject as service } from '@ember/service'; -// import { computed } from '@ember/object'; -import ENV from '../config/environment'; - -export default class Application extends JSONAPIAdapter { - @service tenant; - - - // @computed - get host() { - if (!this.tenant.tenant) { - this.tenant.setTenant(); - } - return `${ENV.APP.API_HOST}/${this.tenant.tenant}`; - } - - ajaxOptions(/*defaultOptions, adapter*/) { - const options = super.ajaxOptions(...arguments); - options.credentials = 'include'; - - return options; - } - - // set headers(v) { - // return v - // } -} diff --git a/app/adapters/public.js b/app/adapters/public.js deleted file mode 100644 index 8e3499ca..00000000 --- a/app/adapters/public.js +++ /dev/null @@ -1,6 +0,0 @@ -import JSONAPIAdapter from '@ember-data/adapter/json-api'; -import ENV from '../config/environment'; - -export default class Public extends JSONAPIAdapter { - host = `${ENV.APP.API_HOST}/public`; -} diff --git a/app/adapters/theme.js.bk b/app/adapters/theme.js.bk deleted file mode 100644 index d1519dd0..00000000 --- a/app/adapters/theme.js.bk +++ /dev/null @@ -1,3 +0,0 @@ -import Public from './public'; - -export default class Theme extends Public {} diff --git a/app/adapters/tour-set.js b/app/adapters/tour-set.js deleted file mode 100644 index 94581813..00000000 --- a/app/adapters/tour-set.js +++ /dev/null @@ -1,10 +0,0 @@ -import Public from './public'; - -export default class TourSet extends Public { - ajaxOptions(/*defaultOptions, adapter*/) { - const options = super.ajaxOptions(...arguments); - options.credentials = 'include'; - - return options; - } -} diff --git a/app/adapters/user.js b/app/adapters/user.js deleted file mode 100644 index 8c62044f..00000000 --- a/app/adapters/user.js +++ /dev/null @@ -1,14 +0,0 @@ -import Public from './public'; - -export default class User extends Public { - ajaxOptions(/*defaultOptions, adapter*/) { - const options = super.ajaxOptions(...arguments); - options.credentials = 'include'; - - return options; - } - - shouldReloadAll() { - return true; - } -} diff --git a/app/app.css b/app/app.css new file mode 100644 index 00000000..4cc5a977 --- /dev/null +++ b/app/app.css @@ -0,0 +1,20 @@ +@import "tailwindcss"; + +@theme { + --font-sans: + "Inter", ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", + "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; +} + +html, +body { + /* @apply bg-white dark:bg-gray-950; + + @media (prefers-color-scheme: dark) { + color-scheme: dark; + } */ + + .jodit-wysiwyg p { + margin-bottom: 1rem; + } +} diff --git a/app/app.js b/app/app.js deleted file mode 100644 index d8e2088b..00000000 --- a/app/app.js +++ /dev/null @@ -1,12 +0,0 @@ -import Application from '@ember/application'; -import Resolver from 'ember-resolver'; -import loadInitializers from 'ember-load-initializers'; -import config from './config/environment'; - -export default class App extends Application { - modulePrefix = config.modulePrefix; - podModulePrefix = config.podModulePrefix; - Resolver = Resolver; -} - -loadInitializers(App, config.modulePrefix); diff --git a/app/choices.ts b/app/choices.ts new file mode 100644 index 00000000..0a98ed89 --- /dev/null +++ b/app/choices.ts @@ -0,0 +1,152 @@ +export const languages = [ + { + value: "fr-FR", + label: "French ", + }, + { + value: "de-DE", + label: "German ", + }, + { + value: "pl-PL", + label: "Polish ", + }, + { + value: "nl-NI", + label: "Dutch ", + }, + { + value: "fi-FI", + label: "Finnish", + }, + { + value: "sv-SE", + label: "Swedish", + }, + { + value: "it-IT", + label: "Italian", + }, + { + value: "es-ES", + label: "Spanish (Spain)", + }, + { + value: "pt-PT", + label: "Portuguese (Portugal)", + }, + { + value: "ru-RU", + label: "Russian", + }, + { + value: "pt-BR", + label: "Portuguese (Brazil)", + }, + { + value: "es-MX", + label: "Spanish (Mexico)", + }, + { + value: "en-US", + label: "English", + }, + { + value: "zh-CN", + label: "Chinese (PRC) ", + }, + { + value: "zh-TW", + label: "Chinese (Taiwan)", + }, + { + value: "ja-JP", + label: "Japanese", + }, + { + value: "ko-KR", + label: "Korean ", + }, +]; + +export const themes = [ + { + value: 1, + label: "default", + }, + { + value: 2, + label: "blue", + }, + { + value: 3, + label: "atl", + }, + { + value: 4, + label: "red", + }, + { + value: 5, + label: "dark-blue", + }, + { + value: 6, + label: "purple", + }, + { + value: 7, + label: "green", + }, + { + value: 8, + label: "orange", + }, + { + value: 9, + label: "dark-green", + }, + { + value: 10, + label: "austrian", + }, + { + value: 11, + label: "emory", + }, + { + value: 12, + label: "gsu", + }, + { + value: 13, + label: "gatech", + }, + { + value: 14, + label: "uga", + }, + { + value: 15, + label: "ksu", + }, +]; + +export const modes = [ + { + value: 1, + label: "BICYCLING", + }, + { + value: 2, + label: "DRIVING", + }, + { + value: 3, + label: "TRANSIT", + }, + { + value: 4, + label: "WALKING", + }, +]; diff --git a/app/components/Account.tsx b/app/components/Account.tsx new file mode 100644 index 00000000..598ceaae --- /dev/null +++ b/app/components/Account.tsx @@ -0,0 +1,33 @@ +import { useContext } from "react"; +import { AuthContext } from "~/context"; +import { signOut } from "~/utils/fetchers"; + +const Account = () => { + const { signedIn, setCurrentUser } = useContext(AuthContext); + + const handelSignOut = async () => { + const { response } = await signOut(); + if (response.ok) setCurrentUser(undefined); + }; + + if (signedIn) { + return ( + + ); + } + return ( + + sign in + + ); +}; + +export default Account; diff --git a/app/components/ClientOnly.tsx b/app/components/ClientOnly.tsx new file mode 100644 index 00000000..876cf0ed --- /dev/null +++ b/app/components/ClientOnly.tsx @@ -0,0 +1,19 @@ +/* +This was adapted from the ClientOnly component form remix-utils +https://github.com/sergiodxa/remix-utils/blob/e5795afaf918fb996870a55bbe888bcbc60a4d01/src/react/client-only.tsx +*/ +import { useIsHydrated } from "~/useIsHydrated"; +import type { ReactNode } from "react"; + +interface Props { + children: ReactNode; + fallback?: ReactNode; +} + +const ClientOnly = ({ children, fallback = null }: Props) => { + const isHydrated = useIsHydrated; + + return isHydrated() ? <>{children} : <>{fallback}; +}; + +export default ClientOnly; diff --git a/app/components/List.tsx b/app/components/List.tsx new file mode 100644 index 00000000..e0cc9846 --- /dev/null +++ b/app/components/List.tsx @@ -0,0 +1,44 @@ +import { faTrash } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { Link } from "react-router"; +import type { TTour, TTourSet } from "~/types"; + +interface Props { + items: TTourSet[] | TTour[]; + handleDelete: () => void; + heading: string; +} + +const List = ({ items, handleDelete, heading }: Props) => { + return ( +
+
+
Delete
+
{heading ?? "Name"}
+
+ {items.map((item, index) => { + return ( +
+ +
+ + {item.type === "tour_sets" && item.attributes.name} + {item.type === "tours" && item.attributes.title} + +
+
+ ); + })} +
+ ); +}; + +export default List; diff --git a/app/components/MediaGrid.tsx b/app/components/MediaGrid.tsx new file mode 100644 index 00000000..050af825 --- /dev/null +++ b/app/components/MediaGrid.tsx @@ -0,0 +1,102 @@ +import { + DndContext, + closestCenter, + KeyboardSensor, + PointerSensor, + useSensor, + useSensors, + type DragEndEvent, +} from "@dnd-kit/core"; +import { + arrayMove, + SortableContext, + sortableKeyboardCoordinates, + useSortable, + rectSortingStrategy, +} from "@dnd-kit/sortable"; +import { useState } from "react"; +import { CSS } from "@dnd-kit/utilities"; +import type { TMedium } from "~/types"; + +const SortableItem = ({ medium }: { medium: TMedium }) => { + const { attributes, listeners, setNodeRef, transform, transition } = + useSortable({ id: medium.id }); + + const style = { + transform: CSS.Transform.toString(transform), + transition, + }; + + return ( +
+
+
+ +
{medium.title ?? "no caption"}
+
+
+ + +
+
+
+ ); +}; + +const MediaGrid = ({ media }: { media: TMedium[] }) => { + const [items, setItems] = useState(media); + const sensors = useSensors( + useSensor(PointerSensor), + useSensor(KeyboardSensor, { + coordinateGetter: sortableKeyboardCoordinates, + }) + ); + + const handleDragEnd = (event: DragEndEvent) => { + const { active, over } = event; + + if (active.id !== over?.id) { + setItems((items) => { + const oldIndex = items.indexOf( + // @ts-expect-error: We know it will be there. + items.find((medium) => medium.id == active.id) + ); + const newIndex = items.indexOf( + // @ts-expect-error: We know it will be there. + items.find((medium) => medium.id == over.id) + ); + + return arrayMove(items, oldIndex, newIndex); + }); + } + }; + + return ( + + +
Media
+
+ {items.map((medium) => ( + + ))} +
+
+
+ ); +}; + +export default MediaGrid; diff --git a/app/components/Navbar.tsx b/app/components/Navbar.tsx new file mode 100644 index 00000000..19fd209a --- /dev/null +++ b/app/components/Navbar.tsx @@ -0,0 +1,44 @@ +import { NavLink } from "react-router"; +import Account from "./Account"; + +const Navbar = () => { + return ( + + ); +}; + +export default Navbar; diff --git a/app/components/ThemeSelector.tsx b/app/components/ThemeSelector.tsx new file mode 100644 index 00000000..f2c8f720 --- /dev/null +++ b/app/components/ThemeSelector.tsx @@ -0,0 +1,71 @@ +import { faCheckSquare } from "@fortawesome/free-solid-svg-icons"; +import { faSquare } from "@fortawesome/free-regular-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { Field, Legend, Radio, RadioGroup } from "@headlessui/react"; +import { useContext, useEffect, useRef, useState } from "react"; +import { themes } from "~/choices"; +import { sendUpdate } from "~/utils/fetchers"; +import { RecordContext } from "~/contexts"; + +interface Props { + theme: number; +} + +const ThemeSelector = ({ theme }: Props) => { + const [currentValue, setCurrentValue] = useState(theme); + const valueRef = useRef(currentValue); + const { recordId, tenant } = useContext(RecordContext); + + useEffect(() => { + const update = async () => { + await sendUpdate({ + tenant, + record: recordId, + body: { + model: "tour", + attribute: "theme", + value: currentValue, + related_model: "theme", + related_type: "belongs_to", + }, + }); + valueRef.current = currentValue; + }; + + if (currentValue !== valueRef.current) update(); + }, [tenant, recordId, currentValue]); + + return ( + + Themes + {themes.map((theme) => { + return ( + + + +
+ +
+
+
+ ); + })} +
+ ); +}; + +export default ThemeSelector; diff --git a/app/components/g-map.hbs b/app/components/g-map.hbs deleted file mode 100644 index 9f3da4b9..00000000 --- a/app/components/g-map.hbs +++ /dev/null @@ -1,20 +0,0 @@ -{{#let (component 'g-map/canvas' onCanvasReady=(fn this.getCanvas)) as |Canvas|}} - {{#unless (has-block-params)}} - {{!-- Compatibility: @classNames should be deprecated --}} - - {{/unless}} - - {{yield (hash - map=this.map - canvas=Canvas - imageOverlay=(component 'g-map/image-overlay' getContext=(fn this.getComponent)) - marker=(component 'g-map/marker' getContext=(fn this.getComponent)) - circle=(component 'g-map/circle' getContext=(fn this.getComponent)) - polyline=(component 'g-map/polyline' getContext=(fn this.getComponent)) - directions=(component 'g-map/directions' getContext=(fn this.getComponent)) - control=(component 'g-map/control' getContext=(fn this.getComponent)) - overlay=(component 'g-map/overlay' getContext=(fn this.getComponent)) - infoWindow=(component 'g-map/info-window' getContext=(fn this.getComponent)) - autocomplete=(component 'g-map/autocomplete' getContext=(fn this.getComponent)) - )}} -{{/let}} \ No newline at end of file diff --git a/app/components/g-map.js b/app/components/g-map.js deleted file mode 100644 index f8400a19..00000000 --- a/app/components/g-map.js +++ /dev/null @@ -1,4 +0,0 @@ -import GMap from 'ember-google-maps/components/g-map'; - -export default class GMapComponent extends GMap { -} diff --git a/app/components/g-map/image-overlay.hbs b/app/components/g-map/image-overlay.hbs deleted file mode 100644 index b30f308e..00000000 --- a/app/components/g-map/image-overlay.hbs +++ /dev/null @@ -1,10 +0,0 @@ -{{#if (has-block)}} - {{#if this.container}} - {{#in-element this.container}} -
- - {{yield}} -
- {{/in-element}} - {{/if}} -{{/if}} \ No newline at end of file diff --git a/app/components/g-map/image-overlay.js b/app/components/g-map/image-overlay.js deleted file mode 100644 index bc149d86..00000000 --- a/app/components/g-map/image-overlay.js +++ /dev/null @@ -1,74 +0,0 @@ -import OverlayView from 'ember-google-maps/components/g-map/overlay'; -import { action } from '@ember/object'; -/* global google */ - -export default class GMapImageOverlayComponent extends OverlayView { - - overlayElement = null; - paneName = 'overlayLayer'; - - get position() { - let { south, west, north, east } = this.args; - - if ([south, west, north, east].some( boundary => boundary == null)) { - return null; - } - return this.toBounds(south, west, north, east); - } - - toBounds(south, west, north, east) { - return new google.maps.LatLngBounds( - new google.maps.LatLng(south, west), - new google.maps.LatLng(north, east) - ); - } - - onAdd() { - // this.map.fitBounds(this.position); - - let panes = this.mapComponent.getPanes(); - this.targetPane = panes[this.paneName]; - - this.targetPane.appendChild(this.overlayElement); - - this.addEventsToMapComponent( - this.overlayElement, - this.events, - this.publicAPI - ); - } - - draw() { - let { position, zIndex } = this; - - let overlayProjection = this.mapComponent.getProjection(); - let sw = overlayProjection.fromLatLngToDivPixel(position.getSouthWest()); - let ne = overlayProjection.fromLatLngToDivPixel(position.getNorthEast()); - - - this.overlayElement.style.cssText = ` - position: absolute; - left: ${sw.x}px; - top: ${ne.y}px; - height: ${sw.y - ne.y}px; - width: ${ne.x - sw.x}px; - z-index: ${zIndex}; - `; - - this.mapComponent.didDraw ||= true; - } - - @action - getOverlay(element) { - this.overlayElement = element; - } - - @action - fitBounds() { - google.maps.event.addListenerOnce(this.map, 'idle', () => { - if (this.args.onLoad) this.args.onLoad(); - }); - - if (!this.args.maintainBounds) this.map.fitBounds(this.position); - } -} diff --git a/app/components/inputs/FileUpload.tsx b/app/components/inputs/FileUpload.tsx new file mode 100644 index 00000000..1cd9c41f --- /dev/null +++ b/app/components/inputs/FileUpload.tsx @@ -0,0 +1,7 @@ +interface Props {} + +const FileUpload = ({}: Props) => { + return <>; +}; + +export default FileUpload; diff --git a/app/components/inputs/InputWrapper.tsx b/app/components/inputs/InputWrapper.tsx new file mode 100644 index 00000000..ec77f02c --- /dev/null +++ b/app/components/inputs/InputWrapper.tsx @@ -0,0 +1,13 @@ +import { Field } from "@headlessui/react"; +import type { ReactNode } from "react"; + +interface Props { + children: ReactNode; + className?: string; +} + +const InputWrapper = ({ children, className }: Props) => { + return {children}; +}; + +export default InputWrapper; diff --git a/app/components/inputs/SelectInput.tsx b/app/components/inputs/SelectInput.tsx new file mode 100644 index 00000000..d5b14e0d --- /dev/null +++ b/app/components/inputs/SelectInput.tsx @@ -0,0 +1,76 @@ +import { Description, Select } from "@headlessui/react"; +import InputWrapper from "./InputWrapper"; +import type { InputProps, TChoices } from "~/types"; +import ToolTip from "./ToolTip"; +import { useContext, useEffect, useRef, useState } from "react"; +import { sendUpdate } from "~/utils/fetchers"; +import { RecordContext } from "~/contexts"; + +type SelectProps = { + value: string; + options: TChoices[]; +}; + +const SelectInput = ({ + id, + label, + model, + value, + helpText, + options, +}: InputProps & SelectProps) => { + const [currentValue, setCurrentValue] = useState(value); + const inputRef = useRef(null); + const valueRef = useRef(value); + const { tenant, recordId } = useContext(RecordContext); + + useEffect(() => { + const update = async () => { + await sendUpdate({ + tenant, + record: recordId, + body: { model, attribute: id, value: currentValue }, + }); + valueRef.current = currentValue; + }; + if (valueRef.current !== currentValue) update(); + }, [currentValue, tenant, model, id, recordId]); + + const handleSelect = () => { + if (!inputRef.current) return; + setCurrentValue(inputRef.current.value); + }; + + return ( + + + {helpText && ( + + {helpText} + + )} + + + ); +}; + +export default SelectInput; diff --git a/app/components/inputs/TextInput.tsx b/app/components/inputs/TextInput.tsx new file mode 100644 index 00000000..d1cd03e5 --- /dev/null +++ b/app/components/inputs/TextInput.tsx @@ -0,0 +1,133 @@ +import { + lazy, + useCallback, + useContext, + useEffect, + useRef, + useState, +} from "react"; +import { Description, Input, Textarea } from "@headlessui/react"; +import { sendUpdate } from "~/utils/fetchers"; +import InputWrapper from "./InputWrapper"; +import { RecordContext } from "~/contexts"; +import type { InputProps } from "~/types"; +import ToolTip from "./ToolTip"; +import ClientOnly from "../ClientOnly"; + +const JoditEditor = lazy(() => import("jodit-react")); + +const config = { + readonly: false, + placeholder: "Start typings...", + buttons: [ + "bold", + "strikethrough", + "underline", + "italic", + "ul", + "ol", + "link", + "indent", + "undo", + "redo", + "source", + ], + statusbar: false, + height: 200, +}; + +type TextInputProps = { + value: string; + type: "text" | "text-area" | "rich-text"; +}; + +const TextInput = ({ + helpText, + id, + label, + model, + value, + type, +}: InputProps & TextInputProps) => { + const [currentValue, setCurrentValue] = useState(value); + const inputRef = useRef(null); + const valueRef = useRef(value); + + const { recordId, tenant } = useContext(RecordContext); + + const update = useCallback(async () => { + await sendUpdate({ + tenant, + record: recordId, + body: { model, attribute: id, value: currentValue }, + }); + valueRef.current = currentValue; + }, [tenant, recordId, model, currentValue, id]); + + useEffect(() => { + if (currentValue === valueRef.current) return; + + const timeoutId = setTimeout(() => { + if (currentValue !== valueRef.current) update(); + }, 500); + + return () => clearTimeout(timeoutId); + }, [currentValue, id, model, recordId, tenant, update]); + + const handleChange = () => { + if (!inputRef.current) return; + setCurrentValue(inputRef.current.value); + }; + + // const handleRichTextChange = (value) => { + // setCurrentValue + // } + + return ( + + + {helpText && ( + + {helpText} + + )} +
+ {type === "text" && ( + + )} + {type === "text-area" && ( +