Skip to content

Commit

Permalink
feat: upload v2 work [2025-01-18]
Browse files Browse the repository at this point in the history
  • Loading branch information
CHRISCARLON committed Jan 18, 2025
1 parent 7083156 commit 7338b5e
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 21 deletions.
2 changes: 1 addition & 1 deletion gridwalk-backend/.env
Original file line number Diff line number Diff line change
Expand Up @@ -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=[email protected]
GW_USER_PASSWORD=password
37 changes: 25 additions & 12 deletions gridwalk-backend/src/routes/layer_v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use std::error::Error;
#[derive(Debug)]
enum FileType {
Geopackage,
Json,
GeoJson,
Shapefile,
Excel,
Expand Down Expand Up @@ -459,26 +460,38 @@ fn match_magic_numbers(header: &[u8]) -> Option<FileType> {
}

fn detect_content_based_type(buffer: &[u8]) -> Result<FileType, Box<dyn Error>> {
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();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import {
INITIAL_MAP_CONFIG,
MapClientProps,
LayerUpload,
SupportedFileTypes,
FileHandlerResponse,
} from "./types";

Expand Down Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions gridwalk-ui/src/app/workspace/WorkspaceContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ type Workspace = {
id: string;
name: string;
};

type WorkspaceContextType = {
workspaces: Workspace[];
};
Expand Down
35 changes: 32 additions & 3 deletions gridwalk-ui/src/app/workspace/createWorkspaceModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
Expand All @@ -66,6 +93,8 @@ export function CreateWorkspaceModal({
}
}}
/>
{error && <p className="text-sm text-red-500">{error}</p>}
{success && <p className="text-sm text-green-500">{success}</p>}
</div>
</div>
<DialogFooter>
Expand Down
File renamed without changes.

0 comments on commit 7338b5e

Please sign in to comment.