diff --git a/README.md b/README.md index 5d387f59..eac976d4 100644 --- a/README.md +++ b/README.md @@ -44,8 +44,9 @@ Docs. **Note:** Remember to fork the repository before deploying the application. Connecting your own Github repository to Heroku will make manual deploys easier. -See [documentation about deploying to Heroku](./docs/deploying-to-production.md#deploying-to-Heroku) -for more information. +See the +[How to deploy FTW to production](https://www.sharetribe.com/docs/guides/how-to-deploy-ftw-to-production/) +guide in Flex Docs for more information. [![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy) diff --git a/docs/README.md b/docs/README.md index a88e4061..93ed94b6 100644 --- a/docs/README.md +++ b/docs/README.md @@ -57,29 +57,25 @@ Flex Docs: - [How to change Terms of Service and Privacy Policy in FTW](https://www.sharetribe.com/docs/guides/how-to-change-tos-and-privacy-policy-in-ftw/) - [How to change FTW icons](https://www.sharetribe.com/docs/guides/how-to-change-ftw-icons/) - [FTW customization checklist](https://www.sharetribe.com/docs/guides/ftw-customization-checklist/) +- [How to deploy FTW to production](https://www.sharetribe.com/docs/guides/how-to-deploy-ftw-to-production/) See also the following articles: +- [How to extend listing data in FTW](https://www.sharetribe.com/docs/guides/how-to-extend-listing-data-in-ftw/) +- [How to change search filters in FTW](https://www.sharetribe.com/docs/guides/how-to-change-search-filters-in-ftw/) - [How to add static pages in FTW](https://www.sharetribe.com/docs/guides/how-to-add-static-pages-in-ftw/) - [How to test FTW](https://www.sharetribe.com/docs/guides/how-to-test-ftw/) - [How to set up Sentry to log errors in FTW](https://www.sharetribe.com/docs/guides/how-to-set-up-sentry-to-log-errors-in-ftw/) - [How to set up Analytics for FTW](https://www.sharetribe.com/docs/guides/how-to-set-up-analytics-for-ftw/) - [How to set up Content Security Policy (CSP) for FTW](https://www.sharetribe.com/docs/guides/how-to-set-up-csp-for-ftw/) +- [How to use CI with FTW](https://www.sharetribe.com/docs/guides/how-to-use-ci-with-ftw/) Extra documentation for specific topics can be found in the following files: - [Folder structure](folder-structure.md) - [Routing](routing.md) - [Redux and duck files](redux.md) -- [Extended data](extended-data.md) - - See also the - [Extended data reference](https://www.sharetribe.com/docs/references/extended-data/) in Flex - Docs -- [Extend the listing data model](extend-listing.md) -- [Search filters](search-filters.md) -- [Continuous Integration (CI)](ci.md) - [Improving performance](improving-performance.md) -- [Deploying to production](deploying-to-production.md) - [Original create-react-app documentation](https://github.com/sharetribe/create-react-app/blob/master/packages/react-scripts/template/README.md) The application was bootstrapped with a forked version of diff --git a/docs/_layouts/default.html b/docs/_layouts/default.html deleted file mode 100644 index 406762f2..00000000 --- a/docs/_layouts/default.html +++ /dev/null @@ -1,66 +0,0 @@ - - - - - - - -{% seo %} - - - - -
-
-

{{ site.title | default: site.github.repository_name }}

- - {% if site.logo %} - Logo - {% endif %} - -

{{ site.description | default: site.github.project_tagline }}

- - {% if site.github.is_project_page %} -

View the Project on GitHub {{ site.github.repository_nwo }}

- {% endif %} - - {% if site.github.is_user_page %} -

View My GitHub Profile

- {% endif %} - - {% if site.show_downloads %} - - {% endif %} -
- -
- - {{ content }} - -
- -
- - {% if site.google_analytics %} - - {% endif %} - - diff --git a/docs/assets/deploying-to-production/fork_button.png b/docs/assets/deploying-to-production/fork_button.png deleted file mode 100644 index 2297f0c1..00000000 Binary files a/docs/assets/deploying-to-production/fork_button.png and /dev/null differ diff --git a/docs/assets/deploying-to-production/heroku-config-vars.png b/docs/assets/deploying-to-production/heroku-config-vars.png deleted file mode 100644 index cbe5da4c..00000000 Binary files a/docs/assets/deploying-to-production/heroku-config-vars.png and /dev/null differ diff --git a/docs/assets/deploying-to-production/heroku-connect-git.png b/docs/assets/deploying-to-production/heroku-connect-git.png deleted file mode 100644 index db89e9d5..00000000 Binary files a/docs/assets/deploying-to-production/heroku-connect-git.png and /dev/null differ diff --git a/docs/assets/deploying-to-production/heroku-domains.png b/docs/assets/deploying-to-production/heroku-domains.png deleted file mode 100644 index 75dcc9dc..00000000 Binary files a/docs/assets/deploying-to-production/heroku-domains.png and /dev/null differ diff --git a/docs/assets/deploying-to-production/heroku-logs.png b/docs/assets/deploying-to-production/heroku-logs.png deleted file mode 100644 index 37d4befc..00000000 Binary files a/docs/assets/deploying-to-production/heroku-logs.png and /dev/null differ diff --git a/docs/assets/extend-listing/capacity_listing_page.png b/docs/assets/extend-listing/capacity_listing_page.png deleted file mode 100644 index 23a2b3b3..00000000 Binary files a/docs/assets/extend-listing/capacity_listing_page.png and /dev/null differ diff --git a/docs/assets/extend-listing/edit_capacity.png b/docs/assets/extend-listing/edit_capacity.png deleted file mode 100644 index a7f7478c..00000000 Binary files a/docs/assets/extend-listing/edit_capacity.png and /dev/null differ diff --git a/docs/assets/search-filters/desktop-filters.png b/docs/assets/search-filters/desktop-filters.png deleted file mode 100644 index dd5d1975..00000000 Binary files a/docs/assets/search-filters/desktop-filters.png and /dev/null differ diff --git a/docs/assets/search-filters/filters-panel.png b/docs/assets/search-filters/filters-panel.png deleted file mode 100644 index 6923ce0b..00000000 Binary files a/docs/assets/search-filters/filters-panel.png and /dev/null differ diff --git a/docs/assets/search-filters/mobile-filters.png b/docs/assets/search-filters/mobile-filters.png deleted file mode 100644 index 5789a477..00000000 Binary files a/docs/assets/search-filters/mobile-filters.png and /dev/null differ diff --git a/docs/assets/styling/styling-find-component.png b/docs/assets/styling/styling-find-component.png deleted file mode 100644 index 0760ee38..00000000 Binary files a/docs/assets/styling/styling-find-component.png and /dev/null differ diff --git a/docs/assets/translations/translations_cli.gif b/docs/assets/translations/translations_cli.gif deleted file mode 100644 index efab523f..00000000 Binary files a/docs/assets/translations/translations_cli.gif and /dev/null differ diff --git a/docs/ci.md b/docs/ci.md index bc39d56a..65d71f5f 100644 --- a/docs/ci.md +++ b/docs/ci.md @@ -1,34 +1,5 @@ # CI -The application provides a configuration to use [CircleCI](https://circleci.com/) as a continuous -integration server to run tests and other scripts. This ensures quality and can be forced to avoid -merging changes that break tests or fail audits. +Documentation moved to the Flex Docs site: -The [circle.yml](../circle.yml) file is used to configure CircleCI. - -Currently the CI runs the following scripts: - -## Security audit: `yarn run audit` - -This command runs the security audit using [nsp](https://www.npmjs.com/package/nsp). The audit -checks for installed packages with known vulnerabilities and warns about those. - -The scripts outputs information about the dependency path that added the package. If that -information is not enough, `yarn why package-name` can be used to get more detailed information -about why the package is installed. - -If you are convinced that the vulnerability can be ignored, exceptions can be added to the -[.nsprc](../.nsprc) file. - -## Code formatting: `yarn run format-ci` - -This command fails if there are changes in the formatting that are not committed. Run -`yarn run format` to format the code and get rid of the error. - -## Build: `yarn run build` - -This command ensures that the build passes. - -## Tests: `yarn run test-ci` - -This command runs the tests. +https://www.sharetribe.com/docs/guides/how-to-use-ci-with-ftw/ diff --git a/docs/deploying-to-production.md b/docs/deploying-to-production.md index d28b4429..4b3a9a34 100644 --- a/docs/deploying-to-production.md +++ b/docs/deploying-to-production.md @@ -1,142 +1,5 @@ # Deploying to production -## Getting started +Documentation moved to the Flex Docs site: -The easiest way to get started is deploying the application to Heroku. Before creating the app you -need three accounts: [Heroku](https://heroku.com/), [Stripe](https://stripe.com/) and -[MapBox](https://www.mapbox.com/). Creating the accounts is free unless you start heavily using -them. - -Read more from [detailed Heroku instructions](#deploying-to-heroku) with a deploy-to-Heroku button. - -In other production environments, make sure that you have [Node.js](https://nodejs.org/) and -[Yarn](https://yarnpkg.com/) installed. You will also need accounts to Stripe and MapBox. - -After this you need to do the following steps: - -1. [Set environment variables](#enviroment-variables) -2. [Build the app](#building-the-app) -3. [Run the node server](#starting-the-app) - -### Enviroment variables - -To deploy the application add at least the following variables: - -| Variable | Description | -| ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `NODE_ENV` | Use value 'production' for production. | -| `PORT` | Must be set if the production environment doesn't set it by default. Not needed in Heroku. | -| `REACT_APP_SHARETRIBE_SDK_CLIENT_ID` | Client ID (API key) you can check from [Flex Console](https://flex-console.sharetribe.com/login/). You will get this from the Sharetribe team. | -| `REACT_APP_STRIPE_PUBLISHABLE_KEY` | Stripe publishable API key for generating tokens with Stripe API. It can be found from Stripe [API keys](https://dashboard.stripe.com/account/apikeys) page. Make sure to toggle _"View test data"_ on if you don't want to use real money yet. The secret key needs to be added to Flex Console. | -| `REACT_APP_MAPBOX_ACCESS_TOKEN` | Sign up for Mapbox and go to [account page](https://www.mapbox.com/account/access-tokens). Then click `Create access token`. See the Flex Docs [How to set up Mapbox for FTW](https://www.sharetribe.com/docs/guides/how-to-set-up-mapbox-for-ftw/) guide for more information. | -| `REACT_APP_CANONICAL_ROOT_URL` | Canonical root URL of the marketplace. E.g. https://the-name-of-your-app.herokuapp.com or your actual domain. This is needed for social media sharing and SEO optimization. | - -There are also some other variables that can be used. See the Flex Docs -[FTW Environment configuration variables](https://www.sharetribe.com/docs/references/ftw-env/) -reference for more information. - -### Building the app - -Running the following command builds the app for production to the build folder. It correctly -bundles React in production mode and optimizes the build for the best performance. - -`yarn build` - -After this, your app is ready to be deployed. - -### Starting the app - -Start the server: - -`yarn start` - -## Deploying to Heroku - -- [Forking the repository](#forking-the-repository) -- [Creating new app](#creating-new-app) -- [Connect GitHub to Heroku](#connect-github-to-heroku) -- [Heroku settings](#heroku-settings) - - [Updating enviroment variables](#updating-enviroment-variables) - - [Domains and certificates](#domains-and-certificates) -- [Heroku logs](#heroku-logs) - -Before creating the app you need three accounts: [Heroku](https://heroku.com/), -[Stripe](https://stripe.com/fi) and [MapBox](https://www.mapbox.com/). Creating the accounts is free -unless you start heavily using them. - -### Forking the repository - -When deploying to Heroku you should use a forked repository. This makes it possible to make -[manual deploys](https://devcenter.heroku.com/articles/github-integration#manual-deploys) after -changes to environment variables. - -If you haven't done this already, you just need to click the "Fork" button on the top-right corner -of the page: - -For more information, see the -[How to Customize FTW](https://www.sharetribe.com/docs/guides/how-to-customize-ftw/) guide in Flex -Docs. - -### Creating a new app - -The easiest way is to click the _"Deploy to Heroku"_ button (check the root folder in your forked -repository) and fill in the needed information. - -After deploying you will find your app from your Heroku [dashboard](https://dashboard.heroku.com/). -If you click your application you will see Overview and some other tabs. - -If you don't want to use the deploy button you can also manually create a new application in Heroku -dashboard but then you have to manually set all the environment variables. If you prefer using the -command line you can also use [Heroku CLI](https://devcenter.heroku.com/articles/git). - -### Connect GitHub to Heroku - -Connecting your GitHub account to Heroku makes it possible to do automatic and manual deploys in the -dashboard using GitHub branches. - -1. In Heroku dashboard go to _Deploy tab_ and find _Deployment method_ section. -2. Click _Connect to GitHub_ button. After that, you will see _Connect to GitHub_ section. -3. Search the repository you have forked to your account and click connect. - -![Connect GitHub to Heroku](./assets/deploying-to-production/heroku-connect-git.png) - -After this, you can enable automatic deploys from specific GitHub branch or do the manual deploy -from any branch in the repository. - -Read more from Heroku docs: - -- [GitHub Integration](https://devcenter.heroku.com/articles/github-integration) - -### Heroku settings - -In the _Settings tab_ you can manage your application's information, set it to maintenance mode and -delete the app if needed. - -#### Updating enviroment variables - -In the _Settings tab_ click _"Reveal Config Vars"_ button to see the applications environment -variables. Click edit to update them or add new ones if needed. - -> **NOTE:** If you change these variables, you need to deploy the app again. - -![Heroku settings](./assets/deploying-to-production/heroku-config-vars.png) - -#### Domains and certificates - -Heroku manages SSL certificates automatically for new applications. You can change your domain and -SSH settings in the _Settings tab_. - -Read more from Heroku docs: - -- [Custom Domain Names for Apps](https://devcenter.heroku.com/articles/custom-domains) -- [Manually Acquiring an SSL/TLS Certificate](https://devcenter.heroku.com/articles/acquiring-an-ssl-certificate) - -![Heroku settings](./assets/deploying-to-production/heroku-domains.png) - -### Heroku logs - -You can find your application's logs by clicking button _"More"_ in the upper right corner and -selecting _"View logs"_ from the opening dropdown. Logs can be useful if there are problems when -deploying the app. - -![Heroku logs](./assets/deploying-to-production/heroku-logs.png) +https://www.sharetribe.com/docs/guides/how-to-deploy-ftw-to-production/ diff --git a/docs/extend-listing.md b/docs/extend-listing.md index 109d23d6..7c1859c8 100644 --- a/docs/extend-listing.md +++ b/docs/extend-listing.md @@ -1,475 +1,5 @@ # Extend listing data -This guide walks you through the different steps required to expand the listing data model in your -marketplace. We'll have a look on how the data is added, how it can be presented and finally how it -can be used to filter searches. +Documentation moved to the Flex Docs site: -Adding new attributes to the data model relies on a Flex concept called -[extended data](./extended-data.md). - -Three main areas in extending the listing data model are: - -- [Declare the attribute and it's possible values](#declare-the-attribute-and-its-possible-values) -- [Edit the listing wizard](#edit-the-listing-wizard) -- [Show the attribute on listing page](#show-the-attribute-on-listing-page) -- [Use the attribute as a search filter](#use-the-attribute-as-a-search-filter) - -In this guide we will extend the listing data model by adding a _capacity_ attribute. - -## Add a new attribute - -### Declare the attribute and its possible values - -So, you have come up with a great new extension to the listing data model, fantastic! Depending of -the type of data the new attribute is going to be used for it might make sense to define and store -the possible values for the attribute. In case the data will be free form text there might not be -use for this but in our case we wan't to fix the possible values that can be used for the capacity. -Also if the attribute will be used as a search filter it helps to keep track of the possible values -somewhere. - -A commonly used way of storing the possible values for a listing attribute is to declare them in the -[marketplace-custom-config.js](../src/marketplace-custom-config.js) file. Place the values in an -array and export the variable containing the array. The exports from that file are included in the -`src/config.js` file and are available as properties in `config.custom`. Search filters and some -components used to edit and present the data rely on a data model of an array of objects that -contain `key` and `label` properties. - -For the capacity attribute we'll define a set of values that describe the capacity in a few ranges -of how many people can fit into a given sauna: - -```js -export const capacityOptions = [ - { key: '1to3', label: '1 to 3' }, - { key: '4to6', label: '4 to 6' }, - { key: '7to9', label: '7 to 9' }, - { key: '10plus', label: '10 plus' }, -]; -``` - -### Edit the listing wizard - -Next step is to add means for modifying the attribute data in listings. This is achieved by adding -proper inputs to the `EditListingWizard`. It could probably make sense to add the input to the -_description_ tab or modify the _amenities_ tab to also include capacity but for the sake of clarity -let's add a new tab to the wizard. The new tab will be placed between the _amenities_ and _policy_ -tabs. - -First lets declare the tab in `EditListingWizardTab`: - -```js -export const DESCRIPTION = 'description'; -export const FEATURES = 'features'; -export const CAPACITY = 'capacity'; -export const POLICY = 'policy'; -export const LOCATION = 'location'; -export const PRICING = 'pricing'; -export const PHOTOS = 'photos'; - -// EditListingWizardTab component supports these tabs -export const SUPPORTED_TABS = [DESCRIPTION, FEATURES, CAPACITY, POLICY, LOCATION, PRICING, PHOTOS]; -``` - -Now in `EditListingWizard` we can take that tab declaration into use. Import the tab name variable -from `EditListingWizardTab` and add it to the `TABS` array. - -```js -export const TABS = [DESCRIPTION, FEATURES, CAPACITY, POLICY, LOCATION, PRICING, PHOTOS]; -``` - -Also remember to add a label for the tab in the `tabLabel` function: - -```js -const tabLabel = (intl, tab) => { - let key = null; - if (tab === DESCRIPTION) { - key = 'EditListingWizard.tabLabelDescription'; - } else if (tab === FEATURES) { - key = 'EditListingWizard.tabLabelFeatures'; - } else if (tab === CAPACITY) { - key = 'EditListingWizard.tabLabelCapacity'; - } else if (tab === POLICY) { - key = 'EditListingWizard.tabLabelPolicy'; - } else if (tab === LOCATION) { - key = 'EditListingWizard.tabLabelLocation'; - } else if (tab === PRICING) { - key = 'EditListingWizard.tabLabelPricing'; - } else if (tab === PHOTOS) { - key = 'EditListingWizard.tabLabelPhotos'; - } - - return intl.formatMessage({ id: key }); -}; -``` - -The `tabCompleted` function keeps track of which data the user has already provided in order to tell -which tabs are completed. As we will be storing the capacity information in the listing's _public -data_ property (see the [extended data](./extended-data.md) documentation for more info on different -extended data types) we shall look into that property when resolving whether the capacity tab has -alerady been completed or not: - -```js -/** - * Check if a wizard tab is completed. - * - * @param tab wizard's tab - * @param listing is contains some specific data if tab is completed - * - * @return true if tab / step is completed. - */ -const tabCompleted = (tab, listing) => { - const { description, geolocation, price, title, publicData } = listing.attributes; - const images = listing.images; - - switch (tab) { - case DESCRIPTION: - return !!(description && title); - case FEATURES: - return !!(publicData && publicData.amenities); - case CAPACITY: - return !!(publicData && publicData.capacity); - case POLICY: - return !!(publicData && typeof publicData.rules !== 'undefined'); - case LOCATION: - return !!(geolocation && publicData && publicData.location && publicData.location.address); - case PRICING: - return !!price; - case PHOTOS: - return images && images.length > 0; - default: - return false; - } -}; -``` - -Next task is to add form and panel components that render the capacity tab. As for the form, let's -create a `EditListingCapacityForm` component: - -```js -import React from 'react'; -import { arrayOf, bool, func, shape, string } from 'prop-types'; -import { compose } from 'redux'; -import { Form as FinalForm } from 'react-final-form'; -import { intlShape, injectIntl, FormattedMessage } from 'react-intl'; -import classNames from 'classnames'; -import { propTypes } from '../../util/types'; -import { Form, Button, FieldSelect } from '../../components'; -import { required } from '../../util/validators'; - -import css from './EditListingCapacityForm.css'; - -export const EditListingCapacityFormComponent = props => ( - { - const { - className, - disabled, - handleSubmit, - intl, - invalid, - pristine, - saveActionMsg, - updated, - updateError, - updateInProgress, - capacityOptions, - } = fieldRenderProps; - - const capacityPlaceholder = intl.formatMessage({ - id: 'EditListingCapacityForm.capacityPlaceholder', - }); - - const errorMessage = updateError ? ( -

- -

- ) : null; - - const capacityRequired = required( - intl.formatMessage({ - id: 'EditListingCapacityForm.capacityRequired', - }) - ); - - const classes = classNames(css.root, className); - const submitReady = updated && pristine; - const submitInProgress = updateInProgress; - const submitDisabled = invalid || disabled || submitInProgress; - - return ( -
- {errorMessage} - - - - {capacityOptions.map(c => ( - - ))} - - - -
- ); - }} - /> -); - -EditListingCapacityFormComponent.defaultProps = { - selectedPlace: null, - updateError: null, -}; - -EditListingCapacityFormComponent.propTypes = { - intl: intlShape.isRequired, - onSubmit: func.isRequired, - saveActionMsg: string.isRequired, - updated: bool.isRequired, - updateError: propTypes.error, - updateInProgress: bool.isRequired, - capacityOptions: arrayOf( - shape({ - key: string.isRequired, - label: string.isRequired, - }) - ).isRequired, -}; - -export default compose(injectIntl)(EditListingCapacityFormComponent); -``` - -The form component receives `capacityOptions` as a prop which are used to populate a `FieldSelect` -component for selecting the capacity. The `EditListingCapacityForm` is also added to the -`src/containers/index.js` file so that it can easilly be referenced from other components. To use -the capacity editing form we'll add a panel component which is then used in `EditListingWizardTab` -to render the wizard phase. This component we'll call `EditListingCapacityPanel`: - -```js -import React from 'react'; -import PropTypes from 'prop-types'; -import classNames from 'classnames'; -import { FormattedMessage } from 'react-intl'; -import { ensureOwnListing } from '../../util/data'; -import { ListingLink } from '../../components'; -import { EditListingCapacityForm } from '../../containers'; -import config from '../../config.js'; - -import css from './EditListingCapacityPanel.css'; - -const EditListingCapacityPanel = props => { - const { - className, - rootClassName, - listing, - onSubmit, - onChange, - submitButtonText, - panelUpdated, - updateInProgress, - errors, - } = props; - - const classes = classNames(rootClassName || css.root, className); - const currentListing = ensureOwnListing(listing); - const { publicData } = currentListing.attributes; - - const panelTitle = currentListing.id ? ( - }} - /> - ) : ( - - ); - - return ( -
-

{panelTitle}

- { - const { capacity } = values; - const updateValues = { - publicData: { - capacity, - }, - }; - onSubmit(updateValues); - }} - onChange={onChange} - saveActionMsg={submitButtonText} - updated={panelUpdated} - updateError={errors.updateListingError} - updateInProgress={updateInProgress} - capacityOptions={config.custom.capacityOptions} - /> -
- ); -}; - -const { func, object, string, bool } = PropTypes; - -EditListingCapacityPanel.defaultProps = { - className: null, - rootClassName: null, - listing: null, -}; - -EditListingCapacityPanel.propTypes = { - className: string, - rootClassName: string, - - // We cannot use propTypes.listing since the listing might be a draft. - listing: object, - - onSubmit: func.isRequired, - onChange: func.isRequired, - submitButtonText: string.isRequired, - panelUpdated: bool.isRequired, - updateInProgress: bool.isRequired, - errors: object.isRequired, -}; - -export default EditListingCapacityPanel; -``` - -In the panel component we check for an initial capacity value from the `public_data` property of the -listing. In the submit handler the new capacity value is stored in the same property. The updated -listing object is eventually passed to the `updateListingDraft` and `requestUpdateListing` functions -in the `EditListingDetails.duck.js` file where the data updates are handled. Respectively -`EditListingCapacityPanel` needs to be exported from `src/components/index.js` for easier access -from other files. - -Now that we have the panel and the form all ready we can add the panel to the `EditListingWizardTab` -component. This is done by adding a new block to the `switch` structure that handles rendering the -correct panel: - -```js -case CAPACITY: { - const submitButtonTranslationKey = isNew - ? 'EditListingWizard.saveNewCapacity' - : 'EditListingWizard.saveEditCapacity'; - return ( - { - onCompleteEditListingWizardTab(tab, values); - }} - /> - ); -} -``` - -There! Now we've extended the listing data model with capacity information. - -![Edit the capacity](./assets/extend-listing/edit_capacity.png) - -The capacity data can now be added to new and existing listings. Next chapter describes how that -data can be presented in the listing page. - -## Show the attribute on listing page - -Next step in adding a new attribute to the listing is to present it in the listing page. On some -cases an extension to the listing data model can be used purely to enhance the application logic but -in our case with the capacity information we want to show the added attribute in the listing page -for potential customers. - -To show the capacity attribute in the listing page, let's create a specific component for it and -place it in the `ListingPage` container. Desired outcome could also be achieved just by editing the -`ListingPage` but extracting the capacity UI parts into a separate component will simplify the -`ListingPage` and make possible upstream updates from the Flex web template repo easier as there's -less chances for merge conflicts. So, let's create a `SectionCapacity` component in the -`src/containers/ListingPage/` directory: - -```js -import React from 'react'; -import { array, shape, string } from 'prop-types'; -import { FormattedMessage } from 'react-intl'; - -import css from './ListingPage.css'; - -const SectionCapacity = props => { - const { publicData, options } = props; - - const capacity = publicData.capacity; - const capacityOption = options.find(option => option.key === capacity); - - return capacityOption ? ( -
-

- -

-

{capacityOption.label}

-
- ) : null; -}; - -SectionCapacity.propTypes = { - options: array.isRequired, - publicData: shape({ - capacity: string, - }).isRequired, -}; - -export default SectionCapacity; -``` - -Remember to add corresponding css definitions to `ListingPage.css` to get the styling right. Import -the component into `ListingPage` and place it inside the `
` -element: - -```js -import SectionCapacity from './SectionCapacity'; -``` - -```js -
- {/* other sections */} - - - - {/* other sections */} -
-``` - -```js -ListingPageComponent.defaultProps = { - // other default props - capacityOptions: config.custom.capacityOptions, -}; - -ListingPageComponent.propTypes = { - // other props - capacityOptions: array, -}; -``` - -In the snippet above, the capacity options are passed to the `ListingPage` as a property, with the -default property value pulling the options from the custom config (`marketplace-custom-config.js`). -This way the listing page test can define it's own capacity options that are in line with test data -used in the test and custom config changes will not affect test results. - -And voilĂ , we have listing capacity presented in the listing page! - -![Capacity on listing page](./assets/extend-listing/capacity_listing_page.png) - -## Use the attribute as a search filter - -To see how the capacity attribute can be used to filter search results, please refer to the -[search filters documentation](./search-filters.md). +https://www.sharetribe.com/docs/guides/how-to-extend-listing-data-in-ftw/ diff --git a/docs/extended-data.md b/docs/extended-data.md deleted file mode 100644 index 7c3f792e..00000000 --- a/docs/extended-data.md +++ /dev/null @@ -1,37 +0,0 @@ -# Extended Data - -_Extended data_ is arbitrary information that can be attached to certain resources in the Flex API. -Extended data values can be of any valid JSON data type, including JSON data structures like object -and array. The purpose of extended data is to expand the ways in which the Flex API can be used. - -## Types of extended data - -Extended data is divided into two types: _public data_ and _protected data_. - -### Public data - -Public data can be written by anyone who has write permission to a resource and it is visible to -anyone who can read that resource. - -### Protected data - -Protected data, just like public data, can be modified by anyone with write access to the resource. -However, it can only be read by marketplace operators. It is also possible to reveal the protected -data at a specific point in the booking transaction process, for example if listing's address is -presented to the customer after a booking has been confirmed. - -## Data schema and searched - -Extended data works out of the box without any prior configuration to the marketplace. Public and -protected data can be added to listings just by passing it in the create/update request to the API. -However, optionally a _data schema_ can be defined for an extended data key. The data schema allows -that extended data attribute to be used as a search parameter in some API endpoints, like in the -listings query endpoint. Contact Sharetribe team if you need to add a schema to your extended data. - -## Extended data in Flex template app - -The Flex web template uses extended data by default with listing and user resources. In case of the -listings, category and amenities information is stored in the `publicData` attribute. For the users, -phone number is stored in the `protectedData` attribute. As for indexed data, the template app -relies on data schemas for categories and amenities to be configured for the marketplace in the API -as those values are used as filters in the listing search. diff --git a/docs/search-filters.md b/docs/search-filters.md index 89582094..c7b35498 100644 --- a/docs/search-filters.md +++ b/docs/search-filters.md @@ -1,194 +1,5 @@ # Search filters -The search experience can be improved by adding search filters to narrow down the results. The -filters rely on listing's indexed data. +Documentation moved to the Flex Docs site: -- [Filter types](#filter-types) -- [Adding a new search filter](#adding-a-new-search-filter) - - [Common changes](#common-changes) - - [Desktop filters](#desktop-filters) - - [Desktop filters panel](#desktop-filters-panel) - - [Mobile filters](#mobile-filters) -- [Creating your own filter types](#creating-your-own-filter-types) - -## Filter types - -The Flex template for web has different filter types: _price_, _date range_, _select single_ and -_select multiple_. Select single and select multiple filters are generic in a way that they can be -used to filter search results using different kinds of data. The price and date range filters on the -other hand are only used for filtering by price and date range. - -> NOTE: price filter should be configured from `src/marketplace-custom-config.js`. Current maximum -> value for the range is set to 1000 (USD/EUR). - -Filters _select single_ and _select multiple_ can be used with extended data. The _select single_ -one can be used to filter out search results with only one value per search parameter. The _select -multiple_ filters on the other hand can take multiple values for a single search parameter. These -two filter types for extended data are implemented with two different components: - -- Select single filter: `SelectSingleFilter` -- Select multiple filter: `SelectMultipleFilter` - -## Adding a new search filter - -Next we'll guide you through the steps of adding a _capacity_ filter to the marketplace. - -First step for adding a new filter is to make sure that the data being used for filtering is saved -in the listing's `publicData` attribute. On how to achieve this, please refer to the -[documentation on extending the listing data model](./extend-listing.md). Another aspect in search -filters is that the public data needs to be indexed in the API. This is currently achieved with a -manual operation done by the Sharetribe support. Once a public data attribute is added to the -listings and the data attribute is indexed, the listing searches can be filtered by that attribute -by adding a query parameter that consists of a preceding "pub\_" and the attribute name, so for the -_capacity_ attribute the parameter would be "pub_capacity". - -Further reading on public data can be found in the -[extended data documentation](./extended-data.md). - -**NOTE:** Please contact the Sharetribe support in order to update your listing index schema when -planning to use new listing fields as search filters. - -### Common changes - -A few common changes are required to add a select single or a select multiple filter to desktop and -mobile views. - -First of all, the value options for the filter need to be defined. One handy place to store these is -the `marketplace-custom-config.js` file. The correct format is a list of objects with `key` and -`label` fields: - -```js -export const capacityOptions = [ - { - key: 'oneToThree', - label: '1 to 3', - }, - { - key: 'fourToSix', - label: '4 to 6', - }, - ... -]; -``` - -A few changes need to be made to the `SearchPage` container in order to get the filters to work. - -`SearchPage` needs the filter options. One handy way is to add the options as a prop to the -component and then set `defaultProps` value from `config.custom` (contains the -`marketplace-custom-config.js` exports). This way tests can pass in their own values for the filter -options and filter option changes won't affect tests. - -```js -SearchPageComponent.defaultProps = { - // other default props - capacityOptions: config.custom.capacityOptions, -}; - -SearchPageComponent.propTypes = { - // other props - capacityOptions: array, -}; -``` - -Also a filter configuration needs to be added to the object returned by the `filters` method. The -`filters` method combines query param name and options information of each filter so that those can -be passed on to subcomponents and used for validating the filter values and rendering the filter -components. - -To add a filter configuration, extract the filter options from the props and set the param name as -the one defined in your extended data indexing configuration: - -```js -filters() { - const { capacityOptions } = this.props; - - return { - capacityFilter: { - paramName: 'pub_capacity', - options: capacityOptions, - }, - ... - }; -} -``` - -Final thing to do in `SearchPage` is to pass the filters configuration on to the components that -take care of rendering the filters. This is achieved by `primaryFilters` and `secondaryFilters` -props that are passed to `MainPanel`. The configurations are passed as an object in the same form as -the configuration object in `filters`. - -```js - -``` - -These props are used for resolving filter states and validating the filter values. The contents are -passed as props to the components that render the filters: `SearchFilters`, `SearchFiltersPanel` and -`SearchFiltersMobile` .The difference between filters passed as primary and secondary varies in -mobile and desktop views: - -- **Desktop:** Primary filters are shown in the top of the search view, secondary filters are - rendered in a distinct panel that opens on top of search results. -- **Mobile:** Both primary and secondary filters are rendered in the same modal. - -### Desktop filters - -![Desktop filters](./assets/search-filters/desktop-filters.png) - -A basic desktop filter that renders as a dropdown button on top of the search results panel is -achieved using the `SelectSingleFilter` and `SelectMultipleFilter` components. To add standard -desktop filters, perform the following in `SearchFilters` component: - -- declare a prop with the same name that you added the filter config to `primaryFilters` -- resolve the filters initial value with `initialValue` and `initialValues` functions -- render the filter by using a `SelectSingleFilter` or `SelectMultipleFilter` component inside the - `
` element. - -### Desktop filters panel - -![Desktop filter panel](./assets/search-filters/filters-panel.png) - -If more filters are required than can fit into the top filter bar, the `SearchFiltersPanel` -component can be used. It renders as a button in the top filter bar that opens a new panel that can -contain a set of filters. - -To use the `SearchFiltersPanel`, do the following: - -- declare a prop with the same name that you added the filter config to `secondaryFilters` -- resolve the filters initial value with `initialValue` and `initialValues` methods -- use the `SelectSingleFilter` and `SelectMultipleFilter` components inside the - `
` element to render the filters. - -### Mobile filters - -![Mobile filters](./assets/search-filters/mobile-filters.png) - -The mobile view uses the same `SelectSingleFilter` and `SelectMultipleFilter` components as the -filter panel. In this case the filter components are declared in `SearchFiltersMobile`. The -following steps are required to add a mobile filter: - -- declare a prop with the same name that you added the filter config to `primaryFilters` or - `secondaryFilters` -- resolve the filters initial value with `initialValue` and `initialValues` methods -- use the `SelectSingleFilter` and `SelectMultipleFilter` components inside the - `
` element to render the filters. - -## Creating your own filter types - -If you are creating new filter types note that we are using two different types of components: popup -and plain. Popup components are rendered as primary dropdowns in the search view in `SearchFilters`. -Plain components are used with `SearchFiltersMobile` and `SearchFiltersPanel`. `SearchFiltersPanel` -opens sacondary filters in a distinct panel in order to fit additional filters to the desktop search -view. - -To make creating new filters easier, there are two generic components: `FilterPoup` and -`FilterPlain`. These components expect that you give form fields as child component. Check -`SelectMultipleFilter` to see how these components work. +https://www.sharetribe.com/docs/guides/how-to-change-search-filters-in-ftw/