Skip to content

Commit 6e348bc

Browse files
committed
feat(Tasks): split task details into separate page (closes #1297)
1 parent 464d4f6 commit 6e348bc

22 files changed

Lines changed: 555 additions & 610 deletions

‎app/src/app/app/changelog/page.tsx‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,19 @@ export default async function Page() {
3434
</div>
3535

3636
<div className="flex flex-col gap-4 max-w-prose mt-4 lg:mt-8 mx-auto">
37+
<Day heading="12. Mai 2025">
38+
<DayItem heading="Tasks">
39+
<p>
40+
Die Details zu einem Task sind nun auf einer separaten Seiten zu
41+
finden.
42+
</p>
43+
<p>
44+
Beschreibung und Freitext-Belohnung können nun 2048 Zeichen lang
45+
sein.
46+
</p>
47+
</DayItem>
48+
</Day>
49+
3750
<Day heading="2. Mai 2025">
3851
<DayItem heading="Neu: SILC-Gehälter">
3952
<p>

‎app/src/app/app/events/[id]/page.tsx‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,7 @@ export default async function Page({ params }: Props) {
4343
const authentication = await authenticatePage("/app/events/[id]");
4444
await authentication.authorizePage("event", "read");
4545

46-
const eventId = (await params).id;
47-
const event = await getEventById(eventId);
46+
const event = await getEventById((await params).id);
4847
if (!event) notFound();
4948

5049
return (
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { authenticatePage } from "@/auth/server";
2+
import { log } from "@/logging";
3+
import { Overview } from "@/tasks/components/Overview";
4+
import { getTaskById } from "@/tasks/queries";
5+
import { type Metadata } from "next";
6+
import { notFound } from "next/navigation";
7+
import { serializeError } from "serialize-error";
8+
9+
type Params = Promise<{
10+
id: string;
11+
}>;
12+
13+
export async function generateMetadata(props: {
14+
params: Params;
15+
}): Promise<Metadata> {
16+
try {
17+
const task = await getTaskById((await props.params).id);
18+
if (!task) notFound();
19+
20+
return {
21+
title: `${task.title} - Task | S.A.M. - Sinister Incorporated`,
22+
};
23+
} catch (error) {
24+
void log.error(
25+
"Error while generating metadata for /app/tasks/[id]/page.tsx",
26+
{
27+
error: serializeError(error),
28+
},
29+
);
30+
31+
return {
32+
title: `Error | S.A.M. - Sinister Incorporated`,
33+
};
34+
}
35+
}
36+
37+
interface Props {
38+
readonly params: Params;
39+
}
40+
41+
export default async function Page({ params }: Props) {
42+
const authentication = await authenticatePage("/app/tasks/[id]");
43+
await authentication.authorizePage("task", "read");
44+
45+
const task = await getTaskById((await params).id);
46+
if (!task) notFound();
47+
48+
return (
49+
<main className="p-4 pb-20 lg:p-8">
50+
<Overview task={task} />
51+
</main>
52+
);
53+
}

‎app/src/common/components/Accordeon.tsx‎

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import clsx from "clsx";
22
import type { ComponentProps } from "react";
3-
import { FaChevronDown, FaChevronUp } from "react-icons/fa";
3+
import { FaChevronDown, FaChevronRight, FaChevronUp } from "react-icons/fa";
44

5-
interface Props extends ComponentProps<"button"> {
5+
interface AccordeonToggleProps extends ComponentProps<"button"> {
66
readonly isOpen: boolean;
77
}
88

9-
export const AccordeonToggle = (props: Props) => {
9+
export const AccordeonToggle = (props: AccordeonToggleProps) => {
1010
const { className, isOpen, ...rest } = props;
1111

1212
return (
@@ -27,3 +27,22 @@ export const AccordeonToggle = (props: Props) => {
2727
</button>
2828
);
2929
};
30+
31+
type AccordeonLinkProps = ComponentProps<"div">;
32+
33+
export const AccordeonLink = (props: AccordeonLinkProps) => {
34+
const { className, ...rest } = props;
35+
36+
return (
37+
<div
38+
title="Details öffnen"
39+
className={clsx(
40+
"flex-none p-3 flex items-center justify-center border-l border-white/10",
41+
className,
42+
)}
43+
{...rest}
44+
>
45+
<FaChevronRight className="text-sinister-red-500" />
46+
</div>
47+
);
48+
};

‎app/src/common/components/Tile.tsx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ export const Tile = ({
2121
className={clsx("rounded-primary background-secondary", className)}
2222
>
2323
<div className="flex justify-between items-center border-b border-white/5">
24-
<h2 className="font-thin text-2xl p-4 lg:px-8">{heading}</h2>
24+
<h2 className="font-thin text-2xl p-4 lg:px-8 flex-1">{heading}</h2>
2525

26-
{cta && <div className="pr-4 lg:pr-8">{cta}</div>}
26+
{cta && <div className="pr-4 lg:pr-8 flex-initial">{cta}</div>}
2727
</div>
2828

2929
<div className={clsx("p-4 lg:p-8", childrenClassName)}>{children}</div>

‎app/src/common/components/form/EditableTextarea.tsx‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,6 @@ export const EditableTextarea = ({
106106
},
107107
)}
108108
autoFocus
109-
required
110109
ref={inputRef}
111110
/>
112111

@@ -125,7 +124,9 @@ export const EditableTextarea = ({
125124
className="flex gap-2 items-center group text-left"
126125
title="Klicken, um zu bearbeiten"
127126
>
128-
<pre className="font-[inherit]">{value || "-"}</pre>
127+
<pre className="font-[inherit] whitespace-pre-wrap">
128+
{value || "-"}
129+
</pre>
129130
<FaPen className="flex-1 text-sinister-red-500 group-hover:text-sinister-red-300 text-sm" />
130131
</button>
131132
)}

‎app/src/tasks/actions/createTask.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@ const schema = z.object({
1616
assignmentLimit: z.coerce.number().min(1).optional(),
1717
assignedToIds: z.array(z.string().cuid()).optional(),
1818
title: z.string().trim().max(64),
19-
description: z.string().trim().max(512).optional(),
19+
description: z.string().trim().max(2048).optional(),
2020
expiresAt: z.coerce.date().optional(),
2121
rewardType: z.nativeEnum(TaskRewardType),
22-
rewardTypeTextValue: z.string().trim().max(128).optional(),
22+
rewardTypeTextValue: z.string().trim().max(2048).optional(),
2323
rewardTypeSilcValue: z.coerce.number().min(1).optional(),
2424
rewardTypeNewSilcValue: z.coerce.number().min(1).optional(),
2525
repeatable: z.coerce.number().min(1),

‎app/src/tasks/actions/updateTaskDescription.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { isTaskUpdatable } from "../utils/isTaskUpdatable";
1010

1111
const schema = z.object({
1212
id: z.union([z.string().cuid(), z.string().cuid2()]),
13-
description: z.string().trim().max(512),
13+
description: z.string().trim().max(2048),
1414
});
1515

1616
export const updateTaskDescription = createAuthenticatedAction(

‎app/src/tasks/actions/updateTaskRewardTypeTextValue.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { isTaskUpdatable } from "../utils/isTaskUpdatable";
1010

1111
const schema = z.object({
1212
id: z.union([z.string().cuid(), z.string().cuid2()]),
13-
rewardTypeTextValue: z.string().trim().max(128),
13+
rewardTypeTextValue: z.string().trim().max(2048),
1414
});
1515

1616
export const updateTaskRewardTypeTextValue = createAuthenticatedAction(

‎app/src/tasks/components/CancelTask.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export const CancelTask = ({ className, task }: Props) => {
3535
<AlertDialogTrigger asChild>
3636
<button
3737
disabled={isPending}
38-
className="text-sinister-red-500 hover:text-sinister-red-300 flex items-center px-2"
38+
className="text-sinister-red-500 hover:text-sinister-red-300 flex items-center px-2 h-full"
3939
title="Task abbrechen"
4040
>
4141
<TbCancel />

0 commit comments

Comments
 (0)