Compare commits

..

4 Commits

3 changed files with 81 additions and 26 deletions

View File

@@ -71,7 +71,7 @@ export default function FormField({
// 2. Relation Handling (Select / Multi-Select) // 2. Relation Handling (Select / Multi-Select)
if (field.relation && relationDataMap[field.relation]) { if (field.relation && relationDataMap[field.relation]) {
const relationData = relationDataMap[field.relation]; const relationData = relationDataMap[field.relation].data;
const isArrayRelation = field.type === 'array'; const isArrayRelation = field.type === 'array';
// Determine how to display the related item // Determine how to display the related item

View File

@@ -30,10 +30,36 @@ export default function HistoryChart({
}; };
const activeDataKey = activeTab.toLowerCase(); const activeDataKey = activeTab.toLowerCase();
const currentData = data[activeDataKey] || data[activeTab] || []; const rawData = data[activeDataKey] || data[activeTab] || [];
const currentData = [...rawData].reverse();
const maxAmount = Math.max(...currentData.map((d) => d.amount), 1); const maxAmount =
currentData.length > 0
? Math.max(...currentData.map((d) => d.amount), 1)
: 1;
// ✅ Formatter (₹ + adaptive units)
const formatAmount = (amount: number) => {
const tab = activeTab.toLowerCase();
if (amount === 0) return "";
if (tab === "year") {
if (amount >= 100000) {
return `${(amount / 100000).toFixed(2)} L`;
}
return `${amount.toLocaleString("en-IN")}`;
}
if (tab === "month") {
if (amount >= 1000) {
return `${(amount / 1000).toFixed(1)} K`;
}
return `${amount.toLocaleString("en-IN")}`;
}
return `${amount.toLocaleString("en-IN")}`;
};
return ( return (
<Paper sx={{ p: { xs: 2, sm: 4 }, borderRadius: 4, width: "100%", boxShadow: 'none', border: '1px solid', borderColor: 'divider' }}> <Paper sx={{ p: { xs: 2, sm: 4 }, borderRadius: 4, width: "100%", boxShadow: 'none', border: '1px solid', borderColor: 'divider' }}>
<Typography variant="h6" fontWeight={700} gutterBottom> <Typography variant="h6" fontWeight={700} gutterBottom>
@@ -83,7 +109,11 @@ export default function HistoryChart({
const heightPerc = (point.amount / maxAmount) * 100; const heightPerc = (point.amount / maxAmount) * 100;
return ( return (
<Box <Box
key={point.id} key={
activeTab.toLowerCase() === "month"
? point.id.replace(" - ", "\n")
: point.id
}
sx={{ sx={{
flex: 1, flex: 1,
display: "flex", display: "flex",
@@ -93,10 +123,20 @@ export default function HistoryChart({
height: "100%", height: "100%",
}} }}
> >
<Typography variant="caption" sx={{ mb: 1, opacity: 0.7, fontSize: '0.65rem', display: { xs: 'none', sm: 'block' } }}> <Typography
{point.amount > 0 ? `Rs ${point.amount}` : ''} variant="caption"
color="text.secondary"
sx={{
mt: 1,
fontWeight: 500,
fontSize: '0.7rem',
textAlign: 'center',
whiteSpace: 'pre-line'
}}
>
{point.amount > 0 ? formatAmount(point.amount) : ""}
</Typography> </Typography>
<Box <Box
sx={{ sx={{
width: "40%", width: "40%",
@@ -112,7 +152,7 @@ export default function HistoryChart({
}), }),
}} }}
/> />
<Typography variant="caption" color="text.secondary" sx={{ mt: 1, fontWeight: 500, fontSize: '0.7rem' }}> <Typography variant="caption" color="text.secondary" sx={{ mt: 1, fontWeight: 500, fontSize: '0.7rem' }}>
{point.id} {point.id}
</Typography> </Typography>

View File

@@ -47,7 +47,7 @@ export async function fetchLatestTransactions(
return items return items
.filter((item: any) => isValid(Number(item.amount) || 0)) .filter((item: any) => isValid(Number(item.amount) || 0))
.slice(0, 10) .slice(0, 5)
.map((exp: any, index: number) => { .map((exp: any, index: number) => {
const time = new Date( const time = new Date(
exp.occurred_at || exp.created_at || Date.now() exp.occurred_at || exp.created_at || Date.now()
@@ -77,6 +77,7 @@ export interface AggregatedDashboardData {
topPayees: Array<{ payeeName: string; amount: number }>; topPayees: Array<{ payeeName: string; amount: number }>;
} }
// ---------------- AGGREGATION ----------------
// ---------------- AGGREGATION ---------------- // ---------------- AGGREGATION ----------------
export async function fetchAggregatedData( export async function fetchAggregatedData(
type: "expense" | "income" type: "expense" | "income"
@@ -103,7 +104,7 @@ export async function fetchAggregatedData(
const weekStart = getStartOfWeek(now); const weekStart = getStartOfWeek(now);
const weekEnd = endOfDay(new Date(weekStart.getTime() + 6 * 86400000)); const weekEnd = endOfDay(new Date(weekStart.getTime() + 6 * 86400000));
// ---------------- MONTH (5 rolling weeks) ---------------- // ---------------- MONTH (rolling 5 weeks, MonSun aligned) ----------------
const monthBuckets: { const monthBuckets: {
label: string; label: string;
start: Date; start: Date;
@@ -111,9 +112,11 @@ export async function fetchAggregatedData(
amount: number; amount: number;
}[] = []; }[] = [];
const currentWeekStart = getStartOfWeek(now);
for (let i = 0; i < 5; i++) { for (let i = 0; i < 5; i++) {
const end = endOfDay(new Date(now.getTime() - i * 7 * 86400000)); const start = new Date(currentWeekStart.getTime() - i * 7 * 86400000);
const start = startOfDay(new Date(end.getTime() - 6 * 86400000)); const end = endOfDay(new Date(start.getTime() + 6 * 86400000));
monthBuckets.push({ monthBuckets.push({
label: `${format(start)} - ${format(end)}`, label: `${format(start)} - ${format(end)}`,
@@ -123,7 +126,7 @@ export async function fetchAggregatedData(
}); });
} }
// ---------------- YEAR (12 months) ---------------- // ---------------- YEAR (rolling 12 months) ----------------
const yearBuckets: { const yearBuckets: {
label: string; label: string;
start: Date; start: Date;
@@ -136,10 +139,18 @@ export async function fetchAggregatedData(
d.setMonth(d.getMonth() - i); d.setMonth(d.getMonth() - i);
const start = new Date(d.getFullYear(), d.getMonth(), 1); const start = new Date(d.getFullYear(), d.getMonth(), 1);
const end = endOfDay(new Date(d.getFullYear(), d.getMonth() + 1, 0));
const end =
i === 0
? endOfDay(now) // current month → till now
: endOfDay(new Date(d.getFullYear(), d.getMonth() + 1, 0));
const label = `${d.toLocaleString("default", {
month: "short"
})}-${String(d.getFullYear()).slice(2)}`;
yearBuckets.push({ yearBuckets.push({
label: d.toLocaleString("default", { month: "short" }), label,
start, start,
end, end,
amount: 0 amount: 0
@@ -170,27 +181,31 @@ export async function fetchAggregatedData(
} }
} }
// MONTH // MONTH (rolling weeks)
monthBuckets.forEach(b => { for (const b of monthBuckets) {
if (d >= b.start && d <= b.end) { if (d >= b.start && d <= b.end) {
b.amount += amt; b.amount += amt;
} }
}); }
// YEAR // YEAR (rolling months)
yearBuckets.forEach(b => { for (const b of yearBuckets) {
if (d >= b.start && d <= b.end) { if (d >= b.start && d <= b.end) {
b.amount += amt; b.amount += amt;
} }
}); }
} }
const toPoints = (b: any): ChartDataPoint[] => const toPoints = (b: any): ChartDataPoint[] =>
Object.entries(b).map(([k, v]: any) => ({ Array.isArray(b)
id: k, ? b.map((x) => ({
amount: typeof v === "number" ? v : v.amount, id: x.label,
subLabel: typeof v === "number" ? undefined : v.range amount: x.amount
})); }))
: Object.entries(b).map(([k, v]: any) => ({
id: k,
amount: v
}));
const chartData = { const chartData = {
week: toPoints(weekBuckets), week: toPoints(weekBuckets),