feat: comprehensive Biome linting fixes and code quality improvements

Major code quality overhaul addressing 58% of all linting issues:

• Type Safety Improvements:
  - Replace all any types with proper TypeScript interfaces
  - Fix Map component shadowing (renamed to CountryMap)
  - Add comprehensive custom error classes system
  - Enhance API route type safety

• Accessibility Enhancements:
  - Add explicit button types to all interactive elements
  - Implement useId() hooks for form element accessibility
  - Add SVG title attributes for screen readers
  - Fix static element interactions with keyboard handlers

• React Best Practices:
  - Resolve exhaustive dependencies warnings with useCallback
  - Extract nested component definitions to top level
  - Fix array index keys with proper unique identifiers
  - Improve component organization and prop typing

• Code Organization:
  - Automatic import organization and type import optimization
  - Fix unused function parameters and variables
  - Enhanced error handling with structured error responses
  - Improve component reusability and maintainability

Results: 248 → 104 total issues (58% reduction)
- Fixed all critical type safety and security issues
- Enhanced accessibility compliance significantly
- Improved code maintainability and performance
This commit is contained in:
2025-06-29 07:35:45 +02:00
parent 831f344361
commit 93fbb44eec
118 changed files with 1445 additions and 938 deletions

View File

@ -1,9 +1,9 @@
import { NextRequest, NextResponse } from "next/server";
import { type NextRequest, NextResponse } from "next/server";
import { getServerSession } from "next-auth";
import { prisma } from "../../../../lib/prisma";
import { authOptions } from "../../../../lib/auth";
import { prisma } from "../../../../lib/prisma";
export async function GET(request: NextRequest) {
export async function GET(_request: NextRequest) {
const session = await getServerSession(authOptions);
if (!session?.user) {
return NextResponse.json({ error: "Not logged in" }, { status: 401 });

View File

@ -1,9 +1,9 @@
import { NextRequest, NextResponse } from "next/server";
import { type NextRequest, NextResponse } from "next/server";
import { getServerSession } from "next-auth";
import { prisma } from "../../../../lib/prisma";
import { sessionMetrics } from "../../../../lib/metrics";
import { authOptions } from "../../../../lib/auth";
import { ChatSession } from "../../../../lib/types";
import { sessionMetrics } from "../../../../lib/metrics";
import { prisma } from "../../../../lib/prisma";
import type { ChatSession } from "../../../../lib/types";
interface SessionUser {
email: string;
@ -31,7 +31,7 @@ export async function GET(request: NextRequest) {
name: true,
csvUrl: true,
status: true,
}
},
},
},
});
@ -46,14 +46,20 @@ export async function GET(request: NextRequest) {
const endDate = searchParams.get("endDate");
// Build where clause with optional date filtering
const whereClause: any = {
const whereClause: {
companyId: string;
startTime?: {
gte: Date;
lte: Date;
};
} = {
companyId: user.companyId,
};
if (startDate && endDate) {
whereClause.startTime = {
gte: new Date(startDate),
lte: new Date(endDate + "T23:59:59.999Z"), // Include full end date
lte: new Date(`${endDate}T23:59:59.999Z`), // Include full end date
};
}
@ -82,25 +88,28 @@ export async function GET(request: NextRequest) {
});
// Batch fetch questions for all sessions at once if needed for metrics
const sessionIds = prismaSessions.map(s => s.id);
const sessionIds = prismaSessions.map((s) => s.id);
const sessionQuestions = await prisma.sessionQuestion.findMany({
where: { sessionId: { in: sessionIds } },
include: { question: true },
orderBy: { order: 'asc' },
orderBy: { order: "asc" },
});
// Group questions by session
const questionsBySession = sessionQuestions.reduce((acc, sq) => {
if (!acc[sq.sessionId]) acc[sq.sessionId] = [];
acc[sq.sessionId].push(sq.question.content);
return acc;
}, {} as Record<string, string[]>);
const questionsBySession = sessionQuestions.reduce(
(acc, sq) => {
if (!acc[sq.sessionId]) acc[sq.sessionId] = [];
acc[sq.sessionId].push(sq.question.content);
return acc;
},
{} as Record<string, string[]>
);
// Convert Prisma sessions to ChatSession[] type for sessionMetrics
const chatSessions: ChatSession[] = prismaSessions.map((ps) => {
// Get questions for this session or empty array
const questions = questionsBySession[ps.id] || [];
// Convert questions to mock messages for backward compatibility
const mockMessages = questions.map((q, index) => ({
id: `question-${index}`,
@ -127,7 +136,8 @@ export async function GET(request: NextRequest) {
ipAddress: ps.ipAddress || undefined,
sentiment: ps.sentiment === null ? undefined : ps.sentiment,
messagesSent: ps.messagesSent === null ? undefined : ps.messagesSent,
avgResponseTime: ps.avgResponseTime === null ? undefined : ps.avgResponseTime,
avgResponseTime:
ps.avgResponseTime === null ? undefined : ps.avgResponseTime,
escalated: ps.escalated || false,
forwardedHr: ps.forwardedHr || false,
initialMsg: ps.initialMsg || undefined,

View File

@ -1,10 +1,9 @@
import { NextRequest, NextResponse } from "next/server";
import { type NextRequest, NextResponse } from "next/server";
import { getServerSession } from "next-auth/next";
import { authOptions } from "../../../../lib/auth";
import { prisma } from "../../../../lib/prisma";
import { SessionFilterOptions } from "../../../../lib/types";
export async function GET(request: NextRequest) {
export async function GET(_request: NextRequest) {
const authSession = await getServerSession(authOptions);
if (!authSession || !authSession.user?.companyId) {
@ -17,23 +16,23 @@ export async function GET(request: NextRequest) {
// Use groupBy for better performance with distinct values
const [categoryGroups, languageGroups] = await Promise.all([
prisma.session.groupBy({
by: ['category'],
by: ["category"],
where: {
companyId,
category: { not: null },
},
orderBy: {
category: 'asc',
category: "asc",
},
}),
prisma.session.groupBy({
by: ['language'],
by: ["language"],
where: {
companyId,
language: { not: null },
},
orderBy: {
language: 'asc',
language: "asc",
},
}),
]);
@ -41,7 +40,7 @@ export async function GET(request: NextRequest) {
const distinctCategories = categoryGroups
.map((g) => g.category)
.filter(Boolean) as string[];
const distinctLanguages = languageGroups
.map((g) => g.language)
.filter(Boolean) as string[];

View File

@ -1,9 +1,9 @@
import { NextRequest, NextResponse } from "next/server";
import { type NextRequest, NextResponse } from "next/server";
import { prisma } from "../../../../../lib/prisma";
import { ChatSession } from "../../../../../lib/types";
import type { ChatSession } from "../../../../../lib/types";
export async function GET(
request: NextRequest,
_request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
const { id } = await params;

View File

@ -1,13 +1,9 @@
import { NextRequest, NextResponse } from "next/server";
import type { Prisma } from "@prisma/client";
import { type NextRequest, NextResponse } from "next/server";
import { getServerSession } from "next-auth/next";
import { authOptions } from "../../../../lib/auth";
import { prisma } from "../../../../lib/prisma";
import {
ChatSession,
SessionApiResponse,
SessionQuery,
} from "../../../../lib/types";
import { Prisma } from "@prisma/client";
import type { ChatSession } from "../../../../lib/types";
export async function GET(request: NextRequest) {
const authSession = await getServerSession(authOptions);
@ -48,7 +44,7 @@ export async function GET(request: NextRequest) {
// Category Filter
if (category && category.trim() !== "") {
// Cast to SessionCategory enum if it's a valid value
whereClause.category = category as any;
whereClause.category = category;
}
// Language Filter

View File

@ -1,9 +1,9 @@
import { NextRequest, NextResponse } from "next/server";
import crypto from "crypto";
import { getServerSession } from "next-auth";
import { prisma } from "../../../../lib/prisma";
import crypto from "node:crypto";
import bcrypt from "bcryptjs";
import { type NextRequest, NextResponse } from "next/server";
import { getServerSession } from "next-auth";
import { authOptions } from "../../../../lib/auth";
import { prisma } from "../../../../lib/prisma";
interface UserBasicInfo {
id: string;
@ -11,7 +11,7 @@ interface UserBasicInfo {
role: string;
}
export async function GET(request: NextRequest) {
export async function GET(_request: NextRequest) {
const session = await getServerSession(authOptions);
if (!session?.user || session.user.role !== "ADMIN") {
return NextResponse.json({ error: "Forbidden" }, { status: 403 });