0% found this document useful (0 votes)
321 views43 pages

Pwa Documentation - V4

The document provides steps to install Magento PWA Studio and create a custom theme. It also describes how to add a banner/slider component to the homepage including creating a GraphQL resolver and schema.

Uploaded by

test
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as ODT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
321 views43 pages

Pwa Documentation - V4

The document provides steps to install Magento PWA Studio and create a custom theme. It also describes how to add a banner/slider component to the homepage including creating a GraphQL resolver and schema.

Uploaded by

test
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as ODT, PDF, TXT or read online on Scribd
You are on page 1/ 43

PWA Installation and Custom theme Creation

→ Credentials

PC :
Username : pcq75
Password : tatva123

Database :
Username : root
Password : tatva123

→ For setting up Magento 2.3 PWA Studio follow below steps

1. Enter the following command:


composer create-project --repository-url=https://repo.magento.com/ magento/project-community-
edition /var/www/html/etatvasoftpwa

2. Install Magento by Command Line:


sudo php bin/magento setup:install --base-url=http://localhost/etatvasoftpwa --db-host=localhost
--db-name=etatva --db-user=root –db-password=”” --admin-firstname=John --admin-lastname=Doe
[email protected] --admin-user=admin --admin-password=admin123 --backend-
frontname=admin --language=en_US --currency=USD --timezone=Asia/Tbilisi --cleanup-database –
use-rewrites=1
We have completed with Magento installation which you can see in the above image.
3. Now clone pwa-studio repository from github
git clone https://github.com/magento-research/pwa-studio.git

We have cloned this repository in magento root directory ex: /var/www/html/etatvasoftpwa


4. You will see the pwa-studio directory in /var/www/html/etatvasoftpwa.
Enter into this directoy by : cd pwa-studio/

5. Run - sudo yarn install


6. Specify the Magento backend server in .env file. you can see the .env.dist file in
/var/www/html/etatvasoftpwa/pwa-studio/packages/venia-concept/ directory. If you are not able
see, enable show hidden files. Now create .env file from this env.dist. Run command to copy : cp
packages/venia-concept/.env.dist packages/venia-concept/.env . If you can’t find “env.dist.” file than
run below command to generate .env and skip the step 7.
MAGENTO_BACKEND_URL="https://localhost/etatvasoftpwa/" yarn buildpack create-env-file
packages/venia-concept

7. Open this .env file and change/add the MAGENTO_BACKEND_URL to your local magento instance.

ex: MAGENTO_BACKEND_URL= https://localhost/etatvasoftpwa

8. Generate SSL certificate because PWA features requires an HTTPS Secure Domain. From the root
directory of PWA (/pwa-studio) run below command:

sudo yarn buildpack create-custom-origin packages/venia-concept

9. Find the deployVeniaSampleData.sh file in


/var/www/html/etatvasoftpwa/pwa-studio/packages/venia-concept/ directory. and copy this
file in your Magento root directory. Now it must look like
/var/www/html/etatvasoftpwa/deployVeniaSampleData.sh. Go to the magneto root directory and
Now run:

bash deployVeniaSampleData.sh

10. After successful installation run:

bin/magento setup:upgrade

bin/magento indexer:reindex
bin/magento cache:flush

11. Now go back to pwa-studio directory/var/www/html/etatvasoftpwa/pwa-studio and start Server.

Command : sudo yarn run build

12. Run server, Use any of the following commands from the project root directory to start the server:

Command : sudo yarn run watch:venia

Note : if you find 500 error than its permission issue for that run command : sudo chmod -R 777
/var/DirectoryName

Note : You might get the issues that product’s images are not loading on PWA.
To fix this issue, create a virtual host.

→ Steps to create Virtual Host in Ubuntu

1. Create configuration file for host


sudo cp /etc/apache2/sites-available/000-default.conf /etc/apache2/sites-
available/etatvasoftpwa.com.conf
Now, modify the configuration files to match with our virtual hosts and put below code.
2. sudo gedit /etc/apache2/sites-available/etatvasoftpwa.com.conf
<VirtualHost *:80>
ServerAdmin [email protected]
ServerName etatvasoftpwa.com
ServerAlias www.etatvasoftpwa.com
DocumentRoot /var/www/html/etatvasoftpwa/pub
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
<Directory /var/www/html/etatvasoftpwa/pub>
Options Indexes FollowSymLinks MultiViews
AllowOverride All
Order allow,deny
allow from all

</Directory>

</VirtualHost>

3. Enable virtual host configuration files


sudo a2ensite etatvasoftpwa.com.conf
4. Restart apache web server.
sudo service apache2 restart
5. Add your virtual host like below and put code.
gedit /etc/hosts
127.0.0.1 etatvasoftpwa.com
6. Now edit core_config_data table and put url here like below
http://etatvasoftpwa .com/
Permission issue if caused then run the following command :
sudo chmod -R 777 {pub/media,pub/static,var,generated,app/etc}

→ Custom Theme Creation

Here we will create a theme name “etatvasoft” for that follow the below steps.

1. Copy venia-concept folder and create new theme folder. like example etatvasoft-concept
theme path etatvasoftpwa/pwa-studio/packages.

2. Edit etatvasoftpwa/pwa-studio/package.json file


i) "packages/etatvasoft-concept", under "workspaces":
ii) add below three lines under "scripts":
"stage:etatvasoft": "yarn workspace @magento/etatvasoft-concept run start; cd - >/dev/null",
"stats:etatvasoft": "yarn workspace @magento/etatvasoft-concept run build:analyze && yarn
workspace @magento/etatvasoft-concept run stats",
"watch:etatvasoft": "yarn workspace @magento/etatvasoft-concept run watch; cd - >/dev/null"
3. Edit etatvasoftpwa/pwa-studio/packages/tatvasoft-concept/package.json file
change name first line like below
"name": "@magento/etatvasoft-concept"

4. Run command from path etatvasoftpwa/pwa-studio


sudo yarn run watch: etatvasoft

→ Adding Banner\Slider on HomePage

1. I consider that you have to create a Banner Module in Magento Admin. In the same module you
have to create a “Resolver”.

A resolver performs GraphQL request processing. In general, it is responsible for constructing a query,
fetching data and performing any calculations, then transforming the fetched and calculated data into
a GraphQL array format.

Ex : etatvasoftpwa/app/code/Etatvasoft/Banner/Model/Resolver/HomePageBanner.php
HomePageBanner.php
<?php
declare(strict_types=1);
namespace Etatvasoft\Banner\Model\Resolver;
use Magento\Framework\GraphQl\Config\Element\Field;
use Magento\Framework\GraphQl\Query\ResolverInterface;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;

class HomePageBanner implements ResolverInterface


{
public function __construct(\Magento\Store\Model\StoreManagerInterface $storeManager)
{
$this->_storeManager = $storeManager;
}
public function resolve(Field $field, $context, ResolveInfo $info, array $value = null, array $args =
null)
{
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
$bannerCollection = $objectManager-
>create('\Etatvasoft\Banner\Model\ResourceModel\Banner\Collection');
$mediaUrl = $this ->_storeManager->getStore()-
>getBaseUrl(\Magento\Framework\UrlInterface::URL_TYPE_MEDIA);
foreach($bannerCollection as $_data){
$result[]=[
'banner_id' => $_data->getData('banner_id'),
'banner_title' => $_data->getData('banner_title'),
'banner_image' => $mediaUrl.$_data→getData('banner_image')
]
}
return $result;
}
}
2. We will create “schema.graphql” in etc directory. GraphQL stands for Graph Query Language. At its
core, this is what GraphQLactually is: a language for writing standardized data queries.

Ex : magentoroot/app/code/Etatvasoft/Banner/etc/di/schema.graphqls

schema.graphqls
type Query
{
homePageBanner: [BannerDetails] @resolver(class:
"Etatvasoft\\Banner\\Model\\Resolver\\HomePageBanner") @doc(description: "BannerDetails")
}

type BannerDetails
{
banner_id: String @doc(description: "ID of Banner")
banner_title: String @doc(description: "Title of Banner")
banner_image: String @doc(description: "Image url of Banner")
}

→ Here we have created schema for querying list of Banner images, title and media path of magneto.

3. Install carousel in react by this command :


sudo npm install react-responsive-carousel --save
* If you face errors after the above command than run “sudo yarn install”.

4. Create “getHomePageBanner.graphql” file. GraphQL is a syntax that describes how to ask for data,
and is generally used to load data from a server to a client.

Ex : magentoroot/pwa-studio/packages/venia-ui/lib/queries/ getHomePageBanner.graphql

getHomePageBanner.graphql
{
homePageBanner {
banner_id
banner_title
banner_image
}
}
→ Here you can see the graphql result which display the list of banners. You run this by installing
graphql extension in your pc. Also set the end point as your “baseurl/graphql”.

5. Go to the following path - etatvasoftpwa/pwa-studio/packages/venia-


ui/lib/RootComponents/CMS/cms.js
Replace the code with the below code.
cms.js
import React, {Component} from 'react';
import classify from '../../classify';
import { useQuery } from '@apollo/react-hooks';
import { fullPageLoadingIndicator } from '../../components/LoadingIndicator';
import { Carousel } from 'react-responsive-carousel';
import "react-responsive-carousel/lib/styles/carousel.min.css";
import CategoryList from '../../components/CategoryList';
import BannerListQuery from '../../queries/getHomePageBanner.graphql';
import defaultCMSClasses from './cms.css';
import { mergeClasses } from '../../classify';

const CMSPage = props => {


const { loading, error, data } = useQuery(BannerListQuery);
const classes = mergeClasses(defaultCMSClasses, props.classes);
if (error) {
return <div className={classes.fetchError}>
Data Fetch Error: <pre>{error.message}</pre></div>;
}
if (loading) {
return fullPageLoadingIndicator;
}
if (data) {
return (
<Carousel showArrows={true} showThumbs={true} infiniteLoop={true} >
{data.homePageBanner.map((item,index) => (
<div className="banner-inner" key={index}>
<img src={item.bannerimage}/>
<p className="legend">{item.banner_title}</p>
</div>
))}
</Carousel>
);}};
class CMS extends Component {
render() {
return (
<div className="Home-Custom">
<CMSPage/>
</div>
);}};
export default classify(defaultCMSClasses)(CMS);
→ The Query part is the slider/banner part of Homepage. We have included Carousal and Query
Component. To retrieve data from the GraphQL endpoint this component makes use of another
component “Query”.

5. Create cms.css file if not present at the following path - etatvasoftpwa/pwa-studio/packages/venia-


ui/lib/RootComponents/CMS/
Replace the code with the below code.

cms.css
/* custom*/
:global(p) {
font-size: 15px;
line-height: 19px;
letter-spacing: normal;
}
:global(p:not(:last-child)) {
margin-bottom: 12px;;
}
/* Home Content Section Starts */
:global(.Home-Custom .richContent-root-2JD) {
padding: 30px 0;
}
/* Home Content Section Ends */
:global(.Home-Custom) {
position: relative;
}
:global(.banner-inner img) {
display: block;
height: 500px;
}
:global(.carousel .control-dots .dot) {
height: 10px;
width: 10px;
margin: 0 6px;
}
:global(.carousel .control-dots .dot:focus) {
outline: none;
}
:global(.thumbs-wrapper.axis-vertical) {
display: none;
}
:global(.h2-title) {
/* color: #053b7d; */
color: #111111;
text-align: center;
padding: 0;
font-size: 36px;
letter-spacing: 0.8px;
margin-bottom: 35px;
text-shadow: none;
font-weight: 700 !important;
text-transform: capitalize !important;
}
:global(.h2-title span) {
display: inline-block;
}
:global(button) {
outline: none;
}
:global(a:hover),
:global(a:focus) {
text-decoration: none;
}

→ Homepage will have the above slider if, you have followed all the above mentioned steps.
→ Adding Featured Product Section In Homepage

1. Let us consider that you have already developed a featured Product Module in magento backend.
And will use custom attribute “featured_product”. Now we will create a custom module “Featured
Product” and in that will override the magento default graphql and schema of products and will make
changes so that we can get the featured products in product list array.

2. We will create a Resolver which returns the custom attribute “featured_product” product values for
the products.
Ex. etatvasoftpwa/app/code/Etatvasoft/FeaturedProduct/Model/Resolver/FeaturedProduct.php

FeaturedProduct.php
<?php
declare(strict_types=1);
namespace Etatvasoft\FeaturedProduct\Model\Resolver\Product;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
use Magento\Catalog\Model\Product;
use Magento\Framework\GraphQl\Config\Element\Field;
use Magento\Framework\GraphQl\Query\ResolverInterface;

class FeaturedProduct implements ResolverInterface


{
public function resolve(
Field $field,
$context,
ResolveInfo $info,
array $value = null,
array $args = null
){
if (!isset($value['model'])) {
throw new LocalizedException(__('"model" value should be specified'));
}
$product = $value['model'];
$renderedValue = $product->getData('featured_product');
if($renderedValue!='1')
{
$renderedValue=0;
}
return $renderedValue;
}}

3. Create file “schema.graphqls” in etc directory.


Ex : magentoroot/app/code/Etatvasoft/FeaturedProduct/etc/di/schema.graphqls

schema.graphqls
input ProductFilterInput
{
featured_product: FilterTypeInput @doc(description: "Product is featured or not")
@resolver(class:
"Etatvasoft\\FeaturedProduct\\Model\\Resolver\\Product\\FeaturedProduct")
}

interface ProductInterface
{
featured_product: String @doc(description: "Product is featured or not")
@resolver(class:
"Etatvasoft\\FeaturedProduct\\Model\\Resolver\\Product\\FeaturedProduct")
}

4. Now its the time to move to react part. Create “getFeaturedProducts.graphql” file.
Ex : magentoroot/pwa-studio/packages/venia-ui/lib/queries/ getFeaturedProducts.graphql
getFeaturedProducts.graphql
query FeaturedProducts {
products (
filter: { featured_product: { like: "1" } }
pageSize: 100
currentPage: 1
sort: { name: ASC }
)
{
items{
name
id
url_key
price {
regularPrice{
amount {
currency
value
}
}
}
sku
featured_product
small_image {
url
}
}
total_count
}
}
→ Here you can see the graphql result which display list of Featured Products. Here we have added
Filter “featured_product” attribute which assures the the product to be rendered are featured one.

5. Now we will create featured product component in react. Create Index.js in which will export
FeaturedProducts.js.

Ex : magentoroot/pwa-studio/packages/venia-ui/lib/components/FeaturedProducts index.js

Index.js
export { default } from './FeaturedProducts';

6. Create FeaturedProducts.js which is exported in index.js. It imports the


“getFeaturedProducts.graphql” which request graphql request format to get featured products.

Ex : magentoroot/pwa-studio/packages/venia-ui/lib/components/FeaturedProducts
FeaturedProducts.js
FeaturedProducts.js
import React from 'react';
import classify from '../../classify';
import { fullPageLoadingIndicator } from '../../components/LoadingIndicator';
import defaultClasses1 from './featuredProducts.css';
import FeaturedProductsTile from './FeaturedProductsTile';
import FeaturedProductsListQuery from '../../queries/getFeaturedProducts.graphql';
import { useQuery } from '@apollo/react-hooks';
import { mergeClasses } from '../../classify';

const FeaturedProducts = props => {


const { loading, error, data } = useQuery(FeaturedProductsListQuery);

const classes = mergeClasses(defaultClasses1, props.classes);

const header = <h2 className='h2-title'>


<span>Featured Products</span>
</h2>
let product;
if (error) {
product = (
<div className={classes.fetchError}>
Data Fetch Error: <pre>{error.message}</pre>
</div>
);
}
if (loading) {
product= fullPageLoadingIndicator;
}
if (data.products.items.length === 0) {
product= (
<div className={classes.noResults}>
No Products found.
</div>
);
}

if (data.products.items.length < 1) {
product = (
<div className={classes.noResults}>
{data.products.items.length} products found..!!
</div>
);
}
else{
product= (
<div className="featured-section-div row">
{data.products.items.map((item,index) => (
<FeaturedProductsTile key={index} item={item} />
))}
</div>
);
}

return (
<div className="featured-section">
<div className="container">
{header}
{product}
</div>
</div>
);

};
export default classify(defaultClasses1)(FeaturedProducts);

7. Create FeaturedProductsTile.js which is imported in FeaturedProducts.js. I imports the


“getFeaturedProducts.graphql” which is the graphql request format to get featured products.

Ex : magentoroot/pwa-studio/packages/venia-
ui/lib/components/FeaturedProducts/FeaturedProductsTile.js
FeaturedProductsTile.js
import React, { Component } from 'react';
import {string, shape } from 'prop-types';
import classify from '../../classify';
import { Link, resourceUrl } from '../../drivers';
import defaultClasses from './FeaturedProductsTile.css';
import "react-responsive-carousel/lib/styles/carousel.min.css";

const previewImageSize = 480;

class FeaturedProductsTile extends Component {


static propTypes = {
item: shape({
name: string,
featured_product: string,
url_key: string.isRequired
}).isRequired,
classes: shape({
item: string,
image: string,
imageWrapper: string,
name: string
}).isRequired
};

get imagePath() {

const previewProduct = this.props.item;


if (previewProduct) {
return resourceUrl(previewProduct.small_image.url, {
type: 'image-product',
width: previewImageSize
});
} else {
return null;
}
}

render() {
console.log(this.props.item);
const { imagePath, props } = this;
const { classes, item } = props;
// interpolation doesn't work inside `url()` for legacy reasons
// so a custom property should wrap its value in `url()`
const imageUrl = imagePath ? `url(${imagePath})` : 'none';
const style = { '--venia-image': imageUrl };

// render an actual image element for accessibility


const imagePreview = imagePath ? (
<img className="featured-block-img" src={imagePath} alt={item.name} />
) : null;
return (
<Link className="featured-block col-md-3"
to={`/${item.url_key}.html`}>
<i className="featured-block-image">
{imagePreview}
</i>
<span className="item-name">{this.props.item.name}<br/></span>
<span className="item-price"><b>Price : </b> US$ {this.props.item.price.re
gularPrice.amount.value}<br/></span>
</Link>
)
}
}

export default classify(defaultClasses)(FeaturedProductsTile);

8. Create featuredProducts.css

Ex : magentoroot/pwa-studio/packages/venia-
ui/lib/components/FeaturedProducts/featuredProducts.css
featuredProducts.css
.root {
padding: 1rem;
}
.header {
margin-bottom: 2rem;
text-align: center;
}
.title {
text-transform: uppercase;
}
.content {
display: grid;
grid-gap: 3rem 1rem;
grid-template-columns: repeat(auto-fit, 6rem);
justify-content: center;
}
.fetchError {
background-color: rgb(var(--venia-warning-light));
color: rgb(var(--venia-warning-dark));
}
.fetchingData {
color: rgb(--venia-grey);
}
.noResults {
composes: fetchingData;
}
:global(.hide_features) {
display: none !important;
}
/* starts here */
:global(.featured-section) {
display: block;
padding: 50px 0 40px;
background: #f4f4f4;
}
:global(.featured-section-div .featured-block) {
text-align: center;
padding: 15px;
margin-bottom: 15px;
transition: all .25s;
}
:global(img.featured-block-img) {
width: auto;
display: inline-block;
max-width: 100%;
}
:global(i.featured-block-image) {
display: block;
box-shadow: none;
margin-bottom: 15px;
}
:global(.featured-block i.featured-block-image img) {
max-height: 250px;
transition: all .25s;
}
:global(.featured-block:hover) {
background-color: #ffffff;
box-shadow: 0px 3px 15.75px -3.75px rgba(0,0,0,0.5);
}
:global(.item-price) {
display: block;
font-size: 14px;
letter-spacing: 0.2px;
line-height: 18px;
color: #00686c;
}
:global(.featured-block > span:not(:last-child)) {
margin-bottom: 8px;
}
:global(.featured-block:nth-last-child(1)),
:global(.featured-block:nth-last-child(2)),
:global(.featured-block:nth-last-child(3)),
:global(.featured-block:nth-last-child(4)) {
margin-bottom: 0;
}
:global(.item-name) {
display: block;
font-size: 18px;
color: #000;
letter-spacing: 0.2px;
line-height: 22px;
font-weight: 600;
transition: all .25s;
}
:global(.item-name:hover) {
color: #043b7d;
}
9. Create FeaturedProductsTile.css

Ex : magentoroot/pwa-studio/packages/venia-
ui/lib/components/FeaturedProducts/FeaturedProductsTile.css

FeaturedProductsTile.css
.root {
display: block;
line-height: 1rem;
text-align: center;
width: 6rem;
}
.imageWrapper {
background-image: var(--venia-image);
background-position: 50% 50%;
background-size: cover;
border-radius: 50%;
box-shadow: 0 0 0 1px rgb(var(--venia-border));
display: block;
height: 5rem;
margin: 0 auto 1rem auto;
width: 5rem;
}
.image {
height: 100%;
opacity: 0;
width: 100%;
}
.name {
display: block;
}
.hide_features {
display: none !important;
}

10. In cms.js we have to update out new component which is featured product section.

i. Add Import statement in import section.


“import FeaturedProduct from '../../components/FeaturedProducts';”
ii. Also add Component in side function cms() after <CMSPage/> component.
“<FeaturedProduct />”
Now, the section Featured product will be shown in Homepage.
→ Get Data from Magento Admin System Configuration (Custom Configurations)
Let us assume that we have already created custom module to store custom configuration in magento
database.
Suppose we have two fields Logo and Title which we have fetch from Magento Admin in System
Configurations.

1. We will create di.xml file.

di.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<type name="Magento\StoreGraphQl\Model\Resolver\Store\StoreConfigDataProvider">
<arguments>
<argument name="extendedConfigData" xsi:type="array">
<item name="pwa_logo" xsi:type="string">pwaconfiguration/general/pwa_logo</item>
<item name="pwa_title"
xsi:type="string">pwaconfiguration/general/pwa_website_title</item>
<item name="pwa_website_copyright"
xsi:type="string">pwaconfiguration/general/pwa_website_copyright</item>
<item name="pwa_website_email"
xsi:type="string">pwaconfiguration/general/pwa_website_email</item>
<item name="pwa_website_phone"
xsi:type="string">pwaconfiguration/general/pwa_website_phone</item>
<item name="pwa_website_address"
xsi:type="string">pwaconfiguration/general/pwa_website_address</item>
</argument>
</arguments>
</type>
</config>

→ Here “pwaconfiguration/general/pwa_title”, “pwaconfiguration/general/pwa_logo”,


“pwaconfiguration/general/pwa_website_copyright”,
“pwaconfiguration/general/pwa_website_email”, “pwaconfiguration/general/pwa_website_phone”,
“pwaconfiguration/general/pwa_website_address” are the fields which we need to fetch from
configurations.
2. We will create schema.graphql file.
schema.graphqls
type StoreConfig {
pwa_logo : String @doc(description: "Extended Config Data - section/group/field")
pwa_title : String @doc(description: "Extended Config Data - section/group/field")
pwa_website_copyright : String @doc(description: "Extended Config Data - section/group/field")
pwa_website_email : String @doc(description: "Extended Config Data - section/group/field")
pwa_website_phone : String @doc(description: "Extended Config Data - section/group/field")
pwa_website_address : String @doc(description: "Extended Config Data - section/group/field")
}

→ As you can in the image how the configuration data is requested and the respected result is
displayed in graphql.

3. Now its the time to move to react part. Open the “getStoreConfigData.graphql” file and add the
configuration fields.
Ex : magentoroot/pwa-studio/packages/venia-ui/lib/queries/ getStoreConfigData.graphql
getStoreConfigData.graphql
query storeConfigData {
storeConfig {
id
copyright
pwa_logo
pwa_title
base_media_url
pwa_website_copyright
pwa_website_address
pwa_website_email
pwa_website_phone
}
}

4. Now will use this “pwa_logo” for the website. Go to the following path - etatvasoftpwa/pwa-
studio/packages/venia-ui/lib/components/Logo/logo.js
Replace the code with the below code.
import React from 'react';
import PropTypes from 'prop-types';
import { mergeClasses } from '../../classify';
import Image from '../Image';
import logo from './logo.svg';
import { useQuery } from '@apollo/react-hooks';
import storeConfigDataQuery from '../../queries/getStoreConfigData.graphql';

const Logo = props => {


const { height, width } = props;
const classes = mergeClasses({}, props.classes);
const { loading, error, data } = useQuery(storeConfigDataQuery);
let logo = null;
let title = null;
if (data && data.storeConfig) {
logo = data.storeConfig.base_media_url+'pwa_logo/'+data.storeConfig.pwa_logo;
title= data.storeConfig.pwa_title;
}
if (error) {
return <div>Data Fetch Error: <pre>{error.message}</pre></div>;
}
else{
return (
<>
{logo ? (
<img
className={classes.logo}
src={logo}
height={height}
alt={title}
title={title} />
):(
<span>Fetching</span>
)}
</>
);
}
};
Logo.propTypes = {
classes: PropTypes.shape({
logo: PropTypes.string
}),
height: PropTypes.number
};
Logo.defaultProps = {
height: 100
};
export default Logo;

→ Get CMS Block Data from Magento Admin

1. Let us assume that we have already created a block “Home First Block” with identifier - “home-first-
block” in Magento Admin with below contents.

“<div class="container">
<h2 class="h2-title">Welcome to&nbsp;Progressive Web Apps</h2>
<p>A Progressive Web App (PWA) is a web app that uses modern web capabilities to deliver an app-
like experience to users. These apps meet certain requirements (see below), are deployed to servers,
accessible through URLs, and indexed by search engines.&nbsp;This can work in conjunction with
Cordova to provide a multiple deploy targets for all your users. You can deploy your app as a PWA as
well as Native app and take advantage of both channels.</p>
<p>Ionic allows you to ship your app to not only the app store, but also deploy to the mobile web as a
PWA.&nbsp;A Progressive Web App (PWA) is a web app that uses modern web capabilities to deliver
an app-like experience to users. These apps meet certain requirements (see below), are deployed to
servers, accessible through URLs, and indexed by search engines.&nbsp;This can work in conjunction
with Cordova to provide a multiple deploy targets for all your users. You can deploy your app as a PWA
as well as Native app and take advantage of both channels.</p>
</div>”

2. Now we can get this block content in our site Homepage. For that we have to place
“<CmsBlock identifiers={2} />” in “Home-Custom“ section inside cms.js file. Here identifier
is the id of the block which we want to access from magneto admin.
Add “import CmsBlock from '../../components/CmsBlock';” in the file in import section.

As shown above the content will be shown in website.

→ Get Categories List From Magento admin.

1. Now will add categories list to the Homepage. We have CategoryList component in pwa. Go to the
following path - etatvasoftpwa/pwa-studio/packages/venia-
ui/lib/components/CategoryList/categoryList.js. Replace the code with the below code.
categoryList.js
import React from 'react';
import { string, number, shape } from 'prop-types';
import { mergeClasses } from '../../classify';
import { fullPageLoadingIndicator } from '../LoadingIndicator';
import defaultClasses from './categoryList.css';
import CategoryTile from './categoryTile';
import categoryListQuery from '../../queries/getCategoryList.graphql';
import { useCategoryList } from '@magento/peregrine/lib/talons/CategoryList/useCategoryLi
st';

// map Magento 2.3.1 schema changes to Venia 2.0.0 proptype shape to maintain backwards
compatibility
const mapCategory = categoryItem => {
const { items } = categoryItem.productImagePreview;
return {
...categoryItem,
productImagePreview: {
items: items.map(item => {
const { small_image } = item;
return {
...item,
small_image:
typeof small_image === 'object'
? small_image.url
: small_image
};
})
}
};
};
const CategoryList = props => {
const { id } = props;
const talonProps = useCategoryList({
query: categoryListQuery,
id
});
const { childCategories, error, loading } = talonProps;
const classes = mergeClasses(defaultClasses, props.classes);
const header =(
<h2 className='h2-title'>
<span>Our Categories</span>
</h2>
);
let child;
if (error) {
child = (
<div className={classes.fetchError}>
Data Fetch Error: <pre>{error.message}</pre>
</div>
);
}
if (loading || !childCategories) {
child = fullPageLoadingIndicator;
} else if (childCategories.length === 0) {
child = (
<div className={classes.noResults}>No child categories found.</div>
);
} else {
child = (
<div className="category-section-div row">
{childCategories.map(item => (
<CategoryTile item={mapCategory(item)} key={item.url_key} />
))}
</div>
);
}
return (
<div className="category-section">
<div className="container">
{header}
{child}
</div>
</div>
);
};
CategoryList.propTypes = {
id: number,
title: string,
classes: shape({
root: string,
header: string,
content: string
})
};
export default CategoryList;
2. Go to the following path - etatvasoftpwa/pwa-studio/packages/venia-
ui/lib/components/CategoryList/categoryTile.js. Replace the code with the below code.

categoryTile.js
import React, { Component } from 'react';
import { arrayOf, string, shape } from 'prop-types';
import classify from '../../classify';
import { Link, resourceUrl } from '@magento/venia-drivers';
import defaultClasses from './categoryTile.css';

// TODO: get categoryUrlSuffix from graphql storeOptions when it is ready


const categoryUrlSuffix = '.html';
const previewImageSize = 480;

class CategoryTile extends Component {


static propTypes = {
item: shape({
image: string,
name: string.isRequired,
productImagePreview: shape({
items: arrayOf(
shape({
small_image: string
})
)
}),
url_key: string.isRequired
}).isRequired,
classes: shape({
item: string,
image: string,
imageWrapper: string,
name: string
}).isRequired
};
get imagePath() {
const { image, productImagePreview } = this.props.item;
const previewProduct = productImagePreview.items[0];
if (image) {
return resourceUrl(image, {
type: 'image-category',
width: previewImageSize
});
}
else if (previewProduct) {
return resourceUrl(previewProduct.small_image, {
type: 'image-product',
width: previewImageSize
});
} else {
return null;
}
}
render() {
const { imagePath, props } = this;
const { classes, item } = props;
// interpolation doesn't work inside `url()` for legacy reasons
// so a custom property should wrap its value in `url()`
const imageUrl = imagePath ? `url(${imagePath})` : 'none';
const style = { '--venia-image': imageUrl };
// render an actual image element for accessibility
const imagePreview = imagePath ? (
<img className={classes.image} src={imagePath} alt={item.name} />
) : null;
return (
<Link
className="category-block col-md-3"
to={`/${item.url_key}${categoryUrlSuffix}`}
>
<i className={classes.imageWrapper} style={style}>
{imagePreview}
</i>
<span class='category-name'>
{item.name}
</span>
</Link>
);
}
}
export default classify(defaultClasses)(CategoryTile);
3. Go to the following path - etatvasoftpwa/pwa-studio/packages/venia-
ui/lib/components/CategoryList/categoryList.css. Add the following css to file categoryList.css.
categoryList.css
/* Starts Here */

:global(.category-section) {
display: block;
padding: 50px 0 40px;
}
:global(.category-section-div .category-block) {
padding: 0 15px;
margin-bottom: 35px;
}
:global(.category-block > i) {
width: auto;
height: auto;
overflow: hidden;
max-height: 150px;
}
:global(.category-block > i img) {
max-width: 100%;
height: 100%;
width: 100%;
opacity: 1;
object-fit: cover;
transition: all .30s ease;
}
:global(.category-block:hover > i img) {
transform: scale(1.06);
transition: all .25s;
}
:global(.category-name) {
display: block;
font-size: 18px;
letter-spacing: 0.2px;
line-height: 22px;
font-weight: 600;
transition: all .25s;
text-align: center;
}
:global(.category-name:hover) {
color: #043b7d;
}
:global(.category-block:nth-last-child(1)),
:global(.category-block:nth-last-child(2)),
:global(.category-block:nth-last-child(3)),
:global(.category-block:nth-last-child(4)) {
margin-bottom: 0;
}
@media (max-width: 991px) {
:global(.category-block) {
flex:0 0 33%; max-width: 33%;
}
:global(.category-block > i) {
max-height: 120px;
}
}
@media (max-width: 767px) {

:global(.category-block) {
flex:0 0 50%; max-width: 50%;
}
:global(.category-section) {
padding: 35px 0;
}
:global(.category-section-div .category-block) {
margin-bottom: 25px;
}
:global(.category-section-div .category-block:last-child) {
margin-bottom: 0;
}
}
@media (max-width: 480px) {
:global(.category-section-div .category-block) {
flex:0 0 100%; max-width: 100%;
}
:global(.category-block > i) {
max-height: 153px;
}
}
4. Go to the following path - etatvasoftpwa/pwa-studio/packages/venia-
ui/lib/components/CategoryList/ categoryTile.css. Add the following css to file categoryTile.css.

categoryTile.css
.root {
display: block;
line-height: 1rem;
text-align: center;
width: 6rem;
}
.imageContainer {
margin: 0 0.5rem 1rem 0.5rem;
}
.image {
border-radius: 0;
box-shadow: 0 0 0 1px rgb(var(--venia-border));
display: block;
height: 5rem;
object-fit: cover;
}
.imageWrapper {
display: block;
margin-bottom: 10px;
}
.category-name {
text-align: center;
}
.image_empty {
composes: image;
}
.name {
display: block;
}
@media (max-width: 480px) {
:global(.category-name) { font-size: 15px; line-height: 19px; }
}

5. Now we can get this category list in our site Homepage. For that we have to place
“<CategoryList id={2} />” in “Home-Custom“ section inside cms.js file. Here id is category id
that the root category whose subcategory we want to access from magneto admin.
Add “importmport CategoryList from '../../components/CategoryList'” in the file in import
section.
→ Get Footer details From Magento admin and set it in frontend footer section.

1. Now will create footer for our site.We already have Footer component in pwa. Go to the following
path - etatvasoftpwa/pwa-studio/packages/venia-ui/lib/components/Footer/footer.js. Replace the
code with the below code.

footer.js
import React,{useEffect} from 'react';
import { shape, string } from 'prop-types';
import { useQuery } from '@apollo/react-hooks';
import { mergeClasses } from '../../classify';
import defaultClasses from './footer.css';
import GET_STORE_CONFIG_DATA from '../../queries/getStoreConfigData.graphql';
import FooterCategories from './footerCategories.js';
import { fullPageLoadingIndicator } from '../LoadingIndicator';

const Footer = props => {


const classes = mergeClasses(defaultClasses, props.classes);
const { loading, error, data } = useQuery(GET_STORE_CONFIG_DATA);

if (error) {
return <div className={classes.fetchError}>
Data Fetch Error: <pre>{error.message}</pre>
</div>;
}
if (loading) {
return fullPageLoadingIndicator;
}
let copyright = null;
let address = null;
let phone = null;
let email = null;
if (data && data.storeConfig) {
copyright = data.storeConfig.pwa_website_copyright;
address = 'Address : '+data.storeConfig.pwa_website_address;
phone = 'Phone No. : '+data.storeConfig.pwa_website_phone;
email = 'Email : '+data.storeConfig.pwa_website_email;
}
return (
<footer className="footer">
<div className="footer-top">
<div className="container">
<div className="row">
<div className="col-md-3">
<h2 className={classes.tileTitle}>
<span>Quick Links</span>
</h2>
<p className={classes.tileBody}>
<span>
<a href="/">Home</a>
</span>
</p>
<p className={classes.tileBody}>
<span>
<a href="about">About Us</a>
</span>
</p>
<p className={classes.tileBody}>
<span>
<a href="">Terms & Conditions</a>
</span>
</p>
<p className={classes.tileBody}>
<span>
<a href="">Privacy Policy</a>
</span>
</p>
</div>
<div className="col-md-3">
<h2 className={classes.tileTitle}>
<span>Categories</span>
</h2>
<FooterCategories />
</div>
<div className="col-md-6">
<h2 className={classes.tileTitle}>
<span>Contact Us</span>
</h2>
<p className={classes.tileBody}>
<span>
{address}
</span>
</p>
<p className={classes.tileBody}>
<span>
<a href="tel:{phone}">{phone}</a>
</span>
</p>
<p className={classes.tileBody}>
<span>
<a href="mailto:{email}">{email}</a>
</span>
</p>
</div>
</div>
</div>
</div>
<div className="footer-copyright">
<div className="container">
<p className={classes.copyright}>{copyright}</p>
</div>
</div>
</footer>
);
};

Footer.propTypes = {
classes: shape({
copyright: string,
root: string,
tile: string,
tileBody: string,
tileTitle: string
})
};

export default Footer;

Here we have created three sections in footer.


1. Firstly we have Quick Links section where we have kept the page links. Here we have to create pages
prior so that we can use that page link in footer. This static page creation will see later.

2. The second section is the main categories list section here we have i mported
“FooterCategories” from the file footerCategories.js. Go to the following path -
etatvasoftpwa/pwa-studio/packages/venia-ui/lib/components/Footer and create the
footerCategories.js file with the below below.
footerCategories.js
import React, { Component } from 'react';
import { useQuery } from '@apollo/react-hooks';
import { fullPageLoadingIndicator } from '../LoadingIndicator';
import categoryListQuery from '../../queries/getCategoryList.graphql';
const categoryUrlSuffix = '.html';

const FooterCategories = props => {


const id =2;
const { data, error, loading } = useQuery( categoryListQuery,{variables:{ id }});
if (error) {
return (
<div>
Data Fetch Error: <pre>{error.message}</pre>
</div>
);
}
if (loading) {
return fullPageLoadingIndicator;
}
if (data.category.children.length === 0) {
return (
<div>
No categories found.
</div>
);
}
if (data) {
return (
<>
{data.category.children.map(item => (
<p className="footer-tileBody-2R5">
<span>
<a href={`/${item.url_key}${categoryUrlSuffix}`}>{item.name}</a>
</span></p>
))}
</>
);
}
}
export default FooterCategories;

3. The third section will be the contact detail section. Which we can fetch from magneto admin from
store configuration which are we have already mentioned above in “Custom Configurations” section.
→ Create static Page in Pwa and display data fetached from the Magento Block.
Here we will create About Us page in Pwa.

1. Now will create About Us component for our site. Go to the following path - etatvasoftpwa/pwa-
studio/packages/venia-ui/lib/RootComponents/ and create “About” folder. Inside that we will create
about.js file and index.js file as shown below.
about.js
import React, {Component} from 'react';
import './about.css';
import CmsBlock from '../../components/CmsBlock';
import { Title } from '../../components/Head';

export default class About extends Component {

render() {
const titleContent = 'About Us - Tatvasoft Pwa';
return (
<>
<Title>{titleContent}</Title>
<div className="about-us">
<CmsBlock identifiers={1} />
</div>
</>
);
}
}

index.js
export { default } from './about';

2. Go to the following path - etatvasoftpwa/pwa-studio/packages/venia-


ui/lib/components/App/renderRoutes.js
Add “<Route exact path="/about" component={About} />” inside const renderRoutes like the same
other routes are placed. Also need to import the About componnet like below.
const About = lazy(() => import('../../RootComponents/About'));

so, finally the “renderRoutes.js” will look like below.


renderRoutes.js
import React, { lazy, Suspense } from 'react';
import { Switch, Route } from '@magento/venia-drivers';
import { Page } from '@magento/peregrine';
import ErrorView from '../ErrorView/index';

const CreateAccountPage = lazy(() => import('../CreateAccountPage/index'));


const Search = lazy(() => import('../../RootComponents/Search'));
const About = lazy(() => import('../../RootComponents/About'));
const renderRoutingError = props => <ErrorView {...props} />;

const renderRoutes = () => (


<Suspense fallback={null}>
<Switch>
<Route exact path="/search.html" component={Search} />
<Route exact path="/create-account" component={CreateAccountPage} />
<Route exact path="/about" component={About} />
<Route render={() => <Page>{renderRoutingError}</Page>} />
</Switch>
</Suspense>
);

export default renderRoutes;

→ The page can be accessed by url “http://etatvasoftpwa.com:5000/about”.

You might also like