From 6daad97c5a94236633950981efc6cfd165f23163 Mon Sep 17 00:00:00 2001 From: Ed Chapman - Turing Date: Thu, 25 Jan 2024 17:29:37 +0000 Subject: [PATCH 1/4] optional svg previews on case management screen --- frontend/src/components/CaseMediaPreview.jsx | 68 +++++++++++++++ frontend/src/components/ManageCases.jsx | 37 ++++---- frontend/src/components/Mermaid.js | 11 +-- frontend/src/config.json | 88 +++++++++++++++----- 4 files changed, 160 insertions(+), 44 deletions(-) create mode 100644 frontend/src/components/CaseMediaPreview.jsx diff --git a/frontend/src/components/CaseMediaPreview.jsx b/frontend/src/components/CaseMediaPreview.jsx new file mode 100644 index 00000000..e7e5b226 --- /dev/null +++ b/frontend/src/components/CaseMediaPreview.jsx @@ -0,0 +1,68 @@ +import configData from "../config.json"; +import { CardMedia } from "@mui/material"; +import mockup_diagram from "../images/mockup-diagram.png"; +import { useTheme } from "@emotion/react"; +import { useEffect, useState } from "react"; +import MermaidChart from "./Mermaid"; +import { useLoginToken } from "../hooks/useAuth"; +import { getCase } from "./caseApi"; +import { Box } from "@mui/material"; + +export const CaseMediaPreview = ({ caseObj }) => { + const theme = useTheme(); + const [token] = useLoginToken(); + const [assuranceCase, setAssuranceCase] = useState(); + + useEffect(() => { + if (token) { + let isMounted = true; + getCase(token, caseObj.id) + .then((json) => { + if (!isMounted) { + return; + } + setAssuranceCase(json); + }) + .catch((err) => { + console.error(err); + // TODO show error to user + }); + + return () => { + isMounted = false; + }; + } + }, [token, caseObj]); + + return ( + <> + {configData.use_case_preview_svg && assuranceCase ? ( + + {}} + setMermaidFocus={() => {}} + /> + + ) : ( + + )} + + ); +}; diff --git a/frontend/src/components/ManageCases.jsx b/frontend/src/components/ManageCases.jsx index 90afe2dc..9d4b98d3 100644 --- a/frontend/src/components/ManageCases.jsx +++ b/frontend/src/components/ManageCases.jsx @@ -24,6 +24,7 @@ import CommentSection from "./CommentSection"; import CasePermissionsManager from "./CasePermissionsManager"; import DeleteCaseModal from "./DeleteCaseModal"; import ErrorMessage from "./common/ErrorMessage"; +import { CaseMediaPreview } from "./CaseMediaPreview"; const ThemedCard = ({ sx, ...props }) => { return ( @@ -77,7 +78,7 @@ const formatter = new Intl.DateTimeFormat(undefined, { year: "numeric", }); -const CaseCard = ({ id, name, description, createdDate, reload }) => { +const CaseCard = ({ caseObj, reload }) => { const theme = useTheme(); const [menuOpen, setMenuOpen] = useState(false); @@ -135,16 +136,11 @@ const CaseCard = ({ id, name, description, createdDate, reload }) => { - + + { textDecoration: "none", color: "unset", overflow: "hidden", + zIndex: 99, }} > - {name} + {caseObj.name} { minHeight: 0, }} > - {description?.split("\n").map((str) => ( + {caseObj.description?.split("\n").map((str) => ( <> {str}
@@ -183,7 +180,7 @@ const CaseCard = ({ id, name, description, createdDate, reload }) => {
{/* TODO, designs would prefer the updated date */} - Created: {formatter.format(createdDate)} + Created: {formatter.format(caseObj.createdDate)}
@@ -221,19 +218,23 @@ const CaseCard = ({ id, name, description, createdDate, reload }) => { + -
@@ -353,8 +354,8 @@ const ManageCases = () => { ) : ( <> - {cases.map(({ id, ...props }) => ( - + {cases.map((caseObj) => ( + ))} )} diff --git a/frontend/src/components/Mermaid.js b/frontend/src/components/Mermaid.js index e2271825..f9317dea 100644 --- a/frontend/src/components/Mermaid.js +++ b/frontend/src/components/Mermaid.js @@ -14,8 +14,9 @@ function MermaidChart({ const [collapsedNodes, setCollapsedNodes] = useState([]); const chartmd = useMemo( - () => - jsonToMermaid(assuranceCase, selectedType, selectedId, collapsedNodes), + () => { + return jsonToMermaid(assuranceCase, selectedType, selectedId, collapsedNodes) + }, [assuranceCase, selectedType, selectedId, collapsedNodes], ); @@ -81,7 +82,7 @@ function MermaidChart({ // trigger mermaid reload useEffect(() => { try { - const mermaidDiv = document.querySelector(".mermaid"); + const mermaidDiv = document.querySelector(`.mermaid-${caseId}`); if (mermaidDiv) { // inject the markdown here, rather than via react // so in between render and the effect you don't see the text @@ -89,7 +90,7 @@ function MermaidChart({ // make sure to use textContent and not innerHtml, as our markdown can contain html mermaidDiv.textContent = ""; // Clear the existing content mermaidDiv.textContent = chartmd; // Set new markdown content - mermaid.contentLoaded(); // Inform Mermaid to process the new content + mermaid.contentLoaded(); // Inform Mermaid to process the new content const collapseButtons = document.querySelectorAll(".collapse-expand"); collapseButtons.forEach((button) => @@ -113,7 +114,7 @@ function MermaidChart({ justifyContent: "center", overflow: "visible", }} - className="mermaid" + className={`mermaid-${caseId} mermaid`} /> ); } diff --git a/frontend/src/config.json b/frontend/src/config.json index 37ff7616..528e8a74 100644 --- a/frontend/src/config.json +++ b/frontend/src/config.json @@ -3,7 +3,10 @@ "DEFAULT_GITHUB_CLIENT_ID": "c2c9c84887b1bf94d80b", "DEFAULT_GITHUB_REDIRECT_URI": "http://localhost:3000/login", "BOX_NCHAR": 25, - "property_claim_types": ["Project claim", "System claim"], + "property_claim_types": [ + "Project claim", + "System claim" + ], "mermaid_styles": { "default": { "classTopLevelNormativeGoal": "fill:#638cb0,color:#FFF", @@ -94,19 +97,35 @@ "api_name": "goals", "db_name": "goals", "id_name": "goal_id", - "parent_names": ["AssuranceCase"], - "parent_api_names": ["cases"], - "parent_db_names": ["assurance_case_id"], + "parent_names": [ + "AssuranceCase" + ], + "parent_api_names": [ + "cases" + ], + "parent_db_names": [ + "assurance_case_id" + ], "shape": "hexagon", - "children": ["Context", "PropertyClaim", "Strategy"], + "children": [ + "Context", + "PropertyClaim", + "Strategy" + ], "parent_relation": "one-to-many" }, "Context": { "api_name": "contexts", "db_name": "context", - "parent_names": ["TopLevelNormativeGoal"], - "parent_api_names": ["goals"], - "parent_db_names": ["goal_id"], + "parent_names": [ + "TopLevelNormativeGoal" + ], + "parent_api_names": [ + "goals" + ], + "parent_db_names": [ + "goal_id" + ], "shape": "stadium", "children": [], "parent_relation": "one-to-many" @@ -115,19 +134,37 @@ "api_name": "propertyclaims", "db_name": "property_claims", "id_name": "property_claim_id", - "parent_names": ["TopLevelNormativeGoal", "PropertyClaim"], - "parent_api_names": ["goals", "propertyclaims"], - "parent_db_names": ["goal_id", "property_claim_id"], + "parent_names": [ + "TopLevelNormativeGoal", + "PropertyClaim" + ], + "parent_api_names": [ + "goals", + "propertyclaims" + ], + "parent_db_names": [ + "goal_id", + "property_claim_id" + ], "shape": "rounded", - "children": ["Evidence", "PropertyClaim"], + "children": [ + "Evidence", + "PropertyClaim" + ], "parent_relation": "one-to-many" }, "Evidence": { "api_name": "evidence", "db_name": "evidence", - "parent_names": ["PropertyClaim"], - "parent_api_names": ["propertyclaims"], - "parent_db_names": ["property_claim_id"], + "parent_names": [ + "PropertyClaim" + ], + "parent_api_names": [ + "propertyclaims" + ], + "parent_db_names": [ + "property_claim_id" + ], "shape": "data", "children": [], "parent_relation": "many-to-many" @@ -136,11 +173,19 @@ "api_name": "strategies", "db_name": "strategies", "id_name": "strategy_id", - "parent_names": ["TopLevelNormativeGoal"], - "parent_api_names": ["goals"], - "parent_db_names": ["goal_id"], + "parent_names": [ + "TopLevelNormativeGoal" + ], + "parent_api_names": [ + "goals" + ], + "parent_db_names": [ + "goal_id" + ], "shape": "parallelogram-left", - "children": ["PropertyClaim"], + "children": [ + "PropertyClaim" + ], "parent_relation": "one-to-many" } }, @@ -149,5 +194,6 @@ "desc_char_max_len": 40, "evidence_url_char_max_len": 250 } - } -} + }, + "use_case_preview_svg": true +} \ No newline at end of file From 2626348dfbbafb6e3831d1433e55e598a28bb5b5 Mon Sep 17 00:00:00 2001 From: Ed Chapman - Turing Date: Thu, 25 Jan 2024 18:35:23 +0000 Subject: [PATCH 2/4] improve svg preview crop --- frontend/src/components/Mermaid.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/Mermaid.js b/frontend/src/components/Mermaid.js index f9317dea..397f1db0 100644 --- a/frontend/src/components/Mermaid.js +++ b/frontend/src/components/Mermaid.js @@ -109,9 +109,11 @@ function MermaidChart({ key={chartmd} style={{ display: "flex", + flexDirection: "column", height: "100%", width: "100%", - justifyContent: "center", + justifyContent: "start", + alignItems: "start", overflow: "visible", }} className={`mermaid-${caseId} mermaid`} From 13fa841f36ced2a4b65a9021a493bc1c75a3ddec Mon Sep 17 00:00:00 2001 From: Ed Chapman - Turing Date: Thu, 25 Jan 2024 18:59:06 +0000 Subject: [PATCH 3/4] Add loading spinners while svgs load --- frontend/src/components/CaseMediaPreview.jsx | 71 +++++++++++--------- frontend/src/components/ManageCases.jsx | 2 +- 2 files changed, 41 insertions(+), 32 deletions(-) diff --git a/frontend/src/components/CaseMediaPreview.jsx b/frontend/src/components/CaseMediaPreview.jsx index e7e5b226..46aa2b1e 100644 --- a/frontend/src/components/CaseMediaPreview.jsx +++ b/frontend/src/components/CaseMediaPreview.jsx @@ -7,11 +7,13 @@ import MermaidChart from "./Mermaid"; import { useLoginToken } from "../hooks/useAuth"; import { getCase } from "./caseApi"; import { Box } from "@mui/material"; +import { LoadingCard } from "./ManageCases"; export const CaseMediaPreview = ({ caseObj }) => { const theme = useTheme(); const [token] = useLoginToken(); const [assuranceCase, setAssuranceCase] = useState(); + const [isLoading, setIsLoading] = useState(true); useEffect(() => { if (token) { @@ -22,6 +24,7 @@ export const CaseMediaPreview = ({ caseObj }) => { return; } setAssuranceCase(json); + setIsLoading(false); }) .catch((err) => { console.error(err); @@ -34,35 +37,41 @@ export const CaseMediaPreview = ({ caseObj }) => { } }, [token, caseObj]); - return ( - <> - {configData.use_case_preview_svg && assuranceCase ? ( - - {}} - setMermaidFocus={() => {}} - /> - - ) : ( - - )} - - ); + if (configData.use_case_preview_svg) { + return ( + <> + {isLoading ? ( + + ) : ( + + {}} + setMermaidFocus={() => {}} + /> + + )} + + ); + } else { + return ( + + ); + } }; diff --git a/frontend/src/components/ManageCases.jsx b/frontend/src/components/ManageCases.jsx index 9d4b98d3..b37576ba 100644 --- a/frontend/src/components/ManageCases.jsx +++ b/frontend/src/components/ManageCases.jsx @@ -241,7 +241,7 @@ const CaseCard = ({ caseObj, reload }) => { ); }; -const LoadingCard = () => { +export const LoadingCard = () => { return ( From db282ead6019759fc2a129917629740f28cfdb7e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 26 Jan 2024 15:47:02 +0000 Subject: [PATCH 4/4] style: pre-commit fixes --- frontend/src/components/Mermaid.js | 16 +++--- frontend/src/config.json | 85 +++++++----------------------- 2 files changed, 29 insertions(+), 72 deletions(-) diff --git a/frontend/src/components/Mermaid.js b/frontend/src/components/Mermaid.js index 397f1db0..7455dede 100644 --- a/frontend/src/components/Mermaid.js +++ b/frontend/src/components/Mermaid.js @@ -13,12 +13,14 @@ function MermaidChart({ }) { const [collapsedNodes, setCollapsedNodes] = useState([]); - const chartmd = useMemo( - () => { - return jsonToMermaid(assuranceCase, selectedType, selectedId, collapsedNodes) - }, - [assuranceCase, selectedType, selectedId, collapsedNodes], - ); + const chartmd = useMemo(() => { + return jsonToMermaid( + assuranceCase, + selectedType, + selectedId, + collapsedNodes, + ); + }, [assuranceCase, selectedType, selectedId, collapsedNodes]); // refresh state useEffect(() => { @@ -90,7 +92,7 @@ function MermaidChart({ // make sure to use textContent and not innerHtml, as our markdown can contain html mermaidDiv.textContent = ""; // Clear the existing content mermaidDiv.textContent = chartmd; // Set new markdown content - mermaid.contentLoaded(); // Inform Mermaid to process the new content + mermaid.contentLoaded(); // Inform Mermaid to process the new content const collapseButtons = document.querySelectorAll(".collapse-expand"); collapseButtons.forEach((button) => diff --git a/frontend/src/config.json b/frontend/src/config.json index 528e8a74..d257be46 100644 --- a/frontend/src/config.json +++ b/frontend/src/config.json @@ -3,10 +3,7 @@ "DEFAULT_GITHUB_CLIENT_ID": "c2c9c84887b1bf94d80b", "DEFAULT_GITHUB_REDIRECT_URI": "http://localhost:3000/login", "BOX_NCHAR": 25, - "property_claim_types": [ - "Project claim", - "System claim" - ], + "property_claim_types": ["Project claim", "System claim"], "mermaid_styles": { "default": { "classTopLevelNormativeGoal": "fill:#638cb0,color:#FFF", @@ -97,35 +94,19 @@ "api_name": "goals", "db_name": "goals", "id_name": "goal_id", - "parent_names": [ - "AssuranceCase" - ], - "parent_api_names": [ - "cases" - ], - "parent_db_names": [ - "assurance_case_id" - ], + "parent_names": ["AssuranceCase"], + "parent_api_names": ["cases"], + "parent_db_names": ["assurance_case_id"], "shape": "hexagon", - "children": [ - "Context", - "PropertyClaim", - "Strategy" - ], + "children": ["Context", "PropertyClaim", "Strategy"], "parent_relation": "one-to-many" }, "Context": { "api_name": "contexts", "db_name": "context", - "parent_names": [ - "TopLevelNormativeGoal" - ], - "parent_api_names": [ - "goals" - ], - "parent_db_names": [ - "goal_id" - ], + "parent_names": ["TopLevelNormativeGoal"], + "parent_api_names": ["goals"], + "parent_db_names": ["goal_id"], "shape": "stadium", "children": [], "parent_relation": "one-to-many" @@ -134,37 +115,19 @@ "api_name": "propertyclaims", "db_name": "property_claims", "id_name": "property_claim_id", - "parent_names": [ - "TopLevelNormativeGoal", - "PropertyClaim" - ], - "parent_api_names": [ - "goals", - "propertyclaims" - ], - "parent_db_names": [ - "goal_id", - "property_claim_id" - ], + "parent_names": ["TopLevelNormativeGoal", "PropertyClaim"], + "parent_api_names": ["goals", "propertyclaims"], + "parent_db_names": ["goal_id", "property_claim_id"], "shape": "rounded", - "children": [ - "Evidence", - "PropertyClaim" - ], + "children": ["Evidence", "PropertyClaim"], "parent_relation": "one-to-many" }, "Evidence": { "api_name": "evidence", "db_name": "evidence", - "parent_names": [ - "PropertyClaim" - ], - "parent_api_names": [ - "propertyclaims" - ], - "parent_db_names": [ - "property_claim_id" - ], + "parent_names": ["PropertyClaim"], + "parent_api_names": ["propertyclaims"], + "parent_db_names": ["property_claim_id"], "shape": "data", "children": [], "parent_relation": "many-to-many" @@ -173,19 +136,11 @@ "api_name": "strategies", "db_name": "strategies", "id_name": "strategy_id", - "parent_names": [ - "TopLevelNormativeGoal" - ], - "parent_api_names": [ - "goals" - ], - "parent_db_names": [ - "goal_id" - ], + "parent_names": ["TopLevelNormativeGoal"], + "parent_api_names": ["goals"], + "parent_db_names": ["goal_id"], "shape": "parallelogram-left", - "children": [ - "PropertyClaim" - ], + "children": ["PropertyClaim"], "parent_relation": "one-to-many" } }, @@ -196,4 +151,4 @@ } }, "use_case_preview_svg": true -} \ No newline at end of file +}