diff --git a/apps/web/src/app/(dashboard)/dashboard/page.tsx b/apps/web/src/app/(dashboard)/dashboard/page.tsx index 30ee90ed9..a59daef00 100644 --- a/apps/web/src/app/(dashboard)/dashboard/page.tsx +++ b/apps/web/src/app/(dashboard)/dashboard/page.tsx @@ -5,6 +5,7 @@ import { Clock, File, FileCheck } from 'lucide-react'; import { getRequiredServerComponentSession } from '@documenso/lib/next-auth/get-server-session'; import { findDocuments } from '@documenso/lib/server-only/document/find-documents'; import { getStats } from '@documenso/lib/server-only/document/get-stats'; +import { Recipient } from '@documenso/prisma/client'; import { Table, TableBody, @@ -14,12 +15,51 @@ import { TableRow, } from '@documenso/ui/primitives/table'; +import { StackAvatar } from '~/components/(dashboard)/avatar'; import { CardMetric } from '~/components/(dashboard)/metric-card/metric-card'; import { DocumentStatus } from '~/components/formatter/document-status'; import { LocaleDate } from '~/components/formatter/locale-date'; import { UploadDocument } from './upload-document'; +const renderStackAvatars = (recipients: Recipient[]) => { + const zIndex = 50; + const itemsToRender = recipients.slice(0, 5); + const remainingItems = recipients.length - itemsToRender.length; + + return itemsToRender.map((recipient: Recipient, index: number) => { + const first = index === 0 ? true : false; + const initials = + recipient.name + ?.split(' ') + .map((name: string) => name.slice(0, 1).toUpperCase()) + .slice(0, 2) + .join('') ?? 'UK'; + + const lastItemText = + index === itemsToRender.length - 1 && remainingItems > 0 + ? `+${remainingItems + 1}` + : undefined; + + const type = + recipient.sendStatus === 'SENT' && recipient.signingStatus === 'SIGNED' + ? 'completed' + : recipient.sendStatus === 'SENT' && recipient.signingStatus === 'NOT_SIGNED' + ? 'waiting' + : 'unsigned'; + + return ( + + ); + }); +}; + export default async function DashboardPage() { const session = await getRequiredServerComponentSession(); @@ -60,6 +100,7 @@ export default async function DashboardPage() { ID Title + Reciepient Status Created @@ -76,6 +117,11 @@ export default async function DashboardPage() { {document.title} + + + {renderStackAvatars(document.Recipient)} + + diff --git a/apps/web/src/components/(dashboard)/avatar/index.tsx b/apps/web/src/components/(dashboard)/avatar/index.tsx new file mode 100644 index 000000000..a784cbbff --- /dev/null +++ b/apps/web/src/components/(dashboard)/avatar/index.tsx @@ -0,0 +1,36 @@ +import { Avatar, AvatarFallback } from '@documenso/ui/primitives/avatar'; + +export type StackAvatarProps = { + first?: boolean; + zIndex?: string; + fallbackText?: string; + type: 'unsigned' | 'waiting' | 'completed'; +}; + +export const StackAvatar = ({ first, zIndex, fallbackText, type }: StackAvatarProps) => { + let classes = ''; + switch (type) { + case 'unsigned': + classes = 'bg-dawn-400 text-dawn-900'; + break; + case 'waiting': + classes = 'bg-water text-water-700'; + break; + case 'completed': + classes = 'bg-documenso-200 text-documenso-800'; + break; + default: + break; + } + + return ( + + {fallbackText ?? 'UK'} + + ); +}; diff --git a/packages/lib/server-only/document/find-documents.ts b/packages/lib/server-only/document/find-documents.ts index 627ceee8b..005b6614a 100644 --- a/packages/lib/server-only/document/find-documents.ts +++ b/packages/lib/server-only/document/find-documents.ts @@ -1,5 +1,5 @@ import { prisma } from '@documenso/prisma'; -import { Document, DocumentStatus, Prisma } from '@documenso/prisma/client'; +import { Document, DocumentStatus, Prisma, Recipient } from '@documenso/prisma/client'; import { FindResultSet } from '../../types/find-result-set'; @@ -15,6 +15,10 @@ export interface FindDocumentsOptions { }; } +export type DocumentWithReciepient = Document & { + Recipient: Recipient[]; +}; + export const findDocuments = async ({ userId, term, @@ -22,7 +26,7 @@ export const findDocuments = async ({ page = 1, perPage = 10, orderBy, -}: FindDocumentsOptions): Promise> => { +}: FindDocumentsOptions): Promise> => { const orderByColumn = orderBy?.column ?? 'created'; const orderByDirection = orderBy?.direction ?? 'desc'; @@ -48,6 +52,9 @@ export const findDocuments = async ({ orderBy: { [orderByColumn]: orderByDirection, }, + include: { + Recipient: true, + }, }), prisma.document.count({ where: { diff --git a/packages/prisma/schema.prisma b/packages/prisma/schema.prisma index c22c42bf7..65e4e8b63 100644 --- a/packages/prisma/schema.prisma +++ b/packages/prisma/schema.prisma @@ -1,6 +1,6 @@ generator client { - provider = "prisma-client-js" - previewFeatures = ["extendedWhereUnique", "jsonProtocol"] + provider = "prisma-client-js" + previewFeatures = ["extendedWhereUnique"] } datasource db { @@ -144,10 +144,10 @@ model Field { recipientId Int? type FieldType page Int - positionX Decimal @default(0) - positionY Decimal @default(0) - width Decimal @default(-1) - height Decimal @default(-1) + positionX Decimal @default(0) + positionY Decimal @default(0) + width Decimal @default(-1) + height Decimal @default(-1) customText String inserted Boolean Document Document @relation(fields: [documentId], references: [id], onDelete: Cascade) diff --git a/packages/tailwind-config/index.cjs b/packages/tailwind-config/index.cjs index 3f414588d..d841e2711 100644 --- a/packages/tailwind-config/index.cjs +++ b/packages/tailwind-config/index.cjs @@ -76,6 +76,20 @@ module.exports = { 900: '#52514a', 950: '#2a2925', }, + water: { + DEFAULT: '#d7e4f3', + 50: '#f3f6fb', + 100: '#e3ebf6', + 200: '#d7e4f3', + 300: '#abc7e5', + 400: '#82abd8', + 500: '#658ecc', + 600: '#5175bf', + 700: '#4764ae', + 800: '#3e538f', + 900: '#364772', + 950: '#252d46', + }, }, backgroundImage: { 'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))',