mirror of
https://github.com/netbirdio/dashboard.git
synced 2026-01-26 01:21:04 +00:00
Feature/peers table layout on mobile (#40)
Co-authored-by: Raphael Oliveira <raphael.oliveirarm@gmail.com>
This commit is contained in:
@@ -258,7 +258,7 @@ export const Peers = () => {
|
||||
return (
|
||||
<div className="py-10 bg-gray-50 overflow-hidden rounded max-w-7xl mx-auto sm:px-6 lg:px-8">
|
||||
<header className="sm:flex sm:items-center">
|
||||
<div className="max-w-7xl mx-auto sm:px-6 lg:px-8 sm:flex-auto">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 sm:flex-auto">
|
||||
<h1 className="text-xl font-semibold text-gray-900">Peers</h1>
|
||||
<p className="mt-2 text-sm text-gray-700">
|
||||
A list of all the machines in your account including their name, IP
|
||||
@@ -277,60 +277,67 @@ export const Peers = () => {
|
||||
|
||||
{!empty ? (
|
||||
<div className="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
||||
<div className="flex w-full items-center mt-8 justify-between">
|
||||
<div className="flex">
|
||||
<input
|
||||
className="text-sm rounded p-2 border border-gray-300 focus:border-gray-400 outline-none w-[300px]"
|
||||
placeholder="Search..."
|
||||
type="search"
|
||||
onChange={(e) => handleSearch(e.target.value)}
|
||||
/>
|
||||
<div className="flex items-center mx-auto sm:px-6 lg:px-8">
|
||||
<p className="ml-6 text-sm text-gray-700 px-4">Sort by: </p>
|
||||
<select
|
||||
className="bg-gray-50 text-sm text-gray-500 rounded p-2 border border-gray-300 focus:border-gray-400 outline-none"
|
||||
onChange={(e) => sortTable(e.target.value)}
|
||||
>
|
||||
<option className="text-sm text-gray-500" value={0}>Name: Asc</option>
|
||||
<option className="text-sm text-gray-500" value={1}>Name: Desc</option>
|
||||
<option className="text-sm text-gray-500" value={2}>Last Seen: Asc</option>
|
||||
<option className="text-sm text-gray-500" value={3}>Last Seen: Desc</option>
|
||||
</select>
|
||||
<div className="auto py-8">
|
||||
<div className="grid grid-cols-1 md:grid-cols-1 lg:grid-cols-6 gap-5">
|
||||
<div className="lg:col-span-2">
|
||||
<input
|
||||
className="text-sm w-full rounded p-2 border border-gray-300 focus:border-gray-400 outline-none"
|
||||
placeholder="Search..."
|
||||
type="search"
|
||||
onChange={(e) => handleSearch(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
<span className="relative z-0 inline-flex shadow-sm rounded-md">
|
||||
<button
|
||||
id="btn-show-all"
|
||||
onClick={() => showAll()}
|
||||
type="button"
|
||||
className="relative inline-flex items-center px-4 py-2 rounded-l-md border border-gray-300 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50 z-10 outline-none ring-1 ring-indigo-500 border-indigo-500"
|
||||
>
|
||||
All
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
id="btn-show-online"
|
||||
onClick={() => showConnected()}
|
||||
className="relative inline-flex items-center px-4 py-2 rounded-r-md border border-gray-300 bg-white text-sm font-medium text-gray-700 outline-none hover:bg-gray-50"
|
||||
>
|
||||
Online
|
||||
</button>
|
||||
</span>
|
||||
|
||||
<div className="max-w-7xl mx-auto sm:px-6 lg:px-8 sm:flex-auto mt-2 sm:mt-0 sm:ml-16 sm:flex-none">
|
||||
<div className="lg:col-span-2">
|
||||
<div className="flex items-center">
|
||||
<p className="ml-0 text-sm text-gray-700 lg:px-4 md:pr-2 pr-2" sm>Sort by: </p>
|
||||
<select
|
||||
className="bg-gray-50 flex-1 lg:flex-grow-0 text-sm text-gray-500 rounded p-2 border border-gray-300 focus:border-gray-400 outline-none"
|
||||
onChange={(e) => sortTable(e.target.value)}
|
||||
>
|
||||
<option className="text-sm text-gray-500" value={0}>Name: Asc</option>
|
||||
<option className="text-sm text-gray-500" value={1}>Name: Desc</option>
|
||||
<option className="text-sm text-gray-500" value={2}>Last Seen: Asc</option>
|
||||
<option className="text-sm text-gray-500" value={3}>Last Seen: Desc</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex lg:justify-end justify-center">
|
||||
<div className="flex items-center">
|
||||
<span className="relative z-0 inline-flex shadow-sm rounded-md">
|
||||
<button
|
||||
id="btn-show-all"
|
||||
onClick={() => showAll()}
|
||||
type="button"
|
||||
className="relative inline-flex items-center px-4 py-2 rounded-l-md border border-gray-300 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50 z-10 outline-none ring-1 ring-indigo-500 border-indigo-500"
|
||||
>
|
||||
All
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
id="btn-show-online"
|
||||
onClick={() => showConnected()}
|
||||
className="relative inline-flex items-center px-4 py-2 rounded-r-md border border-gray-300 bg-white text-sm font-medium text-gray-700 outline-none hover:bg-gray-50"
|
||||
>
|
||||
Online
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="lg:flex lg:justify-end">
|
||||
<Link to="/add-peer">
|
||||
<button
|
||||
type="button"
|
||||
className="inline-flex items-center justify-center rounded-md border border-transparent bg-indigo-600 px-4 py-2 text-sm font-medium text-white shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 sm:w-auto"
|
||||
type="button"
|
||||
className="inline-flex w-full lg:w-auto justify-center items-center rounded-md border border-transparent bg-indigo-600 px-4 py-2 text-sm font-medium text-white shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
|
||||
>
|
||||
Add peer
|
||||
</button>
|
||||
</Link>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-4 py-8 sm:px-0">
|
||||
|
||||
<div className="px-4 py-2 sm:px-0">
|
||||
<DeleteModal
|
||||
show={showDeleteDialog}
|
||||
confirmCallback={handleDeleteConfirmation}
|
||||
@@ -340,177 +347,180 @@ export const Peers = () => {
|
||||
{/* table */}
|
||||
<div className="flex flex-col">
|
||||
<div className="-my-2 sm:-mx-6 lg:-mx-8">
|
||||
<div className="py-2 align-middle inline-block min-w-full sm:px-6 lg:px-8">
|
||||
<div className="overflow-hidden shadow ring-1 ring-black ring-opacity-5 md:rounded-lg">
|
||||
<table
|
||||
{...getTableProps()}
|
||||
className="min-w-full divide-y divide-gray-200"
|
||||
>
|
||||
<thead className="bg-gray-50">
|
||||
{headerGroups.map((headerGroup) => (
|
||||
<tr {...headerGroup.getHeaderGroupProps()}>
|
||||
{headerGroup.headers.map((column, i) => (
|
||||
<th
|
||||
{...column.getHeaderProps()}
|
||||
className={
|
||||
i === 0
|
||||
? "px-6 py-3.5 text-left text-sm font-semibold text-gray-900"
|
||||
: "px-3 py-3.5 text-left text-sm font-semibold text-gray-900"
|
||||
}
|
||||
>
|
||||
{column.render("Header")}
|
||||
</th>
|
||||
))}
|
||||
<th
|
||||
scope="col"
|
||||
className="relative px-6 py-3"
|
||||
>
|
||||
<span className="sr-only">Edit</span>
|
||||
</th>
|
||||
</tr>
|
||||
))}
|
||||
</thead>
|
||||
<tbody
|
||||
{...getTableBodyProps()}
|
||||
className="bg-white divide-y divide-gray-200"
|
||||
<div className="py-2 align-middle min-w-full sm:px-6 lg:px-8">
|
||||
<div className="shadow ring-1 ring-black ring-opacity-5 md:rounded-lg">
|
||||
<div className="overflow-x-auto">
|
||||
|
||||
<table
|
||||
{...getTableProps()}
|
||||
className="min-w-full divide-y divide-gray-200"
|
||||
>
|
||||
{page.map((row) => {
|
||||
prepareRow(row);
|
||||
return (
|
||||
<tr {...row.getRowProps()}>
|
||||
{row.cells.map((cell) => {
|
||||
return (
|
||||
<td
|
||||
{...cell.getCellProps()}
|
||||
className={
|
||||
cell.column.id === "Name"
|
||||
? td_class_name
|
||||
: td_class_other
|
||||
}
|
||||
>
|
||||
{cell.column.id === "IP" && (
|
||||
<CopyText
|
||||
text={cell.value.toUpperCase()}
|
||||
idPrefix={
|
||||
"peers-ip-" + cell.value
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{cell.column.id === "Connected" &&
|
||||
(cell.value ? (
|
||||
<span className="inline-flex rounded-full bg-green-100 px-2 text-xs leading-5 text-green-800">
|
||||
online
|
||||
</span>
|
||||
) : (
|
||||
<span className="inline-flex rounded-full bg-red-100 px-2 text-xs leading-5 text-red-800">
|
||||
offline
|
||||
</span>
|
||||
))}
|
||||
{cell.column.id === "LastSeen" &&
|
||||
(cell.row.original.Connected
|
||||
? "just now"
|
||||
: timeAgo(cell.value))}
|
||||
{cell.column.id === "OS" &&
|
||||
formatOS(cell.value)}
|
||||
{(cell.column.id === "Name" ||
|
||||
cell.column.id === "Version") &&
|
||||
cell.value}
|
||||
</td>
|
||||
);
|
||||
})}
|
||||
<td className={td_class_other}>
|
||||
<EditButton
|
||||
items={[{ name: "Delete" }]}
|
||||
handler={(action) =>
|
||||
handleRowMenuClick(
|
||||
action,
|
||||
row.cells
|
||||
)
|
||||
<thead className="bg-gray-50">
|
||||
{headerGroups.map((headerGroup) => (
|
||||
<tr {...headerGroup.getHeaderGroupProps()}>
|
||||
{headerGroup.headers.map((column, i) => (
|
||||
<th
|
||||
{...column.getHeaderProps()}
|
||||
className={
|
||||
i === 0
|
||||
? "px-6 py-3.5 text-left text-sm font-semibold text-gray-900"
|
||||
: "px-3 py-3.5 text-left text-sm font-semibold text-gray-900"
|
||||
}
|
||||
/>
|
||||
</td>
|
||||
>
|
||||
{column.render("Header")}
|
||||
</th>
|
||||
))}
|
||||
<th
|
||||
scope="col"
|
||||
className="relative px-6 py-3"
|
||||
>
|
||||
<span className="sr-only">Edit</span>
|
||||
</th>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
))}
|
||||
</thead>
|
||||
<tbody
|
||||
{...getTableBodyProps()}
|
||||
className="bg-white divide-y divide-gray-200"
|
||||
>
|
||||
{page.map((row) => {
|
||||
prepareRow(row);
|
||||
return (
|
||||
<tr {...row.getRowProps()}>
|
||||
{row.cells.map((cell) => {
|
||||
return (
|
||||
<td
|
||||
{...cell.getCellProps()}
|
||||
className={
|
||||
cell.column.id === "Name"
|
||||
? td_class_name
|
||||
: td_class_other
|
||||
}
|
||||
>
|
||||
{cell.column.id === "IP" && (
|
||||
<CopyText
|
||||
text={cell.value.toUpperCase()}
|
||||
idPrefix={
|
||||
"peers-ip-" + cell.value
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{cell.column.id === "Connected" &&
|
||||
(cell.value ? (
|
||||
<span className="inline-flex rounded-full bg-green-100 px-2 text-xs leading-5 text-green-800">
|
||||
online
|
||||
</span>
|
||||
) : (
|
||||
<span className="inline-flex rounded-full bg-red-100 px-2 text-xs leading-5 text-red-800">
|
||||
offline
|
||||
</span>
|
||||
))}
|
||||
{cell.column.id === "LastSeen" &&
|
||||
(cell.row.original.Connected
|
||||
? "just now"
|
||||
: timeAgo(cell.value))}
|
||||
{cell.column.id === "OS" &&
|
||||
formatOS(cell.value)}
|
||||
{(cell.column.id === "Name" ||
|
||||
cell.column.id === "Version") &&
|
||||
cell.value}
|
||||
</td>
|
||||
);
|
||||
})}
|
||||
<td className={td_class_other}>
|
||||
<EditButton
|
||||
items={[{ name: "Delete" }]}
|
||||
handler={(action) =>
|
||||
handleRowMenuClick(
|
||||
action,
|
||||
row.cells
|
||||
)
|
||||
}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{/* pagenation */}
|
||||
<div className="bg-white px-4 py-3 flex items-center justify-between border-t border-gray-200 sm:px-6">
|
||||
<div className="hidden sm:flex-1 sm:flex sm:items-center sm:justify-between">
|
||||
<div className="bg-white px-4 py-3 flex items-center justify-center sm:justify-between border-t border-gray-200 sm:px-6">
|
||||
<div className="sm:flex-1 sm:flex sm:items-center sm:justify-between">
|
||||
<div>
|
||||
<p className=" text-gray-700">
|
||||
<p className="text-gray-700 text-center sm:text-left">
|
||||
Showing{" "}
|
||||
<span className="font-medium">
|
||||
{pageCount === 0
|
||||
? 0
|
||||
: pageIndex * pageSize + 1}
|
||||
</span>{" "}
|
||||
{pageCount === 0
|
||||
? 0
|
||||
: pageIndex * pageSize + 1}
|
||||
</span>{" "}
|
||||
to{" "}
|
||||
<span className="font-medium">
|
||||
{pageCount === 0
|
||||
? 0
|
||||
: pageIndex === pageCount - 1
|
||||
? data.length
|
||||
: pageIndex * pageSize + pageSize}
|
||||
</span>{" "}
|
||||
{pageCount === 0
|
||||
? 0
|
||||
: pageIndex === pageCount - 1
|
||||
? data.length
|
||||
: pageIndex * pageSize + pageSize}
|
||||
</span>{" "}
|
||||
of{" "}
|
||||
<span className="font-medium">
|
||||
{data.length}
|
||||
</span>{" "}
|
||||
{data.length}
|
||||
</span>{" "}
|
||||
{data.length === 1 ? "peer" : "peers"}
|
||||
</p>
|
||||
</div>
|
||||
{pageCount === 1 || pageCount === 0 ? (
|
||||
<div />
|
||||
<div />
|
||||
) : (
|
||||
<div>
|
||||
<nav
|
||||
className="relative z-0 inline-flex rounded-md shadow-sm -space-x-px"
|
||||
aria-label="Pagination"
|
||||
>
|
||||
<button
|
||||
className="relative inline-flex rounded-l-md items-center px-2 py-2 border border-gray-300 bg-white text-gray-500 hover:bg-gray-50"
|
||||
onClick={() => gotoPage(0)}
|
||||
disabled={!canPreviousPage}
|
||||
<div>
|
||||
<nav
|
||||
className="py-3 relative z-0 inline-flex rounded-md shadow-sm -space-x-px"
|
||||
aria-label="Pagination"
|
||||
>
|
||||
First
|
||||
</button>
|
||||
<button
|
||||
className="relative inline-flex items-center px-2 py-2 border border-gray-300 bg-white text-gray-500 hover:bg-gray-50"
|
||||
onClick={() => previousPage()}
|
||||
disabled={!canPreviousPage}
|
||||
>
|
||||
<span className="sr-only">
|
||||
Previous
|
||||
</span>
|
||||
<ChevronLeftIcon
|
||||
className="h-5 w-5"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</button>
|
||||
<div>
|
||||
<InnerPageNumbers />
|
||||
</div>
|
||||
<button
|
||||
className="relative inline-flex items-center px-2 py-2 border border-gray-300 bg-white text-gray-500 hover:bg-gray-50"
|
||||
onClick={() => nextPage()}
|
||||
disabled={!canNextPage}
|
||||
>
|
||||
<span className="sr-only">Next</span>
|
||||
<ChevronRightIcon
|
||||
className="h-5 w-5"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
className="relative inline-flex rounded-r-md items-center px-2 py-2 border border-gray-300 bg-white text-gray-500 hover:bg-gray-50"
|
||||
onClick={() => gotoPage(pageCount - 1)}
|
||||
disabled={!canNextPage}
|
||||
>
|
||||
Last
|
||||
</button>
|
||||
</nav>
|
||||
</div>
|
||||
<button
|
||||
className="relative inline-flex rounded-l-md items-center px-2 py-2 border border-gray-300 bg-white text-gray-500 hover:bg-gray-50"
|
||||
onClick={() => gotoPage(0)}
|
||||
disabled={!canPreviousPage}
|
||||
>
|
||||
First
|
||||
</button>
|
||||
<button
|
||||
className="relative inline-flex items-center px-2 py-2 border border-gray-300 bg-white text-gray-500 hover:bg-gray-50"
|
||||
onClick={() => previousPage()}
|
||||
disabled={!canPreviousPage}
|
||||
>
|
||||
<span className="sr-only">
|
||||
Previous
|
||||
</span>
|
||||
<ChevronLeftIcon
|
||||
className="h-5 w-5"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</button>
|
||||
<div>
|
||||
<InnerPageNumbers />
|
||||
</div>
|
||||
<button
|
||||
className="relative inline-flex items-center px-2 py-2 border border-gray-300 bg-white text-gray-500 hover:bg-gray-50"
|
||||
onClick={() => nextPage()}
|
||||
disabled={!canNextPage}
|
||||
>
|
||||
<span className="sr-only">Next</span>
|
||||
<ChevronRightIcon
|
||||
className="h-5 w-5"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</button>
|
||||
<button
|
||||
className="relative inline-flex rounded-r-md items-center px-2 py-2 border border-gray-300 bg-white text-gray-500 hover:bg-gray-50"
|
||||
onClick={() => gotoPage(pageCount - 1)}
|
||||
disabled={!canNextPage}
|
||||
>
|
||||
Last
|
||||
</button>
|
||||
</nav>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user