Merge pull request #6 from mitchell131/fetch-data-from-api
Fetch data from api
This commit is contained in:
commit
2864bf8cdc
25
payment-iq/app/api/transactions/route.ts
Normal file
25
payment-iq/app/api/transactions/route.ts
Normal file
@ -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);
|
||||
}
|
||||
37
payment-iq/app/components/searchFilter/SearchFilters.tsx
Normal file
37
payment-iq/app/components/searchFilter/SearchFilters.tsx
Normal file
@ -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) => (
|
||||
<Chip
|
||||
key={key}
|
||||
label={
|
||||
<Typography variant="body2" sx={{ fontWeight: key === 'state' ? 'bold' : 'normal' }}>
|
||||
{label} {value}
|
||||
</Typography>
|
||||
}
|
||||
onDelete={() => onDeleteFilter(key)}
|
||||
sx={{ mr: 1, mb: 1 }}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<Box display="flex" alignItems="center" flexWrap="wrap" sx={{ p: 2 }}>
|
||||
{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) && (
|
||||
<Button
|
||||
onClick={onClearAll}
|
||||
sx={{ ml: 1, textDecoration: 'underline', color: 'black' }}
|
||||
>
|
||||
Clear All
|
||||
</Button>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default SearchFilters;
|
||||
@ -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<any[]>([]);
|
||||
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 (
|
||||
<LocalizationProvider dateAdapter={AdapterDateFns}>
|
||||
<Box p={2}>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
Search
|
||||
</Typography>
|
||||
<div className="p-4">
|
||||
<h1 className="text-xl font-bold mb-4">Transaction Search</h1>
|
||||
|
||||
<Grid container spacing={2}>
|
||||
{[
|
||||
{ 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 }) => (
|
||||
<Grid
|
||||
container
|
||||
item
|
||||
xs={12}
|
||||
alignItems="center"
|
||||
spacing={1}
|
||||
key={field}
|
||||
>
|
||||
<Grid item xs={4}>
|
||||
<Typography variant="body2" fontWeight={600}>
|
||||
{label}
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={8}>
|
||||
{type === "text" && (
|
||||
<TextField
|
||||
fullWidth
|
||||
size="small"
|
||||
value={form[field]}
|
||||
onChange={(e) => handleChange(field, e.target.value)}
|
||||
/>
|
||||
)}
|
||||
{type === "select" && (
|
||||
<FormControl fullWidth size="small">
|
||||
<Select
|
||||
value={form[field]}
|
||||
onChange={(e) => handleChange(field, e.target.value)}
|
||||
displayEmpty
|
||||
>
|
||||
<MenuItem value="">
|
||||
<em>{label}</em>
|
||||
</MenuItem>
|
||||
{options.map((option) => (
|
||||
<MenuItem value={option} key={option}>
|
||||
{option}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
)}
|
||||
{type === "date" && (
|
||||
<DatePicker
|
||||
value={form[field]}
|
||||
onChange={(newValue) => handleChange(field, newValue)}
|
||||
renderInput={(params) => (
|
||||
<TextField fullWidth size="small" {...params} />
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</Grid>
|
||||
</Grid>
|
||||
))}
|
||||
<div className="flex gap-4 mb-4">
|
||||
<div>
|
||||
<label className="block text-sm mb-1">User ID</label>
|
||||
<input
|
||||
type="text"
|
||||
value={userId}
|
||||
onChange={(e) => setUserId(e.target.value)}
|
||||
className="border p-2 rounded text-sm"
|
||||
placeholder="Filter by user ID"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Buttons */}
|
||||
<Grid item xs={12} display="flex" justifyContent="flex-end" gap={2}>
|
||||
<Button
|
||||
variant="outlined"
|
||||
startIcon={<RefreshIcon />}
|
||||
onClick={resetForm}
|
||||
>
|
||||
Reset
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
startIcon={<SearchIcon />}
|
||||
onClick={() => console.log("Apply Filter", form)}
|
||||
>
|
||||
Apply Filter
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Box>
|
||||
</LocalizationProvider>
|
||||
<div>
|
||||
<label className="block text-sm mb-1">State</label>
|
||||
<input
|
||||
type="text"
|
||||
value={state}
|
||||
onChange={(e) => setState(e.target.value)}
|
||||
className="border p-2 rounded text-sm"
|
||||
placeholder="Filter by state"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm mb-1">Status Code</label>
|
||||
<input
|
||||
type="text"
|
||||
value={statusCode}
|
||||
onChange={(e) => setStatusCode(e.target.value)}
|
||||
className="border p-2 rounded text-sm"
|
||||
placeholder="Filter by status code"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex items-end">
|
||||
<button
|
||||
onClick={fetchTransactions}
|
||||
disabled={loading}
|
||||
className="bg-blue-500 text-white px-4 py-2 rounded text-sm"
|
||||
>
|
||||
{loading ? 'Loading...' : 'Search'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{transactions.length > 0 ? (
|
||||
<div className="border rounded overflow-hidden">
|
||||
<table className="min-w-full">
|
||||
<thead className="bg-gray-100">
|
||||
<tr>
|
||||
<th className="py-2 px-4 text-left text-sm">ID</th>
|
||||
<th className="py-2 px-4 text-left text-sm">User</th>
|
||||
<th className="py-2 px-4 text-left text-sm">State</th>
|
||||
<th className="py-2 px-4 text-left text-sm">Status Code</th>
|
||||
<th className="py-2 px-4 text-left text-sm">Created</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{transactions.map((tx) => (
|
||||
<tr key={tx.id} className="border-t">
|
||||
<td className="py-2 px-4 text-sm">{tx.id}</td>
|
||||
<td className="py-2 px-4 text-sm">{tx.user}</td>
|
||||
<td className="py-2 px-4 text-sm">{tx.state}</td>
|
||||
<td className="py-2 px-4 text-sm">{tx.pspStatusCode}</td>
|
||||
<td className="py-2 px-4 text-sm">{tx.created}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
) : (
|
||||
<div className="text-center py-4 text-sm">
|
||||
{loading ? 'Loading transactions...' : 'No transactions found'}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// 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,
|
||||
}
|
||||
];
|
||||
|
||||
@ -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 (
|
||||
<div style={{ width: "100%" }}>
|
||||
<div style={{ width: "70%" }}>
|
||||
{/* This page will now be rendered on the client-side */}
|
||||
<TransactionTable />
|
||||
</div>
|
||||
|
||||
@ -21,7 +21,7 @@ const states = ["Pending", "Completed", "Failed"];
|
||||
const transactionTypes = ["Credit", "Debit"];
|
||||
const paymentMethods = ["Card", "Bank Transfer"];
|
||||
|
||||
export default function AdvancedSearch({setForm, form, resetForm}) {
|
||||
export default function AdvancedSearch({ setForm, form, resetForm }) {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const handleChange = (field: string, value: any) => {
|
||||
@ -172,7 +172,7 @@ export default function AdvancedSearch({setForm, form, resetForm}) {
|
||||
</Box>
|
||||
);
|
||||
return (
|
||||
<Box sx={{width: '185px'}}>
|
||||
<Box sx={{ width: '185px' }}>
|
||||
<Button
|
||||
sx={{
|
||||
borderRadius: "8px",
|
||||
@ -205,4 +205,4 @@ export default function AdvancedSearch({setForm, form, resetForm}) {
|
||||
</Drawer>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -21,16 +21,13 @@ 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";
|
||||
import SearchFilters from "@/app/components/searchFilter/SearchFilters";
|
||||
|
||||
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<typeof rows>([])
|
||||
|
||||
const [form, setForm] = useState({
|
||||
keyword: "",
|
||||
@ -78,54 +75,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 +103,43 @@ export default function TransactionTable() {
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
|
||||
const [transactions, setTransactions] = useState<any[]>([]);
|
||||
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
fetchTransactions();
|
||||
}, [form]);
|
||||
|
||||
const handleDeleteFilter = (key) => {
|
||||
setForm((prev) => ({ ...prev, [key]: '' }));
|
||||
};
|
||||
|
||||
const handleClearAll = () => {
|
||||
resetForm()
|
||||
fetchTransactions()
|
||||
};
|
||||
|
||||
|
||||
const handleClickField = (field: string, value: any) => {
|
||||
setForm((prev) => ({ ...prev, [field]: value }));
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<StyledPaper>
|
||||
<Stack
|
||||
@ -163,6 +157,7 @@ export default function TransactionTable() {
|
||||
sx={{ width: 300 }}
|
||||
/>
|
||||
<AdvancedSearch form={form} resetForm={resetForm} setForm={setForm} />
|
||||
<SearchFilters filters={form} onDeleteFilter={handleDeleteFilter} onClearAll={handleClearAll} />
|
||||
{/* <RightTemporaryDrawer /> */}
|
||||
{/* <SearchFilterForm /> */}
|
||||
<Button
|
||||
@ -175,11 +170,20 @@ export default function TransactionTable() {
|
||||
</Stack>
|
||||
|
||||
<DataGrid
|
||||
rows={filteredRows}
|
||||
rows={transactions}
|
||||
columns={columns}
|
||||
initialState={{ pagination: { paginationModel } }}
|
||||
pageSizeOptions={[50, 100]}
|
||||
sx={{ border: 0 }}
|
||||
sx={{ border: 0, cursor: 'pointer' }}
|
||||
|
||||
onCellClick={(params, event) => {
|
||||
// Check if the click is on a specific column
|
||||
// Do something when this specific column is clicked
|
||||
handleClickField(params.field, params.value)
|
||||
console.log('Clicked cell value:', params.value); // The cell's value
|
||||
console.log('Column field:', params.field); // The column's field name (from your columns definition)
|
||||
console.log('Column header:', params.colDef.headerName); // The column's display name // Your custom logic here
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Export Dialog */}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -2,7 +2,7 @@
|
||||
|
||||
import { ReactNode } from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import { store } from '../../lib/store';
|
||||
import { store } from '../redux/store';
|
||||
|
||||
export function Providers({ children }: { children: ReactNode }) {
|
||||
return <Provider store={store}>{children}</Provider>;
|
||||
|
||||
11
payment-iq/app/redux/store.ts
Normal file
11
payment-iq/app/redux/store.ts
Normal file
@ -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,
|
||||
},
|
||||
});
|
||||
|
||||
3
payment-iq/app/redux/transactions/selectors.ts
Normal file
3
payment-iq/app/redux/transactions/selectors.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import { RootState } from "../types";
|
||||
|
||||
export const selectTransactions = (state: RootState) => state.transactions.data;
|
||||
83
payment-iq/app/redux/transactions/transactionsSlice.ts
Normal file
83
payment-iq/app/redux/transactions/transactionsSlice.ts
Normal file
@ -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
|
||||
}
|
||||
);
|
||||
|
||||
5
payment-iq/app/redux/types.ts
Normal file
5
payment-iq/app/redux/types.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { store } from "./store";
|
||||
|
||||
export type RootState = ReturnType<typeof store.getState>;
|
||||
export type AppDispatch = typeof store.dispatch;
|
||||
|
||||
29
payment-iq/app/test1/page.tsx
Normal file
29
payment-iq/app/test1/page.tsx
Normal file
@ -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 (
|
||||
<div>
|
||||
<SearchFilters
|
||||
filters={filters}
|
||||
onDeleteFilter={handleDeleteFilter}
|
||||
onClearAll={handleClearAll}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -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<typeof store.getState>;
|
||||
export type AppDispatch = typeof store.dispatch;
|
||||
|
||||
@ -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
|
||||
});
|
||||
}),
|
||||
];
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user