feat: wip send invoice preview

This commit is contained in:
Ahmed Bouhuolia
2024-10-26 18:39:36 +02:00
parent 728b4cacd9
commit ce40d67ea2
7 changed files with 198 additions and 80 deletions

View File

@@ -1,5 +1,9 @@
import { Classes, Text } from '@blueprintjs/core'; import { Classes, Text } from '@blueprintjs/core';
import { PaperTemplate, PaperTemplateTotalBorder } from './PaperTemplate'; import {
PaperTemplate,
PaperTemplateProps,
PaperTemplateTotalBorder,
} from './PaperTemplate';
import { Box, Group, Stack } from '@/components'; import { Box, Group, Stack } from '@/components';
import { import {
DefaultPdfTemplateTerms, DefaultPdfTemplateTerms,
@@ -23,7 +27,7 @@ interface PaperTax {
amount: string; amount: string;
} }
export interface InvoicePaperTemplateProps { export interface InvoicePaperTemplateProps extends PaperTemplateProps {
primaryColor?: string; primaryColor?: string;
secondaryColor?: string; secondaryColor?: string;
@@ -177,9 +181,14 @@ export function InvoicePaperTemplate({
statementLabel = 'Statement', statementLabel = 'Statement',
showStatement = true, showStatement = true,
statement = DefaultPdfTemplateStatement, statement = DefaultPdfTemplateStatement,
...props
}: InvoicePaperTemplateProps) { }: InvoicePaperTemplateProps) {
return ( return (
<PaperTemplate primaryColor={primaryColor} secondaryColor={secondaryColor}> <PaperTemplate
primaryColor={primaryColor}
secondaryColor={secondaryColor}
{...props}
>
<Stack spacing={24}> <Stack spacing={24}>
<Group align="start" spacing={10}> <Group align="start" spacing={10}>
<Stack flex={1}> <Stack flex={1}>

View File

@@ -1,10 +1,10 @@
import React from 'react'; import React from 'react';
import clsx from 'classnames'; import clsx from 'classnames';
import { get, isFunction } from 'lodash'; import { get, isFunction } from 'lodash';
import { Box, Group, GroupProps } from '@/components'; import { Box, BoxProps, Group, GroupProps } from '@/components';
import styles from './InvoicePaperTemplate.module.scss'; import styles from './InvoicePaperTemplate.module.scss';
export interface PaperTemplateProps { export interface PaperTemplateProps extends BoxProps {
primaryColor?: string; primaryColor?: string;
secondaryColor?: string; secondaryColor?: string;
children?: React.ReactNode; children?: React.ReactNode;
@@ -14,13 +14,13 @@ export function PaperTemplate({
primaryColor, primaryColor,
secondaryColor, secondaryColor,
children, children,
...restProps
}: PaperTemplateProps) { }: PaperTemplateProps) {
return ( return (
<div className={styles.root}> <Box {...restProps} className={clsx(styles.root, restProps?.className)}>
<style>{`:root { --invoice-primary-color: ${primaryColor}; --invoice-secondary-color: ${secondaryColor}; }`}</style> <style>{`:root { --invoice-primary-color: ${primaryColor}; --invoice-secondary-color: ${secondaryColor}; }`}</style>
{children} {children}
</div> </Box>
); );
} }
@@ -118,9 +118,9 @@ PaperTemplate.TotalLine = ({
); );
}; };
PaperTemplate.MutedText = () => { }; PaperTemplate.MutedText = () => {};
PaperTemplate.Text = () => { }; PaperTemplate.Text = () => {};
PaperTemplate.AddressesGroup = (props: GroupProps) => { PaperTemplate.AddressesGroup = (props: GroupProps) => {
return ( return (

View File

@@ -1,7 +1,13 @@
import * as R from 'ramda'; import * as R from 'ramda';
import { Drawer, DrawerSuspense } from '@/components'; import { Drawer, DrawerSuspense } from '@/components';
import { InvoiceSendMailContent } from './InvoiceSendMailContent';
import withDrawers from '@/containers/Drawer/withDrawers'; import withDrawers from '@/containers/Drawer/withDrawers';
import React from 'react';
const InvoiceSendMailContent = React.lazy(() =>
import('./InvoiceSendMailContent').then((module) => ({
default: module.InvoiceSendMailContent,
})),
);
interface InvoiceSendMailDrawerProps { interface InvoiceSendMailDrawerProps {
name: string; name: string;

View File

@@ -1,24 +1,21 @@
// @ts-nocheck // @ts-nocheck
import { useState } from 'react';
import { Button, Intent, MenuItem } from '@blueprintjs/core'; import { Button, Intent, MenuItem } from '@blueprintjs/core';
import { useFormikContext } from 'formik'; import { useFormikContext } from 'formik';
import { css } from '@emotion/css';
import { x } from '@xstyled/emotion';
import { unique, chain } from 'lodash';
import { import {
FFormGroup, FFormGroup,
FInputGroup, FInputGroup,
FMultiSelect, FMultiSelect,
FTextArea,
Group, Group,
Stack, Stack,
} from '@/components'; } from '@/components';
import { useDrawerContext } from '@/components/Drawer/DrawerProvider'; import { useDrawerContext } from '@/components/Drawer/DrawerProvider';
import { useDrawerActions } from '@/hooks/state'; import { useDrawerActions } from '@/hooks/state';
import { SelectOptionProps } from '@blueprintjs-formik/select';
const commonAddressSelect = {
placeholder: '',
labelAccessor: '',
valueAccessor: 'mail',
tagAccessor: (item) => `<${item.label}> (${item.mail})`,
textAccessor: (item) => `<${item.label}> (${item.mail})`,
};
// Create new account renderer. // Create new account renderer.
const createNewItemRenderer = (query, active, handleClick) => { const createNewItemRenderer = (query, active, handleClick) => {
@@ -36,12 +33,47 @@ const createNewItemRenderer = (query, active, handleClick) => {
const createNewItemFromQuery = (name) => ({ name }); const createNewItemFromQuery = (name) => ({ name });
export function InvoiceSendMailFields() { export function InvoiceSendMailFields() {
const allowCreate = true; const [showCCField, setShowCCField] = useState<boolean>(false);
// Maybe inject new item props to select component. const [showBccField, setShowBccField] = useState<boolean>(false);
const maybeCreateNewItemRenderer = allowCreate ? createNewItemRenderer : null;
const maybeCreateNewItemFromQuery = allowCreate const { values, setFieldValue } = useFormikContext();
? createNewItemFromQuery
: null; const items = chain([...values?.to, ...values?.cc, ...values?.bcc])
.filter((email) => !!email?.trim())
.uniq()
.map((email) => ({
value: email,
text: email,
}))
.value();
const handleClickCcBtn = (event) => {
event.preventDefault();
event.stopPropagation();
setShowCCField(true);
};
const handleClickBccBtn = (event) => {
event.preventDefault();
event.stopPropagation();
setShowBccField(true);
};
const handleCreateToItemSelect = (value: SelectOptionProps) => {
const _value = [...values?.to, value?.name];
setFieldValue('to', _value);
};
const handleCreateCcItemSelect = (value: SelectOptionProps) => {
const _value = [...values?.cc, value?.name];
setFieldValue('cc', _value);
};
const handleCreateBccItemSelect = (value: SelectOptionProps) => {
const _value = [...values?.bcc, value?.name];
setFieldValue('bcc', _value);
};
return ( return (
<Stack <Stack
@@ -51,54 +83,109 @@ export function InvoiceSendMailFields() {
spacing={0} spacing={0}
borderRight="1px solid #dcdcdd" borderRight="1px solid #dcdcdd"
> >
<Stack overflow="auto" flex="1" p={'30px'}> <Stack spacing={0} overflow="auto" flex="1" p={'30px'} className={css`
<FFormGroup label={'to'} name={'To'}>
<Stack spacing={0}> `}>
<FFormGroup label={'To'} name={'to'}>
<Stack spacing={0} className={css`
> :not(:first-of-type) .bp4-input{
border-top-color: transparent;
border-top-right-radius: 0;
border-top-left-radius: 0;
}
> :not(:last-of-type) .bp4-input {
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
`}>
<FMultiSelect <FMultiSelect
items={[]} items={items}
name={'to'} name={'to'}
placeholder={''}
popoverProps={{ minimal: true, fill: true }} popoverProps={{ minimal: true, fill: true }}
tagInputProps={{ tagInputProps={{
tagProps: { round: true, minimal: true, large: true }, tagProps: { round: true, minimal: true, large: true },
large: true, large: true,
rightElement: ( rightElement: (
<Group> <Group
<a href="#">CC</a> spacing={10}
<a href="#">BCC</a> paddingRight={12}
fontWeight={500}
color={'#000'}
>
<x.a
href="#"
onClick={handleClickCcBtn}
color="#404854"
fontSize={'12px'}
className={css`
&:hover {
text-decoration: none;
}
`}
>
CC
</x.a>
<x.a
href="#"
onClick={handleClickBccBtn}
color="#404854"
fontSize={'12px'}
className={css`
&:hover {
text-decoration: none;
}
`}
>
BCC
</x.a>
</Group> </Group>
), ),
}} }}
fill={true} createNewItemRenderer={createNewItemRenderer}
createNewItemRenderer={maybeCreateNewItemRenderer} createNewItemFromQuery={createNewItemFromQuery}
createNewItemFromQuery={maybeCreateNewItemFromQuery} onCreateItemSelect={handleCreateToItemSelect}
{...commonAddressSelect} resetOnQuery
/> resetOnSelect
<FMultiSelect fill
items={[]}
name={'cc'}
popoverProps={{ minimal: true, fill: true }}
tagInputProps={{
tagProps: { round: true, minimal: true, large: true },
large: true,
}}
fill={true}
createNewItemRenderer={maybeCreateNewItemRenderer}
createNewItemFromQuery={maybeCreateNewItemFromQuery}
{...commonAddressSelect}
/>
<FMultiSelect
items={[]}
name={'bcc'}
popoverProps={{ minimal: true, fill: true }}
tagInputProps={{
tagProps: { round: true, minimal: true, large: true },
large: true,
}}
fill={true}
createNewItemRenderer={maybeCreateNewItemRenderer}
createNewItemFromQuery={maybeCreateNewItemFromQuery}
{...commonAddressSelect}
/> />
{showCCField && (
<FMultiSelect
items={items}
name={'cc'}
placeholder={''}
popoverProps={{ minimal: true, fill: true }}
tagInputProps={{
tagProps: { round: true, minimal: true, large: true },
large: true,
}}
createNewItemRenderer={createNewItemRenderer}
createNewItemFromQuery={createNewItemFromQuery}
onCreateItemSelect={handleCreateCcItemSelect}
resetOnQuery
resetOnSelect
fill
/>
)}
{showBccField && (
<FMultiSelect
items={items}
name={'bcc'}
placeholder={''}
popoverProps={{ minimal: true, fill: true }}
tagInputProps={{
tagProps: { round: true, minimal: true, large: true },
large: true,
}}
createNewItemRenderer={createNewItemRenderer}
createNewItemFromQuery={createNewItemFromQuery}
onCreateItemSelect={handleCreateBccItemSelect}
resetOnQuery
resetOnSelect
fill
/>
)}
</Stack> </Stack>
</FFormGroup> </FFormGroup>
@@ -107,7 +194,15 @@ export function InvoiceSendMailFields() {
</FFormGroup> </FFormGroup>
<FFormGroup label={'Message'} name={'message'}> <FFormGroup label={'Message'} name={'message'}>
<FInputGroup name={'message'} large /> <FTextArea
name={'message'}
large
fill
className={css`
resize: vertical;
min-height: 200px;
`}
/>
</FFormGroup> </FFormGroup>
</Stack> </Stack>

View File

@@ -3,11 +3,22 @@ import { Form, Formik, FormikHelpers } from 'formik';
import { css } from '@emotion/css'; import { css } from '@emotion/css';
const initialValues = { const initialValues = {
subject: '', subject: 'invoice INV-0002 for AED 0.00',
message: '', message: `Hi Ahmed,
to: [],
Heres invoice INV-0002 for AED 0.00
The amount outstanding of AED $100,00 is due on 2 October 2024
View your bill online From your online you can print a PDF or pay your outstanding bills,
If you have any questions, please let us know,
Thanks,
Mohamed`,
to: ['a.bouhuolia@gmail.com'],
cc: [], cc: [],
bcc: [] bcc: [],
}; };
interface InvoiceSendMailFormValues { interface InvoiceSendMailFormValues {
subject: string; subject: string;

View File

@@ -1,20 +1,17 @@
import { Button, Classes } from "@blueprintjs/core"; import { Button, Classes } from '@blueprintjs/core';
import { Group, Icon } from "@/components"; import { x } from '@xstyled/emotion';
import { useDrawerContext } from "@/components/Drawer/DrawerProvider"; import { Group, Icon } from '@/components';
import { useDrawerActions } from "@/hooks/state"; import { useDrawerContext } from '@/components/Drawer/DrawerProvider';
import { useDrawerActions } from '@/hooks/state';
interface ElementCustomizeHeaderProps { interface ElementCustomizeHeaderProps {
label?: string; label?: string;
children?: React.ReactNode; children?: React.ReactNode;
closeButton?: boolean;
onClose?: () => void;
} }
export function InvoiceSendMailHeader({ export function InvoiceSendMailHeader({
label, label,
closeButton = true, closeButton = true,
onClose,
children,
}: ElementCustomizeHeaderProps) { }: ElementCustomizeHeaderProps) {
const { name } = useDrawerContext(); const { name } = useDrawerContext();
const { closeDrawer } = useDrawerActions(); const { closeDrawer } = useDrawerActions();
@@ -25,7 +22,7 @@ export function InvoiceSendMailHeader({
return ( return (
<Group <Group
p={'10px'} p={'10px'}
pl={'15px'} pl={'30px'}
bg="white" bg="white"
alignItems={'center'} alignItems={'center'}
boxShadow={'0 1px 0 rgba(17, 20, 24, .15)'} boxShadow={'0 1px 0 rgba(17, 20, 24, .15)'}
@@ -33,9 +30,9 @@ export function InvoiceSendMailHeader({
style={{ position: 'relative' }} style={{ position: 'relative' }}
> >
{label && ( {label && (
<h1 style={{ margin: 0, fontSize: 20, fontWeight: 500, color: '#666' }}> <x.h1 margin={0} fontSize={20} fontWeight={500} color={'#666'}>
{label} {label}
</h1> </x.h1>
)} )}
{closeButton && ( {closeButton && (
<Button <Button

View File

@@ -101,7 +101,7 @@ function InvoicesDataTable({
// Handle send mail invoice. // Handle send mail invoice.
const handleSendMailInvoice = ({ id }) => { const handleSendMailInvoice = ({ id }) => {
openDialog(DialogsName.InvoiceMail, { invoiceId: id }); openDrawer(DRAWERS.INVOICE_SEND_MAIL, { invoiceId: id });
}; };
// Handle cell click. // Handle cell click.