Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
7e4e9e9
style: use datepicker instead of date textfield
colombefioren Aug 26, 2025
9237f86
refactor: fix prisma models to match openapi spec
colombefioren Aug 27, 2025
a2d4062
refactor: remove unnecessary endpoints (income categories)
colombefioren Aug 27, 2025
93473c2
refactor: remove income category from service layer
colombefioren Aug 27, 2025
ed7ed6e
refactor: update to use new field names (id)
colombefioren Aug 27, 2025
aeda380
refactor: remove category references
colombefioren Aug 27, 2025
93fe19d
refactor: simplify income routes by removing category endpoints
colombefioren Aug 27, 2025
f19ddcd
refactor: remove income category from types
colombefioren Aug 27, 2025
4512003
refactor: remove income category methods from services
colombefioren Aug 27, 2025
9434623
refactor: remove category field and set default date (because of the …
colombefioren Aug 27, 2025
d3a91cc
refactor: remove category display from incomelist
colombefioren Aug 27, 2025
a471bab
feat: make 1 to be the least amount when creating an income
colombefioren Aug 27, 2025
696b554
refactor: add a dash for when source is empty
colombefioren Aug 27, 2025
16db986
feat: required true to textfield source
colombefioren Aug 27, 2025
d17e039
refactor: make the default amount to be 1 instead of 0
colombefioren Aug 27, 2025
f3fb671
fix: updated password field to be hashed_password
colombefioren Aug 27, 2025
7293c21
refactor: prisma models attributes to follow the precendent ones
colombefioren Aug 27, 2025
6f4b756
Merge branch 'dev' into refactor/income-logic
Mathieu-bot Aug 27, 2025
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
2 changes: 1 addition & 1 deletion client/src/api/core/OpenAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,4 @@ export const OpenAPI: OpenAPIConfig = {
PASSWORD: undefined,
HEADERS: undefined,
ENCODE_PATH: undefined,
};
};
83 changes: 1 addition & 82 deletions client/src/api/services/DefaultService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,86 +149,6 @@ export class DefaultService {
},
});
}
/**
* Get system income categories
* @returns any List of system income categories"
* @throws ApiError
*/
public static getIncomesCategories(): CancelablePromise<any> {
return __request(OpenAPI, {
method: "GET",
url: "/incomes/categories",
});
}
/**
* Get user's custom income categories
* @returns any List of user's custom income categories
* @throws ApiError
*/
public static getIncomesCustomCategories(): CancelablePromise<any> {
return __request(OpenAPI, {
method: "GET",
url: "/incomes/custom-categories",
});
}
/**
* Create a new custom income category
* @param requestBody
* @returns any Income category created
* @throws ApiError
*/
public static postIncomesCustomCategories(requestBody: {
category_name: string;
icon_url?: string;
}): CancelablePromise<any> {
return __request(OpenAPI, {
method: "POST",
url: "/incomes/custom-categories",
body: requestBody,
mediaType: "application/json",
});
}
/**
* Update an income category
* @param id
* @param requestBody
* @returns any Income category updated
* @throws ApiError
*/
public static putIncomesCustomCategories(
id: string,
requestBody: {
category_name: string;
icon_url?: string;
}
): CancelablePromise<any> {
return __request(OpenAPI, {
method: "PUT",
url: "/incomes/custom-categories/{id}",
path: {
id: id,
},
body: requestBody,
mediaType: "application/json",
});
}
/**
* Delete an income category
* @param id
* @returns void
* @throws ApiError
*/
public static deleteIncomesCustomCategories(
id: string
): CancelablePromise<void> {
return __request(OpenAPI, {
method: "DELETE",
url: "/incomes/custom-categories/{id}",
path: {
id: id,
},
});
}
/**
* List all incomes
* @param start
Expand Down Expand Up @@ -257,10 +177,9 @@ export class DefaultService {
*/
public static postIncomes(requestBody?: {
amount: number;
date?: string;
date: string;
source?: string;
description?: string;
category_id?: number;
}): CancelablePromise<any> {
return __request(OpenAPI, {
method: "POST",
Expand Down
51 changes: 18 additions & 33 deletions client/src/components/IncomeForm.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import React, { useState, useEffect } from "react";
import type { Income, IncomeFormData } from "../types/Income";
import { useIncomeCategories } from "../hooks/useIncomeCategories";
import { Button, TextField, Select, Dialog, Skeleton } from "../ui";
import { Button, TextField, Dialog, DatePicker } from "../ui";
import { useMascot } from "../hooks/useMascot";

interface IncomeFormProps {
Expand All @@ -17,18 +16,16 @@ export const IncomeForm: React.FC<IncomeFormProps> = ({
onCancel,
open,
}) => {
const { categories, loading: categoriesLoading } = useIncomeCategories();
const [saving, setSaving] = useState(false);
const { showSuccess, showError } = useMascot();

const [formData, setFormData] = useState<IncomeFormData>({
amount: income?.amount || 0,
amount: income?.amount || 1,
date: income?.date
? new Date(income.date).toISOString().split("T")[0]
: new Date().toISOString().split("T")[0],
source: income?.source || "",
description: income?.description || "",
category_id: income?.category_id || 1,
});

useEffect(() => {
Expand All @@ -38,15 +35,13 @@ export const IncomeForm: React.FC<IncomeFormProps> = ({
date: new Date(income.date).toISOString().split("T")[0],
source: income.source,
description: income.description || "",
category_id: income.category_id,
});
} else {
setFormData({
amount: 0,
amount: 1,
date: new Date().toISOString().split("T")[0],
source: "",
description: "",
category_id: 1,
});
}
}, [income, open]);
Expand All @@ -72,11 +67,17 @@ export const IncomeForm: React.FC<IncomeFormProps> = ({
) => {
setFormData((prev) => ({
...prev,
[field]:
field === "amount" || field === "category_id" ? Number(value) : value,
[field]: field === "amount" ? Number(value) : value,
}));
};

const handleDateChange = (date: Date | null) => {
if (date) {
const formattedDate = date.toISOString().split("T")[0];
handleChange("date", formattedDate);
}
};

return (
<Dialog
open={open}
Expand All @@ -91,41 +92,25 @@ export const IncomeForm: React.FC<IncomeFormProps> = ({
onChange={(e) => handleChange("amount", e.target.value)}
required
fullWidth
min={1}
/>

<TextField
<DatePicker
value={formData.date ? new Date(formData.date) : new Date()}
onChange={handleDateChange}
label="Date"
type="date"
value={formData.date ?? ""}
onChange={(e) => handleChange("date", e.target.value)}
fullWidth
size="medium"
required
/>

<TextField
label="Source"
value={formData.source ?? ""}
onChange={(e) => handleChange("source", e.target.value)}
fullWidth
required
/>

{categoriesLoading ? (
<Skeleton variant="rect" height={56} rounded="rounded-md" />
) : (
<div>
<label className="block mb-1 text-sm font-medium text-gray-700">
Category
</label>
<Select
value={formData.category_id ?? null}
onChange={(value) => handleChange("category_id", value)}
options={categories.map((cat) => ({
label: cat.category_name,
value: cat.category_id,
}))}
/>
</div>
)}

<TextField
label="Description"
value={formData.description || ""}
Expand Down
9 changes: 1 addition & 8 deletions client/src/components/IncomeList.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { forwardRef, useImperativeHandle } from "react";
import type { Income } from "../types/Income";
import { useIncomes } from "../hooks/useIncomes";
import { Button, Chip } from "../ui";
import { Button } from "../ui";

interface IncomeListProps {
startDate?: string;
Expand Down Expand Up @@ -65,13 +65,6 @@ export const IncomeList = forwardRef<IncomeListRef, IncomeListProps>(
<h3 className="font-medium text-lg">
{income.amount.toFixed(2)} MGA
</h3>
{income.category && (
<Chip
label={income.category.category_name}
size="small"
className="bg-blue-100 text-blue-800"
/>
)}
</div>
<p className="text-gray-600 mb-1">{income.source}</p>
{income.description && (
Expand Down
45 changes: 0 additions & 45 deletions client/src/hooks/useIncomeCategories.ts

This file was deleted.

51 changes: 27 additions & 24 deletions client/src/pages/IncomesPage.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useState, useRef } from "react";
import type { Income } from "../types/Income";
import { IncomeList } from "../components/IncomeList";
import { Button, TextField, Dialog, useToast } from "../ui";
import { Button, Dialog, useToast, DatePicker } from "../ui";
import { IncomeService } from "../services/IncomeService";
import { useNavigate } from "react-router-dom";

Expand Down Expand Up @@ -33,7 +33,6 @@ export const IncomesPage = () => {
toast.success("Income deleted successfully");
setDeleteConfirmOpen(false);
setIncomeToDelete(null);
//refresh after every operations
incomeListRef.current?.refetch();
} catch (error) {
const message =
Expand All @@ -46,6 +45,20 @@ export const IncomesPage = () => {
navigate("/incomes/new");
};

const handleStartDateChange = (date: Date | null) => {
setDateFilter((prev) => ({
...prev,
start: date ? date.toISOString().split("T")[0] : undefined,
}));
};

const handleEndDateChange = (date: Date | null) => {
setDateFilter((prev) => ({
...prev,
end: date ? date.toISOString().split("T")[0] : undefined,
}));
};

return (
<div className="p-6 max-w-4xl mx-auto relative z-2">
<div className="flex pt-20 justify-between items-center mb-6">
Expand All @@ -60,27 +73,17 @@ export const IncomesPage = () => {

<div className="bg-white rounded-lg shadow p-6 mb-6">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6">
<TextField
label="From Date"
type="date"
value={dateFilter.start || ""}
onChange={(e) =>
setDateFilter((prev) => ({
...prev,
start: e.target.value || undefined,
}))
}
<DatePicker
value={dateFilter.start ? new Date(dateFilter.start) : null}
onChange={handleStartDateChange}
label="Start Date"
size="medium"
/>
<TextField
label="To Date"
type="date"
value={dateFilter.end || ""}
onChange={(e) =>
setDateFilter((prev) => ({
...prev,
end: e.target.value || undefined,
}))
}
<DatePicker
value={dateFilter.end ? new Date(dateFilter.end) : null}
onChange={handleEndDateChange}
label="End Date"
size="medium"
/>
</div>

Expand Down Expand Up @@ -118,10 +121,10 @@ export const IncomesPage = () => {
{incomeToDelete && (
<div className="mt-3 p-3 bg-gray-50 rounded">
<p>
<strong>Amount:</strong> ${incomeToDelete.amount.toFixed(2)}
<strong>Amount: </strong>{incomeToDelete.amount.toFixed(2)} MGA
</p>
<p>
<strong>Source:</strong> {incomeToDelete.source}
<strong>Source:</strong> {incomeToDelete.source.length > 0 ? incomeToDelete.source : "-"}
</p>
<p>
<strong>Date:</strong>{" "}
Expand Down
Loading