Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
→ Credentials
PC :
Username : pcq75
Password : tatva123
Database :
Username : root
Password : tatva123
7. Open this .env file and change/add the MAGENTO_BACKEND_URL to your local
magento instance.
bash deployVeniaSampleData.sh
bin/magento indexer:reindex
bin/magento cache:flush
12. Run server, Use any of the following commands from the project root directory
to start the server:
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.
</Directory>
</VirtualHost>
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.
1. I consider that you have to create a Banner Module in Magento Admin. In the
same module you have to create a “Resolver”.
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;
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.
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”.
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.
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;
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")
}
Ex : magentoroot/pwa-studio/packages/venia-ui/lib/components/FeaturedProducts
index.js
Index.js
export { default } from './FeaturedProducts';
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';
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);
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";
get imagePath() {
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 };
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.
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.
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.
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 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. 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. 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. 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.
// 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';
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.
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';
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
})
};
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';
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.
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';
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';
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';