Enhance people

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
This commit is contained in:
Sacha Al Himdani
2025-08-12 10:29:13 +02:00
parent 6eb9a39c17
commit d65bc5f7e2
23 changed files with 430 additions and 118 deletions

View File

@@ -31,7 +31,7 @@ import { useOrganizationId } from "/hooks/useOrganizationId";
import { ControlledField } from "/components/form/ControlledField";
import { useFormWithSchema } from "/hooks/useFormWithSchema";
import z from "zod";
import { getAuditStateLabel, getAuditStateVariant, auditStates, fileSize, sprintf } from "@probo/helpers";
import { getAuditStateLabel, getAuditStateVariant, auditStates, fileSize, sprintf, formatDatetime } from "@probo/helpers";
import type { AuditGraphNodeQuery } from "/hooks/graph/__generated__/AuditGraphNodeQuery.graphql";
const updateAuditSchema = z.object({
@@ -77,11 +77,6 @@ export default function AuditDetailsPage(props: Props) {
if (!auditEntry.id) return;
try {
const formatDatetime = (dateString?: string) => {
if (!dateString) return undefined;
return `${dateString}T00:00:00Z`;
};
await updateAudit({
id: auditEntry.id,
validFrom: formatDatetime(formData.validFrom),

View File

@@ -16,7 +16,7 @@ import z from "zod";
import { useFormWithSchema } from "/hooks/useFormWithSchema";
import { ControlledField } from "/components/form/ControlledField";
import { useCreateAudit } from "/hooks/graph/AuditGraph";
import { auditStates, getAuditStateLabel } from "@probo/helpers";
import { auditStates, getAuditStateLabel, formatDatetime } from "@probo/helpers";
import { useLazyLoadQuery } from "react-relay";
import { graphql } from "relay-runtime";
import { Suspense } from "react";
@@ -74,12 +74,6 @@ export function CreateAuditDialog({
const onSubmit = handleSubmit(async (data) => {
try {
// Convert date strings to datetime format
const formatDatetime = (dateString?: string) => {
if (!dateString) return undefined;
return `${dateString}T00:00:00Z`;
};
await createAudit({
organizationId,
frameworkId: data.frameworkId,

View File

@@ -26,6 +26,14 @@ import { useOrganizationId } from "/hooks/useOrganizationId";
type People = NodeOf<PeopleGraphPaginatedFragment$data["peoples"]>;
const isContractEnded = (person: People): boolean => {
if (!person.contractEndDate) return false;
const endDate = new Date(person.contractEndDate);
const today = new Date();
today.setHours(0, 0, 0, 0);
return endDate < today;
};
export default function PeopleListPage({
queryRef,
}: {
@@ -87,9 +95,13 @@ function PeopleRow({
const organizationId = useOrganizationId();
const { __ } = useTranslate();
const deletePeople = useDeletePeople(people, connectionId);
const contractEnded = isContractEnded(people);
return (
<Tr to={`/organizations/${organizationId}/people/${people.id}/tasks`}>
<Tr
to={`/organizations/${organizationId}/people/${people.id}/tasks`}
className={contractEnded ? "opacity-50" : ""}
>
<Td>
<div className="flex gap-3 items-center">
<Avatar name={people.fullName} />

View File

@@ -1,5 +1,5 @@
import z from "zod";
import { peopleRoles } from "@probo/helpers";
import { peopleRoles, formatDatetime } from "@probo/helpers";
import { useOutletContext } from "react-router";
import type { PeopleGraphNodeQuery$data } from "/hooks/graph/__generated__/PeopleGraphNodeQuery.graphql";
import { useTranslate } from "@probo/i18n";
@@ -7,7 +7,7 @@ import { useFormWithSchema } from "/hooks/useFormWithSchema";
import { useMutationWithToasts } from "/hooks/useMutationWithToasts";
import type { PeopleGraphUpdateMutation } from "/hooks/graph/__generated__/PeopleGraphUpdateMutation.graphql";
import { updatePeopleMutation } from "/hooks/graph/PeopleGraph";
import { Button, Card, Field } from "@probo/ui";
import { Button, Card, Field, Input } from "@probo/ui";
import { EmailsField } from "/components/form/EmailsField";
const schema = z.object({
@@ -20,6 +20,8 @@ const schema = z.object({
z.array(z.string().email())
),
kind: z.enum(peopleRoles),
contractStartDate: z.string().optional().nullable(),
contractEndDate: z.string().optional().nullable(),
});
export default function PeopleProfileTab() {
@@ -35,6 +37,8 @@ export default function PeopleProfileTab() {
primaryEmailAddress: people.primaryEmailAddress,
position: people.position,
additionalEmailAddresses: [...(people.additionalEmailAddresses ?? [])],
contractStartDate: people.contractStartDate?.split('T')[0] || "",
contractEndDate: people.contractEndDate?.split('T')[0] || "",
},
});
const [mutate, isMutating] = useMutationWithToasts<PeopleGraphUpdateMutation>(
@@ -46,18 +50,19 @@ export default function PeopleProfileTab() {
);
const onSubmit = handleSubmit((data) => {
const input = {
id: people.id!,
fullName: data.fullName,
primaryEmailAddress: data.primaryEmailAddress,
position: data.position,
additionalEmailAddresses: data.additionalEmailAddresses,
kind: people.kind,
contractStartDate: formatDatetime(data.contractStartDate) ?? null,
contractEndDate: formatDatetime(data.contractEndDate) ?? null,
};
mutate({
variables: {
input: {
id: people.id!,
fullName: data.fullName,
primaryEmailAddress: data.primaryEmailAddress,
position: data.position,
additionalEmailAddresses: data.additionalEmailAddresses,
// TODO : make these field optional in the query (server side)
kind: people.kind,
},
},
variables: { input },
onCompleted: () => {
reset(data);
},
@@ -80,6 +85,12 @@ export default function PeopleProfileTab() {
type="email"
/>
<EmailsField control={control} register={register} />
<Field label={__("Contract start date")}>
<Input {...register("contractStartDate")} type="date" />
</Field>
<Field label={__("Contract end date")}>
<Input {...register("contractEndDate")} type="date" />
</Field>
</Card>
<div className="flex justify-end">
{formState.isDirty && (

View File

@@ -47,6 +47,8 @@ export default function PeopleRoleTab() {
fullName: people.fullName!,
primaryEmailAddress: people.primaryEmailAddress!,
additionalEmailAddresses: people.additionalEmailAddresses ?? [],
contractStartDate: people.contractStartDate,
contractEndDate: people.contractEndDate,
},
},
onCompleted: () => {