Browse Source

new layout style for payment

master
azri 1 day ago
parent
commit
c006d37bb8
4 changed files with 268 additions and 100 deletions
  1. 1
    0
      app/user/layout.tsx
  2. 102
    97
      app/user/payment/page.tsx
  3. 162
    0
      app/user/payment/transaction-history/page.tsx
  4. 3
    3
      app/user/wallet/page.tsx

+ 1
- 0
app/user/layout.tsx View File

@@ -16,6 +16,7 @@ const AppLayout = ({ children }: { children: React.ReactNode }) => {
16 16
     '/user/persona/create',
17 17
     '/user/knowledge/create',
18 18
     '/user/settings/profile',
19
+    '/user/payment',
19 20
     '/user/payment'
20 21
   ];
21 22
 

+ 102
- 97
app/user/payment/page.tsx View File

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

+ 162
- 0
app/user/payment/transaction-history/page.tsx View File

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

+ 3
- 3
app/user/wallet/page.tsx View File

@@ -29,16 +29,16 @@ const Wallet: React.FC = () => {
29 29
             {/* Page Header */}
30 30
             <PageTitle>Wallet</PageTitle>
31 31
 
32
-            <Row justify="center" className="mt-6 px-4">
32
+            <Row justify="center" className="mt-6">
33 33
                 <Col xs={24} sm={20} md={16} lg={12}>
34
-                    <Form layout="vertical" className="space-y-6">
34
+                    <Form layout="vertical">
35 35
 
36 36
                         {/* 1. Balance + History Link */}
37 37
                         <Form.Item>
38 38
                             <Space className="w-full justify-between" align="center">
39 39
                                 <Text strong>Current Balance</Text>
40 40
                                 <Link
41
-                                    href="/transaction-history"
41
+                                    href="/user/payment/transaction-history"
42 42
                                     className="text-blue-500 underline text-sm"
43 43
                                 >
44 44
                                     Transaction History

Loading…
Cancel
Save