Promote useMutation to the @probo/relay package
Extract the awaitable useMutation into @probo/relay as a createUseMutation factory that delegates feedback to an injected MutationNotifier, keeping the package free of UI and i18n dependencies. compliance-portal binds it to its Base UI toast + i18next + formatError stack and imports it by explicit path (#/lib/relay/useMutation), dropping the lone intra-app barrel; a compliance-portal-scoped no-restricted-imports rule forbids react-relay's useMutation. Bring packages/relay and packages/routes into the shared ESLint scope and fix the violations that surfaced, and deprecate the legacy withQueryRef / loaderFromQueryLoader helpers. Document the shared-hook pattern and the "index.ts for package entrypoints only" rule in the relay, hooks, and app-arborescence guides. Signed-off-by: Émile Ré <emile@probo.com>
This commit is contained in:
@@ -10,12 +10,16 @@
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@base-ui/react": "^1.6.0",
|
||||
"@probo/helpers": "1.0.0",
|
||||
"@probo/react-lazy": "1.0.0",
|
||||
"@probo/relay": "1.0.0",
|
||||
"@probo/routes": "1.0.0",
|
||||
"@probo/ui": "1.0.0",
|
||||
"clsx": "^2.1.1",
|
||||
"i18next": "^26.3.1",
|
||||
"react": "^19.2.7",
|
||||
"react-dom": "^19.2.7",
|
||||
"react-i18next": "^17.0.8",
|
||||
"react-relay": "^21.0.1",
|
||||
"react-router": "^8.0.0",
|
||||
"relay-runtime": "^21.0.1"
|
||||
|
||||
53
apps/compliance-portal/src/lib/relay/useMutation.ts
Normal file
53
apps/compliance-portal/src/lib/relay/useMutation.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
// Copyright (c) 2026 Probo Inc <hello@probo.com>.
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
// PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
import { Toast } from "@base-ui/react/toast";
|
||||
import { formatError, type GraphQLError } from "@probo/helpers";
|
||||
import { createUseMutation, type MutationNotifier } from "@probo/relay";
|
||||
import { useMemo } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
/**
|
||||
* Binds the shared awaitable useMutation (`@probo/relay`) to this app's
|
||||
* feedback stack: Base UI toasts, i18next titles, and `formatError`
|
||||
* descriptions. This is the only place those opinions are wired.
|
||||
*
|
||||
* Always import useMutation from `#/lib/relay/useMutation` — never useMutation
|
||||
* from react-relay.
|
||||
*/
|
||||
function useMutationNotifier(): MutationNotifier {
|
||||
const toast = Toast.useToastManager();
|
||||
const { t } = useTranslation();
|
||||
|
||||
return useMemo<MutationNotifier>(
|
||||
() => ({
|
||||
notifySuccess: (title) => {
|
||||
toast.add({ title, type: "success" });
|
||||
},
|
||||
notifyError: (error, title) => {
|
||||
const finalTitle = title ?? t("common.error");
|
||||
toast.add({
|
||||
title: finalTitle,
|
||||
description: formatError(finalTitle, error as GraphQLError),
|
||||
type: "error",
|
||||
});
|
||||
},
|
||||
}),
|
||||
[toast, t],
|
||||
);
|
||||
}
|
||||
|
||||
export type { MutationFeedback } from "@probo/relay";
|
||||
|
||||
export const useMutation = createUseMutation(useMutationNotifier);
|
||||
Reference in New Issue
Block a user