diff --git a/.env b/.env
index e43d662..ea302a4 100644
--- a/.env
+++ b/.env
@@ -1 +1 @@
-VITE_API_URL=api-url:port
\ No newline at end of file
+VITE_API_URL=api-url:port
diff --git a/package-lock.json b/package-lock.json
index c56c088..efd10e2 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -15,7 +15,7 @@
"@mantine/hooks": "^7.13.1",
"@tabler/icons-react": "3.17.0",
"dayjs": "^1.11.13",
- "nuqs": "^2.2.1",
+ "nuqs": "^2.2.3",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-router-dom": "^6.27.0",
@@ -4794,10 +4794,11 @@
}
},
"node_modules/express": {
- "version": "4.21.1",
- "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz",
- "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==",
+ "version": "4.21.2",
+ "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
+ "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"accepts": "~1.3.8",
"array-flatten": "1.1.1",
@@ -4818,7 +4819,7 @@
"methods": "~1.1.2",
"on-finished": "2.4.1",
"parseurl": "~1.3.3",
- "path-to-regexp": "0.1.10",
+ "path-to-regexp": "0.1.12",
"proxy-addr": "~2.0.7",
"qs": "6.13.0",
"range-parser": "~1.2.1",
@@ -4833,6 +4834,10 @@
},
"engines": {
"node": ">= 0.10.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
}
},
"node_modules/express/node_modules/debug": {
@@ -6416,9 +6421,9 @@
"license": "MIT"
},
"node_modules/nanoid": {
- "version": "3.3.7",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
- "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
+ "version": "3.3.8",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz",
+ "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==",
"dev": true,
"funding": [
{
@@ -6488,9 +6493,9 @@
}
},
"node_modules/nuqs": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/nuqs/-/nuqs-2.2.1.tgz",
- "integrity": "sha512-P0swZtg6k9LRXzlT9gQAFtQg25Edan2Evt5vwyGvh00Z+rHH19gb2ZajU/E/SfhwE858aM/jb+wJziWbbwSlcg==",
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/nuqs/-/nuqs-2.2.3.tgz",
+ "integrity": "sha512-nMCcUW06KSqEXA0xp+LiRqDpIE59BVYbjZLe0HUisJAlswfihHYSsAjYTzV0lcE1thfh8uh+LqUHGdQ8qq8rfA==",
"license": "MIT",
"dependencies": {
"mitt": "^3.0.1"
@@ -6713,9 +6718,9 @@
"license": "MIT"
},
"node_modules/path-to-regexp": {
- "version": "0.1.10",
- "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz",
- "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==",
+ "version": "0.1.12",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
+ "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==",
"dev": true,
"license": "MIT"
},
diff --git a/package.json b/package.json
index 40add0d..27e0a47 100644
--- a/package.json
+++ b/package.json
@@ -21,7 +21,7 @@
"@mantine/hooks": "^7.13.1",
"@tabler/icons-react": "3.17.0",
"dayjs": "^1.11.13",
- "nuqs": "^2.2.1",
+ "nuqs": "^2.2.3",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-router-dom": "^6.27.0",
diff --git a/src/components/DataTable.tsx b/src/components/DataTable.tsx
index ad11740..2da86b2 100644
--- a/src/components/DataTable.tsx
+++ b/src/components/DataTable.tsx
@@ -1,5 +1,6 @@
import { Table } from "@mantine/core";
import { useMantineTheme } from "@mantine/core";
+import { Input, Textarea } from "@mantine/core";
interface DataTableProps {
data?: MCAPFileInformation[];
@@ -17,7 +18,7 @@ export default function DataTable({
setSelectedData,
}: DataTableProps) {
const theme = useMantineTheme();
-
+
const setPreviewData = (file: MCAPFileInformation) => {
if (selectedRow === file.id) {
setSelectedRow("");
@@ -30,8 +31,10 @@ export default function DataTable({
// Take out when API server team implements filename id in their get route
const getFileNameWithoutExtension = (fileNameWithExtension: string) => {
- const lastDotIndex = fileNameWithExtension.lastIndexOf('.');
- return lastDotIndex !== -1 ? fileNameWithExtension.slice(0, lastDotIndex) : fileNameWithExtension;
+ const lastDotIndex = fileNameWithExtension.lastIndexOf(".");
+ return lastDotIndex !== -1
+ ? fileNameWithExtension.slice(0, lastDotIndex)
+ : fileNameWithExtension;
};
const rows = !data ? (
@@ -51,18 +54,38 @@ export default function DataTable({
setPreviewData(file)}
-
bg={selectedRow === file.id ? theme.primaryColor : ""}
>
- {getFileNameWithoutExtension(file.mcap_files[0].file_name)}
+
+ {getFileNameWithoutExtension(file.mcap_files[0].file_name)}
+
{file.date}
{file.location}
- {file.notes}
+
+ {/* Change back to notes once notes field is implemented in the server */}
+ {/* {file.car_model} */}
+
+
+
+
+
))
);
return (
-
+
0}
@@ -70,10 +93,10 @@ export default function DataTable({
>
- Name
+ Name
Date
Location
- Notes
+ Notes
{rows}
diff --git a/src/components/FileUpload.tsx b/src/components/FileUpload.tsx
index 2eef877..f6ccbe1 100644
--- a/src/components/FileUpload.tsx
+++ b/src/components/FileUpload.tsx
@@ -1,6 +1,6 @@
-import React, { useState } from 'react';
-import { Modal, Button, Notification, FileInput } from '@mantine/core';
-import '@/css/FileUpload.css';
+import React, { useState } from "react";
+import { Modal, Button, Notification, FileInput } from "@mantine/core";
+import "@/css/FileUpload.css";
interface FileUploadProps {
uploadUrl: string;
@@ -25,6 +25,24 @@ const FileUpload: React.FC = ({ uploadUrl }) => {
setError(null);
setSuccess(null)
if (selectedFiles.length > 0) {
+ const formData = new FormData();
+ selectedFiles.forEach((file) => {
+ formData.append("files", file);
+ });
+
+ try {
+ const response = await fetch(uploadUrl, {
+ method: "POST",
+ body: formData,
+ });
+
+ if (!response.ok) {
+ setError("Upload failed. Network response was not ok.");
+ return;
+ }
+
+ const data = await response.json();
+ console.log("Upload successful:", data);
try {
const formData = new FormData();
selectedFiles.forEach(file => {
@@ -57,11 +75,12 @@ const FileUpload: React.FC = ({ uploadUrl }) => {
setSelectedFiles([]);
} catch (error) {
- console.error('Upload failed:', error);
- setError('An error occurred while uploading. Please try again.');
+ console.error("Upload failed:", error);
+ setError("An error occurred while uploading. Please try again.");
}
- } else {
- setError('Please select files to upload.');
+ } catch (error) {
+ setError("Please select files to upload.");
+ }
}
setLoading(false);
};
@@ -86,7 +105,7 @@ const FileUpload: React.FC = ({ uploadUrl }) => {
onClose={handleClose}
title="Select files to upload"
centered
- style={{ textAlign: "center" }}
+ style={{ textAlign: "center" }}
>
= ({ uploadUrl }) => {
onChange={handleFileChange}
placeholder="Select files to upload"
label="Choose files"
- style={{ display: 'block', margin: '0 auto' }}
+ style={{ display: "block", margin: "0 auto" }}
/>
{selectedFiles.length > 0 && (
@@ -117,7 +136,11 @@ const FileUpload: React.FC
= ({ uploadUrl }) => {
)}
{error && (
- setError(null)} style={{ marginTop: 10 }}>
+ setError(null)}
+ style={{ marginTop: 10 }}
+ >
{error}
)}
diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx
index 54d8346..065db25 100644
--- a/src/components/Navbar.tsx
+++ b/src/components/Navbar.tsx
@@ -1,7 +1,7 @@
import "@mantine/core/styles.css";
import "@/css/Navbar.css";
import { NavLink } from "react-router-dom";
-import FileUpload from "@/components/FileUpload"
+import FileUpload from "@/components/FileUpload";
const mainLinksData = [
{ name: "Files", url: "/" },
@@ -27,6 +27,7 @@ export default function Navbar() {
/>
{links}
+ {/* Once POST API is out -- Currently WIP */}
{/* Optionally render active link or other content here */}
diff --git a/src/components/PreviewCard.tsx b/src/components/PreviewCard.tsx
index f86df15..fae3f14 100644
--- a/src/components/PreviewCard.tsx
+++ b/src/components/PreviewCard.tsx
@@ -29,34 +29,39 @@ function PreviewCard({ selectedData }: PreviewCardProps) {
const [success, setSuccess] = useState(null);
const handleDelete = async () => {
- setLoading(true)
+ setLoading(true);
setError(null);
- setSuccess(null)
+ setSuccess(null);
try {
- const response = await fetch(`${import.meta.env.VITE_API_URL}/mcaps/${selectedData?.id}`, {
- method: 'DELETE',
- });
+ const response = await fetch(
+ `${import.meta.env.VITE_API_URL}/mcaps/${selectedData?.id}`,
+ {
+ method: "DELETE",
+ },
+ );
- if (!response.ok) {
- if (response.status === 503) {
- const errorMsg = await response.text();
- setError(`Failed to delete: ${errorMsg} \nTry again in a few minutes!`);
- } else {
- const errorMsg = await response.text();
- setError(`Failed to delete: ${errorMsg}`);
- }
+ if (!response.ok) {
+ if (response.status === 503) {
+ const errorMsg = await response.text();
+ setError(
+ `Failed to delete: ${errorMsg} \nTry again in a few minutes!`,
+ );
} else {
- const result = await response.json();
- setSuccess('File deleted successfully!');
- console.log('Delete successful:', result);
+ const errorMsg = await response.text();
+ setError(`Failed to delete: ${errorMsg}`);
}
+ } else {
+ const result = await response.json();
+ setSuccess("File deleted successfully!");
+ console.log("Delete successful:", result);
+ }
} catch (error) {
- console.error('Error sending Delete request:', error);
- setError('An error occurred during file deletion.');
+ console.error("Error sending Delete request:", error);
+ setError("An error occurred during file deletion.");
}
- setLoading(false)
- }
-
+ setLoading(false);
+ };
+
const formatDate = (dateString: string) => {
const date = new Date(dateString);
return date.toLocaleDateString("en-US", {
@@ -69,30 +74,30 @@ function PreviewCard({ selectedData }: PreviewCardProps) {
// Take out when API server team implements filename id in their get route
const getFileNameWithoutExtension = (fileNameWithExtension: string) => {
- const lastDotIndex = fileNameWithExtension.lastIndexOf('.');
- return lastDotIndex !== -1 ? fileNameWithExtension.slice(0, lastDotIndex) : fileNameWithExtension;
+ const lastDotIndex = fileNameWithExtension.lastIndexOf(".");
+ return lastDotIndex !== -1
+ ? fileNameWithExtension.slice(0, lastDotIndex)
+ : fileNameWithExtension;
};
- const imageUrl = selectedData?.content_files?.vn_lat_lon_plot?.[0]?.signed_url ?? "https://camo.githubusercontent.com/25de56138803873d9ea83567c55b9a022ad86d0acb53bb7c733bb038583e2279/68747470733a2f2f6d69726f2e6d656469756d2e636f6d2f76322f726573697a653a6669743a3430302f312a7241676c6b664c4c316676384a6363697a4a33572d512e706e67"; // Fallback to a default image if none exists.
-
+ const imageUrl =
+ selectedData?.content_files?.vn_lat_lon_plot?.[0]?.signed_url ??
+ "https://camo.githubusercontent.com/25de56138803873d9ea83567c55b9a022ad86d0acb53bb7c733bb038583e2279/68747470733a2f2f6d69726f2e6d656469756d2e636f6d2f76322f726573697a653a6669743a3430302f312a7241676c6b664c4c316676384a6363697a4a33572d512e706e67"; // Fallback to a default image if none exists.
+
const formatTime = (dateString: string) => {
const date = new Date(dateString);
return date.toLocaleTimeString("en-US", {
- hour: "2-digit",
- minute: "2-digit",
- second: "2-digit",
- hour12: true,
+ hour: "2-digit",
+ minute: "2-digit",
+ second: "2-digit",
+ hour12: true,
});
};
return (
-
+
+
+
+ run 2024-18-10.mcap
+
+
+
+
+ Date:{" "}
+
+ {/* Spacer */}
+
+ Fri, Oct 18, 2024
+
+
+
+
+ Time:{" "}
+
+ {/* Spacer */}
+
+ 12:24:02 PM
+
+
+
+
+ Location:{" "}
+
+ {/* Spacer */}
+
+ MRDC
+
+
+
+
+ Sensors:{" "}
+
+ {/* Spacer */}
+
+ aero_sensor_1
+
+
+
{selectedData ? (
<>
-
+
))}
-
+
{success && (
- setSuccess(null)} style={{ marginTop: 10 }}>
+ setSuccess(null)}
+ style={{ marginTop: 10 }}
+ >
{success}
)}
{error && (
- setError(null)} style={{ marginTop: 10 }}>
+ setError(null)}
+ style={{ marginTop: 10 }}
+ >
{error}
)}
-
>
) : (
@@ -234,7 +324,7 @@ export function DownloadButton({
{
handleSearch(e.target.value);
}}
/>
-
+
{" "}
{/* Scrollable area with height limit */}