Restyle and support logs.

This commit is contained in:
Thomas E Lackey 2024-02-15 13:41:12 -06:00
parent 02e2053bdf
commit a40bb93abb
6 changed files with 184 additions and 84 deletions

View File

@ -41,5 +41,9 @@
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
"prettier": "^3.2.5"
}
}

View File

@ -1,53 +1,64 @@
import {
useQuery,
useMutation,
useQueryClient,
QueryClient,
QueryClientProvider,
} from '@tanstack/react-query'
import {fetchAllStatuses} from './utils/data-utils';
import {QueryClient, QueryClientProvider, useQuery, useQueryClient,} from '@tanstack/react-query'
import React, {useState} from "react";
import {fetchAllStatuses, fetchLogs} from './utils/data-utils';
import Modal from './Modal'
// Create a client
const queryClient = new QueryClient()
function App() {
return (
// Provide the client to your App
return (// Provide the client to your App
<QueryClientProvider client={queryClient}>
<center>
<StatusTable />
</center>
</QueryClientProvider>
)
<StatusTable/>
</QueryClientProvider>)
}
function StatusTable() {
// Access the client
const queryClient = useQueryClient()
const [logId, setLogId] = useState("");
// Queries
const query = useQuery({ queryKey: ['allStatus'], queryFn: fetchAllStatuses })
const query = useQuery({queryKey: ['allStatus'], queryFn: fetchAllStatuses})
const queryLog = useQuery({queryKey: ['log', logId], queryFn: () => fetchLogs(logId)});
return (
<table id="all-status">
<tr>
<th>URL</th>
<th>Application</th>
<th>Status</th>
<th>Last Update</th>
<th>Request ID</th>
</tr>
{query.data?.map((status: any) =>
<tr className={"tr-status " + status.lastState.toLowerCase()}>
<td className="td-url">{status.deployment && <a href={status.url}>{status.url}</a>}</td>
<td className="td-app-crn">{status.app}</td>
<td className="td-status">{status.lastState}</td>
<td className="td-last-update">{status.lastUpdate}</td>
<td className="td-id">{status.id}</td>
</tr>
)}
</table>
)
return (<div>
{logId && (<Modal open={true}>
<button className="close" onClick={() => setLogId("")}>CLOSE</button>
<div className="log-data">
<pre>{queryLog.data}</pre>
</div>
</Modal>)}
<div id="all-status">
{query.data?.map((status: any) => <div className={status.lastState.toLowerCase() + " status-row"}>
<div className="status-line">
<span className="status-key">Request ID:</span>
<span className="status-value">{status.id}</span>
</div>
<div className="status-line">
<span className="status-key">Application:</span>
<span className="status-value">{status.app}</span>
</div>
{status.deployment && <div className="status-line">
<span className="status-key">URL:</span>
<span className="status-value"><a href={status.url}>{status.url}</a></span>
</div>}
<div className="status-line">
<span className="status-key">Last Update:</span>
<span className="status-value">{status.lastUpdate}</span>
</div>
<div className="status-line">
<span className="status-key">Last State:</span>
<span className="status-value">{(status.logAvailable &&
<button className={"button-" + status.lastState.toLowerCase()}
onClick={() => {
queryClient.invalidateQueries({queryKey: ["log"]}).then(() => setLogId(status.id))
}}>{status.lastState}
</button>) || status.lastState}</span>
</div>
</div>)}
</div>
</div>)
}
export default App;

37
src/Modal.tsx Normal file
View File

@ -0,0 +1,37 @@
import {Component} from "react";
const MODAL_STYLES = {
position: "absolute" as "absolute",
backgroundColor: "#FFF",
marginTop: "2em",
padding: "2em",
zIndex: "1000",
width: "75%",
height: "85%",
borderRadius: ".5em",
};
const OVERLAY_STYLE = {
position: "fixed" as "fixed",
display: "flex",
justifyContent: "center",
top: "0",
left: "0",
width: "100%",
height: "100%",
backgroundColor: "rgba(0,0,0, .8)",
zIndex: "1000",
};
class Modal extends Component<{ open: any, children: any }> {
render() {
let {open, children} = this.props;
if (!open) return null;
return (<>
<div style={OVERLAY_STYLE}>
<div style={MODAL_STYLES}>{children}</div>
</div>
</>);
}
}
export default Modal;

View File

@ -5,6 +5,7 @@ body {
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
background-color: white;
}
code {
@ -12,70 +13,74 @@ code {
monospace;
}
button {
cursor: pointer;
}
#all-status {
width: 85%;
margin: auto;
min-width: 50%;
max-width: 75%;
padding: 10px;
}
#all-status td {
padding: 0.3em;
.log-data {
overflow: auto;
color: grey;
background-color: black;
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace;
width: 98%;
height: 95%;
padding: 1em;
}
table {
border-collapse:separate;
border:solid lightblue 1px;
border-radius:6px;
.status-row {
width: auto;
background-color: white;
padding: 1em;
margin: 1em;
border-radius: 25px;
}
td, th {
border-left:solid lightblue 1px;
border-top:solid lightblue 1px;
.status-key {
font-weight: bold;
margin-right: 0.5em;
}
th {
border-top: none;
}
td:first-child, th:first-child {
border-left: none;
.status-value {
}
.deployed {
background-color: #ddffdd;
background-color: #ddffdd;
}
.button-deployed {
background-color: lawngreen;
color: white;
}
.error {
background-color: #ffdddd;
background-color: #ffdddd;
}
.button-error {
background-color: red;
}
.cancelled {
background-color: #efefef;
text-color: #dddddd;
background-color: #efefef;
text-color: #dddddd;
}
.removed {
background-color: #efeffa;
text-color: #dddddd;
background-color: #efeffa;
text-color: #dddddd;
}
.submitted {
background-color: #ffffdd;
background-color: #ffffdd;
}
.deploying {
background-color: #fff1dd;
background-color: #fff1dd;
}
.tr-status {
font-size: small;
font-family: 'Courier New', Courier, monospace;
}
.td-id {
font-size: xx-small;
}
.td-last-update{
}
.td-status{
}

View File

@ -1,8 +1,21 @@
export const fetchAllStatuses = async () => {
let url = "http://localhost:9555"
const res = await fetch(url);
if (!res.ok) {
throw new Error('Network response was not ok')
}
return await res.json();
}
export const fetchAllStatuses = async () => {
let url = "http://localhost:9555"
const res = await fetch(url);
if (!res.ok) {
throw new Error('Network response was not ok')
}
return await res.json();
}
export const fetchLogs = async (id: string) => {
if (!id) {
return "";
}
let url = `http://localhost:9555/log/${id}`
const res = await fetch(url);
if (!res.ok) {
throw new Error('Network response was not ok')
}
return await res.text();
}

View File

@ -87,7 +87,7 @@
"@jridgewell/trace-mapping" "^0.3.17"
jsesc "^2.5.1"
"@babel/helper-annotate-as-pure@^7.22.5":
"@babel/helper-annotate-as-pure@^7.18.6", "@babel/helper-annotate-as-pure@^7.22.5":
version "7.22.5"
resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz#e7f06737b197d580a01edf75d97e2c8be99d3882"
integrity sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==
@ -127,6 +127,21 @@
"@babel/helper-split-export-declaration" "^7.22.6"
semver "^6.3.1"
"@babel/helper-create-class-features-plugin@^7.21.0":
version "7.23.10"
resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.23.10.tgz#25d55fafbaea31fd0e723820bb6cc3df72edf7ea"
integrity sha512-2XpP2XhkXzgxecPNEEK8Vz8Asj9aRxt08oKOqtiZoqV2UGZ5T+EkyP9sXQ9nwMxBIG34a7jmasVqoMop7VdPUw==
dependencies:
"@babel/helper-annotate-as-pure" "^7.22.5"
"@babel/helper-environment-visitor" "^7.22.20"
"@babel/helper-function-name" "^7.23.0"
"@babel/helper-member-expression-to-functions" "^7.23.0"
"@babel/helper-optimise-call-expression" "^7.22.5"
"@babel/helper-replace-supers" "^7.22.20"
"@babel/helper-skip-transparent-expression-wrappers" "^7.22.5"
"@babel/helper-split-export-declaration" "^7.22.6"
semver "^6.3.1"
"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.22.15", "@babel/helper-create-regexp-features-plugin@^7.22.5":
version "7.22.15"
resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.15.tgz#5ee90093914ea09639b01c711db0d6775e558be1"
@ -380,6 +395,16 @@
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz#7844f9289546efa9febac2de4cfe358a050bd703"
integrity sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==
"@babel/plugin-proposal-private-property-in-object@^7.21.11":
version "7.21.11"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.11.tgz#69d597086b6760c4126525cfa154f34631ff272c"
integrity sha512-0QZ8qP/3RLDVBwBFoWAwCtgcDZJVwA5LUJRZU8x2YFfKNuFq161wK3cuGrALu5yiPu+vzwTAg/sMWVNeWeNyaw==
dependencies:
"@babel/helper-annotate-as-pure" "^7.18.6"
"@babel/helper-create-class-features-plugin" "^7.21.0"
"@babel/helper-plugin-utils" "^7.20.2"
"@babel/plugin-syntax-private-property-in-object" "^7.14.5"
"@babel/plugin-syntax-async-generators@^7.8.4":
version "7.8.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d"
@ -7676,6 +7701,11 @@ prelude-ls@~1.1.2:
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
integrity sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==
prettier@^3.2.5:
version "3.2.5"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.2.5.tgz#e52bc3090586e824964a8813b09aba6233b28368"
integrity sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==
pretty-bytes@^5.3.0, pretty-bytes@^5.4.1:
version "5.6.0"
resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb"