diff --git a/jam-ui/cypress/e2e/account/edit_profile-page.cy.js b/jam-ui/cypress/e2e/account/edit-profile-page.cy.js similarity index 100% rename from jam-ui/cypress/e2e/account/edit_profile-page.cy.js rename to jam-ui/cypress/e2e/account/edit-profile-page.cy.js diff --git a/jam-ui/cypress/e2e/account/payments-page.cy.js b/jam-ui/cypress/e2e/account/payments-page.cy.js new file mode 100644 index 000000000..876a84e86 --- /dev/null +++ b/jam-ui/cypress/e2e/account/payments-page.cy.js @@ -0,0 +1,46 @@ +import makeFakeUser from '../../factories/user'; + +describe('Payments Page', () => { + beforeEach(() => { + // Place any setup code here, such as logging in or navigating to the payments page + const currentUser = makeFakeUser(); + cy.stubAuthenticate({ ...currentUser }); + cy.intercept('GET', /\S+\/invoice_history\?limit=10&cursor=0/, { fixture: 'payments_page1' }).as('getPayments1'); + cy.intercept('GET', /\S+\/invoice_history\?limit=10&cursor=10/, { fixture: 'payments_page2' }).as('getPayments2'); + cy.intercept('GET', /\S+\/invoice_history\?limit=10&cursor=20/, { fixture: 'payments_page3' }).as('getPayments3'); + }) + + it('should list user\'s payments', () => { + // Call the external API that fetches the payment records + cy.visit('/account/payments') + cy.wait('@getPayments1') + cy.get('[data-testid="paymentsListTable"]').should('be.visible').find('tbody tr').first().within(() => { + cy.contains('05/01/2022') + }) + + }) + + it.only('paginate through the payments', () => { + // Call the external API that fetches the payment records + cy.visit('/account/payments') + cy.wait('@getPayments1') + cy.get('[data-testid="paymentsListTable"]').should('be.visible').find('tbody tr').first().within(() => { + cy.contains('12/01/2022') + }) + + // Click the next page button + cy.get('[data-testid="nextPageButton"]').click() + cy.wait('@getPayments2') + cy.get('[data-testid="paymentsListTable"]').should('be.visible').find('tbody tr').first().within(() => { + cy.contains('02/01/2022') + }) + + // Click the next page button + cy.get('[data-testid="nextPageButton"]').click() + cy.wait('@getPayments3') + cy.get('[data-testid="paymentsListTable"]').should('be.visible').find('tbody tr').first().within(() => { + cy.contains('03/01/2021') + }) + }); + +}) \ No newline at end of file diff --git a/jam-ui/cypress/fixtures/payments_page1.json b/jam-ui/cypress/fixtures/payments_page1.json new file mode 100644 index 000000000..184b01854 --- /dev/null +++ b/jam-ui/cypress/fixtures/payments_page1.json @@ -0,0 +1,74 @@ +{ + "entries": [ + { + "id": "a1b2c3d4-e5f6-g7h8-i9j0-k1l2m3n4o5p6", + "created_at": "2022-12-01T00:00:00Z", + "total_in_cents": 999, + "description": "JamKazam Gold Plan - Monthly", + "status": "paid" + }, + { + "id": "q7r8s9t0-u1v2-w3x4-y5z6-a7b8c9d0e1f2", + "created_at": "2022-11-01T00:00:00Z", + "total_in_cents": 999, + "description": "JamKazam Gold Plan - Monthly", + "status": "paid" + }, + { + "id": "g3h4i5j6-k7l8-m9n0-o1p2-q3r4s5t6u7v8w9x0", + "created_at": "2022-10-01T00:00:00Z", + "total_in_cents": 999, + "description": "JamKazam Gold Plan - Monthly", + "status": "paid" + }, + { + "id": "y1z2a3b4-c5d6-e7f8-g9h0-i1j2k3l4m5n6o7", + "created_at": "2022-09-01T00:00:00Z", + "total_in_cents": 999, + "description": "JamKazam Gold Plan - Monthly", + "status": "paid" + }, + { + "id": "p8q9r0s1-t2u3-v4w5-x6y7-z8a9b0c1d2e3f4", + "created_at": "2022-08-01T00:00:00Z", + "total_in_cents": 999, + "description": "JamKazam Gold Plan - Monthly", + "status": "paid" + }, + { + "id": "g5h6i7j8-k9l0-m1n2-o3p4-q5r6s7t8u9v0w1x2", + "created_at": "2022-07-01T00:00:00Z", + "total_in_cents": 999, + "description": "JamKazam Gold Plan - Monthly", + "status": "paid" + }, + { + "id": "y3z4a5b6-c7d8-e9f0-g1h2-i3j4k5l6m7n8o9", + "created_at": "2022-06-01T00:00:00Z", + "total_in_cents": 999, + "description": "JamKazam Gold Plan - Monthly", + "status": "paid" + }, + { + "id": "p2q3r4s5-t6u7-v8w9-x0y1-z2a3b4c5d6e7f8", + "created_at": "2022-05-01T00:00:00Z", + "total_in_cents": 999, + "description": "JamKazam Gold Plan - Monthly", + "status": "paid" + }, + { + "id": "g9h0i1j2-k3l4-m5n6-o7p8-q9r0s1t2u3v4w5x6", + "created_at": "2022-04-01T00:00:00Z", + "total_in_cents": 999, + "description": "JamKazam Gold Plan - Monthly", + "status": "paid" + }, + { + "id": "y7z8a9b0-c1d2-e3f4-g5h6-i7j8k9l0m1n2o3p4", + "created_at": "2022-03-01T00:00:00Z", + "total_in_cents": 999, + "description": "JamKazam Gold Plan - Monthly", + "status": "paid" + } + ] +} \ No newline at end of file diff --git a/jam-ui/cypress/fixtures/payments_page2.json b/jam-ui/cypress/fixtures/payments_page2.json new file mode 100644 index 000000000..e811ea886 --- /dev/null +++ b/jam-ui/cypress/fixtures/payments_page2.json @@ -0,0 +1,82 @@ +{ + "entries": [ + { + "id": "a1b2c3d4-e5f6-g7h8-i9j0-k1l2m3n4o5p6", + "created_at": "2022-02-01T00:00:00Z", + "total_in_cents": 999, + "description": "JamKazam Gold Plan - Monthly", + "status": "paid" + }, + { + "id": "q7r8s9t0-u1v2-w3x4-y5z6-a7b8c9d0e1f2", + "created_at": "2022-01-01T00:00:00Z", + "total_in_cents": 999, + "description": "JamKazam Gold Plan - Monthly", + "status": "paid" + }, + { + "id": "g3h4i5j6-k7l8-m9n0-o1p2-q3r4s5t6u7v8w9x0", + "created_at": "2021-12-01T00:00:00Z", + "total_in_cents": 999, + "description": "JamKazam Gold Plan - Monthly", + "status": "paid" + }, + { + "id": "y1z2a3b4-c5d6-e7f8-g9h0-i1j2k3l4m5n6o7", + "created_at": "2021-11-01T00:00:00Z", + "total_in_cents": 999, + "description": "JamKazam Gold Plan - Monthly", + "status": "paid" + }, + { + "id": "p8q9r0s1-t2u3-v4w5-x6y7-z8a9b0c1d2e3f4", + "created_at": "2021-10-01T00:00:00Z", + "total_in_cents": 999, + "description": "JamKazam Gold Plan - Monthly", + "status": "paid" + }, + { + "id": "g5h6i7j8-k9l0-m1n2-o3p4-q5r6s7t8u9v0w1x2", + "created_at": "2021-09-01T00:00:00Z", + "total_in_cents": 999, + "description": "JamKazam Gold Plan - Monthly", + "status": "paid" + }, + { + "id": "y3z4a5b6-c7d8-e9f0-g1h2-i3j4k5l6m7n8o9", + "created_at": "2021-08-01T00:00:00Z", + "total_in_cents": 999, + "description": "JamKazam Gold Plan - Monthly", + "status": "paid" + }, + { + "id": "p2q3r4s5-t6u7-v8w9-x0y1-z2a3b4c5d6e7f8", + "created_at": "2021-07-01T00:00:00Z", + "total_in_cents": 999, + "description": "JamKazam Gold Plan - Monthly", + "status": "paid" + }, + { + "id": "g9h0i1j2-k3l4-m5n6-o7p8-q9r0s1t2u3v4w5x6", + "created_at": "2021-06-01T00:00:00Z", + "total_in_cents": 999, + "description": "JamKazam Gold Plan - Monthly", + "status": "paid" + }, + { + "id": "y7z8a9b0-c1d2-e3f4-g5h6-i7j8k9l0m1n2o3p4", + "created_at": "2021-05-01T00:00:00Z", + "total_in_cents": 999, + "description": "JamKazam Gold Plan - Monthly", + "status": "paid" + }, + { + "id": "a1b2c3d4-e5f6-g7h8-i9j0-k1l2m3n4o5p6", + "created_at": "2021-04-01T00:00:00Z", + "total_in_cents": 999, + "description": "JamKazam Gold Plan - Monthly", + "status": "paid" + } + + ] +} \ No newline at end of file diff --git a/jam-ui/cypress/fixtures/payments_page3.json b/jam-ui/cypress/fixtures/payments_page3.json new file mode 100644 index 000000000..e3b84648f --- /dev/null +++ b/jam-ui/cypress/fixtures/payments_page3.json @@ -0,0 +1,19 @@ +{ + "entries": [ + { + "id": "a1b2c3d4-e5f6-g7h8-i9j0-k1l2m3n4o5p6", + "created_at": "2021-03-01T00:00:00Z", + "total_in_cents": 999, + "description": "JamKazam Gold Plan - Monthly", + "status": "paid" + }, + { + "id": "q7r8s9t0-u1v2-w3x4-y5z6-a7b8c9d0e1f2", + "created_at": "2021-02-01T00:00:00Z", + "total_in_cents": 999, + "description": "JamKazam Gold Plan - Monthly", + "status": "paid" + } + + ] +} \ No newline at end of file diff --git a/jam-ui/src/components/dashboard/JKDashboardMain.js b/jam-ui/src/components/dashboard/JKDashboardMain.js index 796fad886..1540f5535 100644 --- a/jam-ui/src/components/dashboard/JKDashboardMain.js +++ b/jam-ui/src/components/dashboard/JKDashboardMain.js @@ -38,6 +38,7 @@ import JKMusicSessionsLobby from '../page/JKMusicSessionsLobby'; import JKEditProfile from '../page/JKEditProfile'; import JKEditAccount from '../page/JKEditAccount'; import JKAccountSubscription from '../page/JKAccountSubscription'; +import JKPaymentHistory from '../page/JKPaymentHistory'; //import loadable from '@loadable/component'; @@ -265,6 +266,7 @@ function JKDashboardMain() { + {/*Redirect*/} diff --git a/jam-ui/src/components/page/JKPaymentHistory.js b/jam-ui/src/components/page/JKPaymentHistory.js new file mode 100644 index 000000000..8e21ff91b --- /dev/null +++ b/jam-ui/src/components/page/JKPaymentHistory.js @@ -0,0 +1,92 @@ +import React, { useEffect, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { Card, CardBody, Row, Col, Table, Alert, Button } from 'reactstrap'; +import FalconCardHeader from '../common/FalconCardHeader'; +import { getInvoiceHistory } from '../../helpers/rest'; +import { isIterableArray } from '../../helpers/utils'; + +const JKPaymentHistory = () => { + const { t } = useTranslation('account'); + const [payments, setPayments] = useState([]); + const LIMIT = 10; + const [offset, setOffset] = useState(0); + const [loading, setLoading] = useState(false); + + const fetchPayments = async () => { + const options = { limit: LIMIT, cursor: offset }; + try { + setLoading(true); + const response = await getInvoiceHistory(options); + const data = await response.json(); + setPayments(data.entries); + setOffset(prev => prev + LIMIT); + }catch(error) { + console.error(error); + }finally { + setLoading(false); + } + }; + + useEffect(async () => { + // fetch payments + const options = { cursor: offset, limit: LIMIT }; + await fetchPayments(options); + }, []); + + const goToNextPage = async () => { + const options = { cursor: offset, limit: LIMIT }; + await fetchPayments(options); + } + + return ( + + + + + + {isIterableArray(payments) && payments.length ? ( + <> + + + + + + + + + + + {payments.map(payment => ( + + + + + + + ))} + +
{t('payments.payment_attributes.date')} + {t('payments.payment_attributes.description')} + {t('payments.payment_attributes.status')}{t('payments.payment_attributes.amount')}
{new Date(payment.created_at).toLocaleDateString("en-US", { + year: "numeric", + month: "2-digit", + day: "2-digit"} + )} + {payment.description}{payment.status}{`$${(payment.total_in_cents / 100).toFixed(2)}`}
+ + + ) : ( + + {t('payments.no_payments')} + + )} + +
+
+
+ ); +}; + +export default JKPaymentHistory; diff --git a/jam-ui/src/helpers/rest.js b/jam-ui/src/helpers/rest.js index 0a5ae8f5b..fc882ad9f 100644 --- a/jam-ui/src/helpers/rest.js +++ b/jam-ui/src/helpers/rest.js @@ -311,4 +311,12 @@ export const changeSubscription = (plan_code) => { .then(response => resolve(response)) .catch(error => reject(error)); }) +} + +export const getInvoiceHistory = (options = {}) => { + return new Promise((resolve, reject) => { + apiFetch(`/recurly/invoice_history?${new URLSearchParams(options)}`) + .then(response => resolve(response)) + .catch(error => reject(error)) + }); } \ No newline at end of file diff --git a/jam-ui/src/i18n/locales/en/account.json b/jam-ui/src/i18n/locales/en/account.json index aabe7cf5e..db3d05fe4 100644 --- a/jam-ui/src/i18n/locales/en/account.json +++ b/jam-ui/src/i18n/locales/en/account.json @@ -84,5 +84,17 @@ "failed_to_change_plan": "Failed to update subscription plan. Please try again later. Please contact support@jamkazam.com if you continue to have problems.", "changed_plan_successfully": "You have successfully updated your subscription plan." } + }, + "payments": { + "page_title": "Payment History", + "payment_attributes": { + "date": "Date", + "description": "Description", + "amount": "Amount", + "status": "Status" + }, + "no_payments": "No payments found.", + "load_more": "Load More", + "loading": "Loading..." } } \ No newline at end of file diff --git a/jam-ui/src/routes.js b/jam-ui/src/routes.js index d23788c9a..0c1404d20 100644 --- a/jam-ui/src/routes.js +++ b/jam-ui/src/routes.js @@ -55,10 +55,11 @@ export const accountRoutes = { exact: true, icon: 'lock', children: [ - { to: '/account/subscription', name: 'Subscription'}, - { to: '/account/payments', name: 'Payments'}, { to: '/account/identity', name: 'Identity'}, - { to: '/account/affiliate', name: 'Affiliate'} + { to: '/account/subscription', name: 'Subscription'}, + { to: '/account/payments', name: 'Payment History'}, + { to: '/account/payment-method', name: 'Payment Method'}, + { to: '/account/affiliate', name: 'Affiliate Program'}, ] }