Task 3: Backend

This commit is contained in:
RobinB27
2025-11-16 14:45:18 +01:00
parent 3a61b72642
commit f65cf176f9
9 changed files with 6492 additions and 34 deletions

View File

@@ -1,9 +1,9 @@
import "./App.css"; import "./css/App.css";
import '@fortawesome/fontawesome-free/css/all.css'; import '@fortawesome/fontawesome-free/css/all.css';
import { readCSV } from './csv'; import { readCSV } from './csv';
import { CSV_Data, fileInfo } from './types'; import { CSV_Data, fileInfo } from './types';
import React, { useState } from "react"; import React, { useState, useRef } from "react";
import Layout from "./Layout"; import Layout from "./Layout";
import CSVCard from "./components/CSVCard"; import CSVCard from "./components/CSVCard";
import InfoCard from "./components/InfoCard"; import InfoCard from "./components/InfoCard";
@@ -18,6 +18,8 @@ function App() {
rowcount: 0 rowcount: 0
}); });
const formRef = useRef(null);
// This is triggered in CSVCard // This is triggered in CSVCard
const handleFileChange = async (e: React.ChangeEvent<HTMLInputElement>): Promise<void> => { const handleFileChange = async (e: React.ChangeEvent<HTMLInputElement>): Promise<void> => {
const file = e.target.files?.[0]; const file = e.target.files?.[0];
@@ -33,11 +35,22 @@ function App() {
} }
setInfo(newFileInfo); setInfo(newFileInfo);
setData(data); setData(data);
if (formRef.current) {
// Upload to server
const formData = new FormData(formRef.current);
const res = await fetch("/upload", {
method: "POST",
body: formData
});
const result = await res.json();
console.log(result);
}
} }
return ( return (
<Layout> <Layout>
<CSVCard handleChange={handleFileChange}></CSVCard> <CSVCard handleChange={handleFileChange} formRef={formRef}></CSVCard>
<InfoCard info={info}></InfoCard> <InfoCard info={info}></InfoCard>
<DataTable data={data}></DataTable> <DataTable data={data}></DataTable>
</Layout> </Layout>

View File

@@ -1,5 +1,5 @@
import React from "react"; import React from "react";
import "./Layout.css"; import "./css/Layout.css";
const Layout = (props: { children: React.ReactNode }) => { const Layout = (props: { children: React.ReactNode }) => {
return ( return (

View File

@@ -1,19 +1,20 @@
import React from "react"; import React from "react";
import "../Layout.css"; import "../css/Layout.css";
const CSVCard = (props: { const CSVCard = (props: {
handleChange: (e: React.ChangeEvent<HTMLInputElement>) => void handleChange: (e: React.ChangeEvent<HTMLInputElement>) => void,
formRef: React.RefObject<HTMLFormElement>
}) => { }) => {
return ( return (
<article> <article>
<header> <header>
<h2>Select CSV data</h2> <h2>Select CSV data</h2>
</header> </header>
<form> <form ref={props.formRef} action="/upload" method="post" encType="multipart/form-data" >
<label htmlFor="file-input" className="custom-file-upload"> <label htmlFor="file-input" className="custom-file-upload">
<i className="fa fa-file-csv"></i> Select CSV file to explore <i className="fa fa-file-csv"></i> Select CSV file to explore
</label> </label>
<input id="file-input" type="file" aria-describedby="fileHelp" accept="text/csv" onChange={props.handleChange}/> <input id="file-input" type="file" name="dataFile" aria-describedby="fileHelp" accept="text/csv" onChange={props.handleChange}/>
<small>Please upload a CSV file, where the first row is the header.</small> <small>Please upload a CSV file, where the first row is the header.</small>
</form> </form>
</article> </article>

View File

@@ -35,30 +35,37 @@ const DataTable = (props: {data: CSV_Data}) => {
} }
return ( return (
<table id="table-content"> <article className="table-container">
<thead> <header>
<tr> <h2>Data table</h2>
{ </header>
header.map( (col) => ( <div className="table-scroll-wrapper">
<ColHeader col={col} sortingHandle={sortingHandler} isSelected={col == sortCol} sortType={sortType}></ColHeader> <table id="table-content">
)) <thead>
} <tr>
</tr> {
</thead> header.map( (col) => (
<tbody> <ColHeader col={col} sortingHandle={sortingHandler} isSelected={col == sortCol} sortType={sortType}></ColHeader>
{ ))
props.data.map( (row, i) => ( }
<tr key={i}>
{
header.map( (col) => (
<Row col={col} content={row[col] as String}></Row>
))
}
</tr> </tr>
)) </thead>
} <tbody>
</tbody> {
</table> props.data.map( (row, i) => (
<tr key={i}>
{
header.map( (col) => (
<Row col={col} content={row[col] as String}></Row>
))
}
</tr>
))
}
</tbody>
</table>
</div>
</article>
) )
} }

View File

@@ -1,4 +1,3 @@
import "../Layout.css";
import { fileInfo } from "../types"; import { fileInfo } from "../types";
const InfoCard = (props: { const InfoCard = (props: {

View File

@@ -1,10 +1,27 @@
import express from "express"; import express from "express";
import ViteExpress from "vite-express"; import ViteExpress from "vite-express";
import multer from "multer";
// creates the expres app do not change
const app = express(); const app = express();
// add your routes here const storage = multer.diskStorage({
destination: "./src/server/uploads",
filename: (_req, file, cb) => {
// Suggested in Multer's readme
const uniqueSuffix = Date.now() + "-" + Math.round(Math.random() * 1E9);
cb(null, file.fieldname + "-" + uniqueSuffix)
}
});
const upload = multer({ storage: storage });
app.post(
"/upload",
upload.single("dataFile"),
(req, res, next) => {
console.log(req, res, next)
}
);
// example route which returns a message // example route which returns a message
app.get("/hello", async function (_req, res) { app.get("/hello", async function (_req, res) {

File diff suppressed because it is too large Load Diff