diff --git a/app/components/troi.client.tsx b/app/components/troi.client.tsx
index d4063e0c..c6607adb 100644
--- a/app/components/troi.client.tsx
+++ b/app/components/troi.client.tsx
@@ -12,6 +12,8 @@ import {
import moment from "moment";
import { TimeEntries, TimeEntry } from "~/troi/TimeEntry";
import { CalculationPosition } from "~/troi/CalculationPosition";
+import { useFetcher } from "@remix-run/react";
+import { convertToCacheFormat } from "~/utils/TimeEntryCache";
interface Props {
username: string;
@@ -71,21 +73,25 @@ export default function Troi(props: Props) {
selectedDate,
);
+ const fetcher = useFetcher();
+
async function onAddEntryClicked(
position: CalculationPosition,
hours: number,
description: string,
) {
- // await troiController?.addEntry(
- // selectedDate,
- // position,
- // hours,
- // description,
- // () => {}
- // );
- // troiController?.getEntriesFor(selectedDate).then((entries) => {
- // setEntriesForSelectedDate(entries);
- // });
+ fetcher.submit(
+ {
+ hours,
+ description,
+ },
+ {
+ method: "POST",
+ action: `/calculation_postions/${
+ position.id
+ }/time_entries/${convertToCacheFormat(selectedDate)}`,
+ },
+ );
}
async function onUpdateEntryClicked(
@@ -98,12 +104,7 @@ export default function Troi(props: Props) {
// });
}
- async function onDeleteEntryClicked(entry: TimeEntry, positionId: number) {
- // await troiController?.deleteEntry(entry, positionId, () => {});
- // troiController?.getEntriesFor(selectedDate).then((entries) => {
- // setEntriesForSelectedDate(entries);
- // });
- }
+ async function onDeleteEntryClicked(entry: TimeEntry, positionId: number) {}
return (
diff --git a/app/routes/calculation_postions.$calculationPositionId.time_entries.$date.tsx b/app/routes/calculation_postions.$calculationPositionId.time_entries.$date.tsx
new file mode 100644
index 00000000..1ea52ab1
--- /dev/null
+++ b/app/routes/calculation_postions.$calculationPositionId.time_entries.$date.tsx
@@ -0,0 +1,41 @@
+import { ActionFunctionArgs } from "@remix-run/node";
+import TroiApiService from "troi-library";
+import { login } from "~/cookies.server";
+import { addTimeEntry } from "~/troi/troiControllerServer";
+
+export async function action({ request, params }: ActionFunctionArgs) {
+ if (request.method !== "POST") {
+ throw new Error("MethodNotAllowed");
+ // todo (Malte Laukötter, 2023-12-15): throw a error with the correct error type (405)
+ }
+
+ if (params.calculationPositionId === undefined) {
+ throw new Error("Missing calculationPositionId");
+ }
+
+ if (params.date === undefined) {
+ throw new Error("Missing date");
+ }
+
+ const body = await request.formData();
+
+ const hours = body.get("hours");
+ if (typeof hours !== "string") {
+ throw new Error("Missing hours");
+ }
+
+ const description = body.get("description");
+ if (typeof description !== "string") {
+ throw new Error("Missing description");
+ }
+
+ addTimeEntry(
+ request,
+ parseInt(params.calculationPositionId, 10),
+ params.date,
+ parseFloat(hours),
+ description,
+ );
+
+ return;
+}
diff --git a/app/troi/troiControllerServer.ts b/app/troi/troiControllerServer.ts
index fa3a3a7b..bbf3e8e2 100644
--- a/app/troi/troiControllerServer.ts
+++ b/app/troi/troiControllerServer.ts
@@ -102,8 +102,14 @@ async function fetchCalculationPositionsAndSaveToSession(request: Request) {
clientId: clientId.toString(),
favoritesOnly: true.toString(),
},
- })) as any[]
- ).map((obj: any) => ({
+ })) as {
+ Id: number;
+ DisplayPath: string;
+ Subproject: {
+ id: number;
+ };
+ }[]
+ ).map((obj) => ({
name: obj.DisplayPath,
id: obj.Id,
subprojectId: obj.Subproject.id,
@@ -169,28 +175,37 @@ async function fetchTimeEntriesAndSaveToSession(request: Request) {
const calculationPositions = await getCalculationPositions(request);
const entries: TimeEntry[] = (
await Promise.all(
- calculationPositions.map((calcPos) =>
- troiApi.makeRequest({
- url: "/billings/hours",
- params: {
- clientId: clientId.toString(),
- employeeId: employeeId.toString(),
- calculationPositionId: calcPos.id.toString(),
- startDate: formatDateToYYYYMMDD(addDaysToDate(new Date(), -366)),
- endDate: formatDateToYYYYMMDD(addDaysToDate(new Date(), 366)),
- },
- }),
+ calculationPositions.map(
+ (calcPos) =>
+ troiApi.makeRequest({
+ url: "/billings/hours",
+ params: {
+ clientId: clientId.toString(),
+ employeeId: employeeId.toString(),
+ calculationPositionId: calcPos.id.toString(),
+ startDate: formatDateToYYYYMMDD(addDaysToDate(new Date(), -366)),
+ endDate: formatDateToYYYYMMDD(addDaysToDate(new Date(), 366)),
+ },
+ }) as Promise<{
+ id: number;
+ Date: string;
+ Quantity: string;
+ Remark: string;
+ CalculationPosition: {
+ id: number;
+ };
+ }>,
),
)
)
.flat()
- .map((e: any) => {
+ .map((entry) => {
return {
- id: e.id,
- date: e.Date,
- hours: e.Quantity,
- description: e.Remark,
- calculationPosition: e.CalculationPosition.id,
+ id: entry.id,
+ date: entry.Date,
+ hours: parseFloat(entry.Quantity),
+ description: entry.Remark,
+ calculationPosition: entry.CalculationPosition.id,
};
});
@@ -213,6 +228,50 @@ export async function getTimeEntries(request: Request): Promise {
);
}
+export async function addTimeEntry(
+ request: Request,
+ calculationPostionId: number,
+ date: string,
+ hours: number,
+ description: string,
+) {
+ const cookieHeader = request.headers.get("Cookie");
+ const session = await getSession(cookieHeader);
+
+ const troiApi = await getTroiApi(
+ session.get("username"),
+ session.get("troiPassword"),
+ await getClientId(request),
+ await getEmployeeId(request),
+ );
+
+ const result = (await troiApi.postTimeEntry(
+ calculationPostionId,
+ date,
+ hours,
+ description,
+ )) as {
+ id: number;
+ Name: string;
+ Quantity: string;
+ };
+
+ const existingEntries = session.get("troiTimeEntries");
+ if (existingEntries !== undefined) {
+ existingEntries[result.id] = {
+ id: result.id,
+ date: date,
+ hours: parseFloat(result.Quantity),
+ description: result.Name,
+ calculationPosition: calculationPostionId,
+ };
+ }
+
+ await commitSession(session);
+
+ return result;
+}
+
/**
* Return data from the session cache and revalidate cached data in the background.
*