diff --git a/ui/src/views/api-keys/CreateAdminApiKey.tsx b/ui/src/views/api-keys/CreateAdminApiKey.tsx index d890f35f..831cb845 100644 --- a/ui/src/views/api-keys/CreateAdminApiKey.tsx +++ b/ui/src/views/api-keys/CreateAdminApiKey.tsx @@ -10,8 +10,10 @@ import { ApiKey, CreateApiKeyRequest } from "@chirpstack/chirpstack-api-grpc-web import ApiKeyForm from "./ApiKeyForm"; import ApiKeyToken from "./ApiKeyToken"; import InternalStore from "../../stores/InternalStore"; +import { useTitle } from "../helpers"; function CreateAdminApiKey() { + useTitle("Network Server", "API keys", "Add"); const [createApiKeyResponse, setCreateApiKeyResponse] = useState(undefined); const onFinish = (obj: ApiKey) => { @@ -34,7 +36,7 @@ function CreateAdminApiKey() { breadcrumbRender={() => ( - Network-server + Network Server diff --git a/ui/src/views/api-keys/CreateTenantApiKey.tsx b/ui/src/views/api-keys/CreateTenantApiKey.tsx index 3ed17aa7..da36e5bd 100644 --- a/ui/src/views/api-keys/CreateTenantApiKey.tsx +++ b/ui/src/views/api-keys/CreateTenantApiKey.tsx @@ -11,6 +11,7 @@ import type { Tenant } from "@chirpstack/chirpstack-api-grpc-web/api/tenant_pb"; import ApiKeyForm from "./ApiKeyForm"; import ApiKeyToken from "./ApiKeyToken"; import InternalStore from "../../stores/InternalStore"; +import { useTitle } from "../helpers"; interface IProps { tenant: Tenant; @@ -18,6 +19,7 @@ interface IProps { function CreateTenantApiKey(props: IProps) { const [createApiKeyResponse, setCreateApiKeyResponse] = useState(undefined); + useTitle("Tenants", props.tenant.getName(), "API Keys", "Add"); const onFinish = (obj: ApiKey) => { obj.setTenantId(props.tenant.getId()); diff --git a/ui/src/views/api-keys/ListAdminApiKeys.tsx b/ui/src/views/api-keys/ListAdminApiKeys.tsx index ae2eefb6..f2e54387 100644 --- a/ui/src/views/api-keys/ListAdminApiKeys.tsx +++ b/ui/src/views/api-keys/ListAdminApiKeys.tsx @@ -13,8 +13,10 @@ import type { GetPageCallbackFunc } from "../../components/DataTable"; import DataTable from "../../components/DataTable"; import InternalStore from "../../stores/InternalStore"; import DeleteConfirm from "../../components/DeleteConfirm"; +import { useTitle } from "../helpers"; function ListAdminApiKeys() { + useTitle("Network Server", "API keys"); const [refreshKey, setRefreshKey] = useState(1); const columns: ColumnsType = [ diff --git a/ui/src/views/api-keys/ListTenantApiKeys.tsx b/ui/src/views/api-keys/ListTenantApiKeys.tsx index 736f881f..c5fcdc33 100644 --- a/ui/src/views/api-keys/ListTenantApiKeys.tsx +++ b/ui/src/views/api-keys/ListTenantApiKeys.tsx @@ -15,6 +15,7 @@ import DataTable from "../../components/DataTable"; import InternalStore from "../../stores/InternalStore"; import DeleteConfirm from "../../components/DeleteConfirm"; import Admin from "../../components/Admin"; +import { useTitle } from "../helpers"; interface IProps { tenant: Tenant; @@ -22,6 +23,7 @@ interface IProps { function ListTenantApiKeys(props: IProps) { const [refreshKey, setRefreshKey] = useState(1); + useTitle("Tenants", props.tenant.getName(), "API Keys"); const columns: ColumnsType = [ { diff --git a/ui/src/views/applications/ApplicationLayout.tsx b/ui/src/views/applications/ApplicationLayout.tsx index 37e32ca7..97fadf12 100644 --- a/ui/src/views/applications/ApplicationLayout.tsx +++ b/ui/src/views/applications/ApplicationLayout.tsx @@ -38,6 +38,7 @@ import EditThingsBoardIntegration from "./integrations/EditThingsBoardIntegratio import GenerateMqttCertificate from "./integrations/GenerateMqttCertificate"; import CreateIftttIntegration from "./integrations/CreateIftttIntegration"; import EditIftttIntegration from "./integrations/EditIftttIntegration"; +import { useTitle } from "../helpers"; interface IProps { tenant: Tenant; @@ -48,6 +49,7 @@ interface IProps { function ApplicationLayout(props: IProps) { const navigate = useNavigate(); const location = useLocation(); + useTitle("Tenants", props.tenant.getName(), "Applications", props.application.getName()); const deleteApplication = () => { const req = new DeleteApplicationRequest(); diff --git a/ui/src/views/applications/CreateApplication.tsx b/ui/src/views/applications/CreateApplication.tsx index 3753a582..eef061f3 100644 --- a/ui/src/views/applications/CreateApplication.tsx +++ b/ui/src/views/applications/CreateApplication.tsx @@ -9,6 +9,7 @@ import { Application, CreateApplicationRequest } from "@chirpstack/chirpstack-ap import ApplicationForm from "./ApplicationForm"; import ApplicationStore from "../../stores/ApplicationStore"; +import { useTitle } from "../helpers"; interface IProps { tenant: Tenant; @@ -16,6 +17,7 @@ interface IProps { function CreateApplication(props: IProps) { const navigate = useNavigate(); + useTitle("Tenants", props.tenant.getName(), "Applications", "Add"); const onFinish = (obj: Application) => { obj.setTenantId(props.tenant.getId()); diff --git a/ui/src/views/applications/ListApplications.tsx b/ui/src/views/applications/ListApplications.tsx index 2021879a..d9932893 100644 --- a/ui/src/views/applications/ListApplications.tsx +++ b/ui/src/views/applications/ListApplications.tsx @@ -15,12 +15,14 @@ import type { GetPageCallbackFunc } from "../../components/DataTable"; import DataTable from "../../components/DataTable"; import ApplicationStore from "../../stores/ApplicationStore"; import Admin from "../../components/Admin"; +import { useTitle } from "../helpers"; interface IProps { tenant: Tenant; } function ListApplications(props: IProps) { + useTitle("Tenants", props.tenant.getName(), "Applications"); const columns: ColumnsType = [ { title: "Name", diff --git a/ui/src/views/dashboard/Dashboard.tsx b/ui/src/views/dashboard/Dashboard.tsx index f9552267..ec14c3ee 100644 --- a/ui/src/views/dashboard/Dashboard.tsx +++ b/ui/src/views/dashboard/Dashboard.tsx @@ -26,6 +26,7 @@ import InternalStore from "../../stores/InternalStore"; import GatewayStore from "../../stores/GatewayStore"; import type { MarkerColor } from "../../components/Map"; import Map, { Marker } from "../../components/Map"; +import { useTitle } from "../helpers"; function GatewaysMap() { const [items, setItems] = useState([]); @@ -224,6 +225,7 @@ function DevicesDataRates({ summary }: { summary?: GetDevicesSummaryResponse }) } function Dashboard() { + useTitle("Network Server", "Dashboard"); const [gatewaysSummary, setGatewaysSummary] = useState(undefined); const [devicesSummary, setDevicesSummary] = useState(undefined); diff --git a/ui/src/views/device-profile-templates/CreateDeviceProfileTemplate.tsx b/ui/src/views/device-profile-templates/CreateDeviceProfileTemplate.tsx index 7e688395..875f223a 100644 --- a/ui/src/views/device-profile-templates/CreateDeviceProfileTemplate.tsx +++ b/ui/src/views/device-profile-templates/CreateDeviceProfileTemplate.tsx @@ -11,8 +11,10 @@ import { import DeviceProfileTemplateForm from "./DeviceProfileTemplateForm"; import DeviceProfileTemplateStore from "../../stores/DeviceProfileTemplateStore"; +import { useTitle } from "../helpers"; function CreateDeviceProfileTemplate() { + useTitle("Network Server", "Device-profile templates", "Add"); const navigate = useNavigate(); const onFinish = (obj: DeviceProfileTemplate) => { diff --git a/ui/src/views/device-profile-templates/EditDeviceProfileTemplate.tsx b/ui/src/views/device-profile-templates/EditDeviceProfileTemplate.tsx index bf17d8a5..095ac119 100644 --- a/ui/src/views/device-profile-templates/EditDeviceProfileTemplate.tsx +++ b/ui/src/views/device-profile-templates/EditDeviceProfileTemplate.tsx @@ -18,11 +18,13 @@ import { import DeviceProfileTemplateForm from "./DeviceProfileTemplateForm"; import DeviceProfileTemplateStore from "../../stores/DeviceProfileTemplateStore"; import DeleteConfirm from "../../components/DeleteConfirm"; +import { useTitle } from "../helpers"; function EditDeviceProfileTemplate() { const navigate = useNavigate(); const [deviceProfileTemplate, setDeviceProfileTemplate] = useState(undefined); const { deviceProfileTemplateId } = useParams(); + useTitle("Network Server", "Device-profile templates", deviceProfileTemplate?.getName()); useEffect(() => { const id = deviceProfileTemplateId!; diff --git a/ui/src/views/device-profile-templates/ListDeviceProfileTemplates.tsx b/ui/src/views/device-profile-templates/ListDeviceProfileTemplates.tsx index 10deba4a..e99059f5 100644 --- a/ui/src/views/device-profile-templates/ListDeviceProfileTemplates.tsx +++ b/ui/src/views/device-profile-templates/ListDeviceProfileTemplates.tsx @@ -15,8 +15,10 @@ import { getEnumName } from "../helpers"; import type { GetPageCallbackFunc } from "../../components/DataTable"; import DataTable from "../../components/DataTable"; import DeviceProfileTemplateStore from "../../stores/DeviceProfileTemplateStore"; +import { useTitle } from "../helpers"; function ListDeviceProfileTemplates() { + useTitle("Network Server", "Device-profile templates"); const columns: ColumnsType = [ { title: "Vendor", diff --git a/ui/src/views/device-profiles/CreateDeviceProfile.tsx b/ui/src/views/device-profiles/CreateDeviceProfile.tsx index a0424662..8d52140a 100644 --- a/ui/src/views/device-profiles/CreateDeviceProfile.tsx +++ b/ui/src/views/device-profiles/CreateDeviceProfile.tsx @@ -11,6 +11,7 @@ import type { Tenant } from "@chirpstack/chirpstack-api-grpc-web/api/tenant_pb"; import DeviceProfileForm from "./DeviceProfileForm"; import DeviceProfileStore from "../../stores/DeviceProfileStore"; +import { useTitle } from "../helpers"; interface IProps { tenant: Tenant; @@ -18,6 +19,7 @@ interface IProps { function CreateDeviceProfile(props: IProps) { const navigate = useNavigate(); + useTitle("Tenants", props.tenant.getName(), "Device profiles", "Add"); const onFinish = (obj: DeviceProfile) => { obj.setTenantId(props.tenant.getId()); diff --git a/ui/src/views/device-profiles/EditDeviceProfile.tsx b/ui/src/views/device-profiles/EditDeviceProfile.tsx index 274d6089..d44763c7 100644 --- a/ui/src/views/device-profiles/EditDeviceProfile.tsx +++ b/ui/src/views/device-profiles/EditDeviceProfile.tsx @@ -20,6 +20,7 @@ import DeviceProfileStore from "../../stores/DeviceProfileStore"; import SessionStore from "../../stores/SessionStore"; import DeleteConfirm from "../../components/DeleteConfirm"; import Admin from "../../components/Admin"; +import { useTitle } from "../helpers"; interface IProps { tenant: Tenant; @@ -29,6 +30,7 @@ function EditDeviceProfile(props: IProps) { const navigate = useNavigate(); const [deviceProfile, setDeviceProfile] = useState(undefined); const { deviceProfileId } = useParams(); + useTitle("Tenants", props.tenant.getName(), "Device profiles", deviceProfile?.getName()); useEffect(() => { const id = deviceProfileId!; diff --git a/ui/src/views/device-profiles/ListDeviceProfiles.tsx b/ui/src/views/device-profiles/ListDeviceProfiles.tsx index 549b0e05..22c57270 100644 --- a/ui/src/views/device-profiles/ListDeviceProfiles.tsx +++ b/ui/src/views/device-profiles/ListDeviceProfiles.tsx @@ -17,12 +17,14 @@ import type { GetPageCallbackFunc } from "../../components/DataTable"; import DataTable from "../../components/DataTable"; import DeviceProfileStore from "../../stores/DeviceProfileStore"; import Admin from "../../components/Admin"; +import { useTitle } from "../helpers"; interface IProps { tenant: Tenant; } function ListDeviceProfiles(props: IProps) { + useTitle("Tenants", props.tenant.getName(), "Device profiles"); const columns: ColumnsType = [ { title: "Name", diff --git a/ui/src/views/devices/CreateDevice.tsx b/ui/src/views/devices/CreateDevice.tsx index abf8d097..ca76b1f7 100644 --- a/ui/src/views/devices/CreateDevice.tsx +++ b/ui/src/views/devices/CreateDevice.tsx @@ -12,6 +12,7 @@ import { GetDeviceProfileRequest } from "@chirpstack/chirpstack-api-grpc-web/api import DeviceForm from "./DeviceForm"; import DeviceStore from "../../stores/DeviceStore"; import DeviceProfileStore from "../../stores/DeviceProfileStore"; +import { useTitle } from "../helpers"; interface IProps { tenant: Tenant; @@ -20,6 +21,7 @@ interface IProps { function CreateDevice(props: IProps) { const navigate = useNavigate(); + useTitle("Tenants", props.tenant.getName(), "Applications", props.application.getName(), "Add device"); const onFinish = (obj: Device) => { obj.setApplicationId(props.application.getId()); diff --git a/ui/src/views/devices/DeviceLayout.tsx b/ui/src/views/devices/DeviceLayout.tsx index fd2d6e7c..33467281 100644 --- a/ui/src/views/devices/DeviceLayout.tsx +++ b/ui/src/views/devices/DeviceLayout.tsx @@ -26,6 +26,7 @@ import DeviceFrames from "./DeviceFrames"; import DeviceEvents from "./DeviceEvents"; import DeviceQueue from "./DeviceQueue"; import DeviceActivation from "./DeviceActivation"; +import { useTitle } from "../helpers"; interface IProps { tenant: Tenant; @@ -40,6 +41,14 @@ function DeviceLayout(props: IProps) { const [device, setDevice] = useState(undefined); const [deviceProfile, setDeviceProfile] = useState(undefined); const [lastSeenAt, setLastSeenAt] = useState(undefined); + useTitle( + "Tenants", + props.tenant.getName(), + "Applications", + props.application.getName(), + "Devices", + device?.getName(), + ); useEffect(() => { const loadDevice = () => { diff --git a/ui/src/views/gateways/CreateGateway.tsx b/ui/src/views/gateways/CreateGateway.tsx index 095b95f8..1b9e9ed0 100644 --- a/ui/src/views/gateways/CreateGateway.tsx +++ b/ui/src/views/gateways/CreateGateway.tsx @@ -8,6 +8,7 @@ import type { Tenant } from "@chirpstack/chirpstack-api-grpc-web/api/tenant_pb"; import GatewayForm from "./GatewayForm"; import GatewayStore from "../../stores/GatewayStore"; +import { useTitle } from "../helpers"; interface IProps { tenant: Tenant; @@ -15,6 +16,7 @@ interface IProps { function CreateGateway(props: IProps) { const navigate = useNavigate(); + useTitle("Tenants", props.tenant.getName(), "Gateways", "Add"); const onFinish = (obj: Gateway) => { obj.setTenantId(props.tenant.getId()); diff --git a/ui/src/views/gateways/GatewayLayout.tsx b/ui/src/views/gateways/GatewayLayout.tsx index 9725ecbd..03f04a5d 100644 --- a/ui/src/views/gateways/GatewayLayout.tsx +++ b/ui/src/views/gateways/GatewayLayout.tsx @@ -18,6 +18,7 @@ import GatewayFrames from "./GatewayFrames"; import GatewayCertificate from "./GatewayCertificate"; import Admin from "../../components/Admin"; import SessionStore from "../../stores/SessionStore"; +import { useTitle } from "../helpers"; interface IProps { tenant: Tenant; @@ -29,6 +30,7 @@ function GatewayLayout(props: IProps) { const location = useLocation(); const [gateway, setGateway] = useState(undefined); const [lastSeenAt, setLastSeenAt] = useState(undefined); + useTitle("Tenants", props.tenant.getName(), "Gateways", gateway?.getName()); useEffect(() => { const req = new GetGatewayRequest(); diff --git a/ui/src/views/gateways/ListGateways.tsx b/ui/src/views/gateways/ListGateways.tsx index cf37850c..d88e51f8 100644 --- a/ui/src/views/gateways/ListGateways.tsx +++ b/ui/src/views/gateways/ListGateways.tsx @@ -23,6 +23,7 @@ import GatewayStore from "../../stores/GatewayStore"; import ApplicationStore from "../../stores/ApplicationStore"; import MulticastGroupStore from "../../stores/MulticastGroupStore"; import Admin from "../../components/Admin"; +import { useTitle } from "../helpers"; interface IProps { tenant: Tenant; @@ -40,6 +41,7 @@ function ListGateways(props: IProps) { const [multicastGroups, setMulticastGroups] = useState([]); const [mgModalVisible, setMgModalVisible] = useState(false); const [mgSelected, setMgSelected] = useState(""); + useTitle("Tenants", props.tenant.getName(), "Gateways"); const columns: ColumnsType = [ { diff --git a/ui/src/views/gateways/mesh/ListRelayGateways.tsx b/ui/src/views/gateways/mesh/ListRelayGateways.tsx index f5df33df..f122f207 100644 --- a/ui/src/views/gateways/mesh/ListRelayGateways.tsx +++ b/ui/src/views/gateways/mesh/ListRelayGateways.tsx @@ -15,12 +15,14 @@ import type { Tenant } from "@chirpstack/chirpstack-api-grpc-web/api/tenant_pb"; import type { GetPageCallbackFunc } from "../../../components/DataTable"; import DataTable from "../../../components/DataTable"; import GatewayStore from "../../../stores/GatewayStore"; +import { useTitle } from "../../helpers"; interface IProps { tenant: Tenant; } function ListRelayGateways(props: IProps) { + useTitle("Tenants", props.tenant.getName(), "Gateway Mesh", "Relay Gateways"); const columns: ColumnsType = [ { title: "", diff --git a/ui/src/views/gateways/mesh/RelayGatewayLayout.tsx b/ui/src/views/gateways/mesh/RelayGatewayLayout.tsx index ec8361e1..a52094da 100644 --- a/ui/src/views/gateways/mesh/RelayGatewayLayout.tsx +++ b/ui/src/views/gateways/mesh/RelayGatewayLayout.tsx @@ -15,6 +15,7 @@ import GatewayStore from "../../../stores/GatewayStore"; import DeleteConfirm from "../../../components/DeleteConfirm"; import EditRelayGateway from "./EditRelayGateway"; +import { useTitle } from "../../helpers"; interface IProps { tenant: Tenant; @@ -24,6 +25,7 @@ function RelayGatewayLayout(props: IProps) { const { relayId } = useParams(); const navigate = useNavigate(); const [relayGateway, setRelayGateway] = useState(undefined); + useTitle("Tenants", props.tenant.getName(), "Gateway Mesh", "Relay Gateways", relayGateway?.getName()); useEffect(() => { const req = new GetRelayGatewayRequest(); diff --git a/ui/src/views/helpers.ts b/ui/src/views/helpers.ts index 67387392..81387716 100644 --- a/ui/src/views/helpers.ts +++ b/ui/src/views/helpers.ts @@ -1,5 +1,6 @@ import { notification } from "antd"; import { MacVersion, RegParamsRevision } from "@chirpstack/chirpstack-api-grpc-web/common/common_pb"; +import { useRef, useEffect } from "react"; export function formatMacVersion(m: MacVersion) { switch (m) { @@ -61,3 +62,28 @@ export function onFinishFailed() { duration: 3, }); } + +/** + * Sets the Document Title in Reverse Order + * @example + * ``` + * useTitle("Tenants", "Tenant", "Edit"); // Edit | Tenant | Tenants | ChirpStack LoRaWAN® Network-Server + * ``` + */ +export function useTitle(...v: unknown[]) { + const documentDefined = typeof document !== "undefined"; + + useEffect(() => { + if (!documentDefined) return; + + const title = ["ChirpStack LoRaWAN® Network-Server", ...v].reverse().join(" | "); + + if (document.title !== title) { + document.title = title; + } + + return () => { + document.title = "ChirpStack LoRaWAN® Network-Server"; + }; + }, [documentDefined, v]); +} diff --git a/ui/src/views/multicast-groups/CreateMulticastGroup.tsx b/ui/src/views/multicast-groups/CreateMulticastGroup.tsx index d47490ec..c07c0ad4 100644 --- a/ui/src/views/multicast-groups/CreateMulticastGroup.tsx +++ b/ui/src/views/multicast-groups/CreateMulticastGroup.tsx @@ -13,6 +13,7 @@ import { import MulticastGroupForm from "./MulticastGroupForm"; import MulticastGroupStore from "../../stores/MulticastGroupStore"; +import { useTitle } from "../helpers"; interface IProps { tenant: Tenant; @@ -21,6 +22,7 @@ interface IProps { function CreateMulticastGroup(props: IProps) { const navigate = useNavigate(); + useTitle("Tenants", props.tenant.getName(), "Applications", props.application.getName(), "Add multicast-group"); const onFinish = (obj: MulticastGroup) => { obj.setApplicationId(props.application.getId()); diff --git a/ui/src/views/multicast-groups/MulticastGroupLayout.tsx b/ui/src/views/multicast-groups/MulticastGroupLayout.tsx index 6024fb67..779b3d5a 100644 --- a/ui/src/views/multicast-groups/MulticastGroupLayout.tsx +++ b/ui/src/views/multicast-groups/MulticastGroupLayout.tsx @@ -22,6 +22,7 @@ import ListMulticastGroupGateways from "./ListMulticastGroupGateways"; import EditMulticastGroup from "./EditMulticastGroup"; import Admin from "../../components/Admin"; import MulticastGroupQueue from "./MulticastGroupQueue"; +import { useTitle } from "../helpers"; interface IProps { tenant: Tenant; @@ -33,6 +34,14 @@ function MulticastGroupLayout(props: IProps) { const navigate = useNavigate(); const location = useLocation(); const [multicastGroup, setMulticastGroup] = useState(undefined); + useTitle( + "Tenants", + props.tenant.getName(), + "Applications", + props.application.getName(), + "Multicast-groups", + multicastGroup?.getName(), + ); useEffect(() => { const req = new GetMulticastGroupRequest(); diff --git a/ui/src/views/regions/ListRegions.tsx b/ui/src/views/regions/ListRegions.tsx index dfa797d6..2a25ed02 100644 --- a/ui/src/views/regions/ListRegions.tsx +++ b/ui/src/views/regions/ListRegions.tsx @@ -9,8 +9,10 @@ import type { ListRegionsResponse, RegionListItem } from "@chirpstack/chirpstack import { getEnumName } from "../helpers"; import InternalStore from "../../stores/InternalStore"; +import { useTitle } from "../helpers"; function ListRegions() { + useTitle("Network Server", "Regions"); const [regions, setRegions] = useState(undefined); useEffect(() => { diff --git a/ui/src/views/regions/RegionDetails.tsx b/ui/src/views/regions/RegionDetails.tsx index e2523cda..c797de90 100644 --- a/ui/src/views/regions/RegionDetails.tsx +++ b/ui/src/views/regions/RegionDetails.tsx @@ -12,10 +12,12 @@ import { GetRegionRequest } from "@chirpstack/chirpstack-api-grpc-web/api/intern import { getEnumName } from "../helpers"; import InternalStore from "../../stores/InternalStore"; +import { useTitle } from "../helpers"; function RegionDetails() { const [region, setRegion] = useState(undefined); const { id } = useParams(); + useTitle("Network Server", "Regions", region?.getDescription()); useEffect(() => { const req = new GetRegionRequest(); diff --git a/ui/src/views/relays/RelayLayout.tsx b/ui/src/views/relays/RelayLayout.tsx index 25417410..54aeef26 100644 --- a/ui/src/views/relays/RelayLayout.tsx +++ b/ui/src/views/relays/RelayLayout.tsx @@ -11,6 +11,7 @@ import { GetDeviceRequest } from "@chirpstack/chirpstack-api-grpc-web/api/device import DeviceStore from "../../stores/DeviceStore"; import ListRelayDevices from "./ListRelayDevices"; +import { useTitle } from "../helpers"; interface IProps { tenant: Tenant; @@ -20,6 +21,14 @@ interface IProps { function RelayLayout(props: IProps) { const [relayDevice, setRelayDevice] = useState(undefined); const { relayDevEui } = useParams(); + useTitle( + "Tenants", + props.tenant.getName(), + "Applications", + props.application.getName(), + "Relays", + relayDevice?.getName(), + ); useEffect(() => { const req = new GetDeviceRequest(); diff --git a/ui/src/views/tenants/CreateTenant.tsx b/ui/src/views/tenants/CreateTenant.tsx index 7e662627..62743bb3 100644 --- a/ui/src/views/tenants/CreateTenant.tsx +++ b/ui/src/views/tenants/CreateTenant.tsx @@ -8,8 +8,10 @@ import { Tenant, CreateTenantRequest } from "@chirpstack/chirpstack-api-grpc-web import TenantForm from "./TenantForm"; import TenantStore from "../../stores/TenantStore"; +import { useTitle } from "../helpers"; function CreateTenant() { + useTitle("Network Server", "Tenants", "Add"); const navigate = useNavigate(); const onFinish = (obj: Tenant) => { @@ -29,7 +31,7 @@ function CreateTenant() { breadcrumbRender={() => ( - Network-server + Network Server diff --git a/ui/src/views/tenants/CreateTenantUser.tsx b/ui/src/views/tenants/CreateTenantUser.tsx index cdb3df1b..1dcc37b9 100644 --- a/ui/src/views/tenants/CreateTenantUser.tsx +++ b/ui/src/views/tenants/CreateTenantUser.tsx @@ -8,9 +8,11 @@ import { TenantUser, AddTenantUserRequest } from "@chirpstack/chirpstack-api-grp import TenantUserForm from "./TenantUserForm"; import TenantStore from "../../stores/TenantStore"; +import { useTitle } from "../helpers"; function CreateTenantUser({ tenant }: { tenant: Tenant }) { const navigate = useNavigate(); + useTitle("Tenants", tenant.getName(), "Tenant users", "Add"); const onFinish = (obj: TenantUser) => { obj.setTenantId(tenant.getId()); diff --git a/ui/src/views/tenants/EditTenantUser.tsx b/ui/src/views/tenants/EditTenantUser.tsx index 390c73c5..c4e64114 100644 --- a/ui/src/views/tenants/EditTenantUser.tsx +++ b/ui/src/views/tenants/EditTenantUser.tsx @@ -16,11 +16,13 @@ import TenantStore from "../../stores/TenantStore"; import SessionStore from "../../stores/SessionStore"; import DeleteConfirm from "../../components/DeleteConfirm"; import Admin from "../../components/Admin"; +import { useTitle } from "../helpers"; function EditTenantUser({ tenant }: { tenant: Tenant }) { const [tenantUser, setTenantUser] = useState(undefined); const { userId } = useParams(); const navigate = useNavigate(); + useTitle("Tenants", tenant.getName(), "Tenant users", tenantUser?.getEmail()); useEffect(() => { const req = new GetTenantUserRequest(); diff --git a/ui/src/views/tenants/ListTenantUsers.tsx b/ui/src/views/tenants/ListTenantUsers.tsx index 65850a4c..d63ca513 100644 --- a/ui/src/views/tenants/ListTenantUsers.tsx +++ b/ui/src/views/tenants/ListTenantUsers.tsx @@ -12,12 +12,14 @@ import type { GetPageCallbackFunc } from "../../components/DataTable"; import DataTable from "../../components/DataTable"; import TenantStore from "../../stores/TenantStore"; import Admin from "../../components/Admin"; +import { useTitle } from "../helpers"; interface IProps { tenant: Tenant; } -function ListTenatUsers(props: IProps) { +function ListTenantUsers(props: IProps) { + useTitle("Tenants", props.tenant.getName(), "Tenant users"); const columns: ColumnsType = [ { title: "Email", @@ -107,4 +109,4 @@ function ListTenatUsers(props: IProps) { ); } -export default ListTenatUsers; +export default ListTenantUsers; diff --git a/ui/src/views/tenants/ListTenants.tsx b/ui/src/views/tenants/ListTenants.tsx index f5aab655..e753d267 100644 --- a/ui/src/views/tenants/ListTenants.tsx +++ b/ui/src/views/tenants/ListTenants.tsx @@ -10,8 +10,10 @@ import { ListTenantsRequest } from "@chirpstack/chirpstack-api-grpc-web/api/tena import type { GetPageCallbackFunc } from "../../components/DataTable"; import DataTable from "../../components/DataTable"; import TenantStore from "../../stores/TenantStore"; +import { useTitle } from "../helpers"; function ListTenants() { + useTitle("Network Server", "Tenants"); const columns: ColumnsType = [ { title: "Name", diff --git a/ui/src/views/tenants/TenantLayout.tsx b/ui/src/views/tenants/TenantLayout.tsx index dd5405f1..a3d3eae6 100644 --- a/ui/src/views/tenants/TenantLayout.tsx +++ b/ui/src/views/tenants/TenantLayout.tsx @@ -11,8 +11,10 @@ import DeleteConfirm from "../../components/DeleteConfirm"; import Admin from "../../components/Admin"; import EditTenant from "./EditTenant"; import TenantDashboard from "./TenantDashboard"; +import { useTitle } from "../helpers"; function TenantLayout({ tenant }: { tenant: Tenant }) { + useTitle("Tenants", tenant.getName()); const navigate = useNavigate(); const location = useLocation(); diff --git a/ui/src/views/users/ChangeUserPassword.tsx b/ui/src/views/users/ChangeUserPassword.tsx index 28599887..c8a17173 100644 --- a/ui/src/views/users/ChangeUserPassword.tsx +++ b/ui/src/views/users/ChangeUserPassword.tsx @@ -8,11 +8,13 @@ import type { User, GetUserResponse } from "@chirpstack/chirpstack-api-grpc-web/ import { GetUserRequest, UpdateUserPasswordRequest } from "@chirpstack/chirpstack-api-grpc-web/api/user_pb"; import UserStore from "../../stores/UserStore"; import PasswordForm from "./PasswordForm"; +import { useTitle } from "../helpers"; function ChangeUserPassword() { const navigate = useNavigate(); const { userId } = useParams(); const [user, setUser] = useState(undefined); + useTitle("Users", user?.getEmail(), "Change password"); useEffect(() => { const req = new GetUserRequest(); diff --git a/ui/src/views/users/CreateUser.tsx b/ui/src/views/users/CreateUser.tsx index ddb3f0b4..3765deb9 100644 --- a/ui/src/views/users/CreateUser.tsx +++ b/ui/src/views/users/CreateUser.tsx @@ -8,8 +8,10 @@ import { User, CreateUserRequest } from "@chirpstack/chirpstack-api-grpc-web/api import UserForm from "./UserForm"; import UserStore from "../../stores/UserStore"; +import { useTitle } from "../helpers"; function CreateUser() { + useTitle("Network Server", "Users", "Add"); const navigate = useNavigate(); const onFinish = (obj: User, password: string) => { @@ -30,7 +32,7 @@ function CreateUser() { breadcrumbRender={() => ( - Network-server + Network Server diff --git a/ui/src/views/users/EditUser.tsx b/ui/src/views/users/EditUser.tsx index 627e98f8..bceddacf 100644 --- a/ui/src/views/users/EditUser.tsx +++ b/ui/src/views/users/EditUser.tsx @@ -10,11 +10,13 @@ import { GetUserRequest, UpdateUserRequest, DeleteUserRequest } from "@chirpstac import UserForm from "./UserForm"; import UserStore from "../../stores/UserStore"; import DeleteConfirm from "../../components/DeleteConfirm"; +import { useTitle } from "../helpers"; function EditUser() { const navigate = useNavigate(); const { userId } = useParams(); const [user, setUser] = useState(undefined); + useTitle("Network Server", "Users", user?.getEmail()); useEffect(() => { const req = new GetUserRequest(); @@ -57,7 +59,7 @@ function EditUser() { breadcrumbRender={() => ( - Network-server + Network Server diff --git a/ui/src/views/users/ListUsers.tsx b/ui/src/views/users/ListUsers.tsx index 8300c45d..35afc8b4 100644 --- a/ui/src/views/users/ListUsers.tsx +++ b/ui/src/views/users/ListUsers.tsx @@ -11,8 +11,10 @@ import type { GetPageCallbackFunc } from "../../components/DataTable"; import DataTable from "../../components/DataTable"; import UserStore from "../../stores/UserStore"; +import { useTitle } from "../helpers"; function ListUsers() { + useTitle("Network Server", "Users"); const columns: ColumnsType = [ { title: "Email", diff --git a/ui/src/views/users/Login.tsx b/ui/src/views/users/Login.tsx index 5a510343..de55bc51 100644 --- a/ui/src/views/users/Login.tsx +++ b/ui/src/views/users/Login.tsx @@ -9,6 +9,7 @@ import { OpenIdConnectLoginRequest, OAuth2LoginRequest } from "@chirpstack/chirp import SessionStore from "../../stores/SessionStore"; import InternalStore from "../../stores/InternalStore"; +import { useTitle } from "../helpers"; const layout = { labelCol: { @@ -122,6 +123,7 @@ function LoginForm() { } function Login() { + useTitle("Login"); const location = useLocation(); const navigate = useNavigate();