mirror of
https://github.com/spliit-app/spliit.git
synced 2026-02-25 17:06:13 +01:00
Implement "infinite scroll" for expenses (#95)
* Extract ExpenseCard vom ExpenseList * Implement simple pagination of expenses (see #30) - display only this year's entries by default - a "Show more" button reveals all expenses * Turn getPrisma() into constant "prisma" - getPrisma() is not async and doesn't need to be awaited - turn getPrisma() into exported constant "prisma" * Select fields to be returned by getGroupExpenses() - make JSON more concise and less redundant - some properties were removed (i.e.instead of "expense.paidById" use "expense.paidBy.id") * Remove "participants" from ExpenseCard - no need to search for participant by id to get it's name - name property is already present in expense * Add option to fetch a slice of group expenses - specify offset and length to get expenses for [offset, offset+length[ - add function to get total number of group expenses * Add api route for client to fetch group expenses * Remove "Show more" button from expense list * Implement infinite scroll - in server component Page - only load first 200 expenses max - pass preloaded expenses and total count - in client component ExpenseList, if there are more expenses to show - test if there are more expenses - append preloading "skeletons" to end of list - fetch more expenses when last item in list comes into view - after each fetch increase fetch-length by factor 1.5 - rationale: db fetch usually is not the issue here, the longer the list gets, the longer react needs to redraw * Use server action instead of api endpoint * Fixes --------- Co-authored-by: Sebastien Castiel <sebastien@castiel.me>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { getPrisma } from '@/lib/prisma'
|
||||
import { prisma } from '@/lib/prisma'
|
||||
import { ExpenseFormValues, GroupFormValues } from '@/lib/schemas'
|
||||
import { Expense } from '@prisma/client'
|
||||
import { nanoid } from 'nanoid'
|
||||
@@ -8,7 +8,6 @@ export function randomId() {
|
||||
}
|
||||
|
||||
export async function createGroup(groupFormValues: GroupFormValues) {
|
||||
const prisma = await getPrisma()
|
||||
return prisma.group.create({
|
||||
data: {
|
||||
id: randomId(),
|
||||
@@ -42,7 +41,6 @@ export async function createExpense(
|
||||
throw new Error(`Invalid participant ID: ${participant}`)
|
||||
}
|
||||
|
||||
const prisma = await getPrisma()
|
||||
return prisma.expense.create({
|
||||
data: {
|
||||
id: randomId(),
|
||||
@@ -78,7 +76,6 @@ export async function createExpense(
|
||||
}
|
||||
|
||||
export async function deleteExpense(expenseId: string) {
|
||||
const prisma = await getPrisma()
|
||||
await prisma.expense.delete({
|
||||
where: { id: expenseId },
|
||||
include: { paidFor: true, paidBy: true },
|
||||
@@ -90,15 +87,14 @@ export async function getGroupExpensesParticipants(groupId: string) {
|
||||
return Array.from(
|
||||
new Set(
|
||||
expenses.flatMap((e) => [
|
||||
e.paidById,
|
||||
...e.paidFor.map((pf) => pf.participantId),
|
||||
e.paidBy.id,
|
||||
...e.paidFor.map((pf) => pf.participant.id),
|
||||
]),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
export async function getGroups(groupIds: string[]) {
|
||||
const prisma = await getPrisma()
|
||||
return (
|
||||
await prisma.group.findMany({
|
||||
where: { id: { in: groupIds } },
|
||||
@@ -129,7 +125,6 @@ export async function updateExpense(
|
||||
throw new Error(`Invalid participant ID: ${participant}`)
|
||||
}
|
||||
|
||||
const prisma = await getPrisma()
|
||||
return prisma.expense.update({
|
||||
where: { id: expenseId },
|
||||
data: {
|
||||
@@ -198,7 +193,6 @@ export async function updateGroup(
|
||||
const existingGroup = await getGroup(groupId)
|
||||
if (!existingGroup) throw new Error('Invalid group ID')
|
||||
|
||||
const prisma = await getPrisma()
|
||||
return prisma.group.update({
|
||||
where: { id: groupId },
|
||||
data: {
|
||||
@@ -230,7 +224,6 @@ export async function updateGroup(
|
||||
}
|
||||
|
||||
export async function getGroup(groupId: string) {
|
||||
const prisma = await getPrisma()
|
||||
return prisma.group.findUnique({
|
||||
where: { id: groupId },
|
||||
include: { participants: true },
|
||||
@@ -238,25 +231,43 @@ export async function getGroup(groupId: string) {
|
||||
}
|
||||
|
||||
export async function getCategories() {
|
||||
const prisma = await getPrisma()
|
||||
return prisma.category.findMany()
|
||||
}
|
||||
|
||||
export async function getGroupExpenses(groupId: string) {
|
||||
const prisma = await getPrisma()
|
||||
export async function getGroupExpenses(
|
||||
groupId: string,
|
||||
options?: { offset: number; length: number },
|
||||
) {
|
||||
return prisma.expense.findMany({
|
||||
where: { groupId },
|
||||
include: {
|
||||
paidFor: { include: { participant: true } },
|
||||
paidBy: true,
|
||||
select: {
|
||||
amount: true,
|
||||
category: true,
|
||||
createdAt: true,
|
||||
expenseDate: true,
|
||||
id: true,
|
||||
isReimbursement: true,
|
||||
paidBy: { select: { id: true, name: true } },
|
||||
paidFor: {
|
||||
select: {
|
||||
participant: { select: { id: true, name: true } },
|
||||
shares: true,
|
||||
},
|
||||
},
|
||||
splitMode: true,
|
||||
title: true,
|
||||
},
|
||||
where: { groupId },
|
||||
orderBy: [{ expenseDate: 'desc' }, { createdAt: 'desc' }],
|
||||
skip: options && options.offset,
|
||||
take: options && options.length,
|
||||
})
|
||||
}
|
||||
|
||||
export async function getGroupExpenseCount(groupId: string) {
|
||||
return prisma.expense.count({ where: { groupId } })
|
||||
}
|
||||
|
||||
export async function getExpense(groupId: string, expenseId: string) {
|
||||
const prisma = await getPrisma()
|
||||
return prisma.expense.findUnique({
|
||||
where: { id: expenseId },
|
||||
include: { paidBy: true, paidFor: true, category: true, documents: true },
|
||||
|
||||
Reference in New Issue
Block a user