+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam elit felis, molestie id venenatis at, commodo ac tortor. Pellentesque tempus aliquet purus, sed vulputate elit tempus ut.
+
+
Product Specifications
+
+
+
+
+
+
+
+
+ {product.price}
+
+
+
+
+
+
+
+
+ >
+ );
+}
\ No newline at end of file
diff --git a/03_source/mobile/src/pages/DemoReactShop/components/ProductReviews.jsx b/03_source/mobile/src/pages/DemoReactShop/components/ProductReviews.jsx
new file mode 100644
index 0000000..d564773
--- /dev/null
+++ b/03_source/mobile/src/pages/DemoReactShop/components/ProductReviews.jsx
@@ -0,0 +1,23 @@
+import { IonCol, IonIcon, IonNote } from "@ionic/react";
+import { star } from "ionicons/icons";
+import { useEffect, useState } from "react";
+import { randomCount } from "../utils";
+
+export const ProductReviews = () => {
+
+ // This count could come from the product (if real data was fed)
+ const [reviewCount, setReviewCount] = useState(0);
+
+ useEffect(() => {
+
+ setReviewCount(randomCount());
+ }, []);
+
+ return (
+
+
+
+ {reviewCount} review{reviewCount > 1 && "s"}
+
+ );
+}
\ No newline at end of file
diff --git a/03_source/mobile/src/pages/DemoReactShop/components/ProductSpecificationsAccordion.jsx b/03_source/mobile/src/pages/DemoReactShop/components/ProductSpecificationsAccordion.jsx
new file mode 100644
index 0000000..77ede79
--- /dev/null
+++ b/03_source/mobile/src/pages/DemoReactShop/components/ProductSpecificationsAccordion.jsx
@@ -0,0 +1,58 @@
+import { IonAccordion, IonAccordionGroup, IonItem, IonLabel, IonList, IonNote } from "@ionic/react";
+import { useRef } from "react";
+import { productSpecs } from "../utils";
+
+export const ProductSpecificationsAccordion = ({type, contentRef}) => {
+
+ const accordionGroupRef = useRef(null);
+
+ const log = () => {
+
+ const selectedAccordion = accordionGroupRef.current.value;
+
+ if (selectedAccordion) {
+
+ setTimeout(() => contentRef.current.scrollToBottom(400), 200);
+ }
+ }
+
+ return (
+
+ {Object.keys(productSpecs).map((spec, index) => {
+
+ const {header, options, wrapText = false, noteColor = false} = productSpecs[spec];
+
+ return (
+
+
+
+ {header}
+
+
+
+
+ {options.map((option, index2) => {
+
+ const {label, value} = option;
+
+ return (
+
+
+
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam elit felis, molestie id venenatis at, commodo ac tortor. Pellentesque tempus aliquet purus, sed vulputate elit tempus ut.
+
+
Product Specifications
+
+
+
+
+
+
+
+
+ {product.price}
+
+
+
+
+
+
+
+
+ >
+ );
+}
\ No newline at end of file
diff --git a/03_source/mobile/src/pages/components/ProductReviews.jsx b/03_source/mobile/src/pages/components/ProductReviews.jsx
new file mode 100644
index 0000000..d564773
--- /dev/null
+++ b/03_source/mobile/src/pages/components/ProductReviews.jsx
@@ -0,0 +1,23 @@
+import { IonCol, IonIcon, IonNote } from "@ionic/react";
+import { star } from "ionicons/icons";
+import { useEffect, useState } from "react";
+import { randomCount } from "../utils";
+
+export const ProductReviews = () => {
+
+ // This count could come from the product (if real data was fed)
+ const [reviewCount, setReviewCount] = useState(0);
+
+ useEffect(() => {
+
+ setReviewCount(randomCount());
+ }, []);
+
+ return (
+
+
+
+ {reviewCount} review{reviewCount > 1 && "s"}
+
+ );
+}
\ No newline at end of file
diff --git a/03_source/mobile/src/pages/components/ProductSpecificationsAccordion.jsx b/03_source/mobile/src/pages/components/ProductSpecificationsAccordion.jsx
new file mode 100644
index 0000000..77ede79
--- /dev/null
+++ b/03_source/mobile/src/pages/components/ProductSpecificationsAccordion.jsx
@@ -0,0 +1,58 @@
+import { IonAccordion, IonAccordionGroup, IonItem, IonLabel, IonList, IonNote } from "@ionic/react";
+import { useRef } from "react";
+import { productSpecs } from "../utils";
+
+export const ProductSpecificationsAccordion = ({type, contentRef}) => {
+
+ const accordionGroupRef = useRef(null);
+
+ const log = () => {
+
+ const selectedAccordion = accordionGroupRef.current.value;
+
+ if (selectedAccordion) {
+
+ setTimeout(() => contentRef.current.scrollToBottom(400), 200);
+ }
+ }
+
+ return (
+
+ {Object.keys(productSpecs).map((spec, index) => {
+
+ const {header, options, wrapText = false, noteColor = false} = productSpecs[spec];
+
+ return (
+
+
+
+ {header}
+
+
+
+
+ {options.map((option, index2) => {
+
+ const {label, value} = option;
+
+ return (
+
+
+
+