Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions tavern/internal/www/build/asset-manifest.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion tavern/internal/www/build/index.html

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 0 additions & 4 deletions tavern/internal/www/build/static/css/main.1f7c8452.css

This file was deleted.

1 change: 0 additions & 1 deletion tavern/internal/www/build/static/css/main.1f7c8452.css.map

This file was deleted.

4 changes: 4 additions & 0 deletions tavern/internal/www/build/static/css/main.cd0bc6bf.css

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions tavern/internal/www/build/static/css/main.cd0bc6bf.css.map

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions tavern/internal/www/build/static/js/main.b1a3afe5.js

Large diffs are not rendered by default.

Large diffs are not rendered by default.

3 changes: 0 additions & 3 deletions tavern/internal/www/build/static/js/main.f948a169.js

This file was deleted.

40 changes: 40 additions & 0 deletions tavern/internal/www/src/components/Breadcrumbs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { HomeIcon } from '@heroicons/react/20/solid'
import { Link } from 'react-router-dom'

export default function Breadcrumbs({ pages }: { pages: Array<{ label: string | undefined, link: string | undefined }> }) {
return (
<nav aria-label="Breadcrumb" className="flex">
<ol role="list" className="flex space-x-4 py-4">
<li className="flex">
<div className="flex items-center">
<Link to="/" className="text-gray-400 hover:text-gray-500">
<HomeIcon aria-hidden="true" className="h-5 w-5 flex-shrink-0" />
<span className="sr-only">Home</span>
</Link>
</div>
</li>
{pages.map((page, index) => (
<li key={page.label} className="flex">
<div className="flex items-center">
<svg
fill="currentColor"
viewBox="0 0 24 44"
preserveAspectRatio="none"
aria-hidden="true"
className="h-full w-2 flex-shrink-0 text-gray-400"
>
<path d="M.293 0l22 22-22 22h1.414l22-22-22-22H.293z" />
</svg>
<Link
to={page.link || ""}
aria-current={(pages.length - 1 === index) ? 'page' : undefined}
className="ml-4 text-sm font-medium text-gray-500 hover:text-gray-700"
>{page.label}
</Link>
</div>
</li>
))}
</ol>
</nav>
)
}
23 changes: 23 additions & 0 deletions tavern/internal/www/src/components/tavern-base-ui/PageHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { ReactNode } from "react"

type PageHeaderType = {
title: string,
description?: string
children?: ReactNode
}
const PageHeader = ({ title, description, children }: PageHeaderType) => {
return (
<div className="border-b border-gray-200 pb-5 flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
<div className="flex-1 flex flex-col gap-2">
<h3 className="text-xl font-semibold leading-6 text-gray-900">{title}</h3>
{description &&
<div className="max-w-2xl text-sm">
{description}
</div>
}
{children && <div className="max-w-2xl text-sm">{children}</div>}
</div>
</div>
)
}
export default PageHeader;
147 changes: 72 additions & 75 deletions tavern/internal/www/src/components/tavern-base-ui/Table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,82 +50,79 @@ export const Table = ({
};

return (
<div className="p-2">
<div className="h-2" />
<table className="w-full divide-y divide-gray-200 overflow-scroll table-fixed">
<thead className="bg-gray-50">
{table.getHeaderGroups().map(headerGroup => (
<tr key={headerGroup.id}>
{headerGroup.headers.map(header => {
return (
<th
key={header.id}
colSpan={header.colSpan}
scope="col"
className={`px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider ${header.column.getCanSort() && "cursor-pointer"}`}
onClick={header.column.getToggleSortingHandler()}
style={{
width: header.getSize() !== 0 ? header.getSize() : undefined,
}}
>
{header.isPlaceholder ? null : (
<div
{...{
className: header.column.getCanSort()
? 'cursor-pointer select-none'
: '',
onClick: header.column.getToggleSortingHandler(),
}}
>
{flexRender(
header.column.columnDef.header,
header.getContext()
)}
{{
asc: <TriangleUpIcon w={4} />,
desc: <TriangleDownIcon w={4} />
}[header.column.getIsSorted() as string] ?? null}
</div>
)}
</th>
)
})}
</tr>
))}
</thead>
<tbody className="bg-white divide-y divide-gray-200" ref={tbodyRef}>
{table.getRowModel().rows.map(row => {
return (
<Fragment key={row.id}>
<tr onClick={(event) => onRowClick && onRowClick(row, event)} tabIndex={0} onKeyDown={(e) => handleKeyDown(e, row)} className={onRowClick && `hover:cursor-pointer hover:bg-gray-100`}>
{/* first row is a normal row */}
{row.getVisibleCells().map(cell => {
return (
<td key={cell.id} className="px-6 py-4" style={{
width: cell.column.getSize(),
}}>
{flexRender(
cell.column.columnDef.cell,
cell.getContext()
)}
</td>
)
})}
</tr>
{row.getIsExpanded() && (
<tr>
{/* 2nd row is a custom 1 cell row */}
<td colSpan={row.getVisibleCells().length}>
{renderSubComponent && renderSubComponent({ row })}
<table className="w-full divide-y divide-gray-200 overflow-scroll table-fixed">
<thead className="bg-gray-50">
{table.getHeaderGroups().map(headerGroup => (
<tr key={headerGroup.id}>
{headerGroup.headers.map(header => {
return (
<th
key={header.id}
colSpan={header.colSpan}
scope="col"
className={`px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider ${header.column.getCanSort() && "cursor-pointer"}`}
onClick={header.column.getToggleSortingHandler()}
style={{
width: header.getSize() !== 0 ? header.getSize() : undefined,
}}
>
{header.isPlaceholder ? null : (
<div
{...{
className: header.column.getCanSort()
? 'cursor-pointer select-none'
: '',
onClick: header.column.getToggleSortingHandler(),
}}
>
{flexRender(
header.column.columnDef.header,
header.getContext()
)}
{{
asc: <TriangleUpIcon w={4} />,
desc: <TriangleDownIcon w={4} />
}[header.column.getIsSorted() as string] ?? null}
</div>
)}
</th>
)
})}
</tr>
))}
</thead>
<tbody className="bg-white divide-y divide-gray-200" ref={tbodyRef}>
{table.getRowModel().rows.map(row => {
return (
<Fragment key={row.id}>
<tr onClick={(event) => onRowClick && onRowClick(row, event)} tabIndex={0} onKeyDown={(e) => handleKeyDown(e, row)} className={onRowClick && `hover:cursor-pointer hover:bg-gray-100`}>
{/* first row is a normal row */}
{row.getVisibleCells().map(cell => {
return (
<td key={cell.id} className="px-6 py-4" style={{
width: cell.column.getSize(),
}}>
{flexRender(
cell.column.columnDef.cell,
cell.getContext()
)}
</td>
</tr>
)}
</Fragment>
)
})}
</tbody>
</table>
</div>
)
})}
</tr>
{row.getIsExpanded() && (
<tr>
{/* 2nd row is a custom 1 cell row */}
<td colSpan={row.getVisibleCells().length}>
{renderSubComponent && renderSubComponent({ row })}
</td>
</tr>
)}
</Fragment>
)
})}
</tbody>
</table>
)
}
export default Table;
35 changes: 35 additions & 0 deletions tavern/internal/www/src/context/HostContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import React, { createContext } from "react";
import { useQuery } from "@apollo/client";
import { HostType } from "../utils/consts";
import { GET_HOST_QUERY } from "../utils/queries";
import { useParams } from "react-router-dom";

export type HostContextQueryType = {
data: undefined | HostType;
loading: boolean;
error: any;
}

const defaultValue = { data: undefined, loading: false, error: undefined } as HostContextQueryType;

export const HostContext = createContext(defaultValue);

export const HostContextProvider = ({ children }: { children: React.ReactNode }) => {
const { hostId } = useParams();

const { loading, data, error } = useQuery(GET_HOST_QUERY, {
variables: {
"where": {
"id": hostId
}
}
});

const host = data?.hosts && data?.hosts.length > 0 ? data.hosts[0] : undefined as HostType | undefined;

return (
<HostContext.Provider value={{ data: host, loading, error }}>
{children}
</HostContext.Provider>
);
};
52 changes: 52 additions & 0 deletions tavern/internal/www/src/context/HostTaskContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import React, { createContext, Dispatch, SetStateAction } from "react";
import { useParams } from "react-router-dom";
import { useTasks } from "../hooks/useTasks";
import { DEFAULT_QUERY_TYPE } from "../utils/enums";

export type HostTaskContextQueryType = {
data: undefined | any;
loading: boolean;
error: any;
page: number,
filtersSelected: Array<any>,
setPage: Dispatch<SetStateAction<number>>,
setSearch: (search: string) => void,
setFiltersSelected: (filters: Array<any>) => void,
updateTaskList: () => void
}

const defaultValue = {
data: undefined,
loading: false,
error: false,
page: 1,
filtersSelected: [],
setPage: null,
setSearch: null,
setFiltersSelected: null,
updateTaskList: null,
} as any;

export const HostTaskContext = createContext<HostTaskContextQueryType>(defaultValue);

export const HostTaskContextProvider = ({ children }: { children: React.ReactNode }) => {
const { hostId } = useParams();

const {
data,
loading,
error,
setSearch,
setFiltersSelected,
filtersSelected,
updateTaskList,
page,
setPage
} = useTasks(DEFAULT_QUERY_TYPE.hostIDQuery, hostId);

return (
<HostTaskContext.Provider value={{ data, loading, error, setSearch, setFiltersSelected, filtersSelected, updateTaskList, page, setPage }}>
{children}
</HostTaskContext.Provider>
);
};
4 changes: 2 additions & 2 deletions tavern/internal/www/src/features/page-wrapper/PageWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ export const PageWrapper: FunctionComponent<Props> = ({ children, currNavItem })
}
<MobileNav currNavItem={currNavItem} sidebarOpen={sidebarOpen} handleSidebarOpen={setSidebarOpen} />

<main className={classNames("py-10", sidebarMinimized ? "lg:ml-24" : "lg:ml-72")}>
<div className="px-4 sm:px-6 xl:px-8">{children}</div>
<main className={classNames("py-4", sidebarMinimized ? "lg:ml-24" : "lg:ml-72")}>
<div className="px-4 sm:px-6 xl:px-8 flex flex-col gap-4">{children}</div>
</main>
</div>
</AccessGate>
Expand Down
20 changes: 8 additions & 12 deletions tavern/internal/www/src/pages/admin/AdminPortal.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,22 @@
import { AdminAccessGate } from "../../components/admin-access-gate";
import Breadcrumbs from "../../components/Breadcrumbs";
import PageHeader from "../../components/tavern-base-ui/PageHeader";
import { PageWrapper } from "../../features/page-wrapper";
import { PageNavItem } from "../../utils/enums";
import { UserTableWrapper } from "./components/UserTableWrapper";

export const AdminPortal = () => {

// Admin access gate is nested in PageWrapper to allow users who somehow get to an unauthorized page the ability to view the navigation to traverse back to acceptable pages

return (
<PageWrapper currNavItem={PageNavItem.admin}>
<AdminAccessGate>
<div className="border-b border-gray-200 pb-5 flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
<div className="flex-1 flex flex-col gap-2">
<h3 className="text-xl font-semibold leading-6 text-gray-900">Admin Portal</h3>
<div className="max-w-2xl text-sm">
<span>This portal is only accessible to Realm Admin. You can Activate/Deactivate users to grant or remove access to Realm. You can Promote/Demote users to grant or remove Admin privileges.</span>
</div>
</div>
</div>
<div>
<UserTableWrapper />
</div>
<Breadcrumbs pages={[{
label: "Admin",
link: "/admin"
}]} />
<PageHeader title="Admin" description="This portal is only accessible to Realm Admin. You can Activate/Deactivate users to grant or remove access to Realm. You can Promote/Demote users to grant or remove Admin privileges." />
<UserTableWrapper />
</AdminAccessGate>
</PageWrapper>
);
Expand Down
Loading
Loading