Skip to content

Commit baf0dd0

Browse files
authored
Merge pull request #121 from AllenCell/feature/layout
2 parents 5e5862f + 99748b5 commit baf0dd0

File tree

12 files changed

+239
-196
lines changed

12 files changed

+239
-196
lines changed

src/App.css

Lines changed: 26 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,38 @@
11
#root {
2-
margin: 0 auto;
3-
text-align: center;
2+
margin: 0 auto;
3+
--header-height: 48px;
4+
--recipe-select-height: 52px;
5+
--tab-height: 62px;
6+
--footer-height: 64px;
47
}
58

69
.app-container {
7-
display: flex;
8-
flex-direction: column;
9-
justify-self: flex-start;
10-
align-self: flex-start;
11-
align-items: center;
10+
width: 100vw;
11+
height: 100vh;
1212
}
1313

1414
.header {
15-
width: 90vw;
16-
display: flex;
17-
align-items: center;
15+
width: 100vw;
16+
display: flex;
17+
align-items: center;
18+
height: var(--header-height);
19+
box-sizing: border-box;
20+
border-bottom: 1px solid;
1821
}
1922

20-
.content-container {
21-
display: flex;
22-
flex-direction: column;
23-
justify-content: center;
24-
justify-self: center;
25-
gap: 20px;
26-
width: 900px;
27-
padding: 20px;
28-
margin-top: 36px;
23+
.sider {
24+
height: calc(
25+
100vh - var(--header-height) - var(--footer-height)
26+
); /* full height minus header and footer */
27+
overflow: scroll;
28+
padding: 0 40px;
2929
}
3030

31-
.status-container {
32-
display: flex;
33-
flex-direction: row;
34-
gap: 30px;
35-
border: 1px solid #D3D3D3;
36-
padding: 10px;
37-
border-radius: 10px;
38-
padding-left: 20px;
31+
.footer {
32+
display: flex;
33+
flex-direction: row;
34+
padding: 10px;
35+
padding-left: 20px;
36+
gap: 20px;
37+
align-items: stretch;
3938
}
40-
41-
.status-row {
42-
display: flex;
43-
flex-direction: row;
44-
gap: 20px;
45-
align-items: stretch;
46-
}
47-
48-
.status-bar {
49-
flex: 1;
50-
display: flex;
51-
align-items: center;
52-
}
53-
54-
.download-button {
55-
height: auto;
56-
min-height: 48px;
57-
}

src/App.tsx

Lines changed: 86 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { useState } from "react";
2-
import { v4 as uuidv4 } from 'uuid';
2+
import { v4 as uuidv4 } from "uuid";
33
import { Layout, Typography } from "antd";
44
import { getJobStatus, addRecipe } from "./utils/firebase";
55
import { getFirebaseRecipe, jsonToString } from "./utils/recipeLoader";
@@ -8,11 +8,10 @@ import { FIRESTORE_FIELDS } from "./constants/firebase";
88
import { SIMULARIUM_EMBED_URL } from "./constants/urls";
99
import PackingInput from "./components/PackingInput";
1010
import Viewer from "./components/Viewer";
11-
import ErrorLogs from "./components/ErrorLogs";
1211
import StatusBar from "./components/StatusBar";
1312
import "./App.css";
1413

15-
const { Header, Content } = Layout;
14+
const { Header, Content, Sider, Footer } = Layout;
1615
const { Link } = Typography;
1716

1817
function App() {
@@ -29,12 +28,27 @@ function App() {
2928
return new Promise((resolve) => setTimeout(resolve, ms));
3029
}
3130

32-
const recipeHasChanged = async (recipeId: string, recipeString: string): Promise<boolean> => {
31+
const resetState = () => {
32+
setJobId("");
33+
setJobStatus("");
34+
setJobLogs("");
35+
setResultUrl("");
36+
setRunTime(0);
37+
};
38+
39+
const recipeHasChanged = async (
40+
recipeId: string,
41+
recipeString: string
42+
): Promise<boolean> => {
3343
const originalRecipe = await getFirebaseRecipe(recipeId);
3444
return !(jsonToString(originalRecipe) == recipeString);
35-
}
45+
};
3646

37-
const recipeToFirebase = (recipe: string, path: string, id: string): object => {
47+
const recipeToFirebase = (
48+
recipe: string,
49+
path: string,
50+
id: string
51+
): object => {
3852
const recipeJson = JSON.parse(recipe);
3953
if (recipeJson.bounding_box) {
4054
const flattened_array = Object.assign({}, recipeJson.bounding_box);
@@ -44,26 +58,37 @@ function App() {
4458
recipeJson[FIRESTORE_FIELDS.NAME] = id;
4559
recipeJson[FIRESTORE_FIELDS.TIMESTAMP] = Date.now();
4660
return recipeJson;
47-
}
61+
};
4862

49-
const submitRecipe = async (recipeId: string, configId: string, recipeString: string) => {
50-
setResultUrl("");
51-
setRunTime(0);
63+
const submitRecipe = async (
64+
recipeId: string,
65+
configId: string,
66+
recipeString: string
67+
) => {
68+
resetState();
5269
let firebaseRecipe = "firebase:recipes/" + recipeId;
53-
const firebaseConfig = configId ? "firebase:configs/" + configId : undefined;
54-
const recipeChanged: boolean = await recipeHasChanged(recipeId, recipeString);
70+
const firebaseConfig = configId
71+
? "firebase:configs/" + configId
72+
: undefined;
73+
const recipeChanged: boolean = await recipeHasChanged(
74+
recipeId,
75+
recipeString
76+
);
5577
if (recipeChanged) {
5678
const recipeId = uuidv4();
5779
firebaseRecipe = "firebase:recipes_edited/" + recipeId;
58-
const recipeJson = recipeToFirebase(recipeString, firebaseRecipe, recipeId);
80+
const recipeJson = recipeToFirebase(
81+
recipeString,
82+
firebaseRecipe,
83+
recipeId
84+
);
5985
try {
6086
await addRecipe(recipeId, recipeJson);
6187
} catch (e) {
6288
setJobStatus(JOB_STATUS.FAILED);
6389
setJobLogs(String(e));
6490
return;
6591
}
66-
6792
}
6893
const url = getSubmitPackingUrl(firebaseRecipe, firebaseConfig);
6994
const request: RequestInfo = new Request(url, { method: "POST" });
@@ -81,18 +106,29 @@ function App() {
81106
}
82107
};
83108

84-
const startPacking = async (recipeId: string, configId: string, recipeString: string) => {
85-
await submitRecipe(recipeId, configId, recipeString)
86-
.then((jobIdFromSubmit) => checkStatus(jobIdFromSubmit));
87-
}
109+
const startPacking = async (
110+
recipeId: string,
111+
configId: string,
112+
recipeString: string
113+
) => {
114+
await submitRecipe(recipeId, configId, recipeString).then(
115+
(jobIdFromSubmit) => checkStatus(jobIdFromSubmit)
116+
);
117+
};
88118

89119
const checkStatus = async (jobIdFromSubmit: string) => {
90120
const id = jobIdFromSubmit || jobId;
91121
let localJobStatus = await getJobStatus(id);
92-
while (localJobStatus?.status !== JOB_STATUS.DONE && localJobStatus?.status !== JOB_STATUS.FAILED) {
122+
while (
123+
localJobStatus?.status !== JOB_STATUS.DONE &&
124+
localJobStatus?.status !== JOB_STATUS.FAILED
125+
) {
93126
await sleep(500);
94127
const newJobStatus = await getJobStatus(id);
95-
if (newJobStatus && localJobStatus?.status !== newJobStatus.status) {
128+
if (
129+
newJobStatus &&
130+
localJobStatus?.status !== newJobStatus.status
131+
) {
96132
localJobStatus = newJobStatus;
97133
setJobStatus(newJobStatus.status);
98134
}
@@ -107,22 +143,39 @@ function App() {
107143
}
108144
};
109145

110-
const showLogs = jobStatus == JOB_STATUS.FAILED;
111-
112146
return (
113-
<div className="app-container">
114-
<Header className="header" style={{ justifyContent: "space-between" }}>
147+
<Layout className="app-container">
148+
<Header
149+
className="header"
150+
style={{ justifyContent: "space-between" }}
151+
>
115152
<h2 className="header-title">cellPACK demo</h2>
116-
<Link href="https://github.com/mesoscope/cellpack" className="header-link">GitHub</Link>
153+
<Link
154+
href="https://github.com/mesoscope/cellpack"
155+
className="header-link"
156+
>
157+
GitHub
158+
</Link>
117159
</Header>
118-
<Content className="content-container">
119-
<PackingInput startPacking={startPacking} />
120-
{jobStatus && <StatusBar jobStatus={jobStatus} runTime={runTime} jobId={jobId} outputDir={outputDir} />}
121-
{showLogs && <ErrorLogs errorLogs={jobLogs} />}
122-
</Content>
123-
{resultUrl && <Viewer resultUrl={resultUrl} />}
124-
</div>
160+
<Layout>
161+
<Sider width="35%" theme="light" className="sider">
162+
<PackingInput startPacking={startPacking} />
163+
</Sider>
164+
<Content className="content-container">
165+
<Viewer resultUrl={resultUrl} />
166+
</Content>
167+
</Layout>
168+
<Footer className="footer">
169+
<StatusBar
170+
jobStatus={jobStatus}
171+
runTime={runTime}
172+
jobId={jobId}
173+
errorLogs={jobLogs}
174+
outputDir={outputDir}
175+
/>
176+
</Footer>
177+
</Layout>
125178
);
126179
}
127180

128-
export default App;
181+
export default App;

src/components/ErrorLogs/index.tsx

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { useState } from "react";
2-
import { Collapse } from "antd";
2+
import { Button, Drawer } from "antd";
33
import "./style.css";
44

55
interface ErrorLogsProps {
@@ -12,26 +12,26 @@ const ErrorLogs = (props: ErrorLogsProps): JSX.Element => {
1212

1313
const toggleLogs = () => {
1414
setViewErrorLogs(!viewErrorLogs);
15-
}
16-
const items = [{
17-
key: "1",
18-
label: "Logs",
19-
children: (
20-
<div className="log-box">
21-
<pre>{errorLogs}</pre>
22-
</div>
23-
)
24-
}];
15+
};
2516

2617
return (
27-
<div>
28-
<Collapse
29-
items={items}
30-
activeKey={viewErrorLogs && errorLogs.length > 0 ? ["1"] : []}
31-
onChange={toggleLogs}
32-
/>
33-
</div>
18+
<>
19+
<Button color="primary" variant="filled" onClick={toggleLogs}>
20+
Logs
21+
</Button>
22+
<Drawer
23+
title="Logs"
24+
placement="right"
25+
closable={true}
26+
onClose={toggleLogs}
27+
open={viewErrorLogs}
28+
>
29+
<div className="log-box">
30+
<pre>{errorLogs}</pre>
31+
</div>
32+
</Drawer>
33+
</>
3434
);
3535
};
3636

37-
export default ErrorLogs;
37+
export default ErrorLogs;

src/components/JSONViewer/index.tsx

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,26 +14,28 @@ const JSONViewer = (props: JSONViewerProps): JSX.Element => {
1414
const [viewContent, setViewContent] = useState<boolean>(true);
1515

1616
if (!content) {
17-
return (<></>)
17+
return <></>;
1818
}
19-
20-
const items = [{
21-
key: "1",
22-
label: title,
23-
children: isEditable ? (
24-
<Input.TextArea
25-
value={content}
26-
onChange={(e) => onChange(e.target.value)}
27-
rows={14}
28-
/>
29-
) : (
30-
<pre className="json-content">{content}</pre>
31-
)
32-
}];
33-
19+
20+
const items = [
21+
{
22+
key: "1",
23+
label: title,
24+
children: isEditable ? (
25+
<Input.TextArea
26+
value={content}
27+
onChange={(e) => onChange(e.target.value)}
28+
rows={14}
29+
/>
30+
) : (
31+
<pre className="json-content">{content}</pre>
32+
),
33+
},
34+
];
35+
3436
return (
3537
<div className={`${title.toLowerCase()}-box`}>
36-
<Collapse
38+
<Collapse
3739
items={items}
3840
activeKey={viewContent ? ["1"] : []}
3941
onChange={() => setViewContent(!viewContent)}
@@ -42,4 +44,4 @@ const JSONViewer = (props: JSONViewerProps): JSX.Element => {
4244
);
4345
};
4446

45-
export default JSONViewer;
47+
export default JSONViewer;

0 commit comments

Comments
 (0)