mirror of
https://github.com/documenso/documenso.git
synced 2026-07-10 21:15:15 +10:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f51f5426ef |
+1
-3
@@ -127,6 +127,4 @@ E2E_TEST_AUTHENTICATE_USER_EMAIL="testuser@mail.com"
|
||||
E2E_TEST_AUTHENTICATE_USER_PASSWORD="test_Password123"
|
||||
|
||||
# [[LOGGER]]
|
||||
# OPTIONAL: The file to save the logger output to. Will disable stdout if provided.
|
||||
NEXT_PRIVATE_LOGGER_FILE_PATH=
|
||||
|
||||
NEXT_PRIVATE_LOGGER_HONEY_BADGER_API_KEY=
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
---
|
||||
name: Pull Request
|
||||
about: Submit changes to the project for review and inclusion
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!--- Describe the changes introduced by this pull request. -->
|
||||
|
||||
@@ -50,10 +50,3 @@ yarn-error.log*
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
|
||||
# logs
|
||||
logs.json
|
||||
|
||||
# claude
|
||||
.claude
|
||||
CLAUDE.md
|
||||
@@ -1,6 +1,5 @@
|
||||
{
|
||||
"index": "Get Started",
|
||||
"authentication": "Authentication",
|
||||
"rate-limits": "Rate Limits",
|
||||
"versioning": "Versioning"
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ Our new API V2 supports the following typed SDKs:
|
||||
|
||||
<Callout type="info">
|
||||
For the staging API, please use the following base URL:
|
||||
`https://stg-app.documenso.com/api/v2-beta/`
|
||||
`https://stg-app.documenso.dev/api/v2-beta/`
|
||||
</Callout>
|
||||
|
||||
🚀 [V2 Announcement](https://documen.so/sdk-blog)
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
import { Callout } from 'nextra/components';
|
||||
|
||||
# Rate Limits
|
||||
|
||||
Documenso enforces rate limits on all API endpoints to ensure service stability.
|
||||
|
||||
## HTTP Rate Limits
|
||||
|
||||
**Limit:** 100 requests per minute per IP address
|
||||
**Response:** 429 Too Many Requests
|
||||
|
||||
### Rate Limit Response
|
||||
|
||||
```json
|
||||
{
|
||||
"error": "Too many requests, please try again later."
|
||||
}
|
||||
```
|
||||
|
||||
<Callout type="warning">
|
||||
No rate limit headers are currently provided. When you receive a 429 response, wait at least 60
|
||||
seconds before retrying.
|
||||
</Callout>
|
||||
|
||||
## Resource Limits
|
||||
|
||||
Beyond HTTP rate limits, your account has usage limits based on your subscription plan.
|
||||
|
||||
### Plan Limits
|
||||
|
||||
| Resource | Free | Paid | Self-hosted | Enterprise |
|
||||
| ---------------- | ---- | --------- | ----------- | ---------- |
|
||||
| Documents/month | 5 | Unlimited | Unlimited | Unlimited |
|
||||
| Total Recipients | 10 | Unlimited | Unlimited | Unlimited |
|
||||
| Direct Templates | 3 | Unlimited | Unlimited | Unlimited |
|
||||
|
||||
### Error Response
|
||||
|
||||
When you exceed a resource limit:
|
||||
|
||||
```json
|
||||
{
|
||||
"error": "You have reached your document limit for this month. Please upgrade your plan.",
|
||||
"code": "LIMIT_EXCEEDED",
|
||||
"statusCode": 400
|
||||
}
|
||||
```
|
||||
|
||||
## Error Codes
|
||||
|
||||
| Code | Status | Description |
|
||||
| ------------------- | ------ | ----------------------------- |
|
||||
| `TOO_MANY_REQUESTS` | 429 | HTTP rate limit exceeded |
|
||||
| `LIMIT_EXCEEDED` | 400 | Resource usage limit exceeded |
|
||||
@@ -619,18 +619,6 @@ Example payload for the `document.rejected` event:
|
||||
}
|
||||
```
|
||||
|
||||
## Webhook Events Testing
|
||||
|
||||
You can trigger test webhook events to test the webhook functionality. To trigger a test webhook, navigate to the [Webhooks page](/developers/webhooks) and click on the "Test Webhook" button.
|
||||
|
||||

|
||||
|
||||
This opens a dialog where you can select the event type to test.
|
||||
|
||||

|
||||
|
||||
Choose the appropriate event and click "Send Test Webhook." You’ll shortly receive a test payload from Documenso with sample data.
|
||||
|
||||
## Availability
|
||||
|
||||
Webhooks are available to individual users and teams.
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 45 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 98 KiB |
@@ -127,16 +127,6 @@ export const DocumentMoveToFolderDialog = ({
|
||||
return;
|
||||
}
|
||||
|
||||
if (error.code === AppErrorCode.UNAUTHORIZED) {
|
||||
toast({
|
||||
title: _(msg`Error`),
|
||||
description: _(msg`You are not allowed to move this document.`),
|
||||
variant: 'destructive',
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
toast({
|
||||
title: _(msg`Error`),
|
||||
description: _(msg`An error occurred while moving the document.`),
|
||||
|
||||
@@ -116,8 +116,8 @@ export const FolderDeleteDialog = ({ folder, isOpen, onOpenChange }: FolderDelet
|
||||
<Alert variant="destructive">
|
||||
<AlertDescription>
|
||||
<Trans>
|
||||
This folder contains multiple items. Deleting it will remove all subfolders and move
|
||||
all nested documents and templates to the root folder.
|
||||
This folder contains multiple items. Deleting it will also delete all items in the
|
||||
folder, including nested folders and their contents.
|
||||
</Trans>
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
|
||||
+21
-40
@@ -1,8 +1,8 @@
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { Trans } from '@lingui/react/macro';
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { useLingui } from '@lingui/react';
|
||||
import type * as DialogPrimitive from '@radix-ui/react-dialog';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { z } from 'zod';
|
||||
@@ -14,7 +14,6 @@ import type { TFolderWithSubfolders } from '@documenso/trpc/server/folder-router
|
||||
import { Button } from '@documenso/ui/primitives/button';
|
||||
import {
|
||||
Dialog,
|
||||
DialogClose,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
@@ -41,7 +40,7 @@ import { useToast } from '@documenso/ui/primitives/use-toast';
|
||||
|
||||
import { useOptionalCurrentTeam } from '~/providers/team';
|
||||
|
||||
export type FolderUpdateDialogProps = {
|
||||
export type FolderSettingsDialogProps = {
|
||||
folder: TFolderWithSubfolders | null;
|
||||
isOpen: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
@@ -54,8 +53,12 @@ export const ZUpdateFolderFormSchema = z.object({
|
||||
|
||||
export type TUpdateFolderFormSchema = z.infer<typeof ZUpdateFolderFormSchema>;
|
||||
|
||||
export const FolderUpdateDialog = ({ folder, isOpen, onOpenChange }: FolderUpdateDialogProps) => {
|
||||
const { t } = useLingui();
|
||||
export const FolderSettingsDialog = ({
|
||||
folder,
|
||||
isOpen,
|
||||
onOpenChange,
|
||||
}: FolderSettingsDialogProps) => {
|
||||
const { _ } = useLingui();
|
||||
const team = useOptionalCurrentTeam();
|
||||
|
||||
const { toast } = useToast();
|
||||
@@ -81,9 +84,7 @@ export const FolderUpdateDialog = ({ folder, isOpen, onOpenChange }: FolderUpdat
|
||||
}, [folder, form]);
|
||||
|
||||
const onFormSubmit = async (data: TUpdateFolderFormSchema) => {
|
||||
if (!folder) {
|
||||
return;
|
||||
}
|
||||
if (!folder) return;
|
||||
|
||||
try {
|
||||
await updateFolder({
|
||||
@@ -95,7 +96,7 @@ export const FolderUpdateDialog = ({ folder, isOpen, onOpenChange }: FolderUpdat
|
||||
});
|
||||
|
||||
toast({
|
||||
title: t`Folder updated successfully`,
|
||||
title: _(msg`Folder updated successfully`),
|
||||
});
|
||||
|
||||
onOpenChange(false);
|
||||
@@ -104,7 +105,7 @@ export const FolderUpdateDialog = ({ folder, isOpen, onOpenChange }: FolderUpdat
|
||||
|
||||
if (error.code === AppErrorCode.NOT_FOUND) {
|
||||
toast({
|
||||
title: t`Folder not found`,
|
||||
title: _(msg`Folder not found`),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -114,12 +115,8 @@ export const FolderUpdateDialog = ({ folder, isOpen, onOpenChange }: FolderUpdat
|
||||
<Dialog open={isOpen} onOpenChange={onOpenChange}>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
<Trans>Folder Settings</Trans>
|
||||
</DialogTitle>
|
||||
<DialogDescription>
|
||||
<Trans>Manage the settings for this folder.</Trans>
|
||||
</DialogDescription>
|
||||
<DialogTitle>Folder Settings</DialogTitle>
|
||||
<DialogDescription>Manage the settings for this folder.</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<Form {...form}>
|
||||
@@ -129,9 +126,7 @@ export const FolderUpdateDialog = ({ folder, isOpen, onOpenChange }: FolderUpdat
|
||||
name="name"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
<Trans>Name</Trans>
|
||||
</FormLabel>
|
||||
<FormLabel>Name</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
@@ -146,25 +141,19 @@ export const FolderUpdateDialog = ({ folder, isOpen, onOpenChange }: FolderUpdat
|
||||
name="visibility"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
<Trans>Visibility</Trans>
|
||||
</FormLabel>
|
||||
<FormLabel>Visibility</FormLabel>
|
||||
<Select onValueChange={field.onChange} defaultValue={field.value}>
|
||||
<FormControl>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder={t`Select visibility`} />
|
||||
<SelectValue placeholder="Select visibility" />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
<SelectItem value={DocumentVisibility.EVERYONE}>
|
||||
<Trans>Everyone</Trans>
|
||||
</SelectItem>
|
||||
<SelectItem value={DocumentVisibility.EVERYONE}>Everyone</SelectItem>
|
||||
<SelectItem value={DocumentVisibility.MANAGER_AND_ABOVE}>
|
||||
<Trans>Managers and above</Trans>
|
||||
</SelectItem>
|
||||
<SelectItem value={DocumentVisibility.ADMIN}>
|
||||
<Trans>Admins only</Trans>
|
||||
Managers and above
|
||||
</SelectItem>
|
||||
<SelectItem value={DocumentVisibility.ADMIN}>Admins only</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FormMessage />
|
||||
@@ -174,15 +163,7 @@ export const FolderUpdateDialog = ({ folder, isOpen, onOpenChange }: FolderUpdat
|
||||
)}
|
||||
|
||||
<DialogFooter>
|
||||
<DialogClose asChild>
|
||||
<Button variant="secondary">
|
||||
<Trans>Cancel</Trans>
|
||||
</Button>
|
||||
</DialogClose>
|
||||
|
||||
<Button type="submit" loading={form.formState.isSubmitting}>
|
||||
<Trans>Update</Trans>
|
||||
</Button>
|
||||
<Button type="submit">Save Changes</Button>
|
||||
</DialogFooter>
|
||||
</form>
|
||||
</Form>
|
||||
@@ -19,7 +19,6 @@ import { IS_BILLING_ENABLED } from '@documenso/lib/constants/app';
|
||||
import { AppError } from '@documenso/lib/errors/app-error';
|
||||
import { INTERNAL_CLAIM_ID } from '@documenso/lib/types/subscription';
|
||||
import { parseMessageDescriptorMacro } from '@documenso/lib/utils/i18n';
|
||||
import { isPersonalLayout } from '@documenso/lib/utils/organisations';
|
||||
import { trpc } from '@documenso/trpc/react';
|
||||
import { ZCreateOrganisationRequestSchema } from '@documenso/trpc/server/organisation-router/create-organisation.types';
|
||||
import { cn } from '@documenso/ui/lib/utils';
|
||||
@@ -47,8 +46,6 @@ import { SpinnerBox } from '@documenso/ui/primitives/spinner';
|
||||
import { Tabs, TabsList, TabsTrigger } from '@documenso/ui/primitives/tabs';
|
||||
import { useToast } from '@documenso/ui/primitives/use-toast';
|
||||
|
||||
import { IndividualPersonalLayoutCheckoutButton } from '../general/billing-plans';
|
||||
|
||||
export type OrganisationCreateDialogProps = {
|
||||
trigger?: React.ReactNode;
|
||||
} & Omit<DialogPrimitive.DialogProps, 'children'>;
|
||||
@@ -62,11 +59,10 @@ export type TCreateOrganisationFormSchema = z.infer<typeof ZCreateOrganisationFo
|
||||
export const OrganisationCreateDialog = ({ trigger, ...props }: OrganisationCreateDialogProps) => {
|
||||
const { t } = useLingui();
|
||||
const { toast } = useToast();
|
||||
const { refreshSession, organisations } = useSession();
|
||||
const { refreshSession } = useSession();
|
||||
|
||||
const [searchParams] = useSearchParams();
|
||||
const updateSearchParams = useUpdateSearchParams();
|
||||
const isPersonalLayoutMode = isPersonalLayout(organisations);
|
||||
|
||||
const actionSearchParam = searchParams?.get('action');
|
||||
|
||||
@@ -137,13 +133,6 @@ export const OrganisationCreateDialog = ({ trigger, ...props }: OrganisationCrea
|
||||
form.reset();
|
||||
}, [open, form]);
|
||||
|
||||
const isIndividualPlan = (priceId: string) => {
|
||||
return (
|
||||
plansData?.plans[INTERNAL_CLAIM_ID.INDIVIDUAL]?.monthlyPrice?.id === priceId ||
|
||||
plansData?.plans[INTERNAL_CLAIM_ID.INDIVIDUAL]?.yearlyPrice?.id === priceId
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
{...props}
|
||||
@@ -188,15 +177,9 @@ export const OrganisationCreateDialog = ({ trigger, ...props }: OrganisationCrea
|
||||
<Trans>Cancel</Trans>
|
||||
</Button>
|
||||
|
||||
{isIndividualPlan(selectedPriceId) && isPersonalLayoutMode ? (
|
||||
<IndividualPersonalLayoutCheckoutButton priceId={selectedPriceId}>
|
||||
<Trans>Checkout</Trans>
|
||||
</IndividualPersonalLayoutCheckoutButton>
|
||||
) : (
|
||||
<Button type="submit" onClick={() => setStep('create')}>
|
||||
<Trans>Continue</Trans>
|
||||
</Button>
|
||||
)}
|
||||
<Button type="submit" onClick={() => setStep('create')}>
|
||||
<Trans>Continue</Trans>
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</fieldset>
|
||||
</>
|
||||
@@ -323,11 +306,7 @@ const BillingPlanForm = ({
|
||||
}, [value]);
|
||||
|
||||
const onBillingPeriodChange = (billingPeriod: 'monthlyPrice' | 'yearlyPrice') => {
|
||||
const plan = dynamicPlans.find(
|
||||
(plan) =>
|
||||
// Purposely using the opposite billing period to get the correct plan.
|
||||
plan[billingPeriod === 'monthlyPrice' ? 'yearlyPrice' : 'monthlyPrice']?.id === value,
|
||||
);
|
||||
const plan = dynamicPlans.find((plan) => plan[billingPeriod]?.id === value);
|
||||
|
||||
setBillingPeriod(billingPeriod);
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ import { z } from 'zod';
|
||||
|
||||
import { downloadFile } from '@documenso/lib/client-only/download-file';
|
||||
import { useCurrentOrganisation } from '@documenso/lib/client-only/providers/organisation';
|
||||
import { IS_BILLING_ENABLED, SUPPORT_EMAIL } from '@documenso/lib/constants/app';
|
||||
import { IS_BILLING_ENABLED } from '@documenso/lib/constants/app';
|
||||
import { ORGANISATION_MEMBER_ROLE_HIERARCHY } from '@documenso/lib/constants/organisations';
|
||||
import { ORGANISATION_MEMBER_ROLE_MAP } from '@documenso/lib/constants/organisations-translations';
|
||||
import { INTERNAL_CLAIM_ID } from '@documenso/lib/types/subscription';
|
||||
@@ -303,8 +303,8 @@ export const OrganisationMemberInviteDialog = ({
|
||||
<AlertDescription>
|
||||
<Trans>
|
||||
Your plan does not support inviting members. Please upgrade or your plan or
|
||||
contact sales at <a href={`mailto:${SUPPORT_EMAIL}`}>{SUPPORT_EMAIL}</a> if you
|
||||
would like to discuss your options.
|
||||
contact sales at <a href="mailto:support@documenso.com">support@documenso.com</a>{' '}
|
||||
if you would like to discuss your options.
|
||||
</Trans>
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
|
||||
@@ -12,11 +12,7 @@ import type { z } from 'zod';
|
||||
import { useUpdateSearchParams } from '@documenso/lib/client-only/hooks/use-update-search-params';
|
||||
import { useCurrentOrganisation } from '@documenso/lib/client-only/providers/organisation';
|
||||
import { useSession } from '@documenso/lib/client-only/providers/session';
|
||||
import {
|
||||
IS_BILLING_ENABLED,
|
||||
NEXT_PUBLIC_WEBAPP_URL,
|
||||
SUPPORT_EMAIL,
|
||||
} from '@documenso/lib/constants/app';
|
||||
import { IS_BILLING_ENABLED, NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
|
||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||
import { trpc } from '@documenso/trpc/react';
|
||||
import { ZCreateTeamRequestSchema } from '@documenso/trpc/server/team-router/create-team.types';
|
||||
@@ -197,8 +193,8 @@ export const TeamCreateDialog = ({ trigger, onCreated, ...props }: TeamCreateDia
|
||||
<AlertDescription className="mt-0">
|
||||
<Trans>
|
||||
You have reached the maximum number of teams for your plan. Please contact sales
|
||||
at <a href={`mailto:${SUPPORT_EMAIL}`}>{SUPPORT_EMAIL}</a> if you would like to
|
||||
adjust your plan.
|
||||
at <a href="mailto:support@documenso.com">support@documenso.com</a> if you would
|
||||
like to adjust your plan.
|
||||
</Trans>
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
|
||||
@@ -1,170 +0,0 @@
|
||||
import { useState } from 'react';
|
||||
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { useLingui } from '@lingui/react';
|
||||
import { Trans } from '@lingui/react/macro';
|
||||
import type { Webhook } from '@prisma/client';
|
||||
import { WebhookTriggerEvents } from '@prisma/client';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { toFriendlyWebhookEventName } from '@documenso/lib/universal/webhook/to-friendly-webhook-event-name';
|
||||
import { trpc } from '@documenso/trpc/react';
|
||||
import { Button } from '@documenso/ui/primitives/button';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from '@documenso/ui/primitives/dialog';
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from '@documenso/ui/primitives/form/form';
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '@documenso/ui/primitives/select';
|
||||
import { useToast } from '@documenso/ui/primitives/use-toast';
|
||||
|
||||
import { useCurrentTeam } from '~/providers/team';
|
||||
|
||||
export type WebhookTestDialogProps = {
|
||||
webhook: Pick<Webhook, 'id' | 'webhookUrl' | 'eventTriggers'>;
|
||||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
const ZTestWebhookFormSchema = z.object({
|
||||
event: z.nativeEnum(WebhookTriggerEvents),
|
||||
});
|
||||
|
||||
type TTestWebhookFormSchema = z.infer<typeof ZTestWebhookFormSchema>;
|
||||
|
||||
export const WebhookTestDialog = ({ webhook, children }: WebhookTestDialogProps) => {
|
||||
const { _ } = useLingui();
|
||||
const { toast } = useToast();
|
||||
|
||||
const team = useCurrentTeam();
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const { mutateAsync: testWebhook } = trpc.webhook.testWebhook.useMutation();
|
||||
|
||||
const form = useForm<TTestWebhookFormSchema>({
|
||||
resolver: zodResolver(ZTestWebhookFormSchema),
|
||||
defaultValues: {
|
||||
event: webhook.eventTriggers[0],
|
||||
},
|
||||
});
|
||||
|
||||
const onSubmit = async ({ event }: TTestWebhookFormSchema) => {
|
||||
try {
|
||||
await testWebhook({
|
||||
id: webhook.id,
|
||||
event,
|
||||
teamId: team.id,
|
||||
});
|
||||
|
||||
toast({
|
||||
title: _(msg`Test webhook sent`),
|
||||
description: _(msg`The test webhook has been successfully sent to your endpoint.`),
|
||||
duration: 5000,
|
||||
});
|
||||
|
||||
setOpen(false);
|
||||
} catch (error) {
|
||||
toast({
|
||||
title: _(msg`Test webhook failed`),
|
||||
description: _(
|
||||
msg`We encountered an error while sending the test webhook. Please check your endpoint and try again.`,
|
||||
),
|
||||
variant: 'destructive',
|
||||
duration: 5000,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={(value) => !form.formState.isSubmitting && setOpen(value)}>
|
||||
<DialogTrigger asChild>{children}</DialogTrigger>
|
||||
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
<Trans>Test Webhook</Trans>
|
||||
</DialogTitle>
|
||||
|
||||
<DialogDescription>
|
||||
<Trans>
|
||||
Send a test webhook with sample data to verify your integration is working correctly.
|
||||
</Trans>
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)}>
|
||||
<fieldset
|
||||
className="flex h-full flex-col space-y-4"
|
||||
disabled={form.formState.isSubmitting}
|
||||
>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="event"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
<Trans>Event Type</Trans>
|
||||
</FormLabel>
|
||||
<Select onValueChange={field.onChange} value={field.value}>
|
||||
<FormControl>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Select an event type" />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
{webhook.eventTriggers.map((event) => (
|
||||
<SelectItem key={event} value={event}>
|
||||
{toFriendlyWebhookEventName(event)}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<div className="rounded-md border p-4">
|
||||
<h4 className="mb-2 text-sm font-medium">
|
||||
<Trans>Webhook URL</Trans>
|
||||
</h4>
|
||||
<p className="text-muted-foreground break-all text-sm">{webhook.webhookUrl}</p>
|
||||
</div>
|
||||
|
||||
<DialogFooter>
|
||||
<Button type="button" variant="secondary" onClick={() => setOpen(false)}>
|
||||
<Trans>Cancel</Trans>
|
||||
</Button>
|
||||
|
||||
<Button type="submit" loading={form.formState.isSubmitting}>
|
||||
<Trans>Send Test Webhook</Trans>
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</fieldset>
|
||||
</form>
|
||||
</Form>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
@@ -10,9 +10,7 @@ import { useForm } from 'react-hook-form';
|
||||
import type { InternalClaimPlans } from '@documenso/ee/server-only/stripe/get-internal-claim-plans';
|
||||
import { useIsMounted } from '@documenso/lib/client-only/hooks/use-is-mounted';
|
||||
import { useCurrentOrganisation } from '@documenso/lib/client-only/providers/organisation';
|
||||
import { useSession } from '@documenso/lib/client-only/providers/session';
|
||||
import { INTERNAL_CLAIM_ID } from '@documenso/lib/types/subscription';
|
||||
import { isPersonalLayout } from '@documenso/lib/utils/organisations';
|
||||
import { trpc } from '@documenso/trpc/react';
|
||||
import { Button } from '@documenso/ui/primitives/button';
|
||||
import { Card, CardContent, CardTitle } from '@documenso/ui/primitives/card';
|
||||
@@ -51,12 +49,8 @@ export type BillingPlansProps = {
|
||||
export const BillingPlans = ({ plans }: BillingPlansProps) => {
|
||||
const isMounted = useIsMounted();
|
||||
|
||||
const { organisations } = useSession();
|
||||
|
||||
const [interval, setInterval] = useState<'monthlyPrice' | 'yearlyPrice'>('yearlyPrice');
|
||||
|
||||
const isPersonalLayoutMode = isPersonalLayout(organisations);
|
||||
|
||||
const pricesToDisplay = useMemo(() => {
|
||||
const prices = [];
|
||||
|
||||
@@ -132,18 +126,12 @@ export const BillingPlans = ({ plans }: BillingPlansProps) => {
|
||||
|
||||
<div className="flex-1" />
|
||||
|
||||
{isPersonalLayoutMode && price.claim === INTERNAL_CLAIM_ID.INDIVIDUAL ? (
|
||||
<IndividualPersonalLayoutCheckoutButton priceId={price.id}>
|
||||
<Trans>Subscribe</Trans>
|
||||
</IndividualPersonalLayoutCheckoutButton>
|
||||
) : (
|
||||
<BillingDialog
|
||||
priceId={price.id}
|
||||
planName={price.product.name}
|
||||
memberCount={price.memberCount}
|
||||
claim={price.claim}
|
||||
/>
|
||||
)}
|
||||
<BillingDialog
|
||||
priceId={price.id}
|
||||
planName={price.product.name}
|
||||
memberCount={price.memberCount}
|
||||
claim={price.claim}
|
||||
/>
|
||||
</CardContent>
|
||||
</MotionCard>
|
||||
))}
|
||||
@@ -327,48 +315,3 @@ const BillingDialog = ({
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Custom checkout button for individual organisations in personal layout mode.
|
||||
*
|
||||
* This is so they don't create an additional organisation which is not needed since
|
||||
* it will clutter up the UI for them with unnecessary organisations.
|
||||
*/
|
||||
export const IndividualPersonalLayoutCheckoutButton = ({
|
||||
priceId,
|
||||
children,
|
||||
}: {
|
||||
priceId: string;
|
||||
children: React.ReactNode;
|
||||
}) => {
|
||||
const { t } = useLingui();
|
||||
const { toast } = useToast();
|
||||
const { organisations } = useSession();
|
||||
|
||||
const { mutateAsync: createSubscription, isPending } =
|
||||
trpc.billing.subscription.create.useMutation();
|
||||
|
||||
const onSubscribeClick = async () => {
|
||||
try {
|
||||
const createSubscriptionResponse = await createSubscription({
|
||||
organisationId: organisations[0].id,
|
||||
priceId,
|
||||
isPersonalLayoutMode: true,
|
||||
});
|
||||
|
||||
window.location.href = createSubscriptionResponse.redirectUrl;
|
||||
} catch (_err) {
|
||||
toast({
|
||||
title: t`Something went wrong`,
|
||||
description: t`An error occurred while trying to create a checkout session.`,
|
||||
variant: 'destructive',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Button loading={isPending} onClick={() => void onSubscribeClick()}>
|
||||
{children}
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -10,7 +10,6 @@ import { useRevalidator } from 'react-router';
|
||||
import { P, match } from 'ts-pattern';
|
||||
|
||||
import { unsafe_useEffectOnce } from '@documenso/lib/client-only/hooks/use-effect-once';
|
||||
import { AUTO_SIGNABLE_FIELD_TYPES } from '@documenso/lib/constants/autosign';
|
||||
import { DocumentAuth } from '@documenso/lib/types/document-auth';
|
||||
import { extractInitials } from '@documenso/lib/utils/recipient-formatter';
|
||||
import { trpc } from '@documenso/trpc/react';
|
||||
@@ -31,6 +30,13 @@ import { DocumentSigningDisclosure } from '~/components/general/document-signing
|
||||
import { useRequiredDocumentSigningAuthContext } from './document-signing-auth-provider';
|
||||
import { useRequiredDocumentSigningContext } from './document-signing-provider';
|
||||
|
||||
const AUTO_SIGNABLE_FIELD_TYPES: string[] = [
|
||||
FieldType.NAME,
|
||||
FieldType.INITIALS,
|
||||
FieldType.EMAIL,
|
||||
FieldType.DATE,
|
||||
];
|
||||
|
||||
// The action auth types that are not allowed to be auto signed
|
||||
//
|
||||
// Reasoning: If the action auth is a passkey or 2FA, it's likely that the owner of the document
|
||||
|
||||
+3
-17
@@ -17,7 +17,6 @@ import type {
|
||||
TSignFieldWithTokenMutationSchema,
|
||||
} from '@documenso/trpc/server/field-router/schema';
|
||||
import { FieldToolTip } from '@documenso/ui/components/field/field-tooltip';
|
||||
import { cn } from '@documenso/ui/lib/utils';
|
||||
import { Checkbox } from '@documenso/ui/primitives/checkbox';
|
||||
import { checkboxValidationSigns } from '@documenso/ui/primitives/document-flow/field-items-advanced-settings/constants';
|
||||
import { Label } from '@documenso/ui/primitives/label';
|
||||
@@ -277,14 +276,7 @@ export const DocumentSigningCheckboxField = ({
|
||||
{validationSign?.label} {checkboxValidationLength}
|
||||
</FieldToolTip>
|
||||
)}
|
||||
<div
|
||||
className={cn(
|
||||
'z-50 my-0.5 flex gap-1',
|
||||
parsedFieldMeta.direction === 'horizontal'
|
||||
? 'flex-row flex-wrap'
|
||||
: 'flex-col gap-y-1',
|
||||
)}
|
||||
>
|
||||
<div className="z-50 my-0.5 flex flex-col gap-y-1">
|
||||
{values?.map((item: { id: number; value: string; checked: boolean }, index: number) => {
|
||||
const itemValue = item.value || `empty-value-${item.id}`;
|
||||
|
||||
@@ -294,7 +286,6 @@ export const DocumentSigningCheckboxField = ({
|
||||
className="h-3 w-3"
|
||||
id={`checkbox-${field.id}-${item.id}`}
|
||||
checked={checkedValues.includes(itemValue)}
|
||||
disabled={isReadOnly}
|
||||
onCheckedChange={() => handleCheckboxChange(item.value, item.id)}
|
||||
/>
|
||||
{!item.value.includes('empty-value-') && item.value && (
|
||||
@@ -313,12 +304,7 @@ export const DocumentSigningCheckboxField = ({
|
||||
)}
|
||||
|
||||
{field.inserted && (
|
||||
<div
|
||||
className={cn(
|
||||
'my-0.5 flex gap-1',
|
||||
parsedFieldMeta.direction === 'horizontal' ? 'flex-row flex-wrap' : 'flex-col gap-y-1',
|
||||
)}
|
||||
>
|
||||
<div className="my-0.5 flex flex-col gap-y-1">
|
||||
{values?.map((item: { id: number; value: string; checked: boolean }, index: number) => {
|
||||
const itemValue = item.value || `empty-value-${item.id}`;
|
||||
|
||||
@@ -328,7 +314,7 @@ export const DocumentSigningCheckboxField = ({
|
||||
className="h-3 w-3"
|
||||
id={`checkbox-${field.id}-${item.id}`}
|
||||
checked={parsedCheckedValues.includes(itemValue)}
|
||||
disabled={isLoading || isReadOnly}
|
||||
disabled={isLoading}
|
||||
onCheckedChange={() => void handleCheckboxOptionClick(item)}
|
||||
/>
|
||||
{!item.value.includes('empty-value-') && item.value && (
|
||||
|
||||
+9
-6
@@ -131,12 +131,7 @@ export const DocumentSigningFieldContainer = ({
|
||||
|
||||
return (
|
||||
<div className={cn('[container-type:size]')}>
|
||||
<FieldRootContainer
|
||||
color={
|
||||
field.fieldMeta?.readOnly ? RECIPIENT_COLOR_STYLES.readOnly : RECIPIENT_COLOR_STYLES.green
|
||||
}
|
||||
field={field}
|
||||
>
|
||||
<FieldRootContainer color={RECIPIENT_COLOR_STYLES.green} field={field}>
|
||||
{!field.inserted && !loading && !readOnlyField && (
|
||||
<button
|
||||
type="submit"
|
||||
@@ -145,6 +140,14 @@ export const DocumentSigningFieldContainer = ({
|
||||
/>
|
||||
)}
|
||||
|
||||
{readOnlyField && (
|
||||
<button className="bg-background/40 absolute inset-0 z-10 flex h-full w-full items-center justify-center rounded-md text-sm opacity-0 duration-200 group-hover:opacity-100">
|
||||
<span className="bg-foreground/50 text-background rounded-xl p-2">
|
||||
<Trans>Read only field</Trans>
|
||||
</span>
|
||||
</button>
|
||||
)}
|
||||
|
||||
{type === 'Checkbox' && field.inserted && !loading && !readOnlyField && (
|
||||
<button
|
||||
className="absolute -bottom-10 flex items-center justify-evenly rounded-md border bg-gray-900 opacity-0 group-hover:opacity-100"
|
||||
|
||||
@@ -34,7 +34,7 @@ export const DocumentSigningFieldsInserted = ({
|
||||
textAlign = 'left',
|
||||
}: DocumentSigningFieldsInsertedProps) => {
|
||||
return (
|
||||
<div className="flex h-full w-full items-center overflow-hidden">
|
||||
<div className="flex h-full w-full items-center">
|
||||
<p
|
||||
className={cn(
|
||||
'text-foreground w-full text-left text-[clamp(0.425rem,25cqw,0.825rem)] duration-200',
|
||||
|
||||
+9
-6
@@ -54,7 +54,7 @@ export const DocumentSigningNumberField = ({
|
||||
const { toast } = useToast();
|
||||
const { revalidate } = useRevalidator();
|
||||
|
||||
const { recipient, isAssistantMode } = useDocumentSigningRecipientContext();
|
||||
const { recipient, targetSigner, isAssistantMode } = useDocumentSigningRecipientContext();
|
||||
|
||||
const [showNumberModal, setShowNumberModal] = useState(false);
|
||||
|
||||
@@ -62,8 +62,8 @@ export const DocumentSigningNumberField = ({
|
||||
const parsedFieldMeta = safeFieldMeta.success ? safeFieldMeta.data : null;
|
||||
|
||||
const defaultValue = parsedFieldMeta?.value;
|
||||
const [localNumber, setLocalNumber] = useState(() =>
|
||||
parsedFieldMeta?.value ? String(parsedFieldMeta.value) : '',
|
||||
const [localNumber, setLocalNumber] = useState(
|
||||
parsedFieldMeta?.value ? String(parsedFieldMeta.value) : '0',
|
||||
);
|
||||
|
||||
const initialErrors: ValidationErrors = {
|
||||
@@ -213,13 +213,16 @@ export const DocumentSigningNumberField = ({
|
||||
|
||||
useEffect(() => {
|
||||
if (!showNumberModal) {
|
||||
setLocalNumber(parsedFieldMeta?.value ? String(parsedFieldMeta.value) : '');
|
||||
setLocalNumber(parsedFieldMeta?.value ? String(parsedFieldMeta.value) : '0');
|
||||
setErrors(initialErrors);
|
||||
}
|
||||
}, [showNumberModal]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!field.inserted && defaultValue) {
|
||||
if (
|
||||
(!field.inserted && defaultValue && localNumber) ||
|
||||
(!field.inserted && parsedFieldMeta?.readOnly && defaultValue)
|
||||
) {
|
||||
void executeActionAuthProcedure({
|
||||
onReauthFormSubmit: async (authOptions) => await onSign(authOptions),
|
||||
actionTarget: field.type,
|
||||
@@ -315,7 +318,7 @@ export const DocumentSigningNumberField = ({
|
||||
variant="secondary"
|
||||
onClick={() => {
|
||||
setShowNumberModal(false);
|
||||
setLocalNumber(parsedFieldMeta?.value ? String(parsedFieldMeta.value) : '');
|
||||
setLocalNumber('');
|
||||
}}
|
||||
>
|
||||
<Trans>Cancel</Trans>
|
||||
|
||||
@@ -72,11 +72,9 @@ export const DocumentSigningPageView = ({
|
||||
}
|
||||
|
||||
const selectedSigner = allRecipients?.find((r) => r.id === selectedSignerId);
|
||||
const targetSigner =
|
||||
recipient.role === RecipientRole.ASSISTANT && selectedSigner ? selectedSigner : null;
|
||||
|
||||
return (
|
||||
<DocumentSigningRecipientProvider recipient={recipient} targetSigner={targetSigner}>
|
||||
<DocumentSigningRecipientProvider recipient={recipient} targetSigner={selectedSigner ?? null}>
|
||||
<div className="mx-auto w-full max-w-screen-xl">
|
||||
<h1
|
||||
className="mt-4 block max-w-[20rem] truncate text-2xl font-semibold md:max-w-[30rem] md:text-3xl"
|
||||
|
||||
@@ -41,7 +41,6 @@ export const DocumentSigningRadioField = ({
|
||||
const { recipient, targetSigner, isAssistantMode } = useDocumentSigningRecipientContext();
|
||||
|
||||
const parsedFieldMeta = ZRadioFieldMeta.parse(field.fieldMeta);
|
||||
const isReadOnly = parsedFieldMeta.readOnly;
|
||||
const values = parsedFieldMeta.values?.map((item) => ({
|
||||
...item,
|
||||
value: item.value.length > 0 ? item.value : `empty-value-${item.id}`,
|
||||
@@ -165,7 +164,6 @@ export const DocumentSigningRadioField = ({
|
||||
value={item.value}
|
||||
id={`option-${field.id}-${item.id}`}
|
||||
checked={item.checked}
|
||||
disabled={isReadOnly}
|
||||
/>
|
||||
{!item.value.includes('empty-value-') && item.value && (
|
||||
<Label
|
||||
@@ -189,7 +187,6 @@ export const DocumentSigningRadioField = ({
|
||||
value={item.value}
|
||||
id={`option-${field.id}-${item.id}`}
|
||||
checked={item.value === field.customText}
|
||||
disabled={isReadOnly}
|
||||
/>
|
||||
{!item.value.includes('empty-value-') && item.value && (
|
||||
<Label
|
||||
|
||||
+5
@@ -38,6 +38,11 @@ export const DocumentSigningRecipientProvider = ({
|
||||
recipient,
|
||||
targetSigner = null,
|
||||
}: DocumentSigningRecipientProviderProps) => {
|
||||
// console.log({
|
||||
// recipient,
|
||||
// targetSigner,
|
||||
// isAssistantMode: !!targetSigner,
|
||||
// });
|
||||
return (
|
||||
<DocumentSigningRecipientContext.Provider
|
||||
value={{
|
||||
|
||||
@@ -262,7 +262,9 @@ export const DocumentSigningTextField = ({
|
||||
|
||||
{field.inserted && (
|
||||
<DocumentSigningFieldsInserted textAlign={parsedFieldMeta?.textAlign}>
|
||||
{field.customText}
|
||||
{field.customText.length < 20
|
||||
? field.customText
|
||||
: field.customText.substring(0, 20) + '...'}
|
||||
</DocumentSigningFieldsInserted>
|
||||
)}
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import { msg } from '@lingui/core/macro';
|
||||
import { useLingui } from '@lingui/react';
|
||||
import { DocumentDistributionMethod, DocumentStatus } from '@prisma/client';
|
||||
import { useNavigate, useSearchParams } from 'react-router';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { DocumentSignatureType } from '@documenso/lib/constants/document';
|
||||
import { isValidLanguageCode } from '@documenso/lib/constants/i18n';
|
||||
@@ -13,7 +12,6 @@ import {
|
||||
SKIP_QUERY_BATCH_META,
|
||||
} from '@documenso/lib/constants/trpc';
|
||||
import type { TDocument } from '@documenso/lib/types/document';
|
||||
import { ZDocumentAccessAuthTypesSchema } from '@documenso/lib/types/document-auth';
|
||||
import { trpc } from '@documenso/trpc/react';
|
||||
import { cn } from '@documenso/ui/lib/utils';
|
||||
import { Card, CardContent } from '@documenso/ui/primitives/card';
|
||||
@@ -177,17 +175,13 @@ export const DocumentEditForm = ({
|
||||
try {
|
||||
const { timezone, dateFormat, redirectUrl, language, signatureTypes } = data.meta;
|
||||
|
||||
const parsedGlobalAccessAuth = z
|
||||
.array(ZDocumentAccessAuthTypesSchema)
|
||||
.safeParse(data.globalAccessAuth);
|
||||
|
||||
await updateDocument({
|
||||
documentId: document.id,
|
||||
data: {
|
||||
title: data.title,
|
||||
externalId: data.externalId || null,
|
||||
visibility: data.visibility,
|
||||
globalAccessAuth: parsedGlobalAccessAuth.success ? parsedGlobalAccessAuth.data : [],
|
||||
globalAccessAuth: data.globalAccessAuth ?? [],
|
||||
globalActionAuth: data.globalActionAuth ?? [],
|
||||
},
|
||||
meta: {
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import React from 'react';
|
||||
|
||||
import { Badge } from '@documenso/ui/primitives/badge';
|
||||
|
||||
export type DocumentHistorySheetChangesProps = {
|
||||
values: {
|
||||
key: string | React.ReactNode;
|
||||
value: string | React.ReactNode;
|
||||
}[];
|
||||
};
|
||||
|
||||
export const DocumentHistorySheetChanges = ({ values }: DocumentHistorySheetChangesProps) => {
|
||||
return (
|
||||
<Badge
|
||||
className="text-muted-foreground mt-3 block w-full space-y-0.5 text-xs"
|
||||
variant="neutral"
|
||||
>
|
||||
{values.map(({ key, value }, i) => (
|
||||
<p key={typeof key === 'string' ? key : i}>
|
||||
<span>{key}: </span>
|
||||
<span className="font-normal">{value}</span>
|
||||
</p>
|
||||
))}
|
||||
</Badge>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,410 @@
|
||||
import { useMemo, useState } from 'react';
|
||||
|
||||
import { useLingui } from '@lingui/react';
|
||||
import { Trans } from '@lingui/react/macro';
|
||||
import { ArrowRightIcon, Loader } from 'lucide-react';
|
||||
import { DateTime } from 'luxon';
|
||||
import { match } from 'ts-pattern';
|
||||
import { UAParser } from 'ua-parser-js';
|
||||
|
||||
import { DOCUMENT_AUDIT_LOG_EMAIL_FORMAT } from '@documenso/lib/constants/document-audit-logs';
|
||||
import { DOCUMENT_AUTH_TYPES } from '@documenso/lib/constants/document-auth';
|
||||
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs';
|
||||
import { formatDocumentAuditLogAction } from '@documenso/lib/utils/document-audit-logs';
|
||||
import { trpc } from '@documenso/trpc/react';
|
||||
import { cn } from '@documenso/ui/lib/utils';
|
||||
import { Avatar, AvatarFallback } from '@documenso/ui/primitives/avatar';
|
||||
import { Badge } from '@documenso/ui/primitives/badge';
|
||||
import { Button } from '@documenso/ui/primitives/button';
|
||||
import { Sheet, SheetContent, SheetTrigger } from '@documenso/ui/primitives/sheet';
|
||||
|
||||
import { DocumentHistorySheetChanges } from './document-history-sheet-changes';
|
||||
|
||||
export type DocumentHistorySheetProps = {
|
||||
documentId: number;
|
||||
userId: number;
|
||||
isMenuOpen?: boolean;
|
||||
onMenuOpenChange?: (_value: boolean) => void;
|
||||
children?: React.ReactNode;
|
||||
};
|
||||
|
||||
export const DocumentHistorySheet = ({
|
||||
documentId,
|
||||
userId,
|
||||
isMenuOpen,
|
||||
onMenuOpenChange,
|
||||
children,
|
||||
}: DocumentHistorySheetProps) => {
|
||||
const { _, i18n } = useLingui();
|
||||
|
||||
const [isUserDetailsVisible, setIsUserDetailsVisible] = useState(false);
|
||||
|
||||
const {
|
||||
data,
|
||||
isLoading,
|
||||
isLoadingError,
|
||||
refetch,
|
||||
hasNextPage,
|
||||
fetchNextPage,
|
||||
isFetchingNextPage,
|
||||
} = trpc.document.findDocumentAuditLogs.useInfiniteQuery(
|
||||
{
|
||||
documentId,
|
||||
},
|
||||
{
|
||||
getNextPageParam: (lastPage) => lastPage.nextCursor,
|
||||
placeholderData: (previousData) => previousData,
|
||||
},
|
||||
);
|
||||
|
||||
const documentAuditLogs = useMemo(() => (data?.pages ?? []).flatMap((page) => page.data), [data]);
|
||||
|
||||
const extractBrowser = (userAgent?: string | null) => {
|
||||
if (!userAgent) {
|
||||
return 'Unknown';
|
||||
}
|
||||
|
||||
const parser = new UAParser(userAgent);
|
||||
|
||||
parser.setUA(userAgent);
|
||||
|
||||
const result = parser.getResult();
|
||||
|
||||
return result.browser.name;
|
||||
};
|
||||
|
||||
/**
|
||||
* Applies the following formatting for a given text:
|
||||
* - Uppercase first lower, lowercase rest
|
||||
* - Replace _ with spaces
|
||||
*
|
||||
* @param text The text to format
|
||||
* @returns The formatted text
|
||||
*/
|
||||
const formatGenericText = (text?: string | string[] | null): string => {
|
||||
if (!text) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (Array.isArray(text)) {
|
||||
return text.map((t) => formatGenericText(t)).join(', ');
|
||||
}
|
||||
|
||||
return (text.charAt(0).toUpperCase() + text.slice(1).toLowerCase()).replaceAll('_', ' ');
|
||||
};
|
||||
|
||||
return (
|
||||
<Sheet open={isMenuOpen} onOpenChange={onMenuOpenChange}>
|
||||
{children && <SheetTrigger asChild>{children}</SheetTrigger>}
|
||||
|
||||
<SheetContent
|
||||
sheetClass="backdrop-blur-none"
|
||||
className="flex w-full max-w-[500px] flex-col overflow-y-auto p-0"
|
||||
>
|
||||
<div className="text-foreground px-6 pt-6">
|
||||
<h1 className="text-lg font-medium">
|
||||
<Trans>Document history</Trans>
|
||||
</h1>
|
||||
<button
|
||||
className="text-muted-foreground text-sm"
|
||||
onClick={() => setIsUserDetailsVisible(!isUserDetailsVisible)}
|
||||
>
|
||||
{isUserDetailsVisible ? (
|
||||
<Trans>Hide additional information</Trans>
|
||||
) : (
|
||||
<Trans>Show additional information</Trans>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{isLoading && (
|
||||
<div className="flex h-full items-center justify-center">
|
||||
<Loader className="text-muted-foreground h-6 w-6 animate-spin" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isLoadingError && (
|
||||
<div className="flex h-full flex-col items-center justify-center">
|
||||
<p className="text-foreground/80 text-sm">
|
||||
<Trans>Unable to load document history</Trans>
|
||||
</p>
|
||||
<button
|
||||
onClick={async () => refetch()}
|
||||
className="text-foreground/70 hover:text-muted-foreground mt-2 text-sm"
|
||||
>
|
||||
<Trans>Click here to retry</Trans>
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{data && (
|
||||
<ul
|
||||
className={cn('divide-y border-t', {
|
||||
'mb-4 border-b': !hasNextPage,
|
||||
})}
|
||||
>
|
||||
{documentAuditLogs.map((auditLog) => (
|
||||
<li className="px-4 py-2.5" key={auditLog.id}>
|
||||
<div className="flex flex-row items-center">
|
||||
<Avatar className="mr-2 h-9 w-9">
|
||||
<AvatarFallback className="text-xs text-gray-400">
|
||||
{(auditLog?.email ?? auditLog?.name ?? '?').slice(0, 1).toUpperCase()}
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
|
||||
<div>
|
||||
<p className="text-foreground text-xs font-bold">
|
||||
{formatDocumentAuditLogAction(_, auditLog, userId).description}
|
||||
</p>
|
||||
<p className="text-foreground/50 text-xs">
|
||||
{DateTime.fromJSDate(auditLog.createdAt)
|
||||
.setLocale(i18n.locales?.[0] || i18n.locale)
|
||||
.toFormat('d MMM, yyyy HH:MM a')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{match(auditLog)
|
||||
.with(
|
||||
{ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_CREATED },
|
||||
{ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_COMPLETED },
|
||||
{ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_DELETED },
|
||||
{ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_OPENED },
|
||||
{ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_RECIPIENT_COMPLETED },
|
||||
{ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_RECIPIENT_REJECTED },
|
||||
{ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_SENT },
|
||||
{ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_MOVED_TO_TEAM },
|
||||
() => null,
|
||||
)
|
||||
.with(
|
||||
{ type: DOCUMENT_AUDIT_LOG_TYPE.RECIPIENT_CREATED },
|
||||
{ type: DOCUMENT_AUDIT_LOG_TYPE.RECIPIENT_DELETED },
|
||||
({ data }) => {
|
||||
const values = [
|
||||
{
|
||||
key: 'Email',
|
||||
value: data.recipientEmail,
|
||||
},
|
||||
{
|
||||
key: 'Role',
|
||||
value: formatGenericText(data.recipientRole),
|
||||
},
|
||||
];
|
||||
|
||||
// Insert the name to the start of the array if available.
|
||||
if (data.recipientName) {
|
||||
values.unshift({
|
||||
key: 'Name',
|
||||
value: data.recipientName,
|
||||
});
|
||||
}
|
||||
|
||||
return <DocumentHistorySheetChanges values={values} />;
|
||||
},
|
||||
)
|
||||
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.RECIPIENT_UPDATED }, ({ data }) => {
|
||||
if (data.changes.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<DocumentHistorySheetChanges
|
||||
values={data.changes.map(({ type, from, to }) => ({
|
||||
key: formatGenericText(type),
|
||||
value: (
|
||||
<span className="inline-flex flex-row items-center">
|
||||
<span>{type === 'ROLE' ? formatGenericText(from) : from}</span>
|
||||
<ArrowRightIcon className="h-4 w-4" />
|
||||
<span>{type === 'ROLE' ? formatGenericText(to) : to}</span>
|
||||
</span>
|
||||
),
|
||||
}))}
|
||||
/>
|
||||
);
|
||||
})
|
||||
.with(
|
||||
{ type: DOCUMENT_AUDIT_LOG_TYPE.FIELD_CREATED },
|
||||
{ type: DOCUMENT_AUDIT_LOG_TYPE.FIELD_DELETED },
|
||||
{ type: DOCUMENT_AUDIT_LOG_TYPE.FIELD_UPDATED },
|
||||
({ data }) => (
|
||||
<DocumentHistorySheetChanges
|
||||
values={[
|
||||
{
|
||||
key: 'Field',
|
||||
value: formatGenericText(data.fieldType),
|
||||
},
|
||||
{
|
||||
key: 'Recipient',
|
||||
value: formatGenericText(data.fieldRecipientEmail),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
),
|
||||
)
|
||||
.with(
|
||||
{ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_GLOBAL_AUTH_ACCESS_UPDATED },
|
||||
{ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_GLOBAL_AUTH_ACTION_UPDATED },
|
||||
({ data }) => (
|
||||
<DocumentHistorySheetChanges
|
||||
values={[
|
||||
{
|
||||
key: 'Old',
|
||||
value: Array.isArray(data.from)
|
||||
? data.from
|
||||
.map((f) => DOCUMENT_AUTH_TYPES[f]?.value || 'None')
|
||||
.join(', ')
|
||||
: DOCUMENT_AUTH_TYPES[data.from || '']?.value || 'None',
|
||||
},
|
||||
{
|
||||
key: 'New',
|
||||
value: Array.isArray(data.to)
|
||||
? data.to
|
||||
.map((f) => DOCUMENT_AUTH_TYPES[f]?.value || 'None')
|
||||
.join(', ')
|
||||
: DOCUMENT_AUTH_TYPES[data.to || '']?.value || 'None',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
),
|
||||
)
|
||||
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_META_UPDATED }, ({ data }) => {
|
||||
if (data.changes.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<DocumentHistorySheetChanges
|
||||
values={data.changes.map((change) => ({
|
||||
key: formatGenericText(change.type),
|
||||
value: change.type === 'PASSWORD' ? '*********' : change.to,
|
||||
}))}
|
||||
/>
|
||||
);
|
||||
})
|
||||
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_TITLE_UPDATED }, ({ data }) => (
|
||||
<DocumentHistorySheetChanges
|
||||
values={[
|
||||
{
|
||||
key: 'Old',
|
||||
value: data.from,
|
||||
},
|
||||
{
|
||||
key: 'New',
|
||||
value: data.to,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
))
|
||||
.with(
|
||||
{ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_EXTERNAL_ID_UPDATED },
|
||||
({ data }) => (
|
||||
<DocumentHistorySheetChanges
|
||||
values={[
|
||||
{
|
||||
key: 'Old',
|
||||
value: data.from,
|
||||
},
|
||||
{
|
||||
key: 'New',
|
||||
value: data.to,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
),
|
||||
)
|
||||
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_FIELD_INSERTED }, ({ data }) => (
|
||||
<DocumentHistorySheetChanges
|
||||
values={[
|
||||
{
|
||||
key: 'Field inserted',
|
||||
value: formatGenericText(data.field.type),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
))
|
||||
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_FIELD_UNINSERTED }, ({ data }) => (
|
||||
<DocumentHistorySheetChanges
|
||||
values={[
|
||||
{
|
||||
key: 'Field uninserted',
|
||||
value: formatGenericText(data.field),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
))
|
||||
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.EMAIL_SENT }, ({ data }) => (
|
||||
<DocumentHistorySheetChanges
|
||||
values={[
|
||||
{
|
||||
key: 'Type',
|
||||
value: DOCUMENT_AUDIT_LOG_EMAIL_FORMAT[data.emailType].description,
|
||||
},
|
||||
{
|
||||
key: 'Sent to',
|
||||
value: data.recipientEmail,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
))
|
||||
.with(
|
||||
{ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_VISIBILITY_UPDATED },
|
||||
({ data }) => (
|
||||
<DocumentHistorySheetChanges
|
||||
values={[
|
||||
{
|
||||
key: 'Old',
|
||||
value: data.from,
|
||||
},
|
||||
{
|
||||
key: 'New',
|
||||
value: data.to,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
),
|
||||
)
|
||||
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_FIELD_PREFILLED }, ({ data }) => (
|
||||
<DocumentHistorySheetChanges
|
||||
values={[
|
||||
{
|
||||
key: 'Field prefilled',
|
||||
value: formatGenericText(data.field.type),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
))
|
||||
.exhaustive()}
|
||||
|
||||
{isUserDetailsVisible && (
|
||||
<>
|
||||
<div className="mb-1 mt-2 flex flex-row space-x-2">
|
||||
<Badge variant="neutral" className="text-muted-foreground">
|
||||
IP: {auditLog.ipAddress ?? 'Unknown'}
|
||||
</Badge>
|
||||
|
||||
<Badge variant="neutral" className="text-muted-foreground">
|
||||
Browser: {extractBrowser(auditLog.userAgent)}
|
||||
</Badge>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</li>
|
||||
))}
|
||||
|
||||
{hasNextPage && (
|
||||
<div className="flex items-center justify-center py-4">
|
||||
<Button
|
||||
variant="outline"
|
||||
loading={isFetchingNextPage}
|
||||
onClick={async () => fetchNextPage()}
|
||||
>
|
||||
Show more
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</ul>
|
||||
)}
|
||||
</SheetContent>
|
||||
</Sheet>
|
||||
);
|
||||
};
|
||||
@@ -13,7 +13,7 @@ import { Skeleton } from '@documenso/ui/primitives/skeleton';
|
||||
import { FolderCreateDialog } from '~/components/dialogs/folder-create-dialog';
|
||||
import { FolderDeleteDialog } from '~/components/dialogs/folder-delete-dialog';
|
||||
import { FolderMoveDialog } from '~/components/dialogs/folder-move-dialog';
|
||||
import { FolderUpdateDialog } from '~/components/dialogs/folder-update-dialog';
|
||||
import { FolderSettingsDialog } from '~/components/dialogs/folder-settings-dialog';
|
||||
import { TemplateCreateDialog } from '~/components/dialogs/template-create-dialog';
|
||||
import { DocumentUploadDropzone } from '~/components/general/document/document-upload';
|
||||
import { FolderCard, FolderCardEmpty } from '~/components/general/folder/folder-card';
|
||||
@@ -219,7 +219,7 @@ export const FolderGrid = ({ type, parentId }: FolderGridProps) => {
|
||||
}}
|
||||
/>
|
||||
|
||||
<FolderUpdateDialog
|
||||
<FolderSettingsDialog
|
||||
folder={folderToSettings}
|
||||
isOpen={isSettingsFolderOpen}
|
||||
onOpenChange={(open) => {
|
||||
|
||||
@@ -5,23 +5,18 @@ import { useLingui } from '@lingui/react';
|
||||
import { Trans } from '@lingui/react/macro';
|
||||
import { SubscriptionStatus } from '@prisma/client';
|
||||
import { AlertTriangle } from 'lucide-react';
|
||||
import { Link } from 'react-router';
|
||||
import { match } from 'ts-pattern';
|
||||
|
||||
import { useOptionalCurrentOrganisation } from '@documenso/lib/client-only/providers/organisation';
|
||||
import { SUPPORT_EMAIL } from '@documenso/lib/constants/app';
|
||||
import { canExecuteOrganisationAction } from '@documenso/lib/utils/organisations';
|
||||
import { trpc } from '@documenso/trpc/react';
|
||||
import { cn } from '@documenso/ui/lib/utils';
|
||||
import { Alert, AlertDescription } from '@documenso/ui/primitives/alert';
|
||||
import { Button } from '@documenso/ui/primitives/button';
|
||||
import {
|
||||
Dialog,
|
||||
DialogClose,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from '@documenso/ui/primitives/dialog';
|
||||
import { useToast } from '@documenso/ui/primitives/use-toast';
|
||||
@@ -91,7 +86,7 @@ export const OrganisationBillingBanner = () => {
|
||||
className={cn({
|
||||
'text-yellow-900 hover:bg-yellow-100 dark:hover:bg-yellow-500':
|
||||
subscriptionStatus === SubscriptionStatus.PAST_DUE,
|
||||
'text-destructive-foreground hover:bg-destructive hover:text-white':
|
||||
'text-destructive-foreground hover:bg-destructive-foreground hover:text-white':
|
||||
subscriptionStatus === SubscriptionStatus.INACTIVE,
|
||||
})}
|
||||
disabled={isPending}
|
||||
@@ -104,78 +99,38 @@ export const OrganisationBillingBanner = () => {
|
||||
</div>
|
||||
|
||||
<Dialog open={isOpen} onOpenChange={(value) => !isPending && setIsOpen(value)}>
|
||||
{match(subscriptionStatus)
|
||||
.with(SubscriptionStatus.PAST_DUE, () => (
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
<Trans>Payment overdue</Trans>
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogTitle>
|
||||
<Trans>Payment overdue</Trans>
|
||||
</DialogTitle>
|
||||
|
||||
<DialogDescription>
|
||||
<Trans>
|
||||
Your payment is overdue. Please settle the payment to avoid any service
|
||||
disruptions.
|
||||
</Trans>
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
{match(subscriptionStatus)
|
||||
.with(SubscriptionStatus.PAST_DUE, () => (
|
||||
<DialogDescription>
|
||||
<Trans>
|
||||
Your payment for teams is overdue. Please settle the payment to avoid any service
|
||||
disruptions.
|
||||
</Trans>
|
||||
</DialogDescription>
|
||||
))
|
||||
.with(SubscriptionStatus.INACTIVE, () => (
|
||||
<DialogDescription>
|
||||
<Trans>
|
||||
Due to an unpaid invoice, your team has been restricted. Please settle the payment
|
||||
to restore full access to your team.
|
||||
</Trans>
|
||||
</DialogDescription>
|
||||
))
|
||||
.otherwise(() => null)}
|
||||
|
||||
{canExecuteOrganisationAction(
|
||||
'MANAGE_BILLING',
|
||||
organisation.currentOrganisationRole,
|
||||
) && (
|
||||
<DialogFooter>
|
||||
<Button
|
||||
loading={isPending}
|
||||
onClick={async () => handleCreatePortal(organisation.id)}
|
||||
>
|
||||
<Trans>Resolve payment</Trans>
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
)}
|
||||
</DialogContent>
|
||||
))
|
||||
.with(SubscriptionStatus.INACTIVE, () => (
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
<Trans>Subscription invalid</Trans>
|
||||
</DialogTitle>
|
||||
|
||||
<DialogDescription>
|
||||
<Trans>
|
||||
Your plan is no longer valid. Please subscribe to a new plan to continue using
|
||||
Documenso.
|
||||
</Trans>
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<Alert variant="neutral">
|
||||
<AlertDescription>
|
||||
<Trans>
|
||||
If there is any issue with your subscription, please contact us at{' '}
|
||||
<a href={`mailto:${SUPPORT_EMAIL}`}>{SUPPORT_EMAIL}</a>.
|
||||
</Trans>
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
|
||||
{canExecuteOrganisationAction(
|
||||
'MANAGE_BILLING',
|
||||
organisation.currentOrganisationRole,
|
||||
) && (
|
||||
<DialogFooter>
|
||||
<DialogClose asChild>
|
||||
<Button asChild>
|
||||
<Link to={`/o/${organisation.url}/settings/billing`}>
|
||||
<Trans>Manage Billing</Trans>
|
||||
</Link>
|
||||
</Button>
|
||||
</DialogClose>
|
||||
</DialogFooter>
|
||||
)}
|
||||
</DialogContent>
|
||||
))
|
||||
.otherwise(() => null)}
|
||||
{canExecuteOrganisationAction('MANAGE_BILLING', organisation.currentOrganisationRole) && (
|
||||
<DialogFooter>
|
||||
<Button loading={isPending} onClick={async () => handleCreatePortal(organisation.id)}>
|
||||
<Trans>Resolve payment</Trans>
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
)}
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -3,7 +3,6 @@ import { useEffect, useState } from 'react';
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { useLingui } from '@lingui/react';
|
||||
import { useNavigate } from 'react-router';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { DocumentSignatureType } from '@documenso/lib/constants/document';
|
||||
import { isValidLanguageCode } from '@documenso/lib/constants/i18n';
|
||||
@@ -11,7 +10,6 @@ import {
|
||||
DO_NOT_INVALIDATE_QUERY_ON_MUTATION,
|
||||
SKIP_QUERY_BATCH_META,
|
||||
} from '@documenso/lib/constants/trpc';
|
||||
import { ZDocumentAccessAuthTypesSchema } from '@documenso/lib/types/document-auth';
|
||||
import type { TTemplate } from '@documenso/lib/types/template';
|
||||
import { trpc } from '@documenso/trpc/react';
|
||||
import { cn } from '@documenso/ui/lib/utils';
|
||||
@@ -127,10 +125,6 @@ export const TemplateEditForm = ({
|
||||
const onAddSettingsFormSubmit = async (data: TAddTemplateSettingsFormSchema) => {
|
||||
const { signatureTypes } = data.meta;
|
||||
|
||||
const parsedGlobalAccessAuth = z
|
||||
.array(ZDocumentAccessAuthTypesSchema)
|
||||
.safeParse(data.globalAccessAuth);
|
||||
|
||||
try {
|
||||
await updateTemplateSettings({
|
||||
templateId: template.id,
|
||||
@@ -138,7 +132,7 @@ export const TemplateEditForm = ({
|
||||
title: data.title,
|
||||
externalId: data.externalId || null,
|
||||
visibility: data.visibility,
|
||||
globalAccessAuth: parsedGlobalAccessAuth.success ? parsedGlobalAccessAuth.data : [],
|
||||
globalAccessAuth: data.globalAccessAuth ?? [],
|
||||
globalActionAuth: data.globalActionAuth ?? [],
|
||||
},
|
||||
meta: {
|
||||
|
||||
@@ -188,7 +188,7 @@ export const DocumentsTableActionDropdown = ({
|
||||
<Trans>Duplicate</Trans>
|
||||
</DropdownMenuItem>
|
||||
|
||||
{onMoveDocument && canManageDocument && (
|
||||
{onMoveDocument && (
|
||||
<DropdownMenuItem onClick={onMoveDocument} onSelect={(e) => e.preventDefault()}>
|
||||
<FolderInput className="mr-2 h-4 w-4" />
|
||||
<Trans>Move to Folder</Trans>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { useLingui } from '@lingui/react';
|
||||
import { Trans } from '@lingui/react/macro';
|
||||
import { SubscriptionStatus } from '@prisma/client';
|
||||
import { Loader } from 'lucide-react';
|
||||
import type Stripe from 'stripe';
|
||||
import { match } from 'ts-pattern';
|
||||
@@ -135,9 +134,7 @@ export default function TeamsSettingBillingPage() {
|
||||
|
||||
<hr className="my-4" />
|
||||
|
||||
{(!subscription ||
|
||||
subscription.organisationSubscription.status === SubscriptionStatus.INACTIVE) &&
|
||||
canManageBilling && <BillingPlans plans={plans} />}
|
||||
{!subscription && canManageBilling && <BillingPlans plans={plans} />}
|
||||
|
||||
<section className="mt-6">
|
||||
<OrganisationBillingInvoicesTable
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useLingui } from '@lingui/react';
|
||||
import { Plural, Trans } from '@lingui/react/macro';
|
||||
import { DocumentStatus, TeamMemberRole } from '@prisma/client';
|
||||
import { ChevronLeft, Users2 } from 'lucide-react';
|
||||
import { ChevronLeft, Clock9, Users2 } from 'lucide-react';
|
||||
import { Link, redirect } from 'react-router';
|
||||
import { match } from 'ts-pattern';
|
||||
|
||||
@@ -13,9 +13,11 @@ import { DocumentVisibility } from '@documenso/lib/types/document-visibility';
|
||||
import { formatDocumentsPath } from '@documenso/lib/utils/teams';
|
||||
import { DocumentReadOnlyFields } from '@documenso/ui/components/document/document-read-only-fields';
|
||||
import { Badge } from '@documenso/ui/primitives/badge';
|
||||
import { Button } from '@documenso/ui/primitives/button';
|
||||
import { Card, CardContent } from '@documenso/ui/primitives/card';
|
||||
import { PDFViewer } from '@documenso/ui/primitives/pdf-viewer';
|
||||
|
||||
import { DocumentHistorySheet } from '~/components/general/document/document-history-sheet';
|
||||
import { DocumentPageViewButton } from '~/components/general/document/document-page-view-button';
|
||||
import { DocumentPageViewDropdown } from '~/components/general/document/document-page-view-dropdown';
|
||||
import { DocumentPageViewInformation } from '~/components/general/document/document-page-view-information';
|
||||
@@ -99,6 +101,9 @@ export default function DocumentPage() {
|
||||
|
||||
const { recipients, documentData, documentMeta } = document;
|
||||
|
||||
// This was a feature flag. Leave to false since it's not ready.
|
||||
const isDocumentHistoryEnabled = false;
|
||||
|
||||
return (
|
||||
<div className="mx-auto -mt-4 w-full max-w-screen-xl px-4 md:px-8">
|
||||
{document.status === DocumentStatus.PENDING && (
|
||||
@@ -149,6 +154,17 @@ export default function DocumentPage() {
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{isDocumentHistoryEnabled && (
|
||||
<div className="self-end">
|
||||
<DocumentHistorySheet documentId={document.id} userId={user.id}>
|
||||
<Button variant="outline">
|
||||
<Clock9 className="mr-1.5 h-4 w-4" />
|
||||
<Trans>Document history</Trans>
|
||||
</Button>
|
||||
</DocumentHistorySheet>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="mt-6 grid w-full grid-cols-12 gap-8">
|
||||
|
||||
@@ -14,7 +14,7 @@ import { Input } from '@documenso/ui/primitives/input';
|
||||
import { FolderCreateDialog } from '~/components/dialogs/folder-create-dialog';
|
||||
import { FolderDeleteDialog } from '~/components/dialogs/folder-delete-dialog';
|
||||
import { FolderMoveDialog } from '~/components/dialogs/folder-move-dialog';
|
||||
import { FolderUpdateDialog } from '~/components/dialogs/folder-update-dialog';
|
||||
import { FolderSettingsDialog } from '~/components/dialogs/folder-settings-dialog';
|
||||
import { FolderCard } from '~/components/general/folder/folder-card';
|
||||
import { useCurrentTeam } from '~/providers/team';
|
||||
import { appMetaTags } from '~/utils/meta';
|
||||
@@ -177,7 +177,7 @@ export default function DocumentsFoldersPage() {
|
||||
}}
|
||||
/>
|
||||
|
||||
<FolderUpdateDialog
|
||||
<FolderSettingsDialog
|
||||
folder={folderToSettings}
|
||||
isOpen={isSettingsFolderOpen}
|
||||
onOpenChange={(open) => {
|
||||
|
||||
@@ -2,14 +2,13 @@ import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { useLingui } from '@lingui/react';
|
||||
import { Trans } from '@lingui/react/macro';
|
||||
import { Loader } from 'lucide-react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { useRevalidator } from 'react-router';
|
||||
import { Link } from 'react-router';
|
||||
import type { z } from 'zod';
|
||||
|
||||
import { trpc } from '@documenso/trpc/react';
|
||||
import { ZEditWebhookRequestSchema } from '@documenso/trpc/server/webhook-router/schema';
|
||||
import { Alert, AlertDescription, AlertTitle } from '@documenso/ui/primitives/alert';
|
||||
import { Button } from '@documenso/ui/primitives/button';
|
||||
import {
|
||||
Form,
|
||||
@@ -22,12 +21,9 @@ import {
|
||||
} from '@documenso/ui/primitives/form/form';
|
||||
import { Input } from '@documenso/ui/primitives/input';
|
||||
import { PasswordInput } from '@documenso/ui/primitives/password-input';
|
||||
import { SpinnerBox } from '@documenso/ui/primitives/spinner';
|
||||
import { Switch } from '@documenso/ui/primitives/switch';
|
||||
import { useToast } from '@documenso/ui/primitives/use-toast';
|
||||
|
||||
import { WebhookTestDialog } from '~/components/dialogs/webhook-test-dialog';
|
||||
import { GenericErrorLayout } from '~/components/general/generic-error-layout';
|
||||
import { SettingsHeader } from '~/components/general/settings-header';
|
||||
import { WebhookMultiSelectCombobox } from '~/components/general/webhook-multiselect-combobox';
|
||||
import { useCurrentTeam } from '~/providers/team';
|
||||
@@ -96,45 +92,25 @@ export default function WebhookPage({ params }: Route.ComponentProps) {
|
||||
}
|
||||
};
|
||||
|
||||
if (isLoading) {
|
||||
return <SpinnerBox className="py-32" />;
|
||||
}
|
||||
|
||||
// Todo: Update UI, currently out of place.
|
||||
if (!webhook) {
|
||||
return (
|
||||
<GenericErrorLayout
|
||||
errorCode={404}
|
||||
errorCodeMap={{
|
||||
404: {
|
||||
heading: msg`Webhook not found`,
|
||||
subHeading: msg`404 Webhook not found`,
|
||||
message: msg`The webhook you are looking for may have been removed, renamed or may have never
|
||||
existed.`,
|
||||
},
|
||||
}}
|
||||
primaryButton={
|
||||
<Button asChild>
|
||||
<Link to={`/t/${team.url}/settings/webhooks`}>
|
||||
<Trans>Go back</Trans>
|
||||
</Link>
|
||||
</Button>
|
||||
}
|
||||
secondaryButton={null}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="max-w-2xl">
|
||||
<div>
|
||||
<SettingsHeader
|
||||
title={_(msg`Edit webhook`)}
|
||||
subtitle={_(msg`On this page, you can edit the webhook and its settings.`)}
|
||||
/>
|
||||
|
||||
{isLoading && (
|
||||
<div className="absolute inset-0 z-50 flex items-center justify-center bg-white/50">
|
||||
<Loader className="h-8 w-8 animate-spin text-gray-500" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)}>
|
||||
<fieldset className="flex h-full flex-col gap-y-6" disabled={form.formState.isSubmitting}>
|
||||
<fieldset
|
||||
className="flex h-full max-w-xl flex-col gap-y-6"
|
||||
disabled={form.formState.isSubmitting}
|
||||
>
|
||||
<div className="flex flex-col-reverse gap-4 md:flex-row">
|
||||
<FormField
|
||||
control={form.control}
|
||||
@@ -227,7 +203,7 @@ export default function WebhookPage({ params }: Route.ComponentProps) {
|
||||
)}
|
||||
/>
|
||||
|
||||
<div className="flex justify-end gap-4">
|
||||
<div className="mt-4">
|
||||
<Button type="submit" loading={form.formState.isSubmitting}>
|
||||
<Trans>Update webhook</Trans>
|
||||
</Button>
|
||||
@@ -235,30 +211,6 @@ export default function WebhookPage({ params }: Route.ComponentProps) {
|
||||
</fieldset>
|
||||
</form>
|
||||
</Form>
|
||||
|
||||
<Alert
|
||||
className="mt-6 flex flex-col items-center justify-between gap-4 p-6 md:flex-row"
|
||||
variant="neutral"
|
||||
>
|
||||
<div>
|
||||
<AlertTitle>
|
||||
<Trans>Test Webhook</Trans>
|
||||
</AlertTitle>
|
||||
<AlertDescription className="mr-2">
|
||||
<Trans>
|
||||
Send a test webhook with sample data to verify your integration is working correctly.
|
||||
</Trans>
|
||||
</AlertDescription>
|
||||
</div>
|
||||
|
||||
<div className="flex-shrink-0">
|
||||
<WebhookTestDialog webhook={webhook}>
|
||||
<Button variant="outline" disabled={!webhook.enabled}>
|
||||
<Trans>Test Webhook</Trans>
|
||||
</Button>
|
||||
</WebhookTestDialog>
|
||||
</div>
|
||||
</Alert>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ export default function WebhookPage() {
|
||||
</div>
|
||||
)}
|
||||
{webhooks && webhooks.length > 0 && (
|
||||
<div className="mt-4 flex max-w-2xl flex-col gap-y-4">
|
||||
<div className="mt-4 flex max-w-xl flex-col gap-y-4">
|
||||
{webhooks?.map((webhook) => (
|
||||
<div
|
||||
key={webhook.id}
|
||||
|
||||
@@ -14,7 +14,7 @@ import { Input } from '@documenso/ui/primitives/input';
|
||||
import { FolderCreateDialog } from '~/components/dialogs/folder-create-dialog';
|
||||
import { FolderDeleteDialog } from '~/components/dialogs/folder-delete-dialog';
|
||||
import { FolderMoveDialog } from '~/components/dialogs/folder-move-dialog';
|
||||
import { FolderUpdateDialog } from '~/components/dialogs/folder-update-dialog';
|
||||
import { FolderSettingsDialog } from '~/components/dialogs/folder-settings-dialog';
|
||||
import { FolderCard } from '~/components/general/folder/folder-card';
|
||||
import { useCurrentTeam } from '~/providers/team';
|
||||
import { appMetaTags } from '~/utils/meta';
|
||||
@@ -177,7 +177,7 @@ export default function TemplatesFoldersPage() {
|
||||
}}
|
||||
/>
|
||||
|
||||
<FolderUpdateDialog
|
||||
<FolderSettingsDialog
|
||||
folder={folderToSettings}
|
||||
isOpen={isSettingsFolderOpen}
|
||||
onOpenChange={(open: boolean) => {
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { Trans } from '@lingui/react/macro';
|
||||
import { Link } from 'react-router';
|
||||
|
||||
import { SUPPORT_EMAIL } from '@documenso/lib/constants/app';
|
||||
import { Button } from '@documenso/ui/primitives/button';
|
||||
|
||||
const SUPPORT_EMAIL = 'support@documenso.com';
|
||||
|
||||
export default function SignatureDisclosure() {
|
||||
return (
|
||||
<div>
|
||||
|
||||
@@ -41,7 +41,6 @@
|
||||
"colord": "^2.9.3",
|
||||
"framer-motion": "^10.12.8",
|
||||
"hono": "4.7.0",
|
||||
"hono-rate-limiter": "^0.4.2",
|
||||
"hono-react-router-adapter": "^0.6.2",
|
||||
"input-otp": "^1.2.4",
|
||||
"isbot": "^5.1.17",
|
||||
@@ -101,5 +100,5 @@
|
||||
"vite-plugin-babel-macros": "^1.0.6",
|
||||
"vite-tsconfig-paths": "^5.1.4"
|
||||
},
|
||||
"version": "1.12.2-rc.2"
|
||||
"version": "1.12.0-rc.4"
|
||||
}
|
||||
|
||||
@@ -1,16 +1,10 @@
|
||||
import { Hono } from 'hono';
|
||||
import { rateLimiter } from 'hono-rate-limiter';
|
||||
import { contextStorage } from 'hono/context-storage';
|
||||
import { requestId } from 'hono/request-id';
|
||||
import type { RequestIdVariables } from 'hono/request-id';
|
||||
import type { Logger } from 'pino';
|
||||
|
||||
import { tsRestHonoApp } from '@documenso/api/hono';
|
||||
import { auth } from '@documenso/auth/server';
|
||||
import { API_V2_BETA_URL } from '@documenso/lib/constants/app';
|
||||
import { jobsClient } from '@documenso/lib/jobs/client';
|
||||
import { getIpAddress } from '@documenso/lib/universal/get-ip-address';
|
||||
import { logger } from '@documenso/lib/utils/logger';
|
||||
import { openApiDocument } from '@documenso/trpc/server/open-api';
|
||||
|
||||
import { filesRoute } from './api/files';
|
||||
@@ -20,33 +14,13 @@ import { openApiTrpcServerHandler } from './trpc/hono-trpc-open-api';
|
||||
import { reactRouterTrpcServer } from './trpc/hono-trpc-remix';
|
||||
|
||||
export interface HonoEnv {
|
||||
Variables: RequestIdVariables & {
|
||||
Variables: {
|
||||
context: AppContext;
|
||||
logger: Logger;
|
||||
};
|
||||
}
|
||||
|
||||
const app = new Hono<HonoEnv>();
|
||||
|
||||
/**
|
||||
* Rate limiting for v1 and v2 API routes only.
|
||||
* - 100 requests per minute per IP address
|
||||
*/
|
||||
const rateLimitMiddleware = rateLimiter({
|
||||
windowMs: 60 * 1000, // 1 minute
|
||||
limit: 100, // 100 requests per window
|
||||
keyGenerator: (c) => {
|
||||
try {
|
||||
return getIpAddress(c.req.raw);
|
||||
} catch (error) {
|
||||
return 'unknown';
|
||||
}
|
||||
},
|
||||
message: {
|
||||
error: 'Too many requests, please try again later.',
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Attach session and context to requests.
|
||||
*/
|
||||
@@ -57,24 +31,6 @@ app.use(appContext);
|
||||
* RR7 app middleware.
|
||||
*/
|
||||
app.use('*', appMiddleware);
|
||||
app.use('*', requestId());
|
||||
app.use(async (c, next) => {
|
||||
const metadata = c.get('context').requestMetadata;
|
||||
|
||||
const honoLogger = logger.child({
|
||||
requestId: c.var.requestId,
|
||||
ipAddress: metadata.ipAddress,
|
||||
userAgent: metadata.userAgent,
|
||||
});
|
||||
|
||||
c.set('logger', honoLogger);
|
||||
|
||||
await next();
|
||||
});
|
||||
|
||||
// Apply rate limit to /api/v1/*
|
||||
app.use('/api/v1/*', rateLimitMiddleware);
|
||||
app.use('/api/v2/*', rateLimitMiddleware);
|
||||
|
||||
// Auth server.
|
||||
app.route('/api/auth', auth);
|
||||
|
||||
Generated
+142
-203
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@documenso/root",
|
||||
"version": "1.12.2-rc.2",
|
||||
"version": "1.12.0-rc.4",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@documenso/root",
|
||||
"version": "1.12.2-rc.2",
|
||||
"version": "1.12.0-rc.4",
|
||||
"workspaces": [
|
||||
"apps/*",
|
||||
"packages/*"
|
||||
@@ -89,7 +89,7 @@
|
||||
},
|
||||
"apps/remix": {
|
||||
"name": "@documenso/remix",
|
||||
"version": "1.12.2-rc.2",
|
||||
"version": "1.12.0-rc.4",
|
||||
"dependencies": {
|
||||
"@documenso/api": "*",
|
||||
"@documenso/assets": "*",
|
||||
@@ -118,7 +118,6 @@
|
||||
"colord": "^2.9.3",
|
||||
"framer-motion": "^10.12.8",
|
||||
"hono": "4.7.0",
|
||||
"hono-rate-limiter": "^0.4.2",
|
||||
"hono-react-router-adapter": "^0.6.2",
|
||||
"input-otp": "^1.2.4",
|
||||
"isbot": "^5.1.17",
|
||||
@@ -3073,6 +3072,36 @@
|
||||
"integrity": "sha512-lhqDEAvWixy3bZ+UOYbPwUbBkwBq5C1LAJ/xPC8Oi+lL54oyakv/npbA0aU2hgCsx/1NUd4IBvV03+aUBWxerw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@honeybadger-io/core": {
|
||||
"version": "6.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@honeybadger-io/core/-/core-6.7.0.tgz",
|
||||
"integrity": "sha512-bEXRe2UVbfr9q3434/2eO3AHguUT0froYEqrHfTPphR4Aw6+AlFac0YE8elqDZqUSgRQ6m1OXqxmq/HOF+W6LQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"json-nd": "^1.0.0",
|
||||
"stacktrace-parser": "^0.1.10"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/@honeybadger-io/js": {
|
||||
"version": "6.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@honeybadger-io/js/-/js-6.11.0.tgz",
|
||||
"integrity": "sha512-nSibKUr9ccrs6Jb3Ql7uO/4MdEEv3ONGP1CrD0w3zSMHUvQKHe43NPYfESA7btxjrf9PVeV+m6ETP/193BSILg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@honeybadger-io/core": "^6.7.0",
|
||||
"@types/aws-lambda": "^8.10.89",
|
||||
"@types/express": "^4.17.13"
|
||||
},
|
||||
"bin": {
|
||||
"honeybadger-checkins-sync": "scripts/check-ins-sync-bin.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/@hono/node-server": {
|
||||
"version": "1.14.2",
|
||||
"resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.14.2.tgz",
|
||||
@@ -11685,6 +11714,16 @@
|
||||
"@babel/types": "^7.20.7"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/body-parser": {
|
||||
"version": "1.19.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz",
|
||||
"integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/connect": "*",
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/bunyan": {
|
||||
"version": "1.8.11",
|
||||
"resolved": "https://registry.npmjs.org/@types/bunyan/-/bunyan-1.8.11.tgz",
|
||||
@@ -11805,6 +11844,30 @@
|
||||
"@types/estree": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/express": {
|
||||
"version": "4.17.22",
|
||||
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.22.tgz",
|
||||
"integrity": "sha512-eZUmSnhRX9YRSkplpz0N+k6NljUUn5l3EWZIKZvYzhvMphEuNiyyy1viH/ejgt66JWgALwC/gtSUAeQKtSwW/w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/body-parser": "*",
|
||||
"@types/express-serve-static-core": "^4.17.33",
|
||||
"@types/qs": "*",
|
||||
"@types/serve-static": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/express-serve-static-core": {
|
||||
"version": "4.19.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz",
|
||||
"integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "*",
|
||||
"@types/qs": "*",
|
||||
"@types/range-parser": "*",
|
||||
"@types/send": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/formidable": {
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/formidable/-/formidable-2.0.6.tgz",
|
||||
@@ -11834,6 +11897,12 @@
|
||||
"hoist-non-react-statics": "^3.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/http-errors": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz",
|
||||
"integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/istanbul-lib-coverage": {
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz",
|
||||
@@ -11907,6 +11976,12 @@
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/mime": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz",
|
||||
"integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/minimist": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz",
|
||||
@@ -11994,6 +12069,12 @@
|
||||
"integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/qs": {
|
||||
"version": "6.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz",
|
||||
"integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/ramda": {
|
||||
"version": "0.30.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/ramda/-/ramda-0.30.2.tgz",
|
||||
@@ -12003,6 +12084,12 @@
|
||||
"types-ramda": "^0.30.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/range-parser": {
|
||||
"version": "1.2.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz",
|
||||
"integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/react": {
|
||||
"version": "18.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.5.tgz",
|
||||
@@ -12036,6 +12123,27 @@
|
||||
"integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/send": {
|
||||
"version": "0.17.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz",
|
||||
"integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/mime": "^1",
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/serve-static": {
|
||||
"version": "1.15.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz",
|
||||
"integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/http-errors": "*",
|
||||
"@types/node": "*",
|
||||
"@types/send": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/shimmer": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/shimmer/-/shimmer-1.2.0.tgz",
|
||||
@@ -13203,15 +13311,6 @@
|
||||
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/atomic-sleep": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz",
|
||||
"integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/attr-accept": {
|
||||
"version": "2.2.5",
|
||||
"resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.5.tgz",
|
||||
@@ -14837,6 +14936,7 @@
|
||||
"version": "2.0.20",
|
||||
"resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
|
||||
"integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/colors": {
|
||||
@@ -16018,15 +16118,6 @@
|
||||
"url": "https://github.com/sponsors/kossnocorp"
|
||||
}
|
||||
},
|
||||
"node_modules/dateformat": {
|
||||
"version": "4.6.3",
|
||||
"resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz",
|
||||
"integrity": "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/dayjs": {
|
||||
"version": "1.11.13",
|
||||
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz",
|
||||
@@ -18149,12 +18240,6 @@
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/fast-copy": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/fast-copy/-/fast-copy-3.0.2.tgz",
|
||||
"integrity": "sha512-dl0O9Vhju8IrcLndv2eU4ldt1ftXMqqfgN4H1cpmGV7P6jeB9FwpN9a2c8DPGE1Ys88rNUJVYDHq73CGAGOPfQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-deep-equal": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||
@@ -18223,21 +18308,6 @@
|
||||
"integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-redact": {
|
||||
"version": "3.5.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.5.0.tgz",
|
||||
"integrity": "sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/fast-safe-stringify": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz",
|
||||
"integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-uri": {
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz",
|
||||
@@ -20171,15 +20241,6 @@
|
||||
"node": ">=16.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/hono-rate-limiter": {
|
||||
"version": "0.4.2",
|
||||
"resolved": "https://registry.npmjs.org/hono-rate-limiter/-/hono-rate-limiter-0.4.2.tgz",
|
||||
"integrity": "sha512-AAtFqgADyrmbDijcRTT/HJfwqfvhalya2Zo+MgfdrMPas3zSMD8SU03cv+ZsYwRU1swv7zgVt0shwN059yzhjw==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"hono": "^4.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/hono-react-router-adapter": {
|
||||
"version": "0.6.5",
|
||||
"resolved": "https://registry.npmjs.org/hono-react-router-adapter/-/hono-react-router-adapter-0.6.5.tgz",
|
||||
@@ -21582,6 +21643,7 @@
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz",
|
||||
"integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
@@ -21790,6 +21852,12 @@
|
||||
"integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/json-nd": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/json-nd/-/json-nd-1.0.0.tgz",
|
||||
"integrity": "sha512-8TIp0HZAY0VVrwRQJJPb4+nOTSPoOWZeEKBTLizUfQO4oym5Fc/MKqN8vEbLCxcyxDf2vwNxOQ1q84O49GWPyQ==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/json-parse-even-better-errors": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
|
||||
@@ -26006,15 +26074,6 @@
|
||||
"integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/on-exit-leak-free": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz",
|
||||
"integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/on-finished": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
|
||||
@@ -26834,82 +26893,6 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/pino": {
|
||||
"version": "9.7.0",
|
||||
"resolved": "https://registry.npmjs.org/pino/-/pino-9.7.0.tgz",
|
||||
"integrity": "sha512-vnMCM6xZTb1WDmLvtG2lE/2p+t9hDEIvTWJsu6FejkE62vB7gDhvzrpFR4Cw2to+9JNQxVnkAKVPA1KPB98vWg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"atomic-sleep": "^1.0.0",
|
||||
"fast-redact": "^3.1.1",
|
||||
"on-exit-leak-free": "^2.1.0",
|
||||
"pino-abstract-transport": "^2.0.0",
|
||||
"pino-std-serializers": "^7.0.0",
|
||||
"process-warning": "^5.0.0",
|
||||
"quick-format-unescaped": "^4.0.3",
|
||||
"real-require": "^0.2.0",
|
||||
"safe-stable-stringify": "^2.3.1",
|
||||
"sonic-boom": "^4.0.1",
|
||||
"thread-stream": "^3.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"pino": "bin.js"
|
||||
}
|
||||
},
|
||||
"node_modules/pino-abstract-transport": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-2.0.0.tgz",
|
||||
"integrity": "sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"split2": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/pino-abstract-transport/node_modules/split2": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
|
||||
"integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">= 10.x"
|
||||
}
|
||||
},
|
||||
"node_modules/pino-pretty": {
|
||||
"version": "13.0.0",
|
||||
"resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-13.0.0.tgz",
|
||||
"integrity": "sha512-cQBBIVG3YajgoUjo1FdKVRX6t9XPxwB9lcNJVD5GCnNM4Y6T12YYx8c6zEejxQsU0wrg9TwmDulcE9LR7qcJqA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"colorette": "^2.0.7",
|
||||
"dateformat": "^4.6.3",
|
||||
"fast-copy": "^3.0.2",
|
||||
"fast-safe-stringify": "^2.1.1",
|
||||
"help-me": "^5.0.0",
|
||||
"joycon": "^3.1.1",
|
||||
"minimist": "^1.2.6",
|
||||
"on-exit-leak-free": "^2.1.0",
|
||||
"pino-abstract-transport": "^2.0.0",
|
||||
"pump": "^3.0.0",
|
||||
"secure-json-parse": "^2.4.0",
|
||||
"sonic-boom": "^4.0.1",
|
||||
"strip-json-comments": "^3.1.1"
|
||||
},
|
||||
"bin": {
|
||||
"pino-pretty": "bin.js"
|
||||
}
|
||||
},
|
||||
"node_modules/pino-pretty/node_modules/help-me": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz",
|
||||
"integrity": "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/pino-std-serializers": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.0.0.tgz",
|
||||
"integrity": "sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/pirates": {
|
||||
"version": "4.0.7",
|
||||
"resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz",
|
||||
@@ -27787,22 +27770,6 @@
|
||||
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/process-warning": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.0.0.tgz",
|
||||
"integrity": "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/fastify"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/fastify"
|
||||
}
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/progress": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
|
||||
@@ -28040,12 +28007,6 @@
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/quick-format-unescaped": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz",
|
||||
"integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/quick-lru": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz",
|
||||
@@ -29437,15 +29398,6 @@
|
||||
"integrity": "sha512-onYyVhBNr4CmAxFsKS7bz+uTLRakypIe4R+5A824vBSkQy/hB3fZepoVEf8OVAxzLvK+H/jm9TzpI3ETSm64Kg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/real-require": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz",
|
||||
"integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 12.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/recharts": {
|
||||
"version": "2.15.3",
|
||||
"resolved": "https://registry.npmjs.org/recharts/-/recharts-2.15.3.tgz",
|
||||
@@ -30497,15 +30449,6 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/safe-stable-stringify": {
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz",
|
||||
"integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||
@@ -30571,12 +30514,6 @@
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/secure-json-parse": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz",
|
||||
"integrity": "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/selderee": {
|
||||
"version": "0.11.0",
|
||||
"resolved": "https://registry.npmjs.org/selderee/-/selderee-0.11.0.tgz",
|
||||
@@ -31138,15 +31075,6 @@
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/sonic-boom": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.0.tgz",
|
||||
"integrity": "sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"atomic-sleep": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/sort-keys": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-5.1.0.tgz",
|
||||
@@ -31376,6 +31304,27 @@
|
||||
"integrity": "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/stacktrace-parser": {
|
||||
"version": "0.1.11",
|
||||
"resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.11.tgz",
|
||||
"integrity": "sha512-WjlahMgHmCJpqzU8bIBy4qtsZdU9lRlcZE3Lvyej6t4tuOuv1vk57OW3MBrj6hXBFx/nNoC9MPMTcr5YA7NQbg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"type-fest": "^0.7.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/stacktrace-parser/node_modules/type-fest": {
|
||||
"version": "0.7.1",
|
||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz",
|
||||
"integrity": "sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==",
|
||||
"license": "(MIT OR CC0-1.0)",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/statuses": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
|
||||
@@ -32391,15 +32340,6 @@
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/thread-stream": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-3.1.0.tgz",
|
||||
"integrity": "sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"real-require": "^0.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/through": {
|
||||
"version": "2.3.8",
|
||||
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
|
||||
@@ -36044,6 +35984,7 @@
|
||||
"@documenso/email": "*",
|
||||
"@documenso/prisma": "*",
|
||||
"@documenso/signing": "*",
|
||||
"@honeybadger-io/js": "^6.10.1",
|
||||
"@lingui/core": "^5.2.0",
|
||||
"@lingui/macro": "^5.2.0",
|
||||
"@lingui/react": "^5.2.0",
|
||||
@@ -36064,8 +36005,6 @@
|
||||
"oslo": "^0.17.0",
|
||||
"pdf-lib": "^1.17.1",
|
||||
"pg": "^8.11.3",
|
||||
"pino": "^9.7.0",
|
||||
"pino-pretty": "^13.0.0",
|
||||
"playwright": "1.52.0",
|
||||
"posthog-js": "^1.245.0",
|
||||
"posthog-node": "^4.17.0",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"private": true,
|
||||
"version": "1.12.2-rc.2",
|
||||
"version": "1.12.0-rc.4",
|
||||
"scripts": {
|
||||
"build": "turbo run build",
|
||||
"dev": "turbo run dev --filter=@documenso/remix",
|
||||
|
||||
@@ -8,12 +8,10 @@ import { testCredentialsHandler } from '@documenso/lib/server-only/public-api/te
|
||||
import { listDocumentsHandler } from '@documenso/lib/server-only/webhooks/zapier/list-documents';
|
||||
import { subscribeHandler } from '@documenso/lib/server-only/webhooks/zapier/subscribe';
|
||||
import { unsubscribeHandler } from '@documenso/lib/server-only/webhooks/zapier/unsubscribe';
|
||||
// This is a bit nasty. Todo: Extract
|
||||
import type { HonoEnv } from '@documenso/remix/server/router';
|
||||
|
||||
// This is bad, ts-router will be created on each request.
|
||||
// But don't really have a choice here.
|
||||
export const tsRestHonoApp = new Hono<HonoEnv>();
|
||||
export const tsRestHonoApp = new Hono();
|
||||
|
||||
tsRestHonoApp
|
||||
.get('/openapi', (c) => c.redirect('https://openapi-v1.documenso.com'))
|
||||
|
||||
+174
-272
@@ -77,15 +77,9 @@ export const ApiContractV1Implementation = tsr.router(ApiContractV1, {
|
||||
};
|
||||
}),
|
||||
|
||||
getDocument: authenticatedMiddleware(async (args, user, team, { logger }) => {
|
||||
getDocument: authenticatedMiddleware(async (args, user, team) => {
|
||||
const { id: documentId } = args.params;
|
||||
|
||||
logger.info({
|
||||
input: {
|
||||
id: documentId,
|
||||
},
|
||||
});
|
||||
|
||||
try {
|
||||
const document = await getDocumentById({
|
||||
documentId: Number(documentId),
|
||||
@@ -145,16 +139,10 @@ export const ApiContractV1Implementation = tsr.router(ApiContractV1, {
|
||||
}
|
||||
}),
|
||||
|
||||
downloadSignedDocument: authenticatedMiddleware(async (args, user, team, { logger }) => {
|
||||
downloadSignedDocument: authenticatedMiddleware(async (args, user, team) => {
|
||||
const { id: documentId } = args.params;
|
||||
const { downloadOriginalDocument } = args.query;
|
||||
|
||||
logger.info({
|
||||
input: {
|
||||
id: documentId,
|
||||
},
|
||||
});
|
||||
|
||||
try {
|
||||
if (process.env.NEXT_PUBLIC_UPLOAD_TRANSPORT !== 's3') {
|
||||
return {
|
||||
@@ -216,15 +204,9 @@ export const ApiContractV1Implementation = tsr.router(ApiContractV1, {
|
||||
}
|
||||
}),
|
||||
|
||||
deleteDocument: authenticatedMiddleware(async (args, user, team, { logger, metadata }) => {
|
||||
deleteDocument: authenticatedMiddleware(async (args, user, team, { metadata }) => {
|
||||
const { id: documentId } = args.params;
|
||||
|
||||
logger.info({
|
||||
input: {
|
||||
id: documentId,
|
||||
},
|
||||
});
|
||||
|
||||
try {
|
||||
const document = await getDocumentById({
|
||||
documentId: Number(documentId),
|
||||
@@ -400,15 +382,9 @@ export const ApiContractV1Implementation = tsr.router(ApiContractV1, {
|
||||
}
|
||||
}),
|
||||
|
||||
deleteTemplate: authenticatedMiddleware(async (args, user, team, { logger }) => {
|
||||
deleteTemplate: authenticatedMiddleware(async (args, user, team) => {
|
||||
const { id: templateId } = args.params;
|
||||
|
||||
logger.info({
|
||||
input: {
|
||||
id: templateId,
|
||||
},
|
||||
});
|
||||
|
||||
try {
|
||||
const deletedTemplate = await deleteTemplate({
|
||||
id: Number(templateId),
|
||||
@@ -430,15 +406,9 @@ export const ApiContractV1Implementation = tsr.router(ApiContractV1, {
|
||||
}
|
||||
}),
|
||||
|
||||
getTemplate: authenticatedMiddleware(async (args, user, team, { logger }) => {
|
||||
getTemplate: authenticatedMiddleware(async (args, user, team) => {
|
||||
const { id: templateId } = args.params;
|
||||
|
||||
logger.info({
|
||||
input: {
|
||||
id: templateId,
|
||||
},
|
||||
});
|
||||
|
||||
try {
|
||||
const template = await getTemplateById({
|
||||
id: Number(templateId),
|
||||
@@ -493,224 +463,202 @@ export const ApiContractV1Implementation = tsr.router(ApiContractV1, {
|
||||
}
|
||||
}),
|
||||
|
||||
createDocumentFromTemplate: authenticatedMiddleware(
|
||||
async (args, user, team, { logger, metadata }) => {
|
||||
const { body, params } = args;
|
||||
createDocumentFromTemplate: authenticatedMiddleware(async (args, user, team, { metadata }) => {
|
||||
const { body, params } = args;
|
||||
|
||||
logger.info({
|
||||
input: {
|
||||
templateId: params.templateId,
|
||||
const { remaining } = await getServerLimits({ userId: user.id, teamId: team?.id });
|
||||
|
||||
if (remaining.documents <= 0) {
|
||||
return {
|
||||
status: 400,
|
||||
body: {
|
||||
message: 'You have reached the maximum number of documents allowed for this month',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const templateId = Number(params.templateId);
|
||||
|
||||
const fileName = body.title.endsWith('.pdf') ? body.title : `${body.title}.pdf`;
|
||||
|
||||
const document = await createDocumentFromTemplateLegacy({
|
||||
templateId,
|
||||
userId: user.id,
|
||||
teamId: team?.id,
|
||||
recipients: body.recipients,
|
||||
});
|
||||
|
||||
let documentDataId = document.documentDataId;
|
||||
|
||||
if (body.formValues) {
|
||||
const pdf = await getFileServerSide(document.documentData);
|
||||
|
||||
const prefilled = await insertFormValuesInPdf({
|
||||
pdf: Buffer.from(pdf),
|
||||
formValues: body.formValues,
|
||||
});
|
||||
|
||||
const { remaining } = await getServerLimits({ userId: user.id, teamId: team?.id });
|
||||
const newDocumentData = await putPdfFileServerSide({
|
||||
name: fileName,
|
||||
type: 'application/pdf',
|
||||
arrayBuffer: async () => Promise.resolve(prefilled),
|
||||
});
|
||||
|
||||
if (remaining.documents <= 0) {
|
||||
return {
|
||||
status: 400,
|
||||
body: {
|
||||
message: 'You have reached the maximum number of documents allowed for this month',
|
||||
documentDataId = newDocumentData.id;
|
||||
}
|
||||
|
||||
await updateDocument({
|
||||
documentId: document.id,
|
||||
userId: user.id,
|
||||
teamId: team?.id,
|
||||
data: {
|
||||
title: fileName,
|
||||
externalId: body.externalId || null,
|
||||
formValues: body.formValues,
|
||||
documentData: {
|
||||
connect: {
|
||||
id: documentDataId,
|
||||
},
|
||||
};
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const templateId = Number(params.templateId);
|
||||
if (body.meta) {
|
||||
await upsertDocumentMeta({
|
||||
documentId: document.id,
|
||||
userId: user.id,
|
||||
teamId: team?.id,
|
||||
...body.meta,
|
||||
requestMetadata: metadata,
|
||||
});
|
||||
}
|
||||
|
||||
const fileName = body.title.endsWith('.pdf') ? body.title : `${body.title}.pdf`;
|
||||
if (body.authOptions) {
|
||||
await updateDocumentSettings({
|
||||
documentId: document.id,
|
||||
userId: user.id,
|
||||
teamId: team?.id,
|
||||
data: body.authOptions,
|
||||
requestMetadata: metadata,
|
||||
});
|
||||
}
|
||||
|
||||
const document = await createDocumentFromTemplateLegacy({
|
||||
return {
|
||||
status: 200,
|
||||
body: {
|
||||
documentId: document.id,
|
||||
recipients: document.recipients.map((recipient) => ({
|
||||
recipientId: recipient.id,
|
||||
name: recipient.name,
|
||||
email: recipient.email,
|
||||
token: recipient.token,
|
||||
role: recipient.role,
|
||||
signingOrder: recipient.signingOrder,
|
||||
|
||||
signingUrl: `${NEXT_PUBLIC_WEBAPP_URL()}/sign/${recipient.token}`,
|
||||
})),
|
||||
},
|
||||
};
|
||||
}),
|
||||
|
||||
generateDocumentFromTemplate: authenticatedMiddleware(async (args, user, team, { metadata }) => {
|
||||
const { body, params } = args;
|
||||
|
||||
const { remaining } = await getServerLimits({ userId: user.id, teamId: team?.id });
|
||||
|
||||
if (remaining.documents <= 0) {
|
||||
return {
|
||||
status: 400,
|
||||
body: {
|
||||
message: 'You have reached the maximum number of documents allowed for this month',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const templateId = Number(params.templateId);
|
||||
|
||||
let document: Awaited<ReturnType<typeof createDocumentFromTemplate>> | null = null;
|
||||
|
||||
try {
|
||||
document = await createDocumentFromTemplate({
|
||||
templateId,
|
||||
externalId: body.externalId || null,
|
||||
userId: user.id,
|
||||
teamId: team?.id,
|
||||
recipients: body.recipients,
|
||||
prefillFields: body.prefillFields,
|
||||
override: {
|
||||
title: body.title,
|
||||
...body.meta,
|
||||
},
|
||||
requestMetadata: metadata,
|
||||
});
|
||||
} catch (err) {
|
||||
return AppError.toRestAPIError(err);
|
||||
}
|
||||
|
||||
if (body.formValues) {
|
||||
const fileName = document.title.endsWith('.pdf') ? document.title : `${document.title}.pdf`;
|
||||
|
||||
const pdf = await getFileServerSide(document.documentData);
|
||||
|
||||
const prefilled = await insertFormValuesInPdf({
|
||||
pdf: Buffer.from(pdf),
|
||||
formValues: body.formValues,
|
||||
});
|
||||
|
||||
let documentDataId = document.documentDataId;
|
||||
|
||||
if (body.formValues) {
|
||||
const pdf = await getFileServerSide(document.documentData);
|
||||
|
||||
const prefilled = await insertFormValuesInPdf({
|
||||
pdf: Buffer.from(pdf),
|
||||
formValues: body.formValues,
|
||||
});
|
||||
|
||||
const newDocumentData = await putPdfFileServerSide({
|
||||
name: fileName,
|
||||
type: 'application/pdf',
|
||||
arrayBuffer: async () => Promise.resolve(prefilled),
|
||||
});
|
||||
|
||||
documentDataId = newDocumentData.id;
|
||||
}
|
||||
const newDocumentData = await putPdfFileServerSide({
|
||||
name: fileName,
|
||||
type: 'application/pdf',
|
||||
arrayBuffer: async () => Promise.resolve(prefilled),
|
||||
});
|
||||
|
||||
await updateDocument({
|
||||
documentId: document.id,
|
||||
userId: user.id,
|
||||
teamId: team?.id,
|
||||
data: {
|
||||
title: fileName,
|
||||
externalId: body.externalId || null,
|
||||
formValues: body.formValues,
|
||||
documentData: {
|
||||
connect: {
|
||||
id: documentDataId,
|
||||
id: newDocumentData.id,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (body.meta) {
|
||||
await upsertDocumentMeta({
|
||||
documentId: document.id,
|
||||
userId: user.id,
|
||||
teamId: team?.id,
|
||||
...body.meta,
|
||||
requestMetadata: metadata,
|
||||
});
|
||||
}
|
||||
|
||||
if (body.authOptions) {
|
||||
await updateDocumentSettings({
|
||||
documentId: document.id,
|
||||
userId: user.id,
|
||||
teamId: team?.id,
|
||||
data: body.authOptions,
|
||||
requestMetadata: metadata,
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
status: 200,
|
||||
body: {
|
||||
documentId: document.id,
|
||||
recipients: document.recipients.map((recipient) => ({
|
||||
recipientId: recipient.id,
|
||||
name: recipient.name,
|
||||
email: recipient.email,
|
||||
token: recipient.token,
|
||||
role: recipient.role,
|
||||
signingOrder: recipient.signingOrder,
|
||||
|
||||
signingUrl: `${NEXT_PUBLIC_WEBAPP_URL()}/sign/${recipient.token}`,
|
||||
})),
|
||||
},
|
||||
};
|
||||
},
|
||||
),
|
||||
|
||||
generateDocumentFromTemplate: authenticatedMiddleware(
|
||||
async (args, user, team, { logger, metadata }) => {
|
||||
const { body, params } = args;
|
||||
|
||||
logger.info({
|
||||
input: {
|
||||
templateId: params.templateId,
|
||||
},
|
||||
if (body.authOptions) {
|
||||
await updateDocumentSettings({
|
||||
documentId: document.id,
|
||||
userId: user.id,
|
||||
teamId: team?.id,
|
||||
data: body.authOptions,
|
||||
requestMetadata: metadata,
|
||||
});
|
||||
}
|
||||
|
||||
const { remaining } = await getServerLimits({ userId: user.id, teamId: team?.id });
|
||||
return {
|
||||
status: 200,
|
||||
body: {
|
||||
documentId: document.id,
|
||||
recipients: document.recipients.map((recipient) => ({
|
||||
recipientId: recipient.id,
|
||||
name: recipient.name,
|
||||
email: recipient.email,
|
||||
token: recipient.token,
|
||||
role: recipient.role,
|
||||
signingOrder: recipient.signingOrder,
|
||||
signingUrl: `${NEXT_PUBLIC_WEBAPP_URL()}/sign/${recipient.token}`,
|
||||
})),
|
||||
},
|
||||
};
|
||||
}),
|
||||
|
||||
if (remaining.documents <= 0) {
|
||||
return {
|
||||
status: 400,
|
||||
body: {
|
||||
message: 'You have reached the maximum number of documents allowed for this month',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const templateId = Number(params.templateId);
|
||||
|
||||
let document: Awaited<ReturnType<typeof createDocumentFromTemplate>> | null = null;
|
||||
|
||||
try {
|
||||
document = await createDocumentFromTemplate({
|
||||
templateId,
|
||||
externalId: body.externalId || null,
|
||||
userId: user.id,
|
||||
teamId: team?.id,
|
||||
recipients: body.recipients,
|
||||
prefillFields: body.prefillFields,
|
||||
override: {
|
||||
title: body.title,
|
||||
...body.meta,
|
||||
},
|
||||
requestMetadata: metadata,
|
||||
});
|
||||
} catch (err) {
|
||||
return AppError.toRestAPIError(err);
|
||||
}
|
||||
|
||||
if (body.formValues) {
|
||||
const fileName = document.title.endsWith('.pdf') ? document.title : `${document.title}.pdf`;
|
||||
|
||||
const pdf = await getFileServerSide(document.documentData);
|
||||
|
||||
const prefilled = await insertFormValuesInPdf({
|
||||
pdf: Buffer.from(pdf),
|
||||
formValues: body.formValues,
|
||||
});
|
||||
|
||||
const newDocumentData = await putPdfFileServerSide({
|
||||
name: fileName,
|
||||
type: 'application/pdf',
|
||||
arrayBuffer: async () => Promise.resolve(prefilled),
|
||||
});
|
||||
|
||||
await updateDocument({
|
||||
documentId: document.id,
|
||||
userId: user.id,
|
||||
teamId: team?.id,
|
||||
data: {
|
||||
formValues: body.formValues,
|
||||
documentData: {
|
||||
connect: {
|
||||
id: newDocumentData.id,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (body.authOptions) {
|
||||
await updateDocumentSettings({
|
||||
documentId: document.id,
|
||||
userId: user.id,
|
||||
teamId: team?.id,
|
||||
data: body.authOptions,
|
||||
requestMetadata: metadata,
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
status: 200,
|
||||
body: {
|
||||
documentId: document.id,
|
||||
recipients: document.recipients.map((recipient) => ({
|
||||
recipientId: recipient.id,
|
||||
name: recipient.name,
|
||||
email: recipient.email,
|
||||
token: recipient.token,
|
||||
role: recipient.role,
|
||||
signingOrder: recipient.signingOrder,
|
||||
signingUrl: `${NEXT_PUBLIC_WEBAPP_URL()}/sign/${recipient.token}`,
|
||||
})),
|
||||
},
|
||||
};
|
||||
},
|
||||
),
|
||||
|
||||
sendDocument: authenticatedMiddleware(async (args, user, team, { logger, metadata }) => {
|
||||
sendDocument: authenticatedMiddleware(async (args, user, team, { metadata }) => {
|
||||
const { id: documentId } = args.params;
|
||||
const { sendEmail, sendCompletionEmails } = args.body;
|
||||
|
||||
logger.info({
|
||||
input: {
|
||||
id: documentId,
|
||||
},
|
||||
});
|
||||
|
||||
try {
|
||||
const document = await getDocumentById({
|
||||
documentId: Number(documentId),
|
||||
@@ -782,16 +730,10 @@ export const ApiContractV1Implementation = tsr.router(ApiContractV1, {
|
||||
}
|
||||
}),
|
||||
|
||||
resendDocument: authenticatedMiddleware(async (args, user, team, { logger, metadata }) => {
|
||||
resendDocument: authenticatedMiddleware(async (args, user, team, { metadata }) => {
|
||||
const { id: documentId } = args.params;
|
||||
const { recipients } = args.body;
|
||||
|
||||
logger.info({
|
||||
input: {
|
||||
id: documentId,
|
||||
},
|
||||
});
|
||||
|
||||
try {
|
||||
await resendDocument({
|
||||
userId: user.id,
|
||||
@@ -817,16 +759,10 @@ export const ApiContractV1Implementation = tsr.router(ApiContractV1, {
|
||||
}
|
||||
}),
|
||||
|
||||
createRecipient: authenticatedMiddleware(async (args, user, team, { logger, metadata }) => {
|
||||
createRecipient: authenticatedMiddleware(async (args, user, team, { metadata }) => {
|
||||
const { id: documentId } = args.params;
|
||||
const { name, email, role, authOptions, signingOrder } = args.body;
|
||||
|
||||
logger.info({
|
||||
input: {
|
||||
id: documentId,
|
||||
},
|
||||
});
|
||||
|
||||
const document = await getDocumentById({
|
||||
documentId: Number(documentId),
|
||||
userId: user.id,
|
||||
@@ -914,17 +850,10 @@ export const ApiContractV1Implementation = tsr.router(ApiContractV1, {
|
||||
}
|
||||
}),
|
||||
|
||||
updateRecipient: authenticatedMiddleware(async (args, user, team, { logger, metadata }) => {
|
||||
updateRecipient: authenticatedMiddleware(async (args, user, team, { metadata }) => {
|
||||
const { id: documentId, recipientId } = args.params;
|
||||
const { name, email, role, authOptions, signingOrder } = args.body;
|
||||
|
||||
logger.info({
|
||||
input: {
|
||||
id: documentId,
|
||||
recipientId,
|
||||
},
|
||||
});
|
||||
|
||||
const document = await getDocumentById({
|
||||
documentId: Number(documentId),
|
||||
userId: user.id,
|
||||
@@ -987,16 +916,9 @@ export const ApiContractV1Implementation = tsr.router(ApiContractV1, {
|
||||
};
|
||||
}),
|
||||
|
||||
deleteRecipient: authenticatedMiddleware(async (args, user, team, { logger, metadata }) => {
|
||||
deleteRecipient: authenticatedMiddleware(async (args, user, team, { metadata }) => {
|
||||
const { id: documentId, recipientId } = args.params;
|
||||
|
||||
logger.info({
|
||||
input: {
|
||||
id: documentId,
|
||||
recipientId,
|
||||
},
|
||||
});
|
||||
|
||||
const document = await getDocumentById({
|
||||
documentId: Number(documentId),
|
||||
userId: user.id,
|
||||
@@ -1048,15 +970,8 @@ export const ApiContractV1Implementation = tsr.router(ApiContractV1, {
|
||||
};
|
||||
}),
|
||||
|
||||
createField: authenticatedMiddleware(async (args, user, team, { logger, metadata }) => {
|
||||
createField: authenticatedMiddleware(async (args, user, team, { metadata }) => {
|
||||
const { id: documentId } = args.params;
|
||||
|
||||
logger.info({
|
||||
input: {
|
||||
id: documentId,
|
||||
},
|
||||
});
|
||||
|
||||
const fields = Array.isArray(args.body) ? args.body : [args.body];
|
||||
|
||||
const document = await prisma.document.findFirst({
|
||||
@@ -1216,18 +1131,11 @@ export const ApiContractV1Implementation = tsr.router(ApiContractV1, {
|
||||
}
|
||||
}),
|
||||
|
||||
updateField: authenticatedMiddleware(async (args, user, team, { logger, metadata }) => {
|
||||
updateField: authenticatedMiddleware(async (args, user, team, { metadata }) => {
|
||||
const { id: documentId, fieldId } = args.params;
|
||||
const { recipientId, type, pageNumber, pageWidth, pageHeight, pageX, pageY, fieldMeta } =
|
||||
args.body;
|
||||
|
||||
logger.info({
|
||||
input: {
|
||||
id: documentId,
|
||||
fieldId,
|
||||
},
|
||||
});
|
||||
|
||||
const document = await getDocumentById({
|
||||
documentId: Number(documentId),
|
||||
userId: user.id,
|
||||
@@ -1314,16 +1222,9 @@ export const ApiContractV1Implementation = tsr.router(ApiContractV1, {
|
||||
};
|
||||
}),
|
||||
|
||||
deleteField: authenticatedMiddleware(async (args, user, team, { logger, metadata }) => {
|
||||
deleteField: authenticatedMiddleware(async (args, user, team, { metadata }) => {
|
||||
const { id: documentId, fieldId } = args.params;
|
||||
|
||||
logger.info({
|
||||
input: {
|
||||
id: documentId,
|
||||
fieldId,
|
||||
},
|
||||
});
|
||||
|
||||
const document = await getDocumentById({
|
||||
documentId: Number(documentId),
|
||||
userId: user.id,
|
||||
@@ -1432,6 +1333,7 @@ const updateDocument = async ({
|
||||
return await prisma.document.update({
|
||||
where: {
|
||||
id: documentId,
|
||||
userId,
|
||||
team: buildTeamWhereQuery({ teamId, userId }),
|
||||
},
|
||||
data: {
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
import type { Team, User } from '@prisma/client';
|
||||
import type { TsRestRequest } from '@ts-rest/serverless';
|
||||
import type { Logger } from 'pino';
|
||||
|
||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||
import { getApiTokenByToken } from '@documenso/lib/server-only/public-api/get-api-token-by-token';
|
||||
import type { BaseApiLog, RootApiLog } from '@documenso/lib/types/api-logs';
|
||||
import type { ApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
|
||||
import { extractRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
|
||||
import { nanoid } from '@documenso/lib/universal/id';
|
||||
import { logger } from '@documenso/lib/utils/logger';
|
||||
|
||||
type B = {
|
||||
// appRoute: any;
|
||||
@@ -31,24 +27,10 @@ export const authenticatedMiddleware = <
|
||||
args: T & { req: TsRestRequest },
|
||||
user: Pick<User, 'id' | 'email' | 'name' | 'disabled'>,
|
||||
team: Team,
|
||||
options: { metadata: ApiRequestMetadata; logger: Logger },
|
||||
options: { metadata: ApiRequestMetadata },
|
||||
) => Promise<R>,
|
||||
) => {
|
||||
return async (args: T, { request }: B) => {
|
||||
const requestMetadata = extractRequestMetadata(request);
|
||||
|
||||
const apiLogger = logger.child({
|
||||
ipAddress: requestMetadata.ipAddress,
|
||||
userAgent: requestMetadata.userAgent,
|
||||
requestId: nanoid(),
|
||||
} satisfies RootApiLog);
|
||||
|
||||
const infoToLog: BaseApiLog = {
|
||||
auth: 'api',
|
||||
source: 'apiV1',
|
||||
path: request.url,
|
||||
};
|
||||
|
||||
try {
|
||||
const { authorization } = args.headers;
|
||||
|
||||
@@ -69,14 +51,8 @@ export const authenticatedMiddleware = <
|
||||
});
|
||||
}
|
||||
|
||||
apiLogger.info({
|
||||
...infoToLog,
|
||||
userId: apiToken.user.id,
|
||||
apiTokenId: apiToken.id,
|
||||
} satisfies BaseApiLog);
|
||||
|
||||
const metadata: ApiRequestMetadata = {
|
||||
requestMetadata,
|
||||
requestMetadata: extractRequestMetadata(request),
|
||||
source: 'apiV1',
|
||||
auth: 'api',
|
||||
auditUser: {
|
||||
@@ -93,12 +69,10 @@ export const authenticatedMiddleware = <
|
||||
},
|
||||
apiToken.user,
|
||||
apiToken.team,
|
||||
{ metadata, logger: apiLogger },
|
||||
{ metadata },
|
||||
);
|
||||
} catch (err) {
|
||||
console.log({ err });
|
||||
|
||||
apiLogger.info(infoToLog);
|
||||
console.log({ err: err });
|
||||
|
||||
let message = 'Unauthorized';
|
||||
|
||||
|
||||
@@ -1,540 +0,0 @@
|
||||
import { expect, test } from '@playwright/test';
|
||||
|
||||
import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
|
||||
import { createApiToken } from '@documenso/lib/server-only/public-api/create-api-token';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
import { FieldType } from '@documenso/prisma/client';
|
||||
import {
|
||||
seedBlankDocument,
|
||||
seedCompletedDocument,
|
||||
seedDraftDocument,
|
||||
seedPendingDocumentWithFullFields,
|
||||
} from '@documenso/prisma/seed/documents';
|
||||
import { seedBlankTemplate } from '@documenso/prisma/seed/templates';
|
||||
import { seedUser } from '@documenso/prisma/seed/users';
|
||||
|
||||
const WEBAPP_BASE_URL = NEXT_PUBLIC_WEBAPP_URL();
|
||||
|
||||
test.describe.configure({
|
||||
mode: 'parallel',
|
||||
});
|
||||
|
||||
test.describe('Document Access API V1', () => {
|
||||
test('should block unauthorized access to documents not owned by the user', async ({
|
||||
request,
|
||||
}) => {
|
||||
const { user: userA, team: teamA } = await seedUser();
|
||||
|
||||
const { user: userB, team: teamB } = await seedUser();
|
||||
const { token: tokenB } = await createApiToken({
|
||||
userId: userB.id,
|
||||
teamId: teamB.id,
|
||||
tokenName: 'userB',
|
||||
expiresIn: null,
|
||||
});
|
||||
|
||||
const documentA = await seedBlankDocument(userA, teamA.id);
|
||||
|
||||
// User B cannot access User A's document
|
||||
const resB = await request.get(`${WEBAPP_BASE_URL}/api/v1/documents/${documentA.id}`, {
|
||||
headers: { Authorization: `Bearer ${tokenB}` },
|
||||
});
|
||||
|
||||
expect(resB.ok()).toBeFalsy();
|
||||
expect(resB.status()).toBe(404);
|
||||
});
|
||||
|
||||
test('should block unauthorized access to document download endpoint', async ({ request }) => {
|
||||
const { user: userA, team: teamA } = await seedUser();
|
||||
|
||||
const { user: userB, team: teamB } = await seedUser();
|
||||
const { token: tokenB } = await createApiToken({
|
||||
userId: userB.id,
|
||||
teamId: teamB.id,
|
||||
tokenName: 'userB',
|
||||
expiresIn: null,
|
||||
});
|
||||
|
||||
const documentA = await seedCompletedDocument(userA, teamA.id, ['test@example.com'], {
|
||||
createDocumentOptions: { title: 'Document 1 - Completed' },
|
||||
});
|
||||
|
||||
const resB = await request.get(`${WEBAPP_BASE_URL}/api/v1/documents/${documentA.id}/download`, {
|
||||
headers: { Authorization: `Bearer ${tokenB}` },
|
||||
});
|
||||
|
||||
expect(resB.ok()).toBeFalsy();
|
||||
expect(resB.status()).toBe(500);
|
||||
});
|
||||
|
||||
test('should block unauthorized access to document delete endpoint', async ({ request }) => {
|
||||
const { user: userA, team: teamA } = await seedUser();
|
||||
|
||||
const { user: userB, team: teamB } = await seedUser();
|
||||
const { token: tokenB } = await createApiToken({
|
||||
userId: userB.id,
|
||||
teamId: teamB.id,
|
||||
tokenName: 'userB',
|
||||
expiresIn: null,
|
||||
});
|
||||
|
||||
const documentA = await seedBlankDocument(userA, teamA.id);
|
||||
|
||||
const resB = await request.delete(`${WEBAPP_BASE_URL}/api/v1/documents/${documentA.id}`, {
|
||||
headers: { Authorization: `Bearer ${tokenB}` },
|
||||
});
|
||||
|
||||
expect(resB.ok()).toBeFalsy();
|
||||
expect(resB.status()).toBe(404);
|
||||
});
|
||||
|
||||
test('should block unauthorized access to document send endpoint', async ({ request }) => {
|
||||
const { user: userA, team: teamA } = await seedUser();
|
||||
|
||||
const { user: userB, team: teamB } = await seedUser();
|
||||
const { token: tokenB } = await createApiToken({
|
||||
userId: userB.id,
|
||||
teamId: teamB.id,
|
||||
tokenName: 'userB',
|
||||
expiresIn: null,
|
||||
});
|
||||
|
||||
const { document: documentA } = await seedPendingDocumentWithFullFields({
|
||||
owner: userA,
|
||||
recipients: ['test@example.com'],
|
||||
teamId: teamA.id,
|
||||
});
|
||||
|
||||
const resB = await request.post(`${WEBAPP_BASE_URL}/api/v1/documents/${documentA.id}/send`, {
|
||||
headers: { Authorization: `Bearer ${tokenB}` },
|
||||
data: {},
|
||||
});
|
||||
|
||||
expect(resB.ok()).toBeFalsy();
|
||||
expect(resB.status()).toBe(500);
|
||||
});
|
||||
|
||||
test('should block unauthorized access to document resend endpoint', async ({ request }) => {
|
||||
const { user: userA, team: teamA } = await seedUser();
|
||||
|
||||
const { user: userB, team: teamB } = await seedUser();
|
||||
const { token: tokenB } = await createApiToken({
|
||||
userId: userB.id,
|
||||
teamId: teamB.id,
|
||||
tokenName: 'userB',
|
||||
expiresIn: null,
|
||||
});
|
||||
|
||||
const { user: recipientUser } = await seedUser();
|
||||
|
||||
const { document: documentA, recipients } = await seedPendingDocumentWithFullFields({
|
||||
owner: userA,
|
||||
recipients: [recipientUser.email],
|
||||
teamId: teamA.id,
|
||||
});
|
||||
|
||||
const resB = await request.post(`${WEBAPP_BASE_URL}/api/v1/documents/${documentA.id}/resend`, {
|
||||
headers: { Authorization: `Bearer ${tokenB}` },
|
||||
data: {
|
||||
recipients: [recipients[0].id],
|
||||
},
|
||||
});
|
||||
|
||||
expect(resB.ok()).toBeFalsy();
|
||||
expect(resB.status()).toBe(500);
|
||||
});
|
||||
|
||||
test('should block unauthorized access to document recipients endpoint', async ({ request }) => {
|
||||
const { user: userA, team: teamA } = await seedUser();
|
||||
|
||||
const { user: userB, team: teamB } = await seedUser();
|
||||
const { token: tokenB } = await createApiToken({
|
||||
userId: userB.id,
|
||||
teamId: teamB.id,
|
||||
tokenName: 'userB',
|
||||
expiresIn: null,
|
||||
});
|
||||
|
||||
const documentA = await seedBlankDocument(userA, teamA.id);
|
||||
|
||||
const resB = await request.post(
|
||||
`${WEBAPP_BASE_URL}/api/v1/documents/${documentA.id}/recipients`,
|
||||
{
|
||||
headers: { Authorization: `Bearer ${tokenB}` },
|
||||
data: { name: 'Test', email: 'test@example.com' },
|
||||
},
|
||||
);
|
||||
|
||||
expect(resB.ok()).toBeFalsy();
|
||||
expect(resB.status()).toBe(401);
|
||||
});
|
||||
|
||||
test('should block unauthorized access to PATCH on recipients endpoint', async ({ request }) => {
|
||||
const { user: userA, team: teamA } = await seedUser();
|
||||
|
||||
const { user: userB, team: teamB } = await seedUser();
|
||||
const { token: tokenB } = await createApiToken({
|
||||
userId: userB.id,
|
||||
teamId: teamB.id,
|
||||
tokenName: 'userB',
|
||||
expiresIn: null,
|
||||
});
|
||||
|
||||
const { user: userRecipient } = await seedUser();
|
||||
|
||||
const documentA = await seedDraftDocument(userA, teamA.id, [userRecipient.email]);
|
||||
|
||||
const recipient = await prisma.recipient.findFirst({
|
||||
where: {
|
||||
documentId: documentA.id,
|
||||
email: userRecipient.email,
|
||||
},
|
||||
});
|
||||
|
||||
const patchRes = await request.patch(
|
||||
`${WEBAPP_BASE_URL}/api/v1/documents/${documentA.id}/recipients/${recipient!.id}`,
|
||||
{
|
||||
headers: { Authorization: `Bearer ${tokenB}` },
|
||||
data: {
|
||||
name: 'New Name',
|
||||
email: 'new@example.com',
|
||||
role: 'SIGNER',
|
||||
signingOrder: null,
|
||||
authOptions: {
|
||||
accessAuth: [],
|
||||
actionAuth: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
expect(patchRes.ok()).toBeFalsy();
|
||||
expect(patchRes.status()).toBe(401);
|
||||
});
|
||||
|
||||
test('should block unauthorized access to DELETE on recipients endpoint', async ({ request }) => {
|
||||
const { user: userA, team: teamA } = await seedUser();
|
||||
|
||||
const { user: userB, team: teamB } = await seedUser();
|
||||
const { token: tokenB } = await createApiToken({
|
||||
userId: userB.id,
|
||||
teamId: teamB.id,
|
||||
tokenName: 'userB',
|
||||
expiresIn: null,
|
||||
});
|
||||
|
||||
const { user: userRecipient } = await seedUser();
|
||||
|
||||
const documentA = await seedDraftDocument(userA, teamA.id, [userRecipient.email]);
|
||||
|
||||
const recipient = await prisma.recipient.findFirst({
|
||||
where: {
|
||||
documentId: documentA.id,
|
||||
email: userRecipient.email,
|
||||
},
|
||||
});
|
||||
|
||||
const deleteRes = await request.delete(
|
||||
`${WEBAPP_BASE_URL}/api/v1/documents/${documentA.id}/recipients/${recipient!.id}`,
|
||||
{
|
||||
headers: { Authorization: `Bearer ${tokenB}` },
|
||||
data: {},
|
||||
},
|
||||
);
|
||||
|
||||
expect(deleteRes.ok()).toBeFalsy();
|
||||
expect(deleteRes.status()).toBe(401);
|
||||
});
|
||||
|
||||
test('should block unauthorized access to document fields endpoint', async ({ request }) => {
|
||||
const { user: userA, team: teamA } = await seedUser();
|
||||
|
||||
const { user: userB, team: teamB } = await seedUser();
|
||||
const { token: tokenB } = await createApiToken({
|
||||
userId: userB.id,
|
||||
teamId: teamB.id,
|
||||
tokenName: 'userB',
|
||||
expiresIn: null,
|
||||
});
|
||||
|
||||
const { user: recipientUser } = await seedUser();
|
||||
|
||||
const documentA = await seedDraftDocument(userA, teamA.id, [recipientUser.email]);
|
||||
|
||||
const documentRecipient = await prisma.recipient.findFirst({
|
||||
where: {
|
||||
documentId: documentA.id,
|
||||
email: recipientUser.email,
|
||||
},
|
||||
});
|
||||
|
||||
const resB = await request.post(`${WEBAPP_BASE_URL}/api/v1/documents/${documentA.id}/fields`, {
|
||||
headers: { Authorization: `Bearer ${tokenB}` },
|
||||
data: {
|
||||
recipientId: documentRecipient!.id,
|
||||
type: 'SIGNATURE',
|
||||
pageNumber: 1,
|
||||
pageX: 1,
|
||||
pageY: 1,
|
||||
pageWidth: 1,
|
||||
pageHeight: 1,
|
||||
},
|
||||
});
|
||||
|
||||
expect(resB.ok()).toBeFalsy();
|
||||
expect(resB.status()).toBe(404);
|
||||
});
|
||||
|
||||
test('should block unauthorized access to template get endpoint', async ({ request }) => {
|
||||
const { user: userA, team: teamA } = await seedUser();
|
||||
|
||||
const { user: userB, team: teamB } = await seedUser();
|
||||
const { token: tokenB } = await createApiToken({
|
||||
userId: userB.id,
|
||||
teamId: teamB.id,
|
||||
tokenName: 'userB',
|
||||
expiresIn: null,
|
||||
});
|
||||
|
||||
const templateA = await seedBlankTemplate(userA, teamA.id);
|
||||
|
||||
const resB = await request.get(`${WEBAPP_BASE_URL}/api/v1/templates/${templateA.id}`, {
|
||||
headers: { Authorization: `Bearer ${tokenB}` },
|
||||
});
|
||||
|
||||
expect(resB.ok()).toBeFalsy();
|
||||
expect(resB.status()).toBe(404);
|
||||
});
|
||||
|
||||
test('should block unauthorized access to template delete endpoint', async ({ request }) => {
|
||||
const { user: userA, team: teamA } = await seedUser();
|
||||
|
||||
const { user: userB, team: teamB } = await seedUser();
|
||||
const { token: tokenB } = await createApiToken({
|
||||
userId: userB.id,
|
||||
teamId: teamB.id,
|
||||
tokenName: 'userB',
|
||||
expiresIn: null,
|
||||
});
|
||||
|
||||
const templateA = await seedBlankTemplate(userA, teamA.id);
|
||||
|
||||
const resB = await request.delete(`${WEBAPP_BASE_URL}/api/v1/templates/${templateA.id}`, {
|
||||
headers: { Authorization: `Bearer ${tokenB}` },
|
||||
});
|
||||
|
||||
expect(resB.ok()).toBeFalsy();
|
||||
expect(resB.status()).toBe(404);
|
||||
});
|
||||
|
||||
test('should block unauthorized access to PATCH on fields endpoint', async ({ request }) => {
|
||||
const { user: userA, team: teamA } = await seedUser();
|
||||
|
||||
const { user: userB, team: teamB } = await seedUser();
|
||||
const { token: tokenB } = await createApiToken({
|
||||
userId: userB.id,
|
||||
teamId: teamB.id,
|
||||
tokenName: 'userB',
|
||||
expiresIn: null,
|
||||
});
|
||||
|
||||
const { user: userRecipient } = await seedUser();
|
||||
const documentA = await seedDraftDocument(userA, teamA.id, [userRecipient.email]);
|
||||
|
||||
const recipient = await prisma.recipient.findFirst({
|
||||
where: {
|
||||
documentId: documentA.id,
|
||||
email: userRecipient.email,
|
||||
},
|
||||
});
|
||||
|
||||
const field = await prisma.field.create({
|
||||
data: {
|
||||
documentId: documentA.id,
|
||||
recipientId: recipient!.id,
|
||||
type: FieldType.TEXT,
|
||||
page: 1,
|
||||
positionX: 5,
|
||||
positionY: 5,
|
||||
width: 10,
|
||||
height: 5,
|
||||
customText: '',
|
||||
inserted: false,
|
||||
fieldMeta: {
|
||||
type: 'text',
|
||||
label: 'Default Text Field',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const patchRes = await request.patch(
|
||||
`${WEBAPP_BASE_URL}/api/v1/documents/${documentA.id}/fields/${field.id}`,
|
||||
{
|
||||
headers: { Authorization: `Bearer ${tokenB}` },
|
||||
data: {
|
||||
recipientId: recipient!.id,
|
||||
type: FieldType.TEXT,
|
||||
pageNumber: 1,
|
||||
pageX: 99,
|
||||
pageY: 99,
|
||||
pageWidth: 99,
|
||||
pageHeight: 99,
|
||||
fieldMeta: {
|
||||
type: 'text',
|
||||
label: 'My new field',
|
||||
},
|
||||
},
|
||||
},
|
||||
);
|
||||
expect(patchRes.ok()).toBeFalsy();
|
||||
expect(patchRes.status()).toBe(401);
|
||||
});
|
||||
|
||||
test('should block unauthorized access to DELETE on fields endpoint', async ({ request }) => {
|
||||
const { user: userA, team: teamA } = await seedUser();
|
||||
|
||||
const { user: userB, team: teamB } = await seedUser();
|
||||
const { token: tokenB } = await createApiToken({
|
||||
userId: userB.id,
|
||||
teamId: teamB.id,
|
||||
tokenName: 'userB',
|
||||
expiresIn: null,
|
||||
});
|
||||
|
||||
const { user: userRecipient } = await seedUser();
|
||||
const documentA = await seedDraftDocument(userA, teamA.id, [userRecipient.email]);
|
||||
|
||||
const recipient = await prisma.recipient.findFirst({
|
||||
where: {
|
||||
documentId: documentA.id,
|
||||
email: userRecipient.email,
|
||||
},
|
||||
});
|
||||
|
||||
const field = await prisma.field.create({
|
||||
data: {
|
||||
documentId: documentA.id,
|
||||
recipientId: recipient!.id,
|
||||
type: FieldType.NUMBER,
|
||||
page: 1,
|
||||
positionX: 5,
|
||||
positionY: 5,
|
||||
width: 10,
|
||||
height: 5,
|
||||
customText: '',
|
||||
inserted: false,
|
||||
fieldMeta: {
|
||||
type: 'number',
|
||||
label: 'Default Number Field',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const deleteRes = await request.delete(
|
||||
`${WEBAPP_BASE_URL}/api/v1/documents/${documentA.id}/fields/${field.id}`,
|
||||
{
|
||||
headers: { Authorization: `Bearer ${tokenB}` },
|
||||
data: {},
|
||||
},
|
||||
);
|
||||
|
||||
expect(deleteRes.ok()).toBeFalsy();
|
||||
expect(deleteRes.status()).toBe(401);
|
||||
});
|
||||
|
||||
test('should block unauthorized access to documents list endpoint', async ({ request }) => {
|
||||
const { user: userA, team: teamA } = await seedUser();
|
||||
|
||||
const { user: userB, team: teamB } = await seedUser();
|
||||
const { token: tokenB } = await createApiToken({
|
||||
userId: userB.id,
|
||||
teamId: teamB.id,
|
||||
tokenName: 'userB',
|
||||
expiresIn: null,
|
||||
});
|
||||
|
||||
await seedBlankDocument(userA, teamA.id);
|
||||
|
||||
const resB = await request.get(`${WEBAPP_BASE_URL}/api/v1/documents`, {
|
||||
headers: { Authorization: `Bearer ${tokenB}` },
|
||||
});
|
||||
|
||||
const reqData = await resB.json();
|
||||
|
||||
expect(resB.ok()).toBeTruthy();
|
||||
expect(resB.status()).toBe(200);
|
||||
expect(reqData.documents.every((doc: { userId: number }) => doc.userId !== userA.id)).toBe(
|
||||
true,
|
||||
);
|
||||
expect(reqData.documents.length).toBe(0);
|
||||
expect(reqData.totalPages).toBe(0);
|
||||
});
|
||||
|
||||
test('should block unauthorized access to templates list endpoint', async ({ request }) => {
|
||||
const { user: userA, team: teamA } = await seedUser();
|
||||
|
||||
const { user: userB, team: teamB } = await seedUser();
|
||||
const { token: tokenB } = await createApiToken({
|
||||
userId: userB.id,
|
||||
teamId: teamB.id,
|
||||
tokenName: 'userB',
|
||||
expiresIn: null,
|
||||
});
|
||||
|
||||
await seedBlankTemplate(userA, teamA.id);
|
||||
|
||||
const resB = await request.get(`${WEBAPP_BASE_URL}/api/v1/templates`, {
|
||||
headers: { Authorization: `Bearer ${tokenB}` },
|
||||
});
|
||||
|
||||
const reqData = await resB.json();
|
||||
|
||||
expect(resB.ok()).toBeTruthy();
|
||||
expect(resB.status()).toBe(200);
|
||||
expect(reqData.templates.every((tpl: { userId: number }) => tpl.userId !== userA.id)).toBe(
|
||||
true,
|
||||
);
|
||||
expect(reqData.templates.length).toBe(0);
|
||||
expect(reqData.totalPages).toBe(0);
|
||||
});
|
||||
|
||||
test('should block unauthorized access to create-document-from-template endpoint', async ({
|
||||
request,
|
||||
}) => {
|
||||
const { user: userA, team: teamA } = await seedUser();
|
||||
|
||||
const { user: userB, team: teamB } = await seedUser();
|
||||
const { token: tokenB } = await createApiToken({
|
||||
userId: userB.id,
|
||||
teamId: teamB.id,
|
||||
tokenName: 'userB',
|
||||
expiresIn: null,
|
||||
});
|
||||
|
||||
const templateA = await seedBlankTemplate(userA, teamA.id);
|
||||
|
||||
const resB = await request.post(
|
||||
`${WEBAPP_BASE_URL}/api/v1/templates/${templateA.id}/create-document`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${tokenB}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
data: {
|
||||
title: 'Should not work',
|
||||
recipients: [{ name: 'Test user', email: 'test@example.com' }],
|
||||
meta: {
|
||||
subject: 'Test',
|
||||
message: 'Test',
|
||||
timezone: 'UTC',
|
||||
dateFormat: 'yyyy-MM-dd',
|
||||
redirectUrl: 'https://example.com',
|
||||
},
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
expect(resB.ok()).toBeFalsy();
|
||||
expect(resB.status()).toBe(401);
|
||||
});
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,100 +0,0 @@
|
||||
import { expect, test } from '@playwright/test';
|
||||
|
||||
import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
|
||||
import {
|
||||
seedBlankDocument,
|
||||
seedCompletedDocument,
|
||||
seedPendingDocument,
|
||||
} from '@documenso/prisma/seed/documents';
|
||||
import { seedUser } from '@documenso/prisma/seed/users';
|
||||
|
||||
import { apiSignin } from '../fixtures/authentication';
|
||||
|
||||
test.describe.configure({
|
||||
mode: 'parallel',
|
||||
});
|
||||
|
||||
test.describe('Unauthorized Access to Documents', () => {
|
||||
test('should block unauthorized access to the draft document page', async ({ page }) => {
|
||||
const { user, team } = await seedUser();
|
||||
const document = await seedBlankDocument(user, team.id);
|
||||
|
||||
const { user: unauthorizedUser } = await seedUser();
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: unauthorizedUser.email,
|
||||
redirectPath: `/t/${team.url}/documents`,
|
||||
});
|
||||
|
||||
await page.goto(`${NEXT_PUBLIC_WEBAPP_URL()}/t/${team.url}/documents/${document.id}`);
|
||||
await expect(page.getByRole('heading', { name: 'Oops! Something went wrong.' })).toBeVisible();
|
||||
});
|
||||
|
||||
test('should block unauthorized access to the draft document edit page', async ({ page }) => {
|
||||
const { user, team } = await seedUser();
|
||||
const document = await seedBlankDocument(user, team.id);
|
||||
|
||||
const { user: unauthorizedUser } = await seedUser();
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: unauthorizedUser.email,
|
||||
redirectPath: `/t/${team.url}/documents/${document.id}/edit`,
|
||||
});
|
||||
|
||||
await page.goto(`${NEXT_PUBLIC_WEBAPP_URL()}/t/${team.url}/documents/${document.id}/edit`);
|
||||
await expect(page.getByRole('heading', { name: 'Oops! Something went wrong.' })).toBeVisible();
|
||||
});
|
||||
|
||||
test('should block unauthorized access to the pending document page', async ({ page }) => {
|
||||
const { user, team } = await seedUser();
|
||||
const { user: recipient } = await seedUser();
|
||||
const document = await seedPendingDocument(user, team.id, [recipient]);
|
||||
|
||||
const { user: unauthorizedUser } = await seedUser();
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: unauthorizedUser.email,
|
||||
redirectPath: `/t/${team.url}/documents/${document.id}`,
|
||||
});
|
||||
|
||||
await page.goto(`${NEXT_PUBLIC_WEBAPP_URL()}/t/${team.url}/documents/${document.id}`);
|
||||
await expect(page.getByRole('heading', { name: 'Oops! Something went wrong.' })).toBeVisible();
|
||||
});
|
||||
|
||||
test('should block unauthorized access to pending document edit page', async ({ page }) => {
|
||||
const { user, team } = await seedUser();
|
||||
const { user: recipient } = await seedUser();
|
||||
const document = await seedPendingDocument(user, team.id, [recipient]);
|
||||
|
||||
const { user: unauthorizedUser } = await seedUser();
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: unauthorizedUser.email,
|
||||
redirectPath: `/t/${team.url}/documents/${document.id}/edit`,
|
||||
});
|
||||
|
||||
await page.goto(`${NEXT_PUBLIC_WEBAPP_URL()}/t/${team.url}/documents/${document.id}/edit`);
|
||||
await expect(page.getByRole('heading', { name: 'Oops! Something went wrong.' })).toBeVisible();
|
||||
});
|
||||
|
||||
test('should block unauthorized access to completed document page', async ({ page }) => {
|
||||
const { user, team } = await seedUser();
|
||||
const { user: recipient } = await seedUser();
|
||||
const document = await seedCompletedDocument(user, team.id, [recipient]);
|
||||
|
||||
const { user: unauthorizedUser } = await seedUser();
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: unauthorizedUser.email,
|
||||
redirectPath: `/t/${team.url}/documents/${document.id}`,
|
||||
});
|
||||
|
||||
await page.goto(`${NEXT_PUBLIC_WEBAPP_URL()}/t/${team.url}/documents/${document.id}`);
|
||||
await expect(page.getByRole('heading', { name: 'Oops! Something went wrong.' })).toBeVisible();
|
||||
});
|
||||
});
|
||||
@@ -168,7 +168,7 @@ test('[TEAMS]: can rename a document folder', async ({ page }) => {
|
||||
await page.getByRole('menuitem', { name: 'Settings' }).click();
|
||||
|
||||
await page.getByLabel('Name').fill('Team Archive');
|
||||
await page.getByRole('button', { name: 'Update' }).click();
|
||||
await page.getByRole('button', { name: 'Save Changes' }).click();
|
||||
|
||||
await expect(page.getByText('Team Archive')).toBeVisible();
|
||||
});
|
||||
@@ -470,7 +470,7 @@ test('[TEAMS]: can rename a template folder', async ({ page }) => {
|
||||
await page.getByRole('menuitem', { name: 'Settings' }).click();
|
||||
|
||||
await page.getByLabel('Name').fill('Updated Team Template Folder');
|
||||
await page.getByRole('button', { name: 'Update' }).click();
|
||||
await page.getByRole('button', { name: 'Save Changes' }).click();
|
||||
|
||||
await expect(page.getByText('Updated Team Template Folder')).toBeVisible();
|
||||
});
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
import { expect, test } from '@playwright/test';
|
||||
|
||||
import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
|
||||
import { seedBlankTemplate } from '@documenso/prisma/seed/templates';
|
||||
import { seedUser } from '@documenso/prisma/seed/users';
|
||||
|
||||
import { apiSignin } from '../fixtures/authentication';
|
||||
|
||||
test.describe.configure({
|
||||
mode: 'parallel',
|
||||
});
|
||||
|
||||
test.describe('Unauthorized Access to Templates', () => {
|
||||
test('should block unauthorized access to the template page', async ({ page }) => {
|
||||
const { user, team } = await seedUser();
|
||||
const template = await seedBlankTemplate(user, team.id);
|
||||
|
||||
const { user: unauthorizedUser } = await seedUser();
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: unauthorizedUser.email,
|
||||
redirectPath: `/t/${team.url}/templates/${template.id}`,
|
||||
});
|
||||
|
||||
await page.goto(`${NEXT_PUBLIC_WEBAPP_URL()}/t/${team.url}/templates/${template.id}`);
|
||||
await expect(page.getByRole('heading', { name: 'Oops! Something went wrong.' })).toBeVisible();
|
||||
});
|
||||
|
||||
test('should block unauthorized access to the template edit page', async ({ page }) => {
|
||||
const { user, team } = await seedUser();
|
||||
const template = await seedBlankTemplate(user, team.id);
|
||||
|
||||
const { user: unauthorizedUser } = await seedUser();
|
||||
|
||||
await apiSignin({
|
||||
page,
|
||||
email: unauthorizedUser.email,
|
||||
redirectPath: `/t/${team.url}/templates/${template.id}/edit`,
|
||||
});
|
||||
|
||||
await page.goto(`${NEXT_PUBLIC_WEBAPP_URL()}/t/${team.url}/templates/${template.id}/edit`);
|
||||
await expect(page.getByRole('heading', { name: 'Oops! Something went wrong.' })).toBeVisible();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,13 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
export const ZEarlyAdopterCheckoutMetadataSchema = z.object({
|
||||
name: z.string(),
|
||||
email: z.string(),
|
||||
signatureText: z.string(),
|
||||
signatureDataUrl: z.string().optional(),
|
||||
source: z.literal('marketing'),
|
||||
});
|
||||
|
||||
export type TEarlyAdopterCheckoutMetadataSchema = z.infer<
|
||||
typeof ZEarlyAdopterCheckoutMetadataSchema
|
||||
>;
|
||||
@@ -81,34 +81,17 @@ export const onSubscriptionCreated = async ({ subscription }: OnSubscriptionCrea
|
||||
|
||||
const status = match(subscription.status)
|
||||
.with('active', () => SubscriptionStatus.ACTIVE)
|
||||
.with('trialing', () => SubscriptionStatus.ACTIVE)
|
||||
.with('past_due', () => SubscriptionStatus.PAST_DUE)
|
||||
.otherwise(() => SubscriptionStatus.INACTIVE);
|
||||
|
||||
const periodEnd =
|
||||
subscription.status === 'trialing' && subscription.trial_end
|
||||
? new Date(subscription.trial_end * 1000)
|
||||
: new Date(subscription.current_period_end * 1000);
|
||||
|
||||
await prisma.subscription.upsert({
|
||||
where: {
|
||||
organisationId,
|
||||
},
|
||||
create: {
|
||||
await prisma.subscription.create({
|
||||
data: {
|
||||
organisationId,
|
||||
status,
|
||||
customerId,
|
||||
planId: subscription.id,
|
||||
priceId: subscription.items.data[0].price.id,
|
||||
periodEnd,
|
||||
cancelAtPeriodEnd: subscription.cancel_at_period_end,
|
||||
},
|
||||
update: {
|
||||
status,
|
||||
customerId,
|
||||
planId: subscription.id,
|
||||
priceId: subscription.items.data[0].price.id,
|
||||
periodEnd,
|
||||
periodEnd: new Date(subscription.current_period_end * 1000),
|
||||
cancelAtPeriodEnd: subscription.cancel_at_period_end,
|
||||
},
|
||||
});
|
||||
@@ -189,17 +172,14 @@ const handleOrganisationUpdate = async ({ customerId, claim }: HandleOrganisatio
|
||||
}
|
||||
|
||||
// Todo: logging
|
||||
if (
|
||||
organisation.subscription &&
|
||||
organisation.subscription.status !== SubscriptionStatus.INACTIVE
|
||||
) {
|
||||
console.error('Organisation already has an active subscription');
|
||||
if (organisation.subscription) {
|
||||
console.error('Organisation already has a subscription');
|
||||
|
||||
// This should never happen
|
||||
throw Response.json(
|
||||
{
|
||||
success: false,
|
||||
message: `Organisation already has an active subscription`,
|
||||
message: `Organisation already has a subscription`,
|
||||
} satisfies StripeWebhookResponse,
|
||||
{ status: 500 },
|
||||
);
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { OrganisationType, SubscriptionStatus } from '@prisma/client';
|
||||
import { SubscriptionStatus } from '@prisma/client';
|
||||
import { match } from 'ts-pattern';
|
||||
|
||||
import { createOrganisationClaimUpsertData } from '@documenso/lib/server-only/organisation/create-organisation';
|
||||
import { type Stripe, stripe } from '@documenso/lib/server-only/stripe';
|
||||
import { INTERNAL_CLAIM_ID } from '@documenso/lib/types/subscription';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
|
||||
export type OnSubscriptionUpdatedOptions = {
|
||||
@@ -56,12 +55,8 @@ export const onSubscriptionUpdated = async ({
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
organisation.subscription &&
|
||||
organisation.subscription.status !== SubscriptionStatus.INACTIVE &&
|
||||
organisation.subscription.planId !== subscription.id
|
||||
) {
|
||||
console.error('[WARNING]: Organisation might have two subscriptions');
|
||||
if (organisation.subscription?.planId !== subscription.id) {
|
||||
console.error('[WARNING]: Organisation has two subscriptions');
|
||||
}
|
||||
|
||||
const previousItem = previousAttributes?.items?.data[0];
|
||||
@@ -88,41 +83,20 @@ export const onSubscriptionUpdated = async ({
|
||||
|
||||
const status = match(subscription.status)
|
||||
.with('active', () => SubscriptionStatus.ACTIVE)
|
||||
.with('trialing', () => SubscriptionStatus.ACTIVE)
|
||||
.with('past_due', () => SubscriptionStatus.PAST_DUE)
|
||||
.otherwise(() => SubscriptionStatus.INACTIVE);
|
||||
|
||||
const periodEnd =
|
||||
subscription.status === 'trialing' && subscription.trial_end
|
||||
? new Date(subscription.trial_end * 1000)
|
||||
: new Date(subscription.current_period_end * 1000);
|
||||
|
||||
// Migrate the organisation type if it is no longer an individual plan.
|
||||
if (
|
||||
updatedSubscriptionClaim.id !== INTERNAL_CLAIM_ID.INDIVIDUAL &&
|
||||
updatedSubscriptionClaim.id !== INTERNAL_CLAIM_ID.FREE &&
|
||||
organisation.type === OrganisationType.PERSONAL
|
||||
) {
|
||||
await prisma.organisation.update({
|
||||
where: {
|
||||
id: organisation.id,
|
||||
},
|
||||
data: {
|
||||
type: OrganisationType.ORGANISATION,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
await prisma.$transaction(async (tx) => {
|
||||
await tx.subscription.update({
|
||||
where: {
|
||||
organisationId: organisation.id,
|
||||
planId: subscription.id,
|
||||
},
|
||||
data: {
|
||||
organisationId: organisation.id,
|
||||
status: status,
|
||||
planId: subscription.id,
|
||||
priceId: subscription.items.data[0].price.id,
|
||||
periodEnd,
|
||||
periodEnd: new Date(subscription.current_period_end * 1000),
|
||||
cancelAtPeriodEnd: subscription.cancel_at_period_end,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { getBoundingClientRect } from '@documenso/lib/client-only/get-bounding-client-rect';
|
||||
|
||||
@@ -10,7 +10,7 @@ export const useElementBounds = (elementOrSelector: HTMLElement | string, withSc
|
||||
width: 0,
|
||||
});
|
||||
|
||||
const calculateBounds = useCallback(() => {
|
||||
const calculateBounds = () => {
|
||||
const $el =
|
||||
typeof elementOrSelector === 'string'
|
||||
? document.querySelector<HTMLElement>(elementOrSelector)
|
||||
@@ -32,11 +32,11 @@ export const useElementBounds = (elementOrSelector: HTMLElement | string, withSc
|
||||
width,
|
||||
height,
|
||||
};
|
||||
}, [elementOrSelector, withScroll]);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setBounds(calculateBounds());
|
||||
}, []);
|
||||
}, [calculateBounds]);
|
||||
|
||||
useEffect(() => {
|
||||
const onResize = () => {
|
||||
@@ -48,7 +48,7 @@ export const useElementBounds = (elementOrSelector: HTMLElement | string, withSc
|
||||
return () => {
|
||||
window.removeEventListener('resize', onResize);
|
||||
};
|
||||
}, []);
|
||||
}, [calculateBounds]);
|
||||
|
||||
useEffect(() => {
|
||||
const $el =
|
||||
@@ -69,7 +69,7 @@ export const useElementBounds = (elementOrSelector: HTMLElement | string, withSc
|
||||
return () => {
|
||||
observer.disconnect();
|
||||
};
|
||||
}, []);
|
||||
}, [calculateBounds]);
|
||||
|
||||
return bounds;
|
||||
};
|
||||
|
||||
@@ -12,5 +12,3 @@ export const NEXT_PRIVATE_INTERNAL_WEBAPP_URL =
|
||||
export const IS_BILLING_ENABLED = () => env('NEXT_PUBLIC_FEATURE_BILLING_ENABLED') === 'true';
|
||||
|
||||
export const API_V2_BETA_URL = '/api/v2-beta';
|
||||
|
||||
export const SUPPORT_EMAIL = 'support@documenso.com';
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
import { FieldType } from '@prisma/client';
|
||||
|
||||
export const AUTO_SIGNABLE_FIELD_TYPES: FieldType[] = [
|
||||
FieldType.NAME,
|
||||
FieldType.INITIALS,
|
||||
FieldType.EMAIL,
|
||||
FieldType.DATE,
|
||||
];
|
||||
@@ -23,9 +23,11 @@
|
||||
"@documenso/email": "*",
|
||||
"@documenso/prisma": "*",
|
||||
"@documenso/signing": "*",
|
||||
"@honeybadger-io/js": "^6.10.1",
|
||||
"@lingui/core": "^5.2.0",
|
||||
"@lingui/macro": "^5.2.0",
|
||||
"@lingui/react": "^5.2.0",
|
||||
"jose": "^6.0.0",
|
||||
"@noble/ciphers": "0.4.0",
|
||||
"@noble/hashes": "1.3.2",
|
||||
"@node-rs/bcrypt": "^1.10.0",
|
||||
@@ -35,7 +37,6 @@
|
||||
"@vvo/tzdb": "^6.117.0",
|
||||
"csv-parse": "^5.6.0",
|
||||
"inngest": "^3.19.13",
|
||||
"jose": "^6.0.0",
|
||||
"kysely": "0.26.3",
|
||||
"luxon": "^3.4.0",
|
||||
"micro": "^10.0.1",
|
||||
@@ -43,8 +44,6 @@
|
||||
"oslo": "^0.17.0",
|
||||
"pdf-lib": "^1.17.1",
|
||||
"pg": "^8.11.3",
|
||||
"pino": "^9.7.0",
|
||||
"pino-pretty": "^13.0.0",
|
||||
"playwright": "1.52.0",
|
||||
"posthog-js": "^1.245.0",
|
||||
"posthog-node": "^4.17.0",
|
||||
@@ -60,4 +59,4 @@
|
||||
"@types/luxon": "^3.3.1",
|
||||
"@types/pg": "^8.11.4"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,6 @@ import { prefixedId } from '../../universal/id';
|
||||
import { getFileServerSide } from '../../universal/upload/get-file.server';
|
||||
import { putPdfFileServerSide } from '../../universal/upload/put-file.server';
|
||||
import { determineDocumentVisibility } from '../../utils/document-visibility';
|
||||
import { buildTeamWhereQuery } from '../../utils/teams';
|
||||
import { getTeamById } from '../team/get-team';
|
||||
import { getTeamSettings } from '../team/get-team-settings';
|
||||
import { triggerWebhook } from '../webhooks/trigger/trigger-webhook';
|
||||
@@ -59,10 +58,8 @@ export const createDocument = async ({
|
||||
const folder = await prisma.folder.findFirst({
|
||||
where: {
|
||||
id: folderId,
|
||||
team: buildTeamWhereQuery({
|
||||
teamId,
|
||||
userId,
|
||||
}),
|
||||
userId,
|
||||
teamId,
|
||||
},
|
||||
select: {
|
||||
visibility: true,
|
||||
|
||||
@@ -47,6 +47,7 @@ export const duplicateDocument = async ({
|
||||
documentMeta: true,
|
||||
recipients: {
|
||||
select: {
|
||||
id: true,
|
||||
email: true,
|
||||
name: true,
|
||||
role: true,
|
||||
|
||||
+9
-7
@@ -54,7 +54,14 @@ export const resendDocument = async ({
|
||||
const document = await prisma.document.findUnique({
|
||||
where: documentWhereInput,
|
||||
include: {
|
||||
recipients: true,
|
||||
recipients: {
|
||||
where: {
|
||||
id: {
|
||||
in: recipients,
|
||||
},
|
||||
signingStatus: SigningStatus.NOT_SIGNED,
|
||||
},
|
||||
},
|
||||
documentMeta: true,
|
||||
team: {
|
||||
select: {
|
||||
@@ -83,11 +90,6 @@ export const resendDocument = async ({
|
||||
throw new Error('Can not send completed document');
|
||||
}
|
||||
|
||||
const recipientsToRemind = document.recipients.filter(
|
||||
(recipient) =>
|
||||
recipients.includes(recipient.id) && recipient.signingStatus === SigningStatus.NOT_SIGNED,
|
||||
);
|
||||
|
||||
const isRecipientSigningRequestEmailEnabled = extractDerivedDocumentEmailSettings(
|
||||
document.documentMeta,
|
||||
).recipientSigningRequest;
|
||||
@@ -104,7 +106,7 @@ export const resendDocument = async ({
|
||||
});
|
||||
|
||||
await Promise.all(
|
||||
recipientsToRemind.map(async (recipient) => {
|
||||
document.recipients.map(async (recipient) => {
|
||||
if (recipient.role === RecipientRole.CC) {
|
||||
return;
|
||||
}
|
||||
@@ -117,12 +117,7 @@ export const sealDocument = async ({
|
||||
? await getCertificatePdf({
|
||||
documentId,
|
||||
language: document.documentMeta?.language,
|
||||
}).catch((e) => {
|
||||
console.log('Failed to get certificate PDF');
|
||||
console.error(e);
|
||||
|
||||
return null;
|
||||
})
|
||||
}).catch(() => null)
|
||||
: null;
|
||||
|
||||
const doc = await PDFDocument.load(pdfData);
|
||||
|
||||
@@ -27,6 +27,7 @@ export const viewedDocument = async ({
|
||||
const recipient = await prisma.recipient.findFirst({
|
||||
where: {
|
||||
token,
|
||||
readStatus: ReadStatus.NOT_OPENED,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -36,30 +37,6 @@ export const viewedDocument = async ({
|
||||
|
||||
const { documentId } = recipient;
|
||||
|
||||
await prisma.documentAuditLog.create({
|
||||
data: createDocumentAuditLogData({
|
||||
type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_VIEWED,
|
||||
documentId,
|
||||
user: {
|
||||
name: recipient.name,
|
||||
email: recipient.email,
|
||||
},
|
||||
requestMetadata,
|
||||
data: {
|
||||
recipientEmail: recipient.email,
|
||||
recipientId: recipient.id,
|
||||
recipientName: recipient.name,
|
||||
recipientRole: recipient.role,
|
||||
accessAuth: recipientAccessAuth ?? [],
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
// Early return if already opened.
|
||||
if (recipient.readStatus === ReadStatus.OPENED) {
|
||||
return;
|
||||
}
|
||||
|
||||
await prisma.$transaction(async (tx) => {
|
||||
await tx.recipient.update({
|
||||
where: {
|
||||
|
||||
@@ -26,6 +26,7 @@ export const deleteField = async ({
|
||||
id: fieldId,
|
||||
document: {
|
||||
id: documentId,
|
||||
userId,
|
||||
team: buildTeamWhereQuery({ teamId, userId }),
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { DocumentStatus, FieldType, RecipientRole, SigningStatus } from '@prisma/client';
|
||||
import { DateTime } from 'luxon';
|
||||
import { isDeepEqual } from 'remeda';
|
||||
import { match } from 'ts-pattern';
|
||||
|
||||
import { validateCheckboxField } from '@documenso/lib/advanced-fields-validation/validate-checkbox';
|
||||
@@ -11,7 +10,6 @@ import { validateTextField } from '@documenso/lib/advanced-fields-validation/val
|
||||
import { fromCheckboxValue } from '@documenso/lib/universal/field-checkbox';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
|
||||
import { AUTO_SIGNABLE_FIELD_TYPES } from '../../constants/autosign';
|
||||
import { DEFAULT_DOCUMENT_DATE_FORMAT } from '../../constants/date-formats';
|
||||
import { DEFAULT_DOCUMENT_TIME_ZONE } from '../../constants/time-zones';
|
||||
import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs';
|
||||
@@ -207,29 +205,6 @@ export const signFieldWithToken = async ({
|
||||
throw new Error('Typed signatures are not allowed. Please draw your signature');
|
||||
}
|
||||
|
||||
if (field.fieldMeta?.readOnly && !AUTO_SIGNABLE_FIELD_TYPES.includes(field.type)) {
|
||||
// !: This is a bit of a hack at the moment, readonly fields with default values
|
||||
// !: should be inserted with their default value on document creation instead of
|
||||
// !: this weird programattic approach. Until that's fixed though this will verify
|
||||
// !: that the programmatic signed value is only that of its default.
|
||||
const isAutomaticSigningValueValid = match(field.fieldMeta)
|
||||
.with({ type: 'text' }, (meta) => customText === meta.text)
|
||||
.with({ type: 'number' }, (meta) => customText === meta.value)
|
||||
.with({ type: 'checkbox' }, (meta) =>
|
||||
isDeepEqual(
|
||||
fromCheckboxValue(customText ?? ''),
|
||||
meta.values?.filter((v) => v.checked).map((v) => v.value) ?? [],
|
||||
),
|
||||
)
|
||||
.with({ type: 'radio' }, (meta) => customText === meta.values?.find((v) => v.checked)?.value)
|
||||
.with({ type: 'dropdown' }, (meta) => customText === meta.defaultValue)
|
||||
.otherwise(() => false);
|
||||
|
||||
if (!isAutomaticSigningValueValid) {
|
||||
throw new Error('Field is read only and only accepts its default value for signing.');
|
||||
}
|
||||
}
|
||||
|
||||
const assistant = recipient.role === RecipientRole.ASSISTANT ? recipient : undefined;
|
||||
|
||||
return await prisma.$transaction(async (tx) => {
|
||||
|
||||
@@ -48,6 +48,7 @@ export const updateField = async ({
|
||||
id: fieldId,
|
||||
document: {
|
||||
id: documentId,
|
||||
userId,
|
||||
team: buildTeamWhereQuery({ teamId, userId }),
|
||||
},
|
||||
},
|
||||
|
||||
@@ -4,7 +4,6 @@ import { match } from 'ts-pattern';
|
||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
|
||||
import { buildTeamWhereQuery } from '../../utils/teams';
|
||||
import { getTeamById } from '../team/get-team';
|
||||
|
||||
export interface DeleteFolderOptions {
|
||||
@@ -19,10 +18,8 @@ export const deleteFolder = async ({ userId, teamId, folderId }: DeleteFolderOpt
|
||||
const folder = await prisma.folder.findFirst({
|
||||
where: {
|
||||
id: folderId,
|
||||
team: buildTeamWhereQuery({
|
||||
teamId,
|
||||
userId,
|
||||
}),
|
||||
userId,
|
||||
teamId,
|
||||
},
|
||||
include: {
|
||||
documents: true,
|
||||
|
||||
@@ -2,8 +2,6 @@ import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||
import type { ApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
|
||||
import { buildTeamWhereQuery } from '../../utils/teams';
|
||||
|
||||
export interface MoveFolderOptions {
|
||||
userId: number;
|
||||
teamId?: number;
|
||||
@@ -17,10 +15,8 @@ export const moveFolder = async ({ userId, teamId, folderId, parentId }: MoveFol
|
||||
const folder = await tx.folder.findFirst({
|
||||
where: {
|
||||
id: folderId,
|
||||
team: buildTeamWhereQuery({
|
||||
teamId,
|
||||
userId,
|
||||
}),
|
||||
userId,
|
||||
teamId,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -2,8 +2,6 @@ import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||
import { FolderType } from '@documenso/lib/types/folder-type';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
|
||||
import { buildTeamWhereQuery } from '../../utils/teams';
|
||||
|
||||
export interface MoveTemplateToFolderOptions {
|
||||
userId: number;
|
||||
teamId?: number;
|
||||
@@ -17,47 +15,45 @@ export const moveTemplateToFolder = async ({
|
||||
templateId,
|
||||
folderId,
|
||||
}: MoveTemplateToFolderOptions) => {
|
||||
const template = await prisma.template.findFirst({
|
||||
where: {
|
||||
id: templateId,
|
||||
team: buildTeamWhereQuery({
|
||||
teamId,
|
||||
userId,
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
if (!template) {
|
||||
throw new AppError(AppErrorCode.NOT_FOUND, {
|
||||
message: 'Template not found',
|
||||
});
|
||||
}
|
||||
|
||||
if (folderId !== null) {
|
||||
const folder = await prisma.folder.findFirst({
|
||||
return await prisma.$transaction(async (tx) => {
|
||||
const template = await tx.template.findFirst({
|
||||
where: {
|
||||
id: folderId,
|
||||
team: buildTeamWhereQuery({
|
||||
teamId,
|
||||
userId,
|
||||
}),
|
||||
type: FolderType.TEMPLATE,
|
||||
id: templateId,
|
||||
userId,
|
||||
teamId,
|
||||
},
|
||||
});
|
||||
|
||||
if (!folder) {
|
||||
if (!template) {
|
||||
throw new AppError(AppErrorCode.NOT_FOUND, {
|
||||
message: 'Folder not found',
|
||||
message: 'Template not found',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return await prisma.template.update({
|
||||
where: {
|
||||
id: templateId,
|
||||
},
|
||||
data: {
|
||||
folderId,
|
||||
},
|
||||
if (folderId !== null) {
|
||||
const folder = await tx.folder.findFirst({
|
||||
where: {
|
||||
id: folderId,
|
||||
userId,
|
||||
teamId,
|
||||
type: FolderType.TEMPLATE,
|
||||
},
|
||||
});
|
||||
|
||||
if (!folder) {
|
||||
throw new AppError(AppErrorCode.NOT_FOUND, {
|
||||
message: 'Folder not found',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return await tx.template.update({
|
||||
where: {
|
||||
id: templateId,
|
||||
},
|
||||
data: {
|
||||
folderId,
|
||||
},
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
@@ -2,7 +2,6 @@ import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
|
||||
import type { TFolderType } from '../../types/folder-type';
|
||||
import { buildTeamWhereQuery } from '../../utils/teams';
|
||||
|
||||
export interface PinFolderOptions {
|
||||
userId: number;
|
||||
@@ -15,10 +14,8 @@ export const pinFolder = async ({ userId, teamId, folderId, type }: PinFolderOpt
|
||||
const folder = await prisma.folder.findFirst({
|
||||
where: {
|
||||
id: folderId,
|
||||
team: buildTeamWhereQuery({
|
||||
teamId,
|
||||
userId,
|
||||
}),
|
||||
userId,
|
||||
teamId,
|
||||
type,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -2,7 +2,6 @@ import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
|
||||
import type { TFolderType } from '../../types/folder-type';
|
||||
import { buildTeamWhereQuery } from '../../utils/teams';
|
||||
|
||||
export interface UnpinFolderOptions {
|
||||
userId: number;
|
||||
@@ -15,10 +14,8 @@ export const unpinFolder = async ({ userId, teamId, folderId, type }: UnpinFolde
|
||||
const folder = await prisma.folder.findFirst({
|
||||
where: {
|
||||
id: folderId,
|
||||
team: buildTeamWhereQuery({
|
||||
teamId,
|
||||
userId,
|
||||
}),
|
||||
userId,
|
||||
teamId,
|
||||
type,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -4,7 +4,6 @@ import { DocumentVisibility } from '@documenso/prisma/generated/types';
|
||||
|
||||
import type { TFolderType } from '../../types/folder-type';
|
||||
import { FolderType } from '../../types/folder-type';
|
||||
import { buildTeamWhereQuery } from '../../utils/teams';
|
||||
|
||||
export interface UpdateFolderOptions {
|
||||
userId: number;
|
||||
@@ -26,10 +25,8 @@ export const updateFolder = async ({
|
||||
const folder = await prisma.folder.findFirst({
|
||||
where: {
|
||||
id: folderId,
|
||||
team: buildTeamWhereQuery({
|
||||
teamId,
|
||||
userId,
|
||||
}),
|
||||
userId,
|
||||
teamId,
|
||||
type,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
import { getPortalSession } from '@documenso/ee/server-only/stripe/get-portal-session';
|
||||
import { IS_BILLING_ENABLED } from '@documenso/lib/constants/app';
|
||||
import { TEAM_MEMBER_ROLE_PERMISSIONS_MAP } from '@documenso/lib/constants/teams';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
|
||||
export type CreateTeamBillingPortalOptions = {
|
||||
userId: number;
|
||||
teamId: number;
|
||||
};
|
||||
|
||||
export const createTeamBillingPortal = async ({
|
||||
userId,
|
||||
teamId,
|
||||
}: CreateTeamBillingPortalOptions) => {
|
||||
if (!IS_BILLING_ENABLED()) {
|
||||
throw new Error('Billing is not enabled');
|
||||
}
|
||||
|
||||
const team = await prisma.team.findFirstOrThrow({
|
||||
where: {
|
||||
id: teamId,
|
||||
members: {
|
||||
some: {
|
||||
userId,
|
||||
role: {
|
||||
in: TEAM_MEMBER_ROLE_PERMISSIONS_MAP['MANAGE_BILLING'],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
include: {
|
||||
subscription: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!team.subscription) {
|
||||
throw new Error('Team has no subscription');
|
||||
}
|
||||
|
||||
if (!team.customerId) {
|
||||
throw new Error('Team has no customerId');
|
||||
}
|
||||
|
||||
return getPortalSession({
|
||||
customerId: team.customerId,
|
||||
});
|
||||
};
|
||||
@@ -240,79 +240,35 @@ export const insertFieldInPDF = async (pdf: PDFDocument, field: FieldWithSignatu
|
||||
}));
|
||||
|
||||
const selected: string[] = fromCheckboxValue(field.customText);
|
||||
const direction = meta.data.direction ?? 'vertical';
|
||||
|
||||
const topPadding = 12;
|
||||
const leftCheckboxPadding = 8;
|
||||
const leftCheckboxLabelPadding = 12;
|
||||
const checkboxSpaceY = 13;
|
||||
|
||||
if (direction === 'horizontal') {
|
||||
// Horizontal layout: arrange checkboxes side by side with wrapping
|
||||
let currentX = leftCheckboxPadding;
|
||||
let currentY = topPadding;
|
||||
const maxWidth = pageWidth - fieldX - leftCheckboxPadding * 2;
|
||||
for (const [index, item] of (values ?? []).entries()) {
|
||||
const offsetY = index * checkboxSpaceY + topPadding;
|
||||
|
||||
for (const [index, item] of (values ?? []).entries()) {
|
||||
const checkbox = pdf.getForm().createCheckBox(`checkbox.${field.secondaryId}.${index}`);
|
||||
const checkbox = pdf.getForm().createCheckBox(`checkbox.${field.secondaryId}.${index}`);
|
||||
|
||||
if (selected.includes(item.value)) {
|
||||
checkbox.check();
|
||||
}
|
||||
|
||||
const labelText = item.value.includes('empty-value-') ? '' : item.value;
|
||||
const labelWidth = font.widthOfTextAtSize(labelText, 12);
|
||||
const itemWidth = leftCheckboxLabelPadding + labelWidth + 16; // checkbox + padding + label + margin
|
||||
|
||||
// Check if item fits on current line, if not wrap to next line
|
||||
if (currentX + itemWidth > maxWidth && index > 0) {
|
||||
currentX = leftCheckboxPadding;
|
||||
currentY += checkboxSpaceY;
|
||||
}
|
||||
|
||||
page.drawText(labelText, {
|
||||
x: fieldX + currentX + leftCheckboxLabelPadding,
|
||||
y: pageHeight - (fieldY + currentY),
|
||||
size: 12,
|
||||
font,
|
||||
rotate: degrees(pageRotationInDegrees),
|
||||
});
|
||||
|
||||
checkbox.addToPage(page, {
|
||||
x: fieldX + currentX,
|
||||
y: pageHeight - (fieldY + currentY),
|
||||
height: 8,
|
||||
width: 8,
|
||||
});
|
||||
|
||||
currentX += itemWidth;
|
||||
if (selected.includes(item.value)) {
|
||||
checkbox.check();
|
||||
}
|
||||
} else {
|
||||
// Vertical layout: original behavior
|
||||
for (const [index, item] of (values ?? []).entries()) {
|
||||
const offsetY = index * checkboxSpaceY + topPadding;
|
||||
|
||||
const checkbox = pdf.getForm().createCheckBox(`checkbox.${field.secondaryId}.${index}`);
|
||||
page.drawText(item.value.includes('empty-value-') ? '' : item.value, {
|
||||
x: fieldX + leftCheckboxPadding + leftCheckboxLabelPadding,
|
||||
y: pageHeight - (fieldY + offsetY),
|
||||
size: 12,
|
||||
font,
|
||||
rotate: degrees(pageRotationInDegrees),
|
||||
});
|
||||
|
||||
if (selected.includes(item.value)) {
|
||||
checkbox.check();
|
||||
}
|
||||
|
||||
page.drawText(item.value.includes('empty-value-') ? '' : item.value, {
|
||||
x: fieldX + leftCheckboxPadding + leftCheckboxLabelPadding,
|
||||
y: pageHeight - (fieldY + offsetY),
|
||||
size: 12,
|
||||
font,
|
||||
rotate: degrees(pageRotationInDegrees),
|
||||
});
|
||||
|
||||
checkbox.addToPage(page, {
|
||||
x: fieldX + leftCheckboxPadding,
|
||||
y: pageHeight - (fieldY + offsetY),
|
||||
height: 8,
|
||||
width: 8,
|
||||
});
|
||||
}
|
||||
checkbox.addToPage(page, {
|
||||
x: fieldX + leftCheckboxPadding,
|
||||
y: pageHeight - (fieldY + offsetY),
|
||||
height: 8,
|
||||
width: 8,
|
||||
});
|
||||
}
|
||||
})
|
||||
.with({ type: FieldType.RADIO }, (field) => {
|
||||
|
||||
@@ -228,7 +228,6 @@ const getUpdatedFieldMeta = (field: Field, prefillField?: TFieldMetaPrefillField
|
||||
type: 'checkbox',
|
||||
label: field.label,
|
||||
values: newValues,
|
||||
direction: checkboxMeta.direction ?? 'vertical',
|
||||
};
|
||||
|
||||
return meta;
|
||||
|
||||
@@ -25,6 +25,7 @@ export const duplicateTemplate = async ({
|
||||
include: {
|
||||
recipients: {
|
||||
select: {
|
||||
id: true,
|
||||
email: true,
|
||||
name: true,
|
||||
role: true,
|
||||
|
||||
@@ -2,8 +2,6 @@ import type { WebhookTriggerEvents } from '@prisma/client';
|
||||
|
||||
import { prisma } from '@documenso/prisma';
|
||||
|
||||
import { buildTeamWhereQuery } from '../../utils/teams';
|
||||
|
||||
export type GetAllWebhooksByEventTriggerOptions = {
|
||||
event: WebhookTriggerEvents;
|
||||
userId: number;
|
||||
@@ -21,10 +19,22 @@ export const getAllWebhooksByEventTrigger = async ({
|
||||
eventTriggers: {
|
||||
has: event,
|
||||
},
|
||||
team: buildTeamWhereQuery({
|
||||
teamId,
|
||||
userId,
|
||||
}),
|
||||
team: {
|
||||
id: teamId,
|
||||
teamGroups: {
|
||||
some: {
|
||||
organisationGroup: {
|
||||
organisationGroupMembers: {
|
||||
some: {
|
||||
organisationMember: {
|
||||
userId,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import { prisma } from '@documenso/prisma';
|
||||
|
||||
import { TEAM_MEMBER_ROLE_PERMISSIONS_MAP } from '../../constants/teams';
|
||||
import { buildTeamWhereQuery } from '../../utils/teams';
|
||||
|
||||
export type GetWebhookByIdOptions = {
|
||||
id: string;
|
||||
userId: number;
|
||||
@@ -13,11 +10,23 @@ export const getWebhookById = async ({ id, userId, teamId }: GetWebhookByIdOptio
|
||||
return await prisma.webhook.findFirstOrThrow({
|
||||
where: {
|
||||
id,
|
||||
team: buildTeamWhereQuery({
|
||||
teamId,
|
||||
userId,
|
||||
roles: TEAM_MEMBER_ROLE_PERMISSIONS_MAP.MANAGE_TEAM,
|
||||
}),
|
||||
userId,
|
||||
team: {
|
||||
id: teamId,
|
||||
teamGroups: {
|
||||
some: {
|
||||
organisationGroup: {
|
||||
organisationGroupMembers: {
|
||||
some: {
|
||||
organisationMember: {
|
||||
userId,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
import type { WebhookTriggerEvents } from '@prisma/client';
|
||||
|
||||
import { getWebhookById } from './get-webhook-by-id';
|
||||
import { generateSampleWebhookPayload } from './trigger/generate-sample-data';
|
||||
import { triggerWebhook } from './trigger/trigger-webhook';
|
||||
|
||||
export type TriggerTestWebhookOptions = {
|
||||
id: string;
|
||||
event: WebhookTriggerEvents;
|
||||
userId: number;
|
||||
teamId: number;
|
||||
};
|
||||
|
||||
export const triggerTestWebhook = async ({
|
||||
id,
|
||||
event,
|
||||
userId,
|
||||
teamId,
|
||||
}: TriggerTestWebhookOptions) => {
|
||||
const webhook = await getWebhookById({ id, userId, teamId });
|
||||
|
||||
if (!webhook.enabled) {
|
||||
throw new Error('Webhook is disabled');
|
||||
}
|
||||
|
||||
if (!webhook.eventTriggers.includes(event)) {
|
||||
throw new Error(`Webhook does not support event: ${event}`);
|
||||
}
|
||||
|
||||
const samplePayload = generateSampleWebhookPayload(event, webhook.webhookUrl);
|
||||
|
||||
try {
|
||||
await triggerWebhook({
|
||||
event,
|
||||
data: samplePayload,
|
||||
userId,
|
||||
teamId,
|
||||
});
|
||||
|
||||
return { success: true, message: 'Test webhook triggered successfully' };
|
||||
} catch (error) {
|
||||
return { success: false, error: error instanceof Error ? error.message : 'Unknown error' };
|
||||
}
|
||||
};
|
||||
@@ -1,485 +0,0 @@
|
||||
import {
|
||||
DocumentDistributionMethod,
|
||||
DocumentSigningOrder,
|
||||
DocumentSource,
|
||||
DocumentStatus,
|
||||
DocumentVisibility,
|
||||
ReadStatus,
|
||||
RecipientRole,
|
||||
SendStatus,
|
||||
SigningStatus,
|
||||
WebhookTriggerEvents,
|
||||
} from '@prisma/client';
|
||||
|
||||
import type { WebhookPayload } from '../../../types/webhook-payload';
|
||||
|
||||
export const generateSampleWebhookPayload = (
|
||||
event: WebhookTriggerEvents,
|
||||
webhookUrl: string,
|
||||
): WebhookPayload => {
|
||||
const now = new Date();
|
||||
const basePayload = {
|
||||
id: 10,
|
||||
externalId: null,
|
||||
userId: 1,
|
||||
authOptions: null,
|
||||
formValues: null,
|
||||
visibility: DocumentVisibility.EVERYONE,
|
||||
title: 'documenso.pdf',
|
||||
status: DocumentStatus.DRAFT,
|
||||
documentDataId: 'hs8qz1ktr9204jn7mg6c5dxy0',
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
completedAt: null,
|
||||
deletedAt: null,
|
||||
teamId: null,
|
||||
templateId: null,
|
||||
source: DocumentSource.DOCUMENT,
|
||||
documentMeta: {
|
||||
id: 'doc_meta_123',
|
||||
subject: 'Please sign this document',
|
||||
message: 'Hello, please review and sign this document.',
|
||||
timezone: 'UTC',
|
||||
password: null,
|
||||
dateFormat: 'MM/DD/YYYY',
|
||||
redirectUrl: null,
|
||||
signingOrder: DocumentSigningOrder.PARALLEL,
|
||||
allowDictateNextSigner: false,
|
||||
typedSignatureEnabled: true,
|
||||
uploadSignatureEnabled: true,
|
||||
drawSignatureEnabled: true,
|
||||
language: 'en',
|
||||
distributionMethod: DocumentDistributionMethod.EMAIL,
|
||||
emailSettings: null,
|
||||
},
|
||||
recipients: [
|
||||
{
|
||||
id: 52,
|
||||
documentId: 10,
|
||||
templateId: null,
|
||||
email: 'signer@documenso.com',
|
||||
name: 'John Doe',
|
||||
token: 'SIGNING_TOKEN',
|
||||
documentDeletedAt: null,
|
||||
expired: null,
|
||||
signedAt: null,
|
||||
authOptions: null,
|
||||
signingOrder: 1,
|
||||
rejectionReason: null,
|
||||
role: RecipientRole.SIGNER,
|
||||
readStatus: ReadStatus.NOT_OPENED,
|
||||
signingStatus: SigningStatus.NOT_SIGNED,
|
||||
sendStatus: SendStatus.NOT_SENT,
|
||||
},
|
||||
],
|
||||
Recipient: [
|
||||
{
|
||||
id: 52,
|
||||
documentId: 10,
|
||||
templateId: null,
|
||||
email: 'signer@documenso.com',
|
||||
name: 'John Doe',
|
||||
token: 'SIGNING_TOKEN',
|
||||
documentDeletedAt: null,
|
||||
expired: null,
|
||||
signedAt: null,
|
||||
authOptions: null,
|
||||
signingOrder: 1,
|
||||
rejectionReason: null,
|
||||
role: RecipientRole.SIGNER,
|
||||
readStatus: ReadStatus.NOT_OPENED,
|
||||
signingStatus: SigningStatus.NOT_SIGNED,
|
||||
sendStatus: SendStatus.NOT_SENT,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
if (event === WebhookTriggerEvents.DOCUMENT_CREATED) {
|
||||
return {
|
||||
event,
|
||||
payload: {
|
||||
...basePayload,
|
||||
status: DocumentStatus.DRAFT,
|
||||
},
|
||||
createdAt: now.toISOString(),
|
||||
webhookEndpoint: webhookUrl,
|
||||
};
|
||||
}
|
||||
|
||||
if (event === WebhookTriggerEvents.DOCUMENT_SENT) {
|
||||
return {
|
||||
event,
|
||||
payload: {
|
||||
...basePayload,
|
||||
status: DocumentStatus.PENDING,
|
||||
recipients: [
|
||||
{
|
||||
...basePayload.recipients[0],
|
||||
email: 'signer2@documenso.com',
|
||||
name: 'Signer 2',
|
||||
role: RecipientRole.VIEWER,
|
||||
sendStatus: SendStatus.SENT,
|
||||
documentDeletedAt: null,
|
||||
expired: null,
|
||||
signedAt: null,
|
||||
authOptions: null,
|
||||
signingOrder: 1,
|
||||
rejectionReason: null,
|
||||
readStatus: ReadStatus.NOT_OPENED,
|
||||
signingStatus: SigningStatus.NOT_SIGNED,
|
||||
},
|
||||
],
|
||||
Recipient: [
|
||||
{
|
||||
...basePayload.Recipient[0],
|
||||
email: 'signer1@documenso.com',
|
||||
name: 'Signer 1',
|
||||
token: 'SIGNING_TOKEN',
|
||||
signingOrder: 2,
|
||||
role: RecipientRole.SIGNER,
|
||||
sendStatus: SendStatus.SENT,
|
||||
documentDeletedAt: null,
|
||||
expired: null,
|
||||
signedAt: null,
|
||||
authOptions: null,
|
||||
rejectionReason: null,
|
||||
readStatus: ReadStatus.NOT_OPENED,
|
||||
signingStatus: SigningStatus.NOT_SIGNED,
|
||||
},
|
||||
],
|
||||
},
|
||||
createdAt: now.toISOString(),
|
||||
webhookEndpoint: webhookUrl,
|
||||
};
|
||||
}
|
||||
|
||||
if (event === WebhookTriggerEvents.DOCUMENT_OPENED) {
|
||||
return {
|
||||
event,
|
||||
payload: {
|
||||
...basePayload,
|
||||
status: DocumentStatus.PENDING,
|
||||
recipients: [
|
||||
{
|
||||
...basePayload.recipients[0],
|
||||
email: 'signer2@documenso.com',
|
||||
name: 'Signer 2',
|
||||
role: RecipientRole.VIEWER,
|
||||
readStatus: ReadStatus.OPENED,
|
||||
sendStatus: SendStatus.SENT,
|
||||
documentDeletedAt: null,
|
||||
expired: null,
|
||||
signedAt: null,
|
||||
authOptions: null,
|
||||
signingOrder: 1,
|
||||
rejectionReason: null,
|
||||
signingStatus: SigningStatus.NOT_SIGNED,
|
||||
},
|
||||
],
|
||||
Recipient: [
|
||||
{
|
||||
...basePayload.Recipient[0],
|
||||
email: 'signer2@documenso.com',
|
||||
name: 'Signer 2',
|
||||
role: RecipientRole.VIEWER,
|
||||
readStatus: ReadStatus.OPENED,
|
||||
sendStatus: SendStatus.SENT,
|
||||
documentDeletedAt: null,
|
||||
expired: null,
|
||||
signedAt: null,
|
||||
authOptions: null,
|
||||
signingOrder: 1,
|
||||
rejectionReason: null,
|
||||
signingStatus: SigningStatus.NOT_SIGNED,
|
||||
},
|
||||
],
|
||||
},
|
||||
createdAt: now.toISOString(),
|
||||
webhookEndpoint: webhookUrl,
|
||||
};
|
||||
}
|
||||
|
||||
if (event === WebhookTriggerEvents.DOCUMENT_SIGNED) {
|
||||
return {
|
||||
event,
|
||||
payload: {
|
||||
...basePayload,
|
||||
status: DocumentStatus.COMPLETED,
|
||||
completedAt: now,
|
||||
recipients: [
|
||||
{
|
||||
...basePayload.recipients[0],
|
||||
id: 51,
|
||||
email: 'signer1@documenso.com',
|
||||
name: 'Signer 1',
|
||||
token: 'SIGNING_TOKEN',
|
||||
signedAt: now,
|
||||
authOptions: {
|
||||
accessAuth: null,
|
||||
actionAuth: null,
|
||||
},
|
||||
readStatus: ReadStatus.OPENED,
|
||||
signingStatus: SigningStatus.SIGNED,
|
||||
sendStatus: SendStatus.SENT,
|
||||
documentDeletedAt: null,
|
||||
expired: null,
|
||||
signingOrder: 1,
|
||||
rejectionReason: null,
|
||||
},
|
||||
],
|
||||
Recipient: [
|
||||
{
|
||||
...basePayload.Recipient[0],
|
||||
id: 51,
|
||||
email: 'signer1@documenso.com',
|
||||
name: 'Signer 1',
|
||||
token: 'SIGNING_TOKEN',
|
||||
signedAt: now,
|
||||
authOptions: {
|
||||
accessAuth: null,
|
||||
actionAuth: null,
|
||||
},
|
||||
readStatus: ReadStatus.OPENED,
|
||||
signingStatus: SigningStatus.SIGNED,
|
||||
sendStatus: SendStatus.SENT,
|
||||
documentDeletedAt: null,
|
||||
expired: null,
|
||||
signingOrder: 1,
|
||||
rejectionReason: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
createdAt: now.toISOString(),
|
||||
webhookEndpoint: webhookUrl,
|
||||
};
|
||||
}
|
||||
|
||||
if (event === WebhookTriggerEvents.DOCUMENT_COMPLETED) {
|
||||
return {
|
||||
event,
|
||||
payload: {
|
||||
...basePayload,
|
||||
status: DocumentStatus.COMPLETED,
|
||||
completedAt: now,
|
||||
recipients: [
|
||||
{
|
||||
id: 50,
|
||||
documentId: 10,
|
||||
templateId: null,
|
||||
email: 'signer2@documenso.com',
|
||||
name: 'Signer 2',
|
||||
token: 'SIGNING_TOKEN',
|
||||
documentDeletedAt: null,
|
||||
expired: null,
|
||||
signedAt: now,
|
||||
authOptions: {
|
||||
accessAuth: null,
|
||||
actionAuth: null,
|
||||
},
|
||||
signingOrder: 1,
|
||||
rejectionReason: null,
|
||||
role: RecipientRole.VIEWER,
|
||||
readStatus: ReadStatus.OPENED,
|
||||
signingStatus: SigningStatus.SIGNED,
|
||||
sendStatus: SendStatus.SENT,
|
||||
},
|
||||
{
|
||||
id: 51,
|
||||
documentId: 10,
|
||||
templateId: null,
|
||||
email: 'signer1@documenso.com',
|
||||
name: 'Signer 1',
|
||||
token: 'SIGNING_TOKEN',
|
||||
documentDeletedAt: null,
|
||||
expired: null,
|
||||
signedAt: now,
|
||||
authOptions: {
|
||||
accessAuth: null,
|
||||
actionAuth: null,
|
||||
},
|
||||
signingOrder: 2,
|
||||
rejectionReason: null,
|
||||
role: RecipientRole.SIGNER,
|
||||
readStatus: ReadStatus.OPENED,
|
||||
signingStatus: SigningStatus.SIGNED,
|
||||
sendStatus: SendStatus.SENT,
|
||||
},
|
||||
],
|
||||
Recipient: [
|
||||
{
|
||||
id: 50,
|
||||
documentId: 10,
|
||||
templateId: null,
|
||||
email: 'signer2@documenso.com',
|
||||
name: 'Signer 2',
|
||||
token: 'SIGNING_TOKEN',
|
||||
documentDeletedAt: null,
|
||||
expired: null,
|
||||
signedAt: now,
|
||||
authOptions: {
|
||||
accessAuth: null,
|
||||
actionAuth: null,
|
||||
},
|
||||
signingOrder: 1,
|
||||
rejectionReason: null,
|
||||
role: RecipientRole.VIEWER,
|
||||
readStatus: ReadStatus.OPENED,
|
||||
signingStatus: SigningStatus.SIGNED,
|
||||
sendStatus: SendStatus.SENT,
|
||||
},
|
||||
{
|
||||
id: 51,
|
||||
documentId: 10,
|
||||
templateId: null,
|
||||
email: 'signer1@documenso.com',
|
||||
name: 'Signer 1',
|
||||
token: 'SIGNING_TOKEN',
|
||||
documentDeletedAt: null,
|
||||
expired: null,
|
||||
signedAt: now,
|
||||
authOptions: {
|
||||
accessAuth: null,
|
||||
actionAuth: null,
|
||||
},
|
||||
signingOrder: 2,
|
||||
rejectionReason: null,
|
||||
role: RecipientRole.SIGNER,
|
||||
readStatus: ReadStatus.OPENED,
|
||||
signingStatus: SigningStatus.SIGNED,
|
||||
sendStatus: SendStatus.SENT,
|
||||
},
|
||||
],
|
||||
},
|
||||
createdAt: now.toISOString(),
|
||||
webhookEndpoint: webhookUrl,
|
||||
};
|
||||
}
|
||||
|
||||
if (event === WebhookTriggerEvents.DOCUMENT_REJECTED) {
|
||||
return {
|
||||
event,
|
||||
payload: {
|
||||
...basePayload,
|
||||
status: DocumentStatus.PENDING,
|
||||
recipients: [
|
||||
{
|
||||
...basePayload.recipients[0],
|
||||
signedAt: now,
|
||||
authOptions: {
|
||||
accessAuth: null,
|
||||
actionAuth: null,
|
||||
},
|
||||
rejectionReason: 'I do not agree with the terms',
|
||||
readStatus: ReadStatus.OPENED,
|
||||
signingStatus: SigningStatus.REJECTED,
|
||||
sendStatus: SendStatus.SENT,
|
||||
documentDeletedAt: null,
|
||||
expired: null,
|
||||
signingOrder: 1,
|
||||
},
|
||||
],
|
||||
Recipient: [
|
||||
{
|
||||
...basePayload.Recipient[0],
|
||||
signedAt: now,
|
||||
authOptions: {
|
||||
accessAuth: null,
|
||||
actionAuth: null,
|
||||
},
|
||||
rejectionReason: 'I do not agree with the terms',
|
||||
readStatus: ReadStatus.OPENED,
|
||||
signingStatus: SigningStatus.REJECTED,
|
||||
sendStatus: SendStatus.SENT,
|
||||
documentDeletedAt: null,
|
||||
expired: null,
|
||||
signingOrder: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
createdAt: now.toISOString(),
|
||||
webhookEndpoint: webhookUrl,
|
||||
};
|
||||
}
|
||||
|
||||
if (event === WebhookTriggerEvents.DOCUMENT_CANCELLED) {
|
||||
return {
|
||||
event,
|
||||
payload: {
|
||||
...basePayload,
|
||||
id: 7,
|
||||
externalId: null,
|
||||
userId: 3,
|
||||
status: DocumentStatus.PENDING,
|
||||
documentDataId: 'cm6exvn93006hi02ru90a265a',
|
||||
documentMeta: {
|
||||
...basePayload.documentMeta,
|
||||
id: 'cm6exvn96006ji02rqvzjvwoy',
|
||||
subject: '',
|
||||
message: '',
|
||||
timezone: 'Etc/UTC',
|
||||
dateFormat: 'yyyy-MM-dd hh:mm a',
|
||||
redirectUrl: '',
|
||||
emailSettings: {
|
||||
documentDeleted: true,
|
||||
documentPending: true,
|
||||
recipientSigned: true,
|
||||
recipientRemoved: true,
|
||||
documentCompleted: true,
|
||||
ownerDocumentCompleted: true,
|
||||
recipientSigningRequest: true,
|
||||
},
|
||||
},
|
||||
recipients: [
|
||||
{
|
||||
id: 7,
|
||||
documentId: 7,
|
||||
templateId: null,
|
||||
email: 'signer1@documenso.com',
|
||||
name: 'Signer 1',
|
||||
token: 'SIGNING_TOKEN',
|
||||
documentDeletedAt: null,
|
||||
expired: null,
|
||||
signedAt: null,
|
||||
authOptions: {
|
||||
accessAuth: null,
|
||||
actionAuth: null,
|
||||
},
|
||||
signingOrder: 1,
|
||||
rejectionReason: null,
|
||||
role: RecipientRole.SIGNER,
|
||||
readStatus: ReadStatus.NOT_OPENED,
|
||||
signingStatus: SigningStatus.NOT_SIGNED,
|
||||
sendStatus: SendStatus.SENT,
|
||||
},
|
||||
],
|
||||
Recipient: [
|
||||
{
|
||||
id: 7,
|
||||
documentId: 7,
|
||||
templateId: null,
|
||||
email: 'signer@documenso.com',
|
||||
name: 'Signer',
|
||||
token: 'SIGNING_TOKEN',
|
||||
documentDeletedAt: null,
|
||||
expired: null,
|
||||
signedAt: null,
|
||||
authOptions: {
|
||||
accessAuth: null,
|
||||
actionAuth: null,
|
||||
},
|
||||
signingOrder: 1,
|
||||
rejectionReason: null,
|
||||
role: RecipientRole.SIGNER,
|
||||
readStatus: ReadStatus.NOT_OPENED,
|
||||
signingStatus: SigningStatus.NOT_SIGNED,
|
||||
sendStatus: SendStatus.SENT,
|
||||
},
|
||||
],
|
||||
},
|
||||
createdAt: now.toISOString(),
|
||||
webhookEndpoint: webhookUrl,
|
||||
};
|
||||
}
|
||||
|
||||
throw new Error(`Unsupported event type: ${event}`);
|
||||
};
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: de\n"
|
||||
"Project-Id-Version: documenso-app\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2025-06-19 06:05\n"
|
||||
"PO-Revision-Date: 2025-06-10 02:27\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: German\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
@@ -75,12 +75,12 @@ msgstr "{0, plural, one {# Zeichen über dem Limit} other {# Zeichen über dem L
|
||||
#. placeholder {0}: folder._count.documents
|
||||
#: apps/remix/app/components/general/folder/folder-card.tsx
|
||||
msgid "{0, plural, one {# document} other {# documents}}"
|
||||
msgstr "{0, plural, one {# Dokument} other {# Dokumente}}"
|
||||
msgstr ""
|
||||
|
||||
#. placeholder {0}: folder._count.subfolders
|
||||
#: apps/remix/app/components/general/folder/folder-card.tsx
|
||||
msgid "{0, plural, one {# folder} other {# folders}}"
|
||||
msgstr "{0, plural, one {# Ordner} other {# Ordner}}"
|
||||
msgstr ""
|
||||
|
||||
#. placeholder {0}: template.recipients.length
|
||||
#: apps/remix/app/routes/_recipient+/d.$token+/_index.tsx
|
||||
@@ -95,7 +95,7 @@ msgstr "{0, plural, one {# Team} other {# Teams}}"
|
||||
#. placeholder {0}: folder._count.templates
|
||||
#: apps/remix/app/components/general/folder/folder-card.tsx
|
||||
msgid "{0, plural, one {# template} other {# templates}}"
|
||||
msgstr "{0, plural, one {# Vorlage} other {# Vorlagen}}"
|
||||
msgstr ""
|
||||
|
||||
#. placeholder {0}: data.length
|
||||
#: apps/remix/app/components/general/organisations/organisation-invitations.tsx
|
||||
@@ -770,7 +770,7 @@ msgstr "Aktiv"
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security._index.tsx
|
||||
msgid "Active sessions"
|
||||
msgstr "Aktive Sitzungen"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/stats.tsx
|
||||
msgid "Active Subscriptions"
|
||||
@@ -1341,7 +1341,7 @@ msgstr "Sind Sie sicher, dass Sie den folgenden Antrag löschen möchten?"
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-delete-dialog.tsx
|
||||
msgid "Are you sure you want to delete this folder?"
|
||||
msgstr "Möchten Sie diesen Ordner wirklich löschen?"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/token-delete-dialog.tsx
|
||||
msgid "Are you sure you want to delete this token?"
|
||||
@@ -2161,11 +2161,11 @@ msgstr "Dokument aus der Vorlage erstellen"
|
||||
|
||||
#: apps/remix/app/components/general/folder/folder-card.tsx
|
||||
msgid "Create folder"
|
||||
msgstr "Ordner erstellen"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-create-dialog.tsx
|
||||
msgid "Create Folder"
|
||||
msgstr "Ordner erstellen"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/organisation-group-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/organisation-group-create-dialog.tsx
|
||||
@@ -2178,7 +2178,7 @@ msgstr "Gruppen erstellen"
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-create-dialog.tsx
|
||||
msgid "Create New Folder"
|
||||
msgstr "Neuen Ordner erstellen"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/_profile+/_layout.tsx
|
||||
msgid "Create now"
|
||||
@@ -2300,11 +2300,11 @@ msgstr "CSV-Struktur"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/stats.tsx
|
||||
msgid "Cumulative MAU (signed in)"
|
||||
msgstr "Kumulative MAU (angemeldet)"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
msgid "Current"
|
||||
msgstr "Aktuell"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/forms/password.tsx
|
||||
msgid "Current Password"
|
||||
@@ -2444,7 +2444,7 @@ msgstr "Dokument löschen"
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-delete-dialog.tsx
|
||||
msgid "Delete Folder"
|
||||
msgstr "Ordner löschen"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.general.tsx
|
||||
msgid "Delete organisation"
|
||||
@@ -3192,7 +3192,7 @@ msgstr "Beigefügte Dokument"
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-create-dialog.tsx
|
||||
msgid "Enter a name for your new folder. Folders help you organise your items."
|
||||
msgstr "Geben Sie einen Namen für Ihren neuen Ordner ein. Ordner helfen Ihnen, Ihre Dateien zu organisieren."
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/forms/subscription-claim-form.tsx
|
||||
msgid "Enter claim name"
|
||||
@@ -3332,7 +3332,7 @@ msgstr "Fehler beim Erstellen des Abonnementsanspruchs."
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-delete-dialog.tsx
|
||||
msgid "Failed to delete folder"
|
||||
msgstr "Ordner konnte nicht gelöscht werden"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/claim-delete-dialog.tsx
|
||||
msgid "Failed to delete subscription claim."
|
||||
@@ -3344,7 +3344,7 @@ msgstr "Fehler beim Laden des Dokuments"
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-move-dialog.tsx
|
||||
msgid "Failed to move folder"
|
||||
msgstr "Ordner konnte nicht verschoben werden"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/documents.$id.tsx
|
||||
msgid "Failed to reseal document"
|
||||
@@ -3352,7 +3352,7 @@ msgstr "Dokument konnte nicht erneut versiegelt werden"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
msgid "Failed to revoke session"
|
||||
msgstr "Sitzung konnte nicht widerrufen werden"
|
||||
msgstr ""
|
||||
|
||||
#: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx
|
||||
msgid "Failed to save settings."
|
||||
@@ -3360,7 +3360,7 @@ msgstr "Einstellungen konnten nicht gespeichert werden."
|
||||
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
msgid "Failed to sign out all sessions"
|
||||
msgstr "Fehler beim Abmelden aller Sitzungen"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/embed+/v1+/authoring+/document.edit.$id.tsx
|
||||
msgid "Failed to update document"
|
||||
@@ -3464,15 +3464,15 @@ msgstr "Ordner erfolgreich erstellt"
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-delete-dialog.tsx
|
||||
msgid "Folder deleted successfully"
|
||||
msgstr "Ordner erfolgreich gelöscht"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-move-dialog.tsx
|
||||
msgid "Folder moved successfully"
|
||||
msgstr "Ordner erfolgreich verschoben"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-create-dialog.tsx
|
||||
msgid "Folder Name"
|
||||
msgstr "Ordnername"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-settings-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/folder-move-dialog.tsx
|
||||
@@ -3725,7 +3725,7 @@ msgstr "Startseite"
|
||||
#: apps/remix/app/components/dialogs/template-move-to-folder-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/document-move-to-folder-dialog.tsx
|
||||
msgid "Home (No Folder)"
|
||||
msgstr "Startseite (kein Ordner)"
|
||||
msgstr ""
|
||||
|
||||
#: packages/lib/constants/recipient-roles.ts
|
||||
msgid "I am a signer of this document"
|
||||
@@ -3974,7 +3974,7 @@ msgstr "Die letzten 7 Tage"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
msgid "Last Active"
|
||||
msgstr "Zuletzt aktiv"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/general/template/template-page-view-information.tsx
|
||||
#: apps/remix/app/components/general/document/document-page-view-information.tsx
|
||||
@@ -4127,7 +4127,7 @@ msgstr "Verwalten Sie Berechtigungen und Zugangskontrollen"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security._index.tsx
|
||||
msgid "Manage sessions"
|
||||
msgstr "Sitzungen verwalten"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/organisations.$id.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/organisations.$id.tsx
|
||||
@@ -4208,7 +4208,7 @@ msgstr "MAU (hat Dokument abgeschlossen)"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/stats.tsx
|
||||
msgid "MAU (signed in)"
|
||||
msgstr "MAU (angemeldet)"
|
||||
msgstr ""
|
||||
|
||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx
|
||||
msgid "Max"
|
||||
@@ -4297,7 +4297,7 @@ msgstr "Dokument in Ordner verschieben"
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-move-dialog.tsx
|
||||
msgid "Move Folder"
|
||||
msgstr "Ordner verschieben"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/template-move-to-folder-dialog.tsx
|
||||
msgid "Move Template to Folder"
|
||||
@@ -4314,7 +4314,7 @@ msgstr "Es können mehrere Zugriffsmethoden ausgewählt werden."
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-create-dialog.tsx
|
||||
msgid "My Folder"
|
||||
msgstr "Mein Ordner"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/users.$id.tsx
|
||||
#: apps/remix/app/components/tables/settings-security-passkey-table.tsx
|
||||
@@ -4407,12 +4407,12 @@ msgstr "Keine aktiven Entwürfe"
|
||||
#: apps/remix/app/components/dialogs/template-move-to-folder-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/document-move-to-folder-dialog.tsx
|
||||
msgid "No folders found"
|
||||
msgstr "Keine Ordner gefunden"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/templates.folders._index.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/documents.folders._index.tsx
|
||||
msgid "No folders found matching \"{searchTerm}\""
|
||||
msgstr "Keine Ordner gefunden, die \"{searchTerm}\" entsprechen"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/_recipient+/sign.$token+/rejected.tsx
|
||||
#: apps/remix/app/components/embed/embed-document-rejected.tsx
|
||||
@@ -4725,7 +4725,7 @@ msgstr "Organisationen, in denen der Benutzer Mitglied ist."
|
||||
|
||||
#: apps/remix/app/components/general/folder/folder-card.tsx
|
||||
msgid "Organise your documents"
|
||||
msgstr "Organisiere deine Dokumente"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/organisation-group-create-dialog.tsx
|
||||
msgid "Organise your members into groups which can be assigned to teams"
|
||||
@@ -4733,7 +4733,7 @@ msgstr "Organisieren Sie Ihre Mitglieder in Gruppen, die Teams zugewiesen werden
|
||||
|
||||
#: apps/remix/app/components/general/folder/folder-card.tsx
|
||||
msgid "Organise your templates"
|
||||
msgstr "Organisiere deine Vorlagen"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/o.$orgUrl._index.tsx
|
||||
msgid "Organize your documents and templates"
|
||||
@@ -4912,7 +4912,7 @@ msgstr "Wählen Sie eine der folgenden Vereinbarungen aus und beginnen Sie das S
|
||||
|
||||
#: apps/remix/app/components/general/folder/folder-card.tsx
|
||||
msgid "Pin"
|
||||
msgstr "Anheften"
|
||||
msgstr ""
|
||||
|
||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx
|
||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx
|
||||
@@ -5468,7 +5468,7 @@ msgstr "Zugriff widerrufen"
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
msgid "Revoke all sessions"
|
||||
msgstr "Alle Sitzungen widerrufen"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/tables/user-organisations-table.tsx
|
||||
#: apps/remix/app/components/tables/team-members-table.tsx
|
||||
@@ -5546,7 +5546,7 @@ msgstr "Dokumente suchen..."
|
||||
#: apps/remix/app/components/dialogs/folder-move-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/document-move-to-folder-dialog.tsx
|
||||
msgid "Search folders..."
|
||||
msgstr "Ordner durchsuchen..."
|
||||
msgstr ""
|
||||
|
||||
#: packages/ui/components/common/language-switcher-dialog.tsx
|
||||
msgid "Search languages..."
|
||||
@@ -5574,7 +5574,7 @@ msgstr "Auswählen"
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-move-dialog.tsx
|
||||
msgid "Select a destination for this folder."
|
||||
msgstr "Wählen Sie ein Ziel für diesen Ordner aus."
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/document-move-to-folder-dialog.tsx
|
||||
msgid "Select a folder to move this document to."
|
||||
@@ -5631,7 +5631,7 @@ msgstr "Gruppen auswählen"
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
msgid "Select groups of members to add to the team."
|
||||
msgstr "Mitgliedsgruppen auswählen, die dem Team hinzugefügt werden sollen."
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
msgid "Select groups to add to this team"
|
||||
@@ -5755,11 +5755,11 @@ msgstr "Gesendet"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
msgid "Session revoked"
|
||||
msgstr "Sitzung widerrufen"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
msgid "Sessions have been revoked"
|
||||
msgstr "Sitzungen wurden widerrufen"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/general/claim-account.tsx
|
||||
msgid "Set a password"
|
||||
@@ -6633,7 +6633,8 @@ msgid "The following team has been deleted. You will no longer be able to access
|
||||
msgstr "Das folgende Team wurde gelöscht. Sie können nicht mehr auf dieses Team und seine Dokumente zugreifen."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.groups.$id.tsx
|
||||
msgid "The organisation group you are looking for may have been removed, renamed or may have never\n"
|
||||
msgid ""
|
||||
"The organisation group you are looking for may have been removed, renamed or may have never\n"
|
||||
" existed."
|
||||
msgstr "Die Organisationsgruppe, nach der Sie suchen, wurde möglicherweise entfernt, umbenannt oder hat möglicherweise nie existiert."
|
||||
|
||||
@@ -6642,12 +6643,14 @@ msgid "The organisation role that will be applied to all members in this group."
|
||||
msgstr "Die Organisationsrolle, die auf alle Mitglieder in dieser Gruppe angewendet wird."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/organisations.$id.tsx
|
||||
msgid "The organisation you are looking for may have been removed, renamed or may have never\n"
|
||||
msgid ""
|
||||
"The organisation you are looking for may have been removed, renamed or may have never\n"
|
||||
" existed."
|
||||
msgstr "Die Organisation, nach der Sie suchen, wurde möglicherweise entfernt, umbenannt oder hat möglicherweise nie existiert."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/_layout.tsx
|
||||
msgid "The organisation you are looking for may have been removed, renamed or may have never\n"
|
||||
msgid ""
|
||||
"The organisation you are looking for may have been removed, renamed or may have never\n"
|
||||
" existed."
|
||||
msgstr "Die Organisation, nach der Sie suchen, wurde möglicherweise entfernt, umbenannt oder hat möglicherweise nie existiert."
|
||||
|
||||
@@ -6731,12 +6734,14 @@ msgid "The team email <0>{teamEmail}</0> has been removed from the following tea
|
||||
msgstr "Die Team-E-Mail <0>{teamEmail}</0> wurde aus dem folgenden Team entfernt"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/_layout.tsx
|
||||
msgid "The team you are looking for may have been removed, renamed or may have never\n"
|
||||
msgid ""
|
||||
"The team you are looking for may have been removed, renamed or may have never\n"
|
||||
" existed."
|
||||
msgstr "Das Team, das Sie suchen, wurde möglicherweise entfernt, umbenannt oder hat möglicherweise nie existiert."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/_layout.tsx
|
||||
msgid "The team you are looking for may have been removed, renamed or may have never\n"
|
||||
msgid ""
|
||||
"The team you are looking for may have been removed, renamed or may have never\n"
|
||||
" existed."
|
||||
msgstr "Das Team, das Sie suchen, könnte entfernt, umbenannt oder nie existiert haben."
|
||||
|
||||
@@ -6774,7 +6779,8 @@ msgid "The URL for Documenso to send webhook events to."
|
||||
msgstr "Die URL für Documenso, um Webhook-Ereignisse zu senden."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/users.$id.tsx
|
||||
msgid "The user you are looking for may have been removed, renamed or may have never\n"
|
||||
msgid ""
|
||||
"The user you are looking for may have been removed, renamed or may have never\n"
|
||||
" existed."
|
||||
msgstr "Der Benutzer, nach dem Sie suchen, wurde möglicherweise entfernt, umbenannt oder hat möglicherweise nie existiert."
|
||||
|
||||
@@ -6930,7 +6936,7 @@ msgstr "Dieses Feld kann nicht geändert oder gelöscht werden. Wenn Sie den dir
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-delete-dialog.tsx
|
||||
msgid "This folder contains multiple items. Deleting it will also delete all items in the folder, including nested folders and their contents."
|
||||
msgstr "Dieser Ordner enthält mehrere Elemente. Wenn Sie ihn löschen, werden auch alle Elemente im Ordner gelöscht, einschließlich verschachtelter Ordner und deren Inhalt."
|
||||
msgstr ""
|
||||
|
||||
#: packages/ui/primitives/template-flow/add-template-settings.tsx
|
||||
msgid "This is how the document will reach the recipients once the document is ready for signing."
|
||||
@@ -7012,7 +7018,7 @@ msgstr "Diese werden NUR Funktionsflags zurückspielen, die auf wahr gesetzt sin
|
||||
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
msgid "This will sign you out of all other devices. You will need to sign in again on those devices to continue using your account."
|
||||
msgstr "Dies meldet Sie auf allen anderen Geräten ab. Sie müssen sich erneut auf diesen Geräten anmelden, um Ihr Konto weiter zu nutzen."
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/tables/internal-audit-log-table.tsx
|
||||
#: apps/remix/app/components/tables/document-logs-table.tsx
|
||||
@@ -7316,7 +7322,7 @@ msgstr "Unbegrenzte Dokumente, API und mehr"
|
||||
|
||||
#: apps/remix/app/components/general/folder/folder-card.tsx
|
||||
msgid "Unpin"
|
||||
msgstr "Lösen"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
msgid "Untitled Group"
|
||||
@@ -7663,7 +7669,7 @@ msgstr "Sehen Sie sich alle Sicherheitsaktivitäten in Ihrem Konto an."
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security._index.tsx
|
||||
msgid "View and manage all active sessions for your account."
|
||||
msgstr "Alle aktiven Sitzungen Ihres Kontos anzeigen und verwalten."
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/forms/2fa/view-recovery-codes-dialog.tsx
|
||||
msgid "View Codes"
|
||||
@@ -8728,4 +8734,3 @@ msgstr "Ihr Token wurde erfolgreich erstellt! Stellen Sie sicher, dass Sie es ko
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.tokens.tsx
|
||||
msgid "Your tokens will be shown here once you create them."
|
||||
msgstr "Ihre Tokens werden hier angezeigt, sobald Sie sie erstellt haben."
|
||||
|
||||
|
||||
@@ -308,10 +308,6 @@ msgstr "{prefix} updated the document title"
|
||||
msgid "{prefix} updated the document visibility"
|
||||
msgstr "{prefix} updated the document visibility"
|
||||
|
||||
#: packages/lib/utils/document-audit-logs.ts
|
||||
msgid "{prefix} viewed the document"
|
||||
msgstr "{prefix} viewed the document"
|
||||
|
||||
#: apps/remix/app/components/general/direct-template/direct-template-page.tsx
|
||||
msgid "{recipientActionVerb} document"
|
||||
msgstr "{recipientActionVerb} document"
|
||||
@@ -1766,6 +1762,7 @@ msgstr "Click here to get started"
|
||||
#: apps/remix/app/components/tables/settings-public-profile-templates-table.tsx
|
||||
#: apps/remix/app/components/general/template/template-page-view-recent-activity.tsx
|
||||
#: apps/remix/app/components/general/document/document-page-view-recent-activity.tsx
|
||||
#: apps/remix/app/components/general/document/document-history-sheet.tsx
|
||||
msgid "Click here to retry"
|
||||
msgstr "Click here to retry"
|
||||
|
||||
@@ -2747,6 +2744,11 @@ msgstr "Document external ID updated"
|
||||
msgid "Document found in your account"
|
||||
msgstr "Document found in your account"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/documents.$id._index.tsx
|
||||
#: apps/remix/app/components/general/document/document-history-sheet.tsx
|
||||
msgid "Document history"
|
||||
msgstr "Document history"
|
||||
|
||||
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/audit-log.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/documents.$id.logs.tsx
|
||||
msgid "Document ID"
|
||||
@@ -2865,10 +2867,6 @@ msgstr "Document upload disabled due to unpaid invoices"
|
||||
msgid "Document uploaded"
|
||||
msgstr "Document uploaded"
|
||||
|
||||
#: packages/lib/utils/document-audit-logs.ts
|
||||
msgid "Document viewed"
|
||||
msgstr "Document viewed"
|
||||
|
||||
#: apps/remix/app/routes/_recipient+/sign.$token+/complete.tsx
|
||||
msgid "Document Viewed"
|
||||
msgstr "Document Viewed"
|
||||
@@ -3709,6 +3707,10 @@ msgstr "Hi, {userName} <0>({userEmail})</0>"
|
||||
msgid "Hide"
|
||||
msgstr "Hide"
|
||||
|
||||
#: apps/remix/app/components/general/document/document-history-sheet.tsx
|
||||
msgid "Hide additional information"
|
||||
msgstr "Hide additional information"
|
||||
|
||||
#: apps/remix/app/components/general/generic-error-layout.tsx
|
||||
#: apps/remix/app/components/general/folder/folder-grid.tsx
|
||||
#: apps/remix/app/components/dialogs/folder-move-dialog.tsx
|
||||
@@ -4132,10 +4134,10 @@ msgstr "Manage subscription"
|
||||
msgid "Manage the {0} organisation"
|
||||
msgstr "Manage the {0} organisation"
|
||||
|
||||
#. placeholder {1}: organisation.name
|
||||
#. placeholder {0}: organisation.name
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/organisations.$id.tsx
|
||||
msgid "Manage the {1} organisation subscription"
|
||||
msgstr "Manage the {1} organisation subscription"
|
||||
msgid "Manage the {0} organisation subscription"
|
||||
msgstr "Manage the {0} organisation subscription"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.groups._index.tsx
|
||||
msgid "Manage the custom groups of members for your organisation."
|
||||
@@ -5807,6 +5809,10 @@ msgstr "Share your signing experience!"
|
||||
msgid "Show"
|
||||
msgstr "Show"
|
||||
|
||||
#: apps/remix/app/components/general/document/document-history-sheet.tsx
|
||||
msgid "Show additional information"
|
||||
msgstr "Show additional information"
|
||||
|
||||
#: packages/ui/primitives/template-flow/add-template-placeholder-recipients.tsx
|
||||
#: packages/ui/primitives/document-flow/add-signers.tsx
|
||||
msgid "Show advanced settings"
|
||||
@@ -7248,6 +7254,7 @@ msgid "Unable to join this organisation at this time."
|
||||
msgstr "Unable to join this organisation at this time."
|
||||
|
||||
#: apps/remix/app/components/general/document/document-page-view-recent-activity.tsx
|
||||
#: apps/remix/app/components/general/document/document-history-sheet.tsx
|
||||
msgid "Unable to load document history"
|
||||
msgstr "Unable to load document history"
|
||||
|
||||
@@ -8016,8 +8023,11 @@ msgstr "We were unable to verify your email at this time."
|
||||
msgid "We were unable to verify your email. If your email is not verified already, please try again."
|
||||
msgstr "We were unable to verify your email. If your email is not verified already, please try again."
|
||||
|
||||
#: apps/remix/app/components/dialogs/template-use-dialog.tsx
|
||||
#: packages/ui/primitives/document-flow/add-subject.tsx
|
||||
msgid "We will generate signing links for with you, which you can send to the recipients through your method of choice."
|
||||
msgstr "We will generate signing links for with you, which you can send to the recipients through your method of choice."
|
||||
|
||||
#: apps/remix/app/components/dialogs/template-use-dialog.tsx
|
||||
msgid "We will generate signing links for you, which you can send to the recipients through your method of choice."
|
||||
msgstr "We will generate signing links for you, which you can send to the recipients through your method of choice."
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: es\n"
|
||||
"Project-Id-Version: documenso-app\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2025-07-14 04:19\n"
|
||||
"PO-Revision-Date: 2025-06-10 02:27\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Spanish\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
@@ -75,12 +75,12 @@ msgstr "{0, plural, one {# carácter sobre el límite} other {# caracteres sobre
|
||||
#. placeholder {0}: folder._count.documents
|
||||
#: apps/remix/app/components/general/folder/folder-card.tsx
|
||||
msgid "{0, plural, one {# document} other {# documents}}"
|
||||
msgstr "{0, plural, one {# documento} other {# documentos}}"
|
||||
msgstr ""
|
||||
|
||||
#. placeholder {0}: folder._count.subfolders
|
||||
#: apps/remix/app/components/general/folder/folder-card.tsx
|
||||
msgid "{0, plural, one {# folder} other {# folders}}"
|
||||
msgstr "{0, plural, one {# carpeta} other {# carpetas}}"
|
||||
msgstr ""
|
||||
|
||||
#. placeholder {0}: template.recipients.length
|
||||
#: apps/remix/app/routes/_recipient+/d.$token+/_index.tsx
|
||||
@@ -95,7 +95,7 @@ msgstr "{0, plural, one {# equipo} other {# equipos}}"
|
||||
#. placeholder {0}: folder._count.templates
|
||||
#: apps/remix/app/components/general/folder/folder-card.tsx
|
||||
msgid "{0, plural, one {# template} other {# templates}}"
|
||||
msgstr "{0, plural, one {# plantilla} other {# plantillas}}"
|
||||
msgstr ""
|
||||
|
||||
#. placeholder {0}: data.length
|
||||
#: apps/remix/app/components/general/organisations/organisation-invitations.tsx
|
||||
@@ -770,7 +770,7 @@ msgstr "Activo"
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security._index.tsx
|
||||
msgid "Active sessions"
|
||||
msgstr "Sesiones activas"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/stats.tsx
|
||||
msgid "Active Subscriptions"
|
||||
@@ -1341,7 +1341,7 @@ msgstr "¿Estás seguro de que quieres eliminar la siguiente solicitud?"
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-delete-dialog.tsx
|
||||
msgid "Are you sure you want to delete this folder?"
|
||||
msgstr "¿Está seguro de que quiere eliminar esta carpeta?"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/token-delete-dialog.tsx
|
||||
msgid "Are you sure you want to delete this token?"
|
||||
@@ -1693,7 +1693,7 @@ msgstr "Con copia"
|
||||
|
||||
#: packages/lib/constants/recipient-roles.ts
|
||||
msgid "Ccers"
|
||||
msgstr "Firmantes"
|
||||
msgstr ""
|
||||
|
||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx
|
||||
msgid "Character Limit"
|
||||
@@ -2161,11 +2161,11 @@ msgstr "Crear documento a partir de la plantilla"
|
||||
|
||||
#: apps/remix/app/components/general/folder/folder-card.tsx
|
||||
msgid "Create folder"
|
||||
msgstr "Crear carpeta"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-create-dialog.tsx
|
||||
msgid "Create Folder"
|
||||
msgstr "Crear Carpeta"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/organisation-group-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/organisation-group-create-dialog.tsx
|
||||
@@ -2178,7 +2178,7 @@ msgstr "Crear Grupos"
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-create-dialog.tsx
|
||||
msgid "Create New Folder"
|
||||
msgstr "Crear Nueva Carpeta"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/_profile+/_layout.tsx
|
||||
msgid "Create now"
|
||||
@@ -2300,11 +2300,11 @@ msgstr "Estructura CSV"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/stats.tsx
|
||||
msgid "Cumulative MAU (signed in)"
|
||||
msgstr "MAU acumulativo (con sesión iniciada)"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
msgid "Current"
|
||||
msgstr "Actual"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/forms/password.tsx
|
||||
msgid "Current Password"
|
||||
@@ -2444,7 +2444,7 @@ msgstr "Eliminar Documento"
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-delete-dialog.tsx
|
||||
msgid "Delete Folder"
|
||||
msgstr "Eliminar Carpeta"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.general.tsx
|
||||
msgid "Delete organisation"
|
||||
@@ -3192,7 +3192,7 @@ msgstr "Documento Adjunto"
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-create-dialog.tsx
|
||||
msgid "Enter a name for your new folder. Folders help you organise your items."
|
||||
msgstr "Ingrese un nombre para su nueva carpeta. Las carpetas le ayudan a organizar sus elementos."
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/forms/subscription-claim-form.tsx
|
||||
msgid "Enter claim name"
|
||||
@@ -3332,7 +3332,7 @@ msgstr "Error al crear reclamación de suscripción."
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-delete-dialog.tsx
|
||||
msgid "Failed to delete folder"
|
||||
msgstr "Error al eliminar la carpeta"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/claim-delete-dialog.tsx
|
||||
msgid "Failed to delete subscription claim."
|
||||
@@ -3344,7 +3344,7 @@ msgstr "Error al cargar el documento"
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-move-dialog.tsx
|
||||
msgid "Failed to move folder"
|
||||
msgstr "Error al mover la carpeta"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/documents.$id.tsx
|
||||
msgid "Failed to reseal document"
|
||||
@@ -3352,7 +3352,7 @@ msgstr "Falló al volver a sellar el documento"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
msgid "Failed to revoke session"
|
||||
msgstr "Error al revocar la sesión"
|
||||
msgstr ""
|
||||
|
||||
#: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx
|
||||
msgid "Failed to save settings."
|
||||
@@ -3360,7 +3360,7 @@ msgstr "Fallo al guardar configuraciones."
|
||||
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
msgid "Failed to sign out all sessions"
|
||||
msgstr "Error al cerrar sesión en todas las sesiones"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/embed+/v1+/authoring+/document.edit.$id.tsx
|
||||
msgid "Failed to update document"
|
||||
@@ -3464,15 +3464,15 @@ msgstr "Carpeta creada exitosamente"
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-delete-dialog.tsx
|
||||
msgid "Folder deleted successfully"
|
||||
msgstr "Carpeta eliminada correctamente"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-move-dialog.tsx
|
||||
msgid "Folder moved successfully"
|
||||
msgstr "Carpeta movida correctamente"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-create-dialog.tsx
|
||||
msgid "Folder Name"
|
||||
msgstr "Nombre de la Carpeta"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-settings-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/folder-move-dialog.tsx
|
||||
@@ -3725,7 +3725,7 @@ msgstr "Inicio"
|
||||
#: apps/remix/app/components/dialogs/template-move-to-folder-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/document-move-to-folder-dialog.tsx
|
||||
msgid "Home (No Folder)"
|
||||
msgstr "Inicio (Sin Carpeta)"
|
||||
msgstr ""
|
||||
|
||||
#: packages/lib/constants/recipient-roles.ts
|
||||
msgid "I am a signer of this document"
|
||||
@@ -3974,7 +3974,7 @@ msgstr "Últimos 7 días"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
msgid "Last Active"
|
||||
msgstr "Última actividad"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/general/template/template-page-view-information.tsx
|
||||
#: apps/remix/app/components/general/document/document-page-view-information.tsx
|
||||
@@ -4127,7 +4127,7 @@ msgstr "Gestiona permisos y controles de acceso"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security._index.tsx
|
||||
msgid "Manage sessions"
|
||||
msgstr "Gestionar sesiones"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/organisations.$id.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/organisations.$id.tsx
|
||||
@@ -4208,7 +4208,7 @@ msgstr "MAU (documento completado)"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/stats.tsx
|
||||
msgid "MAU (signed in)"
|
||||
msgstr "MAU (con sesión iniciada)"
|
||||
msgstr ""
|
||||
|
||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx
|
||||
msgid "Max"
|
||||
@@ -4297,7 +4297,7 @@ msgstr "Mover Documento a Carpeta"
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-move-dialog.tsx
|
||||
msgid "Move Folder"
|
||||
msgstr "Mover Carpeta"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/template-move-to-folder-dialog.tsx
|
||||
msgid "Move Template to Folder"
|
||||
@@ -4314,7 +4314,7 @@ msgstr "Se pueden seleccionar varios métodos de acceso."
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-create-dialog.tsx
|
||||
msgid "My Folder"
|
||||
msgstr "Mi Carpeta"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/users.$id.tsx
|
||||
#: apps/remix/app/components/tables/settings-security-passkey-table.tsx
|
||||
@@ -4407,12 +4407,12 @@ msgstr "No hay borradores activos"
|
||||
#: apps/remix/app/components/dialogs/template-move-to-folder-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/document-move-to-folder-dialog.tsx
|
||||
msgid "No folders found"
|
||||
msgstr "No se encontraron carpetas"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/templates.folders._index.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/documents.folders._index.tsx
|
||||
msgid "No folders found matching \"{searchTerm}\""
|
||||
msgstr "No se encontraron carpetas que coincidan con \"{searchTerm}\""
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/_recipient+/sign.$token+/rejected.tsx
|
||||
#: apps/remix/app/components/embed/embed-document-rejected.tsx
|
||||
@@ -4725,7 +4725,7 @@ msgstr "Organizaciones de las que el usuario es miembro."
|
||||
|
||||
#: apps/remix/app/components/general/folder/folder-card.tsx
|
||||
msgid "Organise your documents"
|
||||
msgstr "Organiza tus documentos"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/organisation-group-create-dialog.tsx
|
||||
msgid "Organise your members into groups which can be assigned to teams"
|
||||
@@ -4733,7 +4733,7 @@ msgstr "Organiza a tus miembros en grupos que se puedan asignar a equipos"
|
||||
|
||||
#: apps/remix/app/components/general/folder/folder-card.tsx
|
||||
msgid "Organise your templates"
|
||||
msgstr "Organiza tus plantillas"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/o.$orgUrl._index.tsx
|
||||
msgid "Organize your documents and templates"
|
||||
@@ -4912,7 +4912,7 @@ msgstr "Elige cualquiera de los siguientes acuerdos a continuación y comience a
|
||||
|
||||
#: apps/remix/app/components/general/folder/folder-card.tsx
|
||||
msgid "Pin"
|
||||
msgstr "Fijar"
|
||||
msgstr ""
|
||||
|
||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx
|
||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx
|
||||
@@ -5468,7 +5468,7 @@ msgstr "Revocar acceso"
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
msgid "Revoke all sessions"
|
||||
msgstr "Revocar todas las sesiones"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/tables/user-organisations-table.tsx
|
||||
#: apps/remix/app/components/tables/team-members-table.tsx
|
||||
@@ -5546,7 +5546,7 @@ msgstr "Buscar documentos..."
|
||||
#: apps/remix/app/components/dialogs/folder-move-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/document-move-to-folder-dialog.tsx
|
||||
msgid "Search folders..."
|
||||
msgstr "Buscar carpetas..."
|
||||
msgstr ""
|
||||
|
||||
#: packages/ui/components/common/language-switcher-dialog.tsx
|
||||
msgid "Search languages..."
|
||||
@@ -5574,7 +5574,7 @@ msgstr "Seleccionar"
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-move-dialog.tsx
|
||||
msgid "Select a destination for this folder."
|
||||
msgstr "Selecciona un destino para esta carpeta."
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/document-move-to-folder-dialog.tsx
|
||||
msgid "Select a folder to move this document to."
|
||||
@@ -5631,7 +5631,7 @@ msgstr "Seleccionar grupos"
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
msgid "Select groups of members to add to the team."
|
||||
msgstr "Seleccionar grupos de miembros para añadir al equipo."
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
msgid "Select groups to add to this team"
|
||||
@@ -5755,11 +5755,11 @@ msgstr "Enviado"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
msgid "Session revoked"
|
||||
msgstr "Sesión revocada"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
msgid "Sessions have been revoked"
|
||||
msgstr "Las sesiones han sido revocadas"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/general/claim-account.tsx
|
||||
msgid "Set a password"
|
||||
@@ -6633,7 +6633,8 @@ msgid "The following team has been deleted. You will no longer be able to access
|
||||
msgstr "El siguiente equipo ha sido eliminado. Ya no podrá acceder a este equipo y sus documentos"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.groups.$id.tsx
|
||||
msgid "The organisation group you are looking for may have been removed, renamed or may have never\n"
|
||||
msgid ""
|
||||
"The organisation group you are looking for may have been removed, renamed or may have never\n"
|
||||
" existed."
|
||||
msgstr "El grupo de organización que está buscando puede haber sido eliminado, renombrado o puede que nunca haya existido."
|
||||
|
||||
@@ -6642,12 +6643,14 @@ msgid "The organisation role that will be applied to all members in this group."
|
||||
msgstr "El rol de organización que se aplicará a todos los miembros de este grupo."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/organisations.$id.tsx
|
||||
msgid "The organisation you are looking for may have been removed, renamed or may have never\n"
|
||||
msgid ""
|
||||
"The organisation you are looking for may have been removed, renamed or may have never\n"
|
||||
" existed."
|
||||
msgstr "La organización que está buscando puede haber sido eliminada, renombrada o puede que nunca haya existido."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/_layout.tsx
|
||||
msgid "The organisation you are looking for may have been removed, renamed or may have never\n"
|
||||
msgid ""
|
||||
"The organisation you are looking for may have been removed, renamed or may have never\n"
|
||||
" existed."
|
||||
msgstr "La organización que está buscando puede haber sido eliminada, renombrada o puede que nunca haya existido."
|
||||
|
||||
@@ -6731,14 +6734,17 @@ msgid "The team email <0>{teamEmail}</0> has been removed from the following tea
|
||||
msgstr "El correo electrónico del equipo <0>{teamEmail}</0> ha sido eliminado del siguiente equipo"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/_layout.tsx
|
||||
msgid "The team you are looking for may have been removed, renamed or may have never\n"
|
||||
msgid ""
|
||||
"The team you are looking for may have been removed, renamed or may have never\n"
|
||||
" existed."
|
||||
msgstr "El equipo que está buscando puede haber sido eliminado, renombrado o puede que nunca haya existido."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/_layout.tsx
|
||||
msgid "The team you are looking for may have been removed, renamed or may have never\n"
|
||||
msgid ""
|
||||
"The team you are looking for may have been removed, renamed or may have never\n"
|
||||
" existed."
|
||||
msgstr "El equipo que buscas puede haber sido eliminado, renombrado o quizás nunca\n"
|
||||
msgstr ""
|
||||
"El equipo que buscas puede haber sido eliminado, renombrado o quizás nunca\n"
|
||||
" existió."
|
||||
|
||||
#: apps/remix/app/components/dialogs/template-move-to-folder-dialog.tsx
|
||||
@@ -6775,7 +6781,8 @@ msgid "The URL for Documenso to send webhook events to."
|
||||
msgstr "La URL para Documenso para enviar eventos de webhook."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/users.$id.tsx
|
||||
msgid "The user you are looking for may have been removed, renamed or may have never\n"
|
||||
msgid ""
|
||||
"The user you are looking for may have been removed, renamed or may have never\n"
|
||||
" existed."
|
||||
msgstr "El usuario que está buscando puede haber sido eliminado, renombrado o puede que nunca haya existido."
|
||||
|
||||
@@ -6931,7 +6938,7 @@ msgstr "Este campo no se puede modificar ni eliminar. Cuando comparta el enlace
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-delete-dialog.tsx
|
||||
msgid "This folder contains multiple items. Deleting it will also delete all items in the folder, including nested folders and their contents."
|
||||
msgstr "Esta carpeta contiene múltiples elementos. Eliminándola también se eliminarán todos los elementos de la carpeta, incluidas las carpetas anidadas y sus contenidos."
|
||||
msgstr ""
|
||||
|
||||
#: packages/ui/primitives/template-flow/add-template-settings.tsx
|
||||
msgid "This is how the document will reach the recipients once the document is ready for signing."
|
||||
@@ -7013,7 +7020,7 @@ msgstr "Esto solo retroalimentará las banderas de características que estén c
|
||||
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
msgid "This will sign you out of all other devices. You will need to sign in again on those devices to continue using your account."
|
||||
msgstr "Esto cerrará la sesión en todos los demás dispositivos. Necesitarás iniciar sesión nuevamente en esos dispositivos para continuar usando tu cuenta."
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/tables/internal-audit-log-table.tsx
|
||||
#: apps/remix/app/components/tables/document-logs-table.tsx
|
||||
@@ -7317,7 +7324,7 @@ msgstr "Documentos ilimitados, API y más"
|
||||
|
||||
#: apps/remix/app/components/general/folder/folder-card.tsx
|
||||
msgid "Unpin"
|
||||
msgstr "Desanclar"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
msgid "Untitled Group"
|
||||
@@ -7664,7 +7671,7 @@ msgstr "Ver toda la actividad de seguridad relacionada con tu cuenta."
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security._index.tsx
|
||||
msgid "View and manage all active sessions for your account."
|
||||
msgstr "Ver y gestionar todas las sesiones activas de tu cuenta."
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/forms/2fa/view-recovery-codes-dialog.tsx
|
||||
msgid "View Codes"
|
||||
@@ -8729,4 +8736,3 @@ msgstr "¡Tu token se creó con éxito! ¡Asegúrate de copiarlo porque no podr
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.tokens.tsx
|
||||
msgid "Your tokens will be shown here once you create them."
|
||||
msgstr "Tus tokens se mostrarán aquí una vez que los crees."
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: fr\n"
|
||||
"Project-Id-Version: documenso-app\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2025-06-19 06:05\n"
|
||||
"PO-Revision-Date: 2025-06-10 02:27\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: French\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||
@@ -75,12 +75,12 @@ msgstr "{0, plural, one {# caractère au-dessus de la limite} other {# caractèr
|
||||
#. placeholder {0}: folder._count.documents
|
||||
#: apps/remix/app/components/general/folder/folder-card.tsx
|
||||
msgid "{0, plural, one {# document} other {# documents}}"
|
||||
msgstr "{0, plural, one {# document} other {# documents}}"
|
||||
msgstr ""
|
||||
|
||||
#. placeholder {0}: folder._count.subfolders
|
||||
#: apps/remix/app/components/general/folder/folder-card.tsx
|
||||
msgid "{0, plural, one {# folder} other {# folders}}"
|
||||
msgstr "{0, plural, one {# dossier} other {# dossiers}}"
|
||||
msgstr ""
|
||||
|
||||
#. placeholder {0}: template.recipients.length
|
||||
#: apps/remix/app/routes/_recipient+/d.$token+/_index.tsx
|
||||
@@ -95,7 +95,7 @@ msgstr "{0, plural, one {# équipe} other {# équipes}}"
|
||||
#. placeholder {0}: folder._count.templates
|
||||
#: apps/remix/app/components/general/folder/folder-card.tsx
|
||||
msgid "{0, plural, one {# template} other {# templates}}"
|
||||
msgstr "{0, plural, one {# modèle} other {# modèles}}"
|
||||
msgstr ""
|
||||
|
||||
#. placeholder {0}: data.length
|
||||
#: apps/remix/app/components/general/organisations/organisation-invitations.tsx
|
||||
@@ -770,7 +770,7 @@ msgstr "Actif"
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security._index.tsx
|
||||
msgid "Active sessions"
|
||||
msgstr "Sessions actives"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/stats.tsx
|
||||
msgid "Active Subscriptions"
|
||||
@@ -1341,7 +1341,7 @@ msgstr "Êtes-vous sûr de vouloir supprimer la réclamation suivante?"
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-delete-dialog.tsx
|
||||
msgid "Are you sure you want to delete this folder?"
|
||||
msgstr "Êtes-vous sûr de vouloir supprimer ce dossier ?"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/token-delete-dialog.tsx
|
||||
msgid "Are you sure you want to delete this token?"
|
||||
@@ -2161,11 +2161,11 @@ msgstr "Créer un document à partir du modèle"
|
||||
|
||||
#: apps/remix/app/components/general/folder/folder-card.tsx
|
||||
msgid "Create folder"
|
||||
msgstr "Créer un dossier"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-create-dialog.tsx
|
||||
msgid "Create Folder"
|
||||
msgstr "Créer un Dossier"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/organisation-group-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/organisation-group-create-dialog.tsx
|
||||
@@ -2178,7 +2178,7 @@ msgstr "Créer des groupes"
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-create-dialog.tsx
|
||||
msgid "Create New Folder"
|
||||
msgstr "Créer un Nouveau Dossier"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/_profile+/_layout.tsx
|
||||
msgid "Create now"
|
||||
@@ -2300,11 +2300,11 @@ msgstr "Structure CSV"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/stats.tsx
|
||||
msgid "Cumulative MAU (signed in)"
|
||||
msgstr "MAU cumulatif (connecté)"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
msgid "Current"
|
||||
msgstr "Actuel"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/forms/password.tsx
|
||||
msgid "Current Password"
|
||||
@@ -2444,7 +2444,7 @@ msgstr "Supprimer le document"
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-delete-dialog.tsx
|
||||
msgid "Delete Folder"
|
||||
msgstr "Supprimer le Dossier"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.general.tsx
|
||||
msgid "Delete organisation"
|
||||
@@ -3192,7 +3192,7 @@ msgstr "Document joint"
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-create-dialog.tsx
|
||||
msgid "Enter a name for your new folder. Folders help you organise your items."
|
||||
msgstr "Entrez un nom pour votre nouveau dossier. Les dossiers vous aident à organiser vos éléments."
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/forms/subscription-claim-form.tsx
|
||||
msgid "Enter claim name"
|
||||
@@ -3332,7 +3332,7 @@ msgstr "Échec de la création de la réclamation d'abonnement."
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-delete-dialog.tsx
|
||||
msgid "Failed to delete folder"
|
||||
msgstr "Échec de la suppression du dossier"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/claim-delete-dialog.tsx
|
||||
msgid "Failed to delete subscription claim."
|
||||
@@ -3344,7 +3344,7 @@ msgstr "Échec du chargement du document"
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-move-dialog.tsx
|
||||
msgid "Failed to move folder"
|
||||
msgstr "Échec du déplacement du dossier"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/documents.$id.tsx
|
||||
msgid "Failed to reseal document"
|
||||
@@ -3352,7 +3352,7 @@ msgstr "Échec du reseal du document"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
msgid "Failed to revoke session"
|
||||
msgstr "Échec de la révocation de la session"
|
||||
msgstr ""
|
||||
|
||||
#: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx
|
||||
msgid "Failed to save settings."
|
||||
@@ -3360,7 +3360,7 @@ msgstr "Échec de l'enregistrement des paramètres."
|
||||
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
msgid "Failed to sign out all sessions"
|
||||
msgstr "Impossible de se déconnecter de toutes les sessions"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/embed+/v1+/authoring+/document.edit.$id.tsx
|
||||
msgid "Failed to update document"
|
||||
@@ -3464,15 +3464,15 @@ msgstr "Dossier créé avec succès"
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-delete-dialog.tsx
|
||||
msgid "Folder deleted successfully"
|
||||
msgstr "Dossier supprimé avec succès"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-move-dialog.tsx
|
||||
msgid "Folder moved successfully"
|
||||
msgstr "Dossier déplacé avec succès"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-create-dialog.tsx
|
||||
msgid "Folder Name"
|
||||
msgstr "Nom du Dossier"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-settings-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/folder-move-dialog.tsx
|
||||
@@ -3725,7 +3725,7 @@ msgstr "Accueil"
|
||||
#: apps/remix/app/components/dialogs/template-move-to-folder-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/document-move-to-folder-dialog.tsx
|
||||
msgid "Home (No Folder)"
|
||||
msgstr "Accueil (Pas de Dossier)"
|
||||
msgstr ""
|
||||
|
||||
#: packages/lib/constants/recipient-roles.ts
|
||||
msgid "I am a signer of this document"
|
||||
@@ -3974,7 +3974,7 @@ msgstr "7 derniers jours"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
msgid "Last Active"
|
||||
msgstr "Dernière activité"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/general/template/template-page-view-information.tsx
|
||||
#: apps/remix/app/components/general/document/document-page-view-information.tsx
|
||||
@@ -4127,7 +4127,7 @@ msgstr "Gérez les autorisations et les contrôles d'accès"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security._index.tsx
|
||||
msgid "Manage sessions"
|
||||
msgstr "Gérer les sessions"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/organisations.$id.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/organisations.$id.tsx
|
||||
@@ -4208,7 +4208,7 @@ msgstr "MAU (document terminé)"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/stats.tsx
|
||||
msgid "MAU (signed in)"
|
||||
msgstr "MAU (connecté)"
|
||||
msgstr ""
|
||||
|
||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx
|
||||
msgid "Max"
|
||||
@@ -4297,7 +4297,7 @@ msgstr "Déplacer le document vers un dossier"
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-move-dialog.tsx
|
||||
msgid "Move Folder"
|
||||
msgstr "Déplacer le Dossier"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/template-move-to-folder-dialog.tsx
|
||||
msgid "Move Template to Folder"
|
||||
@@ -4314,7 +4314,7 @@ msgstr "Plusieurs méthodes d'accès peuvent être sélectionnées."
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-create-dialog.tsx
|
||||
msgid "My Folder"
|
||||
msgstr "Mon Dossier"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/users.$id.tsx
|
||||
#: apps/remix/app/components/tables/settings-security-passkey-table.tsx
|
||||
@@ -4407,12 +4407,12 @@ msgstr "Pas de brouillons actifs"
|
||||
#: apps/remix/app/components/dialogs/template-move-to-folder-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/document-move-to-folder-dialog.tsx
|
||||
msgid "No folders found"
|
||||
msgstr "Aucun dossier trouvé"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/templates.folders._index.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/documents.folders._index.tsx
|
||||
msgid "No folders found matching \"{searchTerm}\""
|
||||
msgstr "Aucun dossier correspondant à \"{searchTerm}\" trouvé"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/_recipient+/sign.$token+/rejected.tsx
|
||||
#: apps/remix/app/components/embed/embed-document-rejected.tsx
|
||||
@@ -4725,7 +4725,7 @@ msgstr "Organisations dont l'utilisateur est membre."
|
||||
|
||||
#: apps/remix/app/components/general/folder/folder-card.tsx
|
||||
msgid "Organise your documents"
|
||||
msgstr "Organisez vos documents"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/organisation-group-create-dialog.tsx
|
||||
msgid "Organise your members into groups which can be assigned to teams"
|
||||
@@ -4733,7 +4733,7 @@ msgstr "Organisez vos membres en groupes qui peuvent être assignés à des équ
|
||||
|
||||
#: apps/remix/app/components/general/folder/folder-card.tsx
|
||||
msgid "Organise your templates"
|
||||
msgstr "Organisez vos modèles"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/o.$orgUrl._index.tsx
|
||||
msgid "Organize your documents and templates"
|
||||
@@ -4912,7 +4912,7 @@ msgstr "Choisissez l'un des accords suivants ci-dessous et commencez à signer p
|
||||
|
||||
#: apps/remix/app/components/general/folder/folder-card.tsx
|
||||
msgid "Pin"
|
||||
msgstr "Épingler"
|
||||
msgstr ""
|
||||
|
||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx
|
||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx
|
||||
@@ -5468,7 +5468,7 @@ msgstr "Révoquer l'accès"
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
msgid "Revoke all sessions"
|
||||
msgstr "Révoquer toutes les sessions"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/tables/user-organisations-table.tsx
|
||||
#: apps/remix/app/components/tables/team-members-table.tsx
|
||||
@@ -5546,7 +5546,7 @@ msgstr "Rechercher des documents..."
|
||||
#: apps/remix/app/components/dialogs/folder-move-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/document-move-to-folder-dialog.tsx
|
||||
msgid "Search folders..."
|
||||
msgstr "Rechercher dans les dossiers..."
|
||||
msgstr ""
|
||||
|
||||
#: packages/ui/components/common/language-switcher-dialog.tsx
|
||||
msgid "Search languages..."
|
||||
@@ -5574,7 +5574,7 @@ msgstr "Sélectionner"
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-move-dialog.tsx
|
||||
msgid "Select a destination for this folder."
|
||||
msgstr "Sélectionnez une destination pour ce dossier."
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/document-move-to-folder-dialog.tsx
|
||||
msgid "Select a folder to move this document to."
|
||||
@@ -5631,7 +5631,7 @@ msgstr "Sélectionnez des groupes"
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
msgid "Select groups of members to add to the team."
|
||||
msgstr "Sélectionnez des groupes de membres à ajouter à l'équipe."
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
msgid "Select groups to add to this team"
|
||||
@@ -5755,11 +5755,11 @@ msgstr "Envoyé"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
msgid "Session revoked"
|
||||
msgstr "Session révoquée"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
msgid "Sessions have been revoked"
|
||||
msgstr "Les sessions ont été révoquées"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/general/claim-account.tsx
|
||||
msgid "Set a password"
|
||||
@@ -6633,7 +6633,8 @@ msgid "The following team has been deleted. You will no longer be able to access
|
||||
msgstr "L'équipe suivante a été supprimée. Vous ne pourrez plus accéder à cette équipe et à ses documents"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.groups.$id.tsx
|
||||
msgid "The organisation group you are looking for may have been removed, renamed or may have never\n"
|
||||
msgid ""
|
||||
"The organisation group you are looking for may have been removed, renamed or may have never\n"
|
||||
" existed."
|
||||
msgstr "Le groupe d'organisation que vous cherchez peut avoir été supprimé, renommé ou n'a peut-être jamais existé."
|
||||
|
||||
@@ -6642,12 +6643,14 @@ msgid "The organisation role that will be applied to all members in this group."
|
||||
msgstr "Le rôle d'organisation qui sera appliqué à tous les membres de ce groupe."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/organisations.$id.tsx
|
||||
msgid "The organisation you are looking for may have been removed, renamed or may have never\n"
|
||||
msgid ""
|
||||
"The organisation you are looking for may have been removed, renamed or may have never\n"
|
||||
" existed."
|
||||
msgstr "L'organisation que vous cherchez peut avoir été supprimée, renommée ou n'a peut-être jamais existé."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/_layout.tsx
|
||||
msgid "The organisation you are looking for may have been removed, renamed or may have never\n"
|
||||
msgid ""
|
||||
"The organisation you are looking for may have been removed, renamed or may have never\n"
|
||||
" existed."
|
||||
msgstr "L'organisation que vous cherchez peut avoir été supprimée, renommée ou n'a peut-être jamais existé."
|
||||
|
||||
@@ -6731,12 +6734,14 @@ msgid "The team email <0>{teamEmail}</0> has been removed from the following tea
|
||||
msgstr "L'email d'équipe <0>{teamEmail}</0> a été supprimé de l'équipe suivante"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/_layout.tsx
|
||||
msgid "The team you are looking for may have been removed, renamed or may have never\n"
|
||||
msgid ""
|
||||
"The team you are looking for may have been removed, renamed or may have never\n"
|
||||
" existed."
|
||||
msgstr "L'équipe que vous cherchez peut avoir été supprimée, renommée ou n'a peut-être jamais existé."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/_layout.tsx
|
||||
msgid "The team you are looking for may have been removed, renamed or may have never\n"
|
||||
msgid ""
|
||||
"The team you are looking for may have been removed, renamed or may have never\n"
|
||||
" existed."
|
||||
msgstr "L'équipe que vous cherchez a peut-être été supprimée, renommée ou n'a peut-être jamais existé."
|
||||
|
||||
@@ -6774,7 +6779,8 @@ msgid "The URL for Documenso to send webhook events to."
|
||||
msgstr "L'URL pour Documenso pour envoyer des événements webhook."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/users.$id.tsx
|
||||
msgid "The user you are looking for may have been removed, renamed or may have never\n"
|
||||
msgid ""
|
||||
"The user you are looking for may have been removed, renamed or may have never\n"
|
||||
" existed."
|
||||
msgstr "L'utilisateur que vous cherchez peut avoir été supprimé, renommé ou n'a peut-être jamais existé."
|
||||
|
||||
@@ -6930,7 +6936,7 @@ msgstr "Ce champ ne peut pas être modifié ou supprimé. Lorsque vous partagez
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-delete-dialog.tsx
|
||||
msgid "This folder contains multiple items. Deleting it will also delete all items in the folder, including nested folders and their contents."
|
||||
msgstr "Ce dossier contient plusieurs éléments. Le supprimer supprimera également tous les éléments du dossier, y compris les dossiers imbriqués et leur contenu."
|
||||
msgstr ""
|
||||
|
||||
#: packages/ui/primitives/template-flow/add-template-settings.tsx
|
||||
msgid "This is how the document will reach the recipients once the document is ready for signing."
|
||||
@@ -7012,7 +7018,7 @@ msgstr "Cela ne fera que rétroporter les drapeaux de fonctionnalité qui sont a
|
||||
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
msgid "This will sign you out of all other devices. You will need to sign in again on those devices to continue using your account."
|
||||
msgstr "Cela entraînera votre déconnexion de tous les autres appareils. Vous devrez vous reconnecter sur ces appareils pour continuer à utiliser votre compte."
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/tables/internal-audit-log-table.tsx
|
||||
#: apps/remix/app/components/tables/document-logs-table.tsx
|
||||
@@ -7316,7 +7322,7 @@ msgstr "Documents illimités, API et plus"
|
||||
|
||||
#: apps/remix/app/components/general/folder/folder-card.tsx
|
||||
msgid "Unpin"
|
||||
msgstr "Détacher"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
msgid "Untitled Group"
|
||||
@@ -7663,7 +7669,7 @@ msgstr "Voir toute l'activité de sécurité liée à votre compte."
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security._index.tsx
|
||||
msgid "View and manage all active sessions for your account."
|
||||
msgstr "Afficher et gérer toutes les sessions actives de votre compte."
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/forms/2fa/view-recovery-codes-dialog.tsx
|
||||
msgid "View Codes"
|
||||
@@ -8728,4 +8734,3 @@ msgstr "Votre token a été créé avec succès ! Assurez-vous de le copier car
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.tokens.tsx
|
||||
msgid "Your tokens will be shown here once you create them."
|
||||
msgstr "Vos tokens seront affichés ici une fois que vous les aurez créés."
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: it\n"
|
||||
"Project-Id-Version: documenso-app\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2025-07-14 04:19\n"
|
||||
"PO-Revision-Date: 2025-06-10 02:27\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Italian\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
@@ -75,12 +75,12 @@ msgstr "{0, plural, one {# carattere oltre il limite} other {# caratteri oltre i
|
||||
#. placeholder {0}: folder._count.documents
|
||||
#: apps/remix/app/components/general/folder/folder-card.tsx
|
||||
msgid "{0, plural, one {# document} other {# documents}}"
|
||||
msgstr "{0, plural, one {# documento} other {# documenti}}"
|
||||
msgstr ""
|
||||
|
||||
#. placeholder {0}: folder._count.subfolders
|
||||
#: apps/remix/app/components/general/folder/folder-card.tsx
|
||||
msgid "{0, plural, one {# folder} other {# folders}}"
|
||||
msgstr "{0, plural, one {# cartella} other {# cartelle}}"
|
||||
msgstr ""
|
||||
|
||||
#. placeholder {0}: template.recipients.length
|
||||
#: apps/remix/app/routes/_recipient+/d.$token+/_index.tsx
|
||||
@@ -95,7 +95,7 @@ msgstr "{0, plural, one {# squadra} other {# squadre}}"
|
||||
#. placeholder {0}: folder._count.templates
|
||||
#: apps/remix/app/components/general/folder/folder-card.tsx
|
||||
msgid "{0, plural, one {# template} other {# templates}}"
|
||||
msgstr "{0, plural, one {# modello} other {# modelli}}"
|
||||
msgstr ""
|
||||
|
||||
#. placeholder {0}: data.length
|
||||
#: apps/remix/app/components/general/organisations/organisation-invitations.tsx
|
||||
@@ -770,7 +770,7 @@ msgstr "Attivo"
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security._index.tsx
|
||||
msgid "Active sessions"
|
||||
msgstr "Sessioni attive"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/stats.tsx
|
||||
msgid "Active Subscriptions"
|
||||
@@ -1341,7 +1341,7 @@ msgstr "Sei sicuro di voler eliminare la seguente richiesta?"
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-delete-dialog.tsx
|
||||
msgid "Are you sure you want to delete this folder?"
|
||||
msgstr "Sei sicuro di voler eliminare questa cartella?"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/token-delete-dialog.tsx
|
||||
msgid "Are you sure you want to delete this token?"
|
||||
@@ -2161,11 +2161,11 @@ msgstr "Crea documento da modello"
|
||||
|
||||
#: apps/remix/app/components/general/folder/folder-card.tsx
|
||||
msgid "Create folder"
|
||||
msgstr "Crea cartella"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-create-dialog.tsx
|
||||
msgid "Create Folder"
|
||||
msgstr "Crea Cartella"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/organisation-group-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/organisation-group-create-dialog.tsx
|
||||
@@ -2178,7 +2178,7 @@ msgstr "Crea Gruppi"
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-create-dialog.tsx
|
||||
msgid "Create New Folder"
|
||||
msgstr "Crea Nuova Cartella"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/_profile+/_layout.tsx
|
||||
msgid "Create now"
|
||||
@@ -2300,11 +2300,11 @@ msgstr "Struttura CSV"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/stats.tsx
|
||||
msgid "Cumulative MAU (signed in)"
|
||||
msgstr "MAU cumulativi (autenticati)"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
msgid "Current"
|
||||
msgstr "Corrente"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/forms/password.tsx
|
||||
msgid "Current Password"
|
||||
@@ -2444,7 +2444,7 @@ msgstr "Elimina Documento"
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-delete-dialog.tsx
|
||||
msgid "Delete Folder"
|
||||
msgstr "Elimina Cartella"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.general.tsx
|
||||
msgid "Delete organisation"
|
||||
@@ -3192,7 +3192,7 @@ msgstr "Documento Allegato"
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-create-dialog.tsx
|
||||
msgid "Enter a name for your new folder. Folders help you organise your items."
|
||||
msgstr "Inserisci un nome per la tua nuova cartella. Le cartelle ti aiutano a organizzare i tuoi elementi."
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/forms/subscription-claim-form.tsx
|
||||
msgid "Enter claim name"
|
||||
@@ -3332,7 +3332,7 @@ msgstr "Creazione della richiesta di abbonamento non riuscita."
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-delete-dialog.tsx
|
||||
msgid "Failed to delete folder"
|
||||
msgstr "Impossibile eliminare la cartella"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/claim-delete-dialog.tsx
|
||||
msgid "Failed to delete subscription claim."
|
||||
@@ -3344,7 +3344,7 @@ msgstr "Caricamento documento fallito."
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-move-dialog.tsx
|
||||
msgid "Failed to move folder"
|
||||
msgstr "Impossibile spostare la cartella"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/documents.$id.tsx
|
||||
msgid "Failed to reseal document"
|
||||
@@ -3352,7 +3352,7 @@ msgstr "Fallito il risigillo del documento"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
msgid "Failed to revoke session"
|
||||
msgstr "Impossibile revocare la sessione"
|
||||
msgstr ""
|
||||
|
||||
#: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx
|
||||
msgid "Failed to save settings."
|
||||
@@ -3360,7 +3360,7 @@ msgstr "Impossibile salvare le impostazioni."
|
||||
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
msgid "Failed to sign out all sessions"
|
||||
msgstr "Non è stato possibile disconnettere tutte le sessioni"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/embed+/v1+/authoring+/document.edit.$id.tsx
|
||||
msgid "Failed to update document"
|
||||
@@ -3464,15 +3464,15 @@ msgstr "Cartella creata con successo"
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-delete-dialog.tsx
|
||||
msgid "Folder deleted successfully"
|
||||
msgstr "Cartella eliminata con successo"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-move-dialog.tsx
|
||||
msgid "Folder moved successfully"
|
||||
msgstr "Cartella spostata con successo"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-create-dialog.tsx
|
||||
msgid "Folder Name"
|
||||
msgstr "Nome Cartella"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-settings-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/folder-move-dialog.tsx
|
||||
@@ -3725,7 +3725,7 @@ msgstr "Home"
|
||||
#: apps/remix/app/components/dialogs/template-move-to-folder-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/document-move-to-folder-dialog.tsx
|
||||
msgid "Home (No Folder)"
|
||||
msgstr "Home (Nessuna Cartella)"
|
||||
msgstr ""
|
||||
|
||||
#: packages/lib/constants/recipient-roles.ts
|
||||
msgid "I am a signer of this document"
|
||||
@@ -3974,7 +3974,7 @@ msgstr "Ultimi 7 giorni"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
msgid "Last Active"
|
||||
msgstr "Ultimo accesso"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/general/template/template-page-view-information.tsx
|
||||
#: apps/remix/app/components/general/document/document-page-view-information.tsx
|
||||
@@ -4127,7 +4127,7 @@ msgstr "Gestisci le autorizzazioni e i controlli di accesso"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security._index.tsx
|
||||
msgid "Manage sessions"
|
||||
msgstr "Gestisci le sessioni"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/organisations.$id.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/organisations.$id.tsx
|
||||
@@ -4208,7 +4208,7 @@ msgstr "MAU (ha completato il documento)"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/stats.tsx
|
||||
msgid "MAU (signed in)"
|
||||
msgstr "MAU (autenticati)"
|
||||
msgstr ""
|
||||
|
||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx
|
||||
msgid "Max"
|
||||
@@ -4297,7 +4297,7 @@ msgstr "Sposta documento nella cartella"
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-move-dialog.tsx
|
||||
msgid "Move Folder"
|
||||
msgstr "Sposta Cartella"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/template-move-to-folder-dialog.tsx
|
||||
msgid "Move Template to Folder"
|
||||
@@ -4314,7 +4314,7 @@ msgstr "Possono essere selezionati più metodi di accesso."
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-create-dialog.tsx
|
||||
msgid "My Folder"
|
||||
msgstr "La Mia Cartella"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/users.$id.tsx
|
||||
#: apps/remix/app/components/tables/settings-security-passkey-table.tsx
|
||||
@@ -4407,12 +4407,12 @@ msgstr "Nessuna bozza attiva"
|
||||
#: apps/remix/app/components/dialogs/template-move-to-folder-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/document-move-to-folder-dialog.tsx
|
||||
msgid "No folders found"
|
||||
msgstr "Nessuna cartella trovata"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/templates.folders._index.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/documents.folders._index.tsx
|
||||
msgid "No folders found matching \"{searchTerm}\""
|
||||
msgstr "Nessuna cartella trovata corrispondente a \"{searchTerm}\""
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/_recipient+/sign.$token+/rejected.tsx
|
||||
#: apps/remix/app/components/embed/embed-document-rejected.tsx
|
||||
@@ -4725,7 +4725,7 @@ msgstr "Organizzazioni di cui l'utente è membro."
|
||||
|
||||
#: apps/remix/app/components/general/folder/folder-card.tsx
|
||||
msgid "Organise your documents"
|
||||
msgstr "Organizza i tuoi documenti"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/organisation-group-create-dialog.tsx
|
||||
msgid "Organise your members into groups which can be assigned to teams"
|
||||
@@ -4733,7 +4733,7 @@ msgstr "Organizza i tuoi membri in gruppi che possono essere assegnati ai team."
|
||||
|
||||
#: apps/remix/app/components/general/folder/folder-card.tsx
|
||||
msgid "Organise your templates"
|
||||
msgstr "Organizza i tuoi modelli"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/o.$orgUrl._index.tsx
|
||||
msgid "Organize your documents and templates"
|
||||
@@ -4912,7 +4912,7 @@ msgstr "Scegli uno dei seguenti accordi e inizia a firmare per iniziare"
|
||||
|
||||
#: apps/remix/app/components/general/folder/folder-card.tsx
|
||||
msgid "Pin"
|
||||
msgstr "Fissa"
|
||||
msgstr ""
|
||||
|
||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx
|
||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx
|
||||
@@ -5468,7 +5468,7 @@ msgstr "Revoca l'accesso"
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
msgid "Revoke all sessions"
|
||||
msgstr "Revoca tutte le sessioni"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/tables/user-organisations-table.tsx
|
||||
#: apps/remix/app/components/tables/team-members-table.tsx
|
||||
@@ -5546,7 +5546,7 @@ msgstr "Cerca documenti..."
|
||||
#: apps/remix/app/components/dialogs/folder-move-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/document-move-to-folder-dialog.tsx
|
||||
msgid "Search folders..."
|
||||
msgstr "Cerca cartelle..."
|
||||
msgstr ""
|
||||
|
||||
#: packages/ui/components/common/language-switcher-dialog.tsx
|
||||
msgid "Search languages..."
|
||||
@@ -5574,7 +5574,7 @@ msgstr "Seleziona"
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-move-dialog.tsx
|
||||
msgid "Select a destination for this folder."
|
||||
msgstr "Seleziona una destinazione per questa cartella."
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/document-move-to-folder-dialog.tsx
|
||||
msgid "Select a folder to move this document to."
|
||||
@@ -5631,7 +5631,7 @@ msgstr "Seleziona gruppi"
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
msgid "Select groups of members to add to the team."
|
||||
msgstr "Seleziona i gruppi di membri da aggiungere al team."
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
msgid "Select groups to add to this team"
|
||||
@@ -5755,11 +5755,11 @@ msgstr "Inviato"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
msgid "Session revoked"
|
||||
msgstr "Sessione revocata"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
msgid "Sessions have been revoked"
|
||||
msgstr "Le sessioni sono state revocate"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/general/claim-account.tsx
|
||||
msgid "Set a password"
|
||||
@@ -6633,9 +6633,11 @@ msgid "The following team has been deleted. You will no longer be able to access
|
||||
msgstr "Il seguente team è stato eliminato. Non potrai più accedere a questo team e ai suoi documenti"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.groups.$id.tsx
|
||||
msgid "The organisation group you are looking for may have been removed, renamed or may have never\n"
|
||||
msgid ""
|
||||
"The organisation group you are looking for may have been removed, renamed or may have never\n"
|
||||
" existed."
|
||||
msgstr "Il gruppo organizzativo che cerchi potrebbe essere stato rimosso, rinominato o potrebbe non essere mai\n"
|
||||
msgstr ""
|
||||
"Il gruppo organizzativo che cerchi potrebbe essere stato rimosso, rinominato o potrebbe non essere mai\n"
|
||||
" esistito."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.groups.$id.tsx
|
||||
@@ -6643,15 +6645,19 @@ msgid "The organisation role that will be applied to all members in this group."
|
||||
msgstr "Il ruolo organizzativo che verrà applicato a tutti i membri in questo gruppo."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/organisations.$id.tsx
|
||||
msgid "The organisation you are looking for may have been removed, renamed or may have never\n"
|
||||
msgid ""
|
||||
"The organisation you are looking for may have been removed, renamed or may have never\n"
|
||||
" existed."
|
||||
msgstr "L'organizzazione che cerchi potrebbe essere stata rimossa, rinominata o potrebbe non essere mai\n"
|
||||
msgstr ""
|
||||
"L'organizzazione che cerchi potrebbe essere stata rimossa, rinominata o potrebbe non essere mai\n"
|
||||
" esistita."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/_layout.tsx
|
||||
msgid "The organisation you are looking for may have been removed, renamed or may have never\n"
|
||||
msgid ""
|
||||
"The organisation you are looking for may have been removed, renamed or may have never\n"
|
||||
" existed."
|
||||
msgstr "L'organizzazione che cerchi potrebbe essere stata rimossa, rinominata o potrebbe non essere mai\n"
|
||||
msgstr ""
|
||||
"L'organizzazione che cerchi potrebbe essere stata rimossa, rinominata o potrebbe non essere mai\n"
|
||||
" esistita."
|
||||
|
||||
#: apps/remix/app/components/general/generic-error-layout.tsx
|
||||
@@ -6734,15 +6740,19 @@ msgid "The team email <0>{teamEmail}</0> has been removed from the following tea
|
||||
msgstr "L'email del team <0>{teamEmail}</0> è stata rimossa dal seguente team"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/_layout.tsx
|
||||
msgid "The team you are looking for may have been removed, renamed or may have never\n"
|
||||
msgid ""
|
||||
"The team you are looking for may have been removed, renamed or may have never\n"
|
||||
" existed."
|
||||
msgstr "Il team che cerchi potrebbe essere stato rimosso, rinominato o potrebbe non essere mai\n"
|
||||
msgstr ""
|
||||
"Il team che cerchi potrebbe essere stato rimosso, rinominato o potrebbe non essere mai\n"
|
||||
" esistito."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/_layout.tsx
|
||||
msgid "The team you are looking for may have been removed, renamed or may have never\n"
|
||||
msgid ""
|
||||
"The team you are looking for may have been removed, renamed or may have never\n"
|
||||
" existed."
|
||||
msgstr "La squadra che stai cercando potrebbe essere stata rimossa, rinominata o potrebbe non essere mai\n"
|
||||
msgstr ""
|
||||
"La squadra che stai cercando potrebbe essere stata rimossa, rinominata o potrebbe non essere mai\n"
|
||||
" esistita."
|
||||
|
||||
#: apps/remix/app/components/dialogs/template-move-to-folder-dialog.tsx
|
||||
@@ -6779,9 +6789,11 @@ msgid "The URL for Documenso to send webhook events to."
|
||||
msgstr "L'URL per Documenso per inviare eventi webhook."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/users.$id.tsx
|
||||
msgid "The user you are looking for may have been removed, renamed or may have never\n"
|
||||
msgid ""
|
||||
"The user you are looking for may have been removed, renamed or may have never\n"
|
||||
" existed."
|
||||
msgstr "L'utente che cerchi potrebbe essere stato rimosso, rinominato o potrebbe non essere mai\n"
|
||||
msgstr ""
|
||||
"L'utente che cerchi potrebbe essere stato rimosso, rinominato o potrebbe non essere mai\n"
|
||||
" esistito."
|
||||
|
||||
#: apps/remix/app/components/dialogs/webhook-delete-dialog.tsx
|
||||
@@ -6936,7 +6948,7 @@ msgstr "Questo campo non può essere modificato o eliminato. Quando condividi il
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-delete-dialog.tsx
|
||||
msgid "This folder contains multiple items. Deleting it will also delete all items in the folder, including nested folders and their contents."
|
||||
msgstr "Questa cartella contiene più elementi. Cancellarla eliminerà anche tutti gli elementi nella cartella, incluse le cartelle nidificate e i loro contenuti."
|
||||
msgstr ""
|
||||
|
||||
#: packages/ui/primitives/template-flow/add-template-settings.tsx
|
||||
msgid "This is how the document will reach the recipients once the document is ready for signing."
|
||||
@@ -7018,7 +7030,7 @@ msgstr "Questo farà SOLO il retroporting degli indicatori delle funzionalità i
|
||||
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
msgid "This will sign you out of all other devices. You will need to sign in again on those devices to continue using your account."
|
||||
msgstr "Questa azione ti disconnetterà da tutti gli altri dispositivi. Dovrai accedere nuovamente su quei dispositivi per continuare a usare il tuo account."
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/tables/internal-audit-log-table.tsx
|
||||
#: apps/remix/app/components/tables/document-logs-table.tsx
|
||||
@@ -7322,7 +7334,7 @@ msgstr "Documenti illimitati, API e altro"
|
||||
|
||||
#: apps/remix/app/components/general/folder/folder-card.tsx
|
||||
msgid "Unpin"
|
||||
msgstr "Rimuovi"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
msgid "Untitled Group"
|
||||
@@ -7669,7 +7681,7 @@ msgstr "Visualizza tutte le attività di sicurezza relative al tuo account."
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security._index.tsx
|
||||
msgid "View and manage all active sessions for your account."
|
||||
msgstr "Visualizza e gestisci tutte le sessioni attive per il tuo account."
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/components/forms/2fa/view-recovery-codes-dialog.tsx
|
||||
msgid "View Codes"
|
||||
@@ -8734,4 +8746,3 @@ msgstr "Il tuo token è stato creato con successo! Assicurati di copiarlo perch
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.tokens.tsx
|
||||
msgid "Your tokens will be shown here once you create them."
|
||||
msgstr "I tuoi token verranno mostrati qui una volta creati."
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: ko\n"
|
||||
"Project-Id-Version: documenso-app\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2025-06-19 06:05\n"
|
||||
"PO-Revision-Date: 2025-06-10 02:27\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Korean\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
@@ -72,16 +72,6 @@ msgstr "{0, plural, other {(#자 초과)}}"
|
||||
msgid "{0, plural, one {# character over the limit} other {# characters over the limit}}"
|
||||
msgstr "{0, plural, other {#자 제한 초과}}"
|
||||
|
||||
#. placeholder {0}: folder._count.documents
|
||||
#: apps/remix/app/components/general/folder/folder-card.tsx
|
||||
msgid "{0, plural, one {# document} other {# documents}}"
|
||||
msgstr "{0, plural, other {# 문서}}"
|
||||
|
||||
#. placeholder {0}: folder._count.subfolders
|
||||
#: apps/remix/app/components/general/folder/folder-card.tsx
|
||||
msgid "{0, plural, one {# folder} other {# folders}}"
|
||||
msgstr "{0, plural, other {# 폴더}}"
|
||||
|
||||
#. placeholder {0}: template.recipients.length
|
||||
#: apps/remix/app/routes/_recipient+/d.$token+/_index.tsx
|
||||
msgid "{0, plural, one {# recipient} other {# recipients}}"
|
||||
@@ -92,11 +82,6 @@ msgstr "{0, plural, other {#명의 수신자}}"
|
||||
msgid "{0, plural, one {# team} other {# teams}}"
|
||||
msgstr ""
|
||||
|
||||
#. placeholder {0}: folder._count.templates
|
||||
#: apps/remix/app/components/general/folder/folder-card.tsx
|
||||
msgid "{0, plural, one {# template} other {# templates}}"
|
||||
msgstr "{0, plural, other {# 템플릿}}"
|
||||
|
||||
#. placeholder {0}: data.length
|
||||
#: apps/remix/app/components/general/organisations/organisation-invitations.tsx
|
||||
#: apps/remix/app/components/general/organisations/organisation-invitations.tsx
|
||||
@@ -767,11 +752,6 @@ msgstr "행동들"
|
||||
msgid "Active"
|
||||
msgstr "활성화됨"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security._index.tsx
|
||||
msgid "Active sessions"
|
||||
msgstr "활성 세션"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/stats.tsx
|
||||
msgid "Active Subscriptions"
|
||||
msgstr "활성 구독"
|
||||
@@ -837,13 +817,13 @@ msgstr "필드 추가"
|
||||
msgid "Add group roles"
|
||||
msgstr "그룹 역할 추가"
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
msgid "Add groups"
|
||||
msgstr "그룹 추가"
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-member-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/team-member-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
msgid "Add members"
|
||||
msgstr "멤버 추가"
|
||||
|
||||
@@ -1263,14 +1243,17 @@ msgstr "예상치 못한 오류가 발생했습니다."
|
||||
msgid "An unknown error occurred"
|
||||
msgstr "알 수 없는 오류가 발생했습니다"
|
||||
|
||||
#: apps/remix/app/components/dialogs/template-folder-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/folder-create-dialog.tsx
|
||||
msgid "An unknown error occurred while creating the folder."
|
||||
msgstr "폴더를 생성하는 동안 알 수 없는 오류가 발생했습니다."
|
||||
|
||||
#: apps/remix/app/components/dialogs/template-folder-delete-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/folder-delete-dialog.tsx
|
||||
msgid "An unknown error occurred while deleting the folder."
|
||||
msgstr "폴더를 삭제하는 동안 알 수 없는 오류가 발생했습니다."
|
||||
|
||||
#: apps/remix/app/components/dialogs/template-folder-move-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/folder-move-dialog.tsx
|
||||
msgid "An unknown error occurred while moving the folder."
|
||||
msgstr "폴더를 이동하는 동안 알 수 없는 오류가 발생했습니다."
|
||||
@@ -1339,10 +1322,6 @@ msgstr "문서를 완료하시겠습니까? 이 작업은 되돌릴 수 없습
|
||||
msgid "Are you sure you want to delete the following claim?"
|
||||
msgstr "정말 이 클레임을 삭제하시겠습니까?"
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-delete-dialog.tsx
|
||||
msgid "Are you sure you want to delete this folder?"
|
||||
msgstr "이 폴더를 삭제하시겠습니까?"
|
||||
|
||||
#: apps/remix/app/components/dialogs/token-delete-dialog.tsx
|
||||
msgid "Are you sure you want to delete this token?"
|
||||
msgstr "정말 이 토큰을 삭제하시겠습니까?"
|
||||
@@ -1635,7 +1614,6 @@ msgstr "준비 가능"
|
||||
#: apps/remix/app/components/dialogs/team-delete-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/team-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/team-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/public-profile-template-manage-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/passkey-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/organisation-member-update-dialog.tsx
|
||||
@@ -1648,9 +1626,6 @@ msgstr "준비 가능"
|
||||
#: apps/remix/app/components/dialogs/organisation-delete-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/organisation-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/organisation-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/folder-move-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/folder-delete-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/folder-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/document-resend-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/document-move-to-folder-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/document-duplicate-dialog.tsx
|
||||
@@ -1946,6 +1921,7 @@ msgstr "확인을 위해 입력하세요 <0>{deleteMessage}</0>"
|
||||
|
||||
#: apps/remix/app/components/dialogs/webhook-delete-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/token-delete-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/template-folder-delete-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/folder-delete-dialog.tsx
|
||||
msgid "Confirm by typing: <0>{deleteMessage}</0>"
|
||||
msgstr "확인을 위해 입력하세요: <0>{deleteMessage}</0>"
|
||||
@@ -2089,7 +2065,6 @@ msgstr "토큰 복사"
|
||||
#: apps/remix/app/components/dialogs/webhook-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/organisation-group-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/organisation-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/folder-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/admin-organisation-create-dialog.tsx
|
||||
msgid "Create"
|
||||
msgstr "만들기"
|
||||
@@ -2159,14 +2134,6 @@ msgstr "직접 서명 링크 생성"
|
||||
msgid "Create document from template"
|
||||
msgstr "템플릿에서 문서 생성"
|
||||
|
||||
#: apps/remix/app/components/general/folder/folder-card.tsx
|
||||
msgid "Create folder"
|
||||
msgstr "폴더 만들기"
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-create-dialog.tsx
|
||||
msgid "Create Folder"
|
||||
msgstr "폴더 생성"
|
||||
|
||||
#: apps/remix/app/components/dialogs/organisation-group-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/organisation-group-create-dialog.tsx
|
||||
msgid "Create group"
|
||||
@@ -2176,10 +2143,6 @@ msgstr "그룹 생성"
|
||||
msgid "Create Groups"
|
||||
msgstr "그룹 생성"
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-create-dialog.tsx
|
||||
msgid "Create New Folder"
|
||||
msgstr "새 폴더 생성"
|
||||
|
||||
#: apps/remix/app/routes/_profile+/_layout.tsx
|
||||
msgid "Create now"
|
||||
msgstr "지금 생성"
|
||||
@@ -2258,7 +2221,6 @@ msgstr "계정을 만들고 최첨단 문서 서명을 사용해보세요."
|
||||
msgid "Create your account and start using state-of-the-art document signing. Open and beautiful signing is within your grasp."
|
||||
msgstr "계정을 만들고 최첨단 문서 서명을 사용해보세요. 개방적이고 아름다운 서명은 당신의 손 안에 있습니다."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/documents._index.tsx
|
||||
#: apps/remix/app/components/tables/templates-table.tsx
|
||||
#: apps/remix/app/components/tables/settings-security-passkey-table.tsx
|
||||
@@ -2298,14 +2260,6 @@ msgstr "{0}에 생성됨"
|
||||
msgid "CSV Structure"
|
||||
msgstr "CSV 구조"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/stats.tsx
|
||||
msgid "Cumulative MAU (signed in)"
|
||||
msgstr "누적 MAU (로그인 완료)"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
msgid "Current"
|
||||
msgstr "현재"
|
||||
|
||||
#: apps/remix/app/components/forms/password.tsx
|
||||
msgid "Current Password"
|
||||
msgstr "현재 비밀번호"
|
||||
@@ -2388,7 +2342,6 @@ msgstr "삭제"
|
||||
#: apps/remix/app/components/tables/organisation-groups-table.tsx
|
||||
#: apps/remix/app/components/tables/documents-table-action-dropdown.tsx
|
||||
#: apps/remix/app/components/tables/admin-claims-table.tsx
|
||||
#: apps/remix/app/components/general/folder/folder-card.tsx
|
||||
#: apps/remix/app/components/general/document/document-page-view-dropdown.tsx
|
||||
#: apps/remix/app/components/dialogs/webhook-delete-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/token-delete-dialog.tsx
|
||||
@@ -2400,7 +2353,6 @@ msgstr "삭제"
|
||||
#: apps/remix/app/components/dialogs/organisation-group-delete-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/organisation-delete-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/organisation-delete-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/folder-delete-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/document-delete-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/claim-delete-dialog.tsx
|
||||
msgid "Delete"
|
||||
@@ -2408,9 +2360,10 @@ msgstr "삭제"
|
||||
|
||||
#. placeholder {0}: webhook.webhookUrl
|
||||
#. placeholder {0}: token.name
|
||||
#. placeholder {0}: organisation.name
|
||||
#. placeholder {0}: folder?.name ?? 'folder'
|
||||
#: apps/remix/app/components/dialogs/webhook-delete-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/token-delete-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/template-folder-delete-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/organisation-delete-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/folder-delete-dialog.tsx
|
||||
msgid "delete {0}"
|
||||
@@ -2442,10 +2395,6 @@ msgstr "문서 삭제"
|
||||
msgid "Delete Document"
|
||||
msgstr "문서 삭제"
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-delete-dialog.tsx
|
||||
msgid "Delete Folder"
|
||||
msgstr "폴더 삭제"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.general.tsx
|
||||
msgid "Delete organisation"
|
||||
msgstr "조직 삭제"
|
||||
@@ -2504,7 +2453,6 @@ msgid "Details"
|
||||
msgstr "세부 정보"
|
||||
|
||||
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/components/tables/settings-security-activity-table.tsx
|
||||
msgid "Device"
|
||||
msgstr "장치"
|
||||
@@ -2885,6 +2833,7 @@ msgid "Document will be permanently deleted"
|
||||
msgstr "문서가 영구적으로 삭제될 것입니다"
|
||||
|
||||
#: apps/remix/app/routes/_profile+/p.$url.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/documents.f.$folderId._index.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/documents._index.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/documents.$id.edit.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/documents.$id._index.tsx
|
||||
@@ -3190,10 +3139,6 @@ msgstr "계정을 활성화하면 사용자가 다시 계정을 사용할 수
|
||||
msgid "Enclosed Document"
|
||||
msgstr "첨부 문서"
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-create-dialog.tsx
|
||||
msgid "Enter a name for your new folder. Folders help you organise your items."
|
||||
msgstr "새 폴더의 이름을 입력하세요. 폴더는 항목을 정리하는 데 도움을 줍니다."
|
||||
|
||||
#: apps/remix/app/components/forms/subscription-claim-form.tsx
|
||||
msgid "Enter claim name"
|
||||
msgstr "클레임 이름 입력"
|
||||
@@ -3234,7 +3179,6 @@ msgstr "엔터프라이즈"
|
||||
#: apps/remix/app/routes/embed+/v1+/authoring+/template.edit.$id.tsx
|
||||
#: apps/remix/app/routes/embed+/v1+/authoring+/document.edit.$id.tsx
|
||||
#: apps/remix/app/routes/embed+/v1+/authoring+/document.edit.$id.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/users.$id.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/organisations.$id.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/documents.$id.tsx
|
||||
@@ -3277,7 +3221,6 @@ msgstr "엔터프라이즈"
|
||||
#: apps/remix/app/components/dialogs/template-move-to-folder-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/template-move-to-folder-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/template-duplicate-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/document-move-to-folder-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/document-move-to-folder-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/admin-user-enable-dialog.tsx
|
||||
@@ -3322,7 +3265,8 @@ msgstr "{0}에 만료됨"
|
||||
msgid "External ID"
|
||||
msgstr "외부 ID"
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/template-folder-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/template-folder-create-dialog.tsx
|
||||
msgid "Failed to create folder"
|
||||
msgstr "폴더 생성에 실패했습니다"
|
||||
|
||||
@@ -3330,10 +3274,6 @@ msgstr "폴더 생성에 실패했습니다"
|
||||
msgid "Failed to create subscription claim."
|
||||
msgstr "구독 클레임 생성에 실패했습니다."
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-delete-dialog.tsx
|
||||
msgid "Failed to delete folder"
|
||||
msgstr "폴더 삭제 실패"
|
||||
|
||||
#: apps/remix/app/components/dialogs/claim-delete-dialog.tsx
|
||||
msgid "Failed to delete subscription claim."
|
||||
msgstr "구독 클레임 삭제에 실패했습니다."
|
||||
@@ -3342,26 +3282,14 @@ msgstr "구독 클레임 삭제에 실패했습니다."
|
||||
msgid "Failed to load document"
|
||||
msgstr "문서 로드에 실패했습니다"
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-move-dialog.tsx
|
||||
msgid "Failed to move folder"
|
||||
msgstr "폴더 이동 실패"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/documents.$id.tsx
|
||||
msgid "Failed to reseal document"
|
||||
msgstr "문서 걸쇠 잠금 실패"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
msgid "Failed to revoke session"
|
||||
msgstr "세션을 취소하는 데 실패했습니다"
|
||||
|
||||
#: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx
|
||||
msgid "Failed to save settings."
|
||||
msgstr "설정 저장 실패."
|
||||
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
msgid "Failed to sign out all sessions"
|
||||
msgstr "모든 세션에서 로그아웃에 실패했습니다."
|
||||
|
||||
#: apps/remix/app/routes/embed+/v1+/authoring+/document.edit.$id.tsx
|
||||
msgid "Failed to update document"
|
||||
msgstr "문서 업데이트에 실패했습니다"
|
||||
@@ -3458,28 +3386,16 @@ msgstr "새로운 구독 클레임을 만들기 위한 세부 정보를 입력
|
||||
msgid "Folder"
|
||||
msgstr "폴더"
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/template-folder-create-dialog.tsx
|
||||
msgid "Folder created successfully"
|
||||
msgstr "폴더가 성공적으로 생성되었습니다"
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-delete-dialog.tsx
|
||||
msgid "Folder deleted successfully"
|
||||
msgstr "폴더가 성공적으로 삭제되었습니다"
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-move-dialog.tsx
|
||||
msgid "Folder moved successfully"
|
||||
msgstr "폴더가 성공적으로 이동되었습니다"
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-create-dialog.tsx
|
||||
msgid "Folder Name"
|
||||
msgstr "폴더 이름"
|
||||
|
||||
#: apps/remix/app/components/dialogs/template-folder-settings-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/folder-settings-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/folder-move-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/folder-delete-dialog.tsx
|
||||
msgid "Folder not found"
|
||||
msgstr "폴더를 찾을 수 없습니다"
|
||||
|
||||
#: apps/remix/app/components/dialogs/template-folder-settings-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/folder-settings-dialog.tsx
|
||||
msgid "Folder updated successfully"
|
||||
msgstr "폴더가 성공적으로 업데이트되었습니다"
|
||||
@@ -3717,16 +3633,9 @@ msgid "Hide additional information"
|
||||
msgstr "추가 정보 숨기기"
|
||||
|
||||
#: apps/remix/app/components/general/generic-error-layout.tsx
|
||||
#: apps/remix/app/components/general/folder/folder-grid.tsx
|
||||
#: apps/remix/app/components/dialogs/folder-move-dialog.tsx
|
||||
msgid "Home"
|
||||
msgstr "홈"
|
||||
|
||||
#: apps/remix/app/components/dialogs/template-move-to-folder-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/document-move-to-folder-dialog.tsx
|
||||
msgid "Home (No Folder)"
|
||||
msgstr "홈 (폴더 없음)"
|
||||
|
||||
#: packages/lib/constants/recipient-roles.ts
|
||||
msgid "I am a signer of this document"
|
||||
msgstr "저는 이 문서의 서명자입니다"
|
||||
@@ -3838,6 +3747,7 @@ msgstr "잘못된 코드입니다. 다시 시도해 주세요."
|
||||
msgid "Invalid email"
|
||||
msgstr "잘못된 이메일"
|
||||
|
||||
#: apps/remix/app/routes/_unauthenticated+/team.verify.transfer.$token.tsx
|
||||
#: apps/remix/app/routes/_unauthenticated+/team.verify.email.$token.tsx
|
||||
msgid "Invalid link"
|
||||
msgstr "잘못된 링크"
|
||||
@@ -3901,7 +3811,6 @@ msgid "Invoice"
|
||||
msgstr "송장"
|
||||
|
||||
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/components/tables/internal-audit-log-table.tsx
|
||||
msgid "IP Address"
|
||||
msgstr "IP 주소"
|
||||
@@ -3972,10 +3881,6 @@ msgstr "지난 30일"
|
||||
msgid "Last 7 days"
|
||||
msgstr "지난 7일"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
msgid "Last Active"
|
||||
msgstr "마지막 활동"
|
||||
|
||||
#: apps/remix/app/components/general/template/template-page-view-information.tsx
|
||||
#: apps/remix/app/components/general/document/document-page-view-information.tsx
|
||||
msgid "Last modified"
|
||||
@@ -4125,10 +4030,6 @@ msgstr "패스키 관리"
|
||||
msgid "Manage permissions and access controls"
|
||||
msgstr "권한 및 접근 제어 관리"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security._index.tsx
|
||||
msgid "Manage sessions"
|
||||
msgstr "세션 관리"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/organisations.$id.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/organisations.$id.tsx
|
||||
msgid "Manage subscription"
|
||||
@@ -4206,10 +4107,6 @@ msgstr "MAU (생성된 문서)"
|
||||
msgid "MAU (had document completed)"
|
||||
msgstr "MAU (문서 완료)"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/stats.tsx
|
||||
msgid "MAU (signed in)"
|
||||
msgstr "MAU (로그인 완료)"
|
||||
|
||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx
|
||||
msgid "Max"
|
||||
msgstr "최대"
|
||||
@@ -4280,9 +4177,7 @@ msgstr "월간 활성 사용자: 최소 하나의 문서를 생성한 사용자"
|
||||
msgid "Monthly Active Users: Users that had at least one of their documents completed"
|
||||
msgstr "월간 활성 사용자: 최소 한 개의 문서가 완료된 사용자"
|
||||
|
||||
#: apps/remix/app/components/general/folder/folder-card.tsx
|
||||
#: apps/remix/app/components/dialogs/template-move-to-folder-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/folder-move-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/document-move-to-folder-dialog.tsx
|
||||
msgid "Move"
|
||||
msgstr "이동"
|
||||
@@ -4295,10 +4190,6 @@ msgstr "\"{templateTitle}\"를 폴더로 이동"
|
||||
msgid "Move Document to Folder"
|
||||
msgstr "문서를 폴더로 이동"
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-move-dialog.tsx
|
||||
msgid "Move Folder"
|
||||
msgstr "폴더 이동"
|
||||
|
||||
#: apps/remix/app/components/dialogs/template-move-to-folder-dialog.tsx
|
||||
msgid "Move Template to Folder"
|
||||
msgstr "템플릿을 폴더로 이동"
|
||||
@@ -4312,10 +4203,6 @@ msgstr "폴더로 이동"
|
||||
msgid "Multiple access methods can be selected."
|
||||
msgstr "다중 접근 방법을 선택할 수 있습니다."
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-create-dialog.tsx
|
||||
msgid "My Folder"
|
||||
msgstr "내 폴더"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/users.$id.tsx
|
||||
#: apps/remix/app/components/tables/settings-security-passkey-table.tsx
|
||||
#: apps/remix/app/components/tables/settings-security-passkey-table-actions.tsx
|
||||
@@ -4404,16 +4291,6 @@ msgstr "아니요"
|
||||
msgid "No active drafts"
|
||||
msgstr "활성화된 초안이 없음"
|
||||
|
||||
#: apps/remix/app/components/dialogs/template-move-to-folder-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/document-move-to-folder-dialog.tsx
|
||||
msgid "No folders found"
|
||||
msgstr "폴더를 찾을 수 없습니다"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/templates.folders._index.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/documents.folders._index.tsx
|
||||
msgid "No folders found matching \"{searchTerm}\""
|
||||
msgstr "'{searchTerm}'와 일치하는 폴더를 찾을 수 없습니다"
|
||||
|
||||
#: apps/remix/app/routes/_recipient+/sign.$token+/rejected.tsx
|
||||
#: apps/remix/app/components/embed/embed-document-rejected.tsx
|
||||
msgid "No further action is required from you at this time."
|
||||
@@ -4723,18 +4600,10 @@ msgstr "조직들"
|
||||
msgid "Organisations that the user is a member of."
|
||||
msgstr "이 사용자가 멤버로 있는 조직들."
|
||||
|
||||
#: apps/remix/app/components/general/folder/folder-card.tsx
|
||||
msgid "Organise your documents"
|
||||
msgstr "문서를 정리하세요"
|
||||
|
||||
#: apps/remix/app/components/dialogs/organisation-group-create-dialog.tsx
|
||||
msgid "Organise your members into groups which can be assigned to teams"
|
||||
msgstr "멤버들을 팀에 할당할 수 있는 그룹으로 구성하세요"
|
||||
|
||||
#: apps/remix/app/components/general/folder/folder-card.tsx
|
||||
msgid "Organise your templates"
|
||||
msgstr "템플릿을 정리하세요"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/o.$orgUrl._index.tsx
|
||||
msgid "Organize your documents and templates"
|
||||
msgstr "문서 및 템플릿을 정리하세요"
|
||||
@@ -4910,10 +4779,6 @@ msgstr "비밀번호 설정"
|
||||
msgid "Pick any of the following agreements below and start signing to get started"
|
||||
msgstr "다음의 동의서 중 하나를 선택하고 서명을 시작하여 시작하세요"
|
||||
|
||||
#: apps/remix/app/components/general/folder/folder-card.tsx
|
||||
msgid "Pin"
|
||||
msgstr "고정"
|
||||
|
||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx
|
||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx
|
||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx
|
||||
@@ -5440,6 +5305,7 @@ msgstr "문서 보유"
|
||||
msgid "Retry"
|
||||
msgstr "다시 시도"
|
||||
|
||||
#: apps/remix/app/routes/_unauthenticated+/team.verify.transfer.$token.tsx
|
||||
#: apps/remix/app/routes/_unauthenticated+/team.verify.email.$token.tsx
|
||||
#: apps/remix/app/routes/_unauthenticated+/organisation.invite.$token.tsx
|
||||
#: apps/remix/app/routes/_unauthenticated+/organisation.decline.$token.tsx
|
||||
@@ -5455,7 +5321,6 @@ msgstr "홈으로 돌아가기"
|
||||
msgid "Return to sign in"
|
||||
msgstr "로그인 화면으로 돌아가기"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/components/general/teams/team-email-usage.tsx
|
||||
msgid "Revoke"
|
||||
msgstr "취소"
|
||||
@@ -5464,12 +5329,6 @@ msgstr "취소"
|
||||
msgid "Revoke access"
|
||||
msgstr "접근 권한 취소"
|
||||
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
msgid "Revoke all sessions"
|
||||
msgstr "모든 세션 취소"
|
||||
|
||||
#: apps/remix/app/components/tables/user-organisations-table.tsx
|
||||
#: apps/remix/app/components/tables/team-members-table.tsx
|
||||
#: apps/remix/app/components/tables/team-groups-table.tsx
|
||||
@@ -5489,6 +5348,11 @@ msgstr "역할"
|
||||
msgid "Roles"
|
||||
msgstr "역할들"
|
||||
|
||||
#: apps/remix/app/components/dialogs/template-move-to-folder-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/document-move-to-folder-dialog.tsx
|
||||
msgid "Root (No Folder)"
|
||||
msgstr "루트 (폴더 없음)"
|
||||
|
||||
#: packages/ui/primitives/data-table-pagination.tsx
|
||||
msgid "Rows per page"
|
||||
msgstr "페이지당 행 수"
|
||||
@@ -5540,14 +5404,6 @@ msgstr "조직 ID, 이름, 고객 ID 또는 소유자 이메일로 검색"
|
||||
msgid "Search documents..."
|
||||
msgstr "문서 검색…"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/templates.folders._index.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/documents.folders._index.tsx
|
||||
#: apps/remix/app/components/dialogs/template-move-to-folder-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/folder-move-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/document-move-to-folder-dialog.tsx
|
||||
msgid "Search folders..."
|
||||
msgstr "폴더 검색..."
|
||||
|
||||
#: packages/ui/components/common/language-switcher-dialog.tsx
|
||||
msgid "Search languages..."
|
||||
msgstr "언어 검색…"
|
||||
@@ -5572,10 +5428,6 @@ msgstr "보안 활동"
|
||||
msgid "Select"
|
||||
msgstr "선택"
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-move-dialog.tsx
|
||||
msgid "Select a destination for this folder."
|
||||
msgstr "이 폴더의 목적지를 선택하세요."
|
||||
|
||||
#: apps/remix/app/components/dialogs/document-move-to-folder-dialog.tsx
|
||||
msgid "Select a folder to move this document to."
|
||||
msgstr "이 문서를 이동할 폴더를 선택하세요."
|
||||
@@ -5629,10 +5481,6 @@ msgstr "기본 옵션 선택"
|
||||
msgid "Select groups"
|
||||
msgstr "그룹 선택"
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
msgid "Select groups of members to add to the team."
|
||||
msgstr "팀에 추가할 구성원 그룹을 선택하세요."
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
msgid "Select groups to add to this team"
|
||||
msgstr "이 팀에 추가할 그룹 선택"
|
||||
@@ -5644,6 +5492,7 @@ msgid "Select members"
|
||||
msgstr "멤버 선택"
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-member-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
msgid "Select members or groups of members to add to the team."
|
||||
msgstr "팀에 추가할 멤버 또는 멤버 그룹을 선택하세요."
|
||||
|
||||
@@ -5753,14 +5602,6 @@ msgstr "보내는 중…"
|
||||
msgid "Sent"
|
||||
msgstr "보냈음"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
msgid "Session revoked"
|
||||
msgstr "세션이 해제되었습니다."
|
||||
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
msgid "Sessions have been revoked"
|
||||
msgstr "세션이 취소되었습니다."
|
||||
|
||||
#: apps/remix/app/components/general/claim-account.tsx
|
||||
msgid "Set a password"
|
||||
msgstr "비밀번호 설정"
|
||||
@@ -5780,7 +5621,6 @@ msgstr "템플릿 속성과 수신자 정보를 설정하세요"
|
||||
#: apps/remix/app/components/general/app-nav-mobile.tsx
|
||||
#: apps/remix/app/components/general/app-command-menu.tsx
|
||||
#: apps/remix/app/components/general/app-command-menu.tsx
|
||||
#: apps/remix/app/components/general/folder/folder-card.tsx
|
||||
msgid "Settings"
|
||||
msgstr "설정"
|
||||
|
||||
@@ -6478,6 +6318,7 @@ msgstr "템플릿 제목"
|
||||
msgid "Template updated successfully"
|
||||
msgstr "템플릿이 성공적으로 업데이트되었습니다"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/templates.f.$folderId._index.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/templates._index.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/templates.$id._index.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.public-profile.tsx
|
||||
@@ -6608,10 +6449,12 @@ msgstr "URL로 전송될 웹훅을 트리거할 이벤트입니다."
|
||||
msgid "The fields have been updated to the new field insertion method successfully"
|
||||
msgstr "필드가 새로운 필드 삽입 방법으로 성공적으로 업데이트되었습니다"
|
||||
|
||||
#: apps/remix/app/components/dialogs/template-folder-delete-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/folder-delete-dialog.tsx
|
||||
msgid "The folder you are trying to delete does not exist."
|
||||
msgstr "삭제하려는 폴더가 존재하지 않습니다."
|
||||
|
||||
#: apps/remix/app/components/dialogs/template-folder-move-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/folder-move-dialog.tsx
|
||||
msgid "The folder you are trying to move does not exist."
|
||||
msgstr "이동하려는 폴더가 존재하지 않습니다."
|
||||
@@ -6928,9 +6771,10 @@ msgstr "다른 수신자가 아직 서명하지 않았을 경우 문서에 서
|
||||
msgid "This field cannot be modified or deleted. When you share this template's direct link or add it to your public profile, anyone who accesses it can input their name and email, and fill in the fields assigned to them."
|
||||
msgstr "이 필드는 수정되거나 삭제될 수 없습니다. 이 템플릿의 직접 링크를 공유하거나 공개 프로필에 추가하면, 누구나 이름과 이메일을 입력하고 할당된 필드를 기입할 수 있습니다."
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-delete-dialog.tsx
|
||||
msgid "This folder contains multiple items. Deleting it will also delete all items in the folder, including nested folders and their contents."
|
||||
msgstr "이 폴더에는 여러 항목이 포함되어 있습니다. 삭제하면 중첩 폴더 및 그 내용물을 포함한 모든 항목이 삭제됩니다."
|
||||
#: apps/remix/app/components/dialogs/template-folder-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/folder-create-dialog.tsx
|
||||
msgid "This folder name is already taken."
|
||||
msgstr "이 폴더 이름은 이미 사용 중입니다."
|
||||
|
||||
#: packages/ui/primitives/template-flow/add-template-settings.tsx
|
||||
msgid "This is how the document will reach the recipients once the document is ready for signing."
|
||||
@@ -6940,6 +6784,10 @@ msgstr "이 문서를 서명할 준비가 되면 수신자에게 이렇게 도
|
||||
msgid "This is the claim that this organisation was initially created with. Any feature flag changes to this claim will be backported into this organisation."
|
||||
msgstr "이것이 조직 초기 설립 당시의 클레임입니다. 이 클레임으로의 기능 플래그 변경은 조직에 백포팅됩니다."
|
||||
|
||||
#: apps/remix/app/routes/_unauthenticated+/team.verify.transfer.$token.tsx
|
||||
msgid "This link is invalid or has expired."
|
||||
msgstr "이 링크는 유효하지 않거나 만료되었습니다."
|
||||
|
||||
#: apps/remix/app/routes/_unauthenticated+/team.verify.email.$token.tsx
|
||||
msgid "This link is invalid or has expired. Please contact your team to resend a verification."
|
||||
msgstr "이 링크는 유효하지 않거나 만료되었습니다. 팀에 문의하여 확인을 다시 요청하십시오."
|
||||
@@ -7010,10 +6858,6 @@ msgstr "문서가 완전히 완료된 후 문서 소유자에게 이것이 발
|
||||
msgid "This will ONLY backport feature flags which are set to true, anything disabled in the initial claim will not be backported"
|
||||
msgstr "기능 플래그 기본 설정은 사실의 경우에만 백포팅됩니다. 초기 클레임에 비활성화된 사항은 백포팅되지 않습니다."
|
||||
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
msgid "This will sign you out of all other devices. You will need to sign in again on those devices to continue using your account."
|
||||
msgstr "이 작업은 다른 모든 기기에서 로그아웃됩니다. 계정을 계속 사용하려면 해당 기기에서 다시 로그인해야 합니다."
|
||||
|
||||
#: apps/remix/app/components/tables/internal-audit-log-table.tsx
|
||||
#: apps/remix/app/components/tables/document-logs-table.tsx
|
||||
msgid "Time"
|
||||
@@ -7300,9 +7144,6 @@ msgstr "미완료"
|
||||
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
|
||||
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
|
||||
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
msgid "Unknown"
|
||||
msgstr "알 수 없음"
|
||||
|
||||
@@ -7314,10 +7155,6 @@ msgstr "무제한"
|
||||
msgid "Unlimited documents, API and more"
|
||||
msgstr "무제한 문서, API 및 기타"
|
||||
|
||||
#: apps/remix/app/components/general/folder/folder-card.tsx
|
||||
msgid "Unpin"
|
||||
msgstr "고정 해제"
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
msgid "Untitled Group"
|
||||
msgstr "제목없는 그룹"
|
||||
@@ -7660,11 +7497,6 @@ msgstr "모든 관련 문서 보기"
|
||||
msgid "View all security activity related to your account."
|
||||
msgstr "귀하의 계정과 관련된 모든 보안 활동 보기."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security._index.tsx
|
||||
msgid "View and manage all active sessions for your account."
|
||||
msgstr "계정의 모든 활성 세션을 보고 관리하십시오."
|
||||
|
||||
#: apps/remix/app/components/forms/2fa/view-recovery-codes-dialog.tsx
|
||||
msgid "View Codes"
|
||||
msgstr "코드 보기"
|
||||
@@ -8023,6 +7855,7 @@ msgstr "저희가 서명 링크를 생성해드리며, 원하시는 방법으로
|
||||
msgid "We won't send anything to notify recipients."
|
||||
msgstr "수신자에게 알리기 위해 아무것도 보내지 않습니다."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/templates.f.$folderId._index.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/templates._index.tsx
|
||||
#: apps/remix/app/components/tables/documents-table-empty-state.tsx
|
||||
msgid "We're all empty"
|
||||
@@ -8382,6 +8215,7 @@ msgstr "귀하에게 {recipientActionVerb}을 요구하는 문서 {0}을 시작
|
||||
msgid "You have no webhooks yet. Your webhooks will be shown here once you create them."
|
||||
msgstr "아직 웹훅이 없습니다.웹훅을 만들면 여기에 표시됩니다."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/templates.f.$folderId._index.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/templates._index.tsx
|
||||
msgid "You have not yet created any templates. To create a template please upload one."
|
||||
msgstr "아직 템플릿을 생성하지 않았습니다.템플릿을 만들려면 하나를 업로드하세요."
|
||||
@@ -8483,6 +8317,7 @@ msgstr "<0>{0}</0>에 대한 이메일 주소를 확인했습니다."
|
||||
msgid "You must enter '{deleteMessage}' to proceed"
|
||||
msgstr "계속하려면 '{deleteMessage}'을(를) 입력해야 합니다"
|
||||
|
||||
#: apps/remix/app/components/dialogs/template-folder-delete-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/folder-delete-dialog.tsx
|
||||
msgid "You must type '{deleteMessage}' to confirm"
|
||||
msgstr "확인을 위해 '{deleteMessage}'를 입력해야 합니다"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+196
-191
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Language: sq\n"
|
||||
"Project-Id-Version: documenso-app\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"PO-Revision-Date: 2025-06-19 06:05\n"
|
||||
"PO-Revision-Date: 2025-06-10 02:27\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Albanian\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
@@ -72,16 +72,6 @@ msgstr "{0, plural, one {(1 karakter më shumë)} other {(# karaktere më shumë
|
||||
msgid "{0, plural, one {# character over the limit} other {# characters over the limit}}"
|
||||
msgstr "{0, plural, one {# karakter mbi kufi} other {# karaktere mbi kufi}}"
|
||||
|
||||
#. placeholder {0}: folder._count.documents
|
||||
#: apps/remix/app/components/general/folder/folder-card.tsx
|
||||
msgid "{0, plural, one {# document} other {# documents}}"
|
||||
msgstr "{0, plural, one {# dokument} other {# dokumente}}"
|
||||
|
||||
#. placeholder {0}: folder._count.subfolders
|
||||
#: apps/remix/app/components/general/folder/folder-card.tsx
|
||||
msgid "{0, plural, one {# folder} other {# folders}}"
|
||||
msgstr "{0, plural, one {# dosje} other {# dosje}}"
|
||||
|
||||
#. placeholder {0}: template.recipients.length
|
||||
#: apps/remix/app/routes/_recipient+/d.$token+/_index.tsx
|
||||
msgid "{0, plural, one {# recipient} other {# recipients}}"
|
||||
@@ -92,11 +82,6 @@ msgstr "{0, plural, one {# marrës} other {# marrësa}}"
|
||||
msgid "{0, plural, one {# team} other {# teams}}"
|
||||
msgstr "{0, plural, one {# ekip} other {# ekipe}}"
|
||||
|
||||
#. placeholder {0}: folder._count.templates
|
||||
#: apps/remix/app/components/general/folder/folder-card.tsx
|
||||
msgid "{0, plural, one {# template} other {# templates}}"
|
||||
msgstr "{0, plural, one {# model} other {# modele}}"
|
||||
|
||||
#. placeholder {0}: data.length
|
||||
#: apps/remix/app/components/general/organisations/organisation-invitations.tsx
|
||||
#: apps/remix/app/components/general/organisations/organisation-invitations.tsx
|
||||
@@ -767,11 +752,6 @@ msgstr "Veprime"
|
||||
msgid "Active"
|
||||
msgstr "Aktive"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security._index.tsx
|
||||
msgid "Active sessions"
|
||||
msgstr "Seanca aktive"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/stats.tsx
|
||||
msgid "Active Subscriptions"
|
||||
msgstr "Abonime aktive"
|
||||
@@ -837,13 +817,13 @@ msgstr "Shto fusha"
|
||||
msgid "Add group roles"
|
||||
msgstr "Shto rolet e grupeve"
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
msgid "Add groups"
|
||||
msgstr "Shto grupet"
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-member-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/team-member-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
msgid "Add members"
|
||||
msgstr "Shto anëtarët"
|
||||
|
||||
@@ -1263,14 +1243,17 @@ msgstr "Ndodhi një gabim i papritur."
|
||||
msgid "An unknown error occurred"
|
||||
msgstr "Ndodhi një gabim i panjohur"
|
||||
|
||||
#: apps/remix/app/components/dialogs/template-folder-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/folder-create-dialog.tsx
|
||||
msgid "An unknown error occurred while creating the folder."
|
||||
msgstr "Një gabim i panjohur ndodhi gjatë krijimit të dosjes."
|
||||
|
||||
#: apps/remix/app/components/dialogs/template-folder-delete-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/folder-delete-dialog.tsx
|
||||
msgid "An unknown error occurred while deleting the folder."
|
||||
msgstr "Një gabim i panjohur ndodhi gjatë fshirjes së dosjes."
|
||||
|
||||
#: apps/remix/app/components/dialogs/template-folder-move-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/folder-move-dialog.tsx
|
||||
msgid "An unknown error occurred while moving the folder."
|
||||
msgstr "Një gabim i panjohur ndodhi gjatë zhvendosjes së dosjes."
|
||||
@@ -1339,10 +1322,6 @@ msgstr "A jeni të sigurt se doni të përfundoni dokumentin? Ky veprim nuk mund
|
||||
msgid "Are you sure you want to delete the following claim?"
|
||||
msgstr "A jeni të sigurt që doni të fshini ankesën e mëposhtme?"
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-delete-dialog.tsx
|
||||
msgid "Are you sure you want to delete this folder?"
|
||||
msgstr "A jeni i sigurt që doni ta fshini këtë dosje?"
|
||||
|
||||
#: apps/remix/app/components/dialogs/token-delete-dialog.tsx
|
||||
msgid "Are you sure you want to delete this token?"
|
||||
msgstr "A jeni i sigurt që doni të fshini këtë token?"
|
||||
@@ -1635,7 +1614,6 @@ msgstr "Mund të përgatitë"
|
||||
#: apps/remix/app/components/dialogs/team-delete-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/team-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/team-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/public-profile-template-manage-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/passkey-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/organisation-member-update-dialog.tsx
|
||||
@@ -1648,9 +1626,6 @@ msgstr "Mund të përgatitë"
|
||||
#: apps/remix/app/components/dialogs/organisation-delete-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/organisation-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/organisation-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/folder-move-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/folder-delete-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/folder-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/document-resend-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/document-move-to-folder-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/document-duplicate-dialog.tsx
|
||||
@@ -1946,6 +1921,7 @@ msgstr "Konfirmo duke shtypur <0>{deleteMessage}</0>"
|
||||
|
||||
#: apps/remix/app/components/dialogs/webhook-delete-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/token-delete-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/template-folder-delete-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/folder-delete-dialog.tsx
|
||||
msgid "Confirm by typing: <0>{deleteMessage}</0>"
|
||||
msgstr "Konfirmo duke shtypur: <0>{deleteMessage}</0>"
|
||||
@@ -2089,7 +2065,6 @@ msgstr "Kopjo token"
|
||||
#: apps/remix/app/components/dialogs/webhook-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/organisation-group-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/organisation-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/folder-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/admin-organisation-create-dialog.tsx
|
||||
msgid "Create"
|
||||
msgstr "Krijo"
|
||||
@@ -2159,14 +2134,6 @@ msgstr "Krijo Lidhje të Drejtpërdrejtë për Nënshkrim"
|
||||
msgid "Create document from template"
|
||||
msgstr "Krijo dokument nga modeli"
|
||||
|
||||
#: apps/remix/app/components/general/folder/folder-card.tsx
|
||||
msgid "Create folder"
|
||||
msgstr "Krijo dosje"
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-create-dialog.tsx
|
||||
msgid "Create Folder"
|
||||
msgstr "Krijo Dosje"
|
||||
|
||||
#: apps/remix/app/components/dialogs/organisation-group-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/organisation-group-create-dialog.tsx
|
||||
msgid "Create group"
|
||||
@@ -2176,10 +2143,6 @@ msgstr "Krijo grup"
|
||||
msgid "Create Groups"
|
||||
msgstr "Krijo Grupet"
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-create-dialog.tsx
|
||||
msgid "Create New Folder"
|
||||
msgstr "Krijo Dosje të Re"
|
||||
|
||||
#: apps/remix/app/routes/_profile+/_layout.tsx
|
||||
msgid "Create now"
|
||||
msgstr "Krijo tani"
|
||||
@@ -2258,7 +2221,6 @@ msgstr "Krijoni llogarinë tuaj dhe filloni të përdorni nënshkrimin e dokumen
|
||||
msgid "Create your account and start using state-of-the-art document signing. Open and beautiful signing is within your grasp."
|
||||
msgstr "Krijoni llogarinë tuaj dhe filloni të përdorni nënshkrimin e dokumenteve në gjendje të artit. Nënshkrimi i hapur dhe i bukur është brenda arritjes tuaj."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/documents._index.tsx
|
||||
#: apps/remix/app/components/tables/templates-table.tsx
|
||||
#: apps/remix/app/components/tables/settings-security-passkey-table.tsx
|
||||
@@ -2298,14 +2260,6 @@ msgstr "Krijuar më {0}"
|
||||
msgid "CSV Structure"
|
||||
msgstr "Struktura CSV"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/stats.tsx
|
||||
msgid "Cumulative MAU (signed in)"
|
||||
msgstr ""
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
msgid "Current"
|
||||
msgstr "Aktual"
|
||||
|
||||
#: apps/remix/app/components/forms/password.tsx
|
||||
msgid "Current Password"
|
||||
msgstr "Fjalëkalimi aktual"
|
||||
@@ -2388,7 +2342,6 @@ msgstr "fshi"
|
||||
#: apps/remix/app/components/tables/organisation-groups-table.tsx
|
||||
#: apps/remix/app/components/tables/documents-table-action-dropdown.tsx
|
||||
#: apps/remix/app/components/tables/admin-claims-table.tsx
|
||||
#: apps/remix/app/components/general/folder/folder-card.tsx
|
||||
#: apps/remix/app/components/general/document/document-page-view-dropdown.tsx
|
||||
#: apps/remix/app/components/dialogs/webhook-delete-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/token-delete-dialog.tsx
|
||||
@@ -2400,7 +2353,6 @@ msgstr "fshi"
|
||||
#: apps/remix/app/components/dialogs/organisation-group-delete-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/organisation-delete-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/organisation-delete-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/folder-delete-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/document-delete-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/claim-delete-dialog.tsx
|
||||
msgid "Delete"
|
||||
@@ -2408,9 +2360,10 @@ msgstr "Fshi"
|
||||
|
||||
#. placeholder {0}: webhook.webhookUrl
|
||||
#. placeholder {0}: token.name
|
||||
#. placeholder {0}: organisation.name
|
||||
#. placeholder {0}: folder?.name ?? 'folder'
|
||||
#: apps/remix/app/components/dialogs/webhook-delete-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/token-delete-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/template-folder-delete-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/organisation-delete-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/folder-delete-dialog.tsx
|
||||
msgid "delete {0}"
|
||||
@@ -2442,10 +2395,6 @@ msgstr "Fshi dokumentin"
|
||||
msgid "Delete Document"
|
||||
msgstr "Fshi Dokumentin"
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-delete-dialog.tsx
|
||||
msgid "Delete Folder"
|
||||
msgstr "Fshij Dosjen"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.general.tsx
|
||||
msgid "Delete organisation"
|
||||
msgstr "Fshij organizatën"
|
||||
@@ -2504,7 +2453,6 @@ msgid "Details"
|
||||
msgstr "Detaje"
|
||||
|
||||
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/components/tables/settings-security-activity-table.tsx
|
||||
msgid "Device"
|
||||
msgstr "Pajisje"
|
||||
@@ -2885,6 +2833,7 @@ msgid "Document will be permanently deleted"
|
||||
msgstr "Dokumenti do të Fshihet Përgjithmonë"
|
||||
|
||||
#: apps/remix/app/routes/_profile+/p.$url.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/documents.f.$folderId._index.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/documents._index.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/documents.$id.edit.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/documents.$id._index.tsx
|
||||
@@ -3190,10 +3139,6 @@ msgstr "Aktivizimi i llogarisë rezulton që përdoruesi të jetë në gjendje t
|
||||
msgid "Enclosed Document"
|
||||
msgstr "Dokumenti i Bashkangjitur"
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-create-dialog.tsx
|
||||
msgid "Enter a name for your new folder. Folders help you organise your items."
|
||||
msgstr "Shkruani emrin për dosjen tuaj të re. Dosjet ju ndihmojnë të organizoni sendet tuaja."
|
||||
|
||||
#: apps/remix/app/components/forms/subscription-claim-form.tsx
|
||||
msgid "Enter claim name"
|
||||
msgstr "Vendos emrin e pretendimit"
|
||||
@@ -3234,7 +3179,6 @@ msgstr "Ndërmarrje"
|
||||
#: apps/remix/app/routes/embed+/v1+/authoring+/template.edit.$id.tsx
|
||||
#: apps/remix/app/routes/embed+/v1+/authoring+/document.edit.$id.tsx
|
||||
#: apps/remix/app/routes/embed+/v1+/authoring+/document.edit.$id.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/users.$id.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/organisations.$id.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/documents.$id.tsx
|
||||
@@ -3277,7 +3221,6 @@ msgstr "Ndërmarrje"
|
||||
#: apps/remix/app/components/dialogs/template-move-to-folder-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/template-move-to-folder-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/template-duplicate-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/document-move-to-folder-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/document-move-to-folder-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/admin-user-enable-dialog.tsx
|
||||
@@ -3322,7 +3265,8 @@ msgstr "Skadon më {0}"
|
||||
msgid "External ID"
|
||||
msgstr "ID i jashtëm"
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/template-folder-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/template-folder-create-dialog.tsx
|
||||
msgid "Failed to create folder"
|
||||
msgstr "Dështoi krijimi i dosjes"
|
||||
|
||||
@@ -3330,10 +3274,6 @@ msgstr "Dështoi krijimi i dosjes"
|
||||
msgid "Failed to create subscription claim."
|
||||
msgstr "Dështoi krijimi i pretendimit të abonimit."
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-delete-dialog.tsx
|
||||
msgid "Failed to delete folder"
|
||||
msgstr "Deshtim në fshirjen e dosjes"
|
||||
|
||||
#: apps/remix/app/components/dialogs/claim-delete-dialog.tsx
|
||||
msgid "Failed to delete subscription claim."
|
||||
msgstr "Dështoi fshirja e pretendimit të abonimit."
|
||||
@@ -3342,26 +3282,14 @@ msgstr "Dështoi fshirja e pretendimit të abonimit."
|
||||
msgid "Failed to load document"
|
||||
msgstr "Dështoi ngarkimi i dokumentit"
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-move-dialog.tsx
|
||||
msgid "Failed to move folder"
|
||||
msgstr "Deshtim në zhvendosjen e dosjes"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/documents.$id.tsx
|
||||
msgid "Failed to reseal document"
|
||||
msgstr "Dështoi rihapja e dokumentit"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
msgid "Failed to revoke session"
|
||||
msgstr "Dështoi të revokojë seancën"
|
||||
|
||||
#: packages/ui/primitives/document-flow/field-item-advanced-settings.tsx
|
||||
msgid "Failed to save settings."
|
||||
msgstr "Dështoi të ruajë parametrat."
|
||||
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
msgid "Failed to sign out all sessions"
|
||||
msgstr "Dështoi të dilni nga të gjitha sesionet"
|
||||
|
||||
#: apps/remix/app/routes/embed+/v1+/authoring+/document.edit.$id.tsx
|
||||
msgid "Failed to update document"
|
||||
msgstr "Dështoi përditësimi i dokumentit"
|
||||
@@ -3458,28 +3386,16 @@ msgstr "Plotësoni detajet për të krijuar një pretendim të ri të abonimit."
|
||||
msgid "Folder"
|
||||
msgstr "Dosje"
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/template-folder-create-dialog.tsx
|
||||
msgid "Folder created successfully"
|
||||
msgstr "Dosja u krijua me sukses"
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-delete-dialog.tsx
|
||||
msgid "Folder deleted successfully"
|
||||
msgstr "Dosja u fshi me sukses"
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-move-dialog.tsx
|
||||
msgid "Folder moved successfully"
|
||||
msgstr "Dosja u zhvendos me sukses"
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-create-dialog.tsx
|
||||
msgid "Folder Name"
|
||||
msgstr "Emri i Dosjes"
|
||||
|
||||
#: apps/remix/app/components/dialogs/template-folder-settings-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/folder-settings-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/folder-move-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/folder-delete-dialog.tsx
|
||||
msgid "Folder not found"
|
||||
msgstr "Dosja nuk u gjet"
|
||||
|
||||
#: apps/remix/app/components/dialogs/template-folder-settings-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/folder-settings-dialog.tsx
|
||||
msgid "Folder updated successfully"
|
||||
msgstr "Dosja përditësuar me sukses"
|
||||
@@ -3717,16 +3633,9 @@ msgid "Hide additional information"
|
||||
msgstr "Fshih informacionin shtesë"
|
||||
|
||||
#: apps/remix/app/components/general/generic-error-layout.tsx
|
||||
#: apps/remix/app/components/general/folder/folder-grid.tsx
|
||||
#: apps/remix/app/components/dialogs/folder-move-dialog.tsx
|
||||
msgid "Home"
|
||||
msgstr "Shtëpia"
|
||||
|
||||
#: apps/remix/app/components/dialogs/template-move-to-folder-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/document-move-to-folder-dialog.tsx
|
||||
msgid "Home (No Folder)"
|
||||
msgstr "Kryefaqe (Pa Dosje)"
|
||||
|
||||
#: packages/lib/constants/recipient-roles.ts
|
||||
msgid "I am a signer of this document"
|
||||
msgstr "Unë jam një nënshkrues i këtij dokumenti"
|
||||
@@ -3838,6 +3747,7 @@ msgstr "Kodi i pavlefshëm. Ju lutemi provoni përsëri."
|
||||
msgid "Invalid email"
|
||||
msgstr "Email i pavlefshëm"
|
||||
|
||||
#: apps/remix/app/routes/_unauthenticated+/team.verify.transfer.$token.tsx
|
||||
#: apps/remix/app/routes/_unauthenticated+/team.verify.email.$token.tsx
|
||||
msgid "Invalid link"
|
||||
msgstr "Lidhja e pavlefshme"
|
||||
@@ -3901,7 +3811,6 @@ msgid "Invoice"
|
||||
msgstr "Fatura"
|
||||
|
||||
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/components/tables/internal-audit-log-table.tsx
|
||||
msgid "IP Address"
|
||||
msgstr "Adresa IP"
|
||||
@@ -3972,10 +3881,6 @@ msgstr "30 ditët e fundit"
|
||||
msgid "Last 7 days"
|
||||
msgstr "7 ditët e fundit"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
msgid "Last Active"
|
||||
msgstr "Aktiviteti i fundit"
|
||||
|
||||
#: apps/remix/app/components/general/template/template-page-view-information.tsx
|
||||
#: apps/remix/app/components/general/document/document-page-view-information.tsx
|
||||
msgid "Last modified"
|
||||
@@ -4125,10 +4030,6 @@ msgstr "Menaxho çelësat e aksesit"
|
||||
msgid "Manage permissions and access controls"
|
||||
msgstr "Menaxhoni lejet dhe kontrollin e qasjes"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security._index.tsx
|
||||
msgid "Manage sessions"
|
||||
msgstr "Menaxho seancat"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/organisations.$id.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/organisations.$id.tsx
|
||||
msgid "Manage subscription"
|
||||
@@ -4206,10 +4107,6 @@ msgstr "MAU (dokument i krijuar)"
|
||||
msgid "MAU (had document completed)"
|
||||
msgstr "MAU (dokument i përfunduar)"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/stats.tsx
|
||||
msgid "MAU (signed in)"
|
||||
msgstr ""
|
||||
|
||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx
|
||||
msgid "Max"
|
||||
msgstr "Maks"
|
||||
@@ -4280,9 +4177,7 @@ msgstr "Përdorues Aktivë Mujorë: Përdorues që kanë krijuar të paktën nj
|
||||
msgid "Monthly Active Users: Users that had at least one of their documents completed"
|
||||
msgstr "Përdorues Aktivë Mujorë: Përdorues që kanë përfunduar të paktën një nga dokumentet e tyre"
|
||||
|
||||
#: apps/remix/app/components/general/folder/folder-card.tsx
|
||||
#: apps/remix/app/components/dialogs/template-move-to-folder-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/folder-move-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/document-move-to-folder-dialog.tsx
|
||||
msgid "Move"
|
||||
msgstr "Zhvendos"
|
||||
@@ -4295,10 +4190,6 @@ msgstr "Zhvendos \"{templateTitle}\" në një dosje"
|
||||
msgid "Move Document to Folder"
|
||||
msgstr "Zhvendos Dokumentin në Dosje"
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-move-dialog.tsx
|
||||
msgid "Move Folder"
|
||||
msgstr "Zhvendos Dosjen"
|
||||
|
||||
#: apps/remix/app/components/dialogs/template-move-to-folder-dialog.tsx
|
||||
msgid "Move Template to Folder"
|
||||
msgstr "Zhvendos Shabllonin në Dosje"
|
||||
@@ -4312,10 +4203,6 @@ msgstr "Zhvendos në Dosje"
|
||||
msgid "Multiple access methods can be selected."
|
||||
msgstr "Mund të zgjedhni metoda të shumta të qasje."
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-create-dialog.tsx
|
||||
msgid "My Folder"
|
||||
msgstr "Dosja Ime"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/admin+/users.$id.tsx
|
||||
#: apps/remix/app/components/tables/settings-security-passkey-table.tsx
|
||||
#: apps/remix/app/components/tables/settings-security-passkey-table-actions.tsx
|
||||
@@ -4404,16 +4291,6 @@ msgstr "Jo"
|
||||
msgid "No active drafts"
|
||||
msgstr "Asnjë draft aktiv"
|
||||
|
||||
#: apps/remix/app/components/dialogs/template-move-to-folder-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/document-move-to-folder-dialog.tsx
|
||||
msgid "No folders found"
|
||||
msgstr "Nuk u gjetën dosje"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/templates.folders._index.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/documents.folders._index.tsx
|
||||
msgid "No folders found matching \"{searchTerm}\""
|
||||
msgstr "Nuk u gjetën dosje që përputhen me \"{searchTerm}\""
|
||||
|
||||
#: apps/remix/app/routes/_recipient+/sign.$token+/rejected.tsx
|
||||
#: apps/remix/app/components/embed/embed-document-rejected.tsx
|
||||
msgid "No further action is required from you at this time."
|
||||
@@ -4723,18 +4600,10 @@ msgstr "Organizatat"
|
||||
msgid "Organisations that the user is a member of."
|
||||
msgstr "Organizatat të cilat përdoruesi është anëtar i. "
|
||||
|
||||
#: apps/remix/app/components/general/folder/folder-card.tsx
|
||||
msgid "Organise your documents"
|
||||
msgstr "Organizoni dokumentet tuaja"
|
||||
|
||||
#: apps/remix/app/components/dialogs/organisation-group-create-dialog.tsx
|
||||
msgid "Organise your members into groups which can be assigned to teams"
|
||||
msgstr "Organizoni anëtarët tuaj në grupe që mund të caktohen në ekipe"
|
||||
|
||||
#: apps/remix/app/components/general/folder/folder-card.tsx
|
||||
msgid "Organise your templates"
|
||||
msgstr "Organizoni shabllonet tuaja"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/o.$orgUrl._index.tsx
|
||||
msgid "Organize your documents and templates"
|
||||
msgstr "Organizoni dokumentet dhe shabllonet tuaj"
|
||||
@@ -4910,10 +4779,6 @@ msgstr "Zgjidhni një fjalëkalim"
|
||||
msgid "Pick any of the following agreements below and start signing to get started"
|
||||
msgstr "Zgjidhni ndonjë nga marrëveshjet e mëposhtme dhe filloni të nënshkruani për të nisur"
|
||||
|
||||
#: apps/remix/app/components/general/folder/folder-card.tsx
|
||||
msgid "Pin"
|
||||
msgstr "Vendos"
|
||||
|
||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/text-field.tsx
|
||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx
|
||||
#: packages/ui/primitives/document-flow/field-items-advanced-settings/number-field.tsx
|
||||
@@ -5440,6 +5305,7 @@ msgstr "Ruajtja e Dokumenteve"
|
||||
msgid "Retry"
|
||||
msgstr "Provoni përsëri"
|
||||
|
||||
#: apps/remix/app/routes/_unauthenticated+/team.verify.transfer.$token.tsx
|
||||
#: apps/remix/app/routes/_unauthenticated+/team.verify.email.$token.tsx
|
||||
#: apps/remix/app/routes/_unauthenticated+/organisation.invite.$token.tsx
|
||||
#: apps/remix/app/routes/_unauthenticated+/organisation.decline.$token.tsx
|
||||
@@ -5455,7 +5321,6 @@ msgstr "Kthehu në Faqen Kryesore"
|
||||
msgid "Return to sign in"
|
||||
msgstr "Kthehu te Hyrja"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/components/general/teams/team-email-usage.tsx
|
||||
msgid "Revoke"
|
||||
msgstr "Revoko"
|
||||
@@ -5464,12 +5329,6 @@ msgstr "Revoko"
|
||||
msgid "Revoke access"
|
||||
msgstr "Revoko qasje"
|
||||
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
msgid "Revoke all sessions"
|
||||
msgstr "Revoko të gjitha seancat"
|
||||
|
||||
#: apps/remix/app/components/tables/user-organisations-table.tsx
|
||||
#: apps/remix/app/components/tables/team-members-table.tsx
|
||||
#: apps/remix/app/components/tables/team-groups-table.tsx
|
||||
@@ -5489,6 +5348,11 @@ msgstr "Roli"
|
||||
msgid "Roles"
|
||||
msgstr "Rolet"
|
||||
|
||||
#: apps/remix/app/components/dialogs/template-move-to-folder-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/document-move-to-folder-dialog.tsx
|
||||
msgid "Root (No Folder)"
|
||||
msgstr "Rrënjë (Pa Dosje)"
|
||||
|
||||
#: packages/ui/primitives/data-table-pagination.tsx
|
||||
msgid "Rows per page"
|
||||
msgstr "Rreshta për faqe"
|
||||
@@ -5540,14 +5404,6 @@ msgstr "Kërko sipas ID të organizatës, emrit, ID të klientit ose email i pro
|
||||
msgid "Search documents..."
|
||||
msgstr "Kërko dokumente..."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/templates.folders._index.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/documents.folders._index.tsx
|
||||
#: apps/remix/app/components/dialogs/template-move-to-folder-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/folder-move-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/document-move-to-folder-dialog.tsx
|
||||
msgid "Search folders..."
|
||||
msgstr "Kërkoni dosje..."
|
||||
|
||||
#: packages/ui/components/common/language-switcher-dialog.tsx
|
||||
msgid "Search languages..."
|
||||
msgstr "Kërko gjuhë..."
|
||||
@@ -5572,10 +5428,6 @@ msgstr "Aktiviteti i Sigurisë"
|
||||
msgid "Select"
|
||||
msgstr "Zgjidh"
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-move-dialog.tsx
|
||||
msgid "Select a destination for this folder."
|
||||
msgstr "Zgjidhni një destinacion për këtë dosje."
|
||||
|
||||
#: apps/remix/app/components/dialogs/document-move-to-folder-dialog.tsx
|
||||
msgid "Select a folder to move this document to."
|
||||
msgstr "Zgjidhni një dosje për të zhvendosur këtë dokument."
|
||||
@@ -5629,10 +5481,6 @@ msgstr "Zgjidh opsionin e paracaktuar"
|
||||
msgid "Select groups"
|
||||
msgstr "Zgjidh grupet"
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
msgid "Select groups of members to add to the team."
|
||||
msgstr "Zgjidh grupet e anëtarëve për t'i shtuar në ekip."
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
msgid "Select groups to add to this team"
|
||||
msgstr "Zgjidh grupet për t'u shtuar në këtë ekip"
|
||||
@@ -5644,6 +5492,7 @@ msgid "Select members"
|
||||
msgstr "Zgjidh anëtarët"
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-member-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
msgid "Select members or groups of members to add to the team."
|
||||
msgstr "Zgjidh anëtarët ose grupet e anëtarëve për t'i shtuar në ekip."
|
||||
|
||||
@@ -5753,14 +5602,6 @@ msgstr "Duke Dërguar..."
|
||||
msgid "Sent"
|
||||
msgstr "Dërguar"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
msgid "Session revoked"
|
||||
msgstr "Sesion i revokuar"
|
||||
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
msgid "Sessions have been revoked"
|
||||
msgstr "Sesione janë revokuar"
|
||||
|
||||
#: apps/remix/app/components/general/claim-account.tsx
|
||||
msgid "Set a password"
|
||||
msgstr "Vendosni një fjalëkalim"
|
||||
@@ -5780,7 +5621,6 @@ msgstr "Vendosni cilësitë e shabllonit tuaj dhe informacionin e marrësit"
|
||||
#: apps/remix/app/components/general/app-nav-mobile.tsx
|
||||
#: apps/remix/app/components/general/app-command-menu.tsx
|
||||
#: apps/remix/app/components/general/app-command-menu.tsx
|
||||
#: apps/remix/app/components/general/folder/folder-card.tsx
|
||||
msgid "Settings"
|
||||
msgstr "Cilësimet"
|
||||
|
||||
@@ -6478,6 +6318,7 @@ msgstr "Titulli i shabllonit"
|
||||
msgid "Template updated successfully"
|
||||
msgstr "Shablloni u përditësua me sukses"
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/templates.f.$folderId._index.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/templates._index.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/templates.$id._index.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.public-profile.tsx
|
||||
@@ -6608,10 +6449,12 @@ msgstr "Ngjarjet që do të nxisin dërgimin e një webhook në URL-në tuaj."
|
||||
msgid "The fields have been updated to the new field insertion method successfully"
|
||||
msgstr "Fushat janë përditësuar në metodën e re të futjes së fushës me sukses"
|
||||
|
||||
#: apps/remix/app/components/dialogs/template-folder-delete-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/folder-delete-dialog.tsx
|
||||
msgid "The folder you are trying to delete does not exist."
|
||||
msgstr "Dosja që po përpiqeni të fshini nuk ekziston."
|
||||
|
||||
#: apps/remix/app/components/dialogs/template-folder-move-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/folder-move-dialog.tsx
|
||||
msgid "The folder you are trying to move does not exist."
|
||||
msgstr "Dosja që po përpiqeni të zhvendosni nuk ekziston."
|
||||
@@ -6928,9 +6771,10 @@ msgstr "Ky email do t'i ndërlidhet marrësit që sapo ka nënshkruar dokumentin
|
||||
msgid "This field cannot be modified or deleted. When you share this template's direct link or add it to your public profile, anyone who accesses it can input their name and email, and fill in the fields assigned to them."
|
||||
msgstr "Ky fushë nuk mund të modifikohet apo të fshihet. Kur ndani lidhjen direkte të këtij model ose e shtoni në profilin tuaj publik, kushdo që e akseson mund të shkruajë emrin dhe email-in e tij, dhe të plotësojë fushat e caktuara për ta."
|
||||
|
||||
#: apps/remix/app/components/dialogs/folder-delete-dialog.tsx
|
||||
msgid "This folder contains multiple items. Deleting it will also delete all items in the folder, including nested folders and their contents."
|
||||
msgstr "Kjo dosje përmban disa artikuj. Fshirja e saj do të fshijë gjithashtu të gjitha artikujt në dosje, duke përfshirë dosjet e mbivendosura dhe përmbajtjet e tyre."
|
||||
#: apps/remix/app/components/dialogs/template-folder-create-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/folder-create-dialog.tsx
|
||||
msgid "This folder name is already taken."
|
||||
msgstr "Ky emër dosjeje është marrë tashmë."
|
||||
|
||||
#: packages/ui/primitives/template-flow/add-template-settings.tsx
|
||||
msgid "This is how the document will reach the recipients once the document is ready for signing."
|
||||
@@ -6940,6 +6784,10 @@ msgstr "Kështu do të arrijë dokumenti te marrësit sapo dokumenti të jetë g
|
||||
msgid "This is the claim that this organisation was initially created with. Any feature flag changes to this claim will be backported into this organisation."
|
||||
msgstr "Kjo është kërkesa që kjo organizatë fillimisht u krijua me të. Çdo ndryshim në flamurët e veçorive në këtë kërkesë do të transferohet në këtë organizatë."
|
||||
|
||||
#: apps/remix/app/routes/_unauthenticated+/team.verify.transfer.$token.tsx
|
||||
msgid "This link is invalid or has expired."
|
||||
msgstr "Ky lidhje është e pavlefshme ose ka skaduar."
|
||||
|
||||
#: apps/remix/app/routes/_unauthenticated+/team.verify.email.$token.tsx
|
||||
msgid "This link is invalid or has expired. Please contact your team to resend a verification."
|
||||
msgstr "Ky link është i pavlefshëm ose ka skaduar. Ju lutem kontaktoni ekipin tuaj për të dërguar përsëri një verifikim."
|
||||
@@ -7010,10 +6858,6 @@ msgstr "Kjo do të dërgohet pronarit të dokumentit sapo dokumenti të jetë pl
|
||||
msgid "This will ONLY backport feature flags which are set to true, anything disabled in the initial claim will not be backported"
|
||||
msgstr "Kjo do të TRANSFEROHET VETËM flamujt e veçorive që janë vendosur në të vërtetë, çdo gjë që është çaktivizuar në kërkesën fillestare nuk do të transferohet"
|
||||
|
||||
#: apps/remix/app/components/dialogs/session-logout-all-dialog.tsx
|
||||
msgid "This will sign you out of all other devices. You will need to sign in again on those devices to continue using your account."
|
||||
msgstr "Kjo do t'ju nxjerrë nga të gjitha pajisjet e tjera. Do të duhet të hyni përsëri në ato pajisje për të vazhduar përdorimin e llogarisë suaj."
|
||||
|
||||
#: apps/remix/app/components/tables/internal-audit-log-table.tsx
|
||||
#: apps/remix/app/components/tables/document-logs-table.tsx
|
||||
msgid "Time"
|
||||
@@ -7300,9 +7144,6 @@ msgstr "I papërfunduar"
|
||||
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
|
||||
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
|
||||
#: apps/remix/app/routes/_internal+/[__htmltopdf]+/certificate.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
msgid "Unknown"
|
||||
msgstr "E panjohur"
|
||||
|
||||
@@ -7314,10 +7155,6 @@ msgstr "E pakufizuar"
|
||||
msgid "Unlimited documents, API and more"
|
||||
msgstr "Dokumente pa kufi, API dhe më shumë"
|
||||
|
||||
#: apps/remix/app/components/general/folder/folder-card.tsx
|
||||
msgid "Unpin"
|
||||
msgstr "Çaktivizo"
|
||||
|
||||
#: apps/remix/app/components/dialogs/team-group-create-dialog.tsx
|
||||
msgid "Untitled Group"
|
||||
msgstr "Grup pa titull"
|
||||
@@ -7660,11 +7497,6 @@ msgstr "Shiko të gjitha dokumentet e lidhura"
|
||||
msgid "View all security activity related to your account."
|
||||
msgstr "Shiko të gjitha aktivitetet e sigurisë lidhur me llogarinë tuaj."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security.sessions.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/settings+/security._index.tsx
|
||||
msgid "View and manage all active sessions for your account."
|
||||
msgstr "Shiko dhe menaxho të gjitha seancat aktive për llogarinë tuaj."
|
||||
|
||||
#: apps/remix/app/components/forms/2fa/view-recovery-codes-dialog.tsx
|
||||
msgid "View Codes"
|
||||
msgstr "Shiko Kodet"
|
||||
@@ -8023,6 +7855,7 @@ msgstr "Ne do të gjenerojmë lidhje nënshkrimi për ju, të cilat mund t'i dë
|
||||
msgid "We won't send anything to notify recipients."
|
||||
msgstr "Ne nuk do të dërgojmë asgjë për të njoftuar pranuesit."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/templates.f.$folderId._index.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/templates._index.tsx
|
||||
#: apps/remix/app/components/tables/documents-table-empty-state.tsx
|
||||
msgid "We're all empty"
|
||||
@@ -8382,6 +8215,7 @@ msgstr "Ju e keni iniciuar dokumentin {0} që ju kërkon të {recipientActionVer
|
||||
msgid "You have no webhooks yet. Your webhooks will be shown here once you create them."
|
||||
msgstr "Ju nuk keni ende ndonjë webhook. Webhook-et tuaja do të shfaqen këtu sapo t'i krijoni."
|
||||
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/templates.f.$folderId._index.tsx
|
||||
#: apps/remix/app/routes/_authenticated+/t.$teamUrl+/templates._index.tsx
|
||||
msgid "You have not yet created any templates. To create a template please upload one."
|
||||
msgstr "Ju ende nuk keni krijuar ndonjë shabllon. Për të krijuar një shabllon ju lutemi ngarkoni një."
|
||||
@@ -8483,6 +8317,7 @@ msgstr "Ju keni verifikuar adresën tuaj të emailit për <0>{0}</0>."
|
||||
msgid "You must enter '{deleteMessage}' to proceed"
|
||||
msgstr "Duhet të shkruani '{deleteMessage}' për të vazhduar"
|
||||
|
||||
#: apps/remix/app/components/dialogs/template-folder-delete-dialog.tsx
|
||||
#: apps/remix/app/components/dialogs/folder-delete-dialog.tsx
|
||||
msgid "You must type '{deleteMessage}' to confirm"
|
||||
msgstr "Ju duhet të shkruani '{deleteMessage}' për të konfirmuar"
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
import type { ApiRequestMetadata } from '../universal/extract-request-metadata';
|
||||
|
||||
/**
|
||||
* The minimum required fields that the parent API logger must contain.
|
||||
*/
|
||||
export type RootApiLog = {
|
||||
ipAddress?: string;
|
||||
userAgent?: string;
|
||||
requestId: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* The minimum API log that must be logged at the start of every API request.
|
||||
*/
|
||||
export type BaseApiLog = Partial<RootApiLog> & {
|
||||
path: string;
|
||||
auth: ApiRequestMetadata['auth'];
|
||||
source: ApiRequestMetadata['source'];
|
||||
userId?: number | null;
|
||||
apiTokenId?: number | null;
|
||||
};
|
||||
|
||||
/**
|
||||
* The TRPC API log.
|
||||
*/
|
||||
export type TrpcApiLog = BaseApiLog & {
|
||||
trpcMiddleware: string;
|
||||
unverifiedTeamId?: number | null;
|
||||
};
|
||||
@@ -33,7 +33,6 @@ export const ZDocumentAuditLogTypeSchema = z.enum([
|
||||
'DOCUMENT_GLOBAL_AUTH_ACTION_UPDATED', // When the global action authentication is updated.
|
||||
'DOCUMENT_META_UPDATED', // When the document meta data is updated.
|
||||
'DOCUMENT_OPENED', // When the document is opened by a recipient.
|
||||
'DOCUMENT_VIEWED', // When the document is viewed by a recipient.
|
||||
'DOCUMENT_RECIPIENT_REJECTED', // When a recipient rejects the document.
|
||||
'DOCUMENT_RECIPIENT_COMPLETED', // When a recipient completes all their required tasks for the document.
|
||||
'DOCUMENT_SENT', // When the document transitions from DRAFT to PENDING.
|
||||
@@ -439,22 +438,6 @@ export const ZDocumentAuditLogEventDocumentOpenedSchema = z.object({
|
||||
}),
|
||||
});
|
||||
|
||||
/**
|
||||
* Event: Document viewed.
|
||||
*/
|
||||
export const ZDocumentAuditLogEventDocumentViewedSchema = z.object({
|
||||
type: z.literal(DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_VIEWED),
|
||||
data: ZBaseRecipientDataSchema.extend({
|
||||
accessAuth: z.preprocess((unknownValue) => {
|
||||
if (!unknownValue) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return Array.isArray(unknownValue) ? unknownValue : [unknownValue];
|
||||
}, z.array(ZRecipientAccessAuthTypesSchema)),
|
||||
}),
|
||||
});
|
||||
|
||||
/**
|
||||
* Event: Document recipient completed the document (the recipient has fully actioned and completed their required steps for the document).
|
||||
*/
|
||||
@@ -618,7 +601,6 @@ export const ZDocumentAuditLogSchema = ZDocumentAuditLogBaseSchema.and(
|
||||
ZDocumentAuditLogEventDocumentGlobalAuthActionUpdatedSchema,
|
||||
ZDocumentAuditLogEventDocumentMetaUpdatedSchema,
|
||||
ZDocumentAuditLogEventDocumentOpenedSchema,
|
||||
ZDocumentAuditLogEventDocumentViewedSchema,
|
||||
ZDocumentAuditLogEventDocumentRecipientCompleteSchema,
|
||||
ZDocumentAuditLogEventDocumentRecipientRejectedSchema,
|
||||
ZDocumentAuditLogEventDocumentSentSchema,
|
||||
|
||||
@@ -96,7 +96,6 @@ export const ZCheckboxFieldMeta = ZBaseFieldMeta.extend({
|
||||
.optional(),
|
||||
validationRule: z.string().optional(),
|
||||
validationLength: z.number().optional(),
|
||||
direction: z.enum(['vertical', 'horizontal']).optional().default('vertical'),
|
||||
});
|
||||
|
||||
export type TCheckboxFieldMeta = z.infer<typeof ZCheckboxFieldMeta>;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Document, DocumentMeta, Recipient, WebhookTriggerEvents } from '@prisma/client';
|
||||
import type { Document, DocumentMeta, Recipient } from '@prisma/client';
|
||||
import {
|
||||
DocumentDistributionMethod,
|
||||
DocumentSigningOrder,
|
||||
@@ -87,13 +87,6 @@ export const ZWebhookDocumentSchema = z.object({
|
||||
export type TWebhookRecipient = z.infer<typeof ZWebhookRecipientSchema>;
|
||||
export type TWebhookDocument = z.infer<typeof ZWebhookDocumentSchema>;
|
||||
|
||||
export type WebhookPayload = {
|
||||
event: WebhookTriggerEvents;
|
||||
payload: TWebhookDocument;
|
||||
createdAt: string;
|
||||
webhookEndpoint: string;
|
||||
};
|
||||
|
||||
export const mapDocumentToWebhookDocumentPayload = (
|
||||
document: Document & {
|
||||
recipients: Recipient[];
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
import { getIpAddress } from './get-ip-address';
|
||||
|
||||
const ZIpSchema = z.string().ip();
|
||||
|
||||
export const ZRequestMetadataSchema = z.object({
|
||||
@@ -42,13 +40,11 @@ export type ApiRequestMetadata = {
|
||||
};
|
||||
|
||||
export const extractRequestMetadata = (req: Request): RequestMetadata => {
|
||||
let ip: string | undefined = undefined;
|
||||
|
||||
try {
|
||||
ip = getIpAddress(req);
|
||||
} catch {
|
||||
// Do nothing.
|
||||
}
|
||||
const forwardedFor = req.headers.get('x-forwarded-for');
|
||||
const ip = forwardedFor
|
||||
?.split(',')
|
||||
.map((ip) => ip.trim())
|
||||
.at(0);
|
||||
|
||||
const parsedIp = ZIpSchema.safeParse(ip);
|
||||
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
export const getIpAddress = (req: Request) => {
|
||||
// Check for forwarded headers first (common in proxy setups)
|
||||
const forwarded = req.headers.get('x-forwarded-for');
|
||||
|
||||
if (forwarded) {
|
||||
// x-forwarded-for can contain multiple IPs, take the first one
|
||||
return forwarded.split(',')[0].trim();
|
||||
}
|
||||
|
||||
// Check for real IP header (used by some proxies)
|
||||
const realIp = req.headers.get('x-real-ip');
|
||||
|
||||
if (realIp) {
|
||||
return realIp;
|
||||
}
|
||||
|
||||
// Check for client IP header
|
||||
const clientIp = req.headers.get('x-client-ip');
|
||||
|
||||
if (clientIp) {
|
||||
return clientIp;
|
||||
}
|
||||
|
||||
// Check for CF-Connecting-IP (Cloudflare)
|
||||
const cfConnectingIp = req.headers.get('cf-connecting-ip');
|
||||
|
||||
if (cfConnectingIp) {
|
||||
return cfConnectingIp;
|
||||
}
|
||||
|
||||
// Check for True-Client-IP (Akamai and Cloudflare)
|
||||
const trueClientIp = req.headers.get('true-client-ip');
|
||||
|
||||
if (trueClientIp) {
|
||||
return trueClientIp;
|
||||
}
|
||||
|
||||
throw new Error('No IP address found');
|
||||
};
|
||||
@@ -338,10 +338,6 @@ export const formatDocumentAuditLogAction = (
|
||||
anonymous: msg`Document opened`,
|
||||
identified: msg`${prefix} opened the document`,
|
||||
}))
|
||||
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_VIEWED }, () => ({
|
||||
anonymous: msg`Document viewed`,
|
||||
identified: msg`${prefix} viewed the document`,
|
||||
}))
|
||||
.with({ type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_TITLE_UPDATED }, () => ({
|
||||
anonymous: msg`Document title updated`,
|
||||
identified: msg`${prefix} updated the document title`,
|
||||
|
||||
+103
-26
@@ -1,35 +1,112 @@
|
||||
import { type TransportTargetOptions, pino } from 'pino';
|
||||
import Honeybadger from '@honeybadger-io/js';
|
||||
|
||||
import { env } from './env';
|
||||
|
||||
const transports: TransportTargetOptions[] = [];
|
||||
export const buildLogger = () => {
|
||||
if (env('NEXT_PRIVATE_LOGGER_HONEY_BADGER_API_KEY')) {
|
||||
return new HoneybadgerLogger();
|
||||
}
|
||||
|
||||
if (env('NODE_ENV') !== 'production' && !env('INTERNAL_FORCE_JSON_LOGGER')) {
|
||||
transports.push({
|
||||
target: 'pino-pretty',
|
||||
level: 'info',
|
||||
});
|
||||
return new DefaultLogger();
|
||||
};
|
||||
|
||||
interface LoggerDescriptionOptions {
|
||||
method?: string;
|
||||
path?: string;
|
||||
context?: Record<string, unknown>;
|
||||
|
||||
/**
|
||||
* The type of log to be captured.
|
||||
*
|
||||
* Defaults to `info`.
|
||||
*/
|
||||
level?: 'info' | 'error' | 'critical';
|
||||
}
|
||||
|
||||
const loggingFilePath = env('NEXT_PRIVATE_LOGGER_FILE_PATH');
|
||||
/**
|
||||
* Basic logger implementation intended to be used in the server side for capturing
|
||||
* explicit errors and other logs.
|
||||
*
|
||||
* Not intended to capture the request and responses.
|
||||
*/
|
||||
interface Logger {
|
||||
log(message: string, options?: LoggerDescriptionOptions): void;
|
||||
|
||||
if (loggingFilePath) {
|
||||
transports.push({
|
||||
target: 'pino/file',
|
||||
level: 'info',
|
||||
options: {
|
||||
destination: loggingFilePath,
|
||||
mkdir: true,
|
||||
},
|
||||
});
|
||||
error(error: Error, options?: LoggerDescriptionOptions): void;
|
||||
}
|
||||
|
||||
export const logger = pino({
|
||||
level: 'info',
|
||||
transport:
|
||||
transports.length > 0
|
||||
? {
|
||||
targets: transports,
|
||||
}
|
||||
: undefined,
|
||||
});
|
||||
class DefaultLogger implements Logger {
|
||||
log(_message: string, _options?: LoggerDescriptionOptions) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
error(_error: Error, _options?: LoggerDescriptionOptions): void {
|
||||
// Do nothing.
|
||||
}
|
||||
}
|
||||
|
||||
class HoneybadgerLogger implements Logger {
|
||||
constructor() {
|
||||
if (!env('NEXT_PRIVATE_LOGGER_HONEY_BADGER_API_KEY')) {
|
||||
throw new Error('NEXT_PRIVATE_LOGGER_HONEY_BADGER_API_KEY is not set');
|
||||
}
|
||||
|
||||
Honeybadger.configure({
|
||||
apiKey: env('NEXT_PRIVATE_LOGGER_HONEY_BADGER_API_KEY'),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Honeybadger doesn't really have a non-error logging system.
|
||||
*/
|
||||
log(message: string, options?: LoggerDescriptionOptions) {
|
||||
const { context = {}, level = 'info' } = options || {};
|
||||
|
||||
try {
|
||||
Honeybadger.event({
|
||||
message,
|
||||
context: {
|
||||
level,
|
||||
...context,
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
// Do nothing.
|
||||
}
|
||||
}
|
||||
|
||||
error(error: Error, options?: LoggerDescriptionOptions): void {
|
||||
const { context = {}, level = 'error', method, path } = options || {};
|
||||
|
||||
// const tags = [`level:${level}`];
|
||||
const tags = [];
|
||||
|
||||
let errorMessage = error.message;
|
||||
|
||||
if (method) {
|
||||
tags.push(`method:${method}`);
|
||||
|
||||
errorMessage = `[${method}]: ${error.message}`;
|
||||
}
|
||||
|
||||
if (path) {
|
||||
tags.push(`path:${path}`);
|
||||
}
|
||||
|
||||
try {
|
||||
Honeybadger.notify(errorMessage, {
|
||||
context: {
|
||||
level,
|
||||
...context,
|
||||
},
|
||||
tags,
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
|
||||
// Do nothing.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "Document" DROP CONSTRAINT "Document_folderId_fkey";
|
||||
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "Template" DROP CONSTRAINT "Template_folderId_fkey";
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Document" ADD CONSTRAINT "Document_folderId_fkey" FOREIGN KEY ("folderId") REFERENCES "Folder"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Template" ADD CONSTRAINT "Template_folderId_fkey" FOREIGN KEY ("folderId") REFERENCES "Folder"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
@@ -397,7 +397,7 @@ model Document {
|
||||
template Template? @relation(fields: [templateId], references: [id], onDelete: SetNull)
|
||||
|
||||
auditLogs DocumentAuditLog[]
|
||||
folder Folder? @relation(fields: [folderId], references: [id], onDelete: SetNull)
|
||||
folder Folder? @relation(fields: [folderId], references: [id], onDelete: Cascade)
|
||||
folderId String?
|
||||
|
||||
@@unique([documentDataId])
|
||||
@@ -866,7 +866,7 @@ model Template {
|
||||
directLink TemplateDirectLink?
|
||||
documents Document[]
|
||||
|
||||
folder Folder? @relation(fields: [folderId], references: [id], onDelete: SetNull)
|
||||
folder Folder? @relation(fields: [folderId], references: [id], onDelete: Cascade)
|
||||
folderId String?
|
||||
|
||||
@@unique([templateDocumentDataId])
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user