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
10 changes: 10 additions & 0 deletions frontend/src/external/bcanSatchel/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { Grant } from '../../../../middle-layer/types/Grant'
import { User } from '../../../../middle-layer/types/User'
import { Status } from '../../../../middle-layer/types/Status'
import { Notification } from '../../../../middle-layer/types/Notification';
import { CashflowCost } from '../../../../middle-layer/types/CashflowCost';
import { CashflowRevenue } from '../../../../middle-layer/types/CashflowRevenue';

/**
* Set whether the user is authenticated, update the user object,
Expand Down Expand Up @@ -47,6 +49,14 @@ export const fetchAllGrants = action("fetchAllGrants", (grants: Grant[]) => ({
grants,
}));

export const fetchCashflowRevenues = action("fetchCashflowRevenues", (revenues: CashflowRevenue[]) => ({
revenues,
}));

export const fetchCashflowCosts = action("fetchCashflowCosts", (costs: CashflowCost[]) => ({
costs,
}));

export const updateFilter = action("updateFilter", (status: Status | null) => ({
status,
}));
Expand Down
12 changes: 12 additions & 0 deletions frontend/src/external/bcanSatchel/mutators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import {
updateUserQuery,
updateUserSort,
removeProfilePic,
fetchCashflowRevenues,
fetchCashflowCosts,
} from './actions';
import { getAppStore, persistToSessionStorage } from './store';
import { setActiveUsers, setInactiveUsers } from './actions';
Expand Down Expand Up @@ -111,6 +113,16 @@ mutator(fetchAllGrants, (actionMessage) => {
store.allGrants = actionMessage.grants;
});

mutator(fetchCashflowCosts, (actionMessage) => {
const store = getAppStore();
store.costSources = actionMessage.costs;
});

mutator(fetchCashflowRevenues, (actionMessage) => {
const store = getAppStore();
store.revenueSources = actionMessage.revenues;
});

/**
* Modifies satchel store to get updated version of the filter
*/
Expand Down
8 changes: 4 additions & 4 deletions frontend/src/external/bcanSatchel/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ export interface AppState {
userSort: {header: keyof User, sort: "asc" | "desc" | "none"} | null;
notifications: Notification[];
userQuery: string;
revenueSource: CashflowRevenue[];
costSource: CashflowCost[];
revenueSources: CashflowRevenue[];
costSources: CashflowCost[];
}

// Define initial state
Expand All @@ -52,8 +52,8 @@ const initialState: AppState = {
sort: null,
userSort: null,
userQuery: '',
revenueSource: [],
costSource: [],
revenueSources: [],
costSources: [],
};

/**
Expand Down
24 changes: 14 additions & 10 deletions frontend/src/main-page/cash-flow/CashFlowPage.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { observer } from "mobx-react-lite";
import CashFlowCard from "./components/CashFlowCard";
import CashflowKPICard from "./components/CashflowKPICard";
import {
faDollarSign,
faArrowTrendUp,
Expand All @@ -9,33 +9,37 @@ import "../dashboard/styles/Dashboard.css";
import CashPosition from "./components/CashPosition";
import CashAnnualSettings from "./components/CashAnnualSettings";
import CashProjection from "./components/CashProjection";
import CashAdd from "./components/CashCreateLineItem";
import CashSourceList from "./components/CashSourceList";
import { ProcessCashflowData } from "./processCashflowData";
import CashCreateLineItem from "./components/CashCreateLineItem";

const CashFlowPage = observer(() => {

const { costs, revenues } = ProcessCashflowData();

return (
<div className="">
<div className="grid grid-cols-4 grid-rows-[auto_auto_1fr] gap-4">
{/* Row 1 */}
<CashFlowCard
<CashflowKPICard
text="Current Cash"
value="$50,000"
logo={faDollarSign}
className="text-blue"
/>
<CashFlowCard
<CashflowKPICard
text="Total Revenue"
value="$50,000"
logo={faArrowTrendUp}
className="text-green"
/>
<CashFlowCard
<CashflowKPICard
text="Monthly Costs"
value="$50,000"
logo={faUserGroup}
className="text-primary"
/>
<CashFlowCard
<CashflowKPICard
text="Annual Increases"
value="Salary: 4.5% | Benefits: 4%"
logo={faArrowTrendUp}
Expand All @@ -48,12 +52,12 @@ const CashFlowPage = observer(() => {
<CashAnnualSettings />

{/* Row 3 */}
<CashAdd />
<CashProjection />
<CashCreateLineItem />
<CashProjection costs={costs} revenues={revenues} />

{/* Row 4 */}
<CashSourceList type="Revenue" />
<CashSourceList type="Cost" />
<CashSourceList type="Revenue" lineItems={revenues} />
<CashSourceList type="Cost" lineItems={costs} />
</div>
</div>
);
Expand Down
11 changes: 9 additions & 2 deletions frontend/src/main-page/cash-flow/components/CashProjection.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import { CashflowCost } from "../../../../../middle-layer/types/CashflowCost";
import { CashflowRevenue } from "../../../../../middle-layer/types/CashflowRevenue";
import CashProjectionChart from "./CashProjectionChart";

export default function CashProjection() {
type ProjectionProps = {
costs: CashflowCost[];
revenues: CashflowRevenue[];
};

export default function CashProjection({costs, revenues}:ProjectionProps) {

// replace with actual data
const cards = [
Expand All @@ -15,7 +22,7 @@ export default function CashProjection() {
<div className="text-lg lg:text-xl mb-2 w-full text-left font-bold">
{"36-Month Cash Flow Projection"}
</div>
<CashProjectionChart />
<CashProjectionChart costs={costs} revenues={revenues} />
<div className="flex-wrap gap-2 items-center grid grid-cols-2 lg:grid-cols-4 mt-1">
{cards.map((c) => (
<div
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,15 @@ import {
} from "recharts";
import { observer } from "mobx-react-lite";
import "../../dashboard/styles/Dashboard.css";
import { CashflowRevenue } from "../../../../../middle-layer/types/CashflowRevenue";
import { CashflowCost } from "../../../../../middle-layer/types/CashflowCost";

const CashProjectionChart = observer(({}: {}) => {
type ChartProps = {
costs: CashflowCost[];
revenues: CashflowRevenue[];
};

const CashProjectionChart = observer(({}: ChartProps) => {

// replace with actual data, filter for 36 months
const data = [
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { CashflowCost } from "../../../../../middle-layer/types/CashflowCost";
import { CashflowRevenue } from "../../../../../middle-layer/types/CashflowRevenue";
import CashEditLineItem from "./CashEditLineItem";

type SourceProps = {
type: "Revenue" | "Cost";
lineItems: CashflowRevenue[] | CashflowCost[];
};

export default function CashSourceList({ type }: SourceProps) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ type CardProps = {
size?: "small" | "medium";
};

export default function CashFlowCard({
export default function CashflowKPICard({
text,
value,
className,
Expand Down
60 changes: 60 additions & 0 deletions frontend/src/main-page/cash-flow/processCashflowData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { useEffect } from "react";
import { getAppStore } from "../../external/bcanSatchel/store.ts";
import { fetchCashflowCosts, fetchCashflowRevenues } from "../../external/bcanSatchel/actions.ts";
import {CashflowRevenue} from "../../../../middle-layer/types/CashflowRevenue.ts";
import {CashflowCost} from "../../../../middle-layer/types/CashflowCost.ts";
import { api } from "../../api.ts";

// This has not been tested yet but the basic structure when implemented should be the same
// Mirrored format for processGrantData.ts

// fetch line items
export const fetchCosts = async () => {
try {
// Need to replace with actual endpoint
const response = await api("/cost");
if (!response.ok) {
throw new Error(`HTTP Error, Status: ${response.status}`);
}
const updatedCosts: CashflowCost[] = await response.json();
fetchCashflowCosts(updatedCosts);
} catch (error) {
console.error("Error fetching costs:", error);
}
};

export const fetchRevenues = async () => {
try {
// Need to replace with actual endpoint
const response = await api("/revenue");
if (!response.ok) {
throw new Error(`HTTP Error, Status: ${response.status}`);
}
const updatedRevenues: CashflowRevenue[] = await response.json();
fetchCashflowRevenues(updatedRevenues);
} catch (error) {
console.error("Error fetching revenues:", error);
}
};


// could contain callbacks for sorting and filtering line items
// stores state for list of costs/revenues
export const ProcessCashflowData = () => {
const {
costSources,
revenueSources
} = getAppStore();

// fetch costs on mount if empty
useEffect(() => {
if (costSources.length === 0) fetchCosts();
}, [costSources.length]);

// fetch revenues on mount if empty
useEffect(() => {
if (revenueSources.length === 0) fetchRevenues();
}, [revenueSources.length]);

return { costs: costSources, revenues: revenueSources };
};
Loading
Loading