wip: make screen responsive for mobile and browser.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
"use client";
|
||||
import { Paper, Select, Title, Button, Text, Grid, ScrollArea, Table, Divider, Center, Loader, Stack, Group } from "@mantine/core";
|
||||
import { Paper, Select, Title, Button, Text, Grid, ScrollArea, Table, Divider, Center, Loader, Stack, Group, Card } from "@mantine/core";
|
||||
import { DateInput } from '@mantine/dates';
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { useSearchParams } from "next/navigation";
|
||||
@@ -8,10 +8,11 @@ import dayjs from 'dayjs';
|
||||
import { IconFileSpreadsheet, IconFileText, IconFileTypePdf } from "@tabler/icons-react";
|
||||
import { generatePDF } from "@/app/_components/statement_download/PdfGenerator";
|
||||
import { generateCSV } from "@/app/_components/statement_download/CsvGenerator";
|
||||
import { useMediaQuery } from "@mantine/hooks";
|
||||
|
||||
export default function AccountStatementPage() {
|
||||
|
||||
const pdfRef = useRef<HTMLDivElement>(null);
|
||||
// const pdfRef = useRef<HTMLDivElement>(null);
|
||||
const [accountOptions, setAccountOptions] = useState<{ value: string; label: string }[]>([]);
|
||||
const [selectedAccNo, setSelectedAccNo] = useState<string | null>(null);
|
||||
const [startDate, setStartDate] = useState<Date | null>(null);
|
||||
@@ -21,6 +22,7 @@ export default function AccountStatementPage() {
|
||||
const passedAccNo = searchParams.get("accNo");
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [availableBalance, setAvailableBalance] = useState<string | null>(null);
|
||||
const isMobile = useMediaQuery("(max-width: 768px)");
|
||||
|
||||
useEffect(() => {
|
||||
const saved = sessionStorage.getItem("accountData");
|
||||
@@ -237,9 +239,19 @@ export default function AccountStatementPage() {
|
||||
</Group>
|
||||
|
||||
<Divider size="xs" />
|
||||
{(startDate && endDate) && (
|
||||
<Text fs="italic" c="#228be6" ta="center">
|
||||
Transactions from {dayjs(startDate).format("DD/MM/YYYY")} to {dayjs(endDate).format("DD/MM/YYYY")}
|
||||
</Text>
|
||||
)}
|
||||
|
||||
{(!startDate && !endDate && transactions.length > 0) && (
|
||||
<Text fs="italic" c="#228be6" ta="center">
|
||||
Last 5 Transactions
|
||||
</Text>
|
||||
)}
|
||||
<ScrollArea style={{ flex: 1 }}>
|
||||
{loading ? (
|
||||
|
||||
<Center h="100%">
|
||||
<Stack align="center" gap="sm">
|
||||
<Loader size="lg" color="cyan" type="bars" />
|
||||
@@ -248,42 +260,70 @@ export default function AccountStatementPage() {
|
||||
</Center>
|
||||
) : transactions.length === 0 ? (
|
||||
<p>No transactions found.</p>
|
||||
) : isMobile ? (
|
||||
// ✅ Mobile View – Card Layout
|
||||
<Stack gap="sm">
|
||||
{transactions.map((txn, i) => (
|
||||
<Card key={i} shadow="xs" radius="md" withBorder>
|
||||
<Group p="apart">
|
||||
<Text fw={600} size="sm">
|
||||
{txn.name || "—"}
|
||||
</Text>
|
||||
<Text size="xs" c="dimmed">
|
||||
{txn.date || "—"}
|
||||
</Text>
|
||||
</Group>
|
||||
<Divider my="xs" />
|
||||
<Group p="apart">
|
||||
<Text size="sm" c={txn.type === "DR" ? "red" : "green"}>
|
||||
₹{parseFloat(txn.amount).toLocaleString("en-IN", { minimumFractionDigits: 2 })}{" "}
|
||||
<span style={{ fontSize: 11 }}>{txn.type === "DR" ? "Dr." : "Cr."}</span>
|
||||
</Text>
|
||||
<Text size="xs" c="blue">
|
||||
Bal: ₹{txn.balance || "—"}
|
||||
</Text>
|
||||
</Group>
|
||||
</Card>
|
||||
))}
|
||||
</Stack>
|
||||
) : (
|
||||
<>
|
||||
<Text fs="italic" c='#228be6' ta='center'>
|
||||
{!startDate && !endDate ? 'Last 5 Transactions'
|
||||
: startDate && endDate ? `Transactions from ${dayjs(startDate).format("DD/MM/YYYY")} to ${dayjs(endDate).format("DD/MM/YYYY")}`
|
||||
: ""}
|
||||
</Text>
|
||||
<Table style={{ borderCollapse: "collapse", width: '100%' }}>
|
||||
<thead style={{ backgroundColor: "#3385ff" }}>
|
||||
{/* <tr>
|
||||
<th style={{ ...cellStyle, position: 'sticky', textAlign: "left" }}>Name</th>
|
||||
<th style={{ ...cellStyle, position: 'sticky', textAlign: "left" }}>Date</th>
|
||||
<th style={{ ...cellStyle, position: 'sticky', textAlign: "left" }}>Type</th>
|
||||
<th style={{ ...cellStyle, position: 'sticky', textAlign: "left" }}>Amount(₹)</th>
|
||||
<th style={{ ...cellStyle, position: 'sticky', textAlign: "left" }}>Available Balance(₹)</th>
|
||||
</tr> */}
|
||||
</thead>
|
||||
<tbody style={{ maxHeight: '250px', overflowY: 'auto', width: '100%' }}>
|
||||
{transactions.map((txn, i) => (
|
||||
<tr key={i}>
|
||||
<td style={{ ...cellStyle, textAlign: "left" }}> {txn.name || "—"}</td>
|
||||
<td style={{ ...cellStyle, textAlign: "left" }}>{txn.date || "—"}</td>
|
||||
{/* <td style={{ ...cellStyle, textAlign: "left" }}>{txn.type}</td> */}
|
||||
<td style={{ ...cellStyle, textAlign: "right", color: txn.type === "DR" ? "#e03131" : "#2f9e44" }}>
|
||||
{parseFloat(txn.amount).toLocaleString("en-IN", {
|
||||
minimumFractionDigits: 2,
|
||||
})} <span style={{ fontSize: '10px' }}>{txn.type === "DR" ? "Dr." : "Cr."}</span>
|
||||
</td>
|
||||
<td style={{ ...cellStyle, textAlign: "right", color: "blue", fontSize: '12px' }}>₹{txn.balance || "—"}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</Table>
|
||||
</>
|
||||
// ✅ Desktop View – Table Layout
|
||||
<Table style={{ borderCollapse: "collapse", width: "100%" }}>
|
||||
<thead style={{ backgroundColor: "#3385ff" }}>
|
||||
<tr>
|
||||
<th style={{ ...cellStyle, textAlign: "left", color: "white" }}>Name</th>
|
||||
<th style={{ ...cellStyle, textAlign: "left", color: "white" }}>Date</th>
|
||||
<th style={{ ...cellStyle, textAlign: "right", color: "white" }}>Amount (₹)</th>
|
||||
<th style={{ ...cellStyle, textAlign: "right", color: "white" }}>Balance (₹)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{transactions.map((txn, i) => (
|
||||
<tr key={i}>
|
||||
<td style={{ ...cellStyle, textAlign: "left" }}>{txn.name || "—"}</td>
|
||||
<td style={{ ...cellStyle, textAlign: "left" }}>{txn.date || "—"}</td>
|
||||
<td
|
||||
style={{
|
||||
...cellStyle,
|
||||
textAlign: "right",
|
||||
color: txn.type === "DR" ? "#e03131" : "#2f9e44",
|
||||
}}
|
||||
>
|
||||
{parseFloat(txn.amount).toLocaleString("en-IN", {
|
||||
minimumFractionDigits: 2,
|
||||
})}{" "}
|
||||
<span style={{ fontSize: "10px" }}>{txn.type === "DR" ? "Dr." : "Cr."}</span>
|
||||
</td>
|
||||
<td style={{ ...cellStyle, textAlign: "right", color: "blue", fontSize: "12px" }}>
|
||||
₹{txn.balance || "—"}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</Table>
|
||||
)}
|
||||
</ScrollArea>
|
||||
|
||||
</Paper>
|
||||
</Grid.Col >
|
||||
</Grid >
|
||||
|
||||
@@ -1,18 +1,23 @@
|
||||
"use client";
|
||||
import { Divider, Stack, Text } from '@mantine/core';
|
||||
import { Box, Burger, Button, Divider, Drawer, Stack, Text } from '@mantine/core';
|
||||
import { usePathname } from 'next/navigation';
|
||||
import Link from 'next/link';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useMediaQuery } from '@mantine/hooks';
|
||||
import Image from "next/image";
|
||||
import logo from "@/app/image/logo1.jpg";
|
||||
|
||||
export default function Layout({ children }: { children: React.ReactNode }) {
|
||||
const [authorized, SetAuthorized] = useState<boolean | null>(null);
|
||||
const router = useRouter();
|
||||
const pathname = usePathname();
|
||||
const isMobile = useMediaQuery("(max-width: 768px)");
|
||||
const [drawerOpened, setDrawerOpened] = useState(false);
|
||||
|
||||
const links = [
|
||||
{ label: "Account Summary", href: "/accounts" },
|
||||
{ label: "Statement of Account", href: "/accounts/account_statement" },
|
||||
{ label: "Account Statement ", href: "/accounts/account_statement" },
|
||||
{ label: "Account Details", href: "/accounts/account_details" },
|
||||
];
|
||||
useEffect(() => {
|
||||
@@ -28,45 +33,145 @@ export default function Layout({ children }: { children: React.ReactNode }) {
|
||||
|
||||
if (authorized) {
|
||||
return (
|
||||
<div style={{ display: "flex", height: '100%' }}>
|
||||
<div
|
||||
style={{
|
||||
width: "16%",
|
||||
backgroundColor: '#c5e4f9',
|
||||
borderRight: "1px solid #ccc",
|
||||
}}
|
||||
>
|
||||
<Stack style={{ background: '#228be6', height: '10%', alignItems: 'center' }}>
|
||||
<Text fw={700} c='white' style={{ textAlign: 'center', marginTop: '10px' }}>
|
||||
My Accounts
|
||||
</Text>
|
||||
</Stack>
|
||||
<Box style={{ display: "flex", height: "100%", flexDirection: isMobile ? "column" : "row" }}>
|
||||
{/* Desktop Sidebar */}
|
||||
{!isMobile && (
|
||||
<Box
|
||||
style={{
|
||||
width: "16%",
|
||||
backgroundColor: "#c5e4f9",
|
||||
borderRight: "1px solid #ccc",
|
||||
}}
|
||||
>
|
||||
<Stack style={{ background: "#228be6", height: "10%", alignItems: "center" }}>
|
||||
<Text fw={700} c="white" style={{ textAlign: "center", marginTop: "10px" }}>
|
||||
My Accounts
|
||||
</Text>
|
||||
</Stack>
|
||||
|
||||
<Stack gap="sm" justify="flex-start" style={{ padding: '1rem' }}>
|
||||
{links.map(link => {
|
||||
const isActive = pathname === link.href;
|
||||
return (
|
||||
<Text
|
||||
key={link.href}
|
||||
component={Link}
|
||||
href={link.href}
|
||||
c={isActive ? 'darkblue' : 'blue'}
|
||||
style={{
|
||||
textDecoration: isActive ? 'underline' : 'none',
|
||||
fontWeight: isActive ? 600 : 400,
|
||||
}}
|
||||
>
|
||||
{link.label}
|
||||
</Text>
|
||||
);
|
||||
})}
|
||||
</Stack>
|
||||
</div>
|
||||
<Stack gap="sm" justify="flex-start" style={{ padding: "1rem" }}>
|
||||
{links.map((link) => {
|
||||
const isActive = pathname === link.href;
|
||||
return (
|
||||
<Text
|
||||
key={link.href}
|
||||
component={Link}
|
||||
href={link.href}
|
||||
c={isActive ? "darkblue" : "blue"}
|
||||
style={{
|
||||
textDecoration: isActive ? "underline" : "none",
|
||||
fontWeight: isActive ? 600 : 400,
|
||||
}}
|
||||
>
|
||||
{link.label}
|
||||
</Text>
|
||||
);
|
||||
})}
|
||||
</Stack>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
<div style={{ flex: 1, padding: '1rem' }}>
|
||||
{/* Mobile: Burger & Drawer */}
|
||||
{isMobile && (
|
||||
<>
|
||||
{/* Top header with burger and title */}
|
||||
<Box
|
||||
style={{
|
||||
backgroundColor: "#228be6",
|
||||
// padding: "0.5rem 1rem",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
}}
|
||||
>
|
||||
<Burger
|
||||
opened={drawerOpened}
|
||||
onClick={() => setDrawerOpened(!drawerOpened)}
|
||||
size="sm"
|
||||
color="white"
|
||||
/>
|
||||
<Text fw={500} c="white">
|
||||
My Accounts
|
||||
</Text>
|
||||
</Box>
|
||||
|
||||
<Drawer
|
||||
opened={drawerOpened}
|
||||
onClose={() => setDrawerOpened(false)}
|
||||
padding="md"
|
||||
size="75%"
|
||||
overlayProps={{ color: "black", opacity: 0.55, blur: 3 }}
|
||||
styles={{
|
||||
root: {
|
||||
backgroundColor: "#e6f5ff", // soft background for drawer
|
||||
// borderLeft: "4px solid #228be6",
|
||||
// borderRadius: "8px",
|
||||
},
|
||||
}}
|
||||
>
|
||||
{/* Logo and Drawer Header */}
|
||||
<Box style={{ display: "flex", alignItems: "center", marginBottom: "1rem" }}>
|
||||
<>
|
||||
<Image src={logo} alt="KCCB Logo" width={40} height={40} style={{ borderRadius: "50%" }} />
|
||||
<Text
|
||||
fw={700}
|
||||
ml="10px"
|
||||
style={{ fontSize: "18px", color: "#228be6" }}
|
||||
>
|
||||
My Accounts
|
||||
</Text>
|
||||
</>
|
||||
</Box>
|
||||
|
||||
{/* Menu Items */}
|
||||
<Stack gap="sm">
|
||||
{links.map((link) => {
|
||||
const isActive = pathname === link.href;
|
||||
|
||||
return (
|
||||
<Button
|
||||
key={link.href}
|
||||
variant="subtle"
|
||||
component={Link}
|
||||
href={link.href}
|
||||
fullWidth
|
||||
style={{
|
||||
justifyContent: "flex-start",
|
||||
fontWeight: isActive ? 600 : 400,
|
||||
textDecoration: isActive ? "underline" : "none",
|
||||
color: isActive ? "#fff" : "#228be6",
|
||||
backgroundColor: isActive ? "#228be6" : "#dceeff",
|
||||
borderRadius: "8px",
|
||||
padding: "10px 12px",
|
||||
transition: "0.3s",
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
const target = e.currentTarget as unknown as HTMLElement;
|
||||
target.style.backgroundColor = "#228be6";
|
||||
target.style.color = "#fff";
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
const target = e.currentTarget as unknown as HTMLElement;
|
||||
target.style.backgroundColor = isActive ? "#228be6" : "#dceeff";
|
||||
target.style.color = isActive ? "#fff" : "#228be6";
|
||||
}}
|
||||
onClick={() => setDrawerOpened(false)}
|
||||
>
|
||||
{link.label}
|
||||
</Button>
|
||||
);
|
||||
})}
|
||||
</Stack>
|
||||
</Drawer>
|
||||
</>
|
||||
)}
|
||||
|
||||
|
||||
{/* Content Area */}
|
||||
<Box style={{ flex: 1, padding: isMobile ? "0.5rem" : "1rem", overflowY: "auto" }}>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { Group, Paper, ScrollArea, Table, Text, Title } from "@mantine/core";
|
||||
import { Group, Paper, ScrollArea, Stack, Table, Text, Title } from "@mantine/core";
|
||||
import { useMediaQuery } from "@mantine/hooks";
|
||||
import { notifications } from "@mantine/notifications";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useEffect, useState } from "react";
|
||||
@@ -16,6 +17,7 @@ export default function AccountSummary() {
|
||||
const router = useRouter();
|
||||
const [authorized, setAuthorized] = useState<boolean | null>(null);
|
||||
const [accountData, setAccountData] = useState<accountData[]>([]);
|
||||
const isMobile = useMediaQuery("(max-width: 768px)");
|
||||
|
||||
async function FetchAccountDetails() {
|
||||
try {
|
||||
@@ -61,7 +63,8 @@ export default function AccountSummary() {
|
||||
|
||||
const cellStyle = {
|
||||
border: "1px solid #ccc",
|
||||
padding: "10px",
|
||||
padding: isMobile ? "8px" : "10px",
|
||||
fontSize: isMobile ? "13px" : "14px",
|
||||
};
|
||||
|
||||
// Filter accounts
|
||||
@@ -96,7 +99,7 @@ export default function AccountSummary() {
|
||||
<Paper shadow="sm" radius="md" p="md" withBorder w="100%"
|
||||
// bg="#97E6B8"
|
||||
>
|
||||
<Title order={4} mb="sm">
|
||||
<Title order={isMobile ? 5 : 4} mb="sm">
|
||||
{title}
|
||||
</Title>
|
||||
<ScrollArea>
|
||||
@@ -117,25 +120,38 @@ export default function AccountSummary() {
|
||||
</Paper>
|
||||
);
|
||||
|
||||
if (authorized) {
|
||||
return (
|
||||
<Paper shadow="sm" radius="md" p="md" withBorder h={400}
|
||||
// bg="linear-gradient(90deg,rgba(195, 218, 227, 1) 0%, rgba(151, 230, 184, 1) 50%)"
|
||||
if (!authorized) return null;
|
||||
|
||||
return (
|
||||
<Paper shadow="sm" radius="md" p="md" withBorder>
|
||||
<Title
|
||||
order={isMobile ? 4 : 3}
|
||||
mb="md"
|
||||
style={{ textAlign: isMobile ? "center" : "left" }}
|
||||
>
|
||||
<Title order={3} mb="md">Account Summary</Title>
|
||||
Account Summary
|
||||
</Title>
|
||||
|
||||
{/* ✅ Responsive layout: Group for desktop, Stack for mobile */}
|
||||
{isMobile ? (
|
||||
<Stack gap="md">
|
||||
{depositAccounts.length > 0 &&
|
||||
renderTable("Deposit Accounts (INR)", renderRows(depositAccounts))}
|
||||
{loanAccounts.length > 0 &&
|
||||
renderTable("Loan Accounts (INR)", renderRows(loanAccounts))}
|
||||
</Stack>
|
||||
) : (
|
||||
<Group align="flex-start" grow>
|
||||
{/* Left table for Deposit Accounts */}
|
||||
{depositAccounts.length > 0 && renderTable("Deposit Accounts (INR)", renderRows(depositAccounts))}
|
||||
|
||||
{/* Right table for Loan Accounts (only shown if available) */}
|
||||
{loanAccounts.length > 0 && renderTable("Loan Accounts (INR)", renderRows(loanAccounts))}
|
||||
{depositAccounts.length > 0 &&
|
||||
renderTable("Deposit Accounts (INR)", renderRows(depositAccounts))}
|
||||
{loanAccounts.length > 0 &&
|
||||
renderTable("Loan Accounts (INR)", renderRows(loanAccounts))}
|
||||
</Group>
|
||||
<Text mt="sm" size="xs" c="dimmed">
|
||||
* Book Balance includes uncleared effects.
|
||||
</Text>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
)}
|
||||
|
||||
return null;
|
||||
<Text mt="sm" size="xs" c="dimmed">
|
||||
* Book Balance includes uncleared effects.
|
||||
</Text>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user