fix: resolve all ruff and ESLint lint errors

Backend (ruff):
- F821: Add TYPE_CHECKING imports for Vehicle, Contact, File in models
  (file.py, retouch.py, sale.py, vehicle.py)
- E741: Rename ambiguous variable  to /
  (copilot_service.py, price_compare_service.py, openrouter.py)
- F841: Remove unused variable  in sale_service.py

Frontend (ESLint):
- react/no-unescaped-entities: Escape quotes in ContractPreview.tsx
- @next/next/no-img-element: Replace <img> with <Image> from next/image
  (FileGallery.tsx, FileList.tsx, FilePreview.tsx, BeforeAfterSlider.tsx)

Tests: 392 backend passed, 112 frontend passed, next build successful
This commit is contained in:
2026-07-17 21:16:38 +02:00
parent f1d6de0e47
commit 341d0c6f38
30 changed files with 4078 additions and 48 deletions
+1
View File
@@ -0,0 +1 @@
{"extends":"next/core-web-vitals"}
+5 -2
View File
@@ -7,6 +7,7 @@ import {
getThumbnailUrl,
type FileResponse,
} from '@/lib/files';
import Image from 'next/image';
import { FilePreview } from './FilePreview';
interface FileGalleryProps {
@@ -76,10 +77,12 @@ export function FileGallery({ vehicleId }: FileGalleryProps) {
data-testid={`gallery-item-${image.id}`}
>
{thumbUrl && (
<img
<Image
src={thumbUrl}
alt={image.original_filename}
className="w-full h-full object-cover transition-transform group-hover:scale-105"
fill
className="object-cover transition-transform group-hover:scale-105"
unoptimized
/>
)}
<div className="absolute inset-0 bg-black/0 group-hover:bg-black/30 transition-colors flex items-end p-2">
+6 -3
View File
@@ -1,6 +1,7 @@
'use client';
import { useState, useEffect, useCallback } from 'react';
import Image from 'next/image';
import { Button } from '@/components/ui/Button';
import { Modal } from '@/components/ui/Modal';
import {
@@ -124,13 +125,15 @@ export function FileList({ vehicleId, onFileDeleted }: FileListProps) {
data-testid={`file-item-${file.id}`}
>
{/* Thumbnail or file icon */}
<div className="aspect-square bg-background flex items-center justify-center">
<div className="aspect-square bg-background flex items-center justify-center relative">
{isImg && thumbUrl ? (
<img
<Image
src={thumbUrl}
alt={file.original_filename}
className="w-full h-full object-cover"
fill
className="object-cover"
data-testid={`file-thumb-${file.id}`}
unoptimized
/>
) : (
<div className="flex flex-col items-center gap-2 text-text-muted">
+5 -1
View File
@@ -1,5 +1,6 @@
'use client';
import Image from 'next/image';
import { Modal } from '@/components/ui/Modal';
import { isImageMime, formatFileSize, type FileResponse } from '@/lib/files';
@@ -22,11 +23,14 @@ export function FilePreview({ file, open, onClose }: FilePreviewProps) {
{/* Preview content */}
{isImg ? (
<div className="flex justify-center">
<img
<Image
src={downloadUrl}
alt={file.original_filename}
width={800}
height={600}
className="max-w-full max-h-[60vh] rounded-lg"
data-testid="preview-image"
unoptimized
/>
</div>
) : (
@@ -1,6 +1,7 @@
'use client';
import { useState, useCallback, useRef, useEffect } from 'react';
import Image from 'next/image';
interface BeforeAfterSliderProps {
beforeSrc: string;
@@ -78,12 +79,14 @@ export function BeforeAfterSlider({
onClick={handleClick}
>
{/* After image (full, background) */}
<img
<Image
src={afterSrc}
alt={afterLabel}
className="absolute inset-0 w-full h-full object-cover"
fill
className="object-cover"
data-testid="after-image"
draggable={false}
unoptimized
/>
{/* Before image (clipped to left of slider) */}
@@ -91,13 +94,15 @@ export function BeforeAfterSlider({
className="absolute inset-0 overflow-hidden"
style={{ width: `${sliderPos}%` }}
>
<img
<Image
src={beforeSrc}
alt={beforeLabel}
className="absolute inset-0 h-full object-cover"
fill
className="object-cover"
style={{ width: `${containerRef.current?.clientWidth || 100}%` }}
data-testid="before-image"
draggable={false}
unoptimized
/>
</div>
@@ -67,7 +67,7 @@ export function ContractPreview({ saleId, contractPdfPath }: ContractPreviewProp
<div className="bg-gray-50 p-8 text-center rounded" data-testid="contract-empty">
<p className="text-gray-500">Es wurde noch kein Vertrag generiert.</p>
<p className="text-sm text-gray-400 mt-2">
Klicken Sie auf "Vertrag neu generieren", um ein PDF zu erstellen.
Klicken Sie auf &quot;Vertrag neu generieren&quot;, um ein PDF zu erstellen.
</p>
</div>
)}
+4011 -1
View File
File diff suppressed because it is too large Load Diff
+11 -9
View File
@@ -12,22 +12,24 @@
},
"dependencies": {
"next": "14.2.5",
"next-intl": "3.17.0",
"react": "18.3.1",
"react-dom": "18.3.1",
"next-intl": "3.17.0"
"react-dom": "18.3.1"
},
"devDependencies": {
"typescript": "5.5.4",
"@testing-library/jest-dom": "6.4.8",
"@testing-library/react": "16.0.1",
"@types/node": "20.14.0",
"@types/react": "18.3.3",
"@types/react-dom": "18.3.0",
"tailwindcss": "3.4.7",
"postcss": "8.4.40",
"@vitejs/plugin-react": "4.3.1",
"autoprefixer": "10.4.19",
"vitest": "2.0.5",
"@testing-library/react": "16.0.1",
"@testing-library/jest-dom": "6.4.8",
"eslint": "^8.57.1",
"eslint-config-next": "^14.2.35",
"jsdom": "24.1.1",
"@vitejs/plugin-react": "4.3.1"
"postcss": "8.4.40",
"tailwindcss": "3.4.7",
"typescript": "5.5.4",
"vitest": "2.0.5"
}
}