Skip to content
Merged
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
2 changes: 1 addition & 1 deletion .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ jobs:
run: |
python -m pip install --upgrade pip
pip install poetry
poetry install --no-dev
poetry install --only main
- name: Run Tests
env:
TEST: 1
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/deploy-bare-metal.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ jobs:
run: |
python -m pip install --upgrade pip
pip install poetry
poetry install --no-dev
poetry install --only main
- name: Run Tests
env:
TEST: 1
Expand Down Expand Up @@ -123,7 +123,7 @@ jobs:
cd app
python3 -m pip install --upgrade pip
pip install poetry
poetry install --no-dev
poetry install --only main
echo -e "~~~~~~~~~~~~~~~~~~~~~~~~ Installed python requirements successfully ~~~~~~~~~~~~~~~~~~~~~~~~\n"

poetry run python3 manage.py migrate
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/deploy-vm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ jobs:
run: |
python -m pip install --upgrade pip
pip install poetry
poetry install --no-dev
poetry install --only main
- name: Run Tests
env:
TEST: 1
Expand All @@ -76,7 +76,7 @@ jobs:
- uses: actions/checkout@v4
- name: Run tests
run: |
docker build . --build-arg AUTH_TOKEN=${{ secrets.AUTH_KEY }}--build-arg ALLOWED_HOSTS=${{ secrets.ALLOWED_HOSTS }} --file Dockerfile
docker build . --build-arg AUTH_TOKEN=${{ secrets.AUTH_KEY }} --build-arg ALLOWED_HOSTS=${{ secrets.ALLOWED_HOSTS }} --file Dockerfile

# Push image to GitHub Packages.
# See also https://docs.docker.com/docker-hub/builds/
Expand Down
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ RUN cd frontend \
&& pnpm run build

# Build backend
FROM python:3.12-rc-slim-buster
FROM python:3.12-slim-bookworm
WORKDIR /usr/src/app
ARG ALLOWED_HOSTS
ENV ALLOWED_HOSTS $ALLOWED_HOSTS
Expand All @@ -26,4 +26,4 @@ RUN pip install --upgrade pip
RUN pip install poetry
COPY ./pyproject.toml /usr/src/app/pyproject.toml
COPY ./poetry.lock /usr/src/app/poetry.lock
RUN poetry install --no-dev
RUN poetry install --only main
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<img alt="django-react-typescript logo" src="assets/Logo.png" align="right" width="120" height="120" />

This is an non-opinionated Django 5 + React 18 boilerplate built with great development experience and easy deployment in mind.
This is a non-opinionated Django 5 + React 18 boilerplate built with great development experience and easy deployment in mind.

This template is ideal if you want to bootstrap a blog or a portfolio website quickly, or even a more complex application that requires a CMS, all while leveraging the best from React and Django.

Expand Down Expand Up @@ -90,7 +90,7 @@ Below you will find the stack used for each part of the application and the feat
| Stack | Libraries and services | Features |
| ---------- | ----------------------------------------------------------------- | -------------------------------- |
| Frontend | React 18, React Router 6, Typescript 5, Webpack 5, Tailwind CSS 3 | Publication listing and search |
| Backend | Django 5, Django Rest Framework | Publication CRUD, API Key CRUD |
| Backend | Django 5.1+, Django Rest Framework | Publication CRUD, API Key CRUD |
| Database | Postgres | - |
| CDN | Cloudinary | - |
| CI/CD | GitHub Actions | Multiple deploy workflow options |
Expand Down
27 changes: 11 additions & 16 deletions core/settings/base.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
"""
Django settings for es project.
Django settings for this project.

Generated by 'django-admin startproject' using Django 3.0.7.

For more information on this file, see
https://docs.djangoproject.com/en/3.0/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/3.0/ref/settings/
See https://docs.djangoproject.com/en/stable/topics/settings/
and https://docs.djangoproject.com/en/stable/ref/settings/
"""

import os
Expand Down Expand Up @@ -54,7 +49,7 @@


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.0/howto/deployment/checklist/
# See https://docs.djangoproject.com/en/stable/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = os.environ.get(
Expand Down Expand Up @@ -97,6 +92,8 @@

ROOT_URLCONF = "core.urls"

DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"

TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
Expand All @@ -117,7 +114,7 @@


# Database
# https://docs.djangoproject.com/en/3.0/ref/settings/#databases
# https://docs.djangoproject.com/en/stable/ref/settings/#databases

if os.getenv("TEST"):
DATABASES = {
Expand All @@ -140,7 +137,7 @@


# Password validation
# https://docs.djangoproject.com/en/3.0/ref/settings/#auth-password-validators
# https://docs.djangoproject.com/en/stable/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
{
Expand All @@ -159,21 +156,19 @@


# Internationalization
# https://docs.djangoproject.com/en/3.0/topics/i18n/
# https://docs.djangoproject.com/en/stable/topics/i18n/

LANGUAGE_CODE = "en"

TIME_ZONE = "America/New_York"

USE_I18N = True

USE_L10N = True

USE_TZ = True


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.0/howto/static-files/
# https://docs.djangoproject.com/en/stable/howto/static-files/

STATIC_URL = "/static/"
STATIC_ROOT = os.path.join(BASE_DIR, "static/")
Expand All @@ -184,7 +179,7 @@
config(cloud_name=CDN_NAME, api_key=CDN_API_KEY, api_secret=CDN_API_SECRET, secure=True)

# Email
# https://docs.djangoproject.com/en/3.0/topics/email/
# https://docs.djangoproject.com/en/stable/topics/email/

# Gmail SMTP requirements
# https://support.google.com/a/answer/176600?hl=en
Expand Down
113 changes: 113 additions & 0 deletions docs/STACK_ANALYSIS_AND_UPDATES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# Stack analysis and update recommendations

Analysis date: February 2025. See also the changes already applied in this branch (Django 5.1, USE_L10N removed, Tailwind content paths fixed, Webpack asset modules, Jest added). This document summarizes the current boilerplate stack, latest versions, and concrete steps to modernize and align with the “AI-friendly” reality (e.g. Tailwind-first, widely known tooling).

---

## 1. Django

### Current
- **pyproject.toml**: `django = "^5.0.6"`
- **Python**: `^3.12`
- **Settings**: `core/settings/base.py` still references Django 3.0 in docstrings and uses **deprecated `USE_L10N`** (deprecated in 4.0, removed in 5.0 — has no effect; locale formatting is always on).

### Latest
- **Django 6.0** is the current stable (e.g. 6.0.2 on PyPI). Requires Python ≥ 3.12.
- **Django 5.1** is the latest 5.x LTS-friendly line.

### How to leverage the latest Django

1. **Upgrade version**
- For latest features and Python 3.12+ only: `django = "^6.0"` (or `"^5.1"` for a conservative jump).
- Run tests and `python manage.py check` after upgrading.

2. **Remove deprecated setting**
- In `core/settings/base.py`, **remove** `USE_L10N = True` (no longer valid in Django 5+).

3. **Update settings docstrings**
- Replace Django 3.0 references in `base.py` with the actual version (e.g. 5.0 / 6.0) and current docs URLs.

4. **Optional Django 5/6 features you can adopt**
- **Database-computed defaults**: `Field.db_default` for DB-level defaults.
- **GeneratedField**: DB-generated columns from expressions.
- **Form field groups**: `{{ form.field.as_field_group }}` in templates.
- **Async**: `async` views, `aget_object_or_404`, async auth helpers if you move to async views.

---

## 2. React and frontend build

### Current
- **React**: `^18.3.1`
- **Build**: Webpack 5, TypeScript 5, Babel for React.
- **Frontend entry**: `index.tsx`; components are **`.tsx`** under `lib/`.

### Latest
- **React 19** is current (e.g. 19.2.4). React 18 remains supported.

### Recommendations
- **React 19**: Upgrade to `react@^19` and `react-dom@^19` when ready; update `@types/react` and `@types/react-dom` to versions that support React 19. Run the test suite and fix any type or behavior changes.
- **Build**: Webpack 5 is still fine. Alternative for a future refresh: Vite (faster dev, simple config, very common and LLM-friendly).

---

## 3. Tailwind and “AI-friendly” design system

### Current
- **Tailwind CSS**: `^3.4.4` (v3).
- **Plugins**: `@tailwindcss/forms`, `@tailwindcss/typography`, **flowbite-react**.
- **Config**: `frontend/tailwind.config.js` — **content paths list only `*.js`**, but all React sources are **`.tsx`**. So Tailwind is **not** scanning your components; utility classes used only in TSX can be purged incorrectly. This is a **bug**.

### Does it come with Tailwind by default?
**Yes.** The boilerplate is already Tailwind-based, which is a design system LLMs are highly proficient at. No change of design system is required for “AI reality.”

### Fixes and improvements

1. **Fix content paths (important)**
- In `tailwind.config.js`, include TSX (and JS/JSX if any):
- e.g. `"./lib/**/*.{js,jsx,ts,tsx}"`, `"./index.tsx"`, and keep `./templates/frontend/**/*.html` and `flowbite.content()`.

2. **Tailwind v4 (optional, larger change)**
- Tailwind v4 is available (new engine, CSS-first config). Staying on v3 is fine for stability; v4 can be a follow-up migration.

3. **Flowbite**
- Heavily used in this boilerplate (navbar, cards, buttons, inputs, spinner). Keep for now; if you want to reduce dependencies later, you could replace with headless components + Tailwind. Not required for “AI-friendly” — Tailwind itself is the main lever.

4. **Safelist**
- The current safelist is broad. Once content paths include `.tsx`, you can often shrink the safelist and rely on Tailwind’s scan; only keep patterns that are truly dynamic (e.g. class names built in JS).

---

## 4. Dependencies: unnecessary or deprecated

### Backend (Python)
- **django-better-admin-arrayfield**: Verify you still use array fields in the admin; if not, remove.
- **twilio**: Only needed if you use Twilio (SMS/whatsapp). Remove if unused.
- **cloudinary**: Only if you use it as CDN; keep or replace per your deployment.
- **sentry-sdk**, **gunicorn**, **django-cors-headers**, **django-filter**, **djangorestframework**, **pillow**, **psycopg2-binary**, **python-dotenv**: All current and commonly used; keep.

### Frontend
- **file-loader**: Webpack 5 prefers **Asset Modules** (e.g. `type: "asset/resource"`) instead of `file-loader`. Migrating avoids a deprecated loader.
- **Jest**: The `test` script in `frontend/package.json` runs `jest`, but **Jest is not listed in devDependencies**; it only appears in the lockfile as a transitive dependency. Either add `jest`, `ts-jest`, and necessary types as devDependencies or remove/fix the test script.
- **dayjs**, **query-string**, **react-router-dom**, **@heroicons/react**: All in common use; keep unless you intentionally replace them.

---

## 5. Summary: what to do first

| Priority | Action |
|----------|--------|
| High | Fix Tailwind **content** paths to include `**/*.tsx` (and relevant JS/JSX) so Tailwind scans all component files. |
| High | Remove **USE_L10N** from `core/settings/base.py` (Django 5+). |
| High | Update **Django** to `^5.1` or `^6.0` in `pyproject.toml` and refresh lockfile; update settings docstrings. |
| Medium | Replace **file-loader** in Webpack with Asset Modules. |
| Medium | Add **Jest** (and ts-jest/types) to frontend devDependencies or adjust test script. |
| Medium | Upgrade to **React 19** and matching types when ready. |
| Low | Consider **Tailwind v4** later; optional. |
| Low | Drop **twilio** / **django-better-admin-arrayfield** / **cloudinary** if not used. |

---

## 6. Tailwind = default and AI-friendly

The boilerplate **already uses Tailwind by default**. That aligns well with the “AI reality”: LLMs are very good at generating and modifying Tailwind utility classes. No need to add Tailwind for that reason; the main fix is ensuring Tailwind actually sees your `.tsx` files via correct `content` paths.
10 changes: 10 additions & 0 deletions frontend/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/** @type {import('jest').Config} */
module.exports = {
preset: "ts-jest",
testEnvironment: "jsdom",
testMatch: ["**/__tests__/**/*.ts?(x)", "**/?(*.)+(spec|test).ts?(x)"],
moduleNameMapper: {
"^@/(.*)$": "<rootDir>/lib/$1",
},
collectCoverageFrom: ["lib/**/*.{ts,tsx}", "!**/__tests__/**"],
};
24 changes: 18 additions & 6 deletions frontend/lib/api/use-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,19 @@ import {
getPaginatedFilteredPublicationsEndoint,
} from "./utils";
import { getSecrets } from "../config";
import type { GetPaginatedPublicationsResponse, Publication } from "./types";
import type {
GetPaginatedPublicationsResponse,
Publication,
} from "./types";

const emptyPaginatedResponse: GetPaginatedPublicationsResponse = {
count: 0,
current_page: 1,
total_pages: 0,
next: null,
previous: null,
results: [],
};

const { isProd, authToken } = getSecrets();

Expand Down Expand Up @@ -52,7 +64,7 @@ export function useApi() {
headers: getHeaders,
})
.then((response) => response.json())
.catch((error) => {
.catch((error): Publication[] => {
console.error(error);
return [];
});
Expand Down Expand Up @@ -113,9 +125,9 @@ export function useApi() {
headers: getHeaders,
})
.then((response) => response.json())
.catch((error) => {
.catch((error): GetPaginatedPublicationsResponse => {
console.error(error);
return [];
return emptyPaginatedResponse;
});
}

Expand All @@ -131,9 +143,9 @@ export function useApi() {
}
)
.then((response) => response.json())
.catch((error) => {
.catch((error: unknown): never => {
console.error(error);
return [];
throw error;
});
}

Expand Down
10 changes: 7 additions & 3 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@
"dev:css": "postcss ./tailwind.css -o ./lib/index.css --watch",
"dev": "concurrently -n css,react \"pnpm run dev:css\" \"pnpm run dev:react\"",
"test:static": "tsc -p tsconfig.json --noEmit",
"test": "jest \"(/__tests__/.)*\\.tsx?$\" --coverage --colors --silent",
"test:watch": "jest \"(/__tests__/.)*\\.tsx?$\" --coverage --colors --watch"
"test": "jest \"(/__tests__/.)*\\.tsx?$\" --coverage --colors --silent --passWithNoTests",
"test:watch": "jest \"(/__tests__/.)*\\.tsx?$\" --coverage --colors --watch --passWithNoTests"
},
"dependencies": {
"@heroicons/react": "^2.1.4",
Expand Down Expand Up @@ -63,6 +63,10 @@
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^5.0.4",
"webpack-manifest-plugin": "^5.0.0",
"workbox-webpack-plugin": "^7.1.0"
"workbox-webpack-plugin": "^7.1.0",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"ts-jest": "^29.2.5",
"@types/jest": "^29.5.14"
}
}
Loading