Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integrate Axios for Handling API Calls #28

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 21 additions & 21 deletions frontend/.gitignore
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

node_modules
dist
dist-ssr
*.local
# dependencies
/node_modules
/.pnp
.pnp.js

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
# testing
/coverage

# production
/build

# misc
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
.env.local
.env.development.local
.env.test.local
.env.production.local
.idea

npm-debug.log*
yarn-debug.log*
yarn-error.log*
5 changes: 5 additions & 0 deletions frontend/eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ export default tseslint.config(
'warn',
{ allowConstantExport: true },
],
'no-unused-vars': 'off',
'@typescript-eslint/no-unused-vars': [
'warn',
{ vars: 'all', args: 'after-used', ignoreRestSiblings: true },
],
},
},
)
4 changes: 3 additions & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,18 @@
"preview": "vite preview"
},
"dependencies": {
"@types/axios": "^0.14.0",
"axios": "^1.7.7",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-router-dom": "^6.26.1"
},
"devDependencies": {
"@types/react-router-dom": "^5.3.3",
"@ant-design/icons": "^5.4.0",
"@eslint/js": "^9.9.0",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@types/react-router-dom": "^5.3.3",
"@vitejs/plugin-react": "^4.3.1",
"antd": "^5.20.5",
"eslint": "^9.9.0",
Expand Down
38 changes: 38 additions & 0 deletions frontend/src/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import axios from 'axios';
import { QualityControlData } from './interfaces';
const API_URL = import.meta.env.API_URL;


const axiosGET = (endPoint: string, token?: string) => {
const headers = token ? { Authorization: `Bearer ${token}` } : {};
return axios
.get(endPoint, { headers })
.then((response) => response.data)
.catch((error) => {
console.error('GET error:', error);
});
};


const axiosPOST = (endPoint: string, data: unknown) => {
return axios
.post(endPoint, data)
.then((response) => response.data)
.catch((error) => {
console.error('POST error:', error);
});
};


export const getQualityControl = async (): Promise<QualityControlData[]> => {
const endPoint = `${API_URL}/quality_control`;
return axiosGET(endPoint);
};


export const postCall = async (data: string): Promise<unknown> => {
const endPoint = `${API_URL}/post_call`;
return axiosPOST(endPoint, data);
};


12 changes: 9 additions & 3 deletions frontend/src/appLayout/AppLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,15 @@ const { Header, Content, Footer } = Layout;
const appMenuItems = () => [
{
key: '/',
label: <Link to="/">Home</Link>,
label: <Link to="/">Quality Control</Link>,
},
{
key: '/about',
label: <Link to="/about">About</Link>,
key: '/classification-results',
label: <Link to="/classification-results">Classification Results</Link>,
},
{
key: '/metadata',
label: <Link to="/metadata">Metadata</Link>,
}
];

Expand All @@ -23,7 +27,9 @@ export const AppLayout = () => {
<Layout>
<Header className="header">
<div className="logo">
<Link to="/">
<img src="/meta-vis-logo.svg" alt="logo" />
</Link>
</div>
<Menu
theme="dark"
Expand Down
15 changes: 15 additions & 0 deletions frontend/src/interfaces.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export interface QualityControlData {
key: string;
totalReads: number;
totalReadsAfterFiltering: number;
humanMappedReads: number;
humanKrakenReads: number;
totalHumanReadsPercent: string;
bacterialReadsPercent: string;
virusReadsPercent: string;
eukaryoteReadsPercent: string;
otherReadsPercent: string;
unclassifiedReadsPercent: string;
spikeReadsPercent: string;
spike: string;
}
5 changes: 0 additions & 5 deletions frontend/src/pages/About.tsx

This file was deleted.

9 changes: 9 additions & 0 deletions frontend/src/pages/ClassificationResults.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export const ClassificationResults = () => {
return (
<>
<h2>Visualization of classification results</h2>
<li>Individual Krona plots for each classifier.</li>
<li>Visualization of the number of reads per hit from different classifiers based on Taxpasta outputs.</li>
</>
)
}
5 changes: 0 additions & 5 deletions frontend/src/pages/Home.tsx

This file was deleted.

10 changes: 10 additions & 0 deletions frontend/src/pages/Metadata.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export const Metadata = () => {
return (
<>
<h2>Metadata</h2>
<li> A table including sample information, sample source, batch, ticket number, priority, accreditation, library preparation, CT values, wet-lab methods, Taxprofiler version, and software versions.</li>
<li>A brief description, a few sentences, of the taxprofiler pipeline analysis used to produce the results.</li>
<li> A summary table of tickets, sample information, key QC metrics, pass or fail status (?), and the number of reads assigned to viruses.</li>
</>
)
}
177 changes: 177 additions & 0 deletions frontend/src/pages/QualityControl.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
import { useEffect, useState } from "react";
import { Button, message, notification, Table } from "antd"
import { DownloadOutlined } from '@ant-design/icons';
import { QualityControlData } from "../interfaces";
import { getQualityControl } from "../api";


export const QualityControl: React.FC = () => {
const [qualityControlData, setQualityControlData] = useState<QualityControlData[]>([]);

useEffect(() => {
getQualityControl()
.then((data) => {
if (Array.isArray(data)) {
setQualityControlData(data);
} else {
notification.error({
message: "Data Error",
description: "Invalid data format from API",
});
}
})
.catch(() => {
notification.error({
message: "Fetch Error",
description: "Failed to fetch quality control data",
});
});
}, []);

const mockData: QualityControlData[] = [
{
key: '1',
totalReads: 180000,
totalReadsAfterFiltering: 175000,
humanMappedReads: 85000,
humanKrakenReads: 5000,
totalHumanReadsPercent: '48.57%',
bacterialReadsPercent: '22.86%',
virusReadsPercent: '1.71%',
eukaryoteReadsPercent: '4%',
otherReadsPercent: '1.71%',
unclassifiedReadsPercent: '4%',
spikeReadsPercent: '2.29%',
spike: 'Emesvirus zinderi',
},
{
key: '2',
totalReads: 150000,
totalReadsAfterFiltering: 140000,
humanMappedReads: 60000,
humanKrakenReads: 4000,
totalHumanReadsPercent: '45.71%',
bacterialReadsPercent: '25.71%',
virusReadsPercent: '2.14%',
eukaryoteReadsPercent: '5%',
otherReadsPercent: '2.14%',
unclassifiedReadsPercent: '2.86%',
spikeReadsPercent: '1.43%',
spike: 'Emesvirus zinderi',
},
{
key: '3',
totalReads: 200000,
totalReadsAfterFiltering: 190000,
humanMappedReads: 90000,
humanKrakenReads: 7000,
totalHumanReadsPercent: '50.53%',
bacterialReadsPercent: '26.32%',
virusReadsPercent: '3.16%',
eukaryoteReadsPercent: '4.74%',
otherReadsPercent: '1.58%',
unclassifiedReadsPercent: '2.11%',
spikeReadsPercent: '1.58%',
spike: 'Emesvirus zinderi',
},
{
key: '4',
totalReads: 160000,
totalReadsAfterFiltering: 155000,
humanMappedReads: 70000,
humanKrakenReads: 4000,
totalHumanReadsPercent: '47.74%',
bacterialReadsPercent: '22.58%',
virusReadsPercent: '1.94%',
eukaryoteReadsPercent: '3.87%',
otherReadsPercent: '1.94%',
unclassifiedReadsPercent: '3.23%',
spikeReadsPercent: '2.58%',
spike: 'Emesvirus zinderi',
},
];



const columns = [
{
title: 'Total Reads',
dataIndex: 'totalReads',
key: 'totalReads',
},
{
title: 'Total Reads After Filtering',
dataIndex: 'totalReadsAfterFiltering',
key: 'totalReadsAfterFiltering',
},
{
title: 'Human Mapped Reads',
dataIndex: 'humanMappedReads',
key: 'humanMappedReads',
},
{
title: 'Human Kraken Reads',
dataIndex: 'humanKrakenReads',
key: 'humanKrakenReads',
},
{
title: 'Total Human Reads',
dataIndex: 'totalHumanReadsPercent',
key: 'totalHumanReadsPercent',
},
{
title: 'Bacterial Reads',
dataIndex: 'bacterialReadsPercent',
key: 'bacterialReadsPercent',
},
{
title: 'Virus Reads',
dataIndex: 'virusReadsPercent',
key: 'virusReadsPercent',
},
{
title: 'Eukaryote Reads',
dataIndex: 'eukaryoteReadsPercent',
key: 'eukaryoteReadsPercent',
},
{
title: 'Other Reads',
dataIndex: 'otherReadsPercent',
key: 'otherReadsPercent',
},
{
title: 'Unclassified Reads',
dataIndex: 'unclassifiedReadsPercent',
key: 'unclassifiedReadsPercent',
},
{
title: 'Spike Reads',
dataIndex: 'spikeReadsPercent',
key: 'spikeReadsPercent',
},
{
title: 'Spike Used',
dataIndex: 'spike',
key: 'spike',
},
{
title: 'Action',
key: 'action',
render: () => (
<Button icon={<DownloadOutlined />} onClick={() => message.success('Download')} />
),
},
];





return (
<Table<QualityControlData>
dataSource={qualityControlData.length ? qualityControlData: mockData}
columns={columns}
bordered
/>
)
}
10 changes: 6 additions & 4 deletions frontend/src/routes/AppRouter.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import { createBrowserRouter, createRoutesFromElements, Route, RouterProvider } from 'react-router-dom';
import { Home } from '../pages/Home';
import { About } from '../pages/About';
import { QualityControl } from '../pages/QualityControl';
import { ClassificationResults } from '../pages/ClassificationResults';
import { AppLayout } from '../appLayout/AppLayout';
import { Metadata } from '../pages/Metadata';


const router = createBrowserRouter(
createRoutesFromElements(
<Route element={<AppLayout />}>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/" element={<QualityControl />} />
<Route path="/classification-results" element={<ClassificationResults />} />
<Route path="/metadata" element={<Metadata />} />
<Route path="*" element={<div>404 Not Found</div>} />
</Route>
)
Expand Down
Loading