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
859 changes: 377 additions & 482 deletions client/src/api/services/DefaultService.ts

Large diffs are not rendered by default.

39 changes: 9 additions & 30 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, DatePicker } 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,8 +67,7 @@ export const IncomeForm: React.FC<IncomeFormProps> = ({
) => {
setFormData((prev) => ({
...prev,
[field]:
field === "amount" || field === "category_id" ? Number(value) : value,
[field]: field === "amount" ? Number(value) : value,
}));
};

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

<DatePicker
value={formData.date ? new Date(formData.date) : null}
value={formData.date ? new Date(formData.date) : new Date()}
onChange={handleDateChange}
label="Select date"
label="Date"
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.

31 changes: 14 additions & 17 deletions client/src/pages/IncomesPage.tsx
Original file line number Diff line number Diff line change
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 Down Expand Up @@ -74,20 +73,18 @@ 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">

<DatePicker
value={dateFilter.start ? new Date(dateFilter.start) : null}
onChange={handleStartDateChange}
label="Start Date"
size="medium"
/>

<DatePicker
value={dateFilter.end ? new Date(dateFilter.end) : null}
onChange={handleEndDateChange}
label="End Date"
size="medium"
/>
<DatePicker
value={dateFilter.start ? new Date(dateFilter.start) : null}
onChange={handleStartDateChange}
label="Start Date"
size="medium"
/>
<DatePicker
value={dateFilter.end ? new Date(dateFilter.end) : null}
onChange={handleEndDateChange}
label="End Date"
size="medium"
/>
</div>

<IncomeList
Expand Down Expand Up @@ -124,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
91 changes: 0 additions & 91 deletions client/src/services/IncomeService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { DefaultService } from "../api/services/DefaultService";
import { useMascotStore } from "../stores/mascotStore";
import type {
Income,
IncomeCategory,
CreateIncomeRequest,
UpdateIncomeRequest,
} from "../types/Income";
Expand All @@ -16,7 +15,6 @@ export class IncomeService {
return response as Income[];
} catch (error) {
useMascotStore.getState().setExpression("error");

console.error("Error fetching incomes:", error);
throw new Error("Failed to fetch incomes");
}
Expand All @@ -27,11 +25,9 @@ export class IncomeService {
try {
const response = await DefaultService.getIncomes1(id);
useMascotStore.getState().setExpression("success");

return response as Income;
} catch (error) {
useMascotStore.getState().setExpression("error");

console.error(`Error fetching income ${id}:`, error);
throw new Error("Failed to fetch income");
}
Expand All @@ -45,9 +41,6 @@ export class IncomeService {
amount: Number(incomeData.amount),
};

if (incomeData.date) {
requestData.date = incomeData.date;
}
const response = await DefaultService.postIncomes(requestData);
useMascotStore.getState().setExpression("success");
return response as Income;
Expand All @@ -68,11 +61,9 @@ export class IncomeService {

const response = await DefaultService.putIncomes(id, requestData);
useMascotStore.getState().setExpression("success");

return response as Income;
} catch (error) {
useMascotStore.getState().setExpression("error");

console.error(`Error updating income ${id}:`, error);
throw new Error("Failed to update income");
}
Expand All @@ -85,90 +76,8 @@ export class IncomeService {
useMascotStore.getState().setExpression("success");
} catch (error) {
useMascotStore.getState().setExpression("error");

console.error(`Error deleting income ${id}:`, error);
throw new Error("Failed to delete income");
}
}

//GET ALL income categories (custom and system)
static async getIncomeCategories() {
try {
const response = await DefaultService.getIncomesCategories();
useMascotStore.getState().setExpression("success");

return response as IncomeCategory[];
} catch (error) {
useMascotStore.getState().setExpression("error");

console.error("Error fetching income categories:", error);
throw new Error("Failed to fetch income categories");
}
}

//GET custom categories
static async getCustomIncomeCategories() {
try {
const response = await DefaultService.getIncomesCustomCategories();
useMascotStore.getState().setExpression("success");

return response as IncomeCategory[];
} catch (error) {
useMascotStore.getState().setExpression("error");

console.error("Error fetching custom income categories:", error);
throw new Error("Failed to fetch custom income categories");
}
}

//POST new category
static async createIncomeCategory(name: string) {
try {
const response = await DefaultService.postIncomesCustomCategories({
category_name: name,
});
useMascotStore.getState().setExpression("success");

return response as IncomeCategory;
} catch (error) {
useMascotStore.getState().setExpression("error");

console.error("Error creating income category:", error);
throw new Error("Failed to create income category");
}
}

//UPDATE a category
static async updateIncomeCategory(
id: string,
name: string
): Promise<IncomeCategory> {
try {
const response = await DefaultService.putIncomesCustomCategories(id, {
category_name: name,
});
useMascotStore.getState().setExpression("success");

return response as IncomeCategory;
} catch (error) {
useMascotStore.getState().setExpression("error");

console.error(`Error updating income category ${id}:`, error);
throw new Error("Failed to update income category");
}
}

//DELETE category
static async deleteIncomeCategory(id: string) {
try {
useMascotStore.getState().setExpression("success");

await DefaultService.deleteIncomesCustomCategories(id);
} catch (error) {
useMascotStore.getState().setExpression("error");

console.error(`Error deleting income category ${id}:`, error);
throw new Error("Failed to delete income category");
}
}
}
Loading