Skip to content

Commit ec8014f

Browse files
alcercujaybuidl
authored andcommitted
feat(web): add dispute template previewer
1 parent 95ac7f1 commit ec8014f

File tree

3 files changed

+174
-0
lines changed

3 files changed

+174
-0
lines changed

web/src/app.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import Home from "./pages/Home";
1111
import Cases from "./pages/Cases";
1212
import Dashboard from "./pages/Dashboard";
1313
import Courts from "./pages/Courts";
14+
import DisputeTemplateView from "./pages/DisputeTemplateView";
1415

1516
const App: React.FC = () => {
1617
return (
@@ -24,6 +25,7 @@ const App: React.FC = () => {
2425
<Route path="cases/*" element={<Cases />} />
2526
<Route path="courts/*" element={<Courts />} />
2627
<Route path="dashboard" element={<Dashboard />} />
28+
<Route path="disputeTemplate" element={<DisputeTemplateView />} />
2729
<Route path="*" element={<h1>Justice not found here ¯\_( ͡° ͜ʖ ͡°)_/¯</h1>} />
2830
</Route>
2931
</Routes>

web/src/components/ReactMarkdown.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import React from "react";
2+
import styled from "styled-components";
3+
import Reactmkdwn from "react-markdown";
4+
5+
const StyledMarkdown = styled(Reactmkdwn)`
6+
font-size: 16px;
7+
*,
8+
** {
9+
font-size: 16px;
10+
}
11+
`;
12+
13+
const ReactMarkdown: React.FC<{ children: string }> = ({ children }) => <StyledMarkdown>{children}</StyledMarkdown>;
14+
15+
export default ReactMarkdown;

web/src/pages/DisputeTemplateView.tsx

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
import React, { useMemo, useState } from "react";
2+
import styled from "styled-components";
3+
import Skeleton from "react-loading-skeleton";
4+
import { Textarea } from "@kleros/ui-components-library";
5+
import PolicyIcon from "svgs/icons/policy.svg";
6+
import ReactMarkdown from "components/ReactMarkdown";
7+
import { isUndefined } from "utils/index";
8+
9+
const Wrapper = styled.div`
10+
min-height: calc(100vh - 144px);
11+
margin: 24px;
12+
display: flex;
13+
gap: 12px;
14+
`;
15+
16+
const StyledTextArea = styled(Textarea)`
17+
width: 50%;
18+
height: calc(100vh - 300px);
19+
`;
20+
21+
const DisputeTemplateView: React.FC = () => {
22+
const [disputeTemplate, setDisputeTemplate] = useState<string>("");
23+
const [errorMsg, setErrorMessage] = useState<string>("");
24+
const parsedDisputeTemplate = useMemo(() => {
25+
try {
26+
const parsed = JSON.parse(disputeTemplate);
27+
setErrorMessage("");
28+
return parsed;
29+
} catch (e) {
30+
setErrorMessage((e as SyntaxError).message);
31+
return undefined;
32+
}
33+
}, [disputeTemplate]);
34+
const isThereInput = useMemo(() => disputeTemplate !== "", [disputeTemplate]);
35+
return (
36+
<Wrapper>
37+
<StyledTextArea
38+
value={disputeTemplate}
39+
onChange={(e) => setDisputeTemplate(e.target.value)}
40+
placeholder="Enter dispute template"
41+
variant={isThereInput && errorMsg !== "" ? "error" : ""}
42+
message={isThereInput ? errorMsg : ""}
43+
/>
44+
<Overview disputeTemplate={parsedDisputeTemplate} />
45+
</Wrapper>
46+
);
47+
};
48+
49+
const Container = styled.div`
50+
width: 50%;
51+
height: auto;
52+
display: flex;
53+
flex-direction: column;
54+
gap: 16px;
55+
56+
> h1 {
57+
margin: 0;
58+
}
59+
60+
> hr {
61+
width: 100%;
62+
}
63+
`;
64+
65+
const QuestionAndDescription = styled.div`
66+
display: flex;
67+
flex-direction: column;
68+
> * {
69+
margin: 0px;
70+
}
71+
`;
72+
73+
const VotingOptions = styled(QuestionAndDescription)`
74+
display: flex;
75+
flex-direction: column;
76+
> span {
77+
margin: 0px;
78+
display: flex;
79+
gap: 8px;
80+
}
81+
`;
82+
83+
const ShadeArea = styled.div`
84+
width: 100%;
85+
padding: 16px;
86+
margin-top: 16px;
87+
background-color: ${({ theme }) => theme.mediumBlue};
88+
> p {
89+
margin-top: 0;
90+
}
91+
`;
92+
93+
const StyledA = styled.a`
94+
display: flex;
95+
align-items: center;
96+
gap: 4px;
97+
> svg {
98+
width: 16px;
99+
fill: ${({ theme }) => theme.primaryBlue};
100+
}
101+
`;
102+
103+
const LinkContainer = styled.div`
104+
display: flex;
105+
justify-content: space-between;
106+
`;
107+
108+
const Overview: React.FC<{ disputeTemplate: any }> = ({ disputeTemplate }) => {
109+
return (
110+
<>
111+
<Container>
112+
<h1>
113+
{isUndefined(disputeTemplate) ? (
114+
<Skeleton />
115+
) : (
116+
disputeTemplate?.title ?? "The dispute's template is not correct please vote refuse to arbitrate"
117+
)}
118+
</h1>
119+
<QuestionAndDescription>
120+
<ReactMarkdown>{disputeTemplate?.question}</ReactMarkdown>
121+
<p>{disputeTemplate?.description}</p>
122+
</QuestionAndDescription>
123+
{disputeTemplate?.frontendUrl && (
124+
<a href={disputeTemplate?.frontendUrl} target="_blank" rel="noreferrer">
125+
Go to arbitrable
126+
</a>
127+
)}
128+
<VotingOptions>
129+
{disputeTemplate && <h3>Voting Options</h3>}
130+
{disputeTemplate?.answers?.map((answer: { title: string; description: string }, i: number) => (
131+
<span key={i}>
132+
<small>Option {i + 1}:</small>
133+
<label>{answer.title}</label>
134+
</span>
135+
))}
136+
</VotingOptions>
137+
<ShadeArea>
138+
<p>Make sure you understand the Policies</p>
139+
<LinkContainer>
140+
{disputeTemplate?.policyURI && (
141+
<StyledA
142+
href={`https://cloudflare-ipfs.com${disputeTemplate.policyURI}`}
143+
target="_blank"
144+
rel="noreferrer"
145+
>
146+
<PolicyIcon />
147+
Dispute Policy
148+
</StyledA>
149+
)}
150+
</LinkContainer>
151+
</ShadeArea>
152+
</Container>
153+
</>
154+
);
155+
};
156+
157+
export default DisputeTemplateView;

0 commit comments

Comments
 (0)