From 405073d229b54ff32f066bf067597785d9266f48 Mon Sep 17 00:00:00 2001 From: ivan-amelyanenka Date: Mon, 22 May 2023 22:13:54 +0200 Subject: [PATCH] finish --- BeerApp - Senior/.env.example | 2 + BeerApp - Senior/package-lock.json | 348 ++++++++++++++---- BeerApp - Senior/package.json | 9 +- BeerApp - Senior/public/index.html | 11 + BeerApp - Senior/public/service-worker.js | 29 ++ .../api/localStorageApi/beerLocalStorage.ts | 41 +++ BeerApp - Senior/src/types/apiCall.d.ts | 4 +- BeerApp - Senior/src/types/beer.d.ts | 4 +- BeerApp - Senior/src/views/Beer/Beer.css | 7 + BeerApp - Senior/src/views/Beer/index.tsx | 104 +++++- BeerApp - Senior/src/views/Beer/utils.ts | 33 +- BeerApp - Senior/src/views/BeerList/index.tsx | 169 ++++++++- BeerApp - Senior/src/views/BeerList/utils.ts | 17 +- BeerApp - Senior/src/views/Home/index.tsx | 48 ++- BeerApp - Senior/src/views/Home/utils.ts | 18 +- BeerApp - Senior/src/views/Offline/index.tsx | 10 +- 16 files changed, 732 insertions(+), 122 deletions(-) create mode 100644 BeerApp - Senior/.env.example create mode 100644 BeerApp - Senior/public/service-worker.js create mode 100644 BeerApp - Senior/src/api/localStorageApi/beerLocalStorage.ts create mode 100644 BeerApp - Senior/src/views/Beer/Beer.css diff --git a/BeerApp - Senior/.env.example b/BeerApp - Senior/.env.example new file mode 100644 index 0000000..8417805 --- /dev/null +++ b/BeerApp - Senior/.env.example @@ -0,0 +1,2 @@ +REACT_APP_API=some_api +REACT_APP_GOOGLE_API_KEY=google_api_key \ No newline at end of file diff --git a/BeerApp - Senior/package-lock.json b/BeerApp - Senior/package-lock.json index 7fe8bd1..6bcc801 100644 --- a/BeerApp - Senior/package-lock.json +++ b/BeerApp - Senior/package-lock.json @@ -12,11 +12,16 @@ "@emotion/styled": "^11.11.0", "@fontsource/roboto": "^4.5.8", "@mui/icons-material": "^5.11.16", - "@mui/material": "^5.13.0", + "@mui/material": "^5.13.1", + "@mui/styled-engine-sc": "^5.12.0", + "@react-google-maps/api": "^2.18.1", "axios": "^1.4.0", + "mui": "^0.0.1", "react": "^18.2.0", "react-dom": "^18.2.0", - "react-router-dom": "^6.11.1" + "react-router-dom": "^6.11.1", + "styled-components": "^5.3.10", + "tss-react": "^4.8.3" }, "devDependencies": { "@testing-library/jest-dom": "^5.16.5", @@ -161,7 +166,6 @@ "version": "7.21.5", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.5.tgz", "integrity": "sha512-SrKK/sRv8GesIW1bDagf9cCG38IOMYZusoe1dfg0D8aiUe3Amvoj1QtjTPAWcfrZFvIwlleLb0gxzQidL9w14w==", - "dev": true, "dependencies": { "@babel/types": "^7.21.5", "@jridgewell/gen-mapping": "^0.3.2", @@ -176,7 +180,6 @@ "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==", - "dev": true, "dependencies": { "@babel/types": "^7.18.6" }, @@ -312,7 +315,6 @@ "version": "7.21.5", "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.21.5.tgz", "integrity": "sha512-IYl4gZ3ETsWocUWgsFZLM5i1BYx9SoemminVEXadgLBa9TdeorzgLKm8wWLA6J1N/kT3Kch8XIk1laNzYoHKvQ==", - "dev": true, "engines": { "node": ">=6.9.0" } @@ -321,7 +323,6 @@ "version": "7.21.0", "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz", "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==", - "dev": true, "dependencies": { "@babel/template": "^7.20.7", "@babel/types": "^7.21.0" @@ -334,7 +335,6 @@ "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", - "dev": true, "dependencies": { "@babel/types": "^7.18.6" }, @@ -468,7 +468,6 @@ "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", - "dev": true, "dependencies": { "@babel/types": "^7.18.6" }, @@ -611,7 +610,6 @@ "version": "7.21.8", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.8.tgz", "integrity": "sha512-6zavDGdzG3gUqAdWvlLFfk+36RilI+Pwyuuh7HItyeScCWP3k6i8vKclAQ0bM/0y/Kz/xiwvxhMv9MgTJP5gmA==", - "dev": true, "bin": { "parser": "bin/babel-parser.js" }, @@ -2020,7 +2018,6 @@ "version": "7.20.7", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", - "dev": true, "dependencies": { "@babel/code-frame": "^7.18.6", "@babel/parser": "^7.20.7", @@ -2034,7 +2031,6 @@ "version": "7.21.5", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.5.tgz", "integrity": "sha512-AhQoI3YjWi6u/y/ntv7k48mcrCXmus0t79J9qPNlk/lAsFlCiJ047RmbfMOawySTHtywXhbXgpx/8nXMYd+oFw==", - "dev": true, "dependencies": { "@babel/code-frame": "^7.21.4", "@babel/generator": "^7.21.5", @@ -2474,6 +2470,11 @@ } } }, + "node_modules/@emotion/stylis": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz", + "integrity": "sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==" + }, "node_modules/@emotion/unitless": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", @@ -2603,6 +2604,23 @@ "resolved": "https://registry.npmjs.org/@fontsource/roboto/-/roboto-4.5.8.tgz", "integrity": "sha512-CnD7zLItIzt86q4Sj3kZUiLcBk1dSk81qcqgMGaZe7SQ1P8hFNxhMl5AZthK1zrDM5m74VVhaOpuMGIL4gagaA==" }, + "node_modules/@googlemaps/js-api-loader": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/@googlemaps/js-api-loader/-/js-api-loader-1.15.1.tgz", + "integrity": "sha512-AsnEgNsB7S/VdrHGEQUaUM2e5tmjFGKBAfzR/AqO8O7TPq/jQGvoRw5liPBw4EMF38RDsHmKDV89q/X+qiUREQ==", + "dependencies": { + "fast-deep-equal": "^3.1.3" + } + }, + "node_modules/@googlemaps/markerclusterer": { + "version": "2.0.15", + "resolved": "https://registry.npmjs.org/@googlemaps/markerclusterer/-/markerclusterer-2.0.15.tgz", + "integrity": "sha512-/I6Esi5FtyeVHsezN9Kut8zRJoqe7KkTIJXGVqpKFf6BjC7qQ1xRajLMkOz0s8XKgLevbr+KdYjuvmj+LohOGg==", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "supercluster": "^7.1.3" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.8", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", @@ -3005,7 +3023,6 @@ "version": "0.3.3", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", - "dev": true, "dependencies": { "@jridgewell/set-array": "^1.0.1", "@jridgewell/sourcemap-codec": "^1.4.10", @@ -3019,7 +3036,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", - "dev": true, "engines": { "node": ">=6.0.0" } @@ -3028,7 +3044,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", - "dev": true, "engines": { "node": ">=6.0.0" } @@ -3046,14 +3061,12 @@ "node_modules/@jridgewell/sourcemap-codec": { "version": "1.4.15", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.18", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", - "dev": true, "dependencies": { "@jridgewell/resolve-uri": "3.1.0", "@jridgewell/sourcemap-codec": "1.4.14" @@ -3062,8 +3075,7 @@ "node_modules/@jridgewell/trace-mapping/node_modules/@jridgewell/sourcemap-codec": { "version": "1.4.14", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", - "dev": true + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" }, "node_modules/@leichtgewicht/ip-codec": { "version": "2.0.4", @@ -3072,14 +3084,14 @@ "dev": true }, "node_modules/@mui/base": { - "version": "5.0.0-beta.0", - "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.0.tgz", - "integrity": "sha512-ap+juKvt8R8n3cBqd/pGtZydQ4v2I/hgJKnvJRGjpSh3RvsvnDHO4rXov8MHQlH6VqpOekwgilFLGxMZjNTucA==", + "version": "5.0.0-beta.1", + "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.1.tgz", + "integrity": "sha512-xrkDCeu3JQE+JjJUnJnOrdQJMXwKhbV4AW+FRjMIj5i9cHK3BAuatG/iqbf1M+jklVWLk0KdbgioKwK+03aYbA==", "dependencies": { "@babel/runtime": "^7.21.0", "@emotion/is-prop-valid": "^1.2.0", "@mui/types": "^7.2.4", - "@mui/utils": "^5.12.3", + "@mui/utils": "^5.13.1", "@popperjs/core": "^2.11.7", "clsx": "^1.2.1", "prop-types": "^15.8.1", @@ -3109,9 +3121,9 @@ "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" }, "node_modules/@mui/core-downloads-tracker": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.13.0.tgz", - "integrity": "sha512-5nXz2k8Rv2ZjtQY6kXirJVyn2+ODaQuAJmXSJtLDUQDKWp3PFUj6j3bILqR0JGOs9R5ejgwz3crLKsl6GwjwkQ==", + "version": "5.13.1", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.13.1.tgz", + "integrity": "sha512-qDHtNDO72NcBQMhaWBt9EZMvNiO+OXjPg5Sdk/6LgRDw6Zr3HdEZ5n2FJ/qtYsaT/okGyCuQavQkcZCOCEVf/g==", "funding": { "type": "opencollective", "url": "https://opencollective.com/mui" @@ -3143,16 +3155,16 @@ } }, "node_modules/@mui/material": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.13.0.tgz", - "integrity": "sha512-ckS+9tCpAzpdJdaTF+btF0b6mF9wbXg/EVKtnoAWYi0UKXoXBAVvEUMNpLGA5xdpCdf+A6fPbVUEHs9TsfU+Yw==", + "version": "5.13.1", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.13.1.tgz", + "integrity": "sha512-qSnbJZer8lIuDYFDv19/t3s0AXYY9SxcOdhCnGvetRSfOG4gy3TkiFXNCdW5OLNveTieiMpOuv46eXUmE3ZA6A==", "dependencies": { "@babel/runtime": "^7.21.0", - "@mui/base": "5.0.0-beta.0", - "@mui/core-downloads-tracker": "^5.13.0", - "@mui/system": "^5.12.3", + "@mui/base": "5.0.0-beta.1", + "@mui/core-downloads-tracker": "^5.13.1", + "@mui/system": "^5.13.1", "@mui/types": "^7.2.4", - "@mui/utils": "^5.12.3", + "@mui/utils": "^5.13.1", "@types/react-transition-group": "^4.4.6", "clsx": "^1.2.1", "csstype": "^3.1.2", @@ -3192,12 +3204,12 @@ "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" }, "node_modules/@mui/private-theming": { - "version": "5.12.3", - "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.12.3.tgz", - "integrity": "sha512-o1e7Z1Bp27n4x2iUHhegV4/Jp6H3T6iBKHJdLivS5GbwsuAE/5l4SnZ+7+K+e5u9TuhwcAKZLkjvqzkDe8zqfA==", + "version": "5.13.1", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.13.1.tgz", + "integrity": "sha512-HW4npLUD9BAkVppOUZHeO1FOKUJWAwbpy0VQoGe3McUYTlck1HezGHQCfBQ5S/Nszi7EViqiimECVl9xi+/WjQ==", "dependencies": { "@babel/runtime": "^7.21.0", - "@mui/utils": "^5.12.3", + "@mui/utils": "^5.13.1", "prop-types": "^15.8.1" }, "engines": { @@ -3248,16 +3260,41 @@ } } }, + "node_modules/@mui/styled-engine-sc": { + "version": "5.12.0", + "resolved": "https://registry.npmjs.org/@mui/styled-engine-sc/-/styled-engine-sc-5.12.0.tgz", + "integrity": "sha512-3MgYoY2YG5tx0E5oKqvCv94oL0ABVBr+qpcyvciXW/v0wzPG6bXvuZV80GHYlJfasgnnRa1AbRWf5a9FcX8v6g==", + "dependencies": { + "@babel/runtime": "^7.21.0", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui" + }, + "peerDependencies": { + "@types/styled-components": "^5.1.14", + "styled-components": "^5.3.1" + }, + "peerDependenciesMeta": { + "@types/styled-components": { + "optional": true + } + } + }, "node_modules/@mui/system": { - "version": "5.12.3", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.12.3.tgz", - "integrity": "sha512-JB/6sypHqeJCqwldWeQ1MKkijH829EcZAKKizxbU2MJdxGG5KSwZvTBa5D9qiJUA1hJFYYupjiuy9ZdJt6rV6w==", + "version": "5.13.1", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.13.1.tgz", + "integrity": "sha512-BsDUjhiO6ZVAvzKhnWBHLZ5AtPJcdT+62VjnRLyA4isboqDKLg4fmYIZXq51yndg/soDK9RkY5lYZwEDku13Ow==", "dependencies": { "@babel/runtime": "^7.21.0", - "@mui/private-theming": "^5.12.3", + "@mui/private-theming": "^5.13.1", "@mui/styled-engine": "^5.12.3", "@mui/types": "^7.2.4", - "@mui/utils": "^5.12.3", + "@mui/utils": "^5.13.1", "clsx": "^1.2.1", "csstype": "^3.1.2", "prop-types": "^15.8.1" @@ -3301,13 +3338,13 @@ } }, "node_modules/@mui/utils": { - "version": "5.12.3", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.12.3.tgz", - "integrity": "sha512-D/Z4Ub3MRl7HiUccid7sQYclTr24TqUAQFFlxHQF8FR177BrCTQ0JJZom7EqYjZCdXhwnSkOj2ph685MSKNtIA==", + "version": "5.13.1", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.13.1.tgz", + "integrity": "sha512-6lXdWwmlUbEU2jUI8blw38Kt+3ly7xkmV9ljzY4Q20WhsJMWiNry9CX8M+TaP/HbtuyR8XKsdMgQW7h7MM3n3A==", "dependencies": { "@babel/runtime": "^7.21.0", "@types/prop-types": "^15.7.5", - "@types/react-is": "^16.7.1 || ^17.0.0", + "@types/react-is": "^18.2.0", "prop-types": "^15.8.1", "react-is": "^18.2.0" }, @@ -3452,6 +3489,33 @@ "url": "https://opencollective.com/popperjs" } }, + "node_modules/@react-google-maps/api": { + "version": "2.18.1", + "resolved": "https://registry.npmjs.org/@react-google-maps/api/-/api-2.18.1.tgz", + "integrity": "sha512-KVlUO/Shh+0g/3egWaKmY0sz6+0QOnYkBGvrBMJbz23519LauA+iJFc4NDCmWNHqD5Vhb/Bkg0kSJgq0Stz3Iw==", + "dependencies": { + "@googlemaps/js-api-loader": "1.15.1", + "@googlemaps/markerclusterer": "2.0.15", + "@react-google-maps/infobox": "2.16.0", + "@react-google-maps/marker-clusterer": "2.16.1", + "@types/google.maps": "3.50.5", + "invariant": "2.2.4" + }, + "peerDependencies": { + "react": "^16.8 || ^17 || ^18", + "react-dom": "^16.8 || ^17 || ^18" + } + }, + "node_modules/@react-google-maps/infobox": { + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/@react-google-maps/infobox/-/infobox-2.16.0.tgz", + "integrity": "sha512-ZojiMS25388RcUHQPycUAerSqdHDom+3dHczVcXHdT/i8fka3O8InkHxXwMhvBoM143ips7mv2BPaYOAJ5f4Nw==" + }, + "node_modules/@react-google-maps/marker-clusterer": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/@react-google-maps/marker-clusterer/-/marker-clusterer-2.16.1.tgz", + "integrity": "sha512-jOuyqzWLeXvQcoAu6TCVWHAuko+sDt0JjawNHBGqUNLywMtTCvYP0L0PiqJZOUCUeRYGdUy0AKxQ+30vAkvwag==" + }, "node_modules/@remix-run/router": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.6.1.tgz", @@ -4063,6 +4127,11 @@ "@types/send": "*" } }, + "node_modules/@types/google.maps": { + "version": "3.50.5", + "resolved": "https://registry.npmjs.org/@types/google.maps/-/google.maps-3.50.5.tgz", + "integrity": "sha512-RuZf1MJtctGlpW+Gd4a/eGtAufUDjMf+eyN1l+B3fbe2YLScJbg8KEljJfb+6vnSPFAeM1/48geVIEg3vqOkxw==" + }, "node_modules/@types/graceful-fs": { "version": "4.1.6", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz", @@ -4217,21 +4286,11 @@ } }, "node_modules/@types/react-is": { - "version": "17.0.4", - "resolved": "https://registry.npmjs.org/@types/react-is/-/react-is-17.0.4.tgz", - "integrity": "sha512-FLzd0K9pnaEvKz4D1vYxK9JmgQPiGk1lu23o1kqGsLeT0iPbRSF7b76+S5T9fD8aRa0B8bY7I/3DebEj+1ysBA==", - "dependencies": { - "@types/react": "^17" - } - }, - "node_modules/@types/react-is/node_modules/@types/react": { - "version": "17.0.59", - "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.59.tgz", - "integrity": "sha512-gSON5zWYIGyoBcycCE75E9+r6dCC2dHdsrVkOEiIYNU5+Q28HcBAuqvDuxHcCbMfHBHdeT5Tva/AFn3rnMKE4g==", + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/@types/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-1vz2yObaQkLL7YFe/pme2cpvDsCwI1WXIfL+5eLz0MI9gFG24Re16RzUsI8t9XZn9ZWvgLNDrJBmrqXJO7GNQQ==", "dependencies": { - "@types/prop-types": "*", - "@types/scheduler": "*", - "csstype": "^3.0.2" + "@types/react": "*" } }, "node_modules/@types/react-transition-group": { @@ -5404,6 +5463,26 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/babel-plugin-styled-components": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-2.1.1.tgz", + "integrity": "sha512-c8lJlszObVQPguHkI+akXv8+Jgb9Ccujx0EetL7oIvwU100LxO6XAGe45qry37wUL40a5U9f23SYrivro2XKhA==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.16.0", + "@babel/helper-module-imports": "^7.16.0", + "babel-plugin-syntax-jsx": "^6.18.0", + "lodash": "^4.17.21", + "picomatch": "^2.3.0" + }, + "peerDependencies": { + "styled-components": ">= 2" + } + }, + "node_modules/babel-plugin-syntax-jsx": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz", + "integrity": "sha512-qrPaCSo9c8RHNRHIotaufGbuOBN8rtdC4QrrFFc43vyWCCz7Kl7GL1PGaXtMGQZUXrkCjNEgxDfmAuAabr/rlw==" + }, "node_modules/babel-plugin-transform-react-remove-prop-types": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz", @@ -5746,6 +5825,14 @@ "node": ">= 6" } }, + "node_modules/camelize": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz", + "integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/caniuse-api": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", @@ -6321,6 +6408,14 @@ "postcss": "^8.4" } }, + "node_modules/css-color-keywords": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", + "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==", + "engines": { + "node": ">=4" + } + }, "node_modules/css-declaration-sorter": { "version": "6.4.0", "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.4.0.tgz", @@ -6514,6 +6609,16 @@ "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==", "dev": true }, + "node_modules/css-to-react-native": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz", + "integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==", + "dependencies": { + "camelize": "^1.0.0", + "css-color-keywords": "^1.0.0", + "postcss-value-parser": "^4.0.2" + } + }, "node_modules/css-tree": { "version": "1.0.0-alpha.37", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", @@ -6745,7 +6850,6 @@ "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, "dependencies": { "ms": "2.1.2" }, @@ -8223,8 +8327,7 @@ "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "node_modules/fast-glob": { "version": "3.2.12", @@ -8866,7 +8969,6 @@ "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, "engines": { "node": ">=4" } @@ -9481,6 +9583,14 @@ "node": ">= 0.4" } }, + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, "node_modules/ipaddr.js": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", @@ -11016,7 +11126,6 @@ "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true, "bin": { "jsesc": "bin/jsesc" }, @@ -11093,6 +11202,11 @@ "node": ">=4.0" } }, + "node_modules/kdbush": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/kdbush/-/kdbush-3.0.0.tgz", + "integrity": "sha512-hRkd6/XW4HTsA9vjVpY9tuXJYLSlelnkTmVFu4M9/7MIYQtFcHpbugAU7UbOfjOiVSVYl2fqgBuJ32JUmRo5Ew==" + }, "node_modules/kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", @@ -11292,8 +11406,7 @@ "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "node_modules/lodash.camelcase": { "version": "4.3.0", @@ -11644,8 +11757,12 @@ "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/mui": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/mui/-/mui-0.0.1.tgz", + "integrity": "sha512-iB9zfxsJBcMkZ/SY6X+HGSPr4fftCZIQ76ZMH8iSMfVkidVzRtZlLW2gbWXUe+IMcj8JLv1p+dGKvPVlgtiocA==" }, "node_modules/multicast-dns": { "version": "7.2.5", @@ -12255,7 +12372,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, "engines": { "node": ">=8.6" }, @@ -13671,8 +13787,7 @@ "node_modules/postcss-value-parser": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "dev": true + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, "node_modules/prelude-ls": { "version": "1.2.1", @@ -14030,8 +14145,7 @@ "node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" }, "node_modules/react-refresh": { "version": "0.11.0", @@ -14949,6 +15063,11 @@ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "dev": true }, + "node_modules/shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -15392,6 +15511,59 @@ "webpack": "^5.0.0" } }, + "node_modules/styled-components": { + "version": "5.3.10", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.10.tgz", + "integrity": "sha512-3kSzSBN0TiCnGJM04UwO1HklIQQSXW7rCARUk+VyMR7clz8XVlA3jijtf5ypqoDIdNMKx3la4VvaPFR855SFcg==", + "dependencies": { + "@babel/helper-module-imports": "^7.0.0", + "@babel/traverse": "^7.4.5", + "@emotion/is-prop-valid": "^1.1.0", + "@emotion/stylis": "^0.8.4", + "@emotion/unitless": "^0.7.4", + "babel-plugin-styled-components": ">= 1.12.0", + "css-to-react-native": "^3.0.0", + "hoist-non-react-statics": "^3.0.0", + "shallowequal": "^1.1.0", + "supports-color": "^5.5.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/styled-components" + }, + "peerDependencies": { + "react": ">= 16.8.0", + "react-dom": ">= 16.8.0", + "react-is": ">= 16.8.0" + } + }, + "node_modules/styled-components/node_modules/@emotion/unitless": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", + "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==" + }, + "node_modules/styled-components/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/styled-components/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/stylehacks": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz", @@ -15486,6 +15658,14 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/supercluster": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/supercluster/-/supercluster-7.1.5.tgz", + "integrity": "sha512-EulshI3pGUM66o6ZdH3ReiFcvHpM3vAigyK+vcxdjpJyEbIIrtbmBdY23mGgnI24uXiGFvrGq9Gkum/8U7vJWg==", + "dependencies": { + "kdbush": "^3.0.0" + } + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -16027,6 +16207,26 @@ "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", "dev": true }, + "node_modules/tss-react": { + "version": "4.8.3", + "resolved": "https://registry.npmjs.org/tss-react/-/tss-react-4.8.3.tgz", + "integrity": "sha512-zxHffa5PB8pv72/fGWVZS5GykmFca2wfqxha2P0nAdQElurh7pLDQfhHS3hHkW8lTWqkns46dBaTvDUw5zKQcQ==", + "dependencies": { + "@emotion/cache": "*", + "@emotion/serialize": "*", + "@emotion/utils": "*" + }, + "peerDependencies": { + "@emotion/react": "^11.4.1", + "@emotion/server": "^11.4.0", + "react": "^16.8.0 || ^17.0.2 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/server": { + "optional": true + } + } + }, "node_modules/tsutils": { "version": "3.21.0", "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", diff --git a/BeerApp - Senior/package.json b/BeerApp - Senior/package.json index 8a45b59..74edbfd 100644 --- a/BeerApp - Senior/package.json +++ b/BeerApp - Senior/package.json @@ -7,11 +7,16 @@ "@emotion/styled": "^11.11.0", "@fontsource/roboto": "^4.5.8", "@mui/icons-material": "^5.11.16", - "@mui/material": "^5.13.0", + "@mui/material": "^5.13.1", + "@mui/styled-engine-sc": "^5.12.0", + "@react-google-maps/api": "^2.18.1", "axios": "^1.4.0", + "mui": "^0.0.1", "react": "^18.2.0", "react-dom": "^18.2.0", - "react-router-dom": "^6.11.1" + "react-router-dom": "^6.11.1", + "styled-components": "^5.3.10", + "tss-react": "^4.8.3" }, "devDependencies": { "@testing-library/jest-dom": "^5.16.5", diff --git a/BeerApp - Senior/public/index.html b/BeerApp - Senior/public/index.html index 2e08722..db36ddb 100644 --- a/BeerApp - Senior/public/index.html +++ b/BeerApp - Senior/public/index.html @@ -19,4 +19,15 @@
+ diff --git a/BeerApp - Senior/public/service-worker.js b/BeerApp - Senior/public/service-worker.js new file mode 100644 index 0000000..45b3233 --- /dev/null +++ b/BeerApp - Senior/public/service-worker.js @@ -0,0 +1,29 @@ +/* eslint-disable no-restricted-globals */ +const CACHE_NAME = 'beer-app-cache'; +const urlsToCache = [ + '/', + '/index.html', +]; + +self.addEventListener('install', function(event) { + event.waitUntil( + caches.open(CACHE_NAME) + .then(function(cache) { + console.log('Opened cache'); + return cache.addAll(urlsToCache); + }) + ); +}); + +self.addEventListener('fetch', function(event) { + event.respondWith( + caches.match(event.request) + .then(function(response) { + if (response) { + return response; + } + return fetch(event.request); + } + ) + ); +}); diff --git a/BeerApp - Senior/src/api/localStorageApi/beerLocalStorage.ts b/BeerApp - Senior/src/api/localStorageApi/beerLocalStorage.ts new file mode 100644 index 0000000..cb4e792 --- /dev/null +++ b/BeerApp - Senior/src/api/localStorageApi/beerLocalStorage.ts @@ -0,0 +1,41 @@ +import { Beer } from "../../types"; + + +const getSavedBeer = (id: string): Beer | undefined => { + const savedListString = localStorage.getItem('savedList'); + if (!savedListString) { + return + } + const savedListParsed = JSON.parse(savedListString); + return savedListParsed.find((beer: Beer) => beer.id === id); +}; + +const getSavedBeerList = (): Array => { + const savedListString = localStorage.getItem('savedList'); + if (savedListString) { + const savedListParsed = JSON.parse(savedListString); + return savedListParsed; + } + return []; +}; + +const saveBeerList = (beerList: Array) => { + localStorage.setItem('savedList', JSON.stringify(beerList)); +}; + +const toggleSavedListItem = (id: string, savedList: Array, beerList: Array) => { + const isSaved = savedList.some((brewery) => brewery.id === id); + let updatedSavedList: Beer[]; + + if (isSaved) { + updatedSavedList = savedList.filter((brewery) => brewery.id !== id); + } else { + const selectedBrewery = beerList.find((brewery) => brewery.id === id); + updatedSavedList = selectedBrewery ? [...savedList, selectedBrewery] : savedList; + } + + return updatedSavedList; + }; + + +export { getSavedBeer, getSavedBeerList, saveBeerList, toggleSavedListItem }; diff --git a/BeerApp - Senior/src/types/apiCall.d.ts b/BeerApp - Senior/src/types/apiCall.d.ts index 7976a75..e98fab1 100644 --- a/BeerApp - Senior/src/types/apiCall.d.ts +++ b/BeerApp - Senior/src/types/apiCall.d.ts @@ -1,9 +1,9 @@ import { TYPE, SORT } from './'; interface ApiParams { - per_page?: number; // Int between 1 and 200. Default is 50. + per_page?: number | string; // Int between 1 and 200. Default is 50. page?: number; - sort?: SORT; // Not working with by_dist. + sort?: string //name,type:ASC/DESC by_city?: string; by_dist?: string; // `${latitude as Number}, ${longitude as Number}` by_name?: string; diff --git a/BeerApp - Senior/src/types/beer.d.ts b/BeerApp - Senior/src/types/beer.d.ts index cc19882..19bea5d 100644 --- a/BeerApp - Senior/src/types/beer.d.ts +++ b/BeerApp - Senior/src/types/beer.d.ts @@ -11,8 +11,8 @@ interface Beer { state_province: string; postal_code: string; country: string; - longitude: string; - latitude: string; + longitude: number; + latitude: number; phone: string; website_url: string; state: string; diff --git a/BeerApp - Senior/src/views/Beer/Beer.css b/BeerApp - Senior/src/views/Beer/Beer.css new file mode 100644 index 0000000..221ad19 --- /dev/null +++ b/BeerApp - Senior/src/views/Beer/Beer.css @@ -0,0 +1,7 @@ +.map-container { + height:400px; + width: 100%; + min-width:400px; + margin-top: 20px; + background-color: grey; +} \ No newline at end of file diff --git a/BeerApp - Senior/src/views/Beer/index.tsx b/BeerApp - Senior/src/views/Beer/index.tsx index a772ab0..62663fc 100644 --- a/BeerApp - Senior/src/views/Beer/index.tsx +++ b/BeerApp - Senior/src/views/Beer/index.tsx @@ -1,28 +1,98 @@ import { useEffect, useState } from 'react'; import { Beer as IBeer } from '../../types'; -import { fetchData } from './utils'; +import { fetchBeerData, useMuiStyles } from './utils'; import { useParams } from 'react-router-dom'; +import Paper from '@mui/material/Paper'; +import Typography from '@mui/material/Typography'; +import { GoogleMap, useLoadScript } from '@react-google-maps/api'; +import { MarkerF } from '@react-google-maps/api' +import "./Beer.css"; +import { getSavedBeer, getSavedBeerList, saveBeerList, toggleSavedListItem } from '../../api/localStorageApi/beerLocalStorage'; +import { Checkbox } from '@mui/material'; + const Beer = () => { - const { id } = useParams(); - const [beer, setBeer] = useState(); + const { id } = useParams() as { id: string }; + const [ beer, setBeer ] = useState(); + const [savedList, setSavedList] = useState>([]); + const { classes } = useMuiStyles(); + + const { isLoaded } = useLoadScript({ + googleMapsApiKey: process.env.REACT_APP_GOOGLE_API_KEY || '', + }); + + useEffect(() => { + setSavedList(getSavedBeerList()); + }, []); + + useEffect(() => { + if (navigator.onLine) { + fetchBeerData(setBeer, id) + } else { + setBeer(getSavedBeer(id)) + } + }, [id]); - // eslint-disable-next-line - useEffect(fetchData.bind(this, setBeer, id), [id]); + const handleToggleFavorite = (id: string) => { + const updatedSavedList = toggleSavedListItem(id, savedList, [beer as IBeer]); + saveBeerList(updatedSavedList); + setSavedList(updatedSavedList); + }; return ( -
-
-
-

{beer?.name}

-
-
- - Type: {beer?.brewery_type} - -
-
-
+ + {!beer && (
Loading ...
)} + {beer && (
+
+ + fav.id === id)} + onChange={() => handleToggleFavorite(id)} /> + id: {beer?.id} + + + {beer?.name} + + + Type: {beer?.brewery_type} + + Website: + + { beer?.website_url + ? ({beer?.website_url}) + : ('No website available') + } + + Phone: + + { beer?.phone + ? ({beer?.phone}) + : ('No phone available') + } + + Location: + + {beer?.street}, {beer?.postal_code} +
{beer?.city}, {beer?.state} +
{beer?.country} +
+ {beer?.latitude && beer?.longitude && ( +
+ {!isLoaded ? ( +

Map is Loading...

+ ) : ( + + + + )} +
+ )} +
+
)} +
); }; diff --git a/BeerApp - Senior/src/views/Beer/utils.ts b/BeerApp - Senior/src/views/Beer/utils.ts index 8a57ec9..5a7f145 100644 --- a/BeerApp - Senior/src/views/Beer/utils.ts +++ b/BeerApp - Senior/src/views/Beer/utils.ts @@ -1,13 +1,19 @@ import { getBeer } from '../../api'; import { Beer } from '../../types'; import handle from '../../utils/error'; +import { makeStyles } from 'tss-react/mui'; -const fetchData = (setData: (data: Beer) => void, id?: string) => { +const fetchBeerData = (setData: (data: Beer) => void, id?: string) => { if (!id) return; (async () => { try { const response = await getBeer(id); + console.log(response.data); + const latitude = parseFloat(response.data.latitude) || undefined; + const longitude = parseFloat(response.data.longitude) || undefined; + response.data.latitude = latitude; + response.data.longitude = longitude; setData(response.data); } catch (error) { handle(error); @@ -15,4 +21,27 @@ const fetchData = (setData: (data: Beer) => void, id?: string) => { })(); }; -export { fetchData }; +const useMuiStyles = makeStyles()({ + root: { + margin: 'auto', + padding: '20px', + }, + title: { + fontWeight: 'bold', + fontSize: '3rem', + marginTop: '10px' + }, + subtitle: { + fontSize: '2rem', + marginBottom: '10px' + }, + subheading: { + fontSize: '1.75rem', + marginTop: '20px' + }, + cardContent: { + display: 'flex' + } +}); + +export { fetchBeerData, useMuiStyles }; diff --git a/BeerApp - Senior/src/views/BeerList/index.tsx b/BeerApp - Senior/src/views/BeerList/index.tsx index 07dfd6f..fb52a15 100644 --- a/BeerApp - Senior/src/views/BeerList/index.tsx +++ b/BeerApp - Senior/src/views/BeerList/index.tsx @@ -1,19 +1,107 @@ import { useEffect, useState } from 'react'; -import { Beer } from '../../types'; +import { ApiParams, Beer, TYPE } from '../../types'; import { fetchData } from './utils'; -import { Avatar, List, ListItemAvatar, ListItemButton, ListItemText } from '@mui/material'; +import { + Avatar, + Checkbox, + List, + TextField, + ListItemAvatar, + ListItemButton, + ListItemText, + FormControl, + InputLabel, + Select, + MenuItem, + FormControlLabel, + Switch, + Grid, + Pagination + } from '@mui/material'; import SportsBar from '@mui/icons-material/SportsBar'; import { useNavigate } from 'react-router-dom'; +import { getSavedBeerList, saveBeerList, toggleSavedListItem } from '../../api/localStorageApi/beerLocalStorage'; + + +const breweryTypes = [ + 'ALL', + 'micro', + 'nano', + 'regional', + 'brewpub', + 'large', + 'planning', + 'bar', + 'contract', + 'proprietor', + 'closed' +] + +const perPageOptions = [ + '10', + '20', + '50', + '100', + '200' +] + const BeerList = () => { const navigate = useNavigate(); const [beerList, setBeerList] = useState>([]); + const [savedList, setSavedList] = useState>([]); + const [name, setName] = useState(''); + const [type, setType] = useState(breweryTypes[0]); + const [sortAsc, setSortAsc] = useState(true); + const [page, setPage] = useState(1); + const [perPage, setPerPage] = useState(perPageOptions[0]); + const [totalPages, setTotalPages] = useState(10); + const [ savedOnly, setSavedOnly] = useState(false); - // eslint-disable-next-line - useEffect(fetchData.bind(this, setBeerList), []); + const handleChangePage = (event:any, value:any) => { + setPage(value); + }; + + const handleToggleFavorite = (id: string) => { + const updatedSavedList = toggleSavedListItem(id, savedList, beerList); + saveBeerList(updatedSavedList); + setSavedList(updatedSavedList); + }; const onBeerClick = (id: string) => navigate(`/beer/${id}`); + // eslint-disable-next-line + useEffect(() => { + const params = { + per_page: perPage, + page: page, + sort: `type,name:${(sortAsc ? 'asc' : 'desc')}`, + by_name: name + } as ApiParams; + if(type !== 'ALL') params.by_type = type as TYPE; + + fetchData( + setBeerList, + setTotalPages, + params + ) + }, [name, type, perPage, sortAsc, page, savedOnly]); + + useEffect(() => { + setSavedList(getSavedBeerList()) + window.addEventListener('online', setSavedOnly.bind(this, false)); + if (navigator.onLine) { + window.addEventListener('offline', () => { + setSavedOnly(true); + setBeerList(getSavedBeerList()) + }); + } else { + setSavedOnly(true); + setBeerList(getSavedBeerList()) + + } + }, []); + return (
@@ -21,18 +109,87 @@ const BeerList = () => {

BeerList page

+ + + {! savedOnly && (<> + + setName(e.target.value)} + fullWidth + /> + + + + Type + + + + + + Per Page + + + + + + setSortAsc(e.target.checked)} + name="sort" + color="primary" + /> + } + label="Sort Ascending" + /> + + + + + )} + {beerList.map((beer) => ( - + + fav.id === beer.id)} + onChange={() => handleToggleFavorite(beer.id)} /> - + ))} + + {! savedOnly && (<> + + + + )} + + +
diff --git a/BeerApp - Senior/src/views/BeerList/utils.ts b/BeerApp - Senior/src/views/BeerList/utils.ts index 49d0cd5..b35c547 100644 --- a/BeerApp - Senior/src/views/BeerList/utils.ts +++ b/BeerApp - Senior/src/views/BeerList/utils.ts @@ -1,12 +1,21 @@ -import { getBeerList } from '../../api'; -import { Beer } from '../../types'; +import { getBeerList, getBeerMetaData } from '../../api'; +import { ApiParams, Beer } from '../../types'; import handle from '../../utils/error'; -const fetchData = (setData: (data: Array) => void) => { +const fetchData = ( + setData: (data: Array) => void, + setTotalPages: (setTotalPages: number) => void, + params: ApiParams) => { (async () => { try { - const response = await getBeerList(); + const [response, metadata] = + await Promise.all([ + getBeerList(params), + getBeerMetaData(params) + ]); + setData(response.data); + setTotalPages(Math.ceil(metadata.data.total / metadata.data.per_page)); } catch (error) { handle(error); } diff --git a/BeerApp - Senior/src/views/Home/index.tsx b/BeerApp - Senior/src/views/Home/index.tsx index 2adc6df..a6489ea 100644 --- a/BeerApp - Senior/src/views/Home/index.tsx +++ b/BeerApp - Senior/src/views/Home/index.tsx @@ -1,16 +1,42 @@ import { useEffect, useState } from 'react'; -import { fetchData } from './utils'; +import { fetchData, searchBreweries } from './utils'; import { Beer } from '../../types'; import { Link as RouterLink } from 'react-router-dom'; import { Button, Checkbox, Paper, TextField, Link } from '@mui/material'; import styles from './Home.module.css'; +import { getSavedBeerList, saveBeerList, toggleSavedListItem } from '../../api/localStorageApi/beerLocalStorage'; const Home = () => { const [beerList, setBeerList] = useState>([]); const [savedList, setSavedList] = useState>([]); + const [search, setSearch] = useState(''); // eslint-disable-next-line useEffect(fetchData.bind(this, setBeerList), []); + useEffect(() => { + setSavedList(getSavedBeerList()) + }, []); + + const refreshList = () => { + fetchData(setBeerList); + }; + + + const handleToggleFavorite = (id: string) => { + const updatedSavedList = toggleSavedListItem(id, savedList, beerList); + saveBeerList(updatedSavedList); + setSavedList(updatedSavedList); + }; + + const handleSearchBreweries = (event: React.ChangeEvent) => { + setSearch(event.target.value); + searchBreweries(setBeerList, search); + } + + const handleClearFavorits = () => { + saveBeerList([]); + setSavedList([]); + } return (
@@ -19,13 +45,15 @@ const Home = () => {
- - + +
    {beerList.map((beer, index) => ( -
  • - +
  • + fav.id === beer.id)} + onChange={() => handleToggleFavorite(beer.id)} /> {beer.name} @@ -39,14 +67,18 @@ const Home = () => {

    Saved items

    -
      {savedList.map((beer, index) => ( -
    • - +
    • + handleToggleFavorite(beer.id)}/> {beer.name} diff --git a/BeerApp - Senior/src/views/Home/utils.ts b/BeerApp - Senior/src/views/Home/utils.ts index ef99082..755b3a1 100644 --- a/BeerApp - Senior/src/views/Home/utils.ts +++ b/BeerApp - Senior/src/views/Home/utils.ts @@ -1,11 +1,23 @@ -import { getRandomBeerList } from '../../api'; +import { getRandomBeerList, searchBeerList } from '../../api'; import { Beer } from '../../types'; import handle from '../../utils/error'; const fetchData = (setData: (data: Array) => void) => { (async () => { try { - const { data } = await getRandomBeerList(10); + const randomNumber = Math.round(10 + Math.random() * 40); + const { data } = await getRandomBeerList(randomNumber); + setData(data.slice(0,10)); + } catch (error) { + handle(error); + } + })(); +}; + +const searchBreweries = (setData: (data: Array) => void, query: string) => { + (async () => { + try { + const { data } = await searchBeerList(query); setData(data); } catch (error) { handle(error); @@ -13,4 +25,4 @@ const fetchData = (setData: (data: Array) => void) => { })(); }; -export { fetchData }; +export { fetchData, searchBreweries }; diff --git a/BeerApp - Senior/src/views/Offline/index.tsx b/BeerApp - Senior/src/views/Offline/index.tsx index dd3d575..64a5359 100644 --- a/BeerApp - Senior/src/views/Offline/index.tsx +++ b/BeerApp - Senior/src/views/Offline/index.tsx @@ -1,3 +1,4 @@ +import { OfflineBolt, PortableWifiOffOutlined, SignalWifiOffOutlined, SportsBar, WifiOff } from '@mui/icons-material'; import { useEffect, useState } from 'react'; const Offline = () => { @@ -9,6 +10,11 @@ const Offline = () => { useEffect(() => { window.addEventListener('online', setOnline); window.addEventListener('offline', setOffline); + if (navigator.onLine) { + setOnline(); + } else { + setOffline(); + } return () => { window.addEventListener('online', setOnline); @@ -20,10 +26,10 @@ const Offline = () => {
      -

      You are offline

      +

      You are offline

      - App needs internet to start working +

      Only Saved items are availble