Skip to content

Commit 2ff6aa4

Browse files
authored
feat(RV-417): Use local storage as fallback from api (#418)
* feat(RV-417): Use local storage as fallback from api * feat(RV-417): Use local storage as fallback from api * feat(RV-417): Use local storage as fallback from api
1 parent 15bfdac commit 2ff6aa4

File tree

3 files changed

+157
-65
lines changed

3 files changed

+157
-65
lines changed

frontend/e2e/App.spec.ts

+34-45
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,6 @@
11
import { test, expect } from '@playwright/test';
22

33

4-
test.beforeEach(async ({ page }) => {
5-
await page.route("*/**/templates", async (route) => {
6-
route.fulfill({});
7-
})
8-
});
9-
104
test('has STLT Name', async ({ page }) => {
115

126
await page.goto('/');
@@ -24,45 +18,40 @@ test('has new template button', async ({ page , baseURL}) => {
2418

2519
test.describe('when templates exist', async () => {
2620
test.beforeEach(async ({page}) => {
27-
await page.route("*/**/templates", async (route) => {
28-
route.fulfill({
29-
status: 200,
30-
body: JSON.stringify({
31-
_embedded: {
32-
templates: [
33-
{
34-
name: "MumpsQuestV1",
35-
lab: "Quest",
36-
createdBy: "J.Smith",
37-
status: "Completed",
38-
updatedAt: new Date(Date.parse("2025-03-24T12:00:00.000-05:00"))
39-
},
40-
{
41-
name: "LBTIRadar",
42-
lab: "Radar",
43-
createdBy: "C.Alex",
44-
status: "Completed",
45-
updatedAt: new Date(Date.parse("2025-05-30T12:00:00.000-05:00"))
46-
},
47-
{
48-
name: "COVIDBaylor1",
49-
lab: "Emory",
50-
createdBy: "A.Bryant",
51-
status: "Completed",
52-
updatedAt: new Date(Date.parse("2025-06-21T12:00:00.000-05:00"))
53-
},
54-
{
55-
name: "COVIDEMory",
56-
lab: "Baylor",
57-
createdBy: "D.Smith",
58-
status: "Completed",
59-
updatedAt: new Date(Date.parse("2024-06-21T12:00:00.000-05:00"))
60-
},
61-
]
62-
}
63-
})
64-
})
65-
});
21+
await page.goto('/')
22+
await page.evaluate(() => {
23+
const templates = [
24+
{
25+
name: "MumpsQuestV1",
26+
lab: "Quest",
27+
createdBy: "J.Smith",
28+
status: "Completed",
29+
lastUpdated: new Date(Date.parse("2025-03-24T12:00:00.000-05:00"))
30+
},
31+
{
32+
name: "LBTIRadar",
33+
lab: "Radar",
34+
createdBy: "C.Alex",
35+
status: "Completed",
36+
lastUpdated: new Date(Date.parse("2025-05-30T12:00:00.000-05:00"))
37+
},
38+
{
39+
name: "COVIDBaylor1",
40+
lab: "Emory",
41+
createdBy: "A.Bryant",
42+
status: "Completed",
43+
lastUpdated: new Date(Date.parse("2025-06-21T12:00:00.000-05:00"))
44+
},
45+
{
46+
name: "COVIDEMory",
47+
lab: "Baylor",
48+
createdBy: "D.Smith",
49+
status: "Completed",
50+
lastUpdated: new Date(Date.parse("2024-06-21T12:00:00.000-05:00"))
51+
},
52+
];
53+
localStorage.setItem('templates', JSON.stringify(templates))
54+
})
6655
})
6756
test('displays list of templates if they exist and sorting functions', async ({page}) => {
6857
await page.goto('/')

frontend/src/components/ExtractUploadFile.tsx

+55-12
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { ChangeEvent, useId, useState } from "react";
1+
import React, { ChangeEvent, useEffect, useId, useState } from "react";
22
import { Button, Select } from "@trussworks/react-uswds";
33
import { useFiles } from "../contexts/FilesContext";
44
import { useNavigate } from "react-router-dom";
@@ -24,23 +24,23 @@ interface IFilesObj {
2424
files: File[];
2525
}
2626

27-
// interface Template {
28-
// name: string;
29-
// description: string;
30-
// pages: {
31-
// image: string;
32-
// fieldNames: string[];
33-
// }[];
34-
// }
27+
interface Template {
28+
name: string;
29+
description: string;
30+
pages: {
31+
image: string;
32+
fieldNames: string[];
33+
}[];
34+
}
3535

3636
export const ExtractUploadFile: React.FC<ExtractUploadFileProps> = ({
3737
onUploadComplete,
3838
}) => {
3939
const id = useId();
40-
const { addFile, files, setSelectedTemplates, selectedTemplates , clearTemplates} = useFiles();
40+
const { addFile, files, setSelectedTemplates, selectedTemplates , clearTemplates,} = useFiles();
4141
const navigate = useNavigate();
42-
// const [templates, setTemplates] = useState<Template[]>([]);
43-
const templates = useQuery(
42+
const [templates, setTemplates] = useState<Template[]>([]);
43+
const _templates = useQuery(
4444
{
4545
queryKey: ['templates'],
4646
queryFn: TemplateAPI.getTemplates
@@ -49,6 +49,49 @@ export const ExtractUploadFile: React.FC<ExtractUploadFileProps> = ({
4949
const [isUploadComplete, setIsUploadComplete] = useState<boolean>(false);
5050
const [uploadedFile, setUploadedFile] = useState<File[]>([]);
5151

52+
const loadTemplatesTestData = () => {
53+
const sampleTemplates: Template[] = [
54+
{
55+
name: "Test Template COVID",
56+
description: "This is the first sample template.",
57+
pages: [
58+
{
59+
image: "base64encodedimage1",
60+
fieldNames: ["patient_name", "patient_dob"],
61+
},
62+
],
63+
},
64+
{
65+
name: "Test Template Syph",
66+
description: "This is the second sample template.",
67+
pages: [
68+
{
69+
image: "base64encodedimage2",
70+
fieldNames: ["patient_name", "address"],
71+
},
72+
],
73+
},
74+
];
75+
setTemplates(sampleTemplates);
76+
};
77+
78+
79+
useEffect(() => {
80+
// Load templates from local storage, and if none are found, load test data
81+
const loadTemplatesFromLocalStorage = () => {
82+
const storedTemplates = localStorage.getItem("templates");
83+
if (_templates.length > 0) {
84+
setTemplates(_templates);
85+
} else if (storedTemplates) {
86+
const parsedTemplates = JSON.parse(storedTemplates);
87+
setTemplates(parsedTemplates);
88+
} else {
89+
loadTemplatesTestData();
90+
}
91+
};
92+
loadTemplatesFromLocalStorage();
93+
94+
}, []);
5295
const simulateFileUpload = async(files: File[]) => {
5396
onUploadComplete(true);
5497
files.forEach(file => addFile(file));

frontend/src/components/TemplatesIndex/TemplatesIndex.tsx

+68-8
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,25 @@ export const TemplatesIndex: FC<TemplateIndexProps> = () => {
1919
}
2020
)
2121
useEffect(() => {
22-
const templatesJSON = localStorage.getItem("templates")
23-
setTemplates(JSON.parse(templatesJSON))
24-
}, []);
25-
22+
const getTemplates = async () => {
23+
const templatesJSON = localStorage.getItem("templates") || "[]"
24+
if (templateQuery.data && templateQuery.data?.length > 0) {
25+
setTemplates(templateQuery.data as [])
26+
} else if (templatesJSON) {
27+
setTemplates(JSON.parse(templatesJSON).map(template => ({ ...template, updatedAt: template.lastUpdated })))
28+
} else {
29+
setTemplates([])
30+
}
31+
}
32+
getTemplates();
33+
}, [templateQuery.data]);
34+
2635
useEffect(() => {
2736

2837
const localStorageEvent = (event) => {
2938
if (event.storageArea === localStorage) {
3039
const templatesJSON = localStorage.getItem("templates")
31-
setTemplates(JSON.parse(templatesJSON))
40+
setTemplates(JSON.parse(templatesJSON || '[]'))
3241
}
3342
}
3443
window.addEventListener("storage", localStorageEvent, false)
@@ -42,6 +51,8 @@ export const TemplatesIndex: FC<TemplateIndexProps> = () => {
4251
const templateColumnNames = {
4352
'name': 'Name',
4453
'labName': 'Lab',
54+
'lab': 'Lab',
55+
'createdBy': 'Creator',
4556
'status': 'Status',
4657
'updatedAt': 'Updated On'
4758
}
@@ -55,15 +66,64 @@ export const TemplatesIndex: FC<TemplateIndexProps> = () => {
5566
}
5667
return new Date(date).toLocaleDateString()
5768

69+
},
70+
'lastUpdated': (d) => {
71+
const date = Date.parse(d)
72+
if (isNaN(date)) {
73+
return new Date().toLocaleDateString()
74+
}
75+
return new Date(date).toLocaleDateString()
76+
5877
}
5978
}
6079

6180
const templateColumns = [
62-
'name', 'updatedAt', 'createdBy', 'labName', 'status'
81+
'name', 'updatedAt', 'createdBy', 'lab', 'status', 'labName'
6382
]
6483

84+
useEffect(() => {
85+
console.debug(`
86+
The following methods have been added to the window:
87+
88+
LoadNiceTemplates - this will load some pre-formatted templates that display nicely
89+
SaveTemplates - this will save the current templates to 'oldTemplates'
90+
LoadSavedTemplates - this will load the templates saved in 'oldTemplates'
91+
ClearTemplates - this will delete the current templates
92+
93+
`)
94+
window.LoadNiceTemplates = () => {
95+
const oldTemplates = localStorage.getItem('templates')
96+
localStorage.setItem('oldTemplates', oldTemplates)
97+
localStorage.setItem('templates', JSON.stringify(templates2))
98+
setTemplates(templates2)
99+
}
100+
window.SaveTemplates = () => {
101+
const oldTemplates = localStorage.getItem('templates')
102+
if (!oldTemplates) {
103+
return
104+
}
105+
localStorage.setItem('oldTemplates', oldTemplates)
106+
}
107+
window.ClearTemplates = () => {
108+
localStorage.removeItem('templates')
109+
setTemplates([])
110+
}
111+
window.LoadSavedTemplates = () => {
112+
const oldTemplates = localStorage.getItem('oldTemplates')
113+
if (oldTemplates) {
114+
localStorage.setItem('templates', oldTemplates)
115+
setTemplates(JSON.parse(oldTemplates))
116+
}
117+
}
118+
return () => {
119+
delete window.LoadNiceTemplates
120+
delete window.SaveTemplates
121+
delete window.ClearTemplates
122+
delete window.LoadSavedTemplates
123+
}
124+
});
65125

66-
if (!templateQuery.data || templateQuery.data.length === 0) {
126+
if (!templates || templates.length === 0) {
67127
return (
68128
<><img
69129
className="display-block margin-left-auto margin-right-auto padding-top-8"
@@ -98,7 +158,7 @@ export const TemplatesIndex: FC<TemplateIndexProps> = () => {
98158
</div>
99159
<div className="padding-1 border-1px border-gray-5 bg-white">
100160
<h2>Saved Templates</h2>
101-
<SortableTable columns={templateColumns} data={templateQuery.data || []}
161+
<SortableTable columns={templateColumns} data={templates}
102162
formatters={templateColumnFormatters}
103163
columnNames={templateColumnNames}
104164
/>

0 commit comments

Comments
 (0)