Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

One year allocation on forecast #96

Merged
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
68 changes: 44 additions & 24 deletions frontend/src/app/components/OneYearAllocation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ interface ContributionProps {
clientName?: string;
sponsor?: string;
caseTitle?: string;
kind?: "consulting" | "handsOn" | "squad" | "internal";
hideTotals?: boolean;
}

interface WeekInfo {
Expand Down Expand Up @@ -111,6 +113,8 @@ const OneYearAllocation: React.FC<ContributionProps> = ({
clientName,
sponsor,
caseTitle,
kind,
hideTotals = false,
}) => {
type KindType = "consulting" | "handsOn" | "squad" | "internal";

Expand All @@ -130,7 +134,7 @@ const OneYearAllocation: React.FC<ContributionProps> = ({
};

// Initialize with empty string to ensure we select first non-zero value
const [selectedKind, setSelectedKind] = useState<KindType | "">("");
const [selectedKind, setSelectedKind] = useState<KindType | "">(kind || "");
const [selectedBinIndex, setSelectedBinIndex] = useState<number | null>(null);
const currentDate = new Date();
const specifiedMonth = month || currentDate.getMonth() + 1;
Expand Down Expand Up @@ -177,6 +181,14 @@ const OneYearAllocation: React.FC<ContributionProps> = ({
},
]
: []),
...(kind
? [
{
field: "Kind",
selectedValues: [kind.charAt(0).toUpperCase() + kind.slice(1)],
},
]
: []),
];

const { loading, error, data } = useQuery(ALLOCATION_QUERY, {
Expand Down Expand Up @@ -232,8 +244,8 @@ const OneYearAllocation: React.FC<ContributionProps> = ({
});
}

// Set initial selected kind to first non-zero value if not already set
if (selectedKind === "") {
// Set initial selected kind to first non-zero value if not already set and kind is not provided
if (selectedKind === "" && !kind) {
const firstNonZeroKind = Object.entries(totals).find(
([_, value]) => value > 0
)?.[0] as KindType;
Expand Down Expand Up @@ -552,7 +564,7 @@ const OneYearAllocation: React.FC<ContributionProps> = ({
const histogramData = calculateHistogram();

const renderKinds = () => {
if (!data) return null;
if (!data || kind) return null;

const totalHours =
totals.consulting + totals.handsOn + totals.squad + totals.internal;
Expand Down Expand Up @@ -637,38 +649,38 @@ const OneYearAllocation: React.FC<ContributionProps> = ({
);

return (
<div className="mt-4 mb-4">
<div className="grid grid-cols-2 md:grid-cols-5 gap-4">
<div className="mt-2 mb-2">
<div className="grid grid-cols-2 md:grid-cols-5 gap-2">
{histogramData.map((bin, index) => (
<div
key={index}
className={`p-2 rounded-lg cursor-pointer transition-all hover:ring-1 hover:ring-gray-300 relative
className={`p-1.5 rounded cursor-pointer transition-all hover:ring-1 hover:ring-gray-200 relative
${
selectedBinIndex === index
? "ring-2 ring-blue-500 hover:ring-2 hover:ring-blue-500"
? "ring-1 ring-gray-400 hover:ring-gray-400"
: ""
}`}
style={{
backgroundColor: getColorWithOpacity(
getKindColor(selectedKind as KindType),
selectedKind ? bin.opacity : 0.3
selectedKind ? bin.opacity * 0.7 : 0.2
),
}}
onClick={() =>
setSelectedBinIndex(selectedBinIndex === index ? null : index)
}
>
<div className="flex flex-col">
<span className="text-xs font-medium text-gray-600">
<span className="text-[10px] font-medium text-gray-600">
{bin.min.toFixed(1)}h - {bin.max.toFixed(1)}h
</span>
<div className="flex items-center gap-1 mt-1">
<span className="text-lg font-semibold">{bin.count}</span>
<span className="text-xs text-gray-500">occurrences</span>
<div className="flex items-center gap-0.5">
<span className="text-sm font-medium">{bin.count}</span>
<span className="text-[10px] text-gray-500">×</span>
</div>
</div>
<div className="absolute bottom-1 right-2 text-xs opacity-90">
{((bin.count / totalOccurrences) * 100).toFixed(1)}%
<div className="absolute bottom-1 right-1.5 text-[9px] text-gray-500">
{((bin.count / totalOccurrences) * 100).toFixed(0)}%
</div>
</div>
))}
Expand Down Expand Up @@ -934,14 +946,16 @@ const OneYearAllocation: React.FC<ContributionProps> = ({
))}
</tr>
))}
<tr>
<td className="text-xs text-gray-600 font-normal">Total (h)</td>
{totalHoursRow.map((cell, index) => (
<td key={index} className="text-[8px] text-gray-600 font-normal">
{Number.isInteger(cell) ? cell.toString() : cell.toFixed(1)}
</td>
))}
</tr>
{!hideTotals && (
<tr>
<td className="text-xs text-gray-600 font-normal">Total (h)</td>
{totalHoursRow.map((cell, index) => (
<td key={index} className="text-[8px] text-gray-600 font-normal">
{Number.isInteger(cell) ? cell.toString() : cell.toFixed(1)}
</td>
))}
</tr>
)}
</tbody>
</table>
</div>
Expand All @@ -950,11 +964,17 @@ const OneYearAllocation: React.FC<ContributionProps> = ({

if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {(error as ApolloError).message}</div>;

var title = "One Year Allocation"
if (kind) {
const capitalizedKind = kind.charAt(0).toUpperCase() + kind.slice(1);
title = `${title} - ${capitalizedKind}`
}

return (
<div className="mb-8">
<SectionHeader
title="One Year Allocation"
title={title}
subtitle={`${startDate.toLocaleDateString("en-US", {
month: "short",
year: "numeric",
Expand Down
27 changes: 20 additions & 7 deletions frontend/src/app/financial/revenue-forecast/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { processForecastData } from "./forecastData";
import { OtherTable } from "./OtherTable";
import { ConsultingPreTable } from "./ConsultingPreTable";
import { GraphVizDaily } from "./GraphVizDaily";
import OneYearAllocation from "@/app/components/OneYearAllocation";

export default function RevenueForecastPage() {
const [date, setDate] = useState<Date>(new Date());
Expand Down Expand Up @@ -187,11 +188,11 @@ export default function RevenueForecastPage() {
title: "Consulting",
subtitle: "By Consultant",
},
{
id: "consultingFuture",
title: "Consulting",
subtitle: "Next three months",
},
// {
// id: "consultingFuture",
// title: "Consulting",
// subtitle: "Next three months",
// },
{
id: "consultingPre",
title: "Consulting Pre",
Expand All @@ -212,6 +213,10 @@ export default function RevenueForecastPage() {
]}
/>

<div className="mt-4">
<OneYearAllocation kind="consulting" hideTotals={true} />
</div>

<ConsultingTable
title="Consulting"
tableData={forecastData.consulting}
Expand Down Expand Up @@ -242,7 +247,7 @@ export default function RevenueForecastPage() {
setNormalized={setNormalized}
/>

<ConsultingTableFuture
{/* <ConsultingTableFuture
title="Consulting"
tableData={forecastData.consulting}
tableId="consultingFuture"
Expand All @@ -256,7 +261,7 @@ export default function RevenueForecastPage() {
toggleClient={toggleClient}
setNormalized={setNormalized}
setUseHistorical={setUseHistorical}
/>
/> */}

<ConsultingPreTable
title="Consulting Pre"
Expand All @@ -268,6 +273,10 @@ export default function RevenueForecastPage() {
toggleClient={(clientSlug) => toggleClient(clientSlug, "consultingPre")}
/>

<div className="mt-4">
<OneYearAllocation kind="handsOn" hideTotals={true} />
</div>

<OtherTable
title="Hands On"
tableData={forecastData.handsOn}
Expand All @@ -279,6 +288,10 @@ export default function RevenueForecastPage() {
toggleClient={toggleClient}
/>

<div className="mt-4">
<OneYearAllocation kind="squad" hideTotals={true} />
</div>

<OtherTable
title="Squad"
tableData={forecastData.squad}
Expand Down
Loading