@driveflux/pdf 1.0.15 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__mocks__/quotation.d.ts +1 -1
- package/dist/__mocks__/quotation.d.ts.map +1 -1
- package/dist/__mocks__/quotation.js +1 -2
- package/dist/__mocks__/subscription.d.ts +1 -1
- package/dist/__mocks__/subscription.d.ts.map +1 -1
- package/dist/templates/QuotationPdf/CustomerSubscriptionSection.d.ts.map +1 -1
- package/dist/templates/QuotationPdf/CustomerSubscriptionSection.js +2 -10
- package/dist/templates/QuotationPdf/index.d.ts +1 -1
- package/dist/templates/QuotationPdf/index.d.ts.map +1 -1
- package/dist/templates/QuotationPdf/translations.d.ts +0 -1
- package/dist/templates/QuotationPdf/translations.d.ts.map +1 -1
- package/dist/templates/QuotationPdf/translations.js +44 -47
- package/dist/templates/QuotationPdf/utils.d.ts +3 -4
- package/dist/templates/QuotationPdf/utils.d.ts.map +1 -1
- package/dist/templates/QuotationPdf/utils.js +12 -26
- package/dist/templates/SubscriptionAgreement/index.d.ts +1 -1
- package/dist/templates/SubscriptionAgreement/index.d.ts.map +1 -1
- package/dist/templates/SubscriptionAgreement/translations.d.ts.map +1 -1
- package/dist/templates/SubscriptionAgreement/translations.js +269 -269
- package/dist/templates/SubscriptionAgreement/utils.d.ts +1 -1
- package/dist/templates/SubscriptionAgreement/utils.d.ts.map +1 -1
- package/dist/theme/colors.d.ts.map +1 -1
- package/dist/theme/colors.js +36 -36
- package/dist/utils.d.ts +2 -8
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +7 -31
- package/dist/watch.js +1 -1
- package/package.json +23 -20
- package/.eslintrc +0 -3
- package/.swcrc +0 -14
- package/.turbo/turbo-build.log +0 -6
- package/CHANGELOG.md +0 -186
- package/dist/cjs/__mocks__/quotation.js +0 -70
- package/dist/cjs/__mocks__/subscription.js +0 -1079
- package/dist/cjs/components/FluxLogo.js +0 -76
- package/dist/cjs/components/StandardPage.js +0 -129
- package/dist/cjs/debug.js +0 -234
- package/dist/cjs/index.js +0 -20
- package/dist/cjs/templates/QuotationPdf/CustomerSubscriptionSection.js +0 -155
- package/dist/cjs/templates/QuotationPdf/GFV.js +0 -57
- package/dist/cjs/templates/QuotationPdf/Header.js +0 -86
- package/dist/cjs/templates/QuotationPdf/Pricing.js +0 -430
- package/dist/cjs/templates/QuotationPdf/QuotationPdfPage.js +0 -45
- package/dist/cjs/templates/QuotationPdf/SectionBoxNew.js +0 -43
- package/dist/cjs/templates/QuotationPdf/TermsSection.js +0 -60
- package/dist/cjs/templates/QuotationPdf/index.js +0 -208
- package/dist/cjs/templates/QuotationPdf/translations.js +0 -106
- package/dist/cjs/templates/QuotationPdf/utils.js +0 -108
- package/dist/cjs/templates/SubscriptionAgreement/Confirmation.js +0 -95
- package/dist/cjs/templates/SubscriptionAgreement/CoverPage.js +0 -110
- package/dist/cjs/templates/SubscriptionAgreement/CoverPageSection.js +0 -35
- package/dist/cjs/templates/SubscriptionAgreement/Details.js +0 -161
- package/dist/cjs/templates/SubscriptionAgreement/Footer.js +0 -68
- package/dist/cjs/templates/SubscriptionAgreement/Header.js +0 -61
- package/dist/cjs/templates/SubscriptionAgreement/LabelValue.js +0 -39
- package/dist/cjs/templates/SubscriptionAgreement/SubscriptionAgreementPage.js +0 -48
- package/dist/cjs/templates/SubscriptionAgreement/TermsOfUse.js +0 -72
- package/dist/cjs/templates/SubscriptionAgreement/TermsOfUseSubSection.js +0 -108
- package/dist/cjs/templates/SubscriptionAgreement/index.js +0 -276
- package/dist/cjs/templates/SubscriptionAgreement/translations.js +0 -666
- package/dist/cjs/templates/SubscriptionAgreement/utils.js +0 -14
- package/dist/cjs/templates/index.js +0 -19
- package/dist/cjs/theme/colors.js +0 -147
- package/dist/cjs/theme/index.js +0 -18
- package/dist/cjs/types.js +0 -4
- package/dist/cjs/utils.js +0 -184
- package/dist/cjs/watch.js +0 -20
- package/index.cjs +0 -1
- package/index.d.ts +0 -1
- package/src/__mocks__/quotation.ts +0 -63
- package/src/__mocks__/subscription.ts +0 -803
- package/src/components/FluxLogo.tsx +0 -52
- package/src/components/StandardPage.tsx +0 -32
- package/src/debug.ts +0 -45
- package/src/index.ts +0 -3
- package/src/templates/QuotationPdf/CustomerSubscriptionSection.tsx +0 -73
- package/src/templates/QuotationPdf/GFV.tsx +0 -29
- package/src/templates/QuotationPdf/Header.tsx +0 -31
- package/src/templates/QuotationPdf/Pricing.tsx +0 -214
- package/src/templates/QuotationPdf/QuotationPdfPage.tsx +0 -25
- package/src/templates/QuotationPdf/SectionBoxNew.tsx +0 -20
- package/src/templates/QuotationPdf/TermsSection.tsx +0 -28
- package/src/templates/QuotationPdf/index.tsx +0 -46
- package/src/templates/QuotationPdf/translations.ts +0 -84
- package/src/templates/QuotationPdf/utils.ts +0 -59
- package/src/templates/SubscriptionAgreement/Confirmation.tsx +0 -49
- package/src/templates/SubscriptionAgreement/CoverPage.tsx +0 -52
- package/src/templates/SubscriptionAgreement/CoverPageSection.tsx +0 -25
- package/src/templates/SubscriptionAgreement/Details.tsx +0 -127
- package/src/templates/SubscriptionAgreement/Footer.tsx +0 -37
- package/src/templates/SubscriptionAgreement/Header.tsx +0 -37
- package/src/templates/SubscriptionAgreement/LabelValue.tsx +0 -19
- package/src/templates/SubscriptionAgreement/SubscriptionAgreementPage.tsx +0 -24
- package/src/templates/SubscriptionAgreement/TermsOfUse.tsx +0 -30
- package/src/templates/SubscriptionAgreement/TermsOfUseSubSection.tsx +0 -44
- package/src/templates/SubscriptionAgreement/index.tsx +0 -66
- package/src/templates/SubscriptionAgreement/translations.ts +0 -664
- package/src/templates/SubscriptionAgreement/utils.ts +0 -7
- package/src/templates/index.ts +0 -2
- package/src/theme/colors.ts +0 -180
- package/src/theme/index.ts +0 -1
- package/src/types.ts +0 -1
- package/src/utils.ts +0 -27
- package/src/watch.ts +0 -16
- package/tsconfig.json +0 -13
- package/tsconfig.tsbuildinfo +0 -1
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
const enTranslation = {
|
|
2
|
-
quotation: `Quotation`,
|
|
3
|
-
date: 'Date',
|
|
4
|
-
customer: {
|
|
5
|
-
title: `Customer`,
|
|
6
|
-
name: `Name`,
|
|
7
|
-
email: `Email`,
|
|
8
|
-
address: `Address`
|
|
9
|
-
},
|
|
10
|
-
subscription: {
|
|
11
|
-
title: `Subscription`,
|
|
12
|
-
vehicleName: `Vehicle`,
|
|
13
|
-
plan: `Subscription Plan`,
|
|
14
|
-
mileagePackage: `Mileage Package`
|
|
15
|
-
},
|
|
16
|
-
subIncludes: {
|
|
17
|
-
title: `What's Included:`,
|
|
18
|
-
content: [
|
|
19
|
-
`Comprehensive insurance`,
|
|
20
|
-
`Road tax`,
|
|
21
|
-
`Regularly scheduled maintenance`,
|
|
22
|
-
`Selective wear & tear`,
|
|
23
|
-
`Ability to swap vehicles`,
|
|
24
|
-
`Doorstep delivery`,
|
|
25
|
-
`24/7 nationwide roadside assistance`,
|
|
26
|
-
`Theft recovery services`,
|
|
27
|
-
`Independently rated vehicle inspection`,
|
|
28
|
-
`Option to purchase the vehicle at the end of the subscription`
|
|
29
|
-
],
|
|
30
|
-
excessMileage: (excessMileage: number) =>
|
|
31
|
-
`*If usage exceeds chosen mileage package, excess mileage fees for this vehicle is RM${excessMileage} per KM.`
|
|
32
|
-
},
|
|
33
|
-
pricing: {
|
|
34
|
-
title: `Pricing`,
|
|
35
|
-
fees: {
|
|
36
|
-
upfront: {
|
|
37
|
-
title: `Upfront Fees`,
|
|
38
|
-
content: [
|
|
39
|
-
{ title: `Booking Fee`, key: 'reservationFee' },
|
|
40
|
-
{ title: `Activation Fee`, key: 'startFee' },
|
|
41
|
-
{ title: `Refundable Deposit`, key: 'deposit' }
|
|
42
|
-
]
|
|
43
|
-
},
|
|
44
|
-
monthly: {
|
|
45
|
-
title: `Monthly Fees`,
|
|
46
|
-
content: [{ title: `Subscription Fee`, key: 'subscriptionFee' }]
|
|
47
|
-
},
|
|
48
|
-
dueToday: {
|
|
49
|
-
title: `Due Today`,
|
|
50
|
-
description: `Grand Total is due before your subscription start. Monthly fee is due on the first day of every cycle period.`,
|
|
51
|
-
subTotal: `Upfront Fees + Monthly Fee`,
|
|
52
|
-
serviceFee: `Service Fee (8%)`,
|
|
53
|
-
grandTotal: `Grand Total`
|
|
54
|
-
},
|
|
55
|
-
extraCharge: `Extra Charge`,
|
|
56
|
-
promo: `Promo`,
|
|
57
|
-
gfv: {
|
|
58
|
-
title: `Guaranteed Future Value (GFV)`
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
},
|
|
62
|
-
terms: {
|
|
63
|
-
title: `Terms`,
|
|
64
|
-
content: [
|
|
65
|
-
`This quotation is valid for 7 days.`,
|
|
66
|
-
`All vehicles proposed are unique and are available to our customers on a first come first served basis.`,
|
|
67
|
-
`The Mileage Package is the monthly mileage cap for the vehicle. If you exceed this cap for any given month, excess mileage charges will be incurred.`,
|
|
68
|
-
`Initial payment to be made within 7 days of the invoice date.`,
|
|
69
|
-
`Subscription starts on the date of the car delivery.`,
|
|
70
|
-
`The upfront fees are billed one-time per subscription, before the subscription starts.`,
|
|
71
|
-
`All monthly recurring fees are billed on the same date as the delivery of the vehicle.`,
|
|
72
|
-
`Scheduling delivery will only commence Grand Total is fully paid, 7 - 30+ days after payment and subject to vehicle availability.`
|
|
73
|
-
]
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export type QuotationPdfTranslation = typeof enTranslation
|
|
78
|
-
|
|
79
|
-
export const useTranslations = (locale: 'en'): QuotationPdfTranslation => {
|
|
80
|
-
const translation = {
|
|
81
|
-
en: enTranslation
|
|
82
|
-
}
|
|
83
|
-
return translation[locale]
|
|
84
|
-
}
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import { MileagePackage, PlanType } from '@driveflux/db'
|
|
2
|
-
import { Address } from '@driveflux/db/types'
|
|
3
|
-
|
|
4
|
-
export const formatAddress = (originalAddress: Address) => {
|
|
5
|
-
const { coordinates, metadata, ...address } = originalAddress
|
|
6
|
-
return (Object.keys(address) as Array<keyof typeof address>)
|
|
7
|
-
.filter((key) => key !== 'name' && !!address[key])
|
|
8
|
-
.map((a) => address[a])
|
|
9
|
-
.join(', ')
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export const getSimplifiedPlanTranslation = (plan: PlanType) => {
|
|
13
|
-
switch (plan) {
|
|
14
|
-
case 'plan60':
|
|
15
|
-
return `60 Months`
|
|
16
|
-
case 'plan36':
|
|
17
|
-
return `36 Months`
|
|
18
|
-
case 'plan24':
|
|
19
|
-
return `24 Months`
|
|
20
|
-
case 'plan12':
|
|
21
|
-
return `12 Months`
|
|
22
|
-
case 'plan1':
|
|
23
|
-
return `Monthly`
|
|
24
|
-
case 'planWeekly':
|
|
25
|
-
return `Weekly`
|
|
26
|
-
default:
|
|
27
|
-
return `Unknown Plan`
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export const getMileagePackageTranslation = (mileagePackage?: MileagePackage) => {
|
|
32
|
-
switch (mileagePackage) {
|
|
33
|
-
case 'lite':
|
|
34
|
-
return `Lite`
|
|
35
|
-
case 'standard':
|
|
36
|
-
return `Standard`
|
|
37
|
-
case 'plus':
|
|
38
|
-
return `Plus`
|
|
39
|
-
case 'unlimited':
|
|
40
|
-
return `Unlimited`
|
|
41
|
-
default:
|
|
42
|
-
return `Unknown Mileage Package`
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export const getFullMileagePackageTranslation = (plan?: MileagePackage | null) => {
|
|
47
|
-
switch (plan) {
|
|
48
|
-
case 'lite':
|
|
49
|
-
return `Lite - 1,250KM`
|
|
50
|
-
case 'standard':
|
|
51
|
-
return `Standard - 2,000KM`
|
|
52
|
-
case 'plus':
|
|
53
|
-
return `Plus - 2,750KM`
|
|
54
|
-
case 'unlimited':
|
|
55
|
-
return `Unlimited`
|
|
56
|
-
default:
|
|
57
|
-
return `Unknown Mileage Package`
|
|
58
|
-
}
|
|
59
|
-
}
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import { userName } from '@driveflux/db/models/user'
|
|
2
|
-
import { SubscriptionWithUser, SubscriptionWithVehicle } from '@driveflux/db/types'
|
|
3
|
-
import { Image, Text, View } from '@react-pdf/renderer'
|
|
4
|
-
import { format } from 'date-fns'
|
|
5
|
-
import { FC } from 'react'
|
|
6
|
-
import { colors } from '../../theme/index.js'
|
|
7
|
-
import LabelValue from './LabelValue.js'
|
|
8
|
-
import { SubscriptionAgreementTranslation } from './translations.js'
|
|
9
|
-
|
|
10
|
-
type Props = {
|
|
11
|
-
tConfirmation: SubscriptionAgreementTranslation['confirmation']
|
|
12
|
-
subscription: SubscriptionWithUser & SubscriptionWithVehicle
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const Confirmation: FC<Props> = ({ tConfirmation, subscription }) => {
|
|
16
|
-
if (!subscription.signature?.signature) {
|
|
17
|
-
throw new Error('Missing signature')
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const border = `1px solid ${colors.primary2}`
|
|
21
|
-
|
|
22
|
-
return (
|
|
23
|
-
<View style={{ borderLeft: border, borderRight: border, borderBottom: border }}>
|
|
24
|
-
<View style={{ backgroundColor: colors.primary1, borderTop: border, padding: 8 }}>
|
|
25
|
-
<Text style={{ fontWeight: 'bold', textTransform: 'uppercase', letterSpacing: 1 }}>{tConfirmation.title}</Text>
|
|
26
|
-
</View>
|
|
27
|
-
<View style={{ padding: 8, paddingBottom: 16, rowGap: 8, borderTop: border }}>
|
|
28
|
-
<Text>{tConfirmation.agreementText(subscription.businessId ? 'business' : 'member')}</Text>
|
|
29
|
-
<View style={{ height: 160, alignItems: 'flex-start' }}>
|
|
30
|
-
<Image src={subscription.signature.signature} />
|
|
31
|
-
</View>
|
|
32
|
-
<View style={{ rowGap: 8 }}>
|
|
33
|
-
<LabelValue label={tConfirmation.name} value={userName(subscription.user)} />
|
|
34
|
-
<LabelValue label={tConfirmation.idOrPassportNo} value={subscription.user.identification?.number} />
|
|
35
|
-
<LabelValue
|
|
36
|
-
label={tConfirmation.date}
|
|
37
|
-
value={
|
|
38
|
-
subscription.signature.signedAt
|
|
39
|
-
? format(subscription.signature.signedAt, 'dd-MM-yyyy hh:mm a')
|
|
40
|
-
: undefined
|
|
41
|
-
}
|
|
42
|
-
/>
|
|
43
|
-
</View>
|
|
44
|
-
</View>
|
|
45
|
-
</View>
|
|
46
|
-
)
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export default Confirmation
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import { User } from '@driveflux/db'
|
|
2
|
-
import { userName } from '@driveflux/db/models/user'
|
|
3
|
-
import { Text, View } from '@react-pdf/renderer'
|
|
4
|
-
import { format } from 'date-fns'
|
|
5
|
-
import { FC } from 'react'
|
|
6
|
-
import { colors } from '../../theme/colors.js'
|
|
7
|
-
import CoverPageSection from './CoverPageSection.js'
|
|
8
|
-
import LabelValue from './LabelValue.js'
|
|
9
|
-
import { SubscriptionAgreementTranslation } from './translations.js'
|
|
10
|
-
|
|
11
|
-
type Props = {
|
|
12
|
-
tTitle: SubscriptionAgreementTranslation['title']
|
|
13
|
-
tCover: SubscriptionAgreementTranslation['cover']
|
|
14
|
-
signedAt: Date
|
|
15
|
-
user: User
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const CoverPage: FC<Props> = ({ tTitle, tCover, signedAt, user }) => {
|
|
19
|
-
return (
|
|
20
|
-
<View style={{ flex: 1, paddingVertical: 80, justifyContent: 'space-between' }}>
|
|
21
|
-
<Text style={{ fontWeight: 'bold', fontSize: 40, textTransform: 'uppercase', flex: 1 }}>{tTitle}</Text>
|
|
22
|
-
<View style={{ padding: 16, rowGap: 32, border: `1px solid ${colors.primary2}`, fontSize: 10 }}>
|
|
23
|
-
<CoverPageSection title={tCover.effectiveDate}>
|
|
24
|
-
<View style={{ flexDirection: 'row', columnGap: 4 }}>
|
|
25
|
-
<Text style={{ textDecoration: 'underline' }}>{format(signedAt, 'do')}</Text>
|
|
26
|
-
<Text>{tCover.of}</Text>
|
|
27
|
-
<Text style={{ textDecoration: 'underline' }}>{format(signedAt, 'MMM')}</Text>
|
|
28
|
-
<Text>{format(signedAt, 'yyyy')}</Text>
|
|
29
|
-
</View>
|
|
30
|
-
</CoverPageSection>
|
|
31
|
-
<CoverPageSection title={tCover.agreementBetween}>
|
|
32
|
-
<LabelValue label={tCover.name} value={userName(user)} />
|
|
33
|
-
<LabelValue label={tCover.idOrPassportNo} value={user.identification?.number} />
|
|
34
|
-
</CoverPageSection>
|
|
35
|
-
<Text
|
|
36
|
-
style={{
|
|
37
|
-
fontSize: 8,
|
|
38
|
-
fontWeight: 'bold',
|
|
39
|
-
color: colors.primary3,
|
|
40
|
-
textTransform: 'uppercase',
|
|
41
|
-
letterSpacing: 1
|
|
42
|
-
}}
|
|
43
|
-
>
|
|
44
|
-
{tCover.and}
|
|
45
|
-
</Text>
|
|
46
|
-
<Text>FLUX Sdn Bhd (1264766-H)</Text>
|
|
47
|
-
</View>
|
|
48
|
-
</View>
|
|
49
|
-
)
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export default CoverPage
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { Text, View } from '@react-pdf/renderer'
|
|
2
|
-
import { FC, PropsWithChildren } from 'react'
|
|
3
|
-
import { colors } from '../../theme/colors.js'
|
|
4
|
-
|
|
5
|
-
type Props = {
|
|
6
|
-
title: string
|
|
7
|
-
}
|
|
8
|
-
const CoverPageSection: FC<PropsWithChildren<Props>> = ({ title, children }) => {
|
|
9
|
-
return (
|
|
10
|
-
<View style={{ rowGap: 16 }}>
|
|
11
|
-
<Text
|
|
12
|
-
style={{
|
|
13
|
-
fontSize: 8,
|
|
14
|
-
fontWeight: 'bold',
|
|
15
|
-
color: colors.primary3,
|
|
16
|
-
textTransform: 'uppercase',
|
|
17
|
-
letterSpacing: 1
|
|
18
|
-
}}
|
|
19
|
-
>{`${title}:`}</Text>
|
|
20
|
-
{children}
|
|
21
|
-
</View>
|
|
22
|
-
)
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export default CoverPageSection
|
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
import { MileagePackage, PlanType, VehicleType } from '@driveflux/db'
|
|
2
|
-
import { userName } from '@driveflux/db/models/user'
|
|
3
|
-
import { format } from '@driveflux/time'
|
|
4
|
-
import { Text, View } from '@react-pdf/renderer'
|
|
5
|
-
import { FC, useMemo } from 'react'
|
|
6
|
-
import { colors } from '../../theme/index.js'
|
|
7
|
-
import LabelValue from './LabelValue.js'
|
|
8
|
-
import { SubscriptionForAgreement } from './index.js'
|
|
9
|
-
import { SubscriptionAgreementTranslation } from './translations.js'
|
|
10
|
-
|
|
11
|
-
type Props = {
|
|
12
|
-
tDetails: SubscriptionAgreementTranslation['details']
|
|
13
|
-
subscription: SubscriptionForAgreement
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const Details: FC<Props> = ({ tDetails, subscription }) => {
|
|
17
|
-
const details = useMemo(
|
|
18
|
-
() => ({
|
|
19
|
-
member: [
|
|
20
|
-
{
|
|
21
|
-
key: tDetails.member.name,
|
|
22
|
-
value: userName(subscription.user)
|
|
23
|
-
},
|
|
24
|
-
{
|
|
25
|
-
key: tDetails.member.idOrPassportNo,
|
|
26
|
-
value: subscription.user.identification?.number
|
|
27
|
-
}
|
|
28
|
-
],
|
|
29
|
-
vehicle: [
|
|
30
|
-
{
|
|
31
|
-
key: tDetails.vehicle.make,
|
|
32
|
-
value: subscription.vehicle?.make
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
key: tDetails.vehicle.model,
|
|
36
|
-
value: subscription.vehicle?.vehicleModel
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
key: tDetails.vehicle.year,
|
|
40
|
-
value: subscription.vehicle?.year
|
|
41
|
-
},
|
|
42
|
-
{
|
|
43
|
-
key: tDetails.vehicle.plateNo,
|
|
44
|
-
value: subscription.vehicle?.registrationNumber
|
|
45
|
-
}
|
|
46
|
-
],
|
|
47
|
-
subscription: [
|
|
48
|
-
{
|
|
49
|
-
key: tDetails.subscription.subscriptionPlan,
|
|
50
|
-
value: getPlanTranslation(subscription.plan)
|
|
51
|
-
},
|
|
52
|
-
{
|
|
53
|
-
key: tDetails.subscription.mileagePackage,
|
|
54
|
-
value: getFullMileagePackageTranslation(subscription.mileagePackage)
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
key: tDetails.subscription.startDate,
|
|
58
|
-
value: subscription.startedAt ? format(subscription.startedAt, 'dd-MM-yyyy') : undefined
|
|
59
|
-
},
|
|
60
|
-
{
|
|
61
|
-
key: tDetails.subscription.endDate,
|
|
62
|
-
value: subscription.endedAt
|
|
63
|
-
? format(subscription.endedAt, 'dd-MM-yyyy')
|
|
64
|
-
: subscription.schedule?.endAt
|
|
65
|
-
? format(subscription.schedule?.endAt, 'dd-MM-yyyy')
|
|
66
|
-
: undefined
|
|
67
|
-
}
|
|
68
|
-
]
|
|
69
|
-
}),
|
|
70
|
-
[]
|
|
71
|
-
)
|
|
72
|
-
|
|
73
|
-
const border = `1px solid ${colors.primary2}`
|
|
74
|
-
|
|
75
|
-
return (
|
|
76
|
-
<View style={{ borderLeft: border, borderRight: border }}>
|
|
77
|
-
<View style={{ backgroundColor: colors.primary1, borderTop: border, padding: 8 }}>
|
|
78
|
-
<Text style={{ fontWeight: 'bold', textTransform: 'uppercase', letterSpacing: 1 }}>{tDetails.agreement}</Text>
|
|
79
|
-
</View>
|
|
80
|
-
{Object.keys(details).map((key, i) => (
|
|
81
|
-
<View key={key} style={{ padding: 8, paddingBottom: 16, rowGap: 8, borderTop: border }}>
|
|
82
|
-
<Text style={{ textTransform: 'uppercase', color: colors.primary2, fontSize: 8, letterSpacing: 1 }}>{`${key} details`}</Text>
|
|
83
|
-
{details[key as keyof typeof details].map((line) => (
|
|
84
|
-
<LabelValue label={line.key} value={line.value} key={line.key} />
|
|
85
|
-
))}
|
|
86
|
-
</View>
|
|
87
|
-
))}
|
|
88
|
-
</View>
|
|
89
|
-
)
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
export default Details
|
|
93
|
-
|
|
94
|
-
// TODO: replace this with dump's once the package is ready
|
|
95
|
-
const getPlanTranslation = (plan?: PlanType | null, vehicleType?: VehicleType) => {
|
|
96
|
-
switch (plan) {
|
|
97
|
-
case 'plan60':
|
|
98
|
-
return vehicleType === 'motorcycle' ? '60 Months' : '60 Months (Basic)'
|
|
99
|
-
case 'plan36':
|
|
100
|
-
return '36 Months'
|
|
101
|
-
case 'plan24':
|
|
102
|
-
return '24 Months'
|
|
103
|
-
case 'plan12':
|
|
104
|
-
return '12 Months'
|
|
105
|
-
case 'plan1':
|
|
106
|
-
return 'Monthly (Ultra)'
|
|
107
|
-
case 'planWeekly':
|
|
108
|
-
return 'Weekly'
|
|
109
|
-
default:
|
|
110
|
-
return 'Unknown Plan'
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
const getFullMileagePackageTranslation = (plan?: MileagePackage | null) => {
|
|
115
|
-
switch (plan) {
|
|
116
|
-
case 'lite':
|
|
117
|
-
return 'Lite - 1,250KM'
|
|
118
|
-
case 'standard':
|
|
119
|
-
return 'Standard - 2,000KM'
|
|
120
|
-
case 'plus':
|
|
121
|
-
return 'Plus - 2,750KM'
|
|
122
|
-
case 'unlimited':
|
|
123
|
-
return 'Unlimited'
|
|
124
|
-
default:
|
|
125
|
-
return 'Unknown Mileage Package'
|
|
126
|
-
}
|
|
127
|
-
}
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { Text, View } from '@react-pdf/renderer'
|
|
2
|
-
import { FC } from 'react'
|
|
3
|
-
import { colors } from '../../theme/colors.js'
|
|
4
|
-
import { SubscriptionAgreementTranslation } from './translations.js'
|
|
5
|
-
|
|
6
|
-
type Props = {
|
|
7
|
-
tComputerGenerated: SubscriptionAgreementTranslation['computerGenerated']
|
|
8
|
-
tPage: SubscriptionAgreementTranslation['page']
|
|
9
|
-
tOf: SubscriptionAgreementTranslation['of']
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
const Footer: FC<Props> = ({ tComputerGenerated, tPage, tOf }) => {
|
|
13
|
-
return (
|
|
14
|
-
<View
|
|
15
|
-
fixed
|
|
16
|
-
style={{
|
|
17
|
-
justifyContent: 'space-between',
|
|
18
|
-
flexDirection: 'row',
|
|
19
|
-
paddingTop: 16,
|
|
20
|
-
marginTop: 16,
|
|
21
|
-
borderTop: `1px solid ${colors.links}`,
|
|
22
|
-
color: colors.primary2,
|
|
23
|
-
fontSize: 8,
|
|
24
|
-
}}
|
|
25
|
-
>
|
|
26
|
-
<Text>{tComputerGenerated}</Text>
|
|
27
|
-
<View style={{ flexDirection: 'row', columnGap: 4 }}>
|
|
28
|
-
<Text>{tPage}</Text>
|
|
29
|
-
<Text style={{ fontWeight: 'bold', color: colors.black }} render={({ pageNumber }) => `${pageNumber}`} />
|
|
30
|
-
<Text>{tOf}</Text>
|
|
31
|
-
<Text style={{ fontWeight: 'bold', color: colors.black }} render={({ totalPages }) => `${totalPages}`} />
|
|
32
|
-
</View>
|
|
33
|
-
</View>
|
|
34
|
-
)
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export default Footer
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { Link, Text, View } from '@react-pdf/renderer'
|
|
2
|
-
import { FC } from 'react'
|
|
3
|
-
import FluxLogo from '../../components/FluxLogo.js'
|
|
4
|
-
import { colors } from '../../theme/colors.js'
|
|
5
|
-
import { SubscriptionAgreementTranslation } from './translations.js'
|
|
6
|
-
|
|
7
|
-
type Props = {
|
|
8
|
-
tTitle: SubscriptionAgreementTranslation['title']
|
|
9
|
-
userId: string
|
|
10
|
-
subscriptionId: string
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
const Header: FC<Props> = ({ tTitle, userId, subscriptionId }) => {
|
|
14
|
-
return (
|
|
15
|
-
<View
|
|
16
|
-
fixed
|
|
17
|
-
style={{
|
|
18
|
-
flexDirection: 'row',
|
|
19
|
-
justifyContent: 'space-between',
|
|
20
|
-
alignItems: 'center',
|
|
21
|
-
paddingBottom: 40,
|
|
22
|
-
// to not get pushed by the content
|
|
23
|
-
minHeight: 69
|
|
24
|
-
}}
|
|
25
|
-
>
|
|
26
|
-
<Link src="https://driveflux.com">
|
|
27
|
-
<FluxLogo />
|
|
28
|
-
</Link>
|
|
29
|
-
<View style={{ rowGap: 4, fontSize: 8, alignItems: 'flex-end' }}>
|
|
30
|
-
<Text style={{ fontWeight: 'bold' }}>{tTitle}</Text>
|
|
31
|
-
<Text style={{ color: colors.primary2 }}>{`FLUX-${userId}-${subscriptionId}`}</Text>
|
|
32
|
-
</View>
|
|
33
|
-
</View>
|
|
34
|
-
)
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export default Header
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { Text, View } from '@react-pdf/renderer'
|
|
2
|
-
import { FC } from 'react'
|
|
3
|
-
|
|
4
|
-
type Props = {
|
|
5
|
-
label: string
|
|
6
|
-
value?: string | number | boolean | null
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
const LabelValue: FC<Props> = ({ label, value }) => {
|
|
10
|
-
return (
|
|
11
|
-
<View style={{ flexDirection: 'row' }}>
|
|
12
|
-
<Text style={{ minWidth: 120, textTransform: 'capitalize' }}>{label}</Text>
|
|
13
|
-
<Text>{`:`}</Text>
|
|
14
|
-
<Text style={{ paddingLeft: 16 }}>{!!value ? `${value}` : '-'}</Text>
|
|
15
|
-
</View>
|
|
16
|
-
)
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export default LabelValue
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { View } from '@react-pdf/renderer'
|
|
2
|
-
import { FC, PropsWithChildren } from 'react'
|
|
3
|
-
import StandardPage from '../../components/StandardPage.js'
|
|
4
|
-
import Footer from './Footer.js'
|
|
5
|
-
import Header from './Header.js'
|
|
6
|
-
import { SubscriptionAgreementTranslation } from './translations.js'
|
|
7
|
-
|
|
8
|
-
type Props = {
|
|
9
|
-
t: SubscriptionAgreementTranslation
|
|
10
|
-
userId: string
|
|
11
|
-
subscriptionId: string
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const SubscriptionAgreementPage: FC<PropsWithChildren<Props>> = ({ t, userId, subscriptionId, children }) => {
|
|
15
|
-
return (
|
|
16
|
-
<StandardPage style={{ padding: 40, fontSize: 10 }}>
|
|
17
|
-
<Header tTitle={t.title} userId={userId} subscriptionId={subscriptionId} />
|
|
18
|
-
<View style={{ flex: 1 }}>{children}</View>
|
|
19
|
-
<Footer tComputerGenerated={t.computerGenerated} tPage={t.page} tOf={t.of} />
|
|
20
|
-
</StandardPage>
|
|
21
|
-
)
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export default SubscriptionAgreementPage
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { Text, View } from '@react-pdf/renderer'
|
|
2
|
-
import { FC } from 'react'
|
|
3
|
-
import TermsOfUseSubSection from './TermsOfUseSubSection.js'
|
|
4
|
-
import { SubscriptionAgreementTranslation } from './translations.js'
|
|
5
|
-
|
|
6
|
-
type Props = {
|
|
7
|
-
t: SubscriptionAgreementTranslation['termsOfUse']
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
const TermsOfUse: FC<Props> = ({ t }) => {
|
|
11
|
-
return (
|
|
12
|
-
<View style={{ rowGap: 16 }}>
|
|
13
|
-
<Text style={{ fontWeight: 'bold', textTransform: 'uppercase' }}>{t.title}</Text>
|
|
14
|
-
<View style={{ rowGap: 16, lineHeight: 1.4 }}>
|
|
15
|
-
{t.content.map((section, i) => (
|
|
16
|
-
<View key={section.title} style={{ rowGap: 16 }}>
|
|
17
|
-
<Text style={{ fontWeight: 'bold' }}>{`${i + 1} ${section.title}`}</Text>
|
|
18
|
-
<View style={{ rowGap: 16, paddingLeft: 16 }}>
|
|
19
|
-
{section.subSections.map((subSection, j) => (
|
|
20
|
-
<TermsOfUseSubSection indices={[i + 1, j + 1]} subSection={subSection} key={`${i + 1}.${j}`} />
|
|
21
|
-
))}
|
|
22
|
-
</View>
|
|
23
|
-
</View>
|
|
24
|
-
))}
|
|
25
|
-
</View>
|
|
26
|
-
</View>
|
|
27
|
-
)
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export default TermsOfUse
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { Text, View } from '@react-pdf/renderer'
|
|
2
|
-
import type { FC } from 'react'
|
|
3
|
-
import { useMemo } from 'react'
|
|
4
|
-
|
|
5
|
-
type TermsOfUseSubSectionType =
|
|
6
|
-
| string
|
|
7
|
-
| {
|
|
8
|
-
paragraph: string
|
|
9
|
-
subSections: TermsOfUseSubSectionType[]
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
type Props = {
|
|
13
|
-
indices: number[]
|
|
14
|
-
subSection: TermsOfUseSubSectionType
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const TermsOfUseSubSection: FC<Props> = ({ indices, subSection }) => {
|
|
18
|
-
const { indicesString, paddingLeft } = useMemo(
|
|
19
|
-
() => ({
|
|
20
|
-
indicesString: indices.reduce((prev, curr, i) => `${prev}${curr}${i === indices.length - 1 ? '' : '.'}`, ''),
|
|
21
|
-
paddingLeft: Math.max(indices.length - 2, 0) * 16
|
|
22
|
-
}),
|
|
23
|
-
[]
|
|
24
|
-
)
|
|
25
|
-
|
|
26
|
-
return typeof subSection === 'string' ? (
|
|
27
|
-
<View style={{ flexDirection: 'row', paddingLeft, columnGap: 16 }} key={indicesString}>
|
|
28
|
-
<Text style={{ fontWeight: 'bold' }}>{indicesString}</Text>
|
|
29
|
-
<Text style={{ textAlign: 'justify', flex: 1 }}>{subSection}</Text>
|
|
30
|
-
</View>
|
|
31
|
-
) : 'subSections' in subSection ? (
|
|
32
|
-
<View key={indicesString} style={{ gap: 16 }}>
|
|
33
|
-
<View style={{ flexDirection: 'row', paddingLeft, columnGap: 16 }} key={indicesString}>
|
|
34
|
-
<Text style={{ fontWeight: 'bold' }}>{indicesString}</Text>
|
|
35
|
-
<Text style={{ textAlign: 'justify', flex: 1 }}>{subSection.paragraph}</Text>
|
|
36
|
-
</View>
|
|
37
|
-
{subSection.subSections.map((subSection, i) => (
|
|
38
|
-
<TermsOfUseSubSection key={`${indices}.${i}`} indices={[...indices, i + 1]} subSection={subSection} />
|
|
39
|
-
))}
|
|
40
|
-
</View>
|
|
41
|
-
) : null
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export default TermsOfUseSubSection
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import { SignatureDetails, Subscription, SubscriptionSchedule, User, Vehicle } from '@driveflux/db'
|
|
2
|
-
import { Document } from '@react-pdf/renderer'
|
|
3
|
-
import { FC, useMemo } from 'react'
|
|
4
|
-
import { PdfTemplatesSupportedLocales } from '../../types.js'
|
|
5
|
-
import { toPdf } from '../../utils.js'
|
|
6
|
-
import Confirmation from './Confirmation.js'
|
|
7
|
-
import CoverPage from './CoverPage.js'
|
|
8
|
-
import Details from './Details.js'
|
|
9
|
-
import SubscriptionAgreementPage from './SubscriptionAgreementPage.js'
|
|
10
|
-
import TermsOfUse from './TermsOfUse.js'
|
|
11
|
-
import { useTranslations } from './translations.js'
|
|
12
|
-
|
|
13
|
-
export type SubscriptionForAgreement = Subscription & {
|
|
14
|
-
user: User
|
|
15
|
-
vehicle: Vehicle
|
|
16
|
-
schedule?: SubscriptionSchedule | null
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
type Options = {
|
|
20
|
-
locale?: PdfTemplatesSupportedLocales
|
|
21
|
-
subscription: SubscriptionForAgreement
|
|
22
|
-
}
|
|
23
|
-
export const generateSubscriptionAgreementPdf = async ({ locale = 'en', subscription }: Options, fileName?: string) => {
|
|
24
|
-
if (!subscription.signature) {
|
|
25
|
-
throw new Error('Signature not found')
|
|
26
|
-
}
|
|
27
|
-
return await toPdf(
|
|
28
|
-
<SubscriptionAgreement locale={locale} signature={subscription.signature} subscription={subscription} />,
|
|
29
|
-
fileName
|
|
30
|
-
)
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
type Props = {
|
|
34
|
-
locale?: PdfTemplatesSupportedLocales
|
|
35
|
-
signature: SignatureDetails
|
|
36
|
-
subscription: SubscriptionForAgreement
|
|
37
|
-
}
|
|
38
|
-
const SubscriptionAgreement: FC<Props> = ({ locale = 'en', signature, subscription }) => {
|
|
39
|
-
if (!signature.signedAt) {
|
|
40
|
-
throw new Error(`Missing 'signature.signedAt'`)
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const pageProps = useMemo(() => {
|
|
44
|
-
return {
|
|
45
|
-
t: useTranslations(locale),
|
|
46
|
-
userId: subscription.userId,
|
|
47
|
-
subscriptionId: subscription.id
|
|
48
|
-
}
|
|
49
|
-
}, [])
|
|
50
|
-
const { t } = pageProps
|
|
51
|
-
|
|
52
|
-
return (
|
|
53
|
-
<Document title={t.title} language={locale}>
|
|
54
|
-
<SubscriptionAgreementPage {...pageProps}>
|
|
55
|
-
<CoverPage tTitle={t.title} tCover={t.cover} signedAt={signature.signedAt} user={subscription.user} />
|
|
56
|
-
</SubscriptionAgreementPage>
|
|
57
|
-
<SubscriptionAgreementPage {...pageProps}>
|
|
58
|
-
<TermsOfUse t={t.termsOfUse} />
|
|
59
|
-
</SubscriptionAgreementPage>
|
|
60
|
-
<SubscriptionAgreementPage {...pageProps}>
|
|
61
|
-
<Details tDetails={t.details} subscription={subscription} />
|
|
62
|
-
<Confirmation tConfirmation={t.confirmation} subscription={subscription} />
|
|
63
|
-
</SubscriptionAgreementPage>
|
|
64
|
-
</Document>
|
|
65
|
-
)
|
|
66
|
-
}
|