diff --git a/src/App.tsx b/src/App.tsx index babdcd59..d121ee19 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -27,13 +27,12 @@ const App: React.FC = () => { const dispatch = useAppDispatch(); useEffect(() => { - if (!user) return; - + if (!user) { reloadUserProfile() .then((data) => dispatch(setUser(data))) .catch((err) => console.error(err)); - }, [dispatch]); - + } + }, [user, dispatch]); useEffect(() => { document.addEventListener('click', event => trackClicks(event, user)); diff --git a/src/api/apiToken.api.ts b/src/api/apiToken.api.ts index 6d62a550..074bf9ba 100644 --- a/src/api/apiToken.api.ts +++ b/src/api/apiToken.api.ts @@ -11,6 +11,7 @@ export interface ApiTableRow { key: string; isActive: boolean; createdAt: Date; + updatedAt: Date; } export interface Pagination { @@ -50,18 +51,20 @@ export const toggleApiToken = (id: number): Promise> => export const getApiTableData = (pagination: Pagination): Promise => { return new Promise((res) => { - const noPrevData = { + const noPrevData: ApiTableRow = { id: -1, - key: "No API key found, please click an 'Create' button to generate one.", + key: "No API key found, please click on 'Create' button to generate one.", createdAt: new Date(), isActive: false, + updatedAt: new Date(), }; - const addLastEntry = { + const addFirstEntry: ApiTableRow = { id: -1, - key: "Please click an 'Create' button to generate one.", + key: "Please click on 'Create' button to generate one.", createdAt: new Date(), isActive: false, + updatedAt: new Date(), }; getAllApiToken() @@ -69,7 +72,8 @@ export const getApiTableData = (pagination: Pagination): Promise = if (resp.length === 0) { resp.push(noPrevData); } else { - resp.push(addLastEntry); + /// + resp.unshift(addFirstEntry); } res({ diff --git a/src/components/apiKeys/ApiKeys.tsx b/src/components/apiKeys/ApiKeys.tsx index a0470a7e..77c70bc2 100644 --- a/src/components/apiKeys/ApiKeys.tsx +++ b/src/components/apiKeys/ApiKeys.tsx @@ -1,6 +1,5 @@ import { useAppSelector } from '@app/hooks/reduxHooks'; import { useResponsive } from '@app/hooks/useResponsive'; -import { BaseCard } from '../common/BaseCard/BaseCard'; import { ApiKeyTable } from './apiKeysTable/ApiKeysTable'; import { BaseRow } from '../common/BaseRow/BaseRow'; import { BaseCol } from '../common/BaseCol/BaseCol'; diff --git a/src/components/apiKeys/apiKeysTable/ActionRow.tsx b/src/components/apiKeys/apiKeysTable/ActionRow.tsx index 2444a707..04f2190b 100644 --- a/src/components/apiKeys/apiKeysTable/ActionRow.tsx +++ b/src/components/apiKeys/apiKeysTable/ActionRow.tsx @@ -38,6 +38,7 @@ export const ActionRow: React.FC = ({ ghost onClick={() => handleToggleRow(record.id)} icon={record.isActive ? : } + style={{color: !record.isActive ? '' : '#777777'}} > {record.isActive ? t('apiTable.deactivate') : t('apiTable.activate')} diff --git a/src/components/apiKeys/apiKeysTable/ApiKeysTable.tsx b/src/components/apiKeys/apiKeysTable/ApiKeysTable.tsx index 435ab7d0..a343f522 100644 --- a/src/components/apiKeys/apiKeysTable/ApiKeysTable.tsx +++ b/src/components/apiKeys/apiKeysTable/ApiKeysTable.tsx @@ -1,13 +1,10 @@ import { ApiTableRow, - Pagination, createApiToken, deleteApiToken, getApiTableData, toggleApiToken, } from '@app/api/apiToken.api'; -import { BaseButton } from '@app/components/common/BaseButton/BaseButton'; -import { BaseSpace } from '@app/components/common/BaseSpace/BaseSpace'; import { BaseTable } from '@app/components/common/BaseTable/BaseTable'; import { useFeedback } from '@app/hooks/useFeedback'; import { useMounted } from '@app/hooks/useMounted'; @@ -17,42 +14,29 @@ import { useCallback, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { ActionRow } from './ActionRow'; -const initialPagination: Pagination = { - current: 1, - pageSize: 5, -}; - export const ApiKeyTable: React.FC = () => { - const [tableData, setTableData] = useState<{ data: ApiTableRow[]; pagination: Pagination; loading: boolean }>({ + const [tableData, setTableData] = useState<{ data: ApiTableRow[]; loading: boolean }>({ data: [], - pagination: initialPagination, loading: false, }); const { t } = useTranslation(); const { isMounted } = useMounted(); const { notification } = useFeedback(); - const fetchData = useCallback( - (pagination: Pagination) => { - setTableData((prev) => ({ ...prev, loading: true })); + const fetchData = useCallback(() => { + setTableData((prev) => ({ ...prev, loading: true })); - getApiTableData(pagination).then((res) => { - if (isMounted.current) { - setTableData({ data: res.data, pagination: res.pagination, loading: false }); - } - }); - }, - [isMounted], - ); + getApiTableData().then((res) => { + if (isMounted.current) { + setTableData({ data: res.data, loading: false }); + } + }); + }, [isMounted]); useEffect(() => { - fetchData(initialPagination); + fetchData(); }, [fetch]); - const handleTableChange = (pagination: Pagination) => { - fetchData(pagination); - }; - const handleCreateRow = () => { createApiToken() .then((resp) => { @@ -62,10 +46,6 @@ export const ApiKeyTable: React.FC = () => { return { ...prev, data: updatedData, - pagination: { - ...prev.pagination, - total: prev.pagination.total ? prev.pagination.total + 1 : prev.pagination.total, - }, }; }); @@ -77,6 +57,10 @@ export const ApiKeyTable: React.FC = () => { }; const handleToggleRow = (rowId: number) => { + if (rowId === -1) { + handleCreateRow(); + return; + } toggleApiToken(rowId) .then((resp) => { setTableData((prev) => ({ @@ -97,10 +81,6 @@ export const ApiKeyTable: React.FC = () => { setTableData((prev) => ({ ...prev, data: prev.data.filter((token) => token.id !== rowId), - pagination: { - ...prev.pagination, - total: prev.pagination.total ? prev.pagination.total - 1 : prev.pagination.total, - }, })); notification.success({ message: t('apiTable.deleteMessage', { key: resp.key }) }); @@ -114,10 +94,6 @@ export const ApiKeyTable: React.FC = () => { setTableData((prev) => ({ ...prev, data: prev.data.filter((token) => token.id !== rowId), - pagination: { - ...prev.pagination, - total: prev.pagination.total ? prev.pagination.total - 1 : prev.pagination.total, - }, })); notification.success({ message: t('apiTable.deleteTempMessage') }); @@ -129,14 +105,20 @@ export const ApiKeyTable: React.FC = () => { title: t('apiTable.key'), dataIndex: 'key', render: (text: string, record) => { - return !record.isActive ?{text} : {text}; + return record.isActive ?{text} : {text}; }, }, + { + title: t('apiTable.lastUsed'), + dataIndex: 'updatedAt', + render: (text: string, record: ApiTableRow) => + record.isActive ?(record.id !== -1 ? {dayjs(text).format('DD-MM-YYYY HH:mm')} : ): (record.id !== -1 ? {dayjs(text).format('DD-MM-YYYY HH:mm')} : ), + }, { title: t('apiTable.createdAt'), dataIndex: 'createdAt', render: (text: string, record: ApiTableRow) => - !record.isActive ?(record.id !== -1 ? {dayjs(text).format('DD-MM-YYYY HH:mm:ss')} : ): (record.id !== -1 ? {dayjs(text).format('DD-MM-YYYY HH:mm:ss')} : ), + record.isActive ?(record.id !== -1 ? {dayjs(text).format('DD-MM-YYYY HH:mm')} : ): (record.id !== -1 ? {dayjs(text).format('DD-MM-YYYY HH:mm')} : ), }, { title: t('apiTable.actions'), @@ -156,15 +138,18 @@ export const ApiKeyTable: React.FC = () => { } ]; + if (!tableData.loading && !tableData.data.length) { + return
Please click on 'Create' button to generate one.
; + } + return ( record.id} columns={columns} dataSource={tableData.data} - pagination={tableData.pagination} loading={tableData.loading} - onChange={handleTableChange} + pagination={false} scroll={{ x: 800 }} - bordered style={{ padding: '2rem' }} /> ); diff --git a/src/components/common/BaseTable/BaseTable.styles.ts b/src/components/common/BaseTable/BaseTable.styles.ts index ec766959..5dcf0051 100644 --- a/src/components/common/BaseTable/BaseTable.styles.ts +++ b/src/components/common/BaseTable/BaseTable.styles.ts @@ -11,12 +11,12 @@ export const Table = styled(AntTable)` /* Rounded corners + dark gradient background for a subtle effect */ border-radius: 16px; - background: linear-gradient(145deg, #2a2a2a, #303030); + background: linear-gradient(145deg, #343434, #3a3a3a); /* Soft outward shadows for a lightly raised feel on a dark background */ box-shadow: - 8px 8px 15px rgba(0, 0, 0, 0.5), - -8px -8px 15px rgba(255, 255, 255, 0.03); + 6px 6px 12px rgba(0, 0, 0, 0.3), + -6px -6px 12px rgba(255, 255, 255, 0.05); transition: transform 0.3s ease, box-shadow 0.3s ease; @@ -52,7 +52,7 @@ export const Table = styled(AntTable)` ------------------------------------------------------ */ .ant-table-thead > tr > th { - background-color: #2e2e2e; /* distinct dark shade for header cells */ + background-color: #232322; /* distinct dark shade for header cells */ color: #ccc; /* lighter text color for contrast */ text-transform: uppercase; font-weight: 600; @@ -75,11 +75,7 @@ export const Table = styled(AntTable)` ------------------------------------------------------ */ .ant-table-tbody > tr > td:last-child { - background: linear-gradient(135deg, #7e7e81, #595b5d); - &:hover { - background: unset; - - } + /* Remove special background styling */ } /* diff --git a/src/components/layouts/main/MainLayout/MainLayout.styles.ts b/src/components/layouts/main/MainLayout/MainLayout.styles.ts index 0695b303..f7468ae4 100644 --- a/src/components/layouts/main/MainLayout/MainLayout.styles.ts +++ b/src/components/layouts/main/MainLayout/MainLayout.styles.ts @@ -4,7 +4,6 @@ import { media } from '@app/utils/utils'; import { BaseLayout } from '@app/components/common/BaseLayout/BaseLayout'; export const LayoutMaster = styled(BaseLayout)` - height: 100vh; background: #1a1a1a; position: relative; display: flex; diff --git a/src/components/layouts/main/sider/MainSider/MainSider.styles.ts b/src/components/layouts/main/sider/MainSider/MainSider.styles.ts index 2e552174..4af6f53d 100644 --- a/src/components/layouts/main/sider/MainSider/MainSider.styles.ts +++ b/src/components/layouts/main/sider/MainSider/MainSider.styles.ts @@ -9,8 +9,6 @@ export const Sider = styled(BaseLayout.Sider)` overflow: visible; right: 0; z-index: 5; - min-height: 100vh; - max-height: 100vh; background: #2a2a2a !important; border-right: none !important; box-shadow: 10px 10px 20px #1f1f1f, diff --git a/src/components/profile/profileCard/profileFormNav/ProfileFormNav.tsx b/src/components/profile/profileCard/profileFormNav/ProfileFormNav.tsx index 54419e1c..fc12b4f0 100644 --- a/src/components/profile/profileCard/profileFormNav/ProfileFormNav.tsx +++ b/src/components/profile/profileCard/profileFormNav/ProfileFormNav.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { Payments } from './nav/payments/Payments'; import { PersonalInfo } from './nav/PersonalInfo/PersonalInfo'; import { SecuritySettings } from './nav/SecuritySettings/SecuritySettings'; +import { PaymentHistory } from './nav/payments/paymentHistory/PaymentHistory/PaymentHistory'; interface ProfileFormNavProps { menu: string; @@ -26,6 +27,11 @@ export const ProfileFormNav: React.FC = ({ menu }) => { break; } + case 'paymentsHistory': { + currentMenu = ; + break; + } + default: { currentMenu = null; } diff --git a/src/components/profile/profileCard/profileFormNav/nav/payments/Payments.tsx b/src/components/profile/profileCard/profileFormNav/nav/payments/Payments.tsx index bb855cf2..ecfe08b9 100644 --- a/src/components/profile/profileCard/profileFormNav/nav/payments/Payments.tsx +++ b/src/components/profile/profileCard/profileFormNav/nav/payments/Payments.tsx @@ -15,9 +15,9 @@ export const Payments: React.FC = () => { - + {/* - + */} ), [], diff --git a/src/components/profile/profileCard/profileFormNav/nav/payments/paymentPricing/PaymentPricing.tsx b/src/components/profile/profileCard/profileFormNav/nav/payments/paymentPricing/PaymentPricing.tsx index b759c4f1..482c5339 100644 --- a/src/components/profile/profileCard/profileFormNav/nav/payments/paymentPricing/PaymentPricing.tsx +++ b/src/components/profile/profileCard/profileFormNav/nav/payments/paymentPricing/PaymentPricing.tsx @@ -11,10 +11,13 @@ import { BaseSwitch } from '@app/components/common/BaseSwitch/BaseSwitch'; import { BaseCard } from '@app/components/common/BaseCard/BaseCard'; import { BaseDropdown } from '@app/components/common/BaseDropdown/BaseDropdown'; import { BaseButton } from '@app/components/common/BaseButton/BaseButton'; -import { DownOutlined } from '@ant-design/icons'; +import { DownOutlined, ManOutlined, WomanOutlined } from '@ant-design/icons'; import { Flex } from 'antd'; import pricingData from './payment.json'; import { PaymentPricingContent } from './paymentPricingContent/PaymentPricingContent'; +import { BaseSpace } from '@app/components/common/BaseSpace/BaseSpace'; +import { BaseSelect, Option } from '@app/components/common/selects/BaseSelect/BaseSelect'; + export const PaymentPricing = () => { const location = useLocation(); @@ -77,6 +80,20 @@ export const PaymentPricing = () => { setSwithState(!switchState)} /> {t('time.annually')}(10% discount) + {/* + + + */} import('@app/pages/AuthPages/SecuritySettingsPage/SecuritySettingsPage') ); const PaymentsPage = React.lazy(() => import('@app/pages/DashboardPages/PaymentPage/PaymentsPage')); +const PaymentsHistoryPage = React.lazy(() => import('@app/pages/DashboardPages/PaymentHistoryPage/PaymentsHistoryPage')); const Logout = React.lazy(() => import('./Logout')); export const DASHBOARD_PATH = '/'; @@ -53,6 +54,7 @@ const Error404 = withLoading(Error404Page); const PersonalInfo = withLoading(PersonalInfoPage); const SecuritySettings = withLoading(SecuritySettingsPage); const Payments = withLoading(PaymentsPage); +const PaymentsHistory = withLoading(PaymentsHistoryPage); // Dashboard const DocGenDashboard = withLoading(DocGenDashboardPage); @@ -155,6 +157,7 @@ export const AppRouter: React.FC = () => { } /> } /> } /> + } /> }> diff --git a/src/constants/profileNavData.tsx b/src/constants/profileNavData.tsx index 07dc4456..acd566ba 100644 --- a/src/constants/profileNavData.tsx +++ b/src/constants/profileNavData.tsx @@ -1,4 +1,4 @@ -import { DollarOutlined, SecurityScanOutlined, UserOutlined } from '@ant-design/icons'; +import { DollarOutlined, TransactionOutlined, UserOutlined } from '@ant-design/icons'; import React from 'react'; interface ProfileNavItem { @@ -24,4 +24,11 @@ export const profileNavData: ProfileNavItem[] = [ color: 'warning', href: 'payments', }, + { + id: 3, + name: 'profilePage.nav.paymentsHistory', + icon: , + color: 'warning', + href: 'paymentsHistory', + }, ]; diff --git a/src/locales/de/translation.json b/src/locales/de/translation.json index 810bb1f5..3fac4834 100644 --- a/src/locales/de/translation.json +++ b/src/locales/de/translation.json @@ -4,6 +4,7 @@ "activate": "Activate", "create": "Create", "createdAt": "Created At", + "lastUsed": "Last Used", "createMessage": "{{key}} created successfully", "createMessageErr": "Failed to create API Key. Please try again later!", "deactivate": "Deactivate", @@ -13,7 +14,7 @@ "deleteTempMessage": "The key has been deleted successfully", "key": "Key", "toggleMessage": "{{key}} updated successfully", - "toggleMessageErr": "Failed tp update API Key. Please try again later!" + "toggleMessageErr": "Failed to update API Key. Please try again later!" }, "auth": { "common": { @@ -339,7 +340,7 @@ "gender": "Gender", "lang": "Language", "paymentDate": "Date", - "paymentHistory": "Payment History", + "paymentHistory": "Transactions History", "paymentHistoryErr": "No data available", "paymentStatus": "Status", "personalInfo": "Personal Info", @@ -353,7 +354,8 @@ "nav": { "payment": "Payments", "personalInfo": "Personal Info", - "security": "Security Settings" + "security": "Security Settings", + "paymentsHistory": "Transactions History" }, "success": "Success", "title": "Profile" diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index f93336a8..a3dbd814 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -4,6 +4,7 @@ "activate": "Activate", "create": "Create", "createdAt": "Created At", + "lastUsed": "Last Used", "createMessage": "{{key}} created successfully", "createMessageErr": "Failed to create API Key. Please try again later!", "deactivate": "Deactivate", @@ -13,7 +14,7 @@ "deleteTempMessage": "The key has been deleted successfully", "key": "Key", "toggleMessage": "{{key}} updated successfully", - "toggleMessageErr": "Failed tp update API Key. Please try again later!" + "toggleMessageErr": "Failed to update API Key. Please try again later!" }, "auth": { "common": { @@ -339,7 +340,7 @@ "gender": "Gender", "lang": "Language", "paymentDate": "Date", - "paymentHistory": "Payment History", + "paymentHistory": "Transactions History", "paymentHistoryErr": "No data available", "paymentStatus": "Status", "personalInfo": "Personal Info", @@ -353,7 +354,8 @@ "nav": { "payment": "Payments", "personalInfo": "Personal Info", - "security": "Security Settings" + "security": "Security Settings", + "paymentsHistory": "Transactions History" }, "success": "Success", "title": "Profile" diff --git a/src/pages/DashboardPages/PaymentHistoryPage/PaymentsHistoryPage.tsx b/src/pages/DashboardPages/PaymentHistoryPage/PaymentsHistoryPage.tsx new file mode 100644 index 00000000..c0cf98ce --- /dev/null +++ b/src/pages/DashboardPages/PaymentHistoryPage/PaymentsHistoryPage.tsx @@ -0,0 +1,17 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import { PageTitle } from '@app/components/common/PageTitle/PageTitle'; +import { PaymentHistory } from '@app/components/profile/profileCard/profileFormNav/nav/payments/paymentHistory/PaymentHistory/PaymentHistory'; + +const PaymentsPage: React.FC = () => { + const { t } = useTranslation(); + + return ( + <> + {t('profilePage.nav.paymentHistory')} + + + ); +}; + +export default PaymentsPage;