Skip to content

Commit 5d487d1

Browse files
authored
Merge pull request #31 from DavidAmunga/feat/add-charges-export-option
2 parents 9d88986 + f451204 commit 5d487d1

File tree

19 files changed

+403
-44
lines changed

19 files changed

+403
-44
lines changed

.changeset/charges-fees-sheet.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
"mpesa2csv": minor
3+
---
4+
5+
Add optional Charges/Fees sheet to Excel exports
6+
7+
- Add new export option to include a separate "Charges & Fees" sheet when exporting to Excel
8+
- Filter and categorize all transactions containing "charge" in the details
9+
- Display charges with date, amount, and balance information
10+
- Include summary totals for total charges and number of charge transactions
11+

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
# mpesa2csv
22

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

55
## Features
66

7-
- Convert M-PESA statement PDF files to CSV format
7+
- Convert M-PESA statement PDF files to CSV/XLSX Files
88
- Handle password-protected PDF files
99
- Process the data locally - your statements never leave your computer
1010
- Modern and user-friendly interface

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"sync-versions": "node scripts/sync-versions.js"
1515
},
1616
"dependencies": {
17+
"@radix-ui/react-checkbox": "^1.3.3",
1718
"@radix-ui/react-select": "^2.2.6",
1819
"@radix-ui/react-slot": "^1.2.3",
1920
"@tailwindcss/vite": "^4.1.3",

pnpm-lock.yaml

Lines changed: 55 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src-tauri/Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src-tauri/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[package]
22
name = "mpesa2csv"
33
version = "0.0.3"
4-
description = "Convert M-PESA Statements to CSV Files"
4+
description = "Convert M-PESA Statements to CSV/Excel Files"
55
authors = ["David Amunga"]
66
edition = "2021"
77

src-tauri/tauri.conf.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@
1212
"app": {
1313
"windows": [
1414
{
15-
"title": "mpesa2csv - Convert M-PESA Statements to CSV",
15+
"title": "mpesa2csv - Convert M-PESA Statements to CSV/Excel",
1616
"width": 600,
17-
"height": 450,
17+
"height": 500,
1818
"minWidth": 500,
1919
"minHeight": 400,
2020
"resizable": true
@@ -36,8 +36,8 @@
3636
],
3737
"publisher": "David Amunga",
3838
"category": "Utility",
39-
"shortDescription": "Convert M-PESA statements to CSV",
40-
"longDescription": "A desktop application that converts M-PESA statement PDFs to CSV format. Process your statements locally with complete privacy.",
39+
"shortDescription": "Convert M-PESA statements to CSV/Excel",
40+
"longDescription": "A desktop application that converts M-PESA statement PDFs to CSV/Excel files. Process your statements locally with complete privacy.",
4141
"copyright": "Copyright © 2025 David Amunga. All rights reserved.",
4242
"licenseFile": "../LICENSE",
4343
"externalBin": [],
@@ -64,7 +64,7 @@
6464
},
6565
"windowSize": {
6666
"width": 660,
67-
"height": 400
67+
"height": 500
6868
}
6969
}
7070
},

src/App.tsx

Lines changed: 69 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
import { useState, useEffect } from "react";
2-
import { MPesaStatement, FileStatus, ExportFormat } from "./types";
2+
import {
3+
MPesaStatement,
4+
FileStatus,
5+
ExportFormat,
6+
ExportOptions,
7+
} from "./types";
38
import { PdfService } from "./services/pdfService";
49
import { ExportService } from "./services/exportService";
510
import FileUploader from "./components/file-uploader";
@@ -15,6 +20,7 @@ import {
1520
SelectValue,
1621
} from "./components/ui/select";
1722
import { Button } from "./components/ui/button";
23+
import { Checkbox } from "./components/ui/checkbox";
1824

1925
function App() {
2026
const [files, setFiles] = useState<File[]>([]);
@@ -26,6 +32,9 @@ function App() {
2632
const [exportFormat, setExportFormat] = useState<ExportFormat>(
2733
ExportFormat.XLSX
2834
);
35+
const [exportOptions, setExportOptions] = useState<ExportOptions>({
36+
includeChargesSheet: false,
37+
});
2938
const [currentFileIndex, setCurrentFileIndex] = useState<number>(0);
3039
const [isDownloading, setIsDownloading] = useState<boolean>(false);
3140

@@ -119,7 +128,11 @@ function App() {
119128
);
120129

121130
// Generate download link asynchronously
122-
ExportService.createDownloadLink(combinedStatement, exportFormat)
131+
ExportService.createDownloadLink(
132+
combinedStatement,
133+
exportFormat,
134+
exportOptions
135+
)
123136
.then(setExportLink)
124137
.catch(() => setExportLink(""));
125138
setExportFileName(fileName);
@@ -183,7 +196,11 @@ function App() {
183196
formatDateForFilename()
184197
);
185198

186-
ExportService.createDownloadLink(combinedStatement, exportFormat)
199+
ExportService.createDownloadLink(
200+
combinedStatement,
201+
exportFormat,
202+
exportOptions
203+
)
187204
.then(setExportLink)
188205
.catch(() => setExportLink(""));
189206
setExportFileName(fileName);
@@ -221,7 +238,8 @@ function App() {
221238

222239
const arrayBuffer = await ExportService.getFileBuffer(
223240
combinedStatement,
224-
exportFormat
241+
exportFormat,
242+
exportOptions
225243
);
226244
const content = new Uint8Array(arrayBuffer);
227245

@@ -324,7 +342,11 @@ function App() {
324342
formatDateForFilename()
325343
);
326344

327-
ExportService.createDownloadLink(combinedStatement, value)
345+
ExportService.createDownloadLink(
346+
combinedStatement,
347+
value,
348+
exportOptions
349+
)
328350
.then(setExportLink)
329351
.catch(() => setExportLink(""));
330352
setExportFileName(fileName);
@@ -345,6 +367,46 @@ function App() {
345367
</Select>
346368
</div>
347369

370+
{exportFormat === ExportFormat.XLSX && (
371+
<div className="mb-4">
372+
<label className="block text-sm font-medium mb-2">
373+
Additional Sheets
374+
</label>
375+
<div className="space-y-2">
376+
<label className="flex items-center space-x-2">
377+
<Checkbox
378+
checked={exportOptions.includeChargesSheet}
379+
onCheckedChange={(value) => {
380+
const newOptions = {
381+
...exportOptions,
382+
includeChargesSheet: Boolean(value),
383+
};
384+
setExportOptions(newOptions);
385+
386+
// Regenerate download link with new options
387+
const combinedStatement = statements[0];
388+
ExportService.createDownloadLink(
389+
combinedStatement,
390+
exportFormat,
391+
newOptions
392+
)
393+
.then(setExportLink)
394+
.catch(() => setExportLink(""));
395+
}}
396+
className="rounded border-gray-300 text-primary focus:ring-primary"
397+
/>
398+
<span className="text-sm">
399+
Include Charges/Fees Sheet
400+
</span>
401+
</label>
402+
<p className="text-xs text-muted-foreground ml-6">
403+
Creates a separate sheet with all transaction charges
404+
and fees
405+
</p>
406+
</div>
407+
</div>
408+
)}
409+
348410
<div className="flex flex-col sm:flex-row gap-3 justify-center mb-5">
349411
<Button
350412
onClick={handleDownload}
@@ -378,7 +440,7 @@ function App() {
378440
</div>
379441

380442
<div className="pt-3 border-t border-border">
381-
<p className="text-xs text-muted-foreground text-center truncate">
443+
<p className="text-xs text-center truncate">
382444
File: {exportFileName}
383445
</p>
384446
</div>
@@ -387,7 +449,7 @@ function App() {
387449
</div>
388450
</main>
389451

390-
<footer className="flex-shrink-0 text-center text-xs border-t border-border py-3 mt-4">
452+
<footer className="flex-shrink-0 text-center text-xs border-t py-3 mt-0">
391453
<p>
392454
Built by{" "}
393455
<a

src/components/file-uploader.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ const FileUploader: React.FC<FileUploaderProps> = ({
166166
>
167167
{dragActive
168168
? "Drop your PDF files here!"
169-
: "Convert M-PESA PDF's to CSV"}
169+
: "Convert M-PESA PDF's to CSV/Excel"}
170170
</h3>
171171

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

183183
{!dragActive && (

0 commit comments

Comments
 (0)