Skip to content

imlement drag and drop uploader#1

Open
nitheeshdr wants to merge 1 commit intomainfrom
upload-files
Open

imlement drag and drop uploader#1
nitheeshdr wants to merge 1 commit intomainfrom
upload-files

Conversation

@nitheeshdr
Copy link
Copy Markdown
Owner

@nitheeshdr nitheeshdr commented Mar 1, 2026

Summary by CodeRabbit

Release Notes

  • New Features
    • Added image upload functionality with drag-and-drop and click-to-upload support
    • New visualizer page to view uploaded images
    • Upload progress tracking with automatic navigation after processing

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 1, 2026

📝 Walkthrough

Walkthrough

The pull request introduces a new "/visualizer/:id" route with corresponding React components and auto-generated type definitions. It adds an Upload component to handle image file uploads with drag-and-drop functionality, connecting the home route to a new visualizer page via navigation. Constants and CSS formatting are also updated.

Changes

Cohort / File(s) Summary
React Router Type Definitions
.react-router/types/+future.ts, .react-router/types/+routes.ts, .react-router/types/+server-build.d.ts, .react-router/types/app/+types/root.ts, .react-router/types/app/routes/+types/home.ts, .react-router/types/app/routes/+types/visualizer.$id.ts
Auto-generated type definitions updated to support new "/visualizer/:id" route. Includes new types for visualizer route component, formatting adjustments to existing type declarations (semicolon removal, object property formatting), and route module references.
Route Configuration
app/routes.ts, app/routes/home.tsx, app/routes/visualizer.$id.tsx
Added new visualizer route configuration with route() function. Home route updated with Upload component integration and navigation handler. New visualizer.$id component added as placeholder for route handler.
Upload Component
components/Upload.tsx
New Upload component with drag-and-drop and click-to-upload file handling, progress tracking, FileReader integration, and onComplete callback for consuming base64 image data.
Styling and Constants
app/app.css, lib/constants.ts
CSS restructured with equivalent rules and formatting changes. New constants module exporting environment variables, storage paths, timing values, and ROOMIFY\_RENDER\_PROMPT template string for image processing.

Sequence Diagram

sequenceDiagram
    participant User
    participant Home as Home Route
    participant Upload as Upload Component
    participant Router as React Router
    participant Visualizer as Visualizer Route

    User->>Home: Navigate to home
    Home->>Upload: Render Upload component
    User->>Upload: Upload image (drag/drop or click)
    Upload->>Upload: Read file as base64
    Upload->>Upload: Progress interval increments
    Upload->>Upload: onComplete callback triggered
    Upload->>Router: navigate('/visualizer/{id}')
    Router->>Visualizer: Route to /visualizer/:id
    Visualizer->>User: Render visualizer page
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

🐰 A visualizer route hops into view,
Upload dropzone catches what's new,
Images dance base64-true,
Home to Visualizer, through and through! 📸✨

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The PR title 'imlement drag and drop uploader' contains a typo ('imlement' instead of 'implement') and describes a key feature added in the changeset, but the typo makes it less professional and unclear at first glance. Correct the typo: 'implement drag and drop uploader' to make the title clear, professional, and accurately reflect the main feature being added.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch upload-files

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Nitpick comments (1)
app/routes.ts (1)

3-6: Fix formatting and path consistency.

Two minor issues:

  1. Missing space before satisfies keyword on line 6
  2. Inconsistent path formatting: line 4 uses "routes/home.tsx" while line 5 uses './routes/visualizer.$id.tsx' - prefer consistent style (either both with ./ prefix or both without)
✨ Suggested formatting fix
 export default [
-index("routes/home.tsx"),
-route('visualizer/:id', './routes/visualizer.$id.tsx')
-]satisfies RouteConfig;
+  index("routes/home.tsx"),
+  route("visualizer/:id", "./routes/visualizer.$id.tsx"),
+] satisfies RouteConfig;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/routes.ts` around lines 3 - 6, The array export has two style issues: add
a space before the satisfies keyword on the export line and make the route path
strings consistent; update either index("routes/home.tsx") to
index("./routes/home.tsx") to match route('./routes/visualizer.$id.tsx') or
change route('./routes/visualizer.$id.tsx') to
route('routes/visualizer.$id.tsx'), and ensure the export still reads "]
satisfies RouteConfig" (note the space before satisfies) so symbols index(...)
and route(...) remain correct.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/app.css`:
- Line 12: The stylelint blockers are caused by standalone/invalid CSS
custom-property declarations (e.g. the --font-sans line) and a similar stray
declaration at line 458; fix by moving these custom properties into a valid rule
block such as :root { --font-sans: "Inter", sans-serif; /* other vars */ }
(ensure quotes around font family with a dash if needed), remove any stray
characters or duplicate hyphens, and validate the second problematic declaration
at the other location to be a proper property inside a selector or deleted if
accidental.
- Line 159: The `@apply` rule that sets "absolute inset-0 z-60 flex items-center
justify-center bg-black/50 backdrop-blur-sm p-4" uses z-60 which is not in
Tailwind v4.1.13's zIndex scale; change the z utility to an existing scale like
z-50 or use arbitrary value syntax z-[60] in that `@apply` declaration to prevent
Tailwind compile errors (locate the `@apply` line containing "z-60" and replace it
accordingly).

In `@app/routes/home.tsx`:
- Around line 19-23: handleUploadComplete currently ignores the base64Image and
navigates immediately, so /visualizer/:id has no data to render; update
handleUploadComplete to persist the payload before calling navigate by either
(A) storing base64Image in sessionStorage (keyed by newId) or (B) uploading the
image to your backend and using the returned server id as newId, then call
navigate(`/visualizer/${newId}`); ensure the function uses the same newId key
when saving to sessionStorage (or uses the returned id from the upload API) so
Visualizer can retrieve the data by that id.

In `@app/routes/visualizer`.$id.tsx:
- Around line 1-4: The VisualizerId component currently renders static text and
ignores the route parameter; update the VisualizerId React component to read the
route id (e.g., via useParams or the route loader), use that id to fetch or load
the corresponding visualization data (or trigger a redirect when the id is
invalid), and render the real visualization UI instead of the placeholder;
locate the VisualizerId function and replace the hardcoded <div> with logic that
retrieves the id, handles loading/error states, and either displays the
visualization or redirects the user back to a valid page.

In `@components/Upload.tsx`:
- Around line 43-45: Replace use of FileReader.onloadend with FileReader.onload
and add a guard that reader.result is a string before treating it as base64;
only call scheduleProgress and invoke onComplete when the guard passes. In the
Upload component locate the FileReader instance (reader) and change its event
handler from onloadend to onload, add a type-check like typeof reader.result ===
"string" then assign base64Data and proceed; handle errors via reader.onerror or
call onComplete with an error when the guard fails. Apply the same change to the
other reader block (the second occurrence currently around base64Data/
onComplete).
- Around line 84-86: Unify and centralize file validation so both drop and input
paths use the same rules: extend allowedTypes to include 'image/webp', validate
MIME and size (<= 50 * 1024 * 1024) before calling processFile, and return/show
an error for invalid files; specifically update the allowedTypes array, add a
shared validateFile(file) helper, call validateFile from the drop logic that
checks droppedFile and from handleChange (the input change handler), and ensure
processFile is only invoked when validateFile passes.

---

Nitpick comments:
In `@app/routes.ts`:
- Around line 3-6: The array export has two style issues: add a space before the
satisfies keyword on the export line and make the route path strings consistent;
update either index("routes/home.tsx") to index("./routes/home.tsx") to match
route('./routes/visualizer.$id.tsx') or change
route('./routes/visualizer.$id.tsx') to route('routes/visualizer.$id.tsx'), and
ensure the export still reads "] satisfies RouteConfig" (note the space before
satisfies) so symbols index(...) and route(...) remain correct.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 630036b and 75fbfbf.

📒 Files selected for processing (12)
  • .react-router/types/+future.ts
  • .react-router/types/+routes.ts
  • .react-router/types/+server-build.d.ts
  • .react-router/types/app/+types/root.ts
  • .react-router/types/app/routes/+types/home.ts
  • .react-router/types/app/routes/+types/visualizer.$id.ts
  • app/app.css
  • app/routes.ts
  • app/routes/home.tsx
  • app/routes/visualizer.$id.tsx
  • components/Upload.tsx
  • lib/constants.ts

Comment thread app/app.css
--color-secondary: #3b82f6;
--color-accent: #8b5cf6;
--font-serif: "Instrument Serif", serif;
--font-sans: Inter, sans-serif;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix current stylelint blockers on Line 12 and Line 458.

These two reported stylelint errors should be addressed to keep lint clean.

Suggested patch
-    --font-sans: Inter, sans-serif;
+    --font-sans: "Inter", sans-serif;
@@
-                    background-size: 60px 60px;
+                    background-size: 60px 60px;

Also applies to: 458-458

🧰 Tools
🪛 Stylelint (17.3.0)

[error] 12-12: Expected "Inter" to be "inter" (value-keyword-case)

(value-keyword-case)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/app.css` at line 12, The stylelint blockers are caused by
standalone/invalid CSS custom-property declarations (e.g. the --font-sans line)
and a similar stray declaration at line 458; fix by moving these custom
properties into a valid rule block such as :root { --font-sans: "Inter",
sans-serif; /* other vars */ } (ensure quotes around font family with a dash if
needed), remove any stray characters or duplicate hyphens, and validate the
second problematic declaration at the other location to be a proper property
inside a selector or deleted if accidental.

Comment thread app/app.css
.auth-modal {
@apply absolute inset-0 z-60 flex items-center justify-center bg-black/50 backdrop-blur-sm p-4;
.auth-modal {
@apply absolute inset-0 z-60 flex items-center justify-center bg-black/50 backdrop-blur-sm p-4;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

TAILWIND_CONFIGS="$(fd -HI 'tailwind.config.*' .)"
if [ -z "${TAILWIND_CONFIGS// /}" ]; then
  echo "No tailwind config file found."
else
  echo "$TAILWIND_CONFIGS" | while IFS= read -r file; do
    [ -z "$file" ] && continue
    echo "== $file =="
    rg -n "zIndex|extend|60" "$file" || true
  done
fi

echo "== z-60 usages =="
rg -n '\bz-60\b' -g '*.css' -g '*.ts' -g '*.tsx' || true

Repository: nitheeshdr/Roomify

Length of output: 230


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Looking for config files (any extension) =="
find . -type f -name "tailwind.config*" -o -name "tailwind.js" -o -name "tailwindcss.config*" 2>/dev/null || true

echo "== Checking package.json for Tailwind info =="
if [ -f "package.json" ]; then
  cat package.json | jq '.dependencies, .devDependencies' 2>/dev/null | grep -i tailwind || true
  echo ""
  echo "Full package.json:"
  head -50 package.json
fi

echo "== Checking if there's a Tailwind preset or config in PostCSS =="
if [ -f "postcss.config.js" ] || [ -f "postcss.config.mjs" ]; then
  echo "PostCSS config found:"
  ls -la postcss.config.* 2>/dev/null || true
  cat postcss.config.* 2>/dev/null || true
fi

echo "== Default Tailwind zIndex scale (standard) =="
echo "Standard Tailwind zIndex values: auto, 0, 10, 20, 30, 40, 50"
echo "z-60 is NOT in the default scale"

Repository: nitheeshdr/Roomify

Length of output: 1339


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Content of app/app.css =="
cat -n app/app.css

echo ""
echo "== Check if there's theme configuration in CSS files =="
rg -n '@theme|--z-|z-60|zIndex' app/app.css || true

Repository: nitheeshdr/Roomify

Length of output: 23690


Replace z-60 with z-50 or use arbitrary value syntax (z-[60]).

Line 159 uses z-60, which is not in Tailwind CSS v4.1.13's default zIndex scale (auto, 0, 10, 20, 30, 40, 50) and is not configured in the @theme block. The @apply directive will fail at compile time. Either use an existing scale value like z-50 or the arbitrary value syntax z-[60] to apply custom values.

🧰 Tools
🪛 Biome (2.4.4)

[error] 159-159: Tailwind-specific syntax is disabled.

(parse)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/app.css` at line 159, The `@apply` rule that sets "absolute inset-0 z-60
flex items-center justify-center bg-black/50 backdrop-blur-sm p-4" uses z-60
which is not in Tailwind v4.1.13's zIndex scale; change the z utility to an
existing scale like z-50 or use arbitrary value syntax z-[60] in that `@apply`
declaration to prevent Tailwind compile errors (locate the `@apply` line
containing "z-60" and replace it accordingly).

Comment thread app/routes/home.tsx
Comment on lines +19 to +23
const handleUploadComplete = async (base64Image: String) =>{
const newId = Date.now().toString();

navigate(`/visualizer/${newId}`)
return true;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n app/routes/home.tsx | head -80

Repository: nitheeshdr/Roomify

Length of output: 3284


🏁 Script executed:

find app -name "*visualizer*" -type f

Repository: nitheeshdr/Roomify

Length of output: 91


🏁 Script executed:

cat -n app/routes/visualizer.$id.tsx

Repository: nitheeshdr/Roomify

Length of output: 120


🏁 Script executed:

cat -n 'app/routes/visualizer.$id.tsx'

Repository: nitheeshdr/Roomify

Length of output: 213


🏁 Script executed:

rg "sessionStorage|localStorage" app/

Repository: nitheeshdr/Roomify

Length of output: 44


🏁 Script executed:

find app -name "Upload*" -type f

Repository: nitheeshdr/Roomify

Length of output: 44


🏁 Script executed:

fd -i upload app/

Repository: nitheeshdr/Roomify

Length of output: 44


🏁 Script executed:

find app/components -name "*.tsx" -o -name "*.ts" | head -20

Repository: nitheeshdr/Roomify

Length of output: 111


🏁 Script executed:

git ls-files app/ | head -30

Repository: nitheeshdr/Roomify

Length of output: 150


🏁 Script executed:

git ls-files | grep -i upload

Repository: nitheeshdr/Roomify

Length of output: 83


🏁 Script executed:

cat -n components/Upload.tsx

Repository: nitheeshdr/Roomify

Length of output: 6430


🏁 Script executed:

cat -n app/root.tsx

Repository: nitheeshdr/Roomify

Length of output: 3790


Uploaded payload is discarded before navigation.

base64Image is ignored, so /visualizer/:id has no data source to render the uploaded floor plan. Persist the payload (or upload and pass a server id) before route transition.

Suggested patch (sessionStorage handoff)
-  const handleUploadComplete = async (base64Image: String) =>{
+  const handleUploadComplete = async (base64Image: string) =>{
     const newId = Date.now().toString();
-
+    sessionStorage.setItem(`visualizer:${newId}`, base64Image);
     navigate(`/visualizer/${newId}`)
     return true;
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const handleUploadComplete = async (base64Image: String) =>{
const newId = Date.now().toString();
navigate(`/visualizer/${newId}`)
return true;
const handleUploadComplete = async (base64Image: string) =>{
const newId = Date.now().toString();
sessionStorage.setItem(`visualizer:${newId}`, base64Image);
navigate(`/visualizer/${newId}`)
return true;
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/routes/home.tsx` around lines 19 - 23, handleUploadComplete currently
ignores the base64Image and navigates immediately, so /visualizer/:id has no
data to render; update handleUploadComplete to persist the payload before
calling navigate by either (A) storing base64Image in sessionStorage (keyed by
newId) or (B) uploading the image to your backend and using the returned server
id as newId, then call navigate(`/visualizer/${newId}`); ensure the function
uses the same newId key when saving to sessionStorage (or uses the returned id
from the upload API) so Visualizer can retrieve the data by that id.

Comment on lines +1 to +4
const VisualizerId = () => {
return (
<div>VisualizerId</div>
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Visualizer route is currently a placeholder and ignores the route id.

/visualizer/:id renders static text, so users are redirected into a dead-end page after upload. This blocks the intended flow.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/routes/visualizer`.$id.tsx around lines 1 - 4, The VisualizerId component
currently renders static text and ignores the route parameter; update the
VisualizerId React component to read the route id (e.g., via useParams or the
route loader), use that id to fetch or load the corresponding visualization data
(or trigger a redirect when the id is invalid), and render the real
visualization UI instead of the placeholder; locate the VisualizerId function
and replace the hardcoded <div> with logic that retrieves the id, handles
loading/error states, and either displays the visualization or redirects the
user back to a valid page.

Comment thread components/Upload.tsx
Comment on lines +43 to +45
reader.onloadend = () => {
const base64Data = reader.result as string;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Use onload (with a result guard), not onloadend, for successful file completion.

onloadend also fires after read errors. Current flow can still schedule progress and call onComplete with a non-string result.

Suggested patch
-        reader.onerror = () => {
-            setFile(null);
-            setProgress(0);
-        };
-        reader.onloadend = () => {
-            const base64Data = reader.result as string;
+        reader.onerror = () => {
+            setFile(null);
+            setProgress(0);
+        };
+        reader.onload = () => {
+            if (typeof reader.result !== "string") {
+                setFile(null);
+                setProgress(0);
+                return;
+            }
+            const base64Data = reader.result;

Also applies to: 54-57

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/Upload.tsx` around lines 43 - 45, Replace use of
FileReader.onloadend with FileReader.onload and add a guard that reader.result
is a string before treating it as base64; only call scheduleProgress and invoke
onComplete when the guard passes. In the Upload component locate the FileReader
instance (reader) and change its event handler from onloadend to onload, add a
type-check like typeof reader.result === "string" then assign base64Data and
proceed; handle errors via reader.onerror or call onComplete with an error when
the guard fails. Apply the same change to the other reader block (the second
occurrence currently around base64Data/ onComplete).

Comment thread components/Upload.tsx
Comment on lines +84 to +86
const allowedTypes = ['image/jpeg', 'image/png'];
if (droppedFile && allowedTypes.includes(droppedFile.type)) {
processFile(droppedFile);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

File validation rules are inconsistent across input/drop paths.

  • Line 111 accepts .webp, but drop validation only allows JPEG/PNG.
  • handleChange does not enforce MIME type.
  • Line 125 says “Maximum file size 50 MB,” but size isn’t validated.

This can admit unsupported files and produce inconsistent behavior.

Also applies to: 90-96, 111-111, 125-125

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/Upload.tsx` around lines 84 - 86, Unify and centralize file
validation so both drop and input paths use the same rules: extend allowedTypes
to include 'image/webp', validate MIME and size (<= 50 * 1024 * 1024) before
calling processFile, and return/show an error for invalid files; specifically
update the allowedTypes array, add a shared validateFile(file) helper, call
validateFile from the drop logic that checks droppedFile and from handleChange
(the input change handler), and ensure processFile is only invoked when
validateFile passes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant