Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
agnlez committed Nov 20, 2024
1 parent 14ee410 commit e3f3edf
Show file tree
Hide file tree
Showing 22 changed files with 1,487 additions and 140 deletions.
1 change: 1 addition & 0 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"dependencies": {
"@hookform/resolvers": "3.9.0",
"@lukemorales/query-key-factory": "1.3.4",
"@radix-ui/react-accordion": "1.2.1",
"@radix-ui/react-alert-dialog": "1.1.2",
"@radix-ui/react-avatar": "^1.1.1",
"@radix-ui/react-checkbox": "1.1.2",
Expand Down
58 changes: 58 additions & 0 deletions client/src/components/ui/accordion.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
"use client";

import * as React from "react";

import * as AccordionPrimitive from "@radix-ui/react-accordion";
import { ChevronsDownIcon } from "lucide-react";

import { cn } from "@/lib/utils";

const Accordion = AccordionPrimitive.Root;

const AccordionItem = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>
>(({ className, ...props }, ref) => (
<AccordionPrimitive.Item
ref={ref}
className={cn("border-b", className)}
{...props}
/>
));
AccordionItem.displayName = "AccordionItem";

const AccordionTrigger = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
>(({ className, children, ...props }, ref) => (
<AccordionPrimitive.Header className="flex">
<AccordionPrimitive.Trigger
ref={ref}
className={cn(
"flex flex-1 items-start justify-between py-4 text-left text-sm font-medium transition-all [&[data-state=open]>svg]:rotate-180",
className,
)}
{...props}
>
{children}
<ChevronsDownIcon className="[&[data-state=open]:hidden h-4 w-4 shrink-0 text-muted-foreground transition-transform duration-200" />
</AccordionPrimitive.Trigger>
</AccordionPrimitive.Header>
));
AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName;

const AccordionContent = React.forwardRef<
React.ElementRef<typeof AccordionPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>
>(({ className, children, ...props }, ref) => (
<AccordionPrimitive.Content
ref={ref}
className="overflow-hidden text-sm data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
{...props}
>
<div className={cn("pb-4 pt-0", className)}>{children}</div>
</AccordionPrimitive.Content>
));
AccordionContent.displayName = AccordionPrimitive.Content.displayName;

export { Accordion, AccordionItem, AccordionTrigger, AccordionContent };
92 changes: 92 additions & 0 deletions client/src/containers/projects/form/assumptions/columns.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import React from "react";

import { useFormContext } from "react-hook-form";

import { createColumnHelper } from "@tanstack/react-table";

import { formatNumber } from "@/lib/format";

import { CreateCustomProjectForm } from "@/containers/projects/form/setup";

import {
FormControl,
FormField,
FormItem,
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";

const columnHelper = createColumnHelper<{
label: string;
property: `assumption.${keyof NonNullable<CreateCustomProjectForm["assumptions"]>}`;
defaultValue: string;
unit: string;
value: string;
}>();

export const COLUMNS = () => {
const form = useFormContext<CreateCustomProjectForm>();

return [
columnHelper.accessor("label", {
header: () => <span>Cost</span>,
cell: (props) => {
return (
<Label
tooltip={{
title: props.getValue(),
// todo: update with descriptions
content: props.getValue(),
}}
>
{props.getValue()}
</Label>
);
},
}),
columnHelper.accessor("defaultValue", {
header: () => <span>Base value</span>,
cell: (props) => {
const value = props.getValue();
if (value === null || value === undefined) {
return "-";
}

if (!Number(value)) return value;

return formatNumber(Number(value));
},
}),
columnHelper.accessor("unit", {
header: () => <span>Unit</span>,
}),
columnHelper.accessor("value", {
header: () => <span>Override value</span>,
cell: (props) => {
return (
<FormField
control={form.control}
/*
// @ts-expect-error fix later */
name={props.row.original.property}
render={({ field }) => (
<FormItem>
<FormControl>
<Input
type="number"
placeholder="Insert value"
min={0}
{...field}
value={field.value as number}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
);
},
}),
];
};
152 changes: 151 additions & 1 deletion client/src/containers/projects/form/assumptions/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,153 @@
import { ChevronDownIcon, ChevronUpIcon } from "@radix-ui/react-icons";
import {
flexRender,
getCoreRowModel,
useReactTable,
} from "@tanstack/react-table";

// import { client } from "@/lib/query-client";
// import { queryKeys } from "@/lib/query-keys";
// import { cn } from "@/lib/utils";

import { COLUMNS } from "@/containers/projects/form/assumptions/columns";
import { CreateCustomProjectForm } from "@/containers/projects/form/setup";

import {
Accordion,
AccordionContent,
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";

export const NO_DATA = [];

const data: {
label: string;
// todo: this will be parsed in the client. We will only receive the key
property: `assumption.${keyof NonNullable<CreateCustomProjectForm["assumptions"]>}`;
defaultValue: string;
unit: string;
value: string;
}[] = [
{
label: "Verification frequency",
property: "assumption.verificationFrequency",
defaultValue: "5",
unit: "Years",
value: "",
},
{
label: "Discount rate",
property: "assumption.discountRate",
defaultValue: "0.05",
unit: "%",
value: "",
},
];

export default function AssumptionsProjectForm() {
return <div>AssumptionsProjectForm</div>;
// const { queryKey } = queryKeys.customProjects.assumptions;
// const { data, isSuccess } =
// client.customProjects.getDefaultAssumptions.useQuery(
// queryKey,
// {},
// {
// queryKey,
// select: (data) =>
// Object.keys(data.body.data).map((key) => ({
// label: key,
// unit: "N/A",
// defaultValue: data.body.data[key],
// value: "",
// })),
// },
// );

const table = useReactTable({
data,
columns: COLUMNS(),
getCoreRowModel: getCoreRowModel(),
});

return (
<Accordion type="single" collapsible defaultValue="assumptions">
<AccordionItem value="assumptions" className="border-b-0">
<AccordionTrigger>
<div className="flex flex-col gap-3">
<div className="flex items-baseline gap-2">
<h2 className="text-2xl font-medium">Assumptions</h2>
<span className="font-normal text-muted-foreground">
optional
</span>
</div>
<p className="font-normal text-muted-foreground">
Assumptions are applied across all projects, these can be left at
default settings or can be specified to particular values.
</p>
</div>
</AccordionTrigger>
<AccordionContent className="pb-0">
<Table>
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id} className="divide-x-0">
{headerGroup.headers.map((header) => {
return (
<TableHead
key={header.id}
className="bg-transparent font-normal"
>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext(),
)}
</TableHead>
);
})}
</TableRow>
))}
</TableHeader>
<TableBody>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => (
<TableRow
key={row.id}
className="divide-x-0 divide-y-0 border-b-0"
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id} className="py-3">
{flexRender(
cell.column.columnDef.cell,
cell.getContext(),
)}
</TableCell>
))}
</TableRow>
))
) : (
<TableRow>
<TableCell
colSpan={COLUMNS.length}
className="h-24 text-center"
>
No results.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</AccordionContent>
</AccordionItem>
</Accordion>
);
}
Loading

0 comments on commit e3f3edf

Please sign in to comment.