aboutsummaryrefslogtreecommitdiffstats
path: root/src/components
diff options
context:
space:
mode:
authorSebastien Castiel <sebastien@castiel.me>2023-12-07 20:12:40 -0500
committerSebastien Castiel <sebastien@castiel.me>2023-12-07 20:12:40 -0500
commit6f077f141edcc45f2d7467b7c796e62a8f0316f8 (patch)
tree617f8883be6140eac9827bde5c600f85606d85f9 /src/components
parentfb6cff2fe3101e80d2932285c0b03e0d5be872f8 (diff)
Prevent removing participants with expenses
Diffstat (limited to 'src/components')
-rw-r--r--src/components/group-form.tsx57
-rw-r--r--src/components/ui/hover-card.tsx29
2 files changed, 75 insertions, 11 deletions
diff --git a/src/components/group-form.tsx b/src/components/group-form.tsx
index d2140f0..7bf0f8e 100644
--- a/src/components/group-form.tsx
+++ b/src/components/group-form.tsx
@@ -18,6 +18,11 @@ import {
FormLabel,
FormMessage,
} from '@/components/ui/form'
+import {
+ HoverCard,
+ HoverCardContent,
+ HoverCardTrigger,
+} from '@/components/ui/hover-card'
import { Input } from '@/components/ui/input'
import { getGroup } from '@/lib/api'
import { GroupFormValues, groupFormSchema } from '@/lib/schemas'
@@ -28,9 +33,14 @@ import { useFieldArray, useForm } from 'react-hook-form'
export type Props = {
group?: NonNullable<Awaited<ReturnType<typeof getGroup>>>
onSubmit: (groupFormValues: GroupFormValues) => Promise<void>
+ protectedParticipantIds?: string[]
}
-export function GroupForm({ group, onSubmit }: Props) {
+export function GroupForm({
+ group,
+ onSubmit,
+ protectedParticipantIds = [],
+}: Props) {
const form = useForm<GroupFormValues>({
resolver: zodResolver(groupFormSchema),
defaultValues: group
@@ -48,6 +58,7 @@ export function GroupForm({ group, onSubmit }: Props) {
const { fields, append, remove } = useFieldArray({
control: form.control,
name: 'participants',
+ keyName: 'key',
})
return (
@@ -115,7 +126,7 @@ export function GroupForm({ group, onSubmit }: Props) {
</CardDescription>
</CardHeader>
<CardContent>
- <ul className="flex flex-col gap-4">
+ <ul className="flex flex-col gap-2">
{fields.map((item, index) => (
<li key={item.id}>
<FormField
@@ -129,15 +140,39 @@ export function GroupForm({ group, onSubmit }: Props) {
<FormControl>
<div className="flex gap-2">
<Input className="text-base" {...field} />
- <Button
- variant="ghost"
- className="text-destructive"
- onClick={() => remove(index)}
- type="button"
- size="icon"
- >
- <Trash2 className="w-4 h-4" />
- </Button>
+ {item.id &&
+ protectedParticipantIds.includes(item.id) ? (
+ <HoverCard>
+ <HoverCardTrigger>
+ <Button
+ variant="ghost"
+ className="text-destructive-"
+ type="button"
+ size="icon"
+ disabled
+ >
+ <Trash2 className="w-4 h-4 text-destructive opacity-50" />
+ </Button>
+ </HoverCardTrigger>
+ <HoverCardContent
+ align="end"
+ className="text-sm"
+ >
+ This participant is part of expenses, and can
+ not be removed.
+ </HoverCardContent>
+ </HoverCard>
+ ) : (
+ <Button
+ variant="ghost"
+ className="text-destructive"
+ onClick={() => remove(index)}
+ type="button"
+ size="icon"
+ >
+ <Trash2 className="w-4 h-4" />
+ </Button>
+ )}
</div>
</FormControl>
<FormMessage />
diff --git a/src/components/ui/hover-card.tsx b/src/components/ui/hover-card.tsx
new file mode 100644
index 0000000..e54d91c
--- /dev/null
+++ b/src/components/ui/hover-card.tsx
@@ -0,0 +1,29 @@
+"use client"
+
+import * as React from "react"
+import * as HoverCardPrimitive from "@radix-ui/react-hover-card"
+
+import { cn } from "@/lib/utils"
+
+const HoverCard = HoverCardPrimitive.Root
+
+const HoverCardTrigger = HoverCardPrimitive.Trigger
+
+const HoverCardContent = React.forwardRef<
+ React.ElementRef<typeof HoverCardPrimitive.Content>,
+ React.ComponentPropsWithoutRef<typeof HoverCardPrimitive.Content>
+>(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
+ <HoverCardPrimitive.Content
+ ref={ref}
+ align={align}
+ sideOffset={sideOffset}
+ className={cn(
+ "z-50 w-64 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
+ className
+ )}
+ {...props}
+ />
+))
+HoverCardContent.displayName = HoverCardPrimitive.Content.displayName
+
+export { HoverCard, HoverCardTrigger, HoverCardContent }