Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ dist
.cache
.vscode
.DS_Store
.angular
.angular
.claude/
4 changes: 4 additions & 0 deletions server-examples/django/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ setup:
@echo ""
@echo "==> Installing frontend dependencies..."
cd frontend && npm install
cd frontend-angular && npm install
cd frontend-react && npm install
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Makefile setup skips Angular and React build steps

High Severity

The setup target runs npm install for Angular and React frontends but never runs their build or watch scripts before starting Vite. Vite's middleware serves pre-built output from frontend-angular/dist and frontend-react/dist, returning HTTP 503 when those directories are missing. Unlike setup.sh, which runs npm run build and npm run watch & for both, make setup leaves /angular.html and /react.html broken.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 9ef33c1. Configure here.

@echo ""
@echo "==> Starting Vite dev server..."
@echo " Open http://localhost:5173 in your browser."
Expand All @@ -41,3 +43,5 @@ stop:
clean:
docker compose down -v
rm -rf frontend/node_modules
rm -rf frontend-angular/node_modules
rm -rf frontend-react/node_modules
57 changes: 43 additions & 14 deletions server-examples/django/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,40 +8,56 @@ A fully working employee directory that demonstrates Handsontable's `dataProvide
|---|---|
| Database | PostgreSQL 15 (Docker) |
| Backend | Python 3.11, Django 4, Django REST Framework |
| Frontend | Vite, Handsontable (`dataProvider` plugin) |
| Frontend (JS) | Vite, Handsontable (`dataProvider` plugin) |
| Frontend (Angular) | Angular 21, `@handsontable/angular-wrapper` |
| Frontend (React) | React 19, `@handsontable/react-wrapper` |

## Quick start

**Prerequisites:** Docker (with Compose plugin), Node.js, npm

```bash
cd server-examples
chmod +x setup.sh
./setup.sh
bash setup.sh
```

Or with Make:

```bash
cd server-examples
make
make setup
```

The script will:
1. Build and start PostgreSQL + Django via Docker Compose
2. Run database migrations inside the container
3. Seed 50 realistic employee records
4. Install frontend npm dependencies
5. Start the Vite dev server
4. Install all frontend npm dependencies (JS, Angular, React)
5. Build Angular and React apps, then start watchers for live rebuilds
6. Start the Vite dev server

Open **http://localhost:5173** in your browser.

| URL | Description |
|---|---|
| http://localhost:5173 | JS frontend |
| http://localhost:5173/angular.html | Angular frontend |
| http://localhost:5173/react.html | React frontend |

## Available commands

```bash
make setup # Full first-time setup (build → migrate → seed → start)
make backend # Start only the Docker services
make frontend # Install deps and start Vite
make stop # Stop Docker services
make clean # Remove containers, volumes, and node_modules
```

## Project structure

```
server-examples/
server-examples/django/
├── setup.sh # One-run setup script
├── Makefile # Alternative make-based entry point
├── Makefile # Convenience targets
├── docker-compose.yml # PostgreSQL + Django services
├── backend/
│ ├── Dockerfile
Expand All @@ -56,11 +72,24 @@ server-examples/
│ ├── views.py # Sort/filter translation + batch CRUD endpoints
│ ├── urls.py
│ └── management/commands/seed.py
└── frontend/
├── frontend/ # JS entry point + Vite dev server (serves all 3 variants)
│ ├── package.json
│ ├── vite.config.js # Proxies /api/* → Django; serves Angular/React builds
│ ├── favicon.png
│ ├── index.html
│ └── src/main.js # Handsontable + dataProvider setup
├── frontend-angular/ # Angular variant (ng build --watch → served via Vite)
│ ├── angular.json
│ ├── package.json
│ └── src/app/
│ ├── app.component.ts
│ └── app.component.html
└── frontend-react/ # React variant (vite build --watch → served via Vite)
├── vite.config.ts
├── package.json
── vite.config.js # Proxies /api/* → localhost:8000
├── index.html
└── src/main.js # Handsontable + dataProvider setup
── src/
├── main.tsx
└── App.tsx
```

## API endpoints
Expand Down
12 changes: 7 additions & 5 deletions server-examples/django/backend/employees/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,10 +133,12 @@ def get_queryset(self):
@action(detail=False, methods=['post'], url_path='create-rows')
@transaction.atomic
def create_rows(self, request):
serializer = EmployeeSerializer(data=request.data, many=True)
serializer.is_valid(raise_exception=True)
serializer.save()
# Return created rows so dataProvider can update its row map with server-assigned ids.
rows_amount = max(1, int(request.data.get('rowsAmount', 1)))
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

create_rows crashes on null rowsAmount value

Low Severity

int(request.data.get('rowsAmount', 1)) crashes with TypeError when the client sends "rowsAmount": null in JSON, because .get() returns None (the key exists) and int(None) raises TypeError. This produces a 500 instead of a useful validation error. A guard like int(request.data.get('rowsAmount') or 1) would handle the null case.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 9ef33c1. Configure here.

employees = Employee.objects.bulk_create([
Employee(first_name='', last_name='', department='', role='', salary=0)
for _ in range(rows_amount)
])
serializer = EmployeeSerializer(employees, many=True)
return Response(serializer.data, status=201)

@action(detail=False, methods=['patch'], url_path='update-rows')
Expand All @@ -145,7 +147,7 @@ def update_rows(self, request):
updated = []
for row in request.data:
employee = Employee.objects.get(pk=row['id'])
serializer = EmployeeSerializer(employee, data=row, partial=True)
serializer = EmployeeSerializer(employee, data=row['changes'], partial=True)
serializer.is_valid(raise_exception=True)
serializer.save()
updated.append(serializer.data)
Expand Down
2 changes: 0 additions & 2 deletions server-examples/django/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
version: '3.9'

services:
db:
image: postgres:15-alpine
Expand Down
93 changes: 93 additions & 0 deletions server-examples/django/frontend-angular/angular.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"django-angular": {
"projectType": "application",
"schematics": {},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:application",
"options": {
"outputPath": {
"base": "dist",
"browser": "browser"
},
"baseHref": "/angular-assets/",
"index": {
"input": "src/index.html",
"output": "angular.html"
},
"browser": "src/main.ts",
"polyfills": ["zone.js"],
"tsConfig": "tsconfig.app.json",
"assets": [],
"styles": [
"node_modules/handsontable/styles/handsontable.css",
"node_modules/handsontable/styles/ht-theme-main.css",
"src/styles.css"
],
"scripts": [],
"allowedCommonJsDependencies": [
"core-js",
"@handsontable/pikaday",
"numbro"
]
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "500kb",
"maximumError": "1mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "2kb",
"maximumError": "4kb"
}
],
"outputHashing": "all"
},
"development": {
"optimization": false,
"extractLicenses": false,
"sourceMap": true,
"namedChunks": true
}
},
"defaultConfiguration": "production"
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"proxyConfig": "proxy.conf.json"
},
"configurations": {
"production": {
"buildTarget": "django-angular:build:production"
},
"development": {
"buildTarget": "django-angular:build:development"
}
},
"defaultConfiguration": "development"
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "django-angular:build"
}
}
}
}
},
"cli": {
"analytics": false
}
}
Loading