Comparing version 5.2.3 to 5.3.0
@@ -10,36 +10,33 @@ import * as React from 'react'; | ||
* | ||
* By default this component is optimistic: it does not block | ||
* rendering children when checking authentication, but this mode | ||
* can be turned off by setting `requireAuth` to true. | ||
* | ||
* You can set additional `authParams` at will if your authProvider | ||
* requires it. | ||
* | ||
* @see useAuthState | ||
* | ||
* @example | ||
* import { Admin, CustomRoutes, Authenticated } from 'react-admin'; | ||
* import { Admin, CustomRoutes, Authenticated } from 'react-admin'; | ||
* | ||
* const customRoutes = [ | ||
* <Route | ||
* path="/foo" | ||
* element={ | ||
* <Authenticated authParams={{ foo: 'bar' }}> | ||
* <Foo /> | ||
* </Authenticated> | ||
* } | ||
* /> | ||
* ]; | ||
* const App = () => ( | ||
* <Admin> | ||
* <CustomRoutes>{customRoutes}</CustomRoutes> | ||
* </Admin> | ||
* ); | ||
* const customRoutes = [ | ||
* <Route | ||
* path="/foo" | ||
* element={ | ||
* <Authenticated authParams={{ foo: 'bar' }}> | ||
* <Foo /> | ||
* </Authenticated> | ||
* } | ||
* /> | ||
* ]; | ||
* const App = () => ( | ||
* <Admin> | ||
* <CustomRoutes>{customRoutes}</CustomRoutes> | ||
* </Admin> | ||
* ); | ||
*/ | ||
export declare const Authenticated: (props: AuthenticatedProps) => React.JSX.Element | null; | ||
export declare const Authenticated: (props: AuthenticatedProps) => string | number | boolean | Iterable<React.ReactNode> | React.JSX.Element | null; | ||
export interface AuthenticatedProps { | ||
children: ReactNode; | ||
authParams?: object; | ||
loading?: ReactNode; | ||
/** | ||
* @deprecated Authenticated now never renders children when not authenticated. | ||
*/ | ||
requireAuth?: boolean; | ||
} | ||
//# sourceMappingURL=Authenticated.d.ts.map |
@@ -25,9 +25,6 @@ "use strict"; | ||
}; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.Authenticated = void 0; | ||
var React = __importStar(require("react")); | ||
var useAuthState_1 = __importDefault(require("./useAuthState")); | ||
var useAuthenticated_1 = require("./useAuthenticated"); | ||
/** | ||
@@ -40,39 +37,30 @@ * Restrict access to children to authenticated users. | ||
* | ||
* By default this component is optimistic: it does not block | ||
* rendering children when checking authentication, but this mode | ||
* can be turned off by setting `requireAuth` to true. | ||
* | ||
* You can set additional `authParams` at will if your authProvider | ||
* requires it. | ||
* | ||
* @see useAuthState | ||
* | ||
* @example | ||
* import { Admin, CustomRoutes, Authenticated } from 'react-admin'; | ||
* import { Admin, CustomRoutes, Authenticated } from 'react-admin'; | ||
* | ||
* const customRoutes = [ | ||
* <Route | ||
* path="/foo" | ||
* element={ | ||
* <Authenticated authParams={{ foo: 'bar' }}> | ||
* <Foo /> | ||
* </Authenticated> | ||
* } | ||
* /> | ||
* ]; | ||
* const App = () => ( | ||
* <Admin> | ||
* <CustomRoutes>{customRoutes}</CustomRoutes> | ||
* </Admin> | ||
* ); | ||
* const customRoutes = [ | ||
* <Route | ||
* path="/foo" | ||
* element={ | ||
* <Authenticated authParams={{ foo: 'bar' }}> | ||
* <Foo /> | ||
* </Authenticated> | ||
* } | ||
* /> | ||
* ]; | ||
* const App = () => ( | ||
* <Admin> | ||
* <CustomRoutes>{customRoutes}</CustomRoutes> | ||
* </Admin> | ||
* ); | ||
*/ | ||
var Authenticated = function (props) { | ||
var authParams = props.authParams, children = props.children, _a = props.requireAuth, requireAuth = _a === void 0 ? false : _a; | ||
// this hook will log out if the authProvider doesn't validate that the user is authenticated | ||
var _b = (0, useAuthState_1.default)(authParams, true), isPending = _b.isPending, authenticated = _b.authenticated; | ||
// in pessimistic mode don't render the children until authenticated | ||
if ((requireAuth && isPending) || !authenticated) { | ||
return null; | ||
var authParams = props.authParams, _a = props.loading, loading = _a === void 0 ? null : _a, children = props.children; | ||
// this hook will redirect to login if the user is not authenticated | ||
var isPending = (0, useAuthenticated_1.useAuthenticated)({ params: authParams }).isPending; | ||
if (isPending) { | ||
return loading; | ||
} | ||
// render the children in optimistic rendering or after authenticated | ||
return React.createElement(React.Fragment, null, children); | ||
@@ -79,0 +67,0 @@ }; |
@@ -12,8 +12,14 @@ import useAuthProvider from './useAuthProvider'; | ||
export * from './AuthContext'; | ||
export * from './CanAccess'; | ||
export * from './LogoutOnMount'; | ||
export * from './types'; | ||
export * from './useAuthenticated'; | ||
export * from './useCanAccess'; | ||
export * from './useCanAccessResources'; | ||
export * from './useCanAccessCallback'; | ||
export * from './useCheckAuth'; | ||
export * from './useGetIdentity'; | ||
export * from './useHandleAuthCallback'; | ||
export * from './useIsAuthPending'; | ||
export * from './useRequireAccess'; | ||
export * from './addRefreshAuthToAuthProvider'; | ||
@@ -20,0 +26,0 @@ export * from './addRefreshAuthToDataProvider'; |
@@ -41,10 +41,16 @@ "use strict"; | ||
__exportStar(require("./AuthContext"), exports); | ||
__exportStar(require("./CanAccess"), exports); | ||
__exportStar(require("./LogoutOnMount"), exports); | ||
__exportStar(require("./types"), exports); | ||
__exportStar(require("./useAuthenticated"), exports); | ||
__exportStar(require("./useCanAccess"), exports); | ||
__exportStar(require("./useCanAccessResources"), exports); | ||
__exportStar(require("./useCanAccessCallback"), exports); | ||
__exportStar(require("./useCheckAuth"), exports); | ||
__exportStar(require("./useGetIdentity"), exports); | ||
__exportStar(require("./useHandleAuthCallback"), exports); | ||
__exportStar(require("./useIsAuthPending"), exports); | ||
__exportStar(require("./useRequireAccess"), exports); | ||
__exportStar(require("./addRefreshAuthToAuthProvider"), exports); | ||
__exportStar(require("./addRefreshAuthToDataProvider"), exports); | ||
//# sourceMappingURL=index.js.map |
@@ -13,20 +13,26 @@ import { UseQueryOptions } from '@tanstack/react-query'; | ||
* @example | ||
* import { Admin, CustomRoutes, useAuthenticated } from 'react-admin'; | ||
* const FooPage = () => { | ||
* useAuthenticated(); | ||
* return <Foo />; | ||
* } | ||
* const customRoutes = [ | ||
* <Route path="/foo" element={<FooPage />} /> | ||
* ]; | ||
* const App = () => ( | ||
* <Admin> | ||
* <CustomRoutes>{customRoutes}</CustomRoutes> | ||
* </Admin> | ||
* ); | ||
* import { Admin, CustomRoutes, useAuthenticated } from 'react-admin'; | ||
* | ||
* const FooPage = () => { | ||
* const { isPending } = useAuthenticated(); | ||
* if (isPending) return null; | ||
* return <Foo />; | ||
* } | ||
* | ||
* const customRoutes = [ | ||
* <Route path="/foo" element={<FooPage />} /> | ||
* ]; | ||
* | ||
* const App = () => ( | ||
* <Admin> | ||
* <CustomRoutes>{customRoutes}</CustomRoutes> | ||
* </Admin> | ||
* ); | ||
*/ | ||
export declare const useAuthenticated: <ParamsType = any>({ params, ...options }?: UseAuthenticatedOptions<ParamsType>) => void; | ||
export declare const useAuthenticated: <ParamsType = any>({ params, logoutOnFailure, ...options }?: UseAuthenticatedOptions<ParamsType>) => import("./useAuthState").UseAuthStateResult<any>; | ||
export type UseAuthenticatedOptions<ParamsType> = Omit<UseQueryOptions<boolean, any> & { | ||
params?: ParamsType; | ||
}, 'queryKey' | 'queryFn'>; | ||
}, 'queryKey' | 'queryFn'> & { | ||
logoutOnFailure?: boolean; | ||
}; | ||
//# sourceMappingURL=useAuthenticated.d.ts.map |
@@ -30,20 +30,24 @@ "use strict"; | ||
* @example | ||
* import { Admin, CustomRoutes, useAuthenticated } from 'react-admin'; | ||
* const FooPage = () => { | ||
* useAuthenticated(); | ||
* return <Foo />; | ||
* } | ||
* const customRoutes = [ | ||
* <Route path="/foo" element={<FooPage />} /> | ||
* ]; | ||
* const App = () => ( | ||
* <Admin> | ||
* <CustomRoutes>{customRoutes}</CustomRoutes> | ||
* </Admin> | ||
* ); | ||
* import { Admin, CustomRoutes, useAuthenticated } from 'react-admin'; | ||
* | ||
* const FooPage = () => { | ||
* const { isPending } = useAuthenticated(); | ||
* if (isPending) return null; | ||
* return <Foo />; | ||
* } | ||
* | ||
* const customRoutes = [ | ||
* <Route path="/foo" element={<FooPage />} /> | ||
* ]; | ||
* | ||
* const App = () => ( | ||
* <Admin> | ||
* <CustomRoutes>{customRoutes}</CustomRoutes> | ||
* </Admin> | ||
* ); | ||
*/ | ||
var useAuthenticated = function (_a) { | ||
if (_a === void 0) { _a = {}; } | ||
var params = _a.params, options = __rest(_a, ["params"]); | ||
(0, useAuthState_1.default)(params !== null && params !== void 0 ? params : emptyParams, true, options); | ||
var params = _a.params, _b = _a.logoutOnFailure, logoutOnFailure = _b === void 0 ? true : _b, options = __rest(_a, ["params", "logoutOnFailure"]); | ||
return (0, useAuthState_1.default)(params !== null && params !== void 0 ? params : emptyParams, logoutOnFailure, options); | ||
}; | ||
@@ -50,0 +54,0 @@ exports.useAuthenticated = useAuthenticated; |
@@ -46,5 +46,5 @@ import { QueryObserverResult, UseQueryOptions } from '@tanstack/react-query'; | ||
export type UseAuthStateResult<ErrorType = Error> = QueryObserverResult<boolean, ErrorType> & { | ||
authenticated: boolean; | ||
authenticated?: QueryObserverResult<boolean, ErrorType>['data']; | ||
}; | ||
export default useAuthState; | ||
//# sourceMappingURL=useAuthState.d.ts.map |
@@ -105,3 +105,3 @@ "use strict"; | ||
var onSuccess = queryOptions.onSuccess, onError = queryOptions.onError, onSettled = queryOptions.onSettled, options = __rest(queryOptions, ["onSuccess", "onError", "onSettled"]); | ||
var result = (0, react_query_1.useQuery)(__assign({ queryKey: ['auth', 'checkAuth', params], queryFn: function (_a) { | ||
var queryResult = (0, react_query_1.useQuery)(__assign({ queryKey: ['auth', 'checkAuth', params], queryFn: function (_a) { | ||
var signal = _a.signal; | ||
@@ -126,40 +126,58 @@ // The authProvider is optional in react-admin | ||
var onErrorEvent = (0, util_1.useEvent)(onError !== null && onError !== void 0 ? onError : (function (error) { | ||
if (!logoutOnFailure) | ||
return; | ||
var loginUrl = (0, routing_1.removeDoubleSlashes)("".concat(basename, "/").concat(useAuthProvider_1.defaultAuthParams.loginUrl)); | ||
if (logoutOnFailure) { | ||
logout({}, error && error.redirectTo != null | ||
? error.redirectTo | ||
: loginUrl); | ||
var shouldSkipNotify = error && error.message === false; | ||
!shouldSkipNotify && | ||
notify(getErrorMessage(error, 'ra.auth.auth_check_error'), { type: 'error' }); | ||
} | ||
logout({}, error && error.redirectTo != null | ||
? error.redirectTo | ||
: loginUrl); | ||
var shouldSkipNotify = error && error.message === false; | ||
!shouldSkipNotify && | ||
notify(getErrorMessage(error, 'ra.auth.auth_check_error'), { | ||
type: 'error', | ||
}); | ||
})); | ||
(0, react_1.useEffect)(function () { | ||
if (result.data === undefined || result.isFetching) | ||
if (queryResult.data === undefined || queryResult.isFetching) | ||
return; | ||
onSuccessEvent(result.data); | ||
}, [onSuccessEvent, result.data, result.isFetching]); | ||
if (queryOptions.enabled === false) | ||
return; | ||
onSuccessEvent(queryResult.data); | ||
}, [ | ||
onSuccessEvent, | ||
queryResult.data, | ||
queryResult.isFetching, | ||
queryOptions.enabled, | ||
]); | ||
(0, react_1.useEffect)(function () { | ||
if (result.error == null || result.isFetching) | ||
if (queryResult.error == null || queryResult.isFetching) | ||
return; | ||
onErrorEvent(result.error); | ||
}, [onErrorEvent, result.error, result.isFetching]); | ||
if (queryOptions.enabled === false) | ||
return; | ||
onErrorEvent(queryResult.error); | ||
}, [ | ||
onErrorEvent, | ||
queryResult.error, | ||
queryResult.isFetching, | ||
queryOptions.enabled, | ||
]); | ||
(0, react_1.useEffect)(function () { | ||
if (result.status === 'pending' || result.isFetching) | ||
if (queryResult.status === 'pending' || queryResult.isFetching) | ||
return; | ||
onSettledEvent(result.data, result.error); | ||
if (queryOptions.enabled === false) | ||
return; | ||
onSettledEvent(queryResult.data, queryResult.error); | ||
}, [ | ||
onSettledEvent, | ||
result.data, | ||
result.error, | ||
result.status, | ||
result.isFetching, | ||
queryResult.data, | ||
queryResult.error, | ||
queryResult.status, | ||
queryResult.isFetching, | ||
queryOptions.enabled, | ||
]); | ||
return (0, react_1.useMemo)(function () { | ||
var _a; | ||
return __assign(__assign({}, result), { | ||
// If the data is undefined and the query isn't loading anymore, it means the query failed. | ||
// In that case, we set authenticated to false unless there's no authProvider. | ||
authenticated: ((_a = result.data) !== null && _a !== void 0 ? _a : result.isLoading) ? true : authProvider == null }); | ||
}, [authProvider, result]); | ||
var result = (0, react_1.useMemo)(function () { | ||
return __assign(__assign({}, queryResult), { authenticated: queryResult.error ? false : queryResult.data }); | ||
}, [queryResult]); | ||
return authProvider != null | ||
? result | ||
: noAuthProviderQueryResult; | ||
}; | ||
@@ -175,2 +193,29 @@ exports.default = useAuthState; | ||
var noop = function () { }; | ||
var noAuthProviderQueryResult = { | ||
authenticated: true, | ||
data: true, | ||
dataUpdatedAt: 0, | ||
error: null, | ||
errorUpdatedAt: 0, | ||
errorUpdateCount: 0, | ||
failureCount: 0, | ||
failureReason: null, | ||
fetchStatus: 'idle', | ||
isError: false, | ||
isInitialLoading: false, | ||
isLoading: false, | ||
isLoadingError: false, | ||
isFetched: true, | ||
isFetchedAfterMount: true, | ||
isFetching: false, | ||
isPaused: false, | ||
isPlaceholderData: false, | ||
isPending: false, | ||
isRefetchError: false, | ||
isRefetching: false, | ||
isStale: false, | ||
isSuccess: true, | ||
status: 'success', | ||
refetch: function () { return Promise.resolve(noAuthProviderQueryResult); }, | ||
}; | ||
//# sourceMappingURL=useAuthState.js.map |
@@ -8,3 +8,2 @@ "use strict"; | ||
var useAuthProvider_1 = __importDefault(require("./useAuthProvider")); | ||
var getPermissionsWithoutProvider = function () { return Promise.resolve([]); }; | ||
/** | ||
@@ -46,7 +45,8 @@ * Get a callback for calling the authProvider.getPermissions() method. | ||
// react-query requires the query to return something | ||
return authProvider | ||
? authProvider | ||
if (authProvider && authProvider.getPermissions) { | ||
return authProvider | ||
.getPermissions(params) | ||
.then(function (result) { return result !== null && result !== void 0 ? result : null; }) | ||
: getPermissionsWithoutProvider(); | ||
.then(function (result) { return result !== null && result !== void 0 ? result : null; }); | ||
} | ||
return Promise.resolve([]); | ||
}, [authProvider]); | ||
@@ -53,0 +53,0 @@ return getPermissions; |
@@ -108,3 +108,3 @@ "use strict"; | ||
var _a = queryParams !== null && queryParams !== void 0 ? queryParams : {}, onSuccess = _a.onSuccess, onError = _a.onError, onSettled = _a.onSettled, queryOptions = __rest(_a, ["onSuccess", "onError", "onSettled"]); | ||
var result = (0, react_query_1.useQuery)(__assign({ queryKey: ['auth', 'getPermissions', params], queryFn: function (_a) { | ||
var queryResult = (0, react_query_1.useQuery)(__assign({ queryKey: ['auth', 'getPermissions', params], queryFn: function (_a) { | ||
var signal = _a.signal; | ||
@@ -116,4 +116,5 @@ return __awaiter(void 0, void 0, void 0, function () { | ||
case 0: | ||
if (!authProvider) | ||
return [2 /*return*/, Promise.resolve([])]; | ||
if (!authProvider || !authProvider.getPermissions) { | ||
return [2 /*return*/, []]; | ||
} | ||
return [4 /*yield*/, authProvider.getPermissions(__assign(__assign({}, params), { signal: signal }))]; | ||
@@ -136,26 +137,56 @@ case 1: | ||
(0, react_1.useEffect)(function () { | ||
if (result.data === undefined || result.isFetching) | ||
if (queryResult.data === undefined || queryResult.isFetching) | ||
return; | ||
onSuccessEvent(result.data); | ||
}, [onSuccessEvent, result.data, result.isFetching]); | ||
onSuccessEvent(queryResult.data); | ||
}, [onSuccessEvent, queryResult.data, queryResult.isFetching]); | ||
(0, react_1.useEffect)(function () { | ||
if (result.error == null || result.isFetching) | ||
if (queryResult.error == null || queryResult.isFetching) | ||
return; | ||
onErrorEvent(result.error); | ||
}, [onErrorEvent, result.error, result.isFetching]); | ||
onErrorEvent(queryResult.error); | ||
}, [onErrorEvent, queryResult.error, queryResult.isFetching]); | ||
(0, react_1.useEffect)(function () { | ||
if (result.status === 'pending' || result.isFetching) | ||
if (queryResult.status === 'pending' || queryResult.isFetching) | ||
return; | ||
onSettledEvent(result.data, result.error); | ||
onSettledEvent(queryResult.data, queryResult.error); | ||
}, [ | ||
onSettledEvent, | ||
result.data, | ||
result.error, | ||
result.status, | ||
result.isFetching, | ||
queryResult.data, | ||
queryResult.error, | ||
queryResult.status, | ||
queryResult.isFetching, | ||
]); | ||
return (0, react_1.useMemo)(function () { return (__assign(__assign({}, result), { permissions: result.data })); }, [result]); | ||
var result = (0, react_1.useMemo)(function () { return (__assign(__assign({}, queryResult), { permissions: queryResult.data })); }, [queryResult]); | ||
return !authProvider || !authProvider.getPermissions | ||
? fakeQueryResult | ||
: result; | ||
}; | ||
exports.default = usePermissions; | ||
var noop = function () { }; | ||
var fakeQueryResult = { | ||
permissions: undefined, | ||
data: undefined, | ||
dataUpdatedAt: 0, | ||
error: null, | ||
errorUpdatedAt: 0, | ||
errorUpdateCount: 0, | ||
failureCount: 0, | ||
failureReason: null, | ||
fetchStatus: 'idle', | ||
isError: false, | ||
isInitialLoading: false, | ||
isLoading: false, | ||
isLoadingError: false, | ||
isFetched: true, | ||
isFetchedAfterMount: true, | ||
isFetching: false, | ||
isPaused: false, | ||
isPlaceholderData: false, | ||
isPending: false, | ||
isRefetchError: false, | ||
isRefetching: false, | ||
isStale: false, | ||
isSuccess: true, | ||
status: 'success', | ||
refetch: function () { return Promise.resolve(fakeQueryResult); }, | ||
}; | ||
//# sourceMappingURL=usePermissions.js.map |
@@ -0,1 +1,2 @@ | ||
import * as React from 'react'; | ||
import { ReactElement, ComponentType } from 'react'; | ||
@@ -11,2 +12,3 @@ import { Location } from 'react-router-dom'; | ||
component?: ComponentType<any>; | ||
loading?: ComponentType<any>; | ||
location?: Location; | ||
@@ -17,4 +19,4 @@ render?: WithPermissionsChildren; | ||
} | ||
declare const _default: ComponentType<WithPermissionsProps>; | ||
declare const _default: React.ComponentType<WithPermissionsProps>; | ||
export default _default; | ||
//# sourceMappingURL=WithPermissions.d.ts.map |
@@ -13,2 +13,25 @@ "use strict"; | ||
}; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
o[k2] = m[k]; | ||
})); | ||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { | ||
Object.defineProperty(o, "default", { enumerable: true, value: v }); | ||
}) : function(o, v) { | ||
o["default"] = v; | ||
}); | ||
var __importStar = (this && this.__importStar) || function (mod) { | ||
if (mod && mod.__esModule) return mod; | ||
var result = {}; | ||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); | ||
__setModuleDefault(result, mod); | ||
return result; | ||
}; | ||
var __rest = (this && this.__rest) || function (s, e) { | ||
@@ -29,2 +52,3 @@ var t = {}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var React = __importStar(require("react")); | ||
var react_1 = require("react"); | ||
@@ -70,9 +94,14 @@ var warning_1 = __importDefault(require("../util/warning")); | ||
var WithPermissions = function (props) { | ||
var authParams = props.authParams, children = props.children, render = props.render, component = props.component, staticContext = props.staticContext, rest = __rest(props, ["authParams", "children", "render", "component", "staticContext"]); | ||
var authParams = props.authParams, children = props.children, render = props.render, component = props.component, _a = props.loading, Loading = _a === void 0 ? null : _a, staticContext = props.staticContext, rest = __rest(props, ["authParams", "children", "render", "component", "loading", "staticContext"]); | ||
(0, warning_1.default)((render && children && !isEmptyChildren(children)) || | ||
(render && component) || | ||
(component && children && !isEmptyChildren(children)), 'You should only use one of the `component`, `render` and `children` props in <WithPermissions>'); | ||
(0, useAuthenticated_1.useAuthenticated)(authParams); | ||
var permissions = (0, usePermissions_1.default)(authParams).permissions; | ||
// render even though the usePermissions() call isn't finished (optimistic rendering) | ||
var isAuthenticationPending = (0, useAuthenticated_1.useAuthenticated)(authParams).isPending; | ||
var _b = (0, usePermissions_1.default)(authParams, { | ||
enabled: !isAuthenticationPending, | ||
}), permissions = _b.permissions, isPendingPermissions = _b.isPending; | ||
// We must check both pending states here as if the authProvider does not implement getPermissions, isPendingPermissions will always be false | ||
if (isAuthenticationPending || isPendingPermissions) { | ||
return Loading ? React.createElement(Loading, null) : null; | ||
} | ||
if (component) { | ||
@@ -79,0 +108,0 @@ return (0, react_1.createElement)(component, __assign({ permissions: permissions }, rest)); |
@@ -31,2 +31,3 @@ "use strict"; | ||
var core_1 = require("../../core"); | ||
var i18n_1 = require("../../i18n"); | ||
/** | ||
@@ -83,15 +84,21 @@ * Prepare a set of callbacks for a delete button guarded by confirmation dialog | ||
var useDeleteWithConfirmController = function (props) { | ||
var record = props.record, _a = props.redirect, redirectTo = _a === void 0 ? 'list' : _a, mutationMode = props.mutationMode, onClick = props.onClick, _b = props.mutationOptions, mutationOptions = _b === void 0 ? {} : _b, _c = props.successMessage, successMessage = _c === void 0 ? 'ra.notification.deleted' : _c; | ||
var record = props.record, _a = props.redirect, redirectTo = _a === void 0 ? 'list' : _a, mutationMode = props.mutationMode, onClick = props.onClick, _b = props.mutationOptions, mutationOptions = _b === void 0 ? {} : _b, successMessage = props.successMessage; | ||
var mutationMeta = mutationOptions.meta, otherMutationOptions = __rest(mutationOptions, ["meta"]); | ||
var resource = (0, core_1.useResourceContext)(props); | ||
var _d = (0, react_1.useState)(false), open = _d[0], setOpen = _d[1]; | ||
var _c = (0, react_1.useState)(false), open = _c[0], setOpen = _c[1]; | ||
var notify = (0, notification_1.useNotify)(); | ||
var unselect = (0, controller_1.useUnselect)(resource); | ||
var redirect = (0, routing_1.useRedirect)(); | ||
var _e = (0, dataProvider_1.useDelete)(resource, undefined, { | ||
var translate = (0, i18n_1.useTranslate)(); | ||
var _d = (0, dataProvider_1.useDelete)(resource, undefined, { | ||
onSuccess: function () { | ||
setOpen(false); | ||
notify(successMessage, { | ||
notify(successMessage !== null && successMessage !== void 0 ? successMessage : "resources.".concat(resource, ".notifications.deleted"), { | ||
type: 'info', | ||
messageArgs: { smart_count: 1 }, | ||
messageArgs: { | ||
smart_count: 1, | ||
_: translate('ra.notification.deleted', { | ||
smart_count: 1, | ||
}), | ||
}, | ||
undoable: mutationMode === 'undoable', | ||
@@ -117,3 +124,3 @@ }); | ||
}, | ||
}), deleteOne = _e[0], isPending = _e[1].isPending; | ||
}), deleteOne = _d[0], isPending = _d[1].isPending; | ||
var handleDialogOpen = function (e) { | ||
@@ -120,0 +127,0 @@ setOpen(true); |
@@ -31,2 +31,3 @@ "use strict"; | ||
var core_1 = require("../../core"); | ||
var i18n_1 = require("../../i18n"); | ||
/** | ||
@@ -68,3 +69,3 @@ * Prepare callback for a Delete button with undo support | ||
var useDeleteWithUndoController = function (props) { | ||
var record = props.record, _a = props.redirect, redirectTo = _a === void 0 ? 'list' : _a, onClick = props.onClick, _b = props.mutationOptions, mutationOptions = _b === void 0 ? {} : _b, _c = props.successMessage, successMessage = _c === void 0 ? 'ra.notification.deleted' : _c; | ||
var record = props.record, _a = props.redirect, redirectTo = _a === void 0 ? 'list' : _a, onClick = props.onClick, _b = props.mutationOptions, mutationOptions = _b === void 0 ? {} : _b, successMessage = props.successMessage; | ||
var mutationMeta = mutationOptions.meta, otherMutationOptions = __rest(mutationOptions, ["meta"]); | ||
@@ -75,7 +76,13 @@ var resource = (0, core_1.useResourceContext)(props); | ||
var redirect = (0, routing_1.useRedirect)(); | ||
var _d = (0, dataProvider_1.useDelete)(resource, undefined, { | ||
var translate = (0, i18n_1.useTranslate)(); | ||
var _c = (0, dataProvider_1.useDelete)(resource, undefined, { | ||
onSuccess: function () { | ||
notify(successMessage, { | ||
notify(successMessage !== null && successMessage !== void 0 ? successMessage : "resources.".concat(resource, ".notifications.deleted"), { | ||
type: 'info', | ||
messageArgs: { smart_count: 1 }, | ||
messageArgs: { | ||
smart_count: 1, | ||
_: translate('ra.notification.deleted', { | ||
smart_count: 1, | ||
}), | ||
}, | ||
undoable: true, | ||
@@ -100,3 +107,3 @@ }); | ||
}, | ||
}), deleteOne = _d[0], isPending = _d[1].isPending; | ||
}), deleteOne = _c[0], isPending = _c[1].isPending; | ||
var handleDelete = (0, react_1.useCallback)(function (event) { | ||
@@ -103,0 +110,0 @@ event.stopPropagation(); |
@@ -36,5 +36,9 @@ import * as React from 'react'; | ||
id: Identifier; | ||
}>({ children, ...props }: CreateControllerProps<RecordType, Error, ResultRecordType> & { | ||
}, MutationOptionsError = Error>({ children, loading, ...props }: CreateBaseProps<RecordType, ResultRecordType, MutationOptionsError>) => string | number | boolean | Iterable<React.ReactNode> | React.JSX.Element | null; | ||
export interface CreateBaseProps<RecordType extends Omit<RaRecord, 'id'> = any, ResultRecordType extends RaRecord = RecordType & { | ||
id: Identifier; | ||
}, MutationOptionsError = Error> extends CreateControllerProps<RecordType, MutationOptionsError, ResultRecordType> { | ||
children: ReactNode; | ||
}) => React.JSX.Element; | ||
loading?: ReactNode; | ||
} | ||
//# sourceMappingURL=CreateBase.d.ts.map |
@@ -42,2 +42,3 @@ "use strict"; | ||
var core_1 = require("../../core"); | ||
var auth_1 = require("../../auth"); | ||
/** | ||
@@ -73,10 +74,17 @@ * Call useCreateController and put the value in a CreateContext | ||
var CreateBase = function (_a) { | ||
var children = _a.children, props = __rest(_a, ["children"]); | ||
var children = _a.children, _b = _a.loading, loading = _b === void 0 ? null : _b, props = __rest(_a, ["children", "loading"]); | ||
var controllerProps = (0, useCreateController_1.useCreateController)(props); | ||
var body = (React.createElement(CreateContextProvider_1.CreateContextProvider, { value: controllerProps }, children)); | ||
return props.resource ? ( | ||
// support resource override via props | ||
React.createElement(core_1.ResourceContextProvider, { value: props.resource }, body)) : (body); | ||
var isAuthPending = (0, auth_1.useIsAuthPending)({ | ||
resource: controllerProps.resource, | ||
action: 'create', | ||
}); | ||
if (isAuthPending && !props.disableAuthentication) { | ||
return loading; | ||
} | ||
return ( | ||
// We pass props.resource here as we don't need to create a new ResourceContext if the props is not provided | ||
React.createElement(core_1.OptionalResourceContextProvider, { value: props.resource }, | ||
React.createElement(CreateContextProvider_1.CreateContextProvider, { value: controllerProps }, children))); | ||
}; | ||
exports.CreateBase = CreateBase; | ||
//# sourceMappingURL=CreateBase.js.map |
@@ -49,2 +49,3 @@ import { Location } from 'react-router-dom'; | ||
resource: string; | ||
saving: boolean; | ||
} | ||
@@ -51,0 +52,0 @@ /** |
@@ -93,3 +93,2 @@ "use strict"; | ||
var disableAuthentication = props.disableAuthentication, record = props.record, redirectTo = props.redirect, transform = props.transform, _b = props.mutationOptions, mutationOptions = _b === void 0 ? {} : _b; | ||
(0, auth_1.useAuthenticated)({ enabled: !disableAuthentication }); | ||
var resource = (0, core_1.useResourceContext)(props); | ||
@@ -99,2 +98,11 @@ if (!resource) { | ||
} | ||
var isPendingAuthenticated = (0, auth_1.useAuthenticated)({ | ||
enabled: !disableAuthentication, | ||
}).isPending; | ||
var isPendingCanAccess = (0, auth_1.useRequireAccess)({ | ||
action: 'create', | ||
resource: resource, | ||
// If disableAuthentication is true then isPendingAuthenticated will always be true so this hook is disabled | ||
enabled: !isPendingAuthenticated, | ||
}).isPending; | ||
var _c = (0, core_1.useResourceDefinition)(props), hasEdit = _c.hasEdit, hasShow = _c.hasShow; | ||
@@ -114,5 +122,10 @@ var finalRedirectTo = redirectTo !== null && redirectTo !== void 0 ? redirectTo : getDefaultRedirectRoute(hasShow, hasEdit); | ||
} | ||
notify('ra.notification.created', { | ||
notify("resources.".concat(resource, ".notifications.created"), { | ||
type: 'info', | ||
messageArgs: { smart_count: 1 }, | ||
messageArgs: { | ||
smart_count: 1, | ||
_: translate("ra.notification.created", { | ||
smart_count: 1, | ||
}), | ||
}, | ||
}); | ||
@@ -191,3 +204,3 @@ redirect(finalRedirectTo, resource, data.id, data); | ||
isLoading: false, | ||
isPending: saving, | ||
isPending: disableAuthentication ? false : isPendingCanAccess, | ||
saving: saving, | ||
@@ -194,0 +207,0 @@ defaultTitle: defaultTitle, |
@@ -34,5 +34,7 @@ import * as React from 'react'; | ||
*/ | ||
export declare const EditBase: <RecordType extends RaRecord<import("../../types").Identifier> = any>({ children, ...props }: { | ||
export declare const EditBase: <RecordType extends RaRecord<import("../../types").Identifier> = any, ErrorType = Error>({ children, loading, ...props }: EditBaseProps<RecordType, ErrorType>) => string | number | boolean | Iterable<React.ReactNode> | React.JSX.Element | null; | ||
export interface EditBaseProps<RecordType extends RaRecord = RaRecord, ErrorType = Error> extends EditControllerProps<RecordType, ErrorType> { | ||
children: ReactNode; | ||
} & EditControllerProps<RecordType, Error>) => React.JSX.Element; | ||
loading?: ReactNode; | ||
} | ||
//# sourceMappingURL=EditBase.d.ts.map |
@@ -42,2 +42,3 @@ "use strict"; | ||
var core_1 = require("../../core"); | ||
var auth_1 = require("../../auth"); | ||
/** | ||
@@ -73,10 +74,17 @@ * Call useEditController and put the value in a EditContext | ||
var EditBase = function (_a) { | ||
var children = _a.children, props = __rest(_a, ["children"]); | ||
var children = _a.children, _b = _a.loading, loading = _b === void 0 ? null : _b, props = __rest(_a, ["children", "loading"]); | ||
var controllerProps = (0, useEditController_1.useEditController)(props); | ||
var body = (React.createElement(EditContextProvider_1.EditContextProvider, { value: controllerProps }, children)); | ||
return props.resource ? ( | ||
// support resource override via props | ||
React.createElement(core_1.ResourceContextProvider, { value: props.resource }, body)) : (body); | ||
var isAuthPending = (0, auth_1.useIsAuthPending)({ | ||
resource: controllerProps.resource, | ||
action: 'edit', | ||
}); | ||
if (isAuthPending && !props.disableAuthentication) { | ||
return loading; | ||
} | ||
return ( | ||
// We pass props.resource here as we don't need to create a new ResourceContext if the props is not provided | ||
React.createElement(core_1.OptionalResourceContextProvider, { value: props.resource }, | ||
React.createElement(EditContextProvider_1.EditContextProvider, { value: controllerProps }, children))); | ||
}; | ||
exports.EditBase = EditBase; | ||
//# sourceMappingURL=EditBase.js.map |
@@ -46,2 +46,3 @@ import { RaRecord, MutationMode, TransformData } from '../../types'; | ||
resource: string; | ||
saving: boolean; | ||
} | ||
@@ -48,0 +49,0 @@ export interface EditControllerLoadingResult<RecordType extends RaRecord = any> extends EditControllerBaseResult<RecordType> { |
@@ -95,4 +95,3 @@ "use strict"; | ||
if (props === void 0) { props = {}; } | ||
var disableAuthentication = props.disableAuthentication, propsId = props.id, _a = props.mutationMode, mutationMode = _a === void 0 ? 'undoable' : _a, _b = props.mutationOptions, mutationOptions = _b === void 0 ? {} : _b, _c = props.queryOptions, queryOptions = _c === void 0 ? {} : _c, _d = props.redirect, redirectTo = _d === void 0 ? DefaultRedirect : _d, transform = props.transform; | ||
(0, auth_1.useAuthenticated)({ enabled: !disableAuthentication }); | ||
var _a = props.disableAuthentication, disableAuthentication = _a === void 0 ? false : _a, propsId = props.id, _b = props.mutationMode, mutationMode = _b === void 0 ? 'undoable' : _b, _c = props.mutationOptions, mutationOptions = _c === void 0 ? {} : _c, _d = props.queryOptions, queryOptions = _d === void 0 ? {} : _d, _e = props.redirect, redirectTo = _e === void 0 ? DefaultRedirect : _e, transform = props.transform; | ||
var resource = (0, core_1.useResourceContext)(props); | ||
@@ -102,2 +101,11 @@ if (!resource) { | ||
} | ||
var isPendingAuthenticated = (0, auth_1.useAuthenticated)({ | ||
enabled: !disableAuthentication, | ||
}).isPending; | ||
var isPendingCanAccess = (0, auth_1.useRequireAccess)({ | ||
action: 'edit', | ||
resource: resource, | ||
// If disableAuthentication is true then isPendingAuthenticated will always be true so this hook is disabled | ||
enabled: !isPendingAuthenticated, | ||
}).isPending; | ||
var getRecordRepresentation = (0, core_1.useGetRecordRepresentation)(resource); | ||
@@ -115,4 +123,5 @@ var translate = (0, i18n_1.useTranslate)(); | ||
var mutationMeta = mutationOptions.meta, onSuccess = mutationOptions.onSuccess, onError = mutationOptions.onError, otherMutationOptions = __rest(mutationOptions, ["meta", "onSuccess", "onError"]); | ||
var _e = (0, saveContext_1.useMutationMiddlewares)(), registerMutationMiddleware = _e.registerMutationMiddleware, getMutateWithMiddlewares = _e.getMutateWithMiddlewares, unregisterMutationMiddleware = _e.unregisterMutationMiddleware; | ||
var _f = (0, dataProvider_1.useGetOne)(resource, { id: id, meta: queryMeta }, __assign({ onError: function () { | ||
var _f = (0, saveContext_1.useMutationMiddlewares)(), registerMutationMiddleware = _f.registerMutationMiddleware, getMutateWithMiddlewares = _f.getMutateWithMiddlewares, unregisterMutationMiddleware = _f.unregisterMutationMiddleware; | ||
var _g = (0, dataProvider_1.useGetOne)(resource, { id: id, meta: queryMeta }, __assign({ enabled: (!isPendingAuthenticated && !isPendingCanAccess) || | ||
disableAuthentication, onError: function () { | ||
notify('ra.notification.item_doesnt_exist', { | ||
@@ -123,3 +132,3 @@ type: 'error', | ||
refresh(); | ||
}, refetchOnReconnect: false, refetchOnWindowFocus: false, retry: false }, otherQueryOptions)), record = _f.data, error = _f.error, isLoading = _f.isLoading, isFetching = _f.isFetching, isPending = _f.isPending, refetch = _f.refetch; | ||
}, refetchOnReconnect: false, refetchOnWindowFocus: false, retry: false }, otherQueryOptions)), record = _g.data, error = _g.error, isLoading = _g.isLoading, isFetching = _g.isFetching, isPending = _g.isPending, refetch = _g.refetch; | ||
// eslint-disable-next-line eqeqeq | ||
@@ -140,3 +149,3 @@ if (record && record.id && record.id != id) { | ||
var recordCached = { id: id, previousData: record }; | ||
var _g = (0, dataProvider_1.useUpdate)(resource, recordCached, __assign(__assign({ onSuccess: function (data, variables, context) { return __awaiter(void 0, void 0, void 0, function () { | ||
var _h = (0, dataProvider_1.useUpdate)(resource, recordCached, __assign(__assign({ onSuccess: function (data, variables, context) { return __awaiter(void 0, void 0, void 0, function () { | ||
return __generator(this, function (_a) { | ||
@@ -146,5 +155,10 @@ if (onSuccess) { | ||
} | ||
notify('ra.notification.updated', { | ||
notify("resources.".concat(resource, ".notifications.updated"), { | ||
type: 'info', | ||
messageArgs: { smart_count: 1 }, | ||
messageArgs: { | ||
smart_count: 1, | ||
_: translate('ra.notification.updated', { | ||
smart_count: 1, | ||
}), | ||
}, | ||
undoable: mutationMode === 'undoable', | ||
@@ -184,3 +198,3 @@ }); | ||
} | ||
} }, otherMutationOptions), { mutationMode: mutationMode, returnPromise: mutationMode === 'pessimistic', getMutateWithMiddlewares: getMutateWithMiddlewares })), update = _g[0], saving = _g[1].isPending; | ||
} }, otherMutationOptions), { mutationMode: mutationMode, returnPromise: mutationMode === 'pessimistic', getMutateWithMiddlewares: getMutateWithMiddlewares })), update = _h[0], saving = _h[1].isPending; | ||
var save = (0, react_1.useCallback)(function (data, _a) { | ||
@@ -187,0 +201,0 @@ var _b = _a === void 0 ? {} : _a, onSuccessFromSave = _b.onSuccess, onErrorFromSave = _b.onError, transformFromSave = _b.transform, metaFromSave = _b.meta; |
@@ -39,5 +39,7 @@ import * as React from 'react'; | ||
*/ | ||
export declare const InfiniteListBase: <RecordType extends RaRecord<import("../../types").Identifier> = any>({ children, ...props }: InfiniteListControllerProps<RecordType> & { | ||
export declare const InfiniteListBase: <RecordType extends RaRecord<import("../../types").Identifier> = any>({ children, loading, ...props }: InfiniteListBaseProps<RecordType>) => string | number | boolean | Iterable<React.ReactNode> | React.JSX.Element | null; | ||
export interface InfiniteListBaseProps<RecordType extends RaRecord = any> extends InfiniteListControllerProps<RecordType> { | ||
children: ReactNode; | ||
}) => React.JSX.Element; | ||
loading?: ReactNode; | ||
} | ||
//# sourceMappingURL=InfiniteListBase.d.ts.map |
@@ -43,2 +43,3 @@ "use strict"; | ||
var InfinitePaginationContext_1 = require("./InfinitePaginationContext"); | ||
var auth_1 = require("../../auth"); | ||
/** | ||
@@ -79,5 +80,14 @@ * Call useInfiniteListController and put the value in a ListContext | ||
var InfiniteListBase = function (_a) { | ||
var children = _a.children, props = __rest(_a, ["children"]); | ||
var children = _a.children, _b = _a.loading, loading = _b === void 0 ? null : _b, props = __rest(_a, ["children", "loading"]); | ||
var controllerProps = (0, useInfiniteListController_1.useInfiniteListController)(props); | ||
return (React.createElement(core_1.ResourceContextProvider, { value: props.resource }, | ||
var isAuthPending = (0, auth_1.useIsAuthPending)({ | ||
resource: controllerProps.resource, | ||
action: 'list', | ||
}); | ||
if (isAuthPending && !props.disableAuthentication) { | ||
return loading; | ||
} | ||
return ( | ||
// We pass props.resource here as we don't need to create a new ResourceContext if the props is not provided | ||
React.createElement(core_1.OptionalResourceContextProvider, { value: props.resource }, | ||
React.createElement(ListContextProvider_1.ListContextProvider, { value: controllerProps }, | ||
@@ -84,0 +94,0 @@ React.createElement(InfinitePaginationContext_1.InfinitePaginationContext.Provider, { value: { |
@@ -39,5 +39,7 @@ import * as React from 'react'; | ||
*/ | ||
export declare const ListBase: <RecordType extends RaRecord<import("../../types").Identifier> = any>({ children, ...props }: ListControllerProps<RecordType> & { | ||
export declare const ListBase: <RecordType extends RaRecord<import("../../types").Identifier> = any>({ children, loading, ...props }: ListBaseProps<RecordType>) => string | number | boolean | Iterable<React.ReactNode> | React.JSX.Element | null; | ||
export interface ListBaseProps<RecordType extends RaRecord = any> extends ListControllerProps<RecordType> { | ||
children: ReactNode; | ||
}) => React.JSX.Element; | ||
loading?: ReactNode; | ||
} | ||
//# sourceMappingURL=ListBase.d.ts.map |
@@ -42,2 +42,3 @@ "use strict"; | ||
var ListContextProvider_1 = require("./ListContextProvider"); | ||
var auth_1 = require("../../auth"); | ||
/** | ||
@@ -78,7 +79,17 @@ * Call useListController and put the value in a ListContext | ||
var ListBase = function (_a) { | ||
var children = _a.children, props = __rest(_a, ["children"]); | ||
return (React.createElement(core_1.ResourceContextProvider, { value: props.resource }, | ||
React.createElement(ListContextProvider_1.ListContextProvider, { value: (0, useListController_1.useListController)(props) }, children))); | ||
var children = _a.children, _b = _a.loading, loading = _b === void 0 ? null : _b, props = __rest(_a, ["children", "loading"]); | ||
var controllerProps = (0, useListController_1.useListController)(props); | ||
var isAuthPending = (0, auth_1.useIsAuthPending)({ | ||
resource: controllerProps.resource, | ||
action: 'list', | ||
}); | ||
if (isAuthPending && !props.disableAuthentication) { | ||
return loading; | ||
} | ||
return ( | ||
// We pass props.resource here as we don't need to create a new ResourceContext if the props is not provided | ||
React.createElement(core_1.OptionalResourceContextProvider, { value: props.resource }, | ||
React.createElement(ListContextProvider_1.ListContextProvider, { value: controllerProps }, children))); | ||
}; | ||
exports.ListBase = ListBase; | ||
//# sourceMappingURL=ListBase.js.map |
@@ -63,6 +63,5 @@ "use strict"; | ||
if (props === void 0) { props = {}; } | ||
var _a = props.debounce, debounce = _a === void 0 ? 500 : _a, disableAuthentication = props.disableAuthentication, disableSyncWithLocation = props.disableSyncWithLocation, _b = props.exporter, exporter = _b === void 0 ? export_1.defaultExporter : _b, filter = props.filter, filterDefaultValues = props.filterDefaultValues, _c = props.perPage, perPage = _c === void 0 ? 10 : _c, queryOptions = props.queryOptions, sort = props.sort, storeKey = props.storeKey; | ||
(0, auth_1.useAuthenticated)({ enabled: !disableAuthentication }); | ||
var _a = props.debounce, debounce = _a === void 0 ? 500 : _a, _b = props.disableAuthentication, disableAuthentication = _b === void 0 ? false : _b, _c = props.disableSyncWithLocation, disableSyncWithLocation = _c === void 0 ? false : _c, _d = props.exporter, exporter = _d === void 0 ? export_1.defaultExporter : _d, filter = props.filter, filterDefaultValues = props.filterDefaultValues, _e = props.perPage, perPage = _e === void 0 ? 10 : _e, queryOptions = props.queryOptions, sort = props.sort, storeKey = props.storeKey; | ||
var resource = (0, core_1.useResourceContext)(props); | ||
var _d = queryOptions !== null && queryOptions !== void 0 ? queryOptions : {}, meta = _d.meta, otherQueryOptions = __rest(_d, ["meta"]); | ||
var _f = queryOptions !== null && queryOptions !== void 0 ? queryOptions : {}, meta = _f.meta, otherQueryOptions = __rest(_f, ["meta"]); | ||
if (!resource) { | ||
@@ -74,5 +73,14 @@ throw new Error("<InfiniteList> was called outside of a ResourceContext and without a resource prop. You must set the resource prop."); | ||
} | ||
var isPendingAuthenticated = (0, auth_1.useAuthenticated)({ | ||
enabled: !disableAuthentication, | ||
}).isPending; | ||
var isPendingCanAccess = (0, auth_1.useRequireAccess)({ | ||
action: 'list', | ||
resource: resource, | ||
// If disableAuthentication is true then isPendingAuthenticated will always be true so this hook is disabled | ||
enabled: !isPendingAuthenticated, | ||
}).isPending; | ||
var translate = (0, i18n_1.useTranslate)(); | ||
var notify = (0, notification_1.useNotify)(); | ||
var _e = (0, useListParams_1.useListParams)({ | ||
var _g = (0, useListParams_1.useListParams)({ | ||
debounce: debounce, | ||
@@ -85,5 +93,5 @@ disableSyncWithLocation: disableSyncWithLocation, | ||
storeKey: storeKey, | ||
}), query = _e[0], queryModifiers = _e[1]; | ||
var _f = (0, useRecordSelection_1.useRecordSelection)({ resource: resource }), selectedIds = _f[0], selectionModifiers = _f[1]; | ||
var _g = (0, dataProvider_1.useInfiniteGetList)(resource, { | ||
}), query = _g[0], queryModifiers = _g[1]; | ||
var _h = (0, useRecordSelection_1.useRecordSelection)({ resource: resource }), selectedIds = _h[0], selectionModifiers = _h[1]; | ||
var _j = (0, dataProvider_1.useInfiniteGetList)(resource, { | ||
pagination: { | ||
@@ -96,3 +104,4 @@ page: query.page, | ||
meta: meta, | ||
}, __assign({ placeholderData: function (previousData) { return previousData; }, retry: false, onError: function (error) { | ||
}, __assign({ enabled: (!isPendingAuthenticated && !isPendingCanAccess) || | ||
disableAuthentication, placeholderData: function (previousData) { return previousData; }, retry: false, onError: function (error) { | ||
return notify((error === null || error === void 0 ? void 0 : error.message) || 'ra.notification.http_error', { | ||
@@ -104,3 +113,3 @@ type: 'error', | ||
}); | ||
} }, otherQueryOptions)), data = _g.data, total = _g.total, error = _g.error, isLoading = _g.isLoading, isPending = _g.isPending, isFetching = _g.isFetching, hasNextPage = _g.hasNextPage, hasPreviousPage = _g.hasPreviousPage, fetchNextPage = _g.fetchNextPage, isFetchingNextPage = _g.isFetchingNextPage, fetchPreviousPage = _g.fetchPreviousPage, isFetchingPreviousPage = _g.isFetchingPreviousPage, refetch = _g.refetch; | ||
} }, otherQueryOptions)), data = _j.data, total = _j.total, error = _j.error, isLoading = _j.isLoading, isPending = _j.isPending, isFetching = _j.isFetching, hasNextPage = _j.hasNextPage, hasPreviousPage = _j.hasPreviousPage, fetchNextPage = _j.fetchNextPage, isFetchingNextPage = _j.isFetchingNextPage, fetchPreviousPage = _j.fetchPreviousPage, isFetchingPreviousPage = _j.isFetchingPreviousPage, refetch = _j.refetch; | ||
// change page if there is no data | ||
@@ -107,0 +116,0 @@ (0, react_1.useEffect)(function () { |
@@ -31,3 +31,2 @@ "use strict"; | ||
var dataProvider_1 = require("../../dataProvider"); | ||
var queryReducer_1 = require("./queryReducer"); | ||
var export_1 = require("../../export"); | ||
@@ -37,2 +36,3 @@ var core_1 = require("../../core"); | ||
var useListParams_1 = require("./useListParams"); | ||
var queryReducer_1 = require("./queryReducer"); | ||
/** | ||
@@ -57,4 +57,3 @@ * Prepare data for the List view | ||
if (props === void 0) { props = {}; } | ||
var _a = props.debounce, debounce = _a === void 0 ? 500 : _a, disableAuthentication = props.disableAuthentication, disableSyncWithLocation = props.disableSyncWithLocation, _b = props.exporter, exporter = _b === void 0 ? export_1.defaultExporter : _b, filter = props.filter, filterDefaultValues = props.filterDefaultValues, _c = props.perPage, perPage = _c === void 0 ? 10 : _c, _d = props.queryOptions, queryOptions = _d === void 0 ? {} : _d, _e = props.sort, sort = _e === void 0 ? defaultSort : _e, storeKey = props.storeKey; | ||
(0, auth_1.useAuthenticated)({ enabled: !disableAuthentication }); | ||
var _a = props.debounce, debounce = _a === void 0 ? 500 : _a, _b = props.disableAuthentication, disableAuthentication = _b === void 0 ? false : _b, _c = props.disableSyncWithLocation, disableSyncWithLocation = _c === void 0 ? false : _c, _d = props.exporter, exporter = _d === void 0 ? export_1.defaultExporter : _d, filter = props.filter, filterDefaultValues = props.filterDefaultValues, _e = props.perPage, perPage = _e === void 0 ? 10 : _e, _f = props.queryOptions, queryOptions = _f === void 0 ? {} : _f, _g = props.sort, sort = _g === void 0 ? defaultSort : _g, storeKey = props.storeKey; | ||
var resource = (0, core_1.useResourceContext)(props); | ||
@@ -70,5 +69,14 @@ var meta = queryOptions.meta, otherQueryOptions = __rest(queryOptions, ["meta"]); | ||
} | ||
var isPendingAuthenticated = (0, auth_1.useAuthenticated)({ | ||
enabled: !disableAuthentication, | ||
}).isPending; | ||
var isPendingCanAccess = (0, auth_1.useRequireAccess)({ | ||
action: 'list', | ||
resource: resource, | ||
// If disableAuthentication is true then isPendingAuthenticated will always be true so this hook is disabled | ||
enabled: !isPendingAuthenticated, | ||
}).isPending; | ||
var translate = (0, i18n_1.useTranslate)(); | ||
var notify = (0, notification_1.useNotify)(); | ||
var _f = (0, useListParams_1.useListParams)({ | ||
var _h = (0, useListParams_1.useListParams)({ | ||
debounce: debounce, | ||
@@ -81,8 +89,8 @@ disableSyncWithLocation: disableSyncWithLocation, | ||
storeKey: storeKey, | ||
}), query = _f[0], queryModifiers = _f[1]; | ||
var _g = (0, useRecordSelection_1.useRecordSelection)({ | ||
}), query = _h[0], queryModifiers = _h[1]; | ||
var _j = (0, useRecordSelection_1.useRecordSelection)({ | ||
resource: resource, | ||
disableSyncWithStore: storeKey === false, | ||
}), selectedIds = _g[0], selectionModifiers = _g[1]; | ||
var _h = (0, dataProvider_1.useGetList)(resource, { | ||
}), selectedIds = _j[0], selectionModifiers = _j[1]; | ||
var _k = (0, dataProvider_1.useGetList)(resource, { | ||
pagination: { | ||
@@ -95,3 +103,4 @@ page: query.page, | ||
meta: meta, | ||
}, __assign({ placeholderData: function (previousData) { return previousData; }, retry: false, onError: function (error) { | ||
}, __assign({ enabled: (!isPendingAuthenticated && !isPendingCanAccess) || | ||
disableAuthentication, placeholderData: function (previousData) { return previousData; }, retry: false, onError: function (error) { | ||
return notify((error === null || error === void 0 ? void 0 : error.message) || 'ra.notification.http_error', { | ||
@@ -103,3 +112,3 @@ type: 'error', | ||
}); | ||
} }, otherQueryOptions)), data = _h.data, pageInfo = _h.pageInfo, total = _h.total, responseMeta = _h.meta, error = _h.error, isLoading = _h.isLoading, isFetching = _h.isFetching, isPending = _h.isPending, refetch = _h.refetch; | ||
} }, otherQueryOptions)), data = _k.data, pageInfo = _k.pageInfo, total = _k.total, responseMeta = _k.meta, error = _k.error, isLoading = _k.isLoading, isFetching = _k.isFetching, isPending = _k.isPending, refetch = _k.refetch; | ||
// change page if there is no data | ||
@@ -106,0 +115,0 @@ (0, react_1.useEffect)(function () { |
import * as React from 'react'; | ||
import { ReactElement } from 'react'; | ||
import { RaRecord } from '../../types'; | ||
@@ -34,5 +33,7 @@ import { ShowControllerProps } from './useShowController'; | ||
*/ | ||
export declare const ShowBase: <RecordType extends RaRecord<import("../../types").Identifier> = any>({ children, ...props }: { | ||
children: ReactElement; | ||
} & ShowControllerProps<RecordType>) => React.JSX.Element; | ||
export declare const ShowBase: <RecordType extends RaRecord<import("../../types").Identifier> = any>({ children, loading, ...props }: ShowBaseProps<RecordType>) => string | number | boolean | Iterable<React.ReactNode> | React.JSX.Element | null; | ||
export interface ShowBaseProps<RecordType extends RaRecord = RaRecord> extends ShowControllerProps<RecordType> { | ||
children: React.ReactNode; | ||
loading?: React.ReactNode; | ||
} | ||
//# sourceMappingURL=ShowBase.d.ts.map |
@@ -42,2 +42,3 @@ "use strict"; | ||
var core_1 = require("../../core"); | ||
var auth_1 = require("../../auth"); | ||
/** | ||
@@ -73,10 +74,17 @@ * Call useShowController and put the value in a ShowContext | ||
var ShowBase = function (_a) { | ||
var children = _a.children, props = __rest(_a, ["children"]); | ||
var children = _a.children, _b = _a.loading, loading = _b === void 0 ? null : _b, props = __rest(_a, ["children", "loading"]); | ||
var controllerProps = (0, useShowController_1.useShowController)(props); | ||
var body = (React.createElement(ShowContextProvider_1.ShowContextProvider, { value: controllerProps }, children)); | ||
return props.resource ? ( | ||
// support resource override via props | ||
React.createElement(core_1.ResourceContextProvider, { value: props.resource }, body)) : (body); | ||
var isAuthPending = (0, auth_1.useIsAuthPending)({ | ||
resource: controllerProps.resource, | ||
action: 'show', | ||
}); | ||
if (isAuthPending && !props.disableAuthentication) { | ||
return loading; | ||
} | ||
return ( | ||
// We pass props.resource here as we don't need to create a new ResourceContext if the props is not provided | ||
React.createElement(core_1.OptionalResourceContextProvider, { value: props.resource }, | ||
React.createElement(ShowContextProvider_1.ShowContextProvider, { value: controllerProps }, children))); | ||
}; | ||
exports.ShowBase = ShowBase; | ||
//# sourceMappingURL=ShowBase.js.map |
@@ -67,4 +67,3 @@ "use strict"; | ||
if (props === void 0) { props = {}; } | ||
var disableAuthentication = props.disableAuthentication, propsId = props.id, _a = props.queryOptions, queryOptions = _a === void 0 ? {} : _a; | ||
(0, auth_1.useAuthenticated)({ enabled: !disableAuthentication }); | ||
var _a = props.disableAuthentication, disableAuthentication = _a === void 0 ? false : _a, propsId = props.id, _b = props.queryOptions, queryOptions = _b === void 0 ? {} : _b; | ||
var resource = (0, core_1.useResourceContext)(props); | ||
@@ -74,2 +73,11 @@ if (!resource) { | ||
} | ||
var isPendingAuthenticated = (0, auth_1.useAuthenticated)({ | ||
enabled: !disableAuthentication, | ||
}).isPending; | ||
var isPendingCanAccess = (0, auth_1.useRequireAccess)({ | ||
action: 'show', | ||
resource: resource, | ||
// If disableAuthentication is true then isPendingAuthenticated will always be true so this hook is disabled | ||
enabled: !isPendingAuthenticated, | ||
}).isPending; | ||
var getRecordRepresentation = (0, core_1.useGetRecordRepresentation)(resource); | ||
@@ -86,3 +94,4 @@ var translate = (0, i18n_1.useTranslate)(); | ||
var meta = queryOptions.meta, otherQueryOptions = __rest(queryOptions, ["meta"]); | ||
var _b = (0, dataProvider_1.useGetOne)(resource, { id: id, meta: meta }, __assign({ onError: function () { | ||
var _c = (0, dataProvider_1.useGetOne)(resource, { id: id, meta: meta }, __assign({ enabled: (!isPendingAuthenticated && !isPendingCanAccess) || | ||
disableAuthentication, onError: function () { | ||
notify('ra.notification.item_doesnt_exist', { | ||
@@ -93,3 +102,3 @@ type: 'error', | ||
refresh(); | ||
}, retry: false }, otherQueryOptions)), record = _b.data, error = _b.error, isLoading = _b.isLoading, isFetching = _b.isFetching, isPending = _b.isPending, refetch = _b.refetch; | ||
}, retry: false }, otherQueryOptions)), record = _c.data, error = _c.error, isLoading = _c.isLoading, isFetching = _c.isFetching, isPending = _c.isPending, refetch = _c.refetch; | ||
// eslint-disable-next-line eqeqeq | ||
@@ -96,0 +105,0 @@ if (record && record.id && record.id != id) { |
@@ -110,7 +110,7 @@ "use strict"; | ||
var CoreAdmin = function (props) { | ||
var authProvider = props.authProvider, basename = props.basename, catchAll = props.catchAll, children = props.children, dashboard = props.dashboard, dataProvider = props.dataProvider, disableTelemetry = props.disableTelemetry, error = props.error, i18nProvider = props.i18nProvider, queryClient = props.queryClient, layout = props.layout, loading = props.loading, loginPage = props.loginPage, ready = props.ready, requireAuth = props.requireAuth, store = props.store, _a = props.title, title = _a === void 0 ? 'React Admin' : _a; | ||
var accessDenied = props.accessDenied, authenticationError = props.authenticationError, authProvider = props.authProvider, basename = props.basename, catchAll = props.catchAll, children = props.children, dashboard = props.dashboard, dataProvider = props.dataProvider, disableTelemetry = props.disableTelemetry, error = props.error, i18nProvider = props.i18nProvider, layout = props.layout, loading = props.loading, loginPage = props.loginPage, queryClient = props.queryClient, ready = props.ready, requireAuth = props.requireAuth, store = props.store, _a = props.title, title = _a === void 0 ? 'React Admin' : _a; | ||
return (React.createElement(CoreAdminContext_1.CoreAdminContext, { authProvider: authProvider, basename: basename, dataProvider: dataProvider, i18nProvider: i18nProvider, queryClient: queryClient, store: store }, | ||
React.createElement(CoreAdminUI_1.CoreAdminUI, { layout: layout, dashboard: dashboard, disableTelemetry: disableTelemetry, catchAll: catchAll, title: title, loading: loading, error: error, loginPage: loginPage, requireAuth: requireAuth, ready: ready }, children))); | ||
React.createElement(CoreAdminUI_1.CoreAdminUI, { accessDenied: accessDenied, authenticationError: authenticationError, catchAll: catchAll, dashboard: dashboard, disableTelemetry: disableTelemetry, error: error, layout: layout, loading: loading, loginPage: loginPage, ready: ready, requireAuth: requireAuth, title: title }, children))); | ||
}; | ||
exports.CoreAdmin = CoreAdmin; | ||
//# sourceMappingURL=CoreAdmin.js.map |
@@ -13,3 +13,5 @@ import * as React from 'react'; | ||
ready?: ComponentType; | ||
authenticationError?: ComponentType; | ||
accessDenied?: React.ComponentType; | ||
} | ||
//# sourceMappingURL=CoreAdminRoutes.d.ts.map |
@@ -34,24 +34,10 @@ "use strict"; | ||
var HasDashboardContext_1 = require("./HasDashboardContext"); | ||
var NavigateToFirstResource_1 = require("./NavigateToFirstResource"); | ||
var CoreAdminRoutes = function (props) { | ||
(0, routing_1.useScrollToTop)(); | ||
var createPath = (0, routing_1.useCreatePath)(); | ||
var _a = (0, useConfigureAdminRouterFromChildren_1.useConfigureAdminRouterFromChildren)(props.children), customRoutesWithLayout = _a.customRoutesWithLayout, customRoutesWithoutLayout = _a.customRoutesWithoutLayout, status = _a.status, resources = _a.resources; | ||
var Layout = props.layout, CatchAll = props.catchAll, dashboard = props.dashboard, LoadingPage = props.loading, requireAuth = props.requireAuth, Ready = props.ready; | ||
var _b = (0, react_1.useState)(requireAuth), onlyAnonymousRoutes = _b[0], setOnlyAnonymousRoutes = _b[1]; | ||
var _c = (0, react_1.useState)(requireAuth), checkAuthLoading = _c[0], setCheckAuthLoading = _c[1]; | ||
var checkAuth = (0, auth_1.useCheckAuth)(); | ||
(0, react_1.useEffect)(function () { | ||
if (requireAuth) { | ||
// do not log the user out on failure to allow access to custom routes with no layout | ||
// for other routes, the LogoutOnMount component will log the user out | ||
checkAuth(undefined, false) | ||
.then(function () { | ||
setOnlyAnonymousRoutes(false); | ||
}) | ||
.catch(function () { }) | ||
.finally(function () { | ||
setCheckAuthLoading(false); | ||
}); | ||
} | ||
}, [checkAuth, requireAuth]); | ||
var Layout = props.layout, CatchAll = props.catchAll, dashboard = props.dashboard, LoadingPage = props.loading, requireAuth = props.requireAuth, Ready = props.ready, _b = props.authenticationError, AuthenticationError = _b === void 0 ? Noop : _b, _c = props.accessDenied, AccessDenied = _c === void 0 ? Noop : _c; | ||
var _d = (0, auth_1.useAuthState)(undefined, | ||
// do not log the user out on failure to allow access to custom routes with no layout | ||
false, { enabled: requireAuth }), authenticated = _d.authenticated, isPendingAuthenticated = _d.isPending; | ||
if (status === 'empty') { | ||
@@ -64,3 +50,3 @@ if (!Ready) { | ||
// Note: custom routes with no layout are always rendered, regardless of the auth status | ||
if (status === 'loading' || checkAuthLoading) { | ||
if (status === 'loading' || (requireAuth && isPendingAuthenticated)) { | ||
return (React.createElement(react_router_dom_1.Routes, null, | ||
@@ -71,3 +57,3 @@ customRoutesWithoutLayout, | ||
} | ||
if (onlyAnonymousRoutes) { | ||
if (requireAuth && (isPendingAuthenticated || !authenticated)) { | ||
return (React.createElement(react_router_dom_1.Routes, null, | ||
@@ -84,10 +70,11 @@ customRoutesWithoutLayout, | ||
react_1.Children.map(resources, function (resource) { return (React.createElement(react_router_dom_1.Route, { key: resource.props.name, path: "".concat(resource.props.name, "/*"), element: resource })); }), | ||
React.createElement(react_router_dom_1.Route, { path: "/", element: dashboard ? (React.createElement(auth_1.WithPermissions, { authParams: defaultAuthParams, component: dashboard })) : resources.length > 0 ? (React.createElement(react_router_dom_1.Navigate, { to: createPath({ | ||
resource: resources[0].props.name, | ||
type: 'list', | ||
}) })) : null }), | ||
React.createElement(react_router_dom_1.Route, { path: "/", element: dashboard ? (React.createElement(auth_1.WithPermissions, { authParams: defaultAuthParams, component: dashboard, loading: LoadingPage })) : (React.createElement(NavigateToFirstResource_1.NavigateToFirstResource, { loading: LoadingPage })) }), | ||
React.createElement(react_router_dom_1.Route, { path: "/authentication-error", element: React.createElement(AuthenticationError, null) }), | ||
React.createElement(react_router_dom_1.Route, { path: "/access-denied", element: React.createElement(AccessDenied, null) }), | ||
React.createElement(react_router_dom_1.Route, { path: "*", element: React.createElement(CatchAll, null) })))) }))); | ||
}; | ||
exports.CoreAdminRoutes = CoreAdminRoutes; | ||
// FIXME in v6: make dashboard anonymous by default to remove this hack | ||
var defaultAuthParams = { params: { route: 'dashboard' } }; | ||
var Noop = function () { return null; }; | ||
//# sourceMappingURL=CoreAdminRoutes.js.map |
@@ -207,4 +207,56 @@ import * as React from 'react'; | ||
title?: TitleComponent; | ||
/** | ||
* The page to display when an authentication error occurs | ||
* | ||
* @see https://marmelab.com/react-admin/Admin.html#authenticationError | ||
* @example | ||
* import { Admin } from 'react-admin'; | ||
* | ||
* const AuthenticationError = () => ( | ||
* <div> | ||
* <h1>Authentication Error</h1> | ||
* <p>The authentication server returned an error and your credentials could not be checked.</p> | ||
* </div> | ||
* ) | ||
* | ||
* const App = () => ( | ||
* <Admin authenticationError={AuthenticationError}> | ||
* ... | ||
* </Admin> | ||
* ); | ||
*/ | ||
authenticationError?: ComponentType; | ||
/** | ||
* A react component to display when users don't have access to the page they're trying to access | ||
* | ||
* @see https://marmelab.com/react-admin/Admin.html#accessDenied | ||
* @example | ||
* // in src/AccessDenied.js | ||
* import Card from '@mui/material/Card'; | ||
* import CardContent from '@mui/material/CardContent'; | ||
* import { Title } from 'react-admin'; | ||
* | ||
* export const AccessDenied = () => ( | ||
* <Card> | ||
* <Title title="AccessDenied" /> | ||
* <CardContent> | ||
* <h1>You're not authorized to see this page</h1> | ||
* </CardContent> | ||
* </Card> | ||
* ); | ||
* | ||
* // in src/App.js | ||
* import { Admin } from 'react-admin'; | ||
* import { dataProvider } from './dataProvider'; | ||
* import { AccessDenied } from './AccessDenied'; | ||
* | ||
* const App = () => ( | ||
* <Admin accessDenied={AccessDenied} dataProvider={dataProvider}> | ||
* ... | ||
* </Admin> | ||
* ); | ||
*/ | ||
accessDenied?: React.ComponentType; | ||
} | ||
export declare const CoreAdminUI: (props: CoreAdminUIProps) => React.JSX.Element; | ||
//# sourceMappingURL=CoreAdminUI.d.ts.map |
@@ -50,3 +50,3 @@ "use strict"; | ||
var _a = (0, react_1.useState)({}), errorInfo = _a[0], setErrorInfo = _a[1]; | ||
var _b = props.authCallbackPage, LoginCallbackPage = _b === void 0 ? false : _b, _c = props.catchAll, catchAll = _c === void 0 ? Noop : _c, children = props.children, dashboard = props.dashboard, _d = props.disableTelemetry, disableTelemetry = _d === void 0 ? false : _d, _e = props.error, ErrorComponent = _e === void 0 ? DefaultError : _e, _f = props.layout, layout = _f === void 0 ? DefaultLayout : _f, _g = props.loading, loading = _g === void 0 ? Noop : _g, _h = props.loginPage, LoginPage = _h === void 0 ? false : _h, _j = props.ready, ready = _j === void 0 ? util_1.Ready : _j, _k = props.requireAuth, requireAuth = _k === void 0 ? false : _k, _l = props.title, title = _l === void 0 ? 'React Admin' : _l; | ||
var _b = props.authCallbackPage, LoginCallbackPage = _b === void 0 ? false : _b, _c = props.catchAll, catchAll = _c === void 0 ? Noop : _c, children = props.children, dashboard = props.dashboard, _d = props.disableTelemetry, disableTelemetry = _d === void 0 ? false : _d, _e = props.error, ErrorComponent = _e === void 0 ? DefaultError : _e, _f = props.layout, layout = _f === void 0 ? DefaultLayout : _f, _g = props.loading, loading = _g === void 0 ? Noop : _g, _h = props.loginPage, LoginPage = _h === void 0 ? false : _h, _j = props.ready, ready = _j === void 0 ? util_1.Ready : _j, _k = props.requireAuth, requireAuth = _k === void 0 ? false : _k, _l = props.title, title = _l === void 0 ? 'React Admin' : _l, _m = props.authenticationError, authenticationError = _m === void 0 ? Noop : _m, _o = props.accessDenied, accessDenied = _o === void 0 ? Noop : _o; | ||
(0, react_1.useEffect)(function () { | ||
@@ -74,3 +74,3 @@ if (disableTelemetry || | ||
LoginCallbackPage !== true ? (React.createElement(react_router_dom_1.Route, { path: "/auth-callback", element: createOrGetElement(LoginCallbackPage) })) : null, | ||
React.createElement(react_router_dom_1.Route, { path: "/*", element: React.createElement(CoreAdminRoutes_1.CoreAdminRoutes, { catchAll: catchAll, dashboard: dashboard, layout: layout, loading: loading, requireAuth: requireAuth, ready: ready }, children) }))))); | ||
React.createElement(react_router_dom_1.Route, { path: "/*", element: React.createElement(CoreAdminRoutes_1.CoreAdminRoutes, { catchAll: catchAll, dashboard: dashboard, layout: layout, loading: loading, requireAuth: requireAuth, ready: ready, authenticationError: authenticationError, accessDenied: accessDenied }, children) }))))); | ||
}; | ||
@@ -77,0 +77,0 @@ exports.CoreAdminUI = CoreAdminUI; |
@@ -8,2 +8,3 @@ export * from './CoreAdmin'; | ||
export * from './HasDashboardContext'; | ||
export * from './NavigateToFirstResource'; | ||
export * from './OptionalResourceContextProvider'; | ||
@@ -15,3 +16,5 @@ export * from './Resource'; | ||
export * from './SourceContext'; | ||
export * from './useFirstResourceWithListAccess'; | ||
export * from './useGetResourceLabel'; | ||
export * from './useGetRecordRepresentation'; | ||
export * from './useResourceDefinitionContext'; | ||
@@ -21,4 +24,3 @@ export * from './useResourceContext'; | ||
export * from './useResourceDefinitions'; | ||
export * from './useGetRecordRepresentation'; | ||
export * from './useWrappedSource'; | ||
//# sourceMappingURL=index.d.ts.map |
@@ -24,2 +24,3 @@ "use strict"; | ||
__exportStar(require("./HasDashboardContext"), exports); | ||
__exportStar(require("./NavigateToFirstResource"), exports); | ||
__exportStar(require("./OptionalResourceContextProvider"), exports); | ||
@@ -31,3 +32,5 @@ __exportStar(require("./Resource"), exports); | ||
__exportStar(require("./SourceContext"), exports); | ||
__exportStar(require("./useFirstResourceWithListAccess"), exports); | ||
__exportStar(require("./useGetResourceLabel"), exports); | ||
__exportStar(require("./useGetRecordRepresentation"), exports); | ||
__exportStar(require("./useResourceDefinitionContext"), exports); | ||
@@ -37,4 +40,3 @@ __exportStar(require("./useResourceContext"), exports); | ||
__exportStar(require("./useResourceDefinitions"), exports); | ||
__exportStar(require("./useGetRecordRepresentation"), exports); | ||
__exportStar(require("./useWrappedSource"), exports); | ||
//# sourceMappingURL=index.js.map |
@@ -72,2 +72,5 @@ "use strict"; | ||
}; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
@@ -77,4 +80,5 @@ exports.useConfigureAdminRouterFromChildren = void 0; | ||
var react_1 = require("react"); | ||
var auth_1 = require("../auth"); | ||
var util_1 = require("../util"); | ||
var useLogout_1 = __importDefault(require("../auth/useLogout")); | ||
var usePermissions_1 = __importDefault(require("../auth/usePermissions")); | ||
var hooks_1 = require("../util/hooks"); | ||
var useResourceDefinitionContext_1 = require("./useResourceDefinitionContext"); | ||
@@ -101,3 +105,3 @@ /** | ||
var useConfigureAdminRouterFromChildren = function (children) { | ||
var _a = (0, auth_1.usePermissions)(), permissions = _a.permissions, isPending = _a.isPending; | ||
var _a = (0, usePermissions_1.default)(), permissions = _a.permissions, isPending = _a.isPending; | ||
// Whenever children are updated, update our custom routes and resources | ||
@@ -126,5 +130,5 @@ var _b = useRoutesAndResourcesFromChildren(children, permissions, isPending), routesAndResources = _b[0], status = _b[1]; | ||
// initialize the status at the next stop | ||
var doLogout = (0, auth_1.useLogout)(); | ||
var doLogout = (0, useLogout_1.default)(); | ||
var _a = useRoutesAndResourcesState(getRoutesAndResourceFromNodes(children)), routesAndResources = _a[0], setRoutesAndResources = _a[1], mergeRoutesAndResources = _a[2]; | ||
var _b = (0, util_1.useSafeSetState)(function () { | ||
var _b = (0, hooks_1.useSafeSetState)(function () { | ||
return getStatus(__assign({ children: children }, routesAndResources)); | ||
@@ -131,0 +135,0 @@ }), status = _b[0], setStatus = _b[1]; |
@@ -8,2 +8,3 @@ "use strict"; | ||
var react_1 = require("react"); | ||
var react_query_1 = require("@tanstack/react-query"); | ||
var DataProviderContext_1 = __importDefault(require("./DataProviderContext")); | ||
@@ -14,2 +15,3 @@ var defaultDataProvider_1 = require("./defaultDataProvider"); | ||
var dataFetchActions_1 = require("./dataFetchActions"); | ||
var populateQueryCache_1 = require("./populateQueryCache"); | ||
/** | ||
@@ -84,2 +86,3 @@ * Hook for getting a dataProvider | ||
defaultDataProvider_1.defaultDataProvider); | ||
var queryClient = (0, react_query_1.useQueryClient)(); | ||
var logoutIfAccessDenied = (0, useLogoutIfAccessDenied_1.default)(); | ||
@@ -108,2 +111,3 @@ var dataProviderProxy = (0, react_1.useMemo)(function () { | ||
.then(function (response) { | ||
var _a; | ||
if (process.env.NODE_ENV !== 'production' && | ||
@@ -113,2 +117,8 @@ dataFetchActions_1.reactAdminFetchActions.includes(type)) { | ||
} | ||
if ((_a = response === null || response === void 0 ? void 0 : response.meta) === null || _a === void 0 ? void 0 : _a.prefetched) { | ||
(0, populateQueryCache_1.populateQueryCache)({ | ||
data: response === null || response === void 0 ? void 0 : response.meta.prefetched, | ||
queryClient: queryClient, | ||
}); | ||
} | ||
return response; | ||
@@ -142,3 +152,3 @@ }) | ||
}); | ||
}, [dataProvider, logoutIfAccessDenied]); | ||
}, [dataProvider, logoutIfAccessDenied, queryClient]); | ||
return dataProviderProxy; | ||
@@ -145,0 +155,0 @@ }; |
@@ -65,2 +65,4 @@ export interface StringMap { | ||
invite: string; | ||
access_denied: string; | ||
authentication_error: string; | ||
}; | ||
@@ -111,2 +113,4 @@ input: { | ||
unsaved_changes: string; | ||
access_denied: string; | ||
authentication_error: string; | ||
}; | ||
@@ -113,0 +117,0 @@ navigation: { |
@@ -1,2 +0,2 @@ | ||
import { Identifier } from '../types'; | ||
import { HintedString, Identifier } from '../types'; | ||
/** | ||
@@ -37,4 +37,3 @@ * Get a callback to create a link to a given page in the admin app. | ||
export declare const useCreatePath: () => ({ resource, id, type }: CreatePathParams) => string; | ||
type AnyString = string & {}; | ||
export type CreatePathType = 'list' | 'edit' | 'show' | 'create' | AnyString; | ||
export type CreatePathType = HintedString<'list' | 'edit' | 'show' | 'create'>; | ||
export interface CreatePathParams { | ||
@@ -46,3 +45,2 @@ type: CreatePathType; | ||
export declare const removeDoubleSlashes: (path: string) => string; | ||
export {}; | ||
//# sourceMappingURL=useCreatePath.d.ts.map |
@@ -11,3 +11,3 @@ import type { RaRecord } from '../types'; | ||
* const EditLink = () => { | ||
* const path = useGetRouteForRecord(); | ||
* const path = useGetPathForRecord(); | ||
* return path ? <Link to={path}>Edit</Link> : null; | ||
@@ -18,3 +18,3 @@ * }; | ||
* const EditLink = ({ record, resource }) => { | ||
* const path = useGetRouteForRecord({ record, resource, link: 'edit' }); | ||
* const path = useGetPathForRecord({ record, resource, link: 'edit' }); | ||
* return path ? <Link to={path}>Edit</Link> : null; | ||
@@ -25,3 +25,3 @@ * }; | ||
* const EditLink = ({ record, resource }) => { | ||
* const path = useGetRouteForRecord({ record, resource, link: (record, resource) => record.canEdit ? 'edit' : false }); | ||
* const path = useGetPathForRecord({ record, resource, link: (record, resource) => record.canEdit ? 'edit' : false }); | ||
* return path ? <Link to={path}>Edit</Link> : null; | ||
@@ -32,3 +32,3 @@ * }; | ||
* const EditLink = ({ record, resource }) => { | ||
* const path = useGetRouteForRecord({ record, resource, link: async (record, resource) => { | ||
* const path = useGetPathForRecord({ record, resource, link: async (record, resource) => { | ||
* const canEdit = await canEditRecord(record, resource); | ||
@@ -40,3 +40,3 @@ * return canEdit ? 'edit' : false; | ||
*/ | ||
export declare const useGetPathForRecord: <RecordType extends RaRecord<import("../types").Identifier> = RaRecord<import("../types").Identifier>>(options: UseGetPathForRecordOptions<RecordType>) => string | false | undefined; | ||
export declare const useGetPathForRecord: <RecordType extends RaRecord<import("../types").Identifier> = RaRecord<import("../types").Identifier>>(options?: UseGetPathForRecordOptions<RecordType>) => string | false | undefined; | ||
export interface UseGetPathForRecordOptions<RecordType extends RaRecord = RaRecord> { | ||
@@ -43,0 +43,0 @@ resource?: string; |
@@ -5,5 +5,7 @@ "use strict"; | ||
var react_1 = require("react"); | ||
var useResourceContext_1 = require("../core/useResourceContext"); | ||
var useRecordContext_1 = require("../controller/record/useRecordContext"); | ||
var auth_1 = require("../auth"); | ||
var core_1 = require("../core"); | ||
var controller_1 = require("../controller"); | ||
var useGetPathForRecordCallback_1 = require("./useGetPathForRecordCallback"); | ||
var useCreatePath_1 = require("./useCreatePath"); | ||
/** | ||
@@ -17,3 +19,3 @@ * Get a path for a record, based on the current resource and the link type. | ||
* const EditLink = () => { | ||
* const path = useGetRouteForRecord(); | ||
* const path = useGetPathForRecord(); | ||
* return path ? <Link to={path}>Edit</Link> : null; | ||
@@ -24,3 +26,3 @@ * }; | ||
* const EditLink = ({ record, resource }) => { | ||
* const path = useGetRouteForRecord({ record, resource, link: 'edit' }); | ||
* const path = useGetPathForRecord({ record, resource, link: 'edit' }); | ||
* return path ? <Link to={path}>Edit</Link> : null; | ||
@@ -31,3 +33,3 @@ * }; | ||
* const EditLink = ({ record, resource }) => { | ||
* const path = useGetRouteForRecord({ record, resource, link: (record, resource) => record.canEdit ? 'edit' : false }); | ||
* const path = useGetPathForRecord({ record, resource, link: (record, resource) => record.canEdit ? 'edit' : false }); | ||
* return path ? <Link to={path}>Edit</Link> : null; | ||
@@ -38,3 +40,3 @@ * }; | ||
* const EditLink = ({ record, resource }) => { | ||
* const path = useGetRouteForRecord({ record, resource, link: async (record, resource) => { | ||
* const path = useGetPathForRecord({ record, resource, link: async (record, resource) => { | ||
* const canEdit = await canEditRecord(record, resource); | ||
@@ -47,36 +49,83 @@ * return canEdit ? 'edit' : false; | ||
var useGetPathForRecord = function (options) { | ||
if (options === void 0) { options = {}; } | ||
var link = (options || {}).link; | ||
var record = (0, controller_1.useRecordContext)(options); | ||
var resource = (0, core_1.useResourceContext)(options); | ||
var record = (0, useRecordContext_1.useRecordContext)(options); | ||
var resource = (0, useResourceContext_1.useResourceContext)(options); | ||
if (!resource) { | ||
throw new Error('Cannot generate a link for a record without a resource. You must use useGetPathForRecord within a ResourceContextProvider, or pass a resource prop.'); | ||
} | ||
var getPathForRecord = (0, useGetPathForRecordCallback_1.useGetPathForRecordCallback)(options); | ||
// we initialize the path with the link value | ||
var _a = (0, react_1.useState)(function () { | ||
getPathForRecord({ | ||
record: record, | ||
var resourceDefinition = (0, core_1.useResourceDefinition)(options); | ||
var createPath = (0, useCreatePath_1.useCreatePath)(); | ||
var _a = (0, react_1.useState)(link && typeof link !== 'function' && record != null | ||
? createPath({ | ||
resource: resource, | ||
link: link, | ||
}).then(function (resolvedLink) { | ||
if (resolvedLink === false) { | ||
// already set to false by default | ||
id: record.id, | ||
type: link, | ||
}) | ||
: false), path = _a[0], setPath = _a[1]; | ||
// in preparation for the default value, does the user have access to the show and edit pages? | ||
// (we can't run hooks conditionally, so we need to run them even though the link is specified) | ||
var canAccessShow = (0, auth_1.useCanAccess)({ | ||
action: 'show', | ||
resource: resource, | ||
record: record, | ||
enabled: link == null && resourceDefinition.hasShow, | ||
}).canAccess; | ||
var canAccessEdit = (0, auth_1.useCanAccess)({ | ||
action: 'edit', | ||
resource: resource, | ||
record: record, | ||
enabled: link == null && resourceDefinition.hasEdit, | ||
}).canAccess; | ||
(0, react_1.useEffect)(function () { | ||
if (!record) | ||
return; | ||
// Handle the inferred link type case | ||
if (link == null) { | ||
// We must check whether the resource has an edit view because if there is no | ||
// authProvider, canAccessShow will always be true | ||
if (resourceDefinition.hasShow && canAccessShow) { | ||
setPath(createPath({ | ||
resource: resource, | ||
id: record.id, | ||
type: 'show', | ||
})); | ||
return; | ||
} | ||
// update the path when the promise resolves | ||
setPath(resolvedLink); | ||
}); | ||
return false; | ||
}), path = _a[0], setPath = _a[1]; | ||
// update the path if the record changes | ||
(0, react_1.useEffect)(function () { | ||
getPathForRecord({ | ||
record: record, | ||
resource: resource, | ||
link: link, | ||
}).then(function (resolvedLink) { | ||
// update the path when the promise resolves | ||
setPath(resolvedLink); | ||
}); | ||
}, [getPathForRecord, link, record, resource]); | ||
// We must check whether the resource has an edit view because if there is no | ||
// authProvider, canAccessEdit will always be true | ||
if (resourceDefinition.hasEdit && canAccessEdit) { | ||
setPath(createPath({ | ||
resource: resource, | ||
id: record.id, | ||
type: 'edit', | ||
})); | ||
return; | ||
} | ||
} | ||
// Handle the link function case | ||
if (typeof link === 'function') { | ||
var linkResult = link(record, resource); | ||
if (linkResult instanceof Promise) { | ||
linkResult.then(function (resolvedPath) { return setPath(resolvedPath); }); | ||
return; | ||
} | ||
setPath(linkResult | ||
? createPath({ | ||
resource: resource, | ||
id: record.id, | ||
type: linkResult, | ||
}) | ||
: false); | ||
} | ||
}, [ | ||
createPath, | ||
canAccessShow, | ||
canAccessEdit, | ||
link, | ||
record, | ||
resource, | ||
resourceDefinition.hasEdit, | ||
resourceDefinition.hasShow, | ||
]); | ||
return path; | ||
@@ -83,0 +132,0 @@ }; |
@@ -41,47 +41,82 @@ "use strict"; | ||
var react_1 = require("react"); | ||
var core_1 = require("../core"); | ||
var useResourceContext_1 = require("../core/useResourceContext"); | ||
var useResourceDefinitions_1 = require("../core/useResourceDefinitions"); | ||
var useCanAccessCallback_1 = require("../auth/useCanAccessCallback"); | ||
var useCreatePath_1 = require("./useCreatePath"); | ||
var useGetPathForRecordCallback = function (options) { | ||
if (options === void 0) { options = {}; } | ||
var resource = (0, core_1.useResourceContext)(options); | ||
var resourceDefinitions = (0, core_1.useResourceDefinitions)(); | ||
var resource = (0, useResourceContext_1.useResourceContext)(options); | ||
var resourceDefinitions = (0, useResourceDefinitions_1.useResourceDefinitions)(); | ||
var createPath = (0, useCreatePath_1.useCreatePath)(); | ||
var canAccess = (0, useCanAccessCallback_1.useCanAccessCallback)(); | ||
return (0, react_1.useCallback)(function (params) { return __awaiter(void 0, void 0, void 0, function () { | ||
var _a, link, record, finalResource, resourceDefinition, linkFunc, defaultLink, isLinkFalse, linkResult, linkResultIsPromise; | ||
var _b, _c, _d; | ||
var _a, link, record, finalResource, resourceDefinition, _b, canAccessShow, canAccessEdit, linkFunc, linkResult, linkResultIsPromise, resolvedLink; | ||
var _c, _d; | ||
return __generator(this, function (_e) { | ||
_a = params || {}, link = _a.link, record = _a.record; | ||
finalResource = (_b = params.resource) !== null && _b !== void 0 ? _b : resource; | ||
if (!finalResource) { | ||
throw new Error('Cannot generate a link for a record without a resource. You must use useGetPathForRecordCallback within a ResourceContextProvider, or pass a resource parameter.'); | ||
} | ||
resourceDefinition = (_c = resourceDefinitions[finalResource]) !== null && _c !== void 0 ? _c : {}; | ||
linkFunc = typeof link === 'function' ? link : function () { return link; }; | ||
defaultLink = resourceDefinition.hasShow | ||
? 'show' | ||
: resourceDefinition.hasEdit | ||
? 'edit' | ||
: false; | ||
isLinkFalse = link === false || (link == null && defaultLink === false); | ||
if (record == null || isLinkFalse) { | ||
return [2 /*return*/, false]; | ||
} | ||
linkResult = (_d = linkFunc(record, finalResource)) !== null && _d !== void 0 ? _d : defaultLink; | ||
linkResultIsPromise = isPromise(linkResult); | ||
if (linkResultIsPromise) { | ||
return [2 /*return*/, linkResult.then(function (resolvedLink) { | ||
if (resolvedLink === false) { | ||
// already set to false by default | ||
return; | ||
} | ||
return createPath({ | ||
switch (_e.label) { | ||
case 0: | ||
_a = params || {}, link = _a.link, record = _a.record; | ||
finalResource = (_c = params.resource) !== null && _c !== void 0 ? _c : resource; | ||
if (!finalResource) { | ||
throw new Error('Cannot generate a link for a record without a resource. You must use useGetPathForRecordCallback within a ResourceContextProvider, or pass a resource parameter.'); | ||
} | ||
resourceDefinition = (_d = resourceDefinitions[finalResource]) !== null && _d !== void 0 ? _d : {}; | ||
if (record == null || link === false) { | ||
return [2 /*return*/, false]; | ||
} | ||
if (!(link == null)) return [3 /*break*/, 2]; | ||
return [4 /*yield*/, Promise.all([ | ||
resourceDefinition.hasShow | ||
? canAccess({ | ||
action: 'show', | ||
resource: finalResource, | ||
record: record, | ||
}) | ||
: Promise.resolve(false), | ||
resourceDefinition.hasEdit | ||
? canAccess({ | ||
action: 'edit', | ||
resource: finalResource, | ||
record: record, | ||
}) | ||
: Promise.resolve(false), | ||
])]; | ||
case 1: | ||
_b = _e.sent(), canAccessShow = _b[0], canAccessEdit = _b[1]; | ||
if (canAccessShow) { | ||
return [2 /*return*/, createPath({ | ||
resource: finalResource, | ||
id: record.id, | ||
type: 'show', | ||
})]; | ||
} | ||
if (canAccessEdit) { | ||
return [2 /*return*/, createPath({ | ||
resource: finalResource, | ||
id: record.id, | ||
type: 'edit', | ||
})]; | ||
} | ||
return [2 /*return*/, false]; | ||
case 2: | ||
linkFunc = typeof link === 'function' ? link : function () { return link; }; | ||
linkResult = linkFunc(record, finalResource); | ||
if (linkResult === false) { | ||
return [2 /*return*/, false]; | ||
} | ||
linkResultIsPromise = isPromise(linkResult); | ||
if (!linkResultIsPromise) return [3 /*break*/, 4]; | ||
return [4 /*yield*/, linkResult]; | ||
case 3: | ||
resolvedLink = _e.sent(); | ||
if (resolvedLink === false) { | ||
// already set to false by default | ||
return [2 /*return*/]; | ||
} | ||
return [2 /*return*/, createPath({ | ||
resource: finalResource, | ||
id: record.id, | ||
type: resolvedLink, | ||
}); | ||
})]; | ||
} | ||
return [2 /*return*/, linkResult === false || linkResultIsPromise | ||
? false | ||
: createPath({ | ||
})]; | ||
case 4: return [2 /*return*/, createPath({ | ||
resource: finalResource, | ||
@@ -91,4 +126,5 @@ id: record.id, | ||
})]; | ||
} | ||
}); | ||
}); }, [createPath, resourceDefinitions, resource]); | ||
}); }, [canAccess, createPath, resourceDefinitions, resource]); | ||
}; | ||
@@ -95,0 +131,0 @@ exports.useGetPathForRecordCallback = useGetPathForRecordCallback; |
import * as React from 'react'; | ||
import { ReactNode } from 'react'; | ||
/** | ||
@@ -17,2 +18,5 @@ * This is a storybook decorator that wrap the story inside a fake browser. | ||
export declare const FakeBrowserDecorator: (Story: any, context: any) => React.JSX.Element; | ||
export declare const Browser: ({ children }: { | ||
children: ReactNode; | ||
}) => React.JSX.Element; | ||
//# sourceMappingURL=FakeBrowser.d.ts.map |
@@ -26,3 +26,3 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.FakeBrowserDecorator = void 0; | ||
exports.Browser = exports.FakeBrowserDecorator = void 0; | ||
var React = __importStar(require("react")); | ||
@@ -47,3 +47,3 @@ var routing_1 = require("../routing"); | ||
return (React.createElement(routing_1.TestMemoryRouter, { initialEntries: context.parameters.initialEntries }, | ||
React.createElement(Browser, null, | ||
React.createElement(exports.Browser, null, | ||
React.createElement(Story, null)))); | ||
@@ -62,2 +62,3 @@ }; | ||
}; | ||
exports.Browser = Browser; | ||
var BrowserBar = function () { | ||
@@ -64,0 +65,0 @@ var location = (0, react_router_dom_1.useLocation)(); |
import { ComponentType, ReactElement, ReactNode } from 'react'; | ||
import { FieldPath } from 'react-hook-form'; | ||
import { WithPermissionsChildrenParams } from './auth/WithPermissions'; | ||
@@ -57,5 +58,11 @@ import { AuthActionType } from './auth/types'; | ||
getIdentity?: (params?: QueryFunctionContext) => Promise<UserIdentity>; | ||
getPermissions: (params: any & QueryFunctionContext) => Promise<any>; | ||
getPermissions?: (params: any & QueryFunctionContext) => Promise<any>; | ||
handleCallback?: (params?: QueryFunctionContext) => Promise<AuthRedirectResult | void | any>; | ||
canAccess?: <RecordType extends Record<string, any> = Record<string, any>>(params: QueryFunctionContext & { | ||
action: string; | ||
resource: string; | ||
record?: RecordType; | ||
}) => Promise<boolean>; | ||
[key: string]: any; | ||
supportAbortSignal?: boolean; | ||
}; | ||
@@ -114,2 +121,3 @@ export type AuthRedirectResult = { | ||
data: RecordType; | ||
meta?: any; | ||
} | ||
@@ -123,2 +131,3 @@ export interface GetManyParams<RecordType extends RaRecord = any> { | ||
data: RecordType[]; | ||
meta?: any; | ||
} | ||
@@ -151,2 +160,3 @@ export interface GetManyReferenceParams { | ||
data: RecordType; | ||
meta?: any; | ||
} | ||
@@ -160,2 +170,3 @@ export interface UpdateManyParams<T = any> { | ||
data?: RecordType['id'][]; | ||
meta?: any; | ||
} | ||
@@ -168,2 +179,3 @@ export interface CreateParams<T = any> { | ||
data: RecordType; | ||
meta?: any; | ||
} | ||
@@ -177,2 +189,3 @@ export interface DeleteParams<RecordType extends RaRecord = any> { | ||
data: RecordType; | ||
meta?: any; | ||
} | ||
@@ -185,2 +198,3 @@ export interface DeleteManyParams<RecordType extends RaRecord = any> { | ||
data?: RecordType['id'][]; | ||
meta?: any; | ||
} | ||
@@ -272,2 +286,9 @@ export type DataProviderResult<RecordType extends RaRecord = any> = CreateResult<RecordType> | DeleteResult<RecordType> | DeleteManyResult | GetListResult<RecordType> | GetManyResult<RecordType> | GetManyReferenceResult<RecordType> | GetOneResult<RecordType> | UpdateResult<RecordType> | UpdateManyResult; | ||
}; | ||
export type HintedString<KnownValues extends string> = AnyString | KnownValues; | ||
export type RecordValues = Record<string, any>; | ||
export type RecordPath<TRecordValues extends RecordValues> = FieldPath<TRecordValues>; | ||
export type ExtractRecordPaths<T extends RecordValues> = [ | ||
T | ||
] extends [never] ? string : RecordPath<T>; | ||
export type AnyString = string & {}; | ||
//# sourceMappingURL=types.d.ts.map |
@@ -1,2 +0,2 @@ | ||
import { Call, Objects } from 'hotscript'; | ||
import { ExtractRecordPaths } from '../types'; | ||
/** | ||
@@ -19,7 +19,5 @@ * A hook that gets the value of a field of the current record. | ||
defaultValue?: any; | ||
source: Call<Objects.AllPaths, RecordType> extends never ? AnyString : Call<Objects.AllPaths, RecordType>; | ||
source: ExtractRecordPaths<RecordType>; | ||
record?: RecordType; | ||
} | ||
type AnyString = string & {}; | ||
export {}; | ||
//# sourceMappingURL=useFieldValue.d.ts.map |
@@ -10,36 +10,33 @@ import * as React from 'react'; | ||
* | ||
* By default this component is optimistic: it does not block | ||
* rendering children when checking authentication, but this mode | ||
* can be turned off by setting `requireAuth` to true. | ||
* | ||
* You can set additional `authParams` at will if your authProvider | ||
* requires it. | ||
* | ||
* @see useAuthState | ||
* | ||
* @example | ||
* import { Admin, CustomRoutes, Authenticated } from 'react-admin'; | ||
* import { Admin, CustomRoutes, Authenticated } from 'react-admin'; | ||
* | ||
* const customRoutes = [ | ||
* <Route | ||
* path="/foo" | ||
* element={ | ||
* <Authenticated authParams={{ foo: 'bar' }}> | ||
* <Foo /> | ||
* </Authenticated> | ||
* } | ||
* /> | ||
* ]; | ||
* const App = () => ( | ||
* <Admin> | ||
* <CustomRoutes>{customRoutes}</CustomRoutes> | ||
* </Admin> | ||
* ); | ||
* const customRoutes = [ | ||
* <Route | ||
* path="/foo" | ||
* element={ | ||
* <Authenticated authParams={{ foo: 'bar' }}> | ||
* <Foo /> | ||
* </Authenticated> | ||
* } | ||
* /> | ||
* ]; | ||
* const App = () => ( | ||
* <Admin> | ||
* <CustomRoutes>{customRoutes}</CustomRoutes> | ||
* </Admin> | ||
* ); | ||
*/ | ||
export declare const Authenticated: (props: AuthenticatedProps) => React.JSX.Element | null; | ||
export declare const Authenticated: (props: AuthenticatedProps) => string | number | boolean | Iterable<React.ReactNode> | React.JSX.Element | null; | ||
export interface AuthenticatedProps { | ||
children: ReactNode; | ||
authParams?: object; | ||
loading?: ReactNode; | ||
/** | ||
* @deprecated Authenticated now never renders children when not authenticated. | ||
*/ | ||
requireAuth?: boolean; | ||
} | ||
//# sourceMappingURL=Authenticated.d.ts.map |
import * as React from 'react'; | ||
import useAuthState from './useAuthState'; | ||
import { useAuthenticated } from './useAuthenticated'; | ||
/** | ||
@@ -10,41 +10,32 @@ * Restrict access to children to authenticated users. | ||
* | ||
* By default this component is optimistic: it does not block | ||
* rendering children when checking authentication, but this mode | ||
* can be turned off by setting `requireAuth` to true. | ||
* | ||
* You can set additional `authParams` at will if your authProvider | ||
* requires it. | ||
* | ||
* @see useAuthState | ||
* | ||
* @example | ||
* import { Admin, CustomRoutes, Authenticated } from 'react-admin'; | ||
* import { Admin, CustomRoutes, Authenticated } from 'react-admin'; | ||
* | ||
* const customRoutes = [ | ||
* <Route | ||
* path="/foo" | ||
* element={ | ||
* <Authenticated authParams={{ foo: 'bar' }}> | ||
* <Foo /> | ||
* </Authenticated> | ||
* } | ||
* /> | ||
* ]; | ||
* const App = () => ( | ||
* <Admin> | ||
* <CustomRoutes>{customRoutes}</CustomRoutes> | ||
* </Admin> | ||
* ); | ||
* const customRoutes = [ | ||
* <Route | ||
* path="/foo" | ||
* element={ | ||
* <Authenticated authParams={{ foo: 'bar' }}> | ||
* <Foo /> | ||
* </Authenticated> | ||
* } | ||
* /> | ||
* ]; | ||
* const App = () => ( | ||
* <Admin> | ||
* <CustomRoutes>{customRoutes}</CustomRoutes> | ||
* </Admin> | ||
* ); | ||
*/ | ||
export var Authenticated = function (props) { | ||
var authParams = props.authParams, children = props.children, _a = props.requireAuth, requireAuth = _a === void 0 ? false : _a; | ||
// this hook will log out if the authProvider doesn't validate that the user is authenticated | ||
var _b = useAuthState(authParams, true), isPending = _b.isPending, authenticated = _b.authenticated; | ||
// in pessimistic mode don't render the children until authenticated | ||
if ((requireAuth && isPending) || !authenticated) { | ||
return null; | ||
var authParams = props.authParams, _a = props.loading, loading = _a === void 0 ? null : _a, children = props.children; | ||
// this hook will redirect to login if the user is not authenticated | ||
var isPending = useAuthenticated({ params: authParams }).isPending; | ||
if (isPending) { | ||
return loading; | ||
} | ||
// render the children in optimistic rendering or after authenticated | ||
return React.createElement(React.Fragment, null, children); | ||
}; | ||
//# sourceMappingURL=Authenticated.js.map |
@@ -12,8 +12,14 @@ import useAuthProvider from './useAuthProvider'; | ||
export * from './AuthContext'; | ||
export * from './CanAccess'; | ||
export * from './LogoutOnMount'; | ||
export * from './types'; | ||
export * from './useAuthenticated'; | ||
export * from './useCanAccess'; | ||
export * from './useCanAccessResources'; | ||
export * from './useCanAccessCallback'; | ||
export * from './useCheckAuth'; | ||
export * from './useGetIdentity'; | ||
export * from './useHandleAuthCallback'; | ||
export * from './useIsAuthPending'; | ||
export * from './useRequireAccess'; | ||
export * from './addRefreshAuthToAuthProvider'; | ||
@@ -20,0 +26,0 @@ export * from './addRefreshAuthToDataProvider'; |
@@ -12,8 +12,14 @@ import useAuthProvider from './useAuthProvider'; | ||
export * from './AuthContext'; | ||
export * from './CanAccess'; | ||
export * from './LogoutOnMount'; | ||
export * from './types'; | ||
export * from './useAuthenticated'; | ||
export * from './useCanAccess'; | ||
export * from './useCanAccessResources'; | ||
export * from './useCanAccessCallback'; | ||
export * from './useCheckAuth'; | ||
export * from './useGetIdentity'; | ||
export * from './useHandleAuthCallback'; | ||
export * from './useIsAuthPending'; | ||
export * from './useRequireAccess'; | ||
export * from './addRefreshAuthToAuthProvider'; | ||
@@ -20,0 +26,0 @@ export * from './addRefreshAuthToDataProvider'; |
@@ -13,20 +13,26 @@ import { UseQueryOptions } from '@tanstack/react-query'; | ||
* @example | ||
* import { Admin, CustomRoutes, useAuthenticated } from 'react-admin'; | ||
* const FooPage = () => { | ||
* useAuthenticated(); | ||
* return <Foo />; | ||
* } | ||
* const customRoutes = [ | ||
* <Route path="/foo" element={<FooPage />} /> | ||
* ]; | ||
* const App = () => ( | ||
* <Admin> | ||
* <CustomRoutes>{customRoutes}</CustomRoutes> | ||
* </Admin> | ||
* ); | ||
* import { Admin, CustomRoutes, useAuthenticated } from 'react-admin'; | ||
* | ||
* const FooPage = () => { | ||
* const { isPending } = useAuthenticated(); | ||
* if (isPending) return null; | ||
* return <Foo />; | ||
* } | ||
* | ||
* const customRoutes = [ | ||
* <Route path="/foo" element={<FooPage />} /> | ||
* ]; | ||
* | ||
* const App = () => ( | ||
* <Admin> | ||
* <CustomRoutes>{customRoutes}</CustomRoutes> | ||
* </Admin> | ||
* ); | ||
*/ | ||
export declare const useAuthenticated: <ParamsType = any>({ params, ...options }?: UseAuthenticatedOptions<ParamsType>) => void; | ||
export declare const useAuthenticated: <ParamsType = any>({ params, logoutOnFailure, ...options }?: UseAuthenticatedOptions<ParamsType>) => import("./useAuthState").UseAuthStateResult<any>; | ||
export type UseAuthenticatedOptions<ParamsType> = Omit<UseQueryOptions<boolean, any> & { | ||
params?: ParamsType; | ||
}, 'queryKey' | 'queryFn'>; | ||
}, 'queryKey' | 'queryFn'> & { | ||
logoutOnFailure?: boolean; | ||
}; | ||
//# sourceMappingURL=useAuthenticated.d.ts.map |
@@ -24,22 +24,26 @@ var __rest = (this && this.__rest) || function (s, e) { | ||
* @example | ||
* import { Admin, CustomRoutes, useAuthenticated } from 'react-admin'; | ||
* const FooPage = () => { | ||
* useAuthenticated(); | ||
* return <Foo />; | ||
* } | ||
* const customRoutes = [ | ||
* <Route path="/foo" element={<FooPage />} /> | ||
* ]; | ||
* const App = () => ( | ||
* <Admin> | ||
* <CustomRoutes>{customRoutes}</CustomRoutes> | ||
* </Admin> | ||
* ); | ||
* import { Admin, CustomRoutes, useAuthenticated } from 'react-admin'; | ||
* | ||
* const FooPage = () => { | ||
* const { isPending } = useAuthenticated(); | ||
* if (isPending) return null; | ||
* return <Foo />; | ||
* } | ||
* | ||
* const customRoutes = [ | ||
* <Route path="/foo" element={<FooPage />} /> | ||
* ]; | ||
* | ||
* const App = () => ( | ||
* <Admin> | ||
* <CustomRoutes>{customRoutes}</CustomRoutes> | ||
* </Admin> | ||
* ); | ||
*/ | ||
export var useAuthenticated = function (_a) { | ||
if (_a === void 0) { _a = {}; } | ||
var params = _a.params, options = __rest(_a, ["params"]); | ||
useAuthState(params !== null && params !== void 0 ? params : emptyParams, true, options); | ||
var params = _a.params, _b = _a.logoutOnFailure, logoutOnFailure = _b === void 0 ? true : _b, options = __rest(_a, ["params", "logoutOnFailure"]); | ||
return useAuthState(params !== null && params !== void 0 ? params : emptyParams, logoutOnFailure, options); | ||
}; | ||
var emptyParams = {}; | ||
//# sourceMappingURL=useAuthenticated.js.map |
@@ -46,5 +46,5 @@ import { QueryObserverResult, UseQueryOptions } from '@tanstack/react-query'; | ||
export type UseAuthStateResult<ErrorType = Error> = QueryObserverResult<boolean, ErrorType> & { | ||
authenticated: boolean; | ||
authenticated?: QueryObserverResult<boolean, ErrorType>['data']; | ||
}; | ||
export default useAuthState; | ||
//# sourceMappingURL=useAuthState.d.ts.map |
@@ -77,3 +77,3 @@ var __assign = (this && this.__assign) || function () { | ||
var onSuccess = queryOptions.onSuccess, onError = queryOptions.onError, onSettled = queryOptions.onSettled, options = __rest(queryOptions, ["onSuccess", "onError", "onSettled"]); | ||
var result = useQuery(__assign({ queryKey: ['auth', 'checkAuth', params], queryFn: function (_a) { | ||
var queryResult = useQuery(__assign({ queryKey: ['auth', 'checkAuth', params], queryFn: function (_a) { | ||
var signal = _a.signal; | ||
@@ -98,40 +98,58 @@ // The authProvider is optional in react-admin | ||
var onErrorEvent = useEvent(onError !== null && onError !== void 0 ? onError : (function (error) { | ||
if (!logoutOnFailure) | ||
return; | ||
var loginUrl = removeDoubleSlashes("".concat(basename, "/").concat(defaultAuthParams.loginUrl)); | ||
if (logoutOnFailure) { | ||
logout({}, error && error.redirectTo != null | ||
? error.redirectTo | ||
: loginUrl); | ||
var shouldSkipNotify = error && error.message === false; | ||
!shouldSkipNotify && | ||
notify(getErrorMessage(error, 'ra.auth.auth_check_error'), { type: 'error' }); | ||
} | ||
logout({}, error && error.redirectTo != null | ||
? error.redirectTo | ||
: loginUrl); | ||
var shouldSkipNotify = error && error.message === false; | ||
!shouldSkipNotify && | ||
notify(getErrorMessage(error, 'ra.auth.auth_check_error'), { | ||
type: 'error', | ||
}); | ||
})); | ||
useEffect(function () { | ||
if (result.data === undefined || result.isFetching) | ||
if (queryResult.data === undefined || queryResult.isFetching) | ||
return; | ||
onSuccessEvent(result.data); | ||
}, [onSuccessEvent, result.data, result.isFetching]); | ||
if (queryOptions.enabled === false) | ||
return; | ||
onSuccessEvent(queryResult.data); | ||
}, [ | ||
onSuccessEvent, | ||
queryResult.data, | ||
queryResult.isFetching, | ||
queryOptions.enabled, | ||
]); | ||
useEffect(function () { | ||
if (result.error == null || result.isFetching) | ||
if (queryResult.error == null || queryResult.isFetching) | ||
return; | ||
onErrorEvent(result.error); | ||
}, [onErrorEvent, result.error, result.isFetching]); | ||
if (queryOptions.enabled === false) | ||
return; | ||
onErrorEvent(queryResult.error); | ||
}, [ | ||
onErrorEvent, | ||
queryResult.error, | ||
queryResult.isFetching, | ||
queryOptions.enabled, | ||
]); | ||
useEffect(function () { | ||
if (result.status === 'pending' || result.isFetching) | ||
if (queryResult.status === 'pending' || queryResult.isFetching) | ||
return; | ||
onSettledEvent(result.data, result.error); | ||
if (queryOptions.enabled === false) | ||
return; | ||
onSettledEvent(queryResult.data, queryResult.error); | ||
}, [ | ||
onSettledEvent, | ||
result.data, | ||
result.error, | ||
result.status, | ||
result.isFetching, | ||
queryResult.data, | ||
queryResult.error, | ||
queryResult.status, | ||
queryResult.isFetching, | ||
queryOptions.enabled, | ||
]); | ||
return useMemo(function () { | ||
var _a; | ||
return __assign(__assign({}, result), { | ||
// If the data is undefined and the query isn't loading anymore, it means the query failed. | ||
// In that case, we set authenticated to false unless there's no authProvider. | ||
authenticated: ((_a = result.data) !== null && _a !== void 0 ? _a : result.isLoading) ? true : authProvider == null }); | ||
}, [authProvider, result]); | ||
var result = useMemo(function () { | ||
return __assign(__assign({}, queryResult), { authenticated: queryResult.error ? false : queryResult.data }); | ||
}, [queryResult]); | ||
return authProvider != null | ||
? result | ||
: noAuthProviderQueryResult; | ||
}; | ||
@@ -147,2 +165,29 @@ export default useAuthState; | ||
var noop = function () { }; | ||
var noAuthProviderQueryResult = { | ||
authenticated: true, | ||
data: true, | ||
dataUpdatedAt: 0, | ||
error: null, | ||
errorUpdatedAt: 0, | ||
errorUpdateCount: 0, | ||
failureCount: 0, | ||
failureReason: null, | ||
fetchStatus: 'idle', | ||
isError: false, | ||
isInitialLoading: false, | ||
isLoading: false, | ||
isLoadingError: false, | ||
isFetched: true, | ||
isFetchedAfterMount: true, | ||
isFetching: false, | ||
isPaused: false, | ||
isPlaceholderData: false, | ||
isPending: false, | ||
isRefetchError: false, | ||
isRefetching: false, | ||
isStale: false, | ||
isSuccess: true, | ||
status: 'success', | ||
refetch: function () { return Promise.resolve(noAuthProviderQueryResult); }, | ||
}; | ||
//# sourceMappingURL=useAuthState.js.map |
import { useCallback } from 'react'; | ||
import useAuthProvider from './useAuthProvider'; | ||
var getPermissionsWithoutProvider = function () { return Promise.resolve([]); }; | ||
/** | ||
@@ -40,7 +39,8 @@ * Get a callback for calling the authProvider.getPermissions() method. | ||
// react-query requires the query to return something | ||
return authProvider | ||
? authProvider | ||
if (authProvider && authProvider.getPermissions) { | ||
return authProvider | ||
.getPermissions(params) | ||
.then(function (result) { return result !== null && result !== void 0 ? result : null; }) | ||
: getPermissionsWithoutProvider(); | ||
.then(function (result) { return result !== null && result !== void 0 ? result : null; }); | ||
} | ||
return Promise.resolve([]); | ||
}, [authProvider]); | ||
@@ -47,0 +47,0 @@ return getPermissions; |
@@ -103,3 +103,3 @@ var __assign = (this && this.__assign) || function () { | ||
var _a = queryParams !== null && queryParams !== void 0 ? queryParams : {}, onSuccess = _a.onSuccess, onError = _a.onError, onSettled = _a.onSettled, queryOptions = __rest(_a, ["onSuccess", "onError", "onSettled"]); | ||
var result = useQuery(__assign({ queryKey: ['auth', 'getPermissions', params], queryFn: function (_a) { | ||
var queryResult = useQuery(__assign({ queryKey: ['auth', 'getPermissions', params], queryFn: function (_a) { | ||
var signal = _a.signal; | ||
@@ -111,4 +111,5 @@ return __awaiter(void 0, void 0, void 0, function () { | ||
case 0: | ||
if (!authProvider) | ||
return [2 /*return*/, Promise.resolve([])]; | ||
if (!authProvider || !authProvider.getPermissions) { | ||
return [2 /*return*/, []]; | ||
} | ||
return [4 /*yield*/, authProvider.getPermissions(__assign(__assign({}, params), { signal: signal }))]; | ||
@@ -131,26 +132,56 @@ case 1: | ||
useEffect(function () { | ||
if (result.data === undefined || result.isFetching) | ||
if (queryResult.data === undefined || queryResult.isFetching) | ||
return; | ||
onSuccessEvent(result.data); | ||
}, [onSuccessEvent, result.data, result.isFetching]); | ||
onSuccessEvent(queryResult.data); | ||
}, [onSuccessEvent, queryResult.data, queryResult.isFetching]); | ||
useEffect(function () { | ||
if (result.error == null || result.isFetching) | ||
if (queryResult.error == null || queryResult.isFetching) | ||
return; | ||
onErrorEvent(result.error); | ||
}, [onErrorEvent, result.error, result.isFetching]); | ||
onErrorEvent(queryResult.error); | ||
}, [onErrorEvent, queryResult.error, queryResult.isFetching]); | ||
useEffect(function () { | ||
if (result.status === 'pending' || result.isFetching) | ||
if (queryResult.status === 'pending' || queryResult.isFetching) | ||
return; | ||
onSettledEvent(result.data, result.error); | ||
onSettledEvent(queryResult.data, queryResult.error); | ||
}, [ | ||
onSettledEvent, | ||
result.data, | ||
result.error, | ||
result.status, | ||
result.isFetching, | ||
queryResult.data, | ||
queryResult.error, | ||
queryResult.status, | ||
queryResult.isFetching, | ||
]); | ||
return useMemo(function () { return (__assign(__assign({}, result), { permissions: result.data })); }, [result]); | ||
var result = useMemo(function () { return (__assign(__assign({}, queryResult), { permissions: queryResult.data })); }, [queryResult]); | ||
return !authProvider || !authProvider.getPermissions | ||
? fakeQueryResult | ||
: result; | ||
}; | ||
export default usePermissions; | ||
var noop = function () { }; | ||
var fakeQueryResult = { | ||
permissions: undefined, | ||
data: undefined, | ||
dataUpdatedAt: 0, | ||
error: null, | ||
errorUpdatedAt: 0, | ||
errorUpdateCount: 0, | ||
failureCount: 0, | ||
failureReason: null, | ||
fetchStatus: 'idle', | ||
isError: false, | ||
isInitialLoading: false, | ||
isLoading: false, | ||
isLoadingError: false, | ||
isFetched: true, | ||
isFetchedAfterMount: true, | ||
isFetching: false, | ||
isPaused: false, | ||
isPlaceholderData: false, | ||
isPending: false, | ||
isRefetchError: false, | ||
isRefetching: false, | ||
isStale: false, | ||
isSuccess: true, | ||
status: 'success', | ||
refetch: function () { return Promise.resolve(fakeQueryResult); }, | ||
}; | ||
//# sourceMappingURL=usePermissions.js.map |
@@ -0,1 +1,2 @@ | ||
import * as React from 'react'; | ||
import { ReactElement, ComponentType } from 'react'; | ||
@@ -11,2 +12,3 @@ import { Location } from 'react-router-dom'; | ||
component?: ComponentType<any>; | ||
loading?: ComponentType<any>; | ||
location?: Location; | ||
@@ -17,4 +19,4 @@ render?: WithPermissionsChildren; | ||
} | ||
declare const _default: ComponentType<WithPermissionsProps>; | ||
declare const _default: React.ComponentType<WithPermissionsProps>; | ||
export default _default; | ||
//# sourceMappingURL=WithPermissions.d.ts.map |
@@ -23,2 +23,3 @@ var __assign = (this && this.__assign) || function () { | ||
}; | ||
import * as React from 'react'; | ||
import { Children, createElement } from 'react'; | ||
@@ -64,9 +65,14 @@ import warning from '../util/warning'; | ||
var WithPermissions = function (props) { | ||
var authParams = props.authParams, children = props.children, render = props.render, component = props.component, staticContext = props.staticContext, rest = __rest(props, ["authParams", "children", "render", "component", "staticContext"]); | ||
var authParams = props.authParams, children = props.children, render = props.render, component = props.component, _a = props.loading, Loading = _a === void 0 ? null : _a, staticContext = props.staticContext, rest = __rest(props, ["authParams", "children", "render", "component", "loading", "staticContext"]); | ||
warning((render && children && !isEmptyChildren(children)) || | ||
(render && component) || | ||
(component && children && !isEmptyChildren(children)), 'You should only use one of the `component`, `render` and `children` props in <WithPermissions>'); | ||
useAuthenticated(authParams); | ||
var permissions = usePermissions(authParams).permissions; | ||
// render even though the usePermissions() call isn't finished (optimistic rendering) | ||
var isAuthenticationPending = useAuthenticated(authParams).isPending; | ||
var _b = usePermissions(authParams, { | ||
enabled: !isAuthenticationPending, | ||
}), permissions = _b.permissions, isPendingPermissions = _b.isPending; | ||
// We must check both pending states here as if the authProvider does not implement getPermissions, isPendingPermissions will always be false | ||
if (isAuthenticationPending || isPendingPermissions) { | ||
return Loading ? React.createElement(Loading, null) : null; | ||
} | ||
if (component) { | ||
@@ -73,0 +79,0 @@ return createElement(component, __assign({ permissions: permissions }, rest)); |
@@ -29,2 +29,3 @@ var __assign = (this && this.__assign) || function () { | ||
import { useResourceContext } from '../../core'; | ||
import { useTranslate } from '../../i18n'; | ||
/** | ||
@@ -81,15 +82,21 @@ * Prepare a set of callbacks for a delete button guarded by confirmation dialog | ||
var useDeleteWithConfirmController = function (props) { | ||
var record = props.record, _a = props.redirect, redirectTo = _a === void 0 ? 'list' : _a, mutationMode = props.mutationMode, onClick = props.onClick, _b = props.mutationOptions, mutationOptions = _b === void 0 ? {} : _b, _c = props.successMessage, successMessage = _c === void 0 ? 'ra.notification.deleted' : _c; | ||
var record = props.record, _a = props.redirect, redirectTo = _a === void 0 ? 'list' : _a, mutationMode = props.mutationMode, onClick = props.onClick, _b = props.mutationOptions, mutationOptions = _b === void 0 ? {} : _b, successMessage = props.successMessage; | ||
var mutationMeta = mutationOptions.meta, otherMutationOptions = __rest(mutationOptions, ["meta"]); | ||
var resource = useResourceContext(props); | ||
var _d = useState(false), open = _d[0], setOpen = _d[1]; | ||
var _c = useState(false), open = _c[0], setOpen = _c[1]; | ||
var notify = useNotify(); | ||
var unselect = useUnselect(resource); | ||
var redirect = useRedirect(); | ||
var _e = useDelete(resource, undefined, { | ||
var translate = useTranslate(); | ||
var _d = useDelete(resource, undefined, { | ||
onSuccess: function () { | ||
setOpen(false); | ||
notify(successMessage, { | ||
notify(successMessage !== null && successMessage !== void 0 ? successMessage : "resources.".concat(resource, ".notifications.deleted"), { | ||
type: 'info', | ||
messageArgs: { smart_count: 1 }, | ||
messageArgs: { | ||
smart_count: 1, | ||
_: translate('ra.notification.deleted', { | ||
smart_count: 1, | ||
}), | ||
}, | ||
undoable: mutationMode === 'undoable', | ||
@@ -115,3 +122,3 @@ }); | ||
}, | ||
}), deleteOne = _e[0], isPending = _e[1].isPending; | ||
}), deleteOne = _d[0], isPending = _d[1].isPending; | ||
var handleDialogOpen = function (e) { | ||
@@ -118,0 +125,0 @@ setOpen(true); |
@@ -29,2 +29,3 @@ var __assign = (this && this.__assign) || function () { | ||
import { useResourceContext } from '../../core'; | ||
import { useTranslate } from '../../i18n'; | ||
/** | ||
@@ -66,3 +67,3 @@ * Prepare callback for a Delete button with undo support | ||
var useDeleteWithUndoController = function (props) { | ||
var record = props.record, _a = props.redirect, redirectTo = _a === void 0 ? 'list' : _a, onClick = props.onClick, _b = props.mutationOptions, mutationOptions = _b === void 0 ? {} : _b, _c = props.successMessage, successMessage = _c === void 0 ? 'ra.notification.deleted' : _c; | ||
var record = props.record, _a = props.redirect, redirectTo = _a === void 0 ? 'list' : _a, onClick = props.onClick, _b = props.mutationOptions, mutationOptions = _b === void 0 ? {} : _b, successMessage = props.successMessage; | ||
var mutationMeta = mutationOptions.meta, otherMutationOptions = __rest(mutationOptions, ["meta"]); | ||
@@ -73,7 +74,13 @@ var resource = useResourceContext(props); | ||
var redirect = useRedirect(); | ||
var _d = useDelete(resource, undefined, { | ||
var translate = useTranslate(); | ||
var _c = useDelete(resource, undefined, { | ||
onSuccess: function () { | ||
notify(successMessage, { | ||
notify(successMessage !== null && successMessage !== void 0 ? successMessage : "resources.".concat(resource, ".notifications.deleted"), { | ||
type: 'info', | ||
messageArgs: { smart_count: 1 }, | ||
messageArgs: { | ||
smart_count: 1, | ||
_: translate('ra.notification.deleted', { | ||
smart_count: 1, | ||
}), | ||
}, | ||
undoable: true, | ||
@@ -98,3 +105,3 @@ }); | ||
}, | ||
}), deleteOne = _d[0], isPending = _d[1].isPending; | ||
}), deleteOne = _c[0], isPending = _c[1].isPending; | ||
var handleDelete = useCallback(function (event) { | ||
@@ -101,0 +108,0 @@ event.stopPropagation(); |
@@ -36,5 +36,9 @@ import * as React from 'react'; | ||
id: Identifier; | ||
}>({ children, ...props }: CreateControllerProps<RecordType, Error, ResultRecordType> & { | ||
}, MutationOptionsError = Error>({ children, loading, ...props }: CreateBaseProps<RecordType, ResultRecordType, MutationOptionsError>) => string | number | boolean | Iterable<React.ReactNode> | React.JSX.Element | null; | ||
export interface CreateBaseProps<RecordType extends Omit<RaRecord, 'id'> = any, ResultRecordType extends RaRecord = RecordType & { | ||
id: Identifier; | ||
}, MutationOptionsError = Error> extends CreateControllerProps<RecordType, MutationOptionsError, ResultRecordType> { | ||
children: ReactNode; | ||
}) => React.JSX.Element; | ||
loading?: ReactNode; | ||
} | ||
//# sourceMappingURL=CreateBase.d.ts.map |
@@ -15,3 +15,4 @@ var __rest = (this && this.__rest) || function (s, e) { | ||
import { CreateContextProvider } from './CreateContextProvider'; | ||
import { ResourceContextProvider } from '../../core'; | ||
import { OptionalResourceContextProvider } from '../../core'; | ||
import { useIsAuthPending } from '../../auth'; | ||
/** | ||
@@ -47,9 +48,16 @@ * Call useCreateController and put the value in a CreateContext | ||
export var CreateBase = function (_a) { | ||
var children = _a.children, props = __rest(_a, ["children"]); | ||
var children = _a.children, _b = _a.loading, loading = _b === void 0 ? null : _b, props = __rest(_a, ["children", "loading"]); | ||
var controllerProps = useCreateController(props); | ||
var body = (React.createElement(CreateContextProvider, { value: controllerProps }, children)); | ||
return props.resource ? ( | ||
// support resource override via props | ||
React.createElement(ResourceContextProvider, { value: props.resource }, body)) : (body); | ||
var isAuthPending = useIsAuthPending({ | ||
resource: controllerProps.resource, | ||
action: 'create', | ||
}); | ||
if (isAuthPending && !props.disableAuthentication) { | ||
return loading; | ||
} | ||
return ( | ||
// We pass props.resource here as we don't need to create a new ResourceContext if the props is not provided | ||
React.createElement(OptionalResourceContextProvider, { value: props.resource }, | ||
React.createElement(CreateContextProvider, { value: controllerProps }, children))); | ||
}; | ||
//# sourceMappingURL=CreateBase.js.map |
@@ -49,2 +49,3 @@ import { Location } from 'react-router-dom'; | ||
resource: string; | ||
saving: boolean; | ||
} | ||
@@ -51,0 +52,0 @@ /** |
@@ -62,3 +62,3 @@ var __assign = (this && this.__assign) || function () { | ||
import { useLocation } from 'react-router-dom'; | ||
import { useAuthenticated } from '../../auth'; | ||
import { useAuthenticated, useRequireAccess } from '../../auth'; | ||
import { HttpError, useCreate, } from '../../dataProvider'; | ||
@@ -91,3 +91,2 @@ import { useRedirect } from '../../routing'; | ||
var disableAuthentication = props.disableAuthentication, record = props.record, redirectTo = props.redirect, transform = props.transform, _b = props.mutationOptions, mutationOptions = _b === void 0 ? {} : _b; | ||
useAuthenticated({ enabled: !disableAuthentication }); | ||
var resource = useResourceContext(props); | ||
@@ -97,2 +96,11 @@ if (!resource) { | ||
} | ||
var isPendingAuthenticated = useAuthenticated({ | ||
enabled: !disableAuthentication, | ||
}).isPending; | ||
var isPendingCanAccess = useRequireAccess({ | ||
action: 'create', | ||
resource: resource, | ||
// If disableAuthentication is true then isPendingAuthenticated will always be true so this hook is disabled | ||
enabled: !isPendingAuthenticated, | ||
}).isPending; | ||
var _c = useResourceDefinition(props), hasEdit = _c.hasEdit, hasShow = _c.hasShow; | ||
@@ -112,5 +120,10 @@ var finalRedirectTo = redirectTo !== null && redirectTo !== void 0 ? redirectTo : getDefaultRedirectRoute(hasShow, hasEdit); | ||
} | ||
notify('ra.notification.created', { | ||
notify("resources.".concat(resource, ".notifications.created"), { | ||
type: 'info', | ||
messageArgs: { smart_count: 1 }, | ||
messageArgs: { | ||
smart_count: 1, | ||
_: translate("ra.notification.created", { | ||
smart_count: 1, | ||
}), | ||
}, | ||
}); | ||
@@ -189,3 +202,3 @@ redirect(finalRedirectTo, resource, data.id, data); | ||
isLoading: false, | ||
isPending: saving, | ||
isPending: disableAuthentication ? false : isPendingCanAccess, | ||
saving: saving, | ||
@@ -192,0 +205,0 @@ defaultTitle: defaultTitle, |
@@ -34,5 +34,7 @@ import * as React from 'react'; | ||
*/ | ||
export declare const EditBase: <RecordType extends RaRecord<import("../../types").Identifier> = any>({ children, ...props }: { | ||
export declare const EditBase: <RecordType extends RaRecord<import("../../types").Identifier> = any, ErrorType = Error>({ children, loading, ...props }: EditBaseProps<RecordType, ErrorType>) => string | number | boolean | Iterable<React.ReactNode> | React.JSX.Element | null; | ||
export interface EditBaseProps<RecordType extends RaRecord = RaRecord, ErrorType = Error> extends EditControllerProps<RecordType, ErrorType> { | ||
children: ReactNode; | ||
} & EditControllerProps<RecordType, Error>) => React.JSX.Element; | ||
loading?: ReactNode; | ||
} | ||
//# sourceMappingURL=EditBase.d.ts.map |
@@ -15,3 +15,4 @@ var __rest = (this && this.__rest) || function (s, e) { | ||
import { EditContextProvider } from './EditContextProvider'; | ||
import { ResourceContextProvider } from '../../core'; | ||
import { OptionalResourceContextProvider } from '../../core'; | ||
import { useIsAuthPending } from '../../auth'; | ||
/** | ||
@@ -47,9 +48,16 @@ * Call useEditController and put the value in a EditContext | ||
export var EditBase = function (_a) { | ||
var children = _a.children, props = __rest(_a, ["children"]); | ||
var children = _a.children, _b = _a.loading, loading = _b === void 0 ? null : _b, props = __rest(_a, ["children", "loading"]); | ||
var controllerProps = useEditController(props); | ||
var body = (React.createElement(EditContextProvider, { value: controllerProps }, children)); | ||
return props.resource ? ( | ||
// support resource override via props | ||
React.createElement(ResourceContextProvider, { value: props.resource }, body)) : (body); | ||
var isAuthPending = useIsAuthPending({ | ||
resource: controllerProps.resource, | ||
action: 'edit', | ||
}); | ||
if (isAuthPending && !props.disableAuthentication) { | ||
return loading; | ||
} | ||
return ( | ||
// We pass props.resource here as we don't need to create a new ResourceContext if the props is not provided | ||
React.createElement(OptionalResourceContextProvider, { value: props.resource }, | ||
React.createElement(EditContextProvider, { value: controllerProps }, children))); | ||
}; | ||
//# sourceMappingURL=EditBase.js.map |
@@ -46,2 +46,3 @@ import { RaRecord, MutationMode, TransformData } from '../../types'; | ||
resource: string; | ||
saving: boolean; | ||
} | ||
@@ -48,0 +49,0 @@ export interface EditControllerLoadingResult<RecordType extends RaRecord = any> extends EditControllerBaseResult<RecordType> { |
@@ -61,3 +61,3 @@ var __assign = (this && this.__assign) || function () { | ||
import { useParams } from 'react-router-dom'; | ||
import { useAuthenticated } from '../../auth'; | ||
import { useAuthenticated, useRequireAccess } from '../../auth'; | ||
import { useRedirect } from '../../routing'; | ||
@@ -93,4 +93,3 @@ import { useNotify } from '../../notification'; | ||
if (props === void 0) { props = {}; } | ||
var disableAuthentication = props.disableAuthentication, propsId = props.id, _a = props.mutationMode, mutationMode = _a === void 0 ? 'undoable' : _a, _b = props.mutationOptions, mutationOptions = _b === void 0 ? {} : _b, _c = props.queryOptions, queryOptions = _c === void 0 ? {} : _c, _d = props.redirect, redirectTo = _d === void 0 ? DefaultRedirect : _d, transform = props.transform; | ||
useAuthenticated({ enabled: !disableAuthentication }); | ||
var _a = props.disableAuthentication, disableAuthentication = _a === void 0 ? false : _a, propsId = props.id, _b = props.mutationMode, mutationMode = _b === void 0 ? 'undoable' : _b, _c = props.mutationOptions, mutationOptions = _c === void 0 ? {} : _c, _d = props.queryOptions, queryOptions = _d === void 0 ? {} : _d, _e = props.redirect, redirectTo = _e === void 0 ? DefaultRedirect : _e, transform = props.transform; | ||
var resource = useResourceContext(props); | ||
@@ -100,2 +99,11 @@ if (!resource) { | ||
} | ||
var isPendingAuthenticated = useAuthenticated({ | ||
enabled: !disableAuthentication, | ||
}).isPending; | ||
var isPendingCanAccess = useRequireAccess({ | ||
action: 'edit', | ||
resource: resource, | ||
// If disableAuthentication is true then isPendingAuthenticated will always be true so this hook is disabled | ||
enabled: !isPendingAuthenticated, | ||
}).isPending; | ||
var getRecordRepresentation = useGetRecordRepresentation(resource); | ||
@@ -113,4 +121,5 @@ var translate = useTranslate(); | ||
var mutationMeta = mutationOptions.meta, onSuccess = mutationOptions.onSuccess, onError = mutationOptions.onError, otherMutationOptions = __rest(mutationOptions, ["meta", "onSuccess", "onError"]); | ||
var _e = useMutationMiddlewares(), registerMutationMiddleware = _e.registerMutationMiddleware, getMutateWithMiddlewares = _e.getMutateWithMiddlewares, unregisterMutationMiddleware = _e.unregisterMutationMiddleware; | ||
var _f = useGetOne(resource, { id: id, meta: queryMeta }, __assign({ onError: function () { | ||
var _f = useMutationMiddlewares(), registerMutationMiddleware = _f.registerMutationMiddleware, getMutateWithMiddlewares = _f.getMutateWithMiddlewares, unregisterMutationMiddleware = _f.unregisterMutationMiddleware; | ||
var _g = useGetOne(resource, { id: id, meta: queryMeta }, __assign({ enabled: (!isPendingAuthenticated && !isPendingCanAccess) || | ||
disableAuthentication, onError: function () { | ||
notify('ra.notification.item_doesnt_exist', { | ||
@@ -121,3 +130,3 @@ type: 'error', | ||
refresh(); | ||
}, refetchOnReconnect: false, refetchOnWindowFocus: false, retry: false }, otherQueryOptions)), record = _f.data, error = _f.error, isLoading = _f.isLoading, isFetching = _f.isFetching, isPending = _f.isPending, refetch = _f.refetch; | ||
}, refetchOnReconnect: false, refetchOnWindowFocus: false, retry: false }, otherQueryOptions)), record = _g.data, error = _g.error, isLoading = _g.isLoading, isFetching = _g.isFetching, isPending = _g.isPending, refetch = _g.refetch; | ||
// eslint-disable-next-line eqeqeq | ||
@@ -138,3 +147,3 @@ if (record && record.id && record.id != id) { | ||
var recordCached = { id: id, previousData: record }; | ||
var _g = useUpdate(resource, recordCached, __assign(__assign({ onSuccess: function (data, variables, context) { return __awaiter(void 0, void 0, void 0, function () { | ||
var _h = useUpdate(resource, recordCached, __assign(__assign({ onSuccess: function (data, variables, context) { return __awaiter(void 0, void 0, void 0, function () { | ||
return __generator(this, function (_a) { | ||
@@ -144,5 +153,10 @@ if (onSuccess) { | ||
} | ||
notify('ra.notification.updated', { | ||
notify("resources.".concat(resource, ".notifications.updated"), { | ||
type: 'info', | ||
messageArgs: { smart_count: 1 }, | ||
messageArgs: { | ||
smart_count: 1, | ||
_: translate('ra.notification.updated', { | ||
smart_count: 1, | ||
}), | ||
}, | ||
undoable: mutationMode === 'undoable', | ||
@@ -182,3 +196,3 @@ }); | ||
} | ||
} }, otherMutationOptions), { mutationMode: mutationMode, returnPromise: mutationMode === 'pessimistic', getMutateWithMiddlewares: getMutateWithMiddlewares })), update = _g[0], saving = _g[1].isPending; | ||
} }, otherMutationOptions), { mutationMode: mutationMode, returnPromise: mutationMode === 'pessimistic', getMutateWithMiddlewares: getMutateWithMiddlewares })), update = _h[0], saving = _h[1].isPending; | ||
var save = useCallback(function (data, _a) { | ||
@@ -185,0 +199,0 @@ var _b = _a === void 0 ? {} : _a, onSuccessFromSave = _b.onSuccess, onErrorFromSave = _b.onError, transformFromSave = _b.transform, metaFromSave = _b.meta; |
@@ -39,5 +39,7 @@ import * as React from 'react'; | ||
*/ | ||
export declare const InfiniteListBase: <RecordType extends RaRecord<import("../../types").Identifier> = any>({ children, ...props }: InfiniteListControllerProps<RecordType> & { | ||
export declare const InfiniteListBase: <RecordType extends RaRecord<import("../../types").Identifier> = any>({ children, loading, ...props }: InfiniteListBaseProps<RecordType>) => string | number | boolean | Iterable<React.ReactNode> | React.JSX.Element | null; | ||
export interface InfiniteListBaseProps<RecordType extends RaRecord = any> extends InfiniteListControllerProps<RecordType> { | ||
children: ReactNode; | ||
}) => React.JSX.Element; | ||
loading?: ReactNode; | ||
} | ||
//# sourceMappingURL=InfiniteListBase.d.ts.map |
@@ -14,5 +14,6 @@ var __rest = (this && this.__rest) || function (s, e) { | ||
import { useInfiniteListController, } from './useInfiniteListController'; | ||
import { ResourceContextProvider } from '../../core'; | ||
import { OptionalResourceContextProvider } from '../../core'; | ||
import { ListContextProvider } from './ListContextProvider'; | ||
import { InfinitePaginationContext } from './InfinitePaginationContext'; | ||
import { useIsAuthPending } from '../../auth'; | ||
/** | ||
@@ -53,5 +54,14 @@ * Call useInfiniteListController and put the value in a ListContext | ||
export var InfiniteListBase = function (_a) { | ||
var children = _a.children, props = __rest(_a, ["children"]); | ||
var children = _a.children, _b = _a.loading, loading = _b === void 0 ? null : _b, props = __rest(_a, ["children", "loading"]); | ||
var controllerProps = useInfiniteListController(props); | ||
return (React.createElement(ResourceContextProvider, { value: props.resource }, | ||
var isAuthPending = useIsAuthPending({ | ||
resource: controllerProps.resource, | ||
action: 'list', | ||
}); | ||
if (isAuthPending && !props.disableAuthentication) { | ||
return loading; | ||
} | ||
return ( | ||
// We pass props.resource here as we don't need to create a new ResourceContext if the props is not provided | ||
React.createElement(OptionalResourceContextProvider, { value: props.resource }, | ||
React.createElement(ListContextProvider, { value: controllerProps }, | ||
@@ -58,0 +68,0 @@ React.createElement(InfinitePaginationContext.Provider, { value: { |
@@ -39,5 +39,7 @@ import * as React from 'react'; | ||
*/ | ||
export declare const ListBase: <RecordType extends RaRecord<import("../../types").Identifier> = any>({ children, ...props }: ListControllerProps<RecordType> & { | ||
export declare const ListBase: <RecordType extends RaRecord<import("../../types").Identifier> = any>({ children, loading, ...props }: ListBaseProps<RecordType>) => string | number | boolean | Iterable<React.ReactNode> | React.JSX.Element | null; | ||
export interface ListBaseProps<RecordType extends RaRecord = any> extends ListControllerProps<RecordType> { | ||
children: ReactNode; | ||
}) => React.JSX.Element; | ||
loading?: ReactNode; | ||
} | ||
//# sourceMappingURL=ListBase.d.ts.map |
@@ -14,4 +14,5 @@ var __rest = (this && this.__rest) || function (s, e) { | ||
import { useListController } from './useListController'; | ||
import { ResourceContextProvider } from '../../core'; | ||
import { OptionalResourceContextProvider } from '../../core'; | ||
import { ListContextProvider } from './ListContextProvider'; | ||
import { useIsAuthPending } from '../../auth'; | ||
/** | ||
@@ -52,6 +53,16 @@ * Call useListController and put the value in a ListContext | ||
export var ListBase = function (_a) { | ||
var children = _a.children, props = __rest(_a, ["children"]); | ||
return (React.createElement(ResourceContextProvider, { value: props.resource }, | ||
React.createElement(ListContextProvider, { value: useListController(props) }, children))); | ||
var children = _a.children, _b = _a.loading, loading = _b === void 0 ? null : _b, props = __rest(_a, ["children", "loading"]); | ||
var controllerProps = useListController(props); | ||
var isAuthPending = useIsAuthPending({ | ||
resource: controllerProps.resource, | ||
action: 'list', | ||
}); | ||
if (isAuthPending && !props.disableAuthentication) { | ||
return loading; | ||
} | ||
return ( | ||
// We pass props.resource here as we don't need to create a new ResourceContext if the props is not provided | ||
React.createElement(OptionalResourceContextProvider, { value: props.resource }, | ||
React.createElement(ListContextProvider, { value: controllerProps }, children))); | ||
}; | ||
//# sourceMappingURL=ListBase.js.map |
@@ -33,3 +33,3 @@ var __assign = (this && this.__assign) || function () { | ||
import { isValidElement, useEffect, useMemo } from 'react'; | ||
import { useAuthenticated } from '../../auth'; | ||
import { useAuthenticated, useRequireAccess } from '../../auth'; | ||
import { useTranslate } from '../../i18n'; | ||
@@ -61,6 +61,5 @@ import { useNotify } from '../../notification'; | ||
if (props === void 0) { props = {}; } | ||
var _a = props.debounce, debounce = _a === void 0 ? 500 : _a, disableAuthentication = props.disableAuthentication, disableSyncWithLocation = props.disableSyncWithLocation, _b = props.exporter, exporter = _b === void 0 ? defaultExporter : _b, filter = props.filter, filterDefaultValues = props.filterDefaultValues, _c = props.perPage, perPage = _c === void 0 ? 10 : _c, queryOptions = props.queryOptions, sort = props.sort, storeKey = props.storeKey; | ||
useAuthenticated({ enabled: !disableAuthentication }); | ||
var _a = props.debounce, debounce = _a === void 0 ? 500 : _a, _b = props.disableAuthentication, disableAuthentication = _b === void 0 ? false : _b, _c = props.disableSyncWithLocation, disableSyncWithLocation = _c === void 0 ? false : _c, _d = props.exporter, exporter = _d === void 0 ? defaultExporter : _d, filter = props.filter, filterDefaultValues = props.filterDefaultValues, _e = props.perPage, perPage = _e === void 0 ? 10 : _e, queryOptions = props.queryOptions, sort = props.sort, storeKey = props.storeKey; | ||
var resource = useResourceContext(props); | ||
var _d = queryOptions !== null && queryOptions !== void 0 ? queryOptions : {}, meta = _d.meta, otherQueryOptions = __rest(_d, ["meta"]); | ||
var _f = queryOptions !== null && queryOptions !== void 0 ? queryOptions : {}, meta = _f.meta, otherQueryOptions = __rest(_f, ["meta"]); | ||
if (!resource) { | ||
@@ -72,5 +71,14 @@ throw new Error("<InfiniteList> was called outside of a ResourceContext and without a resource prop. You must set the resource prop."); | ||
} | ||
var isPendingAuthenticated = useAuthenticated({ | ||
enabled: !disableAuthentication, | ||
}).isPending; | ||
var isPendingCanAccess = useRequireAccess({ | ||
action: 'list', | ||
resource: resource, | ||
// If disableAuthentication is true then isPendingAuthenticated will always be true so this hook is disabled | ||
enabled: !isPendingAuthenticated, | ||
}).isPending; | ||
var translate = useTranslate(); | ||
var notify = useNotify(); | ||
var _e = useListParams({ | ||
var _g = useListParams({ | ||
debounce: debounce, | ||
@@ -83,5 +91,5 @@ disableSyncWithLocation: disableSyncWithLocation, | ||
storeKey: storeKey, | ||
}), query = _e[0], queryModifiers = _e[1]; | ||
var _f = useRecordSelection({ resource: resource }), selectedIds = _f[0], selectionModifiers = _f[1]; | ||
var _g = useInfiniteGetList(resource, { | ||
}), query = _g[0], queryModifiers = _g[1]; | ||
var _h = useRecordSelection({ resource: resource }), selectedIds = _h[0], selectionModifiers = _h[1]; | ||
var _j = useInfiniteGetList(resource, { | ||
pagination: { | ||
@@ -94,3 +102,4 @@ page: query.page, | ||
meta: meta, | ||
}, __assign({ placeholderData: function (previousData) { return previousData; }, retry: false, onError: function (error) { | ||
}, __assign({ enabled: (!isPendingAuthenticated && !isPendingCanAccess) || | ||
disableAuthentication, placeholderData: function (previousData) { return previousData; }, retry: false, onError: function (error) { | ||
return notify((error === null || error === void 0 ? void 0 : error.message) || 'ra.notification.http_error', { | ||
@@ -102,3 +111,3 @@ type: 'error', | ||
}); | ||
} }, otherQueryOptions)), data = _g.data, total = _g.total, error = _g.error, isLoading = _g.isLoading, isPending = _g.isPending, isFetching = _g.isFetching, hasNextPage = _g.hasNextPage, hasPreviousPage = _g.hasPreviousPage, fetchNextPage = _g.fetchNextPage, isFetchingNextPage = _g.isFetchingNextPage, fetchPreviousPage = _g.fetchPreviousPage, isFetchingPreviousPage = _g.isFetchingPreviousPage, refetch = _g.refetch; | ||
} }, otherQueryOptions)), data = _j.data, total = _j.total, error = _j.error, isLoading = _j.isLoading, isPending = _j.isPending, isFetching = _j.isFetching, hasNextPage = _j.hasNextPage, hasPreviousPage = _j.hasPreviousPage, fetchNextPage = _j.fetchNextPage, isFetchingNextPage = _j.isFetchingNextPage, fetchPreviousPage = _j.fetchPreviousPage, isFetchingPreviousPage = _j.isFetchingPreviousPage, refetch = _j.refetch; | ||
// change page if there is no data | ||
@@ -105,0 +114,0 @@ useEffect(function () { |
@@ -24,7 +24,6 @@ var __assign = (this && this.__assign) || function () { | ||
import { isValidElement, useEffect, useMemo } from 'react'; | ||
import { useAuthenticated } from '../../auth'; | ||
import { useAuthenticated, useRequireAccess } from '../../auth'; | ||
import { useTranslate } from '../../i18n'; | ||
import { useNotify } from '../../notification'; | ||
import { useGetList, } from '../../dataProvider'; | ||
import { SORT_ASC } from './queryReducer'; | ||
import { defaultExporter } from '../../export'; | ||
@@ -34,2 +33,3 @@ import { useResourceContext, useGetResourceLabel } from '../../core'; | ||
import { useListParams } from './useListParams'; | ||
import { SORT_ASC } from './queryReducer'; | ||
/** | ||
@@ -54,4 +54,3 @@ * Prepare data for the List view | ||
if (props === void 0) { props = {}; } | ||
var _a = props.debounce, debounce = _a === void 0 ? 500 : _a, disableAuthentication = props.disableAuthentication, disableSyncWithLocation = props.disableSyncWithLocation, _b = props.exporter, exporter = _b === void 0 ? defaultExporter : _b, filter = props.filter, filterDefaultValues = props.filterDefaultValues, _c = props.perPage, perPage = _c === void 0 ? 10 : _c, _d = props.queryOptions, queryOptions = _d === void 0 ? {} : _d, _e = props.sort, sort = _e === void 0 ? defaultSort : _e, storeKey = props.storeKey; | ||
useAuthenticated({ enabled: !disableAuthentication }); | ||
var _a = props.debounce, debounce = _a === void 0 ? 500 : _a, _b = props.disableAuthentication, disableAuthentication = _b === void 0 ? false : _b, _c = props.disableSyncWithLocation, disableSyncWithLocation = _c === void 0 ? false : _c, _d = props.exporter, exporter = _d === void 0 ? defaultExporter : _d, filter = props.filter, filterDefaultValues = props.filterDefaultValues, _e = props.perPage, perPage = _e === void 0 ? 10 : _e, _f = props.queryOptions, queryOptions = _f === void 0 ? {} : _f, _g = props.sort, sort = _g === void 0 ? defaultSort : _g, storeKey = props.storeKey; | ||
var resource = useResourceContext(props); | ||
@@ -67,5 +66,14 @@ var meta = queryOptions.meta, otherQueryOptions = __rest(queryOptions, ["meta"]); | ||
} | ||
var isPendingAuthenticated = useAuthenticated({ | ||
enabled: !disableAuthentication, | ||
}).isPending; | ||
var isPendingCanAccess = useRequireAccess({ | ||
action: 'list', | ||
resource: resource, | ||
// If disableAuthentication is true then isPendingAuthenticated will always be true so this hook is disabled | ||
enabled: !isPendingAuthenticated, | ||
}).isPending; | ||
var translate = useTranslate(); | ||
var notify = useNotify(); | ||
var _f = useListParams({ | ||
var _h = useListParams({ | ||
debounce: debounce, | ||
@@ -78,8 +86,8 @@ disableSyncWithLocation: disableSyncWithLocation, | ||
storeKey: storeKey, | ||
}), query = _f[0], queryModifiers = _f[1]; | ||
var _g = useRecordSelection({ | ||
}), query = _h[0], queryModifiers = _h[1]; | ||
var _j = useRecordSelection({ | ||
resource: resource, | ||
disableSyncWithStore: storeKey === false, | ||
}), selectedIds = _g[0], selectionModifiers = _g[1]; | ||
var _h = useGetList(resource, { | ||
}), selectedIds = _j[0], selectionModifiers = _j[1]; | ||
var _k = useGetList(resource, { | ||
pagination: { | ||
@@ -92,3 +100,4 @@ page: query.page, | ||
meta: meta, | ||
}, __assign({ placeholderData: function (previousData) { return previousData; }, retry: false, onError: function (error) { | ||
}, __assign({ enabled: (!isPendingAuthenticated && !isPendingCanAccess) || | ||
disableAuthentication, placeholderData: function (previousData) { return previousData; }, retry: false, onError: function (error) { | ||
return notify((error === null || error === void 0 ? void 0 : error.message) || 'ra.notification.http_error', { | ||
@@ -100,3 +109,3 @@ type: 'error', | ||
}); | ||
} }, otherQueryOptions)), data = _h.data, pageInfo = _h.pageInfo, total = _h.total, responseMeta = _h.meta, error = _h.error, isLoading = _h.isLoading, isFetching = _h.isFetching, isPending = _h.isPending, refetch = _h.refetch; | ||
} }, otherQueryOptions)), data = _k.data, pageInfo = _k.pageInfo, total = _k.total, responseMeta = _k.meta, error = _k.error, isLoading = _k.isLoading, isFetching = _k.isFetching, isPending = _k.isPending, refetch = _k.refetch; | ||
// change page if there is no data | ||
@@ -103,0 +112,0 @@ useEffect(function () { |
import * as React from 'react'; | ||
import { ReactElement } from 'react'; | ||
import { RaRecord } from '../../types'; | ||
@@ -34,5 +33,7 @@ import { ShowControllerProps } from './useShowController'; | ||
*/ | ||
export declare const ShowBase: <RecordType extends RaRecord<import("../../types").Identifier> = any>({ children, ...props }: { | ||
children: ReactElement; | ||
} & ShowControllerProps<RecordType>) => React.JSX.Element; | ||
export declare const ShowBase: <RecordType extends RaRecord<import("../../types").Identifier> = any>({ children, loading, ...props }: ShowBaseProps<RecordType>) => string | number | boolean | Iterable<React.ReactNode> | React.JSX.Element | null; | ||
export interface ShowBaseProps<RecordType extends RaRecord = RaRecord> extends ShowControllerProps<RecordType> { | ||
children: React.ReactNode; | ||
loading?: React.ReactNode; | ||
} | ||
//# sourceMappingURL=ShowBase.d.ts.map |
@@ -15,3 +15,4 @@ var __rest = (this && this.__rest) || function (s, e) { | ||
import { ShowContextProvider } from './ShowContextProvider'; | ||
import { ResourceContextProvider } from '../../core'; | ||
import { OptionalResourceContextProvider } from '../../core'; | ||
import { useIsAuthPending } from '../../auth'; | ||
/** | ||
@@ -47,9 +48,16 @@ * Call useShowController and put the value in a ShowContext | ||
export var ShowBase = function (_a) { | ||
var children = _a.children, props = __rest(_a, ["children"]); | ||
var children = _a.children, _b = _a.loading, loading = _b === void 0 ? null : _b, props = __rest(_a, ["children", "loading"]); | ||
var controllerProps = useShowController(props); | ||
var body = (React.createElement(ShowContextProvider, { value: controllerProps }, children)); | ||
return props.resource ? ( | ||
// support resource override via props | ||
React.createElement(ResourceContextProvider, { value: props.resource }, body)) : (body); | ||
var isAuthPending = useIsAuthPending({ | ||
resource: controllerProps.resource, | ||
action: 'show', | ||
}); | ||
if (isAuthPending && !props.disableAuthentication) { | ||
return loading; | ||
} | ||
return ( | ||
// We pass props.resource here as we don't need to create a new ResourceContext if the props is not provided | ||
React.createElement(OptionalResourceContextProvider, { value: props.resource }, | ||
React.createElement(ShowContextProvider, { value: controllerProps }, children))); | ||
}; | ||
//# sourceMappingURL=ShowBase.js.map |
@@ -24,3 +24,3 @@ var __assign = (this && this.__assign) || function () { | ||
import { useParams } from 'react-router-dom'; | ||
import { useAuthenticated } from '../../auth'; | ||
import { useAuthenticated, useRequireAccess } from '../../auth'; | ||
import { useGetOne, useRefresh, } from '../../dataProvider'; | ||
@@ -65,4 +65,3 @@ import { useTranslate } from '../../i18n'; | ||
if (props === void 0) { props = {}; } | ||
var disableAuthentication = props.disableAuthentication, propsId = props.id, _a = props.queryOptions, queryOptions = _a === void 0 ? {} : _a; | ||
useAuthenticated({ enabled: !disableAuthentication }); | ||
var _a = props.disableAuthentication, disableAuthentication = _a === void 0 ? false : _a, propsId = props.id, _b = props.queryOptions, queryOptions = _b === void 0 ? {} : _b; | ||
var resource = useResourceContext(props); | ||
@@ -72,2 +71,11 @@ if (!resource) { | ||
} | ||
var isPendingAuthenticated = useAuthenticated({ | ||
enabled: !disableAuthentication, | ||
}).isPending; | ||
var isPendingCanAccess = useRequireAccess({ | ||
action: 'show', | ||
resource: resource, | ||
// If disableAuthentication is true then isPendingAuthenticated will always be true so this hook is disabled | ||
enabled: !isPendingAuthenticated, | ||
}).isPending; | ||
var getRecordRepresentation = useGetRecordRepresentation(resource); | ||
@@ -84,3 +92,4 @@ var translate = useTranslate(); | ||
var meta = queryOptions.meta, otherQueryOptions = __rest(queryOptions, ["meta"]); | ||
var _b = useGetOne(resource, { id: id, meta: meta }, __assign({ onError: function () { | ||
var _c = useGetOne(resource, { id: id, meta: meta }, __assign({ enabled: (!isPendingAuthenticated && !isPendingCanAccess) || | ||
disableAuthentication, onError: function () { | ||
notify('ra.notification.item_doesnt_exist', { | ||
@@ -91,3 +100,3 @@ type: 'error', | ||
refresh(); | ||
}, retry: false }, otherQueryOptions)), record = _b.data, error = _b.error, isLoading = _b.isLoading, isFetching = _b.isFetching, isPending = _b.isPending, refetch = _b.refetch; | ||
}, retry: false }, otherQueryOptions)), record = _c.data, error = _c.error, isLoading = _c.isLoading, isFetching = _c.isFetching, isPending = _c.isPending, refetch = _c.refetch; | ||
// eslint-disable-next-line eqeqeq | ||
@@ -94,0 +103,0 @@ if (record && record.id && record.id != id) { |
@@ -84,6 +84,6 @@ import * as React from 'react'; | ||
export var CoreAdmin = function (props) { | ||
var authProvider = props.authProvider, basename = props.basename, catchAll = props.catchAll, children = props.children, dashboard = props.dashboard, dataProvider = props.dataProvider, disableTelemetry = props.disableTelemetry, error = props.error, i18nProvider = props.i18nProvider, queryClient = props.queryClient, layout = props.layout, loading = props.loading, loginPage = props.loginPage, ready = props.ready, requireAuth = props.requireAuth, store = props.store, _a = props.title, title = _a === void 0 ? 'React Admin' : _a; | ||
var accessDenied = props.accessDenied, authenticationError = props.authenticationError, authProvider = props.authProvider, basename = props.basename, catchAll = props.catchAll, children = props.children, dashboard = props.dashboard, dataProvider = props.dataProvider, disableTelemetry = props.disableTelemetry, error = props.error, i18nProvider = props.i18nProvider, layout = props.layout, loading = props.loading, loginPage = props.loginPage, queryClient = props.queryClient, ready = props.ready, requireAuth = props.requireAuth, store = props.store, _a = props.title, title = _a === void 0 ? 'React Admin' : _a; | ||
return (React.createElement(CoreAdminContext, { authProvider: authProvider, basename: basename, dataProvider: dataProvider, i18nProvider: i18nProvider, queryClient: queryClient, store: store }, | ||
React.createElement(CoreAdminUI, { layout: layout, dashboard: dashboard, disableTelemetry: disableTelemetry, catchAll: catchAll, title: title, loading: loading, error: error, loginPage: loginPage, requireAuth: requireAuth, ready: ready }, children))); | ||
React.createElement(CoreAdminUI, { accessDenied: accessDenied, authenticationError: authenticationError, catchAll: catchAll, dashboard: dashboard, disableTelemetry: disableTelemetry, error: error, layout: layout, loading: loading, loginPage: loginPage, ready: ready, requireAuth: requireAuth, title: title }, children))); | ||
}; | ||
//# sourceMappingURL=CoreAdmin.js.map |
@@ -13,3 +13,5 @@ import * as React from 'react'; | ||
ready?: ComponentType; | ||
authenticationError?: ComponentType; | ||
accessDenied?: React.ComponentType; | ||
} | ||
//# sourceMappingURL=CoreAdminRoutes.d.ts.map |
import * as React from 'react'; | ||
import { useState, useEffect, Children } from 'react'; | ||
import { Navigate, Route, Routes } from 'react-router-dom'; | ||
import { WithPermissions, useCheckAuth, LogoutOnMount } from '../auth'; | ||
import { useScrollToTop, useCreatePath } from '../routing'; | ||
import { Children } from 'react'; | ||
import { Route, Routes } from 'react-router-dom'; | ||
import { WithPermissions, LogoutOnMount, useAuthState } from '../auth'; | ||
import { useScrollToTop } from '../routing'; | ||
import { useConfigureAdminRouterFromChildren } from './useConfigureAdminRouterFromChildren'; | ||
import { HasDashboardContextProvider } from './HasDashboardContext'; | ||
import { NavigateToFirstResource } from './NavigateToFirstResource'; | ||
export var CoreAdminRoutes = function (props) { | ||
useScrollToTop(); | ||
var createPath = useCreatePath(); | ||
var _a = useConfigureAdminRouterFromChildren(props.children), customRoutesWithLayout = _a.customRoutesWithLayout, customRoutesWithoutLayout = _a.customRoutesWithoutLayout, status = _a.status, resources = _a.resources; | ||
var Layout = props.layout, CatchAll = props.catchAll, dashboard = props.dashboard, LoadingPage = props.loading, requireAuth = props.requireAuth, Ready = props.ready; | ||
var _b = useState(requireAuth), onlyAnonymousRoutes = _b[0], setOnlyAnonymousRoutes = _b[1]; | ||
var _c = useState(requireAuth), checkAuthLoading = _c[0], setCheckAuthLoading = _c[1]; | ||
var checkAuth = useCheckAuth(); | ||
useEffect(function () { | ||
if (requireAuth) { | ||
// do not log the user out on failure to allow access to custom routes with no layout | ||
// for other routes, the LogoutOnMount component will log the user out | ||
checkAuth(undefined, false) | ||
.then(function () { | ||
setOnlyAnonymousRoutes(false); | ||
}) | ||
.catch(function () { }) | ||
.finally(function () { | ||
setCheckAuthLoading(false); | ||
}); | ||
} | ||
}, [checkAuth, requireAuth]); | ||
var Layout = props.layout, CatchAll = props.catchAll, dashboard = props.dashboard, LoadingPage = props.loading, requireAuth = props.requireAuth, Ready = props.ready, _b = props.authenticationError, AuthenticationError = _b === void 0 ? Noop : _b, _c = props.accessDenied, AccessDenied = _c === void 0 ? Noop : _c; | ||
var _d = useAuthState(undefined, | ||
// do not log the user out on failure to allow access to custom routes with no layout | ||
false, { enabled: requireAuth }), authenticated = _d.authenticated, isPendingAuthenticated = _d.isPending; | ||
if (status === 'empty') { | ||
@@ -37,3 +23,3 @@ if (!Ready) { | ||
// Note: custom routes with no layout are always rendered, regardless of the auth status | ||
if (status === 'loading' || checkAuthLoading) { | ||
if (status === 'loading' || (requireAuth && isPendingAuthenticated)) { | ||
return (React.createElement(Routes, null, | ||
@@ -44,3 +30,3 @@ customRoutesWithoutLayout, | ||
} | ||
if (onlyAnonymousRoutes) { | ||
if (requireAuth && (isPendingAuthenticated || !authenticated)) { | ||
return (React.createElement(Routes, null, | ||
@@ -57,9 +43,10 @@ customRoutesWithoutLayout, | ||
Children.map(resources, function (resource) { return (React.createElement(Route, { key: resource.props.name, path: "".concat(resource.props.name, "/*"), element: resource })); }), | ||
React.createElement(Route, { path: "/", element: dashboard ? (React.createElement(WithPermissions, { authParams: defaultAuthParams, component: dashboard })) : resources.length > 0 ? (React.createElement(Navigate, { to: createPath({ | ||
resource: resources[0].props.name, | ||
type: 'list', | ||
}) })) : null }), | ||
React.createElement(Route, { path: "/", element: dashboard ? (React.createElement(WithPermissions, { authParams: defaultAuthParams, component: dashboard, loading: LoadingPage })) : (React.createElement(NavigateToFirstResource, { loading: LoadingPage })) }), | ||
React.createElement(Route, { path: "/authentication-error", element: React.createElement(AuthenticationError, null) }), | ||
React.createElement(Route, { path: "/access-denied", element: React.createElement(AccessDenied, null) }), | ||
React.createElement(Route, { path: "*", element: React.createElement(CatchAll, null) })))) }))); | ||
}; | ||
// FIXME in v6: make dashboard anonymous by default to remove this hack | ||
var defaultAuthParams = { params: { route: 'dashboard' } }; | ||
var Noop = function () { return null; }; | ||
//# sourceMappingURL=CoreAdminRoutes.js.map |
@@ -207,4 +207,56 @@ import * as React from 'react'; | ||
title?: TitleComponent; | ||
/** | ||
* The page to display when an authentication error occurs | ||
* | ||
* @see https://marmelab.com/react-admin/Admin.html#authenticationError | ||
* @example | ||
* import { Admin } from 'react-admin'; | ||
* | ||
* const AuthenticationError = () => ( | ||
* <div> | ||
* <h1>Authentication Error</h1> | ||
* <p>The authentication server returned an error and your credentials could not be checked.</p> | ||
* </div> | ||
* ) | ||
* | ||
* const App = () => ( | ||
* <Admin authenticationError={AuthenticationError}> | ||
* ... | ||
* </Admin> | ||
* ); | ||
*/ | ||
authenticationError?: ComponentType; | ||
/** | ||
* A react component to display when users don't have access to the page they're trying to access | ||
* | ||
* @see https://marmelab.com/react-admin/Admin.html#accessDenied | ||
* @example | ||
* // in src/AccessDenied.js | ||
* import Card from '@mui/material/Card'; | ||
* import CardContent from '@mui/material/CardContent'; | ||
* import { Title } from 'react-admin'; | ||
* | ||
* export const AccessDenied = () => ( | ||
* <Card> | ||
* <Title title="AccessDenied" /> | ||
* <CardContent> | ||
* <h1>You're not authorized to see this page</h1> | ||
* </CardContent> | ||
* </Card> | ||
* ); | ||
* | ||
* // in src/App.js | ||
* import { Admin } from 'react-admin'; | ||
* import { dataProvider } from './dataProvider'; | ||
* import { AccessDenied } from './AccessDenied'; | ||
* | ||
* const App = () => ( | ||
* <Admin accessDenied={AccessDenied} dataProvider={dataProvider}> | ||
* ... | ||
* </Admin> | ||
* ); | ||
*/ | ||
accessDenied?: React.ComponentType; | ||
} | ||
export declare const CoreAdminUI: (props: CoreAdminUIProps) => React.JSX.Element; | ||
//# sourceMappingURL=CoreAdminUI.d.ts.map |
@@ -24,3 +24,3 @@ import * as React from 'react'; | ||
var _a = useState({}), errorInfo = _a[0], setErrorInfo = _a[1]; | ||
var _b = props.authCallbackPage, LoginCallbackPage = _b === void 0 ? false : _b, _c = props.catchAll, catchAll = _c === void 0 ? Noop : _c, children = props.children, dashboard = props.dashboard, _d = props.disableTelemetry, disableTelemetry = _d === void 0 ? false : _d, _e = props.error, ErrorComponent = _e === void 0 ? DefaultError : _e, _f = props.layout, layout = _f === void 0 ? DefaultLayout : _f, _g = props.loading, loading = _g === void 0 ? Noop : _g, _h = props.loginPage, LoginPage = _h === void 0 ? false : _h, _j = props.ready, ready = _j === void 0 ? Ready : _j, _k = props.requireAuth, requireAuth = _k === void 0 ? false : _k, _l = props.title, title = _l === void 0 ? 'React Admin' : _l; | ||
var _b = props.authCallbackPage, LoginCallbackPage = _b === void 0 ? false : _b, _c = props.catchAll, catchAll = _c === void 0 ? Noop : _c, children = props.children, dashboard = props.dashboard, _d = props.disableTelemetry, disableTelemetry = _d === void 0 ? false : _d, _e = props.error, ErrorComponent = _e === void 0 ? DefaultError : _e, _f = props.layout, layout = _f === void 0 ? DefaultLayout : _f, _g = props.loading, loading = _g === void 0 ? Noop : _g, _h = props.loginPage, LoginPage = _h === void 0 ? false : _h, _j = props.ready, ready = _j === void 0 ? Ready : _j, _k = props.requireAuth, requireAuth = _k === void 0 ? false : _k, _l = props.title, title = _l === void 0 ? 'React Admin' : _l, _m = props.authenticationError, authenticationError = _m === void 0 ? Noop : _m, _o = props.accessDenied, accessDenied = _o === void 0 ? Noop : _o; | ||
useEffect(function () { | ||
@@ -48,3 +48,3 @@ if (disableTelemetry || | ||
LoginCallbackPage !== true ? (React.createElement(Route, { path: "/auth-callback", element: createOrGetElement(LoginCallbackPage) })) : null, | ||
React.createElement(Route, { path: "/*", element: React.createElement(CoreAdminRoutes, { catchAll: catchAll, dashboard: dashboard, layout: layout, loading: loading, requireAuth: requireAuth, ready: ready }, children) }))))); | ||
React.createElement(Route, { path: "/*", element: React.createElement(CoreAdminRoutes, { catchAll: catchAll, dashboard: dashboard, layout: layout, loading: loading, requireAuth: requireAuth, ready: ready, authenticationError: authenticationError, accessDenied: accessDenied }, children) }))))); | ||
}; | ||
@@ -51,0 +51,0 @@ var createOrGetElement = function (el) { return (isValidElement(el) ? el : createElement(el)); }; |
@@ -8,2 +8,3 @@ export * from './CoreAdmin'; | ||
export * from './HasDashboardContext'; | ||
export * from './NavigateToFirstResource'; | ||
export * from './OptionalResourceContextProvider'; | ||
@@ -15,3 +16,5 @@ export * from './Resource'; | ||
export * from './SourceContext'; | ||
export * from './useFirstResourceWithListAccess'; | ||
export * from './useGetResourceLabel'; | ||
export * from './useGetRecordRepresentation'; | ||
export * from './useResourceDefinitionContext'; | ||
@@ -21,4 +24,3 @@ export * from './useResourceContext'; | ||
export * from './useResourceDefinitions'; | ||
export * from './useGetRecordRepresentation'; | ||
export * from './useWrappedSource'; | ||
//# sourceMappingURL=index.d.ts.map |
@@ -8,2 +8,3 @@ export * from './CoreAdmin'; | ||
export * from './HasDashboardContext'; | ||
export * from './NavigateToFirstResource'; | ||
export * from './OptionalResourceContextProvider'; | ||
@@ -15,3 +16,5 @@ export * from './Resource'; | ||
export * from './SourceContext'; | ||
export * from './useFirstResourceWithListAccess'; | ||
export * from './useGetResourceLabel'; | ||
export * from './useGetRecordRepresentation'; | ||
export * from './useResourceDefinitionContext'; | ||
@@ -21,4 +24,3 @@ export * from './useResourceContext'; | ||
export * from './useResourceDefinitions'; | ||
export * from './useGetRecordRepresentation'; | ||
export * from './useWrappedSource'; | ||
//# sourceMappingURL=index.js.map |
@@ -50,4 +50,5 @@ var __assign = (this && this.__assign) || function () { | ||
import { Children, Fragment, useCallback, useEffect, useState, } from 'react'; | ||
import { useLogout, usePermissions } from '../auth'; | ||
import { useSafeSetState } from '../util'; | ||
import useLogout from '../auth/useLogout'; | ||
import usePermissions from '../auth/usePermissions'; | ||
import { useSafeSetState } from '../util/hooks'; | ||
import { useResourceDefinitionContext } from './useResourceDefinitionContext'; | ||
@@ -54,0 +55,0 @@ /** |
import { useContext, useMemo } from 'react'; | ||
import { useQueryClient } from '@tanstack/react-query'; | ||
import DataProviderContext from './DataProviderContext'; | ||
@@ -7,2 +8,3 @@ import { defaultDataProvider } from './defaultDataProvider'; | ||
import { reactAdminFetchActions } from './dataFetchActions'; | ||
import { populateQueryCache } from './populateQueryCache'; | ||
/** | ||
@@ -77,2 +79,3 @@ * Hook for getting a dataProvider | ||
defaultDataProvider); | ||
var queryClient = useQueryClient(); | ||
var logoutIfAccessDenied = useLogoutIfAccessDenied(); | ||
@@ -101,2 +104,3 @@ var dataProviderProxy = useMemo(function () { | ||
.then(function (response) { | ||
var _a; | ||
if (process.env.NODE_ENV !== 'production' && | ||
@@ -106,2 +110,8 @@ reactAdminFetchActions.includes(type)) { | ||
} | ||
if ((_a = response === null || response === void 0 ? void 0 : response.meta) === null || _a === void 0 ? void 0 : _a.prefetched) { | ||
populateQueryCache({ | ||
data: response === null || response === void 0 ? void 0 : response.meta.prefetched, | ||
queryClient: queryClient, | ||
}); | ||
} | ||
return response; | ||
@@ -135,3 +145,3 @@ }) | ||
}); | ||
}, [dataProvider, logoutIfAccessDenied]); | ||
}, [dataProvider, logoutIfAccessDenied, queryClient]); | ||
return dataProviderProxy; | ||
@@ -138,0 +148,0 @@ }; |
@@ -65,2 +65,4 @@ export interface StringMap { | ||
invite: string; | ||
access_denied: string; | ||
authentication_error: string; | ||
}; | ||
@@ -111,2 +113,4 @@ input: { | ||
unsaved_changes: string; | ||
access_denied: string; | ||
authentication_error: string; | ||
}; | ||
@@ -113,0 +117,0 @@ navigation: { |
@@ -1,2 +0,2 @@ | ||
import { Identifier } from '../types'; | ||
import { HintedString, Identifier } from '../types'; | ||
/** | ||
@@ -37,4 +37,3 @@ * Get a callback to create a link to a given page in the admin app. | ||
export declare const useCreatePath: () => ({ resource, id, type }: CreatePathParams) => string; | ||
type AnyString = string & {}; | ||
export type CreatePathType = 'list' | 'edit' | 'show' | 'create' | AnyString; | ||
export type CreatePathType = HintedString<'list' | 'edit' | 'show' | 'create'>; | ||
export interface CreatePathParams { | ||
@@ -46,3 +45,2 @@ type: CreatePathType; | ||
export declare const removeDoubleSlashes: (path: string) => string; | ||
export {}; | ||
//# sourceMappingURL=useCreatePath.d.ts.map |
@@ -11,3 +11,3 @@ import type { RaRecord } from '../types'; | ||
* const EditLink = () => { | ||
* const path = useGetRouteForRecord(); | ||
* const path = useGetPathForRecord(); | ||
* return path ? <Link to={path}>Edit</Link> : null; | ||
@@ -18,3 +18,3 @@ * }; | ||
* const EditLink = ({ record, resource }) => { | ||
* const path = useGetRouteForRecord({ record, resource, link: 'edit' }); | ||
* const path = useGetPathForRecord({ record, resource, link: 'edit' }); | ||
* return path ? <Link to={path}>Edit</Link> : null; | ||
@@ -25,3 +25,3 @@ * }; | ||
* const EditLink = ({ record, resource }) => { | ||
* const path = useGetRouteForRecord({ record, resource, link: (record, resource) => record.canEdit ? 'edit' : false }); | ||
* const path = useGetPathForRecord({ record, resource, link: (record, resource) => record.canEdit ? 'edit' : false }); | ||
* return path ? <Link to={path}>Edit</Link> : null; | ||
@@ -32,3 +32,3 @@ * }; | ||
* const EditLink = ({ record, resource }) => { | ||
* const path = useGetRouteForRecord({ record, resource, link: async (record, resource) => { | ||
* const path = useGetPathForRecord({ record, resource, link: async (record, resource) => { | ||
* const canEdit = await canEditRecord(record, resource); | ||
@@ -40,3 +40,3 @@ * return canEdit ? 'edit' : false; | ||
*/ | ||
export declare const useGetPathForRecord: <RecordType extends RaRecord<import("../types").Identifier> = RaRecord<import("../types").Identifier>>(options: UseGetPathForRecordOptions<RecordType>) => string | false | undefined; | ||
export declare const useGetPathForRecord: <RecordType extends RaRecord<import("../types").Identifier> = RaRecord<import("../types").Identifier>>(options?: UseGetPathForRecordOptions<RecordType>) => string | false | undefined; | ||
export interface UseGetPathForRecordOptions<RecordType extends RaRecord = RaRecord> { | ||
@@ -43,0 +43,0 @@ resource?: string; |
import { useState, useEffect } from 'react'; | ||
import { useResourceContext } from '../core'; | ||
import { useRecordContext } from '../controller'; | ||
import { useGetPathForRecordCallback } from './useGetPathForRecordCallback'; | ||
import { useResourceContext } from '../core/useResourceContext'; | ||
import { useRecordContext } from '../controller/record/useRecordContext'; | ||
import { useCanAccess } from '../auth'; | ||
import { useResourceDefinition } from '../core'; | ||
import { useCreatePath } from './useCreatePath'; | ||
/** | ||
@@ -13,3 +15,3 @@ * Get a path for a record, based on the current resource and the link type. | ||
* const EditLink = () => { | ||
* const path = useGetRouteForRecord(); | ||
* const path = useGetPathForRecord(); | ||
* return path ? <Link to={path}>Edit</Link> : null; | ||
@@ -20,3 +22,3 @@ * }; | ||
* const EditLink = ({ record, resource }) => { | ||
* const path = useGetRouteForRecord({ record, resource, link: 'edit' }); | ||
* const path = useGetPathForRecord({ record, resource, link: 'edit' }); | ||
* return path ? <Link to={path}>Edit</Link> : null; | ||
@@ -27,3 +29,3 @@ * }; | ||
* const EditLink = ({ record, resource }) => { | ||
* const path = useGetRouteForRecord({ record, resource, link: (record, resource) => record.canEdit ? 'edit' : false }); | ||
* const path = useGetPathForRecord({ record, resource, link: (record, resource) => record.canEdit ? 'edit' : false }); | ||
* return path ? <Link to={path}>Edit</Link> : null; | ||
@@ -34,3 +36,3 @@ * }; | ||
* const EditLink = ({ record, resource }) => { | ||
* const path = useGetRouteForRecord({ record, resource, link: async (record, resource) => { | ||
* const path = useGetPathForRecord({ record, resource, link: async (record, resource) => { | ||
* const canEdit = await canEditRecord(record, resource); | ||
@@ -43,2 +45,3 @@ * return canEdit ? 'edit' : false; | ||
export var useGetPathForRecord = function (options) { | ||
if (options === void 0) { options = {}; } | ||
var link = (options || {}).link; | ||
@@ -50,32 +53,78 @@ var record = useRecordContext(options); | ||
} | ||
var getPathForRecord = useGetPathForRecordCallback(options); | ||
// we initialize the path with the link value | ||
var _a = useState(function () { | ||
getPathForRecord({ | ||
record: record, | ||
var resourceDefinition = useResourceDefinition(options); | ||
var createPath = useCreatePath(); | ||
var _a = useState(link && typeof link !== 'function' && record != null | ||
? createPath({ | ||
resource: resource, | ||
link: link, | ||
}).then(function (resolvedLink) { | ||
if (resolvedLink === false) { | ||
// already set to false by default | ||
id: record.id, | ||
type: link, | ||
}) | ||
: false), path = _a[0], setPath = _a[1]; | ||
// in preparation for the default value, does the user have access to the show and edit pages? | ||
// (we can't run hooks conditionally, so we need to run them even though the link is specified) | ||
var canAccessShow = useCanAccess({ | ||
action: 'show', | ||
resource: resource, | ||
record: record, | ||
enabled: link == null && resourceDefinition.hasShow, | ||
}).canAccess; | ||
var canAccessEdit = useCanAccess({ | ||
action: 'edit', | ||
resource: resource, | ||
record: record, | ||
enabled: link == null && resourceDefinition.hasEdit, | ||
}).canAccess; | ||
useEffect(function () { | ||
if (!record) | ||
return; | ||
// Handle the inferred link type case | ||
if (link == null) { | ||
// We must check whether the resource has an edit view because if there is no | ||
// authProvider, canAccessShow will always be true | ||
if (resourceDefinition.hasShow && canAccessShow) { | ||
setPath(createPath({ | ||
resource: resource, | ||
id: record.id, | ||
type: 'show', | ||
})); | ||
return; | ||
} | ||
// update the path when the promise resolves | ||
setPath(resolvedLink); | ||
}); | ||
return false; | ||
}), path = _a[0], setPath = _a[1]; | ||
// update the path if the record changes | ||
useEffect(function () { | ||
getPathForRecord({ | ||
record: record, | ||
resource: resource, | ||
link: link, | ||
}).then(function (resolvedLink) { | ||
// update the path when the promise resolves | ||
setPath(resolvedLink); | ||
}); | ||
}, [getPathForRecord, link, record, resource]); | ||
// We must check whether the resource has an edit view because if there is no | ||
// authProvider, canAccessEdit will always be true | ||
if (resourceDefinition.hasEdit && canAccessEdit) { | ||
setPath(createPath({ | ||
resource: resource, | ||
id: record.id, | ||
type: 'edit', | ||
})); | ||
return; | ||
} | ||
} | ||
// Handle the link function case | ||
if (typeof link === 'function') { | ||
var linkResult = link(record, resource); | ||
if (linkResult instanceof Promise) { | ||
linkResult.then(function (resolvedPath) { return setPath(resolvedPath); }); | ||
return; | ||
} | ||
setPath(linkResult | ||
? createPath({ | ||
resource: resource, | ||
id: record.id, | ||
type: linkResult, | ||
}) | ||
: false); | ||
} | ||
}, [ | ||
createPath, | ||
canAccessShow, | ||
canAccessEdit, | ||
link, | ||
record, | ||
resource, | ||
resourceDefinition.hasEdit, | ||
resourceDefinition.hasShow, | ||
]); | ||
return path; | ||
}; | ||
//# sourceMappingURL=useGetPathForRecord.js.map |
@@ -38,3 +38,5 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
import { useCallback } from 'react'; | ||
import { useResourceContext, useResourceDefinitions } from '../core'; | ||
import { useResourceContext } from '../core/useResourceContext'; | ||
import { useResourceDefinitions } from '../core/useResourceDefinitions'; | ||
import { useCanAccessCallback } from '../auth/useCanAccessCallback'; | ||
import { useCreatePath } from './useCreatePath'; | ||
@@ -46,40 +48,73 @@ export var useGetPathForRecordCallback = function (options) { | ||
var createPath = useCreatePath(); | ||
var canAccess = useCanAccessCallback(); | ||
return useCallback(function (params) { return __awaiter(void 0, void 0, void 0, function () { | ||
var _a, link, record, finalResource, resourceDefinition, linkFunc, defaultLink, isLinkFalse, linkResult, linkResultIsPromise; | ||
var _b, _c, _d; | ||
var _a, link, record, finalResource, resourceDefinition, _b, canAccessShow, canAccessEdit, linkFunc, linkResult, linkResultIsPromise, resolvedLink; | ||
var _c, _d; | ||
return __generator(this, function (_e) { | ||
_a = params || {}, link = _a.link, record = _a.record; | ||
finalResource = (_b = params.resource) !== null && _b !== void 0 ? _b : resource; | ||
if (!finalResource) { | ||
throw new Error('Cannot generate a link for a record without a resource. You must use useGetPathForRecordCallback within a ResourceContextProvider, or pass a resource parameter.'); | ||
} | ||
resourceDefinition = (_c = resourceDefinitions[finalResource]) !== null && _c !== void 0 ? _c : {}; | ||
linkFunc = typeof link === 'function' ? link : function () { return link; }; | ||
defaultLink = resourceDefinition.hasShow | ||
? 'show' | ||
: resourceDefinition.hasEdit | ||
? 'edit' | ||
: false; | ||
isLinkFalse = link === false || (link == null && defaultLink === false); | ||
if (record == null || isLinkFalse) { | ||
return [2 /*return*/, false]; | ||
} | ||
linkResult = (_d = linkFunc(record, finalResource)) !== null && _d !== void 0 ? _d : defaultLink; | ||
linkResultIsPromise = isPromise(linkResult); | ||
if (linkResultIsPromise) { | ||
return [2 /*return*/, linkResult.then(function (resolvedLink) { | ||
if (resolvedLink === false) { | ||
// already set to false by default | ||
return; | ||
} | ||
return createPath({ | ||
switch (_e.label) { | ||
case 0: | ||
_a = params || {}, link = _a.link, record = _a.record; | ||
finalResource = (_c = params.resource) !== null && _c !== void 0 ? _c : resource; | ||
if (!finalResource) { | ||
throw new Error('Cannot generate a link for a record without a resource. You must use useGetPathForRecordCallback within a ResourceContextProvider, or pass a resource parameter.'); | ||
} | ||
resourceDefinition = (_d = resourceDefinitions[finalResource]) !== null && _d !== void 0 ? _d : {}; | ||
if (record == null || link === false) { | ||
return [2 /*return*/, false]; | ||
} | ||
if (!(link == null)) return [3 /*break*/, 2]; | ||
return [4 /*yield*/, Promise.all([ | ||
resourceDefinition.hasShow | ||
? canAccess({ | ||
action: 'show', | ||
resource: finalResource, | ||
record: record, | ||
}) | ||
: Promise.resolve(false), | ||
resourceDefinition.hasEdit | ||
? canAccess({ | ||
action: 'edit', | ||
resource: finalResource, | ||
record: record, | ||
}) | ||
: Promise.resolve(false), | ||
])]; | ||
case 1: | ||
_b = _e.sent(), canAccessShow = _b[0], canAccessEdit = _b[1]; | ||
if (canAccessShow) { | ||
return [2 /*return*/, createPath({ | ||
resource: finalResource, | ||
id: record.id, | ||
type: 'show', | ||
})]; | ||
} | ||
if (canAccessEdit) { | ||
return [2 /*return*/, createPath({ | ||
resource: finalResource, | ||
id: record.id, | ||
type: 'edit', | ||
})]; | ||
} | ||
return [2 /*return*/, false]; | ||
case 2: | ||
linkFunc = typeof link === 'function' ? link : function () { return link; }; | ||
linkResult = linkFunc(record, finalResource); | ||
if (linkResult === false) { | ||
return [2 /*return*/, false]; | ||
} | ||
linkResultIsPromise = isPromise(linkResult); | ||
if (!linkResultIsPromise) return [3 /*break*/, 4]; | ||
return [4 /*yield*/, linkResult]; | ||
case 3: | ||
resolvedLink = _e.sent(); | ||
if (resolvedLink === false) { | ||
// already set to false by default | ||
return [2 /*return*/]; | ||
} | ||
return [2 /*return*/, createPath({ | ||
resource: finalResource, | ||
id: record.id, | ||
type: resolvedLink, | ||
}); | ||
})]; | ||
} | ||
return [2 /*return*/, linkResult === false || linkResultIsPromise | ||
? false | ||
: createPath({ | ||
})]; | ||
case 4: return [2 /*return*/, createPath({ | ||
resource: finalResource, | ||
@@ -89,4 +124,5 @@ id: record.id, | ||
})]; | ||
} | ||
}); | ||
}); }, [createPath, resourceDefinitions, resource]); | ||
}); }, [canAccess, createPath, resourceDefinitions, resource]); | ||
}; | ||
@@ -93,0 +129,0 @@ var isPromise = function (value) { |
import * as React from 'react'; | ||
import { ReactNode } from 'react'; | ||
/** | ||
@@ -17,2 +18,5 @@ * This is a storybook decorator that wrap the story inside a fake browser. | ||
export declare const FakeBrowserDecorator: (Story: any, context: any) => React.JSX.Element; | ||
export declare const Browser: ({ children }: { | ||
children: ReactNode; | ||
}) => React.JSX.Element; | ||
//# sourceMappingURL=FakeBrowser.d.ts.map |
@@ -23,3 +23,3 @@ import * as React from 'react'; | ||
}; | ||
var Browser = function (_a) { | ||
export var Browser = function (_a) { | ||
var children = _a.children; | ||
@@ -26,0 +26,0 @@ return (React.createElement(React.Fragment, null, |
import { ComponentType, ReactElement, ReactNode } from 'react'; | ||
import { FieldPath } from 'react-hook-form'; | ||
import { WithPermissionsChildrenParams } from './auth/WithPermissions'; | ||
@@ -57,5 +58,11 @@ import { AuthActionType } from './auth/types'; | ||
getIdentity?: (params?: QueryFunctionContext) => Promise<UserIdentity>; | ||
getPermissions: (params: any & QueryFunctionContext) => Promise<any>; | ||
getPermissions?: (params: any & QueryFunctionContext) => Promise<any>; | ||
handleCallback?: (params?: QueryFunctionContext) => Promise<AuthRedirectResult | void | any>; | ||
canAccess?: <RecordType extends Record<string, any> = Record<string, any>>(params: QueryFunctionContext & { | ||
action: string; | ||
resource: string; | ||
record?: RecordType; | ||
}) => Promise<boolean>; | ||
[key: string]: any; | ||
supportAbortSignal?: boolean; | ||
}; | ||
@@ -114,2 +121,3 @@ export type AuthRedirectResult = { | ||
data: RecordType; | ||
meta?: any; | ||
} | ||
@@ -123,2 +131,3 @@ export interface GetManyParams<RecordType extends RaRecord = any> { | ||
data: RecordType[]; | ||
meta?: any; | ||
} | ||
@@ -151,2 +160,3 @@ export interface GetManyReferenceParams { | ||
data: RecordType; | ||
meta?: any; | ||
} | ||
@@ -160,2 +170,3 @@ export interface UpdateManyParams<T = any> { | ||
data?: RecordType['id'][]; | ||
meta?: any; | ||
} | ||
@@ -168,2 +179,3 @@ export interface CreateParams<T = any> { | ||
data: RecordType; | ||
meta?: any; | ||
} | ||
@@ -177,2 +189,3 @@ export interface DeleteParams<RecordType extends RaRecord = any> { | ||
data: RecordType; | ||
meta?: any; | ||
} | ||
@@ -185,2 +198,3 @@ export interface DeleteManyParams<RecordType extends RaRecord = any> { | ||
data?: RecordType['id'][]; | ||
meta?: any; | ||
} | ||
@@ -272,2 +286,9 @@ export type DataProviderResult<RecordType extends RaRecord = any> = CreateResult<RecordType> | DeleteResult<RecordType> | DeleteManyResult | GetListResult<RecordType> | GetManyResult<RecordType> | GetManyReferenceResult<RecordType> | GetOneResult<RecordType> | UpdateResult<RecordType> | UpdateManyResult; | ||
}; | ||
export type HintedString<KnownValues extends string> = AnyString | KnownValues; | ||
export type RecordValues = Record<string, any>; | ||
export type RecordPath<TRecordValues extends RecordValues> = FieldPath<TRecordValues>; | ||
export type ExtractRecordPaths<T extends RecordValues> = [ | ||
T | ||
] extends [never] ? string : RecordPath<T>; | ||
export type AnyString = string & {}; | ||
//# sourceMappingURL=types.d.ts.map |
@@ -1,2 +0,2 @@ | ||
import { Call, Objects } from 'hotscript'; | ||
import { ExtractRecordPaths } from '../types'; | ||
/** | ||
@@ -19,7 +19,5 @@ * A hook that gets the value of a field of the current record. | ||
defaultValue?: any; | ||
source: Call<Objects.AllPaths, RecordType> extends never ? AnyString : Call<Objects.AllPaths, RecordType>; | ||
source: ExtractRecordPaths<RecordType>; | ||
record?: RecordType; | ||
} | ||
type AnyString = string & {}; | ||
export {}; | ||
//# sourceMappingURL=useFieldValue.d.ts.map |
{ | ||
"name": "ra-core", | ||
"version": "5.2.3", | ||
"version": "5.3.0", | ||
"description": "Core components of react-admin, a frontend Framework for building admin applications on top of REST services, using ES6, React", | ||
@@ -65,3 +65,2 @@ "files": [ | ||
"eventemitter3": "^5.0.1", | ||
"hotscript": "^1.0.12", | ||
"inflection": "^3.0.0", | ||
@@ -74,3 +73,3 @@ "jsonexport": "^3.2.0", | ||
}, | ||
"gitHead": "1cba674e0b704ef14ce4cbc3b5c34ccd9935d903" | ||
"gitHead": "e8bf99c16a7bddb11ca5f845faf1882f477febb8" | ||
} |
@@ -13,8 +13,14 @@ import useAuthProvider from './useAuthProvider'; | ||
export * from './AuthContext'; | ||
export * from './CanAccess'; | ||
export * from './LogoutOnMount'; | ||
export * from './types'; | ||
export * from './useAuthenticated'; | ||
export * from './useCanAccess'; | ||
export * from './useCanAccessResources'; | ||
export * from './useCanAccessCallback'; | ||
export * from './useCheckAuth'; | ||
export * from './useGetIdentity'; | ||
export * from './useHandleAuthCallback'; | ||
export * from './useIsAuthPending'; | ||
export * from './useRequireAccess'; | ||
export * from './addRefreshAuthToAuthProvider'; | ||
@@ -21,0 +27,0 @@ export * from './addRefreshAuthToDataProvider'; |
@@ -15,21 +15,26 @@ import { UseQueryOptions } from '@tanstack/react-query'; | ||
* @example | ||
* import { Admin, CustomRoutes, useAuthenticated } from 'react-admin'; | ||
* const FooPage = () => { | ||
* useAuthenticated(); | ||
* return <Foo />; | ||
* } | ||
* const customRoutes = [ | ||
* <Route path="/foo" element={<FooPage />} /> | ||
* ]; | ||
* const App = () => ( | ||
* <Admin> | ||
* <CustomRoutes>{customRoutes}</CustomRoutes> | ||
* </Admin> | ||
* ); | ||
* import { Admin, CustomRoutes, useAuthenticated } from 'react-admin'; | ||
* | ||
* const FooPage = () => { | ||
* const { isPending } = useAuthenticated(); | ||
* if (isPending) return null; | ||
* return <Foo />; | ||
* } | ||
* | ||
* const customRoutes = [ | ||
* <Route path="/foo" element={<FooPage />} /> | ||
* ]; | ||
* | ||
* const App = () => ( | ||
* <Admin> | ||
* <CustomRoutes>{customRoutes}</CustomRoutes> | ||
* </Admin> | ||
* ); | ||
*/ | ||
export const useAuthenticated = <ParamsType = any>({ | ||
params, | ||
logoutOnFailure = true, | ||
...options | ||
}: UseAuthenticatedOptions<ParamsType> = {}) => { | ||
useAuthState(params ?? emptyParams, true, options); | ||
return useAuthState(params ?? emptyParams, logoutOnFailure, options); | ||
}; | ||
@@ -42,4 +47,6 @@ | ||
'queryKey' | 'queryFn' | ||
>; | ||
> & { | ||
logoutOnFailure?: boolean; | ||
}; | ||
const emptyParams = {}; |
@@ -63,3 +63,3 @@ import { useEffect, useMemo } from 'react'; | ||
const result = useQuery<boolean, any>({ | ||
const queryResult = useQuery<boolean, any>({ | ||
queryKey: ['auth', 'checkAuth', params], | ||
@@ -92,19 +92,17 @@ queryFn: ({ signal }) => { | ||
((error: any) => { | ||
if (!logoutOnFailure) return; | ||
const loginUrl = removeDoubleSlashes( | ||
`${basename}/${defaultAuthParams.loginUrl}` | ||
); | ||
if (logoutOnFailure) { | ||
logout( | ||
{}, | ||
error && error.redirectTo != null | ||
? error.redirectTo | ||
: loginUrl | ||
); | ||
const shouldSkipNotify = error && error.message === false; | ||
!shouldSkipNotify && | ||
notify( | ||
getErrorMessage(error, 'ra.auth.auth_check_error'), | ||
{ type: 'error' } | ||
); | ||
} | ||
logout( | ||
{}, | ||
error && error.redirectTo != null | ||
? error.redirectTo | ||
: loginUrl | ||
); | ||
const shouldSkipNotify = error && error.message === false; | ||
!shouldSkipNotify && | ||
notify(getErrorMessage(error, 'ra.auth.auth_check_error'), { | ||
type: 'error', | ||
}); | ||
}) | ||
@@ -114,31 +112,46 @@ ); | ||
useEffect(() => { | ||
if (result.data === undefined || result.isFetching) return; | ||
onSuccessEvent(result.data); | ||
}, [onSuccessEvent, result.data, result.isFetching]); | ||
if (queryResult.data === undefined || queryResult.isFetching) return; | ||
if (queryOptions.enabled === false) return; | ||
onSuccessEvent(queryResult.data); | ||
}, [ | ||
onSuccessEvent, | ||
queryResult.data, | ||
queryResult.isFetching, | ||
queryOptions.enabled, | ||
]); | ||
useEffect(() => { | ||
if (result.error == null || result.isFetching) return; | ||
onErrorEvent(result.error); | ||
}, [onErrorEvent, result.error, result.isFetching]); | ||
if (queryResult.error == null || queryResult.isFetching) return; | ||
if (queryOptions.enabled === false) return; | ||
onErrorEvent(queryResult.error); | ||
}, [ | ||
onErrorEvent, | ||
queryResult.error, | ||
queryResult.isFetching, | ||
queryOptions.enabled, | ||
]); | ||
useEffect(() => { | ||
if (result.status === 'pending' || result.isFetching) return; | ||
onSettledEvent(result.data, result.error); | ||
if (queryResult.status === 'pending' || queryResult.isFetching) return; | ||
if (queryOptions.enabled === false) return; | ||
onSettledEvent(queryResult.data, queryResult.error); | ||
}, [ | ||
onSettledEvent, | ||
result.data, | ||
result.error, | ||
result.status, | ||
result.isFetching, | ||
queryResult.data, | ||
queryResult.error, | ||
queryResult.status, | ||
queryResult.isFetching, | ||
queryOptions.enabled, | ||
]); | ||
return useMemo(() => { | ||
const result = useMemo(() => { | ||
return { | ||
...result, | ||
// If the data is undefined and the query isn't loading anymore, it means the query failed. | ||
// In that case, we set authenticated to false unless there's no authProvider. | ||
authenticated: | ||
result.data ?? result.isLoading ? true : authProvider == null, // Optimistic, | ||
...queryResult, | ||
authenticated: queryResult.error ? false : queryResult.data, | ||
}; | ||
}, [authProvider, result]); | ||
}, [queryResult]); | ||
return authProvider != null | ||
? result | ||
: (noAuthProviderQueryResult as UseAuthStateResult<ErrorType>); | ||
}; | ||
@@ -159,3 +172,3 @@ | ||
> & { | ||
authenticated: boolean; | ||
authenticated?: QueryObserverResult<boolean, ErrorType>['data']; | ||
}; | ||
@@ -173,1 +186,29 @@ | ||
const noop = () => {}; | ||
const noAuthProviderQueryResult = { | ||
authenticated: true, | ||
data: true, | ||
dataUpdatedAt: 0, | ||
error: null, | ||
errorUpdatedAt: 0, | ||
errorUpdateCount: 0, | ||
failureCount: 0, | ||
failureReason: null, | ||
fetchStatus: 'idle', | ||
isError: false, | ||
isInitialLoading: false, | ||
isLoading: false, | ||
isLoadingError: false, | ||
isFetched: true, | ||
isFetchedAfterMount: true, | ||
isFetching: false, | ||
isPaused: false, | ||
isPlaceholderData: false, | ||
isPending: false, | ||
isRefetchError: false, | ||
isRefetching: false, | ||
isStale: false, | ||
isSuccess: true, | ||
status: 'success', | ||
refetch: () => Promise.resolve(noAuthProviderQueryResult), | ||
}; |
@@ -5,4 +5,2 @@ import { useCallback } from 'react'; | ||
const getPermissionsWithoutProvider = () => Promise.resolve([]); | ||
/** | ||
@@ -42,9 +40,11 @@ * Get a callback for calling the authProvider.getPermissions() method. | ||
const getPermissions = useCallback( | ||
(params: any = {}) => | ||
(params: any = {}) => { | ||
// react-query requires the query to return something | ||
authProvider | ||
? authProvider | ||
.getPermissions(params) | ||
.then(result => result ?? null) | ||
: getPermissionsWithoutProvider(), | ||
if (authProvider && authProvider.getPermissions) { | ||
return authProvider | ||
.getPermissions(params) | ||
.then(result => result ?? null); | ||
} | ||
return Promise.resolve([]); | ||
}, | ||
[authProvider] | ||
@@ -51,0 +51,0 @@ ); |
@@ -54,6 +54,8 @@ import { useEffect, useMemo } from 'react'; | ||
const result = useQuery<PermissionsType, ErrorType>({ | ||
const queryResult = useQuery<PermissionsType, ErrorType>({ | ||
queryKey: ['auth', 'getPermissions', params], | ||
queryFn: async ({ signal }) => { | ||
if (!authProvider) return Promise.resolve([]); | ||
if (!authProvider || !authProvider.getPermissions) { | ||
return []; | ||
} | ||
const permissions = await authProvider.getPermissions({ | ||
@@ -81,29 +83,33 @@ ...params, | ||
useEffect(() => { | ||
if (result.data === undefined || result.isFetching) return; | ||
onSuccessEvent(result.data); | ||
}, [onSuccessEvent, result.data, result.isFetching]); | ||
if (queryResult.data === undefined || queryResult.isFetching) return; | ||
onSuccessEvent(queryResult.data); | ||
}, [onSuccessEvent, queryResult.data, queryResult.isFetching]); | ||
useEffect(() => { | ||
if (result.error == null || result.isFetching) return; | ||
onErrorEvent(result.error); | ||
}, [onErrorEvent, result.error, result.isFetching]); | ||
if (queryResult.error == null || queryResult.isFetching) return; | ||
onErrorEvent(queryResult.error); | ||
}, [onErrorEvent, queryResult.error, queryResult.isFetching]); | ||
useEffect(() => { | ||
if (result.status === 'pending' || result.isFetching) return; | ||
onSettledEvent(result.data, result.error); | ||
if (queryResult.status === 'pending' || queryResult.isFetching) return; | ||
onSettledEvent(queryResult.data, queryResult.error); | ||
}, [ | ||
onSettledEvent, | ||
result.data, | ||
result.error, | ||
result.status, | ||
result.isFetching, | ||
queryResult.data, | ||
queryResult.error, | ||
queryResult.status, | ||
queryResult.isFetching, | ||
]); | ||
return useMemo( | ||
const result = useMemo( | ||
() => ({ | ||
...result, | ||
permissions: result.data, | ||
...queryResult, | ||
permissions: queryResult.data, | ||
}), | ||
[result] | ||
[queryResult] | ||
); | ||
return !authProvider || !authProvider.getPermissions | ||
? (fakeQueryResult as UsePermissionsResult<PermissionsType, ErrorType>) | ||
: result; | ||
}; | ||
@@ -131,1 +137,29 @@ | ||
const noop = () => {}; | ||
const fakeQueryResult = { | ||
permissions: undefined, | ||
data: undefined, | ||
dataUpdatedAt: 0, | ||
error: null, | ||
errorUpdatedAt: 0, | ||
errorUpdateCount: 0, | ||
failureCount: 0, | ||
failureReason: null, | ||
fetchStatus: 'idle', | ||
isError: false, | ||
isInitialLoading: false, | ||
isLoading: false, | ||
isLoadingError: false, | ||
isFetched: true, | ||
isFetchedAfterMount: true, | ||
isFetching: false, | ||
isPaused: false, | ||
isPlaceholderData: false, | ||
isPending: false, | ||
isRefetchError: false, | ||
isRefetching: false, | ||
isStale: false, | ||
isSuccess: true, | ||
status: 'success', | ||
refetch: () => Promise.resolve(fakeQueryResult), | ||
}; |
@@ -6,3 +6,3 @@ import { useCallback } from 'react'; | ||
import { useAuthenticated } from '../../auth'; | ||
import { useAuthenticated, useRequireAccess } from '../../auth'; | ||
import { | ||
@@ -27,2 +27,3 @@ HttpError, | ||
} from '../../core'; | ||
import _ from 'lodash'; | ||
@@ -65,3 +66,2 @@ /** | ||
useAuthenticated({ enabled: !disableAuthentication }); | ||
const resource = useResourceContext(props); | ||
@@ -73,2 +73,11 @@ if (!resource) { | ||
} | ||
const { isPending: isPendingAuthenticated } = useAuthenticated({ | ||
enabled: !disableAuthentication, | ||
}); | ||
const { isPending: isPendingCanAccess } = useRequireAccess<RecordType>({ | ||
action: 'create', | ||
resource, | ||
// If disableAuthentication is true then isPendingAuthenticated will always be true so this hook is disabled | ||
enabled: !isPendingAuthenticated, | ||
}); | ||
const { hasEdit, hasShow } = useResourceDefinition(props); | ||
@@ -100,5 +109,10 @@ const finalRedirectTo = | ||
notify('ra.notification.created', { | ||
notify(`resources.${resource}.notifications.created`, { | ||
type: 'info', | ||
messageArgs: { smart_count: 1 }, | ||
messageArgs: { | ||
smart_count: 1, | ||
_: translate(`ra.notification.created`, { | ||
smart_count: 1, | ||
}), | ||
}, | ||
}); | ||
@@ -190,3 +204,3 @@ redirect(finalRedirectTo, resource, data.id, data); | ||
isLoading: false, | ||
isPending: saving, | ||
isPending: disableAuthentication ? false : isPendingCanAccess, | ||
saving, | ||
@@ -232,2 +246,3 @@ defaultTitle, | ||
resource: string; | ||
saving: boolean; | ||
} | ||
@@ -234,0 +249,0 @@ |
import { useCallback } from 'react'; | ||
import { useParams } from 'react-router-dom'; | ||
import { useAuthenticated } from '../../auth'; | ||
import { useAuthenticated, useRequireAccess } from '../../auth'; | ||
import { RaRecord, MutationMode, TransformData } from '../../types'; | ||
@@ -58,3 +58,3 @@ import { useRedirect, RedirectionSideEffect } from '../../routing'; | ||
const { | ||
disableAuthentication, | ||
disableAuthentication = false, | ||
id: propsId, | ||
@@ -67,3 +67,2 @@ mutationMode = 'undoable', | ||
} = props; | ||
useAuthenticated({ enabled: !disableAuthentication }); | ||
const resource = useResourceContext(props); | ||
@@ -75,2 +74,13 @@ if (!resource) { | ||
} | ||
const { isPending: isPendingAuthenticated } = useAuthenticated({ | ||
enabled: !disableAuthentication, | ||
}); | ||
const { isPending: isPendingCanAccess } = useRequireAccess<RecordType>({ | ||
action: 'edit', | ||
resource, | ||
// If disableAuthentication is true then isPendingAuthenticated will always be true so this hook is disabled | ||
enabled: !isPendingAuthenticated, | ||
}); | ||
const getRecordRepresentation = useGetRecordRepresentation(resource); | ||
@@ -111,2 +121,5 @@ const translate = useTranslate(); | ||
{ | ||
enabled: | ||
(!isPendingAuthenticated && !isPendingCanAccess) || | ||
disableAuthentication, | ||
onError: () => { | ||
@@ -155,5 +168,10 @@ notify('ra.notification.item_doesnt_exist', { | ||
} | ||
notify('ra.notification.updated', { | ||
notify(`resources.${resource}.notifications.updated`, { | ||
type: 'info', | ||
messageArgs: { smart_count: 1 }, | ||
messageArgs: { | ||
smart_count: 1, | ||
_: translate('ra.notification.updated', { | ||
smart_count: 1, | ||
}), | ||
}, | ||
undoable: mutationMode === 'undoable', | ||
@@ -297,2 +315,3 @@ }); | ||
resource: string; | ||
saving: boolean; | ||
} | ||
@@ -299,0 +318,0 @@ |
@@ -7,3 +7,3 @@ import { isValidElement, useEffect, useMemo } from 'react'; | ||
import { useAuthenticated } from '../../auth'; | ||
import { useAuthenticated, useRequireAccess } from '../../auth'; | ||
import { useTranslate } from '../../i18n'; | ||
@@ -51,4 +51,4 @@ import { useNotify } from '../../notification'; | ||
debounce = 500, | ||
disableAuthentication, | ||
disableSyncWithLocation, | ||
disableAuthentication = false, | ||
disableSyncWithLocation = false, | ||
exporter = defaultExporter, | ||
@@ -62,3 +62,2 @@ filter, | ||
} = props; | ||
useAuthenticated({ enabled: !disableAuthentication }); | ||
const resource = useResourceContext(props); | ||
@@ -78,2 +77,13 @@ const { meta, ...otherQueryOptions } = queryOptions ?? {}; | ||
const { isPending: isPendingAuthenticated } = useAuthenticated({ | ||
enabled: !disableAuthentication, | ||
}); | ||
const { isPending: isPendingCanAccess } = useRequireAccess<RecordType>({ | ||
action: 'list', | ||
resource, | ||
// If disableAuthentication is true then isPendingAuthenticated will always be true so this hook is disabled | ||
enabled: !isPendingAuthenticated, | ||
}); | ||
const translate = useTranslate(); | ||
@@ -120,2 +130,5 @@ const notify = useNotify(); | ||
{ | ||
enabled: | ||
(!isPendingAuthenticated && !isPendingCanAccess) || | ||
disableAuthentication, | ||
placeholderData: previousData => previousData, | ||
@@ -122,0 +135,0 @@ retry: false, |
import { isValidElement, useEffect, useMemo } from 'react'; | ||
import { useAuthenticated } from '../../auth'; | ||
import { useAuthenticated, useRequireAccess } from '../../auth'; | ||
import { useTranslate } from '../../i18n'; | ||
@@ -11,3 +11,2 @@ import { useNotify } from '../../notification'; | ||
} from '../../dataProvider'; | ||
import { SORT_ASC } from './queryReducer'; | ||
import { defaultExporter } from '../../export'; | ||
@@ -18,2 +17,3 @@ import { FilterPayload, SortPayload, RaRecord, Exporter } from '../../types'; | ||
import { useListParams } from './useListParams'; | ||
import { SORT_ASC } from './queryReducer'; | ||
@@ -42,4 +42,4 @@ /** | ||
debounce = 500, | ||
disableAuthentication, | ||
disableSyncWithLocation, | ||
disableAuthentication = false, | ||
disableSyncWithLocation = false, | ||
exporter = defaultExporter, | ||
@@ -53,3 +53,2 @@ filter, | ||
} = props; | ||
useAuthenticated({ enabled: !disableAuthentication }); | ||
const resource = useResourceContext(props); | ||
@@ -73,2 +72,13 @@ const { meta, ...otherQueryOptions } = queryOptions; | ||
const { isPending: isPendingAuthenticated } = useAuthenticated({ | ||
enabled: !disableAuthentication, | ||
}); | ||
const { isPending: isPendingCanAccess } = useRequireAccess<RecordType>({ | ||
action: 'list', | ||
resource, | ||
// If disableAuthentication is true then isPendingAuthenticated will always be true so this hook is disabled | ||
enabled: !isPendingAuthenticated, | ||
}); | ||
const translate = useTranslate(); | ||
@@ -114,2 +124,5 @@ const notify = useNotify(); | ||
{ | ||
enabled: | ||
(!isPendingAuthenticated && !isPendingCanAccess) || | ||
disableAuthentication, | ||
placeholderData: previousData => previousData, | ||
@@ -116,0 +129,0 @@ retry: false, |
import { useParams } from 'react-router-dom'; | ||
import { useAuthenticated } from '../../auth'; | ||
import { useAuthenticated, useRequireAccess } from '../../auth'; | ||
import { RaRecord } from '../../types'; | ||
@@ -55,4 +55,7 @@ import { | ||
): ShowControllerResult<RecordType> => { | ||
const { disableAuthentication, id: propsId, queryOptions = {} } = props; | ||
useAuthenticated({ enabled: !disableAuthentication }); | ||
const { | ||
disableAuthentication = false, | ||
id: propsId, | ||
queryOptions = {}, | ||
} = props; | ||
const resource = useResourceContext(props); | ||
@@ -64,2 +67,14 @@ if (!resource) { | ||
} | ||
const { isPending: isPendingAuthenticated } = useAuthenticated({ | ||
enabled: !disableAuthentication, | ||
}); | ||
const { isPending: isPendingCanAccess } = useRequireAccess<RecordType>({ | ||
action: 'show', | ||
resource, | ||
// If disableAuthentication is true then isPendingAuthenticated will always be true so this hook is disabled | ||
enabled: !isPendingAuthenticated, | ||
}); | ||
const getRecordRepresentation = useGetRecordRepresentation(resource); | ||
@@ -90,2 +105,5 @@ const translate = useTranslate(); | ||
{ | ||
enabled: | ||
(!isPendingAuthenticated && !isPendingCanAccess) || | ||
disableAuthentication, | ||
onError: () => { | ||
@@ -92,0 +110,0 @@ notify('ra.notification.item_doesnt_exist', { |
@@ -8,2 +8,3 @@ export * from './CoreAdmin'; | ||
export * from './HasDashboardContext'; | ||
export * from './NavigateToFirstResource'; | ||
export * from './OptionalResourceContextProvider'; | ||
@@ -15,3 +16,5 @@ export * from './Resource'; | ||
export * from './SourceContext'; | ||
export * from './useFirstResourceWithListAccess'; | ||
export * from './useGetResourceLabel'; | ||
export * from './useGetRecordRepresentation'; | ||
export * from './useResourceDefinitionContext'; | ||
@@ -21,3 +24,2 @@ export * from './useResourceContext'; | ||
export * from './useResourceDefinitions'; | ||
export * from './useGetRecordRepresentation'; | ||
export * from './useWrappedSource'; |
import { useContext, useMemo } from 'react'; | ||
import { useQueryClient } from '@tanstack/react-query'; | ||
@@ -9,2 +10,3 @@ import DataProviderContext from './DataProviderContext'; | ||
import { reactAdminFetchActions } from './dataFetchActions'; | ||
import { populateQueryCache } from './populateQueryCache'; | ||
@@ -84,2 +86,3 @@ /** | ||
defaultDataProvider) as unknown as TDataProvider; | ||
const queryClient = useQueryClient(); | ||
@@ -116,2 +119,8 @@ const logoutIfAccessDenied = useLogoutIfAccessDenied(); | ||
} | ||
if (response?.meta?.prefetched) { | ||
populateQueryCache({ | ||
data: response?.meta.prefetched, | ||
queryClient, | ||
}); | ||
} | ||
return response; | ||
@@ -152,3 +161,3 @@ }) | ||
}); | ||
}, [dataProvider, logoutIfAccessDenied]); | ||
}, [dataProvider, logoutIfAccessDenied, queryClient]); | ||
@@ -155,0 +164,0 @@ return dataProviderProxy; |
@@ -67,2 +67,4 @@ export interface StringMap { | ||
invite: string; | ||
access_denied: string; | ||
authentication_error: string; | ||
}; | ||
@@ -113,2 +115,4 @@ input: { | ||
unsaved_changes: string; | ||
access_denied: string; | ||
authentication_error: string; | ||
}; | ||
@@ -115,0 +119,0 @@ navigation: { |
import { useCallback } from 'react'; | ||
import { Identifier } from '../types'; | ||
import { HintedString, Identifier } from '../types'; | ||
import { useBasename } from './useBasename'; | ||
@@ -87,5 +87,3 @@ | ||
type AnyString = string & {}; | ||
export type CreatePathType = 'list' | 'edit' | 'show' | 'create' | AnyString; | ||
export type CreatePathType = HintedString<'list' | 'edit' | 'show' | 'create'>; | ||
export interface CreatePathParams { | ||
@@ -92,0 +90,0 @@ type: CreatePathType; |
import { useState, useEffect } from 'react'; | ||
import { useResourceContext } from '../core'; | ||
import { useRecordContext } from '../controller'; | ||
import { useResourceContext } from '../core/useResourceContext'; | ||
import { useRecordContext } from '../controller/record/useRecordContext'; | ||
import type { RaRecord } from '../types'; | ||
import type { LinkToType } from './types'; | ||
import { useGetPathForRecordCallback } from './useGetPathForRecordCallback'; | ||
import { useCanAccess } from '../auth'; | ||
import { useResourceDefinition } from '../core'; | ||
import { useCreatePath } from './useCreatePath'; | ||
@@ -16,3 +18,3 @@ /** | ||
* const EditLink = () => { | ||
* const path = useGetRouteForRecord(); | ||
* const path = useGetPathForRecord(); | ||
* return path ? <Link to={path}>Edit</Link> : null; | ||
@@ -23,3 +25,3 @@ * }; | ||
* const EditLink = ({ record, resource }) => { | ||
* const path = useGetRouteForRecord({ record, resource, link: 'edit' }); | ||
* const path = useGetPathForRecord({ record, resource, link: 'edit' }); | ||
* return path ? <Link to={path}>Edit</Link> : null; | ||
@@ -30,3 +32,3 @@ * }; | ||
* const EditLink = ({ record, resource }) => { | ||
* const path = useGetRouteForRecord({ record, resource, link: (record, resource) => record.canEdit ? 'edit' : false }); | ||
* const path = useGetPathForRecord({ record, resource, link: (record, resource) => record.canEdit ? 'edit' : false }); | ||
* return path ? <Link to={path}>Edit</Link> : null; | ||
@@ -37,3 +39,3 @@ * }; | ||
* const EditLink = ({ record, resource }) => { | ||
* const path = useGetRouteForRecord({ record, resource, link: async (record, resource) => { | ||
* const path = useGetPathForRecord({ record, resource, link: async (record, resource) => { | ||
* const canEdit = await canEditRecord(record, resource); | ||
@@ -46,3 +48,3 @@ * return canEdit ? 'edit' : false; | ||
export const useGetPathForRecord = <RecordType extends RaRecord = RaRecord>( | ||
options: UseGetPathForRecordOptions<RecordType> | ||
options: UseGetPathForRecordOptions<RecordType> = {} | ||
): string | false | undefined => { | ||
@@ -57,34 +59,88 @@ const { link } = options || {}; | ||
} | ||
const getPathForRecord = useGetPathForRecordCallback<RecordType>(options); | ||
const resourceDefinition = useResourceDefinition(options); | ||
const createPath = useCreatePath(); | ||
const [path, setPath] = useState<string | false>( | ||
link && typeof link !== 'function' && record != null | ||
? createPath({ | ||
resource, | ||
id: record.id, | ||
type: link, | ||
}) | ||
: false | ||
); | ||
// we initialize the path with the link value | ||
const [path, setPath] = useState<string | false | undefined>(() => { | ||
getPathForRecord({ | ||
record, | ||
resource, | ||
link, | ||
}).then(resolvedLink => { | ||
if (resolvedLink === false) { | ||
// already set to false by default | ||
// in preparation for the default value, does the user have access to the show and edit pages? | ||
// (we can't run hooks conditionally, so we need to run them even though the link is specified) | ||
const { canAccess: canAccessShow } = useCanAccess({ | ||
action: 'show', | ||
resource, | ||
record, | ||
enabled: link == null && resourceDefinition.hasShow, | ||
}); | ||
const { canAccess: canAccessEdit } = useCanAccess({ | ||
action: 'edit', | ||
resource, | ||
record, | ||
enabled: link == null && resourceDefinition.hasEdit, | ||
}); | ||
useEffect(() => { | ||
if (!record) return; | ||
// Handle the inferred link type case | ||
if (link == null) { | ||
// We must check whether the resource has an edit view because if there is no | ||
// authProvider, canAccessShow will always be true | ||
if (resourceDefinition.hasShow && canAccessShow) { | ||
setPath( | ||
createPath({ | ||
resource, | ||
id: record.id, | ||
type: 'show', | ||
}) | ||
); | ||
return; | ||
} | ||
// update the path when the promise resolves | ||
setPath(resolvedLink); | ||
}); | ||
// We must check whether the resource has an edit view because if there is no | ||
// authProvider, canAccessEdit will always be true | ||
if (resourceDefinition.hasEdit && canAccessEdit) { | ||
setPath( | ||
createPath({ | ||
resource, | ||
id: record.id, | ||
type: 'edit', | ||
}) | ||
); | ||
return; | ||
} | ||
} | ||
return false; | ||
}); | ||
// Handle the link function case | ||
if (typeof link === 'function') { | ||
const linkResult = link(record, resource); | ||
if (linkResult instanceof Promise) { | ||
linkResult.then(resolvedPath => setPath(resolvedPath)); | ||
return; | ||
} | ||
setPath( | ||
linkResult | ||
? createPath({ | ||
resource, | ||
id: record.id, | ||
type: linkResult, | ||
}) | ||
: false | ||
); | ||
} | ||
}, [ | ||
createPath, | ||
canAccessShow, | ||
canAccessEdit, | ||
link, | ||
record, | ||
resource, | ||
resourceDefinition.hasEdit, | ||
resourceDefinition.hasShow, | ||
]); | ||
// update the path if the record changes | ||
useEffect(() => { | ||
getPathForRecord({ | ||
record, | ||
resource, | ||
link, | ||
}).then(resolvedLink => { | ||
// update the path when the promise resolves | ||
setPath(resolvedLink); | ||
}); | ||
}, [getPathForRecord, link, record, resource]); | ||
return path; | ||
@@ -91,0 +147,0 @@ }; |
import { useCallback } from 'react'; | ||
import { useResourceContext, useResourceDefinitions } from '../core'; | ||
import { useResourceContext } from '../core/useResourceContext'; | ||
import { useResourceDefinitions } from '../core/useResourceDefinitions'; | ||
import { useCanAccessCallback } from '../auth/useCanAccessCallback'; | ||
import type { RaRecord } from '../types'; | ||
@@ -15,2 +17,3 @@ import { useCreatePath } from './useCreatePath'; | ||
const createPath = useCreatePath(); | ||
const canAccess = useCanAccessCallback(); | ||
@@ -27,42 +30,73 @@ return useCallback( | ||
const resourceDefinition = resourceDefinitions[finalResource] ?? {}; | ||
const linkFunc = typeof link === 'function' ? link : () => link; | ||
const defaultLink = resourceDefinition.hasShow | ||
? 'show' | ||
: resourceDefinition.hasEdit | ||
? 'edit' | ||
: false; | ||
if (record == null || link === false) { | ||
return false; | ||
} | ||
const isLinkFalse = | ||
link === false || (link == null && defaultLink === false); | ||
// When the link prop is not provided, we infer a default value and check whether users | ||
// can access it | ||
if (link == null) { | ||
// check if the user can access the show and edit pages in parallel | ||
const [canAccessShow, canAccessEdit] = await Promise.all([ | ||
resourceDefinition.hasShow | ||
? canAccess({ | ||
action: 'show', | ||
resource: finalResource, | ||
record, | ||
}) | ||
: Promise.resolve(false), | ||
resourceDefinition.hasEdit | ||
? canAccess({ | ||
action: 'edit', | ||
resource: finalResource, | ||
record, | ||
}) | ||
: Promise.resolve(false), | ||
]); | ||
if (record == null || isLinkFalse) { | ||
if (canAccessShow) { | ||
return createPath({ | ||
resource: finalResource, | ||
id: record.id, | ||
type: 'show', | ||
}); | ||
} | ||
if (canAccessEdit) { | ||
return createPath({ | ||
resource: finalResource, | ||
id: record.id, | ||
type: 'edit', | ||
}); | ||
} | ||
return false; | ||
} | ||
const linkResult = linkFunc(record, finalResource) ?? defaultLink; | ||
const linkFunc = typeof link === 'function' ? link : () => link; | ||
const linkResult = linkFunc(record, finalResource); | ||
if (linkResult === false) { | ||
return false; | ||
} | ||
const linkResultIsPromise = isPromise(linkResult); | ||
if (linkResultIsPromise) { | ||
return linkResult.then(resolvedLink => { | ||
if (resolvedLink === false) { | ||
// already set to false by default | ||
return; | ||
} | ||
return createPath({ | ||
resource: finalResource, | ||
id: record.id, | ||
type: resolvedLink, | ||
}); | ||
const resolvedLink = await linkResult; | ||
if (resolvedLink === false) { | ||
// already set to false by default | ||
return; | ||
} | ||
return createPath({ | ||
resource: finalResource, | ||
id: record.id, | ||
type: resolvedLink, | ||
}); | ||
} | ||
return linkResult === false || linkResultIsPromise | ||
? false | ||
: createPath({ | ||
resource: finalResource, | ||
id: record.id, | ||
type: linkResult, | ||
}); | ||
return createPath({ | ||
resource: finalResource, | ||
id: record.id, | ||
type: linkResult, | ||
}); | ||
}, | ||
[createPath, resourceDefinitions, resource] | ||
[canAccess, createPath, resourceDefinitions, resource] | ||
); | ||
@@ -69,0 +103,0 @@ }; |
import { ComponentType, ReactElement, ReactNode } from 'react'; | ||
import { FieldPath } from 'react-hook-form'; | ||
import { WithPermissionsChildrenParams } from './auth/WithPermissions'; | ||
@@ -68,7 +69,15 @@ import { AuthActionType } from './auth/types'; | ||
getIdentity?: (params?: QueryFunctionContext) => Promise<UserIdentity>; | ||
getPermissions: (params: any & QueryFunctionContext) => Promise<any>; | ||
getPermissions?: (params: any & QueryFunctionContext) => Promise<any>; | ||
handleCallback?: ( | ||
params?: QueryFunctionContext | ||
) => Promise<AuthRedirectResult | void | any>; | ||
canAccess?: <RecordType extends Record<string, any> = Record<string, any>>( | ||
params: QueryFunctionContext & { | ||
action: string; | ||
resource: string; | ||
record?: RecordType; | ||
} | ||
) => Promise<boolean>; | ||
[key: string]: any; | ||
supportAbortSignal?: boolean; | ||
}; | ||
@@ -175,2 +184,3 @@ | ||
data: RecordType; | ||
meta?: any; | ||
} | ||
@@ -185,2 +195,3 @@ | ||
data: RecordType[]; | ||
meta?: any; | ||
} | ||
@@ -215,2 +226,3 @@ | ||
data: RecordType; | ||
meta?: any; | ||
} | ||
@@ -225,2 +237,3 @@ | ||
data?: RecordType['id'][]; | ||
meta?: any; | ||
} | ||
@@ -234,2 +247,3 @@ | ||
data: RecordType; | ||
meta?: any; | ||
} | ||
@@ -244,2 +258,3 @@ | ||
data: RecordType; | ||
meta?: any; | ||
} | ||
@@ -253,2 +268,3 @@ | ||
data?: RecordType['id'][]; | ||
meta?: any; | ||
} | ||
@@ -399,1 +415,22 @@ | ||
}; | ||
// Type for a string that accept one of the known values but also any other string | ||
// Useful for IDE autocompletion without preventing custom values | ||
export type HintedString<KnownValues extends string> = AnyString | KnownValues; | ||
// Re-export react-hook-form implementation of FieldPath that returns all possible paths of an object | ||
// This will allow us to either include the FieldPath implementation from react-hook-form or replace it with our own | ||
// should we move away from react-hook-form | ||
// type Post = { title: string; author: { name: string; }; tags: { id: string; name: string} }; | ||
// => Valid paths are "title" | "author" | "author.name" | "tags.id" | "tags.name" | ||
export type RecordValues = Record<string, any>; | ||
export type RecordPath<TRecordValues extends RecordValues> = | ||
FieldPath<TRecordValues>; | ||
// Returns the union of all possible paths of a type if it is provided, otherwise returns a string | ||
// Useful for props such as "source" in react-admin components | ||
export type ExtractRecordPaths<T extends RecordValues> = | ||
// Trick that allows to check whether T was provided | ||
[T] extends [never] ? string : RecordPath<T>; | ||
export type AnyString = string & {}; |
import get from 'lodash/get'; | ||
import { Call, Objects } from 'hotscript'; | ||
import { useRecordContext } from '../controller'; | ||
import { ExtractRecordPaths } from '../types'; | ||
@@ -41,8 +41,4 @@ /** | ||
defaultValue?: any; | ||
source: Call<Objects.AllPaths, RecordType> extends never | ||
? AnyString | ||
: Call<Objects.AllPaths, RecordType>; | ||
source: ExtractRecordPaths<RecordType>; | ||
record?: RecordType; | ||
} | ||
type AnyString = string & {}; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
5914827
15
2835
120531
- Removedhotscript@^1.0.12
- Removedhotscript@1.0.13(transitive)