feat(panel): özet kartları, anlık durum ve hızlı harcama ekleme

- Gelir, sabit gider, toplam birikim, devreden borç kartları
- Anlık durum: kalan harcanabilir ve günlük harcama önerisi
- Grup planlanan/gerçekleşen (Haftalık/Aylık)
- "Harcama Ekle": masaüstünde başlıkta, mobilde yüzen düğme
- Yükleniyor iskeleti, boş durumlar, açık dönem yokken son dönem özeti

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-12 23:54:32 +03:00
parent 30136ed1d3
commit 64c185ab96
14 changed files with 479 additions and 20 deletions
+19 -5
View File
@@ -4,7 +4,13 @@
* Kayıt değişince dönemin tüm sorguları yenilenir (`queryKeys.period(id)` öneki):
* liste, özet (`spent_total`, `remaining`) ve bütçe (`spent`) birlikte güncellenir.
*/
import { keepPreviousData, useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import {
keepPreviousData,
useMutation,
useQuery,
useQueryClient,
type QueryClient,
} from '@tanstack/react-query'
import type { Entry } from '@/types/Entry'
import type { EntryCreateRequest } from '@/types/EntryCreateRequest'
import type { EntryUpdateRequest } from '@/types/EntryUpdateRequest'
@@ -12,6 +18,14 @@ import { api } from './api'
import { queryKeys } from './query'
/** Bir tarih aralığının kayıtları (ikisi de dahil); en yeni başta */
/** Kayıt değişince: dönemin tüm verileri + Panel istatistikleri (harcanan) */
function refresh(qc: QueryClient, periodId: string) {
return Promise.all([
qc.invalidateQueries({ queryKey: queryKeys.period(periodId) }),
qc.invalidateQueries({ queryKey: queryKeys.stats }),
])
}
export function useEntries(periodId: string, range: { from: string; to: string }) {
return useQuery({
queryKey: queryKeys.entries(periodId, range),
@@ -26,7 +40,7 @@ export function useCreateEntry(periodId: string) {
const qc = useQueryClient()
return useMutation({
mutationFn: (body: EntryCreateRequest) => api.post<Entry>(`/periods/${periodId}/entries`, body),
onSuccess: () => qc.invalidateQueries({ queryKey: queryKeys.period(periodId) }),
onSuccess: () => refresh(qc, periodId),
})
}
@@ -36,7 +50,7 @@ export function useUpdateEntry(periodId: string) {
return useMutation({
mutationFn: ({ id, ...body }: EntryUpdateRequest & { id: string }) =>
api.patch<Entry>(`/entries/${id}`, body),
onSettled: () => qc.invalidateQueries({ queryKey: queryKeys.period(periodId) }),
onSettled: () => refresh(qc, periodId),
})
}
@@ -45,7 +59,7 @@ export function useDeleteEntry(periodId: string) {
const qc = useQueryClient()
return useMutation({
mutationFn: (id: string) => api.delete(`/entries/${id}`),
onSettled: () => qc.invalidateQueries({ queryKey: queryKeys.period(periodId) }),
onSettled: () => refresh(qc, periodId),
})
}
@@ -53,6 +67,6 @@ export function useRestoreEntry(periodId: string) {
const qc = useQueryClient()
return useMutation({
mutationFn: (id: string) => api.post<Entry>(`/entries/${id}/restore`),
onSettled: () => qc.invalidateQueries({ queryKey: queryKeys.period(periodId) }),
onSettled: () => refresh(qc, periodId),
})
}