├── public
├── favicon.ico
├── logo192.png
├── logo512.png
├── robots.txt
├── manifest.json
└── index.html
├── src
├── components
│ ├── Body
│ │ ├── Body.module.css
│ │ └── Body.jsx
│ ├── Footer
│ │ ├── Footer.module.css
│ │ └── Footer.jsx
│ ├── header
│ │ ├── Header.module.css
│ │ └── Header.jsx
│ ├── Layout
│ │ └── Layout.jsx
│ ├── Builder
│ │ ├── Totalprice
│ │ │ ├── Totalprice.module.css
│ │ │ └── Totalprice.jsx
│ │ ├── Builder.module.css
│ │ ├── Items
│ │ │ ├── Items.jsx
│ │ │ └── item
│ │ │ │ ├── Item.module.css
│ │ │ │ └── Item.jsx
│ │ └── Builder.jsx
│ ├── IceCream
│ │ ├── Scoop
│ │ │ ├── Scoop.jsx
│ │ │ └── Scoop.module.css
│ │ ├── IceCream.jsx
│ │ └── IceCream.module.css
│ └── Modal
│ │ ├── Modal.jsx
│ │ └── Modal.module.css
├── assets
│ └── images
│ │ ├── cherry.png
│ │ ├── react.svg
│ │ └── logo.svg
├── container
│ └── IceCreamBuilder
│ │ ├── IceCreamBuilder.module.css
│ │ └── IceCreamBuilder.jsx
├── setupTests.js
├── App.test.js
├── index.css
├── reportWebVitals.js
├── App.jsx
├── index.js
└── App.css
├── README.md
├── .gitignore
└── package.json
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/masud-pervez/Ice-Cream/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/masud-pervez/Ice-Cream/HEAD/public/logo192.png
--------------------------------------------------------------------------------
/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/masud-pervez/Ice-Cream/HEAD/public/logo512.png
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/src/components/Body/Body.module.css:
--------------------------------------------------------------------------------
1 | .mainBody {
2 | margin: 0;
3 | width: 100%;
4 | height: 100%;
5 | }
6 |
--------------------------------------------------------------------------------
/src/assets/images/cherry.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/masud-pervez/Ice-Cream/HEAD/src/assets/images/cherry.png
--------------------------------------------------------------------------------
/src/components/Footer/Footer.module.css:
--------------------------------------------------------------------------------
1 | .container {
2 | border-bottom: 0px !important;
3 | padding-top: 0px;
4 | color: #616163;
5 | }
6 |
7 |
--------------------------------------------------------------------------------
/src/components/header/Header.module.css:
--------------------------------------------------------------------------------
1 | /* header */
2 | .logo {
3 | width: 60px;
4 | }
5 |
6 | .reactLogo {
7 | height: 60px;
8 | vertical-align: middle;
9 | }
--------------------------------------------------------------------------------
/src/container/IceCreamBuilder/IceCreamBuilder.module.css:
--------------------------------------------------------------------------------
1 | @media only screen and (max-width: 767.99px) {
2 | .container {
3 | flex-direction: column;
4 | }
5 | }
--------------------------------------------------------------------------------
/src/components/Layout/Layout.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const Layout = ({ children }) => {
4 | return
{children}
;
5 | }
6 |
7 | export default Layout
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | In the project directory, you can run:
2 |
3 | ### `npm start`
4 |
5 | Runs the app in the development mode.\
6 | Open [http://localhost:3000](http://localhost:3000) to view it in your browser.
7 |
--------------------------------------------------------------------------------
/src/components/Builder/Totalprice/Totalprice.module.css:
--------------------------------------------------------------------------------
1 | .total {
2 | border-top: 1px solid #5250507e;
3 | padding: 10px;
4 | display: flex;
5 | justify-content: space-between;
6 | align-items: center;
7 | font-weight: bold;
8 | width: 100%;
9 | }
--------------------------------------------------------------------------------
/src/setupTests.js:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom';
6 |
--------------------------------------------------------------------------------
/src/App.test.js:
--------------------------------------------------------------------------------
1 | import { render, screen } from '@testing-library/react';
2 | import App from './App';
3 |
4 | test('renders learn react link', () => {
5 | render();
6 | const linkElement = screen.getByText(/learn react/i);
7 | expect(linkElement).toBeInTheDocument();
8 | });
9 |
--------------------------------------------------------------------------------
/src/components/IceCream/Scoop/Scoop.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import classes from "./Scoop.module.css"
3 |
4 |
5 | const Scoop = ({ scoop }) => {
6 | return (
7 |
8 | )
9 | }
10 |
11 | export default Scoop;
--------------------------------------------------------------------------------
/src/components/Body/Body.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import classes from "./Body.module.css"
3 | import IceCreamBuilder from '../../container/IceCreamBuilder/IceCreamBuilder'
4 |
5 | const Body = () => {
6 | return (
7 |
8 |
9 |
10 | )
11 | }
12 |
13 | export default Body
--------------------------------------------------------------------------------
/src/components/Modal/Modal.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import classes from "./Modal.module.css"
3 |
4 | const Modal = ({ children }) => {
5 | return (
6 |
7 |
8 |
9 | {children}
10 |
11 |
12 | )
13 | }
14 |
15 | export default Modal
--------------------------------------------------------------------------------
/src/components/Builder/Totalprice/Totalprice.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import classes from "./Totalprice.module.css"
3 |
4 | const Totalprice = ({price = 0}) => {
5 | return (
6 |
7 |
Total Price
8 |
{price.toFixed(2)} TK
9 |
10 | );
11 | };
12 |
13 | export default Totalprice;
14 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5 | sans-serif;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | }
9 |
10 | code {
11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
12 | monospace;
13 | }
14 |
--------------------------------------------------------------------------------
/src/reportWebVitals.js:
--------------------------------------------------------------------------------
1 | const reportWebVitals = onPerfEntry => {
2 | if (onPerfEntry && onPerfEntry instanceof Function) {
3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
4 | getCLS(onPerfEntry);
5 | getFID(onPerfEntry);
6 | getFCP(onPerfEntry);
7 | getLCP(onPerfEntry);
8 | getTTFB(onPerfEntry);
9 | });
10 | }
11 | };
12 |
13 | export default reportWebVitals;
14 |
--------------------------------------------------------------------------------
/src/components/Builder/Builder.module.css:
--------------------------------------------------------------------------------
1 | /* builder */
2 | .builder {
3 | padding: 20px;
4 | background: #fff;
5 | border: 1px solid #dddfe2;
6 | border-radius: 3px;
7 | }
8 |
9 | .order {
10 | background-color: #28a745;
11 | color: #fff;
12 | border-color: #28a745;
13 | border: 1px solid transparent;
14 | display: block;
15 | margin: 30px auto 0;
16 | width: 100%;
17 | padding: 10px 0;
18 | font-weight: bold;
19 | }
--------------------------------------------------------------------------------
/src/App.jsx:
--------------------------------------------------------------------------------
1 | import "bootstrap/dist/css/bootstrap.min.css";
2 | import "./App.css"
3 | import Layout from "./components/Layout/Layout";
4 | import Header from "./components/header/Header"
5 | import Body from "./components/Body/Body"
6 | import Footer from "./components/Footer/Footer"
7 |
8 |
9 | function App() {
10 | return(
11 |
12 |
13 |
14 |
15 |
16 | );
17 | }
18 |
19 | export default App;
20 |
--------------------------------------------------------------------------------
/src/components/Builder/Items/Items.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Item from "./item/Item";
3 |
4 | const Items = ({ items ,add ,remove ,scoops }) => {
5 | const flavors = Object.keys(items);
6 |
7 | return (
8 |
9 | {flavors.map((flavor) => (
10 |
17 | ))}
18 |
19 | );
20 | };
21 |
22 | export default Items;
23 |
--------------------------------------------------------------------------------
/src/components/Modal/Modal.module.css:
--------------------------------------------------------------------------------
1 | /* modal */
2 | .backdrop {
3 | display: none;
4 | position: fixed;
5 | width: 100%;
6 | height: 100%;
7 | top: 0;
8 | left: 0;
9 | right: 0;
10 | bottom: 0;
11 | background-color: rgba(0, 0, 0, 0.5);
12 | z-index: 9;
13 | cursor: pointer;
14 | }
15 |
16 | .modalBody {
17 | display: none;
18 | position: absolute;
19 | top: 50%;
20 | left: 50%;
21 | transform: translate(-50%, -50%);
22 | -ms-transform: translate(-50%, -50%);
23 | z-index: 10;
24 | background-color: white;
25 | }
26 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom/client';
3 | import './index.css';
4 | import App from './App';
5 | import reportWebVitals from './reportWebVitals';
6 |
7 | const root = ReactDOM.createRoot(document.getElementById('root'));
8 | root.render(
9 |
10 |
11 |
12 | );
13 |
14 | // If you want to start measuring performance in your app, pass a function
15 | // to log results (for example: reportWebVitals(console.log))
16 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
17 | reportWebVitals();
18 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/src/components/IceCream/IceCream.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import classes from "./IceCream.module.css";
3 | import Scoop from "./Scoop/Scoop";
4 |
5 | const IceCream = ({ scoops }) => {
6 | return (
7 |
8 |
9 |
10 |
11 | {scoops?.map((scoop) => (
12 |
14 | ))}
15 |
16 |
17 |
18 |
19 | );
20 | };
21 |
22 | export default IceCream;
23 |
--------------------------------------------------------------------------------
/src/components/Footer/Footer.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import classes from "./Footer.module.css"
3 |
4 | const Footer = () => {
5 | return (
6 |
18 | )
19 | }
20 |
21 | export default Footer
--------------------------------------------------------------------------------
/src/components/Builder/Items/item/Item.module.css:
--------------------------------------------------------------------------------
1 | /* Item */
2 |
3 | .item {
4 | height: 40px;
5 | text-align: left;
6 | clear: both;
7 | padding: 0;
8 | line-height: 40px;
9 | }
10 |
11 | .quantity {
12 | color: #5250507e;
13 | }
14 |
15 | .quantity::after {
16 | content: ")";
17 | }
18 |
19 | .quantity::before {
20 | content: "(";
21 | }
22 |
23 | .plus {
24 | width: 30px;
25 | font-size: 20px;
26 | background: #28a745;
27 | border: 0px;
28 | color: white;
29 | padding: 0px 5px;
30 | }
31 |
32 | .minus {
33 | width: 30px;
34 | font-size: 20px;
35 | background: red;
36 | border: 0px;
37 | color: white;
38 | padding: 0px 5px;
39 | margin-left: 5px;
40 | }
41 |
--------------------------------------------------------------------------------
/src/components/header/Header.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import clsses from './Header.module.css'
3 | import logo from '../../assets/images/logo.svg'
4 | import reactLogo from '../../assets/images/react.svg'
5 | const Header = () => {
6 | return (
7 |
19 | )
20 | }
21 |
22 | export default Header
--------------------------------------------------------------------------------
/src/components/IceCream/IceCream.module.css:
--------------------------------------------------------------------------------
1 |
2 | /* ice cream */
3 | .icecream {
4 | display: flex;
5 | flex-direction: column-reverse;
6 | justify-content: center;
7 | align-items: center;
8 | height: 100%;
9 | width: 100%;
10 | min-height: 632px;
11 | }
12 |
13 | @media only screen and (max-width: 767.99px) {
14 | .icecream {
15 | min-height: 0px !important;
16 | }
17 | }
18 |
19 | .cone {
20 | margin-top: -20px;
21 | height: 0px;
22 | width: 0px;
23 | border-right: 45px solid transparent;
24 | border-left: 45px solid transparent;
25 | border-top: 140px solid tan;
26 | z-index: 1;
27 | }
28 |
29 | .cherry {
30 | background: url(../../assets/images/cherry.png) center/cover no-repeat;
31 | width: 50px;
32 | height: 77px;
33 | z-index: 3;
34 | margin-bottom: -10px;
35 | }
--------------------------------------------------------------------------------
/src/components/IceCream/Scoop/Scoop.module.css:
--------------------------------------------------------------------------------
1 | /* scoop */
2 |
3 | .scoop {
4 | width: 100px;
5 | height: 80px;
6 | border-radius: 100%;
7 | position: relative;
8 | margin-bottom: -45px;
9 | z-index: 3;
10 | }
11 |
12 | .scoop::after {
13 | content: "";
14 | position: absolute;
15 | bottom: 10px;
16 | left: -10px;
17 | height: 35px;
18 | width: 120px;
19 | border-radius: 100%;
20 | z-index: 2;
21 | }
22 |
23 | div.scoop:first-of-type {
24 | margin-bottom: 0px;
25 | }
26 |
27 | .chocolate,
28 | .chocolate::after {
29 | background: #b98160;
30 | }
31 |
32 | .vanilla,
33 | .vanilla::after {
34 | background: #fae8b6;
35 | }
36 |
37 | .strawberry,
38 | .strawberry::after {
39 | background: pink;
40 | }
41 |
42 | .orange,
43 | .orange::after {
44 | background: orange;
45 | }
46 |
47 | .lemon,
48 | .lemon::after {
49 | background: #5fd592;
50 | }
--------------------------------------------------------------------------------
/src/components/Builder/Builder.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import classes from "./Builder.module.css";
3 | import Items from "./Items/Items";
4 | import Totalprice from "./Totalprice/Totalprice";
5 | import Modal from "../Modal/Modal";
6 |
7 |
8 | const Builder = ({ items , price, add, remove , scoops}) => {
9 | return (
10 |
11 |
12 |
Build your own Ice Cream Sundae
13 |
14 |
15 |
16 |
22 |
23 |
24 | hello modal
25 |
26 |
27 | );
28 | };
29 |
30 | export default Builder;
31 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "practice-react",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.16.4",
7 | "@testing-library/react": "^13.2.0",
8 | "@testing-library/user-event": "^13.5.0",
9 | "bootstrap": "^5.1.3",
10 | "react": "^18.1.0",
11 | "react-bootstrap": "^2.3.1",
12 | "react-dom": "^18.1.0",
13 | "react-scripts": "5.0.1",
14 | "web-vitals": "^2.1.4"
15 | },
16 | "scripts": {
17 | "start": "react-scripts start",
18 | "build": "react-scripts build",
19 | "test": "react-scripts test",
20 | "eject": "react-scripts eject"
21 | },
22 | "eslintConfig": {
23 | "extends": [
24 | "react-app",
25 | "react-app/jest"
26 | ]
27 | },
28 | "browserslist": {
29 | "production": [
30 | ">0.2%",
31 | "not dead",
32 | "not op_mini all"
33 | ],
34 | "development": [
35 | "last 1 chrome version",
36 | "last 1 firefox version",
37 | "last 1 safari version"
38 | ]
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/components/Builder/Items/item/Item.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import classes from "./Item.module.css";
3 | import { countBy } from "lodash";
4 |
5 | const Item = ({ name, add, remove, scoops ={} }) => {
6 | const scoopByCount = countBy(scoops);
7 | const str = name;
8 | const str2 = str.charAt(0).toUpperCase() + str.slice(1);
9 |
10 | return (
11 |
12 | {str2}
13 | {scoopByCount[name] >= 0 ? (
14 | {scoopByCount[name]}
15 | ) : null}
16 |
17 |
24 |
31 |
32 |
33 | );
34 | };
35 |
36 | export default Item;
37 |
--------------------------------------------------------------------------------
/src/App.css:
--------------------------------------------------------------------------------
1 | /* common */
2 | * {
3 | box-sizing: border-box;
4 | font-size: 14px;
5 | padding: 5px;
6 | }
7 |
8 | body {
9 | margin: 0;
10 | background: #f3f3f3; /*#53c1de*/
11 | color: #1d2129;
12 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica,
13 | Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
14 | }
15 |
16 | p {
17 | margin-bottom: 30px;
18 | }
19 |
20 | ul {
21 | list-style-type: none;
22 | width: 100%;
23 | margin: 25px auto 10px;
24 | padding: 0;
25 | }
26 |
27 | a,
28 | a:link,
29 | a:visited,
30 | a:hover,
31 | a:active {
32 | color: #385898;
33 | text-decoration: none;
34 | }
35 |
36 | button {
37 | outline: none;
38 | cursor: pointer !important;
39 | }
40 |
41 | input[type="submit"]:disabled,
42 | button:disabled {
43 | opacity: 0.6;
44 | cursor: not-allowed !important;
45 | }
46 |
47 | .right {
48 | float: right;
49 | }
50 |
51 | .textRight {
52 | text-align: right;
53 | }
54 |
55 | .rounded {
56 | border-radius: 3px;
57 | }
58 |
59 | span {
60 | line-height: normal;
61 | vertical-align: middle;
62 | padding: 0;
63 | }
64 |
65 | .red {
66 | color: red;
67 | padding: 0;
68 | margin: 0;
69 | }
70 |
71 | .container {
72 | border-bottom: 1px solid #5250507e;
73 | display: flex;
74 | justify-content: space-between;
75 | align-items: center;
76 | margin: 0 auto;
77 | max-width: 1024px;
78 | }
79 |
80 | .container > div {
81 | width: 100%;
82 | }
83 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | React App
28 |
29 |
30 |
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/src/container/IceCreamBuilder/IceCreamBuilder.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import Icecream from "../../components/IceCream/IceCream";
3 | import Builder from "../../components/Builder/Builder";
4 |
5 | export default class IceCreamBuilder extends Component {
6 | state = {
7 | items: {},
8 | scoops: [],
9 | totalPrice: 0,
10 | };
11 |
12 | componentDidMount() {
13 | fetch("https://ice-cream-92923-default-rtdb.firebaseio.com/Items.json")
14 | .then((response) => response.json())
15 | .then((responseData) => {
16 | this.setState({
17 | items: responseData,
18 | });
19 | });
20 | }
21 |
22 | addscoop = (scoop) => {
23 | const { scoops, items } = this.state;
24 | const workingScoop = [...scoops];
25 | workingScoop.push(scoop);
26 |
27 | this.setState((prevState) => {
28 | return {
29 | scoops: workingScoop,
30 | totalPrice: prevState.totalPrice + items[scoop],
31 | };
32 | });
33 | };
34 |
35 | removescoop = (scoop) => {
36 | const { scoops, items } = this.state;
37 | const workingScoop = [...scoops];
38 | const scoopIndex = workingScoop.findIndex((sc) => sc === scoop);
39 | workingScoop.splice(scoopIndex, 1);
40 |
41 | this.setState((prevState) => {
42 | return {
43 | scoops: workingScoop,
44 | totalPrice: prevState.totalPrice - items[scoop],
45 | };
46 | });
47 | };
48 |
49 | render() {
50 | const { items, totalPrice, scoops } = this.state;
51 | return (
52 |
53 |
54 |
55 |
56 |
57 |
58 |
65 |
66 |
67 |
68 | );
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/assets/images/react.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/images/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
82 |
--------------------------------------------------------------------------------