Sei sulla pagina 1di 40

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 --admin-email=test@webkul.com --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 admin@test.com
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/FeaturedProd
uct.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/
useCategoryList';

// 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 imported
“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”.

Potrebbero piacerti anche