Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .changeset/charges-fees-sheet.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
"mpesa2csv": minor
---

Add optional Charges/Fees sheet to Excel exports

- Add new export option to include a separate "Charges & Fees" sheet when exporting to Excel
- Filter and categorize all transactions containing "charge" in the details
- Display charges with date, amount, and balance information
- Include summary totals for total charges and number of charge transactions

4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# mpesa2csv

A desktop application built with Tauri, React, and TypeScript that converts M-PESA statement PDFs to CSV format.
A desktop application built with Tauri, React, and TypeScript that converts M-PESA statement PDFs to CSV/XLSX files.

## Features

- Convert M-PESA statement PDF files to CSV format
- Convert M-PESA statement PDF files to CSV/XLSX Files
- Handle password-protected PDF files
- Process the data locally - your statements never leave your computer
- Modern and user-friendly interface
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"sync-versions": "node scripts/sync-versions.js"
},
"dependencies": {
"@radix-ui/react-checkbox": "^1.3.3",
"@radix-ui/react-select": "^2.2.6",
"@radix-ui/react-slot": "^1.2.3",
"@tailwindcss/vite": "^4.1.3",
Expand Down
55 changes: 55 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "mpesa2csv"
version = "0.0.3"
description = "Convert M-PESA Statements to CSV Files"
description = "Convert M-PESA Statements to CSV/Excel Files"
authors = ["David Amunga"]
edition = "2021"

Expand Down
10 changes: 5 additions & 5 deletions src-tauri/tauri.conf.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@
"app": {
"windows": [
{
"title": "mpesa2csv - Convert M-PESA Statements to CSV",
"title": "mpesa2csv - Convert M-PESA Statements to CSV/Excel",
"width": 600,
"height": 450,
"height": 500,
"minWidth": 500,
"minHeight": 400,
"resizable": true
Expand All @@ -36,8 +36,8 @@
],
"publisher": "David Amunga",
"category": "Utility",
"shortDescription": "Convert M-PESA statements to CSV",
"longDescription": "A desktop application that converts M-PESA statement PDFs to CSV format. Process your statements locally with complete privacy.",
"shortDescription": "Convert M-PESA statements to CSV/Excel",
"longDescription": "A desktop application that converts M-PESA statement PDFs to CSV/Excel files. Process your statements locally with complete privacy.",
"copyright": "Copyright © 2025 David Amunga. All rights reserved.",
"licenseFile": "../LICENSE",
"externalBin": [],
Expand All @@ -64,7 +64,7 @@
},
"windowSize": {
"width": 660,
"height": 400
"height": 500
}
}
},
Expand Down
76 changes: 69 additions & 7 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { useState, useEffect } from "react";
import { MPesaStatement, FileStatus, ExportFormat } from "./types";
import {
MPesaStatement,
FileStatus,
ExportFormat,
ExportOptions,
} from "./types";
import { PdfService } from "./services/pdfService";
import { ExportService } from "./services/exportService";
import FileUploader from "./components/file-uploader";
Expand All @@ -15,6 +20,7 @@ import {
SelectValue,
} from "./components/ui/select";
import { Button } from "./components/ui/button";
import { Checkbox } from "./components/ui/checkbox";

function App() {
const [files, setFiles] = useState<File[]>([]);
Expand All @@ -26,6 +32,9 @@ function App() {
const [exportFormat, setExportFormat] = useState<ExportFormat>(
ExportFormat.XLSX
);
const [exportOptions, setExportOptions] = useState<ExportOptions>({
includeChargesSheet: false,
});
const [currentFileIndex, setCurrentFileIndex] = useState<number>(0);
const [isDownloading, setIsDownloading] = useState<boolean>(false);

Expand Down Expand Up @@ -119,7 +128,11 @@ function App() {
);

// Generate download link asynchronously
ExportService.createDownloadLink(combinedStatement, exportFormat)
ExportService.createDownloadLink(
combinedStatement,
exportFormat,
exportOptions
)
.then(setExportLink)
.catch(() => setExportLink(""));
setExportFileName(fileName);
Expand Down Expand Up @@ -183,7 +196,11 @@ function App() {
formatDateForFilename()
);

ExportService.createDownloadLink(combinedStatement, exportFormat)
ExportService.createDownloadLink(
combinedStatement,
exportFormat,
exportOptions
)
.then(setExportLink)
.catch(() => setExportLink(""));
setExportFileName(fileName);
Expand Down Expand Up @@ -221,7 +238,8 @@ function App() {

const arrayBuffer = await ExportService.getFileBuffer(
combinedStatement,
exportFormat
exportFormat,
exportOptions
);
const content = new Uint8Array(arrayBuffer);

Expand Down Expand Up @@ -324,7 +342,11 @@ function App() {
formatDateForFilename()
);

ExportService.createDownloadLink(combinedStatement, value)
ExportService.createDownloadLink(
combinedStatement,
value,
exportOptions
)
.then(setExportLink)
.catch(() => setExportLink(""));
setExportFileName(fileName);
Expand All @@ -345,6 +367,46 @@ function App() {
</Select>
</div>

{exportFormat === ExportFormat.XLSX && (
<div className="mb-4">
<label className="block text-sm font-medium mb-2">
Additional Sheets
</label>
<div className="space-y-2">
<label className="flex items-center space-x-2">
<Checkbox
checked={exportOptions.includeChargesSheet}
onCheckedChange={(value) => {
const newOptions = {
...exportOptions,
includeChargesSheet: Boolean(value),
};
setExportOptions(newOptions);

// Regenerate download link with new options
const combinedStatement = statements[0];
ExportService.createDownloadLink(
combinedStatement,
exportFormat,
newOptions
)
.then(setExportLink)
.catch(() => setExportLink(""));
}}
className="rounded border-gray-300 text-primary focus:ring-primary"
/>
<span className="text-sm">
Include Charges/Fees Sheet
</span>
</label>
<p className="text-xs text-muted-foreground ml-6">
Creates a separate sheet with all transaction charges
and fees
</p>
</div>
</div>
)}

<div className="flex flex-col sm:flex-row gap-3 justify-center mb-5">
<Button
onClick={handleDownload}
Expand Down Expand Up @@ -378,7 +440,7 @@ function App() {
</div>

<div className="pt-3 border-t border-border">
<p className="text-xs text-muted-foreground text-center truncate">
<p className="text-xs text-center truncate">
File: {exportFileName}
</p>
</div>
Expand All @@ -387,7 +449,7 @@ function App() {
</div>
</main>

<footer className="flex-shrink-0 text-center text-xs border-t border-border py-3 mt-4">
<footer className="flex-shrink-0 text-center text-xs border-t py-3 mt-0">
<p>
Built by{" "}
<a
Expand Down
4 changes: 2 additions & 2 deletions src/components/file-uploader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ const FileUploader: React.FC<FileUploaderProps> = ({
>
{dragActive
? "Drop your PDF files here!"
: "Convert M-PESA PDF's to CSV"}
: "Convert M-PESA PDF's to CSV/Excel"}
</h3>

<p
Expand All @@ -177,7 +177,7 @@ const FileUploader: React.FC<FileUploaderProps> = ({
>
{dragActive
? "Release to upload your M-PESA statement PDFs"
: "Convert your PDF statements to CSV format instantly. Drag & drop multiple files or click below to get started."}
: "Convert your PDF statements to CSV/Excel files instantly. Drag & drop multiple files or click below to get started."}
</p>

{!dragActive && (
Expand Down
Loading