chore: merge main

This commit is contained in:
Catalin Pit
2025-09-11 14:58:42 +03:00
343 changed files with 14952 additions and 3564 deletions

View File

@ -52,7 +52,7 @@ export const AdminDocumentRecipientItemTable = ({ recipient }: RecipientItemProp
},
});
const { mutateAsync: updateRecipient } = trpc.admin.updateRecipient.useMutation();
const { mutateAsync: updateRecipient } = trpc.admin.recipient.update.useMutation();
const columns = useMemo(() => {
return [

View File

@ -34,7 +34,7 @@ export const DocumentLogsTable = ({ documentId }: DocumentLogsTableProps) => {
const parsedSearchParams = ZUrlSearchParamsSchema.parse(Object.fromEntries(searchParams ?? []));
const { data, isLoading, isLoadingError } = trpc.document.findDocumentAuditLogs.useQuery(
const { data, isLoading, isLoadingError } = trpc.document.auditLog.find.useQuery(
{
documentId,
page: parsedSearchParams.page,

View File

@ -45,7 +45,7 @@ export const DocumentsTableActionButton = ({ row }: DocumentsTableActionButtonPr
const onDownloadClick = async () => {
try {
const document = !recipient
? await trpcClient.document.getDocumentById.query(
? await trpcClient.document.get.query(
{
documentId: row.id,
},

View File

@ -77,7 +77,7 @@ export const DocumentsTableActionDropdown = ({
const onDownloadClick = async () => {
try {
const document = !recipient
? await trpcClient.document.getDocumentById.query({
? await trpcClient.document.get.query({
documentId: row.id,
})
: await trpcClient.document.getDocumentByToken.query({
@ -103,7 +103,7 @@ export const DocumentsTableActionDropdown = ({
const onDownloadOriginalClick = async () => {
try {
const document = !recipient
? await trpcClient.document.getDocumentById.query({
? await trpcClient.document.get.query({
documentId: row.id,
})
: await trpcClient.document.getDocumentByToken.query({

View File

@ -11,7 +11,7 @@ import { useUpdateSearchParams } from '@documenso/lib/client-only/hooks/use-upda
import { useSession } from '@documenso/lib/client-only/providers/session';
import { isDocumentCompleted } from '@documenso/lib/utils/document';
import { formatDocumentsPath } from '@documenso/lib/utils/teams';
import type { TFindDocumentsResponse } from '@documenso/trpc/server/document-router/schema';
import type { TFindDocumentsResponse } from '@documenso/trpc/server/document-router/find-documents.types';
import type { DataTableColumnDef } from '@documenso/ui/primitives/data-table';
import { DataTable } from '@documenso/ui/primitives/data-table';
import { DataTablePagination } from '@documenso/ui/primitives/data-table-pagination';

View File

@ -17,7 +17,6 @@ import { isDocumentCompleted } from '@documenso/lib/utils/document';
import { trpc as trpcClient } from '@documenso/trpc/client';
import { trpc } from '@documenso/trpc/react';
import type { TFindInboxResponse } from '@documenso/trpc/server/document-router/find-inbox.types';
import type { TFindDocumentsResponse } from '@documenso/trpc/server/document-router/schema';
import { Button } from '@documenso/ui/primitives/button';
import type { DataTableColumnDef } from '@documenso/ui/primitives/data-table';
import { DataTable } from '@documenso/ui/primitives/data-table';
@ -32,12 +31,12 @@ import { useOptionalCurrentTeam } from '~/providers/team';
import { StackAvatarsWithTooltip } from '../general/stack-avatars-with-tooltip';
export type DocumentsTableProps = {
data?: TFindDocumentsResponse;
data?: TFindInboxResponse;
isLoading?: boolean;
isLoadingError?: boolean;
};
type DocumentsTableRow = TFindDocumentsResponse['data'][number];
type DocumentsTableRow = TFindInboxResponse['data'][number];
export const InboxTable = () => {
const { _, i18n } = useLingui();

View File

@ -1,20 +1,18 @@
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { DateTime } from 'luxon';
import type { DateTimeFormatOptions } from 'luxon';
import { DateTime } from 'luxon';
import { P, match } from 'ts-pattern';
import { UAParser } from 'ua-parser-js';
import { APP_I18N_OPTIONS } from '@documenso/lib/constants/i18n';
import type { TDocumentAuditLog } from '@documenso/lib/types/document-audit-logs';
import { formatDocumentAuditLogAction } from '@documenso/lib/utils/document-audit-logs';
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from '@documenso/ui/primitives/table';
DOCUMENT_AUDIT_LOG_TYPE,
type TDocumentAuditLog,
} from '@documenso/lib/types/document-audit-logs';
import { formatDocumentAuditLogAction } from '@documenso/lib/utils/document-audit-logs';
import { cn } from '@documenso/ui/lib/utils';
import { Card, CardContent } from '@documenso/ui/primitives/card';
export type AuditLogDataTableProps = {
logs: TDocumentAuditLog[];
@ -25,71 +23,129 @@ const dateFormat: DateTimeFormatOptions = {
hourCycle: 'h12',
};
/**
* Get the color indicator for the audit log type
*/
const getAuditLogIndicatorColor = (type: string) =>
match(type)
.with(DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_RECIPIENT_COMPLETED, () => 'bg-green-500')
.with(DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_RECIPIENT_REJECTED, () => 'bg-red-500')
.with(DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_SENT, () => 'bg-orange-500')
.with(
P.union(
DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_FIELD_INSERTED,
DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_FIELD_UNINSERTED,
),
() => 'bg-blue-500',
)
.otherwise(() => 'bg-muted');
/**
* DO NOT USE TRANS. YOU MUST USE _ FOR THIS FILE AND ALL CHILDREN COMPONENTS.
*/
const formatUserAgent = (userAgent: string | null | undefined, userAgentInfo: UAParser.IResult) => {
if (!userAgent) {
return msg`N/A`;
}
const browser = userAgentInfo.browser.name;
const version = userAgentInfo.browser.version;
const os = userAgentInfo.os.name;
// If we can parse meaningful browser info, format it nicely
if (browser && os) {
const browserInfo = version ? `${browser} ${version}` : browser;
return msg`${browserInfo} on ${os}`;
}
return msg`${userAgent}`;
};
export const InternalAuditLogTable = ({ logs }: AuditLogDataTableProps) => {
const { _ } = useLingui();
const parser = new UAParser();
const uppercaseFistLetter = (text: string) => {
return text.charAt(0).toUpperCase() + text.slice(1);
};
return (
<Table overflowHidden>
<TableHeader>
<TableRow>
<TableHead>{_(msg`Time`)}</TableHead>
<TableHead>{_(msg`User`)}</TableHead>
<TableHead>{_(msg`Action`)}</TableHead>
<TableHead>{_(msg`IP Address`)}</TableHead>
<TableHead>{_(msg`Browser`)}</TableHead>
</TableRow>
</TableHeader>
<div className="space-y-4">
{logs.map((log, index) => {
parser.setUA(log.userAgent || '');
const formattedAction = formatDocumentAuditLogAction(_, log);
const userAgentInfo = parser.getResult();
<TableBody className="print:text-xs">
{logs.map((log, i) => (
<TableRow className="break-inside-avoid" key={i}>
<TableCell>
{DateTime.fromJSDate(log.createdAt)
.setLocale(APP_I18N_OPTIONS.defaultLocale)
.toLocaleString(dateFormat)}
</TableCell>
return (
<Card
key={index}
// Add top margin for the first card to ensure it's not cut off from the 2nd page onwards
className={`border shadow-sm ${index > 0 ? 'print:mt-8' : ''}`}
style={{
pageBreakInside: 'avoid',
breakInside: 'avoid',
}}
>
<CardContent className="p-4">
{/* Header Section with indicator, event type, and timestamp */}
<div className="mb-3 flex items-start justify-between">
<div className="flex items-baseline gap-3">
<div
className={cn(`h-2 w-2 rounded-full`, getAuditLogIndicatorColor(log.type))}
/>
<TableCell>
{log.name || log.email ? (
<div>
{log.name && (
<p className="break-all" title={log.name}>
{log.name}
</p>
)}
<div>
<div className="text-muted-foreground text-sm font-medium uppercase tracking-wide print:text-[8pt]">
{log.type.replace(/_/g, ' ')}
</div>
{log.email && (
<p className="text-muted-foreground break-all" title={log.email}>
{log.email}
</p>
)}
<div className="text-foreground text-sm font-medium print:text-[8pt]">
{formattedAction.description}
</div>
</div>
</div>
) : (
<p>N/A</p>
)}
</TableCell>
<TableCell>
{uppercaseFistLetter(formatDocumentAuditLogAction(_, log).description)}
</TableCell>
<div className="text-muted-foreground text-sm print:text-[8pt]">
{DateTime.fromJSDate(log.createdAt)
.setLocale(APP_I18N_OPTIONS.defaultLocale)
.toLocaleString(dateFormat)}
</div>
</div>
<TableCell>{log.ipAddress}</TableCell>
<hr className="my-4" />
<TableCell>
{log.userAgent ? parser.setUA(log.userAgent).getBrowser().name : 'N/A'}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
{/* Details Section - Two column layout */}
<div className="grid grid-cols-2 gap-x-8 gap-y-2 text-xs print:text-[6pt]">
<div>
<div className="text-muted-foreground/70 font-medium uppercase tracking-wide">
{_(msg`User`)}
</div>
<div className="text-foreground mt-1 font-mono">{log.email || 'N/A'}</div>
</div>
<div className="text-right">
<div className="text-muted-foreground/70 font-medium uppercase tracking-wide">
{_(msg`IP Address`)}
</div>
<div className="text-foreground mt-1 font-mono">{log.ipAddress || 'N/A'}</div>
</div>
<div className="col-span-2">
<div className="text-muted-foreground/70 font-medium uppercase tracking-wide">
{_(msg`User Agent`)}
</div>
<div className="text-foreground mt-1">
{_(formatUserAgent(log.userAgent, userAgentInfo))}
</div>
</div>
</div>
</CardContent>
</Card>
);
})}
</div>
);
};

View File

@ -62,7 +62,7 @@ export const SettingsSecurityPasskeyTableActions = ({
});
const { mutateAsync: updatePasskey, isPending: isUpdatingPasskey } =
trpc.auth.updatePasskey.useMutation({
trpc.auth.passkey.update.useMutation({
onSuccess: () => {
toast({
title: _(msg`Success`),
@ -84,7 +84,7 @@ export const SettingsSecurityPasskeyTableActions = ({
});
const { mutateAsync: deletePasskey, isPending: isDeletingPasskey } =
trpc.auth.deletePasskey.useMutation({
trpc.auth.passkey.delete.useMutation({
onSuccess: () => {
toast({
title: _(msg`Success`),

View File

@ -26,7 +26,7 @@ export const SettingsSecurityPasskeyTable = () => {
const parsedSearchParams = ZUrlSearchParamsSchema.parse(Object.fromEntries(searchParams ?? []));
const { data, isLoading, isLoadingError } = trpc.auth.findPasskeys.useQuery(
const { data, isLoading, isLoadingError } = trpc.auth.passkey.find.useQuery(
{
page: parsedSearchParams.page,
perPage: parsedSearchParams.perPage,