Fix task display

Signed-off-by: Sacha Al Himdani <sacha@getprobo.com>
This commit is contained in:
Sacha Al Himdani
2026-01-05 12:58:42 +01:00
parent e077b5866f
commit 6ab5ace353
18 changed files with 216 additions and 655 deletions

View File

@@ -73,10 +73,7 @@ const createTaskSchema = z.object({
name: z.string().min(1),
description: z.string().optional().nullable(),
timeEstimate: z.string().optional().nullable(),
assignedToId: z.preprocess(
(val) => (val === "" || val == null ? undefined : val),
z.string({ required_error: "Assigned to is required" }).min(1, "Assigned to is required")
),
assignedToId: z.string().optional().nullable(),
measureId: z.preprocess(
(val) => (val === "" || val == null ? undefined : val),
z.string({ required_error: "Measure is required" }).min(1, "Measure is required")
@@ -88,7 +85,7 @@ const updateTaskSchema = z.object({
name: z.string().min(1),
description: z.string().optional().nullable(),
timeEstimate: z.string().optional().nullable(),
assignedToId: z.string().optional(),
assignedToId: z.string().optional().nullable(),
measureId: z.string().optional(),
deadline: z.string().optional().nullable(),
});
@@ -137,6 +134,7 @@ export default function TaskFormDialog(props: Props) {
description: data.description || null,
timeEstimate: data.timeEstimate || null,
deadline: formatDatetime(data.deadline) ?? null,
assignedToId: data.assignedToId ?? null,
},
},
});
@@ -149,7 +147,7 @@ export default function TaskFormDialog(props: Props) {
description: data.description || null,
timeEstimate: data.timeEstimate || null,
deadline: formatDatetime(data.deadline) ?? null,
assignedToId: data.assignedToId,
assignedToId: data.assignedToId || null,
measureId: data.measureId,
},
connections: [props.connection!],
@@ -165,7 +163,6 @@ export default function TaskFormDialog(props: Props) {
dialogRef.current?.close();
});
const showMeasure = !props.measureId && !isUpdating;
const isCreating = !isUpdating;
return (
<Dialog
@@ -198,18 +195,17 @@ export default function TaskFormDialog(props: Props) {
{/* Properties form */}
<div className="py-5 px-6 bg-subtle">
<Label>{__("Properties")}</Label>
{isCreating && (
<PropertyRow
label={__("Assigned to")}
error={formState.errors.assignedToId?.message}
>
<PeopleSelectField
name="assignedToId"
control={control}
organizationId={organizationId}
/>
</PropertyRow>
)}
<PropertyRow
label={__("Assigned to")}
error={formState.errors.assignedToId?.message}
>
<PeopleSelectField
name="assignedToId"
control={control}
organizationId={organizationId}
optional={true}
/>
</PropertyRow>
{showMeasure && (
<PropertyRow
label={__("Measure")}

View File

@@ -1,9 +1,8 @@
import { promisifyMutation } from "@probo/helpers";
import { promisifyMutation, formatDate, formatDuration } from "@probo/helpers";
import { usePageTitle } from "@probo/hooks";
import { useTranslate } from "@probo/i18n";
import {
ActionDropdown,
Avatar,
Card,
DropdownItem,
IconArrowCornerDownLeft,
@@ -40,6 +39,8 @@ type Props = {
name: string;
state: "TODO" | "DONE";
description?: string | null;
timeEstimate?: string | null;
deadline?: string | null;
measure?: {
id: string;
name: string;
@@ -128,7 +129,7 @@ export default function TasksCard({ tasks, connectionId }: Props) {
}
type TaskRowProps = {
task: ItemOf<Props["tasks"]> & TaskFormDialogFragment$key;
task: ItemOf<Props["tasks"]>;
connectionId: string;
hasAnyAction: boolean;
};
@@ -207,30 +208,48 @@ function TaskRow(props: TaskRowProps) {
<TaskStateIcon state={props.task.state} />
</button>
</div>
<div className="text-sm space-y-1">
<div className="text-sm space-y-1 flex-1">
<h2 className="font-medium">{props.task.name}</h2>
{props.task.measure && (
<p className="text-txt-secondary flex items-center gap-2">
<IconArrowCornerDownLeft className="scale-x-[-1]" size={16} />
<Link
className="hover:underline"
to={`/organizations/${organizationId}/measures/${props.task.measure?.id}`}
>
{props.task.measure?.name}
</Link>
{props.task.description && (
<p className="text-txt-secondary whitespace-pre-wrap break-words">
{props.task.description}
</p>
)}
<div className="flex flex-wrap items-center gap-3 text-txt-secondary text-xs">
{props.task.measure && (
<span className="flex items-center gap-1">
<IconArrowCornerDownLeft className="scale-x-[-1]" size={14} />
<Link
className="hover:underline"
to={`/organizations/${organizationId}/measures/${props.task.measure?.id}`}
>
{props.task.measure?.name}
</Link>
</span>
)}
{props.task.timeEstimate && (
<span>{formatDuration(props.task.timeEstimate, __)}</span>
)}
{props.task.deadline && (
<time dateTime={props.task.deadline}>
{formatDate(props.task.deadline)}
</time>
)}
</div>
</div>
</div>
{props.task.assignedTo?.fullName && (
<div className="text-sm text-txt-secondary ml-auto mr-8">
<Link
className="hover:underline"
to={`/organizations/${organizationId}/people/${props.task.assignedTo.id}`}
>
{props.task.assignedTo.fullName}
</Link>
</div>
)}
<div className="flex gap-2 items-center">
{isUpdating && <Spinner size={16} />}
{props.task.assignedTo && (
<Link
to={`/organizations/${organizationId}/people/${props.task.assignedTo?.id}`}
>
<Avatar name={props.task.assignedTo?.fullName ?? ""} />
</Link>
)}
{props.hasAnyAction && (
<ActionDropdown>
{isAuthorized("Task", "updateTask") && (

View File

@@ -1,5 +1,5 @@
/**
* @generated SignedSource<<1cc9998f8dcbb02ac977744023d372d6>>
* @generated SignedSource<<1d9eed2918f5f58de18ef4ea5f0058b7>>
* @lightSyntaxTransform
* @nogrep
*/
@@ -12,6 +12,7 @@ import { ConcreteRequest } from 'relay-runtime';
import { FragmentRefs } from "relay-runtime";
export type TaskState = "DONE" | "TODO";
export type UpdateTaskInput = {
assignedToId?: string | null | undefined;
deadline?: any | null | undefined;
description?: string | null | undefined;
name?: string | null | undefined;