Skip to content

Commit

Permalink
Add react microfrontends
Browse files Browse the repository at this point in the history
  • Loading branch information
blaz-cerpnjak committed Apr 14, 2024
1 parent 6cf07b4 commit 4adeba0
Show file tree
Hide file tree
Showing 52 changed files with 39,686 additions and 0 deletions.
6 changes: 6 additions & 0 deletions Microfrontends/home/.babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"presets": ["@babel/preset-react", "@babel/preset-env"],
"plugins": [
["@babel/transform-runtime"]
]
}
42 changes: 42 additions & 0 deletions Microfrontends/home/src/Navbar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import React from 'react';
import { Menubar } from 'primereact/menubar';
import { Badge } from 'primereact/badge';
import { Avatar } from 'primereact/avatar';
import { Button } from 'primereact/button';

export default function Navbar() {
const itemRenderer = (item) => (
<a className="flex align-items-center p-menuitem-link">
<span className={item.icon} />
<span className="mx-2">{item.label}</span>
{item.badge && <Badge className="ml-auto" value={item.badge} />}
{item.shortcut && <span className="ml-auto border-1 surface-border border-round surface-100 text-xs p-1">{item.shortcut}</span>}
</a>
);
const items = [
{
label: 'Home',
icon: 'pi pi-home'
},
{
label: 'Orders',
icon: 'pi pi-envelope',
badge: 3,
template: itemRenderer
}
];

const start = <img alt="logo" src="https://primefaces.org/cdn/primereact/images/logo.png" height="40" className="mr-2"></img>;
const end = (
<div className="flex align-items-center gap-2">
<Button icon="pi pi-shopping-cart" rounded text severity="secondary" aria-label="Filter" />
<Avatar image="https://primefaces.org/cdn/primereact/images/avatar/amyelsner.png" shape="circle" />
</div>
);

return (
<div className="card">
<Menubar model={items} start={start} end={end} />
</div>
)
}
120 changes: 120 additions & 0 deletions Microfrontends/home/src/ProductsPage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import React, { useState, useEffect } from 'react';
import { Button } from 'primereact/button';
import { DataView, DataViewLayoutOptions } from 'primereact/dataview';
import { Rating } from 'primereact/rating';
import { Tag } from 'primereact/tag';
import { classNames } from 'primereact/utils';
import { getProducts } from "./services/ProductsService";

export default function ProductsPage() {
const [products, setProducts] = useState([]);
const [layout, setLayout] = useState('grid');

useEffect(() => {
console.log('useEffect');
getProducts().then((data) => {
console.log(data);
setProducts(data)
});
}, []);

const getSeverity = (product) => {
return 'success';
/*switch (product.inventoryStatus) {
case 'INSTOCK':
return 'success';
case 'LOWSTOCK':
return 'warning';
case 'OUTOFSTOCK':
return 'danger';
default:
return null;*/
}

const formatProductPrice = (priceInCents) => {
const priceInDollars = priceInCents / 100;
return `$${priceInDollars.toFixed(2)}`;
};

const listItem = (product, index) => {
return (
<div className="col-12" key={product.id}>
<div className={classNames('flex flex-column xl:flex-row xl:align-items-start p-4 gap-4', { 'border-top-1 surface-border': index !== 0 })}>
<img className="w-9 sm:w-16rem xl:w-10rem shadow-2 block xl:block mx-auto border-round" src={`${product.image}`} alt={product.name} />
<div className="flex flex-column sm:flex-row justify-content-between align-items-center xl:align-items-start flex-1 gap-4">
<div className="flex flex-column align-items-center sm:align-items-start gap-3">
<div className="text-2xl font-bold text-900">{product.name}</div>
<Rating value={product.rating} readOnly cancel={false}></Rating>
<div className="flex align-items-center gap-3">
<span className="flex align-items-center gap-2">
<i className="pi pi-tag"></i>
<span className="font-semibold">{product.category}</span>
</span>
<Tag value="IN STOCK" severity={getSeverity(product)}></Tag>
</div>
</div>
<div className="flex sm:flex-column align-items-center sm:align-items-end gap-3 sm:gap-2">
<span className="text-2xl font-semibold">{formatProductPrice(product.price)}</span>
<Button icon="pi pi-shopping-cart" className="p-button-rounded" disabled={product.inventoryStatus === 'OUTOFSTOCK'}></Button>
</div>
</div>
</div>
</div>
);
};

const gridItem = (product) => {
return (
<div className="col-12 sm:col-6 lg:col-12 xl:col-4 p-2" key={product.id}>
<div className="p-4 border-1 surface-border surface-card border-round">
<div className="flex flex-wrap align-items-center justify-content-between gap-2">
<div className="flex align-items-center gap-2">
<i className="pi pi-tag"></i>
<span className="font-semibold">{product.name}</span>
</div>
<Tag value="IN STOCK" severity={getSeverity(product)}></Tag>
</div>
<div className="flex flex-column align-items-center gap-3 py-5">
<img className="w-9 shadow-2 border-round" src={`${product.image}`} alt={product.name} />
<div className="text-2xl font-bold">{product.name}</div>
<Rating value={product.rating} readOnly cancel={false}></Rating>
</div>
<div className="flex align-items-center justify-content-between">
<span className="text-2xl font-semibold">{formatProductPrice(product.price)}</span>
<Button icon="pi pi-shopping-cart" className="p-button-rounded" disabled={product.inventoryStatus === 'OUTOFSTOCK'}></Button>
</div>
</div>
</div>
);
};

const itemTemplate = (product, layout, index) => {
if (!product) {
return;
}

if (layout === 'list') return listItem(product, index);
else if (layout === 'grid') return gridItem(product);
};

const listTemplate = (products, layout) => {
return <div className="grid grid-nogutter">{products.map((product, index) => itemTemplate(product, layout, index))}</div>;
};

const header = () => {
return (
<div className="flex justify-content-end">
<DataViewLayoutOptions layout={layout} onChange={(e) => setLayout(e.value)} />
</div>
);
};

return (
<div className="card">
<DataView value={products} listTemplate={listTemplate} layout={layout} header={header()} />
</div>
)
}
2 changes: 2 additions & 0 deletions Microfrontends/home/src/services/ProductsService.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const getProducts = () =>
fetch(`/api/v1/products/restaurant/66163e8b15226a4958756a26`).then((res) => res.json());
13 changes: 13 additions & 0 deletions Microfrontends/home/src/setupProxy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const { createProxyMiddleware } = require('http-proxy-middleware');

module.exports = function(app) {
app.use(
'/api/v1',
createProxyMiddleware({
target: 'http://localhost:8080/api/v1',
changeOrigin: true,
secure: false
})
);
};

71 changes: 71 additions & 0 deletions Microfrontends/home/webpack.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
const HtmlWebPackPlugin = require("html-webpack-plugin");
const ModuleFederationPlugin = require("webpack/lib/container/ModuleFederationPlugin");

const deps = require("./package.json").dependencies;
module.exports = {
output: {
publicPath: "http://localhost:3000/",
},

resolve: {
extensions: [".tsx", ".ts", ".jsx", ".js", ".json"],
},

devServer: {
port: 3000,
historyApiFallback: true,
},

module: {
rules: [
{
test: /\.m?js/,
type: "javascript/auto",
resolve: {
fullySpecified: false,
},
},
{
test: /\.(css|s[ac]ss)$/i,
use: ["style-loader", "css-loader", "postcss-loader"],
},
{
test: /\.(ts|tsx|js|jsx)$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
},
},
],
},

plugins: [
new ModuleFederationPlugin({
name: "home",
filename: "remoteEntry.js",
remotes: {
orders: "orders@http://localhost:3001/remoteEntry.js",
},
exposes: {
"./Navbar": "./src/Navbar.js",
"./ProductsPage": "./src/ProductsPage.js",
"./ProductsService": "./src/services/ProductsService.js",
},
shared: {
...deps,
react: {
eager: true,
singleton: true,
requiredVersion: deps.react,
},
"react-dom": {
singleton: true,
requiredVersion: deps["react-dom"],
},
},
}),
new HtmlWebPackPlugin({
template: "./public/index.html",
}),
],
};
6 changes: 6 additions & 0 deletions Microfrontends/orders/.babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"presets": ["@babel/preset-react", "@babel/preset-env"],
"plugins": [
["@babel/transform-runtime"]
]
}
115 changes: 115 additions & 0 deletions Microfrontends/orders/src/OrdersPage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import React, { useState, useEffect } from 'react';
import { Button } from 'primereact/button';
import { DataView, DataViewLayoutOptions } from 'primereact/dataview';
import { Rating } from 'primereact/rating';
import { Tag } from 'primereact/tag';
import { classNames } from 'primereact/utils';

export default function OrdersPage() {
const [products, setProducts] = useState([]);
const [layout, setLayout] = useState('grid');

useEffect(() => {
console.log('useEffect');
}, []);

const getSeverity = (product) => {
return 'danger';
/*switch (product.inventoryStatus) {
case 'INSTOCK':
return 'success';
case 'LOWSTOCK':
return 'warning';
case 'OUTOFSTOCK':
return 'danger';
default:
return null;*/
}

const formatProductPrice = (priceInCents) => {
const priceInDollars = priceInCents / 100;
return `$${priceInDollars.toFixed(2)}`;
};

const listItem = (product, index) => {
return (
<div className="col-12" key={product.id}>
<div className={classNames('flex flex-column xl:flex-row xl:align-items-start p-4 gap-4', { 'border-top-1 surface-border': index !== 0 })}>
<img className="w-9 sm:w-16rem xl:w-10rem shadow-2 block xl:block mx-auto border-round" src={`${product.image}`} alt={product.name} />
<div className="flex flex-column sm:flex-row justify-content-between align-items-center xl:align-items-start flex-1 gap-4">
<div className="flex flex-column align-items-center sm:align-items-start gap-3">
<div className="text-2xl font-bold text-900">{product.name}</div>
<Rating value={product.rating} readOnly cancel={false}></Rating>
<div className="flex align-items-center gap-3">
<span className="flex align-items-center gap-2">
<i className="pi pi-tag"></i>
<span className="font-semibold">{product.category}</span>
</span>
<Tag value="IN STOCK" severity={getSeverity(product)}></Tag>
</div>
</div>
<div className="flex sm:flex-column align-items-center sm:align-items-end gap-3 sm:gap-2">
<span className="text-2xl font-semibold">{formatProductPrice(product.price)}</span>
<Button icon="pi pi-shopping-cart" className="p-button-rounded" disabled={product.inventoryStatus === 'OUTOFSTOCK'}></Button>
</div>
</div>
</div>
</div>
);
};

const gridItem = (product) => {
return (
<div className="col-12 sm:col-6 lg:col-12 xl:col-4 p-2" key={product.id}>
<div className="p-4 border-1 surface-border surface-card border-round">
<div className="flex flex-wrap align-items-center justify-content-between gap-2">
<div className="flex align-items-center gap-2">
<i className="pi pi-tag"></i>
<span className="font-semibold">{product.name}</span>
</div>
<Tag value="IN STOCK" severity={getSeverity(product)}></Tag>
</div>
<div className="flex flex-column align-items-center gap-3 py-5">
<img className="w-9 shadow-2 border-round" src={`${product.image}`} alt={product.name} />
<div className="text-2xl font-bold">{product.name}</div>
<Rating value={product.rating} readOnly cancel={false}></Rating>
</div>
<div className="flex align-items-center justify-content-between">
<span className="text-2xl font-semibold">{formatProductPrice(product.price)}</span>
<Button icon="pi pi-shopping-cart" className="p-button-rounded" disabled={product.inventoryStatus === 'OUTOFSTOCK'}></Button>
</div>
</div>
</div>
);
};

const itemTemplate = (product, layout, index) => {
if (!product) {
return;
}

if (layout === 'list') return listItem(product, index);
else if (layout === 'grid') return gridItem(product);
};

const listTemplate = (products, layout) => {
return <div className="grid grid-nogutter">{products.map((product, index) => itemTemplate(product, layout, index))}</div>;
};

const header = () => {
return (
<div className="flex justify-content-end">
<DataViewLayoutOptions layout={layout} onChange={(e) => setLayout(e.value)} />
</div>
);
};

return (
<div className="card">
<DataView value={products} listTemplate={listTemplate} layout={layout} header={header()} />
</div>
)
}
Loading

0 comments on commit 4adeba0

Please sign in to comment.