11import React , { useCallback , useEffect , useMemo } from "react" ;
2- import { Button } from "@litespace/ui/Button" ;
3- import AddCard from "@litespace/assets/AddCard" ;
42import { useFormatMessage } from "@litespace/ui/hooks/intl" ;
53import { useMakeValidators } from "@litespace/ui/hooks/validation" ;
6- import { Typography } from "@litespace/ui/Typography" ;
74import { useForm } from "@litespace/headless/form" ;
85import { validateCvv , validatePhone } from "@litespace/ui/lib/validate" ;
9- import { Select , SelectList } from "@litespace/ui/Select" ;
10- import { PatternInput } from "@litespace/ui/PatternInput" ;
6+ import { SelectList } from "@litespace/ui/Select" ;
117import {
128 useGetAddCardUrl ,
139 useFindCardTokens ,
@@ -16,7 +12,7 @@ import {
1612 useCancelUnpaidOrder ,
1713} from "@litespace/headless/fawry" ;
1814import { IframeDialog } from "@litespace/ui/IframeDilaog" ;
19- import { first , isEmpty } from "lodash" ;
15+ import { first } from "lodash" ;
2016import { useHotkeys } from "react-hotkeys-hook" ;
2117import { env } from "@/lib/env" ;
2218import { useOnError } from "@/hooks/error" ;
@@ -26,12 +22,16 @@ import { IframeMessage } from "@/constants/iframe";
2622import { useLogger } from "@litespace/headless/logger" ;
2723import { ConfirmationDialog } from "@litespace/ui/ConfirmationDialog" ;
2824import RemoveCard from "@litespace/assets/RemoveCard" ;
29- import Lock from "@litespace/assets/Lock" ;
3025import { useBlock } from "@litespace/ui/hooks/common" ;
3126import { useRender } from "@litespace/headless/common" ;
3227import { TxTypeData } from "@/components/Checkout/types" ;
33- import { useCreateLessonWithCard } from "@litespace/headless/lessons" ;
28+ import {
29+ useCreateLessonWithCard ,
30+ useLessonCheckoutUrl ,
31+ } from "@litespace/headless/lessons" ;
3432import { track } from "@/lib/analytics" ;
33+ import { PLAN_PERIOD_LITERAL_TO_PLAN_PERIOD } from "@litespace/utils" ;
34+ import { usePlanCheckoutUrl } from "@litespace/headless/plans" ;
3535
3636type Form = {
3737 card : string ;
@@ -95,6 +95,40 @@ const Payment: React.FC<{
9595 const createLesson = useCreateLessonWithCard ( { onError : onPayError } ) ;
9696
9797 // ==================== form ====================
98+ const planCheckoutQuery = usePlanCheckoutUrl (
99+ txTypeData . type === "paid-plan"
100+ ? {
101+ planId : txTypeData . data . plan ?. id || - 1 ,
102+ period : PLAN_PERIOD_LITERAL_TO_PLAN_PERIOD [ txTypeData . data . period ] ,
103+ returnUrl : document . location . toString ( ) ,
104+ paymentMethod : "CARD" ,
105+ saveCardInfo : true ,
106+ }
107+ : undefined
108+ ) ;
109+
110+ const lessonCheckoutQuery = useLessonCheckoutUrl (
111+ txTypeData . type === "paid-lesson"
112+ ? {
113+ duration : txTypeData . data . duration ,
114+ tutorId : txTypeData . data . tutor ?. id || - 1 ,
115+ slotId : txTypeData . data . slotId ,
116+ start : txTypeData . data . start ,
117+ returnUrl : document . location . toString ( ) ,
118+ paymentMethod : "CARD" ,
119+ saveCardInfo : true ,
120+ }
121+ : undefined
122+ ) ;
123+
124+ const fawryExpressUrl = useMemo (
125+ ( ) =>
126+ txTypeData . type === "paid-plan"
127+ ? planCheckoutQuery ?. data
128+ : lessonCheckoutQuery ?. data ,
129+ [ planCheckoutQuery ?. data , lessonCheckoutQuery ?. data , txTypeData . type ]
130+ ) ;
131+
98132 const validators = useMakeValidators < Form > ( {
99133 phone : {
100134 required : true ,
@@ -281,146 +315,152 @@ const Payment: React.FC<{
281315
282316 return (
283317 < div >
284- < form
285- name = "pay-with-card"
286- onSubmit = { ( e ) => {
287- e . preventDefault ( ) ;
288- form . submit ( ) ;
289- } }
290- className = "flex flex-col gap-6 md:gap-4 lg:gap-6"
291- >
292- < div className = "flex flex-col gap-4" >
293- < Typography tag = "p" className = "text-caption md:text-body font-medium" >
294- { intl ( "checkout.payment.description" ) }
295- </ Typography >
296-
297- < Select
298- id = "card"
299- label = { intl ( "checkout.payment.card.card-number" ) }
300- className = "flex-1"
301- value = { form . state . card }
302- options = { cardOptions }
303- placeholder = { intl ( "checkout.payment.card.card-number-placeholder" ) }
304- valueDir = "ltr"
305- onChange = { ( value ) => {
306- form . set ( "card" , value ) ;
307- track ( "select_card" , "checkout" ) ;
308- } }
309- state = { form . errors . card ? "error" : undefined }
310- helper = { form . errors . card }
311- asButton = { isEmpty ( cardOptions ) }
312- onTriggerClick = { ( ) => {
313- if ( ! isEmpty ( cardOptions ) ) return ;
314- addCardDialog . show ( ) ;
315- track ( "add_card" , "checkout" ) ;
316- } }
317- onOpenChange = { ( open ) => {
318- if ( ! open || ! isEmpty ( cardOptions ) ) return ;
319- addCardDialog . show ( ) ;
320- track ( "add_card" , "checkout" ) ;
321- } }
322- post = {
323- < Button
324- type = "natural"
325- variant = "primary"
326- size = "large"
327- htmlType = "button"
328- startIcon = { < AddCard className = "icon" /> }
329- disabled = { false }
330- loading = { false }
331- onClick = { ( ) => {
332- addCardDialog . show ( ) ;
333- track ( "add_card" , "checkout" ) ;
334- } }
335- className = "ms-2 lg:ms-4 flex-shrink-0"
336- >
337- < Typography
338- tag = "span"
339- className = "text-body font-medium text-natural-700"
340- >
341- { intl ( "checkout.payment.card.add-card" ) }
342- </ Typography >
343- </ Button >
344- }
345- />
346-
347- < div className = "flex flex-col lg:flex-row gap-4" >
348- < PatternInput
349- id = "cvv"
350- size = { 3 }
351- mask = " "
352- format = "###"
353- idleDir = "rtl"
354- inputSize = "large"
355- label = { intl ( "checkout.payment.card.cvv" ) }
356- placeholder = { intl ( "checkout.payment.card.cvv-placeholder" ) }
357- state = { form . errors . cvv ? "error" : undefined }
358- helper = { form . errors . cvv }
359- onValueChange = { ( { value } ) => form . set ( "cvv" , value ) }
360- autoComplete = "off"
361- onBlur = { ( ) => {
362- track ( "enter_cvv" , "checkout" , form . state . cvv ) ;
363- } }
364- />
365- < PatternInput
366- id = "phone"
367- mask = " "
368- idleDir = "ltr"
369- inputSize = "large"
370- name = "phone"
371- format = "### #### ####"
372- label = { intl ( "checkout.payment.card.phone" ) }
373- placeholder = { intl ( "checkout.payment.card.phone-placeholder" ) }
374- state = { form . errors . phone ? "error" : undefined }
375- helper = { form . errors . phone }
376- value = { form . state . phone }
377- autoComplete = "off"
378- disabled = { ! ! phone }
379- onValueChange = { ( { value } ) => form . set ( "phone" , value ) }
380- onBlur = { ( ) => {
381- track ( "enter_phone" , "checkout" , form . state . phone ) ;
382- } }
383- />
384- </ div >
385- </ div >
386-
387- < div >
388- < Typography
389- tag = "p"
390- className = "hidden md:block text-caption text-brand-700 mb-1"
391- >
392- { intl ( "checkout.payment.conditions-acceptance" ) }
393- </ Typography >
394- < Button
395- type = "main"
396- size = "large"
397- htmlType = "submit"
398- className = "w-full"
399- disabled = { pay . isPending || createLesson . isPending }
400- loading = { pay . isPending || createLesson . isPending }
401- >
402- < Typography tag = "span" className = "text text-body font-medium" >
403- { intl ( "checkout.payment.confirm" ) }
404- </ Typography >
405- </ Button >
406- </ div >
407-
408- < Typography tag = "p" className = "text-tiny font-normal text-natural-800" >
409- { intl ( "checkout.payment.card.confirmation-code-note" ) }
410- </ Typography >
411-
412- < div className = "hidden md:flex flex-col gap-2" >
413- < div className = "flex gap-2" >
414- < Lock className = "w-6 h-6" />
415- < Typography tag = "p" className = "text-body font-semibold" >
416- { intl ( "checkout.payment.safe-and-crypted" ) }
417- </ Typography >
418- </ div >
419- < Typography tag = "p" className = "text-caption text-natural-600" >
420- { intl ( "checkout.payment.ensure-your-financial-privacy" ) }
421- </ Typography >
422- </ div >
423- </ form >
318+ { /* <iframe */ }
319+ { /* name="iframe" */ }
320+ { /* className="h-[400px] w-full sm:rounded-md" */ }
321+ { /* src={fawryExpressUrl || ""} */ }
322+ { /* /> */ }
323+ < a href = { fawryExpressUrl || "" } > Fawry Express</ a >
324+ { /* <form */ }
325+ { /* name="pay-with-card" */ }
326+ { /* onSubmit={(e) => { */ }
327+ { /* e.preventDefault(); */ }
328+ { /* form.submit(); */ }
329+ { /* }} */ }
330+ { /* className="flex flex-col gap-6 md:gap-4 lg:gap-6" */ }
331+ { /* > */ }
332+ { /* <div className="flex flex-col gap-4"> */ }
333+ { /* <Typography tag="p" className="text-caption md:text-body font-medium"> */ }
334+ { /* {intl("checkout.payment.description")} */ }
335+ { /* </Typography> */ }
336+ { /**/ }
337+ { /* <Select */ }
338+ { /* id="card" */ }
339+ { /* label={intl("checkout.payment.card.card-number")} */ }
340+ { /* className="flex-1" */ }
341+ { /* value={form.state.card} */ }
342+ { /* options={cardOptions} */ }
343+ { /* placeholder={intl("checkout.payment.card.card-number-placeholder")} */ }
344+ { /* valueDir="ltr" */ }
345+ { /* onChange={(value) => { */ }
346+ { /* form.set("card", value); */ }
347+ { /* track("select_card", "checkout"); */ }
348+ { /* }} */ }
349+ { /* state={form.errors.card ? "error" : undefined} */ }
350+ { /* helper={form.errors.card} */ }
351+ { /* asButton={isEmpty(cardOptions)} */ }
352+ { /* onTriggerClick={() => { */ }
353+ { /* if (!isEmpty(cardOptions)) return; */ }
354+ { /* addCardDialog.show(); */ }
355+ { /* track("add_card", "checkout"); */ }
356+ { /* }} */ }
357+ { /* onOpenChange={(open) => { */ }
358+ { /* if (!open || !isEmpty(cardOptions)) return; */ }
359+ { /* addCardDialog.show(); */ }
360+ { /* track("add_card", "checkout"); */ }
361+ { /* }} */ }
362+ { /* post={ */ }
363+ { /* <Button */ }
364+ { /* type="natural" */ }
365+ { /* variant="primary" */ }
366+ { /* size="large" */ }
367+ { /* htmlType="button" */ }
368+ { /* startIcon={<AddCard className="icon" />} */ }
369+ { /* disabled={false} */ }
370+ { /* loading={false} */ }
371+ { /* onClick={() => { */ }
372+ { /* addCardDialog.show(); */ }
373+ { /* track("add_card", "checkout"); */ }
374+ { /* }} */ }
375+ { /* className="ms-2 lg:ms-4 flex-shrink-0" */ }
376+ { /* > */ }
377+ { /* <Typography */ }
378+ { /* tag="span" */ }
379+ { /* className="text-body font-medium text-natural-700" */ }
380+ { /* > */ }
381+ { /* {intl("checkout.payment.card.add-card")} */ }
382+ { /* </Typography> */ }
383+ { /* </Button> */ }
384+ { /* } */ }
385+ { /* /> */ }
386+ { /**/ }
387+ { /* <div className="flex flex-col lg:flex-row gap-4"> */ }
388+ { /* <PatternInput */ }
389+ { /* id="cvv" */ }
390+ { /* size={3} */ }
391+ { /* mask=" " */ }
392+ { /* format="###" */ }
393+ { /* idleDir="rtl" */ }
394+ { /* inputSize="large" */ }
395+ { /* label={intl("checkout.payment.card.cvv")} */ }
396+ { /* placeholder={intl("checkout.payment.card.cvv-placeholder")} */ }
397+ { /* state={form.errors.cvv ? "error" : undefined} */ }
398+ { /* helper={form.errors.cvv} */ }
399+ { /* onValueChange={({ value }) => form.set("cvv", value)} */ }
400+ { /* autoComplete="off" */ }
401+ { /* onBlur={() => { */ }
402+ { /* track("enter_cvv", "checkout", form.state.cvv); */ }
403+ { /* }} */ }
404+ { /* /> */ }
405+ { /* <PatternInput */ }
406+ { /* id="phone" */ }
407+ { /* mask=" " */ }
408+ { /* idleDir="ltr" */ }
409+ { /* inputSize="large" */ }
410+ { /* name="phone" */ }
411+ { /* format="### #### ####" */ }
412+ { /* label={intl("checkout.payment.card.phone")} */ }
413+ { /* placeholder={intl("checkout.payment.card.phone-placeholder")} */ }
414+ { /* state={form.errors.phone ? "error" : undefined} */ }
415+ { /* helper={form.errors.phone} */ }
416+ { /* value={form.state.phone} */ }
417+ { /* autoComplete="off" */ }
418+ { /* disabled={!!phone} */ }
419+ { /* onValueChange={({ value }) => form.set("phone", value)} */ }
420+ { /* onBlur={() => { */ }
421+ { /* track("enter_phone", "checkout", form.state.phone); */ }
422+ { /* }} */ }
423+ { /* /> */ }
424+ { /* </div> */ }
425+ { /* </div> */ }
426+ { /**/ }
427+ { /* <div> */ }
428+ { /* <Typography */ }
429+ { /* tag="p" */ }
430+ { /* className="hidden md:block text-caption text-brand-700 mb-1" */ }
431+ { /* > */ }
432+ { /* {intl("checkout.payment.conditions-acceptance")} */ }
433+ { /* </Typography> */ }
434+ { /* <Button */ }
435+ { /* type="main" */ }
436+ { /* size="large" */ }
437+ { /* htmlType="submit" */ }
438+ { /* className="w-full" */ }
439+ { /* disabled={pay.isPending || createLesson.isPending} */ }
440+ { /* loading={pay.isPending || createLesson.isPending} */ }
441+ { /* > */ }
442+ { /* <Typography tag="span" className="text text-body font-medium"> */ }
443+ { /* {intl("checkout.payment.confirm")} */ }
444+ { /* </Typography> */ }
445+ { /* </Button> */ }
446+ { /* </div> */ }
447+ { /**/ }
448+ { /* <Typography tag="p" className="text-tiny font-normal text-natural-800"> */ }
449+ { /* {intl("checkout.payment.card.confirmation-code-note")} */ }
450+ { /* </Typography> */ }
451+ { /**/ }
452+ { /* <div className="hidden md:flex flex-col gap-2"> */ }
453+ { /* <div className="flex gap-2"> */ }
454+ { /* <Lock className="w-6 h-6" /> */ }
455+ { /* <Typography tag="p" className="text-body font-semibold"> */ }
456+ { /* {intl("checkout.payment.safe-and-crypted")} */ }
457+ { /* </Typography> */ }
458+ { /* </div> */ }
459+ { /* <Typography tag="p" className="text-caption text-natural-600"> */ }
460+ { /* {intl("checkout.payment.ensure-your-financial-privacy")} */ }
461+ { /* </Typography> */ }
462+ { /* </div> */ }
463+ { /* </form> */ }
424464
425465 < IframeDialog
426466 open = { addCardDialog . open }
0 commit comments