diff --git a/packages/core/admin/admin/src/components/GuidedTour/GuidedTourHomepage/index.js b/packages/core/admin/admin/src/components/GuidedTour/GuidedTourHomepage/index.js new file mode 100644 index 0000000000..a55d52a297 --- /dev/null +++ b/packages/core/admin/admin/src/components/GuidedTour/GuidedTourHomepage/index.js @@ -0,0 +1,37 @@ +import React from 'react'; +import { useIntl } from 'react-intl'; +import { useGuidedTour } from '@strapi/helper-plugin'; +import { Box } from '@strapi/design-system/Box'; +import { Stack } from '@strapi/design-system/Stack'; +import { Typography } from '@strapi/design-system/Typography'; +import { LinkButton } from '@strapi/design-system/LinkButton'; +import layout from '../layout'; + +const GuidedTourHomepage = () => { + const { formatMessage } = useIntl(); + const { guidedTourState } = useGuidedTour(); + + const sections = Object.entries(layout).map(([key, val]) => ({ key, ...val.home })); + + const enrichedSections = sections.map(section => ({ + isDone: Object.entries(guidedTourState[section.key]).every(([, value]) => value), + ...section, + })); + + const activeSection = enrichedSections.find(section => !section.isDone).key; + + return ( + + {enrichedSections.map(section => ( + + {formatMessage(section.title)} + {section.key === activeSection && ( + {formatMessage(section.cta.title)} + )} + + ))} + + ); +}; + +export default GuidedTourHomepage; diff --git a/packages/core/admin/admin/src/components/GuidedTour/GuidedTourModal/Content.js b/packages/core/admin/admin/src/components/GuidedTour/GuidedTourModal/Content.js new file mode 100644 index 0000000000..ccd69b713c --- /dev/null +++ b/packages/core/admin/admin/src/components/GuidedTour/GuidedTourModal/Content.js @@ -0,0 +1,28 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Stack } from '@strapi/design-system/Stack'; +import { Typography } from '@strapi/design-system/Typography'; +import { useIntl } from 'react-intl'; + +const Content = ({ id, defaultMessage }) => { + const { formatMessage } = useIntl(); + + return ( + + {formatMessage( + { id, defaultMessage }, + { + b: children => {children}, + p: children => {children}, + } + )} + + ); +}; + +Content.propTypes = { + id: PropTypes.string.isRequired, + defaultMessage: PropTypes.string.isRequired, +}; + +export default Content; diff --git a/packages/core/admin/admin/src/components/GuidedTour/index.js b/packages/core/admin/admin/src/components/GuidedTour/index.js index 3c153bbaca..cb4670b7ca 100644 --- a/packages/core/admin/admin/src/components/GuidedTour/index.js +++ b/packages/core/admin/admin/src/components/GuidedTour/index.js @@ -1,18 +1,10 @@ -/* - * - * GuidedTour - * - * this component connects the redux state language locale to the - * IntlProvider component and i18n messages (loaded from `app/translations`) - */ - import React, { useReducer } from 'react'; import PropTypes from 'prop-types'; import { GuidedTourProvider } from '@strapi/helper-plugin'; import reducer, { initialState } from './reducer'; const GuidedTour = ({ children }) => { - const [{ currentStep }, dispatch] = useReducer(reducer, initialState); + const [{ currentStep, guidedTourState }, dispatch] = useReducer(reducer, initialState); const setStep = step => { dispatch({ @@ -22,7 +14,11 @@ const GuidedTour = ({ children }) => { }; return ( - + {children} ); diff --git a/packages/core/admin/admin/src/components/GuidedTour/layout.js b/packages/core/admin/admin/src/components/GuidedTour/layout.js new file mode 100644 index 0000000000..f338c83a47 --- /dev/null +++ b/packages/core/admin/admin/src/components/GuidedTour/layout.js @@ -0,0 +1,162 @@ +// TODO: create the modal to consume create and success content + +const layout = { + contentTypeBuilder: { + home: { + title: { + id: 'app.components.GuidedTour.home.CTB.title', + defaultMessage: 'Build the content structure', + }, + cta: { + title: { + id: 'app.components.GuidedTour.home.CTB.cta.title', + defaultMessage: 'Go to the Content-type Builder', + }, + type: 'REDIRECT', + target: '/plugins/content-type-builder', + }, + }, + create: { + title: { + id: 'app.components.GuidedTour.CTB.create.title', + defaultMessage: 'Create a first Collection-type', + }, + content: { + id: 'app.components.GuidedTour.CTB.create.content', + defaultMessage: + '

Collection-types help you manage several entries, Single-types are suitable to manage only one entry.

Ex: For a website, articles would be a Collection type and homepage would be a Single type.

', + }, + cta: { + title: { + id: 'app.components.GuidedTour.CTB.create.cta.title', + defaultMessage: 'Build a Collection-type', + }, + type: 'CLOSE', + }, + }, + success: { + title: { + id: 'app.components.GuidedTour.CTB.success.title', + defaultMessage: 'Step 1: Completed ✅', + }, + content: { + id: 'app.components.GuidedTour.CTB.success.content', + defaultMessage: '

Good going!

What would you like to share with the world? ⚡️', + }, + cta: { + title: { + id: 'app.components.GuidedTour.CTB.success.cta.title', + defaultMessage: 'Create sample content', + }, + type: 'REDIRECT', + target: '/content-manager', + }, + }, + }, + contentManager: { + home: { + title: { + id: 'app.components.GuidedTour.home.CM.title', + defaultMessage: '⚡️ What would you like to share with the world?', + }, + cta: { + title: { + id: 'app.components.GuidedTour.home.CM.cta.title', + defaultMessage: 'Create sample data', + }, + type: 'REDIRECT', + target: '/content-manager', + }, + }, + create: { + title: { + id: 'app.components.GuidedTour.CM.create.title', + defaultMessage: 'Create sample content', + }, + content: { + id: 'app.components.GuidedTour.CM.create.content', + defaultMessage: + '

Create and manage all the content here in the Content Manager.

Ex: Taking the blog website example further, one can write an article, save and publish it as they like.

', + }, + cta: { + title: { + id: 'app.components.GuidedTour.CM.create.ctaTitle', + defaultMessage: 'Create sample content', + }, + type: 'CLOSE', + }, + }, + success: { + title: { + id: 'app.components.GuidedTour.CM.success.title', + defaultMessage: 'Step 2: Completed ✅', + }, + content: { + id: 'app.components.GuidedTour.CM.success.content', + defaultMessage: '

Awesome, one last step to go!

🚀 See content in action', + }, + cta: { + title: { + id: 'app.components.GuidedTour.CM.success.cta.title', + defaultMessage: 'Test the API', + }, + type: 'REDIRECT', + target: '/settings/api-tokens', + }, + }, + }, + apiTokens: { + home: { + title: { + id: 'app.components.GuidedTour.home.apiTokens.title', + defaultMessage: '🚀 See content in action', + }, + cta: { + title: { + id: 'app.components.GuidedTour.home.apiTokens.cta.title', + defaultMessage: 'Test the API', + }, + type: 'REDIRECT', + target: '/content-manager', + }, + }, + create: { + title: { + id: 'app.components.GuidedTour.apiTokens.create.title', + defaultMessage: 'See content in action', + }, + content: { + id: 'app.components.GuidedTour.apiTokens.create.content', + defaultMessage: + 'Generate an authentication token here and retrieve the content you just created', + }, + cta: { + title: { + id: 'app.components.GuidedTour.apiTokens.create.cta.title', + defaultMessage: 'Create sample content', + }, + type: 'CLOSE', + }, + }, + success: { + title: { + id: 'app.components.GuidedTour.apiTokens.success.title', + defaultMessage: 'Step 3: Completed ✅', + }, + content: { + id: 'app.components.GuidedTour.apiTokens.success.content', + defaultMessage: 'You successfully finished the guided tour.', + }, + cta: { + title: { + id: 'app.components.GuidedTour.apiTokens.success.cta.title', + defaultMessage: 'Go back to homepage', + }, + type: 'REDIRECT', + target: '/', + }, + }, + }, +}; + +export default layout; diff --git a/packages/core/admin/admin/src/components/GuidedTour/reducer.js b/packages/core/admin/admin/src/components/GuidedTour/reducer.js index 3bed887b01..37fa172dda 100644 --- a/packages/core/admin/admin/src/components/GuidedTour/reducer.js +++ b/packages/core/admin/admin/src/components/GuidedTour/reducer.js @@ -3,6 +3,20 @@ import produce from 'immer'; export const initialState = { currentStep: null, + guidedTourState: { + contentTypeBuilder: { + create: false, + success: false, + }, + contentManager: { + create: false, + success: false, + }, + apiTokens: { + create: false, + success: false, + }, + }, }; const reducer = (state = initialState, action) => diff --git a/packages/core/admin/admin/src/translations/en.json b/packages/core/admin/admin/src/translations/en.json index 8dff8a2553..921c7fa750 100644 --- a/packages/core/admin/admin/src/translations/en.json +++ b/packages/core/admin/admin/src/translations/en.json @@ -232,6 +232,30 @@ "Users.components.List.empty.withFilters": "There is no users with the applied filters...", "Users.components.List.empty.withSearch": "There is no users corresponding to the search ({search})...", "anErrorOccurred": "Woops! Something went wrong. Please, try again.", + "app.components.GuidedTour.home.CTB.title": "Build the content structure", + "app.components.GuidedTour.home.CTB.cta.title": "Go to the Content-type Builder", + "app.components.GuidedTour.CTB.create.title": "Create a first Collection-type", + "app.components.GuidedTour.CTB.create.content": "

Collection-types help you manage several entries, Single-types are suitable to manage only one entry.

Ex: For a website, articles would be a Collection type and homepage would be a Single type.

", + "app.components.GuidedTour.CTB.create.cta.title": "Build a Collection-type", + "app.components.GuidedTour.CTB.success.title": "Step 1: Completed ✅", + "app.components.GuidedTour.CTB.success.content": "

Good going!

What would you like to share with the world? ⚡️", + "app.components.GuidedTour.CTB.success.cta.title": "Create sample content", + "app.components.GuidedTour.home.CM.title": "⚡️ What would you like to share with the world?", + "app.components.GuidedTour.home.CM.cta.title": "Create sample data", + "app.components.GuidedTour.CM.create.title": "Create sample content", + "app.components.GuidedTour.CM.create.content": "

Create and manage all the content here in the Content Manager.

Ex: Taking the blog website example further, one can write an article, save and publish it as they like.

", + "app.components.GuidedTour.CM.create.ctaTitle": "Create sample content", + "app.components.GuidedTour.CM.success.title": "Step 2: Completed ✅", + "app.components.GuidedTour.CM.success.content": "

Awesome, one last step to go!

🚀 See content in action", + "app.components.GuidedTour.CM.success.cta.title": "Test the API", + "app.components.GuidedTour.home.apiTokens.title": "🚀 See content in action", + "app.components.GuidedTour.home.apiTokens.cta.title": "Test the API", + "app.components.GuidedTour.apiTokens.create.title": "See content in action", + "app.components.GuidedTour.apiTokens.create.content": "Generate an authentication token here and retrieve the content you just created", + "app.components.GuidedTour.apiTokens.create.cta.title": "Create sample content", + "app.components.GuidedTour.apiTokens.success.title": "Step 3: Completed ✅", + "app.components.GuidedTour.apiTokens.success.content": "You successfully finished the guided tour.", + "app.components.GuidedTour.apiTokens.success.cta.title": "Go back to homepage", "app.component.CopyToClipboard.label": "Copy to clipboard", "app.component.search.label": "Search for {target}", "app.component.table.delete": "Delete {target}", diff --git a/packages/core/helper-plugin/lib/src/providers/GuidedTourProvider/index.js b/packages/core/helper-plugin/lib/src/providers/GuidedTourProvider/index.js index 61a5b47253..c1ed815a35 100644 --- a/packages/core/helper-plugin/lib/src/providers/GuidedTourProvider/index.js +++ b/packages/core/helper-plugin/lib/src/providers/GuidedTourProvider/index.js @@ -7,9 +7,9 @@ import React from 'react'; import PropTypes from 'prop-types'; import GuidedTourContext from '../../contexts/GuidedTourContext'; -const GuidedTourProvider = ({ children, currentStep, setStep }) => { +const GuidedTourProvider = ({ children, currentStep, setStep, guidedTourState }) => { return ( - + {children} ); @@ -22,6 +22,12 @@ GuidedTourProvider.defaultProps = { GuidedTourProvider.propTypes = { children: PropTypes.node.isRequired, currentStep: PropTypes.string, + guidedTourState: PropTypes.objectOf( + PropTypes.shape({ + create: PropTypes.bool, + success: PropTypes.bool, + }) + ).isRequired, setStep: PropTypes.func.isRequired, };