Compare commits

..

2 Commits

Author SHA1 Message Date
Ahmed Bouhuolia
0517b3e430 Merge branch 'develop' into add-mail-invoice-receipt-schema 2024-11-10 14:37:11 +02:00
Ahmed Bouhuolia
4c67d2e321 feat: add invoice mail receipt schema 2024-11-06 14:17:28 +02:00
7 changed files with 104 additions and 177 deletions

View File

@@ -32,7 +32,6 @@ import { BrandingTemplatesDrawer } from '@/containers/BrandingTemplates/Branding
import { DRAWERS } from '@/constants/drawers';
import { InvoiceSendMailDrawer } from '@/containers/Sales/Invoices/InvoiceSendMailDrawer/InvoiceSendMailDrawer';
import { NewSubscriptionDrawer } from '@/containers/Subscriptions/drawers/NewSubscriptionDrawer/NewSubscriptionDrawer';
/**
* Drawers container of the dashboard.
@@ -73,7 +72,6 @@ export default function DrawersContainer() {
<TaxRateDetailsDrawer name={DRAWERS.TAX_RATE_DETAILS} />
<CategorizeTransactionDrawer name={DRAWERS.CATEGORIZE_TRANSACTION} />
<ChangeSubscriptionPlanDrawer name={DRAWERS.CHANGE_SUBSCARIPTION_PLAN} />
<NewSubscriptionDrawer name={DRAWERS.NEW_SUBSCRIPTION_PLANS} />
<InvoiceCustomizeDrawer name={DRAWERS.INVOICE_CUSTOMIZE} />
<EstimateCustomizeDrawer name={DRAWERS.ESTIMATE_CUSTOMIZE} />
<ReceiptCustomizeDrawer name={DRAWERS.RECEIPT_CUSTOMIZE} />

View File

@@ -25,7 +25,6 @@ export enum DRAWERS {
TAX_RATE_DETAILS = 'tax-rate-detail-drawer',
CATEGORIZE_TRANSACTION = 'categorize-transaction',
CHANGE_SUBSCARIPTION_PLAN = 'change-subscription-plan',
NEW_SUBSCRIPTION_PLANS = 'NEW_SUBSCRIPTION_PLANS',
INVOICE_CUSTOMIZE = 'INVOICE_CUSTOMIZE',
ESTIMATE_CUSTOMIZE = 'ESTIMATE_CUSTOMIZE',
PAYMENT_RECEIPT_CUSTOMIZE = 'PAYMENT_RECEIPT_CUSTOMIZE',
@@ -35,5 +34,5 @@ export enum DRAWERS {
BRANDING_TEMPLATES = 'BRANDING_TEMPLATES',
PAYMENT_INVOICE_PREVIEW = 'PAYMENT_INVOICE_PREVIEW',
STRIPE_PAYMENT_INTEGRATION_EDIT = 'STRIPE_PAYMENT_INTEGRATION_EDIT',
INVOICE_SEND_MAIL = 'INVOICE_SEND_MAIL',
INVOICE_SEND_MAIL = 'INVOICE_SEND_MAIL'
}

View File

@@ -2,8 +2,8 @@
import * as R from 'ramda';
import clsx from 'classnames';
import { includes } from 'lodash';
import { Button, Card, Classes, Intent, Text } from '@blueprintjs/core';
import { Box, Group, Stack } from '@/components';
import { Button, Card, Classes, Intent, Text } from '@blueprintjs/core';
import withAlertActions from '../Alert/withAlertActions';
import styles from './BillingSubscription.module.scss';
import withDrawerActions from '../Drawer/withDrawerActions';
@@ -18,11 +18,12 @@ function SubscriptionRoot({ openAlert, openDrawer }) {
if (!mainSubscription) {
return null;
}
// Handle cancel subscription button click.
const handleCancelSubBtnClick = () => {
openAlert('cancel-main-subscription');
};
// Handle update payment method button click.
const handleResumeSubBtnClick = () => {
openAlert('resume-main-subscription');
};
const handleUpdatePaymentMethod = () => {
window.LemonSqueezy.Url.Open(
mainSubscription.lemonUrls?.updatePaymentMethod,
@@ -32,10 +33,6 @@ function SubscriptionRoot({ openAlert, openDrawer }) {
const handleUpgradeBtnClick = () => {
openDrawer(DRAWERS.CHANGE_SUBSCARIPTION_PLAN);
};
// Handles renew the expired subscription.
const handleNewSubscriptionBtnClick = () => {
openDrawer(DRAWERS.NEW_SUBSCRIPTION_PLANS);
};
return (
<Card className={styles.root}>
@@ -69,53 +66,50 @@ function SubscriptionRoot({ openAlert, openDrawer }) {
</Text>
<Stack align="flex-start" spacing={8} className={styles.actions}>
<Button
minimal
small
inline
intent={Intent.PRIMARY}
onClick={handleUpgradeBtnClick}
>
Upgrade the Plan
</Button>
{mainSubscription.canceled && (
<Button
minimal
small
inline
intent={Intent.PRIMARY}
onClick={handleNewSubscriptionBtnClick}
onClick={handleResumeSubBtnClick}
>
Renew Subscription
Resume Subscription
</Button>
)}
{!mainSubscription.canceled && (
<>
<Button
minimal
small
inline
intent={Intent.PRIMARY}
onClick={handleUpgradeBtnClick}
>
Upgrade the Plan
</Button>
<Button
minimal
small
inline
intent={Intent.PRIMARY}
onClick={handleCancelSubBtnClick}
>
Cancel Subscription
</Button>
<Button
minimal
small
inline
intent={Intent.PRIMARY}
onClick={handleUpdatePaymentMethod}
>
Change Payment Method
</Button>
</>
<Button
minimal
small
inline
intent={Intent.PRIMARY}
onClick={handleCancelSubBtnClick}
>
Cancel Subscription
</Button>
)}
<Button
minimal
small
inline
intent={Intent.PRIMARY}
onClick={handleUpdatePaymentMethod}
>
Change Payment Method
</Button>
</Stack>
<Group position={'apart'} mt={'auto'}>
<Group position={'apart'} style={{ marginTop: 'auto' }}>
<Group spacing={4}>
<Text className={styles.priceAmount}>
{mainSubscription.planPriceFormatted}
@@ -126,8 +120,8 @@ function SubscriptionRoot({ openAlert, openDrawer }) {
{mainSubscription.planPeriod === 'month'
? 'mo'
: mainSubscription.planPeriod === 'year'
? 'yearly'
: ''}
? 'yearly'
: ''}
</Text>
)}
</Group>
@@ -136,10 +130,10 @@ function SubscriptionRoot({ openAlert, openDrawer }) {
{mainSubscription.canceled && (
<Button
intent={Intent.PRIMARY}
onClick={handleNewSubscriptionBtnClick}
onClick={handleResumeSubBtnClick}
className={styles.subscribeButton}
>
Renew Subscription
Resume Subscription
</Button>
)}
</Box>

View File

@@ -1,26 +0,0 @@
import { Box } from "@/components";
import { SubscriptionPlansPeriodSwitcher } from "@/containers/Setup/SetupSubscription/SubscriptionPlansPeriodSwitcher";
import { Callout, Classes } from "@blueprintjs/core";
import { NewSubscriptionPlans } from "./NewSubscriptionPlans";
export function NewSubscriptionContent() {
return (
<Box className={Classes.DRAWER_BODY}>
<Box
maxWidth={1024}
margin="0 auto"
padding={'50px 20px 80px'}
>
<Callout style={{ marginBottom: '2rem' }} icon={null}>
Simple plans. Simple prices. Only pay for what you really need. All
plans come with award-winning 24/7 customer support. Prices do not
include applicable taxes.
</Callout>
<SubscriptionPlansPeriodSwitcher />
<NewSubscriptionPlans />
</Box>
</Box>
)
}

View File

@@ -1,40 +0,0 @@
// @ts-nocheck
import React, { lazy } from 'react';
import * as R from 'ramda';
import { Position } from '@blueprintjs/core';
import { Drawer, DrawerHeaderContent, DrawerSuspense } from '@/components';
import withDrawers from '@/containers/Drawer/withDrawers';
import { DRAWERS } from '@/constants/drawers';
const NewSubscriptionContent = lazy(() =>
import('./NewSubscriptionContent').then((module) => ({
default: module.NewSubscriptionContent,
})),
);
function NewSubscriptionDrawerRoot({
name,
// #withDrawer
isOpen,
}) {
return (
<Drawer
isOpen={isOpen}
name={name}
size={'calc(100% - 5px)'}
position={Position.BOTTOM}
>
<DrawerSuspense>
<DrawerHeaderContent
name={DRAWERS.NEW_SUBSCRIPTION_PLANS}
title={'Renew Subscription Plan'}
/>
<NewSubscriptionContent />
</DrawerSuspense>
</Drawer>
);
}
export const NewSubscriptionDrawer = R.compose(withDrawers())(
NewSubscriptionDrawerRoot,
);

View File

@@ -1,62 +0,0 @@
// @ts-nocheck
import * as R from 'ramda';
import { Intent } from '@blueprintjs/core';
import { AppToaster, Group } from '@/components';
import { SubscriptionPlan } from '../../component/SubscriptionPlan';
import { SubscriptionPlansPeriod } from '@/store/plans/plans.reducer';
import { useSubscriptionPlans } from '@/hooks/constants/useSubscriptionPlans';
import { withSubscriptionPlanMapper } from '../../component/withSubscriptionPlanMapper';
import { useGetLemonSqueezyCheckout } from '@/hooks/query';
export function NewSubscriptionPlans() {
const subscriptionPlans = useSubscriptionPlans();
return (
<Group spacing={14} noWrap align="stretch">
{subscriptionPlans.map((plan, index) => (
<NewSubscriptionPlanMapped plan={plan} />
))}
</Group>
);
}
export const NewSubscriptionPlanMapped = R.compose(
withSubscriptionPlanMapper,
)(
({
monthlyVariantId,
annuallyVariantId,
plansPeriod,
...props
}) => {
const { mutateAsync: getLemonCheckout, isLoading } =
useGetLemonSqueezyCheckout();
// Handles the subscribe button click.
const handleSubscribe = () => {
const variantId =
plansPeriod === SubscriptionPlansPeriod.Monthly
? monthlyVariantId
: annuallyVariantId;
getLemonCheckout({ variantId })
.then((res) => {
const checkoutUrl = res.data.data.attributes.url;
window.LemonSqueezy.Url.Open(checkoutUrl);
})
.catch(() => {
AppToaster.show({
message: 'Something went wrong!',
intent: Intent.DANGER,
});
});
};
return (
<SubscriptionPlan
{...props}
onSubscribe={handleSubscribe}
subscribeButtonProps={{ loading: isLoading }}
/>
);
},
);

View File

@@ -0,0 +1,64 @@
export interface InvoicePaymentEmailSchemaProps {
companyName: string;
companyLogoUri: string;
total: string;
currencyCode: string;
dueDate: string;
paymentUrl: string;
invoiceNumber: string;
invoiceDate: string;
}
export function InvoicePaymentEmailSchema({
companyName,
companyLogoUri,
currencyCode,
total,
paymentUrl,
dueDate,
invoiceDate,
invoiceNumber
}: InvoicePaymentEmailSchemaProps) {
return (
<script type="application/ld+json">
{`
{
"@context": "http://schema.org",
"@type": "Invoice",
"description": "Invoice for services rendered",
"paymentStatus": "http://schema.org/PaymentDue",
"totalPaymentDue": {
"@type": "MonetaryAmount",
"currency": "${currencyCode}",
"value": "${total}"
},
"provider": {
"@type": "Organization",
"name": "${companyName}",
"logo": "${companyLogoUri}",
"telephone": "+1234567890"
},
"paymentDueDate": "${dueDate}",
"paymentUrl": "${paymentUrl}",
"referencesOrder": {
"@type": "Order",
"orderNumber": "${invoiceNumber}",
"orderStatus": "http://schema.org/OrderPaymentDue",
"orderDate": "${invoiceDate}"
},
"potentialAction": {
"@type": "ViewAction",
"target": "${paymentUrl}",
"name": "View and Pay Invoice"
}
}
`}
</script>
);
}