Skip to content

Commit

Permalink
Support add and delete time entries
Browse files Browse the repository at this point in the history
  • Loading branch information
hannesschaletzky committed Dec 12, 2023
1 parent 1546ec8 commit c19e041
Show file tree
Hide file tree
Showing 7 changed files with 198 additions and 46 deletions.
3 changes: 3 additions & 0 deletions .talismanrc
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ fileignoreconfig:
checksum: 446eff0044854604c36e380d186aab48077af382668430ad518ef49b37d4fa02
- filename: SECURITY.md
checksum: b1743150cdd537be3a66f5308f887d130f0f320ab21628b63713808090a84e3f
- filename: app/routes/time_entries.$id.tsx
allowed_patterns: [password]
- filename: app/routes/login.tsx
allowed_patterns: [password]
- filename: app/components/troi.client.tsx
Expand All @@ -21,6 +23,7 @@ fileignoreconfig:
allowed_patterns: [password]
- filename: tests/unit/transformCalendarEvents.test.ts
allowed_patterns: [key]

version: ""
scopeconfig:
- scope: node
Expand Down
104 changes: 76 additions & 28 deletions app/components/TimeEntryForm.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { Project } from "~/troi/troiController";
import { useEffect, useState } from "react";
import { TroiButton } from "./TroiButton";
import { buttonBlue, buttonRed } from "~/utils/colors";

/*
function onRecurringTaskChange(event) {
Expand Down Expand Up @@ -28,33 +30,6 @@ function removeChip(phaseAndTask) {
);
}
async function handleAdd() {
values.description = description;
errors = await validateForm(values);
if (Object.keys(errors).length === 0) {
addOrUpdateClicked(values.hours, values.description);
values.hours = "";
values.description = "";
}
}
async function handleUpdate() {
values.description = description;
errors = await validateForm(values);
if (Object.keys(errors).length === 0) {
addOrUpdateClicked(values.hours, values.description);
values.hours = "";
values.description = "";
// TODO noci: do like with handleAdd
setTimeout(() => {
updateMode = false;
phases.forEach((phase) => (phase.open = false));
}, 2000);
}
}
async function pollPhaseNames(positionId, subprojectId) {
let whereClause = [];
const phaseIdsForPosition = await nocodbApi.dbViewRow.list(
Expand Down Expand Up @@ -249,6 +224,33 @@ export function TimeEntryForm({
}
}

async function handleAdd() {
values.description = description;
// errors = await validateForm(values);
if (Object.keys(errors).length === 0) {
addOrUpdateClicked(values.hours, values.description);
values.hours = "";
values.description = "";
}
}

async function handleUpdate() {
values.description = description;
// errors = await validateForm(values);

if (Object.keys(errors).length === 0) {
addOrUpdateClicked(values.hours, values.description);
values.hours = "";
values.description = "";

// TODO noci: do like with handleAdd
// setTimeout(() => {
// updateMode = false;
// phases.forEach((phase) => (phase.open = false));
// }, 2000);
}
}

return (
<div data-test="entry-form" className="my-2 flex justify-center">
<div className="block w-full rounded-lg bg-gray-100 p-4 shadow-lg">
Expand Down Expand Up @@ -310,7 +312,34 @@ export function TimeEntryForm({
placeholder="Working the work…"
/>
</div>
<div className="flex flex-row space-x-2 md:flex-col md:space-y-2"></div>
<div className="flex flex-row space-x-2 md:flex-col md:space-y-2">
{!disabled && updateMode && (
<>
<TroiButton
text={"Save"}
testId={`update-${position.id}`}
onClick={handleUpdate}
color={buttonBlue}
/>
<TroiButton
text={"Cancel"}
testId={`cancel-${position.id}`}
onClick={() => {
updateMode = false;
}}
color={buttonRed}
/>
</>
)}
{!disabled && addMode && (
<TroiButton
text={"Save"}
testId={"add-" + position.id}
onClick={handleAdd}
color={buttonBlue}
/>
)}
</div>
</div>
</div>
) : (
Expand All @@ -319,6 +348,25 @@ export function TimeEntryForm({
<br />
<p>{values.description}</p>
<br />
<TroiButton
text={"Delete"}
testId={`delete-${position.id}`}
onClick={() => {
deleteClicked?.();
}}
color={buttonRed}
/>
{!disabled && (
<TroiButton
text={"Edit"}
testId={`edit-${position.id}`}
onClick={() => {
// openPhases();
updateMode = true;
}}
color={buttonBlue}
/>
)}
</div>
)}
</div>
Expand Down
20 changes: 20 additions & 0 deletions app/components/TroiButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { ButtonColor } from "~/utils/colors";

interface Props {
text: string;
testId: string;
onClick: () => void;
color: ButtonColor;
}

export function TroiButton({ text, testId, onClick, color }: Props) {
return (
<button
onClick={onClick}
data-testid={testId}
className={`ease rounded ${color.idle} text-s px-6 py-2.5 font-medium text-white shadow-md transition duration-150 ease-in-out hover:${color.hover} hover:shadow-lg focus:${color.hover} focus:shadow-lg focus:outline-none focus:ring-0 active:${color.active} active:shadow-lg`}
>
{text}
</button>
);
}
37 changes: 34 additions & 3 deletions app/components/troi.client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { InfoBanner } from "./InfoBanner";
import { getWeekDaysFor } from "~/utils/dateUtils";
import { WeekView } from "./WeekView";
import { TroiTimeEntries } from "./TroiTimeEntries";
import { Project } from "~/troi/troiController";

interface Props {
username: string;
Expand Down Expand Up @@ -38,6 +39,34 @@ export default function Troi(props: Props) {
troiController?.getTimesAndEventsFor(selectedWeek) ?? [];
const positions = troiController?.getProjects();

async function onAddEntryClicked(
position: Project,
hours: number,
description: string,
) {
// showLoadingSpinner();
await troiController?.addEntry(
selectedDate,
position,
hours,
description,
() => {},
);
// hideLoadingSpinner();
}

// async function onUpdateEntryClicked(position, entry) {
// showLoadingSpinner();
// await troiController.updateEntry(position, entry, updateUI);
// hideLoadingSpinner();
// }

async function onDeleteEntryClicked(entry: TimeEntry, positionId: number) {
// showLoadingSpinner();
await troiController?.deleteEntry(entry, positionId, () => {});
// hideLoadingSpinner();
}

return (
<div>
{loading && <LoadingOverlay message={"Please wait..."} />}
Expand Down Expand Up @@ -67,9 +96,11 @@ export default function Troi(props: Props) {
recurringTasks={[]}
phaseTasks={[]}
entries={entriesForSelectedDate}
deleteEntry={() => {}}
updateEntry={() => {}}
addEntry={() => {}}
deleteEntry={onDeleteEntryClicked}
updateEntry={(project, entry) => {
console.log("update", entry);
}}
addEntry={onAddEntryClicked}
disabled={false}
/>
)}
Expand Down
22 changes: 22 additions & 0 deletions app/routes/time_entries.$id.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { ActionFunctionArgs } from "@remix-run/node";
import TroiApiService from "troi-library";
import { login } from "~/cookies.server";

export async function action({ request, params }: ActionFunctionArgs) {
if (!params.id) {
throw new Error("entry id required");
}

const cookieHeader = request.headers.get("Cookie");
const cookie = (await login.parse(cookieHeader)) || {};

const troi = new TroiApiService({
baseUrl: "https://digitalservice.troi.software/api/v2/rest",
clientName: "DigitalService GmbH des Bundes",
username: cookie.username,
password: cookie.password,
});

await troi.deleteTimeEntry(Number.parseInt(params.id));
return params.id;
}
35 changes: 20 additions & 15 deletions app/troi/troiController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,21 +111,26 @@ export default class TroiController {
return;
}

for (const project of this._projects ?? []) {
console.log(
"employeeId",
this._troiApi.employeeId,
"projectId",
project.id,
);
const entries = await this._troiApi.getTimeEntries(
project.id,
formatDateToYYYYMMDD(startDate),
formatDateToYYYYMMDD(endDate),
);

timeEntryCache.addEntries(project, entries);
}
await Promise.all(
this._projects?.map(async (project) => {
if (this._troiApi === undefined) {
throw new TroiApiNotInitializedError();
}
console.log(
"employeeId",
this._troiApi.employeeId,
"projectId",
project.id,
);
const entries = await this._troiApi.getTimeEntries(
project.id,
formatDateToYYYYMMDD(startDate),
formatDateToYYYYMMDD(endDate),
);

timeEntryCache.addEntries(project, entries);
}) ?? [],
);
}

async _loadCalendarEventsBetween(startDate: Date, endDate: Date) {
Expand Down
23 changes: 23 additions & 0 deletions app/utils/colors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
export interface ButtonColor {
idle: string;
hover: string;
active: string;
}

export const buttonBlue: ButtonColor = {
idle: "bg-blue-600",
hover: "bg-blue-700",
active: "bg-blue-800",
};

export const buttonRed: ButtonColor = {
idle: "bg-red-600",
hover: "bg-red-700",
active: "bg-red-800",
};

export const buttonGreen: ButtonColor = {
idle: "bg-green-600",
hover: "bg-green-700",
active: "bg-green-800",
};

0 comments on commit c19e041

Please sign in to comment.