mirror of
https://github.com/documenso/documenso.git
synced 2026-08-15 11:01:47 +10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5eab99e8d4 |
@@ -9,7 +9,6 @@ import { useToast } from '@documenso/ui/primitives/use-toast';
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { useLingui } from '@lingui/react';
|
||||
import { DocumentStatus } from '@prisma/client';
|
||||
import { CopyIcon } from 'lucide-react';
|
||||
|
||||
import { StackAvatar } from './stack-avatar';
|
||||
|
||||
@@ -41,8 +40,8 @@ export function AvatarWithRecipient({ recipient, documentStatus }: AvatarWithRec
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn('-mx-2 flex items-center gap-2.5 rounded-md px-2 py-1.5', {
|
||||
'group cursor-pointer hover:bg-muted': signingToken,
|
||||
className={cn('my-1 flex items-center gap-2', {
|
||||
'cursor-pointer hover:underline': signingToken,
|
||||
})}
|
||||
role={signingToken ? 'button' : undefined}
|
||||
title={signingToken ? _(msg`Click to copy signing link for sending to recipient`) : undefined}
|
||||
@@ -53,19 +52,15 @@ export function AvatarWithRecipient({ recipient, documentStatus }: AvatarWithRec
|
||||
key={recipient.id}
|
||||
type={getRecipientType(recipient)}
|
||||
fallbackText={recipientAbbreviation(recipient)}
|
||||
className="h-8 w-8 shrink-0 border-0 text-xs"
|
||||
/>
|
||||
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="truncate font-medium text-foreground text-sm">{recipient.email || recipient.name}</p>
|
||||
<p className="truncate text-muted-foreground text-xs">
|
||||
{_(RECIPIENT_ROLES_DESCRIPTION[recipient.role].roleName)}
|
||||
</p>
|
||||
<div
|
||||
className="text-muted-foreground text-sm"
|
||||
title={signingToken ? _(msg`Click to copy signing link for sending to recipient`) : undefined}
|
||||
>
|
||||
<p>{recipient.email || recipient.name}</p>
|
||||
<p className="text-muted-foreground/70 text-xs">{_(RECIPIENT_ROLES_DESCRIPTION[recipient.role].roleName)}</p>
|
||||
</div>
|
||||
|
||||
{signingToken && (
|
||||
<CopyIcon className="h-3.5 w-3.5 shrink-0 text-muted-foreground opacity-0 group-hover:opacity-100" />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { RecipientStatusType } from '@documenso/lib/client-only/recipient-type';
|
||||
import { cn } from '@documenso/ui/lib/utils';
|
||||
import { Avatar, AvatarFallback } from '@documenso/ui/primitives/avatar';
|
||||
|
||||
const ZIndexes: { [key: string]: string } = {
|
||||
@@ -15,10 +14,9 @@ export type StackAvatarProps = {
|
||||
zIndex?: string;
|
||||
fallbackText?: string;
|
||||
type: RecipientStatusType;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export const StackAvatar = ({ first, zIndex, fallbackText = '', type, className }: StackAvatarProps) => {
|
||||
export const StackAvatar = ({ first, zIndex, fallbackText = '', type }: StackAvatarProps) => {
|
||||
let classes = '';
|
||||
let zIndexClass = '';
|
||||
const firstClass = first ? '' : '-ml-3';
|
||||
@@ -48,14 +46,7 @@ export const StackAvatar = ({ first, zIndex, fallbackText = '', type, className
|
||||
}
|
||||
|
||||
return (
|
||||
<Avatar
|
||||
className={cn(
|
||||
zIndexClass,
|
||||
firstClass,
|
||||
'h-10 w-10 border-2 border-white border-solid dark:border-border',
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<Avatar className={` ${zIndexClass} ${firstClass} h-10 w-10 border-2 border-white border-solid dark:border-border`}>
|
||||
<AvatarFallback className={classes}>{fallbackText}</AvatarFallback>
|
||||
</Avatar>
|
||||
);
|
||||
|
||||
@@ -2,7 +2,6 @@ import { getRecipientType, RecipientStatusType } from '@documenso/lib/client-onl
|
||||
import { RECIPIENT_ROLES_DESCRIPTION } from '@documenso/lib/constants/recipient-roles';
|
||||
import type { TRecipientLite } from '@documenso/lib/types/recipient';
|
||||
import { recipientAbbreviation } from '@documenso/lib/utils/recipient-formatter';
|
||||
import { cn } from '@documenso/ui/lib/utils';
|
||||
import { PopoverHover } from '@documenso/ui/primitives/popover';
|
||||
import { useLingui } from '@lingui/react';
|
||||
import { Trans } from '@lingui/react/macro';
|
||||
@@ -59,90 +58,94 @@ export const StackAvatarsWithTooltip = ({
|
||||
];
|
||||
}, [recipients]);
|
||||
|
||||
const sections = [
|
||||
{
|
||||
key: 'completed',
|
||||
label: <Trans>Completed</Trans>,
|
||||
dotClassName: 'bg-green-500',
|
||||
labelClassName: 'text-green-600 dark:text-green-400',
|
||||
recipients: completedRecipients,
|
||||
isInteractive: false,
|
||||
},
|
||||
{
|
||||
key: 'rejected',
|
||||
label: <Trans>Rejected</Trans>,
|
||||
dotClassName: 'bg-red-500',
|
||||
labelClassName: 'text-red-600 dark:text-red-400',
|
||||
recipients: rejectedRecipients,
|
||||
isInteractive: false,
|
||||
},
|
||||
{
|
||||
key: 'waiting',
|
||||
label: <Trans>Waiting</Trans>,
|
||||
dotClassName: 'bg-blue-500',
|
||||
labelClassName: 'text-blue-600 dark:text-blue-400',
|
||||
recipients: waitingRecipients,
|
||||
isInteractive: true,
|
||||
},
|
||||
{
|
||||
key: 'opened',
|
||||
label: <Trans>Opened</Trans>,
|
||||
dotClassName: 'bg-amber-400',
|
||||
labelClassName: 'text-amber-600 dark:text-amber-400',
|
||||
recipients: openedRecipients,
|
||||
isInteractive: true,
|
||||
},
|
||||
{
|
||||
key: 'uncompleted',
|
||||
label: <Trans>Uncompleted</Trans>,
|
||||
dotClassName: 'bg-gray-400',
|
||||
labelClassName: 'text-muted-foreground',
|
||||
recipients: uncompletedRecipients,
|
||||
isInteractive: true,
|
||||
},
|
||||
].filter((section) => section.recipients.length > 0);
|
||||
|
||||
return (
|
||||
<PopoverHover
|
||||
trigger={children || <StackAvatars recipients={sortedRecipients} />}
|
||||
contentProps={{
|
||||
className: 'w-64 divide-y divide-border/50 overflow-hidden p-0 text-sm',
|
||||
className: 'flex flex-col gap-y-5 py-2',
|
||||
side: position,
|
||||
}}
|
||||
>
|
||||
{sections.map((section) => (
|
||||
<div key={section.key} className="px-3 pt-2.5 pb-1">
|
||||
<div className="flex items-center gap-1.5 text-xs">
|
||||
<span className={cn('h-1.5 w-1.5 rounded-full', section.dotClassName)} />
|
||||
<span className={section.labelClassName}>{section.label}</span>
|
||||
<span className="text-muted-foreground">{section.recipients.length}</span>
|
||||
</div>
|
||||
|
||||
<div className="mt-1.5">
|
||||
{section.isInteractive
|
||||
? section.recipients.map((recipient) => (
|
||||
<AvatarWithRecipient key={recipient.id} recipient={recipient} documentStatus={documentStatus} />
|
||||
))
|
||||
: section.recipients.map((recipient) => (
|
||||
<div key={recipient.id} className="-mx-2 flex items-center gap-2.5 rounded-md px-2 py-1.5">
|
||||
<StackAvatar
|
||||
first={true}
|
||||
type={getRecipientType(recipient)}
|
||||
fallbackText={recipientAbbreviation(recipient)}
|
||||
className="h-8 w-8 shrink-0 border-0 text-xs"
|
||||
/>
|
||||
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="truncate font-medium text-foreground">{recipient.email || recipient.name}</p>
|
||||
<p className="truncate text-muted-foreground text-xs">
|
||||
{_(RECIPIENT_ROLES_DESCRIPTION[recipient.role].roleName)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
{completedRecipients.length > 0 && (
|
||||
<div>
|
||||
<h1 className="font-medium text-base">
|
||||
<Trans>Completed</Trans>
|
||||
</h1>
|
||||
{completedRecipients.map((recipient) => (
|
||||
<div key={recipient.id} className="my-1 flex items-center gap-2">
|
||||
<StackAvatar
|
||||
first={true}
|
||||
key={recipient.id}
|
||||
type={getRecipientType(recipient)}
|
||||
fallbackText={recipientAbbreviation(recipient)}
|
||||
/>
|
||||
<div>
|
||||
<p className="text-muted-foreground text-sm">{recipient.email || recipient.name}</p>
|
||||
<p className="text-muted-foreground/70 text-xs">
|
||||
{_(RECIPIENT_ROLES_DESCRIPTION[recipient.role].roleName)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
)}
|
||||
|
||||
{rejectedRecipients.length > 0 && (
|
||||
<div>
|
||||
<h1 className="font-medium text-base">
|
||||
<Trans>Rejected</Trans>
|
||||
</h1>
|
||||
{rejectedRecipients.map((recipient) => (
|
||||
<div key={recipient.id} className="my-1 flex items-center gap-2">
|
||||
<StackAvatar
|
||||
first={true}
|
||||
key={recipient.id}
|
||||
type={getRecipientType(recipient)}
|
||||
fallbackText={recipientAbbreviation(recipient)}
|
||||
/>
|
||||
<div>
|
||||
<p className="text-muted-foreground text-sm">{recipient.email || recipient.name}</p>
|
||||
<p className="text-muted-foreground/70 text-xs">
|
||||
{_(RECIPIENT_ROLES_DESCRIPTION[recipient.role].roleName)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{waitingRecipients.length > 0 && (
|
||||
<div>
|
||||
<h1 className="font-medium text-base">
|
||||
<Trans>Waiting</Trans>
|
||||
</h1>
|
||||
{waitingRecipients.map((recipient) => (
|
||||
<AvatarWithRecipient key={recipient.id} recipient={recipient} documentStatus={documentStatus} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{openedRecipients.length > 0 && (
|
||||
<div>
|
||||
<h1 className="font-medium text-base">
|
||||
<Trans>Opened</Trans>
|
||||
</h1>
|
||||
{openedRecipients.map((recipient) => (
|
||||
<AvatarWithRecipient key={recipient.id} recipient={recipient} documentStatus={documentStatus} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{uncompletedRecipients.length > 0 && (
|
||||
<div>
|
||||
<h1 className="font-medium text-base">
|
||||
<Trans>Uncompleted</Trans>
|
||||
</h1>
|
||||
{uncompletedRecipients.map((recipient) => (
|
||||
<AvatarWithRecipient key={recipient.id} recipient={recipient} documentStatus={documentStatus} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</PopoverHover>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -38,11 +38,16 @@ export const AdminClaimsTable = ({ licenseFlags }: AdminClaimsTableProps) => {
|
||||
|
||||
const parsedSearchParams = ZUrlSearchParamsSchema.parse(Object.fromEntries(searchParams ?? []));
|
||||
|
||||
const { data, isLoading, isLoadingError } = trpc.admin.claims.find.useQuery({
|
||||
query: parsedSearchParams.query,
|
||||
page: parsedSearchParams.page,
|
||||
perPage: parsedSearchParams.perPage,
|
||||
});
|
||||
const { data, isLoading, isLoadingError } = trpc.admin.claims.find.useQuery(
|
||||
{
|
||||
query: parsedSearchParams.query,
|
||||
page: parsedSearchParams.page,
|
||||
perPage: parsedSearchParams.perPage,
|
||||
},
|
||||
{
|
||||
placeholderData: (previousData) => previousData,
|
||||
},
|
||||
);
|
||||
|
||||
const onPaginationChange = (page: number, perPage: number) => {
|
||||
updateSearchParams({
|
||||
|
||||
@@ -19,11 +19,16 @@ export const AdminDocumentJobsTable = ({ envelopeId }: { envelopeId: string }) =
|
||||
|
||||
const parsedSearchParams = ZUrlSearchParamsSchema.parse(Object.fromEntries(searchParams ?? []));
|
||||
|
||||
const { data, isLoading, isLoadingError, refetch, isFetching } = trpc.admin.document.findJobs.useQuery({
|
||||
envelopeId: envelopeId,
|
||||
page: parsedSearchParams.page,
|
||||
perPage: parsedSearchParams.perPage,
|
||||
});
|
||||
const { data, isLoading, isLoadingError, refetch, isFetching } = trpc.admin.document.findJobs.useQuery(
|
||||
{
|
||||
envelopeId: envelopeId,
|
||||
page: parsedSearchParams.page,
|
||||
perPage: parsedSearchParams.perPage,
|
||||
},
|
||||
{
|
||||
placeholderData: (previousData) => previousData,
|
||||
},
|
||||
);
|
||||
|
||||
const onPaginationChange = (page: number, perPage: number) => {
|
||||
updateSearchParams({
|
||||
|
||||
@@ -30,11 +30,16 @@ export const AdminEmailTransportsTable = () => {
|
||||
|
||||
const parsedSearchParams = ZUrlSearchParamsSchema.parse(Object.fromEntries(searchParams ?? []));
|
||||
|
||||
const { data, isLoading, isLoadingError } = trpc.admin.emailTransport.find.useQuery({
|
||||
query: parsedSearchParams.query,
|
||||
page: parsedSearchParams.page,
|
||||
perPage: parsedSearchParams.perPage,
|
||||
});
|
||||
const { data, isLoading, isLoadingError } = trpc.admin.emailTransport.find.useQuery(
|
||||
{
|
||||
query: parsedSearchParams.query,
|
||||
page: parsedSearchParams.page,
|
||||
perPage: parsedSearchParams.perPage,
|
||||
},
|
||||
{
|
||||
placeholderData: (previousData) => previousData,
|
||||
},
|
||||
);
|
||||
|
||||
const onPaginationChange = (page: number, perPage: number) => {
|
||||
updateSearchParams({
|
||||
|
||||
@@ -77,15 +77,20 @@ export const AdminOrganisationStatsTable = ({ displayMode = 'usage' }: AdminOrga
|
||||
const orderByColumn = parseOrderByColumn(searchParams?.get('orderByColumn') ?? undefined);
|
||||
const orderByDirection = parseOrderByDirection(searchParams?.get('orderByDirection') ?? undefined);
|
||||
|
||||
const { data, isLoading, isLoadingError } = trpc.admin.organisation.stats.find.useQuery({
|
||||
query: parsedSearchParams.query,
|
||||
page: parsedSearchParams.page,
|
||||
perPage: parsedSearchParams.perPage,
|
||||
period,
|
||||
claimId,
|
||||
orderByColumn,
|
||||
orderByDirection,
|
||||
});
|
||||
const { data, isLoading, isLoadingError } = trpc.admin.organisation.stats.find.useQuery(
|
||||
{
|
||||
query: parsedSearchParams.query,
|
||||
page: parsedSearchParams.page,
|
||||
perPage: parsedSearchParams.perPage,
|
||||
period,
|
||||
claimId,
|
||||
orderByColumn,
|
||||
orderByDirection,
|
||||
},
|
||||
{
|
||||
placeholderData: (previousData) => previousData,
|
||||
},
|
||||
);
|
||||
|
||||
const onPaginationChange = (page: number, perPage: number) => {
|
||||
updateSearchParams({
|
||||
|
||||
@@ -55,13 +55,18 @@ export const AdminOrganisationsTable = ({
|
||||
|
||||
const parsedSearchParams = ZUrlSearchParamsSchema.parse(Object.fromEntries(searchParams ?? []));
|
||||
|
||||
const { data, isLoading, isLoadingError } = trpc.admin.organisation.find.useQuery({
|
||||
query: parsedSearchParams.query,
|
||||
page: parsedSearchParams.page,
|
||||
perPage: parsedSearchParams.perPage,
|
||||
ownerUserId,
|
||||
memberUserId,
|
||||
});
|
||||
const { data, isLoading, isLoadingError } = trpc.admin.organisation.find.useQuery(
|
||||
{
|
||||
query: parsedSearchParams.query,
|
||||
page: parsedSearchParams.page,
|
||||
perPage: parsedSearchParams.perPage,
|
||||
ownerUserId,
|
||||
memberUserId,
|
||||
},
|
||||
{
|
||||
placeholderData: (previousData) => previousData,
|
||||
},
|
||||
);
|
||||
|
||||
const onPaginationChange = (page: number, perPage: number) => {
|
||||
updateSearchParams({
|
||||
|
||||
@@ -28,12 +28,17 @@ export const AdminUserTeamsTable = ({ userId }: AdminUserTeamsTableProps) => {
|
||||
|
||||
const parsedSearchParams = ZUrlSearchParamsSchema.parse(Object.fromEntries(searchParams ?? []));
|
||||
|
||||
const { data, isLoading, isLoadingError } = trpc.admin.user.findTeams.useQuery({
|
||||
userId,
|
||||
query: parsedSearchParams.query,
|
||||
page: parsedSearchParams.page,
|
||||
perPage: parsedSearchParams.perPage,
|
||||
});
|
||||
const { data, isLoading, isLoadingError } = trpc.admin.user.findTeams.useQuery(
|
||||
{
|
||||
userId,
|
||||
query: parsedSearchParams.query,
|
||||
page: parsedSearchParams.page,
|
||||
perPage: parsedSearchParams.perPage,
|
||||
},
|
||||
{
|
||||
placeholderData: (previousData) => previousData,
|
||||
},
|
||||
);
|
||||
|
||||
const onPaginationChange = (page: number, perPage: number) => {
|
||||
updateSearchParams({
|
||||
|
||||
@@ -46,10 +46,15 @@ export const InboxTable = () => {
|
||||
const page = searchParams?.get?.('page') ? Number(searchParams.get('page')) : undefined;
|
||||
const perPage = searchParams?.get?.('perPage') ? Number(searchParams.get('perPage')) : undefined;
|
||||
|
||||
const { data, isLoading, isLoadingError } = trpc.document.inbox.find.useQuery({
|
||||
page: page || 1,
|
||||
perPage: perPage || 10,
|
||||
});
|
||||
const { data, isLoading, isLoadingError } = trpc.document.inbox.find.useQuery(
|
||||
{
|
||||
page: page || 1,
|
||||
perPage: perPage || 10,
|
||||
},
|
||||
{
|
||||
placeholderData: (previousData) => previousData,
|
||||
},
|
||||
);
|
||||
|
||||
const columns = useMemo(() => {
|
||||
return [
|
||||
|
||||
@@ -28,12 +28,17 @@ export const OrganisationTeamsTable = () => {
|
||||
|
||||
const parsedSearchParams = ZUrlSearchParamsSchema.parse(Object.fromEntries(searchParams ?? []));
|
||||
|
||||
const { data, isLoading, isLoadingError } = trpc.team.find.useQuery({
|
||||
organisationId: organisation.id,
|
||||
query: parsedSearchParams.query,
|
||||
page: parsedSearchParams.page,
|
||||
perPage: parsedSearchParams.perPage,
|
||||
});
|
||||
const { data, isLoading, isLoadingError } = trpc.team.find.useQuery(
|
||||
{
|
||||
organisationId: organisation.id,
|
||||
query: parsedSearchParams.query,
|
||||
page: parsedSearchParams.page,
|
||||
perPage: parsedSearchParams.perPage,
|
||||
},
|
||||
{
|
||||
placeholderData: (previousData) => previousData,
|
||||
},
|
||||
);
|
||||
|
||||
const onPaginationChange = (page: number, perPage: number) => {
|
||||
updateSearchParams({
|
||||
|
||||
@@ -91,6 +91,7 @@ export default function DocumentsPage() {
|
||||
folderId,
|
||||
},
|
||||
{
|
||||
placeholderData: (previousData) => previousData,
|
||||
...SKIP_QUERY_BATCH_META,
|
||||
},
|
||||
);
|
||||
|
||||
@@ -72,14 +72,19 @@ export default function WebhookPage({ params }: Route.ComponentProps) {
|
||||
data,
|
||||
isLoading: isLogsLoading,
|
||||
isLoadingError: isLogsLoadingError,
|
||||
} = trpc.webhook.calls.find.useQuery({
|
||||
webhookId: params.id,
|
||||
page: parsedSearchParams.page,
|
||||
perPage: parsedSearchParams.perPage,
|
||||
status: parsedSearchParams.status,
|
||||
events: parsedSearchParams.events,
|
||||
query: parsedSearchParams.query,
|
||||
});
|
||||
} = trpc.webhook.calls.find.useQuery(
|
||||
{
|
||||
webhookId: params.id,
|
||||
page: parsedSearchParams.page,
|
||||
perPage: parsedSearchParams.perPage,
|
||||
status: parsedSearchParams.status,
|
||||
events: parsedSearchParams.events,
|
||||
query: parsedSearchParams.query,
|
||||
},
|
||||
{
|
||||
placeholderData: (previousData) => previousData,
|
||||
},
|
||||
);
|
||||
|
||||
/**
|
||||
* Handle debouncing the search query.
|
||||
|
||||
@@ -66,6 +66,7 @@ export default function TemplatesPage() {
|
||||
},
|
||||
{
|
||||
enabled: !isOrgView,
|
||||
placeholderData: (previousData) => previousData,
|
||||
},
|
||||
);
|
||||
|
||||
@@ -76,6 +77,7 @@ export default function TemplatesPage() {
|
||||
},
|
||||
{
|
||||
enabled: isOrgView,
|
||||
placeholderData: (previousData) => previousData,
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user