saleor-dashboard/src/index.tsx

303 lines
10 KiB
TypeScript
Raw Normal View History

import Navigator from "@saleor/components/Navigator";
import useAppState from "@saleor/hooks/useAppState";
import { defaultDataIdFromObject, InMemoryCache } from "apollo-cache-inmemory";
2019-06-19 14:40:52 +00:00
import { ApolloClient } from "apollo-client";
import { ApolloLink } from "apollo-link";
import { BatchHttpLink } from "apollo-link-batch-http";
import { setContext } from "apollo-link-context";
import { ErrorResponse, onError } from "apollo-link-error";
import { createUploadLink } from "apollo-upload-client";
import React from "react";
2019-06-19 14:40:52 +00:00
import { ApolloProvider } from "react-apollo";
import { render } from "react-dom";
import ErrorBoundary from "react-error-boundary";
import TagManager from "react-gtm-module";
import { useIntl } from "react-intl";
import { BrowserRouter, Route, Switch } from "react-router-dom";
Apps (#599) * create Apps view * create more app components, generate types and messages * apps refactor, update snapshots * show error message in tooltip when app installation fail * update apps components and view, add apps list to storybook * update defaultMessages * create app details view * update AppListPage with Skeleton component * create app activate/deactivate dialogs, create app details stories * add AppHeader to AppDetailsPage * update defaultMessages * update AppDetails view and components after review * create custom app details view * refactor webhooks * update webhooks fixtures * update WebhookDetailsPage story * update strings * create CustomAppCreate view and components * update AppListPage story * create AppInstall view and page * handle errors in AppInstall view * update defaultMessages * add AppInstallPage to storybook * add status prop to MessageManager * update defaultMessages * remove service account section * remove service account routes * remove as operator from notify status * add notifications for app installations * update styles for deactivated app * update app installations with local storage * update defaultMessages * AppInstall update * dd delete button to ongoin installations table * fix active installations condition * fix error messages in AppsList * update defaultMessages * add iframe to AppDetailsPage * create AppDetailsSettingsPage * install macaw-ui * apps styles clean up * update schema, fixtures * few apps updates * WebhookCreate - fix onBack button name * WebhookCreatePage story update * rename apps table from external to thirdparty * update defaultMessages * fix test, update snapshots * AppDetailsSettings - add token to headers * fix first number in local apps query * app details settings - use shop domain host * add onSettingsRowClick to InstalledApps * resolve conflicts * update changelog and messages * add noopener noreferrer do app privacy link * update snapshots * update snapshots * updates after review * update defaultMessages * CustomAppDetails - add missing notify status
2020-07-22 10:54:15 +00:00
import AppsSection from "./apps";
import { appsSection } from "./apps/urls";
2019-08-09 11:14:35 +00:00
import AttributeSection from "./attributes";
import { attributeSection } from "./attributes/urls";
import Auth, { getAuthToken, removeAuthToken } from "./auth";
2020-07-14 08:11:43 +00:00
import AuthProvider, { useAuth } from "./auth/AuthProvider";
import LoginLoading from "./auth/components/LoginLoading/LoginLoading";
import SectionRoute from "./auth/components/SectionRoute";
import { isJwtError } from "./auth/errors";
import { hasPermission } from "./auth/misc";
2019-06-19 14:40:52 +00:00
import CategorySection from "./categories";
import CollectionSection from "./collections";
import AppLayout from "./components/AppLayout";
import { DateProvider } from "./components/Date";
import { LocaleProvider } from "./components/Locale";
import MessageManagerProvider from "./components/messages";
import { ShopProvider } from "./components/Shop";
import ThemeProvider from "./components/Theme";
import { WindowTitle } from "./components/WindowTitle";
import { API_URI, APP_MOUNT_URI, GTM_ID } from "./config";
import ConfigurationSection, { createConfigurationMenu } from "./configuration";
import AppStateProvider from "./containers/AppState";
import BackgroundTasksProvider from "./containers/BackgroundTasks";
import { CustomerSection } from "./customers";
2019-06-19 14:40:52 +00:00
import DiscountSection from "./discounts";
import HomePage from "./home";
import { commonMessages } from "./intl";
2019-06-19 14:40:52 +00:00
import NavigationSection from "./navigation";
import { navigationSection } from "./navigation/urls";
2019-06-19 14:40:52 +00:00
import { NotFound } from "./NotFound";
import OrdersSection from "./orders";
import PageSection from "./pages";
import PermissionGroupSection from "./permissionGroups";
2019-08-21 14:30:56 +00:00
import PluginsSection from "./plugins";
2019-06-19 14:40:52 +00:00
import ProductSection from "./products";
import ProductTypesSection from "./productTypes";
import ShippingSection from "./shipping";
import SiteSettingsSection from "./siteSettings";
import StaffSection from "./staff";
import TaxesSection from "./taxes";
import TranslationsSection from "./translations";
import { PermissionEnum } from "./types/globalTypes";
2020-01-30 11:46:35 +00:00
import WarehouseSection from "./warehouses";
import { warehouseSection } from "./warehouses/urls";
2019-06-19 14:40:52 +00:00
interface ResponseError extends ErrorResponse {
networkError?: Error & {
statusCode?: number;
bodyText?: string;
};
}
2020-05-25 23:36:56 +00:00
if (process.env.GTM_ID !== undefined) {
TagManager.initialize({ gtmId: GTM_ID });
}
2019-06-19 14:40:52 +00:00
const invalidTokenLink = onError((error: ResponseError) => {
2020-05-07 11:04:15 +00:00
if (
(error.networkError && error.networkError.statusCode === 401) ||
error.graphQLErrors?.some(isJwtError)
) {
2019-06-19 14:40:52 +00:00
removeAuthToken();
}
});
const authLink = setContext((_, context) => {
const authToken = getAuthToken();
2019-09-03 13:42:15 +00:00
2019-06-19 14:40:52 +00:00
return {
...context,
headers: {
...context.headers,
Authorization: authToken ? `JWT ${authToken}` : null
}
};
});
2019-08-09 11:14:35 +00:00
// DON'T TOUCH THIS
// These are separate clients and do not share configs between themselves
// so we need to explicitly set them
2019-06-19 14:40:52 +00:00
const linkOptions = {
credentials: "same-origin",
uri: API_URI
};
const uploadLink = createUploadLink(linkOptions);
2019-08-09 11:14:35 +00:00
const batchLink = new BatchHttpLink({
batchInterval: 100,
...linkOptions
});
2019-06-19 14:40:52 +00:00
const link = ApolloLink.split(
operation => operation.getContext().useBatching,
batchLink,
uploadLink
);
const apolloClient = new ApolloClient({
cache: new InMemoryCache({
dataIdFromObject: (obj: any) => {
// We need to set manually shop's ID, since it is singleton and
// API does not return its ID
if (obj.__typename === "Shop") {
return "shop";
}
return defaultDataIdFromObject(obj);
}
}),
link: invalidTokenLink.concat(authLink.concat(link))
});
const App: React.FC = () => {
const isDark = localStorage.getItem("theme") === "true";
return (
2019-08-09 11:14:35 +00:00
<ApolloProvider client={apolloClient}>
<BrowserRouter basename={APP_MOUNT_URI}>
<ThemeProvider isDefaultDark={isDark}>
<DateProvider>
<LocaleProvider>
<MessageManagerProvider>
2020-06-23 12:31:27 +00:00
<BackgroundTasksProvider>
<AppStateProvider>
<ShopProvider>
2020-07-14 08:11:43 +00:00
<AuthProvider>
<Routes />
</AuthProvider>
2020-06-23 12:31:27 +00:00
</ShopProvider>
</AppStateProvider>
</BackgroundTasksProvider>
</MessageManagerProvider>
2019-08-09 11:14:35 +00:00
</LocaleProvider>
</DateProvider>
</ThemeProvider>
</BrowserRouter>
</ApolloProvider>
2019-06-19 14:40:52 +00:00
);
};
const Routes: React.FC = () => {
const intl = useIntl();
2019-11-14 14:10:52 +00:00
const [, dispatchAppState] = useAppState();
2020-07-14 08:11:43 +00:00
const {
hasToken,
isAuthenticated,
tokenAuthLoading,
tokenVerifyLoading,
user
} = useAuth();
return (
<>
<WindowTitle title={intl.formatMessage(commonMessages.dashboard)} />
2020-07-14 08:11:43 +00:00
{isAuthenticated && !tokenAuthLoading && !tokenVerifyLoading ? (
<AppLayout>
<Navigator />
<ErrorBoundary
onError={() =>
dispatchAppState({
payload: {
error: "unhandled"
},
type: "displayError"
})
}
>
<Switch>
<SectionRoute exact path="/" component={HomePage} />
<SectionRoute
permissions={[PermissionEnum.MANAGE_PRODUCTS]}
path="/categories"
component={CategorySection}
/>
<SectionRoute
permissions={[PermissionEnum.MANAGE_PRODUCTS]}
path="/collections"
component={CollectionSection}
/>
<SectionRoute
permissions={[PermissionEnum.MANAGE_USERS]}
path="/customers"
component={CustomerSection}
/>
<SectionRoute
permissions={[PermissionEnum.MANAGE_DISCOUNTS]}
path="/discounts"
component={DiscountSection}
/>
<SectionRoute
permissions={[PermissionEnum.MANAGE_PAGES]}
path="/pages"
component={PageSection}
/>
<SectionRoute
permissions={[PermissionEnum.MANAGE_PLUGINS]}
path="/plugins"
component={PluginsSection}
/>
<SectionRoute
permissions={[PermissionEnum.MANAGE_ORDERS]}
path="/orders"
component={OrdersSection}
/>
<SectionRoute
permissions={[PermissionEnum.MANAGE_PRODUCTS]}
path="/products"
component={ProductSection}
/>
<SectionRoute
permissions={[PermissionEnum.MANAGE_PRODUCTS]}
path="/product-types"
component={ProductTypesSection}
/>
<SectionRoute
permissions={[PermissionEnum.MANAGE_STAFF]}
path="/staff"
component={StaffSection}
/>
<SectionRoute
permissions={[PermissionEnum.MANAGE_STAFF]}
path="/permission-groups"
component={PermissionGroupSection}
/>
<SectionRoute
permissions={[PermissionEnum.MANAGE_SETTINGS]}
path="/site-settings"
component={SiteSettingsSection}
/>
<SectionRoute
permissions={[PermissionEnum.MANAGE_SETTINGS]}
path="/taxes"
component={TaxesSection}
/>
<SectionRoute
permissions={[PermissionEnum.MANAGE_SHIPPING]}
path="/shipping"
component={ShippingSection}
/>
<SectionRoute
permissions={[PermissionEnum.MANAGE_TRANSLATIONS]}
path="/translations"
component={TranslationsSection}
/>
<SectionRoute
permissions={[PermissionEnum.MANAGE_MENUS]}
path={navigationSection}
component={NavigationSection}
/>
<SectionRoute
permissions={[PermissionEnum.MANAGE_PRODUCTS]}
path={attributeSection}
component={AttributeSection}
/>
<SectionRoute
permissions={[PermissionEnum.MANAGE_APPS]}
path={appsSection}
component={AppsSection}
/>
<SectionRoute
permissions={[PermissionEnum.MANAGE_PRODUCTS]}
path={warehouseSection}
component={WarehouseSection}
/>
{createConfigurationMenu(intl).filter(menu =>
menu.menuItems.map(item => hasPermission(item.permission, user))
).length > 0 && (
<SectionRoute
exact
path="/configuration"
component={ConfigurationSection}
/>
)}
<Route component={NotFound} />
</Switch>
</ErrorBoundary>
</AppLayout>
) : hasToken && tokenVerifyLoading ? (
<LoginLoading />
) : (
2020-07-21 16:49:20 +00:00
<Auth />
2020-07-14 08:11:43 +00:00
)}
</>
);
};
2019-06-19 14:40:52 +00:00
render(<App />, document.querySelector("#dashboard-app"));