From f22baca56986fb15cfe3def6dcc77b272ad8431f Mon Sep 17 00:00:00 2001 From: Ephraim Atta-Duncan Date: Fri, 23 Jun 2023 12:20:49 +0000 Subject: [PATCH 1/7] feat: stack recipients avatars on dashboard --- .../src/app/(dashboard)/dashboard/page.tsx | 46 +++++++++++++++++++ .../components/(dashboard)/avatar/index.tsx | 36 +++++++++++++++ .../document-dropzone/document-dropzone.tsx | 4 -- .../server-only/document/find-documents.ts | 11 ++++- packages/prisma/schema.prisma | 10 ++-- packages/tailwind-config/index.cjs | 14 ++++++ 6 files changed, 110 insertions(+), 11 deletions(-) create mode 100644 apps/web/src/components/(dashboard)/avatar/index.tsx diff --git a/apps/web/src/app/(dashboard)/dashboard/page.tsx b/apps/web/src/app/(dashboard)/dashboard/page.tsx index b6bad38a9..bdac678ff 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(); @@ -54,6 +94,7 @@ export default async function DashboardPage() { ID Title + Reciepient Status Created @@ -70,6 +111,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/apps/web/src/components/(dashboard)/document-dropzone/document-dropzone.tsx b/apps/web/src/components/(dashboard)/document-dropzone/document-dropzone.tsx index 3688bdfca..10ccba9d0 100644 --- a/apps/web/src/components/(dashboard)/document-dropzone/document-dropzone.tsx +++ b/apps/web/src/components/(dashboard)/document-dropzone/document-dropzone.tsx @@ -2,7 +2,6 @@ import { Variants, motion } from 'framer-motion'; import { Plus } from 'lucide-react'; -import { useTheme } from 'next-themes'; import { useDropzone } from 'react-dropzone'; import { cn } from '@documenso/ui/lib/utils'; @@ -92,8 +91,6 @@ export const DocumentDropzone = ({ className, onDrop, ...props }: DocumentDropzo }, }); - const { theme } = useTheme(); - return ( 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 4d5f900d7..4030af769 100644 --- a/packages/prisma/schema.prisma +++ b/packages/prisma/schema.prisma @@ -1,5 +1,5 @@ generator client { - provider = "prisma-client-js" + provider = "prisma-client-js" previewFeatures = ["extendedWhereUnique"] } @@ -143,10 +143,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 2f6e37954..3d8288ace 100644 --- a/packages/tailwind-config/index.cjs +++ b/packages/tailwind-config/index.cjs @@ -72,6 +72,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))', From 2e8e39c5a98057d7588db17f51391d0a54fea7d6 Mon Sep 17 00:00:00 2001 From: Ephraim Atta-Duncan Date: Fri, 23 Jun 2023 20:19:25 +0000 Subject: [PATCH 2/7] feat: add tooltip on hover on stacked avatars --- .../src/app/(dashboard)/dashboard/page.tsx | 145 +++++++++++++++--- .../components/(dashboard)/avatar/index.tsx | 2 +- 2 files changed, 124 insertions(+), 23 deletions(-) diff --git a/apps/web/src/app/(dashboard)/dashboard/page.tsx b/apps/web/src/app/(dashboard)/dashboard/page.tsx index bdac678ff..8526a8c05 100644 --- a/apps/web/src/app/(dashboard)/dashboard/page.tsx +++ b/apps/web/src/app/(dashboard)/dashboard/page.tsx @@ -14,6 +14,12 @@ import { TableHeader, TableRow, } from '@documenso/ui/primitives/table'; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from '@documenso/ui/primitives/tooltip'; import { StackAvatar } from '~/components/(dashboard)/avatar'; import { CardMetric } from '~/components/(dashboard)/metric-card/metric-card'; @@ -60,6 +66,24 @@ const renderStackAvatars = (recipients: Recipient[]) => { }); }; +const renderAvatar = (recipient: Recipient) => { + const initials = + recipient.name + ?.split(' ') + .map((name: string) => name.slice(0, 1).toUpperCase()) + .slice(0, 2) + .join('') ?? 'UK'; + + 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(); @@ -100,30 +124,107 @@ export default async function DashboardPage() { - {results.data.map((document) => ( - - {document.id} - - - {document.title} - - + {results.data.map((document) => { + const waitingRecipients = document.Recipient.filter( + (recipient) => + recipient.sendStatus === 'SENT' && recipient.signingStatus === 'NOT_SIGNED', + ); - - {renderStackAvatars(document.Recipient)} - + const completedRecipients = document.Recipient.filter( + (recipient) => + recipient.sendStatus === 'SENT' && recipient.signingStatus === 'SIGNED', + ); - - - - - - - - ))} + const uncompletedRecipients = document.Recipient.filter( + (recipient) => + recipient.sendStatus === 'NOT_SENT' && recipient.signingStatus === 'NOT_SIGNED', + ); + + return ( + + {document.id} + + + {document.title} + + + + + + + + {renderStackAvatars(document.Recipient)} + + +
+ {completedRecipients.length > 0 && ( +
+

Completed

+ {completedRecipients.map((recipient: Recipient) => ( +
+ {renderAvatar(recipient)} + + {recipient.email} + +
+ ))} +
+ )} + + {waitingRecipients.length > 0 && ( +
+

Waiting

+ {waitingRecipients.map((recipient: Recipient) => ( +
+ {renderAvatar(recipient)} + + {recipient.email} + +
+ ))} +
+ )} + + {uncompletedRecipients.length > 0 && ( +
+

Uncompleted

+ {uncompletedRecipients.map((recipient: Recipient) => ( +
+ {renderAvatar(recipient)} + + {recipient.email} + +
+ ))} +
+ )} +
+
+
+
+
+ + + + + + + +
+ ); + })} {results.data.length === 0 && ( diff --git a/apps/web/src/components/(dashboard)/avatar/index.tsx b/apps/web/src/components/(dashboard)/avatar/index.tsx index a784cbbff..2a263893a 100644 --- a/apps/web/src/components/(dashboard)/avatar/index.tsx +++ b/apps/web/src/components/(dashboard)/avatar/index.tsx @@ -11,7 +11,7 @@ export const StackAvatar = ({ first, zIndex, fallbackText, type }: StackAvatarPr let classes = ''; switch (type) { case 'unsigned': - classes = 'bg-dawn-400 text-dawn-900'; + classes = 'bg-dawn-200 text-dawn-900'; break; case 'waiting': classes = 'bg-water text-water-700'; From dbcf7771b91d8ed8f4006c0e3ad043ede1c40ff7 Mon Sep 17 00:00:00 2001 From: Ephraim Atta-Duncan Date: Sun, 25 Jun 2023 14:23:18 +0000 Subject: [PATCH 3/7] chore: refactor stacked avatars into component --- .../src/app/(dashboard)/dashboard/page.tsx | 82 +++++-------------- .../(dashboard)/avatar/stack-avatars.tsx | 36 ++++++++ .../lib/client-only/recipient-initials.ts | 6 ++ packages/lib/client-only/recipient-type.ts | 8 ++ 4 files changed, 72 insertions(+), 60 deletions(-) create mode 100644 apps/web/src/components/(dashboard)/avatar/stack-avatars.tsx create mode 100644 packages/lib/client-only/recipient-initials.ts create mode 100644 packages/lib/client-only/recipient-type.ts diff --git a/apps/web/src/app/(dashboard)/dashboard/page.tsx b/apps/web/src/app/(dashboard)/dashboard/page.tsx index 8526a8c05..cf4848ec9 100644 --- a/apps/web/src/app/(dashboard)/dashboard/page.tsx +++ b/apps/web/src/app/(dashboard)/dashboard/page.tsx @@ -2,6 +2,8 @@ import Link from 'next/link'; import { Clock, File, FileCheck } from 'lucide-react'; +import { initials } from '@documenso/lib/client-only/recipient-initials'; +import { type } from '@documenso/lib/client-only/recipient-type'; 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'; @@ -22,68 +24,13 @@ import { } from '@documenso/ui/primitives/tooltip'; import { StackAvatar } from '~/components/(dashboard)/avatar'; +import { StackAvatars } from '~/components/(dashboard)/avatar/stack-avatars'; 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 ( - - ); - }); -}; - -const renderAvatar = (recipient: Recipient) => { - const initials = - recipient.name - ?.split(' ') - .map((name: string) => name.slice(0, 1).toUpperCase()) - .slice(0, 2) - .join('') ?? 'UK'; - - 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(); @@ -156,7 +103,7 @@ export default async function DashboardPage() { - {renderStackAvatars(document.Recipient)} +
@@ -168,7 +115,12 @@ export default async function DashboardPage() { key={recipient.id} className="my-1 flex items-center gap-2" > - {renderAvatar(recipient)} + {recipient.email} @@ -185,7 +137,12 @@ export default async function DashboardPage() { key={recipient.id} className="my-1 flex items-center gap-2" > - {renderAvatar(recipient)} + {recipient.email} @@ -202,7 +159,12 @@ export default async function DashboardPage() { key={recipient.id} className="my-1 flex items-center gap-2" > - {renderAvatar(recipient)} + {recipient.email} diff --git a/apps/web/src/components/(dashboard)/avatar/stack-avatars.tsx b/apps/web/src/components/(dashboard)/avatar/stack-avatars.tsx new file mode 100644 index 000000000..69c2fae7e --- /dev/null +++ b/apps/web/src/components/(dashboard)/avatar/stack-avatars.tsx @@ -0,0 +1,36 @@ +import React from 'react'; + +import { initials } from '@documenso/lib/client-only/recipient-initials'; +import { type } from '@documenso/lib/client-only/recipient-type'; +import { Recipient } from '@documenso/prisma/client'; + +import { StackAvatar } from '.'; + +export function StackAvatars({ recipients }: { recipients: Recipient[] }) { + 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 lastItemText = + index === itemsToRender.length - 1 && remainingItems > 0 + ? `+${remainingItems + 1}` + : undefined; + + return ( + + ); + }); + }; + + return <>{renderStackAvatars(recipients)}; +} diff --git a/packages/lib/client-only/recipient-initials.ts b/packages/lib/client-only/recipient-initials.ts new file mode 100644 index 000000000..0712ccd7d --- /dev/null +++ b/packages/lib/client-only/recipient-initials.ts @@ -0,0 +1,6 @@ +export const initials = (text: string) => + text + ?.split(' ') + .map((name: string) => name.slice(0, 1).toUpperCase()) + .slice(0, 2) + .join('') ?? 'UK'; diff --git a/packages/lib/client-only/recipient-type.ts b/packages/lib/client-only/recipient-type.ts new file mode 100644 index 000000000..dbcf5baaf --- /dev/null +++ b/packages/lib/client-only/recipient-type.ts @@ -0,0 +1,8 @@ +import { Recipient } from '@documenso/prisma/client'; + +export const type = (recipient: Recipient) => + recipient.sendStatus === 'SENT' && recipient.signingStatus === 'SIGNED' + ? 'completed' + : recipient.sendStatus === 'SENT' && recipient.signingStatus === 'NOT_SIGNED' + ? 'waiting' + : 'unsigned'; From aa884310eb71a2fa6fba4590462858f30249dec7 Mon Sep 17 00:00:00 2001 From: Ephraim Atta-Duncan Date: Sun, 25 Jun 2023 15:14:48 +0000 Subject: [PATCH 4/7] feat: add recipients avatars on all tables --- .../src/app/(dashboard)/dashboard/page.tsx | 104 +----------------- .../app/(dashboard)/documents/data-table.tsx | 12 +- .../avatar/{index.tsx => stack-avatar.tsx} | 0 .../avatar/stack-avatars-with-tooltip.tsx | 90 +++++++++++++++ .../(dashboard)/avatar/stack-avatars.tsx | 2 +- .../server-only/document/find-documents.ts | 7 +- packages/lib/types/document-with-recipient.ts | 5 + 7 files changed, 110 insertions(+), 110 deletions(-) rename apps/web/src/components/(dashboard)/avatar/{index.tsx => stack-avatar.tsx} (100%) create mode 100644 apps/web/src/components/(dashboard)/avatar/stack-avatars-with-tooltip.tsx create mode 100644 packages/lib/types/document-with-recipient.ts diff --git a/apps/web/src/app/(dashboard)/dashboard/page.tsx b/apps/web/src/app/(dashboard)/dashboard/page.tsx index cf4848ec9..2f5f259ab 100644 --- a/apps/web/src/app/(dashboard)/dashboard/page.tsx +++ b/apps/web/src/app/(dashboard)/dashboard/page.tsx @@ -2,12 +2,9 @@ import Link from 'next/link'; import { Clock, File, FileCheck } from 'lucide-react'; -import { initials } from '@documenso/lib/client-only/recipient-initials'; -import { type } from '@documenso/lib/client-only/recipient-type'; 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, @@ -16,15 +13,8 @@ import { TableHeader, TableRow, } from '@documenso/ui/primitives/table'; -import { - Tooltip, - TooltipContent, - TooltipProvider, - TooltipTrigger, -} from '@documenso/ui/primitives/tooltip'; -import { StackAvatar } from '~/components/(dashboard)/avatar'; -import { StackAvatars } from '~/components/(dashboard)/avatar/stack-avatars'; +import { StackAvatarsWithTooltip } from '~/components/(dashboard)/avatar/stack-avatars-with-tooltip'; import { CardMetric } from '~/components/(dashboard)/metric-card/metric-card'; import { DocumentStatus } from '~/components/formatter/document-status'; import { LocaleDate } from '~/components/formatter/locale-date'; @@ -72,21 +62,6 @@ export default async function DashboardPage() { {results.data.map((document) => { - const waitingRecipients = document.Recipient.filter( - (recipient) => - recipient.sendStatus === 'SENT' && recipient.signingStatus === 'NOT_SIGNED', - ); - - const completedRecipients = document.Recipient.filter( - (recipient) => - recipient.sendStatus === 'SENT' && recipient.signingStatus === 'SIGNED', - ); - - const uncompletedRecipients = document.Recipient.filter( - (recipient) => - recipient.sendStatus === 'NOT_SENT' && recipient.signingStatus === 'NOT_SIGNED', - ); - return ( {document.id} @@ -100,82 +75,7 @@ export default async function DashboardPage() { - - - - - - -
- {completedRecipients.length > 0 && ( -
-

Completed

- {completedRecipients.map((recipient: Recipient) => ( -
- - - {recipient.email} - -
- ))} -
- )} - - {waitingRecipients.length > 0 && ( -
-

Waiting

- {waitingRecipients.map((recipient: Recipient) => ( -
- - - {recipient.email} - -
- ))} -
- )} - - {uncompletedRecipients.length > 0 && ( -
-

Uncompleted

- {uncompletedRecipients.map((recipient: Recipient) => ( -
- - - {recipient.email} - -
- ))} -
- )} -
-
-
-
+
diff --git a/apps/web/src/app/(dashboard)/documents/data-table.tsx b/apps/web/src/app/(dashboard)/documents/data-table.tsx index 58b6eb1ac..264b3596e 100644 --- a/apps/web/src/app/(dashboard)/documents/data-table.tsx +++ b/apps/web/src/app/(dashboard)/documents/data-table.tsx @@ -7,16 +7,17 @@ import Link from 'next/link'; import { Loader } from 'lucide-react'; import { useUpdateSearchParams } from '@documenso/lib/client-only/hooks/use-update-search-params'; +import { DocumentWithReciepient } from '@documenso/lib/types/document-with-recipient'; import { FindResultSet } from '@documenso/lib/types/find-result-set'; -import { Document } from '@documenso/prisma/client'; import { DataTable } from '@documenso/ui/primitives/data-table'; import { DataTablePagination } from '@documenso/ui/primitives/data-table-pagination'; +import { StackAvatarsWithTooltip } from '~/components/(dashboard)/avatar/stack-avatars-with-tooltip'; import { DocumentStatus } from '~/components/formatter/document-status'; import { LocaleDate } from '~/components/formatter/locale-date'; export type DocumentsDataTableProps = { - results: FindResultSet; + results: FindResultSet; }; export const DocumentsDataTable = ({ results }: DocumentsDataTableProps) => { @@ -49,6 +50,13 @@ export const DocumentsDataTable = ({ results }: DocumentsDataTableProps) => { ), }, + { + header: 'Recipient', + accessorKey: 'recipient', + cell: ({ row }) => { + return ; + }, + }, { header: 'Status', accessorKey: 'status', diff --git a/apps/web/src/components/(dashboard)/avatar/index.tsx b/apps/web/src/components/(dashboard)/avatar/stack-avatar.tsx similarity index 100% rename from apps/web/src/components/(dashboard)/avatar/index.tsx rename to apps/web/src/components/(dashboard)/avatar/stack-avatar.tsx diff --git a/apps/web/src/components/(dashboard)/avatar/stack-avatars-with-tooltip.tsx b/apps/web/src/components/(dashboard)/avatar/stack-avatars-with-tooltip.tsx new file mode 100644 index 000000000..8a918fce0 --- /dev/null +++ b/apps/web/src/components/(dashboard)/avatar/stack-avatars-with-tooltip.tsx @@ -0,0 +1,90 @@ +import { initials } from '@documenso/lib/client-only/recipient-initials'; +import { type } from '@documenso/lib/client-only/recipient-type'; +import { Recipient } from '@documenso/prisma/client'; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from '@documenso/ui/primitives/tooltip'; + +import { StackAvatar } from './stack-avatar'; +import { StackAvatars } from './stack-avatars'; + +export const StackAvatarsWithTooltip = ({ recipients }: { recipients: Recipient[] }) => { + const waitingRecipients = recipients.filter( + (recipient) => recipient.sendStatus === 'SENT' && recipient.signingStatus === 'NOT_SIGNED', + ); + + const completedRecipients = recipients.filter( + (recipient) => recipient.sendStatus === 'SENT' && recipient.signingStatus === 'SIGNED', + ); + + const uncompletedRecipients = recipients.filter( + (recipient) => recipient.sendStatus === 'NOT_SENT' && recipient.signingStatus === 'NOT_SIGNED', + ); + + return ( + + + + + + +
+ {completedRecipients.length > 0 && ( +
+

Completed

+ {completedRecipients.map((recipient: Recipient) => ( +
+ + {recipient.email} +
+ ))} +
+ )} + + {waitingRecipients.length > 0 && ( +
+

Waiting

+ {waitingRecipients.map((recipient: Recipient) => ( +
+ + {recipient.email} +
+ ))} +
+ )} + + {uncompletedRecipients.length > 0 && ( +
+

Uncompleted

+ {uncompletedRecipients.map((recipient: Recipient) => ( +
+ + {recipient.email} +
+ ))} +
+ )} +
+
+
+
+ ); +}; diff --git a/apps/web/src/components/(dashboard)/avatar/stack-avatars.tsx b/apps/web/src/components/(dashboard)/avatar/stack-avatars.tsx index 69c2fae7e..c8d63bb82 100644 --- a/apps/web/src/components/(dashboard)/avatar/stack-avatars.tsx +++ b/apps/web/src/components/(dashboard)/avatar/stack-avatars.tsx @@ -4,7 +4,7 @@ import { initials } from '@documenso/lib/client-only/recipient-initials'; import { type } from '@documenso/lib/client-only/recipient-type'; import { Recipient } from '@documenso/prisma/client'; -import { StackAvatar } from '.'; +import { StackAvatar } from './stack-avatar'; export function StackAvatars({ recipients }: { recipients: Recipient[] }) { const renderStackAvatars = (recipients: Recipient[]) => { diff --git a/packages/lib/server-only/document/find-documents.ts b/packages/lib/server-only/document/find-documents.ts index 005b6614a..60fe8a5b3 100644 --- a/packages/lib/server-only/document/find-documents.ts +++ b/packages/lib/server-only/document/find-documents.ts @@ -1,6 +1,7 @@ import { prisma } from '@documenso/prisma'; -import { Document, DocumentStatus, Prisma, Recipient } from '@documenso/prisma/client'; +import { Document, DocumentStatus, Prisma } from '@documenso/prisma/client'; +import { DocumentWithReciepient } from '../../types/document-with-recipient'; import { FindResultSet } from '../../types/find-result-set'; export interface FindDocumentsOptions { @@ -15,10 +16,6 @@ export interface FindDocumentsOptions { }; } -export type DocumentWithReciepient = Document & { - Recipient: Recipient[]; -}; - export const findDocuments = async ({ userId, term, diff --git a/packages/lib/types/document-with-recipient.ts b/packages/lib/types/document-with-recipient.ts new file mode 100644 index 000000000..208fb2b68 --- /dev/null +++ b/packages/lib/types/document-with-recipient.ts @@ -0,0 +1,5 @@ +import { Document, Recipient } from '@documenso/prisma/client'; + +export type DocumentWithReciepient = Document & { + Recipient: Recipient[]; +}; From 88d15376e30ad57c5c10f881cd006bb38b4575a1 Mon Sep 17 00:00:00 2001 From: Ephraim Atta-Duncan Date: Fri, 30 Jun 2023 23:38:37 +0000 Subject: [PATCH 5/7] feat: update stack avatar with changes from code review --- .../(dashboard)/avatar/stack-avatar.tsx | 29 +++++++++++++++++-- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/apps/web/src/components/(dashboard)/avatar/stack-avatar.tsx b/apps/web/src/components/(dashboard)/avatar/stack-avatar.tsx index 2a263893a..3737e9ccf 100644 --- a/apps/web/src/components/(dashboard)/avatar/stack-avatar.tsx +++ b/apps/web/src/components/(dashboard)/avatar/stack-avatar.tsx @@ -9,6 +9,9 @@ export type StackAvatarProps = { export const StackAvatar = ({ first, zIndex, fallbackText, type }: StackAvatarProps) => { let classes = ''; + let zIndexClass = ''; + const firstClass = first ? '' : '-ml-3'; + switch (type) { case 'unsigned': classes = 'bg-dawn-200 text-dawn-900'; @@ -23,12 +26,32 @@ export const StackAvatar = ({ first, zIndex, fallbackText, type }: StackAvatarPr break; } + switch (zIndex) { + case '10': + zIndexClass = 'z-10'; + break; + case '20': + zIndexClass = 'z-20'; + break; + case '30': + zIndexClass = 'z-30'; + break; + case '40': + zIndexClass = 'z-40'; + break; + case '50': + zIndexClass = 'z-50'; + break; + default: + break; + } + return ( {fallbackText ?? 'UK'} From b50f64d4ad2777e4fef0c035228142f3ee388a52 Mon Sep 17 00:00:00 2001 From: Ephraim Atta-Duncan Date: Fri, 30 Jun 2023 23:49:34 +0000 Subject: [PATCH 6/7] fix: update types from code review --- .../app/(dashboard)/documents/data-table.tsx | 2 +- .../avatar/stack-avatars-with-tooltip.tsx | 8 ++++---- .../(dashboard)/avatar/stack-avatars.tsx | 4 ++-- packages/lib/client-only/recipient-type.ts | 17 +++++++++++------ .../lib/server-only/document/find-documents.ts | 2 +- .../types/document-with-recipient.ts | 0 6 files changed, 19 insertions(+), 14 deletions(-) rename packages/{lib => prisma}/types/document-with-recipient.ts (100%) diff --git a/apps/web/src/app/(dashboard)/documents/data-table.tsx b/apps/web/src/app/(dashboard)/documents/data-table.tsx index 264b3596e..35fdfb4b1 100644 --- a/apps/web/src/app/(dashboard)/documents/data-table.tsx +++ b/apps/web/src/app/(dashboard)/documents/data-table.tsx @@ -7,8 +7,8 @@ import Link from 'next/link'; import { Loader } from 'lucide-react'; import { useUpdateSearchParams } from '@documenso/lib/client-only/hooks/use-update-search-params'; -import { DocumentWithReciepient } from '@documenso/lib/types/document-with-recipient'; import { FindResultSet } from '@documenso/lib/types/find-result-set'; +import { DocumentWithReciepient } from '@documenso/prisma/types/document-with-recipient'; import { DataTable } from '@documenso/ui/primitives/data-table'; import { DataTablePagination } from '@documenso/ui/primitives/data-table-pagination'; diff --git a/apps/web/src/components/(dashboard)/avatar/stack-avatars-with-tooltip.tsx b/apps/web/src/components/(dashboard)/avatar/stack-avatars-with-tooltip.tsx index 8a918fce0..dbd1dc712 100644 --- a/apps/web/src/components/(dashboard)/avatar/stack-avatars-with-tooltip.tsx +++ b/apps/web/src/components/(dashboard)/avatar/stack-avatars-with-tooltip.tsx @@ -1,5 +1,5 @@ import { initials } from '@documenso/lib/client-only/recipient-initials'; -import { type } from '@documenso/lib/client-only/recipient-type'; +import { getRecipientType } from '@documenso/lib/client-only/recipient-type'; import { Recipient } from '@documenso/prisma/client'; import { Tooltip, @@ -40,7 +40,7 @@ export const StackAvatarsWithTooltip = ({ recipients }: { recipients: Recipient[ {recipient.email} @@ -57,7 +57,7 @@ export const StackAvatarsWithTooltip = ({ recipients }: { recipients: Recipient[ {recipient.email} @@ -74,7 +74,7 @@ export const StackAvatarsWithTooltip = ({ recipients }: { recipients: Recipient[ {recipient.email} diff --git a/apps/web/src/components/(dashboard)/avatar/stack-avatars.tsx b/apps/web/src/components/(dashboard)/avatar/stack-avatars.tsx index c8d63bb82..97af9dc9e 100644 --- a/apps/web/src/components/(dashboard)/avatar/stack-avatars.tsx +++ b/apps/web/src/components/(dashboard)/avatar/stack-avatars.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { initials } from '@documenso/lib/client-only/recipient-initials'; -import { type } from '@documenso/lib/client-only/recipient-type'; +import { getRecipientType } from '@documenso/lib/client-only/recipient-type'; import { Recipient } from '@documenso/prisma/client'; import { StackAvatar } from './stack-avatar'; @@ -25,7 +25,7 @@ export function StackAvatars({ recipients }: { recipients: Recipient[] }) { key={recipient.id} first={first} zIndex={String(zIndex - index * 10)} - type={lastItemText && index === 4 ? 'unsigned' : type(recipient)} + type={lastItemText && index === 4 ? 'unsigned' : getRecipientType(recipient)} fallbackText={lastItemText ? lastItemText : initials(recipient.name)} /> ); diff --git a/packages/lib/client-only/recipient-type.ts b/packages/lib/client-only/recipient-type.ts index dbcf5baaf..8250eb707 100644 --- a/packages/lib/client-only/recipient-type.ts +++ b/packages/lib/client-only/recipient-type.ts @@ -1,8 +1,13 @@ import { Recipient } from '@documenso/prisma/client'; -export const type = (recipient: Recipient) => - recipient.sendStatus === 'SENT' && recipient.signingStatus === 'SIGNED' - ? 'completed' - : recipient.sendStatus === 'SENT' && recipient.signingStatus === 'NOT_SIGNED' - ? 'waiting' - : 'unsigned'; +export const getRecipientType = (recipient: Recipient) => { + if (recipient.sendStatus === 'SENT' && recipient.signingStatus === 'SIGNED') { + return 'completed'; + } + + if (recipient.sendStatus === 'SENT' && recipient.signingStatus === 'NOT_SIGNED') { + return 'waiting'; + } + + return 'unsigned'; +}; diff --git a/packages/lib/server-only/document/find-documents.ts b/packages/lib/server-only/document/find-documents.ts index 60fe8a5b3..41e9c858a 100644 --- a/packages/lib/server-only/document/find-documents.ts +++ b/packages/lib/server-only/document/find-documents.ts @@ -1,7 +1,7 @@ import { prisma } from '@documenso/prisma'; import { Document, DocumentStatus, Prisma } from '@documenso/prisma/client'; +import { DocumentWithReciepient } from '@documenso/prisma/types/document-with-recipient'; -import { DocumentWithReciepient } from '../../types/document-with-recipient'; import { FindResultSet } from '../../types/find-result-set'; export interface FindDocumentsOptions { diff --git a/packages/lib/types/document-with-recipient.ts b/packages/prisma/types/document-with-recipient.ts similarity index 100% rename from packages/lib/types/document-with-recipient.ts rename to packages/prisma/types/document-with-recipient.ts From a5334ca6e6219386d14f079eb03ff852c63624f1 Mon Sep 17 00:00:00 2001 From: Ephraim Atta-Duncan Date: Wed, 5 Jul 2023 20:47:12 +0000 Subject: [PATCH 7/7] refactor: read z-index values from an object --- .../(dashboard)/avatar/stack-avatar.tsx | 32 +++++++------------ 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/apps/web/src/components/(dashboard)/avatar/stack-avatar.tsx b/apps/web/src/components/(dashboard)/avatar/stack-avatar.tsx index 3737e9ccf..e79a2e71b 100644 --- a/apps/web/src/components/(dashboard)/avatar/stack-avatar.tsx +++ b/apps/web/src/components/(dashboard)/avatar/stack-avatar.tsx @@ -1,5 +1,13 @@ import { Avatar, AvatarFallback } from '@documenso/ui/primitives/avatar'; +const ZIndexes: { [key: string]: string } = { + '10': 'z-10', + '20': 'z-20', + '30': 'z-30', + '40': 'z-40', + '50': 'z-50', +}; + export type StackAvatarProps = { first?: boolean; zIndex?: string; @@ -12,6 +20,10 @@ export const StackAvatar = ({ first, zIndex, fallbackText, type }: StackAvatarPr let zIndexClass = ''; const firstClass = first ? '' : '-ml-3'; + if (zIndex) { + zIndexClass = ZIndexes[zIndex] ?? ''; + } + switch (type) { case 'unsigned': classes = 'bg-dawn-200 text-dawn-900'; @@ -26,26 +38,6 @@ export const StackAvatar = ({ first, zIndex, fallbackText, type }: StackAvatarPr break; } - switch (zIndex) { - case '10': - zIndexClass = 'z-10'; - break; - case '20': - zIndexClass = 'z-20'; - break; - case '30': - zIndexClass = 'z-30'; - break; - case '40': - zIndexClass = 'z-40'; - break; - case '50': - zIndexClass = 'z-50'; - break; - default: - break; - } - return (