From 0117ba34f8ac58a9a5a56ccd67d69f54fe494cfd Mon Sep 17 00:00:00 2001 From: gitea Date: Thu, 27 Nov 2025 11:19:48 -0300 Subject: [PATCH] =?UTF-8?q?Frontend=20v1.0.68:=20-=20Agregada=20funcionali?= =?UTF-8?q?dad=20de=20edici=C3=B3n=20de=20checklists=20-=20Nuevo=20modal?= =?UTF-8?q?=20para=20editar=20nombre,=20descripci=C3=B3n,=20modo=20IA=20y?= =?UTF-8?q?=20scoring=20-=20Bot=C3=B3n=20"=E2=9C=8F=EF=B8=8F=20Editar"=20e?= =?UTF-8?q?n=20cada=20checklist=20(solo=20admins)=20-=20Mejora=20en=20la?= =?UTF-8?q?=20gesti=C3=B3n=20de=20checklists=20en=20el=20panel=20de=20admi?= =?UTF-8?q?nistraci=C3=B3n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Backend v1.0.68: - Actualización de versión para sincronizar con frontend - Endpoint PUT /api/checklists/{id} ya soportaba la funcionalidad --- .github/copilot-instructions.md | 132 ++++++++++++++++++++++++++ backend/app/main.py | 2 +- frontend/package.json | 2 +- frontend/src/App.jsx | 158 +++++++++++++++++++++++++++++++- gitUpdate.ps1 | 2 +- 5 files changed, 292 insertions(+), 4 deletions(-) create mode 100644 .github/copilot-instructions.md diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..e29572b --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,132 @@ +# No olvides dar lo comentarios de los cambios que se hicieron para el backend y el front en para los comentarios de git +# Siempre actuliza la version del front y del back la version del front esta en el archivo package.json y la del backend en el archivo main.py en una variable llamada BACKEND_VERSION + +# Ayudetec - Intelligent Checklist System for Automotive Workshops + +## Architecture Overview + +**Tech Stack**: FastAPI (Python 3.11) + React 18 + PostgreSQL 15 + MinIO/S3 +**Deployment**: Docker Compose for dev, Docker Stack for production +**Key Feature**: AI-powered inspection analysis with OpenAI/Gemini integration + +### Service Structure +- `backend/` - FastAPI monolith with JWT auth, S3 file storage, PDF generation +- `frontend/` - React SPA with Vite, TailwindCSS, client-side routing +- `postgres` - Main data store with checklist templates, inspections, answers +- MinIO/S3 - Image and PDF storage (configurable endpoint) + +### Core Data Model +**Users** (`role`: admin/mechanic/asesor) → **Checklists** (templates with questions) → **Inspections** (mechanic executions) → **Answers** (responses with photos/scores) + +- **Permissions**: `ChecklistPermission` table controls mechanic access (empty = global access) +- **Nested Questions**: 5-level deep conditional subquestions via `parent_question_id` + `show_if_answer` +- **Scoring**: Auto-calculated from answer points, stored in `Inspection.score/percentage` +- **Question Types**: boolean, single_choice, multiple_choice, scale, text, number, date, time (config in `Question.options` JSON) + +## Development Workflows + +### Running Locally +```powershell +docker-compose up -d # Start all services +docker-compose logs backend # Debug backend issues +``` +**URLs**: Frontend `http://localhost:5173`, Backend API `http://localhost:8000/docs` + +### Database Initialization +Use `init_users.py` to create default admin/mechanic users: +```powershell +docker-compose exec backend python init_users.py +``` +**Default credentials**: `admin/admin123`, `mecanico/mec123` + +### Migrations +Manual SQL scripts in `migrations/` - run via: +```powershell +docker-compose exec backend python -c " +from app.core.database import engine +with open('migrations/your_migration.sql') as f: + engine.execute(f.read()) +" +``` + +### Building for Production +```powershell +.\build-and-push.ps1 # Builds images, pushes to Docker Hub (configured in script) +``` +Then deploy with `docker-compose.prod.yml` or `docker-stack.yml` (Swarm mode) + +## Project-Specific Conventions + +### API Architecture (`backend/app/main.py`) +- **Single 2800+ line file** - all endpoints in main.py (no routers/controllers split) +- Auth via `get_current_user()` dependency returning `models.User` +- File uploads use boto3 S3 client configured from `app/core/config.py` (MinIO compatible) +- PDF generation inline with ReportLab (starts ~line 1208, function `generate_pdf`) + +### AI Integration Modes (see `AI_FUNCTIONALITY.md`) +- `ai_mode` on Checklist: `"off"` (manual) | `"assisted"` (suggestions) | `"copilot"` (auto-complete) +- AI analysis triggered on photo upload, stored in `Answer.ai_analysis` JSON +- Webhook notifications to n8n on completion: `send_completed_inspection_to_n8n()` + +### Frontend Patterns (`frontend/src/App.jsx`) +- **5400+ line single-file component** - all views in App.jsx (Login, Dashboard, Admin panels) +- Auth state in `localStorage` (token + user object) +- API calls use `fetch()` with `import.meta.env.VITE_API_URL` base +- Signature capture via `react-signature-canvas` (saved as base64) + +### Permission Model +When fetching checklists: +- Admins see all checklists +- Mechanics only see checklists with either: + - No `ChecklistPermission` records (global access), OR + - A permission record linking that mechanic +- Implement via JOIN in `GET /api/checklists` endpoint + +### Environment Variables +Critical settings in `.env`: +- `DATABASE_URL` - Postgres connection string +- `SECRET_KEY` - JWT signing (min 32 chars) +- `OPENAI_API_KEY` / `GEMINI_API_KEY` - Optional for AI features +- `MINIO_*` vars - S3-compatible storage (MinIO/AWS S3) +- `NOTIFICACION_ENDPOINT` - n8n webhook for inspection events +- `ALLOWED_ORIGINS` - Comma-separated CORS origins + +## Key Integration Points + +### S3/MinIO Storage +- Configured globally in `main.py` via `boto3.client()` + `Config(signature_version='s3v4')` +- Two buckets: `MINIO_IMAGE_BUCKET` (photos), `MINIO_PDF_BUCKET` (reports) +- Upload pattern: generate UUID filename, `s3_client.upload_fileobj()`, store URL in DB + +### PDF Generation +- ReportLab library generates inspection reports with photos, signatures, scoring +- Triggered on inspection completion, stored to S3, URL saved in `Inspection.pdf_url` +- Uses checklist logo from `Checklist.logo_url` if available + +### Webhook Notifications +When `Question.send_notification = true`, answering triggers `send_answer_notification()` to `NOTIFICACION_ENDPOINT` +On inspection completion, sends full data via `send_completed_inspection_to_n8n()` + +## Common Gotchas + +- **No Alembic auto-migrations** - use manual SQL scripts in `migrations/` +- **Token expiry is 7 days** - set in `config.ACCESS_TOKEN_EXPIRE_MINUTES = 10080` +- **Photos stored as S3 URLs** - not base64 in DB (except signatures) +- **Nested questions limited to 5 levels** - enforced in `Question.depth_level` +- **PowerShell scripts** - Windows-first project (see `.ps1` build scripts) +- **Frontend has no state management** - uses React `useState` only, no Redux/Context + +## Checklist Management + +### Editing Checklists +- Admins can edit checklist name, description, AI mode, and scoring settings via "✏️ Editar" button +- Edit modal (`showEditChecklistModal`) uses PUT `/api/checklists/{id}` endpoint +- Backend endpoint supports partial updates via `exclude_unset=True` in Pydantic model +- Logo and permissions have separate management modals for focused UI + +## Example Tasks + +**Add new question type**: Update `Question.type` validation, modify `QuestionTypeEditor.jsx` UI, handle in `QuestionAnswerInput.jsx` +**Change scoring logic**: Edit `backend/app/main.py` answer submission endpoint, recalculate `Inspection.score` +**Add new user role**: Update `User.role` enum, modify `get_current_user()` checks, adjust frontend role conditionals in `App.jsx` +**Edit checklist properties**: Use existing PUT endpoint, add fields to `editChecklistData` state, update modal form diff --git a/backend/app/main.py b/backend/app/main.py index f54f705..7617784 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -204,7 +204,7 @@ def send_completed_inspection_to_n8n(inspection, db): # No lanzamos excepción para no interrumpir el flujo normal -BACKEND_VERSION = "1.0.67" +BACKEND_VERSION = "1.0.68" app = FastAPI(title="Checklist Inteligente API", version=BACKEND_VERSION) # S3/MinIO configuration diff --git a/frontend/package.json b/frontend/package.json index a041182..e43766d 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { "name": "checklist-frontend", "private": true, - "version": "1.0.67", + "version": "1.0.68", "type": "module", "scripts": { "dev": "vite", diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index edab63e..17080d2 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -1735,6 +1735,7 @@ function ChecklistsTab({ checklists, user, onChecklistCreated, onStartInspection const [showCreateModal, setShowCreateModal] = useState(false) const [showQuestionsModal, setShowQuestionsModal] = useState(false) const [showEditPermissionsModal, setShowEditPermissionsModal] = useState(false) + const [showEditChecklistModal, setShowEditChecklistModal] = useState(false) const [showLogoModal, setShowLogoModal] = useState(false) const [selectedChecklist, setSelectedChecklist] = useState(null) const [creating, setCreating] = useState(false) @@ -1752,6 +1753,12 @@ function ChecklistsTab({ checklists, user, onChecklistCreated, onStartInspection scoring_enabled: true, mechanic_ids: [] }) + const [editChecklistData, setEditChecklistData] = useState({ + name: '', + description: '', + ai_mode: 'off', + scoring_enabled: true + }) const [editPermissionsData, setEditPermissionsData] = useState({ mechanic_ids: [] }) @@ -1875,6 +1882,39 @@ function ChecklistsTab({ checklists, user, onChecklistCreated, onStartInspection } } + const handleEditChecklist = async (e) => { + e.preventDefault() + setUpdating(true) + + try { + const token = localStorage.getItem('token') + const API_URL = import.meta.env.VITE_API_URL || '' + + const response = await fetch(`${API_URL}/api/checklists/${selectedChecklist.id}`, { + method: 'PUT', + headers: { + 'Authorization': `Bearer ${token}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify(editChecklistData), + }) + + if (response.ok) { + setShowEditChecklistModal(false) + setSelectedChecklist(null) + setEditChecklistData({ name: '', description: '', ai_mode: 'off', scoring_enabled: true }) + onChecklistCreated() // Reload checklists + } else { + alert('Error al actualizar checklist') + } + } catch (error) { + console.error('Error:', error) + alert('Error al actualizar checklist') + } finally { + setUpdating(false) + } + } + const handleUploadLogo = async (e) => { const file = e.target.files[0] if (!file) return @@ -2078,9 +2118,25 @@ function ChecklistsTab({ checklists, user, onChecklistCreated, onStartInspection )} -
+
{user.role === 'admin' && ( <> +
)} + {/* Modal Editar Checklist */} + {showEditChecklistModal && selectedChecklist && ( +
+
+
+

✏️ Editar Checklist

+ +
+
+ + setEditChecklistData({ ...editChecklistData, name: e.target.value })} + className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-transparent" + placeholder="Ej: Inspección Pre-entrega" + required + /> +
+ +
+ +