From 5d6916dcf0c000bbd15631b95e11b3e920c710bf Mon Sep 17 00:00:00 2001 From: Jamie Curnow Date: Mon, 27 Oct 2025 23:59:00 +1000 Subject: [PATCH] Tidy up - Add help docs for most sections - Add translations documentation - Fix up todos - Remove german translation --- frontend/check-locales.cjs | 21 +- frontend/package.json | 1 + frontend/src/api/backend/createAccessList.ts | 1 - frontend/src/api/backend/createDeadHost.ts | 1 - frontend/src/api/backend/createProxyHost.ts | 1 - .../src/api/backend/createRedirectionHost.ts | 1 - frontend/src/api/backend/createStream.ts | 1 - frontend/src/api/backend/createUser.ts | 1 - frontend/src/locale/IntlProvider.tsx | 7 - frontend/src/locale/README.md | 41 +- frontend/src/locale/lang/de.json | 3 - frontend/src/locale/lang/en.json | 4 + .../src/locale/src/HelpDoc/en/AccessLists.md | 7 + .../src/locale/src/HelpDoc/en/Certificates.md | 32 ++ .../src/locale/src/HelpDoc/en/DeadHosts.md | 10 + .../src/locale/src/HelpDoc/en/ProxyHosts.md | 7 + .../locale/src/HelpDoc/en/RedirectionHosts.md | 7 + frontend/src/locale/src/HelpDoc/en/Streams.md | 6 + frontend/src/locale/src/HelpDoc/en/index.ts | 6 + frontend/src/locale/src/HelpDoc/index.ts | 20 + frontend/src/locale/src/de.json | 5 - frontend/src/locale/src/en.json | 12 + frontend/src/modals/DeleteConfirmModal.tsx | 22 +- frontend/src/modals/HelpModal.tsx | 54 +++ frontend/src/modals/index.ts | 1 + frontend/src/modules/Validations.tsx | 12 +- frontend/src/pages/Access/Table.tsx | 2 +- frontend/src/pages/Access/TableWrapper.tsx | 22 +- .../src/pages/Certificates/TableWrapper.tsx | 21 +- frontend/src/pages/Dashboard/index.tsx | 5 - frontend/src/pages/Nginx/DeadHosts/Table.tsx | 2 +- .../pages/Nginx/DeadHosts/TableWrapper.tsx | 20 +- .../pages/Nginx/ProxyHosts/TableWrapper.tsx | 20 +- .../Nginx/RedirectionHosts/TableWrapper.tsx | 20 +- frontend/src/pages/Nginx/Streams/Table.tsx | 2 +- .../src/pages/Nginx/Streams/TableWrapper.tsx | 20 +- frontend/vite.config.ts | 1 + frontend/yarn.lock | 382 +++++++++++++++++- 38 files changed, 686 insertions(+), 115 deletions(-) delete mode 100644 frontend/src/locale/lang/de.json create mode 100644 frontend/src/locale/src/HelpDoc/en/AccessLists.md create mode 100644 frontend/src/locale/src/HelpDoc/en/Certificates.md create mode 100644 frontend/src/locale/src/HelpDoc/en/DeadHosts.md create mode 100644 frontend/src/locale/src/HelpDoc/en/ProxyHosts.md create mode 100644 frontend/src/locale/src/HelpDoc/en/RedirectionHosts.md create mode 100644 frontend/src/locale/src/HelpDoc/en/Streams.md create mode 100644 frontend/src/locale/src/HelpDoc/en/index.ts create mode 100644 frontend/src/locale/src/HelpDoc/index.ts delete mode 100644 frontend/src/locale/src/de.json create mode 100644 frontend/src/modals/HelpModal.tsx diff --git a/frontend/check-locales.cjs b/frontend/check-locales.cjs index 6a91dcf3..03a8be14 100755 --- a/frontend/check-locales.cjs +++ b/frontend/check-locales.cjs @@ -8,14 +8,11 @@ const allLocales = [ ["en", "en-US"], - ["de", "de-DE"], ["fa", "fa-IR"], ]; const ignoreUnused = [ - /^capability\..*$/, - /^status\..*$/, - /^type\..*$/, + /^.*$/, ]; const { spawnSync } = require("child_process"); @@ -119,19 +116,9 @@ const compareLocale = (locale) => { const checkForMissing = (locale) => { allKeys.forEach((key) => { if (typeof locale.data[key] === "undefined") { - let ignored = false; - ignoreMissing.map((regex) => { - if (key.match(regex)) { - ignored = true; - } - return null; - }); - - if (!ignored) { - allWarnings.push( - "WARN: `" + locale[0] + "` does not contain item: `" + key + "`", - ); - } + allWarnings.push( + "WARN: `" + locale[0] + "` does not contain item: `" + key + "`", + ); } return null; }); diff --git a/frontend/package.json b/frontend/package.json index b17e3271..f6aa84db 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -33,6 +33,7 @@ "react-bootstrap": "^2.10.10", "react-dom": "^19.2.0", "react-intl": "^7.1.14", + "react-markdown": "^10.1.0", "react-router-dom": "^7.9.4", "react-select": "^5.10.2", "react-toastify": "^11.0.5", diff --git a/frontend/src/api/backend/createAccessList.ts b/frontend/src/api/backend/createAccessList.ts index 8135a976..4a17f675 100644 --- a/frontend/src/api/backend/createAccessList.ts +++ b/frontend/src/api/backend/createAccessList.ts @@ -4,7 +4,6 @@ import type { AccessList } from "./models"; export async function createAccessList(item: AccessList): Promise { return await api.post({ url: "/nginx/access-lists", - // todo: only use whitelist of fields for this data data: item, }); } diff --git a/frontend/src/api/backend/createDeadHost.ts b/frontend/src/api/backend/createDeadHost.ts index 521075a3..f2a9f450 100644 --- a/frontend/src/api/backend/createDeadHost.ts +++ b/frontend/src/api/backend/createDeadHost.ts @@ -4,7 +4,6 @@ import type { DeadHost } from "./models"; export async function createDeadHost(item: DeadHost): Promise { return await api.post({ url: "/nginx/dead-hosts", - // todo: only use whitelist of fields for this data data: item, }); } diff --git a/frontend/src/api/backend/createProxyHost.ts b/frontend/src/api/backend/createProxyHost.ts index 830fb22e..fcde7cd6 100644 --- a/frontend/src/api/backend/createProxyHost.ts +++ b/frontend/src/api/backend/createProxyHost.ts @@ -4,7 +4,6 @@ import type { ProxyHost } from "./models"; export async function createProxyHost(item: ProxyHost): Promise { return await api.post({ url: "/nginx/proxy-hosts", - // todo: only use whitelist of fields for this data data: item, }); } diff --git a/frontend/src/api/backend/createRedirectionHost.ts b/frontend/src/api/backend/createRedirectionHost.ts index 83ae6914..a797f819 100644 --- a/frontend/src/api/backend/createRedirectionHost.ts +++ b/frontend/src/api/backend/createRedirectionHost.ts @@ -4,7 +4,6 @@ import type { RedirectionHost } from "./models"; export async function createRedirectionHost(item: RedirectionHost): Promise { return await api.post({ url: "/nginx/redirection-hosts", - // todo: only use whitelist of fields for this data data: item, }); } diff --git a/frontend/src/api/backend/createStream.ts b/frontend/src/api/backend/createStream.ts index e5f84605..adad2a1e 100644 --- a/frontend/src/api/backend/createStream.ts +++ b/frontend/src/api/backend/createStream.ts @@ -4,7 +4,6 @@ import type { Stream } from "./models"; export async function createStream(item: Stream): Promise { return await api.post({ url: "/nginx/streams", - // todo: only use whitelist of fields for this data data: item, }); } diff --git a/frontend/src/api/backend/createUser.ts b/frontend/src/api/backend/createUser.ts index dcaa5feb..c5ea306d 100644 --- a/frontend/src/api/backend/createUser.ts +++ b/frontend/src/api/backend/createUser.ts @@ -18,7 +18,6 @@ export interface NewUser { export async function createUser(item: NewUser, noAuth?: boolean): Promise { return await api.post({ url: "/users", - // todo: only use whitelist of fields for this data data: item, noAuth, }); diff --git a/frontend/src/locale/IntlProvider.tsx b/frontend/src/locale/IntlProvider.tsx index 683a8e48..8c4a7029 100644 --- a/frontend/src/locale/IntlProvider.tsx +++ b/frontend/src/locale/IntlProvider.tsx @@ -1,5 +1,4 @@ import { createIntl, createIntlCache } from "react-intl"; -import langDe from "./lang/de.json"; import langEn from "./lang/en.json"; import langFa from "./lang/fa.json"; import langList from "./lang/lang-list.json"; @@ -9,15 +8,12 @@ import langList from "./lang/lang-list.json"; // Remember when adding to this list, also update check-locales.js script const localeOptions = [ ["en", "en-US"], - ["de", "de-DE"], ["fa", "fa-IR"], ]; const loadMessages = (locale?: string): typeof langList & typeof langEn => { const thisLocale = locale || "en"; switch (thisLocale.slice(0, 2)) { - case "de": - return Object.assign({}, langList, langEn, langDe); case "fa": return Object.assign({}, langList, langEn, langFa); default: @@ -27,9 +23,6 @@ const loadMessages = (locale?: string): typeof langList & typeof langEn => { const getFlagCodeForLocale = (locale?: string) => { switch (locale) { - case "de-DE": - case "de": - return "DE"; case "fa-IR": case "fa": return "IR"; diff --git a/frontend/src/locale/README.md b/frontend/src/locale/README.md index 94275c8f..2c3f9cd1 100644 --- a/frontend/src/locale/README.md +++ b/frontend/src/locale/README.md @@ -1,23 +1,48 @@ # Internationalisation support +## Before you start + +It's highly recommended that you spin up a development instance of this project +on your docker capable server. It's pretty easy: + +```bash +git clone https://github.com/NginxProxyManager/nginx-proxy-manager.git +cd nginx-proxy-manager +./scripts/start-dev -f +``` + +Then after a while, you can access http://yourserverip:3081 + +This stack will watch the file system for changes, especially to language files, +and reload the site you have open in the browser. + + ## Adding new translations Modify the files in the `src` folder. Follow the conventions already there. +When the development stack is running, it will sort the locale lang files +for you when you save. + ## After making changes -You will need to run `yarn locale-compile` in this frontend folder for +If you're NOT running the development stack, you will need to run +`yarn locale-compile` in the `frontend` folder for the new translations to be compiled into the `lang` folder. -When running in dev mode, this should automatically happen within Vite. + +## Adding a whole new language + +There's a fair bit you'll need to touch. Here's a list that may +not be complete by the time you're reading this: + +- frontend/src/locale/src/[yourlang].json +- frontend/src/locale/src/lang-list.json +- frontend/src/locale/src/HelpDoc/* +- frontend/src/locale/IntlProvider.tsx -## Checking for missing translations in other languages +## Checking for missing translations in languages Run `node check-locales.cjs` in this frontend folder. - - -## Adding new languages - -todo diff --git a/frontend/src/locale/lang/de.json b/frontend/src/locale/lang/de.json deleted file mode 100644 index 809e5291..00000000 --- a/frontend/src/locale/lang/de.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "dashboard": "Armaturenbrett" -} \ No newline at end of file diff --git a/frontend/src/locale/lang/en.json b/frontend/src/locale/lang/en.json index b3483e5a..a428fbbc 100644 --- a/frontend/src/locale/lang/en.json +++ b/frontend/src/locale/lang/en.json @@ -100,7 +100,11 @@ "error.invalid-auth": "Invalid email or password", "error.invalid-domain": "Invalid domain: {domain}", "error.invalid-email": "Invalid email address", + "error.max-character-length": "Maximum length is {max} character{max, plural, one {} other {s}}", "error.max-domains": "Too many domains, max is {max}", + "error.maximum": "Maximum is {max}", + "error.min-character-length": "Minimum length is {min} character{min, plural, one {} other {s}}", + "error.minimum": "Minimum is {min}", "error.passwords-must-match": "Passwords must match", "error.required": "This is required", "expires.on": "Expires: {date}", diff --git a/frontend/src/locale/src/HelpDoc/en/AccessLists.md b/frontend/src/locale/src/HelpDoc/en/AccessLists.md new file mode 100644 index 00000000..cef58263 --- /dev/null +++ b/frontend/src/locale/src/HelpDoc/en/AccessLists.md @@ -0,0 +1,7 @@ +## What is an Access List? + +Access Lists provide a blacklist or whitelist of specific client IP addresses along with authentication for the Proxy Hosts via Basic HTTP Authentication. + +You can configure multiple client rules, usernames and passwords for a single Access List and then apply that to one or more _Proxy Hosts_. + +This is most useful for forwarded web services that do not have authentication mechanisms built in or when you want to protect from unknown clients. diff --git a/frontend/src/locale/src/HelpDoc/en/Certificates.md b/frontend/src/locale/src/HelpDoc/en/Certificates.md new file mode 100644 index 00000000..d79dd04b --- /dev/null +++ b/frontend/src/locale/src/HelpDoc/en/Certificates.md @@ -0,0 +1,32 @@ +## Certificates Help + +### HTTP Certificate + +A HTTP validated certificate means Let's Encrypt servers will +attempt to reach your domains over HTTP (not HTTPS!) and if successful, they +will issue your certificate. + +For this method, you will have to have a _Proxy Host_ created for your domains(s) that +is accessible with HTTP and pointing to this Nginx installation. After a certificate +has been given, you can modify the _Proxy Host_ to also use this certificate for HTTPS +connections. However, the _Proxy Host_ will still need to be configured for HTTP access +in order for the certificate to renew. + +This process _does not_ support wildcard domains. + +### DNS Certificate + +A DNS validated certificate requires you to use a DNS Provider plugin. This DNS +Provider will be used to create temporary records on your domain and then Let's +Encrypt will query those records to be sure you're the owner and if successful, they +will issue your certificate. + +You do not need a _Proxy Host_ to be created prior to requesting this type of +certificate. Nor do you need to have your _Proxy Host_ configured for HTTP access. + +This process _does_ support wildcard domains. + +### Custom Certificate + +Use this option to upload your own SSL Certificate, as provided by your own +Certificate Authority. diff --git a/frontend/src/locale/src/HelpDoc/en/DeadHosts.md b/frontend/src/locale/src/HelpDoc/en/DeadHosts.md new file mode 100644 index 00000000..ef4f3bc4 --- /dev/null +++ b/frontend/src/locale/src/HelpDoc/en/DeadHosts.md @@ -0,0 +1,10 @@ +## What is a 404 Host? + +A 404 Host is simply a host setup that shows a 404 page. + +This can be useful when your domain is listed in search engines and you want +to provide a nicer error page or specifically to tell the search indexers that +the domain pages no longer exist. + +Another benefit of having this host is to track the logs for hits to it and +view the referrers. diff --git a/frontend/src/locale/src/HelpDoc/en/ProxyHosts.md b/frontend/src/locale/src/HelpDoc/en/ProxyHosts.md new file mode 100644 index 00000000..e9630d05 --- /dev/null +++ b/frontend/src/locale/src/HelpDoc/en/ProxyHosts.md @@ -0,0 +1,7 @@ +## What is a Proxy Host? + +A Proxy Host is the incoming endpoint for a web service that you want to forward. + +It provides optional SSL termination for your service that might not have SSL support built in. + +Proxy Hosts are the most common use for the Nginx Proxy Manager. diff --git a/frontend/src/locale/src/HelpDoc/en/RedirectionHosts.md b/frontend/src/locale/src/HelpDoc/en/RedirectionHosts.md new file mode 100644 index 00000000..e57b1b80 --- /dev/null +++ b/frontend/src/locale/src/HelpDoc/en/RedirectionHosts.md @@ -0,0 +1,7 @@ +## What is a Redirection Host? + +A Redirection Host will redirect requests from the incoming domain and push the +viewer to another domain. + +The most common reason to use this type of host is when your website changes +domains but you still have search engine or referrer links pointing to the old domain. diff --git a/frontend/src/locale/src/HelpDoc/en/Streams.md b/frontend/src/locale/src/HelpDoc/en/Streams.md new file mode 100644 index 00000000..358f3e5a --- /dev/null +++ b/frontend/src/locale/src/HelpDoc/en/Streams.md @@ -0,0 +1,6 @@ +## What is a Stream? + +A relatively new feature for Nginx, a Stream will serve to forward TCP/UDP +traffic directly to another computer on the network. + +If you're running game servers, FTP or SSH servers this can come in handy. diff --git a/frontend/src/locale/src/HelpDoc/en/index.ts b/frontend/src/locale/src/HelpDoc/en/index.ts new file mode 100644 index 00000000..a9bb46ba --- /dev/null +++ b/frontend/src/locale/src/HelpDoc/en/index.ts @@ -0,0 +1,6 @@ +export * as AccessLists from "./AccessLists.md"; +export * as Certificates from "./Certificates.md"; +export * as DeadHosts from "./DeadHosts.md"; +export * as ProxyHosts from "./ProxyHosts.md"; +export * as RedirectionHosts from "./RedirectionHosts.md"; +export * as Streams from "./Streams.md"; diff --git a/frontend/src/locale/src/HelpDoc/index.ts b/frontend/src/locale/src/HelpDoc/index.ts new file mode 100644 index 00000000..40ca1b52 --- /dev/null +++ b/frontend/src/locale/src/HelpDoc/index.ts @@ -0,0 +1,20 @@ +// import * as de from "./de/index"; +// import * as fa from "./fa/index"; +import * as en from "./en/index"; + +const items: any = { en }; + +const fallbackLang = "en"; + +export const getHelpFile = (lang: string, section: string): string => { + if (typeof items[lang] !== "undefined" && typeof items[lang][section] !== "undefined") { + return items[lang][section].default; + } + // Fallback to English + if (typeof items[fallbackLang] !== "undefined" && typeof items[fallbackLang][section] !== "undefined") { + return items[fallbackLang][section].default; + } + throw new Error(`Cannot load help doc for ${lang}-${section}`); +}; + +export default items; diff --git a/frontend/src/locale/src/de.json b/frontend/src/locale/src/de.json deleted file mode 100644 index f6fcf1fd..00000000 --- a/frontend/src/locale/src/de.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "dashboard": { - "defaultMessage": "Armaturenbrett" - } -} diff --git a/frontend/src/locale/src/en.json b/frontend/src/locale/src/en.json index d87579cb..d7ad25d6 100644 --- a/frontend/src/locale/src/en.json +++ b/frontend/src/locale/src/en.json @@ -302,9 +302,21 @@ "error.invalid-email": { "defaultMessage": "Invalid email address" }, + "error.max-character-length": { + "defaultMessage": "Maximum length is {max} character{max, plural, one {} other {s}}" + }, "error.max-domains": { "defaultMessage": "Too many domains, max is {max}" }, + "error.maximum": { + "defaultMessage": "Maximum is {max}" + }, + "error.min-character-length": { + "defaultMessage": "Minimum length is {min} character{min, plural, one {} other {s}}" + }, + "error.minimum": { + "defaultMessage": "Minimum is {min}" + }, "error.passwords-must-match": { "defaultMessage": "Passwords must match" }, diff --git a/frontend/src/modals/DeleteConfirmModal.tsx b/frontend/src/modals/DeleteConfirmModal.tsx index e4f0180a..fb89f052 100644 --- a/frontend/src/modals/DeleteConfirmModal.tsx +++ b/frontend/src/modals/DeleteConfirmModal.tsx @@ -52,7 +52,27 @@ const DeleteConfirmModal = EasyModal.create( setError(null)} dismissible> {error} - {children} +
+ + + + + +
+
{children}
+ + + ); +}); + +export { showHelpModal }; diff --git a/frontend/src/modals/index.ts b/frontend/src/modals/index.ts index 237a07fe..4db26738 100644 --- a/frontend/src/modals/index.ts +++ b/frontend/src/modals/index.ts @@ -5,6 +5,7 @@ export * from "./DeadHostModal"; export * from "./DeleteConfirmModal"; export * from "./DNSCertificateModal"; export * from "./EventDetailsModal"; +export * from "./HelpModal"; export * from "./HTTPCertificateModal"; export * from "./PermissionsModal"; export * from "./ProxyHostModal"; diff --git a/frontend/src/modules/Validations.tsx b/frontend/src/modules/Validations.tsx index bb866218..facb875b 100644 --- a/frontend/src/modules/Validations.tsx +++ b/frontend/src/modules/Validations.tsx @@ -11,12 +11,10 @@ const validateString = (minLength = 0, maxLength = 0) => { return intl.formatMessage({ id: "error.required" }); } if (minLength && value.length < minLength) { - // TODO: i18n - return `Minimum length is ${minLength} character${minLength === 1 ? "" : "s"}`; + return intl.formatMessage({ id: "error.min-character-length" }, { min: minLength }); } if (maxLength && (typeof value === "undefined" || value.length > maxLength)) { - // TODO: i18n - return `Maximum length is ${maxLength} character${maxLength === 1 ? "" : "s"}`; + return intl.formatMessage({ id: "error.max-character-length" }, { max: maxLength }); } }; }; @@ -33,12 +31,10 @@ const validateNumber = (min = -1, max = -1) => { return intl.formatMessage({ id: "error.required" }); } if (min > -1 && int < min) { - // TODO: i18n - return `Minimum is ${min}`; + return intl.formatMessage({ id: "error.minimum" }, { min }); } if (max > -1 && int > max) { - // TODO: i18n - return `Maximum is ${max}`; + return intl.formatMessage({ id: "error.maximum" }, { max }); } }; }; diff --git a/frontend/src/pages/Access/Table.tsx b/frontend/src/pages/Access/Table.tsx index 9fb59ec5..64d97b51 100644 --- a/frontend/src/pages/Access/Table.tsx +++ b/frontend/src/pages/Access/Table.tsx @@ -53,7 +53,7 @@ export default function Table({ data, isFetching, isFiltered, onEdit, onDelete, cell: (info: any) => , }), columnHelper.display({ - id: "id", // todo: not needed for a display? + id: "id", cell: (info: any) => { return ( diff --git a/frontend/src/pages/Access/TableWrapper.tsx b/frontend/src/pages/Access/TableWrapper.tsx index 4c5dacef..e52bc146 100644 --- a/frontend/src/pages/Access/TableWrapper.tsx +++ b/frontend/src/pages/Access/TableWrapper.tsx @@ -1,11 +1,11 @@ -import { IconSearch } from "@tabler/icons-react"; +import { IconHelp, IconSearch } from "@tabler/icons-react"; import { useState } from "react"; import Alert from "react-bootstrap/Alert"; import { deleteAccessList } from "src/api/backend"; import { Button, LoadingPage } from "src/components"; import { useAccessLists } from "src/hooks"; import { T } from "src/locale"; -import { showAccessListModal, showDeleteConfirmModal } from "src/modals"; +import { showAccessListModal, showDeleteConfirmModal, showHelpModal } from "src/modals"; import { showObjectSuccess } from "src/notifications"; import Table from "./Table"; @@ -47,9 +47,10 @@ export default function TableWrapper() { - {data?.length ? ( -
-
+ +
+
+ {data?.length ? (
@@ -62,12 +63,17 @@ export default function TableWrapper() { onChange={(e: any) => setSearch(e.target.value.toLowerCase().trim())} />
+ ) : null} + + {data?.length ? ( -
+ ) : null}
- ) : null} +
showAccessListModal(id)} onDelete={(id: number) => showDeleteConfirmModal({ - title: "access.delete.title", + title: , onConfirm: () => handleDelete(id), invalidations: [["access-lists"], ["access-list", id]], children: , diff --git a/frontend/src/pages/Certificates/TableWrapper.tsx b/frontend/src/pages/Certificates/TableWrapper.tsx index 03399854..2a270d19 100644 --- a/frontend/src/pages/Certificates/TableWrapper.tsx +++ b/frontend/src/pages/Certificates/TableWrapper.tsx @@ -1,14 +1,15 @@ -import { IconSearch } from "@tabler/icons-react"; +import { IconHelp, IconSearch } from "@tabler/icons-react"; import { useState } from "react"; import Alert from "react-bootstrap/Alert"; import { deleteCertificate, downloadCertificate } from "src/api/backend"; -import { LoadingPage } from "src/components"; +import { Button, LoadingPage } from "src/components"; import { useCertificates } from "src/hooks"; import { T } from "src/locale"; import { showCustomCertificateModal, showDeleteConfirmModal, showDNSCertificateModal, + showHelpModal, showHTTPCertificateModal, showRenewCertificateModal, } from "src/modals"; @@ -69,9 +70,10 @@ export default function TableWrapper() { - {data?.length ? ( -
-
+ +
+
+ {data?.length ? (
@@ -84,6 +86,11 @@ export default function TableWrapper() { onChange={(e: any) => setSearch(e.target.value.toLowerCase().trim())} />
+ ) : null} + + {data?.length ? (
-
+ ) : null}
- ) : null} +
{ {`Todo: - check mobile -- use statuses for table formatters where applicable: https://docs.tabler.io/ui/components/statuses -- add help docs for host types - REDO SCREENSHOTS in docs folder -- search codebase for "TODO" -- update documentation to add development notes for translations -- double check output of access field selection on proxy host dialog, after access lists are completed - check permissions in all places More for api, then implement here: diff --git a/frontend/src/pages/Nginx/DeadHosts/Table.tsx b/frontend/src/pages/Nginx/DeadHosts/Table.tsx index 0b4c2b55..fc688389 100644 --- a/frontend/src/pages/Nginx/DeadHosts/Table.tsx +++ b/frontend/src/pages/Nginx/DeadHosts/Table.tsx @@ -58,7 +58,7 @@ export default function Table({ data, isFetching, onEdit, onDelete, onDisableTog }, }), columnHelper.display({ - id: "id", // todo: not needed for a display? + id: "id", cell: (info: any) => { return ( diff --git a/frontend/src/pages/Nginx/DeadHosts/TableWrapper.tsx b/frontend/src/pages/Nginx/DeadHosts/TableWrapper.tsx index 79e97541..ecbfacca 100644 --- a/frontend/src/pages/Nginx/DeadHosts/TableWrapper.tsx +++ b/frontend/src/pages/Nginx/DeadHosts/TableWrapper.tsx @@ -1,4 +1,4 @@ -import { IconSearch } from "@tabler/icons-react"; +import { IconHelp, IconSearch } from "@tabler/icons-react"; import { useQueryClient } from "@tanstack/react-query"; import { useState } from "react"; import Alert from "react-bootstrap/Alert"; @@ -6,7 +6,7 @@ import { deleteDeadHost, toggleDeadHost } from "src/api/backend"; import { Button, LoadingPage } from "src/components"; import { useDeadHosts } from "src/hooks"; import { T } from "src/locale"; -import { showDeadHostModal, showDeleteConfirmModal } from "src/modals"; +import { showDeadHostModal, showDeleteConfirmModal, showHelpModal } from "src/modals"; import { showObjectSuccess } from "src/notifications"; import Table from "./Table"; @@ -56,9 +56,10 @@ export default function TableWrapper() { - {data?.length ? ( -
-
+ +
+
+ {data?.length ? (
@@ -71,12 +72,17 @@ export default function TableWrapper() { onChange={(e: any) => setSearch(e.target.value.toLowerCase().trim())} />
+ ) : null} + + {data?.length ? ( -
+ ) : null}
- ) : null} +
- {data?.length ? ( -
-
+ +
+
+ {data?.length ? (
@@ -74,12 +75,17 @@ export default function TableWrapper() { onChange={(e: any) => setSearch(e.target.value.toLowerCase().trim())} />
+ ) : null} + + {data?.length ? ( -
+ ) : null}
- ) : null} +
- {data?.length ? ( -
-
+ +
+
+ {data?.length ? (
@@ -74,6 +75,11 @@ export default function TableWrapper() { onChange={(e: any) => setSearch(e.target.value.toLowerCase().trim())} />
+ ) : null} + + {data?.length ? ( -
+ ) : null}
- ) : null} +
{ return ( diff --git a/frontend/src/pages/Nginx/Streams/TableWrapper.tsx b/frontend/src/pages/Nginx/Streams/TableWrapper.tsx index 40be5abf..ecb8f6ec 100644 --- a/frontend/src/pages/Nginx/Streams/TableWrapper.tsx +++ b/frontend/src/pages/Nginx/Streams/TableWrapper.tsx @@ -1,4 +1,4 @@ -import { IconSearch } from "@tabler/icons-react"; +import { IconHelp, IconSearch } from "@tabler/icons-react"; import { useQueryClient } from "@tanstack/react-query"; import { useState } from "react"; import Alert from "react-bootstrap/Alert"; @@ -6,7 +6,7 @@ import { deleteStream, toggleStream } from "src/api/backend"; import { Button, LoadingPage } from "src/components"; import { useStreams } from "src/hooks"; import { T } from "src/locale"; -import { showDeleteConfirmModal, showStreamModal } from "src/modals"; +import { showDeleteConfirmModal, showHelpModal, showStreamModal } from "src/modals"; import { showObjectSuccess } from "src/notifications"; import Table from "./Table"; @@ -61,9 +61,10 @@ export default function TableWrapper() { - {data?.length ? ( -
-
+ +
+
+ {data?.length ? (
@@ -76,12 +77,17 @@ export default function TableWrapper() { onChange={(e: any) => setSearch(e.target.value.toLowerCase().trim())} />
+ ) : null} + + {data?.length ? ( -
+ ) : null}
- ) : null} +