import * as React from 'react'; import fakeRestDataProvider from 'ra-data-fakerest'; import generateData from 'data-generator-retail'; import Admin from './Admin'; import { addOfflineSupportToQueryClient, Identifier, Resource, useDataProvider, useIsOffline, useNotify, useRecordContext, useRefresh, } from 'ra-core'; import { AppBar, Button, Create, DataTable, DateInput, Edit, EditButton, Layout, List, NumberField, NumberInput, ReferenceField, ReferenceInput, Show, ShowButton, SimpleForm, SimpleShowLayout, TextField, TextInput, TitlePortal, TopToolbar, } from 'ra-ui-materialui'; import { onlineManager, QueryClient, useMutation } from '@tanstack/react-query'; import { PersistQueryClientProvider } from '@tanstack/react-query-persist-client'; import { createAsyncStoragePersister } from '@tanstack/query-async-storage-persister'; import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; export default { title: 'react-admin/offline', }; const baseDataProvider = fakeRestDataProvider(generateData(), true, 350); const dataProvider = { ...baseDataProvider, emptyStock: ({ id, previousData, }: { id: Identifier; previousData: any; }) => { return baseDataProvider.update('products', { id, data: { stock: 0 }, previousData, }); }, }; type CustomDataProvider = typeof dataProvider; export const FullApp = ({ failMutations = false, }: { failMutations?: boolean; }) => { const queryClient = addOfflineSupportToQueryClient({ dataProvider, queryClient: new QueryClient({ defaultOptions: { queries: { gcTime: 1000 * 60 * 60 * 24, // 24 hours }, }, }), resources: ['products'], }); const asyncStoragePersister = createAsyncStoragePersister({ storage: localStorage, }); const localDataProvider = failMutations ? { ...dataProvider, create: resource => { return Promise.reject( new Error(`Server error: cannot create ${resource}`) ); }, update: resource => { return Promise.reject( new Error(`Server error: cannot update ${resource}`) ); }, updateMany: resource => { return Promise.reject( new Error(`Server error: cannot update ${resource}`) ); }, delete: resource => { return Promise.reject( new Error(`Server error: cannot delete ${resource}`) ); }, deleteMany: resource => { return Promise.reject( new Error(`Server error: cannot delete ${resource}`) ); }, emptyStock: () => { return Promise.reject('Could not empty stock'); }, } : dataProvider; return ( { // Resume mutations after initial restore from localStorage is successful queryClient.resumePausedMutations(); }} > ); }; FullApp.args = { failMutations: false, }; FullApp.argTypes = { failMutations: { type: 'boolean', }, }; const ProductList = () => ( ); const ProductEdit = () => ( } > ); const ProductCreate = () => ( ({ id: crypto.randomUUID(), ...data, })} > ); const ProductForm = () => ( ); const ProductShow = () => ( } > ); const EmptyStockButton = () => { const dataProvider = useDataProvider(); const notify = useNotify(); const refresh = useRefresh(); const record = useRecordContext(); const { mutate, isPending } = useMutation({ mutationKey: ['emptyStock'], mutationFn: (params: { id: Identifier; previousData: Record; }) => dataProvider.emptyStock(params), onSuccess: ({ data }) => { notify(`Stock of "${data.reference}" emptied`); refresh(); }, onError: () => { notify('An error occured while emptying the stock'); }, }); if (!record) return null; return ( ); };