diff --git a/src/app/groups/[groupId]/activity/activity-item.tsx b/src/app/groups/[groupId]/activity/activity-item.tsx index 69f64e11..393f0120 100644 --- a/src/app/groups/[groupId]/activity/activity-item.tsx +++ b/src/app/groups/[groupId]/activity/activity-item.tsx @@ -16,7 +16,7 @@ type Props = { dateStyle: DateTimeStyle } -function useSummary(activity: Activity, participantName?: string) { +export function useSummary(activity: Activity, participantName?: string) { const t = useTranslations('Activity') const participant = participantName ?? t('someone') const expense = activity.data ?? '' diff --git a/src/app/groups/[groupId]/activity/activity-list.tsx b/src/app/groups/[groupId]/activity/activity-list.tsx index cf2c010f..deac5eae 100644 --- a/src/app/groups/[groupId]/activity/activity-list.tsx +++ b/src/app/groups/[groupId]/activity/activity-list.tsx @@ -11,7 +11,7 @@ type Props = { activities: Activity[] } -const DATE_GROUPS = { +export const DATE_GROUPS = { TODAY: 'today', YESTERDAY: 'yesterday', EARLIER_THIS_WEEK: 'earlierThisWeek', @@ -45,7 +45,7 @@ function getDateGroup(date: Dayjs, today: Dayjs) { } } -function getGroupedActivitiesByDate(activities: Activity[]) { +export function getGroupedActivitiesByDate(activities: Activity[]) { const today = dayjs() return activities.reduce( (result: { [key: string]: Activity[] }, activity: Activity) => { diff --git a/src/app/groups/[groupId]/expenses/[expenseId]/edit/page.tsx b/src/app/groups/[groupId]/expenses/[expenseId]/edit/page.tsx index a91de1f8..de58fa96 100644 --- a/src/app/groups/[groupId]/expenses/[expenseId]/edit/page.tsx +++ b/src/app/groups/[groupId]/expenses/[expenseId]/edit/page.tsx @@ -4,6 +4,7 @@ import { deleteExpense, getCategories, getExpense, + getExpenseActivity, updateExpense, } from '@/lib/api' import { getRuntimeFeatureFlags } from '@/lib/featureFlags' @@ -40,12 +41,15 @@ export default async function EditExpensePage({ redirect(`/groups/${groupId}`) } + const activities = await getExpenseActivity(expenseId) + return ( >> + activity: Activity + participant?: Participant + expense?: Awaited> + dateStyle: DateTimeStyle +} + +export function ExpenseActivityItem({ + group, + activity, + participant, + expense, + dateStyle, +}: Props) { + const router = useRouter() + const summary = useSummary(activity, participant?.name) + const locale = useLocale() + + return ( +
+
+ {dateStyle !== undefined && ( +
+ {formatDate(activity.time, locale, { dateStyle })} +
+ )} +
+ {formatDate(activity.time, locale, { timeStyle: 'short' })} +
+
+
+
{summary}
+
+ +
+ ) +} diff --git a/src/components/expense-activity-list.tsx b/src/components/expense-activity-list.tsx new file mode 100644 index 00000000..6d9d36c9 --- /dev/null +++ b/src/components/expense-activity-list.tsx @@ -0,0 +1,60 @@ +import { + DATE_GROUPS, + getGroupedActivitiesByDate, +} from '@/app/groups/[groupId]/activity/activity-list' +import { getActivities, getExpense, getGroup } from '@/lib/api' +import { Activity } from '@prisma/client' +import { ExpenseActivityItem } from './expense-activity-item' + +type Props = { + group: NonNullable>> + expense: Awaited> + activities: NonNullable>> +} + +export function ExpenseActivityList({ group, expense, activities }: Props) { + const groupedActivitiesByDate = getGroupedActivitiesByDate(activities) + + return activities.length > 0 ? ( + <> + {Object.values(DATE_GROUPS).map((dateGroup: string) => { + let groupActivities = groupedActivitiesByDate[dateGroup] + if (!groupActivities || groupActivities.length === 0) return null + const dateStyle = + dateGroup == DATE_GROUPS.TODAY || dateGroup == DATE_GROUPS.YESTERDAY + ? undefined + : 'medium' + + return ( +
+
+ {dateGroup} +
+ {groupActivities.map((activity: Activity) => { + const participant = + activity.participantId !== null + ? group.participants.find( + (p) => p.id === activity.participantId, + ) + : undefined + return ( + + ) + })} +
+ ) + })} + + ) : ( +

+ There is not yet any activity for this expense. +

+ ) +} diff --git a/src/components/expense-form.tsx b/src/components/expense-form.tsx index 1a296dbf..1f47bd2c 100644 --- a/src/components/expense-form.tsx +++ b/src/components/expense-form.tsx @@ -1,5 +1,6 @@ 'use client' import { CategorySelector } from '@/components/category-selector' +import { ExpenseActivityList } from '@/components/expense-activity-list' import { ExpenseDocumentsInput } from '@/components/expense-documents-input' import { SubmitButton } from '@/components/submit-button' import { Button } from '@/components/ui/button' @@ -33,7 +34,13 @@ import { SelectTrigger, SelectValue, } from '@/components/ui/select' -import { getCategories, getExpense, getGroup, randomId } from '@/lib/api' +import { + getActivities, + getCategories, + getExpense, + getGroup, + randomId, +} from '@/lib/api' import { RuntimeFeatureFlags } from '@/lib/featureFlags' import { useActiveUser } from '@/lib/hooks' import { @@ -58,6 +65,7 @@ export type Props = { group: NonNullable>> expense?: NonNullable>> categories: NonNullable>> + activities?: NonNullable>> onSubmit: (values: ExpenseFormValues, participantId?: string) => Promise onDelete?: (participantId?: string) => Promise runtimeFeatureFlags: RuntimeFeatureFlags @@ -148,6 +156,7 @@ export function ExpenseForm({ group, expense, categories, + activities, onSubmit, onDelete, runtimeFeatureFlags, @@ -817,6 +826,28 @@ export function ExpenseForm({ + + {!isCreate && activities && ( + + + + Expense History + + + Previous Activity for this expense. + + + + + + + )} ) } diff --git a/src/lib/api.ts b/src/lib/api.ts index 92eaefc5..fe36a69a 100644 --- a/src/lib/api.ts +++ b/src/lib/api.ts @@ -312,6 +312,13 @@ export async function getActivities(groupId: string) { }) } +export async function getExpenseActivity(expenseId: string) { + return prisma.activity.findMany({ + where: { expenseId: expenseId }, + orderBy: [{ time: 'desc' }], + }) +} + export async function logActivity( groupId: string, activityType: ActivityType,