# My work

> What waits on the caller across the installed blocks, from `loadMyWork`: assigned work, approvals, due reminders, mentions and SLA warnings, each linked to its record.

- Blocks: [subjects](https://bettersupabase.com/blocks/subjects.md), [work-items](https://bettersupabase.com/blocks/work-items.md), [approvals](https://bettersupabase.com/blocks/approvals.md), [reminders](https://bettersupabase.com/blocks/reminders.md), [comments](https://bettersupabase.com/blocks/comments.md)
- Kind: screen
- Data: loaded on the server through the block's client
- Page: https://bettersupabase.com/recipes/my-work

## Install the blocks

```bash
npx better-supabase add subjects work-items approvals reminders comments
```

## Web (Next.js)

Add the shadcn components:

```bash
npx shadcn add alert badge button card empty skeleton
```

From `apps/examples/nextjs/src/features/my-work/components/my-work-list.tsx` (https://github.com/ScaleDockHQ/better-supabase/blob/main/apps/examples/nextjs/src/features/my-work/components/my-work-list.tsx):

```tsx
/**
 * Render inside `<Suspense>`: everything that waits on the caller, from
 * `loadMyWork`, newest first.
 */
export async function MyWorkList() {
  const [work, t, format] = await Promise.all([
    getMyWork(),
    getExtracted("myWork"),
    getFormatter(),
  ]);

  if (work.kind === "no-organization") {
    return null;
  }

  const kindLabel = (kind: MyWorkKind): string => {
    switch (kind) {
      case "assigned": {
        return t("Assigned");
      }

      case "approval": {
        return t("Approval");
      }

      case "reminder": {
        return t("Reminder");
      }

      case "mention": {
        return t("Mention");
      }

      case "sla": {
        return t("SLA warning");
      }

      default: {
        const never: never = kind;

        return never;
      }
    }
  };

  return (
    <Card data-recipe="my-work">
      <CardHeader>
        <CardTitle>{t("Waiting on you")}</CardTitle>
        <CardDescription>
          {t(
            "Assigned work, approvals, due reminders, mentions and SLA warnings.",
          )}
        </CardDescription>
      </CardHeader>
      <CardContent>
        {work.kind === "error" ? (
          <Alert variant="destructive">
            <TriangleAlertIcon />
            <AlertTitle>{t("My work didn't load")}</AlertTitle>
            <AlertDescription>{t("Try again in a moment.")}</AlertDescription>
            <AlertAction>
              <form action={retryMyWork}>
                <Button size="sm" variant="outline" type="submit">
                  {t("Retry")}
                </Button>
              </form>
            </AlertAction>
          </Alert>
        ) : work.data.rows.length === 0 ? (
          <Empty className="border">
            <EmptyHeader>
              <EmptyMedia variant="icon">
                <InboxIcon />
              </EmptyMedia>
              <EmptyTitle>{t("Nothing waiting on you")}</EmptyTitle>
              <EmptyDescription>
                {t("Work assigned to you and requests for you show up here.")}
              </EmptyDescription>
            </EmptyHeader>
            <EmptyContent>
              <Link
                href="/work"
                className={buttonVariants({ variant: "outline", size: "sm" })}
              >
                {t("Open the work board")}
              </Link>
            </EmptyContent>
          </Empty>
        ) : (
          <ul className="flex flex-col gap-1">
            {work.data.rows.map((row) => (
              <li key={row.key} className="flex items-center gap-2 text-sm">
                <Badge variant={BADGES[row.kind]}>{kindLabel(row.kind)}</Badge>
                {row.url === null ? (
                  <span className="truncate">{row.title}</span>
                ) : (
                  <Link
                    href={row.url}
                    className="truncate font-medium underline-offset-4 transition-colors hover:underline"
                  >
                    {row.title}
                  </Link>
                )}
                {row.at === null ? null : (
                  <time
                    dateTime={new Date(row.at).toISOString()}
                    className="ml-auto shrink-0 text-xs text-muted-foreground tabular-nums"
                  >
                    {format.relativeTime(row.at)}
                  </time>
                )}
              </li>
            ))}
          </ul>
        )}
      </CardContent>
    </Card>
  );
}
```

## Prompt

Paste this into a coding agent to build the recipe in your app.

### Web

```text
Build "My work" in a React web app that uses shadcn/ui components. What waits on the caller across the installed blocks, from `loadMyWork`: assigned work, approvals, due reminders, mentions and SLA warnings, each linked to its record.

It uses the better-supabase `subjects` block (Subjects) with `work-items`, `approvals`, `reminders`, `comments`. It loads its data on the server through the block's client.

Steps:

1. If the app doesn't have the blocks yet, add the SQL with `npx better-supabase add subjects work-items approvals reminders comments` and apply the migration.
2. Add the shadcn components the recipe uses: `npx shadcn add alert badge button card empty skeleton`.
3. The recipe needs no glue: it calls the block's client on the server.
4. Write the screen. Load the data on the server with the block's client, starting from the recipe's snippet (`apps/examples/nextjs/src/features/my-work/components/my-work-list.tsx`). Adapt it to the app's existing components, styling and data loading instead of copying it as is.
5. Check the result against the recipe and the block's docs: it typechecks, it reads the same block data, and it handles the loading and empty states the snippet handles.

Read the full code first:

- Recipe: https://bettersupabase.com/recipes/my-work.md
- Block docs: https://bettersupabase.com/docs/blocks/subjects
```
