Skip to content

Commit fc3e78c

Browse files
authored
Merge pull request #378 from Code-4-Community/372-dev---upload-cash-starting-amount-personal-benefits-increase-and-personal-salary-increase
372 dev upload cash starting amount personal benefits increase and personal salary increase
2 parents 8e23f32 + 0d0b141 commit fc3e78c

10 files changed

Lines changed: 139 additions & 17 deletions

File tree

backend/src/default-values/default-values.controller.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Body, Controller, Get, Patch, Logger, UseGuards} from '@nestjs/common';
1+
import { Body, Controller, Get, Logger, UseGuards, Put} from '@nestjs/common';
22
import { ApiBody, ApiResponse, ApiTags } from '@nestjs/swagger';
33
import { DefaultValuesService } from './default-values.service';
44
import {
@@ -45,7 +45,7 @@ export class DefaultValuesController {
4545
* @param body - UpdateDefaultValueBody containing the key of the default value to update and the new value
4646
* @returns new DefaultValuesResponse with the updated default values
4747
*/
48-
@Patch()
48+
@Put()
4949
@UseGuards(VerifyAdminRoleGuard)
5050
@ApiBearerAuth()
5151
@ApiBody({ schema: {
@@ -74,9 +74,9 @@ export class DefaultValuesController {
7474
async updateDefaultValue(
7575
@Body() body: UpdateDefaultValueBody,
7676
): Promise<DefaultValuesResponse> {
77-
this.logger.log(`PATCH /default-values - Updating default value for key: ${body.key}`);
77+
this.logger.log(`PUT /default-values - Updating default value for key: ${body.key}`);
7878
const updatedValues = await this.defaultValuesService.updateDefaultValue(body.key, body.value);
79-
this.logger.log(`PATCH /default-values - Successfully updated default value for key: ${body.key}`);
79+
this.logger.log(`PUT /default-values - Successfully updated default value for key: ${body.key}`);
8080
return updatedValues;
8181
}
8282
}

frontend/src/external/bcanSatchel/actions.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { Status } from '../../../../middle-layer/types/Status'
55
import { Notification } from '../../../../middle-layer/types/Notification';
66
import { CashflowCost } from '../../../../middle-layer/types/CashflowCost';
77
import { CashflowRevenue } from '../../../../middle-layer/types/CashflowRevenue';
8+
import { CashflowSettings } from '../../../../middle-layer/types/CashflowSettings';
89

910
/**
1011
* Set whether the user is authenticated, update the user object,
@@ -59,6 +60,10 @@ export const fetchCashflowCosts = action("fetchCashflowCosts", (costs: CashflowC
5960
costs,
6061
}));
6162

63+
export const setCashflowSettings = action("setCashflowSettings",
64+
(cashflowSettings: CashflowSettings) => ({ cashflowSettings })
65+
);
66+
6267
export const updateFilter = action("updateFilter", (status: Status | null) => ({
6368
status,
6469
}));

frontend/src/external/bcanSatchel/mutators.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
removeProfilePic,
2323
fetchCashflowRevenues,
2424
fetchCashflowCosts,
25+
setCashflowSettings
2526
} from "./actions";
2627
import { getAppStore, persistToSessionStorage } from "./store";
2728

@@ -237,3 +238,12 @@ mutator(removeProfilePic, () => {
237238

238239
persistToSessionStorage();
239240
});
241+
242+
/**
243+
* setCashflowSettings mutator
244+
*/
245+
246+
mutator(setCashflowSettings, (actionMessage) => {
247+
const store = getAppStore();
248+
store.cashflowSettings = actionMessage.cashflowSettings;
249+
});

frontend/src/external/bcanSatchel/store.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { Status } from '../../../../middle-layer/types/Status'
55
import { Notification } from '../../../../middle-layer/types/Notification'
66
import { CashflowRevenue } from '../../../../middle-layer/types/CashflowRevenue'
77
import { CashflowCost } from '../../../../middle-layer/types/CashflowCost'
8+
import { CashflowSettings } from '../../../../middle-layer/types/CashflowSettings'
89

910
export interface AppState {
1011
isAuthenticated: boolean;
@@ -29,6 +30,7 @@ export interface AppState {
2930
userQuery: string;
3031
revenueSources: CashflowRevenue[];
3132
costSources: CashflowCost[];
33+
cashflowSettings: CashflowSettings | null;
3234
}
3335

3436
// Define initial state
@@ -54,6 +56,7 @@ const initialState: AppState = {
5456
userQuery: '',
5557
revenueSources: [],
5658
costSources: [],
59+
cashflowSettings: null,
5760
};
5861

5962
/**
Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,27 @@
11
import InputField from "../../../components/InputField";
2+
import { observer } from "mobx-react-lite";
3+
import { getAppStore } from "../../../external/bcanSatchel/store";
4+
import { setCashflowSettings } from "../../../external/bcanSatchel/actions";
25

3-
export default function CashAnnualSettings() {
6+
const CashAnnualSettings = observer(() => {
7+
8+
const { cashflowSettings } = getAppStore();
9+
10+
const handleSalaryChange = (e: React.ChangeEvent<HTMLInputElement>) => {
11+
if (!cashflowSettings) return;
12+
setCashflowSettings({
13+
...cashflowSettings,
14+
salaryIncrease: e.target.valueAsNumber,
15+
});
16+
};
17+
18+
const handleBenefitsChange = (e: React.ChangeEvent<HTMLInputElement>) => {
19+
if (!cashflowSettings) return;
20+
setCashflowSettings({
21+
...cashflowSettings,
22+
benefitsIncrease: e.target.valueAsNumber,
23+
});
24+
};
425

526
return (
627
<div className="chart-container col-span-2 h-full">
@@ -12,16 +33,20 @@ export default function CashAnnualSettings() {
1233
type="number"
1334
id="salary_increase"
1435
label="Personnel Salary Increase (%)"
15-
value={"3.5"}
36+
value={cashflowSettings?.salaryIncrease ?? 0}
37+
onChange={handleSalaryChange}
1638
className=""
1739
/>
1840
<InputField
1941
type="number"
2042
id="benefits_increase"
2143
label="Personnel Benefits Increase (%)"
22-
value={"4.0"}
44+
value={cashflowSettings?.benefitsIncrease ?? 0}
45+
onChange={handleBenefitsChange}
2346
/>
2447
</div>
2548
</div>
2649
);
27-
}
50+
});
51+
52+
export default CashAnnualSettings;
Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,20 @@
11
import InputField from "../../../components/InputField";
2+
import { observer } from "mobx-react-lite";
3+
import { getAppStore } from "../../../external/bcanSatchel/store";
4+
import { setCashflowSettings } from "../../../external/bcanSatchel/actions";
5+
6+
const CashPosition = observer(() => {
7+
8+
const { cashflowSettings } = getAppStore();
9+
10+
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
11+
if (!cashflowSettings) return;
12+
setCashflowSettings({
13+
...cashflowSettings,
14+
startingCash: e.target.valueAsNumber,
15+
});
16+
};
217

3-
export default function CashPosition() {
418
return (
519
<div className="chart-container col-span-2 h-full">
620
<div className="text-lg lg:text-xl mb-2 w-full text-left font-bold">
@@ -10,8 +24,11 @@ export default function CashPosition() {
1024
type="number"
1125
id="starting_balance"
1226
label="Current Cash Balance"
13-
value={"25000"}
27+
value={cashflowSettings?.startingCash ?? 0}
28+
onChange={handleChange}
1429
/>
1530
</div>
1631
);
17-
}
32+
});
33+
34+
export default CashPosition;

frontend/src/main-page/cash-flow/processCashflowData.ts

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import { useEffect } from "react";
22
import { getAppStore } from "../../external/bcanSatchel/store.ts";
3-
import { fetchCashflowCosts, fetchCashflowRevenues } from "../../external/bcanSatchel/actions.ts";
3+
import { fetchCashflowCosts, fetchCashflowRevenues, setCashflowSettings } from "../../external/bcanSatchel/actions.ts";
44
import {CashflowRevenue} from "../../../../middle-layer/types/CashflowRevenue.ts";
55
import {CashflowCost} from "../../../../middle-layer/types/CashflowCost.ts";
6+
import {CashflowSettings} from "../../../../middle-layer/types/CashflowSettings.ts";
67
import { api } from "../../api.ts";
78

89
// This has not been tested yet but the basic structure when implemented should be the same
@@ -37,13 +38,27 @@ export const fetchRevenues = async () => {
3738
}
3839
};
3940

41+
export const fetchCashflowSettings = async () => {
42+
try {
43+
const response = await api("/default-values");
44+
if (!response.ok) {
45+
throw new Error(`HTTP Error, Status: ${response.status}`);
46+
}
47+
const settings: CashflowSettings = await response.json();
48+
setCashflowSettings(settings);
49+
} catch (error) {
50+
console.error("Error fetching cashflow settings:", error);
51+
}
52+
};
53+
4054

4155
// could contain callbacks for sorting and filtering line items
4256
// stores state for list of costs/revenues
4357
export const ProcessCashflowData = () => {
4458
const {
4559
costSources,
46-
revenueSources
60+
revenueSources,
61+
cashflowSettings
4762
} = getAppStore();
4863

4964
// fetch costs on mount if empty
@@ -56,5 +71,10 @@ export const ProcessCashflowData = () => {
5671
if (revenueSources.length === 0) fetchRevenues();
5772
}, [revenueSources.length]);
5873

59-
return { costs: costSources, revenues: revenueSources };
74+
// fetch settings on mount if null
75+
useEffect(() => {
76+
if (!cashflowSettings) fetchCashflowSettings();
77+
}, [cashflowSettings]);
78+
79+
return { costs: costSources, revenues: revenueSources, cashflowSettings };
6080
};

frontend/src/main-page/cash-flow/processCashflowDataEditSave.ts

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import {CashflowRevenue} from "../../../../middle-layer/types/CashflowRevenue.ts";
22
import {CashflowCost} from "../../../../middle-layer/types/CashflowCost.ts";
3+
import { CashflowSettings } from "../../../../middle-layer/types/CashflowSettings.ts";
34
import { api } from "../../api.ts";
4-
import { fetchCosts, fetchRevenues } from "./processCashflowData.ts";
5+
import { fetchCosts, fetchRevenues, fetchCashflowSettings } from "./processCashflowData.ts";
56

67
// This has not been tested yet but the basic structure when implemented should be the same
78
// Mirrored format for processGrantDataEditSave.ts
@@ -209,4 +210,35 @@ export const deleteCost = async (costName: string) => {
209210
);
210211
console.error("Full error:", err);
211212
}
212-
};
213+
};
214+
215+
export const saveCashflowSettings = async (settings: CashflowSettings) => {
216+
try {
217+
const updates = [
218+
{ key: "startingCash", value: settings.startingCash },
219+
{ key: "salaryIncrease", value: settings.salaryIncrease },
220+
{ key: "benefitsIncrease", value: settings.benefitsIncrease },
221+
];
222+
223+
for (const update of updates) {
224+
const response = await api("/default-values", {
225+
method: "PUT",
226+
headers: { "Content-Type": "application/json" },
227+
body: JSON.stringify(update),
228+
});
229+
if (!response.ok) {
230+
const errorData = await response.json();
231+
throw new Error(errorData.message || `Failed to update ${update.key}`);
232+
}
233+
}
234+
235+
await fetchCashflowSettings();
236+
return { success: true };
237+
} catch (error) {
238+
console.error("Error saving cashflow settings:", error);
239+
return {
240+
success: false,
241+
error: error instanceof Error ? error.message : "Server error. Please try again.",
242+
};
243+
}
244+
};

frontend/src/main-page/navbar/NavBar.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { UserStatus } from "../../../../middle-layer/types/UserStatus";
1111
import NavTab, { NavTabProps } from "./NavTab.tsx";
1212
import { faChartLine, faMoneyBill, faClipboardCheck } from "@fortawesome/free-solid-svg-icons";
1313
import { NavBarBranding } from "../../translations/general.ts";
14+
import { saveCashflowSettings } from "../cash-flow/processCashflowDataEditSave";
1415

1516
const tabs: NavTabProps[] = [
1617
{ name: "Dashboard", linkTo: "/main/dashboard", icon: faChartLine },
@@ -25,7 +26,11 @@ const NavBar: React.FC = observer(() => {
2526
const user = getAppStore().user;
2627
const isAdmin = user?.position === UserStatus.Admin;
2728

28-
const handleLogout = () => {
29+
const handleLogout = async () => {
30+
const { cashflowSettings } = getAppStore();
31+
if (cashflowSettings) {
32+
await saveCashflowSettings(cashflowSettings);
33+
}
2934
logoutUser();
3035
clearAllFilters();
3136
navigate("/login");
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export interface CashflowSettings {
2+
startingCash: number;
3+
salaryIncrease: number;
4+
benefitsIncrease: number;
5+
}

0 commit comments

Comments
 (0)