From e2c2ce4f671b17deb887e32f483c8650cb0ad6c1 Mon Sep 17 00:00:00 2001
From: knguyenrise8 <159168836+knguyenrise8@users.noreply.github.com>
Date: Fri, 29 Nov 2024 15:29:40 -0600
Subject: [PATCH] feat(RV-420): Add home page first time UX (#444)
---
frontend/src/App.scss | 53 +++++++++++++++++++
frontend/src/App.test.tsx | 89 +++++++++++++++++++++++++++++++
frontend/src/App.tsx | 94 +++++++++++++++++++++++++--------
frontend/src/assets/comment.svg | 19 +++++++
frontend/src/assets/csv.svg | 13 +++++
5 files changed, 247 insertions(+), 21 deletions(-)
create mode 100644 frontend/src/App.test.tsx
create mode 100644 frontend/src/assets/comment.svg
create mode 100644 frontend/src/assets/csv.svg
diff --git a/frontend/src/App.scss b/frontend/src/App.scss
index 68a99ec5..5aaf2db9 100644
--- a/frontend/src/App.scss
+++ b/frontend/src/App.scss
@@ -25,3 +25,56 @@
.nav-link {
text-decoration: none;
}
+
+.home-header {
+ height: 80px;
+ display: flex;
+ align-items: center;
+ border-bottom: 1px solid rgba(0, 94, 162, 0.14);
+ box-shadow: 0px 2px 4px 0px rgba(0, 94, 162, 0.04);
+}
+
+.first-time-content {
+ padding-inline: 116px;
+ width: 800px;
+ height: 700px;
+ background: white;
+ border: 1px solid #F0F0F0 !important;
+}
+
+.first-time-header {
+ color: #000;
+ font-size: 22px;
+ font-style: normal;
+ font-weight: 700;
+ line-height: normal;
+}
+
+.first-time-copy {
+ color: #000;
+ font-size: 16px;
+ font-style: normal;
+ font-weight: 400;
+ line-height: 162%;
+}
+
+.first-time-card {
+ border-radius: 5px;
+ border: 2px solid #F0F0F0;
+ background: #009EC1;
+ height: 300px;
+ width: 550px;
+}
+
+.img-copy {
+ color: #FFF;
+ font-size: 22px;
+ font-style: normal;
+ font-weight: 700;
+ line-height: normal;
+}
+
+.card-column-image {
+ padding-left: 36px;
+ width: 200px;
+}
\ No newline at end of file
diff --git a/frontend/src/App.test.tsx b/frontend/src/App.test.tsx
new file mode 100644
index 00000000..7eb9e153
--- /dev/null
+++ b/frontend/src/App.test.tsx
@@ -0,0 +1,89 @@
+import { render, screen, fireEvent } from '@testing-library/react';
+import '@testing-library/jest-dom';
+import { describe, it, expect, vi, beforeEach } from 'vitest';
+import { BrowserRouter } from 'react-router-dom';
+import App from './App';
+
+// Mock the imports
+vi.mock('./components/AppHeader/AppHeader.tsx', () => ({
+ AppHeader: () =>
AppHeader
,
+}));
+
+vi.mock('./components/TemplatesIndex/TemplatesIndex.tsx', () => ({
+ TemplatesIndex: () => TemplatesIndex
,
+}));
+
+vi.mock('./assets/comment.svg', () => ({
+ default: 'comment.svg',
+}));
+
+vi.mock('./assets/csv.svg', () => ({
+ default: 'csv.svg',
+}));
+
+describe('App component', () => {
+ beforeEach(() => {
+ localStorage.clear();
+ });
+
+ const renderComponent = () => {
+ render(
+
+
+
+ );
+ };
+
+ it('renders the AppHeader component', () => {
+ renderComponent();
+ expect(screen.getByTestId('app-header')).toBeInTheDocument();
+ });
+
+ it('displays first-time experience content on initial visit', () => {
+ renderComponent();
+ expect(screen.getByTestId('first-time-exp')).toBeInTheDocument();
+ });
+
+ it('displays regular content on subsequent visits', () => {
+ localStorage.setItem('hasVisited', 'true');
+ renderComponent();
+ expect(screen.queryByTestId('first-time-exp')).not.toBeInTheDocument();
+ expect(screen.getByTestId('templates-index')).toBeInTheDocument();
+ });
+
+ it('navigates to the correct URL when a navigation link is clicked (0)', () => {
+ renderComponent();
+ const navLink = screen.getByTestId('nav-link-0');
+ fireEvent.click(navLink);
+ expect(navLink).toHaveAttribute('href', '/');
+ });
+
+ it('navigates to the correct URL when a navigation link is clicked (1)', () => {
+ renderComponent();
+ const navLink = screen.getByTestId('nav-link-1');
+ fireEvent.click(navLink);
+ expect(navLink).toHaveAttribute('href', '/labels');
+ });
+
+ it('navigates to the correct URL when a navigation link is clicked (2)', () => {
+ renderComponent();
+ const navLink = screen.getByTestId('nav-link-2');
+ fireEvent.click(navLink);
+ expect(navLink).toHaveAttribute('href', '/dashboard');
+ });
+
+ it('navigates to the new template upload page when the button is clicked', () => {
+ renderComponent();
+ const newTemplateButton = screen.getByTestId('new-template-button');
+ fireEvent.click(newTemplateButton);
+ expect(window.location.pathname).toBe('/new-template/upload');
+ });
+
+ it('renders navigation links correctly', () => {
+ renderComponent();
+ const navLinks = ['Annotate and Extract', 'Label Management', 'Dashboard'];
+ navLinks.forEach((_, idx) => {
+ expect(screen.getByTestId(`nav-link-${idx}`)).toBeInTheDocument();
+ });
+ });
+});
\ No newline at end of file
diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
index 219898e9..32201b91 100644
--- a/frontend/src/App.tsx
+++ b/frontend/src/App.tsx
@@ -1,13 +1,27 @@
-import "./App.scss";
-import { Link } from "@trussworks/react-uswds";
-import { useLocation } from "react-router-dom";
+import { useState, useEffect } from "react";
+import { Button, Link } from "@trussworks/react-uswds";
+import { useLocation, useNavigate } from "react-router-dom";
import { AppHeader } from "./components/AppHeader/AppHeader.tsx";
-
import { TemplatesIndex } from "./components/TemplatesIndex/TemplatesIndex.tsx";
+import Comment from './assets/comment.svg';
+import CSV from './assets/csv.svg';
+import "./App.scss";
+
function App() {
const { pathname } = useLocation();
+ const navigate = useNavigate();
+ const [isFirstVisit, setIsFirstVisit] = useState(false);
+
+ useEffect(() => {
+ const hasVisited = localStorage.getItem("hasVisited");
+ if (!hasVisited) {
+ setIsFirstVisit(true);
+ localStorage.setItem("hasVisited", "true");
+ }
+ }, []);
+
const navLinks = [
{ text: "Annotate and Extract", url: "/" },
{
@@ -19,10 +33,10 @@ function App() {
return (
<>
-
+
-
-
+
+
{navLinks.map((i, idx) => {
return (
@@ -30,6 +44,7 @@ function App() {
key={idx}
href={i.url}
className={`border-left-2px padding-left-1 padding-top-2 padding-bottom-2 nav-link ${i.url === pathname ? "text-bold" : "border-primary-dark"}`}
+ data-testid={`nav-link-${idx}`}
>
{i.text}
@@ -37,19 +52,56 @@ function App() {
})}
-
-
-
Annotate and Extract
-
-
- Welcome Blake,
-
-
- Extract data from any PDFs, or images to send to your
- surveillance systems using data from your saved templates or
- create new segmentations.
-
-
+
+
Annotate and Extract
+
+ {isFirstVisit ? (
+
+
+ Welcome to Report Vision
+
+
+ Let ReportVision take your lab reports from faxes and PDF’s and extract the data for seamless ingestion into your surveillance system.
+
+
+
+
+
+
+
+
+ 1. Create templates for new lab reports.
+
+
+ 2. Extract data and download it.
+
+
+
+
+ Let’s create your first annotated template for a lab report to enable quick extractions.
+
+
+
+ ) : (
+
+
+ Welcome Blake,
+
+
+ Extract data from any PDFs, or images to send to your
+ surveillance systems using data from your saved templates or
+ create new segmentations.
+
+
+
+ )}
@@ -58,4 +110,4 @@ function App() {
);
}
-export default App;
+export default App;
\ No newline at end of file
diff --git a/frontend/src/assets/comment.svg b/frontend/src/assets/comment.svg
new file mode 100644
index 00000000..ca942b77
--- /dev/null
+++ b/frontend/src/assets/comment.svg
@@ -0,0 +1,19 @@
+
diff --git a/frontend/src/assets/csv.svg b/frontend/src/assets/csv.svg
new file mode 100644
index 00000000..14ea28b9
--- /dev/null
+++ b/frontend/src/assets/csv.svg
@@ -0,0 +1,13 @@
+