diff --git a/src/plugins/components/PluginInfo/PluginInfo.tsx b/src/plugins/components/PluginInfo/PluginInfo.tsx new file mode 100644 index 000000000..b3b288c2f --- /dev/null +++ b/src/plugins/components/PluginInfo/PluginInfo.tsx @@ -0,0 +1,91 @@ +import Card from "@material-ui/core/Card"; +import CardContent from "@material-ui/core/CardContent"; +import { + createStyles, + Theme, + withStyles, + WithStyles +} from "@material-ui/core/styles"; +import TextField from "@material-ui/core/TextField"; +import Typography from "@material-ui/core/Typography"; +import React from "react"; + +import CardTitle from "@saleor/components/CardTitle"; +import ControlledSwitch from "@saleor/components/ControlledSwitch"; +import FormSpacer from "@saleor/components/FormSpacer"; +import Hr from "@saleor/components/Hr"; +import i18n from "../../../i18n"; +import { FormData } from "../PluginsDetailsPage"; + +interface PluginInfoProps { + data: FormData; + errors: Partial<{ + description: string; + domain: string; + name: string; + }>; + disabled: boolean; + onChange: (event: React.ChangeEvent) => void; +} + +const styles = (theme: Theme) => + createStyles({ + title: { + fontSize: 14, + color: "#616161", + paddingTop: 10 + }, + status: { + fontSize: 16, + color: "#3D3D3D", + paddingTop: 20, + fontWeight: "400" + } + }); + +const PluginInfo = withStyles(styles, { name: "PluginInfo" })( + ({ + data, + disabled, + classes, + errors, + onChange + }: PluginInfoProps & WithStyles) => { + return ( + + + + + {i18n.t("Plugin Name")} + + {data.name} + {data.description && ( + <> + + {i18n.t("Plugin Description")} + + {data.description} + + )} + +
+ + {i18n.t("Status")} + + +
+
+ ); + } +); +PluginInfo.displayName = "PluginInfo"; +export default PluginInfo; diff --git a/src/plugins/components/PluginInfo/index.ts b/src/plugins/components/PluginInfo/index.ts new file mode 100644 index 000000000..f2a38dc4e --- /dev/null +++ b/src/plugins/components/PluginInfo/index.ts @@ -0,0 +1,2 @@ +export { default } from "./PluginInfo"; +export * from "./PluginInfo"; diff --git a/src/plugins/components/PluginSettings/PluginSettings.tsx b/src/plugins/components/PluginSettings/PluginSettings.tsx new file mode 100644 index 000000000..2927f9ed4 --- /dev/null +++ b/src/plugins/components/PluginSettings/PluginSettings.tsx @@ -0,0 +1,83 @@ +import Card from "@material-ui/core/Card"; +import CardContent from "@material-ui/core/CardContent"; +import ControlledSwitch from "@saleor/components/ControlledSwitch"; +import { + createStyles, + Theme, + withStyles, + WithStyles +} from "@material-ui/core/styles"; +import TextField from "@material-ui/core/TextField"; +import Typography from "@material-ui/core/Typography"; +import React from "react"; + +import CardTitle from "@saleor/components/CardTitle"; +import FormSpacer from "@saleor/components/FormSpacer"; +import i18n from "../../../i18n"; +import { FormData } from "../PluginsDetailsPage"; + +interface PluginSettingsProps { + data: FormData; + errors: Partial<{ + description: string; + domain: string; + name: string; + }>; + disabled: boolean; + onChange: (event: React.ChangeEvent) => void; +} + +const styles = (theme: Theme) => + createStyles({ + item: { + marginBottom: 5, + marginTop: 10 + } + }); + +const PluginSettings = withStyles(styles, { name: "PluginSettings" })( + ({ + data, + disabled, + classes, + errors, + onChange + }: PluginSettingsProps & WithStyles) => ( + + + + {data.configuration.map((configuration, index) => ( +
+ {configuration.type === "STRING" && ( + + )} + {configuration.type === "BOOLEAN" && ( + + )} +
+ ))} +
+
+ ); +); +PluginSettings.displayName = "PluginSettings"; +export default PluginSettings; diff --git a/src/plugins/components/PluginSettings/index.ts b/src/plugins/components/PluginSettings/index.ts new file mode 100644 index 000000000..422ec9e3c --- /dev/null +++ b/src/plugins/components/PluginSettings/index.ts @@ -0,0 +1,2 @@ +export { default } from "./PluginSettings"; +export * from "./PluginSettings"; diff --git a/src/plugins/components/PluginsDetailsPage/PluginsDetailsPage.tsx b/src/plugins/components/PluginsDetailsPage/PluginsDetailsPage.tsx new file mode 100644 index 000000000..f68947787 --- /dev/null +++ b/src/plugins/components/PluginsDetailsPage/PluginsDetailsPage.tsx @@ -0,0 +1,147 @@ +import React from "react"; +import Typography from "@material-ui/core/Typography"; +import AppHeader from "@saleor/components/AppHeader"; +import CardSpacer from "@saleor/components/CardSpacer"; +import { ConfirmButtonTransitionState } from "@saleor/components/ConfirmButton"; +import Container from "@saleor/components/Container"; +import Form from "@saleor/components/Form"; +import Grid from "@saleor/components/Grid"; +import PageHeader from "@saleor/components/PageHeader"; +import SaveButtonBar from "@saleor/components/SaveButtonBar"; +import SeoForm from "@saleor/components/SeoForm"; +import VisibilityCard from "@saleor/components/VisibilityCard"; +import i18n from "../../../i18n"; +import { maybe } from "../../../misc"; +import { UserError } from "../../../types"; +import { PageDetails_page } from "../../types/PluginDetails"; +import PluginInfo from "../PluginInfo"; +import PluginSettings from "../PluginSettings"; + +export interface FormData { + name: string; + description: string; + active: boolean; + id: string; + configuration: [ + { + name: string; + value: string; + type: string; + helpText: string; + label: string; + } + ]; +} + +export interface PageDetailsPageProps { + disabled: boolean; + errors: UserError[]; + plugin: PageDetails_page; + saveButtonBarState: ConfirmButtonTransitionState; + onBack: () => void; + onSubmit: (data: FormData) => void; +} + +const PageDetailsPage: React.StatelessComponent = ({ + disabled, + errors, + plugin, + saveButtonBarState, + onBack, + onSubmit +}) => { + const initialForm: FormData = { + name: maybe(() => plugin.name, ""), + description: maybe(() => plugin.description, ""), + active: maybe(() => plugin.active, false), + configuration: maybe(() => plugin.configuration, []) + }; + return ( +
+ {({ + change, + data, + errors: formErrors, + hasChanged, + submit, + set, + triggerChange + }) => { + const newData = { + active: data.active, + configuration: data.configuration + }; + + const onChange = event => { + const { name, value } = event.target; + name === "active" + ? (newData.active = value) + : (newData.active = data.active); + + newData.configuration.map(item => { + if (item.name === name) { + item.value = value; + } + }); + + triggerChange(); + set(newData); + }; + return ( + + {i18n.t("Plugins")} + plugin.name, "")} ${i18n.t("Details")}`} + /> + +
+ + {i18n.t("Plugin Information and Status")} + + + {i18n.t( + "These are general information about your store. They define what is the URL of your store and what is shown in brow sers taskbar." + )} + +
+ + {data.configuration && ( + <> +
+ + {i18n.t("Plugin Settings")} + + + {i18n.t( + "This adress will be used to generate invoices and calculate shipping rates. Email adress you provide here will be used as a contact adress for your customers." + )} + +
+ + + )} +
+ +
+ ); + }} +
+ ); +}; +PageDetailsPage.displayName = "PageDetailsPage"; +export default PageDetailsPage; diff --git a/src/plugins/components/PluginsDetailsPage/index.ts b/src/plugins/components/PluginsDetailsPage/index.ts new file mode 100644 index 000000000..f38ee22f8 --- /dev/null +++ b/src/plugins/components/PluginsDetailsPage/index.ts @@ -0,0 +1,2 @@ +export { default } from "./PluginsDetailsPage"; +export * from "./PluginsDetailsPage"; diff --git a/src/plugins/components/PluginsList/PluginsList.tsx b/src/plugins/components/PluginsList/PluginsList.tsx index 5a895c9b9..0086d3559 100644 --- a/src/plugins/components/PluginsList/PluginsList.tsx +++ b/src/plugins/components/PluginsList/PluginsList.tsx @@ -49,7 +49,7 @@ const styles = (theme: Theme) => const numberOfColumns = 4; -const PluginList = withStyles(styles, { name: "PageList" })( +const PluginList = withStyles(styles, { name: "PluginList" })( ({ classes, settings, @@ -66,7 +66,6 @@ const PluginList = withStyles(styles, { name: "PageList" })( toggleAll, toolbar }: PluginListProps & WithStyles) => { - console.log(plugins); return ( diff --git a/src/plugins/index.tsx b/src/plugins/index.tsx index 651efd012..0cd192947 100644 --- a/src/plugins/index.tsx +++ b/src/plugins/index.tsx @@ -20,26 +20,26 @@ const PluginList: React.StatelessComponent> = ({ return ; }; -// const PageDetails: React.StatelessComponent> = ({ -// match -// }) => { -// const qs = parseQs(location.search.substr(1)); -// const params: PluginsListUrlQueryParams = qs; +const PageDetails: React.StatelessComponent> = ({ + match +}) => { + const qs = parseQs(location.search.substr(1)); + const params: PluginsListUrlQueryParams = qs; -// return ( -// -// ); -// }; + return ( + + ); +}; const Component = () => ( <> - {/* */} + ); diff --git a/src/plugins/mutations.ts b/src/plugins/mutations.ts index e69de29bb..e52658daa 100644 --- a/src/plugins/mutations.ts +++ b/src/plugins/mutations.ts @@ -0,0 +1,30 @@ +import gql from "graphql-tag"; + +import { TypedMutation } from "../mutations"; +import { pluginsDetailsFragment } from "./queries"; +import { + pluginConfigurationUpdate, + pluginConfigurationUpdateVariables +} from "./types/pluginConfigurationUpdate"; + +const pluginConfigurationUpdate = gql` + ${pluginsDetailsFragment} + mutation pluginConfigurationUpdate( + $id: ID! + $input: PluginConfigurationUpdateInput! + ) { + pluginConfigurationUpdate(id: $id, input: $input) { + errors { + field + message + } + pluginConfiguration { + ...pluginsDetailsFragment + } + } + } +`; +export const TypedPluginConfigurationUpdate = TypedMutation< + pluginConfigurationUpdate, + pluginConfigurationUpdateVariables +>(pluginConfigurationUpdate); diff --git a/src/plugins/queries.ts b/src/plugins/queries.ts index dfdf1cd69..f5d90461c 100644 --- a/src/plugins/queries.ts +++ b/src/plugins/queries.ts @@ -13,19 +13,19 @@ export const pluginsFragment = gql` } `; -// export const pluginsDetailsFragment = gql` -// ${pluginsFragment} -// fragment pluginsDetailsFragment on PluginConfiguration { -// ...pluginFragment -// configuration { -// name -// type -// value -// helpText -// label -// } -// } -// `; +export const pluginsDetailsFragment = gql` + ${pluginsFragment} + fragment pluginsDetailsFragment on PluginConfiguration { + ...pluginFragment + configuration { + name + type + value + helpText + label + } + } +`; const pluginsList = gql` ${pluginsFragment} @@ -54,15 +54,15 @@ export const TypedPluginsListQuery = TypedQuery< PluginsListVariables >(pluginsList); -// const pluginsDetails = gql` -// ${pluginsDetailsFragment} -// query pluginConfiguration($id: ID!) { -// page(id: $id) { -// ...pluginsDetailsFragment -// } -// } -// `; -// export const TypedPluginsDetailsQuery = TypedQuery< -// PluginDetails, -// PluginDetailsVariables -// >(pluginsDetails); +const pluginsDetails = gql` + ${pluginsDetailsFragment} + query pluginConfiguration($id: ID!) { + pluginConfiguration(id: $id) { + ...pluginsDetailsFragment + } + } +`; +export const TypedPluginsDetailsQuery = TypedQuery< + PluginDetails, + PluginDetailsVariables +>(pluginsDetails); diff --git a/src/plugins/views/PluginsDetails.tsx b/src/plugins/views/PluginsDetails.tsx new file mode 100644 index 000000000..12d00bf61 --- /dev/null +++ b/src/plugins/views/PluginsDetails.tsx @@ -0,0 +1,88 @@ +import DialogContentText from "@material-ui/core/DialogContentText"; +import React from "react"; + +import ActionDialog from "@saleor/components/ActionDialog"; +import { WindowTitle } from "@saleor/components/WindowTitle"; +import useNavigator from "@saleor/hooks/useNavigator"; +import useNotifier from "@saleor/hooks/useNotifier"; +import i18n from "../../i18n"; +import { getMutationState, maybe } from "../../misc"; +import { PluginConfigurationUpdateInput } from "../../types/globalTypes"; +import PluginsDetailsPage, { FormData } from "../components/PluginsDetailsPage"; +import { TypedPluginConfigurationUpdate } from "../mutations"; +import { TypedPluginsDetailsQuery } from "../queries"; +import { pluginsListUrl, pluginsUrl, PluginsUrlQueryParams } from "../urls"; + +export interface PluginsDetailsProps { + id: string; + params: PluginsUrlQueryParams; +} + +export const PluginsDetails: React.StatelessComponent = ({ + id, + params +}) => { + const navigate = useNavigator(); + const notify = useNotifier(); + + return ( + + {(pluginConfigurationUpdate, pluginConfigurationUpdateOpts) => ( + + {PluginDetails => { + const formTransitionState = getMutationState( + pluginConfigurationUpdateOpts.called, + pluginConfigurationUpdateOpts.loading, + maybe( + () => + pluginConfigurationUpdateOpts.data.pluginConfiguration.errors + ) + ); + + return ( + <> + PluginDetails.data.pluginConfiguration.name + )} + /> + + pluginConfigurationUpdateOpts.data.pluginConfiguration + .errors, + [] + )} + saveButtonBarState={formTransitionState} + plugin={maybe(() => PluginDetails.data.pluginConfiguration)} + onBack={() => navigate(pluginsListUrl())} + onSubmit={formData => { + const configurationInput = []; + formData.configuration.map(item => { + configurationInput.push({ + name: item.name, + value: item.value + }); + }); + pluginConfigurationUpdate({ + variables: { + id, + input: { + active: formData.active, + configuration: configurationInput + } + } + }); + }} + /> + + ); + }} + + )} + + ); +}; +PluginsDetails.displayName = "PluginsDetails"; +export default PluginsDetails;