chore: migrate Python dependency management from pip to uv and update project tooling

This commit is contained in:
ashwin 2026-05-09 12:07:23 +05:30
parent 24ccf66546
commit 932afb18d7
20 changed files with 2661 additions and 1320 deletions

View file

@ -25,12 +25,11 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Set up Python 3.12
uses: actions/setup-python@v5
- name: Install uv
uses: astral-sh/setup-uv@v6
with:
python-version: '3.12'
cache: 'pip'
cache-dependency-path: backend/requirements.txt
enable-cache: true
cache-dependency-glob: 'backend/uv.lock'
- name: Install system dependencies (WeasyPrint)
run: |
@ -43,9 +42,8 @@ jobs:
shared-mime-info
- name: Install Python dependencies
run: |
python -m pip install --upgrade pip
pip install -r backend/requirements.txt
working-directory: backend
run: uv sync --frozen
- name: Run tests with coverage
working-directory: backend
@ -53,15 +51,12 @@ jobs:
DJANGO_SETTINGS_MODULE: crm.test_settings
SECRET_KEY: test-secret-key-for-ci
ADMIN_EMAIL: admin@test.com
run: |
pytest -m "not postgres_only" --tb=short -v
run: uv run pytest -m "not postgres_only" --tb=short -v
- name: Generate coverage badge
if: github.ref == 'refs/heads/master' && github.event_name == 'push'
working-directory: backend
run: |
pip install "genbadge[coverage]"
genbadge coverage -i coverage.xml -o ../coverage-badge.svg
run: uvx --from "genbadge[coverage]" genbadge coverage -i coverage.xml -o ../coverage-badge.svg
- name: Commit coverage badge
if: github.ref == 'refs/heads/master' && github.event_name == 'push'

10
.gitignore vendored
View file

@ -27,6 +27,12 @@ venv/
.venv/
ENV/
# Python build artifacts
dist/
build/
*.egg-info/
*.egg
# IDE
.idea/
.vscode/
@ -38,6 +44,10 @@ ENV/
.pytest_cache/
htmlcov/
coverage.xml
# Linter / type checker caches
.ruff_cache/
.mypy_cache/
coverage-badge.svg
# Documentation

View file

@ -1,6 +1,6 @@
onCreate:
- sudo apt-get update -y
- sudo apt install python-is-python3 xvfb libfontconfig wkhtmltopdf libpq-dev python3-dev python3-pip build-essential libssl-dev libffi-dev python3-venv redis-server redis-tools virtualenv zsh python3-virtualenv -y
- sudo apt install python-is-python3 xvfb libfontconfig wkhtmltopdf libpq-dev python3-dev build-essential libssl-dev libffi-dev redis-server redis-tools zsh -y
- |
sudo sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list'
wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
@ -10,15 +10,14 @@ onCreate:
sudo -u postgres psql postgres -c "ALTER USER postgres PASSWORD 'root'"
sudo -u postgres psql postgres -c "CREATE DATABASE bottlecrm WITH OWNER = postgres"
- |
virtualenv -p python3 ../venv
. ../venv/bin/activate
curl -LsSf https://astral.sh/uv/install.sh | sh
export PATH="$HOME/.local/bin:$PATH"
cp ENV.md .env
pip install -U pip
pip install -r requirements.txt
python manage.py migrate
uv sync --frozen
uv run python manage.py migrate
onStart:
- |
. ../venv/bin/activate
export PATH="$HOME/.local/bin:$PATH"
WS_DOMAIN=`cat /home/ubuntu/runcode/ws-url-domain.txt`
printf "\nGO TO THIS URL TO VIEW PAGE \033[4;34m https://8000-${WS_DOMAIN}\033[0;30m\n\n"
python manage.py runserver
uv run python manage.py runserver

View file

@ -17,11 +17,17 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
WORKDIR /app
# Install Python dependencies (layer cached)
COPY backend/requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Install uv (fast Python package manager).
COPY --from=ghcr.io/astral-sh/uv:0.11 /uv /usr/local/bin/uv
# Install Python dependencies into /app/.venv (layer cached on lockfile changes)
COPY backend/pyproject.toml backend/uv.lock backend/.python-version ./
RUN uv sync --frozen --no-install-project
# Copy backend source
COPY backend/ .
# Put the venv's binaries on PATH so `python`, `gunicorn`, `celery` etc. resolve.
ENV PATH="/app/.venv/bin:$PATH"
EXPOSE 8000

View file

@ -67,31 +67,50 @@ BottleCRM is a full-featured Customer Relationship Management system designed fo
### Backend Setup
The backend uses [`uv`](https://docs.astral.sh/uv/) for Python dependency management — it reads `pyproject.toml`, installs from `uv.lock`, and creates the virtual environment for you. uv is much faster than pip and gives reproducible installs out of the box.
```bash
# Clone the repository
git clone https://github.com/MicroPyramid/Django-CRM.git
cd Django-CRM
cd Django-CRM/backend
# Create and activate virtual environment
cd backend
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
# Install uv (one time, system-wide)
curl -LsSf https://astral.sh/uv/install.sh | sh
# Or on macOS via Homebrew: brew install uv
# Install dependencies
pip install -r requirements.txt
# Install Python (matches the version in .python-version) and all deps into .venv/
uv sync
# Set up environment variables (see env.md for details)
cp .env.example .env
# Edit .env with your database and other settings
# Run migrations
python manage.py migrate
uv run python manage.py migrate
# Create a superuser
python manage.py createsuperuser
uv run python manage.py createsuperuser
# Start the development server
python manage.py runserver
uv run python manage.py runserver
```
`uv run <cmd>` resolves binaries from `.venv/bin/` automatically — no need to `source .venv/bin/activate`. If you prefer the activate-then-run flow, that still works:
```bash
source .venv/bin/activate
python manage.py runserver # equivalent to `uv run python manage.py runserver`
```
Common dev commands (from `backend/`):
```bash
uv run pytest # run tests
uv run python manage.py makemigrations # create migrations
uv run celery -A crm worker --loglevel=INFO # background worker
uv add <package> # add a dependency (updates pyproject.toml + uv.lock)
uv add --group dev <package> # add a dev-only dependency
uv lock --upgrade # refresh the lockfile
```
### Frontend Setup
@ -112,8 +131,7 @@ pnpm run dev
```bash
# In a new terminal
cd backend
source venv/bin/activate
celery -A crm worker --loglevel=INFO
uv run celery -A crm worker --loglevel=INFO
```
### Access the Application

View file

@ -100,8 +100,7 @@ Migrations automatically enable RLS on all protected tables:
```bash
cd backend
source venv/bin/activate
python manage.py migrate
uv run python manage.py migrate
```
---

1
backend/.python-version Normal file
View file

@ -0,0 +1 @@
3.12

View file

@ -35,30 +35,37 @@ The backend for BottleCRM, a multi-tenant CRM platform built with Django REST Fr
## Prerequisites
- **Python 3.8+**
- **Python 3.10+** (uv installs a matching Python automatically if needed)
- **PostgreSQL**
- **Redis** (for Celery)
- **virtualenv**
- **[uv](https://docs.astral.sh/uv/)** — Python package & venv manager (replaces pip + virtualenv)
## Installation
### 1. Create and activate virtual environment
### 1. Install uv
```bash
# Linux / macOS
curl -LsSf https://astral.sh/uv/install.sh | sh
# Or via Homebrew
brew install uv
```
### 2. Install Python dependencies
`uv sync` reads `pyproject.toml` + `uv.lock`, picks the Python version from `.python-version`, and creates `.venv/` with everything installed.
```bash
cd backend
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
uv sync
```
### 2. Install dependencies
```bash
pip install -r requirements.txt
```
> Run any backend command with `uv run <cmd>` (e.g. `uv run python manage.py migrate`). uv resolves binaries from `.venv/bin/` automatically — no manual `source .venv/bin/activate` needed (though that still works if you prefer it).
### 3. Install PDF generation system dependencies
Invoice PDF generation uses WeasyPrint (already included in requirements.txt). However, WeasyPrint requires system libraries that must be installed separately:
Invoice PDF generation uses WeasyPrint (a runtime dep). It requires system libraries that must be installed separately:
**Ubuntu/Debian:**
```bash
@ -129,16 +136,16 @@ ALTER USER postgres WITH PASSWORD 'root';
\q
# Run migrations
python manage.py migrate
uv run python manage.py migrate
# Create superuser (optional)
python manage.py createsuperuser
uv run python manage.py createsuperuser
```
### 6. Run the development server
```bash
python manage.py runserver
uv run python manage.py runserver
```
The API will be available at `http://localhost:8000`
@ -148,7 +155,7 @@ The API will be available at `http://localhost:8000`
For background tasks (emails, notifications), run the Celery worker:
```bash
celery -A crm worker --loglevel=INFO
uv run celery -A crm worker --loglevel=INFO
```
## API Documentation
@ -321,7 +328,9 @@ GET/POST /api/<module>/attachment/<pk>/ # Attachments
```
backend/
├── manage.py
├── requirements.txt
├── pyproject.toml # Python deps + project metadata (uv-managed)
├── uv.lock # Pinned, reproducible dependency tree
├── .python-version # Python version pin (uv reads this on `uv sync`)
├── crm/ # Django project settings
│ ├── settings.py
│ ├── urls.py
@ -355,20 +364,36 @@ backend/
```bash
# Format code
black .
uv run black .
# Sort imports
isort .
uv run isort .
# Run tests
pytest
uv run pytest
```
### Managing Dependencies
```bash
# Add a runtime dependency (updates pyproject.toml + uv.lock)
uv add <package>
# Add a dev-only dependency (e.g. test or lint tool)
uv add --group dev <package>
# Remove a dependency
uv remove <package>
# Refresh the lockfile
uv lock --upgrade
```
### Creating a New App
1. Create the app:
```bash
python manage.py startapp myapp
uv run python manage.py startapp myapp
```
2. Add to `INSTALLED_APPS` in `crm/settings.py`
@ -390,8 +415,8 @@ pytest
5. Run migrations:
```bash
python manage.py makemigrations
python manage.py migrate
uv run python manage.py makemigrations
uv run python manage.py migrate
```
## Environment Variables Reference
@ -428,10 +453,10 @@ sudo -u postgres psql -l
```bash
# Show migration status
python manage.py showmigrations
uv run python manage.py showmigrations
# Reset migrations (development only)
python manage.py migrate --fake <app> zero
uv run python manage.py migrate --fake <app> zero
```
### Celery Not Processing Tasks
@ -441,7 +466,7 @@ python manage.py migrate --fake <app> zero
redis-cli ping
# Check Celery worker logs
celery -A crm worker --loglevel=DEBUG
uv run celery -A crm worker --loglevel=DEBUG
```
## License

View file

@ -97,7 +97,7 @@ class Account(AssignableMixin, BaseModel):
),
# Annual revenue must be non-negative
models.CheckConstraint(
check=Q(annual_revenue__gte=0) | Q(annual_revenue__isnull=True),
condition=Q(annual_revenue__gte=0) | Q(annual_revenue__isnull=True),
name="account_revenue_non_negative",
),
]

View file

@ -162,12 +162,12 @@ class Lead(AssignableMixin, BaseModel):
),
# Probability must be 0-100
models.CheckConstraint(
check=Q(probability__gte=0) & Q(probability__lte=100),
condition=Q(probability__gte=0) & Q(probability__lte=100),
name="lead_probability_range",
),
# Opportunity amount must be non-negative
models.CheckConstraint(
check=Q(opportunity_amount__gte=0) | Q(opportunity_amount__isnull=True),
condition=Q(opportunity_amount__gte=0) | Q(opportunity_amount__isnull=True),
name="lead_amount_non_negative",
),
]

View file

@ -128,12 +128,12 @@ class Opportunity(AssignableMixin, BaseModel):
constraints = [
# Probability must be 0-100
models.CheckConstraint(
check=Q(probability__gte=0) & Q(probability__lte=100),
condition=Q(probability__gte=0) & Q(probability__lte=100),
name="opportunity_probability_range",
),
# Amount must be non-negative
models.CheckConstraint(
check=Q(amount__gte=0) | Q(amount__isnull=True),
condition=Q(amount__gte=0) | Q(amount__isnull=True),
name="opportunity_amount_non_negative",
),
]

118
backend/pyproject.toml Normal file
View file

@ -0,0 +1,118 @@
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "django-crm"
version = "1.0.0"
description = "Open-source multi-tenant CRM platform built on Django REST Framework with row-level security."
readme = "README.md"
requires-python = ">=3.12"
license = { text = "MIT" }
authors = [
{ name = "MicroPyramid", email = "hello@micropyramid.com" },
]
keywords = [
"django",
"crm",
"drf",
"django-rest-framework",
"multi-tenant",
"saas",
"sales",
"leads",
"contacts",
"opportunity",
]
classifiers = [
"Development Status :: 5 - Production/Stable",
"Environment :: Web Environment",
"Framework :: Django",
"Framework :: Django :: 6.0",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Topic :: Office/Business",
"Topic :: Software Development :: Libraries :: Application Frameworks",
]
dependencies = [
"django>=6.0.5",
"celery>=5.6.2",
"python-dotenv>=1.2.1",
"django-cors-headers>=4.9.0",
"djangorestframework>=3.16.1",
"djangorestframework-simplejwt>=5.5.1",
"drf-spectacular>=0.29.0",
"django-ses>=4.7.1",
"psycopg2-binary>=2.9.11",
"whitenoise>=6.11.0",
"sentry-sdk>=2.53.0",
"django-storages>=1.14.6",
"django-crum>=0.7.9",
"disposable-email-domains>=0.0.162",
"redis>=7.2.0",
"pytz>=2025.2",
"requests>=2.32.5",
"faker>=40.4.0",
"python-dateutil>=2.9.0.post0",
"google-auth>=2.48.0",
# PDF generation (also needs system libs: cairo, pango, gdk-pixbuf, libffi, shared-mime-info)
"weasyprint>=68.1",
"cairocffi>=1.7.1",
# Production WSGI server
"gunicorn>=25.1.0",
]
[project.urls]
Homepage = "https://github.com/MicroPyramid/Django-CRM"
Repository = "https://github.com/MicroPyramid/Django-CRM"
Documentation = "https://github.com/MicroPyramid/Django-CRM#readme"
Issues = "https://github.com/MicroPyramid/Django-CRM/issues"
Changelog = "https://github.com/MicroPyramid/Django-CRM/releases"
[dependency-groups]
dev = [
"pytest>=9.0.2",
"pytest-django>=4.12.0",
"pytest-cov>=7.0.0",
]
[tool.hatch.build.targets.wheel]
packages = [
"accounts",
"cases",
"common",
"contacts",
"crm",
"invoices",
"leads",
"opportunity",
"orders",
"tasks",
]
[tool.hatch.build.targets.sdist]
include = [
"accounts/",
"cases/",
"common/",
"contacts/",
"crm/",
"invoices/",
"leads/",
"opportunity/",
"orders/",
"tasks/",
"static/",
"templates/",
"manage.py",
".env.example",
"pytest.ini",
"ruff.toml",
"README.md",
]

View file

@ -1,33 +0,0 @@
django==5.2.11
celery==5.6.2
python-dotenv==1.2.1
django-cors-headers==4.9.0
djangorestframework==3.16.1
djangorestframework-simplejwt==5.5.1
drf-spectacular==0.29.0
django-ses==4.7.1
psycopg2-binary==2.9.11
whitenoise==6.11.0
sentry-sdk==2.53.0
django_storages==1.14.6
django-crum==0.7.9
disposable-email-domains>=0.0.162
redis==7.2.0
pytz==2025.2
requests==2.32.5
faker==40.4.0
python-dateutil==2.9.0.post0
google-auth==2.48.0
# PDF Generation
weasyprint==68.1
cairocffi==1.7.1
# Production server
gunicorn==25.1.0
# Testing
pytest==9.0.2
pytest-django==4.12.0
pytest-cov==7.0.0

View file

@ -6,16 +6,15 @@ Management command to generate realistic test data for development and testing.
```bash
cd backend
source venv/bin/activate
# Basic usage (creates 1 org with default counts)
python manage.py seed_data --email admin@example.com
uv run python manage.py seed_data --email admin@example.com
```
## Full Options
```bash
python manage.py seed_data \
uv run python manage.py seed_data \
--email admin@example.com \ # Required: Admin user email
--orgs 2 \ # Number of orgs (default: 1)
--users-per-org 5 \ # Users per org (default: 3)
@ -37,16 +36,16 @@ python manage.py seed_data \
```bash
# Create 2 organizations with lots of data
python manage.py seed_data --email admin@example.com --orgs 2 --leads 100 --accounts 50
uv run python manage.py seed_data --email admin@example.com --orgs 2 --leads 100 --accounts 50
# Reproducible seeding (same data every time)
python manage.py seed_data --email admin@example.com --seed 42
uv run python manage.py seed_data --email admin@example.com --seed 42
# Clear all CRM data and reseed
python manage.py seed_data --email admin@example.com --clear --no-input
uv run python manage.py seed_data --email admin@example.com --clear --no-input
# Minimal data for quick testing
python manage.py seed_data --email admin@example.com --leads 5 --accounts 3 --contacts 5 --opportunities 3 --cases 2 --tasks 5
uv run python manage.py seed_data --email admin@example.com --leads 5 --accounts 3 --contacts 5 --opportunities 3 --cases 2 --tasks 5
```
## Features
@ -81,7 +80,7 @@ This allows you to reseed CRM data without losing user accounts.
## Sample Output
```
$ python manage.py seed_data --email admin@test.com --orgs 2 --seed 42
$ uv run python manage.py seed_data --email admin@test.com --orgs 2 --seed 42
Seeding CRM database...
Using seed: 42
@ -130,8 +129,4 @@ Total time: 1.22 seconds
## Dependencies
The script requires the `faker` package (already in requirements.txt):
```bash
pip install faker
```
The script requires the `faker` package, which is already declared in `pyproject.toml` and installed by `uv sync`. No extra step is needed.

View file

@ -1,46 +0,0 @@
import os
from setuptools import find_packages, setup
with open(os.path.join(os.path.dirname(__file__), "README.rst")) as readme:
README = readme.read()
# allow setup.py to be run from any path
os.chdir(os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir)))
PROJECT_NAME = "django_crm"
data_files = []
for dirpath, dirnames, filenames in os.walk(PROJECT_NAME):
for i, dirname in enumerate(dirnames):
if dirname.startswith("."):
del dirnames[i]
if "__init__.py" in filenames:
continue
elif filenames:
for f in filenames:
data_files.append(os.path.join(dirpath[len(PROJECT_NAME) + 1 :], f))
setup(
name="django-crm",
version="0.7.0",
packages=find_packages(exclude=["tests", "tests.*"]),
include_package_data=True,
description="An opensourse CRM developed on django framework",
long_description=README,
url="https://github.com/MicroPyramid/Django-CRM.git",
author="Micropyramid",
author_email="hello@micropyramid.com",
classifiers=[
"Development Status :: 4 - Beta",
"Framework :: Django",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.2",
"Programming Language :: Python :: 3.3",
"Programming Language :: Python :: 3.4",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
],
install_requires=[],
)

1490
backend/uv.lock Normal file

File diff suppressed because it is too large Load diff

View file

@ -14,45 +14,46 @@
"lint": "prettier --check . && eslint ."
},
"devDependencies": {
"@eslint/compat": "^2.0.3",
"@eslint/compat": "^2.1.0",
"@eslint/js": "^10.0.1",
"@internationalized/date": "^3.12.0",
"@lucide/svelte": "^0.577.0",
"@internationalized/date": "^3.12.1",
"@lucide/svelte": "^1.14.0",
"@sveltejs/adapter-node": "^5.5.4",
"@sveltejs/kit": "^2.55.0",
"@sveltejs/vite-plugin-svelte": "^6.2.4",
"@sveltejs/kit": "^2.59.1",
"@sveltejs/vite-plugin-svelte": "^7.1.2",
"@tailwindcss/typography": "^0.5.19",
"@tailwindcss/vite": "^4.2.1",
"@types/node": "^25.5.0",
"bits-ui": "^2.16.3",
"eslint": "^10.0.3",
"@tailwindcss/vite": "^4.3.0",
"@types/node": "^25.6.2",
"bits-ui": "^2.18.1",
"eslint": "^10.3.0",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-svelte": "^3.15.2",
"globals": "^17.4.0",
"eslint-plugin-svelte": "^3.17.1",
"globals": "^17.6.0",
"mode-watcher": "^1.1.0",
"prettier": "^3.8.1",
"prettier": "^3.8.3",
"prettier-plugin-svelte": "^3.5.1",
"prettier-plugin-tailwindcss": "^0.7.2",
"svelte": "^5.54.0",
"svelte-check": "^4.4.5",
"svelte-sonner": "^1.1.0",
"tailwindcss": "^4.2.1",
"typescript": "^5.9.3",
"vite": "^7.3.1"
"prettier-plugin-tailwindcss": "^0.8.0",
"svelte": "^5.55.5",
"svelte-check": "^4.4.8",
"svelte-sonner": "^1.1.1",
"tailwindcss": "^4.3.0",
"typescript": "^6.0.3",
"vite": "^8.0.11"
},
"pnpm": {
"onlyBuiltDependencies": [
"esbuild"
"esbuild",
"@sentry/cli"
],
"overrides": {
"svelte-eslint-parser>eslint-scope": "^9.1.0"
}
},
"dependencies": {
"@sentry/sveltekit": "^10.44.0",
"axios": "^1.13.6",
"@sentry/sveltekit": "^10.52.0",
"axios": "^1.16.0",
"clsx": "^2.1.1",
"libphonenumber-js": "^1.12.40",
"libphonenumber-js": "^1.13.0",
"svelte-dnd-action": "^0.9.69",
"tailwind-merge": "^3.5.0",
"tailwind-variants": "^3.2.2",

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,5 @@
allowBuilds:
'@sentry/cli': true
onlyBuiltDependencies:
- esbuild
- '@sentry/cli'

View file

@ -79,6 +79,7 @@
───────────────────────────────────────────────────────────────────────── */
--color-primary-default: #ea580c;
--color-primary-hover: #c2410c;
--color-primary-dark: #c2410c;
--color-primary-active: #9a3412;
--color-primary-light: #fff7ed;
@ -98,6 +99,7 @@
/* Success */
--color-success-default: #28a745;
--color-success-dark: #1e7e34;
--color-success-light: #e8f5e9;
/* Info */
@ -141,6 +143,7 @@
BORDER COLORS
───────────────────────────────────────────────────────────────────────── */
--border-default: #e0e0e0;
--border-subtle: #eeeeee;
--border-strong: #bdbdbd;
--border-focus: #ea580c;
--border-error: #dc3545;
@ -191,9 +194,13 @@
PRIORITY LEVELS
───────────────────────────────────────────────────────────────────────── */
--priority-urgent: #dc2626;
--priority-urgent-bg: #fee2e2;
--priority-high: #f97316;
--priority-high-bg: #ffedd5;
--priority-medium: #fbbf24;
--priority-medium-bg: #fef3c7;
--priority-low: #22c55e;
--priority-low-bg: #dcfce7;
--priority-none: #9ca3af;
/* ─────────────────────────────────────────────────────────────────────────
@ -344,6 +351,7 @@
───────────────────────────────────────────────────────────────────────── */
--sidebar-width: 260px;
--sidebar-width-collapsed: 72px;
--sidebar-width-icon: 3rem;
--drawer-width: 520px;
--header-height: 60px;
--content-padding: 28px;
@ -461,6 +469,7 @@
/* Primary color scale - Dark mode */
--color-primary-default: #fb923c;
--color-primary-hover: #fdba74;
--color-primary-dark: #f97316;
--color-primary-active: #f97316;
--color-primary-light: #431407;
@ -481,6 +490,7 @@
/* Border Colors - Dark mode */
--border-default: #3d3d3d;
--border-subtle: #2a2a2a;
--border-strong: #525252;
--border-focus: #fb923c;
--border-error: #f87171;
@ -517,6 +527,7 @@
--color-warning-dark: #fcd34d;
--color-warning-light: rgba(251, 191, 36, 0.15);
--color-success-default: #34d399;
--color-success-dark: #10b981;
--color-success-light: rgba(52, 211, 153, 0.15);
--color-info-default: #38bdf8;
--color-info-light: rgba(56, 189, 248, 0.15);