From f0a60bd4822ae1d061f8265bf402280399eef4e8 Mon Sep 17 00:00:00 2001 From: Luis Eduardo Date: Tue, 4 Feb 2025 18:56:15 +0000 Subject: [PATCH] Run deno fmt --- CONTRIBUTING.md | 49 +++++++---- README.md | 66 ++++++++++----- deno.json | 18 ++++ internal/logger/REAME.md | 10 +-- internal/util/paginateutil/README.md | 23 +++-- internal/validate/README.md | 3 +- internal/view/reqctx/README.md | 12 ++- internal/view/static/css/partials/alpine.css | 2 +- internal/view/static/css/partials/general.css | 2 +- internal/view/static/css/partials/htmx.css | 2 +- .../view/static/css/partials/nodx-lucide.css | 2 +- internal/view/static/css/partials/notyf.css | 2 +- .../view/static/css/partials/scrollbar.css | 2 +- .../view/static/css/partials/slim-select.css | 2 +- .../view/static/css/partials/sweetalert2.css | 2 +- .../view/static/css/partials/tailwind.css | 2 +- internal/view/static/css/style.css | 2 +- internal/view/static/js/app.js | 20 ++--- internal/view/static/js/init-helpers.js | 84 +++++++++---------- internal/view/static/js/init-htmx.js | 76 ++++++++--------- internal/view/static/js/init-notyf.js | 56 ++++++------- internal/view/static/js/init-sweetalert2.js | 50 +++++------ internal/view/static/js/init-theme-helper.js | 22 ++--- .../web/component/change_theme_button.inc.js | 24 +++--- .../web/component/options_dropdown.inc.js | 49 +++++------ .../view/web/component/star_on_github.inc.js | 42 +++++----- .../web/dashboard/summary/index_how_to.inc.js | 22 ++--- .../view/web/layout/dashboard_aside.inc.js | 60 ++++++------- .../layout/dashboard_header_updates.inc.js | 43 +++++----- tailwind.config.js | 30 +++---- taskfile.yaml | 10 +-- 31 files changed, 433 insertions(+), 356 deletions(-) create mode 100644 deno.json diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d2f3efd..0b4dcd4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,45 +1,59 @@ ## PG Back Web - Contribution Guidelines -Thank you for your interest in contributing to the PG Back Web project! Please follow these guidelines to ensure smooth collaboration and consistent code quality. +Thank you for your interest in contributing to the PG Back Web project! Please +follow these guidelines to ensure smooth collaboration and consistent code +quality. ## Open Source -This project is completely open source, so before making a contribution make sure you agree with the license in the [LICENSE](LICENSE) file and that all your contributions are under the same license. +This project is completely open source, so before making a contribution make +sure you agree with the license in the [LICENSE](LICENSE) file and that all your +contributions are under the same license. -This means that any contribution made to this project will be assumed to be made under the same license. +This means that any contribution made to this project will be assumed to be made +under the same license. ### Main Branch Policy -- The **main branch** reflects the latest **stable release** of the project. -- **No direct commits** should ever be made to the main branch. -- All development work should happen in feature branches and merged via **Pull Requests (PRs)** into the **develop** branch. -- The **develop branch** contains the latest code under active development. Once a new release is ready, the main branch will be updated by merging from the development branch. +- The **main branch** reflects the latest **stable release** of the project. +- **No direct commits** should ever be made to the main branch. +- All development work should happen in feature branches and merged via **Pull + Requests (PRs)** into the **develop** branch. +- The **develop branch** contains the latest code under active development. Once + a new release is ready, the main branch will be updated by merging from the + development branch. ### Development Workflow -1. **Fork the repository** and create a feature branch from the `develop` branch. - - Use descriptive names for your branches, e.g., `feature/add-new-endpoint` or `bugfix/fix-connection-issue`. +1. **Fork the repository** and create a feature branch from the `develop` + branch. + - Use descriptive names for your branches, e.g., `feature/add-new-endpoint` + or `bugfix/fix-connection-issue`. 2. **Make your changes** in your feature branch. -3. **Ensure all tests pass** and the code follows the project’s style guidelines. +3. **Ensure all tests pass** and the code follows the project’s style + guidelines. 4. **Open a Pull Request (PR)** against the `develop` branch. 5. Your PR will be reviewed by maintainers. Please be responsive to feedback. -6. Once approved, the changes will be merged into the `develop` branch. A merge into the `main` branch will only occur when releasing a new version. +6. Once approved, the changes will be merged into the `develop` branch. A merge + into the `main` branch will only occur when releasing a new version. ### Project Dependencies This project requires the following dependencies installed on your system: + - **VSCode** - To enter into the devcontainer -- **Docker** – For containerized environments. +- **Docker** – For containerized environments. - **Docker Compose** – To manage multi-container setups. ### Development process -This project uses devcontainers to simplify the development, please refer to the following resources to learn more about devcontainers: +This project uses devcontainers to simplify the development, please refer to the +following resources to learn more about devcontainers: - https://containers.dev - https://code.visualstudio.com/docs/devcontainers/containers @@ -54,8 +68,11 @@ This project uses devcontainers to simplify the development, please refer to the ### Additional Notes -- Always **write clear commit messages** that explain the purpose of your changes. -- **Keep your fork up to date** with the latest changes from the `develop` branch. -- Be respectful and follow the project’s code of conduct when interacting with other contributors. +- Always **write clear commit messages** that explain the purpose of your + changes. +- **Keep your fork up to date** with the latest changes from the `develop` + branch. +- Be respectful and follow the project’s code of conduct when interacting with + other contributors. We appreciate your contributions and are excited to have you on board! diff --git a/README.md b/README.md index 3a6b687..3168dd8 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,8 @@ ## Why PG Back Web? -PG Back Web isn't just another backup tool. It's your trusted ally in ensuring the security and availability of your PostgreSQL data: +PG Back Web isn't just another backup tool. It's your trusted ally in ensuring +the security and availability of your PostgreSQL data: - 🎯 **Designed for everyone**: From individual developers to teams. - ⏱️ **Save time**: Automate your backups and forget about manual tasks. @@ -38,23 +39,33 @@ PG Back Web isn't just another backup tool. It's your trusted ally in ensuring t ## Features -- 📦 **Intuitive web interface**: Manage your backups with ease, no database expertise required. -- 📅 **Scheduled backups**: Set it and forget it. PG Back Web takes care of the rest. -- 📈 **Backup monitoring**: Visualize the status of your backups with execution logs. -- 📤 **Instant download & restore**: Restore and download your backups when you need them, directly from the web interface. +- 📦 **Intuitive web interface**: Manage your backups with ease, no database + expertise required. +- 📅 **Scheduled backups**: Set it and forget it. PG Back Web takes care of the + rest. +- 📈 **Backup monitoring**: Visualize the status of your backups with execution + logs. +- 📤 **Instant download & restore**: Restore and download your backups when you + need them, directly from the web interface. - 🖥 **Multi-version support**: Compatible with PostgreSQL 13, 14, 15, and 16. -- 📁 **Local & S3 storage**: Store backups locally or add as many S3 buckets as you want for greater flexibility. -- ❤️‍🩹 **Health checks**: Automatically check the health of your databases and destinations. -- 🔔 **Webhooks**: Get notified when a backup finishes, failed, health check fails, or other events. +- 📁 **Local & S3 storage**: Store backups locally or add as many S3 buckets as + you want for greater flexibility. +- ❤️‍🩹 **Health checks**: Automatically check the health of your databases and + destinations. +- 🔔 **Webhooks**: Get notified when a backup finishes, failed, health check + fails, or other events. - 🔒 **Security first**: PGP encryption to protect your sensitive information. -- 🛡️ **Open-source trust**: Open-source code under MIT license, backed by the robust pg_dump tool. +- 🛡️ **Open-source trust**: Open-source code under MIT license, backed by the + robust pg_dump tool. - 🌚 **Dark mode**: Because we all love dark mode. ## Installation -PG Back Web is available as a Docker image. You just need to set 3 environment variables and you're good to go! +PG Back Web is available as a Docker image. You just need to set 3 environment +variables and you're good to go! -Here's an example of how you can run PG Back Web with Docker Compose, feel free to adapt it to your needs: +Here's an example of how you can run PG Back Web with Docker Compose, feel free +to adapt it to your needs: ```yaml services: @@ -89,17 +100,23 @@ services: retries: 5 ``` -You can watch [this youtube video](https://www.youtube.com/watch?v=vf7SLrSO8sw) to see how easy it is to set up PG Back Web. +You can watch [this youtube video](https://www.youtube.com/watch?v=vf7SLrSO8sw) +to see how easy it is to set up PG Back Web. ## Configuration You only need to configure the following environment variables: -- `PBW_ENCRYPTION_KEY`: Your encryption key. Generate a strong one and store it in a safe place, as PG Back Web uses it to encrypt sensitive data. +- `PBW_ENCRYPTION_KEY`: Your encryption key. Generate a strong one and store it + in a safe place, as PG Back Web uses it to encrypt sensitive data. -- `PBW_POSTGRES_CONN_STRING`: The connection string for the PostgreSQL database that will store PG Back Web data. +- `PBW_POSTGRES_CONN_STRING`: The connection string for the PostgreSQL database + that will store PG Back Web data. -- `TZ`: Your [timezone](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List) (optional). Default is `UTC`. This impacts logging, backup filenames and default timezone in the web interface. +- `TZ`: Your + [timezone](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List) + (optional). Default is `UTC`. This impacts logging, backup filenames and + default timezone in the web interface. ## Screenshot @@ -107,17 +124,20 @@ You only need to configure the following environment variables: ## Reset password -You can reset your PG Back Web password by running the following command in the server where PG Back Web is running: +You can reset your PG Back Web password by running the following command in the +server where PG Back Web is running: ```bash docker exec -it sh -c change-password ``` -You should replace `` with the name or ID of the PG Back Web container, then just follow the instructions. +You should replace `` with the name or ID of the PG Back +Web container, then just follow the instructions. ## Next steps -In this link you can see a list of features that have been confirmed for future updates: +In this link you can see a list of features that have been confirmed for future +updates: Next steps ⏭️ @@ -125,12 +145,16 @@ In this link you can see a list of features that have been confirmed for future ## Join the Community -Got ideas to improve PG Back Web? Contribute to the project! Every suggestion and pull request is welcome. +Got ideas to improve PG Back Web? Contribute to the project! Every suggestion +and pull request is welcome. ## License -This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. +This project is licensed under the MIT License - see the [LICENSE](LICENSE) file +for details. --- -💖 **Love PG Back Web?** Give us a ⭐ on GitHub and share the project with your colleagues. Together, we can make PostgreSQL backups more accessible to everyone! \ No newline at end of file +💖 **Love PG Back Web?** Give us a ⭐ on GitHub and share the project with your +colleagues. Together, we can make PostgreSQL backups more accessible to +everyone! diff --git a/deno.json b/deno.json new file mode 100644 index 0000000..3c3dc39 --- /dev/null +++ b/deno.json @@ -0,0 +1,18 @@ +{ + "fmt": { + "exclude": [ + "./internal/view/static/libs/**/*" + ] + }, + "lint": { + "exclude": [ + "./internal/view/static/libs/**/*" + ], + "rules": { + "exclude": [ + "no-window", + "no-window-prefix" + ] + } + } +} diff --git a/internal/logger/REAME.md b/internal/logger/REAME.md index 00de7bc..fba9fe1 100644 --- a/internal/logger/REAME.md +++ b/internal/logger/REAME.md @@ -2,10 +2,10 @@ This package includes a custom logger for the project. -Functions are exposed for logging messages easily; it is imperative that -these functions are used throughout the project to maintain a standard in -log messages. +Functions are exposed for logging messages easily; it is imperative that these +functions are used throughout the project to maintain a standard in log +messages. If necessary, the `logWriter` can be edited so that in addition to writing to -stdout, it can also write to a file or send the logs to a server like -Better Stack or New Relic. +stdout, it can also write to a file or send the logs to a server like Better +Stack or New Relic. diff --git a/internal/util/paginateutil/README.md b/internal/util/paginateutil/README.md index a1effc7..8eb0411 100644 --- a/internal/util/paginateutil/README.md +++ b/internal/util/paginateutil/README.md @@ -1,19 +1,24 @@ # paginateutil -This package provides a utility for paginating data. It should be used in conjunction with database queries to paginate results. +This package provides a utility for paginating data. It should be used in +conjunction with database queries to paginate results. -Use these utilities to paginate results and, above all, to return a common structure in the different places where pagination is performed, maintaining consistency throughout the project. +Use these utilities to paginate results and, above all, to return a common +structure in the different places where pagination is performed, maintaining +consistency throughout the project. ## Usage ### 1. Define your queries: -- **PaginateCount:** This function should return the total number of records that match the query. +- **PaginateCount:** This function should return the total number of records + that match the query. - **Paginate:** This function should return the paginated records. ### 2. Create the offset from the request parameters: -Use the `CreateOffsetFromParams` function to create the offset from the request parameters needed to paginate the results. +Use the `CreateOffsetFromParams` function to create the offset from the request +parameters needed to paginate the results. ### 3. Define the signature of your pagination function: @@ -38,9 +43,13 @@ func PaginateXYZ( ### Example -Refer to `internal/service/backups/paginate_backups.go` for an example of how to use the `paginateutil` package. +Refer to `internal/service/backups/paginate_backups.go` for an example of how to +use the `paginateutil` package. ## Notes -- **Default Values:** Ensure to set reasonable default values for pagination parameters (`page` and `limit`) if they are not provided. -- **Common Response Structure:** Use `CreatePaginateResponse` to generate a common response structure that includes information about pagination, such as the total number of items, the current page, and the number of items per page. +- **Default Values:** Ensure to set reasonable default values for pagination + parameters (`page` and `limit`) if they are not provided. +- **Common Response Structure:** Use `CreatePaginateResponse` to generate a + common response structure that includes information about pagination, such as + the total number of items, the current page, and the number of items per page. diff --git a/internal/validate/README.md b/internal/validate/README.md index 0244bc3..febee47 100644 --- a/internal/validate/README.md +++ b/internal/validate/README.md @@ -1,5 +1,6 @@ # Validate functions -All the functions in this directory are used to validate data, all of them _MUST BE PURE FUNCTIONS_. +All the functions in this directory are used to validate data, all of them _MUST +BE PURE FUNCTIONS_. https://en.wikipedia.org/wiki/Pure_function diff --git a/internal/view/reqctx/README.md b/internal/view/reqctx/README.md index 4c03176..0262ce9 100644 --- a/internal/view/reqctx/README.md +++ b/internal/view/reqctx/README.md @@ -1,9 +1,15 @@ # reqctx -`reqctx` is a utility package designed to manage request-specific context values in this project. It helps to encapsulate authentication status and user information within the Echo request context in a type-safe manner. +`reqctx` is a utility package designed to manage request-specific context values +in this project. It helps to encapsulate authentication status and user +information within the Echo request context in a type-safe manner. ## Purpose -When developing web applications, it is common to pass values such as authentication status and user information through the request lifecycle. Using Echo's built-in context (`echo.Context`) can lead to potential issues such as typographical errors, lack of type safety, and reduced code readability. +When developing web applications, it is common to pass values such as +authentication status and user information through the request lifecycle. Using +Echo's built-in context (`echo.Context`) can lead to potential issues such as +typographical errors, lack of type safety, and reduced code readability. -`reqctx` addresses these issues by providing a structured way to manage these context values. +`reqctx` addresses these issues by providing a structured way to manage these +context values. diff --git a/internal/view/static/css/partials/alpine.css b/internal/view/static/css/partials/alpine.css index 53be86c..26f9134 100644 --- a/internal/view/static/css/partials/alpine.css +++ b/internal/view/static/css/partials/alpine.css @@ -1,3 +1,3 @@ [x-cloak] { display: none !important; -} \ No newline at end of file +} diff --git a/internal/view/static/css/partials/general.css b/internal/view/static/css/partials/general.css index 08750cb..982abcd 100644 --- a/internal/view/static/css/partials/general.css +++ b/internal/view/static/css/partials/general.css @@ -5,4 +5,4 @@ html { .table tbody tr { @apply hover:bg-base-200; -} \ No newline at end of file +} diff --git a/internal/view/static/css/partials/htmx.css b/internal/view/static/css/partials/htmx.css index 7c441d0..e0f5449 100644 --- a/internal/view/static/css/partials/htmx.css +++ b/internal/view/static/css/partials/htmx.css @@ -8,4 +8,4 @@ .htmx-request.htmx-indicator { display: block; -} \ No newline at end of file +} diff --git a/internal/view/static/css/partials/nodx-lucide.css b/internal/view/static/css/partials/nodx-lucide.css index 01e77a7..f701de7 100644 --- a/internal/view/static/css/partials/nodx-lucide.css +++ b/internal/view/static/css/partials/nodx-lucide.css @@ -1,3 +1,3 @@ svg[data-nodxgo="lucide"]:not([class*="size-"]) { @apply size-4; -} \ No newline at end of file +} diff --git a/internal/view/static/css/partials/notyf.css b/internal/view/static/css/partials/notyf.css index d5b7979..2a3026c 100644 --- a/internal/view/static/css/partials/notyf.css +++ b/internal/view/static/css/partials/notyf.css @@ -1,3 +1,3 @@ .notyf__toast { @apply rounded-btn break-all !important; -} \ No newline at end of file +} diff --git a/internal/view/static/css/partials/scrollbar.css b/internal/view/static/css/partials/scrollbar.css index 41fb7a5..31bab68 100644 --- a/internal/view/static/css/partials/scrollbar.css +++ b/internal/view/static/css/partials/scrollbar.css @@ -20,4 +20,4 @@ * { scrollbar-width: thin; } -} \ No newline at end of file +} diff --git a/internal/view/static/css/partials/slim-select.css b/internal/view/static/css/partials/slim-select.css index 41f8e6e..e7b2c9d 100644 --- a/internal/view/static/css/partials/slim-select.css +++ b/internal/view/static/css/partials/slim-select.css @@ -58,4 +58,4 @@ .ss-value-delete svg path { @apply stroke-primary-content !important; -} \ No newline at end of file +} diff --git a/internal/view/static/css/partials/sweetalert2.css b/internal/view/static/css/partials/sweetalert2.css index 1d61c49..d70426c 100644 --- a/internal/view/static/css/partials/sweetalert2.css +++ b/internal/view/static/css/partials/sweetalert2.css @@ -6,4 +6,4 @@ body.swal2-shown:not(.swal2-no-backdrop):not(.swal2-toast-shown), html.swal2-shown:not(.swal2-no-backdrop):not(.swal2-toast-shown) { height: 100% !important; overflow-y: visible !important; -} \ No newline at end of file +} diff --git a/internal/view/static/css/partials/tailwind.css b/internal/view/static/css/partials/tailwind.css index adbf45b..510ff1d 100644 --- a/internal/view/static/css/partials/tailwind.css +++ b/internal/view/static/css/partials/tailwind.css @@ -1,4 +1,4 @@ @tailwind base; @tailwind components; @tailwind utilities; -@tailwind variants; \ No newline at end of file +@tailwind variants; diff --git a/internal/view/static/css/style.css b/internal/view/static/css/style.css index 4e2df9b..6832e34 100644 --- a/internal/view/static/css/style.css +++ b/internal/view/static/css/style.css @@ -6,4 +6,4 @@ @import "./partials/slim-select.css"; @import "./partials/notyf.css"; @import "./partials/scrollbar.css"; -@import "./partials/sweetalert2.css"; \ No newline at end of file +@import "./partials/sweetalert2.css"; diff --git a/internal/view/static/js/app.js b/internal/view/static/js/app.js index 9128c41..e7c2b60 100644 --- a/internal/view/static/js/app.js +++ b/internal/view/static/js/app.js @@ -1,11 +1,11 @@ -import { initThemeHelper } from './init-theme-helper.js' -import { initSweetAlert2 } from './init-sweetalert2.js' -import { initNotyf } from './init-notyf.js' -import { initHTMX } from './init-htmx.js' -import { initHelpers } from './init-helpers.js' +import { initThemeHelper } from "./init-theme-helper.js"; +import { initSweetAlert2 } from "./init-sweetalert2.js"; +import { initNotyf } from "./init-notyf.js"; +import { initHTMX } from "./init-htmx.js"; +import { initHelpers } from "./init-helpers.js"; -initThemeHelper() -initSweetAlert2() -initNotyf() -initHTMX() -initHelpers() +initThemeHelper(); +initSweetAlert2(); +initNotyf(); +initHTMX(); +initHelpers(); diff --git a/internal/view/static/js/init-helpers.js b/internal/view/static/js/init-helpers.js index 787b63c..ea0a51c 100644 --- a/internal/view/static/js/init-helpers.js +++ b/internal/view/static/js/init-helpers.js @@ -1,75 +1,75 @@ -export function initHelpers () { - function debounce (fn, delayMilliseconds) { - let timeoutInstance +export function initHelpers() { + function debounce(fn, delayMilliseconds) { + let timeoutInstance; return function (...args) { - clearTimeout(timeoutInstance) + clearTimeout(timeoutInstance); timeoutInstance = setTimeout(() => { - fn.apply(this, args) - }, delayMilliseconds) - } + fn.apply(this, args); + }, delayMilliseconds); + }; } - function copyToClipboard (textToCopy) { - const successMessage = 'Text copied' - const errorMessage = 'Error copying text' + function copyToClipboard(textToCopy) { + const successMessage = "Text copied"; + const errorMessage = "Error copying text"; /* First, try the modern approach */ if (navigator.clipboard && window.isSecureContext) { return navigator.clipboard .writeText(textToCopy) .then(() => { - toaster.success(successMessage) + toaster.success(successMessage); }) .catch((err) => { - console.error(errorMessage, err) - toaster.error(errorMessage) - }) + console.error(errorMessage, err); + toaster.error(errorMessage); + }); } /* Fallback: use execCommand("copy") method */ - const textArea = document.createElement('textarea') - textArea.value = textToCopy + const textArea = document.createElement("textarea"); + textArea.value = textToCopy; - textArea.style.position = 'fixed' - textArea.style.left = '-9999px' - textArea.style.top = '-9999px' - document.body.appendChild(textArea) - textArea.focus() - textArea.select() + textArea.style.position = "fixed"; + textArea.style.left = "-9999px"; + textArea.style.top = "-9999px"; + document.body.appendChild(textArea); + textArea.focus(); + textArea.select(); try { - const successful = document.execCommand('copy') + const successful = document.execCommand("copy"); if (successful) { - toaster.success(successMessage) + toaster.success(successMessage); } else { - console.error('Fallback', errorMessage) - toaster.error(errorMessage) + console.error("Fallback", errorMessage); + toaster.error(errorMessage); } } catch (err) { - console.error('Fallback', errorMessage, err) - toaster.error(errorMessage) + console.error("Fallback", errorMessage, err); + toaster.error(errorMessage); } - document.body.removeChild(textArea) + document.body.removeChild(textArea); } - function textareaAutoGrow (element) { - if (!element?.style) return - element.style.height = '1px' - element.style.height = (element.scrollHeight + 2) + 'px' + function textareaAutoGrow(element) { + if (!element?.style) return; + element.style.height = "1px"; + element.style.height = (element.scrollHeight + 2) + "px"; } - function formatJson (inJSON) { - if (typeof inJSON !== 'string' || inJSON === '') return inJSON + function formatJson(inJSON) { + if (typeof inJSON !== "string" || inJSON === "") return inJSON; try { - return JSON.stringify(JSON.parse(inJSON), null, 2) - } catch (e) { - return inJSON + return JSON.stringify(JSON.parse(inJSON), null, 2); + } catch { + return inJSON; } } - window.debounce = debounce - window.copyToClipboard = copyToClipboard - window.textareaAutoGrow = textareaAutoGrow - window.formatJson = formatJson + window.debounce = debounce; + window.copyToClipboard = copyToClipboard; + window.textareaAutoGrow = textareaAutoGrow; + window.formatJson = formatJson; } diff --git a/internal/view/static/js/init-htmx.js b/internal/view/static/js/init-htmx.js index 0bea7db..d79269f 100644 --- a/internal/view/static/js/init-htmx.js +++ b/internal/view/static/js/init-htmx.js @@ -1,72 +1,72 @@ -export function initHTMX () { +export function initHTMX() { const triggers = { ctm_alert: function (evt) { - const message = decodeURIComponent(evt.detail.value) - window.swalAlert(message) + const message = decodeURIComponent(evt.detail.value); + window.swalAlert(message); }, ctm_alert_with_refresh: function (evt) { - const message = decodeURIComponent(evt.detail.value) + const message = decodeURIComponent(evt.detail.value); window.swalAlert(message).then(() => { - location.reload() - }) + location.reload(); + }); }, ctm_alert_with_redirect: function (evt) { - const payload = decodeURIComponent(evt.detail.value) - const parts = payload.split('-::-::-') + const payload = decodeURIComponent(evt.detail.value); + const parts = payload.split("-::-::-"); if (parts.length !== 2) { - return + return; } - const message = parts[0] - const url = parts[1] + const message = parts[0]; + const url = parts[1]; window.swalAlert(message).then(() => { - location.href = url - }) + location.href = url; + }); }, ctm_toast_success: function (evt) { - const message = decodeURIComponent(evt.detail.value) - toaster.success(message) + const message = decodeURIComponent(evt.detail.value); + toaster.success(message); }, ctm_toast_error: function (evt) { - const message = decodeURIComponent(evt.detail.value) - toaster.error(message) + const message = decodeURIComponent(evt.detail.value); + toaster.error(message); }, ctm_toast_success_infinite: function (evt) { - const message = decodeURIComponent(evt.detail.value) - toaster.successInfinite(message) + const message = decodeURIComponent(evt.detail.value); + toaster.successInfinite(message); }, ctm_toast_error_infinite: function (evt) { - const message = decodeURIComponent(evt.detail.value) - toaster.errorInfinite(message) - } - } + const message = decodeURIComponent(evt.detail.value); + toaster.errorInfinite(message); + }, + }; for (const key in triggers) { - document.addEventListener(key, triggers[key]) + document.addEventListener(key, triggers[key]); } // Add trigger to use sweetalert2 for confirms - document.addEventListener('htmx:confirm', function (e) { - if (!e.detail.target.hasAttribute('hx-confirm')) return + document.addEventListener("htmx:confirm", function (e) { + if (!e.detail.target.hasAttribute("hx-confirm")) return; - e.preventDefault() + e.preventDefault(); window.swalConfirm(e.detail.question).then(function (result) { - if (result.isConfirmed) e.detail.issueRequest(true) - }) - }) + if (result.isConfirmed) e.detail.issueRequest(true); + }); + }); // This fixes this issue: // https://stackoverflow.com/questions/73658449/htmx-request-not-firing-when-hx-attributes-are-added-dynamically-from-javascrip - document.addEventListener('DOMContentLoaded', function () { + document.addEventListener("DOMContentLoaded", function () { const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { mutation.addedNodes.forEach((node) => { - if (node.nodeType === 1 && !node['htmx-internal-data']) { - htmx.process(node) + if (node.nodeType === 1 && !node["htmx-internal-data"]) { + htmx.process(node); } - }) - }) - }) - observer.observe(document, { childList: true, subtree: true }) - }) + }); + }); + }); + observer.observe(document, { childList: true, subtree: true }); + }); } diff --git a/internal/view/static/js/init-notyf.js b/internal/view/static/js/init-notyf.js index cf48093..89dc36d 100644 --- a/internal/view/static/js/init-notyf.js +++ b/internal/view/static/js/init-notyf.js @@ -1,53 +1,53 @@ -export function initNotyf () { - let toastQueue = [] +export function initNotyf() { + let toastQueue = []; const toastCfg = { duration: 5000, ripple: false, - position: { x: 'right', y: 'bottom' }, - dismissible: true - } + position: { x: "right", y: "bottom" }, + dismissible: true, + }; const infiniteToastCfg = { ...toastCfg, - duration: 0 - } + duration: 0, + }; window.toaster = { success: (message) => { - toastQueue.push({ type: 'success', message, config: toastCfg }) + toastQueue.push({ type: "success", message, config: toastCfg }); }, error: (message) => { - toastQueue.push({ type: 'error', message, config: toastCfg }) + toastQueue.push({ type: "error", message, config: toastCfg }); }, successInfinite: (message) => { - toastQueue.push({ type: 'success', message, config: infiniteToastCfg }) + toastQueue.push({ type: "success", message, config: infiniteToastCfg }); }, errorInfinite: (message) => { - toastQueue.push({ type: 'error', message, config: infiniteToastCfg }) - } - } + toastQueue.push({ type: "error", message, config: infiniteToastCfg }); + }, + }; - document.addEventListener('DOMContentLoaded', function () { - const notyf = new Notyf() + document.addEventListener("DOMContentLoaded", function () { + const notyf = new Notyf(); - toastQueue.forEach(item => { - notyf.open({ type: item.type, message: item.message, ...item.config }) - }) + toastQueue.forEach((item) => { + notyf.open({ type: item.type, message: item.message, ...item.config }); + }); - toastQueue = [] + toastQueue = []; window.toaster.success = (message) => { - notyf.open({ type: 'success', message, ...toastCfg }) - } + notyf.open({ type: "success", message, ...toastCfg }); + }; window.toaster.error = (message) => { - notyf.open({ type: 'error', message, ...toastCfg }) - } + notyf.open({ type: "error", message, ...toastCfg }); + }; window.toaster.successInfinite = (message) => { - notyf.open({ type: 'success', message, ...infiniteToastCfg }) - } + notyf.open({ type: "success", message, ...infiniteToastCfg }); + }; window.toaster.errorInfinite = (message) => { - notyf.open({ type: 'error', message, ...infiniteToastCfg }) - } - }) + notyf.open({ type: "error", message, ...infiniteToastCfg }); + }; + }); } diff --git a/internal/view/static/js/init-sweetalert2.js b/internal/view/static/js/init-sweetalert2.js index e82c6eb..2064101 100644 --- a/internal/view/static/js/init-sweetalert2.js +++ b/internal/view/static/js/init-sweetalert2.js @@ -1,34 +1,34 @@ -export function initSweetAlert2 () { +export function initSweetAlert2() { // Docs at https://sweetalert2.github.io/#configuration const defaultConfig = { - icon: 'info', - confirmButtonText: 'Okay', - cancelButtonText: 'Cancel', + icon: "info", + confirmButtonText: "Okay", + cancelButtonText: "Cancel", customClass: { - popup: 'rounded-box bg-base-100 text-base-content', - confirmButton: 'btn btn-primary', - denyButton: 'btn btn-warning', - cancelButton: 'btn btn-error' - } - } + popup: "rounded-box bg-base-100 text-base-content", + confirmButton: "btn btn-primary", + denyButton: "btn btn-warning", + cancelButton: "btn btn-error", + }, + }; - async function swalAlert (text) { - return Swal.fire({ + async function swalAlert(text) { + return await Swal.fire({ ...defaultConfig, - title: text - }) - } - - async function swalConfirm (text) { - return Swal.fire({ - ...defaultConfig, - icon: 'question', title: text, - confirmButtonText: 'Confirm', - showCancelButton: true - }) + }); } - window.swalAlert = swalAlert - window.swalConfirm = swalConfirm + async function swalConfirm(text) { + return await Swal.fire({ + ...defaultConfig, + icon: "question", + title: text, + confirmButtonText: "Confirm", + showCancelButton: true, + }); + } + + window.swalAlert = swalAlert; + window.swalConfirm = swalConfirm; } diff --git a/internal/view/static/js/init-theme-helper.js b/internal/view/static/js/init-theme-helper.js index 3f4dcad..6d42b7c 100644 --- a/internal/view/static/js/init-theme-helper.js +++ b/internal/view/static/js/init-theme-helper.js @@ -1,17 +1,17 @@ -export function initThemeHelper () { - function getTheme () { - const theme = localStorage.getItem('theme') - return theme || '' +export function initThemeHelper() { + function getTheme() { + const theme = localStorage.getItem("theme"); + return theme || ""; } - function setTheme (theme) { - localStorage.setItem('theme', theme) - document.documentElement.setAttribute('data-theme', theme) + function setTheme(theme) { + localStorage.setItem("theme", theme); + document.documentElement.setAttribute("data-theme", theme); } - window.getTheme = getTheme - window.setTheme = setTheme + window.getTheme = getTheme; + window.setTheme = setTheme; - const theme = getTheme() - setTheme(theme) + const theme = getTheme(); + setTheme(theme); } diff --git a/internal/view/web/component/change_theme_button.inc.js b/internal/view/web/component/change_theme_button.inc.js index f4f27fe..0530642 100644 --- a/internal/view/web/component/change_theme_button.inc.js +++ b/internal/view/web/component/change_theme_button.inc.js @@ -1,19 +1,19 @@ window.alpineChangeThemeButton = function () { return { - theme: '', + theme: "", - loadTheme () { - const theme = window.getTheme() - this.theme = theme || 'system' + loadTheme() { + const theme = window.getTheme(); + this.theme = theme || "system"; }, - setTheme (theme) { - window.setTheme(theme) - this.theme = theme || 'system' + setTheme(theme) { + window.setTheme(theme); + this.theme = theme || "system"; }, - init () { - this.loadTheme() - } - } -} + init() { + this.loadTheme(); + }, + }; +}; diff --git a/internal/view/web/component/options_dropdown.inc.js b/internal/view/web/component/options_dropdown.inc.js index 51dadf0..9e39290 100644 --- a/internal/view/web/component/options_dropdown.inc.js +++ b/internal/view/web/component/options_dropdown.inc.js @@ -5,42 +5,43 @@ window.alpineOptionsDropdown = function () { contentEl: null, closeTimeout: null, - init () { - this.buttonEl = this.$refs.button - this.contentEl = this.$refs.content + init() { + this.buttonEl = this.$refs.button; + this.contentEl = this.$refs.content; }, - open () { - this.isOpen = true - this.contentEl.classList.remove('hidden') - this.positionContent() + open() { + this.isOpen = true; + this.contentEl.classList.remove("hidden"); + this.positionContent(); if (this.closeTimeout) { - clearTimeout(this.closeTimeout) - this.closeTimeout = null + clearTimeout(this.closeTimeout); + this.closeTimeout = null; } }, - close () { + close() { this.closeTimeout = setTimeout(() => { - this.isOpen = false - this.contentEl.classList.add('hidden') - }, 200) + this.isOpen = false; + this.contentEl.classList.add("hidden"); + }, 200); }, - positionContent () { - const buttonRect = this.buttonEl.getBoundingClientRect() - const contentHeight = this.contentEl.offsetHeight - const windowHeight = window.innerHeight - const moreSpaceBelow = (windowHeight - buttonRect.bottom) > buttonRect.top + positionContent() { + const buttonRect = this.buttonEl.getBoundingClientRect(); + const contentHeight = this.contentEl.offsetHeight; + const windowHeight = window.innerHeight; + const moreSpaceBelow = + (windowHeight - buttonRect.bottom) > buttonRect.top; - this.contentEl.style.left = `${buttonRect.left}px` + this.contentEl.style.left = `${buttonRect.left}px`; if (moreSpaceBelow) { - this.contentEl.style.top = `${buttonRect.bottom}px` + this.contentEl.style.top = `${buttonRect.bottom}px`; } else { - this.contentEl.style.top = `${buttonRect.top - contentHeight}px` + this.contentEl.style.top = `${buttonRect.top - contentHeight}px`; } - } - } -} + }, + }; +}; diff --git a/internal/view/web/component/star_on_github.inc.js b/internal/view/web/component/star_on_github.inc.js index d2cf1d8..b5bc91e 100644 --- a/internal/view/web/component/star_on_github.inc.js +++ b/internal/view/web/component/star_on_github.inc.js @@ -2,41 +2,41 @@ window.alpineStarOnGithub = function () { return { stars: null, - async init () { - const stars = await this.getStars() + async init() { + const stars = await this.getStars(); if (stars !== null) { - this.stars = stars + this.stars = stars; } }, - async getStars () { - const cacheKey = 'pbw-gh-stars' + async getStars() { + const cacheKey = "pbw-gh-stars"; - const cachedJSON = localStorage.getItem(cacheKey) + const cachedJSON = localStorage.getItem(cacheKey); if (cachedJSON) { - const cached = JSON.parse(cachedJSON) + const cached = JSON.parse(cachedJSON); if (Date.now() - cached.timestamp < 2 * 60 * 1000) { - return cached.value + return cached.value; } } - const url = 'https://api.github.com/repos/eduardolat/pgbackweb' + const url = "https://api.github.com/repos/eduardolat/pgbackweb"; try { - const response = await fetch(url) + const response = await fetch(url); if (!response.ok) { - return null + return null; } - const data = await response.json() - const value = data.stargazers_count + const data = await response.json(); + const value = data.stargazers_count; const dataToCache = JSON.stringify({ value, - timestamp: Date.now() - }) - localStorage.setItem(cacheKey, dataToCache) - return value + timestamp: Date.now(), + }); + localStorage.setItem(cacheKey, dataToCache); + return value; } catch { - return null + return null; } - } - } -} + }, + }; +}; diff --git a/internal/view/web/dashboard/summary/index_how_to.inc.js b/internal/view/web/dashboard/summary/index_how_to.inc.js index a17a7df..3ecd419 100644 --- a/internal/view/web/dashboard/summary/index_how_to.inc.js +++ b/internal/view/web/dashboard/summary/index_how_to.inc.js @@ -3,20 +3,20 @@ window.alpineSummaryHowToSlider = function () { slidesQty: 4, currentSlide: 1, - get hasNextSlide () { - return this.currentSlide < this.slidesQty + get hasNextSlide() { + return this.currentSlide < this.slidesQty; }, - get hasPrevSlide () { - return this.currentSlide > 1 + get hasPrevSlide() { + return this.currentSlide > 1; }, - nextSlide () { - if (this.hasNextSlide) this.currentSlide++ + nextSlide() { + if (this.hasNextSlide) this.currentSlide++; }, - prevSlide () { - if (this.hasPrevSlide) this.currentSlide-- - } - } -} + prevSlide() { + if (this.hasPrevSlide) this.currentSlide--; + }, + }; +}; diff --git a/internal/view/web/layout/dashboard_aside.inc.js b/internal/view/web/layout/dashboard_aside.inc.js index 2f136ee..5fcf3ec 100644 --- a/internal/view/web/layout/dashboard_aside.inc.js +++ b/internal/view/web/layout/dashboard_aside.inc.js @@ -1,53 +1,53 @@ -document.addEventListener('DOMContentLoaded', function () { - const el = document.getElementById('dashboard-aside') - const key = 'dashboard-aside-scroll-position' +document.addEventListener("DOMContentLoaded", function () { + const el = document.getElementById("dashboard-aside"); + const key = "dashboard-aside-scroll-position"; - if (!el) return + if (!el) return; const saveScrollPosition = window.debounce( () => { - const scrollPosition = el.scrollTop - localStorage.setItem(key, scrollPosition) + const scrollPosition = el.scrollTop; + localStorage.setItem(key, scrollPosition); }, - 200 - ) - el.addEventListener('scroll', saveScrollPosition) + 200, + ); + el.addEventListener("scroll", saveScrollPosition); - const scrollPosition = localStorage.getItem(key) + const scrollPosition = localStorage.getItem(key); if (scrollPosition) { - el.scrollTop = parseInt(scrollPosition, 10) + el.scrollTop = parseInt(scrollPosition, 10); } -}) +}); -window.alpineDashboardAsideItem = function (link = '', strict = false) { +window.alpineDashboardAsideItem = function (link = "", strict = false) { return { link, strict, is_active: false, - checkActive () { + checkActive() { if (this.strict) { - this.is_active = window.location.pathname === this.link - return + this.is_active = window.location.pathname === this.link; + return; } - this.is_active = window.location.pathname.startsWith(this.link) + this.is_active = window.location.pathname.startsWith(this.link); }, - init () { - this.checkActive() + init() { + this.checkActive(); - const originalPushState = window.history.pushState + const originalPushState = window.history.pushState; window.history.pushState = (...args) => { - originalPushState.apply(window.history, args) - this.checkActive() - } + originalPushState.apply(window.history, args); + this.checkActive(); + }; - const originalReplaceState = window.history.replaceState + const originalReplaceState = window.history.replaceState; window.history.replaceState = (...args) => { - originalReplaceState.apply(window.history, args) - this.checkActive() - } - } - } -} + originalReplaceState.apply(window.history, args); + this.checkActive(); + }; + }, + }; +}; diff --git a/internal/view/web/layout/dashboard_header_updates.inc.js b/internal/view/web/layout/dashboard_header_updates.inc.js index 1710a8f..e4d7b2f 100644 --- a/internal/view/web/layout/dashboard_header_updates.inc.js +++ b/internal/view/web/layout/dashboard_header_updates.inc.js @@ -2,41 +2,42 @@ window.alpineDashboardHeaderUpdates = function () { return { latestRelease: null, - async init () { - const latestRelease = await this.getLatestRelease() + async init() { + const latestRelease = await this.getLatestRelease(); if (latestRelease !== null) { - this.latestRelease = latestRelease + this.latestRelease = latestRelease; } }, - async getLatestRelease () { - const cacheKey = 'pbw-gh-last-release' + async getLatestRelease() { + const cacheKey = "pbw-gh-last-release"; - const cachedJSON = localStorage.getItem(cacheKey) + const cachedJSON = localStorage.getItem(cacheKey); if (cachedJSON) { - const cached = JSON.parse(cachedJSON) + const cached = JSON.parse(cachedJSON); if (Date.now() - cached.timestamp < 2 * 60 * 1000) { - return cached.value + return cached.value; } } - const url = 'https://api.github.com/repos/eduardolat/pgbackweb/releases/latest' + const url = + "https://api.github.com/repos/eduardolat/pgbackweb/releases/latest"; try { - const response = await fetch(url) + const response = await fetch(url); if (!response.ok) { - return null + return null; } - const data = await response.json() - const value = data.name + const data = await response.json(); + const value = data.name; const dataToCache = JSON.stringify({ value, - timestamp: Date.now() - }) - localStorage.setItem(cacheKey, dataToCache) - return value + timestamp: Date.now(), + }); + localStorage.setItem(cacheKey, dataToCache); + return value; } catch { - return null + return null; } - } - } -} + }, + }; +}; diff --git a/tailwind.config.js b/tailwind.config.js index 66c6804..a8e351f 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,29 +1,29 @@ /** @type {import('tailwindcss').Config} */ module.exports = { - content: ['./internal/view/web/**/*.go'], - plugins: [require('daisyui')], + content: ["./internal/view/web/**/*.go"], + plugins: [require("daisyui")], daisyui: { logs: false, themes: [ { light: { - ...require('daisyui/src/theming/themes').light, - primary: '#2be7c8', - 'success-content': '#ffffff', - 'error-content': '#ffffff' + ...require("daisyui/src/theming/themes").light, + primary: "#2be7c8", + "success-content": "#ffffff", + "error-content": "#ffffff", }, dark: { - ...require('daisyui/src/theming/themes').dracula, - primary: '#2be7c8' - } - } + ...require("daisyui/src/theming/themes").dracula, + primary: "#2be7c8", + }, + }, ], - darkTheme: 'dark' + darkTheme: "dark", }, theme: { screens: { - desk: '768px' // only one breakpoint to keep it simple + desk: "768px", // only one breakpoint to keep it simple }, - extend: {} - } -} + extend: {}, + }, +}; diff --git a/taskfile.yaml b/taskfile.yaml index 94d225d..33b1d5f 100644 --- a/taskfile.yaml +++ b/taskfile.yaml @@ -88,11 +88,11 @@ tasks: silent: true cmds: - > - npm run tailwindcss -- - --minify - --config ./tailwind.config.js - --input ./internal/view/static/css/style.css - --output ./internal/view/static/build/style.min.css + npm run tailwindcss -- + --minify + --config ./tailwind.config.js + --input ./internal/view/static/css/style.css + --output ./internal/view/static/build/style.min.css - ./scripts/build-js.mjs tidy: