ui: Update page-title according to page. (#539)

This commit is contained in:
Bernd Storath 2024-10-15 11:48:33 +02:00 committed by GitHub
parent 20ec2877f2
commit 6621a23652
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
38 changed files with 127 additions and 6 deletions

View File

@ -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<CreateApiKeyResponse | undefined>(undefined);
const onFinish = (obj: ApiKey) => {
@ -34,7 +36,7 @@ function CreateAdminApiKey() {
breadcrumbRender={() => (
<Breadcrumb>
<Breadcrumb.Item>
<span>Network-server</span>
<span>Network Server</span>
</Breadcrumb.Item>
<Breadcrumb.Item>
<span>

View File

@ -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<CreateApiKeyResponse | undefined>(undefined);
useTitle("Tenants", props.tenant.getName(), "API Keys", "Add");
const onFinish = (obj: ApiKey) => {
obj.setTenantId(props.tenant.getId());

View File

@ -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<number>(1);
const columns: ColumnsType<ApiKey.AsObject> = [

View File

@ -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<number>(1);
useTitle("Tenants", props.tenant.getName(), "API Keys");
const columns: ColumnsType<ApiKey.AsObject> = [
{

View File

@ -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();

View File

@ -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());

View File

@ -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<ApplicationListItem.AsObject> = [
{
title: "Name",

View File

@ -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<GatewayListItem[]>([]);
@ -224,6 +225,7 @@ function DevicesDataRates({ summary }: { summary?: GetDevicesSummaryResponse })
}
function Dashboard() {
useTitle("Network Server", "Dashboard");
const [gatewaysSummary, setGatewaysSummary] = useState<GetGatewaysSummaryResponse | undefined>(undefined);
const [devicesSummary, setDevicesSummary] = useState<GetDevicesSummaryResponse | undefined>(undefined);

View File

@ -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) => {

View File

@ -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<DeviceProfileTemplate | undefined>(undefined);
const { deviceProfileTemplateId } = useParams();
useTitle("Network Server", "Device-profile templates", deviceProfileTemplate?.getName());
useEffect(() => {
const id = deviceProfileTemplateId!;

View File

@ -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<DeviceProfileTemplateListItem.AsObject> = [
{
title: "Vendor",

View File

@ -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());

View File

@ -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<DeviceProfile | undefined>(undefined);
const { deviceProfileId } = useParams();
useTitle("Tenants", props.tenant.getName(), "Device profiles", deviceProfile?.getName());
useEffect(() => {
const id = deviceProfileId!;

View File

@ -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<DeviceProfileListItem.AsObject> = [
{
title: "Name",

View File

@ -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());

View File

@ -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<Device | undefined>(undefined);
const [deviceProfile, setDeviceProfile] = useState<DeviceProfile | undefined>(undefined);
const [lastSeenAt, setLastSeenAt] = useState<Date | undefined>(undefined);
useTitle(
"Tenants",
props.tenant.getName(),
"Applications",
props.application.getName(),
"Devices",
device?.getName(),
);
useEffect(() => {
const loadDevice = () => {

View File

@ -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());

View File

@ -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<Gateway | undefined>(undefined);
const [lastSeenAt, setLastSeenAt] = useState<Date | undefined>(undefined);
useTitle("Tenants", props.tenant.getName(), "Gateways", gateway?.getName());
useEffect(() => {
const req = new GetGatewayRequest();

View File

@ -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<MulticastGroup[]>([]);
const [mgModalVisible, setMgModalVisible] = useState<boolean>(false);
const [mgSelected, setMgSelected] = useState<string>("");
useTitle("Tenants", props.tenant.getName(), "Gateways");
const columns: ColumnsType<GatewayListItem.AsObject> = [
{

View File

@ -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<RelayGatewayListItem.AsObject> = [
{
title: "",

View File

@ -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<RelayGateway | undefined>(undefined);
useTitle("Tenants", props.tenant.getName(), "Gateway Mesh", "Relay Gateways", relayGateway?.getName());
useEffect(() => {
const req = new GetRelayGatewayRequest();

View File

@ -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]);
}

View File

@ -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());

View File

@ -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<MulticastGroup | undefined>(undefined);
useTitle(
"Tenants",
props.tenant.getName(),
"Applications",
props.application.getName(),
"Multicast-groups",
multicastGroup?.getName(),
);
useEffect(() => {
const req = new GetMulticastGroupRequest();

View File

@ -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<ListRegionsResponse | undefined>(undefined);
useEffect(() => {

View File

@ -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<GetRegionResponse | undefined>(undefined);
const { id } = useParams();
useTitle("Network Server", "Regions", region?.getDescription());
useEffect(() => {
const req = new GetRegionRequest();

View File

@ -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<Device | undefined>(undefined);
const { relayDevEui } = useParams();
useTitle(
"Tenants",
props.tenant.getName(),
"Applications",
props.application.getName(),
"Relays",
relayDevice?.getName(),
);
useEffect(() => {
const req = new GetDeviceRequest();

View File

@ -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={() => (
<Breadcrumb>
<Breadcrumb.Item>
<span>Network-server</span>
<span>Network Server</span>
</Breadcrumb.Item>
<Breadcrumb.Item>
<span>

View File

@ -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());

View File

@ -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<TenantUser | undefined>(undefined);
const { userId } = useParams();
const navigate = useNavigate();
useTitle("Tenants", tenant.getName(), "Tenant users", tenantUser?.getEmail());
useEffect(() => {
const req = new GetTenantUserRequest();

View File

@ -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<TenantUserListItem.AsObject> = [
{
title: "Email",
@ -107,4 +109,4 @@ function ListTenatUsers(props: IProps) {
);
}
export default ListTenatUsers;
export default ListTenantUsers;

View File

@ -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<TenantListItem.AsObject> = [
{
title: "Name",

View File

@ -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();

View File

@ -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<User | undefined>(undefined);
useTitle("Users", user?.getEmail(), "Change password");
useEffect(() => {
const req = new GetUserRequest();

View File

@ -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={() => (
<Breadcrumb>
<Breadcrumb.Item>
<span>Network-server</span>
<span>Network Server</span>
</Breadcrumb.Item>
<Breadcrumb.Item>
<span>

View File

@ -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<User | undefined>(undefined);
useTitle("Network Server", "Users", user?.getEmail());
useEffect(() => {
const req = new GetUserRequest();
@ -57,7 +59,7 @@ function EditUser() {
breadcrumbRender={() => (
<Breadcrumb>
<Breadcrumb.Item>
<span>Network-server</span>
<span>Network Server</span>
</Breadcrumb.Item>
<Breadcrumb.Item>
<span>

View File

@ -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<UserListItem.AsObject> = [
{
title: "Email",

View File

@ -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();