Apps list page enchancements (#2035)

* Remove marketplace from Apps list

* Move apps in progress to bottom

* Remove pagination from InstalledApps

* Add apps permissions tooltip

* Activate/deactivate InstalledApps from list

* Add changes description to CHANGELOG

* Update package.json to include macaw required changes

* Upadte fixtures

* Rename Local Apps -> Third Party Apps

* Update macaw, fix TS errors

* Refactor AppPermission component to use permission fragment

* Add fragment for app list query, refactor InstalledApps props type

* Fix check for usage within context inside useAppListContext

* Remove redundant errors check in mutation hooks inside AppsList

* Update extracted messages

* Fix AppListPage stories failing

* Fix Tooltip not working in failed installed apps

* Update messages
This commit is contained in:
Jonatan Witoszek 2022-05-31 17:18:15 +02:00 committed by GitHub
parent 1a19289e43
commit 5138608f86
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 726 additions and 778 deletions

View file

@ -6,6 +6,7 @@ All notable, unreleased changes to this project will be documented in this file.
- Added links instead of imperative navigation with onClick - #1969 by @taniotanio7
- Fixed clearing attribute values - #2047 by @witoszekdev
- Fixed EditorJS integration in RichTextEditor input - #2052 by @witoszekdev
- Improvements to the app list page: added toggle and permision preview - #2035 by @witoszekdev
- Added links to table pagination buttons - #2063 by @witoszekdev
- Using push instead of replace to history stack for pagination navigation - #2063 by @witoszekdev

View file

@ -86,6 +86,9 @@
"context": "error message",
"string": "This attribute is already assigned."
},
"+iV0gu": {
"string": "Internal Apps"
},
"+iVKR1": {
"context": "assign attribute value button",
"string": "Assign value"
@ -1658,6 +1661,10 @@
"BtErCZ": {
"string": "Search Plugins..."
},
"BvmnJq": {
"context": "section header",
"string": "Third Party Apps"
},
"Bx367s": {
"context": "product is hidden",
"string": "Hidden"
@ -2760,10 +2767,6 @@
"context": "filtering option",
"string": "All Warehouses"
},
"JufWFT": {
"context": "app installation error",
"string": "There was a problem during installation"
},
"Jwuu4X": {
"context": "select product informations to be exported",
"string": "Information exported:"
@ -3930,10 +3933,6 @@
"context": "order subtotal price",
"string": "Subtotal"
},
"TBaMo2": {
"context": "about app",
"string": "About"
},
"TC/EOG": {
"context": "status section title",
"string": "Status in channel"
@ -4596,6 +4595,10 @@
"context": "filters error messages value required",
"string": "Choose a value"
},
"Xl0o2y": {
"context": "app installation error",
"string": "Problem occured during installation"
},
"XlPKAR": {
"context": "WarehouseSettings private stock description",
"string": "If enabled stock in this warehouse won't be shown"
@ -4809,10 +4812,6 @@
"context": "subsection header",
"string": "Shipping Address"
},
"ZeD2TK": {
"context": "section header",
"string": "Third-party Apps"
},
"Zg0dRo": {
"context": "dialog description",
"string": "You have changed customer assigned to this order. What would you like to do with the shipping address?"
@ -7505,9 +7504,6 @@
"context": "section header",
"string": "Plugin Information and Status"
},
"w4R/SO": {
"string": "Local Apps"
},
"w6Gau0": {
"context": "deactivate named app",
"string": "Are you sure you want to disable {name}? Your data will be kept until you reactivate the app. You will be still billed for the app."
@ -7630,6 +7626,10 @@
"xJQX5t": {
"string": "No staff members found"
},
"xNfh4L": {
"context": "app permissions tooltip header",
"string": "App permissions"
},
"xOEZjV": {
"context": "attribute's label",
"string": "Default Label"

View file

@ -0,0 +1,49 @@
import { AppPermissionFragment } from "@saleor/graphql";
import {
IconButton,
makeStyles,
PermissionsIcon,
Tooltip
} from "@saleor/macaw-ui";
import React from "react";
import { FormattedMessage } from "react-intl";
const useStyles = makeStyles(
() => ({
list: {
margin: 0,
paddingLeft: "16px"
}
}),
{ name: "AppPermissions" }
);
interface AppPermissionsProps {
permissions: AppPermissionFragment[];
}
export const AppPermissions = ({ permissions }: AppPermissionsProps) => {
const classes = useStyles();
return (
<Tooltip
header={
<FormattedMessage
defaultMessage="App permissions"
id="xNfh4L"
description="app permissions tooltip header"
/>
}
title={
<ul className={classes.list}>
{permissions.map(permission => (
<li key={permission.code}>{permission.name}</li>
))}
</ul>
}
>
<IconButton variant="secondary" color="primary">
<PermissionsIcon />
</IconButton>
</Tooltip>
);
};

View file

@ -4,16 +4,20 @@ import {
TableBody,
TableCell,
TableRow,
Tooltip,
Typography
} from "@material-ui/core";
import ErrorIcon from "@material-ui/icons/Error";
import { Button } from "@saleor/components/Button";
import CardTitle from "@saleor/components/CardTitle";
import { IconButton } from "@saleor/components/IconButton";
import { TableButtonWrapper } from "@saleor/components/TableButtonWrapper/TableButtonWrapper";
import { AppsInstallationsQuery, JobStatusEnum } from "@saleor/graphql";
import { DeleteIcon, ResponsiveTable } from "@saleor/macaw-ui";
import {
DeleteIcon,
Indicator,
ResponsiveTable,
Tooltip,
TooltipMountWrapper
} from "@saleor/macaw-ui";
import { renderCollection } from "@saleor/misc";
import classNames from "classnames";
import React from "react";
@ -82,17 +86,14 @@ const AppsInProgress: React.FC<AppsInProgressProps> = ({
>
<Typography variant="body2" className={classes.error}>
<FormattedMessage
id="JufWFT"
defaultMessage="There was a problem during installation"
id="Xl0o2y"
defaultMessage="Problem occured during installation"
description="app installation error"
/>
<Tooltip
title={<Typography variant="body2">{message}</Typography>}
classes={{
tooltip: classes.customTooltip
}}
>
<ErrorIcon />
<Tooltip title={message} variant="error">
<TooltipMountWrapper>
<Indicator icon="error" />
</TooltipMountWrapper>
</Tooltip>
</Typography>
<TableButtonWrapper>

View file

@ -1,3 +1,4 @@
import { AppListContext } from "@saleor/apps/context";
import {
listActionsProps,
pageListProps,
@ -36,6 +37,13 @@ const props: AppsListPageProps = {
storiesOf("Views / Apps / Apps list", module)
.addDecorator(Decorator)
.addDecorator(story => (
<AppListContext.Provider
value={{ activateApp: () => undefined, deactivateApp: () => undefined }}
>
{story()}
</AppListContext.Provider>
))
.addDecorator(PaginatorContextDecorator)
.add("default", () => <AppsListPage {...props} />)
.add("loading", () => (

View file

@ -10,7 +10,6 @@ import { useIntl } from "react-intl";
import AppsInProgress from "../AppsInProgress/AppsInProgress";
import CustomApps from "../CustomApps/CustomApps";
import InstalledApps from "../InstalledApps/InstalledApps";
import Marketplace from "../Marketplace";
export interface AppsListPageProps extends ListProps {
installedAppsList: AppsListQuery["apps"]["edges"];
@ -43,17 +42,6 @@ const AppsListPage: React.FC<AppsListPageProps> = ({
return (
<Container>
<PageHeader title={intl.formatMessage(sectionNames.apps)} />
{!!appsInProgress?.length && (
<>
<AppsInProgress
appsList={appsInProgress}
disabled={loadingAppsInProgress}
onAppInstallRetry={onAppInstallRetry}
onRemove={onAppInProgressRemove}
/>
<CardSpacer />
</>
)}
<InstalledApps
appsList={installedAppsList}
onRemove={onInstalledAppRemove}
@ -65,8 +53,17 @@ const AppsListPage: React.FC<AppsListPageProps> = ({
getCustomAppHref={getCustomAppHref}
onRemove={onCustomAppRemove}
/>
{!!appsInProgress?.length && (
<>
<CardSpacer />
<Marketplace />
<AppsInProgress
appsList={appsInProgress}
disabled={loadingAppsInProgress}
onAppInstallRetry={onAppInstallRetry}
onRemove={onAppInProgressRemove}
/>
</>
)}
</Container>
);
};

View file

@ -1,19 +1,18 @@
import {
Card,
Switch,
TableBody,
TableCell,
TableFooter,
TableRow,
Typography
} from "@material-ui/core";
import { appDetailsUrl, appUrl } from "@saleor/apps/urls";
import { Button } from "@saleor/components/Button";
import { useAppListContext } from "@saleor/apps/context";
import { appUrl } from "@saleor/apps/urls";
import CardTitle from "@saleor/components/CardTitle";
import { IconButton } from "@saleor/components/IconButton";
import { TableButtonWrapper } from "@saleor/components/TableButtonWrapper/TableButtonWrapper";
import { TablePaginationWithContext } from "@saleor/components/TablePagination";
import TableRowLink from "@saleor/components/TableRowLink";
import { AppsListQuery } from "@saleor/graphql";
import { AppListItemFragment, AppsListQuery } from "@saleor/graphql";
import { DeleteIcon, ResponsiveTable } from "@saleor/macaw-ui";
import { renderCollection } from "@saleor/misc";
import { ListProps } from "@saleor/types";
@ -22,14 +21,13 @@ import React from "react";
import { FormattedMessage, useIntl } from "react-intl";
import { useStyles } from "../../styles";
import { AppPermissions } from "../AppPermissions/AppPermissions";
import AppsSkeleton from "../AppsSkeleton";
import DeactivatedText from "../DeactivatedText";
export interface InstalledAppsProps extends ListProps {
appsList: AppsListQuery["apps"]["edges"];
onRemove: (id: string) => void;
}
const numberOfColumns = 2;
const InstalledApps: React.FC<InstalledAppsProps> = ({
appsList,
@ -41,26 +39,26 @@ const InstalledApps: React.FC<InstalledAppsProps> = ({
}) => {
const intl = useIntl();
const classes = useStyles(props);
const { activateApp, deactivateApp } = useAppListContext();
const getHandleToggle = (app: AppListItemFragment) => () => {
if (app.isActive) {
deactivateApp(app.id);
} else {
activateApp(app.id);
}
};
return (
<Card className={classes.apps}>
<CardTitle
title={intl.formatMessage({
id: "ZeD2TK",
defaultMessage: "Third-party Apps",
id: "BvmnJq",
defaultMessage: "Third Party Apps",
description: "section header"
})}
/>
<ResponsiveTable>
<TableFooter>
<TableRow>
<TablePaginationWithContext
colSpan={numberOfColumns}
settings={settings}
onUpdateListSettings={onUpdateListSettings}
/>
</TableRow>
</TableFooter>
<TableBody>
{renderCollection(
appsList,
@ -75,11 +73,6 @@ const InstalledApps: React.FC<InstalledAppsProps> = ({
<span data-tc="name" className={classes.appName}>
{app.node.name}
</span>
{!app.node.isActive && (
<div className={classes.statusWrapper}>
<DeactivatedText />
</div>
)}
</TableCell>
<TableCell className={classes.colAction}>
{app.node.appUrl && (
@ -91,14 +84,12 @@ const InstalledApps: React.FC<InstalledAppsProps> = ({
</Typography>
)}
<TableButtonWrapper>
<Button href={appDetailsUrl(app.node.id)}>
<FormattedMessage
id="TBaMo2"
defaultMessage="About"
description="about app"
<Switch
checked={app.node.isActive}
onChange={getHandleToggle(app.node)}
/>
</Button>
</TableButtonWrapper>
<AppPermissions permissions={app.node.permissions} />
<TableButtonWrapper>
<IconButton
variant="secondary"

View file

@ -1,2 +0,0 @@
export * from "./Marketplace";
export { default } from "./Marketplace";

21
src/apps/context.ts Normal file
View file

@ -0,0 +1,21 @@
/* eslint-disable @typescript-eslint/no-empty-function */
import React from "react";
export interface AppListContextValues {
activateApp: (appId: string) => void;
deactivateApp: (appId: string) => void;
}
export const AppListContext = React.createContext<
AppListContextValues | undefined
>(undefined);
export const useAppListContext = () => {
const context = React.useContext(AppListContext);
if (!context) {
throw new Error("useAppListContext must be used within a AppListContext");
}
return context;
};

View file

@ -17,7 +17,14 @@ export const appsList: AppsListQuery["apps"]["edges"] = [
isActive: true,
name: "app",
type: AppTypeEnum.THIRDPARTY,
appUrl: null
appUrl: null,
permissions: [
{
__typename: "Permission",
code: PermissionEnum.MANAGE_USERS,
name: "Manage customers."
}
]
}
},
{
@ -28,7 +35,19 @@ export const appsList: AppsListQuery["apps"]["edges"] = [
isActive: false,
name: "app1",
type: AppTypeEnum.THIRDPARTY,
appUrl: "http://localhost:3000"
appUrl: "http://localhost:3000",
permissions: [
{
__typename: "Permission",
code: PermissionEnum.MANAGE_ORDERS,
name: "Manage orders."
},
{
__typename: "Permission",
code: PermissionEnum.MANAGE_USERS,
name: "Manage customers."
}
]
}
}
];
@ -42,7 +61,19 @@ export const customAppsList: AppsListQuery["apps"]["edges"] = [
isActive: true,
name: "app custom",
type: AppTypeEnum.LOCAL,
appUrl: null
appUrl: null,
permissions: [
{
__typename: "Permission",
code: PermissionEnum.MANAGE_ORDERS,
name: "Manage orders."
},
{
__typename: "Permission",
code: PermissionEnum.MANAGE_USERS,
name: "Manage customers."
}
]
}
}
];

View file

@ -26,11 +26,7 @@ export const appsList = gql`
totalCount
edges {
node {
id
name
isActive
type
appUrl
...AppListItem
}
}
}

View file

@ -75,13 +75,12 @@ export const useStyles = makeStyles(
padding: "0!important"
},
error: {
"& svg": {
bottom: theme.spacing(0.2),
marginLeft: theme.spacing(0.6),
position: "relative"
"& button": {
marginLeft: theme.spacing(0.6)
},
color: theme.palette.error.main,
margin: theme.spacing(0, 1, 0.7, 0)
marginRight: theme.spacing(1),
alignItems: "flex-end"
},
headerLinkContainer: {
"& svg": {

View file

@ -5,7 +5,12 @@ import { ActiveTab, Dialog, Pagination, SingleAction } from "../types";
export const MANIFEST_ATTR = "manifestUrl";
export type AppListUrlDialog = "remove" | "remove-app" | "remove-custom-app";
export type AppListUrlDialog =
| "remove"
| "remove-app"
| "remove-custom-app"
| "app-activate"
| "app-deactivate";
export type AppDetailsUrlDialog = "app-activate" | "app-deactivate";

View file

@ -1,13 +1,16 @@
import { useApolloClient } from "@apollo/client";
import AppActivateDialog from "@saleor/apps/components/AppActivateDialog";
import AppDeactivateDialog from "@saleor/apps/components/AppDeactivateDialog";
import { AppListContext, AppListContextValues } from "@saleor/apps/context";
import {
AppDeleteFailedInstallationMutation,
AppDeleteMutation,
AppsInstallationsQuery,
AppsListQuery,
AppSortField,
AppTypeEnum,
JobStatusEnum,
OrderDirection,
useAppActivateMutation,
useAppDeactivateMutation,
useAppDeleteFailedInstallationMutation,
useAppDeleteMutation,
useAppRetryInstallMutation,
@ -23,7 +26,6 @@ import usePaginator, {
PaginatorContext
} from "@saleor/hooks/usePaginator";
import { ListViews } from "@saleor/types";
import getAppErrorMessage from "@saleor/utils/errors/app";
import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers";
import React, { useEffect, useRef } from "react";
import { useIntl } from "react-intl";
@ -131,17 +133,36 @@ export const AppsList: React.FC<AppsListProps> = ({ params }) => {
};
const [retryInstallApp] = useAppRetryInstallMutation({
onCompleted: data => {
const errors = data.appRetryInstall.errors;
if (!errors.length) {
if (!data?.appRetryInstall?.errors?.length) {
const appInstallation = data.appRetryInstall.appInstallation;
setActiveInstallations(installations => [
...installations,
{ id: appInstallation.id, name: appInstallation.appName }
]);
} else {
errors.forEach(error =>
notify({ status: "error", text: getAppErrorMessage(error, intl) })
);
}
}
});
const [activateApp, activateAppResult] = useAppActivateMutation({
onCompleted: data => {
if (!data?.appActivate?.errors?.length) {
notify({
status: "success",
text: intl.formatMessage(messages.appActivated)
});
refetch();
closeModal();
}
}
});
const [deactivateApp, deactivateAppResult] = useAppDeactivateMutation({
onCompleted: data => {
if (!data?.appDeactivate?.errors?.length) {
notify({
status: "success",
text: intl.formatMessage(messages.appDeactivated)
});
refetch();
closeModal();
}
}
});
@ -150,9 +171,9 @@ export const AppsList: React.FC<AppsListProps> = ({ params }) => {
AppListUrlQueryParams
>(navigate, appsListUrl, params);
const onAppRemove = (data: AppDeleteMutation) => {
const errors = data.appDelete.errors;
if (errors.length === 0) {
const [deleteApp, deleteAppOpts] = useAppDeleteMutation({
onCompleted: data => {
if (!data?.appDelete?.errors?.length) {
if (data.appDelete.app.type === AppTypeEnum.LOCAL) {
customAppsRefetch();
} else {
@ -161,19 +182,7 @@ export const AppsList: React.FC<AppsListProps> = ({ params }) => {
closeModal();
refetchExtensionList();
removeAppNotify();
} else {
errors.forEach(error =>
notify({
status: "error",
text: getAppErrorMessage(error, intl)
})
);
}
};
const [deleteApp, deleteAppOpts] = useAppDeleteMutation({
onCompleted: data => {
onAppRemove(data);
}
});
const [
@ -181,7 +190,11 @@ export const AppsList: React.FC<AppsListProps> = ({ params }) => {
deleteInProgressAppOpts
] = useAppDeleteFailedInstallationMutation({
onCompleted: data => {
onAppInProgressRemove(data);
if (!data?.appDeleteFailedInstallation?.errors?.length) {
removeAppNotify();
appsInProgressRefetch();
closeModal();
}
}
});
@ -256,28 +269,28 @@ export const AppsList: React.FC<AppsListProps> = ({ params }) => {
});
};
const onAppInProgressRemove = (data: AppDeleteFailedInstallationMutation) => {
const errors = data.appDeleteFailedInstallation.errors;
if (errors.length === 0) {
removeAppNotify();
appsInProgressRefetch();
closeModal();
} else {
errors.forEach(error =>
notify({
status: "error",
text: getAppErrorMessage(error, intl)
})
);
}
};
const handleActivateAppConfirm = () =>
activateApp({ variables: { id: params.id } });
const handleDeactivateAppConfirm = () =>
deactivateApp({ variables: { id: params.id } });
const onAppInstallRetry = (id: string) =>
retryInstallApp({ variables: { id } });
const installedApps = data?.apps?.edges;
const customApps = customAppsData?.apps?.edges;
const context: AppListContextValues = React.useMemo(
() => ({
activateApp: id => openModal("app-activate", { id }),
deactivateApp: id => openModal("app-deactivate", { id })
}),
[activateApp, deactivateApp]
);
return (
<AppListContext.Provider value={context}>
<PaginatorContext.Provider value={paginationValues}>
<AppDeleteDialog
confirmButtonState={deleteAppOpts.status}
@ -290,6 +303,20 @@ export const AppsList: React.FC<AppsListProps> = ({ params }) => {
type={action === "remove-app" ? "EXTERNAL" : "CUSTOM"}
open={action === "remove-app" || action === "remove-custom-app"}
/>
<AppActivateDialog
confirmButtonState={activateAppResult.status}
name={getCurrentAppName(params.id, installedApps)}
onClose={closeModal}
onConfirm={handleActivateAppConfirm}
open={params.action === "app-activate"}
/>
<AppDeactivateDialog
confirmButtonState={deactivateAppResult.status}
name={getCurrentAppName(params.id, installedApps)}
onClose={closeModal}
onConfirm={handleDeactivateAppConfirm}
open={params.action === "app-deactivate"}
/>
<AppInProgressDeleteDialog
confirmButtonState={deleteInProgressAppOpts.status}
name={getAppInProgressName(
@ -327,6 +354,7 @@ export const AppsList: React.FC<AppsListProps> = ({ params }) => {
}
/>
</PaginatorContext.Provider>
</AppListContext.Provider>
);
};

View file

@ -20,5 +20,15 @@ export const messages = defineMessages({
id: "5t/4um",
defaultMessage: "Couldnt Install {name}",
description: "message title"
},
appActivated: {
id: "D/+84n",
defaultMessage: "App activated",
description: "snackbar text"
},
appDeactivated: {
id: "USO8PB",
defaultMessage: "App deactivated",
description: "snackbar text"
}
});

View file

@ -54,7 +54,7 @@ export interface AppListViewSettings {
export const defaultListSettings: AppListViewSettings = {
[ListViews.APPS_LIST]: {
rowNumber: 10
rowNumber: 100
},
[ListViews.ATTRIBUTE_VALUE_LIST]: {
rowNumber: 10

View file

@ -31,3 +31,23 @@ export const appFragment = gql`
}
}
`;
export const appListItemFragment = gql`
fragment AppListItem on App {
id
name
isActive
type
appUrl
permissions {
...AppPermission
}
}
`;
export const appPermissionFragment = gql`
fragment AppPermission on Permission {
name
code
}
`;

View file

@ -47,6 +47,24 @@ export const AppFragmentDoc = gql`
}
}
${WebhookFragmentDoc}`;
export const AppPermissionFragmentDoc = gql`
fragment AppPermission on Permission {
name
code
}
`;
export const AppListItemFragmentDoc = gql`
fragment AppListItem on App {
id
name
isActive
type
appUrl
permissions {
...AppPermission
}
}
${AppPermissionFragmentDoc}`;
export const AttributeFragmentDoc = gql`
fragment Attribute on Attribute {
id
@ -3135,16 +3153,12 @@ export const AppsListDocument = gql`
totalCount
edges {
node {
id
name
isActive
type
appUrl
...AppListItem
}
}
}
}
`;
${AppListItemFragmentDoc}`;
/**
* __useAppsListQuery__

View file

@ -5251,7 +5251,7 @@ export type AppsListQueryVariables = Exact<{
}>;
export type AppsListQuery = { __typename: 'Query', apps: { __typename: 'AppCountableConnection', totalCount: number | null, pageInfo: { __typename: 'PageInfo', hasNextPage: boolean, hasPreviousPage: boolean, startCursor: string | null, endCursor: string | null }, edges: Array<{ __typename: 'AppCountableEdge', node: { __typename: 'App', id: string, name: string | null, isActive: boolean | null, type: AppTypeEnum | null, appUrl: string | null } }> } | null };
export type AppsListQuery = { __typename: 'Query', apps: { __typename: 'AppCountableConnection', totalCount: number | null, pageInfo: { __typename: 'PageInfo', hasNextPage: boolean, hasPreviousPage: boolean, startCursor: string | null, endCursor: string | null }, edges: Array<{ __typename: 'AppCountableEdge', node: { __typename: 'App', id: string, name: string | null, isActive: boolean | null, type: AppTypeEnum | null, appUrl: string | null, permissions: Array<{ __typename: 'Permission', name: string, code: PermissionEnum }> | null } }> } | null };
export type AppsInstallationsQueryVariables = Exact<{ [key: string]: never; }>;
@ -5909,6 +5909,10 @@ export type AddressFragment = { __typename: 'Address', city: string, cityArea: s
export type AppFragment = { __typename: 'App', id: string, name: string | null, created: any | null, isActive: boolean | null, type: AppTypeEnum | null, homepageUrl: string | null, appUrl: string | null, configurationUrl: string | null, supportUrl: string | null, version: string | null, accessToken: string | null, privateMetadata: Array<{ __typename: 'MetadataItem', key: string, value: string }>, metadata: Array<{ __typename: 'MetadataItem', key: string, value: string }>, tokens: Array<{ __typename: 'AppToken', authToken: string | null, id: string, name: string | null }> | null, webhooks: Array<{ __typename: 'Webhook', id: string, name: string, isActive: boolean, app: { __typename: 'App', id: string, name: string | null } }> | null };
export type AppListItemFragment = { __typename: 'App', id: string, name: string | null, isActive: boolean | null, type: AppTypeEnum | null, appUrl: string | null, permissions: Array<{ __typename: 'Permission', name: string, code: PermissionEnum }> | null };
export type AppPermissionFragment = { __typename: 'Permission', name: string, code: PermissionEnum };
export type AttributeValueFragment = { __typename: 'AttributeValue', id: string, name: string | null, slug: string | null, reference: string | null, boolean: boolean | null, date: any | null, dateTime: any | null, value: string | null, file: { __typename: 'File', url: string, contentType: string | null } | null };
export type AttributeValueDetailsFragment = { __typename: 'AttributeValue', richText: any | null, id: string, name: string | null, slug: string | null, reference: string | null, boolean: boolean | null, date: any | null, dateTime: any | null, value: string | null, file: { __typename: 'File', url: string, contentType: string | null } | null };

View file

@ -19,8 +19,8 @@ export const commonMessages = defineMessages({
defaultMessage: "Channel"
},
customApps: {
id: "w4R/SO",
defaultMessage: "Local Apps"
id: "+iV0gu",
defaultMessage: "Internal Apps"
},
dashboard: {
id: "hzSNj4",

File diff suppressed because it is too large Load diff