# ListView Page

For UI component API details, see [components.md](components.md).

## File Structure

```bash
<entity>/
  page.tsx                          # route entry — list query + table
  components/
    <entity>-table.tsx              # Table with row fragment
```

## Pattern

- Query uses connection pattern: `edges { node { ...Fragment } }`
- Two-level fragments: row fragment for items, table fragment for the connection
- `Table.Header` is always rendered — even when the list is empty
- When `edges.length === 0`, show `EmptyState` inside a `Table.Row` / `Table.Cell` with `colSpan` spanning all columns
- Columns map to screen spec's "Required Columns"
- `Layout` with title and create action button
- Query needs `context: { additionalTypenames: ['<EntityName>'] }` for cache invalidation on empty lists

## Example

```tsx
// page.tsx
const UsersPage = () => {
  const [{ data, fetching }] = useQuery({
    query: UsersQuery,
    context: useMemo(() => ({ additionalTypenames: ["User"] }), []),
  });

  return (
    <Layout>
      <Layout.Header
        title="Users"
        actions={<Button render={<Link to="create" />}>Create User</Button>}
      />
      <Layout.Column>
        <UserTable users={data?.users} />
      </Layout.Column>
    </Layout>
  );
};
```

```tsx
// components/user-table.tsx
export const UserRowFragment = graphql(`
  fragment UserRow on User {
    id
    name
    email
    status
  }
`);

export const UserTableFragment = graphql(
  `fragment UserTable on UserConnection {
    edges { node { ...UserRow } }
  }`,
  [UserRowFragment],
);

export const UserTable = ({ users }: { users: FragmentOf<typeof UserTableFragment> }) => {
  const { edges } = readFragment(UserTableFragment, users);
  return (
    <Table.Root>
      <Table.Header>
        <Table.Row>
          <Table.Head>Name</Table.Head>
          <Table.Head>Email</Table.Head>
          <Table.Head>Status</Table.Head>
        </Table.Row>
      </Table.Header>
      <Table.Body>
        {edges.length === 0 ? (
          <Table.Row>
            <Table.Cell colSpan={3}>
              <EmptyState message="No users found" />
            </Table.Cell>
          </Table.Row>
        ) : (
          edges.map(({ node }) => {
            const user = readFragment(UserRowFragment, node);
            return (
              <Table.Row key={user.id}>
                <Table.Cell>
                  <Link to={user.id}>{user.name}</Link>
                </Table.Cell>
                <Table.Cell>{user.email}</Table.Cell>
                <Table.Cell><Badge>{user.status}</Badge></Table.Cell>
              </Table.Row>
            );
          })
        )}
      </Table.Body>
    </Table.Root>
  );
};
```
