From 7338b5e789a376ef6608657034cdf446109aaeec Mon Sep 17 00:00:00 2001 From: Chris Carlon Date: Sat, 18 Jan 2025 18:12:59 +0000 Subject: [PATCH] feat: upload v2 work [2025-01-18] --- gridwalk-backend/.env | 2 +- gridwalk-backend/src/routes/layer_v2.rs | 37 +++++++++++++------ .../[projectName]/components/mapClient.tsx | 29 ++++++++++++--- .../src/app/workspace/WorkspaceContext.tsx | 1 + .../app/workspace/createWorkspaceModal.tsx | 35 ++++++++++++++++-- ...london.geo.json => greater-london.geojson} | 0 6 files changed, 83 insertions(+), 21 deletions(-) rename test_files/{greater-london.geo.json => greater-london.geojson} (100%) diff --git a/gridwalk-backend/.env b/gridwalk-backend/.env index b939cc8..8a34e2a 100644 --- a/gridwalk-backend/.env +++ b/gridwalk-backend/.env @@ -4,6 +4,6 @@ GW_POSTGRES_HOST=localhost GW_POSTGRES_PORT=5432 GW_POSTGRES_DB=gridwalk GW_POSTGRES_USERNAME=admin -GW_POSTGRES_PASSWORD=password1 +GW_POSTGRES_PASSWORD=password GW_USER_EMAIL=hello@gridwalk.co GW_USER_PASSWORD=password diff --git a/gridwalk-backend/src/routes/layer_v2.rs b/gridwalk-backend/src/routes/layer_v2.rs index c228ce9..8bee3bd 100644 --- a/gridwalk-backend/src/routes/layer_v2.rs +++ b/gridwalk-backend/src/routes/layer_v2.rs @@ -20,6 +20,7 @@ use std::error::Error; #[derive(Debug)] enum FileType { Geopackage, + Json, GeoJson, Shapefile, Excel, @@ -459,26 +460,38 @@ fn match_magic_numbers(header: &[u8]) -> Option { } fn detect_content_based_type(buffer: &[u8]) -> Result> { - if let Ok(text) = std::str::from_utf8(buffer) { - let text_lower = text.trim_start().to_lowercase(); - - if text_lower.starts_with("{") - && text_lower.contains("\"type\"") - && (text_lower.contains("\"featurecollection\"") - || text_lower.contains("\"feature\"") - || text_lower.contains("\"geometry\"")) - { - return Ok(FileType::GeoJson); + // Check for GeoJSON magic numbers/patterns + if buffer.len() > 20 { + // Look for {"type":"Feature" pattern + if let Some(window) = buffer.windows(17).next() { + if window == b"{\"type\":\"Feature" { + return Ok(FileType::GeoJson); + } + } + + // Look for {"type":"FeatureCollection" pattern + if let Some(window) = buffer.windows(26).next() { + if window == b"{\"type\":\"FeatureCollection" { + return Ok(FileType::GeoJson); + } } - if is_valid_csv(text) { - return Ok(FileType::Csv); + // Generic JSON check - look for opening curly brace + if buffer[0] == b'{' { + return Ok(FileType::Json); } } + // Convert buffer to string for CSV check + if let Ok(text) = String::from_utf8(buffer.to_vec()) { + if is_valid_csv(&text) { + return Ok(FileType::Csv); + } + } Err("Unknown or unsupported file type".into()) } + fn is_valid_csv(content: &str) -> bool { let lines: Vec<&str> = content.lines().take(5).collect(); diff --git a/gridwalk-ui/src/app/project/[workspaceId]/[projectName]/components/mapClient.tsx b/gridwalk-ui/src/app/project/[workspaceId]/[projectName]/components/mapClient.tsx index 136b99d..972f20f 100644 --- a/gridwalk-ui/src/app/project/[workspaceId]/[projectName]/components/mapClient.tsx +++ b/gridwalk-ui/src/app/project/[workspaceId]/[projectName]/components/mapClient.tsx @@ -20,7 +20,6 @@ import { INITIAL_MAP_CONFIG, MapClientProps, LayerUpload, - SupportedFileTypes, FileHandlerResponse, } from "./types"; @@ -105,10 +104,30 @@ export function MapClient({ apiUrl }: MapClientProps) { return; } - const extension = fileToUpload.name - .split(".") - .pop() - ?.toLowerCase() as SupportedFileTypes; + const extension = fileToUpload.name.split(".").pop()?.toLowerCase(); + if (!extension) { + setUploadError("File must have an extension"); + return; + } + + // Explicitly check if the file type is supported before proceeding + const supportedTypes = [ + "gpkg", + "zip", + "xlsx", + "csv", + "parquet", + "json", + "geojson", + ]; + if (!supportedTypes.includes(extension)) { + setUploadError( + `Unsupported file type: ${extension}. Supported types are: ${supportedTypes.join( + ", " + )}` + ); + return; + } setIsUploading(true); setUploadError(null); diff --git a/gridwalk-ui/src/app/workspace/WorkspaceContext.tsx b/gridwalk-ui/src/app/workspace/WorkspaceContext.tsx index 2d652e4..1a8b151 100644 --- a/gridwalk-ui/src/app/workspace/WorkspaceContext.tsx +++ b/gridwalk-ui/src/app/workspace/WorkspaceContext.tsx @@ -6,6 +6,7 @@ type Workspace = { id: string; name: string; }; + type WorkspaceContextType = { workspaces: Workspace[]; }; diff --git a/gridwalk-ui/src/app/workspace/createWorkspaceModal.tsx b/gridwalk-ui/src/app/workspace/createWorkspaceModal.tsx index 16e8418..17fb767 100644 --- a/gridwalk-ui/src/app/workspace/createWorkspaceModal.tsx +++ b/gridwalk-ui/src/app/workspace/createWorkspaceModal.tsx @@ -12,7 +12,8 @@ import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Button } from "@/components/ui/button"; -// Sidebar for the initial workspace page - appears on righthand side of the main workspaces page +// Sidebar for the initial workspace page - +// It appears on righthand side of the main workspaces page interface CreateWorkspaceModalProps { isOpen: boolean; onClose: () => void; @@ -27,19 +28,45 @@ export function CreateWorkspaceModal({ // Define state const router = useRouter(); const [isCreating, setIsCreating] = useState(false); + const [error, setError] = useState(""); + const [success, setSuccess] = useState(""); const [workspaceName, setWorkspaceName] = useState(""); // Define what happens when creating new workspace submission const handleSubmit = async () => { - if (!workspaceName.trim()) return; + const trimmedName = workspaceName.trim(); + if (!trimmedName) { + setError("Workspace name cannot be empty"); + return; + } + if (trimmedName.length < 3) { + setError("Workspace name must be at least 3 characters long"); + return; + } try { setIsCreating(true); - await onCreate(workspaceName); + setError(""); + await onCreate(trimmedName); setWorkspaceName(""); + setSuccess("Workspace created successfully"); + + // Use Promise to handle the delay more cleanly + await new Promise((resolve) => setTimeout(resolve, 1500)); onClose(); router.refresh(); } catch (error) { + if (error instanceof Error) { + if (error.message.includes("duplicate")) { + setError("A workspace with this name already exists"); + } else if (error.message.includes("unauthorized")) { + setError("You don't have permission to create workspaces"); + } else { + setError(error.message); + } + } else { + setError("Failed to create workspace. Please try again."); + } console.error("Error creating workspace:", error); } finally { setIsCreating(false); @@ -66,6 +93,8 @@ export function CreateWorkspaceModal({ } }} /> + {error &&

{error}

} + {success &&

{success}

} diff --git a/test_files/greater-london.geo.json b/test_files/greater-london.geojson similarity index 100% rename from test_files/greater-london.geo.json rename to test_files/greater-london.geojson