Skip to content

Commit 134e919

Browse files
Merge pull request #137 from terminusdb/beautifulErrorDisplay
Beautiful error display
2 parents 405d2e9 + c0be52b commit 134e919

File tree

12 files changed

+250
-25
lines changed

12 files changed

+250
-25
lines changed

packages/tdb-dashboard/src/App.css

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1241,3 +1241,32 @@ button.graphiql-tab-add>svg
12411241
/* line-height: 1.5 !important; */
12421242
min-height: 49px!important;
12431243
}
1244+
1245+
/** custom alert error message */
1246+
.alert_danger_text {
1247+
color: #842029 !important;
1248+
}
1249+
1250+
.alert_danger {
1251+
background-color: #f8d7da !important;
1252+
border: 1px solid #f5c2c7 !important;
1253+
}
1254+
1255+
.alert_danger_border {
1256+
border: 1px solid #842029 !important;
1257+
}
1258+
1259+
.alert_btn_close {
1260+
color: #842029 !important;
1261+
box-sizing: content-box;
1262+
width: 1em;
1263+
height: 1em;
1264+
padding: 0.25em 0.25em;
1265+
border: 0;
1266+
border-radius: 0.375rem;
1267+
opacity: 0.4;
1268+
}
1269+
1270+
.alert_expand_icons {
1271+
margin-top: -20px;
1272+
}

packages/tdb-dashboard/src/components/Alerts.js

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,24 @@ import {AiOutlineCheckCircle, AiOutlineWarning} from "react-icons/ai"
66
import {BiErrorCircle} from "react-icons/bi"
77
import {BsInfoCircle} from "react-icons/bs"
88
import {queryTimeDisplay} from "./utils"
9+
import { FaTimes, FaExclamationTriangle } from "react-icons/fa"
10+
11+
12+
/**
13+
*
14+
* @param {*} type document Type
15+
* @returns a close button icon
16+
*/
17+
const AlertCloseButton = ({ className, onClick }) => {
18+
// on close button display document list table
19+
return <Button variant="light"
20+
className={`${className} d-contents`}
21+
style={{display: "contents"}}
22+
tilte={`Close`}
23+
onClick={onClick}>
24+
<FaTimes/>
25+
</Button>
26+
}
927

1028
export const Alerts = ({message, type, onCancel, time}) => {
1129
const [hiddenAlerts, setHiddenAlerts] = React.useState([])
@@ -38,17 +56,18 @@ export const Alerts = ({message, type, onCancel, time}) => {
3856

3957
if(type == TERMINUS_DANGER)
4058
return <Alert
41-
className="text-break"
42-
variant="danger"
59+
//className="text-break"
60+
//variant="danger"
61+
className="alert_danger alert_danger_text"
4362
show={shouldShowAlert("danger")}
4463
onClose={() => onClose("danger")}>
4564

4665
<div className="d-flex justify-content-between">
47-
<div>
48-
<BiErrorCircle className="me-1" />
49-
<strong>Error: </strong> {message}
50-
</div>
51-
<Button variant="close" size="xs" onClick={() => onClose("danger")} />
66+
<div className="w-100">
67+
<FaExclamationTriangle className="me-1 mb-1" />
68+
<strong>Oops! Something went wrong.</strong> {message}
69+
</div>
70+
<AlertCloseButton className={"alert_btn_close alert_danger_text"} onClick={() => onClose("danger")}/>
5271
</div>
5372
</Alert>
5473

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import React, {useContext} from "react"
2+
import Accordion from 'react-bootstrap/Accordion';
3+
import { useAccordionButton } from 'react-bootstrap/AccordionButton';
4+
import Card from 'react-bootstrap/Card';
5+
import AccordionContext from 'react-bootstrap/AccordionContext';
6+
import * as CONST from "./constants"
7+
import { MdKeyboardArrowDown, MdKeyboardArrowRight } from "react-icons/md"
8+
9+
// function to show custom buttons on Show/Hide in Accordians
10+
function ToggleErrorDisplay({ children, eventKey, expandComponent, hideComponent, css }) {
11+
const { activeEventKey } = useContext(AccordionContext);
12+
13+
const onExpand = useAccordionButton(eventKey, () => {});
14+
15+
const isCurrentEventKey = activeEventKey === eventKey;
16+
17+
return (
18+
<button
19+
type="button"
20+
className={css}
21+
onClick={onExpand}
22+
>
23+
{ isCurrentEventKey ? hideComponent : expandComponent }
24+
</button>
25+
);
26+
}
27+
28+
// function displays accordians per property error
29+
/**
30+
*
31+
* @param {*} propertyName property name
32+
* @param {*} errorType error description per property
33+
* @param {*} message message per property
34+
* @returns
35+
*/
36+
export const DisplayErrorPerProperty = ({ propertyName, errorType, message }) => {
37+
return <Accordion className="bg-transparent border-0 w-100">
38+
<Accordion.Item eventKey={propertyName} className={"bg-transparent border-0 alert_danger_text"}>
39+
<div className="mb-3">
40+
<div className="fw-bold d-flex">
41+
<ToggleErrorDisplay eventKey={propertyName}
42+
css={CONST.ERROR_DOC_EXPAND_ICON_CLASSNAME}
43+
expandComponent={<MdKeyboardArrowRight/>}
44+
hideComponent={<MdKeyboardArrowDown/>}/>
45+
{errorType}
46+
<pre className="alert_danger_border ml-1 p-1 rounded">{propertyName}</pre>
47+
</div>
48+
</div>
49+
</Accordion.Item>
50+
<Accordion.Collapse eventKey={propertyName} className={"bg-transparent"}>
51+
<div style={{ whiteSpace: "pre-wrap" }}>{message}</div>
52+
</Accordion.Collapse>
53+
</Accordion>
54+
}
55+
56+
57+
// function display error messages
58+
export const ErrorDisplay = ({ errorData, message, css }) => {
59+
return <Accordion className="bg-transparent border-0 w-100">
60+
<Card className="bg-transparent border-0">
61+
<Card.Header className="bg-transparent">
62+
<span className="text-uppercase">{message} </span>
63+
<ToggleErrorDisplay eventKey="0" expandComponent={"More Info" } hideComponent={"Hide"} css={css}/>
64+
</Card.Header>
65+
<Accordion.Collapse eventKey="0">
66+
<Card.Body>
67+
{/*<pre>{JSON.stringify(errorData, null, 2)}</pre>*/}
68+
{errorData}
69+
</Card.Body>
70+
</Accordion.Collapse>
71+
</Card>
72+
</Accordion>
73+
}

packages/tdb-dashboard/src/components/JsonFrameViewer.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ export const JsonFrameViewer = ({jsonData, mode, setExtracted}) => {
6666
if(setExtracted) setExtracted(data)
6767
}
6868

69+
// onBlur
6970
return <React.Fragment>
7071
<CodeMirror
7172
value={JSON.stringify(data, null, 2)}

packages/tdb-dashboard/src/components/constants.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -499,3 +499,6 @@ export const DELETE="Delete"
499499
export const VIEW_LIST="List"
500500

501501

502+
//ERROR MESSAGE CONSTANTS
503+
export const ERROR_MORE_INFO_CLASSNAME = "float-right alert_danger alert_danger_text rounded alert_danger_border"
504+
export const ERROR_DOC_EXPAND_ICON_CLASSNAME = "mr-4 alert_expand_icons bg-transparent border-0 alert_danger_text"

packages/tdb-dashboard/src/components/utils.js

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@ import {format, subSeconds} from "date-fns"
33
//import deAT from 'date-fns/locale/de-AT/index'
44
//import {XSD_DATA_TYPE_PREFIX, XDD_DATA_TYPE_PREFIX} from "./constants"
55
import {FiCopy} from "react-icons/fi"
6-
import React from "react"
6+
import React, {useState} from "react"
77
import {VscGitPullRequestDraft} from "react-icons/vsc"
88
import {VscGitPullRequest} from "react-icons/vsc"
99
import {VscCheck} from "react-icons/vsc"
1010
import {AiOutlineCheck} from "react-icons/ai"
11+
import Stack from 'react-bootstrap/Stack';
1112
import Badge from "react-bootstrap/Badge"
1213
import Button from "react-bootstrap/Button"
1314
import {
@@ -320,11 +321,15 @@ export function sortAlphabetically (list, byID) {
320321
})
321322
}
322323

323-
// function which displays CR Conflict errors
324-
export function getCRConflictError (errorData) {
324+
// function which displays CR Conflict errors
325+
export const getCRConflictError = (errorData) => {
326+
325327
let message = "It looks like there are conflicts, fix these conflicts and then update or exit the Change Request"
326-
return <div>
327-
{message}
328+
return <div className="w-100">
329+
<Stack direction="horizontal" gap={3} className="w-100">
330+
<div>{message}</div>
331+
</Stack>
332+
328333
<pre>{JSON.stringify(errorData, null, 2)}</pre>
329334
</div>
330335
}

packages/tdb-dashboard/src/hooks/DocumentControlContext.js

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ export const DocumentControlContext = React.createContext()
33
export const DocumentControlObj = () => useContext(DocumentControlContext)
44
import {WOQLClientObj} from '../init-woql-client'
55
import * as CONST from "../components/constants"
6+
import { ErrorDisplay } from "../components/ErrorDisplay"
7+
import { DisplayErrorPerProperty } from "../components/ErrorDisplay"
68

79
export const DocumentControlProvider = ({children}) => {
810

@@ -37,6 +39,51 @@ export const DocumentControlProvider = ({children}) => {
3739

3840

3941

42+
// function to format and display errors in document Interface
43+
function formatErrorMessages (error) {
44+
45+
if(!error.hasOwnProperty("api:message")) return error
46+
47+
let message = error["api:message"]
48+
let errorElements = []
49+
if(error["api:error"]) {
50+
if(Array.isArray(error["api:error"]["api:witnesses"])) {
51+
error["api:error"]["api:witnesses"].map(err => {
52+
53+
if(err.hasOwnProperty("constraint_name")) {
54+
// CONSTRAINT ERRORS
55+
let propertyName = err["constraint_name"]
56+
let errorType = `${err["@type"]} on `
57+
let message = err.message
58+
59+
errorElements.push(
60+
<DisplayErrorPerProperty propertyName={propertyName} message={message} errorType={errorType}/>
61+
)
62+
}
63+
else {
64+
if(err.hasOwnProperty("@type")) {
65+
errorElements.push(
66+
<pre>{JSON.stringify(err, null, 2)}</pre>
67+
)
68+
}
69+
else {
70+
// OTHER TYPE ERRORS
71+
for(let items in err) {
72+
let propertyName = items
73+
let errorType = err[propertyName].hasOwnProperty("@type") ? `${err[propertyName]["@type"]} on ` : `Error occured on`
74+
let message = JSON.stringify(err[propertyName], null, 2)
75+
errorElements.push(
76+
<DisplayErrorPerProperty propertyName={propertyName} message={message} errorType={errorType}/>
77+
)
78+
}
79+
}
80+
}
81+
})
82+
}
83+
}
84+
return <ErrorDisplay errorData={errorElements} message={message} css={CONST.ERROR_MORE_INFO_CLASSNAME}/>
85+
}
86+
4087
return (
4188
<DocumentControlContext.Provider
4289
value={{
@@ -46,7 +93,8 @@ export const DocumentControlProvider = ({children}) => {
4693
jsonContent,
4794
setJsonContent,
4895
showFrames,
49-
setShowFrames
96+
setShowFrames,
97+
formatErrorMessages
5098
}}
5199
>
52100
{children}

packages/tdb-dashboard/src/hooks/DocumentHook.js

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { useNavigate, useParams } from "react-router-dom";
44
import { ChangeRequest } from "./ChangeRequest";
55
import { WOQLClientObj } from "../init-woql-client";
66

7+
78
// to be review this
89
export function CheckStatusObj() {
910
const {currentChangeRequest} = WOQLClientObj()
@@ -22,6 +23,14 @@ export function CheckStatusObj() {
2223

2324
}
2425

26+
// function to set error
27+
function setErrorMessage(setErrorMsg, err) {
28+
if(setErrorMsg) {
29+
if(err.hasOwnProperty("data")) setErrorMsg(err.data)
30+
else setErrorMsg(err.message)
31+
}
32+
}
33+
2534
/**
2635
* Create a new document
2736
* @param {*} client TerminusDB Client
@@ -47,7 +56,7 @@ export function CreateDocumentHook(client,document, setLoading, setErrorMsg) {
4756
}
4857
catch(err){
4958
setLoading(false)
50-
if(setErrorMsg) setErrorMsg(err.message)
59+
setErrorMessage(setErrorMsg, err)
5160
}
5261
}
5362

@@ -114,7 +123,8 @@ export function GetDocumentHook(client, documentId, setData, setLoading, setErro
114123
}
115124
catch(err){
116125
if(setLoading) setLoading(false)
117-
if(setErrorMsg) setErrorMsg(err.message)
126+
setErrorMessage(setErrorMsg, err)
127+
//if(setErrorMsg) setErrorMsg(err.message)
118128
}
119129
}
120130

@@ -152,7 +162,8 @@ export function EditDocumentHook(client, extractedUpdate, setLoading, setErrorMs
152162
navigate(-1)
153163
}
154164
catch(err){
155-
setErrorMsg(err.message)
165+
//setErrorMsg(err.message)
166+
setErrorMessage(setErrorMsg, err)
156167
setLoading(false)
157168
}
158169
}

packages/tdb-dashboard/src/hooks/hookUtils.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,9 @@ export function formatErrorMessage (err){
4343
}else if(err.data){
4444
if( err.data["api:message"] === "Incorrect authentication information"){
4545
message = "Incorrect authentication information, wrong username or password"
46-
}else if (err.data["api:status"]==="api:conflict"){
46+
}else if (err.data["api:status"]==="api:conflict") {
4747
message = getCRConflictError(err.data["api:witnesses"])
48-
}else if (err.data["api:message"]=== "Schema check failure"){
48+
}else if (err.data["api:message"]=== "Schema check failure") {
4949
message = `${err.data["api:message"]} ${JSON.stringify(err.data["system:witnesses"], null, 2)}`
5050
}else{
5151
message = err.data["api:message"]

packages/tdb-dashboard/src/pages/ChangeDiff.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ const CRAction = ({}) => {
4343
return <ChangeDiffComponent/>
4444

4545
// if needRebase
46-
return <React.Fragment>
46+
return <React.Fragment>
4747
{errorMessage && <Alerts message={errorMessage} type={CONST.TERMINUS_DANGER} onCancel={setError}/>}
4848
<Card className="update__change__request__card">
4949
<Card.Header className="w-100">

0 commit comments

Comments
 (0)