Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
 
Cp quest view fixup (#437)

* feature(quest): Add searching to quest view and change layout

* fix(build): Build UI

---------

Co-authored-by: Hulto <[email protected]>
  • Loading branch information
cmp5987 and hulto authored Jan 16, 2024
1 parent 62d9050 commit 0c1feba
Show file tree
Hide file tree
Showing 11 changed files with 215 additions and 102 deletions.
6 changes: 3 additions & 3 deletions tavern/internal/www/build/asset-manifest.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"files": {
"main.css": "/static/css/main.48904a80.css",
"main.js": "/static/js/main.d09979ec.js",
"main.js": "/static/js/main.8ec47288.js",
"react-syntax-highlighter/refractor-core-import.js": "/static/js/react-syntax-highlighter/refractor-core-import.d0cd1e85.chunk.js",
"react-syntax-highlighter_languages_refractor_abap.js": "/static/js/react-syntax-highlighter_languages_refractor_abap.a2bf84e3.chunk.js",
"react-syntax-highlighter_languages_refractor_actionscript.js": "/static/js/react-syntax-highlighter_languages_refractor_actionscript.fff5a604.chunk.js",
Expand Down Expand Up @@ -158,7 +158,7 @@
"static/media/eldrich.png": "/static/media/eldrich.a80c74e8249d2461e174.png",
"index.html": "/index.html",
"main.48904a80.css.map": "/static/css/main.48904a80.css.map",
"main.d09979ec.js.map": "/static/js/main.d09979ec.js.map",
"main.8ec47288.js.map": "/static/js/main.8ec47288.js.map",
"refractor-core-import.d0cd1e85.chunk.js.map": "/static/js/react-syntax-highlighter/refractor-core-import.d0cd1e85.chunk.js.map",
"react-syntax-highlighter_languages_refractor_abap.a2bf84e3.chunk.js.map": "/static/js/react-syntax-highlighter_languages_refractor_abap.a2bf84e3.chunk.js.map",
"react-syntax-highlighter_languages_refractor_actionscript.fff5a604.chunk.js.map": "/static/js/react-syntax-highlighter_languages_refractor_actionscript.fff5a604.chunk.js.map",
Expand Down Expand Up @@ -315,6 +315,6 @@
},
"entrypoints": [
"static/css/main.48904a80.css",
"static/js/main.d09979ec.js"
"static/js/main.8ec47288.js"
]
}
2 changes: 1 addition & 1 deletion tavern/internal/www/build/index.html
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png"><link rel="manifest" href="/site.webmanifest"><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Web site created using create-react-app"/><link rel="apple-touch-icon" href="/logo192.png"/><link rel="manifest" href="/manifest.json"/><title>Tavern - Red Team Engagement Platform</title><script defer="defer" src="/static/js/main.d09979ec.js"></script><link href="/static/css/main.48904a80.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png"><link rel="manifest" href="/site.webmanifest"><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Web site created using create-react-app"/><link rel="apple-touch-icon" href="/logo192.png"/><link rel="manifest" href="/manifest.json"/><title>Tavern - Red Team Engagement Platform</title><script defer="defer" src="/static/js/main.8ec47288.js"></script><link href="/static/css/main.48904a80.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
3 changes: 3 additions & 0 deletions tavern/internal/www/build/static/js/main.8ec47288.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.d09979ec.js

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { SearchIcon } from "@chakra-ui/icons";
import { Heading, Input, InputGroup, InputLeftElement } from "@chakra-ui/react";
import React, { useEffect, useRef } from "react";
import { debounce } from "lodash"

type Props = {
placeholder: string;
setSearch: (args: string) => void;
}
const FreeTextSearch = (props: Props) => {
const { placeholder, setSearch } = props;

const debouncedSearch = useRef(
debounce(async (criteria) => {
setSearch(criteria);
}, 300)
).current;

async function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
debouncedSearch(e.target.value);
}

useEffect(() => {
return () => {
debouncedSearch.cancel();
};
}, [debouncedSearch]);

return (
<div className="flex-1 gap-1">
<Heading size="sm" mb={2}> {placeholder}</Heading>
<InputGroup className=" border-gray-300">
<InputLeftElement pointerEvents='none'>
<SearchIcon color='gray.300' />
</InputLeftElement>
<Input type='text' placeholder={placeholder} onChange={handleChange} />
</InputGroup>
</div>
);
}
export default FreeTextSearch;
57 changes: 37 additions & 20 deletions tavern/internal/www/src/pages/quest-list/QuestList.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
import { useQuery } from "@apollo/client";
import { Link } from "react-router-dom";

import { PageWrapper } from "../../components/page-wrapper";
import { EmptyState, EmptyStateType } from "../../components/tavern-base-ui/EmptyState";
import FreeTextSearch from "../../components/tavern-base-ui/FreeTextSearch";
import { PageNavItem } from "../../utils/enums";
import { GET_QUEST_QUERY } from "../../utils/queries";
import { QuestTable } from "./quest-table";
import { QuestTable } from "./components/QuestTable";
import { useQuests } from "./hooks/useQuest";

export const QuestList = () => {
const { loading, data, error } = useQuery(GET_QUEST_QUERY);
const {
hasData,
data,
loading,
error,
setSearch
} = useQuests();

return (
<PageWrapper currNavItem={PageNavItem.quests}>
Expand All @@ -18,23 +24,34 @@ export const QuestList = () => {
<div className="flex flex-col justify-center items-center">
{loading ?
<EmptyState type={EmptyStateType.loading} label="Loading quests..." />
: error ?
<EmptyState type={EmptyStateType.error} label="Error loading quests" />
: data?.quests?.length > 0 ?
<QuestTable quests={data?.quests || []} />
:
<EmptyState label="No quests found" type={EmptyStateType.noData} details="Get started by creating a new quest." >
<Link to="/createQuest">
<button
type="button"
className="inline-flex items-center rounded-md bg-purple-700 px-4 py-2 text-sm font-semibold text-white shadow-sm hover:bg-purple-600 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-purple-700"
>
Create new quest
</button>
</Link>
</EmptyState>
: error ?
<EmptyState type={EmptyStateType.error} label="Error loading quests" />
: hasData ?
<>
<div className="p-4 bg-white rounded-lg shadow-lg mt-2 flex flex-col gap-1 w-full">
<FreeTextSearch setSearch={setSearch} placeholder="Search by quest or tome name" />
</div>
{data.length > 0 ? (
<div className="py-4 bg-white rounded-lg shadow-lg mt-2 flex flex-col gap-1 w-full">
<QuestTable quests={data} />
</div>
) : (
<EmptyState label="No quests matching search term" type={EmptyStateType.noMatches} />
)}
</>
:
<EmptyState label="No quests found" type={EmptyStateType.noData} details="Get started by creating a new quest." >
<Link to="/createQuest">
<button
type="button"
className="inline-flex items-center rounded-md bg-purple-700 px-4 py-2 text-sm font-semibold text-white shadow-sm hover:bg-purple-600 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-purple-700"
>
Create new quest
</button>
</Link>
</EmptyState>
}
</div>
</PageWrapper>
);
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { Badge } from "@chakra-ui/react";
import { Badge, Image } from "@chakra-ui/react";
import { ColumnDef } from "@tanstack/react-table";
import { formatDistance } from "date-fns";
import Table from "../../../components/tavern-base-ui/Table";
import { useNavigate } from "react-router-dom";
import { QuestProps, Task } from "../../../utils/consts";

type QuestTableProps = {
id: string,
Expand All @@ -19,50 +18,61 @@ type Props = {
quests: Array<QuestTableProps>;
}
export const QuestTable = (props: Props) => {
const {quests} = props;
const questTableData = getInitialQuestsTableData(quests);
const { quests } = props;
const navigate = useNavigate();


const currentDate = new Date();

const onToggle = (row:any) => {
const onToggle = (row: any) => {
navigate(`/results/${row?.original?.id}`)
}

const columns: ColumnDef<any>[] = [
{
id: "name",
header: 'Quest name',
accessorFn: row => row.name,
header: 'Quest details',
accessorFn: row => row,
footer: props => props.column.id,
sortingFn: "alphanumeric"
enableSorting: false,
cell: (cellData: any) => {
const questData = cellData.getValue();
return (
<div className="flex flex-col">
<div>{questData.name}</div>
<div className="text-sm flex flex-row gap-1 items-center text-gray-500">
{questData?.tome}
</div>
</div>
);
}
},
{
id: "lastUpdated",
header: 'Last updated',
accessorFn: row => formatDistance(new Date(row.lastUpdated), currentDate),
footer: props => props.column.id,
sortingFn: (
sortingFn: (
rowA,
rowB,
) => {
) => {
const numA = new Date(rowA?.original?.lastUpdated as string);
const numB= new Date(rowB?.original?.lastUpdated as string);
const numB = new Date(rowB?.original?.lastUpdated as string);

return numA < numB ? 1 : numA > numB ? -1 : 0;
}
}
},
{
id: "finished",
header: 'Finished Tasks',
accessorFn: row => row,
maxSize: 100,
cell: (row: any) => {
const rowData = row.row.original;
const finished = rowData.finished;
const allTasks = rowData.inprogress + rowData.queued + rowData.finished;

if(finished < allTasks ){
if (finished < allTasks) {
return (
<Badge ml='1' px='4' colorScheme='alphaWhite' fontSize="font-base">
{finished}/{allTasks}
Expand All @@ -76,17 +86,18 @@ export const QuestTable = (props: Props) => {
</Badge>
);
},
footer: (props:any) => props.column.id,
footer: (props: any) => props.column.id,
enableSorting: false,
},
{
id: "output",
header: 'Output available',
accessorKey: "outputCount",
maxSize: 100,
cell: (cellData: any) => {
const output = cellData.getValue();

if(output === 0 ){
if (output === 0) {
return (
<Badge ml='1' px='4' colorScheme='alphaWhite' fontSize="font-base">
{output}
Expand All @@ -100,70 +111,41 @@ export const QuestTable = (props: Props) => {
</Badge>
);
},
footer: (props:any) => props.column.id,
footer: (props: any) => props.column.id,
sortingFn: "alphanumeric"
},
{
id: "inprogress",
header: 'In progress tasks',
accessorKey: "inprogress",
footer: props => props.column.id,
sortingFn: "alphanumeric"
},
{
id: "queued",
header: 'Queued tasks',
accessorKey: "queued",
id: "creator",
header: 'Created by',
maxSize: 100,
accessorFn: row => row.creator,
footer: props => props.column.id,
sortingFn: "alphanumeric"
},
];


function getInitialQuestsTableData(data:any ){
const formattedData = data?.map( (quest: QuestProps) => {
const taskDetails = quest.tasks.reduce( (map:any, task: Task)=> {
const modMap = {...map};

if(task.execFinishedAt){
modMap.finished += 1;
}
else if(task.execStartedAt){
modMap.inprogress += 1;
}
else{
modMap.queued += 1;
}

if(new Date(task.lastModifiedAt) > new Date(modMap.lastUpdated) ){
modMap.lastUpdated = task.lastModifiedAt;
}

if(task.output !== ""){
modMap.outputCount += 1;
}
enableSorting: false,
cell: (cellData: any) => {
const creatorData = cellData.getValue();

return modMap
},
{
finished: 0,
inprogress: 0,
queued: 0,
outputCount: 0,
lastUpdated: null
if (!creatorData) {
return <div className="text-sm text-gray-500">Not available</div>;
}
);

return {
id: quest.id,
name: quest.name,
...taskDetails
return (
<div className="flex flex-row gap-2 items-center">
<Image
borderRadius='full'
boxSize='20px'
src={creatorData?.photoURL}
alt={`Profile of ${creatorData?.name}`}
/>
<div className="text-sm flex flex-row gap-1 items-center text-gray-500">
{creatorData?.name}
</div>
</div>
);
}
});
return formattedData.sort(function(a:any,b:any){return new Date(b.lastUpdated).getTime() - new Date(a.lastUpdated).getTime()});
};
},
];

return (
<Table data={questTableData} columns={columns} onRowClick={onToggle} />
<Table data={quests} columns={columns} onRowClick={onToggle} />
)
}
}
Loading

0 comments on commit 0c1feba

Please sign in to comment.