mirror of
https://github.com/documenso/documenso.git
synced 2026-09-30 16:54:34 +10:00
Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f4105e47eb | ||
|
|
b8b98f70c0 | ||
|
|
22c697f712 |
@@ -12,11 +12,20 @@ import { cn } from '@documenso/ui/lib/utils';
|
||||
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@documenso/ui/primitives/accordion';
|
||||
import { Button } from '@documenso/ui/primitives/button';
|
||||
import { ColorPicker } from '@documenso/ui/primitives/color-picker';
|
||||
import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel } from '@documenso/ui/primitives/form/form';
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
FormDescription,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from '@documenso/ui/primitives/form/form';
|
||||
import { Input } from '@documenso/ui/primitives/input';
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@documenso/ui/primitives/select';
|
||||
import { Textarea } from '@documenso/ui/primitives/textarea';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { Trans, useLingui } from '@lingui/react/macro';
|
||||
import type { TeamGlobalSettings } from '@prisma/client';
|
||||
import { Loader } from 'lucide-react';
|
||||
@@ -37,14 +46,17 @@ const ZBrandingPreferencesFormSchema = z.object({
|
||||
.instanceof(File)
|
||||
.refine(
|
||||
(file) => file.size <= BRANDING_LOGO_MAX_SIZE_BYTES,
|
||||
`File size must be less than ${BRANDING_LOGO_MAX_SIZE_MB}MB`,
|
||||
msg`File size must be less than ${BRANDING_LOGO_MAX_SIZE_MB}MB`,
|
||||
)
|
||||
.refine(
|
||||
(file) => BRANDING_LOGO_ALLOWED_TYPES.includes(file.type),
|
||||
msg`Only .jpg, .png, and .webp files are accepted`,
|
||||
)
|
||||
.refine((file) => BRANDING_LOGO_ALLOWED_TYPES.includes(file.type), 'Only .jpg, .png, and .webp files are accepted')
|
||||
.nullish(),
|
||||
brandingUrl: z.string().url().optional().or(z.literal('')),
|
||||
brandingCompanyDetails: z.string().max(500).optional(),
|
||||
brandingUrl: z.string().url(msg`Please enter a valid URL`).optional().or(z.literal('')),
|
||||
brandingCompanyDetails: z.string().max(500, msg`Brand details must be less than 500 characters`).optional(),
|
||||
brandingColors: ZCssVarsSchema.default({}),
|
||||
brandingCss: z.string().max(10_000).default(''),
|
||||
brandingCss: z.string().max(10_000, msg`Custom CSS must be less than 10,000 characters`).default(''),
|
||||
});
|
||||
|
||||
export type TBrandingPreferencesFormSchema = z.infer<typeof ZBrandingPreferencesFormSchema>;
|
||||
@@ -349,6 +361,8 @@ export function BrandingPreferencesForm({
|
||||
</span>
|
||||
)}
|
||||
</FormDescription>
|
||||
|
||||
<FormMessage />
|
||||
</div>
|
||||
</InheritableField>
|
||||
)}
|
||||
@@ -379,6 +393,8 @@ export function BrandingPreferencesForm({
|
||||
</span>
|
||||
)}
|
||||
</FormDescription>
|
||||
|
||||
<FormMessage />
|
||||
</InheritableField>
|
||||
)}
|
||||
/>
|
||||
@@ -413,6 +429,8 @@ export function BrandingPreferencesForm({
|
||||
</span>
|
||||
)}
|
||||
</FormDescription>
|
||||
|
||||
<FormMessage />
|
||||
</InheritableField>
|
||||
)}
|
||||
/>
|
||||
@@ -597,6 +615,8 @@ export function BrandingPreferencesForm({
|
||||
<FormDescription>
|
||||
<Trans>Border radius size in REM units (e.g. 0.5rem).</Trans>
|
||||
</FormDescription>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
@@ -634,6 +654,8 @@ export function BrandingPreferencesForm({
|
||||
shown after you save.
|
||||
</Trans>
|
||||
</FormDescription>
|
||||
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
@@ -37,7 +37,7 @@ import { useMemo } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { Link, useNavigate } from 'react-router';
|
||||
import { match } from 'ts-pattern';
|
||||
import { z } from 'zod';
|
||||
import type { z } from 'zod';
|
||||
|
||||
import { AdminOrganisationDeleteDialog } from '~/components/dialogs/admin-organisation-delete-dialog';
|
||||
import { AdminOrganisationMemberDeleteDialog } from '~/components/dialogs/admin-organisation-member-delete-dialog';
|
||||
@@ -563,15 +563,11 @@ const GenericOrganisationAdminForm = ({ organisation }: OrganisationAdminFormOpt
|
||||
);
|
||||
};
|
||||
|
||||
const ZUpdateOrganisationBillingFormSchema = ZUpdateAdminOrganisationRequestSchema.shape.data
|
||||
.pick({
|
||||
claims: true,
|
||||
customerId: true,
|
||||
originalSubscriptionClaimId: true,
|
||||
})
|
||||
.extend({
|
||||
notifyOrganisation: z.boolean(),
|
||||
});
|
||||
const ZUpdateOrganisationBillingFormSchema = ZUpdateAdminOrganisationRequestSchema.shape.data.pick({
|
||||
claims: true,
|
||||
customerId: true,
|
||||
originalSubscriptionClaimId: true,
|
||||
});
|
||||
|
||||
type TUpdateOrganisationBillingFormSchema = z.infer<typeof ZUpdateOrganisationBillingFormSchema>;
|
||||
|
||||
@@ -618,31 +614,19 @@ const OrganisationAdminForm = ({ organisation, licenseFlags }: OrganisationAdmin
|
||||
emailTransportId: organisation.organisationClaim.emailTransportId ?? null,
|
||||
},
|
||||
originalSubscriptionClaimId: organisation.organisationClaim.originalSubscriptionClaimId || '',
|
||||
notifyOrganisation: true,
|
||||
},
|
||||
});
|
||||
|
||||
const onSubmit = async ({ notifyOrganisation, ...values }: TUpdateOrganisationBillingFormSchema) => {
|
||||
const onSubmit = async (values: TUpdateOrganisationBillingFormSchema) => {
|
||||
try {
|
||||
const { isNotificationSent } = await updateOrganisation({
|
||||
await updateOrganisation({
|
||||
organisationId: organisation.id,
|
||||
data: values,
|
||||
notifyOrganisation,
|
||||
});
|
||||
|
||||
form.setValue('notifyOrganisation', false);
|
||||
|
||||
const description = match({ notifyOrganisation, isNotificationSent })
|
||||
.with({ isNotificationSent: true }, () => t`Organisation has been updated and its owner has been notified`)
|
||||
.with(
|
||||
{ notifyOrganisation: true },
|
||||
() => t`Organisation has been updated. No limits changed, so no notification was sent`,
|
||||
)
|
||||
.otherwise(() => t`Organisation has been updated successfully`);
|
||||
|
||||
toast({
|
||||
title: t`Success`,
|
||||
description,
|
||||
description: t`Organisation has been updated successfully`,
|
||||
duration: 5000,
|
||||
});
|
||||
} catch (err) {
|
||||
@@ -935,31 +919,6 @@ const OrganisationAdminForm = ({ organisation, licenseFlags }: OrganisationAdmin
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="notifyOrganisation"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<div className="flex items-center">
|
||||
<FormControl>
|
||||
<Checkbox id="notify-organisation" checked={field.value} onCheckedChange={field.onChange} />
|
||||
</FormControl>
|
||||
|
||||
<label className="ml-2 text-muted-foreground text-sm" htmlFor="notify-organisation">
|
||||
<Trans>Notify the organisation about limit changes</Trans>
|
||||
</label>
|
||||
</div>
|
||||
<FormDescription>
|
||||
<Trans>
|
||||
Emails the organisation owner to let them know their limits have been updated. No email is sent if no
|
||||
limits changed.
|
||||
</Trans>
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<div className="flex justify-end">
|
||||
<Button type="submit" loading={form.formState.isSubmitting}>
|
||||
<Trans>Update</Trans>
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
import { SUPPORT_EMAIL } from '@documenso/lib/constants/app';
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { useLingui } from '@lingui/react';
|
||||
import { Trans } from '@lingui/react/macro';
|
||||
|
||||
import { Body, Container, Head, Hr, Html, Preview, Section, Text } from '../components';
|
||||
import { TemplateBrandingLogo } from '../template-components/template-branding-logo';
|
||||
import { TemplateFooter } from '../template-components/template-footer';
|
||||
|
||||
export type OrganisationLimitsUpdatedEmailProps = {
|
||||
assetBaseUrl: string;
|
||||
organisationName: string;
|
||||
};
|
||||
|
||||
export const OrganisationLimitsUpdatedEmailTemplate = ({
|
||||
assetBaseUrl = 'http://localhost:3002',
|
||||
organisationName = 'Organisation Name',
|
||||
}: OrganisationLimitsUpdatedEmailProps) => {
|
||||
const { _ } = useLingui();
|
||||
|
||||
return (
|
||||
<Html>
|
||||
<Head />
|
||||
<Body className="mx-auto my-auto font-sans">
|
||||
<Preview>{_(ORGANISATION_LIMITS_UPDATED_SUBJECT)}</Preview>
|
||||
|
||||
<Section className="bg-background text-muted-foreground">
|
||||
<Container className="mx-auto mt-8 mb-2 max-w-xl rounded-lg border border-border border-solid p-2 backdrop-blur-sm">
|
||||
<TemplateBrandingLogo assetBaseUrl={assetBaseUrl} className="mb-4 h-6 p-2" />
|
||||
|
||||
<Section className="p-2 text-muted-foreground">
|
||||
<Text className="text-center font-medium text-foreground text-lg">
|
||||
{_(ORGANISATION_LIMITS_UPDATED_SUBJECT)}
|
||||
</Text>
|
||||
|
||||
<div className="mx-auto my-2 w-fit rounded-lg bg-muted px-4 py-2 font-medium text-base text-muted-foreground">
|
||||
{organisationName}
|
||||
</div>
|
||||
|
||||
<Text className="text-center text-base">
|
||||
<Trans>We've updated the usage limits for your organisation.</Trans>
|
||||
</Text>
|
||||
|
||||
<Text className="text-center text-base">
|
||||
<Trans>
|
||||
If you recently received an email regarding your usage limits, this change was made in response to
|
||||
that.
|
||||
</Trans>
|
||||
</Text>
|
||||
|
||||
<Text className="text-center text-base">
|
||||
<Trans>If you have any questions, please contact support at {SUPPORT_EMAIL}.</Trans>
|
||||
</Text>
|
||||
</Section>
|
||||
</Container>
|
||||
|
||||
<Hr className="mx-auto mt-12 max-w-xl" />
|
||||
|
||||
<Container className="mx-auto max-w-xl">
|
||||
<TemplateFooter isDocument={false} />
|
||||
</Container>
|
||||
</Section>
|
||||
</Body>
|
||||
</Html>
|
||||
);
|
||||
};
|
||||
|
||||
export const ORGANISATION_LIMITS_UPDATED_SUBJECT = msg`Your organisation limits have been updated`;
|
||||
|
||||
export default OrganisationLimitsUpdatedEmailTemplate;
|
||||
@@ -7,7 +7,6 @@ import { SEND_DOCUMENT_CREATED_FROM_DIRECT_TEMPLATE_EMAIL_JOB_DEFINITION } from
|
||||
import { SEND_DOCUMENT_DELETED_EMAILS_JOB_DEFINITION } from './definitions/emails/send-document-deleted-emails';
|
||||
import { SEND_DOCUMENT_PENDING_EMAIL_JOB_DEFINITION } from './definitions/emails/send-document-pending-email';
|
||||
import { SEND_ORGANISATION_LIMIT_ALERT_EMAIL_JOB_DEFINITION } from './definitions/emails/send-organisation-limit-alert-email';
|
||||
import { SEND_ORGANISATION_LIMITS_UPDATED_EMAIL_JOB_DEFINITION } from './definitions/emails/send-organisation-limits-updated-email';
|
||||
import { SEND_ORGANISATION_MEMBER_JOINED_EMAIL_JOB_DEFINITION } from './definitions/emails/send-organisation-member-joined-email';
|
||||
import { SEND_ORGANISATION_MEMBER_LEFT_EMAIL_JOB_DEFINITION } from './definitions/emails/send-organisation-member-left-email';
|
||||
import { SEND_OWNER_RECIPIENT_EXPIRED_EMAIL_JOB_DEFINITION } from './definitions/emails/send-owner-recipient-expired-email';
|
||||
@@ -44,7 +43,6 @@ export const jobsClient = new JobClient([
|
||||
SEND_ORGANISATION_MEMBER_JOINED_EMAIL_JOB_DEFINITION,
|
||||
SEND_ORGANISATION_MEMBER_LEFT_EMAIL_JOB_DEFINITION,
|
||||
SEND_ORGANISATION_LIMIT_ALERT_EMAIL_JOB_DEFINITION,
|
||||
SEND_ORGANISATION_LIMITS_UPDATED_EMAIL_JOB_DEFINITION,
|
||||
SEND_TEAM_DELETED_EMAIL_JOB_DEFINITION,
|
||||
SEAL_DOCUMENT_JOB_DEFINITION,
|
||||
SEAL_DOCUMENT_SWEEP_JOB_DEFINITION,
|
||||
|
||||
@@ -129,7 +129,6 @@ export const run = async ({
|
||||
text: `
|
||||
Organisation: ${organisation.name}
|
||||
Organisation ID: ${organisation.id}
|
||||
Admin URL: ${NEXT_PUBLIC_WEBAPP_URL()}/admin/organisations/${organisation.id}
|
||||
Organisation Claim Original ID: ${organisation.organisationClaim.originalSubscriptionClaimId}
|
||||
Email Quota: ${organisation.monthlyStats[0]?.emailCount || 0}/${organisation.organisationClaim.emailQuota ?? 'Unlimited'}
|
||||
API Quota: ${organisation.monthlyStats[0]?.apiCount || 0}/${organisation.organisationClaim.apiQuota ?? 'Unlimited'}
|
||||
|
||||
-65
@@ -1,65 +0,0 @@
|
||||
import OrganisationLimitsUpdatedEmailTemplate, {
|
||||
ORGANISATION_LIMITS_UPDATED_SUBJECT,
|
||||
} from '@documenso/email/templates/organisation-limits-updated';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
import { createElement } from 'react';
|
||||
|
||||
import { getI18nInstance } from '../../../client-only/providers/i18n-server';
|
||||
import { NEXT_PUBLIC_WEBAPP_URL } from '../../../constants/app';
|
||||
import { getEmailContext } from '../../../server-only/email/get-email-context';
|
||||
import { renderEmailWithI18N } from '../../../utils/render-email-with-i18n';
|
||||
import type { JobRunIO } from '../../client/_internal/job';
|
||||
import type { TSendOrganisationLimitsUpdatedEmailJobDefinition } from './send-organisation-limits-updated-email';
|
||||
|
||||
export const run = async ({
|
||||
payload,
|
||||
io,
|
||||
}: {
|
||||
payload: TSendOrganisationLimitsUpdatedEmailJobDefinition;
|
||||
io: JobRunIO;
|
||||
}) => {
|
||||
const organisation = await prisma.organisation.findFirstOrThrow({
|
||||
where: {
|
||||
id: payload.organisationId,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
owner: {
|
||||
select: {
|
||||
email: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const { branding, emailLanguage, senderEmail, emailTransport } = await getEmailContext({
|
||||
emailType: 'INTERNAL',
|
||||
source: {
|
||||
type: 'organisation',
|
||||
organisationId: organisation.id,
|
||||
},
|
||||
});
|
||||
|
||||
await io.runTask('send-organisation-limits-updated-email', async () => {
|
||||
const emailContent = createElement(OrganisationLimitsUpdatedEmailTemplate, {
|
||||
assetBaseUrl: NEXT_PUBLIC_WEBAPP_URL(),
|
||||
organisationName: organisation.name,
|
||||
});
|
||||
|
||||
const [html, text] = await Promise.all([
|
||||
renderEmailWithI18N(emailContent, { lang: emailLanguage, branding }),
|
||||
renderEmailWithI18N(emailContent, { lang: emailLanguage, branding, plainText: true }),
|
||||
]);
|
||||
|
||||
const i18n = await getI18nInstance(emailLanguage);
|
||||
|
||||
await emailTransport.sendMail({
|
||||
to: organisation.owner.email,
|
||||
from: senderEmail,
|
||||
subject: i18n._(ORGANISATION_LIMITS_UPDATED_SUBJECT),
|
||||
html,
|
||||
text,
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -1,31 +0,0 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
import type { JobDefinition } from '../../client/_internal/job';
|
||||
|
||||
const SEND_ORGANISATION_LIMITS_UPDATED_EMAIL_JOB_DEFINITION_ID = 'send.organisation-limits-updated.email';
|
||||
|
||||
const SEND_ORGANISATION_LIMITS_UPDATED_EMAIL_JOB_DEFINITION_SCHEMA = z.object({
|
||||
organisationId: z.string(),
|
||||
});
|
||||
|
||||
export type TSendOrganisationLimitsUpdatedEmailJobDefinition = z.infer<
|
||||
typeof SEND_ORGANISATION_LIMITS_UPDATED_EMAIL_JOB_DEFINITION_SCHEMA
|
||||
>;
|
||||
|
||||
export const SEND_ORGANISATION_LIMITS_UPDATED_EMAIL_JOB_DEFINITION = {
|
||||
id: SEND_ORGANISATION_LIMITS_UPDATED_EMAIL_JOB_DEFINITION_ID,
|
||||
name: 'Send Organisation Limits Updated Email',
|
||||
version: '1.0.0',
|
||||
trigger: {
|
||||
name: SEND_ORGANISATION_LIMITS_UPDATED_EMAIL_JOB_DEFINITION_ID,
|
||||
schema: SEND_ORGANISATION_LIMITS_UPDATED_EMAIL_JOB_DEFINITION_SCHEMA,
|
||||
},
|
||||
handler: async ({ payload, io }) => {
|
||||
const handler = await import('./send-organisation-limits-updated-email.handler');
|
||||
|
||||
await handler.run({ payload, io });
|
||||
},
|
||||
} as const satisfies JobDefinition<
|
||||
typeof SEND_ORGANISATION_LIMITS_UPDATED_EMAIL_JOB_DEFINITION_ID,
|
||||
TSendOrganisationLimitsUpdatedEmailJobDefinition
|
||||
>;
|
||||
@@ -51,6 +51,12 @@ export async function rejectDocumentWithToken({ token, id, reason, requestMetada
|
||||
});
|
||||
}
|
||||
|
||||
if (recipient.signingStatus !== SigningStatus.NOT_SIGNED) {
|
||||
throw new AppError(AppErrorCode.INVALID_REQUEST, {
|
||||
message: `Recipient ${recipient.id} has already actioned this document`,
|
||||
});
|
||||
}
|
||||
|
||||
assertRecipientNotExpired(recipient);
|
||||
|
||||
// Update the recipient status to rejected
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
export type OrganisationLimits = {
|
||||
documentQuota: number | null;
|
||||
emailQuota: number | null;
|
||||
apiQuota: number | null;
|
||||
teamCount: number;
|
||||
memberCount: number;
|
||||
recipientCount: number;
|
||||
envelopeItemCount: number;
|
||||
};
|
||||
|
||||
const ORGANISATION_LIMIT_KEYS = [
|
||||
'documentQuota',
|
||||
'emailQuota',
|
||||
'apiQuota',
|
||||
'teamCount',
|
||||
'memberCount',
|
||||
'recipientCount',
|
||||
'envelopeItemCount',
|
||||
] as const satisfies (keyof OrganisationLimits)[];
|
||||
|
||||
export const hasOrganisationLimitsChanged = (
|
||||
previous: OrganisationLimits,
|
||||
next: Partial<OrganisationLimits>,
|
||||
): boolean => {
|
||||
return ORGANISATION_LIMIT_KEYS.some((key) => next[key] !== undefined && next[key] !== previous[key]);
|
||||
};
|
||||
@@ -1,6 +1,4 @@
|
||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||
import { jobs } from '@documenso/lib/jobs/client';
|
||||
import { hasOrganisationLimitsChanged } from '@documenso/lib/universal/organisation-limit-changes';
|
||||
import { prisma } from '@documenso/prisma';
|
||||
|
||||
import { adminProcedure } from '../trpc';
|
||||
@@ -13,12 +11,11 @@ export const updateAdminOrganisationRoute = adminProcedure
|
||||
.input(ZUpdateAdminOrganisationRequestSchema)
|
||||
.output(ZUpdateAdminOrganisationResponseSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const { organisationId, data, notifyOrganisation } = input;
|
||||
const { organisationId, data } = input;
|
||||
|
||||
ctx.logger.info({
|
||||
input: {
|
||||
organisationId,
|
||||
notifyOrganisation,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -37,8 +34,6 @@ export const updateAdminOrganisationRoute = adminProcedure
|
||||
|
||||
const { name, url, customerId, claims, originalSubscriptionClaimId } = data;
|
||||
|
||||
const isLimitsChanged = claims ? hasOrganisationLimitsChanged(organisation.organisationClaim, claims) : false;
|
||||
|
||||
await prisma.organisation.update({
|
||||
where: {
|
||||
id: organisationId,
|
||||
@@ -59,26 +54,4 @@ export const updateAdminOrganisationRoute = adminProcedure
|
||||
originalSubscriptionClaimId,
|
||||
},
|
||||
});
|
||||
|
||||
if (!notifyOrganisation) {
|
||||
return { isNotificationSent: false };
|
||||
}
|
||||
|
||||
if (!isLimitsChanged) {
|
||||
ctx.logger.info({
|
||||
msg: 'Skipping organisation limits updated email, no limits changed',
|
||||
organisationId,
|
||||
});
|
||||
|
||||
return { isNotificationSent: false };
|
||||
}
|
||||
|
||||
await jobs.triggerJob({
|
||||
name: 'send.organisation-limits-updated.email',
|
||||
payload: {
|
||||
organisationId,
|
||||
},
|
||||
});
|
||||
|
||||
return { isNotificationSent: true };
|
||||
});
|
||||
|
||||
@@ -26,11 +26,8 @@ export const ZUpdateAdminOrganisationRequestSchema = z.object({
|
||||
customerId: z.string().optional(),
|
||||
originalSubscriptionClaimId: z.string().optional(),
|
||||
}),
|
||||
notifyOrganisation: z.boolean().optional(),
|
||||
});
|
||||
|
||||
export const ZUpdateAdminOrganisationResponseSchema = z.object({
|
||||
isNotificationSent: z.boolean(),
|
||||
});
|
||||
export const ZUpdateAdminOrganisationResponseSchema = z.void();
|
||||
|
||||
export type TUpdateAdminOrganisationRequest = z.infer<typeof ZUpdateAdminOrganisationRequestSchema>;
|
||||
|
||||
@@ -41,6 +41,15 @@ export const SignaturePadDialog = ({
|
||||
const [showSignatureModal, setShowSignatureModal] = useState(false);
|
||||
const [signature, setSignature] = useState<string>(value ?? '');
|
||||
|
||||
const onOpenChange = (open: boolean) => {
|
||||
if (disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
setSignature(value ?? '');
|
||||
setShowSignatureModal(open);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
@@ -62,7 +71,7 @@ export const SignaturePadDialog = ({
|
||||
type="button"
|
||||
disabled={disabled}
|
||||
className="absolute inset-0 flex items-center justify-center bg-transparent"
|
||||
onClick={() => setShowSignatureModal(true)}
|
||||
onClick={() => onOpenChange(true)}
|
||||
whileHover="onHover"
|
||||
>
|
||||
{!value && !disableAnimation && (
|
||||
@@ -109,7 +118,7 @@ export const SignaturePadDialog = ({
|
||||
)}
|
||||
</motion.button>
|
||||
|
||||
<Dialog open={showSignatureModal} onOpenChange={disabled ? undefined : setShowSignatureModal}>
|
||||
<Dialog open={showSignatureModal} onOpenChange={onOpenChange}>
|
||||
<DialogContent hideClose={true} className="p-6 pt-4">
|
||||
<SignaturePad
|
||||
id="signature"
|
||||
@@ -135,7 +144,7 @@ export const SignaturePadDialog = ({
|
||||
disabled={!signature}
|
||||
onClick={() => {
|
||||
onChange(signature);
|
||||
setShowSignatureModal(false);
|
||||
onOpenChange(false);
|
||||
}}
|
||||
>
|
||||
{dialogConfirmText ? parseMessageDescriptor(i18n._, dialogConfirmText) : <Trans>Next</Trans>}
|
||||
|
||||
Reference in New Issue
Block a user