Skip to content

Commit f948c2d

Browse files
authored
Merge branch 'issue/1206-Customer-API-Intgration' into issue/1206-Customer-API-Intgration-side
2 parents d7927c9 + 01eb8b8 commit f948c2d

File tree

3 files changed

+103
-11
lines changed

3 files changed

+103
-11
lines changed

customer/hooks/useCart.tsx

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export type CartItem = {
1515
}
1616

1717
export type CartContextType = {
18+
isLoading: boolean,
1819
cart: CartItem[],
1920
addItem: (item: CartItem) => void,
2021
updateItem: (item: CartItem) => void,
@@ -23,11 +24,12 @@ export type CartContextType = {
2324
}
2425

2526
const CartContext = createContext<CartContextType>({
27+
isLoading: true,
2628
cart: [],
2729
addItem: noop,
2830
clearCart: noop,
2931
removeItem: noop,
30-
updateItem: noop
32+
updateItem: noop,
3133
})
3234

3335

@@ -71,19 +73,20 @@ export const CartProvider = ({ children }: PropsWithChildren) => {
7173

7274
// Remove item from cart
7375
const removeItem = (id: string) => {
74-
setCart(cart.filter((item) => item.id !== id))
76+
setCart((prevState) => prevState.filter((item) => item.id !== id))
7577
}
7678

7779
// Update
7880
const updateItem = (update: CartItem) => {
79-
setCart(cart.map((item) => (item.id === update.id? update : item)))
81+
setCart(prevState => prevState.map((item) => (item.id === update.id? update : item)))
8082
}
8183

8284
// Clear cart
83-
const clearCart = () => setCart([])
85+
const clearCart = () => setCart(() => [])
8486

8587
return (
86-
<CartContext.Provider value={{ cart, addItem, removeItem, updateItem, clearCart }}>
88+
<CartContext.Provider
89+
value={{ cart, addItem, removeItem, updateItem, clearCart, isLoading }}>
8790
{children}
8891
</CartContext.Provider>
8992
)

customer/pages/products/overview.tsx

Lines changed: 90 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,23 @@ import { withCart } from '@/hocs/withCart'
2222
import { LoadingAndErrorComponent } from '@helpwave/common/components/LoadingAndErrorComponent'
2323
import { useRouter } from 'next/router'
2424
import { Modal } from '@helpwave/common/components/modals/Modal'
25-
import { useState } from 'react'
25+
import { useEffect, useState } from 'react'
2626
import { Input } from '@helpwave/common/components/user-input/Input'
2727
import { VoucherAPI } from '@/api/services/voucher'
2828
import type { Voucher } from '@/api/dataclasses/voucher'
2929
import { Chip } from '@helpwave/common/components/ChipList'
3030
import { useCustomerProductsCalculateQuery } from '@/api/mutations/customer_product_mutations'
3131
import { defaultLocaleFormatters } from '@/utils/locale'
32+
import { ProductAPI } from '@/api/services/product'
33+
import { LoadingAnimation } from '@helpwave/common/components/LoadingAnimation'
34+
35+
type ReferralStatus = 'loading' | 'error'
36+
type ReferralData = {
37+
product: string,
38+
plan: string,
39+
voucher?: string,
40+
}
41+
3242

3343
type CartOverviewTranslation = {
3444
removeFromCart: string,
@@ -45,6 +55,8 @@ type CartOverviewTranslation = {
4555
redeemVoucherFor: (name: string) => string,
4656
code: string,
4757
invalidCode: string,
58+
referral: string,
59+
referralError: string,
4860
} & ProductPlanTranslation
4961

5062
const defaultCartOverviewTranslations: Record<Languages, CartOverviewTranslation> = {
@@ -64,6 +76,8 @@ const defaultCartOverviewTranslations: Record<Languages, CartOverviewTranslation
6476
redeemVoucherFor: (name: string) => `Redeem Voucher for ${name}`,
6577
code: 'Code',
6678
invalidCode: 'The provided Code is not valid (for this product), please try a different one.',
79+
referral: 'Referral',
80+
referralError: 'The Referral could not be processed.',
6781
},
6882
de: {
6983
...defaultProductPlanTranslation.de,
@@ -81,32 +95,104 @@ const defaultCartOverviewTranslations: Record<Languages, CartOverviewTranslation
8195
redeemVoucherFor: (name: string) => `Gutschein einlösen für ${name}`,
8296
code: 'Code',
8397
invalidCode: 'Der eingegebenen Gutschein-Code ist nicht gültig (für dieses Produkt), versuchen Sie einen anderen.',
98+
referral: 'Überweisung', // TODO fix translation
99+
referralError: 'Die Überweisung hat nicht funktioniert.',
84100
}
85101
}
86102

87103

88104
const CartOverview: NextPage = () => {
89105
const translation = useTranslation(defaultCartOverviewTranslations)
90106
const router = useRouter()
107+
const [hasUsedReferral, setHasUsedReferral] = useState<boolean>(false)
91108
const { authHeader } = useAuth()
92-
const { cart, removeItem, updateItem } = useCart()
109+
const [referralStatus, setReferralStatus] = useState<ReferralStatus>()
110+
const { cart, removeItem, updateItem, addItem, isLoading: cartIsLoading } = useCart()
93111
const [productVoucherModalId, setProductVoucherModalId] = useState<string>()
94112
const [voucherCode, setVoucherCode] = useState<string>('')
95113
const [redeemResponse, setRedeemResponse] = useState<string>()
96114
const { data: products, isError: productsError, isLoading: productsLoading } = useProductsAllQuery()
97-
const { data: prices, isError: pricesError, isLoading: pricesLoading } = useCustomerProductsCalculateQuery(cart.map(item => ({
115+
const {
116+
data: prices,
117+
isError: pricesError,
118+
isLoading: pricesLoading,
119+
isRefetching,
120+
refetch,
121+
} = useCustomerProductsCalculateQuery(cart.map(item => ({
98122
productUuid: item.id,
99123
productPlanUuid: item.plan.uuid,
100124
voucherUuid: item.voucher?.uuid
101125
})))
102126

103127
const localeTranslation = useTranslation(defaultLocaleFormatters)
128+
useEffect(() => {
129+
refetch().catch(console.error)
130+
}, [cart])
131+
132+
useEffect(() => {
133+
if (!router.isReady || cartIsLoading || hasUsedReferral) return
134+
135+
const referralParam = router.query['referral']
136+
137+
if (!referralParam || typeof referralParam !== 'string') return
138+
139+
try {
140+
const decoded = atob(referralParam)
141+
const parsed: ReferralData = JSON.parse(decoded)
142+
143+
const check = async () => {
144+
const products = await ProductAPI.getAvailable(authHeader)
145+
const product = products.find(value => value.uuid === parsed.product)
146+
const plan = product?.plan.find(value => value.uuid === parsed.plan)
147+
148+
// TODO try to parse voucher
149+
150+
if (!product || !plan) {
151+
setReferralStatus('error')
152+
setHasUsedReferral(true)
153+
return
154+
}
155+
156+
if (cart.find(value => value.id === product?.uuid)) {
157+
// TODO maybe show an additional dialog here
158+
updateItem({ id: product?.uuid, plan: plan, quantity: 1 })
159+
} else {
160+
addItem({ id: product?.uuid, plan: plan, quantity: 1 })
161+
}
162+
}
163+
164+
check().catch((reason) => {
165+
console.error(reason)
166+
setReferralStatus('error')
167+
setHasUsedReferral(true)
168+
}).then(() => {
169+
setReferralStatus(undefined)
170+
setHasUsedReferral(true)
171+
})
172+
} catch (err) {
173+
console.error(err)
174+
setReferralStatus('error')
175+
setHasUsedReferral(true)
176+
}
177+
}, [router, cart])
104178

105179
const isError = pricesError || productsError
106-
const isLoading = pricesLoading || productsLoading || Object.keys(prices?.products ?? {}).length === 0
180+
const isLoading = pricesLoading || productsLoading || isRefetching
107181

108182
return (
109183
<Page pageTitle={titleWrapper(translation.overview)}>
184+
<Modal
185+
id="referral-modal"
186+
isOpen={!!referralStatus}
187+
onBackgroundClick={referralStatus === 'error' ? () => setReferralStatus(undefined) : undefined}
188+
onCloseClick={referralStatus === 'error' ? () => setReferralStatus(undefined) : undefined}
189+
titleText={translation.referral}
190+
modalClassName={tw('gap-y-2')}
191+
>
192+
{referralStatus === 'error' ? (
193+
<span className={tw('text-hw-negative-500')}>{translation.referralError}</span>
194+
) : (<LoadingAnimation/>)}
195+
</Modal>
110196
<Modal
111197
id="voucher-modal"
112198
isOpen={!!productVoucherModalId}

customer/pages/products/pay.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ type ProductsTranslation = {
4040
bookingFailure: string,
4141
bookingFailureDesc: string,
4242
toInvoices: string,
43+
noTermsAndConditions: string,
4344
} & ProductPlanTypeTranslation
4445

4546
const defaultProductsTranslations: Record<Languages, ProductsTranslation> = {
@@ -59,6 +60,7 @@ const defaultProductsTranslations: Record<Languages, ProductsTranslation> = {
5960
bookingFailure: 'Booking failed',
6061
bookingFailureDesc: 'Try again or contact our support at:',
6162
toInvoices: 'View Invoices',
63+
noTermsAndConditions: 'No Terms and Conditions required.',
6264
},
6365
de: {
6466
...defaultProductPlanTypeTranslation.de,
@@ -76,6 +78,7 @@ const defaultProductsTranslations: Record<Languages, ProductsTranslation> = {
7678
bookingFailure: 'Buchung fehlgeschlagen',
7779
bookingFailureDesc: 'Versuchen sie es erneut oder kontaktieren sie unseren Support unter:',
7880
toInvoices: 'Zu den Rechnungen',
81+
noTermsAndConditions: 'Keine Verträge und Nutzungsbedingungen benötigt',
7982
}
8083
}
8184

@@ -171,7 +174,7 @@ const Payment: NextPage = () => {
171174
<h4 className={tw('font-bold text-xl')}>{translation.termsAndConditions}</h4>
172175
{products && contracts && (
173176
<form className={tw('flex flex-col gap-y-4')}>
174-
{contracts.map((contract) => {
177+
{contracts.length > 0 ? contracts.map((contract) => {
175178
const isAccepted = acceptedContracts[contract.uuid] ?? false
176179
return (
177180
<div key={contract.uuid} className={tw('flex flex-row gap-x-2')}>
@@ -201,7 +204,7 @@ const Payment: NextPage = () => {
201204
</span>
202205
</div>
203206
)
204-
})}
207+
}) : <span className={tw('text-gray-500')}>{translation.noTermsAndConditions}</span>}
205208
<div className={tw('flex flex-row justify-between')}>
206209
<Button
207210
className={tw('flex flex-row items-center gap-x-2 w-[200px]')}

0 commit comments

Comments
 (0)