diff --git a/frontend/src/components/finance/PayrollDailyWorkSection.tsx b/frontend/src/components/finance/PayrollDailyWorkSection.tsx index 816d0c9a..2fcec274 100644 --- a/frontend/src/components/finance/PayrollDailyWorkSection.tsx +++ b/frontend/src/components/finance/PayrollDailyWorkSection.tsx @@ -7,6 +7,7 @@ import { Loader2, Pencil, Save, + X, } from "lucide-react"; import { useCallback, useEffect, useMemo, useState, type FormEvent } from "react"; import { createPortal } from "react-dom"; @@ -70,6 +71,11 @@ export function PayrollDailyWorkSection({ const [saving, setSaving] = useState(false); const [error, setError] = useState(null); const [notice, setNotice] = useState(null); + const [editingEntryId, setEditingEntryId] = useState(null); + const [inlineActualPoints, setInlineActualPoints] = useState(""); + const [inlineNote, setInlineNote] = useState(""); + const [inlineSaving, setInlineSaving] = useState(false); + const [inlineError, setInlineError] = useState(null); const fillForm = useCallback(( payload: PayrollOwnDailyWorkWorkspace, @@ -135,6 +141,8 @@ export function PayrollDailyWorkSection({ ); const selectWorkDate = (date: string) => { + setEditingEntryId(null); + setInlineError(null); setWorkDate(date); const entry = currentEntryForDate(workspace?.entries || [], date); setActualPoints(normalizePayrollPoints(entry?.actual_points)); @@ -180,6 +188,58 @@ export function PayrollDailyWorkSection({ } }; + const startInlineEdit = (entry: PayrollOwnDailyWorkEntry) => { + selectWorkDate(entry.work_date); + setEditingEntryId(entry.id); + setInlineActualPoints(normalizePayrollPoints(entry.actual_points)); + setInlineNote(entry.note || ""); + setInlineError(null); + }; + + const cancelInlineEdit = () => { + setEditingEntryId(null); + setInlineError(null); + }; + + const submitInlineEdit = async ( + event: FormEvent, + entry: PayrollOwnDailyWorkEntry, + ) => { + event.preventDefault(); + if (!selectedPeriod?.editable || inlineSaving) return; + + const numericInlineActual = Number(inlineActualPoints); + if ( + !inlineActualPoints.trim() + || !Number.isFinite(numericInlineActual) + || numericInlineActual < 0 + ) { + setInlineError("Укажите фактические баллы: число не меньше нуля."); + return; + } + + setInlineSaving(true); + setInlineError(null); + setError(null); + setNotice(null); + try { + await apiClient.saveMyPayrollDailyWorkEntry({ + period_id: selectedPeriod.id, + work_date: entry.work_date, + actual_points: normalizePayrollPoints(inlineActualPoints), + note: inlineNote.trim(), + expected_lock_version: entry.lock_version, + }); + await loadWorkspace(selectedPeriod.id, entry.work_date); + setEditingEntryId(null); + setNotice("Выработка за день обновлена."); + } catch (saveError) { + setInlineError(getPayrollAdminError(saveError, "Не удалось сохранить ежедневную выработку.")); + } finally { + setInlineSaving(false); + } + }; + if (loading && !workspace) { return (
@@ -210,7 +270,7 @@ export function PayrollDailyWorkSection({ selectedPeriod={selectedPeriod} selectedDate={workDate} entries={workspace.entries} - disabled={loading || saving} + disabled={loading || saving || inlineSaving} onSelectDate={selectAvailableWorkDate} onSelectPeriod={(periodId) => void loadWorkspace(periodId)} /> @@ -265,25 +325,106 @@ export function PayrollDailyWorkSection({ {workspace.entries.length ? (
- {workspace.entries.map((entry) => ( - + +
+ + +
+ + +
+ + {inlineError ? ( +
{inlineError}
+ ) : null} + + ) : ( +
+ + + {selectedPeriod?.editable ? ( + + ) : null} +
))} ) : ( @@ -339,7 +480,7 @@ export function PayrollDailyWorkSection({ min="0" step="0.01" value={actualPoints} - disabled={!selectedPeriod?.editable} + disabled={!selectedPeriod?.editable || inlineSaving} required onChange={(event) => setActualPoints(event.target.value)} onBlur={() => setActualPoints((value) => normalizePayrollPoints(value))} @@ -351,7 +492,7 @@ export function PayrollDailyWorkSection({