Skip to content

Commit

Permalink
Merge pull request #46 from drishit96/feat-payment-mode-filter
Browse files Browse the repository at this point in the history
Feat: Filter transactions by payment mode
  • Loading branch information
drishit96 authored Aug 3, 2023
2 parents a2155e0 + a9748a7 commit 5a99c0e
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 2 deletions.
70 changes: 69 additions & 1 deletion app/components/FilterBottomSheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Ripple } from "@rmwc/ripple";
import { useEffect, useState } from "react";
import type { AppContext } from "~/root";
import {
getAllPaymentModes,
getAllTransactionTypes,
getCombinedCategoriesForAllTransactionTypes,
} from "~/utils/category.utils";
Expand All @@ -15,6 +16,7 @@ export type NewFilter = {
selectedCategories: string[];
month: string;
selectedTypes: string[];
selectedPaymentModes: string[];
};

function FilterTab({
Expand Down Expand Up @@ -51,26 +53,37 @@ export default function FilterBottomSheet({
defaultSelectedCategories,
defaultMonth,
defaultTypes,
defaultPaymentModes,
onFilterSet,
}: {
context: AppContext;
defaultSelectedCategories?: string[];
defaultMonth: string;
defaultTypes?: string[];
defaultPaymentModes?: string[];
onFilterSet: (filter: NewFilter) => void;
}) {
const [categories, setCategories] = useState<{ label: string; value: string }[]>([]);
const [paymentModes, setPaymentModes] = useState<{ label: string; value: string }[]>(
[]
);
const [currentTab, setCurrentTab] = useState("category");
const [categorySearchValue, setCategorySearchValue] = useState("");
const [paymentModeSearchValue, setPaymentModeSearchValue] = useState("");
const [selectedTypes, setSelectedTypes] = useState(new Set(defaultTypes));
const [selectedCategories, setSelectedCategories] = useState(
new Set(defaultSelectedCategories)
);
const [selectedPaymentModes, setSelectedPaymentModes] = useState(
new Set(defaultPaymentModes)
);
useEffect(() => {
setCategories(getCombinedCategoriesForAllTransactionTypes());
setPaymentModes(getAllPaymentModes());
setSelectedTypes(new Set(defaultTypes));
setSelectedCategories(new Set(defaultSelectedCategories));
}, [defaultTypes, defaultSelectedCategories]);
setSelectedPaymentModes(new Set(defaultPaymentModes));
}, [defaultTypes, defaultSelectedCategories, defaultPaymentModes]);
return (
<div className="flex flex-col text-primary">
<p className="text-2xl font-semibold">Filter</p>
Expand All @@ -92,6 +105,13 @@ export default function FilterBottomSheet({
isCurrentTab={currentTab === "type"}
onClick={() => setCurrentTab("type")}
/>
<FilterTab
key="paymentMode"
name="Payment mode"
count={selectedPaymentModes.size}
isCurrentTab={currentTab === "paymentMode"}
onClick={() => setCurrentTab("paymentMode")}
/>
</div>
{currentTab === "category" && (
<div className="flex flex-col grow pl-2 pr-1 h-96 overflow-y-scroll bg-base">
Expand Down Expand Up @@ -165,6 +185,52 @@ export default function FilterBottomSheet({
))}
</div>
)}
{currentTab === "paymentMode" && (
<div className="flex flex-col grow pl-2 pr-1 h-96 overflow-y-scroll bg-base">
<div className="sticky top-0 bg-base">
<Input
type="search"
name="paymentModeSearch"
label="Search a payment mode"
value={paymentModeSearchValue}
onChangeHandler={(e) => setPaymentModeSearchValue(e.target.value)}
/>
</div>
<Spacer size={1} />
{paymentModes
.filter(
(c) =>
paymentModeSearchValue == "" ||
c.label
.toLocaleLowerCase()
.includes(paymentModeSearchValue.toLocaleLowerCase())
)
.map((paymentMode) => (
<label key={paymentMode.value}>
<input
className="form-checkbox checkbox"
type="checkbox"
name="category"
value={paymentMode.value}
checked={selectedPaymentModes.has(paymentMode.value)}
onChange={(e) => {
const newSet = new Set(selectedPaymentModes);
if (newSet.has(e.target.value)) {
newSet.delete(e.target.value);
} else {
newSet.add(e.target.value);
}
setSelectedPaymentModes(newSet);
}}
/>
<InlineSpacer />
<span className="text-primary text-lg lg:text-base">
{paymentMode.label}
</span>
</label>
))}
</div>
)}
</div>
<Spacer />
<div className="flex space-x-1">
Expand All @@ -178,6 +244,7 @@ export default function FilterBottomSheet({
selectedCategories: [],
month: defaultMonth,
selectedTypes: [],
selectedPaymentModes: [],
});
history.back();
}}
Expand All @@ -193,6 +260,7 @@ export default function FilterBottomSheet({
selectedCategories: Array.from(selectedCategories),
month: defaultMonth,
selectedTypes: Array.from(selectedTypes),
selectedPaymentModes: Array.from(selectedPaymentModes),
});
history.back();
}}
Expand Down
9 changes: 9 additions & 0 deletions app/modules/transaction/transaction.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,7 @@ export async function getTransactions(
filter?: {
types?: string[];
categories?: string[];
paymentModes?: string[];
}
) {
const currentMonth = month ? parseDate(month) : getFirstDateOfThisMonth(timezone);
Expand Down Expand Up @@ -493,6 +494,14 @@ export async function getTransactions(
}
categoryFilter.length && where.AND.push({ OR: categoryFilter });
}
if (filter.paymentModes) {
const paymentModeFilter = [];
for (const paymentMode of filter.paymentModes) {
if (isNullOrEmpty(paymentMode)) continue;
paymentModeFilter.push({ paymentMode });
}
paymentModeFilter.length && where.AND.push({ OR: paymentModeFilter });
}
}

const transactions = await prisma.transaction.findMany({
Expand Down
21 changes: 20 additions & 1 deletion app/routes/transaction/history.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,11 @@ export let loader: LoaderFunction = async ({ request }): Promise<any> => {
const reqMonth = urlSearchParams.get("month") || undefined;
const types = urlSearchParams.getAll("type") ?? undefined;
const categories = urlSearchParams.getAll("category");
const paymentModes = urlSearchParams.getAll("paymentMode");
const transactions = getTransactions(userId, timezone, reqMonth, {
types,
categories,
paymentModes,
});

const currentMonth = reqMonth ? parseDate(reqMonth) : getFirstDateOfThisMonth(timezone);
Expand All @@ -79,6 +81,7 @@ export let loader: LoaderFunction = async ({ request }): Promise<any> => {
{
types,
categories,
paymentModes,
prevMonth: formatDate_YYY_MM(sub(currentMonth, { months: 1 })),
currentMonth: formatDate_YYY_MM(currentMonth),
nextMonth: formatDate_YYY_MM(add(currentMonth, { months: 1 })),
Expand Down Expand Up @@ -109,13 +112,15 @@ export default function TransactionHistory() {
const {
types,
categories,
paymentModes,
prevMonth,
currentMonth,
nextMonth,
transactions,
}: {
types: string[];
categories: string[];
paymentModes: string[];
prevMonth: string;
currentMonth: string;
nextMonth: string;
Expand All @@ -130,7 +135,7 @@ export default function TransactionHistory() {
}, [context]);

function setNewFilter(newFilter: NewFilter) {
const { selectedCategories, month, selectedTypes } = newFilter;
const { selectedCategories, month, selectedTypes, selectedPaymentModes } = newFilter;

const form = new FormData();
form.set("month", month);
Expand All @@ -143,6 +148,10 @@ export default function TransactionHistory() {
selectedTypes.forEach((type) => form.append("type", type));
}

if (selectedPaymentModes.length) {
selectedPaymentModes.forEach((type) => form.append("paymentMode", type));
}

window.addEventListener(
"popstate",
function () {
Expand Down Expand Up @@ -179,6 +188,15 @@ export default function TransactionHistory() {
value={category}
/>
))}
{paymentModes &&
paymentModes.map((paymentMode) => (
<input
key={paymentMode}
type="hidden"
name="paymentMode"
value={paymentMode}
/>
))}
<Ripple unbounded>
<button className="p-2" type="submit" name="month" value={prevMonth}>
<PrevIcon size={24} />
Expand Down Expand Up @@ -210,6 +228,7 @@ export default function TransactionHistory() {
defaultSelectedCategories={categories}
defaultMonth={currentMonth}
defaultTypes={types}
defaultPaymentModes={paymentModes}
onFilterSet={setNewFilter}
/>
),
Expand Down

0 comments on commit 5a99c0e

Please sign in to comment.