Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

move to production #2448

Merged
merged 7 commits into from
Feb 8, 2025
3 changes: 3 additions & 0 deletions netmanager-app/app/types/users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ export interface UserDetails {
rateLimit: number | null;
jobTitle?: string | null;
description?: string | null;
timezone?: string | null;
profilePicture: string | null;
phoneNumber: string | null;
updatedAt: string;
Expand Down Expand Up @@ -105,6 +106,8 @@ export interface DecodedToken {
privilege: string;
country: string | null;
profilePicture: string | null;
description: string | null;
timezone: string | null;
phoneNumber: string | null;
createdAt: string;
updatedAt: string;
Expand Down
82 changes: 14 additions & 68 deletions netmanager-app/components/Settings/ApiTokens.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type React from "react"
import { useEffect, useState } from "react"
import { useState } from "react"
import moment from "moment"
import { useAppDispatch, useAppSelector } from "@/core/redux/hooks"
import { useAppDispatch } from "@/core/redux/hooks"
import { useToast } from "@/components/ui/use-toast"
import { Button } from "@/components/ui/button"
import { performRefresh } from "@/core/redux/slices/clientsSlice"
Expand All @@ -11,99 +11,51 @@ import DialogWrapper from "./DialogWrapper"
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
import { Skeleton } from "@/components/ui/skeleton"
import { Edit, Copy, Info, Plus } from "lucide-react"
import { users } from "@/core/apis/users"
import type { Client } from "@/app/types/clients"
import { settings } from "@/core/apis/settings"
import { useUserClients } from "@/core/hooks/useUserClients"


const UserClientsTable = () => {
const dispatch = useAppDispatch()
const { toast } = useToast()
const [showInfoModal, setShowInfoModal] = useState(false)
const [isLoading, setIsLoading] = useState(false)
const [isLoadingToken, setIsLoadingToken] = useState(false)
const [isLoadingActivationRequest, setIsLoadingActivationRequest] = useState(false)
const [openEditForm, setOpenEditForm] = useState(false)
const [openCreateForm, setOpenCreateForm] = useState(false)
const [selectedClient, setSelectedClient] = useState({} as Client)
const userInfo = useAppSelector((state) => state.user.userDetails)
const [clients, setClients] = useState<Client[]>([])
const [clientsDetails, setClientsDetails] = useState<Client[]>([])
const [currentPage, setCurrentPage] = useState(1)
const itemsPerPage = 4
const { clients, isLoading, error } = useUserClients();
console.log(clients)

const fetchClients = async () => {
setIsLoading(true)
try {
const res = await users.getUserDetails(userInfo?._id || "")
if (res) {
dispatch({ type: "ADD_CLIENTS", payload: res.users[0].clients })
setCurrentPage(1)
}
setClients(res.users[0].clients)
} catch (error) {
toast({
title: "Error",
description: "Failed to fetch user details",
variant: "destructive",
})
} finally {
setIsLoading(false)
}
}

const fetchClientDetails = async () => {
setIsLoading(true)
try {
const response = await settings.getUserClientsApi(userInfo?._id || "")
if (response) {
dispatch({ type: "ADD_CLIENTS_DETAILS", payload: response })
}
setClientsDetails(response.clients)
} catch (error) {
console.error(error)
toast({
title: "Error",
description: "Failed to fetch client details",
variant: "destructive",
})
} finally {
setIsLoading(false)
}
}

useEffect(() => {
fetchClients()
fetchClientDetails()
}, [userInfo?._id, dispatch])

const hasAccessToken = (clientId: string): boolean => {
const client =
Array.isArray(clientsDetails) && clientsDetails
? clientsDetails?.find((client: Client) => client._id === clientId)
Array.isArray(clients) && clients
? clients?.find((client: Client) => client._id === clientId)
: undefined
return client?.access_token?.token ? true : false
}

const getClientToken = (clientID: string) => {
const client =
Array.isArray(clientsDetails) && clientsDetails
? clientsDetails?.find((client: Client) => client._id === clientID)
Array.isArray(clients) && clients
? clients?.find((client: Client) => client._id === clientID)
: undefined
return client && client.access_token && client.access_token.token
}
const getClientTokenExpiryDate = (clientID: string) => {
const client =
Array.isArray(clientsDetails) && clientsDetails
? clientsDetails?.find((client: Client) => client._id === clientID)
Array.isArray(clients) && clients
? clients?.find((client: Client) => client._id === clientID)
: undefined
return client && client.access_token && client.access_token.expires
}

const getClientTokenCreateAt = (clientID: string) => {
const client =
Array.isArray(clientsDetails) && clientsDetails
? clientsDetails?.find((client: Client) => client._id === clientID)
Array.isArray(clients) && clients
? clients?.find((client: Client) => client._id === clientID)
: undefined
return client && client.access_token && client.access_token.createdAt
}
Expand All @@ -117,6 +69,7 @@ const UserClientsTable = () => {
} else {
try {
const response = await settings.generateTokenApi(res)
await queryClient.invalidateQueries({ queryKey: ["clients"] });
if (response) {
toast({
title: "Success",
Expand Down Expand Up @@ -168,10 +121,6 @@ const UserClientsTable = () => {
return Array.isArray(client.ip_addresses) ? client.ip_addresses.join(", ") : client.ip_addresses
}

const handleClientCreated = () => {
fetchClients()
fetchClientDetails()
}

return (
<div className="overflow-x-auto">
Expand Down Expand Up @@ -202,7 +151,6 @@ const UserClientsTable = () => {
</TableRow>
) : clients?.length > 0 ? (
clients
.slice((currentPage - 1) * itemsPerPage, currentPage * itemsPerPage)
.map((client: Client, index: number) => (
<TableRow key={index}>
<TableCell className="font-medium">{client?.name}</TableCell>
Expand Down Expand Up @@ -292,12 +240,10 @@ const UserClientsTable = () => {
open={openEditForm}
onClose={() => setOpenEditForm(false)}
data={selectedClient}
onClientUpdated={handleClientCreated}
/>
<CreateClientForm
open={openCreateForm}
onClose={() => setOpenCreateForm(false)}
onClientCreated={handleClientCreated}
/>
<DialogWrapper
open={showInfoModal}
Expand Down
41 changes: 11 additions & 30 deletions netmanager-app/components/Settings/Clients.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ import {
ActivateClientDialog,
DeactivateClientDialog,
} from "@/components/clients/dialogs";
import { getClientsApi } from "@/core/apis/analytics";
import { settings } from "@/core/apis/settings";
import { useToast } from "@/components/ui/use-toast";
import type { Client } from "@/app/types/clients";
import { Search, ArrowUpDown, Loader2 } from "lucide-react";
import { useQueryClient } from "@tanstack/react-query";
import {
DropdownMenu,
DropdownMenuContent,
Expand All @@ -36,6 +36,8 @@ import {
PaginationPrevious,
} from "@/components/ui/pagination";

import { useClients } from "@/core/hooks/useClients";

const ITEMS_PER_PAGE = 8;

const formatDate = (dateString: string | undefined): string => {
Expand All @@ -54,8 +56,6 @@ const formatDate = (dateString: string | undefined): string => {
};

const ClientManagement = () => {
const [clients, setClients] = useState<Client[]>([]);
const [loading, setLoading] = useState(false);
const [selectedClient, setSelectedClient] = useState<Client | null>(null);
const [activateDialogOpen, setActivateDialogOpen] = useState(false);
const [deactivateDialogOpen, setDeactivateDialogOpen] = useState(false);
Expand All @@ -64,26 +64,8 @@ const ClientManagement = () => {
const [sortField, setSortField] = useState<keyof Client>("name");
const [sortOrder, setSortOrder] = useState<"asc" | "desc">("asc");
const { toast } = useToast();

const fetchClients = async () => {
setLoading(true);
try {
const response = await getClientsApi();
setClients(response.clients);
} catch (error) {
toast({
title: "Error",
description: "Failed to fetch clients",
variant: "destructive",
});
} finally {
setLoading(false);
}
};

useEffect(() => {
fetchClients();
}, []);
const { clients, isLoading, error } = useClients();
const queryClient = useQueryClient();

const handleActivateDeactivate = async (
clientId: string,
Expand All @@ -95,7 +77,7 @@ const ClientManagement = () => {
};
try {
await settings.activateUserClientApi(data);
await fetchClients();
await queryClient.invalidateQueries({ queryKey: ["clients"] });
toast({
title: "Success",
description: `Client ${
Expand Down Expand Up @@ -135,7 +117,7 @@ const ClientManagement = () => {
};

const filteredClients = clients.filter(
(client) =>
(client: Client) =>
client.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
(client.user?.email?.toLowerCase().includes(searchQuery.toLowerCase()) ??
false)
Expand All @@ -153,9 +135,9 @@ const ClientManagement = () => {
currentPage * ITEMS_PER_PAGE
);

const activatedClients = clients.filter((client) => client.isActive).length;
const activatedClients = clients.filter((client: Client) => client.isActive).length;
const deactivatedClients = clients.filter(
(client) => !client.isActive
(client: Client) => !client.isActive
).length;

const getPageNumbers = () => {
Expand Down Expand Up @@ -194,15 +176,14 @@ const ClientManagement = () => {

return (
<div className=" py-2">
{loading ? (
{isLoading ? (
<div className="flex justify-center items-center h-64">
<Loader2 className="h-8 w-8 animate-spin" />
</div>
) : (
<>
<div className="flex justify-between items-center mb-6">
<h1 className="text-2xl font-bold">Client Management</h1>
<Button onClick={fetchClients}>Refresh</Button>
</div>

<div className="grid grid-cols-2 gap-4 mb-6">
Expand Down Expand Up @@ -379,4 +360,4 @@ const ClientManagement = () => {
);
};

export default ClientManagement;
export default ClientManagement;
4 changes: 1 addition & 3 deletions netmanager-app/components/Settings/CreateClientForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,9 @@ import { useToast } from "@/components/ui/use-toast"
interface CreateClientFormProps {
open: boolean
onClose: () => void
onClientCreated: () => void
}

const CreateClientForm: React.FC<CreateClientFormProps> = ({ open, onClose, onClientCreated }) => {
const CreateClientForm: React.FC<CreateClientFormProps> = ({ open, onClose }) => {
const { toast } = useToast()
const userInfo = useAppSelector((state) => state.user.userDetails)
const [clientName, setClientName] = useState('')
Expand Down Expand Up @@ -59,7 +58,6 @@ const CreateClientForm: React.FC<CreateClientFormProps> = ({ open, onClose, onCl
title: "Success",
description: "Client created successfully",
})
onClientCreated()
onClose()
}
} catch (error: any) {
Expand Down
3 changes: 1 addition & 2 deletions netmanager-app/components/Settings/MyProfile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,7 @@ export default function MyProfile() {
setError(null);

try {
const response = await users.getUserDetails(currentUser._id);
const userData = response?.users?.[0];
const userData = currentUser;

if (userData) {
setProfile({
Expand Down
13 changes: 4 additions & 9 deletions netmanager-app/core/apis/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,11 @@ interface PasswordData {
const axiosInstance = createAxiosInstance();

export const settings = {
getUserClientsApi: async (userID: string): Promise<Client[]> => {
try {
const response = await axiosInstance.get<Client[]>(`${USERS_MGT_URL}/clients`, {
getUserClientsApi: async (userID: string) => {
return await axiosInstance.get(`${USERS_MGT_URL}/clients`, {
params: { user_id: userID },
});
return response.data;
} catch (error) {
console.error("Error fetching clients:", error);
throw error;
}
})
.then((response) => response.data);
},

createClientApi: async (data: CreateClientData): Promise<Client> => {
Expand Down
11 changes: 4 additions & 7 deletions netmanager-app/core/hooks/useUserClients.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,19 @@ export const useUserClients = () => {


const { data, isLoading, error } = useQuery({
queryKey: ["clients", userDetails?._id,],
queryKey: ["clients", userDetails?._id],
queryFn: () =>
settings.getUserClientsApi(
userDetails?._id || ""
),
settings.getUserClientsApi(userDetails?._id || ""),
onSuccess: (data: any) => {
dispatch(setUserClients(data.clients));

dispatch(setUserClients(data));
},
onError: (error: Error) => {
dispatch(setError(error.message));
},
});

return {
clients: data || [],
clients: data?.clients || [],
isLoading,
error: error as Error | null,
};
Expand Down
2 changes: 2 additions & 0 deletions netmanager-app/core/hooks/users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ export const useAuth = () => {
privilege: decoded.privilege,
country: decoded.country,
profilePicture: decoded.profilePicture,
description: decoded.description,
timezone: decoded.timezone,
phoneNumber: decoded.phoneNumber,
createdAt: decoded.createdAt,
updatedAt: decoded.updatedAt,
Expand Down
2 changes: 1 addition & 1 deletion netmanager-app/core/redux/slices/clientsSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const initialState: ClientsState = {
clients: [],
isLoading: false,
clientsDetails: [],
accees_token: [],
access_token: [],
refresh: false,
error: null,
};
Expand Down