mirror of
https://github.com/spliit-app/spliit.git
synced 2026-02-28 02:16:12 +01:00
add group information field to group settings and Information tab (#164)
* add group information field to group and Information tab to display * add breaks to info page * Improve UX --------- Co-authored-by: Sebastien Castiel <sebastien@castiel.me>
This commit is contained in:
@@ -75,6 +75,10 @@
|
|||||||
"placeholder": "Summer vacations",
|
"placeholder": "Summer vacations",
|
||||||
"description": "Enter a name for your group."
|
"description": "Enter a name for your group."
|
||||||
},
|
},
|
||||||
|
"InformationField": {
|
||||||
|
"label": "Group information",
|
||||||
|
"placeholder": "What information is relevant to group participants?"
|
||||||
|
},
|
||||||
"CurrencyField": {
|
"CurrencyField": {
|
||||||
"label": "Currency symbol",
|
"label": "Currency symbol",
|
||||||
"placeholder": "$, €, £…",
|
"placeholder": "$, €, £…",
|
||||||
@@ -273,6 +277,11 @@
|
|||||||
"older": "Older"
|
"older": "Older"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Information": {
|
||||||
|
"title": "Information",
|
||||||
|
"description": "Use this place to add any information that can be relevant to the group participants.",
|
||||||
|
"empty": "No group information yet."
|
||||||
|
},
|
||||||
"Settings": {
|
"Settings": {
|
||||||
"title": "Settings"
|
"title": "Settings"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -75,6 +75,10 @@
|
|||||||
"placeholder": "Kesälomareissu",
|
"placeholder": "Kesälomareissu",
|
||||||
"description": "Syötä ryhmäsi nimi."
|
"description": "Syötä ryhmäsi nimi."
|
||||||
},
|
},
|
||||||
|
"InformationField": {
|
||||||
|
"label": "Ryhmän tiedot",
|
||||||
|
"placeholder": "Mitkä tiedot ovat merkityksellisiä ryhmän osallistujille?"
|
||||||
|
},
|
||||||
"CurrencyField": {
|
"CurrencyField": {
|
||||||
"label": "Valuuttamerkki",
|
"label": "Valuuttamerkki",
|
||||||
"placeholder": "$, €, £…",
|
"placeholder": "$, €, £…",
|
||||||
@@ -273,6 +277,11 @@
|
|||||||
"older": "Vanhemmat"
|
"older": "Vanhemmat"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Information": {
|
||||||
|
"title": "Tiedot",
|
||||||
|
"description": "Käytä tätä paikkaa lisätäksesi kaikki tiedot, joilla voi olla merkitystä ryhmän osallistujille.",
|
||||||
|
"empty": "Ryhmätietoja ei vielä ole."
|
||||||
|
},
|
||||||
"Settings": {
|
"Settings": {
|
||||||
"title": "Asetukset"
|
"title": "Asetukset"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Group" ADD COLUMN "information" TEXT;
|
||||||
@@ -14,6 +14,7 @@ datasource db {
|
|||||||
model Group {
|
model Group {
|
||||||
id String @id
|
id String @id
|
||||||
name String
|
name String
|
||||||
|
information String? @db.Text
|
||||||
currency String @default("$")
|
currency String @default("$")
|
||||||
participants Participant[]
|
participants Participant[]
|
||||||
expenses Expense[]
|
expenses Expense[]
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ export function GroupTabs({ groupId }: Props) {
|
|||||||
<TabsList>
|
<TabsList>
|
||||||
<TabsTrigger value="expenses">{t('Expenses.title')}</TabsTrigger>
|
<TabsTrigger value="expenses">{t('Expenses.title')}</TabsTrigger>
|
||||||
<TabsTrigger value="balances">{t('Balances.title')}</TabsTrigger>
|
<TabsTrigger value="balances">{t('Balances.title')}</TabsTrigger>
|
||||||
|
<TabsTrigger value="information">{t('Information.title')}</TabsTrigger>
|
||||||
<TabsTrigger value="stats">{t('Stats.title')}</TabsTrigger>
|
<TabsTrigger value="stats">{t('Stats.title')}</TabsTrigger>
|
||||||
<TabsTrigger value="activity">{t('Activity.title')}</TabsTrigger>
|
<TabsTrigger value="activity">{t('Activity.title')}</TabsTrigger>
|
||||||
<TabsTrigger value="edit">{t('Settings.title')}</TabsTrigger>
|
<TabsTrigger value="edit">{t('Settings.title')}</TabsTrigger>
|
||||||
|
|||||||
54
src/app/groups/[groupId]/information/page.tsx
Normal file
54
src/app/groups/[groupId]/information/page.tsx
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
import { cached } from '@/app/cached-functions'
|
||||||
|
import { Button } from '@/components/ui/button'
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
CardContent,
|
||||||
|
CardDescription,
|
||||||
|
CardHeader,
|
||||||
|
CardTitle,
|
||||||
|
} from '@/components/ui/card'
|
||||||
|
import { Pencil } from 'lucide-react'
|
||||||
|
import { Metadata } from 'next'
|
||||||
|
import { getTranslations } from 'next-intl/server'
|
||||||
|
import Link from 'next/link'
|
||||||
|
import { notFound } from 'next/navigation'
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: 'Totals',
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function InformationPage({
|
||||||
|
params: { groupId },
|
||||||
|
}: {
|
||||||
|
params: { groupId: string }
|
||||||
|
}) {
|
||||||
|
const group = await cached.getGroup(groupId)
|
||||||
|
if (!group) notFound()
|
||||||
|
|
||||||
|
const t = await getTranslations('Information')
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Card className="mb-4">
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle className="flex justify-between">
|
||||||
|
<span>{t('title')}</span>
|
||||||
|
<Button size="icon" asChild className="-mb-12">
|
||||||
|
<Link href={`/groups/${groupId}/edit`}>
|
||||||
|
<Pencil className="w-4 h-4" />
|
||||||
|
</Link>
|
||||||
|
</Button>
|
||||||
|
</CardTitle>
|
||||||
|
<CardDescription className="mr-12">
|
||||||
|
{t('description')}
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="prose prose-sm sm:prose-base max-w-full whitespace-break-spaces">
|
||||||
|
{group.information || (
|
||||||
|
<p className="text-muted-foreground italic">{t('empty')}</p>
|
||||||
|
)}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -39,6 +39,7 @@ import { useTranslations } from 'next-intl'
|
|||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { useFieldArray, useForm } from 'react-hook-form'
|
import { useFieldArray, useForm } from 'react-hook-form'
|
||||||
|
import { Textarea } from './ui/textarea'
|
||||||
|
|
||||||
export type Props = {
|
export type Props = {
|
||||||
group?: NonNullable<Awaited<ReturnType<typeof getGroup>>>
|
group?: NonNullable<Awaited<ReturnType<typeof getGroup>>>
|
||||||
@@ -60,11 +61,13 @@ export function GroupForm({
|
|||||||
defaultValues: group
|
defaultValues: group
|
||||||
? {
|
? {
|
||||||
name: group.name,
|
name: group.name,
|
||||||
|
information: group.information ?? '',
|
||||||
currency: group.currency,
|
currency: group.currency,
|
||||||
participants: group.participants,
|
participants: group.participants,
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
name: '',
|
name: '',
|
||||||
|
information: '',
|
||||||
currency: '',
|
currency: '',
|
||||||
participants: [
|
participants: [
|
||||||
{ name: t('Participants.John') },
|
{ name: t('Participants.John') },
|
||||||
@@ -162,6 +165,27 @@ export function GroupForm({
|
|||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<div className="col-span-2">
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="information"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>{t('InformationField.label')}</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Textarea
|
||||||
|
rows={10}
|
||||||
|
className="text-base"
|
||||||
|
{...field}
|
||||||
|
placeholder={t('InformationField.placeholder')}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ export async function createGroup(groupFormValues: GroupFormValues) {
|
|||||||
data: {
|
data: {
|
||||||
id: randomId(),
|
id: randomId(),
|
||||||
name: groupFormValues.name,
|
name: groupFormValues.name,
|
||||||
|
information: groupFormValues.information,
|
||||||
currency: groupFormValues.currency,
|
currency: groupFormValues.currency,
|
||||||
participants: {
|
participants: {
|
||||||
createMany: {
|
createMany: {
|
||||||
@@ -226,6 +227,7 @@ export async function updateGroup(
|
|||||||
where: { id: groupId },
|
where: { id: groupId },
|
||||||
data: {
|
data: {
|
||||||
name: groupFormValues.name,
|
name: groupFormValues.name,
|
||||||
|
information: groupFormValues.information,
|
||||||
currency: groupFormValues.currency,
|
currency: groupFormValues.currency,
|
||||||
participants: {
|
participants: {
|
||||||
deleteMany: existingGroup.participants.filter(
|
deleteMany: existingGroup.participants.filter(
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import * as z from 'zod'
|
|||||||
export const groupFormSchema = z
|
export const groupFormSchema = z
|
||||||
.object({
|
.object({
|
||||||
name: z.string().min(2, 'min2').max(50, 'max50'),
|
name: z.string().min(2, 'min2').max(50, 'max50'),
|
||||||
|
information: z.string().optional(),
|
||||||
currency: z.string().min(1, 'min1').max(5, 'max5'),
|
currency: z.string().min(1, 'min1').max(5, 'max5'),
|
||||||
participants: z
|
participants: z
|
||||||
.array(
|
.array(
|
||||||
|
|||||||
Reference in New Issue
Block a user