diff --git a/.env.sample b/.env.sample index 230f180..e325ea9 100644 --- a/.env.sample +++ b/.env.sample @@ -1,25 +1,39 @@ +DATABASE_URL=postgres://claper:claper@db:5432/claper +SECRET_KEY_BASE=0LZiQBLw4WvqPlz4cz8RsHJlxNiSqM9B48y4ChyJ5v1oA0L/TPIqRjQNdPZN3iEG # Generate with `mix phx.gen.secret` + +# Storage configuration + PRESENTATION_STORAGE=local PRESENTATION_STORAGE_DIR=/app/uploads -MAX_FILE_SIZE_MB=15 +#MAX_FILE_SIZE_MB=15 -AWS_ACCESS_KEY_ID=xxx -AWS_SECRET_ACCESS_KEY=xxx -AWS_REGION=eu-west-3 -AWS_PRES_BUCKET=xxx +#AWS_ACCESS_KEY_ID=xxx +#AWS_SECRET_ACCESS_KEY=xxx +#AWS_REGION=eu-west-3 +#AWS_PRES_BUCKET=xxx -SMTP_RELAY=xx.example.com -SMTP_USERNAME=johndoe@example.com -SMTP_PASSWORD=xxx -SMTP_PORT=465 -SMTP_TLS=if_available +# Mail configuration MAIL_TRANSPORT=local MAIL_FROM=noreply@claper.co MAIL_FROM_NAME=Claper -ENABLE_ACCOUNT_CREATION=true -ENABLE_MAILBOX_ROUTE=false -MAILBOX_USER=admin -MAILBOX_PASSWORD=admin +#SMTP_RELAY=xx.example.com +#SMTP_USERNAME=johndoe@example.com +#SMTP_PASSWORD=xxx +#SMTP_PORT=465 +#SMTP_TLS=if_available -GS_JPG_RESOLUTION=300x300 +#ENABLE_MAILBOX_ROUTE=false +#MAILBOX_USER=admin +#MAILBOX_PASSWORD=admin + +# Claper configuration + +#ENABLE_ACCOUNT_CREATION=true +#GS_JPG_RESOLUTION=300x300 + +# Network configuration + +ENDPOINT_PORT=4000 +ENDPOINT_HOST=localhost \ No newline at end of file diff --git a/.formatter.exs b/.formatter.exs index 1470d85..ef8840c 100644 --- a/.formatter.exs +++ b/.formatter.exs @@ -1,6 +1,6 @@ [ - import_deps: [:ecto, :phoenix], - inputs: ["*.{heex,ex,exs}", "{config,lib,test}/**/*.{heex,ex,exs}", "priv/*/seeds.exs"], + import_deps: [:ecto, :ecto_sql, :phoenix], subdirectories: ["priv/*/migrations"], - plugins: [Phoenix.LiveView.HTMLFormatter] + plugins: [Phoenix.LiveView.HTMLFormatter], + inputs: ["*.{heex,ex,exs}", "{config,lib,test}/**/*.{heex,ex,exs}", "priv/*/seeds.exs"] ] diff --git a/.github/workflows/elixir.yml b/.github/workflows/elixir.yml index 3fe1906..38faed9 100644 --- a/.github/workflows/elixir.yml +++ b/.github/workflows/elixir.yml @@ -18,7 +18,7 @@ jobs: services: db: - image: postgres:9 + image: postgres:15 ports: ['5432:5432'] env: POSTGRES_PASSWORD: claper @@ -42,8 +42,8 @@ jobs: - name: Set up Elixir uses: erlef/setup-beam@988e02bfe678367a02564f65ca2e37726dc0268f with: - elixir-version: '1.13.2' - otp-version: '24.1' + elixir-version: '1.15.4' + otp-version: '26' - name: Restore dependencies cache uses: actions/cache@v3 with: diff --git a/CHANGELOG.md b/CHANGELOG.md index c8888f0..fce6095 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,20 @@ +## v2.0.0 + +- Add dynamic layout in the manager view +- Add quick event feature +- Add toggle for message reactions in attendees room +- Add toggle for polls results in attendees room +- Add delete account button in user settings +- Add tour guide for new users +- Add headers to exported CSV in reports +- Add the ability to embed attendees room in an iframe +- Change date picker for a more user-friendly one +- Upgrade Ecto, Phoenix and LiveView +- Fix user avatars in reports +- Fix average voters stats +- Fix some UI/UX issues +- Remove end date for events + ## v1.7.0 - Add keyboard shortcuts to control settings (#64) (@Dhanus3133) @@ -12,6 +29,7 @@ - Security updates ## v1.6.0 + - Improve QR code readability - Add ARM Docker image - Refactor all runtime configuration @@ -46,25 +64,21 @@ - Add MAX_FILE_SIZE_MB environment variable to limit file upload size - Add feature to deactivate messages during a presentation - ## v1.3.0 - Add Form feature to collect data from your public - Improve docs for Docker Compose - Improve Docker Compose file reference - ## v1.2.1 - Fix presenter url (400 error in production) - ## v1.2.0 - Added password change form in settings - Added more documentation on deployment in production - ## v1.1.1 _Security updates_ @@ -72,7 +86,6 @@ _Security updates_ - Added `ENABLE_MAILBOX_ROUTE`, `MAILBOX_USER` and `MAILBOX_PASSWORD` environment variables to enable/disable route to local mailbox (`/dev/mailbox`) and basic auth (optional) - Restricted `/users/register` route if `ENABLE_ACCOUNT_CREATION` is false - ## v1.1.0 - Added password authentication @@ -81,7 +94,6 @@ _Security updates_ - Added new `ENABLE_ACCOUNT_CREATION` environment variable to enable or disable user registration - Improved french localization - ## v1.0.0 This is the first version of the open-source project. Feel free to contribute! diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6223f1e..6a0c50f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -11,4 +11,9 @@ Don't forget to give the project a star! Thanks again! ## Translations -You can contribute to the translations by editing or addind PO files in `/priv/gettext/` \ No newline at end of file +You can contribute to the translations by editing the files in `/priv/gettext/` +Each language has its own directory with the `.po` files. The country code is used as the directory name and following the [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) nomenclature, for example, `en` for English, `fr` for French, `de` for German. You can find the list of country codes [here](https://en.wikipedia.org/wiki/List_of_ISO_639_language_codes). + +### Add new language + +To add a new language, you can copy the `en` directory and rename it with the country code of the new language. Then you can edit the `.po` files with the translations. diff --git a/Dockerfile b/Dockerfile index ff0b8c8..9337e99 100644 --- a/Dockerfile +++ b/Dockerfile @@ -84,7 +84,7 @@ RUN mix release # the compiled release and other runtime necessities FROM ${RUNNER_IMAGE} -RUN apt-get update -y && apt-get install -y libstdc++6 openssl libncurses5 locales ghostscript \ +RUN apt-get update -y && apt-get install -y curl libstdc++6 openssl libncurses5 locales ghostscript \ && apt-get install -y libreoffice --no-install-recommends && apt-get clean && rm -f /var/lib/apt/lists/*_* # Set the locale diff --git a/README.md b/README.md index af7a754..e9690ed 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,3 @@ - [![Contributors][contributors-shield]][contributors-url] [![Forks][forks-shield]][forks-url] [![Stargazers][stars-shield]][stars-url] @@ -26,14 +25,12 @@
- - - [![Product Name Screen Shot][product-screenshot]](https://claper.co) Claper turns your presentations into an interactive, engaging and exciting experience. Claper has a two-sided mission: + - The first one is to help these people presenting an idea or a message by giving them the opportunity to make their presentation unique and to have real-time feedback from their audience. - The second one is to help each participant to take their place, to be an actor in the presentation, in the meeting and to feel important and useful. @@ -43,12 +40,12 @@ Supported languages: 🇬🇧 English, 🇫🇷 French, 🇩🇪 German. Claper is proudly powered by Phoenix and Elixir. -* [![Phoenix][Phoenix]][Phoenix-url] -* [![Elixir][Elixir]][Elixir-url] -* [![Tailwind][Tailwind]][Tailwind-url] - +- [![Phoenix][Phoenix]][Phoenix-url] +- [![Elixir][Elixir]][Elixir-url] +- [![Tailwind][Tailwind]][Tailwind-url] + ## Getting Started This is an example of how you may give instructions on setting up your project locally. @@ -57,18 +54,20 @@ To get a local copy up and running follow these simple example steps. ### Prerequisites To run Claper on your local environment you need to have: -* Postgres >= 9 -* Elixir >= 1.13.2 -* Erlang >= 24 -* NPM >= 6.14.17 -* NodeJS >= 14.19.2 -* Ghostscript >= 9.5.0 (for PDF support) -* Libreoffice >= 6.4 (for PPT/PPTX support) + +- Postgres >= 9 +- Elixir >= 1.13.2 +- Erlang >= 24 +- NPM >= 6.14.17 +- NodeJS >= 14.19.2 +- Ghostscript >= 9.5.0 (for PDF support) +- Libreoffice >= 6.4 (for PPT/PPTX support) You can also use Docker to easily run a Postgres instance: + ```sh - docker run -p 5432:5432 -e POSTGRES_PASSWORD=claper -e POSTGRES_USER=claper -e POSTGRES_DB=claper --name claper-db -d postgres:9 - ``` + docker run -p 5432:5432 -e POSTGRES_PASSWORD=claper -e POSTGRES_USER=claper -e POSTGRES_DB=claper --name claper-db -d postgres:15 +``` ### Configuration @@ -105,7 +104,6 @@ Now you can visit [`localhost:4000`](http://localhost:4000) from your browser. If you have configured `MAIL` to `local`, you can access to the mailbox at [`localhost:4000/dev/mailbox`](http://localhost:4000/dev/mailbox). - ### Using Docker Compose A Docker Compose [reference file](https://github.com/ClaperCo/Claper/blob/main/docker-compose.yml) is provided in the repository. You can use it to run Claper with Docker Compose. @@ -116,19 +114,8 @@ cd Claper docker compose up ``` - -### Using Docker Compose for Dev - -To easy check new features, it is possible to directly build the Docker image from the source code and run the container with the [docker-compose-dev.yml](https://github.com/ClaperCo/Claper/blob/main/docker-compose-dev.yml) file. - -```sh -git clone https://github.com/ClaperCo/Claper.git -cd Claper -docker compose -f docker-compose-dev.yml up -``` - - + ## Contributing Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**. @@ -142,23 +129,23 @@ Don't forget to give the project a star! Thanks again! 4. Push to the Branch (`git push origin feature/amazing_feature`) 5. Open a Pull Request - + ## License Distributed under the GPLv3 License. See `LICENSE.txt` for more information. + ## Contact [](https://x.com/alxlion_) Project Link: [https://github.com/ClaperCo/Claper](https://github.com/ClaperCo/Claper) - - + [contributors-shield]: https://img.shields.io/github/contributors/ClaperCo/Claper.svg?style=for-the-badge [contributors-url]: https://github.com/ClaperCo/Claper/graphs/contributors [forks-shield]: https://img.shields.io/github/forks/ClaperCo/Claper.svg?style=for-the-badge diff --git a/assets/css/app.css b/assets/css/app.css index 565b018..88a6251 100644 --- a/assets/css/app.css +++ b/assets/css/app.css @@ -1,4 +1,4 @@ -@import url("flatpickr/dist/flatpickr.min.css"); +@import url('air-datepicker/air-datepicker.css'); @import url("animate.css/animate.min.css"); @tailwind base; @@ -427,4 +427,21 @@ -ms-transform:rotate(-20deg); -o-transform:rotate(-20deg); transform:rotate(-20deg); +} + +/* Air datepicker */ +.air-datepicker-body--day-name { + @apply text-primary-600; +} + +.air-datepicker-cell.-selected-, .air-datepicker-cell.-selected-.-current- { + @apply bg-primary-500 text-white hover:bg-primary-600; +} + +.air-datepicker-cell.-current- { + @apply text-secondary-500; +} + +.animate__slow_slow { + --animate-duration: 5s; } \ No newline at end of file diff --git a/assets/css/custom.scss b/assets/css/custom.scss index 6fc8cb3..218add2 100644 --- a/assets/css/custom.scss +++ b/assets/css/custom.scss @@ -2,6 +2,8 @@ @import "../node_modules/tiny-slider/src/tiny-slider.scss"; +@import "../node_modules/@sjmc11/tourguidejs/src/scss/tour.scss"; + $particleSize: 20vmin; $animationDuration: 6s; $amount: 20; diff --git a/assets/js/app.js b/assets/js/app.js index c9f68f2..54c6194 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -1,20 +1,3 @@ -// If you want to use Phoenix channels, run `mix help phx.gen.channel` -// to get started and then uncomment the line below. -// import "./user_socket.js" - -// You can include dependencies in two ways. -// -// The simplest option is to put them in assets/vendor and -// import them using relative paths: -// -// import "./vendor/some-package.js" -// -// Alternatively, you can `npm install some-package` and import -// them using a path starting with the package name: -// -// import "some-package" -// - // Include phoenix_html to handle method=PUT/DELETE in forms and buttons. import "phoenix_html" // Establish Phoenix Socket and LiveView configuration. @@ -22,13 +5,18 @@ import {Socket, Presence} from "phoenix" import {LiveSocket} from "phoenix_live_view" import topbar from "../vendor/topbar" import Alpine from 'alpinejs' -import flatpickr from "flatpickr" import moment from "moment-timezone" +import AirDatepicker from 'air-datepicker' +import airdatepickerLocaleEn from 'air-datepicker/locale/en' +import airdatepickerLocaleFr from 'air-datepicker/locale/fr' +import airdatepickerLocaleDe from 'air-datepicker/locale/de' import 'moment/locale/de' import 'moment/locale/fr' import QRCodeStyling from "qr-code-styling" import { Presenter } from "./presenter" import { Manager } from "./manager" +import Split from "split-grid" +import { TourGuideClient } from "@sjmc11/tourguidejs/src/Tour" window.moment = moment window.moment.locale("en") @@ -36,32 +24,125 @@ window.moment.locale(navigator.language.split('-')[0]) window.Alpine = Alpine Alpine.start() +let airdatepickerLocale = { + en: airdatepickerLocaleEn, + fr: airdatepickerLocaleFr, + de: airdatepickerLocaleDe +} let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content") let Hooks = {} +Hooks.EmbeddedBanner = { + mounted() { + if (window !== window.parent) { + this.el.classList.remove("hidden") + } + }, + updated() { + if (window !== window.parent) { + this.el.classList.remove("hidden") + } + } +} + + +Hooks.TourGuide = { + mounted() { + this.tour = new TourGuideClient({ + nextLabel: this.el.dataset.nextLabel, + prevLabel: this.el.dataset.prevLabel, + finishLabel: this.el.dataset.finishLabel, + completeOnFinish: true, + rememberStep: true, + }) + + if (!this.tour.isFinished(this.el.dataset.group)) { + this.tour.start(this.el.dataset.group) + } + + this.tour.onBeforeExit(() => { + this.tour.finishTour(true, this.el.dataset.group) + }) + } +} + +Hooks.Split = { + mounted() { + const type = this.el.dataset.type + const gutter = this.el.dataset.gutter + const columnSlitValue = localStorage.getItem('column-split') || '1fr 10px 1fr' + const rowSlitValue = localStorage.getItem('row-split') || '1fr 10px 1fr' + + if (type === "column") { + this.columnSplit = Split({ + columnGutters: [{ + track: 1, + element: this.el.querySelector(gutter) + }], + onDragEnd: () => { + const currentPosition = this.el.style['grid-template-columns'] + localStorage.setItem('column-split', currentPosition) + }, + }) + this.el.style['grid-template-columns'] = columnSlitValue + } else { + this.rowSplit = Split({ + rowGutters: [{ + track: 1, + element: this.el.querySelector(gutter) + }], + onDragEnd: () => { + const value = this.el.style['grid-template-rows'] + localStorage.setItem('row-split', value) + }, + }) + this.el.style['grid-template-rows'] = rowSlitValue + } + }, + updated() { + if (this.columnSplit) { + const value = localStorage.getItem('column-split') || '1fr 10px 1fr' + this.el.style['grid-template-columns'] = value + } + if (this.rowSplit) { + const value = localStorage.getItem('row-split') || '1fr 10px 1fr' + this.el.style['grid-template-rows'] = value + } + }, + destroyed() { + if (this.columnSplit) { + this.columnSplit.destroy() + } + if (this.rowSplit) { + this.rowSplit.destroy() + } + } +} + Hooks.Scroll = { mounted() { if (this.el.dataset.postsNb > 4) window.scrollTo({top: document.querySelector(this.el.dataset.target).scrollHeight, behavior: 'smooth'}); this.handleEvent("scroll", () => { - let t = document.querySelector(this.el.dataset.target) - if (this.el.childElementCount > 4 && (window.scrollY + window.innerHeight >= t.offsetHeight - 100)) { - window.scrollTo({top: t.scrollHeight, behavior: 'smooth'}); - } }) + }, + updated() { + let t = document.querySelector(this.el.dataset.target) + if (this.el.childElementCount > 4 && (window.scrollY + window.innerHeight >= t.offsetHeight - 300)) { + window.scrollTo({top: t.scrollHeight, behavior: 'smooth'}); + } } } Hooks.ScrollIntoDiv = { mounted() { - let t = document.querySelector(this.el.dataset.target) - if (this.el.dataset.postsNb > 4) t.scrollTo({top: t.scrollHeight, behavior: 'smooth'}); - - this.handleEvent("scroll", () => { - let t = document.querySelector(this.el.dataset.target); - if (this.el.childElementCount > 4 && (t.scrollHeight - t.scrollTop < t.clientHeight + 100)) { - t.scrollTo({top: t.scrollHeight, behavior: 'smooth'}); - } - }) + this.scrollElement(true); + this.handleEvent("scroll", this.scrollElement.bind(this)); + }, + scrollElement(firstScroll) { + let t = this.el.parentElement; + if (firstScroll === true || (t.scrollHeight - t.scrollTop - t.clientHeight) <= 100) { + t.scrollTo({top: t.scrollHeight, behavior: 'smooth'}) + } } } @@ -74,7 +155,7 @@ Hooks.NicknamePicker = { this.el.addEventListener("click", (e) => this.clicked(e)) }, - destroy() { + destroyed() { this.el.removeEventListener("click", (e) => this.clicked(e)) }, clicked(e) { @@ -91,7 +172,7 @@ Hooks.EmptyNickname = { mounted() { this.el.addEventListener("click", (e) => this.clicked(e)) }, - destroy() { + destroyed() { this.el.removeEventListener("click", (e) => this.clicked(e)) }, clicked(e) { @@ -170,33 +251,21 @@ Hooks.CalendarLocalDate = { } Hooks.Pickr = { mounted() { - const getDefaultDate = (dateStart, dateEnd, mode) => { - if (mode == "range") { - return moment.utc(dateStart).format('Y-MM-DD HH:mm') + " - " + moment.utc(dateEnd).format('Y-MM-DD HH:mm') - } else { - return moment.utc(dateStart).format('Y-MM-DD HH:mm') - } - }; - this.pickr = flatpickr(this.el, { - wrap: true, - inline: false, - enableTime: true, - enable: JSON.parse(this.el.dataset.enable), - time_24hr: true, - formatDate: (date, format, locale) => { - return moment(date).utc().format('Y-MM-DD HH:mm'); + const localTime = this.el.querySelector("input[type=text]") + const utcTime = this.el.querySelector("input[type=hidden]") + localTime.value = moment.utc(utcTime.value).local().format("DD-MM-YYYY HH:mm") + this.pickr = new AirDatepicker(localTime, { + dateFormat: "dd-MM-yyyy", + timepicker: true, + minutesStep: 5, + minDate: moment(), + timeFormat: "HH:mm", + selectedDates: [moment(localTime.value, "DD-MM-YYYY HH:mm").toDate()], + onSelect: ({date}) => { + const utc = moment(date).utc().format("YYYY-MM-DDTHH:mm:ss") + utcTime.value = utc }, - parseDate: (datestr, format) => { - return moment.utc(datestr).local().toDate(); - }, - locale: { - firstDayOfWeek: 1, - rangeSeparator: ' - ' - }, - mode: this.el.dataset.mode == "range" ? "range" : "single", - minuteIncrement: 1, - dateFormat: "Y-m-d H:i", - defaultDate: getDefaultDate(this.el.dataset.defaultDateStart, this.el.dataset.defaultDateEnd, this.el.dataset.mode) + locale: airdatepickerLocale[navigator.language.split('-')[0]] }) }, updated() { @@ -298,11 +367,6 @@ Hooks.WelcomeEarly = { }) } } -Hooks.DefaultValue = { - mounted() { - this.el.value = moment(this.el.dataset.defaultValue ? this.el.dataset.defaultValue : undefined).utc().format(); - } -} Hooks.ClickFeedback = { clicked(e) { this.el.className = "animate__animated animate__rubberBand animate__faster"; @@ -313,7 +377,7 @@ Hooks.ClickFeedback = { mounted() { this.el.addEventListener("click", (e) => this.clicked(e)) }, - destroy() { + destroyed() { this.el.removeEventListener("click", (e) => this.clicked(e)) } } @@ -370,6 +434,15 @@ Hooks.QRCode = { } } +Hooks.Dropdown = { + mounted() { + this.el.addEventListener("click", (e) => { + e.preventDefault() + this.el.classList.toggle("hidden") + }) + } +} + let Uploaders = {} Uploaders.S3 = function(entries, onViewError){ diff --git a/assets/js/manager.js b/assets/js/manager.js index 158255c..b138312 100644 --- a/assets/js/manager.js +++ b/assets/js/manager.js @@ -15,8 +15,9 @@ export class Manager { if (el) { setTimeout(() => { - document.getElementById("slide-preview-" + data.current_page).scrollIntoView({ - block: 'center', + document.getElementById("slides").scrollTo({ + top: el.offsetTop - el.scrollHeight, + left: 0, behavior: 'smooth' }); }, data.timeout ? data.timeout : 0) @@ -51,10 +52,13 @@ export class Manager { var el = document.getElementById("slide-preview-" + this.currentPage) if (el) { - document.getElementById("slide-preview-" + this.currentPage).scrollIntoView({ - block: 'center', - behavior: 'smooth' - }); + setTimeout(() => { + document.getElementById("slides").scrollTo({ + top: el.offsetTop - el.scrollHeight, + left: 0, + behavior: 'smooth' + }); + }, 50) } } diff --git a/assets/package-lock.json b/assets/package-lock.json index 74357c5..54969b9 100644 --- a/assets/package-lock.json +++ b/assets/package-lock.json @@ -1,6 +1,1683 @@ { + "name": "assets", + "lockfileVersion": 2, "requires": true, - "lockfileVersion": 1, + "packages": { + "": { + "dependencies": { + "@sjmc11/tourguidejs": "^0.0.16", + "air-datepicker": "^3.5.0", + "animate.css": "^4.1.1", + "moment": "^2.29.4", + "moment-timezone": "^0.5.43", + "phoenix": "file:../deps/phoenix", + "phoenix_html": "file:../deps/phoenix_html", + "phoenix_live_view": "file:../deps/phoenix_live_view", + "qr-code-styling": "^1.6.0-rc.1", + "split-grid": "^1.0.11", + "split.js": "^1.6.5", + "tiny-slider": "^2.9.4" + }, + "devDependencies": { + "alpinejs": "^3.13.1", + "autoprefixer": "^10.4.15", + "esbuild": "^0.14.54", + "postcss": "^8.4.29", + "postcss-import": "^15.1.0", + "tailwindcss": "^3.3.3" + } + }, + "../deps/phoenix": { + "version": "1.7.11", + "license": "MIT" + }, + "../deps/phoenix_html": { + "version": "4.1.0" + }, + "../deps/phoenix_live_view": { + "version": "0.20.14", + "license": "MIT", + "devDependencies": { + "@playwright/test": "^1.41.0" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.14.54.tgz", + "integrity": "sha512-bZBrLAIX1kpWelV0XemxBZllyRmM6vgFQQG2GdNb+r3Fkp0FOh1NJSvekXDs7jq70k4euu1cryLMfU+mTXlEpw==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@floating-ui/core": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.0.tgz", + "integrity": "sha512-PcF++MykgmTj3CIyOQbKA/hDzOAiqI3mhuoN44WRCopIs1sgoDoU4oty4Jtqaj/y3oDU6fnVSm4QG0a3t5i0+g==", + "dependencies": { + "@floating-ui/utils": "^0.2.1" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.3.tgz", + "integrity": "sha512-RnDthu3mzPlQ31Ss/BTwQ1zjzIhr3lk1gZB1OC56h/1vEtaXkESrOqL5fQVMfXpwGtRwX+YsZBdyHtJMQnkArw==", + "dependencies": { + "@floating-ui/core": "^1.0.0", + "@floating-ui/utils": "^0.2.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.1.tgz", + "integrity": "sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==" + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", + "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@sjmc11/tourguidejs": { + "version": "0.0.16", + "resolved": "https://registry.npmjs.org/@sjmc11/tourguidejs/-/tourguidejs-0.0.16.tgz", + "integrity": "sha512-NpK7OyW1l0R1XCajbWp+ibJAJbQK7dpBfqy+Etcy4fj/+qk3JPKY8RwoSX1UzzfBauuriNKJiv1KmTB3RHajQg==", + "dependencies": { + "@floating-ui/dom": "^1.0.3", + "sass": "^1.55.0" + } + }, + "node_modules/@vue/reactivity": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.1.5.tgz", + "integrity": "sha512-1tdfLmNjWG6t/CsPldh+foumYFo3cpyCHgBYQ34ylaMsJ+SNHQ1kApMIa8jN+i593zQuaw3AdWH0nJTARzCFhg==", + "dev": true, + "dependencies": { + "@vue/shared": "3.1.5" + } + }, + "node_modules/@vue/shared": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.1.5.tgz", + "integrity": "sha512-oJ4F3TnvpXaQwZJNF3ZK+kLPHKarDmJjJ6jyzVNDKH9md1dptjC7lWR//jrGuLdek/U6iltWxqAnYOu8gCiOvA==", + "dev": true + }, + "node_modules/air-datepicker": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/air-datepicker/-/air-datepicker-3.5.0.tgz", + "integrity": "sha512-WOpn1MaSl5drcXSwkXg5Gh/jXX/VFfamNnIb8E43AY4UKuW/bNEW06e3GGsiWLDBQLabD22L6b6cP7KHnAy54w==" + }, + "node_modules/alpinejs": { + "version": "3.13.2", + "resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.13.2.tgz", + "integrity": "sha512-WzojeeN082kLZznGI1HAuP8yFJSWqJ1fGdz2mUjj45H4y0XwToE7fFqtI3mCPRR+BpcSbxT/NL+FyPnYAWSltw==", + "dev": true, + "dependencies": { + "@vue/reactivity": "~3.1.1" + } + }, + "node_modules/animate.css": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/animate.css/-/animate.css-4.1.1.tgz", + "integrity": "sha512-+mRmCTv6SbCmtYJCN4faJMNFVNN5EuCTTprDTAo7YzIGji2KADmakjVA3+8mVDkZ2Bf09vayB35lSQIex2+QaQ==" + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true + }, + "node_modules/autoprefixer": { + "version": "10.4.16", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.16.tgz", + "integrity": "sha512-7vd3UC6xKp0HLfua5IjZlcXvGAGy7cBAXTg2lyQ/8WpNhd6SiZ8Be+xm3FyBSYJx5GKcpRCzBh7RH4/0dnY+uQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "browserslist": "^4.21.10", + "caniuse-lite": "^1.0.30001538", + "fraction.js": "^4.3.6", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.0", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.22.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz", + "integrity": "sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001541", + "electron-to-chromium": "^1.4.535", + "node-releases": "^2.0.13", + "update-browserslist-db": "^1.0.13" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001551", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001551.tgz", + "integrity": "sha512-vtBAez47BoGMMzlbYhfXrMV1kvRF2WP/lqiMuDu1Sb4EE4LKEgjopFDSRtZfdVnslNRpOqV/woE+Xgrwj6VQlg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true + }, + "node_modules/electron-to-chromium": { + "version": "1.4.563", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.563.tgz", + "integrity": "sha512-dg5gj5qOgfZNkPNeyKBZQAQitIQ/xwfIDmEQJHCbXaD9ebTZxwJXUsDYcBlAvZGZLi+/354l35J1wkmP6CqYaw==", + "dev": true + }, + "node_modules/esbuild": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.54.tgz", + "integrity": "sha512-Cy9llcy8DvET5uznocPyqL3BFRrFXSVqbgpMJ9Wz8oVjZlh/zUSNbPRbov0VX7VxN2JH1Oa0uNxZ7eLRb62pJA==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/linux-loong64": "0.14.54", + "esbuild-android-64": "0.14.54", + "esbuild-android-arm64": "0.14.54", + "esbuild-darwin-64": "0.14.54", + "esbuild-darwin-arm64": "0.14.54", + "esbuild-freebsd-64": "0.14.54", + "esbuild-freebsd-arm64": "0.14.54", + "esbuild-linux-32": "0.14.54", + "esbuild-linux-64": "0.14.54", + "esbuild-linux-arm": "0.14.54", + "esbuild-linux-arm64": "0.14.54", + "esbuild-linux-mips64le": "0.14.54", + "esbuild-linux-ppc64le": "0.14.54", + "esbuild-linux-riscv64": "0.14.54", + "esbuild-linux-s390x": "0.14.54", + "esbuild-netbsd-64": "0.14.54", + "esbuild-openbsd-64": "0.14.54", + "esbuild-sunos-64": "0.14.54", + "esbuild-windows-32": "0.14.54", + "esbuild-windows-64": "0.14.54", + "esbuild-windows-arm64": "0.14.54" + } + }, + "node_modules/esbuild-android-64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.54.tgz", + "integrity": "sha512-Tz2++Aqqz0rJ7kYBfz+iqyE3QMycD4vk7LBRyWaAVFgFtQ/O8EJOnVmTOiDWYZ/uYzB4kvP+bqejYdVKzE5lAQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-android-arm64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.54.tgz", + "integrity": "sha512-F9E+/QDi9sSkLaClO8SOV6etqPd+5DgJje1F9lOWoNncDdOBL2YF59IhsWATSt0TLZbYCf3pNlTHvVV5VfHdvg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-darwin-64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.54.tgz", + "integrity": "sha512-jtdKWV3nBviOd5v4hOpkVmpxsBy90CGzebpbO9beiqUYVMBtSc0AL9zGftFuBon7PNDcdvNCEuQqw2x0wP9yug==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-darwin-arm64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.54.tgz", + "integrity": "sha512-OPafJHD2oUPyvJMrsCvDGkRrVCar5aVyHfWGQzY1dWnzErjrDuSETxwA2HSsyg2jORLY8yBfzc1MIpUkXlctmw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-freebsd-64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.54.tgz", + "integrity": "sha512-OKwd4gmwHqOTp4mOGZKe/XUlbDJ4Q9TjX0hMPIDBUWWu/kwhBAudJdBoxnjNf9ocIB6GN6CPowYpR/hRCbSYAg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-freebsd-arm64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.54.tgz", + "integrity": "sha512-sFwueGr7OvIFiQT6WeG0jRLjkjdqWWSrfbVwZp8iMP+8UHEHRBvlaxL6IuKNDwAozNUmbb8nIMXa7oAOARGs1Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-32": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.54.tgz", + "integrity": "sha512-1ZuY+JDI//WmklKlBgJnglpUL1owm2OX+8E1syCD6UAxcMM/XoWd76OHSjl/0MR0LisSAXDqgjT3uJqT67O3qw==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.54.tgz", + "integrity": "sha512-EgjAgH5HwTbtNsTqQOXWApBaPVdDn7XcK+/PtJwZLT1UmpLoznPd8c5CxqsH2dQK3j05YsB3L17T8vE7cp4cCg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-arm": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.54.tgz", + "integrity": "sha512-qqz/SjemQhVMTnvcLGoLOdFpCYbz4v4fUo+TfsWG+1aOu70/80RV6bgNpR2JCrppV2moUQkww+6bWxXRL9YMGw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-arm64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.54.tgz", + "integrity": "sha512-WL71L+0Rwv+Gv/HTmxTEmpv0UgmxYa5ftZILVi2QmZBgX3q7+tDeOQNqGtdXSdsL8TQi1vIaVFHUPDe0O0kdig==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-mips64le": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.54.tgz", + "integrity": "sha512-qTHGQB8D1etd0u1+sB6p0ikLKRVuCWhYQhAHRPkO+OF3I/iSlTKNNS0Lh2Oc0g0UFGguaFZZiPJdJey3AGpAlw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-ppc64le": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.54.tgz", + "integrity": "sha512-j3OMlzHiqwZBDPRCDFKcx595XVfOfOnv68Ax3U4UKZ3MTYQB5Yz3X1mn5GnodEVYzhtZgxEBidLWeIs8FDSfrQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-riscv64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.54.tgz", + "integrity": "sha512-y7Vt7Wl9dkOGZjxQZnDAqqn+XOqFD7IMWiewY5SPlNlzMX39ocPQlOaoxvT4FllA5viyV26/QzHtvTjVNOxHZg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-s390x": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.54.tgz", + "integrity": "sha512-zaHpW9dziAsi7lRcyV4r8dhfG1qBidQWUXweUjnw+lliChJqQr+6XD71K41oEIC3Mx1KStovEmlzm+MkGZHnHA==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-netbsd-64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.54.tgz", + "integrity": "sha512-PR01lmIMnfJTgeU9VJTDY9ZerDWVFIUzAtJuDHwwceppW7cQWjBBqP48NdeRtoP04/AtO9a7w3viI+PIDr6d+w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-openbsd-64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.54.tgz", + "integrity": "sha512-Qyk7ikT2o7Wu76UsvvDS5q0amJvmRzDyVlL0qf5VLsLchjCa1+IAvd8kTBgUxD7VBUUVgItLkk609ZHUc1oCaw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-sunos-64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.54.tgz", + "integrity": "sha512-28GZ24KmMSeKi5ueWzMcco6EBHStL3B6ubM7M51RmPwXQGLe0teBGJocmWhgwccA1GeFXqxzILIxXpHbl9Q/Kw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-windows-32": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.54.tgz", + "integrity": "sha512-T+rdZW19ql9MjS7pixmZYVObd9G7kcaZo+sETqNH4RCkuuYSuv9AGHUVnPoP9hhuE1WM1ZimHz1CIBHBboLU7w==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-windows-64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.54.tgz", + "integrity": "sha512-AoHTRBUuYwXtZhjXZbA1pGfTo8cJo3vZIcWGLiUcTNgHpJJMC1rVA44ZereBHMJtotyN71S8Qw0npiCIkW96cQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-windows-arm64": { + "version": "0.14.54", + "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.54.tgz", + "integrity": "sha512-M0kuUvXhot1zOISQGXwWn6YtS+Y/1RT9WrVIOywZnJHo3jCDyewAc79aKNQWFCQm+xNHVTq9h8dZKvygoXQQRg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/fast-glob": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", + "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "dev": true, + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8= sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/immutable": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.5.tgz", + "integrity": "sha512-8eabxkth9gZatlwl5TBuJnCsoTADlL6ftEr7A4qgdaTsPyreilDSnUk57SO+jfKcNtxPa22U5KK6DSeAYhpBJw==" + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/jiti": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.20.0.tgz", + "integrity": "sha512-3TV69ZbrvV6U5DfQimop50jE9Dl6J8O1ja1dvBbMba/sZ3YBEQqJ2VZRoQPVnhlzjNtU1vaXRZVrVjU4qtm8yA==", + "dev": true, + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/moment": { + "version": "2.29.4", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", + "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", + "engines": { + "node": "*" + } + }, + "node_modules/moment-timezone": { + "version": "0.5.43", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.43.tgz", + "integrity": "sha512-72j3aNyuIsDxdF1i7CEgV2FfxM1r6aaqJyLB2vwb33mXYyoyLly+F1zbWqhA3/bVIoJ4szlUoMbUnVdid32NUQ==", + "dependencies": { + "moment": "^2.29.4" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", + "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-releases": { + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", + "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", + "dev": true + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI= sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E= sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18= sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/phoenix": { + "resolved": "../deps/phoenix", + "link": true + }, + "node_modules/phoenix_html": { + "resolved": "../deps/phoenix_html", + "link": true + }, + "node_modules/phoenix_live_view": { + "resolved": "../deps/phoenix_live_view", + "link": true + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw= sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", + "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "dev": true, + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.1.tgz", + "integrity": "sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==", + "dev": true, + "dependencies": { + "lilconfig": "^2.0.5", + "yaml": "^2.1.1" + }, + "engines": { + "node": ">= 14" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/postcss-nested": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz", + "integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.11" + }, + "engines": { + "node": ">=12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.0.13", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz", + "integrity": "sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "node_modules/qr-code-styling": { + "version": "1.6.0-rc.1", + "resolved": "https://registry.npmjs.org/qr-code-styling/-/qr-code-styling-1.6.0-rc.1.tgz", + "integrity": "sha512-ModRIiW6oUnsP18QzrRYZSc/CFKFKIdj7pUs57AEVH20ajlglRpN3HukjHk0UbNMTlKGuaYl7Gt6/O5Gg2NU2Q==", + "dependencies": { + "qrcode-generator": "^1.4.3" + } + }, + "node_modules/qrcode-generator": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/qrcode-generator/-/qrcode-generator-1.4.4.tgz", + "integrity": "sha512-HM7yY8O2ilqhmULxGMpcHSF1EhJJ9yBj8gvDEuZ6M+KGJ0YY2hKpnXvRD+hZPLrDVck3ExIGhmPtSdcjC+guuw==" + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha1-5mTvMRYRZsl1HNvo28+GtftY93Q= sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/sass": { + "version": "1.72.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.72.0.tgz", + "integrity": "sha512-Gpczt3WA56Ly0Mn8Sl21Vj94s1axi9hDIzDFn9Ph9x3C3p4nNyvsqJoQyVXKou6cBlfFWEgRW4rT8Tb4i3XnVA==", + "dependencies": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/split-grid": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/split-grid/-/split-grid-1.0.11.tgz", + "integrity": "sha512-ELtFtxc3r5we5GZfe6Fi0BFFxIi2M6BY1YEntBscKRDD3zx4JVHqx2VnTRSQu1BixCYSTH3MTjKd4esI2R7EgQ==" + }, + "node_modules/split.js": { + "version": "1.6.5", + "resolved": "https://registry.npmjs.org/split.js/-/split.js-1.6.5.tgz", + "integrity": "sha512-mPTnGCiS/RiuTNsVhCm9De9cCAUsrNFFviRbADdKiiV+Kk8HKp/0fWu7Kr8pi3/yBmsqLFHuXGT9UUZ+CNLwFw==" + }, + "node_modules/sucrase": { + "version": "3.34.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.34.0.tgz", + "integrity": "sha512-70/LQEZ07TEcxiU2dz51FKaE6hCTWC6vr7FOk3Gr0U60C3shtAN+H+BFr9XlYe5xqf3RA8nrc+VIwzCfnxuXJw==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "7.1.6", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/sucrase/node_modules/glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tailwindcss": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.3.tgz", + "integrity": "sha512-A0KgSkef7eE4Mf+nKJ83i75TMyq8HqY3qmFIJSWy8bNt0v1lG7jUcpGpoTFxAwYcWOphcTBLPPJg+bDfhDf52w==", + "dev": true, + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.5.3", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.2.12", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.18.2", + "lilconfig": "^2.1.0", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.4.23", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.1", + "postcss-nested": "^6.0.1", + "postcss-selector-parser": "^6.0.11", + "resolve": "^1.22.2", + "sucrase": "^3.32.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tailwindcss/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/tailwindcss/node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tiny-slider": { + "version": "2.9.4", + "resolved": "https://registry.npmjs.org/tiny-slider/-/tiny-slider-2.9.4.tgz", + "integrity": "sha512-LAs2kldWcY+BqCKw4kxd4CMx2RhWrHyEePEsymlOIISTlOVkjfK40sSD7ay73eKXBLg/UkluAZpcfCstimHXew==" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true + }, + "node_modules/update-browserslist-db": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/yaml": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.3.tgz", + "integrity": "sha512-zw0VAJxgeZ6+++/su5AFoqBbZbrEakwu+X0M5HmcwUiBL7AzcuPKjj5we4xfQLp78LkEMpD0cOnUhmgOVy3KdQ==", + "dev": true, + "engines": { + "node": ">= 14" + } + } + }, "dependencies": { "@alloc/quick-lru": { "version": "5.2.0", @@ -15,6 +1692,28 @@ "dev": true, "optional": true }, + "@floating-ui/core": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.0.tgz", + "integrity": "sha512-PcF++MykgmTj3CIyOQbKA/hDzOAiqI3mhuoN44WRCopIs1sgoDoU4oty4Jtqaj/y3oDU6fnVSm4QG0a3t5i0+g==", + "requires": { + "@floating-ui/utils": "^0.2.1" + } + }, + "@floating-ui/dom": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.3.tgz", + "integrity": "sha512-RnDthu3mzPlQ31Ss/BTwQ1zjzIhr3lk1gZB1OC56h/1vEtaXkESrOqL5fQVMfXpwGtRwX+YsZBdyHtJMQnkArw==", + "requires": { + "@floating-ui/core": "^1.0.0", + "@floating-ui/utils": "^0.2.0" + } + }, + "@floating-ui/utils": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.1.tgz", + "integrity": "sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==" + }, "@jridgewell/gen-mapping": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", @@ -80,6 +1779,15 @@ "fastq": "^1.6.0" } }, + "@sjmc11/tourguidejs": { + "version": "0.0.16", + "resolved": "https://registry.npmjs.org/@sjmc11/tourguidejs/-/tourguidejs-0.0.16.tgz", + "integrity": "sha512-NpK7OyW1l0R1XCajbWp+ibJAJbQK7dpBfqy+Etcy4fj/+qk3JPKY8RwoSX1UzzfBauuriNKJiv1KmTB3RHajQg==", + "requires": { + "@floating-ui/dom": "^1.0.3", + "sass": "^1.55.0" + } + }, "@vue/reactivity": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.1.5.tgz", @@ -95,6 +1803,11 @@ "integrity": "sha512-oJ4F3TnvpXaQwZJNF3ZK+kLPHKarDmJjJ6jyzVNDKH9md1dptjC7lWR//jrGuLdek/U6iltWxqAnYOu8gCiOvA==", "dev": true }, + "air-datepicker": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/air-datepicker/-/air-datepicker-3.5.0.tgz", + "integrity": "sha512-WOpn1MaSl5drcXSwkXg5Gh/jXX/VFfamNnIb8E43AY4UKuW/bNEW06e3GGsiWLDBQLabD22L6b6cP7KHnAy54w==" + }, "alpinejs": { "version": "3.13.2", "resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.13.2.tgz", @@ -115,6 +1828,15 @@ "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", "dev": true }, + "anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, "arg": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", @@ -141,6 +1863,11 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==" + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -151,6 +1878,14 @@ "concat-map": "0.0.1" } }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "requires": { + "fill-range": "^7.0.1" + } + }, "browserslist": { "version": "4.22.1", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz", @@ -175,6 +1910,21 @@ "integrity": "sha512-vtBAez47BoGMMzlbYhfXrMV1kvRF2WP/lqiMuDu1Sb4EE4LKEgjopFDSRtZfdVnslNRpOqV/woE+Xgrwj6VQlg==", "dev": true }, + "chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + } + }, "commander": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", @@ -399,48 +2149,6 @@ "micromatch": "^4.0.4" }, "dependencies": { - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true - }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, "micromatch": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", @@ -462,11 +2170,13 @@ "reusify": "^1.0.4" } }, - "flatpickr": { - "version": "4.6.13", - "resolved": "https://registry.npmjs.org/flatpickr/-/flatpickr-4.6.13.tgz", - "integrity": "sha512-97PMG/aywoYpB4IvbvUJi0RQi8vearvU0oov1WW3k0WZPBMrTQVqekSX5CjSG/M4Q3i6A/0FKXC7RyAoAUUSPw==", - "dev": true + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "requires": { + "to-regex-range": "^5.0.1" + } }, "fraction.js": { "version": "4.3.7", @@ -480,12 +2190,26 @@ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8= sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, + "fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "optional": true + }, "function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "dev": true }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "requires": { + "is-glob": "^4.0.1" + } + }, "hasown": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", @@ -495,6 +2219,11 @@ "function-bind": "^1.1.2" } }, + "immutable": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.5.tgz", + "integrity": "sha512-8eabxkth9gZatlwl5TBuJnCsoTADlL6ftEr7A4qgdaTsPyreilDSnUk57SO+jfKcNtxPa22U5KK6DSeAYhpBJw==" + }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -511,6 +2240,14 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "requires": { + "binary-extensions": "^2.0.0" + } + }, "is-core-module": { "version": "2.13.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", @@ -520,6 +2257,24 @@ "hasown": "^2.0.0" } }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" + }, "jiti": { "version": "1.20.0", "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.20.0.tgz", @@ -589,6 +2344,11 @@ "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", "dev": true }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + }, "normalize-range": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", @@ -635,7 +2395,10 @@ "version": "file:../deps/phoenix_html" }, "phoenix_live_view": { - "version": "file:../deps/phoenix_live_view" + "version": "file:../deps/phoenix_live_view", + "requires": { + "@playwright/test": "^1.41.0" + } }, "picocolors": { "version": "1.0.0", @@ -646,8 +2409,7 @@ "picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" }, "pify": { "version": "2.3.0", @@ -755,6 +2517,14 @@ "pify": "^2.3.0" } }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "requires": { + "picomatch": "^2.2.1" + } + }, "resolve": { "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", @@ -781,11 +2551,30 @@ "queue-microtask": "^1.2.2" } }, + "sass": { + "version": "1.72.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.72.0.tgz", + "integrity": "sha512-Gpczt3WA56Ly0Mn8Sl21Vj94s1axi9hDIzDFn9Ph9x3C3p4nNyvsqJoQyVXKou6cBlfFWEgRW4rT8Tb4i3XnVA==", + "requires": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + } + }, "source-map-js": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", - "dev": true + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==" + }, + "split-grid": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/split-grid/-/split-grid-1.0.11.tgz", + "integrity": "sha512-ELtFtxc3r5we5GZfe6Fi0BFFxIi2M6BY1YEntBscKRDD3zx4JVHqx2VnTRSQu1BixCYSTH3MTjKd4esI2R7EgQ==" + }, + "split.js": { + "version": "1.6.5", + "resolved": "https://registry.npmjs.org/split.js/-/split.js-1.6.5.tgz", + "integrity": "sha512-mPTnGCiS/RiuTNsVhCm9De9cCAUsrNFFviRbADdKiiV+Kk8HKp/0fWu7Kr8pi3/yBmsqLFHuXGT9UUZ+CNLwFw==" }, "sucrase": { "version": "3.34.0", @@ -854,74 +2643,6 @@ "sucrase": "^3.32.0" }, "dependencies": { - "anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, - "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "dependencies": { - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - } - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "optional": true - }, "glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -931,30 +2652,6 @@ "is-glob": "^4.0.3" } }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true - }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, "micromatch": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", @@ -964,21 +2661,6 @@ "braces": "^3.0.2", "picomatch": "^2.3.1" } - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "requires": { - "picomatch": "^2.2.1" - } } } }, @@ -1009,17 +2691,8 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, "requires": { "is-number": "^7.0.0" - }, - "dependencies": { - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - } } }, "ts-interface-checker": { diff --git a/assets/package.json b/assets/package.json index 2f4c4a2..17a5616 100644 --- a/assets/package.json +++ b/assets/package.json @@ -6,12 +6,13 @@ "alpinejs": "^3.13.1", "autoprefixer": "^10.4.15", "esbuild": "^0.14.54", - "flatpickr": "^4.6.13", "postcss": "^8.4.29", "postcss-import": "^15.1.0", "tailwindcss": "^3.3.3" }, "dependencies": { + "@sjmc11/tourguidejs": "^0.0.16", + "air-datepicker": "^3.5.0", "animate.css": "^4.1.1", "moment": "^2.29.4", "moment-timezone": "^0.5.43", @@ -19,6 +20,8 @@ "phoenix_html": "file:../deps/phoenix_html", "phoenix_live_view": "file:../deps/phoenix_live_view", "qr-code-styling": "^1.6.0-rc.1", + "split-grid": "^1.0.11", + "split.js": "^1.6.5", "tiny-slider": "^2.9.4" } } diff --git a/config/runtime.exs b/config/runtime.exs index cfcdc70..156f480 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -30,15 +30,17 @@ port = get_int_from_path_or_env(config_dir, "PORT", "4000") secret_key_base = get_var_from_path_or_env(config_dir, "SECRET_KEY_BASE", nil) -case secret_key_base do - nil -> - raise "SECRET_KEY_BASE configuration option is required. See https://docs.claper.co/configuration.html#production-docker" +if Mix.env() == :prod do + case secret_key_base do + nil -> + raise "SECRET_KEY_BASE configuration option is required. See https://docs.claper.co/configuration.html#production-docker" - key when byte_size(key) < 32 -> - raise "SECRET_KEY_BASE must be at least 32 bytes long. See https://docs.claper.co/configuration.html#production-docker" + key when byte_size(key) < 32 -> + raise "SECRET_KEY_BASE must be at least 32 bytes long. See https://docs.claper.co/configuration.html#production-docker" - _ -> - nil + _ -> + nil + end end endpoint_host = get_var_from_path_or_env(config_dir, "ENDPOINT_HOST", "localhost") diff --git a/config/test.exs b/config/test.exs index 0a47223..a3d9540 100644 --- a/config/test.exs +++ b/config/test.exs @@ -11,7 +11,7 @@ config :claper, Claper.Repo, database: "claper_test#{System.get_env("MIX_TEST_PARTITION")}", hostname: "localhost", pool: Ecto.Adapters.SQL.Sandbox, - pool_size: 10 + pool_size: 1 # We don't run a server during test. If one is required, # you can enable the server option below. @@ -24,7 +24,7 @@ config :claper, ClaperWeb.Endpoint, config :claper, Claper.Mailer, adapter: Swoosh.Adapters.Test # Print only warnings and errors during test -config :logger, level: :warn +config :logger, level: :warning # Initialize plugs at runtime for faster test compilation config :phoenix, :plug_init_mode, :runtime diff --git a/docker-compose-dev.yml b/docker-compose-dev.yml deleted file mode 100644 index 2e41cb4..0000000 --- a/docker-compose-dev.yml +++ /dev/null @@ -1,38 +0,0 @@ -version: "3.0" -services: - db: - image: postgres:9 - ports: - - 5432:5432 - volumes: - - postgres-data:/var/lib/postgresql/data - environment: - POSTGRES_PASSWORD: claper - POSTGRES_USER: claper - POSTGRES_DB: claper - healthcheck: - test: ["CMD-SHELL", "pg_isready -U claper"] - interval: 5s - timeout: 5s - retries: 10 - app: - build: . - user: 0:0 - ports: - - 4000:4000 - volumes: - - uploads:/app/uploads - environment: - DATABASE_URL: postgres://claper:claper@db:5432/claper - SECRET_KEY_BASE: 0LZiQBLw4WvqPlz4cz8RsHJlxNiSqM9B48y4ChyJ5v1oA0L/TPIqRjQNdPZN3iEG - MAIL_TRANSPORT: local - ENDPOINT_PORT: 4000 - PRESENTATION_STORAGE: local - MAX_FILE_SIZE_MB: 15 - ENABLE_ACCOUNT_CREATION: true - depends_on: - db: - condition: service_healthy -volumes: - postgres-data: - uploads: diff --git a/docker-compose.yml b/docker-compose.yml index 721e398..8a7f63b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,37 +1,50 @@ -version: "3.0" services: db: - image: postgres:9 - ports: + image: postgres:15 + ports: - 5432:5432 volumes: - - ./postgres-data:/var/lib/postgresql/data + - "claper-db:/var/lib/postgresql/data" + healthcheck: + test: + - CMD + - pg_isready + - "-q" + - "-d" + - "claper" + - "-U" + - "claper" + retries: 3 + timeout: 5s environment: POSTGRES_PASSWORD: claper POSTGRES_USER: claper POSTGRES_DB: claper - healthcheck: - test: ["CMD-SHELL", "pg_isready -U claper"] - interval: 5s - timeout: 5s - retries: 10 + networks: + - claper-net app: - image: ghcr.io/claperco/claper:latest + image: ghcr.io/claperco/claper:latest # or build: . user: 0:0 - ports: + ports: - 4000:4000 volumes: - - uploads:/app/uploads - environment: - DATABASE_URL: postgres://claper:claper@db:5432/claper - SECRET_KEY_BASE: 0LZiQBLw4WvqPlz4cz8RsHJlxNiSqM9B48y4ChyJ5v1oA0L/TPIqRjQNdPZN3iEG - MAIL_TRANSPORT: local - ENDPOINT_PORT: 4000 - PRESENTATION_STORAGE: local - MAX_FILE_SIZE_MB: 15 - ENABLE_ACCOUNT_CREATION: true + - "claper-uploads:/app/uploads" + healthcheck: + test: curl --fail http://localhost:4000 || exit 1 + retries: 3 + start_period: 20s + timeout: 5s + env_file: .env depends_on: db: condition: service_healthy + volumes: - uploads: \ No newline at end of file + claper-db: + driver: local + claper-uploads: + driver: local + +networks: + claper-net: + driver: bridge diff --git a/guides/introduction/deployment.md b/guides/introduction/deployment.md index 6a4926b..9678533 100644 --- a/guides/introduction/deployment.md +++ b/guides/introduction/deployment.md @@ -3,14 +3,14 @@ ## Prerequisites To run Claper on your production environment you need to have: -* Postgres >= 9 -* Elixir >= 1.13.2 -* Erlang >= 24 -* NPM >= 6.14.17 -* NodeJS >= 14.19.2 -* Ghostscript >= 9.5.0 (for PDF support) -* Libreoffice >= 6.4 (for PPT/PPTX support) +- Postgres >= 9 +- Elixir >= 1.13.2 +- Erlang >= 24 +- NPM >= 6.14.17 +- NodeJS >= 14.19.2 +- Ghostscript >= 9.5.0 (for PDF support) +- Libreoffice >= 6.4 (for PPT/PPTX support) ## Steps (without docker) @@ -71,43 +71,83 @@ server { Here is a docker-compose example to run Claper behind Traefik. ```yaml -version: "3.0" services: db: - image: postgres:9 + image: postgres:15 + ports: + - 5432:5432 + volumes: + - "claper-db:/var/lib/postgresql/data" + healthcheck: + test: + - CMD + - pg_isready + - "-q" + - "-d" + - "claper" + - "-U" + - "claper" + retries: 3 + timeout: 5s environment: POSTGRES_PASSWORD: claper POSTGRES_USER: claper POSTGRES_DB: claper + networks: + - claper-net + app: build: . - environment: - DATABASE_URL: postgres://claper:claper@db:5432/claper - SECRET_KEY_BASE: 0LZiQBLw4WvqPlz4cz8RsHJlxNiSqM9B48y4ChyJ5v1oA0L/TPIqRjQNdPZN3iEG - MAIL_TRANSPORT: local - ENDPOINT_HOST: claper.local - ENDPOINT_PORT: 4000 + healthcheck: + test: curl --fail http://localhost:4000 || exit 1 + retries: 3 + start_period: 20s + timeout: 5s + volumes: + - "claper-uploads:/app/uploads" labels: - "traefik.enable=true" - - "traefik.http.routers.claper.rule=Host(`claper.local`)" - - "traefik.http.routers.claper.entrypoints=web" + - "traefik.http.routers.app.rule=Host(`app.claper.co`)" # change to your domain + - "traefik.http.routers.app.tls.certresolver=myresolver" + - "traefik.http.routers.app.entrypoints=web" + - "traefik.http.services.app.loadbalancer.server.port=4000" + env_file: .env depends_on: - db - traefik + networks: + - claper-net traefik: image: traefik command: #- "--log.level=DEBUG" + #- "--api.dashboard=true" + - "--accesslog.filepath=/var/log/traefik/access.log" - "--api.insecure=true" - "--providers.docker=true" - "--providers.docker.exposedbydefault=false" - - "--entrypoints.web.address=:80" + - "--entrypoints.web.address=:443" + - "--certificatesresolvers.myresolver.acme.tlschallenge=true" + - "--certificatesresolvers.myresolver.acme.email=yourmail@example.com" + - "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json" volumes: + - "../letsencrypt:/letsencrypt" - "/var/run/docker.sock:/var/run/docker.sock:ro" + - "/var/log/traefik:/var/log/traefik/" ports: - - "80:80" - - "8080:8080" + - "443:443" + networks: + - claper-net + + volumes: + claper-db: + driver: local + claper-uploads: + driver: local + networks: + claper-net: + driver: bridge ``` ## Behind Kubernetes diff --git a/guides/introduction/installation.md b/guides/introduction/installation.md index b6008dc..c7f826b 100644 --- a/guides/introduction/installation.md +++ b/guides/introduction/installation.md @@ -3,18 +3,20 @@ ## Prerequisites To run Claper on your local environment you need to have: -* Postgres >= 9 -* Elixir >= 1.13.2 -* Erlang >= 24 -* NPM >= 6.14.17 -* NodeJS >= 14.19.2 -* Ghostscript >= 9.5.0 (for PDF support) -* Libreoffice >= 6.4 (for PPT/PPTX support) + +- Postgres >= 9 +- Elixir >= 1.13.2 +- Erlang >= 24 +- NPM >= 6.14.17 +- NodeJS >= 14.19.2 +- Ghostscript >= 9.5.0 (for PDF support) +- Libreoffice >= 6.4 (for PPT/PPTX support) You can also use Docker to easily run a Postgres instance: + ```sh - docker run -p 5432:5432 -e POSTGRES_PASSWORD=claper -e POSTGRES_USER=claper -e POSTGRES_DB=claper --name claper-db -d postgres:9 - ``` + docker run -p 5432:5432 -e POSTGRES_PASSWORD=claper -e POSTGRES_USER=claper -e POSTGRES_DB=claper --name claper-db -d postgres:15 +``` 1. Clone the repo ```sh @@ -45,7 +47,6 @@ Now you can visit [`localhost:4000`](http://localhost:4000) from your browser. If you have configured `MAIL` to `local`, you can access to the mailbox at [`localhost:4000/dev/mailbox`](http://localhost:4000/dev/mailbox). - ## Using Docker Compose A Docker Compose [reference file](https://github.com/ClaperCo/Claper/blob/main/docker-compose.yml) is provided in the repository. You can use it to run Claper with Docker Compose. @@ -56,17 +57,6 @@ cd Claper docker compose up ``` -## Using Docker Compose for Dev - -To easy check new features, it is possible to directly build the Docker image from the source code and run the container with the [docker-compose-dev.yml](https://github.com/ClaperCo/Claper/blob/main/docker-compose-dev.yml) file. - -```sh -git clone https://github.com/ClaperCo/Claper.git -cd Claper -docker compose -f docker-compose-dev.yml up -``` - - ### ARM architecture If you are using an ARM architecture (like Apple M1), the original Docker image won't work. You can build the image yourself by replacing the `BUILDER_IMAGE` argument in the `Dockerfile` with `ARG BUILDER_IMAGE="hexpm/elixir-arm64:1.13.2-erlang-24.2.1-debian-bullseye-20210902-slim"` and then build the image as described above. diff --git a/lib/claper/accounts.ex b/lib/claper/accounts.ex index fa59fd5..9213064 100644 --- a/lib/claper/accounts.ex +++ b/lib/claper/accounts.ex @@ -182,7 +182,7 @@ defmodule Claper.Accounts do ## Examples - iex> deliver_magic_link(user, &Routes.user_confirmation_url(conn, :confirm_magic, &1)) + iex> deliver_magic_link(user, &url(~p"/users/magic/&1")) {:ok, %{to: ..., body: ...}} """ @@ -423,4 +423,8 @@ defmodule Claper.Accounts do UserToken.user_magic_and_contexts_query(token.sent_to, ["magic"]) ) end + + def delete(user) do + Repo.delete(user) + end end diff --git a/lib/claper/events.ex b/lib/claper/events.ex index e52d59b..8672f9d 100644 --- a/lib/claper/events.ex +++ b/lib/claper/events.ex @@ -20,7 +20,7 @@ defmodule Claper.Events do """ def list_events(user_id, preload \\ []) do - from(e in Event, where: e.user_id == ^user_id, order_by: [desc: e.expired_at]) + from(e in Event, where: e.user_id == ^user_id, order_by: [desc: e.inserted_at]) |> Repo.all() |> Repo.preload(preload) end @@ -140,7 +140,7 @@ defmodule Claper.Events do def get_event_with_code!(code, preload \\ []) do now = NaiveDateTime.utc_now() - from(e in Event, where: e.code == ^code and e.expired_at > ^now) + from(e in Event, where: e.code == ^code and (is_nil(e.expired_at) or e.expired_at > ^now)) |> Repo.one!() |> Repo.preload(preload) end @@ -148,7 +148,7 @@ defmodule Claper.Events do def get_event_with_code(code, preload \\ []) do now = DateTime.utc_now() - from(e in Event, where: e.code == ^code and e.expired_at > ^now) + from(e in Event, where: e.code == ^code and (is_nil(e.expired_at) or e.expired_at > ^now)) |> Repo.one() |> Repo.preload(preload) end @@ -234,7 +234,7 @@ defmodule Claper.Events do end @doc """ - Updates a event. + Updates an event. ## Examples @@ -258,6 +258,28 @@ defmodule Claper.Events do end end + @doc """ + Terminates an event. + + ## Examples + + iex> terminate_event(event) + {:ok, %Event{}} + + """ + def terminate_event(%Event{} = event) do + event + |> Event.update_changeset(%{expired_at: NaiveDateTime.utc_now()}) + |> Repo.update() + |> case do + {:ok, event} -> + broadcast({:ok, event, event.uuid}, :event_terminated) + + {:error, changeset} -> + {:error, %{changeset | action: :update}} + end + end + @doc """ Import interactions from another event @@ -421,4 +443,16 @@ defmodule Claper.Events do def change_activity_leader(%ActivityLeader{} = activity_leader, attrs \\ %{}) do ActivityLeader.changeset(activity_leader, attrs) end + + defp broadcast({:error, _reason} = error, _event), do: error + + defp broadcast({:ok, e, event_uuid}, event) do + Phoenix.PubSub.broadcast( + Claper.PubSub, + "event:#{event_uuid}", + {event, event_uuid} + ) + + {:ok, e} + end end diff --git a/lib/claper/events/event.ex b/lib/claper/events/event.ex index 0009463..2a0b7e5 100644 --- a/lib/claper/events/event.ex +++ b/lib/claper/events/event.ex @@ -10,8 +10,6 @@ defmodule Claper.Events.Event do field :started_at, :naive_datetime field :expired_at, :naive_datetime - field :date_range, :string, virtual: true - has_many :posts, Claper.Posts.Post has_many :leaders, Claper.Events.ActivityLeader, on_replace: :delete @@ -30,21 +28,19 @@ defmodule Claper.Events.Event do :code, :started_at, :expired_at, - :date_range, :audience_peak ]) |> cast_assoc(:presentation_file) |> cast_assoc(:leaders) - |> validate_required([:code]) - |> validate_date_range + |> validate_required([:code, :name]) end def create_changeset(event, attrs) do event - |> cast(attrs, [:name, :code, :user_id, :started_at, :expired_at, :date_range]) + |> cast(attrs, [:name, :code, :user_id, :started_at, :expired_at]) |> cast_assoc(:presentation_file) |> cast_assoc(:leaders) - |> validate_required([:code, :started_at, :expired_at]) + |> validate_required([:code, :started_at]) |> downcase_code end @@ -56,38 +52,12 @@ defmodule Claper.Events.Event do ) end - defp validate_date_range(changeset) do - date_range = get_change(changeset, :date_range) - - if date_range != nil do - splited = date_range |> String.split(" - ") - - if splited |> Enum.count() == 2 do - changeset - |> put_change(:started_at, Enum.at(splited, 0)) - |> put_change(:expired_at, Enum.at(splited, 1)) - else - add_error(changeset, :date_range, "invalid date range") - end - else - start_date = get_change(changeset, :started_at) - end_date = get_change(changeset, :expired_at) - - if start_date != nil && end_date != nil do - changeset - |> put_change(:date_range, "#{start_date} - #{end_date}") - else - changeset - end - end - end - def update_changeset(event, attrs) do event - |> cast(attrs, [:name, :code, :started_at, :expired_at, :date_range, :audience_peak]) + |> cast(attrs, [:name, :code, :started_at, :expired_at, :audience_peak]) |> cast_assoc(:presentation_file) |> cast_assoc(:leaders) - |> validate_required([:code, :started_at, :expired_at]) + |> validate_required([:code, :started_at]) |> downcase_code end @@ -105,4 +75,8 @@ defmodule Claper.Events.Event do def started?(event) do NaiveDateTime.compare(NaiveDateTime.utc_now(), event.started_at) == :gt end + + def finished?(event) do + event.expired_at && NaiveDateTime.compare(NaiveDateTime.utc_now(), event.expired_at) == :gt + end end diff --git a/lib/claper/forms.ex b/lib/claper/forms.ex index 210d8d8..635a689 100644 --- a/lib/claper/forms.ex +++ b/lib/claper/forms.ex @@ -238,13 +238,14 @@ defmodule Claper.Forms do [%FormSubmit{}, ...] """ - def list_form_submits(presentation_file_id) do + def list_form_submits(presentation_file_id, preload \\ []) do from(fs in FormSubmit, join: f in Form, on: f.id == fs.form_id, where: f.presentation_file_id == ^presentation_file_id ) |> Repo.all() + |> Repo.preload(preload) end @doc """ diff --git a/lib/claper/presentations/presentation_state.ex b/lib/claper/presentations/presentation_state.ex index d384f2b..dd1ef7c 100644 --- a/lib/claper/presentations/presentation_state.ex +++ b/lib/claper/presentations/presentation_state.ex @@ -9,6 +9,8 @@ defmodule Claper.Presentations.PresentationState do field :join_screen_visible, :boolean field :chat_enabled, :boolean field :anonymous_chat_enabled, :boolean + field :message_reaction_enabled, :boolean, default: true + field :show_poll_results_enabled, :boolean, default: true field :banned, {:array, :string}, default: [] field :show_only_pinned, :boolean, default: false @@ -29,7 +31,9 @@ defmodule Claper.Presentations.PresentationState do :presentation_file_id, :chat_enabled, :anonymous_chat_enabled, - :show_only_pinned + :show_only_pinned, + :message_reaction_enabled, + :show_poll_results_enabled ]) |> validate_required([]) end diff --git a/lib/claper/stats.ex b/lib/claper/stats.ex index 3145912..ea38a5c 100644 --- a/lib/claper/stats.ex +++ b/lib/claper/stats.ex @@ -16,11 +16,14 @@ defmodule Claper.Stats do def total_vote_count(presentation_file_id) do from(p in Claper.Polls.Poll, - join: o in Claper.Polls.PollOpt, - on: o.poll_id == p.id, + join: pv in Claper.Polls.PollVote, + on: pv.poll_id == p.id, where: p.presentation_file_id == ^presentation_file_id, - group_by: o.poll_id, - select: sum(o.vote_count) + group_by: p.presentation_file_id, + select: + count( + fragment("DISTINCT COALESCE(?, CAST(? AS varchar))", pv.attendee_identifier, pv.user_id) + ) ) |> Repo.all() end diff --git a/lib/claper/tasks/converter.ex b/lib/claper/tasks/converter.ex index ca2b4d2..b74e742 100644 --- a/lib/claper/tasks/converter.ex +++ b/lib/claper/tasks/converter.ex @@ -43,8 +43,6 @@ defmodule Claper.Tasks.Converter do Remove the presentation files directory """ def clear(hash) do - IO.puts("Clearing #{hash}...") - if get_presentation_storage() == "local" do File.rm_rf( Path.join([ diff --git a/lib/claper_web.ex b/lib/claper_web.ex index a866b3a..5ebb91b 100644 --- a/lib/claper_web.ex +++ b/lib/claper_web.ex @@ -17,13 +17,16 @@ defmodule ClaperWeb do and import those modules here. """ + def static_paths, do: ~w(assets fonts .well-known images favicon.ico robots.txt) + def controller do quote do use Phoenix.Controller, namespace: ClaperWeb import Plug.Conn import ClaperWeb.Gettext - alias ClaperWeb.Router.Helpers, as: Routes + + unquote(verified_routes()) end end @@ -71,7 +74,9 @@ defmodule ClaperWeb do def view_component do quote do - use Phoenix.HTML + import Phoenix.HTML + import Phoenix.HTML.Form + use PhoenixHTMLHelpers use Phoenix.Component import ClaperWeb.ErrorHelpers alias Phoenix.LiveView.JS @@ -89,7 +94,9 @@ defmodule ClaperWeb do defp view_helpers do quote do # Use all HTML functionality (forms, tags, etc) - use Phoenix.HTML + import Phoenix.HTML + import Phoenix.HTML.Form + use PhoenixHTMLHelpers # Import LiveView and .heex helpers (live_render, live_patch, <.form>, etc) import Phoenix.LiveView.Helpers @@ -101,7 +108,17 @@ defmodule ClaperWeb do import ClaperWeb.ErrorHelpers import ClaperWeb.Gettext - alias ClaperWeb.Router.Helpers, as: Routes + + unquote(verified_routes()) + end + end + + def verified_routes do + quote do + use Phoenix.VerifiedRoutes, + endpoint: ClaperWeb.Endpoint, + router: ClaperWeb.Router, + statics: ClaperWeb.static_paths() end end diff --git a/lib/claper_web/controllers/stat_controller.ex b/lib/claper_web/controllers/stat_controller.ex index 64f4a5a..81ed3ef 100644 --- a/lib/claper_web/controllers/stat_controller.ex +++ b/lib/claper_web/controllers/stat_controller.ex @@ -5,22 +5,22 @@ defmodule ClaperWeb.StatController do def export(conn, %{"form_id" => form_id}) do form = Forms.get_form!(form_id, [:form_submits]) - csv_data = csv_content(form.form_submits |> Enum.map(& &1.response)) + headers = form.fields |> Enum.map(& &1.name) + csv_data = headers |> csv_content(form.form_submits |> Enum.map(& &1.response)) conn |> put_resp_content_type("text/csv") - |> put_resp_header("content-disposition", "attachment; filename=\"export.csv\"") + |> put_resp_header("content-disposition", "attachment; filename=\"#{form.title}.csv\"") |> put_root_layout(false) |> send_resp(200, csv_data) end - defp csv_content(records) do - records - |> Enum.map(fn record -> - record - |> Map.new(fn {k, v} -> {String.to_atom(k), v} end) - |> Map.values() - end) + defp csv_content(headers, records) do + data = + records + |> Enum.map(&(&1 |> Map.values())) + + ([headers] ++ data) |> CSV.encode() |> Enum.to_list() |> to_string() diff --git a/lib/claper_web/controllers/user_auth.ex b/lib/claper_web/controllers/user_auth.ex index 1f525d2..5fb26fe 100644 --- a/lib/claper_web/controllers/user_auth.ex +++ b/lib/claper_web/controllers/user_auth.ex @@ -2,12 +2,12 @@ defmodule ClaperWeb.UserAuth do @moduledoc """ Plug for user authentication. """ + use ClaperWeb, :controller import Plug.Conn import Phoenix.Controller alias Claper.Accounts - alias ClaperWeb.Router.Helpers, as: Routes # Make the remember me cookie valid for 60 days. # If you want bump or reduce this value, also change @@ -137,13 +137,13 @@ defmodule ClaperWeb.UserAuth do conn else conn - # |> redirect(to: Routes.user_registration_path(conn, :confirm)) + # |> redirect(to: ~p"/users/register/confirm") end else conn |> put_flash(:error, "You must log in to access this page.") |> maybe_store_return_to() - |> redirect(to: Routes.user_session_path(conn, :new)) + |> redirect(to: ~p"/users/log_in") |> halt() end end diff --git a/lib/claper_web/controllers/user_confirmation_controller.ex b/lib/claper_web/controllers/user_confirmation_controller.ex index 0fd9c89..a1980ae 100644 --- a/lib/claper_web/controllers/user_confirmation_controller.ex +++ b/lib/claper_web/controllers/user_confirmation_controller.ex @@ -11,7 +11,7 @@ defmodule ClaperWeb.UserConfirmationController do if user = Accounts.get_user_by_email(email) do Accounts.deliver_user_confirmation_instructions( user, - &Routes.user_confirmation_url(conn, :update, &1) + &url(~p"/users/confirm/#{&1}") ) end diff --git a/lib/claper_web/controllers/user_registration_controller.ex b/lib/claper_web/controllers/user_registration_controller.ex index 65fe14b..2e9c6ec 100644 --- a/lib/claper_web/controllers/user_registration_controller.ex +++ b/lib/claper_web/controllers/user_registration_controller.ex @@ -26,7 +26,7 @@ defmodule ClaperWeb.UserRegistrationController do # {:ok, _} = # Accounts.deliver_user_confirmation_instructions( # user, - # &Routes.user_confirmation_url(conn, :update, &1) + # &url(~p"/users/confirm/#{&1}") # ) conn diff --git a/lib/claper_web/controllers/user_reset_password_controller.ex b/lib/claper_web/controllers/user_reset_password_controller.ex index 2f35249..d4d4408 100644 --- a/lib/claper_web/controllers/user_reset_password_controller.ex +++ b/lib/claper_web/controllers/user_reset_password_controller.ex @@ -15,7 +15,7 @@ defmodule ClaperWeb.UserResetPasswordController do if user = Accounts.get_user_by_email(email) do Accounts.deliver_user_reset_password_instructions( user, - &Routes.user_reset_password_url(conn, :edit, &1) + &url(~p"/users/reset_password/#{&1}") ) end diff --git a/lib/claper_web/controllers/user_session_controller.ex b/lib/claper_web/controllers/user_session_controller.ex index c6485e0..1de54e7 100644 --- a/lib/claper_web/controllers/user_session_controller.ex +++ b/lib/claper_web/controllers/user_session_controller.ex @@ -10,10 +10,10 @@ defmodule ClaperWeb.UserSessionController do end # def create(conn, %{"user" => %{"email" => email}} = _user_params) do - # Accounts.deliver_magic_link(email, &Routes.user_confirmation_url(conn, :confirm_magic, &1)) + # Accounts.deliver_magic_link(email, &url(~p"/users/magic/#{&1}")) # conn - # |> redirect(to: Routes.user_registration_path(conn, :confirm, %{email: email})) + # |> redirect(to: ~p"/users/register/confirm?#{[%{email: email}]}") # end def create(conn, %{"user" => user_params}) do %{"email" => email, "password" => password} = user_params diff --git a/lib/claper_web/controllers/user_settings_controller.ex b/lib/claper_web/controllers/user_settings_controller.ex index 6bc6d12..97e2bdc 100644 --- a/lib/claper_web/controllers/user_settings_controller.ex +++ b/lib/claper_web/controllers/user_settings_controller.ex @@ -18,7 +18,7 @@ defmodule ClaperWeb.UserSettingsController do Accounts.deliver_update_email_instructions( applied_user, user.email, - &Routes.user_settings_url(conn, :confirm_email, &1) + &url(~p"/users/settings/confirm_email/#{&1}") ) conn @@ -26,7 +26,7 @@ defmodule ClaperWeb.UserSettingsController do :info, "A link to confirm your email change has been sent to the new address." ) - |> redirect(to: Routes.user_settings_show_path(conn, :show)) + |> redirect(to: ~p"/users/settings") {:error, changeset} -> render(conn, "edit.html", email_changeset: changeset) @@ -38,12 +38,12 @@ defmodule ClaperWeb.UserSettingsController do :ok -> conn |> put_flash(:info, "Email changed successfully.") - |> redirect(to: Routes.user_settings_show_path(conn, :show)) + |> redirect(to: ~p"/users/settings") :error -> conn |> put_flash(:error, "Email change link is invalid or it has expired.") - |> redirect(to: Routes.user_settings_show_path(conn, :show)) + |> redirect(to: ~p"/users/settings") end end diff --git a/lib/claper_web/endpoint.ex b/lib/claper_web/endpoint.ex index d9a213c..6c4896c 100644 --- a/lib/claper_web/endpoint.ex +++ b/lib/claper_web/endpoint.ex @@ -4,11 +4,23 @@ defmodule ClaperWeb.Endpoint do # The session will be stored in the cookie and signed, # this means its contents can be read but not tampered with. # Set :encryption_salt if you would also like to encrypt it. - @session_options [ - store: :cookie, - key: "_claper_key", - signing_salt: "Tg18Y2zU" - ] + @session_options (case Mix.env() do + :dev -> + [ + store: :cookie, + key: "_claper_key", + signing_salt: "Tg18Y2zU", + same_site: "None", + secure: true + ] + + _ -> + [ + store: :cookie, + key: "_claper_key", + signing_salt: "Tg18Y2zU" + ] + end) socket "/live", Phoenix.LiveView.Socket, websocket: [connect_info: [session: @session_options]] @@ -20,8 +32,7 @@ defmodule ClaperWeb.Endpoint do at: "/", from: :claper, gzip: false, - only: - ~w(assets fonts .well-known images favicon.ico robots.txt loaderio-eb3b956a176cdd4f54eb8570ce8bbb06.txt) + only: ClaperWeb.static_paths() plug Plug.Static, at: "/uploads", diff --git a/lib/claper_web/live/event_live/event_card_component.ex b/lib/claper_web/live/event_live/event_card_component.ex index 688da1c..ec8ca15 100644 --- a/lib/claper_web/live/event_live/event_card_component.ex +++ b/lib/claper_web/live/event_live/event_card_component.ex @@ -1,6 +1,8 @@ defmodule ClaperWeb.EventLive.EventCardComponent do use ClaperWeb, :live_component + alias Claper.Events.Event + def render(assigns) do assigns = assigns @@ -15,17 +17,19 @@ defmodule ClaperWeb.EventLive.EventCardComponent do <%= @event.name %>- <%= gettext("In progress") %> -
+ <%= if Event.started?(@event) && !Event.finished?(@event) do %> +<%= gettext("Incoming") %>
<% end %> - <%= if NaiveDateTime.compare(@current_time, @event.expired_at) == :gt do %> + <%= if Event.finished?(@event) do %><%= gettext("Finished") %>
@@ -44,25 +48,22 @@ defmodule ClaperWeb.EventLive.EventCardComponent do class="flex items-center text-sm text-gray-500 space-x-1" phx-update="ignore" > -- <%= gettext("Finish on") %> - -
- <% end %> - <%= if NaiveDateTime.compare(@current_time, @event.started_at) == :lt do %> -- <%= gettext("Starting on") %> - -
- <% end %> - <%= if NaiveDateTime.compare(@current_time, @event.expired_at) == :gt do %> -- <%= gettext("Finished on") %> - -
- <% end %> ++ <%= gettext("Starting on") %> + +
++ <%= gettext("Finished on") %> + +
- <%= gettext("Processing your file...") %>
-
+ <%= gettext("Processing your file...") %>
+ #{gettext("Animations in PPT/PPTX files are not supported, which is why we recommend exporting your presentation to PDF to ensure it displays correctly.")}
"} + data-tg-title={"📄 #{gettext("Presentation file (optional)")}"} + ><%= gettext("or drag and drop") %>
@@ -157,7 +170,7 @@ phx-target={@myself} > <%= gettext("Change file") %> - <%= live_file_input(@uploads.presentation_file, class: "sr-only") %> + <.live_file_input upload={@uploads.presentation_file} class="sr-only" />#{gettext("Attendees attempting to access the event prior to this date will be directed to a waiting room.")}
"} + data-tg-group="create-event" + data-tg-order="3" + phx-update="ignore" + id="date-picker" + > +#{gettext("Note: Facilitators do not have the ability to delete your event.")}
"} + data-tg-group="create-event" + data-tg-order="4" + > <%= gettext("Facilitators can present and manage interactions") %> @@ -278,7 +307,7 @@ type="button" phx-click="add-leader" phx-target={@myself} - class="rounded-md bg-primary-500 hover:bg-primary-600 transition flex items-center mt-3 md:w-max text-white py-7 px-3 text-sm max-h-0" + class="rounded-md bg-primary-500 hover:bg-primary-600 transition flex items-center mt-3 md:w-max text-white py-5 px-3 text-sm max-h-0" > - <%= gettext("Add facilitator") %> + <%= gettext("Add facilitator") %><%= gettext("Create your first presentation") %>
+<%= gettext("Create your first event") %>
- <%= gettext("Return to your last presentation") %> (#<%= @last_event.code %>) -
-- - - -
-
+ <%= gettext("Return to your last event") %> (<%= @last_event.name %>) +
++ + + +
+