diff --git a/kraken_frontend/src/routes.tsx b/kraken_frontend/src/routes.tsx index 48dd0b297..55432e1c3 100644 --- a/kraken_frontend/src/routes.tsx +++ b/kraken_frontend/src/routes.tsx @@ -9,6 +9,7 @@ import AdminGuard from "./components/admin-guard"; import Attacks from "./views/attacks"; import AttackResults from "./views/attack-results"; import WorkspaceOverview from "./views/workspace-overview"; +import Workspace from "./views/workspace"; export const ROUTER = new Router(); @@ -16,6 +17,11 @@ export const ROUTES = { HOME: ROUTER.add({ url: "", parser: {}, render: () => }), ME: ROUTER.add({ url: "me", parser: {}, render: () => }), WORKSPACES: ROUTER.add({ url: "workspaces", parser: {}, render: () => }), + WORKSPACE: ROUTER.add({ + url: "workspaces/{id}", + parser: { id: Number }, + render: ({ id }) => , + }), ATTACKS: ROUTER.add({ url: "attacks", parser: {}, render: () => }), ATTACK_RESULTS: ROUTER.add({ url: "attacks/{id}", diff --git a/kraken_frontend/src/styling/workspace-overview.css b/kraken_frontend/src/styling/workspace-overview.css index 675169dc5..83b738d92 100644 --- a/kraken_frontend/src/styling/workspace-overview.css +++ b/kraken_frontend/src/styling/workspace-overview.css @@ -1,39 +1,39 @@ -.workspace-outer-container { +.workspace-list-outer-container { display: grid; grid-gap: 1em; animation: yourAnimation 0.3s step-End 0.4s 1 normal backwards; } -.workspace-creation { +.workspace-list-creation { display: flex; flex-direction: row; gap: 2em; } -.workspace-creation > .icon { +.workspace-list-creation > .icon { width: 10em; padding: 0 2em; } -.workspace-creation-form { +.workspace-list-creation-form { display: flex; flex-direction: column; width: 100%; align-items: flex-start; } -.workspace-creation-table { +.workspace-list-creation-table { display: grid; gap: 0.5em; grid-template-columns: 8em 20em; } -.workspace-creation-table > .button:last-of-type { +.workspace-list-creation-table > .button:last-of-type { margin-top: 1em; grid-column: 1 / span 2; } -.workspace-creation-table > textarea { +.workspace-list-creation-table > textarea { height: 5em; resize: none; } @@ -46,60 +46,60 @@ padding: 1em 2em; } -.workspace-filter-ownership { +.workspace-list-filter-ownership { display: flex; gap: 2em; align-items: center; } -.workspace-filter-ownership > .heading { +.workspace-list-filter-ownership > .heading { margin: 0; } -.workspace-filter-ownership-table { +.workspace-list-filter-ownership-table { display: grid; grid-template-columns: 1fr 1fr; justify-content: center; align-items: center; } -.workspace-sorting { +.workspace-list-sorting { display: flex; gap: 2em; align-items: center; } -.workspace-sorting > .heading { +.workspace-list-sorting > .heading { margin: 0; } -.workspace-sorting-table { +.workspace-list-sorting-table { display: grid; grid-template-columns: 3fr 1fr 1em 3fr 1fr; align-items: center; } -.workspace-container { +.workspace-list-container { display: flex; flex-wrap: wrap; gap: 1em; } -.workspace { +.workspace-list-item { cursor: pointer; width: 20em; transition: all 200ms; } -.workspace:hover { +.workspace-list-item:hover { box-shadow: 0 0 25em var(--primary-op), inset 0 0 10em #0cf3, inset 0 0 0.5em #0ff2, 0 0 1em var(--primary-op); } -.workspace > .heading { +.workspace-list-item > .heading { margin: 0 0 1em 0; } -.workspace-table { +.workspace-list-table { display: grid; gap: 0.25em; grid-template-columns: 1fr 2fr; diff --git a/kraken_frontend/src/styling/workspace.css b/kraken_frontend/src/styling/workspace.css index e69de29bb..11f6d12b1 100644 --- a/kraken_frontend/src/styling/workspace.css +++ b/kraken_frontend/src/styling/workspace.css @@ -0,0 +1,35 @@ +.workspace-outer-container { + display: flex; + flex-direction: column; + gap: 1em; + animation: yourAnimation 0.3s step-End 0.4s 1 normal backwards; +} + +.workspace-heading > h2 { + margin: 0; +} + +.workspace-section-selector { + display: grid; + width: 100%; + grid-template-columns: 1fr 1fr 1fr 1fr; + gap: 1em; +} + +.workspace-section-selector > .pane { + padding: 1em 2em; + cursor: pointer; + transition: all 200ms; +} + +.workspace-section-selector > .pane:hover { + box-shadow: 0 0 25em var(--primary-op), inset 0 0 10em #0cf3, inset 0 0 0.5em #0ff2, 0 0 1em var(--primary-op); +} + +.workspace-section-selector h3 { + margin: 0; +} + +.workspace-selected-tab { + filter: drop-shadow(0 0 0 var(--primary)); +} diff --git a/kraken_frontend/src/views/workspace-overview.tsx b/kraken_frontend/src/views/workspace-overview.tsx index 2910224a5..48d6bf5db 100644 --- a/kraken_frontend/src/views/workspace-overview.tsx +++ b/kraken_frontend/src/views/workspace-overview.tsx @@ -10,6 +10,7 @@ import "../styling/workspace-overview.css"; import WorkspaceIcon from "../svg/workspace"; import Checkbox from "../components/checkbox"; import USER_CONTEXT from "../context/user"; +import { ROUTES } from "../routes"; type Sorting = "none" | "name" | "createdAt" | "lastModified"; @@ -86,11 +87,11 @@ export default class WorkspaceOverview extends React.Component -
-
+
+
{ e.preventDefault(); @@ -98,7 +99,7 @@ export default class WorkspaceOverview extends React.Component

Create a new workspace

-
+
Name -
+

Filter

-
+
Owner
-
+

Sorting

-
+
Name
-
+
{workspaces .filter((e) => { let include = true; @@ -200,9 +201,14 @@ export default class WorkspaceOverview extends React.Component { return ( -
+
{ + ROUTES.WORKSPACE.visit({ id: w.id }); + }} + >

{w.name}

-
+
Owner: {w.owner.displayName} Description: diff --git a/kraken_frontend/src/views/workspace.tsx b/kraken_frontend/src/views/workspace.tsx index e69de29bb..adfae6d70 100644 --- a/kraken_frontend/src/views/workspace.tsx +++ b/kraken_frontend/src/views/workspace.tsx @@ -0,0 +1,75 @@ +import React from "react"; +import "../styling/workspace.css"; +import { FullWorkspace } from "../api/generated"; +import { Api } from "../api/api"; +import { toast } from "react-toastify"; + +type WorkspaceProps = { + id: number; +}; +type WorkspaceState = { + workspace: FullWorkspace | null; + selectedTab: "domains" | "ips" | "ports" | "services"; +}; + +export default class Workspace extends React.Component { + constructor(props: WorkspaceProps) { + super(props); + + this.state = { workspace: null, selectedTab: "domains" }; + } + + componentDidMount() { + Api.workspaces.get(this.props.id).then((res) => + res.match( + (workspace) => this.setState({ workspace }), + (err) => toast.error(err.message) + ) + ); + } + + render() { + return ( +
+
+

{this.state.workspace?.name}

+
+
+
{ + this.setState({ selectedTab: "domains" }); + }} + > +

Domains

+
+
{ + this.setState({ selectedTab: "ips" }); + }} + > +

IPs

+
+
{ + this.setState({ selectedTab: "ports" }); + }} + > +

Ports

+
+
{ + this.setState({ selectedTab: "services" }); + }} + > +

Services

+
+
+
+
+ ); + } +}