From d354ac17606bb29384893441b7cfeeb11b20cc88 Mon Sep 17 00:00:00 2001 From: Petropoulos Evangelos Date: Sun, 6 Jul 2025 18:39:59 +0300 Subject: [PATCH] dummy --- node_modules/.yarn-integrity | 10 + payment-iq/app/api/transactions/route.ts | 25 + .../components/searchFilter/SearchFilters.tsx | 37 + payment-iq/app/components/test/test2.tsx | 374 +- .../app/dashboard/transactions/page.tsx | 4 +- .../Pages/transactions/Transactions.tsx | 80 +- .../features/Pages/transactions/mockData.ts | 4877 ++++++++++------- payment-iq/app/providers/providers.tsx | 2 +- .../advanedSearch}/advancedSearchSlice.ts | 0 payment-iq/app/redux/store.ts | 11 + .../app/redux/transactions/selectors.ts | 3 + .../redux/transactions/transactionsSlice.ts | 83 + payment-iq/app/redux/types.ts | 5 + payment-iq/app/test1/page.tsx | 29 + payment-iq/lib/store.ts | 12 - payment-iq/mock/handlers.ts | 37 +- yarn.lock | 4 + 17 files changed, 3197 insertions(+), 2396 deletions(-) create mode 100644 node_modules/.yarn-integrity create mode 100644 payment-iq/app/api/transactions/route.ts create mode 100644 payment-iq/app/components/searchFilter/SearchFilters.tsx rename payment-iq/app/{features/AdvancedSearch/store => redux/advanedSearch}/advancedSearchSlice.ts (100%) create mode 100644 payment-iq/app/redux/store.ts create mode 100644 payment-iq/app/redux/transactions/selectors.ts create mode 100644 payment-iq/app/redux/transactions/transactionsSlice.ts create mode 100644 payment-iq/app/redux/types.ts create mode 100644 payment-iq/app/test1/page.tsx delete mode 100644 payment-iq/lib/store.ts create mode 100644 yarn.lock diff --git a/node_modules/.yarn-integrity b/node_modules/.yarn-integrity new file mode 100644 index 0000000..b9eb206 --- /dev/null +++ b/node_modules/.yarn-integrity @@ -0,0 +1,10 @@ +{ + "systemParams": "darwin-arm64-131", + "modulesFolders": [], + "flags": [], + "linkedModules": [], + "topLevelPatterns": [], + "lockfileEntries": {}, + "files": [], + "artifacts": {} +} \ No newline at end of file diff --git a/payment-iq/app/api/transactions/route.ts b/payment-iq/app/api/transactions/route.ts new file mode 100644 index 0000000..0d8f238 --- /dev/null +++ b/payment-iq/app/api/transactions/route.ts @@ -0,0 +1,25 @@ +import { transactionDummyData } from '@/app/features/Pages/transactions/mockData'; +import { NextRequest, NextResponse } from 'next/server'; + +export async function GET(request: NextRequest) { + const { searchParams } = new URL(request.url); + + const state = searchParams.get('state'); + const user = searchParams.get('user'); + + let filteredTransactions = [...transactionDummyData]; + + if (user) { + filteredTransactions = filteredTransactions.filter( + tx => tx.user.toString() === user + ); + } + + if (state) { + filteredTransactions = filteredTransactions.filter( + tx => tx.state.toLowerCase() === state.toLowerCase() + ); + } + + return NextResponse.json(filteredTransactions); +} \ No newline at end of file diff --git a/payment-iq/app/components/searchFilter/SearchFilters.tsx b/payment-iq/app/components/searchFilter/SearchFilters.tsx new file mode 100644 index 0000000..82f5692 --- /dev/null +++ b/payment-iq/app/components/searchFilter/SearchFilters.tsx @@ -0,0 +1,37 @@ +// components/SearchFilters.js +import React from 'react'; +import { Box, Chip, Typography, Button } from '@mui/material'; + +const SearchFilters = ({ filters, onDeleteFilter, onClearAll }) => { + const renderChip = (label, value, key) => ( + + {label} {value} + + } + onDelete={() => onDeleteFilter(key)} + sx={{ mr: 1, mb: 1 }} + /> + ); + + return ( + + {filters.user && renderChip('User', filters.user, 'user')} + {filters.state && renderChip('State', filters.state, 'state')} + {filters.startDate && renderChip('Start Date', filters.startDate, 'startDate')} + + {Object.values(filters).some(Boolean) && ( + + )} + + ); +}; + +export default SearchFilters; diff --git a/payment-iq/app/components/test/test2.tsx b/payment-iq/app/components/test/test2.tsx index 631ba38..93792b4 100644 --- a/payment-iq/app/components/test/test2.tsx +++ b/payment-iq/app/components/test/test2.tsx @@ -1,207 +1,189 @@ -import React, { useState } from "react"; -import { - Box, - Grid, - TextField, - MenuItem, - Button, - InputLabel, - FormControl, - Select, - Typography, -} from "@mui/material"; -import { DatePicker } from "@mui/x-date-pickers/DatePicker"; -import { AdapterDateFns } from "@mui/x-date-pickers/AdapterDateFns"; -import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider"; -import SearchIcon from "@mui/icons-material/Search"; -import RefreshIcon from "@mui/icons-material/Refresh"; +// app/transactions/page.tsx +'use client'; -const currencies = ["USD", "EUR", "GBP"]; -const states = ["Pending", "Completed", "Failed"]; -const transactionTypes = ["Credit", "Debit"]; -const paymentMethods = ["Card", "Bank Transfer"]; -const psps = ["Stripe", "PayPal"]; -const merchants = ["Amazon", "eBay"]; +import { useState } from 'react'; -export default function SearchFilterForm() { - const [form, setForm] = useState({ - keyword: "", - transactionId: "", - transactionReferenceId: "", - user: "", - currency: "", - state: "", - statusDescription: "", - transactionType: "", - paymentMethod: "", - psps: "", - initialPsps: "", - merchants: "", - startDate: null, - endDate: null, - lastUpdatedFrom: null, - lastUpdatedTo: null, - minAmount: "", - maxAmount: "", - channel: "", - }); +export default function TransactionsPage() { + const [userId, setUserId] = useState(''); + const [state, setState] = useState(''); + const [statusCode, setStatusCode] = useState(''); + const [transactions, setTransactions] = useState([]); + const [loading, setLoading] = useState(false); - const handleChange = (field: string, value: any) => { - setForm((prev) => ({ ...prev, [field]: value })); - }; + const fetchTransactions = async () => { + setLoading(true); + try { + const url = new URL('https://api.example.com/transactions'); + if (userId) url.searchParams.append('userId', userId); + if (state) url.searchParams.append('state', state); + if (statusCode) url.searchParams.append('statusCode', statusCode); - const resetForm = () => { - setForm({ - keyword: "", - transactionId: "", - transactionReferenceId: "", - user: "", - currency: "", - state: "", - statusDescription: "", - transactionType: "", - paymentMethod: "", - psps: "", - initialPsps: "", - merchants: "", - startDate: null, - endDate: null, - lastUpdatedFrom: null, - lastUpdatedTo: null, - minAmount: "", - maxAmount: "", - channel: "", - }); + const response = await fetch(url.toString()); + const data = await response.json(); + setTransactions(data.transactions); + } catch (error) { + console.error('Error fetching transactions:', error); + } finally { + setLoading(false); + } }; return ( - - - - Search - +
+

Transaction Search

+ +
+
+ + setUserId(e.target.value)} + className="border p-2 rounded text-sm" + placeholder="Filter by user ID" + /> +
- - {[ - { label: "Keyword", field: "keyword", type: "text" }, - { label: "Transaction ID", field: "transactionId", type: "text" }, - { - label: "Transaction Reference ID", - field: "transactionReferenceId", - type: "text", - }, - { label: "User", field: "user", type: "text" }, - { - label: "Currency", - field: "currency", - type: "select", - options: currencies, - }, - { label: "State", field: "state", type: "select", options: states }, - { - label: "Status Description", - field: "statusDescription", - type: "text", - }, - { - label: "Transaction Type", - field: "transactionType", - type: "select", - options: transactionTypes, - }, - { - label: "Payment Method", - field: "paymentMethod", - type: "select", - options: paymentMethods, - }, - { label: "PSPs", field: "psps", type: "text" }, - { label: "Initial PSPs", field: "initialPsps", type: "text" }, - { label: "Merchants", field: "merchants", type: "text" }, - { label: "Start Date", field: "startDate", type: "date" }, - { label: "End Date", field: "endDate", type: "date" }, - { - label: "Last Updated From", - field: "lastUpdatedFrom", - type: "date", - }, - { label: "Last Updated To", field: "lastUpdatedTo", type: "date" }, - { label: "Min Amount", field: "minAmount", type: "text" }, - { label: "Max Amount", field: "maxAmount", type: "text" }, - { label: "Channel", field: "channel", type: "text" }, - ].map(({ label, field, type, options }) => ( - - - - {label} - - - - {type === "text" && ( - handleChange(field, e.target.value)} - /> - )} - {type === "select" && ( - - - - )} - {type === "date" && ( - handleChange(field, newValue)} - renderInput={(params) => ( - - )} - /> - )} - - - ))} +
+ + setState(e.target.value)} + className="border p-2 rounded text-sm" + placeholder="Filter by state" + /> +
- {/* Buttons */} - - - - -
- - +
+ + setStatusCode(e.target.value)} + className="border p-2 rounded text-sm" + placeholder="Filter by status code" + /> +
+ +
+ +
+
+ + {transactions.length > 0 ? ( +
+ + + + + + + + + + + + {transactions.map((tx) => ( + + + + + + + + ))} + +
IDUserStateStatus CodeCreated
{tx.id}{tx.user}{tx.state}{tx.pspStatusCode}{tx.created}
+
+ ) : ( +
+ {loading ? 'Loading transactions...' : 'No transactions found'} +
+ )} +
); } + + +// mocks/handlers.ts +import { http, HttpResponse } from 'msw'; +import { transactionDummyData } from './transactionData'; + +export const handlers = [ + http.get('https://api.example.com/transactions', ({ request }) => { + const url = new URL(request.url); + + // Get query parameters + const userId = url.searchParams.get('userId'); + const state = url.searchParams.get('state'); + const statusCode = url.searchParams.get('statusCode'); + + // Filter transactions based on query parameters + let filteredTransactions = [...transactionDummyData]; + + if (userId) { + filteredTransactions = filteredTransactions.filter( + tx => tx.user.toString() === userId + ); + } + + if (state) { + filteredTransactions = filteredTransactions.filter( + tx => tx.state.toLowerCase() === state.toLowerCase() + ); + } + + if (statusCode) { + filteredTransactions = filteredTransactions.filter( + tx => tx.pspStatusCode.toString() === statusCode + ); + } + + return HttpResponse.json({ + transactions: filteredTransactions, + count: filteredTransactions.length + }); + }), +]; + + +// mocks/transactionData.ts +export const transactionDummyData = [ + { + id: 1, + merchandId: 100987998, + transactionID: 1049131973, + user: 1, + created: "2025-06-18 10:10:30", + state: "FAILED", + statusDescription: "ERR_ABOVE_LIMIT", + pspStatusCode: 100501, + }, + { + id: 2, + merchandId: 100987998, + transactionID: 1049131973, + user: 2, + created: "2025-06-18 10:10:30", + state: "FAILED", + statusDescription: "ERR_ABOVE_LIMIT", + pspStatusCode: 100501, + }, + { + id: 3, + merchandId: 100987998, + transactionID: 1049131973, + user: 3, + created: "2025-06-18 10:10:30", + state: "FAILED", + statusDescription: "ERR_ABOVE_LIMIT", + pspStatusCode: 100501, + } +]; diff --git a/payment-iq/app/dashboard/transactions/page.tsx b/payment-iq/app/dashboard/transactions/page.tsx index f2059f1..64e0f47 100644 --- a/payment-iq/app/dashboard/transactions/page.tsx +++ b/payment-iq/app/dashboard/transactions/page.tsx @@ -1,11 +1,11 @@ // This ensures this component is rendered only on the client side -"use client"; import TransactionTable from "@/app/features/Pages/Transactions/Transactions"; + export default function TransactionPage() { return ( -
+
{/* This page will now be rendered on the client-side */}
diff --git a/payment-iq/app/features/Pages/transactions/Transactions.tsx b/payment-iq/app/features/Pages/transactions/Transactions.tsx index a8629c5..37161ec 100644 --- a/payment-iq/app/features/Pages/transactions/Transactions.tsx +++ b/payment-iq/app/features/Pages/transactions/Transactions.tsx @@ -21,16 +21,12 @@ import * as XLSX from "xlsx"; import { saveAs } from "file-saver"; import { DataGrid } from "@mui/x-data-grid"; import { columns } from "./constants"; -import { rows } from "./mockData"; +// import { rows } from "./mockData"; import AdvancedSearch from "../../AdvancedSearch/AdvancedSearch"; const paginationModel = { page: 0, pageSize: 50 }; export default function TransactionTable() { - const [open, setOpen] = useState(false); - const [fileType, setFileType] = useState<"csv" | "xls" | "xlsx">("csv"); - const [onlyCurrentTable, setOnlyCurrentTable] = useState(false); - const [filteredRows, setFilteredRows] = useState([]) const [form, setForm] = useState({ keyword: "", @@ -78,54 +74,14 @@ export default function TransactionTable() { }); }; - - const filterRows = (rows1, filters) => { - // debugger - return rows1.filter(row => { - const hasTransactionIdFilter = filters.transactionID !== ""; - const hasStateFilter = filters.state !== ""; - - - if (hasTransactionIdFilter && hasStateFilter) { - console.log(1234) - // Return rows that match BOTH filters - return row.transactionID == filters.transactionID && row.state.toLowerCase() === filters.state.toLowerCase(); - } else if (hasTransactionIdFilter) { - console.log(12345) - // Return rows that match merchandId only - return row.transactionID == filters.transactionID; - } else if (hasStateFilter) { - // Return rows that match state only - console.log(123456) - return row.state.toLowerCase() === filters.state.toLowerCase(); - } else { - console.log(1234567) - // No filters applied, return all rows - return rows; - } - }); - }; - - - useEffect(() => { - console.log(form) - console.log(filterRows(rows, { transactionID: form.transactionID, state: form.state })) - setFilteredRows(filterRows(rows, { transactionID: form.transactionID, state: form.state })); - }, [form]) - - // useEffect(()=>{ - // if(form?.transactionId){ - // setFilteredRows(rows.filter(row => (row.merchandId.toString() === form.transactionId) && (row.state !== "" && (row.state === form.state)))); - // } else{ - // setFilteredRows(rows) - // } - // },[form]) + const [open, setOpen] = useState(false); + const [fileType, setFileType] = useState<"csv" | "xls" | "xlsx">("csv"); + const [onlyCurrentTable, setOnlyCurrentTable] = useState(false); const handleExport = () => { - const exportRows = onlyCurrentTable ? rows.slice(0, 5) : rows; + const exportRows = onlyCurrentTable ? transactions.slice(0, 5) : transactions; const exportData = [ columns.map((col) => col.headerName), - // @ts-expect-error - Dynamic field access from DataGrid columns ...exportRows.map((row) => columns.map((col) => row[col.field] ?? "")), ]; @@ -146,6 +102,30 @@ export default function TransactionTable() { setOpen(false); }; + + const [transactions, setTransactions] = useState([]); + // const [filters, setFilters] = useState({ + // state: '', + // user: '', + // }) + + + useEffect(() => { + const fetchTransactions = async () => { + try { + const query = new URLSearchParams(form as any).toString(); + const res = await fetch(`/api/transactions?${query}`); + const data = await res.json(); + setTransactions(data); + } catch (error) { + console.error('Error fetching transactions:', error); + } + }; + + fetchTransactions(); + }, [form]); + + return ( {children}; diff --git a/payment-iq/app/features/AdvancedSearch/store/advancedSearchSlice.ts b/payment-iq/app/redux/advanedSearch/advancedSearchSlice.ts similarity index 100% rename from payment-iq/app/features/AdvancedSearch/store/advancedSearchSlice.ts rename to payment-iq/app/redux/advanedSearch/advancedSearchSlice.ts diff --git a/payment-iq/app/redux/store.ts b/payment-iq/app/redux/store.ts new file mode 100644 index 0000000..59a6f77 --- /dev/null +++ b/payment-iq/app/redux/store.ts @@ -0,0 +1,11 @@ +import { configureStore } from '@reduxjs/toolkit'; +import advancedSearchReducer from './advanedSearch/advancedSearchSlice'; +import transactionsReducer from './transactions/transactionsSlice'; + +export const store = configureStore({ + reducer: { + advancedSearch: advancedSearchReducer, + transactions: transactionsReducer, + }, +}); + diff --git a/payment-iq/app/redux/transactions/selectors.ts b/payment-iq/app/redux/transactions/selectors.ts new file mode 100644 index 0000000..1ee7436 --- /dev/null +++ b/payment-iq/app/redux/transactions/selectors.ts @@ -0,0 +1,3 @@ +import { RootState } from "../types"; + +export const selectTransactions = (state: RootState) => state.transactions.data; diff --git a/payment-iq/app/redux/transactions/transactionsSlice.ts b/payment-iq/app/redux/transactions/transactionsSlice.ts new file mode 100644 index 0000000..6fcd7a9 --- /dev/null +++ b/payment-iq/app/redux/transactions/transactionsSlice.ts @@ -0,0 +1,83 @@ +import { createAsyncThunk, createSlice } from "@reduxjs/toolkit"; + +interface ITransactionsData { + id: number; + merchandId: number; + transactionID: number; + user: number; + created: string; + state: string; + statusDescription: string; + pspStatusCode: number; + +} +interface ITransactionsState { + data: ITransactionsData[]; + loading: boolean; + error: null | string; + totalTransactions: number +} + + + +const initialState: ITransactionsState = { + data: [], + loading: false, + error: null, + totalTransactions: 0, +} + +const transactionsSlice = createSlice({ + name: 'transactions', + initialState, + reducers: { + }, + extraReducers: (builder) => { + builder + .addCase(getTransactions.pending, (state) => { + state.loading = true; + state.error = null; + }) + .addCase(getTransactions.fulfilled, (state, action) => { + state.data = action.payload.transactions; + state.totalTransactions = action.payload.count; + state.loading = false; + }) + .addCase(getTransactions.rejected, (state, action) => { + state.error = action.error.message || "Failed to fetch categories"; + state.loading = false; + state.data = []; + }) + } +}, +); + +export default transactionsSlice.reducer; + + +export const getTransactions = createAsyncThunk( + 'transactions/getTransactions', + async ( + { + userId = '', + state = '', + statusCode = '', + }: { userId?: string; state?: string; statusCode?: string } = {} + ) => { + const url = new URL('https://api.example.com/transactions'); + + if (userId) url.searchParams.append('userId', userId); + if (state) url.searchParams.append('state', state); + if (statusCode) url.searchParams.append('statusCode', statusCode); + + const response = await fetch(url.toString()); + + if (!response.ok) { + throw new Error('Failed to fetch transactions'); + } + + const data = await response.json(); + return data; // Let the reducer store this + } +); + diff --git a/payment-iq/app/redux/types.ts b/payment-iq/app/redux/types.ts new file mode 100644 index 0000000..574261a --- /dev/null +++ b/payment-iq/app/redux/types.ts @@ -0,0 +1,5 @@ +import { store } from "./store"; + +export type RootState = ReturnType; +export type AppDispatch = typeof store.dispatch; + diff --git a/payment-iq/app/test1/page.tsx b/payment-iq/app/test1/page.tsx new file mode 100644 index 0000000..67a44e9 --- /dev/null +++ b/payment-iq/app/test1/page.tsx @@ -0,0 +1,29 @@ +'use client' +import React, { useState } from 'react'; +import SearchFilters from '../components/searchFilter/SearchFilters'; + +export default function Home() { + const [filters, setFilters] = useState({ + user: '42', + state: 'FAILED', + startDate: '2025-06-28 23:25', + }); + + const handleDeleteFilter = (key) => { + setFilters((prev) => ({ ...prev, [key]: null })); + }; + + const handleClearAll = () => { + setFilters({ user: null, state: null, startDate: null }); + }; + + return ( +
+ +
+ ); +} diff --git a/payment-iq/lib/store.ts b/payment-iq/lib/store.ts deleted file mode 100644 index 812f9b7..0000000 --- a/payment-iq/lib/store.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { configureStore } from '@reduxjs/toolkit'; -import advancedSearchReducer from '@/app/features/AdvancedSearch/store/advancedSearchSlice'; - -export const store = configureStore({ - reducer: { - advancedSearch: advancedSearchReducer, - }, -}); - -export type RootState = ReturnType; -export type AppDispatch = typeof store.dispatch; - diff --git a/payment-iq/mock/handlers.ts b/payment-iq/mock/handlers.ts index 902d618..d80ae73 100644 --- a/payment-iq/mock/handlers.ts +++ b/payment-iq/mock/handlers.ts @@ -21,6 +21,7 @@ // ]; +import { transactionDummyData } from '@/app/features/Pages/transactions/mockData'; import { http, HttpResponse } from 'msw'; export const handlers = [ @@ -78,8 +79,6 @@ export const handlers = [ category: category || 'general', })); - console.log(1234, mockProducts) - // Sort products if sort parameter provided if (sort === 'price') { mockProducts.sort((a, b) => a.price - b.price); @@ -95,4 +94,38 @@ export const handlers = [ sortBy: sort, }); }), + http.get('https://api.example.com/transactions', ({ request }) => { + const url = new URL(request.url); + + // Get query parameters + const userId = url.searchParams.get('userId'); + const state = url.searchParams.get('state'); + const statusCode = url.searchParams.get('statusCode'); + + // Filter transactions based on query parameters + let filteredTransactions = [...transactionDummyData]; + + if (userId) { + filteredTransactions = filteredTransactions.filter( + tx => tx.user.toString() === userId + ); + } + + if (state) { + filteredTransactions = filteredTransactions.filter( + tx => tx.state.toLowerCase() === state.toLowerCase() + ); + } + + if (statusCode) { + filteredTransactions = filteredTransactions.filter( + tx => tx.pspStatusCode.toString() === statusCode + ); + } + + return HttpResponse.json({ + transactions: filteredTransactions, + count: filteredTransactions.length + }); + }), ]; diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..fb57ccd --- /dev/null +++ b/yarn.lock @@ -0,0 +1,4 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + +