diff --git a/my-app/src/AccountDetails.tsx b/my-app/src/AccountDetails.tsx new file mode 100644 index 0000000..68a3471 --- /dev/null +++ b/my-app/src/AccountDetails.tsx @@ -0,0 +1,91 @@ +import React, { useEffect, useState } from 'react'; +import { useParams } from 'react-router-dom'; +import PaymentHistoryTable from './PaymentHistoryTable'; + +interface AccountDetailsProps { + id: string; +} + +interface AccountInfo { + name: string; + socialSecurityNumber: string; + dateOfBirth: string; + address: string; + creditScore: number; + email: string; + phoneNumber: string; + cardNumber: string; +} + +interface AccountInfo { + id: string; + name: string; + socialSecurityNumber: string; + dateOfBirth: string; + address: string; + creditScore: number; + email: string; + phoneNumber: string; + cardNumber: string; +} + +interface PaymentRecord { + date: string; + description: string; + amount: number; + balance: number; +} + +interface AccountDetails { + accountInfo: AccountInfo; + paymentHistory: PaymentRecord[]; +} + + + +const AccountDetails: React.FC = () => { + const { id } = useParams(); + const [accountInfo, setAccountInfo] = useState(null); + + useEffect(() => { + fetchAccountDetails(id) + }, [id]); + + + const fetchAccountDetails = async (accountId: string): Promise => { + try { + const response = await fetch(`https://api.apg.com/accounts/${accountId}`); + + if (!response.ok) { + throw new Error(`HTTP error! Status: ${response.status}`); + } + + const accountDetails: AccountDetails = await response.json(); + setAccountInfo(accountDetails); + } catch (error) { + console.error('Error fetching account details:', error); + throw error; + } + }; + + return ( +
+ {accountInfo && ( +
+

Account Details

+

Name: {accountInfo.name}

+

Social Security Number: {accountInfo.socialSecurityNumber}

+

Date of Birth: {accountInfo.dateOfBirth}

+

Address: {accountInfo.address}

+

Credit Score: {accountInfo.creditScore}

+

Email: {accountInfo.email}

+

Phone Number: {accountInfo.phoneNumber}

+

Card Number: {accountInfo.cardNumber}

+ +
+ )} +
+ ); +}; + +export default AccountDetails; diff --git a/my-app/src/App.tsx b/my-app/src/App.tsx index 0ed220a..ffa396c 100644 --- a/my-app/src/App.tsx +++ b/my-app/src/App.tsx @@ -9,6 +9,7 @@ function App() { + ); diff --git a/my-app/src/PaymentsHistoryTable.tsx b/my-app/src/PaymentsHistoryTable.tsx new file mode 100644 index 0000000..5bb5eea --- /dev/null +++ b/my-app/src/PaymentsHistoryTable.tsx @@ -0,0 +1,64 @@ +import React, { useEffect, useState } from 'react'; + +interface PaymentHistoryTableProps { + accountId: string; +} + +interface PaymentRecord { + date: string; + description: string; + amount: number; + balance: number; +} + +const PaymentHistoryTable: React.FC = ({ accountId }) => { + const [paymentHistory, setPaymentHistory] = useState([]); + + const fetchHistory = async (accountId: string): Promise => { + try { + const response = await fetch(`https://api.apg.com/creditcards/${accountId}/transactions`); + + if (!response.ok) { + throw new Error(`HTTP error! Status: ${response.status}`); + } + + const history: PaymentRecord[] = await response.json(); + setPaymentHistory(history); + } catch (error) { + console.error('Error fetching account details:', error); + throw error; + } + }; + + useEffect(() => { + fetchHistory(accountId) + }, [accountId]); + + return ( +
+

Payment History

+ + + + + + + + + + + {paymentHistory.map((record, index) => ( + + + + + + + ))} + +
DateDescriptionAmountBalance
{record.date}{record.description}${record.amount}${record.balance}
+
+ ); +}; + +export default PaymentHistoryTable; diff --git a/my-app/src/SearchPage.tsx b/my-app/src/SearchPage.tsx index 7e2fcd2..e8c1e58 100644 --- a/my-app/src/SearchPage.tsx +++ b/my-app/src/SearchPage.tsx @@ -1,4 +1,4 @@ -import React, { useState, FormEvent, ChangeEvent } from 'react'; +import React, { useState, FormEvent } from 'react'; import SearchResultsTable from './SearchResultsTable'; export interface SearchResult { @@ -13,18 +13,19 @@ export interface SearchResult { } function SearchPage() { - const [searchQuery, setSearchQuery] = useState(''); + const [firstName, setFirstName] = useState(''); + const [lastName, setLastName] = useState(''); + const [cardNumber, setCardNumber] = useState(''); const [searchResults, setSearchResults] = useState([]); - const submitForm = async (): Promise => { try { - const response = await fetch('https://api.afg.com/account-search', { + const response = await fetch('https://api.afg.com/cardholders/search', { method: 'POST', headers: { 'Content-Type': 'application/json', }, - body: JSON.stringify(searchQuery), + body: JSON.stringify({ firstName, lastName, cardNumber }), }); if (!response.ok) { @@ -32,21 +33,15 @@ function SearchPage() { } const results: SearchResult[] = await response.json(); - setSearchResults(results) + setSearchResults(results); } catch (error) { console.error('Error fetching search results:', error); - throw error; } }; - const handleSearch = (e: FormEvent) => { e.preventDefault(); - submitForm() - }; - - const handleInputChange = (e: ChangeEvent) => { - setSearchQuery(e.target.value); + submitForm(); }; return ( @@ -54,9 +49,21 @@ function SearchPage() {
setFirstName(e.target.value)} + /> + setLastName(e.target.value)} + /> + setCardNumber(e.target.value)} />
diff --git a/server/api/card_holder.py b/server/api/card_holder.py index 73826a2..f2644b5 100644 --- a/server/api/card_holder.py +++ b/server/api/card_holder.py @@ -13,3 +13,8 @@ def search_cardholders(): search_params = request.args return card_holder_controller.get_card_holders(search_params) + + +@cardholder_api.route('/cardholders//details', methods=['GET']) +def get_cardholder_detail(card_holder_id): + return card_holder_controller.get_card_holder_details(card_holder_id) diff --git a/server/api/credit_card.py b/server/api/credit_card.py index 71cd3d3..76f68a7 100644 --- a/server/api/credit_card.py +++ b/server/api/credit_card.py @@ -11,4 +11,4 @@ # Route for getting all details of credit card transactions @creditcard_api.route('/creditcards//transactions', methods=['GET']) def get_credit_card_transactions(card_holder_id): - return credit_card_controller.get_credit_cards_by_card_holder(card_holder_id) + return credit_card_controller.get_credit_card_transactions(card_holder_id) diff --git a/server/controllers/card_holder_controller.py b/server/controllers/card_holder_controller.py index b2ec0b5..1c63ca4 100644 --- a/server/controllers/card_holder_controller.py +++ b/server/controllers/card_holder_controller.py @@ -6,8 +6,18 @@ class CardHolderController(BaseController): model = CardHolder - def get_card_holders(self, search_params): - return self.get_all(**search_params) + def get_card_holders(self, first_name=None, last_name=None, card_number=None): + query = CardHolder.query + + if first_name: + query = query.filter(CardHolder.first_name.ilike(f'%{first_name}%')) + if last_name: + query = query.filter(CardHolder.last_name.ilike(f'%{last_name}%')) + if card_number: + query = query.join(CreditCard).filter(CreditCard.card_number.ilike(f'%{card_number}%')) + + card_holders = query.all() + return jsonify([card_holder.to_dict() for card_holder in card_holders]) def get_card_holders_by_name(self, name): card_holders = CardHolder.query.filter(CardHolder.name.ilike(f'%{name}%')).all() @@ -17,5 +27,12 @@ def get_card_holders_by_card_issuance_date(self, issuance_date): card_holders = CardHolder.query.join(CreditCard).filter(CreditCard.issuance_date == issuance_date).all() return jsonify([card_holder.to_dict() for card_holder in card_holders]) + def get_card_holder_details(self, card_holder_id): + card_holder = CardHolder.query.get(card_holder_id) + if card_holder: + return jsonify(card_holder.to_dict()) + else: + return jsonify({"error": "Card holder not found"}), 404 + card_holder_controller = CardHolderController() diff --git a/server/controllers/credit_card_controller.py b/server/controllers/credit_card_controller.py index f28a080..5403b67 100644 --- a/server/controllers/credit_card_controller.py +++ b/server/controllers/credit_card_controller.py @@ -14,6 +14,15 @@ def get_credit_card_by_number(self, card_number): credit_card = CreditCard.query.filter_by(card_number=card_number).first() return jsonify(credit_card.to_dict() if credit_card else {}) + def get_credit_card_balance(self, card_number): + credit_card = CreditCard.query.filter_by(card_number=card_number).first() + if credit_card: + # Sum up the amounts of all transactions related to the credit card + total_balance = sum(transaction.amount for transaction in credit_card.transactions) + return jsonify({'balance': total_balance}) + else: + return jsonify({'error': 'Credit card not found'}) + def calculate_next_payment_date(self, card_number): credit_card = CreditCard.query.filter_by(card_number=card_number).first() if credit_card: @@ -23,5 +32,13 @@ def calculate_next_payment_date(self, card_number): else: return jsonify({'error': 'Credit card not found'}) + def get_credit_card_transactions(self, card_number): + credit_card = CreditCard.query.filter_by(card_number=card_number).first() + if credit_card: + transactions = credit_card.transactions + return jsonify([transaction.to_dict() for transaction in transactions]) + else: + return jsonify({'error': 'Credit card not found'}) + credit_card_controller = CreditCardController() diff --git a/server/models.py b/server/models.py index a15fe77..2f6ea93 100644 --- a/server/models.py +++ b/server/models.py @@ -25,3 +25,12 @@ class CreditCard(db.Model): overdue_balance_payment_date = db.Column(db.Date, nullable=False) days_overdue = db.Column(db.Integer, nullable=False) card_holder_id = db.Column(db.Integer, db.ForeignKey('card_holder.id'), nullable=False) + + +class Transaction(db.Model): + id = db.Column(db.Integer, primary_key=True) + credit_card_id = db.Column(db.Integer, db.ForeignKey('credit_card.id'), nullable=False) + transaction_date = db.Column(db.Date, nullable=False) + description = db.Column(db.String(255), nullable=False) + amount = db.Column(db.Float, nullable=False) + credit_card = db.relationship('CreditCard', backref=db.backref('transactions', lazy=True))