-
Notifications
You must be signed in to change notification settings - Fork 31
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
[Netmanager]: Profile Settings Page #2401
Changes from 9 commits
7314367
efeceff
99c47c8
5bfa9b7
dabe3f0
5992733
36741ae
4dac54e
3c4ea58
cc432de
91afbf1
6d5b0d6
d97fc00
5a82718
5ff6a08
6375ae9
cf77a7f
4ce1cc3
6cb5db2
5307a67
c0f547d
71ff5a8
845c0fa
48d88c9
83a4cfb
d635349
041bafd
84907a3
bb70faf
9887f72
6680208
882aa7b
0318786
4aff39c
faef2c6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,282 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import type React from "react" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { useEffect, useState } from "react" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import moment from "moment" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { useAppDispatch, useAppSelector } from "@/core/redux/hooks" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { Toast } from "@/components/ui/toast" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { Button } from "@/components/ui/button" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { performRefresh } from "@/lib/store/services/apiClient" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import EditClientForm from "./EditClientForm" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
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 } from "lucide-react" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { api } from "../utils/api"tore" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const UserClientsTable: React.FC = () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const dispatch = useAppDispatch() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const [isError, setIsError] = useState({ isError: false, message: "", type: "" }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const [isActivationRequestError, setIsActivationRequestError] = useState({ isError: false, message: "", type: "" }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
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 [selectedClient, setSelectedClient] = useState(null) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const userInfo = useAppSelector((state) => state.user.userDetails) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const clients = useAppSelector((state) => state.sites.clients) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const clientsDetails = useAppSelector((state) => state.sites.clientsDetails) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const [currentPage, setCurrentPage] = useState(1) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const itemsPerPage = 4 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const onPageChange = (newPage: number) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
setCurrentPage(newPage) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const setErrorState = (message: string, type: string) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
setIsError({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
isError: true, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
message, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
type, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
useEffect(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const fetchData = async () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
setIsLoading(true) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const res = await api.getUserDetailsAccount(userInfo?._id) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (res.success === true) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
dispatch({ type: "ADD_CLIENTS", payload: res.users[0].clients }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
setCurrentPage(1) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} catch (error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
console.error(error) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} finally { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
setIsLoading(false) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
fetchData() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}, [userInfo?._id, dispatch]) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
useEffect(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const fetchData = async () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
setIsLoading(true) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const response = await api.getClients(userInfo?._id) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (response.success === true) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
dispatch({ type: "ADD_CLIENTS_DETAILS", payload: response.clients }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} catch (error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
console.error(error) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} finally { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
setIsLoading(false) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
fetchData() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}, [userInfo?._id, dispatch]) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const hasAccessToken = (clientId: string) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const client = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Array.isArray(clientsDetails) && (clientsDetails) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
? clientsDetails?.find((client: any) => client._id === clientId) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
: undefined | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return client && client.access_token | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const getClientToken = (clientID: string) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const client = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Array.isArray(clientsDetails) && (clientsDetails) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
? clientsDetails?.find((client: any) => 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: any) => client._id === clientID) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
: undefined | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return client && client.access_token && client.access_token.expires | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const handleGenerateToken = async (res: any) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
setIsLoadingToken(true) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (!res?.isActive) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
setShowInfoModal(true) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
setIsLoadingToken(false) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const response = await api.generateToken(res) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (response.success === true) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
setErrorState("Token generated", "success") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
dispatch(performRefresh()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} catch (error: any) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
setErrorState(error.message, "error") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} finally { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
setIsLoadingToken(false) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Improve error handling in token generation. The handleGenerateToken function could benefit from more specific error handling and user feedback. const handleGenerateToken = async (res: any) => {
setIsLoadingToken(true)
if (!res?.isActive) {
setShowInfoModal(true)
setIsLoadingToken(false)
} else {
try {
const response = await api.generateToken(res)
if (response.success === true) {
setErrorState("Token generated", "success")
+ await dispatch(performRefresh())
}
- dispatch(performRefresh())
} catch (error: any) {
- setErrorState(error.message, "error")
+ const errorMessage = error.response?.data?.message || error.message || "Failed to generate token"
+ setErrorState(errorMessage, "error")
+ console.error("Token generation failed:", error)
} finally {
setIsLoadingToken(false)
}
}
} 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const handleActivationRequest = async () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const setActivationRequestErrorState = (message: string, type: string) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
setIsActivationRequestError({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
isError: true, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
message, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
type, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
setIsLoadingActivationRequest(true) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const clientID = selectedClient?._id | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const response = await api.activationRequest(clientID) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (response.success === true) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
setShowInfoModal(false) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
setTimeout(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
setActivationRequestErrorState("Activation request sent successfully", "success") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}, 3000) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} catch (error: any) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
setShowInfoModal(false) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
setTimeout(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
setActivationRequestErrorState(error.message, "error") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}, 3000) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} finally { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
setIsLoadingActivationRequest(false) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const displayIPAddresses = (client: any) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return Array.isArray(client.ip_addresses) ? client.ip_addresses.join(", ") : client.ip_addresses | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<div className="overflow-x-auto"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{isError.isError && <Toast type={isError.type} message={isError.message} />} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<Table> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<TableHeader> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<TableRow> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<TableHead className="w-[200px]">Client name</TableHead> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<TableHead className="w-[138px]">IP Address</TableHead> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<TableHead className="w-[142px]">Client Status</TableHead> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<TableHead className="w-[138px]">Created</TableHead> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<TableHead className="w-[138px]">Token</TableHead> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<TableHead className="w-[138px]">Expires</TableHead> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<TableHead className="w-24"></TableHead> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</TableRow> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</TableHeader> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<TableBody> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{isLoading ? ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<TableRow> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<TableCell colSpan={7}> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<Skeleton className="w-full h-12" /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</TableCell> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</TableRow> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
) : clients?.length > 0 ? ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
clients | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
.slice((currentPage - 1) * itemsPerPage, currentPage * itemsPerPage) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
.map((client: any, index: number) => ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<TableRow key={index}> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<TableCell className="font-medium">{client?.name}</TableCell> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<TableCell>{displayIPAddresses(client)}</TableCell> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<TableCell> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<span | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
className={`px-2 py-1 rounded-full text-xs ${ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
client?.isActive ? "bg-green-100 text-green-800" : "bg-gray-100 text-gray-800" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}`} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{client?.isActive ? "Activated" : "Not Activated"} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</span> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</TableCell> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<TableCell>{moment(client?.createdAt).format("MMM DD, YYYY")}</TableCell> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<TableCell> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{getClientToken(client._id) ? ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<div className="flex items-center"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<span className="mr-2"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{getClientToken(client._id).slice(0, 2)}.... | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{getClientToken(client._id).slice(-2)} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</span> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<Button | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
variant="ghost" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
size="sm" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
onClick={() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
navigator.clipboard.writeText(getClientToken(client._id)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
setErrorState("Token copied to clipboard!", "success") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<Copy className="h-4 w-4" /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</Button> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
) : ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Enhance token display security and UX. The current token display implementation could be improved for better security and user experience. <TableCell>
{getClientToken(client._id) ? (
<div className="flex items-center">
- <span className="mr-2">
- {getClientToken(client._id).slice(0, 2)}....
- {getClientToken(client._id).slice(-2)}
- </span>
+ <span className="mr-2 font-mono">
+ {getClientToken(client._id).slice(0, 4)}
+ <span className="mx-1">•••••••</span>
+ {getClientToken(client._id).slice(-4)}
+ </span>
<Button
variant="ghost"
size="sm"
onClick={() => {
navigator.clipboard.writeText(getClientToken(client._id))
- setErrorState("Token copied to clipboard!", "success")
+ setErrorState("API token copied to clipboard", "success")
}}
+ title="Copy full token"
>
<Copy className="h-4 w-4" />
</Button>
</div> 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<Button | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
variant={!hasAccessToken(client._id) ? "default" : "secondary"} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
size="sm" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
disabled={isLoadingToken} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
onClick={() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const res = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
name: client.name, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
client_id: client._id, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
isActive: client.isActive ? client.isActive : false, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
setSelectedClient(client) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
handleGenerateToken(res) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Generate | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</Button> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
)} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</TableCell> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<TableCell> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{getClientTokenExpiryDate(client._id) && | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
moment(getClientTokenExpiryDate(client._id)).format("MMM DD, YYYY")} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</TableCell> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<TableCell> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<Button | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
variant="ghost" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
size="sm" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
onClick={() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
setOpenEditForm(true) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
setSelectedClient(client) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<Edit className="h-4 w-4" /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</Button> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</TableCell> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</TableRow> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
) : ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<TableRow> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<TableCell colSpan={7} className="text-center"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
No data found | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</TableCell> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</TableRow> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
)} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</TableBody> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</Table> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{/* Add your Pagination component here */} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Implement pagination component. The comment indicates a missing pagination component. This is essential for proper table navigation. - {/* Add your Pagination component here */}
+ {clients?.length > itemsPerPage && (
+ <div className="flex justify-center mt-4">
+ <div className="flex space-x-2">
+ {Array.from({ length: Math.ceil(clients.length / itemsPerPage) }).map((_, index) => (
+ <Button
+ key={index}
+ variant={currentPage === index + 1 ? "default" : "outline"}
+ size="sm"
+ onClick={() => onPageChange(index + 1)}
+ >
+ {index + 1}
+ </Button>
+ ))}
+ </div>
+ </div>
+ )}
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hi @danielmarv , I hope you have been able to review this suggestion? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{isActivationRequestError.isError && ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<Toast type={isActivationRequestError.type} message={isActivationRequestError.message} /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
)} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<EditClientForm open={openEditForm} closeModal={() => setOpenEditForm(false)} data={selectedClient} /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<DialogWrapper | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
open={showInfoModal} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
onClose={() => setShowInfoModal(false)} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
handleClick={handleActivationRequest} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
primaryButtonText={"Send activation request"} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
loading={isLoadingActivationRequest} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<div className="flex items-center"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<Info className="h-5 w-5 mr-2 text-blue-500" /> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<p className="text-sm text-gray-600"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
You cannot generate a token for an inactive client. Reach out to support for assistance at [email protected] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
or send an activation request. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</p> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</DialogWrapper> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
export default UserClientsTable | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,93 @@ | ||||||||||||||||||||||||
import type { FC } from "react" | ||||||||||||||||||||||||
import moment from "moment" | ||||||||||||||||||||||||
import {Button} from "@/components/ui/button" | ||||||||||||||||||||||||
import CopyIcon from "@/icons/Common/copy.svg" | ||||||||||||||||||||||||
import EditIcon from "@/icons/Common/edit-pencil.svg" | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
interface ClientTableRowProps { | ||||||||||||||||||||||||
client: any | ||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Replace 'any' type with a proper Client interface. Using 'any' type reduces type safety and IDE support. Consider using the Client interface that's already defined in your types. - client: any
+ client: Client
|
||||||||||||||||||||||||
onGenerateToken: (client: any) => void | ||||||||||||||||||||||||
onEditClient: (client: any) => void | ||||||||||||||||||||||||
onCopyToken: (token: string) => void | ||||||||||||||||||||||||
getClientToken: (clientId: string) => string | null | ||||||||||||||||||||||||
getClientTokenExpiryDate: (clientId: string) => string | null | ||||||||||||||||||||||||
isLoadingToken: boolean | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
export const ClientTableRow: FC<ClientTableRowProps> = ({ | ||||||||||||||||||||||||
client, | ||||||||||||||||||||||||
onGenerateToken, | ||||||||||||||||||||||||
onEditClient, | ||||||||||||||||||||||||
onCopyToken, | ||||||||||||||||||||||||
getClientToken, | ||||||||||||||||||||||||
getClientTokenExpiryDate, | ||||||||||||||||||||||||
isLoadingToken, | ||||||||||||||||||||||||
}) => { | ||||||||||||||||||||||||
const displayIPAddresses = (client) => { | ||||||||||||||||||||||||
return Array.isArray(client.ip_addresses) ? client.ip_addresses.join(", ") : client.ip_addresses | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Add IP address format validation. The current implementation doesn't validate IP address formats. Consider adding validation to ensure proper IP address formatting. const displayIPAddresses = (client) => {
+ const isValidIP = (ip: string) => {
+ const ipv4Regex = /^(\d{1,3}\.){3}\d{1,3}$/;
+ return ipv4Regex.test(ip) && ip.split('.').every(num => parseInt(num) <= 255);
+ };
+ const addresses = Array.isArray(client.ip_addresses) ? client.ip_addresses : [client.ip_addresses];
+ return addresses.filter(ip => ip && isValidIP(ip)).join(", ") || "Invalid IP";
- return Array.isArray(client.ip_addresses) ? client.ip_addresses.join(", ") : client.ip_addresses
} 📝 Committable suggestion
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @danielmarv ,please consider this suggestion There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||||||||||||||||||||
|
||||||||||||||||||||||||
return ( | ||||||||||||||||||||||||
<tr className="border-b border-b-secondary-neutral-light-100"> | ||||||||||||||||||||||||
<td className="w-[200px] px-4 py-3 font-medium text-sm leading-5 text-secondary-neutral-light-800 uppercase"> | ||||||||||||||||||||||||
{client?.name} | ||||||||||||||||||||||||
</td> | ||||||||||||||||||||||||
<td className="w-[138px] px-4 py-3 font-medium text-sm leading-5 text-secondary-neutral-light-400"> | ||||||||||||||||||||||||
{displayIPAddresses(client)} | ||||||||||||||||||||||||
</td> | ||||||||||||||||||||||||
<td className="w-[142px] px-4 py-3"> | ||||||||||||||||||||||||
<div | ||||||||||||||||||||||||
className={`px-2 py-[2px] rounded-2xl w-auto inline-flex justify-center text-sm leading-5 items-center mx-auto ${ | ||||||||||||||||||||||||
client?.isActive | ||||||||||||||||||||||||
? "bg-success-50 text-success-700" | ||||||||||||||||||||||||
: "bg-secondary-neutral-light-50 text-secondary-neutral-light-500" | ||||||||||||||||||||||||
}`} | ||||||||||||||||||||||||
> | ||||||||||||||||||||||||
{client?.isActive ? "Activated" : "Not Activated"} | ||||||||||||||||||||||||
</div> | ||||||||||||||||||||||||
</td> | ||||||||||||||||||||||||
<td className="w-[138px] px-4 py-3 font-medium text-sm leading-5 text-secondary-neutral-light-400"> | ||||||||||||||||||||||||
{moment(client?.createdAt).format("MMM DD, YYYY")} | ||||||||||||||||||||||||
</td> | ||||||||||||||||||||||||
<td className="w-[138px] px-4 py-3"> | ||||||||||||||||||||||||
{getClientToken(client._id) ? ( | ||||||||||||||||||||||||
<span className="font-medium text-sm leading-5 text-secondary-neutral-light-400 flex items-center gap-2"> | ||||||||||||||||||||||||
{getClientToken(client._id).slice(0, 2)}.... | ||||||||||||||||||||||||
{getClientToken(client._id).slice(-2)} | ||||||||||||||||||||||||
<div | ||||||||||||||||||||||||
className="w-6 h-6 bg-white rounded border border-gray-200 flex justify-center items-center gap-2 cursor-pointer" | ||||||||||||||||||||||||
onClick={() => onCopyToken(getClientToken(client._id))} | ||||||||||||||||||||||||
> | ||||||||||||||||||||||||
<CopyIcon /> | ||||||||||||||||||||||||
</div> | ||||||||||||||||||||||||
</span> | ||||||||||||||||||||||||
) : ( | ||||||||||||||||||||||||
<Button | ||||||||||||||||||||||||
title={!client?.isActive ? "Tap to generate token" : "Token already generated"} | ||||||||||||||||||||||||
className={`px-4 py-2 rounded-2xl w-auto inline-flex justify-center text-sm leading-5 items-center mx-auto ${ | ||||||||||||||||||||||||
!getClientToken(client._id) | ||||||||||||||||||||||||
? "bg-success-700 text-success-50 cursor-pointer" | ||||||||||||||||||||||||
: "bg-secondary-neutral-light-50 text-secondary-neutral-light-500" | ||||||||||||||||||||||||
}`} | ||||||||||||||||||||||||
disabled={isLoadingToken} | ||||||||||||||||||||||||
onClick={() => onGenerateToken(client)} | ||||||||||||||||||||||||
> | ||||||||||||||||||||||||
Generate | ||||||||||||||||||||||||
</Button> | ||||||||||||||||||||||||
)} | ||||||||||||||||||||||||
</td> | ||||||||||||||||||||||||
<td className="w-[138px] px-4 py-3 font-medium text-sm leading-5 text-secondary-neutral-light-400"> | ||||||||||||||||||||||||
{getClientTokenExpiryDate(client._id) && moment(getClientTokenExpiryDate(client._id)).format("MMM DD, YYYY")} | ||||||||||||||||||||||||
</td> | ||||||||||||||||||||||||
<td className="w-24 px-4 py-3 font-medium text-sm leading-5 text-secondary-neutral-light-400 capitalize"> | ||||||||||||||||||||||||
<div | ||||||||||||||||||||||||
className="w-9 h-9 p-2.5 bg-white rounded border border-gray-200 justify-center items-center gap-2 cursor-pointer" | ||||||||||||||||||||||||
onClick={() => onEditClient(client)} | ||||||||||||||||||||||||
> | ||||||||||||||||||||||||
<EditIcon className="w-4 h-4" /> | ||||||||||||||||||||||||
</div> | ||||||||||||||||||||||||
</td> | ||||||||||||||||||||||||
</tr> | ||||||||||||||||||||||||
) | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix syntax error in import statement.
There's a typo in the import statement that would prevent compilation.
🧰 Tools
🪛 Biome (1.9.4)
[error] 13-13: Expected a semicolon or an implicit semicolon after a statement, but found none
An explicit or implicit semicolon is expected here...
...Which is required to end this statement
(parse)
[error] 13-13: unterminated string literal
The closing quote must be on the same line.
(parse)