├── .babelrc ├── .github ├── ISSUE_TEMPLATE │ └── bug_report.md └── workflows │ ├── deploy.yml │ └── revoke-firebase-token.yml ├── .gitignore ├── .nvmrc ├── .prettierignore ├── .prettierrc.yml ├── .vscode └── settings.json ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── dist └── favicon.svg ├── package-lock.json ├── package.json ├── public ├── data │ ├── ladies_outerwear.json │ ├── ladies_tshirts.json │ ├── mens_outerwear.json │ ├── mens_tshirts.json │ ├── sample_error_response.json │ └── sample_success_response.json ├── favicon.svg ├── icon.png ├── images │ ├── categories │ │ ├── ladies_outerwear.jpg │ │ ├── ladies_tshirts.jpg │ │ ├── mens_outerwear.jpg │ │ └── mens_tshirts.jpg │ ├── icons │ │ ├── shop-icon-128.png │ │ ├── shop-icon-192.png │ │ ├── shop-icon-32.png │ │ ├── shop-icon-384.png │ │ └── shop-icon-512.png │ └── shirts │ │ ├── 10-11017A.jpg │ │ ├── 10-11017B.jpg │ │ ├── 10-11019A.jpg │ │ ├── 10-11019B.jpg │ │ ├── 10-13018A.jpg │ │ ├── 10-13018B.jpg │ │ ├── 10-13058A.jpg │ │ ├── 10-13058B.jpg │ │ ├── 10-13097A.jpg │ │ ├── 10-13097B.jpg │ │ ├── 10-13130A.jpg │ │ ├── 10-13130B.jpg │ │ ├── 10-13153A.jpg │ │ ├── 10-13153B.jpg │ │ ├── 10-13213A.jpg │ │ ├── 10-13213B.jpg │ │ ├── 10-13239A.jpg │ │ ├── 10-13239B.jpg │ │ ├── 10-13240A.jpg │ │ ├── 10-13240B.jpg │ │ ├── 10-13241A.jpg │ │ ├── 10-13241B.jpg │ │ ├── 10-13256A.jpg │ │ ├── 10-13256B.jpg │ │ ├── 10-13260A.jpg │ │ ├── 10-13260B.jpg │ │ ├── 10-13262A.jpg │ │ ├── 10-13262B.jpg │ │ ├── 10-13263A.jpg │ │ ├── 10-13263B.jpg │ │ ├── 10-13264A.jpg │ │ ├── 10-13264B.jpg │ │ ├── 10-13265A.jpg │ │ ├── 10-13265B.jpg │ │ ├── 10-13266A.jpg │ │ ├── 10-13266B.jpg │ │ ├── 10-13267A.jpg │ │ ├── 10-13267B.jpg │ │ ├── 10-13268A.jpg │ │ ├── 10-13268B.jpg │ │ ├── 10-13269A.jpg │ │ ├── 10-13269B.jpg │ │ ├── 10-13270A.jpg │ │ ├── 10-13270B.jpg │ │ ├── 10-13271A.jpg │ │ ├── 10-13271B.jpg │ │ ├── 10-13272A.jpg │ │ ├── 10-13272B.jpg │ │ ├── 10-13273A.jpg │ │ ├── 10-13273B.jpg │ │ ├── 10-13274A.jpg │ │ ├── 10-13274B.jpg │ │ ├── 10-13275A.jpg │ │ ├── 10-13275B.jpg │ │ ├── 10-13276A.jpg │ │ ├── 10-13276B.jpg │ │ ├── 10-13277A.jpg │ │ ├── 10-13277B.jpg │ │ ├── 10-13278A.jpg │ │ ├── 10-13278B.jpg │ │ ├── 10-13279A.jpg │ │ ├── 10-13279B.jpg │ │ ├── 10-13280A.jpg │ │ ├── 10-13280B.jpg │ │ ├── 10-13282A.jpg │ │ ├── 10-13282B.jpg │ │ ├── 10-13285A.jpg │ │ ├── 10-13285B.jpg │ │ ├── 10-13286A.jpg │ │ ├── 10-13286B.jpg │ │ ├── 10-13288A.jpg │ │ ├── 10-13288B.jpg │ │ ├── 10-13289A.jpg │ │ ├── 10-13289B.jpg │ │ ├── 10-13290A.jpg │ │ ├── 10-13290B.jpg │ │ ├── 10-13291A.jpg │ │ ├── 10-13291B.jpg │ │ ├── 10-13292A.jpg │ │ ├── 10-13292B.jpg │ │ ├── 10-14133A.jpg │ │ ├── 10-14133B.jpg │ │ ├── 10-14146A.jpg │ │ ├── 10-14146B.jpg │ │ ├── 10-14152A.jpg │ │ ├── 10-14152B.jpg │ │ ├── 10-14153A.jpg │ │ ├── 10-14153B.jpg │ │ ├── 10-14154A.jpg │ │ ├── 10-14154B.jpg │ │ ├── 10-14155A.jpg │ │ ├── 10-14155B.jpg │ │ ├── 10-14157A.jpg │ │ ├── 10-14157B.jpg │ │ ├── 10-14158A.jpg │ │ ├── 10-14158B.jpg │ │ ├── 10-14159A.jpg │ │ ├── 10-14159B.jpg │ │ ├── 10-14160A.jpg │ │ ├── 10-14160B.jpg │ │ ├── 10-14215A.jpg │ │ ├── 10-14215B.jpg │ │ ├── 10-14216A.jpg │ │ ├── 10-14216B.jpg │ │ ├── 10-14217A.jpg │ │ ├── 10-14217B.jpg │ │ ├── 10-15041A.jpg │ │ ├── 10-15041B.jpg │ │ ├── 10-15068A.jpg │ │ ├── 10-15068B.jpg │ │ ├── 10-15103A.jpg │ │ ├── 10-15103B.jpg │ │ ├── 10-23069A.jpg │ │ ├── 10-23069B.jpg │ │ ├── 10-23073A.jpg │ │ ├── 10-23073B.jpg │ │ ├── 10-23169A.jpg │ │ ├── 10-23169B.jpg │ │ ├── 10-23171A.jpg │ │ ├── 10-23171B.jpg │ │ ├── 10-23172A.jpg │ │ ├── 10-23172B.jpg │ │ ├── 10-23173A.jpg │ │ ├── 10-23173B.jpg │ │ ├── 10-23174A.jpg │ │ ├── 10-23174B.jpg │ │ ├── 10-23176A.jpg │ │ ├── 10-23176B.jpg │ │ ├── 10-23177A.jpg │ │ ├── 10-23177B.jpg │ │ ├── 10-23178A.jpg │ │ ├── 10-23178B.jpg │ │ ├── 10-23179A.jpg │ │ ├── 10-23179B.jpg │ │ ├── 10-23180A.jpg │ │ ├── 10-23180B.jpg │ │ ├── 10-23198A.jpg │ │ ├── 10-23198B.jpg │ │ ├── 10-23225A.jpg │ │ ├── 10-23225B.jpg │ │ ├── 10-23226A.jpg │ │ ├── 10-23226B.jpg │ │ ├── 10-23227A.jpg │ │ ├── 10-23227B.jpg │ │ ├── 10-23228A.jpg │ │ ├── 10-23228B.jpg │ │ ├── 10-23229A.jpg │ │ ├── 10-23229B.jpg │ │ ├── 10-23230A.jpg │ │ ├── 10-23230B.jpg │ │ ├── 10-24097A.jpg │ │ ├── 10-24097B.jpg │ │ ├── 10-24098A.jpg │ │ ├── 10-24098B.jpg │ │ ├── 10-24099A.jpg │ │ ├── 10-24099B.jpg │ │ ├── 10-24101A.jpg │ │ ├── 10-24101B.jpg │ │ ├── 10-24102A.jpg │ │ ├── 10-24102B.jpg │ │ ├── 10-25058A.jpg │ │ └── 10-25058B.jpg ├── logo192.png ├── logo512.png ├── manifest.json └── robots.txt ├── src ├── App.css ├── App.tsx ├── Store │ ├── Cart.css │ ├── Cart.tsx │ ├── CartContext.tsx │ ├── CartItem.tsx │ ├── Category.css │ ├── Category.tsx │ ├── Checkout.css │ ├── Checkout.tsx │ ├── Confirmation.tsx │ ├── GooglePay.tsx │ ├── Header.css │ ├── Header.tsx │ ├── Home.css │ ├── Home.tsx │ ├── ItemDetails.css │ ├── ItemDetails.tsx │ ├── List.css │ ├── List.tsx │ ├── ListItem.tsx │ ├── Status.css │ ├── StorageProvider.tsx │ ├── StoreData.tsx │ └── StoreService.tsx ├── config │ ├── CategoryDetails.tsx │ ├── GooglePay.tsx │ └── ShippingOptions.tsx ├── index.css ├── index.ejs ├── index.tsx └── interfaces │ ├── CartItemDetails.tsx │ ├── CategoryDetails.tsx │ └── ItemDetails.tsx ├── tsconfig.json └── webpack.config.cjs /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/env", 4 | "@babel/preset-react" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: deploy 2 | 3 | on: 4 | push: 5 | branches: 6 | - google-pay 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - uses: actions/checkout@v2 14 | - name: Use Node.js 10.x 15 | uses: actions/setup-node@v1 16 | with: 17 | node-version: 10.x 18 | - run: npm install --global firebase-tools 19 | - run: npm install 20 | - run: firebase deploy --project ${{secrets.FIREBASE_PROJECT}} 21 | env: 22 | FIREBASE_TOKEN: ${{secrets.FIREBASE_TOKEN}} 23 | -------------------------------------------------------------------------------- /.github/workflows/revoke-firebase-token.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2024 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | name: Revoke Firebase Token 16 | 17 | on: 18 | workflow_dispatch: 19 | 20 | jobs: 21 | build: 22 | runs-on: ubuntu-latest 23 | steps: 24 | - name: Revoke Firebase Token 25 | run: curl -H "Content-type:application/x-www-form-urlencoded" -s https://accounts.google.com/o/oauth2/revoke?token=${{secrets.FIREBASE_TOKEN}} 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | /node_modules 3 | /.pnp 4 | .pnp.js 5 | 6 | # Testing 7 | /coverage 8 | 9 | # Build 10 | /dist/bundle.js 11 | /dist/index.html 12 | 13 | # Misc 14 | .DS_Store 15 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | lts/* 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .github/ 2 | .vscode/ 3 | node_modules/ 4 | build/ 5 | dist/ 6 | -------------------------------------------------------------------------------- /.prettierrc.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | arrowParens: avoid 16 | printWidth: 120 17 | quoteProps: consistent 18 | semi: true 19 | singleQuote: true 20 | tabWidth: 2 21 | trailingComma: none 22 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.codeActionsOnSave": { 3 | "source.fixAll.eslint": true 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | We'd love to accept your patches and contributions to this project. 4 | 5 | ## Before you begin 6 | 7 | ### Sign our Contributor License Agreement 8 | 9 | Contributions to this project must be accompanied by a 10 | [Contributor License Agreement](https://cla.developers.google.com/about) (CLA). 11 | You (or your employer) retain the copyright to your contribution; this simply 12 | gives us permission to use and redistribute your contributions as part of the 13 | project. 14 | 15 | If you or your current employer have already signed the Google CLA (even if it 16 | was for a different project), you probably don't need to do it again. 17 | 18 | Visit to see your current agreements or to 19 | sign a new one. 20 | 21 | ### Review our Community Guidelines 22 | 23 | This project follows [Google's Open Source Community 24 | Guidelines](https://opensource.google/conduct/). 25 | 26 | ## Contribution process 27 | 28 | ### Code Reviews 29 | 30 | All submissions, including submissions by project members, require review. We 31 | use GitHub pull requests for this purpose. Consult 32 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more 33 | information on using pull requests. 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sample React T-Shirt Store 2 | 3 | The purpose this repository is to showcase the Google Pay integration in a store 4 | built with React. 5 | 6 | A deployed version of this app can be found at this link. 7 | 8 | **Note:** This sample app is not intended for use in a production environment! 9 | Please do not attempt to use it in this manner. For more information on 10 | deploying apps to production, see the 11 | [React documentation](https://create-react-app.dev/docs/deployment/). 12 | 13 | ## Prerequisites 14 | 15 | To configure and run this sample app, you will need the following prerequisites 16 | installed on your workstation: 17 | 18 | * [Node.js and NPM](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) 19 | * Any prerequisites included in the 20 | [React Documentation](https://reactjs.org/docs/getting-started.html) 21 | 22 | ## Setup 23 | 24 | Once the prerequisites are installed, follow the below steps to set up and use 25 | this sample app: 26 | 27 | 1. Clone this repository 28 | 1. In your terminal, navigate to the repository location 29 | 30 | ```bash 31 | cd /path/to/repo 32 | ``` 33 | 34 | 1. Install the Node.js dependencies 35 | 36 | ```bash 37 | npm install . 38 | ``` 39 | 40 | 1. Build `index.html` and `bundle.js` 41 | 42 | ```bash 43 | npm run build 44 | ``` 45 | 46 | ## Usage 47 | 48 | ### Development server 49 | 50 | To start a development server, follow the below steps: 51 | 52 | 1. In your terminal, run `npm start` 53 | 1. In your web browser, open `http://localhost:3000/` 54 | 55 | The app will automatically reload if you change any of the source files. You 56 | will also see any lint errors in the console. 57 | 58 | ### Build 59 | 60 | This command builds the app and outputs the results to the `dist/` directory. 61 | It bundles React in production mode and optimizes the build for the best 62 | performance. The build is minified and the filenames include the hashes. 63 | 64 | 1. In your terminal, run `npm run build` 65 | 66 | ## Support 67 | 68 | For more information about React, refer to the 69 | [React documentation](https://reactjs.org/). 70 | 71 | For any questions on this sample app, please 72 | [submit an issue](https://github.com/google-pay/angular-store/issues/new) to 73 | this repository. 74 | -------------------------------------------------------------------------------- /dist/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-store", 3 | "version": "0.1.0", 4 | "description": "A sample React app that implements Google Pay.", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "webpack-dev-server --mode development --hot", 8 | "build": "webpack --mode development" 9 | }, 10 | "author": "Nick Alteen", 11 | "devDependencies": { 12 | "@babel/cli": "^7.19.3", 13 | "@babel/core": "^7.20.5", 14 | "@babel/preset-env": "^7.20.2", 15 | "@babel/preset-react": "^7.18.6", 16 | "@testing-library/jest-dom": "^5.16.5", 17 | "@testing-library/react": "^13.4.0", 18 | "@testing-library/user-event": "^14.4.3", 19 | "babel-loader": "^9.1.0", 20 | "css-loader": "^6.7.2", 21 | "ejs-loader": "^0.5.0", 22 | "html-webpack-plugin": "^5.5.0", 23 | "react-refresh": "^0.14.0", 24 | "style-loader": "^3.3.1", 25 | "ts-loader": "^9.4.2", 26 | "typescript": "^4.9.4", 27 | "webpack": "^5.75.0", 28 | "webpack-cli": "^5.0.1", 29 | "webpack-dev-server": "^4.11.1" 30 | }, 31 | "dependencies": { 32 | "@emotion/react": "^11.10.5", 33 | "@emotion/styled": "^11.10.5", 34 | "@google-pay/button-react": "^3.0.5", 35 | "@mui/icons-material": "^5.10.16", 36 | "@mui/material": "^5.10.17", 37 | "@types/googlepay": "^0.6.4", 38 | "@types/jest": "^29.2.4", 39 | "@types/node": "^18.11.13", 40 | "@types/react": "^18.0.26", 41 | "@types/react-dom": "^18.0.9", 42 | "dotenv": "^16.0.3", 43 | "handlebars-loader": "^1.7.3", 44 | "query-string": "^8.0.0", 45 | "react": "^18.2.0", 46 | "react-dom": "^18.2.0", 47 | "react-router-dom": "^6.4.5" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /public/data/ladies_outerwear.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name":"Ladies+Modern+Stretch+Full+Zip", 4 | "title":"Ladies Modern Stretch Full Zip", 5 | "category":"ladies_outerwear", 6 | "price":41.60, 7 | "description":"With an updated fit and figure-flattering details, this full-zip combines ultra soft cotton with a dash of spandex to retain its shape all day long.&nbsp;<div><br></div><div>Features:</div><div><ul><li>96% cotton, 4% spandex.</li><li>Gently contoured silhouette &amp; longer length design for a style that moves with you.</li><li>Self-fabric hood.</li><li>Dyed-to-match zipper.&nbsp;</li><li>Front slash pockets.</li><li>Open cuffs &amp; hem.</li><li>Available in Mosaic Blue with the white Google logo embroidered at left chest.&nbsp;</li></ul></div>", 8 | "image":"/images/shirts/10-24102B.jpg", 9 | "largeImage":"/images/shirts/10-24102A.jpg" 10 | }, 11 | { 12 | "name":"Ladies+Colorblock+Wind+Jacket", 13 | "title":"Ladies Colorblock Wind Jacket", 14 | "category":"ladies_outerwear", 15 | "price":45.90, 16 | "description":"Brighten up your commute on gloomy days. This lightweight jacket features a subtle grid texture and a punch of bright pink at each side panel.<div><br></div><div>Features:</div><div><ul><li>100% polyester dobby shell with jersey lining.</li><li>Packable zip-in hood with contrast pink zipper.</li><li>Cadet collar and elastic cuffs.</li><li>Adjustable toggles at waist can be cinched for a flattering fit.</li><li>Available in grey/dark rose with the white Google logo embroidered at left chest.</li></ul></div>", 17 | "image":"/images/shirts/10-25058B.jpg", 18 | "largeImage":"/images/shirts/10-25058A.jpg" 19 | }, 20 | { 21 | "name":"Ladies+Voyage+Fleece+Jacket", 22 | "title":"Ladies Voyage Fleece Jacket", 23 | "category":"ladies_outerwear", 24 | "price":48.00, 25 | "description":"<div>Perhaps the equivalent to that comfort blanket you had years ago is a cozy fleece. This full-zip is the perfect layering piece for those 'in-between' months when mother nature just can't make up her mind.&nbsp;</div><div><br></div><div>Features:</div><div><ul><li>100% polyester anti-pill yarn fleece.</li><li>100% polyester taffeta lining in sleeves.</li><li>Tricot-lined lower pockets with reverse coil zippers.</li><li>Available in purple with the white Google logo embroidered at left chest.</li><li><b>Please note! Sizing runs larger than normal. Consider ordering a size smaller than normal.</b></li></ul></div>", 26 | "image":"/images/shirts/10-24101B.jpg", 27 | "largeImage":"/images/shirts/10-24101A.jpg" 28 | }, 29 | { 30 | "name":"Ladies+Pullover+L+S+Hood", 31 | "title":"Ladies Pullover L/S Hood", 32 | "category":"ladies_outerwear", 33 | "price":36.50, 34 | "description":"A longsleeve layering piece with a hood. What more can you ask for between season changes?&nbsp;<div><br></div><div>Features:</div><div><ul><li>85% polyester, 15% cotton.</li><li>Ultra lightweight, tissue jersey fabric.</li><li>Scoop-neck with hood.</li><li>Available in jewel blue with the white Google logo screenprinted across center chest.</li></ul></div>", 35 | "image":"/images/shirts/10-24098B.jpg", 36 | "largeImage":"/images/shirts/10-24098A.jpg" 37 | }, 38 | { 39 | "name":"Ladies+Sonoma+Hybrid+Knit+Jacket", 40 | "title":"Ladies Sonoma Hybrid Knit Jacket", 41 | "category":"ladies_outerwear", 42 | "price":84.85, 43 | "description":"A modern styled sport jacket that combines a classic silhouette with moisture-wicking fabrics. Technical features include a reversed coil zipper with reflective stripe, interior media exit port, and built-in media pocket.&nbsp;<div><br></div><div>Additional Features:</div><div><ul><li>94% polyester, 6% spandex.</li><li>Available in black with the white Google logo heat transferred onto right hip along zipper.</li></ul></div>", 44 | "image":"/images/shirts/10-24097B.jpg", 45 | "largeImage":"/images/shirts/10-24097A.jpg" 46 | }, 47 | { 48 | "name":"Ladies+Yerba+Knit+Quarter+Zip", 49 | "title":"Ladies Yerba Knit Quarter Zip", 50 | "category":"ladies_outerwear", 51 | "price":64.20, 52 | "description":"This on-trend quarter zip doubles as workout gear.&nbsp;<div><br></div><div>Features:</div><div><ul><li>81% polyester, 19% spandex jersey knit.</li><li>Textured knit fabric features a moisture-wicking finish.</li><li>Exposed contrast reverse coil zipper with contrast inner collar.</li><li>Lightweight design with added stretch.</li><li>Available in heathered indigo with the white Google logo heat transferred vertically onto front right hip.</li></ul></div>", 53 | "image":"/images/shirts/10-24099B.jpg", 54 | "largeImage":"/images/shirts/10-24099A.jpg" 55 | } 56 | ] 57 | -------------------------------------------------------------------------------- /public/data/ladies_tshirts.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name":"Ladies+Chrome+T-Shirt", 4 | "title":"Ladies Chrome T-Shirt", 5 | "category":"ladies_tshirts", 6 | "price":13.30, 7 | "description":"The best of three fabrics combined into one seductively-soft tee.&nbsp;<div><br></div><div>Features:</div><div><ul><li>50% polyester, 25% combed and ring-spun cotton, 25% rayon.</li><li>Side-seamed.</li><li>Semi-relaxed fit.&nbsp;</li><li>Available in heather blue with the white Google Chrome logo screenprinted at center chest.&nbsp;</li></ul></div>", 8 | "image":"/images/shirts/10-23180B.jpg", 9 | "largeImage":"/images/shirts/10-23180A.jpg" 10 | }, 11 | { 12 | "name":"Ladies+Google+New+York+T-Shirt", 13 | "title":"Ladies Google New York T-Shirt", 14 | "category":"ladies_tshirts", 15 | "price":18.35, 16 | "description":"Are you feeling lucky? Inspired by city lights in The Big Apple, this tee features the 'I'm Feeling Lucky New York' phrase at back.<div><br></div><div>Features:</div><div><ul><li>100% cotton.</li><li>American Apparel shirt designed with a ladies fit in mind.</li><li>Available in Black with the Google logo and 'I'm Feeling Lucky' New York printed on back yoke in White.</li><li><b>Sizing runs smaller than normal. Please reference size chart before ordering.</b></li></ul></div>", 17 | "image":"/images/shirts/10-23226B.jpg", 18 | "largeImage":"/images/shirts/10-23226A.jpg" 19 | }, 20 | { 21 | "name":"Ladies+Gmail+T-Shirt", 22 | "title":"Ladies Gmail T-Shirt", 23 | "category":"ladies_tshirts", 24 | "price":16.40, 25 | "description":"Show your inbox some love. The new Gmail tee has arrived, complete with a subtle Mvelope design that showcases all of the Gmail icons you use on the daily.<div><br></div><div>Features:</div><div><ul><li>50% polyester, 25% cotton.</li><li>Bella+Canvas.</li><li>Available in vintage red with the new Gmail print screenprinted across center chest.</li></ul></div>", 26 | "image":"/images/shirts/10-23179B.jpg", 27 | "largeImage":"/images/shirts/10-23179A.jpg" 28 | }, 29 | { 30 | "name":"Ladies+G+Logo+White+T-Shirt", 31 | "title":"Ladies G Logo White T-Shirt", 32 | "category":"ladies_tshirts", 33 | "price":13.30, 34 | "description":"There's a new G in town and it's here to stay. Get your hands on this comfy white tee with the new Google icon.&nbsp;\n\n<div><br></div><div>Features:&nbsp;</div><div><ul><li>100% combed and ring-spun cotton.</li><li>Side seamed, relaxed fit.</li><li>Bella+Canvas.</li><li>Available in white with the Google 'G' icon screenprinted at front.</li></ul></div>", 35 | "image":"/images/shirts/10-23178B.jpg", 36 | "largeImage":"/images/shirts/10-23178A.jpg" 37 | }, 38 | { 39 | "name":"Ladies+Android+Pride+T-Shirt", 40 | "title":"Ladies Android Pride T-Shirt", 41 | "category":"ladies_tshirts", 42 | "price":19.10, 43 | "description":"Stand out proud in this Ladies' Android Pride T-shirt.&nbsp;<div><br></div><div>Features:</div><div><ul><li>100% cotton.</li><li>Available in black and features two Androids holding hands and waving a rainbow flag printed across the front. Google logo screen printed in white on the sleeve.&nbsp;</li><li><b>Sizing runs smaller than normal. Please reference size chart before ordering.</b></li></ul></div>", 44 | "image":"/images/shirts/10-23177B.jpg", 45 | "largeImage":"/images/shirts/10-23177A.jpg" 46 | }, 47 | { 48 | "name":"Ladies+Ringspun+Crew+Neck", 49 | "title":"Ladies Ringspun Crew Neck", 50 | "category":"ladies_tshirts", 51 | "price":19.70, 52 | "description":"Cheery colors make the world a happier place. This bright pink tee is ultra soft and features a comfortable, ladies fit.<div><br></div><div>Features:</div><div><ul><li>100% cotton.</li><li>Tagless label for added comfort.</li><li><b>Relaxed fit.</b></li><li>Available in hot pink with the white Google logo screenprinted at center chest.</li></ul></div>", 53 | "image":"/images/shirts/10-23172B.jpg", 54 | "largeImage":"/images/shirts/10-23172A.jpg" 55 | }, 56 | { 57 | "name":"Ladies+Tri-Blend+V-Neck+T-Shirt", 58 | "title":"Ladies Tri-Blend V-Neck T-Shirt", 59 | "category":"ladies_tshirts", 60 | "price":35.10, 61 | "description":"A tagless label, ultra soft triblend fabric and v-neck cut are three ingredients for a favorite tee.&nbsp;<div><br></div><div>Features:</div><div><ul><li>25% cotton, 50% polyester, 25% rayon.</li><li>Made in California.</li><li>Available in green with the white Google logo screenprinted at center chest.</li></ul></div>", 62 | "image":"/images/shirts/10-23227B.jpg", 63 | "largeImage":"/images/shirts/10-23227A.jpg" 64 | }, 65 | { 66 | "name":"Bella+Ladies+Favorite+Tee", 67 | "title":"Bella Ladies Favorite Tee", 68 | "category":"ladies_tshirts", 69 | "price":10.50, 70 | "description":"This ladies tee features a longer body length perfect for layering up.&nbsp;<div><br></div><div>Features:</div><div><ul><li>100% combed, ringspun cotton.</li><li>Extra soft, lightweight fabric.</li><li><b>Slim fit. Runs small.&nbsp;</b></li><li>Available in aqua with the white Google logo screenprinted at center chest.</li></ul></div>", 71 | "image":"/images/shirts/10-23228B.jpg", 72 | "largeImage":"/images/shirts/10-23228A.jpg" 73 | }, 74 | { 75 | "name":"Ladies+Bamboo+T-Shirt", 76 | "title":"Ladies Bamboo T-Shirt", 77 | "category":"ladies_tshirts", 78 | "price":20.65, 79 | "description":"A bamboo tee that's softer than your favorite cotton t-shirt. Your skin will thank you during those long nights of programming.&nbsp;<div><br></div><div>Features:</div><div><ul><li>70% viscose from organic bamboo, 30% organic cotton.</li><li>Available in vintage pink with the white Google logo screen printed at center chest.</li></ul></div>", 80 | "image":"/images/shirts/10-23176B.jpg", 81 | "largeImage":"/images/shirts/10-23176A.jpg" 82 | }, 83 | { 84 | "name":"Ladies+L+S+Colorblock+Raglan", 85 | "title":"Ladies L/S Colorblock Raglan", 86 | "category":"ladies_tshirts", 87 | "price":36.95, 88 | "description":"Add a dose of mango to your t-shirt lineup. This scoop neck raglan features a bright pop of color and a scoop neck with v-notch.<div><br></div><div>Features:</div><div><ul><li>60% cotton, 40% polyester.</li><li>Scoop hem.</li><li>Self-fabric cuff bands.</li><li>Available in heather/mango with the white Google logo screenprinted at center chest.</li></ul></div>", 89 | "image":"/images/shirts/10-23173B.jpg", 90 | "largeImage":"/images/shirts/10-23173A.jpg" 91 | }, 92 | { 93 | "name":"Bella+Scoop-Neck+Ladies+T-Shirt", 94 | "title":"Bella Scoop-Neck Ladies T-Shirt", 95 | "category":"ladies_tshirts", 96 | "price":13.10, 97 | "description":"A classic that's here to stay is this ladies white baby ribbed tee. Features a feminine scoop cut at the neck and 1x1 baby rib texture. Available in white with the full color Google logo screen printed at center chest.", 98 | "image":"/images/shirts/10-23171B.jpg", 99 | "largeImage":"/images/shirts/10-23171A.jpg" 100 | }, 101 | { 102 | "name":"Ladies+Not+For+Sale+T-Shirt", 103 | "title":"Ladies Not For Sale T-Shirt", 104 | "category":"ladies_tshirts", 105 | "price":24.00, 106 | "description":"This Not for Sale t-shirt features just the right amount of 'V' around the neck with the Google logo placed perfectly underneath. Not for Sale focuses efforts on growing social enterprises to benefit those enslaved and vulnerable communities around the world.&nbsp;<div><br></div><div>Features:</div><div><ul><li>Available in black with the Google logo imprinted in white across upper chest.</li><li><b>Sizing runs smaller than normal. Please consider ordering up one or two sizes.</b></li></ul></div>", 107 | "image":"/images/shirts/10-23225B.jpg", 108 | "largeImage":"/images/shirts/10-23225A.jpg" 109 | }, 110 | { 111 | "name":"Ladies+Android+L+S+Stretch+T-Shirt", 112 | "title":"Ladies Android L/S Stretch T-Shirt", 113 | "category":"ladies_tshirts", 114 | "price":20.00, 115 | "description":"Sparkle and shine in this ladies long sleeve stretch tee.&nbsp;<div><br></div><div>Features;</div><div><ul><li>95% premium, ring-spun cotton, 5% spandex.</li><li>Available in Black with a glitter Android robot at left chest.</li></ul></div>", 116 | "image":"/images/shirts/10-23198B.jpg", 117 | "largeImage":"/images/shirts/10-23198A.jpg" 118 | }, 119 | { 120 | "name":"Ladies+Mountain+View+T-Shirt", 121 | "title":"Ladies Mountain View T-Shirt", 122 | "category":"ladies_tshirts", 123 | "price":17.50, 124 | "description":"The Bay Area city named for its beautiful views of the Santa Cruz Mountains is also home to the Googleplex located at 1600 Amphitheater Parkway. Celebrate the place Google calls home in this ladies scoop neck tee.<div><br></div><div>Features:</div><div><ul><li>100% cotton.</li><li>Available in white with the Mountain View coordinates screenprinted at front and the full color Google logo screenprinted at back yoke.</li></ul></div>", 125 | "image":"/images/shirts/10-23229B.jpg", 126 | "largeImage":"/images/shirts/10-23229A.jpg" 127 | }, 128 | { 129 | "name":"Ladies+Blueprint+for+a+Better+Inbox+T-Shirt", 130 | "title":"Ladies Blueprint for a Better Inbox T-Shirt", 131 | "category":"ladies_tshirts", 132 | "price":14.30, 133 | "description":"The \"Blueprint for better Inbox\" now available for the ladies! This USA made American Apparel t-shirt sports a more fitted design and &nbsp;the new Inbox logo.&nbsp;<div><br></div><div>Additional Features:&nbsp;</div><div><ul><li>50% cotton / 50% polyester for a super soft fit.</li><li>Available in royal blue heather with the \"New Inbox: logo screen printed on the center chest.&nbsp;</li><li><b>Sizing runs smaller than normal. Please reference sizing chart prior to ordering.</b></li></ul></div>", 134 | "image":"/images/shirts/10-23169B.jpg", 135 | "largeImage":"/images/shirts/10-23169A.jpg" 136 | }, 137 | { 138 | "name":"Ladies+Cotton+Poly+w++Thermal+Tee", 139 | "title":"Ladies Cotton Poly w/ Thermal Tee", 140 | "category":"ladies_tshirts", 141 | "price":15.15, 142 | "description":"This thermal long sleeve t-shirt is lightweight enough for all seasons of the year.&nbsp;<div><br></div><div>Features:</div><div><ul><li>60% cotton, 40% polyester.</li><li>Wide boat neck, thermal sleeves and hight length thermal cuffs.</li><li>Longer body for comfortable fit.</li><li>Available in blue/black with the white Google logo screenprinted at center chest.</li></ul></div>", 143 | "image":"/images/shirts/10-23174B.jpg", 144 | "largeImage":"/images/shirts/10-23174A.jpg" 145 | }, 146 | { 147 | "name":"Ladies+YouTube+Favorite+Tee", 148 | "title":"Ladies YouTube Favorite Tee", 149 | "category":"ladies_tshirts", 150 | "price":11.10, 151 | "description":"It's called the 'favorite tee' for a reason. Designed with fashion and comfort in mind, this ladies tee is ultra soft and provides a great fit, every time.&nbsp;<div><br></div><div>Features:</div><div><ul><li>100% combed, ring-spun cotton.</li><li>Designed with a longer length for varying body types.&nbsp;</li><li>Available in asphalt with the full color YouTube logo screen printed across center chest.</li></ul></div>", 152 | "image":"/images/shirts/10-23073B.jpg", 153 | "largeImage":"/images/shirts/10-23073A.jpg" 154 | }, 155 | { 156 | "name":"MTV+Ladies+Yellow+T-Shirt", 157 | "title":"MTV Ladies Yellow T-Shirt", 158 | "category":"ladies_tshirts", 159 | "price":16.90, 160 | "description":"They say home is where the heart is. This vibrant tee features the Bay Area address of Google's head office. &nbsp;<div><br></div><div>Features:</div><div><ul><li>100% combed cotton.</li><li>Made in the USA.&nbsp;</li><li>Available in gold with a striking design at front and the white Google logo at back yoke.&nbsp;</li></ul></div>", 161 | "image":"/images/shirts/10-23230B.jpg", 162 | "largeImage":"/images/shirts/10-23230A.jpg" 163 | }, 164 | { 165 | "name":"Women+s+Android+Heart+T-Shirt", 166 | "title":"Women's Android Heart T-Shirt", 167 | "category":"ladies_tshirts", 168 | "price":10.60, 169 | "description":"The softest, smoothest, best-looking, organic cotton tee shirt available anywhere.&nbsp;<div><br></div><div>Features:</div><div><ul><li>100% certified organic, combed, ringspun cotton.</li><li>Contoured to flatter women's curves.</li><li>Double-needle stitching on the sleeves and bottom hem.</li><li>TearAway™ label for added comfort.&nbsp;</li><li>Available in Berry with a screened Android robot at front chest.</li></ul></div>", 170 | "image":"/images/shirts/10-23069B.jpg", 171 | "largeImage":"/images/shirts/10-23069A.jpg" 172 | } 173 | ] 174 | -------------------------------------------------------------------------------- /public/data/mens_outerwear.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name":"Men+s+Tech+Shell+Full-Zip", 4 | "title":"Men's Tech Shell Full-Zip", 5 | "category":"mens_outerwear", 6 | "price":50.20, 7 | "description":"A versatile full-zip that you can wear all day long and even to the gym. This technical shell features moisture-wicking fabric, added stretch and a hidden pocket for your smartphone or media player.&nbsp;<div><br></div><div>Features:</div><div><ul><li>100% polyester.</li><li>Smooth, technical front with textured mesh back.</li><li>Drawstring bottom for adjustable fit.</li><li>Raglan sleeves.</li><li>Available in forest green with the white Google logo embroidered at left chest.</li></ul></div>", 8 | "image":"/images/shirts/10-15068B.jpg", 9 | "largeImage":"/images/shirts/10-15068A.jpg" 10 | }, 11 | { 12 | "name":"Anvil+L+S+Crew+Neck+-+Grey", 13 | "title":"Anvil L/S Crew Neck - Grey", 14 | "category":"mens_outerwear", 15 | "price":22.15, 16 | "description":"You'll be swooning over this crew neck as soon as you feel how soft it is.&nbsp;<div><br></div><div>Features:</div><div><ul><li>40% preshrunk ring-spun cotton, 60% polyester terry fleece.&nbsp;</li><li>Available in dark heather charcoal with the white Google logo screen printed across center chest.</li></ul></div>", 17 | "image":"/images/shirts/10-14154B.jpg", 18 | "largeImage":"/images/shirts/10-14154A.jpg" 19 | }, 20 | { 21 | "name":"Green+Flex+Fleece+Zip+Hoodie", 22 | "title":"Green Flex Fleece Zip Hoodie", 23 | "category":"mens_outerwear", 24 | "price":45.65, 25 | "description":"Ultra soft. Ultra cozy. Our popular flex fleece hoodie now available in speckled green.&nbsp;<div><br></div><div>Features:</div><div><ul><li>50% cotton / 50% polyester.&nbsp;</li><li>Made in the USA.&nbsp;</li><li>Full-zip.&nbsp;</li><li>Available in green with specks of blue and the white Google logo embroidered at left bicep.&nbsp;</li></ul></div>", 26 | "image":"/images/shirts/10-14157B.jpg", 27 | "largeImage":"/images/shirts/10-14157A.jpg" 28 | }, 29 | { 30 | "name":"Android+Nylon+Packable+Jacket", 31 | "title":"Android Nylon Packable Jacket", 32 | "category":"mens_outerwear", 33 | "price":33.60, 34 | "description":"Pack. Pack. Pack it up! This nylon jacket with reflective trim can literally be packed into itself in seconds. Features a waterproof nylon fabric, Android eyes &amp; antennaes on the hood and a carrying strap when jacket is fully packed. Android robot is printed on back above zipper in a reflective, metallic finish.", 35 | "image":"/images/shirts/10-15041B.jpg", 36 | "largeImage":"/images/shirts/10-15041A.jpg" 37 | }, 38 | { 39 | "name":"YouTube+Ultimate+Hooded+Sweatshirt", 40 | "title":"YouTube Ultimate Hooded Sweatshirt", 41 | "category":"mens_outerwear", 42 | "price":32.35, 43 | "description":"Stay warm in this cozy hoodie made of 50% cotton and 50% polyester. This comfortable design features set in sleeves, dyed to match draw cord and a front pouch pocket. Available in Charcoal with the full color YouTube logo screen printed across the chest. Unisex sizing.", 44 | "image":"/images/shirts/10-14133B.jpg", 45 | "largeImage":"/images/shirts/10-14133A.jpg" 46 | }, 47 | { 48 | "name":"Grey+Heather+Fleece+Zip+Hoodie", 49 | "title":"Grey Heather Fleece Zip Hoodie", 50 | "category":"mens_outerwear", 51 | "price":38.85, 52 | "description":"Cozy up with this full-zip hoodie.&nbsp;<div><br></div><div>Features:</div><div><ul><li>60% combed, ring-spun cotton, 40% polyester.</li><li>Unisex sizing.</li><li>Retail fit.</li><li>Contrast zipper.</li><li>Kangaroo pockets with ribbed cuffs and waistband.</li><li>Available in dark heather with the white Google logo embroidered at left chest.</li></ul></div><div><br></div>", 53 | "image":"/images/shirts/10-14160B.jpg", 54 | "largeImage":"/images/shirts/10-14160A.jpg" 55 | }, 56 | { 57 | "name":"Vastrm+Hoodie", 58 | "title":"Vastrm Hoodie", 59 | "category":"mens_outerwear", 60 | "price":200.00, 61 | "description":"The ultimate in fit and fabric, this Vastrm hoodie doesn't disappoint. Made from soft pique fabric, the lightweight full-zip features a halfmoon accent and matching hoodie strings.&nbsp;<div><br></div><div>Additional Features:</div><div><ul><li>100% cotton.</li><li>Hidden phone pocket neatly cradles your digital device.&nbsp;</li><li>Available in charcoal grey with red strings and hood. White Google logo is embroidered at left bicep.</li></ul></div>", 62 | "image":"/images/shirts/10-14153B.jpg", 63 | "largeImage":"/images/shirts/10-14153A.jpg" 64 | }, 65 | { 66 | "name":"Recycled+Plastic+Bottle+Hoodie+-+Green", 67 | "title":"Recycled Plastic Bottle Hoodie - Green", 68 | "category":"mens_outerwear", 69 | "price":60.95, 70 | "description":"Ever wonder where all of the disposable water bottles of the world end up? We know some of them are reused for a second purpose. Each of these hoodies contain approximately 9 recycled water bottles that are woven into the fabric.<div><br></div><div>Features:&nbsp;</div><div><ul><li>50% recycled cotton, 50% recycled polyester.</li><li>Full zipper and orange drawstring pulls.</li><li>USA made.&nbsp;</li><li>&nbsp;Available in forest green with the white Google logo embroidered at left bicep.</li></ul></div>", 71 | "image":"/images/shirts/10-14158B.jpg", 72 | "largeImage":"/images/shirts/10-14158A.jpg" 73 | }, 74 | { 75 | "name":"Rowan+Pullover+Hood", 76 | "title":"Rowan Pullover Hood", 77 | "category":"mens_outerwear", 78 | "price":60.85, 79 | "description":"In search of the perfect layering piece? This lightweight, triblend pullover is ultra soft and ideal for all seasons.<div><br></div><div>Features:</div><div><ul><li>50% polyester, 38% cotton, 12% rayon triblend.</li><li>Available in black with the white Google logo screenprinted at left bicep.</li></ul></div>", 80 | "image":"/images/shirts/10-14152B.jpg", 81 | "largeImage":"/images/shirts/10-14152A.jpg" 82 | }, 83 | { 84 | "name":"Men+s+Voyage+Fleece+Jacket", 85 | "title":"Men's Voyage Fleece Jacket", 86 | "category":"mens_outerwear", 87 | "price":48.00, 88 | "description":"<div>Perhaps the equivalent to that comfort blanket you had years ago is a cozy fleece. This full-zip is the perfect layering piece for those 'in-between' months when mother nature just can't make up her mind.&nbsp;</div><div><br></div><div>Features:</div><div><ul><li>100% polyester anti-pill yarn fleece.</li><li>100% polyester taffeta lining in sleeves.</li><li>Tricot-lined lower pockets with reverse coil zippers.</li><li>Available in black with the white Google logo embroidered at left chest.</li></ul></div>", 89 | "image":"/images/shirts/10-14155B.jpg", 90 | "largeImage":"/images/shirts/10-14155A.jpg" 91 | }, 92 | { 93 | "name":"Eco-Jersey+Chrome+Zip+Up+Hoodie", 94 | "title":"Eco-Jersey Chrome Zip Up Hoodie", 95 | "category":"mens_outerwear", 96 | "price":37.75, 97 | "description":"An exceptionally soft, eco-friendly full-zip for picture-perfect layering.&nbsp;<div><br></div><div>Features:</div><div><ul><li>50% polyester, 38% cotton (6.25% recycled), 12% rayon (6.25% organic).</li><li>Low-impact yarn dyed and washed.</li><li>Brushed nickel zipper with natural taping.</li><li>Split front pouch pocket.&nbsp;</li><li>Available in deep pacific blue with the white Google Chrome logo screenprinted at left chest.&nbsp;</li></ul><div><br></div><div><br></div></div>", 98 | "image":"/images/shirts/10-14159B.jpg", 99 | "largeImage":"/images/shirts/10-14159A.jpg" 100 | }, 101 | { 102 | "name":"Android+Colorblock+Hooded+Pullover", 103 | "title":"Android Colorblock Hooded Pullover", 104 | "category":"mens_outerwear", 105 | "price":50.20, 106 | "description":"This cozy Android hoodie features a sublimated camo design printed inside hood and along inner sleeves and side panels. Moisture-wicking polyester fabric keeps you cool and dry.&nbsp;<div><br></div><div>Features:</div><div><ul><li>100% polyester.</li><li>Ultra soft, fleece interior.&nbsp;</li><li>Available in smoke/lime green with the white Android robot embroidered at left chest.</li></ul></div>", 107 | "image":"/images/shirts/10-14146B.jpg", 108 | "largeImage":"/images/shirts/10-14146A.jpg" 109 | }, 110 | { 111 | "name":"Tri-blend+Full-Zip+Hoodie", 112 | "title":"Tri-blend Full-Zip Hoodie", 113 | "category":"mens_outerwear", 114 | "price":52.20, 115 | "description":"Comfy cool. This canvas tri-blend full-zip hoodie made of a poly/cotton/rayon blend is sure to please the eyes as well as the senses. Made in the USA. Available in Black with the white Google logo embroidered at left chest.", 116 | "image":"/images/shirts/10-14216B.jpg", 117 | "largeImage":"/images/shirts/10-14216A.jpg" 118 | }, 119 | { 120 | "name":"Fleece+Full-Zip+Hoodie", 121 | "title":"Fleece Full-Zip Hoodie", 122 | "category":"mens_outerwear", 123 | "price":45.65, 124 | "description":"If you find that the 'spark' is missing from your outfit, you may need to add one of these full-zip hoodies to the mix. Resurfacing from 1989, this colorful full-zip features a sporty fit with ultra soft fleece lining.<div><br><div>Additional Features:</div><div><ul><li>50% polyester and 50% cotton fleece.&nbsp;</li><li>Available in blue with the Google logo embroidered in white at left bicep.</li></ul></div></div>", 125 | "image":"/images/shirts/10-14215B.jpg", 126 | "largeImage":"/images/shirts/10-14215A.jpg" 127 | }, 128 | { 129 | "name":"Jacquard-Knit+Full-Zip+Fleece", 130 | "title":"Jacquard-Knit Full-Zip Fleece", 131 | "category":"mens_outerwear", 132 | "price":74.90, 133 | "description":"We love color contrast, especially in Google Blue! This textured jacket features a jacquard texture with contrast stitching and zippers.&nbsp;<div><br></div><div>Additional Features:</div><div><ul><li>100% polyester.&nbsp;</li><li>Audio port access available on inside left pocket.</li><li>Available in Carbon/Olympic Blue with the Google logo embroidered in white on left chest.</li></ul></div>", 134 | "image":"/images/shirts/10-14217B.jpg", 135 | "largeImage":"/images/shirts/10-14217A.jpg" 136 | }, 137 | { 138 | "name":"YouTube+Unisex+Flex+Fleece+Zip+Hoodie", 139 | "title":"YouTube Unisex Flex Fleece Zip Hoodie", 140 | "category":"mens_outerwear", 141 | "price":45.25, 142 | "description":"Our popular flex fleece hoodie, now for YouTube fans everywhere.<div><br></div><div>Features:</div><div><ul><li>50% polyester, 50% cotton fleece.</li><li>Sporty, unisex fit.</li><li>Metal zipper.</li><li>Hood.</li><li>Available in dark heather grey with the YouTube logo embroidered at left bicep.</li></ul><div><br></div><div><br></div></div>", 143 | "image":"/images/shirts/10-15103B.jpg", 144 | "largeImage":"/images/shirts/10-15103A.jpg" 145 | } 146 | ] 147 | -------------------------------------------------------------------------------- /public/data/mens_tshirts.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name":"YouTube+Organic+Cotton+T-Shirt+-+Grey", 4 | "title":"YouTube Organic Cotton T-Shirt - Grey", 5 | "category":"mens_tshirts", 6 | "price":14.75, 7 | "description":"Stay casual and cool in this 100% organic pre-shrunk cotton T-shirt. Available in charcoal grey with full-color YouTube logo screened on front.", 8 | "image":"/images/shirts/10-13058B.jpg", 9 | "largeImage":"/images/shirts/10-13058A.jpg" 10 | }, 11 | { 12 | "name":"Inbox+-+Subtle+Actions+T-Shirt", 13 | "title":"Inbox - Subtle Actions T-Shirt", 14 | "category":"mens_tshirts", 15 | "price":17.05, 16 | "description":"Sometimes even the subtlest of actions can make a big difference. This tee highlights all of the icons &amp; features available in your Gmail inbox!<div><br></div><div>Features:</div><div><ul><li>60% cotton, 40% polyester blend.</li><li>Available in charcoal heather with the inbox icons screenprinted at front chest and inbox tag sewn onto left sleeve.</li></ul></div>", 17 | "image":"/images/shirts/10-13256B.jpg", 18 | "largeImage":"/images/shirts/10-13256A.jpg" 19 | }, 20 | { 21 | "name":"Adult+Android+Superhero+T-Shirt", 22 | "title":"Adult Android Superhero T-Shirt", 23 | "category":"mens_tshirts", 24 | "price":14.95, 25 | "description":"Mr. Kent has nothing on Super Droid, especially since this robot has only one weakness-a sweet tooth (considering all of its confectionery-themed versions)! This adorable Bella+Canvas tee features a unisex fit that is sure to please both male and female Android fans.<div><br></div><div>Additional Features:</div><div><ul><li>100% combed, ringspun cotton.</li><li>Unisex fit.</li><li>Tag-free label for added comfort.</li><li>Available in royal blue with the Super Droid robot screen printed at center chest.</li><li><b>Sizes run smaller than normal. Reference men's sizing chart for additional details.</b></li></ul></div>", 26 | "image":"/images/shirts/10-13239B.jpg", 27 | "largeImage":"/images/shirts/10-13239A.jpg" 28 | }, 29 | { 30 | "name":"Men+s+Vintage+Heather+T-Shirt", 31 | "title":"Men's Vintage Heather T-Shirt", 32 | "category":"mens_tshirts", 33 | "price":15.8, 34 | "description":"<div>A casual-cool, vintage-inspired tee perfect for all. Just remember that the best part about any classic is that it only improves with age. The more you wash it, the softer it feels.&nbsp;</div><div><br></div><div>Features:</div><div><ul><li>65% polyester, 35% cotton.</li><li>Available in heather navy, blue, purple or green with the white Google logo screened across center chest of each.</li></ul></div>", 35 | "image":"/images/shirts/10-13264B.jpg", 36 | "largeImage":"/images/shirts/10-13264A.jpg" 37 | }, 38 | { 39 | "name":"Basic+Black+T-Shirt", 40 | "title":"Basic Black T-Shirt", 41 | "category":"mens_tshirts", 42 | "price":16.9, 43 | "description":"Word on the street is that 'black is the new black.' Embellish your basic fashion statement with the Google logo on an authentic American Apparel t-shirt.&nbsp;<div><br></div><div>Features:</div><div><ul><li>100% organic combed cotton for ultimate softness.&nbsp;</li><li>Flattering fit.&nbsp;</li><li>Available in Black with the Google logo screen printed in White across center chest.</li><li><b>Sizes run smaller than normal.</b>&nbsp;<b>Please reference men's size chart for fit.</b></li></ul></div>", 44 | "image":"/images/shirts/10-13265B.jpg", 45 | "largeImage":"/images/shirts/10-13265A.jpg" 46 | }, 47 | { 48 | "name":"Local+Guides+T-Shirt", 49 | "title":"Local Guides T-Shirt", 50 | "category":"mens_tshirts", 51 | "price":15.7, 52 | "description":"Do you live to explore? Are you the first to tell your friends about the best venues, restaurants and hot spots in town? If you're already a local guide, sport your t-shirt with pride. This ultra soft style is comfortable enough to wear all day long - perfect for all of those adventures you'll tell us about later. To learn more about Local Guides, visit us here:&nbsp;https://www.google.com/local/guides/.<div><br></div><div>Features:</div><div><ul><li>52% combed, ring-spun cotton / 48% polyester.</li><li>Retail fit.</li><li>Available in charcoal with the Local Guides logo screenprinted at front chest and Google logo screenprinted in white at left bicep.</li></ul></div>", 53 | "image":"/images/shirts/10-13280B.jpg", 54 | "largeImage":"/images/shirts/10-13280A.jpg" 55 | }, 56 | { 57 | "name":"Go+Gopher+T-Shirt+in+Teal", 58 | "title":"Go Gopher T-Shirt in Teal", 59 | "category":"mens_tshirts", 60 | "price":10.95, 61 | "description":"Go anywhere in style when wearing this t-shirt featuring The Go Gopher. &nbsp;Tee is made of 100% combed, ring-spun cotton jersey fabric. Available in teal with the Go Gopher screen printed on center.", 62 | "image":"/images/shirts/10-13213B.jpg", 63 | "largeImage":"/images/shirts/10-13213A.jpg" 64 | }, 65 | { 66 | "name":"Android+Ringspun+T-Shirt+-+Green", 67 | "title":"Android Ringspun T-Shirt - Green", 68 | "category":"mens_tshirts", 69 | "price":8.75, 70 | "description":"Display your undying love for Androids everywhere in this 100% certified organic ringspun cotton tee.&nbsp;\n<div><br></div><div>Additional Features:&nbsp;</div><div><ul><li>100% combed, ring-spun cotton.&nbsp;</li><li>Preshrunk for fashion fit.&nbsp;</li><li>Tearaway label.&nbsp;\n</li><li>Available in heather green with the full color Android robot screen printed across center chest.</li></ul></div>", 71 | "image":"/images/shirts/10-13285B.jpg", 72 | "largeImage":"/images/shirts/10-13285A.jpg" 73 | }, 74 | { 75 | "name":"Organic+Cotton+Android+walking+with+dog+T-shirt", 76 | "title":"Organic Cotton Android walking with dog T-shirt", 77 | "category":"mens_tshirts", 78 | "price":17.25, 79 | "description":"What’s better than an organic cotton t-shirt with the Android logo? How about a t-shirt with the Android walking a dog.&nbsp; <div><br></div><div>Features:</div><div><ul><li>100% smooth organic cotton.</li><li>Available in black with the Android design screenprinted at front chest.</li><li><b>Sizing runs smaller than normal. Please reference size chart before ordering.</b></li></ul></div>", 80 | "image":"/images/shirts/10-13018B.jpg", 81 | "largeImage":"/images/shirts/10-13018A.jpg" 82 | }, 83 | { 84 | "name":"Organic+Cotton+T-Shirt+-+Red", 85 | "title":"Organic Cotton T-Shirt - Red", 86 | "category":"mens_tshirts", 87 | "price":14.4, 88 | "description":"Looking to add a little color to your wardrobe? This striking red tee shirt is made of 100% preshrunk organic cotton, so it's healthy for the environment as well as for your look.&nbsp;<div><br></div><div>Features:</div><div><ul><li>Shoulder-to-shoulder tape.</li><li>Seamless collar.</li><li>Available in red with white Google logo screenprinted at center chest.</li><li><b>**This shirt's cut is more of a generous t-shirt cut. Compared to the American Apparel shirts, sizing would run larger.**&nbsp;</b></li></ul></div>", 89 | "image":"/images/shirts/10-13270B.jpg", 90 | "largeImage":"/images/shirts/10-13270A.jpg" 91 | }, 92 | { 93 | "name":"Unisex+Gmail+T-Shirt", 94 | "title":"Unisex Gmail T-Shirt", 95 | "category":"mens_tshirts", 96 | "price":15, 97 | "description":"Show your inbox some love. The new Gmail tee has arrived, complete with a subtle Mvelope design that showcases all of the Gmail icons you use on the daily.<div><br></div><div>Features:</div><div><ul><li>52% cotton / 48% polyester.</li><li>Unisex fit.</li><li>Bella+Canvas.</li><li>Available in dark grey heather with the new Gmail print screenprinted across center chest.</li></ul></div>", 98 | "image":"/images/shirts/10-13282B.jpg", 99 | "largeImage":"/images/shirts/10-13282A.jpg" 100 | }, 101 | { 102 | "name":"Android+Soccer+T-Shirt", 103 | "title":"Android Soccer T-Shirt", 104 | "category":"mens_tshirts", 105 | "price":15.2, 106 | "description":"When it comes to futbol formation, the world's most adorable robots are en pointe. Show your love for the game with this limited edition Android tee.&nbsp;<div><br></div><div>Features:</div><div><ul><li>100% cotton.</li><li>Made in the USA.</li><li>Available in navy with the Android robot design screenprinted at front.</li></ul></div>", 107 | "image":"/images/shirts/10-13289B.jpg", 108 | "largeImage":"/images/shirts/10-13289A.jpg" 109 | }, 110 | { 111 | "name":"Basic+Google+T-Shirt", 112 | "title":"Basic Google T-Shirt", 113 | "category":"mens_tshirts", 114 | "price":13.3, 115 | "description":"Embellish your basic fashion statement with Google's brightly colored logo. Featuring a flattering and stylish fit for virtually any body type, this tee also boasts an ultra-soft feel.&nbsp;<div><br></div><div>Features:</div><div><ul><li>100% cotton.</li><li>Stretchable, reinforced shoulder construction maintains shape through repeated washings.</li><li>Double-stitched bottom hem ensures durability.&nbsp;</li><li>Available in white with the full color Google logo screenprinted across chest.</li><li><b>Sizes run smaller than normal.</b>&nbsp;<b>Please reference men's size chart for fit.</b></li></ul></div>", 116 | "image":"/images/shirts/10-13262B.jpg", 117 | "largeImage":"/images/shirts/10-13262A.jpg" 118 | }, 119 | { 120 | "name":"Tri-Blend+V-Neck+Tee", 121 | "title":"Tri-Blend V-Neck Tee", 122 | "category":"mens_tshirts", 123 | "price":14.95, 124 | "description":"An ultra soft triblend fabric and fashionable v-neck cut make this tee perfect for everyday wear. Available in royal blue with the white Google logo screenprinted at center chest.", 125 | "image":"/images/shirts/10-13273B.jpg", 126 | "largeImage":"/images/shirts/10-13273A.jpg" 127 | }, 128 | { 129 | "name":"Heather+Pocket+Tee+-+Light+Blue", 130 | "title":"Heather Pocket Tee - Light Blue", 131 | "category":"mens_tshirts", 132 | "price":23.3, 133 | "description":"Pocket protector or not, you're sure to look pretty cool in this stylish tee.&nbsp;<div><br></div><div>Features:</div><div><ul><li>60% cotton, 40% polyester.</li><li>Tagless label for added comfort.</li><li>Available in light blue with a grey pocket and the white Google logo screenprinted at center pocket.</li></ul></div>", 134 | "image":"/images/shirts/10-13272B.jpg", 135 | "largeImage":"/images/shirts/10-13272A.jpg" 136 | }, 137 | { 138 | "name":"Google+Now+Skyline+T-Shirt", 139 | "title":"Google Now Skyline T-Shirt", 140 | "category":"mens_tshirts", 141 | "price":20.2, 142 | "description":"A bright and sunny 360° illustration of San Francisco wrapped around this American Apparel t-shirt. This tee, popular at the Googleplex in Mountain View, CA is now available just for you!&nbsp;<div><br></div><div><ul><li>100% cotton.</li><li>Available in Aqua with the Google Now logo screen printed in Gray across chest.</li><li><b>Sizes run smaller than normal.</b>&nbsp;<b>Please reference men's size chart for fit.</b></li></ul></div>", 143 | "image":"/images/shirts/10-13276B.jpg", 144 | "largeImage":"/images/shirts/10-13276A.jpg" 145 | }, 146 | { 147 | "name":"Tri-Blend+G+Logo+Men+s+Polo", 148 | "title":"Tri-Blend G Logo Men's Polo", 149 | "category":"mens_tshirts", 150 | "price":32.7, 151 | "description":"Stock up on this comfy-cool polo featuring the new Google 'G.'&nbsp;<div><br></div><div>Features:</div><div><ul><li>50% polyester / 25% cotton / 25% rayon</li><li>Tri-blend fabric retains shape and elasticity.</li><li>Retail fit.&nbsp;</li><li>Single front pocket.</li><li>Three-button placket.</li><li>Structured collar holds shape through repeated washing.</li><li>Available in black with the Google 'G' icon embroidered at left chest.&nbsp;</li></ul></div>", 152 | "image":"/images/shirts/10-11019B.jpg", 153 | "largeImage":"/images/shirts/10-11019A.jpg" 154 | }, 155 | { 156 | "name":"Tri-Blend+Leisure+Shirt", 157 | "title":"Tri-Blend Leisure Shirt", 158 | "category":"mens_tshirts", 159 | "price":32.95, 160 | "description":"Dress it up, or dress it down. We promise you'll fall in love with the versatility of this triblend polo.&nbsp;<div><br></div><div>Features:&nbsp;</div><div><ul><li>50/25/25 polyester/cotton/rayon blend.&nbsp;</li><li>Three-button placket with structured self-fabric collar and left chest pocket.&nbsp;</li><li>Available in black with the Google logo embroidered at right sleeve.</li><li><b>Sizes run smaller than normal. Please reference size chart before ordering.</b></li></ul></div>", 161 | "image":"/images/shirts/10-11017B.jpg", 162 | "largeImage":"/images/shirts/10-11017A.jpg" 163 | }, 164 | { 165 | "name":"Wise+Android+T-Shirt", 166 | "title":"Wise Android T-Shirt", 167 | "category":"mens_tshirts", 168 | "price":14.95, 169 | "description":"Take a word from the wise: put on this tee and you'll fall in love! Made of 100% preshrunk certified organic cotton for ultimate softness. Available in black with the Wise Android screen printed on the front chest.", 170 | "image":"/images/shirts/10-13153B.jpg", 171 | "largeImage":"/images/shirts/10-13153A.jpg" 172 | }, 173 | { 174 | "name":"Android+Pride+T-Shirt", 175 | "title":"Android Pride T-Shirt", 176 | "category":"mens_tshirts", 177 | "price":19.1, 178 | "description":"<p>Stand out and stand proud in this Android Pride T-shirt.&nbsp;</p><p>Features:</p><p></p><ul><li>100% cotton American Apparel t-shirt.</li><li>Available in black, and features two Androids holding hands and waving a rainbow flag screenprinted at center chest. Google logo is screenprinted in white at sleeve.</li><li><b>Sizing runs smaller than normal. Please reference size chart before ordering.</b></li></ul>", 179 | "image":"/images/shirts/10-13279B.jpg", 180 | "largeImage":"/images/shirts/10-13279A.jpg" 181 | }, 182 | { 183 | "name":"Chrome+Unisex+T-Shirt", 184 | "title":"Chrome Unisex T-Shirt", 185 | "category":"mens_tshirts", 186 | "price":11.35, 187 | "description":"Show your love for Google Chrome in this 100% combed ring-spun cotton T-Shirt.&nbsp;<div><br></div><div>Features:</div><div><ul><li>Soft, jersey fabric.</li><li>Unisex fit.</li><li>Available in dark heather grey with the new Google Chrome logo screenprinted at front.</li></ul></div>", 188 | "image":"/images/shirts/10-13286B.jpg", 189 | "largeImage":"/images/shirts/10-13286A.jpg" 190 | }, 191 | { 192 | "name":"NY+City+Lights+T-Shirt", 193 | "title":"NY City Lights T-Shirt", 194 | "category":"mens_tshirts", 195 | "price":18.35, 196 | "description":"Are you feeling lucky? This anniversary t-shirt celebrates the Big Apple.&nbsp;<div><br></div><div>Features:</div><div><ul><li>100% cotton American Apparel shirt.</li><li>Available in Black with the Google logo and 'I'm Feeling Lucky' New York printed on back yoke in White.</li><li><b>Sizes run smaller than normal. Please reference men's size chart for fit before ordering.</b></li></ul></div>", 197 | "image":"/images/shirts/10-13271B.jpg", 198 | "largeImage":"/images/shirts/10-13271A.jpg" 199 | }, 200 | { 201 | "name":"Omi+Tech+Tee", 202 | "title":"Omi Tech Tee", 203 | "category":"mens_tshirts", 204 | "price":17, 205 | "description":"This performance tee deserves to be one of your wardrobe staples. Micro-polyester mesh fabric is snag-resistant, moisture-wicking and provides UV protection for sunny days. Available in royal blue with the white Google logo transferred onto center chest.", 206 | "image":"/images/shirts/10-13267B.jpg", 207 | "largeImage":"/images/shirts/10-13267A.jpg" 208 | }, 209 | { 210 | "name":"YouTube+S+S+Triblend+T-Shirt", 211 | "title":"YouTube S/S Triblend T-Shirt", 212 | "category":"mens_tshirts", 213 | "price":14.9, 214 | "description":"A perfect blend of three fabrics. This fashionably soft tee is perfect for layering or wearing solo.&nbsp;<div><br></div><div>Features:</div><div><ul><li>50% polyester, 25% combed, ring-spun cotton.</li><li>Retail fit.</li><li>Unisex sizing.&nbsp;</li><li>Available in black with the full color YouTube logo screenprinted at center chest.&nbsp;</li></ul></div>", 215 | "image":"/images/shirts/10-13278B.jpg", 216 | "largeImage":"/images/shirts/10-13278A.jpg" 217 | }, 218 | { 219 | "name":"Nest+T-Shirt", 220 | "title":"Nest T-Shirt", 221 | "category":"mens_tshirts", 222 | "price":17.4, 223 | "description":"We know energy savings make you smile. Why not put on an even bigger grin with this teal Nest t-shirt?&nbsp;<div><br></div><div>Features:</div><div><ul><li>100% combed cotton.</li><li>Made in the USA.</li><li>Available in teal with the white Nest icon screen printed across chest.</li><li><b>Sizing runs smaller than normal. Please reference size chart before ordering.</b></li></ul></div>", 224 | "image":"/images/shirts/10-13241B.jpg", 225 | "largeImage":"/images/shirts/10-13241A.jpg" 226 | }, 227 | { 228 | "name":"98+Short+Sleeve+Tee", 229 | "title":"98 Short Sleeve Tee", 230 | "category":"mens_tshirts", 231 | "price":14.3, 232 | "description":"Classic front. Retro back. This comfy grey tee celebrates Google's heritage, featuring the vintage '98' logo at back.<div><br></div><div>Features:</div><div><ul><li>100% cotton.&nbsp;</li><li>Available in grey with the white Google logo screenprinted at left chest and the vintage '98' screenprinted at back in red, white and blue.&nbsp;</li></ul></div><div><br></div>", 233 | "image":"/images/shirts/10-13291B.jpg", 234 | "largeImage":"/images/shirts/10-13291A.jpg" 235 | }, 236 | { 237 | "name":"Cardboard+T-Shirt", 238 | "title":"Cardboard T-Shirt", 239 | "category":"mens_tshirts", 240 | "price":14.2, 241 | "description":"Crazy for Cardboard? Show your love with this super soft t-shirt. If you're new to the virtual reality scene, check out Google Cardboard here:&nbsp;https://www.google.com/get/cardboard/<div><br></div><div>Features:</div><div><ul><li>60% cotton, 40% polyester.</li><li>Available in charcoal with a Cardboard viewer textured metallic heart at front chest and the Cardboard short url screenprinted at back yoke.</li></ul></div>", 242 | "image":"/images/shirts/10-13260B.jpg", 243 | "largeImage":"/images/shirts/10-13260A.jpg" 244 | }, 245 | { 246 | "name":"Short+Sleeve+Crew+Neck+Raglan", 247 | "title":"Short Sleeve Crew Neck Raglan", 248 | "category":"mens_tshirts", 249 | "price":13.1, 250 | "description":"This vintage style t-shirt features a soft jersey fabric comfortable enough to sleep in.&nbsp;<div><br></div><div>Features:</div><div><ul><li>60% cotton, 40% polyester.</li><li>Self fabric contrast neckband and sleeves.</li><li>Available in light blue/indigo with the white Google logo screenprinted across center chest.</li></ul></div>", 251 | "image":"/images/shirts/10-13266B.jpg", 252 | "largeImage":"/images/shirts/10-13266A.jpg" 253 | }, 254 | { 255 | "name":"MTV+Unisex+Blue+T-Shirt", 256 | "title":"MTV Unisex Blue T-Shirt", 257 | "category":"mens_tshirts", 258 | "price":15.75, 259 | "description":"They say home is where the heart is. This vibrant tee features the Bay Area address of Google's head office.&nbsp;<div><br></div><div>Features:</div><div><ul><li>100% combed cotton.&nbsp;</li><li>Made in the USA.</li><li>Available in royal blue with a striking design at front and the white Google logo at back yoke.&nbsp;</li></ul></div>", 260 | "image":"/images/shirts/10-13292B.jpg", 261 | "largeImage":"/images/shirts/10-13292A.jpg" 262 | }, 263 | { 264 | "name":"Organic+Me-To-We+Tee", 265 | "title":"Organic Me-To-We Tee", 266 | "category":"mens_tshirts", 267 | "price":23.6, 268 | "description":"Buy a tee that gives back. Half of the profits from each organic crewneck tee are distributed to the international charity and educational partner, Free the Children.&nbsp;<div><br></div><div>Features:</div><div><ul><li>100% organic cotton.</li><li>For more information on Free the Children &amp; Me to We, visit http://www.freethechildren.com/about-us/our-story/.&nbsp;</li><li>Available in blue with the white Google logo screenprinted at center chest.</li></ul></div>", 269 | "image":"/images/shirts/10-13268B.jpg", 270 | "largeImage":"/images/shirts/10-13268A.jpg" 271 | }, 272 | { 273 | "name":"Tri-Blend+Raglan+Long+Sleeve", 274 | "title":"Tri-Blend Raglan Long Sleeve", 275 | "category":"mens_tshirts", 276 | "price":51.2, 277 | "description":"Whether you're stealing second, cheering in the stands, or you just love the look of a classic 'baseball' tee, consider this raglan version for your apparel roster.&nbsp;<div><br></div><div>Features:</div><div><ul><li>50% polyester, 38% cotton, 12% rayon super soft triblend.</li><li>Fashionable fit.</li><li>Machine washable.</li><li>Available in grey/black with the white Google logo screenprinted at center chest.</li></ul></div>", 278 | "image":"/images/shirts/10-13274B.jpg", 279 | "largeImage":"/images/shirts/10-13274A.jpg" 280 | }, 281 | { 282 | "name":"Blueprint+for+a+Better+Inbox+T-Shirt", 283 | "title":"Blueprint for a Better Inbox T-Shirt", 284 | "category":"mens_tshirts", 285 | "price":14.3, 286 | "description":"This new \"Blueprint for &nbsp;better Inbox\" t-shirt will be your new favorite t-shirt. &nbsp;It's a USA made American Apparel t-shirt sporting the new logo.<div><br><div>Additional Features:&nbsp;</div><div><ul><li>50% cotton / 50% polyester for a super soft feel&nbsp;</li><li>Unisex fit.&nbsp;</li><li>Available in royal blue heather with the \"New Inbox: logo screen printed on the center chest.</li><li><b style=\"\">Sizing runs smaller than normal. Please reference men's sizing chart prior to ordering.</b></li></ul></div></div>", 287 | "image":"/images/shirts/10-13240B.jpg", 288 | "largeImage":"/images/shirts/10-13240A.jpg" 289 | }, 290 | { 291 | "name":"YouTube+Player+T-Shirt+-+Unisex", 292 | "title":"YouTube Player T-Shirt - Unisex", 293 | "category":"mens_tshirts", 294 | "price":17.8, 295 | "description":"<p>The YouTube Player T-Shirt has arrived. This t-shirt, much coveted by YouTube employees, is now available to the public. Channel your inner player and profess your love for YouTube with this clever design.&nbsp;</p><p>Features:</p><p></p><ul><li>100% combed cotton.</li><li>Reinforced shoulder construction to maintain shape.&nbsp;</li><li>Available in red with the Player logo screenprinted at front chest and YouTube logo screenprinted at back yoke.</li><li><b>Sizes run smaller than normal. Please reference size chart before ordering.</b></li></ul><p></p>\n<p><br></p>", 296 | "image":"/images/shirts/10-13097B.jpg", 297 | "largeImage":"/images/shirts/10-13097A.jpg" 298 | }, 299 | { 300 | "name":"G+Logo+White+T-Shirt", 301 | "title":"G Logo White T-Shirt", 302 | "category":"mens_tshirts", 303 | "price":13, 304 | "description":"There's a new G in town and it's here to stay. Get your hands on this comfy white tee featuring the new Google icon.&nbsp;<div><br></div><div>Features:</div><div><ul><li>100% combed, ringspun cotton.</li><li>Side-seamed.</li><li>Unisex size, retail fit by Bella+Canvas.</li><li>Available in white with the Google 'G' icon screenprinted at front.</li></ul></div>", 305 | "image":"/images/shirts/10-13275B.jpg", 306 | "largeImage":"/images/shirts/10-13275A.jpg" 307 | }, 308 | { 309 | "name":"Android+Concert+T-Shirt", 310 | "title":"Android Concert T-Shirt", 311 | "category":"mens_tshirts", 312 | "price":13.65, 313 | "description":"Back by popular demand! Rock out with this Android Concert t-shirt.&nbsp; <div><br></div><div>Features:</div><div><ul><li>100% combed cotton.</li><li>Made in the USA.</li><li>Available in grey with the Android screen printed in orange at center chest.&nbsp;</li><li><b>Sizing runs smaller than normal. Please reference size chart before ordering.</b></li></ul></div>", 314 | "image":"/images/shirts/10-13130B.jpg", 315 | "largeImage":"/images/shirts/10-13130A.jpg" 316 | }, 317 | { 318 | "name":"Men+s+Bamboo+T-Shirt", 319 | "title":"Men's Bamboo T-Shirt", 320 | "category":"mens_tshirts", 321 | "price":20.65, 322 | "description":"'Seriously soft' is one phrase we're not afraid to throw around when it comes to these bamboo tees.&nbsp;Made with 70% viscose from organic bamboo and 30% organic cotton, your skin will thank you during those long nights of programming.<div><br></div><div>Features:</div><div><ul><li>&nbsp;Available in blue with the Google logo screen printed in white across center chest.</li></ul></div>", 323 | "image":"/images/shirts/10-13269B.jpg", 324 | "largeImage":"/images/shirts/10-13269A.jpg" 325 | }, 326 | { 327 | "name":"Android+Pay+Crew+Neck+T-Shirt", 328 | "title":"Android Pay Crew Neck T-Shirt", 329 | "category":"mens_tshirts", 330 | "price":19.4, 331 | "description":"Wear. Wash. Repeat. This tee celebrates the most exciting way to pay for life's necessities. For more information on Android Pay, visit our site here; https://www.android.com/pay/.<div><br></div><div>Features:</div><div><ul><li>60/40 cotton/polyester blend.</li><li>Tagless label for added comfort.</li><li>Made in the USA.</li><li>Available in Pepper Black with the Android Pay icon screenprinted at front chest and Android Pay logo text at left sleeve.</li></ul><div><br></div><div><br></div></div>", 332 | "image":"/images/shirts/10-13263B.jpg", 333 | "largeImage":"/images/shirts/10-13263A.jpg" 334 | }, 335 | { 336 | "name":"Google+Maps+T-Shirt", 337 | "title":"Google Maps T-Shirt", 338 | "category":"mens_tshirts", 339 | "price":18.35, 340 | "description":"Make a geographical statement with this royal blue American Apparel tee.<div><br></div><div>Features:</div><div><ul><li>100% combed cotton.</li><li>Available in royal with the Google Maps pin and \"I am here\" text on the front, Google Maps logo on the back, and URL on the sleeve.&nbsp;</li><li><b>Sizes run smaller than normal. Please reference size chart before ordering.</b></li></ul></div>", 341 | "image":"/images/shirts/10-13277B.jpg", 342 | "largeImage":"/images/shirts/10-13277A.jpg" 343 | }, 344 | { 345 | "name":"Est.+98+Baseball+Tee", 346 | "title":"Est. 98 Baseball Tee", 347 | "category":"mens_tshirts", 348 | "price":17.9, 349 | "description":"Hit a home run in the style stakes with this classic baseball tee. Designed with traditional contrast sleeves and an 'est 98' graphic honoring the year Google was founded.&nbsp;<div><br></div><div>Features:</div><div><ul><li>Unisex fit.</li><li>52% cotton, 48% polyester.</li><li>Available in white with black sleeves and the white Google logo printed at left bicep.</li></ul></div>", 350 | "image":"/images/shirts/10-13290B.jpg", 351 | "largeImage":"/images/shirts/10-13290A.jpg" 352 | }, 353 | { 354 | "name":"Mountain+View+T-Shirt", 355 | "title":"Mountain View T-Shirt", 356 | "category":"mens_tshirts", 357 | "price":16.5, 358 | "description":"The Bay Area city named for its beautiful views of the Santa Cruz Mountains is also home to the Googleplex located at 1600 Amphitheater Parkway. Celebrate the place Google calls home in this American Apparel tee.<div><br></div><div>Features:</div><div><ul><li>100% combed cotton.</li><li>Made in Los Angeles.</li><li>Available in white with the Mountain View coordinates screenprinted at front and the full color Google logo screenprinted at back yoke.</li></ul></div>", 359 | "image":"/images/shirts/10-13288B.jpg", 360 | "largeImage":"/images/shirts/10-13288A.jpg" 361 | } 362 | ] 363 | -------------------------------------------------------------------------------- /public/data/sample_error_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "error": 1, 3 | "errorMessage": "Transaction failed." 4 | } 5 | -------------------------------------------------------------------------------- /public/data/sample_success_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "success": 1, 3 | "successMessage": "Your order has been received." 4 | } 5 | -------------------------------------------------------------------------------- /public/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /public/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/icon.png -------------------------------------------------------------------------------- /public/images/categories/ladies_outerwear.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/categories/ladies_outerwear.jpg -------------------------------------------------------------------------------- /public/images/categories/ladies_tshirts.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/categories/ladies_tshirts.jpg -------------------------------------------------------------------------------- /public/images/categories/mens_outerwear.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/categories/mens_outerwear.jpg -------------------------------------------------------------------------------- /public/images/categories/mens_tshirts.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/categories/mens_tshirts.jpg -------------------------------------------------------------------------------- /public/images/icons/shop-icon-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/icons/shop-icon-128.png -------------------------------------------------------------------------------- /public/images/icons/shop-icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/icons/shop-icon-192.png -------------------------------------------------------------------------------- /public/images/icons/shop-icon-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/icons/shop-icon-32.png -------------------------------------------------------------------------------- /public/images/icons/shop-icon-384.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/icons/shop-icon-384.png -------------------------------------------------------------------------------- /public/images/icons/shop-icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/icons/shop-icon-512.png -------------------------------------------------------------------------------- /public/images/shirts/10-11017A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-11017A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-11017B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-11017B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-11019A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-11019A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-11019B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-11019B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13018A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13018A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13018B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13018B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13058A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13058A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13058B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13058B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13097A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13097A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13097B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13097B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13130A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13130A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13130B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13130B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13153A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13153A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13153B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13153B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13213A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13213A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13213B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13213B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13239A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13239A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13239B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13239B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13240A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13240A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13240B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13240B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13241A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13241A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13241B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13241B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13256A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13256A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13256B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13256B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13260A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13260A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13260B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13260B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13262A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13262A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13262B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13262B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13263A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13263A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13263B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13263B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13264A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13264A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13264B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13264B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13265A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13265A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13265B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13265B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13266A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13266A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13266B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13266B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13267A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13267A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13267B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13267B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13268A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13268A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13268B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13268B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13269A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13269A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13269B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13269B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13270A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13270A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13270B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13270B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13271A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13271A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13271B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13271B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13272A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13272A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13272B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13272B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13273A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13273A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13273B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13273B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13274A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13274A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13274B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13274B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13275A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13275A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13275B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13275B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13276A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13276A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13276B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13276B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13277A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13277A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13277B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13277B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13278A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13278A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13278B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13278B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13279A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13279A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13279B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13279B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13280A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13280A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13280B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13280B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13282A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13282A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13282B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13282B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13285A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13285A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13285B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13285B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13286A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13286A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13286B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13286B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13288A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13288A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13288B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13288B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13289A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13289A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13289B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13289B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13290A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13290A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13290B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13290B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13291A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13291A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13291B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13291B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13292A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13292A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-13292B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-13292B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-14133A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-14133A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-14133B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-14133B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-14146A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-14146A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-14146B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-14146B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-14152A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-14152A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-14152B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-14152B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-14153A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-14153A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-14153B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-14153B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-14154A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-14154A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-14154B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-14154B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-14155A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-14155A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-14155B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-14155B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-14157A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-14157A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-14157B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-14157B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-14158A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-14158A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-14158B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-14158B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-14159A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-14159A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-14159B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-14159B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-14160A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-14160A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-14160B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-14160B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-14215A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-14215A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-14215B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-14215B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-14216A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-14216A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-14216B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-14216B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-14217A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-14217A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-14217B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-14217B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-15041A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-15041A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-15041B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-15041B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-15068A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-15068A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-15068B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-15068B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-15103A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-15103A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-15103B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-15103B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-23069A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-23069A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-23069B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-23069B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-23073A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-23073A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-23073B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-23073B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-23169A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-23169A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-23169B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-23169B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-23171A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-23171A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-23171B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-23171B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-23172A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-23172A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-23172B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-23172B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-23173A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-23173A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-23173B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-23173B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-23174A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-23174A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-23174B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-23174B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-23176A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-23176A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-23176B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-23176B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-23177A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-23177A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-23177B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-23177B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-23178A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-23178A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-23178B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-23178B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-23179A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-23179A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-23179B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-23179B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-23180A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-23180A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-23180B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-23180B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-23198A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-23198A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-23198B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-23198B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-23225A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-23225A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-23225B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-23225B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-23226A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-23226A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-23226B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-23226B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-23227A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-23227A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-23227B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-23227B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-23228A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-23228A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-23228B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-23228B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-23229A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-23229A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-23229B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-23229B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-23230A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-23230A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-23230B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-23230B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-24097A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-24097A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-24097B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-24097B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-24098A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-24098A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-24098B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-24098B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-24099A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-24099A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-24099B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-24099B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-24101A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-24101A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-24101B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-24101B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-24102A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-24102A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-24102B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-24102B.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-25058A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-25058A.jpg -------------------------------------------------------------------------------- /public/images/shirts/10-25058B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/images/shirts/10-25058B.jpg -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google-pay/react-store/30a2c7f6b40ed62e8d419ef49b0c72229343b09c/public/logo512.png -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React Store", 3 | "name": "Sample React Store", 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 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https: //www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | .App { 18 | text-align: center; 19 | } 20 | 21 | .heading { 22 | flex-grow: 1; 23 | } 24 | 25 | .heading a { 26 | text-decoration: none; 27 | color: inherit; 28 | } 29 | 30 | .App-header { 31 | position: fixed; 32 | top: 0; 33 | text-align: center; 34 | background-color: #ffff; 35 | display: flex; 36 | flex-direction: column; 37 | align-items: center; 38 | justify-content: center; 39 | font-size: 16px; 40 | color: #000; 41 | width: 100vw; 42 | box-shadow: 0 0 5px rgba(0, 0, 0, 0.2); 43 | } 44 | 45 | .App-header .Header { 46 | font-weight: bold; 47 | text-transform: uppercase; 48 | padding: 20px 16px; 49 | display: flex; 50 | position: relative; 51 | } 52 | 53 | .App-header .Header .title { 54 | flex: 1; 55 | flex-basis: 0.01px; 56 | } 57 | 58 | .App-header a { 59 | text-decoration: none; 60 | color: #000; 61 | } 62 | 63 | .App-header>.Navigation { 64 | display: flex; 65 | flex-direction: row; 66 | font-size: 13px; 67 | padding: 0; 68 | margin: 0; 69 | } 70 | 71 | .App-header>.Navigation li { 72 | list-style: none; 73 | margin: 10px; 74 | padding: 2px; 75 | } 76 | 77 | .App-header>.Navigation a { 78 | display: block; 79 | padding: 2px; 80 | font-weight: 500; 81 | } 82 | 83 | .App-header>.Navigation a.active { 84 | border-bottom: 2px solid #000; 85 | } 86 | 87 | .App-header+* { 88 | margin-top: 80px; 89 | } 90 | -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { useEffect, useMemo, useState } from 'react'; 18 | import { Route, BrowserRouter, Routes } from 'react-router-dom'; 19 | 20 | import Cart from './Store/Cart'; 21 | import Checkout from './Store/Checkout'; 22 | import Confirmation from './Store/Confirmation'; 23 | import Header from './Store/Header'; 24 | import Home from './Store/Home'; 25 | import ItemDetails from './Store/ItemDetails'; 26 | import List from './Store/List'; 27 | 28 | import { CartContext } from './Store/CartContext'; 29 | import { StoreData } from './Store/StoreData'; 30 | import { CategoryDetails } from './interfaces/CategoryDetails'; 31 | import { CartItemDetails } from './interfaces/CartItemDetails'; 32 | 33 | import './App.css'; 34 | 35 | /**Builds the base React app */ 36 | function App() { 37 | // Products, cart, and other shopping info 38 | const storeData = useMemo(() => new StoreData(), []); 39 | 40 | // T-shirt categories 41 | const [categories, setCategories] = useState([] as CategoryDetails[]); 42 | 43 | // Current user's shopping cart 44 | const [cart, setCart] = useState(storeData.getCart()); 45 | 46 | // Updates the user's shopping cart 47 | function updateCart(cart: CartItemDetails[]) { 48 | storeData.setCart(cart); 49 | setCart(cart); 50 | } 51 | 52 | // Create list of categories and details 53 | useEffect(() => { 54 | storeData.getCategories().then(data => setCategories(data)); 55 | }, [storeData]); 56 | 57 | // Create the router 58 | return ( 59 | 60 | 61 |
62 | 63 | } /> 64 | } /> 65 | } /> 66 | } /> 67 | } /> 68 | } /> 69 | 70 | 71 | 72 | ); 73 | } 74 | 75 | export default App; 76 | -------------------------------------------------------------------------------- /src/Store/Cart.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | .Cart { 18 | margin: 20px; 19 | text-align: center; 20 | } 21 | 22 | .Cart .total { 23 | margin: 20px 0; 24 | font-weight: bold; 25 | text-align: center; 26 | } 27 | 28 | .Cart .total>* { 29 | display: inline-block; 30 | margin: 10px; 31 | } 32 | 33 | .Cart .buttons { 34 | margin: 20px auto; 35 | max-width: 500px; 36 | } 37 | 38 | .Cart .buttons>* { 39 | width: 100%; 40 | } 41 | 42 | .Cart .cart-items { 43 | display: flex; 44 | flex-direction: column; 45 | text-align: left; 46 | margin: 20px auto; 47 | max-width: 800px; 48 | } 49 | 50 | .cart-item-card { 51 | margin: 5px 0; 52 | padding: 10px; 53 | display: flex; 54 | flex-direction: row; 55 | } 56 | 57 | .cart-item-image { 58 | width: 60px; 59 | align-self: flex-start; 60 | margin-top: 5px; 61 | } 62 | 63 | .cart-item-card .cart-item-content { 64 | padding: 10px; 65 | display: flex; 66 | flex-direction: column; 67 | align-self: stretch; 68 | width: 100%; 69 | } 70 | 71 | .cart-item-card .cart-item-content:last-child { 72 | padding-bottom: 10px; 73 | } 74 | 75 | .cart-item-card .first-row { 76 | display: flex; 77 | flex-direction: row; 78 | justify-content: stretch; 79 | } 80 | 81 | .cart-item-card .first-row .title { 82 | flex-grow: 1; 83 | } 84 | 85 | .cart-item-card .first-row .title a { 86 | color: #000; 87 | text-decoration: none; 88 | } 89 | 90 | .cart-item-card .first-row .remove { 91 | color: rgba(0, 0, 0, 0.4); 92 | flex-grow: 0; 93 | margin: -12px; 94 | } 95 | 96 | .cart-item-card .second-row { 97 | display: flex; 98 | flex-direction: row; 99 | justify-content: stretch; 100 | } 101 | 102 | .cart-item-card .second-row .label { 103 | margin-top: 6px; 104 | margin-right: 10px; 105 | } 106 | 107 | .cart-item-card .second-row .input { 108 | margin-right: 20px; 109 | } 110 | 111 | .cart-item-card .second-row .amount { 112 | flex-grow: 1; 113 | text-align: right; 114 | margin-right: 0; 115 | } 116 | -------------------------------------------------------------------------------- /src/Store/Cart.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { Button, Typography } from '@mui/material'; 18 | import React, { useContext } from 'react'; 19 | import { useNavigate } from 'react-router-dom'; 20 | 21 | import './Cart.css'; 22 | import CartItem from './CartItem'; 23 | import { CartContext } from './CartContext'; 24 | import { StoreData } from './StoreData'; 25 | 26 | interface Props {} 27 | 28 | /** Represents a user's shopping cart */ 29 | const Cart: React.FC = props => { 30 | const navigate = useNavigate(); 31 | 32 | // Get the cart from the context 33 | const { cart } = useContext(CartContext); 34 | 35 | // Get the cart size 36 | const cartSize = StoreData.getCartSize(cart); 37 | 38 | // Return the cart 39 | return ( 40 |
41 | Your Cart 42 | 43 | ({cartSize} {cartSize === 1 ? 'item' : 'items'}) 44 | 45 | 46 |
47 | {cart.map((item, index) => ( 48 | 49 | ))} 50 |
51 | 52 |
53 | Total: 54 | 55 | ${cart.reduce((total, cartItem) => total + cartItem.quantity * cartItem.item.price, 0).toFixed(2)} 56 | 57 |
58 | 59 |
60 | 63 |
64 |
65 | ); 66 | }; 67 | 68 | export default Cart; 69 | -------------------------------------------------------------------------------- /src/Store/CartContext.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import React from 'react'; 18 | 19 | import { CartItemDetails } from '../interfaces/CartItemDetails'; 20 | 21 | // Create an empty cart 22 | let cart: CartItemDetails[] = []; 23 | 24 | // Add the empty cart to the current context 25 | const CartContext = React.createContext({ 26 | cart, 27 | setCart(cart: CartItemDetails[]) { 28 | this.cart = cart; 29 | } 30 | }); 31 | 32 | export { CartContext }; 33 | -------------------------------------------------------------------------------- /src/Store/CartItem.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import React, { useContext } from 'react'; 18 | import { Link } from 'react-router-dom'; 19 | import { Card, CardContent, IconButton, InputLabel, MenuItem, Select, Typography } from '@mui/material'; 20 | import { Close } from '@mui/icons-material'; 21 | 22 | import { CartContext } from './CartContext'; 23 | import { CartItemDetails } from '../interfaces/CartItemDetails'; 24 | 25 | import './List.css'; 26 | 27 | /**Properties for the CartItem component */ 28 | interface Props { 29 | cartItem: CartItemDetails; 30 | } 31 | 32 | /**CartItem React component 33 | * 34 | * @param {Props} props The details of the item in the cart 35 | */ 36 | const CartItem: React.FC = (props: Props) => { 37 | // Get the cart from the current context 38 | const { cart, setCart } = useContext(CartContext); 39 | 40 | /**Handle changes to the quantity of an item in the cart 41 | * 42 | * @param {number} quantity The new quantity of the item 43 | */ 44 | function handleQuantityChange(quantity: number) { 45 | // Find the index of the item in the cart 46 | const index = cart.findIndex( 47 | cartItem => cartItem.size === props.cartItem.size && cartItem.item.name === props.cartItem.item.name 48 | ); 49 | 50 | // Index exists 51 | if (index !== -1) { 52 | // Copy the cart 53 | const newCart = [...cart]; 54 | 55 | // Replace the item at the index with the new item (changing quantity) 56 | newCart.splice(index, 1, { 57 | ...cart[index], 58 | quantity 59 | }); 60 | 61 | // Replace the cart with the updated one 62 | setCart(newCart); 63 | } 64 | } 65 | 66 | /**Handle removing an item from the cart */ 67 | function handleRemoveClick() { 68 | // Find the index of the item in the cart 69 | const index = cart.findIndex( 70 | cartItem => cartItem.size === props.cartItem.size && cartItem.item.name === props.cartItem.item.name 71 | ); 72 | 73 | // Index exists 74 | if (index !== -1) { 75 | // Copy the cart 76 | const newCart = [...cart]; 77 | 78 | // Remove the item at the index 79 | newCart.splice(index, 1); 80 | 81 | // Replace the cart with the updated one 82 | setCart(newCart); 83 | } 84 | } 85 | 86 | // Return the CartItem React component 87 | return ( 88 | 89 | 90 | {props.cartItem.item.title} 91 | 92 | 93 |
94 | 95 | 96 | {props.cartItem.item.title} 97 | 98 | 99 | 100 | 101 | 102 |
103 |
104 | 105 | Qty 106 | 107 | 119 | Size 120 | {props.cartItem.size} 121 | 122 | ${(props.cartItem.quantity * props.cartItem.item.price).toFixed(2)} 123 | 124 |
125 |
126 |
127 | ); 128 | }; 129 | 130 | export default CartItem; 131 | -------------------------------------------------------------------------------- /src/Store/Category.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | .category-link { 18 | text-decoration: none; 19 | } 20 | 21 | .category-card { 22 | margin: 10px; 23 | } 24 | 25 | .category-image { 26 | height: 200px; 27 | } 28 | 29 | .category-card .category-content, 30 | .category-card .category-content:last-child { 31 | padding: 10px; 32 | } 33 | -------------------------------------------------------------------------------- /src/Store/Category.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import React from 'react'; 18 | import { Link } from 'react-router-dom'; 19 | import { Card, CardContent, CardMedia, Typography } from '@mui/material'; 20 | 21 | import { CategoryDetails } from '../interfaces/CategoryDetails'; 22 | 23 | import './Category.css'; 24 | 25 | /**Category React component 26 | * 27 | * @param {CategoryDetails} props The details of the item categories 28 | */ 29 | const Category: React.FC = (props: CategoryDetails) => { 30 | // Return the React component 31 | return ( 32 | 33 | 34 | 35 | 36 | {props.title} 37 | 38 | 39 | 40 | ); 41 | }; 42 | 43 | export default Category; 44 | -------------------------------------------------------------------------------- /src/Store/Checkout.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | .Checkout { 18 | margin: 20px auto; 19 | text-align: left; 20 | max-width: 1000px; 21 | } 22 | 23 | .Checkout .buttons { 24 | margin: 20px auto; 25 | max-width: 500px; 26 | } 27 | 28 | .Checkout .buttons>* { 29 | width: 100%; 30 | } 31 | 32 | .Checkout .checkout-grid { 33 | padding: 0 20px; 34 | } 35 | -------------------------------------------------------------------------------- /src/Store/Checkout.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import React, { useContext, useEffect, useState } from 'react'; 18 | import { useNavigate } from 'react-router-dom'; 19 | import { Button, Checkbox, FormControlLabel, Grid, TextField, Typography } from '@mui/material'; 20 | 21 | import GooglePayButton from '@google-pay/button-react'; 22 | 23 | import { buildPaymentRequest, getUpdatedPaymentData } from './GooglePay'; 24 | import { CartContext } from './CartContext'; 25 | 26 | import './Checkout.css'; 27 | 28 | /**Properties for the Checkout component */ 29 | interface Props {} 30 | 31 | /**Checkout React component */ 32 | const Checkout: React.FC = () => { 33 | const navigate = useNavigate(); 34 | 35 | // Get the cart from the current context 36 | const { cart, setCart } = useContext(CartContext); 37 | 38 | // Current Google Pay payment request details (if in state) 39 | const [paymentRequest, setPaymentRequest] = useState(buildPaymentRequest([])); 40 | 41 | // If the cart changes, update the payment request object 42 | useEffect(() => { 43 | Object.assign( 44 | paymentRequest, 45 | buildPaymentRequest( 46 | cart.map(itemDetail => { 47 | return { 48 | label: itemDetail.item.title, 49 | price: (itemDetail.quantity * itemDetail.item.price).toFixed(2), 50 | type: 'LINE_ITEM' 51 | }; 52 | }) 53 | ) 54 | ); 55 | setPaymentRequest(paymentRequest); 56 | }, [cart, paymentRequest]); 57 | 58 | /**Handles Google Pay checkout 59 | * 60 | * @param {google.payments.api.PaymentData} paymentData Google Pay payment data 61 | */ 62 | function handleLoadPaymentData(paymentData: google.payments.api.PaymentData) { 63 | // Simply log to the console for this sample app 64 | // Normally this would be sent to a backend server for processing 65 | console.log('Payment data', paymentData); 66 | 67 | // Empty cart 68 | setCart([]); 69 | 70 | // Route the user to the success page 71 | navigate('/confirm'); 72 | } 73 | 74 | /**Handles manual checkout (empty the cart and route to confirmation) */ 75 | function handleCheckout() { 76 | setCart([]); 77 | navigate('/confirm'); 78 | } 79 | 80 | // Return the React component 81 | return ( 82 |
83 | 84 | 85 | 86 | Automatic checkout 87 | 88 |
89 | 90 | Skip the forms and check out with Google Pay! 91 | 92 | 93 |
94 | console.error(error)} 100 | onPaymentDataChanged={paymentData => getUpdatedPaymentData(paymentRequest, paymentData)} 101 | /> 102 |
103 |
104 |
105 |
106 | 107 | 108 | Guest checkout 109 | 110 |
111 | 112 | Shipping address 113 | 114 | 115 | 116 | 124 | 125 | 126 | 134 | 135 | 136 | 144 | 145 | 146 | 153 | 154 | 155 | 163 | 164 | 165 | 166 | 167 | 168 | 176 | 177 | 178 | 186 | 187 | 188 | } 190 | label="Use this address for payment details" 191 | /> 192 | 193 | 194 |
195 |
196 | 197 | Payment method 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 218 | 219 | 220 | } 222 | label="Remember credit card details for next time" 223 | /> 224 | 225 | 226 |
227 |
228 | 231 |
232 |
233 |
234 |
235 | ); 236 | }; 237 | 238 | export default Checkout; 239 | -------------------------------------------------------------------------------- /src/Store/Confirmation.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import React from 'react'; 18 | import { useNavigate } from 'react-router-dom'; 19 | import { Button, Typography } from '@mui/material'; 20 | 21 | import './Status.css'; 22 | 23 | /**Properties for the Confirmation component */ 24 | interface Props {} 25 | 26 | /**Confirmation React component 27 | * 28 | * @param {Props} props The details of the order confirmation 29 | */ 30 | const Confirmation: React.FC = (props: Props) => { 31 | const navigate = useNavigate(); 32 | 33 | // Return the React component 34 | return ( 35 |
36 |
37 | Success 38 | 39 | Congratulations, on your purchase. The order has been received and is being processed. 40 | 41 | 42 | This is for demo purposes only. You have not been charged, the order is not being processed. 43 | 44 |
45 | 48 |
49 |
50 |
51 | ); 52 | }; 53 | 54 | export default Confirmation; 55 | -------------------------------------------------------------------------------- /src/Store/GooglePay.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { paymentRequest } from '../config/GooglePay'; 18 | import { shippingOptions } from '../config/ShippingOptions'; 19 | 20 | /**Calculate and return the total price 21 | * 22 | * @param {google.payments.api.DisplayItem[]} displayItems The items in the user's cart 23 | * @returns {number} The total price 24 | */ 25 | function calculateTotalPrice(displayItems: google.payments.api.DisplayItem[]): number { 26 | return displayItems.reduce((total, item) => total + Number(item.price), 0); 27 | } 28 | 29 | /**Build the Google Pay payment request 30 | * 31 | * @param {google.payments.api.DisplayItem[]} displayItems The items in the user's cart 32 | * @returns {google.payments.api.PaymentDataRequest} The payment data request 33 | */ 34 | function buildPaymentRequest(displayItems: google.payments.api.DisplayItem[]): google.payments.api.PaymentDataRequest { 35 | return { 36 | ...paymentRequest, 37 | transactionInfo: { 38 | ...paymentRequest.transactionInfo, 39 | displayItems: [...displayItems], 40 | totalPrice: calculateTotalPrice(displayItems).toFixed(2) 41 | } 42 | }; 43 | } 44 | 45 | /**Get the updated payment data from the payment request 46 | * 47 | * @param {google.payments.api.PaymentDataRequest} paymentRequest The original payment request 48 | * @param {google.payments.api.IntermediatePaymentData} paymentData The new intermediate payment data 49 | * @returns {google.payments.api.PaymentDataRequestUpdate} The updated payment request 50 | */ 51 | function getUpdatedPaymentData( 52 | paymentRequest: google.payments.api.PaymentDataRequest, 53 | paymentData: google.payments.api.IntermediatePaymentData 54 | ): google.payments.api.PaymentDataRequestUpdate { 55 | // Check if a shipping option was chosen 56 | if (paymentData.shippingOptionData?.id) { 57 | // Get the shipping option from the available choices 58 | const shippingOption = shippingOptions.find(option => option.id === paymentData.shippingOptionData!.id); 59 | 60 | if (shippingOption) { 61 | // Update the shopping cart with the chosen shipping option 62 | const displayItems: google.payments.api.DisplayItem[] = [ 63 | ...(paymentRequest.transactionInfo.displayItems || []), 64 | { 65 | label: shippingOption.label, 66 | price: shippingOption.price.toFixed(2), 67 | type: 'SHIPPING_OPTION' 68 | } 69 | ]; 70 | 71 | // Return the updated transaction info and display items 72 | return { 73 | newTransactionInfo: { 74 | ...paymentRequest.transactionInfo, 75 | totalPrice: calculateTotalPrice(displayItems).toFixed(2), 76 | displayItems 77 | } 78 | }; 79 | } 80 | } 81 | 82 | // The shipping option didn't change...return no changes 83 | // In a real-world scenario, this should also check for shopping cart updates, 84 | // promotion codes being added, and other use-cases 85 | return {}; 86 | } 87 | 88 | export { buildPaymentRequest, getUpdatedPaymentData }; 89 | -------------------------------------------------------------------------------- /src/Store/Header.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https: //www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | .App .Header { 18 | background-color: #fff; 19 | color: #000; 20 | } 21 | -------------------------------------------------------------------------------- /src/Store/Header.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import React, { useContext, useEffect, useMemo, useState } from 'react'; 18 | import { Link, useNavigate } from 'react-router-dom'; 19 | import { AppBar, Badge, IconButton, Menu, MenuItem, Toolbar, Typography } from '@mui/material'; 20 | import { Menu as MenuIcon, ShoppingCart } from '@mui/icons-material'; 21 | 22 | import { CartContext } from './CartContext'; 23 | import { CategoryDetails } from '../interfaces/CategoryDetails'; 24 | import { StoreData } from './StoreData'; 25 | 26 | import './Header.css'; 27 | 28 | /**Header React component */ 29 | export default function Header() { 30 | const navigate = useNavigate(); 31 | 32 | // Manages store items, categories, etc. 33 | const storeData = useMemo(() => new StoreData(), []); 34 | 35 | // Current shopping cart from context 36 | const { cart } = useContext(CartContext); 37 | 38 | // Get the anchor element from the current state 39 | const [anchorEl, setAnchorEl] = useState(null); 40 | 41 | // Get the categories from the current state 42 | const [categories, setCategories] = useState([] as CategoryDetails[]); 43 | 44 | // If store data changes, update the categories 45 | useEffect(() => { 46 | storeData.getCategories().then(data => setCategories(data)); 47 | }, [storeData]); 48 | 49 | // Handle clicks to the Shop menu 50 | const handleMenuClick = (event: React.MouseEvent) => { 51 | setAnchorEl(event.currentTarget); 52 | }; 53 | 54 | // Handle clicks to a Shop menu item 55 | const handleMenuItemClick = (category: CategoryDetails) => { 56 | navigate(`/list/${category.name}`); 57 | setAnchorEl(null); 58 | }; 59 | 60 | // Handle closing the Shop menu 61 | const handleMenuClose = () => { 62 | setAnchorEl(null); 63 | }; 64 | 65 | // Handle clicks to the shopping cart 66 | const handleCartClick = (event: React.MouseEvent) => { 67 | navigate('/cart'); 68 | }; 69 | 70 | // Return the Header React component 71 | return ( 72 | 73 | 74 | 75 | 76 | 77 | 78 | {categories.map(cat => ( 79 | handleMenuItemClick(cat)}> 80 | {cat.title} 81 | 82 | ))} 83 | 84 | 85 | Shop 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | ); 95 | } 96 | -------------------------------------------------------------------------------- /src/Store/Home.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https: //www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | .Home { 18 | text-align: center; 19 | margin: 15px auto; 20 | max-width: 800px; 21 | } 22 | -------------------------------------------------------------------------------- /src/Store/Home.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import React from 'react'; 18 | 19 | import Category from './Category'; 20 | 21 | import { CategoryDetails } from '../interfaces/CategoryDetails'; 22 | 23 | import './Home.css'; 24 | 25 | /**Properties for the Home component */ 26 | interface Props { 27 | categories: CategoryDetails[]; 28 | } 29 | 30 | /**Home React component 31 | * 32 | * @param {Props} props The details of the item categories 33 | */ 34 | const Home: React.FC = (props: Props) => { 35 | // Return the Home React component 36 | return ( 37 |
38 | {props.categories.map(cat => ( 39 | 40 | ))} 41 |
42 | ); 43 | }; 44 | 45 | export default Home; 46 | -------------------------------------------------------------------------------- /src/Store/ItemDetails.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | .ItemDetails { 18 | text-align: left; 19 | margin: 20px; 20 | } 21 | 22 | .ItemDetails .container { 23 | max-width: 1000px; 24 | margin: auto; 25 | } 26 | 27 | .ItemDetails .content { 28 | margin-top: 10px; 29 | } 30 | 31 | .image-container { 32 | position: relative; 33 | padding-bottom: 100%; 34 | height: 0; 35 | overflow: hidden; 36 | } 37 | 38 | .item-details-image { 39 | position: absolute; 40 | top: 0; 41 | left: 0; 42 | width: 100%; 43 | height: 100%; 44 | display: block; 45 | } 46 | 47 | .ItemDetails .pickers { 48 | display: flex; 49 | flex-direction: column; 50 | padding: 10px 0; 51 | } 52 | 53 | .ItemDetails .pickers .field-set { 54 | display: flex; 55 | flex-direction: row; 56 | padding: 10px 0; 57 | } 58 | 59 | .ItemDetails .pickers .field-set .label { 60 | margin-top: 6px; 61 | width: 40%; 62 | } 63 | 64 | .ItemDetails .pickers .field-set .input { 65 | flex-grow: 1; 66 | } 67 | 68 | .ItemDetails .description { 69 | margin-bottom: 30px; 70 | } 71 | 72 | .ItemDetails .description h6 { 73 | margin-bottom: 10px; 74 | } 75 | 76 | .ItemDetails .buttons>* { 77 | width: 100%; 78 | margin: 4px 0; 79 | } 80 | 81 | .ItemDetails .snackbar-info { 82 | color: #000; 83 | background-color: #fff; 84 | } 85 | -------------------------------------------------------------------------------- /src/Store/ItemDetails.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { useContext, useEffect, useMemo, useState } from 'react'; 18 | import { Button, Grid, InputLabel, MenuItem, Select, Snackbar, Typography } from '@mui/material'; 19 | import { useNavigate, useParams } from 'react-router-dom'; 20 | import qs from 'querystring'; 21 | 22 | import GooglePayButton from '@google-pay/button-react'; 23 | 24 | import { buildPaymentRequest, getUpdatedPaymentData } from './GooglePay'; 25 | import { CartContext } from './CartContext'; 26 | import { ItemDetails } from '../interfaces/ItemDetails'; 27 | import { StoreData } from './StoreData'; 28 | 29 | import './ItemDetails.css'; 30 | 31 | /**Unescape HTML content */ 32 | function unescapeHtml(text: string) { 33 | const elem = document.createElement('textarea'); 34 | elem.innerHTML = text; 35 | return elem.textContent || ''; 36 | } 37 | 38 | /**ItemDetails React component */ 39 | export default function ItemDetails() { 40 | const navigate = useNavigate(); 41 | 42 | // Manages store items, categories, etc. 43 | const storeData = useMemo(() => new StoreData(), []); 44 | 45 | // Parse querystring params 46 | const query = qs.parse(window.location.search.replace(/^\?/, '')); 47 | 48 | // Dynamic URL parameters 49 | const params = useParams(); 50 | 51 | // Current item details (if in state) 52 | const [item, setItem] = useState(); 53 | 54 | // Current size details (if in state) 55 | const [size, setSize] = useState((query.size as string) || 'M'); 56 | 57 | // Current quantity details (if in state) 58 | const [quantity, setQuantity] = useState(1); 59 | 60 | // Current Google Pay payment request details (if in state) 61 | const [paymentRequest, setPaymentRequest] = useState(buildPaymentRequest([])); 62 | 63 | // "Snack" menu on bottom of screen 64 | const [snackOpen, setSnackOpen] = useState(false); 65 | 66 | const { setCart } = useContext(CartContext); 67 | 68 | // If the params or store data change, update the currently selected item 69 | useEffect(() => { 70 | storeData.getItem(params.listId ?? '', params.itemId ?? '').then(data => setItem(data)); 71 | }, [params, storeData]); 72 | 73 | // If the item, size, quantity, or payment request change, update the payment 74 | // request object 75 | useEffect(() => { 76 | Object.assign( 77 | paymentRequest, 78 | buildPaymentRequest( 79 | item 80 | ? [ 81 | { 82 | label: `${item.title} (${size}) x ${quantity}`, 83 | price: (item.price * quantity).toFixed(2), 84 | type: 'LINE_ITEM' 85 | } 86 | ] 87 | : [] 88 | ) 89 | ); 90 | setPaymentRequest(paymentRequest); 91 | }, [item, size, quantity, paymentRequest]); 92 | 93 | /**Adds an item to the cart */ 94 | function addToCart() { 95 | // Check if an item is currently selected 96 | if (item) { 97 | // Add the item to the cart 98 | storeData.addItemToCart(item, size, quantity); 99 | 100 | // Update the current cart 101 | setCart(storeData.getCart()); 102 | 103 | // Open the snack menu 104 | setSnackOpen(true); 105 | } 106 | } 107 | 108 | /**Closes the snack menu */ 109 | function handleSnackClose() { 110 | setSnackOpen(false); 111 | } 112 | 113 | /**Loads payment data and routes to the confirmation page 114 | * 115 | * @param {google.payments.api.PaymentData} paymentData Google Pay payment data 116 | */ 117 | function handleLoadPaymentData(paymentData: google.payments.api.PaymentData) { 118 | // Simply log to the console for this sample app 119 | // Normally this would be sent to a backend server for processing 120 | console.log('Payment data', paymentData); 121 | 122 | // Route the user to the success page 123 | navigate('/confirm'); 124 | } 125 | 126 | // Return the ItemDetails React component 127 | return ( 128 |
129 | {item && ( 130 | <> 131 | 132 | 133 |
134 | {item.title} 135 |
136 |
137 | 138 | {item.title} 139 | 140 | ${item.price.toFixed(2)} 141 | 142 |
143 |
144 | 145 | Size 146 | 147 | 159 |
160 |
161 | 162 | Quantity 163 | 164 | 176 |
177 |
178 |
179 | Description 180 | 181 |
182 | 183 |
184 |
185 | console.error(error)} 191 | onPaymentDataChanged={paymentData => getUpdatedPaymentData(paymentRequest, paymentData)} 192 | /> 193 | 196 |
197 | 198 | 199 | 210 | 211 | 212 | 213 | 214 | } 215 | /> 216 | 217 | )} 218 |
219 | ); 220 | } 221 | -------------------------------------------------------------------------------- /src/Store/List.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https: //www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | .List { 18 | margin: 10px auto; 19 | display: flex; 20 | flex-direction: row; 21 | flex-wrap: wrap; 22 | justify-content: center; 23 | max-width: 1000px; 24 | } 25 | 26 | .list-item-link { 27 | text-decoration: none; 28 | display: flex; 29 | flex-direction: column; 30 | } 31 | 32 | .list-item-card { 33 | margin: 5px; 34 | width: 175px; 35 | flex-grow: 1; 36 | display: flex; 37 | flex-direction: column; 38 | justify-content: center; 39 | } 40 | 41 | .list-item-image { 42 | height: 175px; 43 | margin-top: 10px; 44 | } 45 | 46 | .list-item-card .list-item-content { 47 | padding: 10px; 48 | display: flex; 49 | flex-direction: column; 50 | } 51 | 52 | .list-item-card .list-item-content:last-child { 53 | padding-bottom: 10px; 54 | } 55 | 56 | .list-item-card .list-item-content .title { 57 | font-size: 13px; 58 | } 59 | -------------------------------------------------------------------------------- /src/Store/List.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import React, { useEffect, useMemo, useState } from 'react'; 18 | import { useParams } from 'react-router-dom'; 19 | 20 | import ListItem from './ListItem'; 21 | 22 | import { CategoryDetails } from '../interfaces/CategoryDetails'; 23 | import { ItemDetails } from '../interfaces/ItemDetails'; 24 | import { StoreData } from './StoreData'; 25 | 26 | /**Properties for the List component */ 27 | interface Props { 28 | categories: CategoryDetails[]; 29 | } 30 | 31 | /**List React component 32 | * 33 | * @param {Props} props The details of the item categories 34 | */ 35 | const List: React.FC = (props: Props) => { 36 | // Manages store items, categories, etc. 37 | const storeData = useMemo(() => new StoreData(), []); 38 | 39 | // Current item details (if in state) 40 | const [items, setItems] = useState([] as ItemDetails[]); 41 | 42 | // Current category details (if in state) 43 | const [category, setCategory] = useState(); 44 | 45 | // Dynamic URL parameters 46 | const params = useParams<{ listId: string }>(); 47 | 48 | // If the list ID or categories change, call setCategory 49 | useEffect(() => { 50 | setCategory(props.categories.find(cat => cat.name === params.listId)); 51 | }, [params.listId, props.categories]); 52 | 53 | // If the category or store data changes, call getItemsByCategory 54 | useEffect(() => { 55 | if (!category) return; 56 | storeData.getItemsByCategory(category.name).then(data => setItems(data)); 57 | }, [category, storeData]); 58 | 59 | // Return the React component 60 | return ( 61 |
62 | {items.map(item => ( 63 | 64 | ))} 65 |
66 | ); 67 | }; 68 | 69 | export default List; 70 | -------------------------------------------------------------------------------- /src/Store/ListItem.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import './List.css'; 18 | import { Card, CardContent, CardMedia, Typography } from '@mui/material'; 19 | import { Link, useParams } from 'react-router-dom'; 20 | import { ItemDetails } from '../interfaces/ItemDetails'; 21 | import React from 'react'; 22 | 23 | interface Props { 24 | item: ItemDetails; 25 | } 26 | 27 | const ListItem: React.FC = props => { 28 | const params = useParams<{ listId: string }>(); 29 | 30 | return ( 31 | 32 | 33 | 34 | 35 | 36 | {props.item.title} 37 | 38 | 39 | ${props.item.price.toFixed(2)} 40 | 41 | 42 | 43 | 44 | ); 45 | }; 46 | 47 | export default ListItem; 48 | -------------------------------------------------------------------------------- /src/Store/Status.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | .Confirmation, 18 | .Error { 19 | margin: 20px; 20 | text-align: left; 21 | display: flex; 22 | flex-direction: column; 23 | } 24 | 25 | .Confirmation p, 26 | .Error p { 27 | margin: 10px 0; 28 | } 29 | 30 | .Confirmation .content, 31 | .Error .content { 32 | max-width: 500px; 33 | margin: 10px auto; 34 | } 35 | 36 | .Confirmation .buttons, 37 | .Error .buttons { 38 | margin-top: 20px; 39 | text-align: right; 40 | } 41 | -------------------------------------------------------------------------------- /src/Store/StorageProvider.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | // Acts as local storage 18 | const map = new Map(); 19 | 20 | /**Mock storage provider */ 21 | export abstract class StorageProvider { 22 | /**Get a value 23 | * 24 | * @param {string} key The item's key in the map 25 | * @returns {T} The value of the item 26 | */ 27 | public abstract get(key: string): T; 28 | 29 | /**Set a value 30 | * 31 | * @param {string} key The item's key in the map 32 | * @param {T} value The item's value 33 | */ 34 | public abstract set(key: string, value: T): void; 35 | 36 | /**Create a new storage provider instance */ 37 | public static create(): StorageProvider { 38 | // Check if a local storage provider has been created 39 | // If so, return it 40 | // If not, create a new one 41 | if (localStorage) { 42 | return new LocalStorageProvider(); 43 | } 44 | return map; 45 | } 46 | } 47 | 48 | /**Mock local storage provider */ 49 | export class LocalStorageProvider extends StorageProvider { 50 | /**Get a value 51 | * 52 | * @param {string} key The item's key in the map 53 | * @returns {T} The value of the item 54 | */ 55 | public get(key: string): T { 56 | return JSON.parse(localStorage.getItem(key) || 'null'); 57 | } 58 | 59 | /**Set a value 60 | * 61 | * @param {string} key The item's key in the map 62 | * @param {T} value The item's value 63 | */ 64 | public set(key: string, value: T): void { 65 | localStorage.setItem(key, JSON.stringify(value)); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/Store/StoreData.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { CartItemDetails } from '../interfaces/CartItemDetails'; 18 | import { categories } from '../config/CategoryDetails'; 19 | import { CategoryDetails } from '../interfaces/CategoryDetails'; 20 | import { ItemDetails } from '../interfaces/ItemDetails'; 21 | import { StorageProvider } from './StorageProvider'; 22 | 23 | /**The store, available items, etc. */ 24 | export class StoreData { 25 | // Caches the items in the currently-selected category 26 | private itemCache = new Map>(); 27 | 28 | // Handles local storage 29 | private storageProvider = StorageProvider.create(); 30 | 31 | /**Get the list of available categories 32 | * 33 | * @returns {Promise} The available categories 34 | */ 35 | public getCategories(): Promise { 36 | return Promise.resolve(categories); 37 | } 38 | 39 | /**Get the items for a specific category 40 | * 41 | * @param {string} name The category name 42 | * @returns {Promise} The items in the category 43 | */ 44 | public getItemsByCategory(name: string): Promise { 45 | // Check if the items in the category are currently in local storage 46 | let promise = this.itemCache.get(name); 47 | 48 | // Found in cache, return this instead 49 | if (promise) return promise; 50 | 51 | // Fetch the items in the category from the JSON file 52 | promise = fetch(`/data/${name}.json`).then(response => response.json()); 53 | 54 | // Cache the output 55 | this.itemCache.set(name, promise); 56 | 57 | // Return the items 58 | return promise; 59 | } 60 | 61 | /**Get a specific item from a category of items 62 | * 63 | * @param {string} category The category to search 64 | * @param {string} name The item name to search for 65 | * @return {Promise} The item, if found 66 | */ 67 | public async getItem(category: string, name: string): Promise { 68 | // Get the items for this category 69 | const items = await this.getItemsByCategory(category); 70 | 71 | // Check if the item is in this list 72 | return items.find(item => item.name === name); 73 | } 74 | 75 | /**Add a selected item to the user's cart 76 | * 77 | * @param {ItemDetails} item The item to add 78 | * @param {string} size The size of the item to add 79 | * @param {number} quantity The number of this item to add 80 | */ 81 | public addItemToCart(item: ItemDetails, size: string, quantity: number) { 82 | // Get the cart, if it exists in local storage 83 | let cart = this.storageProvider.get('cart') || []; 84 | 85 | // Check if the item already exists in the cart 86 | let existing = cart.find(c => c.item.name === item.name && c.size === size); 87 | 88 | if (!existing) { 89 | // Item doesn't exist 90 | existing = { 91 | item, 92 | size, 93 | quantity 94 | }; 95 | 96 | // Add it to the cart 97 | cart = [...cart, existing]; 98 | } else { 99 | // Item already exists 100 | // Update the selected quantity 101 | existing.quantity += quantity; 102 | } 103 | 104 | // Overwrite the cart with the updated version 105 | this.setCart(cart); 106 | } 107 | 108 | /**Get the current cart, if it exists 109 | * 110 | * @returns {CartItemDetails[]} The current cart, or an empty array 111 | */ 112 | public getCart(): CartItemDetails[] { 113 | return this.storageProvider.get('cart') || []; 114 | } 115 | 116 | /**Update the user's cart with a new one 117 | * 118 | * @param {CartItemDetails[]} cart The new cart to use 119 | */ 120 | public setCart(cart: CartItemDetails[]) { 121 | this.storageProvider.set('cart', cart); 122 | } 123 | 124 | /**Get the number of items in the cart 125 | * 126 | * @returns {number} The number of items in the cart 127 | */ 128 | public getCartSize(): number { 129 | // Get the cart size from local storage 130 | return StoreData.getCartSize(this.getCart()); 131 | } 132 | 133 | /**Get the number of items in the provided cart 134 | * 135 | * @param {CartItemDetails[]} cart The cart to inventory 136 | * @returns {number} The number of items in the cart 137 | * @staticmethod 138 | */ 139 | public static getCartSize(cart: CartItemDetails[]): number { 140 | // Need to count the quantity of each item 141 | return cart.reduce((total, current) => total + current.quantity, 0); 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/Store/StoreService.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { CartItemDetails } from '../interfaces/CartItemDetails'; 18 | import { shippingOptions } from '../config/ShippingOptions'; 19 | 20 | /**Mock store services */ 21 | export class StoreService { 22 | /**Retrieve the list of shipping options 23 | * @returns {google.payments.api.ShippingOptionParameters} The available shipping options 24 | */ 25 | public getShippingOptionParameters(): google.payments.api.ShippingOptionParameters { 26 | // Default to free shipping 27 | return { 28 | defaultSelectedOptionId: 'free', 29 | shippingOptions: shippingOptions.map(o => ({ 30 | id: o.id, 31 | label: o.label, 32 | description: o.description 33 | })) 34 | }; 35 | } 36 | 37 | /**Calculate transaction info 38 | * @param {CartItemDetails[]} cart The user's shopping cart items 39 | * @param {google.payments.api.Address?} address The user's shipping address 40 | * @param {google.payments.api.SelectionOptionData?} shippingOptionData The shipping option chosen by the user 41 | * @returns {google.payments.api.TransactionInfo} The updated transaction information 42 | */ 43 | public getTransactionInfo( 44 | cart: CartItemDetails[], 45 | address?: google.payments.api.Address, 46 | shippingOptionData?: google.payments.api.SelectionOptionData 47 | ): google.payments.api.TransactionInfo { 48 | // The items to display on the shopping cart screen 49 | const displayItems: google.payments.api.DisplayItem[] = cart.map(item => ({ 50 | label: `${item.item.title} (${item.size}) x ${item.quantity}`, 51 | price: (item.item.price * item.quantity).toFixed(2), 52 | type: 'LINE_ITEM' 53 | })); 54 | 55 | // The subtotal (before tax, shipping, etc.) 56 | const subtotal = cart.reduce((total, item) => total + item.item.price * item.quantity, 0); 57 | 58 | // Get the shipping option chosen by the user 59 | const shippingOption = shippingOptions.find(option => option.id === shippingOptionData?.id); 60 | 61 | // Get the price of that shipping option 62 | const shipping = shippingOption?.price ?? 0; 63 | 64 | // Calculate tax 65 | const tax = this.getTaxRate(address) * subtotal; 66 | 67 | // Add subtotal, tax, and shipping for total cost 68 | const total = subtotal + shipping + tax; 69 | 70 | // Include the subtotal on the display 71 | displayItems.push({ 72 | label: 'Sub total', 73 | price: subtotal.toFixed(2), 74 | type: 'SUBTOTAL' 75 | }); 76 | 77 | // Include the chosen shipping option 78 | displayItems.push({ 79 | label: shippingOption?.label ?? 'Shipping', 80 | price: shipping.toFixed(2), 81 | type: 'SHIPPING_OPTION' 82 | }); 83 | 84 | // Include the tax 85 | if (tax > 0) { 86 | displayItems.push({ 87 | label: 'Tax', 88 | price: tax.toFixed(2), 89 | type: 'TAX' 90 | }); 91 | } 92 | 93 | // Return the items to display, the total, and currency 94 | return { 95 | displayItems, 96 | totalPrice: total.toFixed(2), 97 | totalPriceStatus: 'FINAL', 98 | totalPriceLabel: 'Total', 99 | currencyCode: 'USD', 100 | countryCode: 'US' 101 | }; 102 | } 103 | 104 | /**Get the tax rate (percentage) 105 | * @param {google.payments.api.Address?} address The purchase address 106 | * @returns {number} The tax rate 107 | */ 108 | public getTaxRate(address?: google.payments.api.Address): number { 109 | return address?.countryCode === 'US' ? 0.1 : 0.11; 110 | } 111 | 112 | /**Processes the order 113 | * 114 | * @param {CartItemDetails[]} cart The user's shopping cart 115 | * @param {google.payments.api.PaymentData} paymentData The payment data from Google Pay 116 | * @returns {Object} A mock order ID (today's date) 117 | */ 118 | public processOrder(cart: CartItemDetails[], paymentData: google.payments.api.PaymentData): Object { 119 | // In a real-world scenario, this would be sent to same backend server to 120 | // process the payment. 121 | console.log( 122 | 'TODO: send order to server', 123 | paymentData.shippingAddress, 124 | paymentData.shippingOptionData?.id, 125 | paymentData.paymentMethodData 126 | ); 127 | 128 | // Return a mock order ID (today's date) 129 | return Promise.resolve({ 130 | orderId: Date.now().toString() 131 | }); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/config/CategoryDetails.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { CategoryDetails } from '../interfaces/CategoryDetails'; 18 | 19 | /**The available merchandise categories */ 20 | export const categories: CategoryDetails[] = [ 21 | { 22 | name: 'mens_outerwear', 23 | title: 'Mens Outerwear', 24 | image: '/images/categories/mens_outerwear.jpg' 25 | }, 26 | { 27 | name: 'ladies_outerwear', 28 | title: 'Ladies Outerwear', 29 | image: '/images/categories/ladies_outerwear.jpg' 30 | }, 31 | { 32 | name: 'mens_tshirts', 33 | title: 'Mens T-Shirts', 34 | image: '/images/categories/mens_tshirts.jpg' 35 | }, 36 | { 37 | name: 'ladies_tshirts', 38 | title: 'Ladies T-Shirts', 39 | image: '/images/categories/ladies_tshirts.jpg' 40 | } 41 | ]; 42 | -------------------------------------------------------------------------------- /src/config/GooglePay.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { shippingOptionParameters } from './ShippingOptions'; 18 | 19 | /**The [Google Pay payment request](https://developers.google.com/pay/api/web/guides/tutorial) 20 | * 21 | * The `stripe:publishableKey` value is a [sample API key provided by Stripe](https://stripe.com/docs/payments/accept-a-payment). 22 | */ 23 | export const paymentRequest: google.payments.api.PaymentDataRequest = { 24 | apiVersion: 2, 25 | apiVersionMinor: 0, 26 | allowedPaymentMethods: [ 27 | { 28 | type: 'CARD', 29 | parameters: { 30 | allowedAuthMethods: ['PAN_ONLY', 'CRYPTOGRAM_3DS'], 31 | allowedCardNetworks: ['MASTERCARD', 'VISA'] 32 | }, 33 | tokenizationSpecification: { 34 | type: 'PAYMENT_GATEWAY', 35 | parameters: { 36 | 'gateway': 'stripe', 37 | 'stripe:version': '2018-10-31', 38 | 'stripe:publishableKey': 'pk_test_MNKMwKAvgdo2yKOhIeCOE6MZ00yS3mWShu' 39 | } 40 | } 41 | } 42 | ], 43 | merchantInfo: { 44 | merchantId: '17613812255336763067', // Test merchant ID provided by Google 45 | merchantName: 'Demo Only (you will not be charged)' 46 | }, 47 | transactionInfo: { 48 | totalPriceStatus: 'FINAL', 49 | totalPriceLabel: 'Total', 50 | totalPrice: '0', // Force this to be zero for the sample app 51 | currencyCode: 'USD', 52 | countryCode: 'US' 53 | }, 54 | shippingAddressRequired: true, 55 | shippingOptionParameters: shippingOptionParameters, 56 | shippingOptionRequired: true, 57 | callbackIntents: ['SHIPPING_ADDRESS', 'SHIPPING_OPTION'] 58 | }; 59 | -------------------------------------------------------------------------------- /src/config/ShippingOptions.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /**The available shipping options, their cost, and descriptions */ 18 | export const shippingOptions = [ 19 | { 20 | id: 'free', 21 | label: 'Free shipping', 22 | description: 'Arrives in 5 to 7 days', 23 | price: 0 24 | }, 25 | { 26 | id: 'express', 27 | label: 'Express shipping', 28 | description: '$5.00 - Arrives in 1 to 3 days', 29 | price: 5 30 | } 31 | ]; 32 | 33 | /**The default selected shipping option and the available options to choose from */ 34 | export const shippingOptionParameters: google.payments.api.ShippingOptionParameters = { 35 | defaultSelectedOptionId: 'free', 36 | shippingOptions: shippingOptions.map(o => ({ 37 | id: o.id, 38 | label: o.label, 39 | description: o.description 40 | })) 41 | }; 42 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https: //www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | body { 18 | margin: 0; 19 | font-family: 'Roboto', 'Noto', sans-serif; 20 | -webkit-font-smoothing: antialiased; 21 | -moz-osx-font-smoothing: grayscale; 22 | } 23 | 24 | code { 25 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 26 | monospace; 27 | } 28 | -------------------------------------------------------------------------------- /src/index.ejs: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | <%= htmlWebpackPlugin.options.title %> 24 | 25 | 26 | 27 | 28 |
29 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import React from 'react'; 18 | import { createRoot } from 'react-dom/client'; 19 | 20 | import App from './App'; 21 | import './index.css'; 22 | 23 | // Get the root container 24 | const container = document.getElementById('root'); 25 | 26 | // Create a Root object 27 | const root = createRoot(container!); 28 | 29 | // Render the app in strict mode 30 | root.render( 31 | 32 | 33 | 34 | ); 35 | -------------------------------------------------------------------------------- /src/interfaces/CartItemDetails.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { ItemDetails } from './ItemDetails'; 18 | 19 | /**The item, size, and quantity for an item in the user's cart */ 20 | export interface CartItemDetails { 21 | item: ItemDetails; 22 | size: string; 23 | quantity: number; 24 | } 25 | -------------------------------------------------------------------------------- /src/interfaces/CategoryDetails.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /**The name, title, and image path for a merchandise category */ 18 | export interface CategoryDetails { 19 | name: string; 20 | title: string; 21 | image: string; 22 | } 23 | -------------------------------------------------------------------------------- /src/interfaces/ItemDetails.tsx: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /**The name, title, category, price, description, and image paths for an item */ 18 | export interface ItemDetails { 19 | name: string; 20 | title: string; 21 | category: string; 22 | price: number; 23 | description: string; 24 | image: string; 25 | largeImage: string; 26 | } 27 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "strict": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "module": "esnext", 16 | "moduleResolution": "node", 17 | "resolveJsonModule": true, 18 | "isolatedModules": true, 19 | "jsx": "react-jsx", 20 | "noFallthroughCasesInSwitch": true 21 | }, 22 | "include": [ 23 | "src" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /webpack.config.cjs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | const path = require('path'); 18 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 19 | 20 | module.exports = { 21 | entry: './src/index.tsx', 22 | mode: 'development', 23 | module: { 24 | rules: [ 25 | { 26 | // Build JS/JSX using babel-loader 27 | // https://webpack.js.org/loaders/babel-loader/ 28 | test: /\.(js|jsx)$/, 29 | exclude: /(node_modules|bower_components)/, 30 | loader: 'babel-loader', 31 | options: { presets: ['@babel/env'] } 32 | }, 33 | { 34 | // Build TS/TSX with ts-loader 35 | // https://webpack.js.org/guides/typescript/ 36 | test: /\.(ts|tsx)$/, 37 | exclude: /(node_modules|bower_components)/, 38 | loader: 'ts-loader' 39 | }, 40 | { 41 | // Build CSS with style-loader 42 | // https://webpack.js.org/loaders/style-loader/ 43 | test: /\.css$/, 44 | use: ['style-loader', 'css-loader'] 45 | } 46 | ] 47 | }, 48 | resolve: { 49 | extensions: ['*', '.js', '.jsx', '.ts', '.tsx'], 50 | fallback: { 51 | querystring: require.resolve('query-string') 52 | } 53 | }, 54 | output: { 55 | // Output to `dist/` directory 56 | path: path.resolve(__dirname, 'dist'), 57 | publicPath: '/', 58 | filename: 'bundle.js' 59 | }, 60 | devServer: { 61 | // Configure devServer to refresh when files are modified and 62 | // fix issues with refreshing the page on a non-root url 63 | port: 3000, 64 | hot: 'only', 65 | historyApiFallback: true 66 | }, 67 | plugins: [ 68 | // Build dist/index.html from src/index.ejs using html-webpack-plugin 69 | // https://webpack.js.org/plugins/html-webpack-plugin/ 70 | new HtmlWebpackPlugin({ 71 | title: 'React Store', 72 | favicon: path.join(__dirname, 'public/favicon.svg'), 73 | meta: { 74 | charset: 'utf-8', 75 | viewport: 'width=device-width, initial-scale=1, shrink-to-fit=no' 76 | } 77 | }) 78 | ] 79 | }; 80 | --------------------------------------------------------------------------------