4.13.0: Добавлено удаление записей в статистике
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 1m22s

This commit is contained in:
poignatov
2026-02-03 18:42:44 +03:00
parent 36dd96976f
commit df3cced995
6 changed files with 168 additions and 8 deletions

View File

@@ -174,6 +174,7 @@ function FullStatistics({ selectedProject, onClearSelection, data, loading, erro
loading={todayEntriesLoading}
error={todayEntriesError}
onRetry={() => fetchTodayEntries && fetchTodayEntries(false, selectedProject, selectedDate)}
onDelete={() => fetchTodayEntries && fetchTodayEntries(false, selectedProject, selectedDate)}
/>
</>
)}

View File

@@ -1,5 +1,6 @@
import React from 'react'
import React, { useState } from 'react'
import LoadingError from './LoadingError'
import { useAuth } from './auth/AuthContext'
// Функция для форматирования скорa (аналогично formatScore из TaskDetail)
const formatScore = (num) => {
@@ -130,7 +131,49 @@ const formatEntryText = (text, nodes) => {
return result.length > 0 ? result : currentText
}
function TodayEntriesList({ data, loading, error, onRetry }) {
function TodayEntriesList({ data, loading, error, onRetry, onDelete }) {
const { authFetch } = useAuth()
const [deletingIds, setDeletingIds] = useState(new Set())
const handleDelete = async (entryId) => {
if (deletingIds.has(entryId)) return
if (!window.confirm('Вы уверены, что хотите удалить эту запись? Это действие нельзя отменить.')) {
return
}
setDeletingIds(prev => new Set(prev).add(entryId))
try {
const response = await authFetch(`/api/entries/${entryId}`, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
},
})
if (!response.ok) {
const errorText = await response.text()
console.error('Delete error:', response.status, errorText)
throw new Error(`Ошибка при удалении записи: ${response.status}`)
}
// Вызываем callback для обновления данных
if (onDelete) {
onDelete()
}
} catch (err) {
console.error('Delete failed:', err)
alert(err.message || 'Не удалось удалить запись')
} finally {
setDeletingIds(prev => {
const next = new Set(prev)
next.delete(entryId)
return next
})
}
}
if (loading) {
return (
<div className="flex justify-center items-center py-8">
@@ -157,9 +200,56 @@ function TodayEntriesList({ data, loading, error, onRetry }) {
{data.map((entry) => (
<div
key={entry.id}
className="bg-white rounded-lg p-4 shadow-sm border border-gray-200"
className="bg-white rounded-lg p-4 shadow-sm border border-gray-200 relative group"
>
<div className="text-gray-800 whitespace-pre-wrap">
<button
onClick={() => handleDelete(entry.id)}
disabled={deletingIds.has(entry.id)}
className="absolute top-4 right-4 disabled:opacity-50 disabled:cursor-not-allowed"
style={{
color: '#6b7280',
background: 'none',
border: 'none',
cursor: 'pointer',
padding: '0.25rem',
borderRadius: '0.25rem',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
width: '24px',
height: '24px',
transition: 'all 0.2s',
opacity: deletingIds.has(entry.id) ? 0.5 : 1,
zIndex: 10
}}
onMouseEnter={(e) => {
if (!deletingIds.has(entry.id)) {
e.currentTarget.style.backgroundColor = '#f3f4f6'
e.currentTarget.style.color = '#1f2937'
}
}}
onMouseLeave={(e) => {
e.currentTarget.style.backgroundColor = 'transparent'
e.currentTarget.style.color = '#6b7280'
}}
title="Удалить запись"
>
{deletingIds.has(entry.id) ? (
<svg className="w-5 h-5 animate-spin" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
) : (
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M3 6h18"></path>
<path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"></path>
<path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"></path>
<line x1="10" y1="11" x2="10" y2="17"></line>
<line x1="14" y1="11" x2="14" y2="17"></line>
</svg>
)}
</button>
<div className="text-gray-800 whitespace-pre-wrap pr-8">
{formatEntryText(entry.text, entry.nodes)}
</div>
{entry.created_date && (

View File

@@ -252,8 +252,8 @@ function WeekProgressChart({ data, allProjectsSorted, currentWeekData, selectedP
)
return (
<div className="mt-8">
<h2 className="text-2xl font-semibold text-gray-800 mb-6">Прогресс недель</h2>
<div>
<h2 className="text-2xl font-semibold text-gray-800 mb-6" style={{ marginTop: '1.25rem' }}>Прогресс недель</h2>
<div className="space-y-3">
{/* Остальные недели (старые сверху, новые снизу) */}
{allOtherWeeksData.map((weekData) => (