diff --git a/package-lock.json b/package-lock.json index dfe6c74..02c5649 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "app-ts", - "version": "2.4.33", + "version": "2.4.34", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "app-ts", - "version": "2.4.33", + "version": "2.4.34", "license": "ISC", "dependencies": { "@anthropic-ai/sdk": "^0.102.0", diff --git a/package.json b/package.json index 226e881..e8d9798 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "app-ts", - "version": "2.4.33", + "version": "2.4.34", "description": "", "main": "dist/server.js", "scripts": { diff --git a/prisma/seed.ts b/prisma/seed.ts index 4f38ba3..2ec5d54 100644 --- a/prisma/seed.ts +++ b/prisma/seed.ts @@ -391,12 +391,12 @@ async function main() { console.log(` Admin role now has all ${allPerms.length} permissions`); // 4. Ensure admin user exists - let adminUser = await prisma.users.findFirst({ + const adminUser = await prisma.users.findFirst({ where: { username: "admin" }, }); if (!adminUser) { const hash = bcrypt.hashSync("admin", 10); - adminUser = await prisma.users.create({ + await prisma.users.create({ data: { username: "admin", email: "admin@boha.local", diff --git a/scripts/migrate-received-invoices-to-nas.ts b/scripts/migrate-received-invoices-to-nas.ts index 38081b7..fc4eaf9 100644 --- a/scripts/migrate-received-invoices-to-nas.ts +++ b/scripts/migrate-received-invoices-to-nas.ts @@ -59,7 +59,7 @@ async function main() { // fails we KEEP file_data and report the row as failed. const expectedSize = rec.file_data.length; const readBack = nasFinancialsManager.readReceivedInvoice(result.filePath); - let writtenSize = -1; + let writtenSize: number; if (readBack) { writtenSize = readBack.data.length; } else { diff --git a/src/__tests__/plan.test.ts b/src/__tests__/plan.test.ts index e5b841b..d877e48 100644 --- a/src/__tests__/plan.test.ts +++ b/src/__tests__/plan.test.ts @@ -1090,26 +1090,6 @@ async function authPost(path: string, token: string, body: unknown) { }); } -async function authPatch(path: string, token: string, body: unknown) { - return app.inject({ - method: "PATCH", - url: path, - headers: { - Authorization: `Bearer ${token}`, - "Content-Type": "application/json", - }, - payload: body as object, - }); -} - -async function authDelete(path: string, token: string) { - return app.inject({ - method: "DELETE", - url: path, - headers: { Authorization: `Bearer ${token}` }, - }); -} - beforeAll(async () => { app = await buildApp(); diff --git a/src/admin/components/PlanCellModal.tsx b/src/admin/components/PlanCellModal.tsx index 9c25d38..75d73d0 100644 --- a/src/admin/components/PlanCellModal.tsx +++ b/src/admin/components/PlanCellModal.tsx @@ -148,7 +148,7 @@ const TrashIcon = ( ); export default function PlanCellModal(props: Props) { - const { open, mode, onClose } = props; + const { open, mode } = props; if (!open || mode.kind === "closed") return null; if (mode.kind === "view") return ; if (mode.kind === "day-in-range") diff --git a/src/admin/hooks/useAttendanceAdmin.ts b/src/admin/hooks/useAttendanceAdmin.ts index 5870eb1..27a41b7 100644 --- a/src/admin/hooks/useAttendanceAdmin.ts +++ b/src/admin/hooks/useAttendanceAdmin.ts @@ -234,7 +234,6 @@ function computeUserTotals( } const holidayDates = holidaysInMonth(yr, mo + 1); - const holidayCount = holidayDates.length; for (const uid of Object.keys(totals)) { const t = totals[uid]; @@ -476,14 +475,12 @@ function buildUserSectionHtml( // Sum of vacation/sick/unpaid hours (in hours, not minutes). The // "holiday" leave_type is auto-computed from Czech holidays further down // and no longer selectable in the UI, so it's deliberately omitted here. - let vacationHours = 0, - sickHours = 0; + let vacationHours = 0; for (const r of userRecords) { const lt = r.leave_type || "work"; if (lt === "work") continue; const h = Number(r.leave_hours) || 8; if (lt === "vacation") vacationHours += h; - else if (lt === "sick") sickHours += h; } // Odpracováno: floor(worked / 8) × 8. @@ -492,7 +489,6 @@ function buildUserSectionHtml( // Free Svátek: 8h per holiday date the user did not work. const holidayDates = holidaysInMonth(yr, mo); - const holidayCount = holidayDates.length; const workedDates = new Set( userRecords .filter((r) => (r.leave_type || "work") === "work") diff --git a/src/admin/lib/queries/mutations.ts b/src/admin/lib/queries/mutations.ts index 9a3504f..a93674b 100644 --- a/src/admin/lib/queries/mutations.ts +++ b/src/admin/lib/queries/mutations.ts @@ -92,7 +92,7 @@ async function performMutation(opts: { return result.data as TOut; } -export interface ApiMutationOptions { +export interface ApiMutationOptions { url: string | ((input: TIn) => string); method: HttpMethod | ((input: TIn) => HttpMethod); /** Query-key prefixes to invalidate on success. Broad per CLAUDE.md. */ @@ -122,7 +122,7 @@ export interface ApiMutationOptions { * as `ApiError` (with `.status` attached) so callers can branch on HTTP code. */ export function useApiMutation( - opts: ApiMutationOptions & + opts: ApiMutationOptions & Omit, "mutationFn">, ) { const { url, method, invalidate, envelope, onSuccess, ...rest } = opts; diff --git a/src/admin/lib/queries/plan.ts b/src/admin/lib/queries/plan.ts index e253cd3..f94a9bc 100644 --- a/src/admin/lib/queries/plan.ts +++ b/src/admin/lib/queries/plan.ts @@ -1,5 +1,4 @@ import { queryOptions } from "@tanstack/react-query"; -import apiFetch from "../../utils/api"; import { jsonQuery } from "../apiAdapter"; export interface PlanUser { diff --git a/src/admin/pages/AttendanceHistory.tsx b/src/admin/pages/AttendanceHistory.tsx index 495bc74..7dce0ad 100644 --- a/src/admin/pages/AttendanceHistory.tsx +++ b/src/admin/pages/AttendanceHistory.tsx @@ -1,5 +1,5 @@ import { useState, useMemo, useRef } from "react"; -import { useQuery, useQueryClient } from "@tanstack/react-query"; +import { useQuery } from "@tanstack/react-query"; import Box from "@mui/material/Box"; import Typography from "@mui/material/Typography"; import { useAuth } from "../context/AuthContext"; @@ -9,7 +9,6 @@ import { iconBadgeSx } from "../theme"; import { attendanceHistoryOptions, type AttendanceRecord, - type ProjectLogEntry, } from "../lib/queries/attendance"; import { formatDate, @@ -201,7 +200,6 @@ const renderProjectCell = (record: AttendanceRecord) => { export default function AttendanceHistory() { const { user, hasPermission } = useAuth(); - const queryClient = useQueryClient(); const { data: companySettings } = useQuery(companySettingsOptions()); const companyName = companySettings?.company_name || ""; const printRef = useRef(null); diff --git a/src/admin/pages/AttendanceLocation.tsx b/src/admin/pages/AttendanceLocation.tsx index 1cd5117..cc0c84d 100644 --- a/src/admin/pages/AttendanceLocation.tsx +++ b/src/admin/pages/AttendanceLocation.tsx @@ -26,10 +26,7 @@ const LocationMap = styled("div")(({ theme }) => ({ })); import { formatDate, formatTime } from "../utils/attendanceHelpers"; -import { - attendanceLocationOptions, - type LocationRecord, -} from "../lib/queries/attendance"; +import { attendanceLocationOptions } from "../lib/queries/attendance"; import { Button, Card, LoadingState, PageEnter, PageHeader } from "../ui"; const BackIcon = ( diff --git a/src/admin/pages/CompanySettings.tsx b/src/admin/pages/CompanySettings.tsx index 0c19d03..62fabcd 100644 --- a/src/admin/pages/CompanySettings.tsx +++ b/src/admin/pages/CompanySettings.tsx @@ -3,10 +3,7 @@ import { useQuery, useQueryClient } from "@tanstack/react-query"; import { useAlert } from "../context/AlertContext"; import { useAuth } from "../context/AuthContext"; import Forbidden from "../components/Forbidden"; -import { - companySettingsOptions, - type CompanySettingsData, -} from "../lib/queries/settings"; +import { companySettingsOptions } from "../lib/queries/settings"; import { bankAccountsOptions, type BankAccount } from "../lib/queries/common"; import Box from "@mui/material/Box"; import Typography from "@mui/material/Typography"; @@ -23,7 +20,6 @@ import { TextField, Select, CheckboxField, - StatusChip, FileUpload, LoadingState, type DataColumn, diff --git a/src/admin/pages/OrderDetail.tsx b/src/admin/pages/OrderDetail.tsx index 72e03b1..19896a8 100644 --- a/src/admin/pages/OrderDetail.tsx +++ b/src/admin/pages/OrderDetail.tsx @@ -1,14 +1,7 @@ import { useState, useEffect, useMemo, useRef } from "react"; import DOMPurify from "dompurify"; import { useQuery } from "@tanstack/react-query"; -import { - orderDetailOptions, - type OrderData, - type OrderItem, - type OrderSection, - type OrderInvoice, - type OrderProject, -} from "../lib/queries/orders"; +import { orderDetailOptions, type OrderItem } from "../lib/queries/orders"; import { useApiMutation } from "../lib/queries/mutations"; import { useAlert } from "../context/AlertContext"; import { useAuth } from "../context/AuthContext"; diff --git a/src/admin/pages/ProjectDetail.tsx b/src/admin/pages/ProjectDetail.tsx index 1bb1a8e..ef7b5bb 100644 --- a/src/admin/pages/ProjectDetail.tsx +++ b/src/admin/pages/ProjectDetail.tsx @@ -11,7 +11,6 @@ import MenuItem from "@mui/material/MenuItem"; import { projectDetailOptions, - type ProjectData, type ProjectNote, } from "../lib/queries/projects"; import { userListOptions, type User as ApiUser } from "../lib/queries/users"; diff --git a/src/admin/pages/Trips.tsx b/src/admin/pages/Trips.tsx index cb283e4..1a09de0 100644 --- a/src/admin/pages/Trips.tsx +++ b/src/admin/pages/Trips.tsx @@ -199,7 +199,7 @@ export default function Trips() { TripForm, { message?: string; error?: string } >({ - url: (formData) => + url: () => editingTrip ? `${API_BASE}/trips/${editingTrip.id}` : `${API_BASE}/trips`, method: () => (editingTrip ? "PUT" : "POST"), invalidate: ["trips", "vehicles"], diff --git a/src/admin/pages/WarehouseInventoryForm.tsx b/src/admin/pages/WarehouseInventoryForm.tsx index 368b1db..29bf853 100644 --- a/src/admin/pages/WarehouseInventoryForm.tsx +++ b/src/admin/pages/WarehouseInventoryForm.tsx @@ -23,7 +23,7 @@ interface InventoryItem { } function parseDecimal(raw: string): number { - const cleaned = raw.replace(/[eE+\-]/g, ""); + const cleaned = raw.replace(/[eE+-]/g, ""); const n = Number(cleaned); return Number.isFinite(n) ? n : 0; } diff --git a/src/admin/pages/WarehouseIssueForm.tsx b/src/admin/pages/WarehouseIssueForm.tsx index ced9e00..dc7633e 100644 --- a/src/admin/pages/WarehouseIssueForm.tsx +++ b/src/admin/pages/WarehouseIssueForm.tsx @@ -65,7 +65,7 @@ interface BatchesResponse { } function parseDecimal(raw: string): number { - const cleaned = raw.replace(/[eE+\-]/g, ""); + const cleaned = raw.replace(/[eE+-]/g, ""); const n = Number(cleaned); return Number.isFinite(n) ? n : 0; } diff --git a/src/admin/pages/WarehouseReceiptForm.tsx b/src/admin/pages/WarehouseReceiptForm.tsx index f71ff3f..38ccb9f 100644 --- a/src/admin/pages/WarehouseReceiptForm.tsx +++ b/src/admin/pages/WarehouseReceiptForm.tsx @@ -50,7 +50,7 @@ interface MovementItem { } function parseDecimal(raw: string): number { - const cleaned = raw.replace(/[eE+\-]/g, ""); + const cleaned = raw.replace(/[eE+-]/g, ""); const n = Number(cleaned); return Number.isFinite(n) ? n : 0; } diff --git a/src/middleware/auth.ts b/src/middleware/auth.ts index 09610c8..4120fb9 100644 --- a/src/middleware/auth.ts +++ b/src/middleware/auth.ts @@ -1,7 +1,6 @@ import { FastifyRequest, FastifyReply } from "fastify"; import { verifyAccessToken } from "../services/auth"; import { error } from "../utils/response"; -import { AuthData } from "../types"; export async function requireAuth( request: FastifyRequest, diff --git a/src/routes/admin/customers.ts b/src/routes/admin/customers.ts index 619e810..2ef7cd2 100644 --- a/src/routes/admin/customers.ts +++ b/src/routes/admin/customers.ts @@ -1,6 +1,6 @@ import { FastifyInstance } from "fastify"; import prisma from "../../config/database"; -import { requireAuth, requirePermission } from "../../middleware/auth"; +import { requirePermission } from "../../middleware/auth"; import { logAudit } from "../../services/audit"; import { success, error, parseId, paginated } from "../../utils/response"; import { parsePagination, buildPaginationMeta } from "../../utils/pagination"; diff --git a/src/routes/admin/invoices.ts b/src/routes/admin/invoices.ts index 03b0c6c..8ef2a50 100644 --- a/src/routes/admin/invoices.ts +++ b/src/routes/admin/invoices.ts @@ -13,7 +13,6 @@ import { markOverdueInvoices, listInvoices, getInvoiceListTotals, - getNextInvoiceNumberFormatted, getNextInvoiceNumberPreview, getInvoiceStats, getOrderDataForInvoice, diff --git a/src/routes/admin/project-files.ts b/src/routes/admin/project-files.ts index a2129cf..4096de9 100644 --- a/src/routes/admin/project-files.ts +++ b/src/routes/admin/project-files.ts @@ -199,7 +199,7 @@ export default async function projectFilesRoutes( const subPath = parsedQuery.data.path || ""; const rawFileName = file.filename; - const fileName = rawFileName.replace(/[\/\\:*?"<>|]/g, "_"); + const fileName = rawFileName.replace(/[/\\:*?"<>|]/g, "_"); const buffer = await file.toBuffer(); const err = await fm.uploadFile( diff --git a/src/routes/admin/warehouse.ts b/src/routes/admin/warehouse.ts index 79873b4..f3e1c6d 100644 --- a/src/routes/admin/warehouse.ts +++ b/src/routes/admin/warehouse.ts @@ -2,7 +2,7 @@ import { FastifyInstance } from "fastify"; import multipart from "@fastify/multipart"; import prisma from "../../config/database"; import { config } from "../../config/env"; -import { requireAuth, requirePermission } from "../../middleware/auth"; +import { requirePermission } from "../../middleware/auth"; import { logAudit } from "../../services/audit"; import { success, error, parseId, paginated } from "../../utils/response"; import { contentDisposition } from "../../utils/content-disposition"; @@ -2209,7 +2209,7 @@ export default async function warehouseRoutes( // Build items with auto-filled system_qty and calculated difference const itemsData = await Promise.all( body.items.map(async (item) => { - let systemQty = 0; + let systemQty: number; if (item.location_id != null) { // Get stock at specific location @@ -2318,7 +2318,7 @@ export default async function warehouseRoutes( if (body.items) { itemsData = await Promise.all( body.items.map(async (item) => { - let systemQty = 0; + let systemQty: number; if (item.location_id != null) { const loc = await prisma.sklad_item_locations.findUnique({ diff --git a/src/services/attendance.service.ts b/src/services/attendance.service.ts index 6a9a5cc..6a17a04 100644 --- a/src/services/attendance.service.ts +++ b/src/services/attendance.service.ts @@ -791,7 +791,7 @@ export async function getProjectReport(year: number) { // Use detailed project logs (started_at/ended_at or hours/minutes) for (const log of rec.attendance_project_logs) { - let hours = 0; + let hours: number; if (log.hours != null || log.minutes != null) { hours = (Number(log.hours) || 0) + (Number(log.minutes) || 0) / 60; } else if (log.started_at && log.ended_at) { diff --git a/src/services/auth.ts b/src/services/auth.ts index d723e93..57805d2 100644 --- a/src/services/auth.ts +++ b/src/services/auth.ts @@ -1,7 +1,7 @@ import crypto from "crypto"; import jwt from "jsonwebtoken"; import bcrypt from "bcryptjs"; -import { FastifyRequest, FastifyReply } from "fastify"; +import { FastifyRequest } from "fastify"; import prisma from "../config/database"; import { config } from "../config/env"; import { AuthData, JwtPayload } from "../types"; diff --git a/src/utils/pagination.ts b/src/utils/pagination.ts index eb80c6c..460d49d 100644 --- a/src/utils/pagination.ts +++ b/src/utils/pagination.ts @@ -1,4 +1,4 @@ -import { PaginationQuery, PaginationMeta } from "../types"; +import { PaginationMeta } from "../types"; /** * Parse common pagination/sort/search query params.