Skip to content

Commit 21a3dff

Browse files
authored
Merge pull request #7 from nikolaysm/refacto-upload-page
feat(frontend): improve upload UI; add compose watch; better commitlint; sqlite dev DB - frontend(FileUpload): redesigned Dropzone wrapper with safe container, added top-right **X** clear button, made submit full-width, and centered hover/focus states; fixed `cn()` missing comma and class duplication; improved empty/error states - frontend(index): centered landing layout and added subtle background for a cleaner first run - frontend(package): enable `next dev --turbopack` for faster local refresh - ci(commitlint): handle new-branch pushes (all-zero `before` SHA) by linting each commit from the event payload via `jq`; fail fast if any commit breaks rules; warn/fail when `jq` is unavailable - makefile: add `compose-watch` for Docker’s file-watcher rebuilds; add `run-db` helper to start Postgres quickly; ensure `run-backend` also runs `uv-update` before launching Uvicorn - backend(config): simplify Pydantic v2 settings, correct `postgresql` typo, and switch default `DATABASE_URL` to `sqlite:///./budget_wise.db` for frictionless local development while keeping env-var override support (case-insensitive) - docker-compose: make `db-network`’s `internal` flag configurable via `INTERNAL_DB_NETWORK` (default `false`) to ease local service access during development
2 parents 920f3ce + bc0738a commit 21a3dff

File tree

7 files changed

+86
-32
lines changed

7 files changed

+86
-32
lines changed

.github/workflows/commitlint.yml

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,35 @@ jobs:
4747
HEAD_SHA="${{ github.event.after }}"
4848
# Handle initial commit (before can be all zeros)
4949
if [ "$BASE_SHA" = "0000000000000000000000000000000000000000" ] || [ -z "$BASE_SHA" ]; then
50-
BASE_SHA=$(git rev-list --max-parents=0 HEAD | tail -n1)
50+
echo "Detected branch creation push (all-zero before). Using per-commit lint from event payload."
51+
# Early per-commit lint for new branch pushes
52+
if command -v jq >/dev/null 2>&1; then
53+
COUNT=$(jq '.commits | length' "$GITHUB_EVENT_PATH")
54+
if [ "$COUNT" -eq 0 ]; then
55+
echo "No commits in event payload. Nothing to lint."
56+
exit 0
57+
fi
58+
FAIL=0
59+
for SHA in $(jq -r '.commits[].id' "$GITHUB_EVENT_PATH"); do
60+
MSG=$(git show -s --format=%B "$SHA" 2>/dev/null || true)
61+
if [ -z "$MSG" ]; then
62+
echo "Warning: could not read commit $SHA; skipping."
63+
continue
64+
fi
65+
echo "Linting commit $SHA"
66+
if ! printf "%s" "$MSG" | pnpm exec commitlint --config .commitlintrc.yml; then
67+
FAIL=1
68+
fi
69+
done
70+
if [ $FAIL -ne 0 ]; then
71+
echo "Commitlint failed for one or more commits."
72+
exit 1
73+
fi
74+
exit 0
75+
else
76+
echo "jq not available; cannot lint per-commit for new branch push. Failing."
77+
exit 2
78+
fi
5179
fi
5280
if [ -z "$HEAD_SHA" ]; then
5381
HEAD_SHA="HEAD"

Makefile

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ uv-update: $(VENV)
4646
cd backend && $(UV) sync
4747

4848
.PHONY: run-backend
49-
run-backend: uv-setup
49+
run-backend: uv-setup uv-update
5050
@echo "[run] Starting FastAPI (reload)"
5151
[ -f "$$HOME/.local/bin/env" ] && . "$$HOME/.local/bin/env" || true; export PATH="$$HOME/.local/bin:$$PATH"; \
5252
cd backend && $(UV) run uvicorn app.main:app --reload
@@ -73,6 +73,12 @@ compose-down:
7373
@echo "[docker] Stopping and removing services + volumes"
7474
docker compose down -v
7575

76+
# Add docker watchers for frontend and backend
77+
.PHONY: compose-watch
78+
compose-watch:
79+
@echo "[docker] Building and starting services with watchers"
80+
docker compose up --watch --build
81+
7682
# Frontend (pnpm) helpers
7783
.PHONY: frontend-install
7884
frontend-install:
@@ -98,3 +104,8 @@ frontend-lint: frontend-install
98104
frontend-typecheck: frontend-install
99105
@echo "[frontend] Type checking"
100106
cd $(FRONTEND_DIR) && $(PNPM) exec tsc --noEmit --project tsconfig.json
107+
108+
.PHONY: run-db
109+
run-db:
110+
@echo "[docker] Starting Postgres database"
111+
docker compose up -d postgres

backend/app/core/config.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,16 @@
1313

1414
class Settings(BaseSettings):
1515
"""Configuration values loaded from environment variables or defaults."""
16+
# Use case-insensitive env vars so DATABASE_URL works as expected
17+
model_config = SettingsConfigDict(
18+
env_file=".env",
19+
case_sensitive=False,
20+
extra="ignore"
21+
)
1622

1723
# Database URL in SQLAlchemy format
1824
# Example: postgresql+psycopg://user:password@localhost:5432/budget_db
19-
database_url: str = "postPgresql+psycopg://budget_user:budget_pass@localhost:5432/budget_db"
20-
21-
# Pydantic v2 settings configuration
22-
# Use case-insensitive env vars so DATABASE_URL works as expected
23-
model_config = SettingsConfigDict(env_file=".env", case_sensitive=False)
25+
database_url: str = "sqlite:///./budget_wise.db"
2426

2527

2628
@lru_cache()

docker-compose.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,6 @@ volumes:
9494
networks:
9595
db-network:
9696
driver: bridge
97-
internal: true
97+
internal: ${INTERNAL_DB_NETWORK:-false}
9898
webapp:
9999
driver: bridge

frontend/components/FileUpload.tsx

Lines changed: 35 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { cn } from '@/lib/utils';
55
import { Upload, Loader2 } from 'lucide-react';
66
import { Dropzone, DropzoneContent, DropzoneEmptyState } from '@/components/ui/shadcn-io/dropzone';
77
import { Progress } from '@/components/ui/progress';
8+
import { X } from "lucide-react"
89

910
/**
1011
* FileUpload component allows users to select and submit CSV or Excel files
@@ -51,36 +52,48 @@ const FileUpload: React.FC = () => {
5152
};
5253

5354
return (
54-
<div className="mt-6 max-w-xl space-y-4">
55-
<Dropzone
56-
accept={{
57-
'text/csv': ['.csv'],
58-
'application/vnd.ms-excel': ['.xls'],
59-
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': ['.xlsx'],
60-
}}
61-
maxFiles={1}
62-
disabled={isUploading}
63-
src={file ? [file] : undefined}
64-
onDrop={(accepted) => setFile(accepted[0] || null)}
65-
>
66-
<DropzoneContent />
67-
<DropzoneEmptyState />
68-
</Dropzone>
69-
70-
<div className="flex items-center gap-3">
71-
<Button type="button" variant="ghost" onClick={() => setFile(null)} disabled={!file || isUploading}>
72-
Clear
73-
</Button>
55+
<div className="mt-6 min-w-xl max-w-xl space-y-4">
56+
<div
57+
className="relative flex size-full flex-col gap-4 rounded-md
58+
overflow-hidden bg-muted items-center justify-center"
59+
>
60+
<div className="flex w-full justify-end cursor-pointer">
61+
<Button
62+
type="button"
63+
variant="ghost"
64+
onClick={() => setFile(null)}
65+
disabled={!file || isUploading}
66+
>
67+
<X />
68+
</Button>
69+
</div>
70+
<Dropzone
71+
className="border-dashed cursor-pointer rounded-t-none"
72+
accept={{
73+
'text/csv': ['.csv'],
74+
'application/vnd.ms-excel': ['.xls'],
75+
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': ['.xlsx'],
76+
}}
77+
maxFiles={1}
78+
disabled={isUploading}
79+
src={file ? [file] : undefined}
80+
onDrop={(accepted) => setFile(accepted[0] || null)}
81+
>
82+
<DropzoneContent />
83+
<DropzoneEmptyState />
84+
</Dropzone>
7485
</div>
7586

7687
<form onSubmit={handleSubmit}>
7788
<Button
89+
variant="secondary"
7890
type="submit"
7991
size="lg"
8092
disabled={!file || isUploading}
8193
className={cn(
8294
'group shadow-sm hover:shadow-md transition-all',
83-
'ring-1 ring-black/10 hover:ring-black/20'
95+
'ring-1 ring-black/10 hover:ring-black/20',
96+
'w-full cursor-pointer'
8497
)}
8598
>
8699
{isUploading ? (
@@ -112,7 +125,7 @@ const FileUpload: React.FC = () => {
112125
{success}
113126
</div>
114127
)}
115-
{error && (
128+
{error && (
116129
<div className="mt-3 inline-flex items-center gap-2 text-red-600 text-sm">
117130
<svg className="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden>
118131
<circle cx="12" cy="12" r="10" />

frontend/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"private": true,
55
"packageManager": "pnpm@10.15.0",
66
"scripts": {
7-
"dev": "next dev",
7+
"dev": "next dev --turbopack",
88
"build": "next build",
99
"start": "next start",
1010
"lint": "eslint . --ext .js,.jsx,.ts,.tsx"

frontend/pages/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ const Home: NextPage = () => {
99
<title>Budget App</title>
1010
<meta name="description" content="Upload your bank statements and categorize transactions" />
1111
</Head>
12-
<main className="p-8">
12+
<main className="p-8 flex flex-col items-center justify-center min-h-screen bg-gray-50">
1313
<h1 className="text-3xl font-bold">Budget Control</h1>
1414
<p className="text-gray-600 mt-2">Upload your CSV or Excel files to parse and store your transactions.</p>
1515
<FileUpload />

0 commit comments

Comments
 (0)