選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

page.tsx 6.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. "use client";
  2. import { useCallback, useState } from 'react';
  3. import PageTitle from '@/components/ui/PageTitle';
  4. import { Typography, Button, Input, Form, Select, Switch, Flex, Row, Col } from 'antd';
  5. import Link from 'next/link';
  6. import { BankFilled, WalletFilled } from '@ant-design/icons';
  7. import type { PaymentType } from '@/types/payment';
  8. import { useQuery } from '@tanstack/react-query';
  9. import { getPaymentType } from '@/app/api/general/paymentService';
  10. import AltLayout from '@/components/layout/AltLayout';
  11. import PrimaryButton from '@/components/ui/PrimaryButton';
  12. const { Text, Title } = Typography;
  13. const CreatePersona: React.FC = () => {
  14. // Form states
  15. const [selectedPaymentID, setSelectedPaymentID] = useState<number | undefined>();
  16. const [companyName, setCompanyName] = useState('');
  17. const [cardNumber, setCardNumber] = useState('');
  18. const [cvc, setCvc] = useState('');
  19. // Fetch payment types using React Query
  20. const { data: paymentType, error, isLoading } = useQuery<PaymentType[] | undefined>({
  21. queryKey: ['getPaymentType'],
  22. queryFn: getPaymentType
  23. });
  24. const renderIcon = useCallback((nameId: string) => {
  25. if (nameId === "card") return <WalletFilled className='!text-white !text-xl' />
  26. else if (nameId === "bank") return <BankFilled className='!text-white !text-xl' />
  27. else return <></>
  28. }, [])
  29. if (isLoading) return <p>Loading...</p>;
  30. return (
  31. <AltLayout header={<PageTitle backButton={true}>PAYMENT</PageTitle>}>
  32. <Row className='!bg-white'>
  33. <Col span={24} className='!p-4'>
  34. {/* Section intro */}
  35. <Flex vertical gap={10} className='!mb-8'>
  36. <Text className='!font-semibold !text-xs !text-gray-400'>Enter your payment details <br />
  37. <Text italic className='!text-xs'>
  38. By continuing you agree to our <Link href="/">Terms</Link>
  39. </Text>
  40. </Text>
  41. </Flex>
  42. {/* Payment type selection */}
  43. <Flex wrap gap={8}>
  44. {paymentType?.map(({ id, type, description, icon_id }) => (
  45. <div
  46. key={id}
  47. className={`
  48. ${selectedPaymentID === id ? 'bg-blue-950' : 'bg-gray-400'}
  49. hover:bg-blue-950 transition-all ease-in-out
  50. rounded-lg p-4 max-w-[160px] cursor-pointer
  51. `}
  52. onClick={() => setSelectedPaymentID(id)}
  53. >
  54. {renderIcon(icon_id)}
  55. <Text className='!text-xs !text-white !block'>Transfer via card number</Text>
  56. </div>
  57. ))}
  58. </Flex>
  59. {/* Payment form */}
  60. <Row>
  61. <Col span={24} className="px-2 mt-4">
  62. <Form layout="vertical">
  63. <Row>
  64. <Col span={24}>
  65. {/* Company Name */}
  66. <Form.Item label={<Text className='!italic !text-xs !font-semibold'>Company Name</Text>}>
  67. <Input
  68. placeholder="Company Name"
  69. value={companyName}
  70. onChange={(e) => setCompanyName(e.target.value)}
  71. />
  72. </Form.Item>
  73. {/* Card Number with formatting */}
  74. <Form.Item label={<Text className='!italic !text-xs !font-semibold'>Card Number</Text>}>
  75. <Input
  76. value={cardNumber}
  77. onChange={(e) => {
  78. const rawValue = e.target.value.replace(/\D/g, '').slice(0, 16);
  79. const formattedValue = rawValue.replace(/(.{4})/g, '$1 ').trim();
  80. setCardNumber(formattedValue);
  81. }}
  82. />
  83. </Form.Item>
  84. {/* Expiration Month & Year */}
  85. <Flex gap={10}>
  86. <Form.Item label={<Text className='!italic !text-xs !font-semibold'>Exp Month</Text>} className="w-full">
  87. <Select
  88. allowClear
  89. placeholder="Please select"
  90. options={Array.from({ length: 12 }, (_, i) => {
  91. const month = String(i + 1).padStart(2, '0');
  92. return { label: month, value: month };
  93. })}
  94. />
  95. </Form.Item>
  96. <Form.Item label={<Text className='!italic !text-xs !font-semibold'>Exp Year</Text>} className="w-full">
  97. <Select
  98. allowClear
  99. placeholder="Please select"
  100. options={Array.from({ length: 10 }, (_, i) => {
  101. const year = new Date().getFullYear() + i;
  102. return { label: String(year), value: String(year) };
  103. })}
  104. />
  105. </Form.Item>
  106. </Flex>
  107. {/* CVC with note */}
  108. <Form.Item label={<Text className='!italic !text-xs !font-semibold'>CVC</Text>}>
  109. <Flex gap={12}>
  110. <Input
  111. placeholder="123"
  112. className='!max-w-[50px]'
  113. value={cvc}
  114. onChange={(e) => {
  115. const rawValue = e.target.value.replace(/\D/g, '').slice(0, 4);
  116. setCvc(rawValue);
  117. }}
  118. />
  119. <Text className='!text-gray-300 !max-w-[160px]' style={{ fontSize: 10 }}>
  120. 3 or 4 digits usually found on the signature strip
  121. </Text>
  122. </Flex>
  123. </Form.Item>
  124. {/* Default payment switch */}
  125. <Form.Item>
  126. <Flex gap={4}>
  127. <Switch checked={true} onChange={() => { }} />
  128. <Text className='!text-xs font-semibold'>SET AS DEFAULT</Text>
  129. </Flex>
  130. </Form.Item>
  131. </Col>
  132. </Row>
  133. {/* Submit Button */}
  134. <PrimaryButton customStyle='!w-full !py-3'>
  135. CONFIRM
  136. </PrimaryButton>
  137. </Form>
  138. </Col>
  139. </Row>
  140. </Col>
  141. </Row>
  142. </AltLayout>
  143. );
  144. };
  145. export default CreatePersona;