mirror of
https://github.com/chirpstack/chirpstack.git
synced 2025-06-01 15:30:46 +00:00
ui: Update page-title according to page. (#539)
This commit is contained in:
parent
20ec2877f2
commit
6621a23652
@ -10,8 +10,10 @@ import { ApiKey, CreateApiKeyRequest } from "@chirpstack/chirpstack-api-grpc-web
|
|||||||
import ApiKeyForm from "./ApiKeyForm";
|
import ApiKeyForm from "./ApiKeyForm";
|
||||||
import ApiKeyToken from "./ApiKeyToken";
|
import ApiKeyToken from "./ApiKeyToken";
|
||||||
import InternalStore from "../../stores/InternalStore";
|
import InternalStore from "../../stores/InternalStore";
|
||||||
|
import { useTitle } from "../helpers";
|
||||||
|
|
||||||
function CreateAdminApiKey() {
|
function CreateAdminApiKey() {
|
||||||
|
useTitle("Network Server", "API keys", "Add");
|
||||||
const [createApiKeyResponse, setCreateApiKeyResponse] = useState<CreateApiKeyResponse | undefined>(undefined);
|
const [createApiKeyResponse, setCreateApiKeyResponse] = useState<CreateApiKeyResponse | undefined>(undefined);
|
||||||
|
|
||||||
const onFinish = (obj: ApiKey) => {
|
const onFinish = (obj: ApiKey) => {
|
||||||
@ -34,7 +36,7 @@ function CreateAdminApiKey() {
|
|||||||
breadcrumbRender={() => (
|
breadcrumbRender={() => (
|
||||||
<Breadcrumb>
|
<Breadcrumb>
|
||||||
<Breadcrumb.Item>
|
<Breadcrumb.Item>
|
||||||
<span>Network-server</span>
|
<span>Network Server</span>
|
||||||
</Breadcrumb.Item>
|
</Breadcrumb.Item>
|
||||||
<Breadcrumb.Item>
|
<Breadcrumb.Item>
|
||||||
<span>
|
<span>
|
||||||
|
@ -11,6 +11,7 @@ import type { Tenant } from "@chirpstack/chirpstack-api-grpc-web/api/tenant_pb";
|
|||||||
import ApiKeyForm from "./ApiKeyForm";
|
import ApiKeyForm from "./ApiKeyForm";
|
||||||
import ApiKeyToken from "./ApiKeyToken";
|
import ApiKeyToken from "./ApiKeyToken";
|
||||||
import InternalStore from "../../stores/InternalStore";
|
import InternalStore from "../../stores/InternalStore";
|
||||||
|
import { useTitle } from "../helpers";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
tenant: Tenant;
|
tenant: Tenant;
|
||||||
@ -18,6 +19,7 @@ interface IProps {
|
|||||||
|
|
||||||
function CreateTenantApiKey(props: IProps) {
|
function CreateTenantApiKey(props: IProps) {
|
||||||
const [createApiKeyResponse, setCreateApiKeyResponse] = useState<CreateApiKeyResponse | undefined>(undefined);
|
const [createApiKeyResponse, setCreateApiKeyResponse] = useState<CreateApiKeyResponse | undefined>(undefined);
|
||||||
|
useTitle("Tenants", props.tenant.getName(), "API Keys", "Add");
|
||||||
|
|
||||||
const onFinish = (obj: ApiKey) => {
|
const onFinish = (obj: ApiKey) => {
|
||||||
obj.setTenantId(props.tenant.getId());
|
obj.setTenantId(props.tenant.getId());
|
||||||
|
@ -13,8 +13,10 @@ import type { GetPageCallbackFunc } from "../../components/DataTable";
|
|||||||
import DataTable from "../../components/DataTable";
|
import DataTable from "../../components/DataTable";
|
||||||
import InternalStore from "../../stores/InternalStore";
|
import InternalStore from "../../stores/InternalStore";
|
||||||
import DeleteConfirm from "../../components/DeleteConfirm";
|
import DeleteConfirm from "../../components/DeleteConfirm";
|
||||||
|
import { useTitle } from "../helpers";
|
||||||
|
|
||||||
function ListAdminApiKeys() {
|
function ListAdminApiKeys() {
|
||||||
|
useTitle("Network Server", "API keys");
|
||||||
const [refreshKey, setRefreshKey] = useState<number>(1);
|
const [refreshKey, setRefreshKey] = useState<number>(1);
|
||||||
|
|
||||||
const columns: ColumnsType<ApiKey.AsObject> = [
|
const columns: ColumnsType<ApiKey.AsObject> = [
|
||||||
|
@ -15,6 +15,7 @@ import DataTable from "../../components/DataTable";
|
|||||||
import InternalStore from "../../stores/InternalStore";
|
import InternalStore from "../../stores/InternalStore";
|
||||||
import DeleteConfirm from "../../components/DeleteConfirm";
|
import DeleteConfirm from "../../components/DeleteConfirm";
|
||||||
import Admin from "../../components/Admin";
|
import Admin from "../../components/Admin";
|
||||||
|
import { useTitle } from "../helpers";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
tenant: Tenant;
|
tenant: Tenant;
|
||||||
@ -22,6 +23,7 @@ interface IProps {
|
|||||||
|
|
||||||
function ListTenantApiKeys(props: IProps) {
|
function ListTenantApiKeys(props: IProps) {
|
||||||
const [refreshKey, setRefreshKey] = useState<number>(1);
|
const [refreshKey, setRefreshKey] = useState<number>(1);
|
||||||
|
useTitle("Tenants", props.tenant.getName(), "API Keys");
|
||||||
|
|
||||||
const columns: ColumnsType<ApiKey.AsObject> = [
|
const columns: ColumnsType<ApiKey.AsObject> = [
|
||||||
{
|
{
|
||||||
|
@ -38,6 +38,7 @@ import EditThingsBoardIntegration from "./integrations/EditThingsBoardIntegratio
|
|||||||
import GenerateMqttCertificate from "./integrations/GenerateMqttCertificate";
|
import GenerateMqttCertificate from "./integrations/GenerateMqttCertificate";
|
||||||
import CreateIftttIntegration from "./integrations/CreateIftttIntegration";
|
import CreateIftttIntegration from "./integrations/CreateIftttIntegration";
|
||||||
import EditIftttIntegration from "./integrations/EditIftttIntegration";
|
import EditIftttIntegration from "./integrations/EditIftttIntegration";
|
||||||
|
import { useTitle } from "../helpers";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
tenant: Tenant;
|
tenant: Tenant;
|
||||||
@ -48,6 +49,7 @@ interface IProps {
|
|||||||
function ApplicationLayout(props: IProps) {
|
function ApplicationLayout(props: IProps) {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
|
useTitle("Tenants", props.tenant.getName(), "Applications", props.application.getName());
|
||||||
|
|
||||||
const deleteApplication = () => {
|
const deleteApplication = () => {
|
||||||
const req = new DeleteApplicationRequest();
|
const req = new DeleteApplicationRequest();
|
||||||
|
@ -9,6 +9,7 @@ import { Application, CreateApplicationRequest } from "@chirpstack/chirpstack-ap
|
|||||||
|
|
||||||
import ApplicationForm from "./ApplicationForm";
|
import ApplicationForm from "./ApplicationForm";
|
||||||
import ApplicationStore from "../../stores/ApplicationStore";
|
import ApplicationStore from "../../stores/ApplicationStore";
|
||||||
|
import { useTitle } from "../helpers";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
tenant: Tenant;
|
tenant: Tenant;
|
||||||
@ -16,6 +17,7 @@ interface IProps {
|
|||||||
|
|
||||||
function CreateApplication(props: IProps) {
|
function CreateApplication(props: IProps) {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
useTitle("Tenants", props.tenant.getName(), "Applications", "Add");
|
||||||
|
|
||||||
const onFinish = (obj: Application) => {
|
const onFinish = (obj: Application) => {
|
||||||
obj.setTenantId(props.tenant.getId());
|
obj.setTenantId(props.tenant.getId());
|
||||||
|
@ -15,12 +15,14 @@ import type { GetPageCallbackFunc } from "../../components/DataTable";
|
|||||||
import DataTable from "../../components/DataTable";
|
import DataTable from "../../components/DataTable";
|
||||||
import ApplicationStore from "../../stores/ApplicationStore";
|
import ApplicationStore from "../../stores/ApplicationStore";
|
||||||
import Admin from "../../components/Admin";
|
import Admin from "../../components/Admin";
|
||||||
|
import { useTitle } from "../helpers";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
tenant: Tenant;
|
tenant: Tenant;
|
||||||
}
|
}
|
||||||
|
|
||||||
function ListApplications(props: IProps) {
|
function ListApplications(props: IProps) {
|
||||||
|
useTitle("Tenants", props.tenant.getName(), "Applications");
|
||||||
const columns: ColumnsType<ApplicationListItem.AsObject> = [
|
const columns: ColumnsType<ApplicationListItem.AsObject> = [
|
||||||
{
|
{
|
||||||
title: "Name",
|
title: "Name",
|
||||||
|
@ -26,6 +26,7 @@ import InternalStore from "../../stores/InternalStore";
|
|||||||
import GatewayStore from "../../stores/GatewayStore";
|
import GatewayStore from "../../stores/GatewayStore";
|
||||||
import type { MarkerColor } from "../../components/Map";
|
import type { MarkerColor } from "../../components/Map";
|
||||||
import Map, { Marker } from "../../components/Map";
|
import Map, { Marker } from "../../components/Map";
|
||||||
|
import { useTitle } from "../helpers";
|
||||||
|
|
||||||
function GatewaysMap() {
|
function GatewaysMap() {
|
||||||
const [items, setItems] = useState<GatewayListItem[]>([]);
|
const [items, setItems] = useState<GatewayListItem[]>([]);
|
||||||
@ -224,6 +225,7 @@ function DevicesDataRates({ summary }: { summary?: GetDevicesSummaryResponse })
|
|||||||
}
|
}
|
||||||
|
|
||||||
function Dashboard() {
|
function Dashboard() {
|
||||||
|
useTitle("Network Server", "Dashboard");
|
||||||
const [gatewaysSummary, setGatewaysSummary] = useState<GetGatewaysSummaryResponse | undefined>(undefined);
|
const [gatewaysSummary, setGatewaysSummary] = useState<GetGatewaysSummaryResponse | undefined>(undefined);
|
||||||
const [devicesSummary, setDevicesSummary] = useState<GetDevicesSummaryResponse | undefined>(undefined);
|
const [devicesSummary, setDevicesSummary] = useState<GetDevicesSummaryResponse | undefined>(undefined);
|
||||||
|
|
||||||
|
@ -11,8 +11,10 @@ import {
|
|||||||
|
|
||||||
import DeviceProfileTemplateForm from "./DeviceProfileTemplateForm";
|
import DeviceProfileTemplateForm from "./DeviceProfileTemplateForm";
|
||||||
import DeviceProfileTemplateStore from "../../stores/DeviceProfileTemplateStore";
|
import DeviceProfileTemplateStore from "../../stores/DeviceProfileTemplateStore";
|
||||||
|
import { useTitle } from "../helpers";
|
||||||
|
|
||||||
function CreateDeviceProfileTemplate() {
|
function CreateDeviceProfileTemplate() {
|
||||||
|
useTitle("Network Server", "Device-profile templates", "Add");
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const onFinish = (obj: DeviceProfileTemplate) => {
|
const onFinish = (obj: DeviceProfileTemplate) => {
|
||||||
|
@ -18,11 +18,13 @@ import {
|
|||||||
import DeviceProfileTemplateForm from "./DeviceProfileTemplateForm";
|
import DeviceProfileTemplateForm from "./DeviceProfileTemplateForm";
|
||||||
import DeviceProfileTemplateStore from "../../stores/DeviceProfileTemplateStore";
|
import DeviceProfileTemplateStore from "../../stores/DeviceProfileTemplateStore";
|
||||||
import DeleteConfirm from "../../components/DeleteConfirm";
|
import DeleteConfirm from "../../components/DeleteConfirm";
|
||||||
|
import { useTitle } from "../helpers";
|
||||||
|
|
||||||
function EditDeviceProfileTemplate() {
|
function EditDeviceProfileTemplate() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [deviceProfileTemplate, setDeviceProfileTemplate] = useState<DeviceProfileTemplate | undefined>(undefined);
|
const [deviceProfileTemplate, setDeviceProfileTemplate] = useState<DeviceProfileTemplate | undefined>(undefined);
|
||||||
const { deviceProfileTemplateId } = useParams();
|
const { deviceProfileTemplateId } = useParams();
|
||||||
|
useTitle("Network Server", "Device-profile templates", deviceProfileTemplate?.getName());
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const id = deviceProfileTemplateId!;
|
const id = deviceProfileTemplateId!;
|
||||||
|
@ -15,8 +15,10 @@ import { getEnumName } from "../helpers";
|
|||||||
import type { GetPageCallbackFunc } from "../../components/DataTable";
|
import type { GetPageCallbackFunc } from "../../components/DataTable";
|
||||||
import DataTable from "../../components/DataTable";
|
import DataTable from "../../components/DataTable";
|
||||||
import DeviceProfileTemplateStore from "../../stores/DeviceProfileTemplateStore";
|
import DeviceProfileTemplateStore from "../../stores/DeviceProfileTemplateStore";
|
||||||
|
import { useTitle } from "../helpers";
|
||||||
|
|
||||||
function ListDeviceProfileTemplates() {
|
function ListDeviceProfileTemplates() {
|
||||||
|
useTitle("Network Server", "Device-profile templates");
|
||||||
const columns: ColumnsType<DeviceProfileTemplateListItem.AsObject> = [
|
const columns: ColumnsType<DeviceProfileTemplateListItem.AsObject> = [
|
||||||
{
|
{
|
||||||
title: "Vendor",
|
title: "Vendor",
|
||||||
|
@ -11,6 +11,7 @@ import type { Tenant } from "@chirpstack/chirpstack-api-grpc-web/api/tenant_pb";
|
|||||||
|
|
||||||
import DeviceProfileForm from "./DeviceProfileForm";
|
import DeviceProfileForm from "./DeviceProfileForm";
|
||||||
import DeviceProfileStore from "../../stores/DeviceProfileStore";
|
import DeviceProfileStore from "../../stores/DeviceProfileStore";
|
||||||
|
import { useTitle } from "../helpers";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
tenant: Tenant;
|
tenant: Tenant;
|
||||||
@ -18,6 +19,7 @@ interface IProps {
|
|||||||
|
|
||||||
function CreateDeviceProfile(props: IProps) {
|
function CreateDeviceProfile(props: IProps) {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
useTitle("Tenants", props.tenant.getName(), "Device profiles", "Add");
|
||||||
|
|
||||||
const onFinish = (obj: DeviceProfile) => {
|
const onFinish = (obj: DeviceProfile) => {
|
||||||
obj.setTenantId(props.tenant.getId());
|
obj.setTenantId(props.tenant.getId());
|
||||||
|
@ -20,6 +20,7 @@ import DeviceProfileStore from "../../stores/DeviceProfileStore";
|
|||||||
import SessionStore from "../../stores/SessionStore";
|
import SessionStore from "../../stores/SessionStore";
|
||||||
import DeleteConfirm from "../../components/DeleteConfirm";
|
import DeleteConfirm from "../../components/DeleteConfirm";
|
||||||
import Admin from "../../components/Admin";
|
import Admin from "../../components/Admin";
|
||||||
|
import { useTitle } from "../helpers";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
tenant: Tenant;
|
tenant: Tenant;
|
||||||
@ -29,6 +30,7 @@ function EditDeviceProfile(props: IProps) {
|
|||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [deviceProfile, setDeviceProfile] = useState<DeviceProfile | undefined>(undefined);
|
const [deviceProfile, setDeviceProfile] = useState<DeviceProfile | undefined>(undefined);
|
||||||
const { deviceProfileId } = useParams();
|
const { deviceProfileId } = useParams();
|
||||||
|
useTitle("Tenants", props.tenant.getName(), "Device profiles", deviceProfile?.getName());
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const id = deviceProfileId!;
|
const id = deviceProfileId!;
|
||||||
|
@ -17,12 +17,14 @@ import type { GetPageCallbackFunc } from "../../components/DataTable";
|
|||||||
import DataTable from "../../components/DataTable";
|
import DataTable from "../../components/DataTable";
|
||||||
import DeviceProfileStore from "../../stores/DeviceProfileStore";
|
import DeviceProfileStore from "../../stores/DeviceProfileStore";
|
||||||
import Admin from "../../components/Admin";
|
import Admin from "../../components/Admin";
|
||||||
|
import { useTitle } from "../helpers";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
tenant: Tenant;
|
tenant: Tenant;
|
||||||
}
|
}
|
||||||
|
|
||||||
function ListDeviceProfiles(props: IProps) {
|
function ListDeviceProfiles(props: IProps) {
|
||||||
|
useTitle("Tenants", props.tenant.getName(), "Device profiles");
|
||||||
const columns: ColumnsType<DeviceProfileListItem.AsObject> = [
|
const columns: ColumnsType<DeviceProfileListItem.AsObject> = [
|
||||||
{
|
{
|
||||||
title: "Name",
|
title: "Name",
|
||||||
|
@ -12,6 +12,7 @@ import { GetDeviceProfileRequest } from "@chirpstack/chirpstack-api-grpc-web/api
|
|||||||
import DeviceForm from "./DeviceForm";
|
import DeviceForm from "./DeviceForm";
|
||||||
import DeviceStore from "../../stores/DeviceStore";
|
import DeviceStore from "../../stores/DeviceStore";
|
||||||
import DeviceProfileStore from "../../stores/DeviceProfileStore";
|
import DeviceProfileStore from "../../stores/DeviceProfileStore";
|
||||||
|
import { useTitle } from "../helpers";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
tenant: Tenant;
|
tenant: Tenant;
|
||||||
@ -20,6 +21,7 @@ interface IProps {
|
|||||||
|
|
||||||
function CreateDevice(props: IProps) {
|
function CreateDevice(props: IProps) {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
useTitle("Tenants", props.tenant.getName(), "Applications", props.application.getName(), "Add device");
|
||||||
|
|
||||||
const onFinish = (obj: Device) => {
|
const onFinish = (obj: Device) => {
|
||||||
obj.setApplicationId(props.application.getId());
|
obj.setApplicationId(props.application.getId());
|
||||||
|
@ -26,6 +26,7 @@ import DeviceFrames from "./DeviceFrames";
|
|||||||
import DeviceEvents from "./DeviceEvents";
|
import DeviceEvents from "./DeviceEvents";
|
||||||
import DeviceQueue from "./DeviceQueue";
|
import DeviceQueue from "./DeviceQueue";
|
||||||
import DeviceActivation from "./DeviceActivation";
|
import DeviceActivation from "./DeviceActivation";
|
||||||
|
import { useTitle } from "../helpers";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
tenant: Tenant;
|
tenant: Tenant;
|
||||||
@ -40,6 +41,14 @@ function DeviceLayout(props: IProps) {
|
|||||||
const [device, setDevice] = useState<Device | undefined>(undefined);
|
const [device, setDevice] = useState<Device | undefined>(undefined);
|
||||||
const [deviceProfile, setDeviceProfile] = useState<DeviceProfile | undefined>(undefined);
|
const [deviceProfile, setDeviceProfile] = useState<DeviceProfile | undefined>(undefined);
|
||||||
const [lastSeenAt, setLastSeenAt] = useState<Date | undefined>(undefined);
|
const [lastSeenAt, setLastSeenAt] = useState<Date | undefined>(undefined);
|
||||||
|
useTitle(
|
||||||
|
"Tenants",
|
||||||
|
props.tenant.getName(),
|
||||||
|
"Applications",
|
||||||
|
props.application.getName(),
|
||||||
|
"Devices",
|
||||||
|
device?.getName(),
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const loadDevice = () => {
|
const loadDevice = () => {
|
||||||
|
@ -8,6 +8,7 @@ import type { Tenant } from "@chirpstack/chirpstack-api-grpc-web/api/tenant_pb";
|
|||||||
|
|
||||||
import GatewayForm from "./GatewayForm";
|
import GatewayForm from "./GatewayForm";
|
||||||
import GatewayStore from "../../stores/GatewayStore";
|
import GatewayStore from "../../stores/GatewayStore";
|
||||||
|
import { useTitle } from "../helpers";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
tenant: Tenant;
|
tenant: Tenant;
|
||||||
@ -15,6 +16,7 @@ interface IProps {
|
|||||||
|
|
||||||
function CreateGateway(props: IProps) {
|
function CreateGateway(props: IProps) {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
useTitle("Tenants", props.tenant.getName(), "Gateways", "Add");
|
||||||
|
|
||||||
const onFinish = (obj: Gateway) => {
|
const onFinish = (obj: Gateway) => {
|
||||||
obj.setTenantId(props.tenant.getId());
|
obj.setTenantId(props.tenant.getId());
|
||||||
|
@ -18,6 +18,7 @@ import GatewayFrames from "./GatewayFrames";
|
|||||||
import GatewayCertificate from "./GatewayCertificate";
|
import GatewayCertificate from "./GatewayCertificate";
|
||||||
import Admin from "../../components/Admin";
|
import Admin from "../../components/Admin";
|
||||||
import SessionStore from "../../stores/SessionStore";
|
import SessionStore from "../../stores/SessionStore";
|
||||||
|
import { useTitle } from "../helpers";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
tenant: Tenant;
|
tenant: Tenant;
|
||||||
@ -29,6 +30,7 @@ function GatewayLayout(props: IProps) {
|
|||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const [gateway, setGateway] = useState<Gateway | undefined>(undefined);
|
const [gateway, setGateway] = useState<Gateway | undefined>(undefined);
|
||||||
const [lastSeenAt, setLastSeenAt] = useState<Date | undefined>(undefined);
|
const [lastSeenAt, setLastSeenAt] = useState<Date | undefined>(undefined);
|
||||||
|
useTitle("Tenants", props.tenant.getName(), "Gateways", gateway?.getName());
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const req = new GetGatewayRequest();
|
const req = new GetGatewayRequest();
|
||||||
|
@ -23,6 +23,7 @@ import GatewayStore from "../../stores/GatewayStore";
|
|||||||
import ApplicationStore from "../../stores/ApplicationStore";
|
import ApplicationStore from "../../stores/ApplicationStore";
|
||||||
import MulticastGroupStore from "../../stores/MulticastGroupStore";
|
import MulticastGroupStore from "../../stores/MulticastGroupStore";
|
||||||
import Admin from "../../components/Admin";
|
import Admin from "../../components/Admin";
|
||||||
|
import { useTitle } from "../helpers";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
tenant: Tenant;
|
tenant: Tenant;
|
||||||
@ -40,6 +41,7 @@ function ListGateways(props: IProps) {
|
|||||||
const [multicastGroups, setMulticastGroups] = useState<MulticastGroup[]>([]);
|
const [multicastGroups, setMulticastGroups] = useState<MulticastGroup[]>([]);
|
||||||
const [mgModalVisible, setMgModalVisible] = useState<boolean>(false);
|
const [mgModalVisible, setMgModalVisible] = useState<boolean>(false);
|
||||||
const [mgSelected, setMgSelected] = useState<string>("");
|
const [mgSelected, setMgSelected] = useState<string>("");
|
||||||
|
useTitle("Tenants", props.tenant.getName(), "Gateways");
|
||||||
|
|
||||||
const columns: ColumnsType<GatewayListItem.AsObject> = [
|
const columns: ColumnsType<GatewayListItem.AsObject> = [
|
||||||
{
|
{
|
||||||
|
@ -15,12 +15,14 @@ import type { Tenant } from "@chirpstack/chirpstack-api-grpc-web/api/tenant_pb";
|
|||||||
import type { GetPageCallbackFunc } from "../../../components/DataTable";
|
import type { GetPageCallbackFunc } from "../../../components/DataTable";
|
||||||
import DataTable from "../../../components/DataTable";
|
import DataTable from "../../../components/DataTable";
|
||||||
import GatewayStore from "../../../stores/GatewayStore";
|
import GatewayStore from "../../../stores/GatewayStore";
|
||||||
|
import { useTitle } from "../../helpers";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
tenant: Tenant;
|
tenant: Tenant;
|
||||||
}
|
}
|
||||||
|
|
||||||
function ListRelayGateways(props: IProps) {
|
function ListRelayGateways(props: IProps) {
|
||||||
|
useTitle("Tenants", props.tenant.getName(), "Gateway Mesh", "Relay Gateways");
|
||||||
const columns: ColumnsType<RelayGatewayListItem.AsObject> = [
|
const columns: ColumnsType<RelayGatewayListItem.AsObject> = [
|
||||||
{
|
{
|
||||||
title: "",
|
title: "",
|
||||||
|
@ -15,6 +15,7 @@ import GatewayStore from "../../../stores/GatewayStore";
|
|||||||
import DeleteConfirm from "../../../components/DeleteConfirm";
|
import DeleteConfirm from "../../../components/DeleteConfirm";
|
||||||
|
|
||||||
import EditRelayGateway from "./EditRelayGateway";
|
import EditRelayGateway from "./EditRelayGateway";
|
||||||
|
import { useTitle } from "../../helpers";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
tenant: Tenant;
|
tenant: Tenant;
|
||||||
@ -24,6 +25,7 @@ function RelayGatewayLayout(props: IProps) {
|
|||||||
const { relayId } = useParams();
|
const { relayId } = useParams();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [relayGateway, setRelayGateway] = useState<RelayGateway | undefined>(undefined);
|
const [relayGateway, setRelayGateway] = useState<RelayGateway | undefined>(undefined);
|
||||||
|
useTitle("Tenants", props.tenant.getName(), "Gateway Mesh", "Relay Gateways", relayGateway?.getName());
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const req = new GetRelayGatewayRequest();
|
const req = new GetRelayGatewayRequest();
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { notification } from "antd";
|
import { notification } from "antd";
|
||||||
import { MacVersion, RegParamsRevision } from "@chirpstack/chirpstack-api-grpc-web/common/common_pb";
|
import { MacVersion, RegParamsRevision } from "@chirpstack/chirpstack-api-grpc-web/common/common_pb";
|
||||||
|
import { useRef, useEffect } from "react";
|
||||||
|
|
||||||
export function formatMacVersion(m: MacVersion) {
|
export function formatMacVersion(m: MacVersion) {
|
||||||
switch (m) {
|
switch (m) {
|
||||||
@ -61,3 +62,28 @@ export function onFinishFailed() {
|
|||||||
duration: 3,
|
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]);
|
||||||
|
}
|
||||||
|
@ -13,6 +13,7 @@ import {
|
|||||||
|
|
||||||
import MulticastGroupForm from "./MulticastGroupForm";
|
import MulticastGroupForm from "./MulticastGroupForm";
|
||||||
import MulticastGroupStore from "../../stores/MulticastGroupStore";
|
import MulticastGroupStore from "../../stores/MulticastGroupStore";
|
||||||
|
import { useTitle } from "../helpers";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
tenant: Tenant;
|
tenant: Tenant;
|
||||||
@ -21,6 +22,7 @@ interface IProps {
|
|||||||
|
|
||||||
function CreateMulticastGroup(props: IProps) {
|
function CreateMulticastGroup(props: IProps) {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
useTitle("Tenants", props.tenant.getName(), "Applications", props.application.getName(), "Add multicast-group");
|
||||||
|
|
||||||
const onFinish = (obj: MulticastGroup) => {
|
const onFinish = (obj: MulticastGroup) => {
|
||||||
obj.setApplicationId(props.application.getId());
|
obj.setApplicationId(props.application.getId());
|
||||||
|
@ -22,6 +22,7 @@ import ListMulticastGroupGateways from "./ListMulticastGroupGateways";
|
|||||||
import EditMulticastGroup from "./EditMulticastGroup";
|
import EditMulticastGroup from "./EditMulticastGroup";
|
||||||
import Admin from "../../components/Admin";
|
import Admin from "../../components/Admin";
|
||||||
import MulticastGroupQueue from "./MulticastGroupQueue";
|
import MulticastGroupQueue from "./MulticastGroupQueue";
|
||||||
|
import { useTitle } from "../helpers";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
tenant: Tenant;
|
tenant: Tenant;
|
||||||
@ -33,6 +34,14 @@ function MulticastGroupLayout(props: IProps) {
|
|||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const [multicastGroup, setMulticastGroup] = useState<MulticastGroup | undefined>(undefined);
|
const [multicastGroup, setMulticastGroup] = useState<MulticastGroup | undefined>(undefined);
|
||||||
|
useTitle(
|
||||||
|
"Tenants",
|
||||||
|
props.tenant.getName(),
|
||||||
|
"Applications",
|
||||||
|
props.application.getName(),
|
||||||
|
"Multicast-groups",
|
||||||
|
multicastGroup?.getName(),
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const req = new GetMulticastGroupRequest();
|
const req = new GetMulticastGroupRequest();
|
||||||
|
@ -9,8 +9,10 @@ import type { ListRegionsResponse, RegionListItem } from "@chirpstack/chirpstack
|
|||||||
|
|
||||||
import { getEnumName } from "../helpers";
|
import { getEnumName } from "../helpers";
|
||||||
import InternalStore from "../../stores/InternalStore";
|
import InternalStore from "../../stores/InternalStore";
|
||||||
|
import { useTitle } from "../helpers";
|
||||||
|
|
||||||
function ListRegions() {
|
function ListRegions() {
|
||||||
|
useTitle("Network Server", "Regions");
|
||||||
const [regions, setRegions] = useState<ListRegionsResponse | undefined>(undefined);
|
const [regions, setRegions] = useState<ListRegionsResponse | undefined>(undefined);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -12,10 +12,12 @@ import { GetRegionRequest } from "@chirpstack/chirpstack-api-grpc-web/api/intern
|
|||||||
|
|
||||||
import { getEnumName } from "../helpers";
|
import { getEnumName } from "../helpers";
|
||||||
import InternalStore from "../../stores/InternalStore";
|
import InternalStore from "../../stores/InternalStore";
|
||||||
|
import { useTitle } from "../helpers";
|
||||||
|
|
||||||
function RegionDetails() {
|
function RegionDetails() {
|
||||||
const [region, setRegion] = useState<GetRegionResponse | undefined>(undefined);
|
const [region, setRegion] = useState<GetRegionResponse | undefined>(undefined);
|
||||||
const { id } = useParams();
|
const { id } = useParams();
|
||||||
|
useTitle("Network Server", "Regions", region?.getDescription());
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const req = new GetRegionRequest();
|
const req = new GetRegionRequest();
|
||||||
|
@ -11,6 +11,7 @@ import { GetDeviceRequest } from "@chirpstack/chirpstack-api-grpc-web/api/device
|
|||||||
|
|
||||||
import DeviceStore from "../../stores/DeviceStore";
|
import DeviceStore from "../../stores/DeviceStore";
|
||||||
import ListRelayDevices from "./ListRelayDevices";
|
import ListRelayDevices from "./ListRelayDevices";
|
||||||
|
import { useTitle } from "../helpers";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
tenant: Tenant;
|
tenant: Tenant;
|
||||||
@ -20,6 +21,14 @@ interface IProps {
|
|||||||
function RelayLayout(props: IProps) {
|
function RelayLayout(props: IProps) {
|
||||||
const [relayDevice, setRelayDevice] = useState<Device | undefined>(undefined);
|
const [relayDevice, setRelayDevice] = useState<Device | undefined>(undefined);
|
||||||
const { relayDevEui } = useParams();
|
const { relayDevEui } = useParams();
|
||||||
|
useTitle(
|
||||||
|
"Tenants",
|
||||||
|
props.tenant.getName(),
|
||||||
|
"Applications",
|
||||||
|
props.application.getName(),
|
||||||
|
"Relays",
|
||||||
|
relayDevice?.getName(),
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const req = new GetDeviceRequest();
|
const req = new GetDeviceRequest();
|
||||||
|
@ -8,8 +8,10 @@ import { Tenant, CreateTenantRequest } from "@chirpstack/chirpstack-api-grpc-web
|
|||||||
|
|
||||||
import TenantForm from "./TenantForm";
|
import TenantForm from "./TenantForm";
|
||||||
import TenantStore from "../../stores/TenantStore";
|
import TenantStore from "../../stores/TenantStore";
|
||||||
|
import { useTitle } from "../helpers";
|
||||||
|
|
||||||
function CreateTenant() {
|
function CreateTenant() {
|
||||||
|
useTitle("Network Server", "Tenants", "Add");
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const onFinish = (obj: Tenant) => {
|
const onFinish = (obj: Tenant) => {
|
||||||
@ -29,7 +31,7 @@ function CreateTenant() {
|
|||||||
breadcrumbRender={() => (
|
breadcrumbRender={() => (
|
||||||
<Breadcrumb>
|
<Breadcrumb>
|
||||||
<Breadcrumb.Item>
|
<Breadcrumb.Item>
|
||||||
<span>Network-server</span>
|
<span>Network Server</span>
|
||||||
</Breadcrumb.Item>
|
</Breadcrumb.Item>
|
||||||
<Breadcrumb.Item>
|
<Breadcrumb.Item>
|
||||||
<span>
|
<span>
|
||||||
|
@ -8,9 +8,11 @@ import { TenantUser, AddTenantUserRequest } from "@chirpstack/chirpstack-api-grp
|
|||||||
|
|
||||||
import TenantUserForm from "./TenantUserForm";
|
import TenantUserForm from "./TenantUserForm";
|
||||||
import TenantStore from "../../stores/TenantStore";
|
import TenantStore from "../../stores/TenantStore";
|
||||||
|
import { useTitle } from "../helpers";
|
||||||
|
|
||||||
function CreateTenantUser({ tenant }: { tenant: Tenant }) {
|
function CreateTenantUser({ tenant }: { tenant: Tenant }) {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
useTitle("Tenants", tenant.getName(), "Tenant users", "Add");
|
||||||
|
|
||||||
const onFinish = (obj: TenantUser) => {
|
const onFinish = (obj: TenantUser) => {
|
||||||
obj.setTenantId(tenant.getId());
|
obj.setTenantId(tenant.getId());
|
||||||
|
@ -16,11 +16,13 @@ import TenantStore from "../../stores/TenantStore";
|
|||||||
import SessionStore from "../../stores/SessionStore";
|
import SessionStore from "../../stores/SessionStore";
|
||||||
import DeleteConfirm from "../../components/DeleteConfirm";
|
import DeleteConfirm from "../../components/DeleteConfirm";
|
||||||
import Admin from "../../components/Admin";
|
import Admin from "../../components/Admin";
|
||||||
|
import { useTitle } from "../helpers";
|
||||||
|
|
||||||
function EditTenantUser({ tenant }: { tenant: Tenant }) {
|
function EditTenantUser({ tenant }: { tenant: Tenant }) {
|
||||||
const [tenantUser, setTenantUser] = useState<TenantUser | undefined>(undefined);
|
const [tenantUser, setTenantUser] = useState<TenantUser | undefined>(undefined);
|
||||||
const { userId } = useParams();
|
const { userId } = useParams();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
useTitle("Tenants", tenant.getName(), "Tenant users", tenantUser?.getEmail());
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const req = new GetTenantUserRequest();
|
const req = new GetTenantUserRequest();
|
||||||
|
@ -12,12 +12,14 @@ import type { GetPageCallbackFunc } from "../../components/DataTable";
|
|||||||
import DataTable from "../../components/DataTable";
|
import DataTable from "../../components/DataTable";
|
||||||
import TenantStore from "../../stores/TenantStore";
|
import TenantStore from "../../stores/TenantStore";
|
||||||
import Admin from "../../components/Admin";
|
import Admin from "../../components/Admin";
|
||||||
|
import { useTitle } from "../helpers";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
tenant: Tenant;
|
tenant: Tenant;
|
||||||
}
|
}
|
||||||
|
|
||||||
function ListTenatUsers(props: IProps) {
|
function ListTenantUsers(props: IProps) {
|
||||||
|
useTitle("Tenants", props.tenant.getName(), "Tenant users");
|
||||||
const columns: ColumnsType<TenantUserListItem.AsObject> = [
|
const columns: ColumnsType<TenantUserListItem.AsObject> = [
|
||||||
{
|
{
|
||||||
title: "Email",
|
title: "Email",
|
||||||
@ -107,4 +109,4 @@ function ListTenatUsers(props: IProps) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ListTenatUsers;
|
export default ListTenantUsers;
|
||||||
|
@ -10,8 +10,10 @@ import { ListTenantsRequest } from "@chirpstack/chirpstack-api-grpc-web/api/tena
|
|||||||
import type { GetPageCallbackFunc } from "../../components/DataTable";
|
import type { GetPageCallbackFunc } from "../../components/DataTable";
|
||||||
import DataTable from "../../components/DataTable";
|
import DataTable from "../../components/DataTable";
|
||||||
import TenantStore from "../../stores/TenantStore";
|
import TenantStore from "../../stores/TenantStore";
|
||||||
|
import { useTitle } from "../helpers";
|
||||||
|
|
||||||
function ListTenants() {
|
function ListTenants() {
|
||||||
|
useTitle("Network Server", "Tenants");
|
||||||
const columns: ColumnsType<TenantListItem.AsObject> = [
|
const columns: ColumnsType<TenantListItem.AsObject> = [
|
||||||
{
|
{
|
||||||
title: "Name",
|
title: "Name",
|
||||||
|
@ -11,8 +11,10 @@ import DeleteConfirm from "../../components/DeleteConfirm";
|
|||||||
import Admin from "../../components/Admin";
|
import Admin from "../../components/Admin";
|
||||||
import EditTenant from "./EditTenant";
|
import EditTenant from "./EditTenant";
|
||||||
import TenantDashboard from "./TenantDashboard";
|
import TenantDashboard from "./TenantDashboard";
|
||||||
|
import { useTitle } from "../helpers";
|
||||||
|
|
||||||
function TenantLayout({ tenant }: { tenant: Tenant }) {
|
function TenantLayout({ tenant }: { tenant: Tenant }) {
|
||||||
|
useTitle("Tenants", tenant.getName());
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
|
|
||||||
|
@ -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 { GetUserRequest, UpdateUserPasswordRequest } from "@chirpstack/chirpstack-api-grpc-web/api/user_pb";
|
||||||
import UserStore from "../../stores/UserStore";
|
import UserStore from "../../stores/UserStore";
|
||||||
import PasswordForm from "./PasswordForm";
|
import PasswordForm from "./PasswordForm";
|
||||||
|
import { useTitle } from "../helpers";
|
||||||
|
|
||||||
function ChangeUserPassword() {
|
function ChangeUserPassword() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { userId } = useParams();
|
const { userId } = useParams();
|
||||||
const [user, setUser] = useState<User | undefined>(undefined);
|
const [user, setUser] = useState<User | undefined>(undefined);
|
||||||
|
useTitle("Users", user?.getEmail(), "Change password");
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const req = new GetUserRequest();
|
const req = new GetUserRequest();
|
||||||
|
@ -8,8 +8,10 @@ import { User, CreateUserRequest } from "@chirpstack/chirpstack-api-grpc-web/api
|
|||||||
|
|
||||||
import UserForm from "./UserForm";
|
import UserForm from "./UserForm";
|
||||||
import UserStore from "../../stores/UserStore";
|
import UserStore from "../../stores/UserStore";
|
||||||
|
import { useTitle } from "../helpers";
|
||||||
|
|
||||||
function CreateUser() {
|
function CreateUser() {
|
||||||
|
useTitle("Network Server", "Users", "Add");
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const onFinish = (obj: User, password: string) => {
|
const onFinish = (obj: User, password: string) => {
|
||||||
@ -30,7 +32,7 @@ function CreateUser() {
|
|||||||
breadcrumbRender={() => (
|
breadcrumbRender={() => (
|
||||||
<Breadcrumb>
|
<Breadcrumb>
|
||||||
<Breadcrumb.Item>
|
<Breadcrumb.Item>
|
||||||
<span>Network-server</span>
|
<span>Network Server</span>
|
||||||
</Breadcrumb.Item>
|
</Breadcrumb.Item>
|
||||||
<Breadcrumb.Item>
|
<Breadcrumb.Item>
|
||||||
<span>
|
<span>
|
||||||
|
@ -10,11 +10,13 @@ import { GetUserRequest, UpdateUserRequest, DeleteUserRequest } from "@chirpstac
|
|||||||
import UserForm from "./UserForm";
|
import UserForm from "./UserForm";
|
||||||
import UserStore from "../../stores/UserStore";
|
import UserStore from "../../stores/UserStore";
|
||||||
import DeleteConfirm from "../../components/DeleteConfirm";
|
import DeleteConfirm from "../../components/DeleteConfirm";
|
||||||
|
import { useTitle } from "../helpers";
|
||||||
|
|
||||||
function EditUser() {
|
function EditUser() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { userId } = useParams();
|
const { userId } = useParams();
|
||||||
const [user, setUser] = useState<User | undefined>(undefined);
|
const [user, setUser] = useState<User | undefined>(undefined);
|
||||||
|
useTitle("Network Server", "Users", user?.getEmail());
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const req = new GetUserRequest();
|
const req = new GetUserRequest();
|
||||||
@ -57,7 +59,7 @@ function EditUser() {
|
|||||||
breadcrumbRender={() => (
|
breadcrumbRender={() => (
|
||||||
<Breadcrumb>
|
<Breadcrumb>
|
||||||
<Breadcrumb.Item>
|
<Breadcrumb.Item>
|
||||||
<span>Network-server</span>
|
<span>Network Server</span>
|
||||||
</Breadcrumb.Item>
|
</Breadcrumb.Item>
|
||||||
<Breadcrumb.Item>
|
<Breadcrumb.Item>
|
||||||
<span>
|
<span>
|
||||||
|
@ -11,8 +11,10 @@ import type { GetPageCallbackFunc } from "../../components/DataTable";
|
|||||||
import DataTable from "../../components/DataTable";
|
import DataTable from "../../components/DataTable";
|
||||||
|
|
||||||
import UserStore from "../../stores/UserStore";
|
import UserStore from "../../stores/UserStore";
|
||||||
|
import { useTitle } from "../helpers";
|
||||||
|
|
||||||
function ListUsers() {
|
function ListUsers() {
|
||||||
|
useTitle("Network Server", "Users");
|
||||||
const columns: ColumnsType<UserListItem.AsObject> = [
|
const columns: ColumnsType<UserListItem.AsObject> = [
|
||||||
{
|
{
|
||||||
title: "Email",
|
title: "Email",
|
||||||
|
@ -9,6 +9,7 @@ import { OpenIdConnectLoginRequest, OAuth2LoginRequest } from "@chirpstack/chirp
|
|||||||
|
|
||||||
import SessionStore from "../../stores/SessionStore";
|
import SessionStore from "../../stores/SessionStore";
|
||||||
import InternalStore from "../../stores/InternalStore";
|
import InternalStore from "../../stores/InternalStore";
|
||||||
|
import { useTitle } from "../helpers";
|
||||||
|
|
||||||
const layout = {
|
const layout = {
|
||||||
labelCol: {
|
labelCol: {
|
||||||
@ -122,6 +123,7 @@ function LoginForm() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function Login() {
|
function Login() {
|
||||||
|
useTitle("Login");
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user