feat(database-view): add row creation

The client only supported editing existing rows (use-update-row =
PATCH only); a freshly created table had no rows and no way to add
one. Add useCreateRow (POST /api/v1/tables/:tableId/rows, bridge
already supports it) and an 'Ajouter une ligne' button in the grid
renderer, gated by canWriteRows, with view-cache invalidation.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Corentin JOGUET 2026-05-18 14:31:50 +00:00
parent 5e0f5cf49e
commit 7a11ff4e85
2 changed files with 58 additions and 0 deletions

View file

@ -0,0 +1,43 @@
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { getBridgeClient, resolveBridgeUrl } from "../services/bridge-client";
import type { BridgeRow } from "../types/database-view.types";
import { VIEW_DATA_QUERY_KEY } from "./use-view-data";
interface UseCreateRowOptions {
tableId: string;
viewId: string;
bridgeUrl?: string | null;
}
/**
* Create a new row in the table via the bridge, then invalidate the view
* cache so the row appears.
*
* Server-first (no optimistic insert): the bridge assigns the row id, and the
* inline editor PATCHes by id afterwards fabricating a temporary id here
* would desync the first edit. An empty payload creates a blank row (Baserow
* fills defaults); callers may pass initial field values.
*/
export function useCreateRow({ tableId, viewId, bridgeUrl }: UseCreateRowOptions) {
const queryClient = useQueryClient();
const url = resolveBridgeUrl(bridgeUrl);
return useMutation<BridgeRow, Error, Record<string, unknown> | void>({
mutationFn: async (fields) => {
const client = getBridgeClient(url);
return (await (client.post(
`/api/v1/tables/${tableId}/rows`,
fields ?? {},
) as unknown)) as BridgeRow;
},
onSettled: () => {
// Reconcile with the server. The bridge also emits an SSE row.created
// event which triggers the same invalidation — idempotent.
queryClient.invalidateQueries({
queryKey: [VIEW_DATA_QUERY_KEY, viewId],
exact: false,
});
},
});
}

View file

@ -23,6 +23,7 @@ import { modals } from "@mantine/modals";
import { useQueryClient } from "@tanstack/react-query";
import { useViewData } from "../hooks/use-view-data";
import { useUpdateRow } from "../hooks/use-update-row";
import { useCreateRow } from "../hooks/use-create-row";
import { useDatabaseRealtimeUpdates } from "../hooks/use-database-realtime-updates";
import { usePermissions } from "../hooks/use-permissions";
import { useAcadenicePermissions } from "@/features/acadenice/rbac/hooks/use-acadenice-permissions";
@ -186,6 +187,7 @@ export function TableRenderer({ tableId, viewId, bridgeUrl }: TableRendererProps
const canAdminTables = acadenicePerms.hasPermission("tables:write");
const queryClient = useQueryClient();
const updateRow = useUpdateRow({ tableId, viewId, bridgeUrl });
const createRow = useCreateRow({ tableId, viewId, bridgeUrl });
// Field admin modal state.
const [fieldModalOpen, setFieldModalOpen] = useState(false);
@ -352,6 +354,19 @@ export function TableRenderer({ tableId, viewId, bridgeUrl }: TableRendererProps
</table>
</div>
{canWriteRows && (
<Button
size="xs"
variant="subtle"
leftSection={<IconPlus size={14} />}
onClick={() => createRow.mutate()}
loading={createRow.isPending}
mt="xs"
>
{t("database_view.table.add_row", "Ajouter une ligne")}
</Button>
)}
{/* Pagination — only shown when there is more than one page. */}
{(page > 1 || hasNextPage) && (
<div className={styles.pagination}>