diff --git a/Makefile b/Makefile index f38af17e0..0f348b8cf 100644 --- a/Makefile +++ b/Makefile @@ -81,3 +81,12 @@ migrate: run: go run -v cmd/server/main.go + +web: web-lint-fix web-lint + +web-lint-fix: + cd web && npm run prettier + +web-lint: + cd web && npm run lint + cd web && npm run lit-analyse diff --git a/web/src/elements/Tabs.ts b/web/src/elements/Tabs.ts index 744932d09..10075ea67 100644 --- a/web/src/elements/Tabs.ts +++ b/web/src/elements/Tabs.ts @@ -9,6 +9,7 @@ import PFTabs from "@patternfly/patternfly/components/Tabs/tabs.css"; import PFGlobal from "@patternfly/patternfly/patternfly-base.css"; import { CURRENT_CLASS, ROUTE_SEPARATOR } from "../constants"; +import { getURLParams, updateURLParams } from "./router/RouteMatch"; @customElement("ak-tabs") export class Tabs extends LitElement { @@ -65,9 +66,9 @@ export class Tabs extends LitElement { onClick(slot?: string): void { this.currentPage = slot; - const currentUrl = window.location.hash.slice(1, Infinity).split(ROUTE_SEPARATOR)[0]; - const newUrl = `#${currentUrl};${slot}`; - history.replaceState(undefined, "", newUrl); + updateURLParams({ + page: slot, + }); } renderTab(page: Element): TemplateResult { @@ -81,18 +82,20 @@ export class Tabs extends LitElement { render(): TemplateResult { const pages = Array.from(this.querySelectorAll("[slot^='page-']")); + if (window.location.hash.includes(ROUTE_SEPARATOR)) { + const params = getURLParams(); + if ("page" in params) { + if (this.querySelector(`[slot='${params.page}']`) !== null) { + // To update the URL to match with the current slot + this.currentPage = params.page; + } + } + } if (!this.currentPage) { if (pages.length < 1) { return html`

${t`no tabs defined`}

`; } - let wantedPage = pages[0].attributes.getNamedItem("slot")?.value; - if (window.location.hash.includes(ROUTE_SEPARATOR)) { - const urlParts = window.location.hash.slice(1, Infinity).split(ROUTE_SEPARATOR); - if (this.querySelector(`[slot='${urlParts[1]}']`) !== null) { - // To update the URL to match with the current slot - wantedPage = urlParts[1]; - } - } + const wantedPage = pages[0].attributes.getNamedItem("slot")?.value; this.onClick(wantedPage); } return html`
diff --git a/web/src/elements/router/RouteMatch.ts b/web/src/elements/router/RouteMatch.ts index cda3235ab..b887d58e3 100644 --- a/web/src/elements/router/RouteMatch.ts +++ b/web/src/elements/router/RouteMatch.ts @@ -1,5 +1,6 @@ import { TemplateResult } from "lit"; +import { ROUTE_SEPARATOR } from "../../constants"; import { Route } from "./Route"; export class RouteMatch { @@ -22,3 +23,44 @@ export class RouteMatch { )}>`; } } + +export function getURLParam(key: string, fallback: T): T { + const params = getURLParams(); + if (key in params) { + return params[key] as T; + } + return fallback; +} + +export function getURLParams(): { [key: string]: unknown } { + const params = {}; + if (window.location.hash.includes(ROUTE_SEPARATOR)) { + const urlParts = window.location.hash.slice(1, Infinity).split(ROUTE_SEPARATOR, 2); + const rawParams = decodeURIComponent(urlParts[1]); + try { + return JSON.parse(rawParams); + } catch { + return params; + } + } + return params; +} + +export function setURLParams(params: { [key: string]: unknown }, replace = true): void { + const paramsString = JSON.stringify(params); + const currentUrl = window.location.hash.slice(1, Infinity).split(ROUTE_SEPARATOR)[0]; + const newUrl = `#${currentUrl};${encodeURIComponent(paramsString)}`; + if (replace) { + history.replaceState(undefined, "", newUrl); + } else { + history.pushState(undefined, "", newUrl); + } +} + +export function updateURLParams(params: { [key: string]: unknown }, replace = true): void { + const currentParams = getURLParams(); + for (const key in params) { + currentParams[key] = params[key] as string; + } + setURLParams(currentParams, replace); +} diff --git a/web/src/elements/table/Table.ts b/web/src/elements/table/Table.ts index 3ab94c6fa..6f57e853e 100644 --- a/web/src/elements/table/Table.ts +++ b/web/src/elements/table/Table.ts @@ -19,6 +19,7 @@ import { groupBy } from "../../utils"; import "../EmptyState"; import "../chips/Chip"; import "../chips/ChipGroup"; +import { getURLParams, updateURLParams } from "../router/RouteMatch"; import "./TablePagination"; import "./TableSearch"; @@ -355,6 +356,9 @@ export abstract class Table extends LitElement { composed: true, }), ); + updateURLParams({ + search: value, + }); }} > `; @@ -393,6 +397,10 @@ export abstract class Table extends LitElement { } firstUpdated(): void { + const params = getURLParams(); + if ("search" in params) { + this.search = params.search; + } this.fetch(); } diff --git a/web/src/pages/property-mappings/PropertyMappingListPage.ts b/web/src/pages/property-mappings/PropertyMappingListPage.ts index 69a5dd026..2492b2a15 100644 --- a/web/src/pages/property-mappings/PropertyMappingListPage.ts +++ b/web/src/pages/property-mappings/PropertyMappingListPage.ts @@ -15,6 +15,7 @@ import "../../elements/buttons/SpinnerButton"; import "../../elements/forms/DeleteBulkForm"; import "../../elements/forms/ModalForm"; import "../../elements/forms/ProxyForm"; +import { getURLParam, updateURLParams } from "../../elements/router/RouteMatch"; import { TableColumn } from "../../elements/table/Table"; import { TablePage } from "../../elements/table/TablePage"; import { groupBy } from "../../utils"; @@ -45,7 +46,7 @@ export class PropertyMappingListPage extends TablePage { order = "name"; @property({ type: Boolean }) - hideManaged = false; + hideManaged = getURLParam("hideManaged", true); async apiEndpoint(page: number): Promise> { return new PropertymappingsApi(DEFAULT_CONFIG).propertymappingsAllList({ @@ -171,6 +172,9 @@ export class PropertyMappingListPage extends TablePage { this.hideManaged = !this.hideManaged; this.page = 1; this.fetch(); + updateURLParams({ + hideManaged: this.hideManaged, + }); }} />