diff --git a/.env b/.env new file mode 100644 index 00000000..8c519632 --- /dev/null +++ b/.env @@ -0,0 +1 @@ +FAST_REFRESH=false\n diff --git a/package-lock.json b/package-lock.json index c205fc57..f34fc72d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "project1-bootcamp", "version": "0.1.0", "dependencies": { + "gh-pages": "^3.2.3", "react": "^18.1.0", "react-dom": "^18.1.0", "react-scripts": "5.0.1" @@ -4121,6 +4122,14 @@ "node": ">=8" } }, + "node_modules/array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/array.prototype.flat": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.0.tgz", @@ -5950,6 +5959,11 @@ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.131.tgz", "integrity": "sha512-oi3YPmaP87hiHn0c4ePB67tXaF+ldGhxvZnT19tW9zX6/Ej+pLN0Afja5rQ6S+TND7I9EuwQTT8JYn1k7R7rrw==" }, + "node_modules/email-addresses": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/email-addresses/-/email-addresses-3.1.0.tgz", + "integrity": "sha512-k0/r7GrWVL32kZlGwfPNgB2Y/mMXVTq/decgLczm/j34whdaspNrZO8CnXPf1laaHxI6ptUlsnAxN+UAPw+fzg==" + }, "node_modules/emittery": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz", @@ -7073,6 +7087,30 @@ "node": ">=10" } }, + "node_modules/filename-reserved-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz", + "integrity": "sha512-lc1bnsSr4L4Bdif8Xb/qrtokGbq5zlsms/CYH8PP+WtCkGNF65DPiQY8vG3SakEdRn8Dlnm+gW/qWKKjS5sZzQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/filenamify": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-4.3.0.tgz", + "integrity": "sha512-hcFKyUG57yWGAzu1CMt/dPzYZuv+jAJUT85bL8mrXvNe6hWj6yEHEc4EdcgiA6Z3oi1/9wXJdZPXF2dZNgwgOg==", + "dependencies": { + "filename-reserved-regex": "^2.0.0", + "strip-outer": "^1.0.1", + "trim-repeated": "^1.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/filesize": { "version": "8.0.7", "resolved": "https://registry.npmjs.org/filesize/-/filesize-8.0.7.tgz", @@ -7508,6 +7546,87 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/gh-pages": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-3.2.3.tgz", + "integrity": "sha512-jA1PbapQ1jqzacECfjUaO9gV8uBgU6XNMV0oXLtfCX3haGLe5Atq8BxlrADhbD6/UdG9j6tZLWAkAybndOXTJg==", + "dependencies": { + "async": "^2.6.1", + "commander": "^2.18.0", + "email-addresses": "^3.0.1", + "filenamify": "^4.3.0", + "find-cache-dir": "^3.3.1", + "fs-extra": "^8.1.0", + "globby": "^6.1.0" + }, + "bin": { + "gh-pages": "bin/gh-pages.js", + "gh-pages-clean": "bin/gh-pages-clean.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/gh-pages/node_modules/array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==", + "dependencies": { + "array-uniq": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gh-pages/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "node_modules/gh-pages/node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/gh-pages/node_modules/globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==", + "dependencies": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gh-pages/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/gh-pages/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", @@ -11470,6 +11589,33 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", + "dependencies": { + "pinkie": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/pirates": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", @@ -14216,6 +14362,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strip-outer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz", + "integrity": "sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==", + "dependencies": { + "escape-string-regexp": "^1.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/style-loader": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.1.tgz", @@ -14680,6 +14837,17 @@ "node": ">=8" } }, + "node_modules/trim-repeated": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz", + "integrity": "sha512-pkonvlKk8/ZuR0D5tLW8ljt5I8kmxp2XKymhepUeOdCEfKpZaktSArkLHZt76OB1ZvO9bssUsDty4SWhLvZpLg==", + "dependencies": { + "escape-string-regexp": "^1.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/tryer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz", @@ -18750,6 +18918,11 @@ "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==" }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==" + }, "array.prototype.flat": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.0.tgz", @@ -20080,6 +20253,11 @@ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.131.tgz", "integrity": "sha512-oi3YPmaP87hiHn0c4ePB67tXaF+ldGhxvZnT19tW9zX6/Ej+pLN0Afja5rQ6S+TND7I9EuwQTT8JYn1k7R7rrw==" }, + "email-addresses": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/email-addresses/-/email-addresses-3.1.0.tgz", + "integrity": "sha512-k0/r7GrWVL32kZlGwfPNgB2Y/mMXVTq/decgLczm/j34whdaspNrZO8CnXPf1laaHxI6ptUlsnAxN+UAPw+fzg==" + }, "emittery": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz", @@ -20909,6 +21087,21 @@ } } }, + "filename-reserved-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz", + "integrity": "sha512-lc1bnsSr4L4Bdif8Xb/qrtokGbq5zlsms/CYH8PP+WtCkGNF65DPiQY8vG3SakEdRn8Dlnm+gW/qWKKjS5sZzQ==" + }, + "filenamify": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-4.3.0.tgz", + "integrity": "sha512-hcFKyUG57yWGAzu1CMt/dPzYZuv+jAJUT85bL8mrXvNe6hWj6yEHEc4EdcgiA6Z3oi1/9wXJdZPXF2dZNgwgOg==", + "requires": { + "filename-reserved-regex": "^2.0.0", + "strip-outer": "^1.0.1", + "trim-repeated": "^1.0.0" + } + }, "filesize": { "version": "8.0.7", "resolved": "https://registry.npmjs.org/filesize/-/filesize-8.0.7.tgz", @@ -21202,6 +21395,70 @@ "get-intrinsic": "^1.1.1" } }, + "gh-pages": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-3.2.3.tgz", + "integrity": "sha512-jA1PbapQ1jqzacECfjUaO9gV8uBgU6XNMV0oXLtfCX3haGLe5Atq8BxlrADhbD6/UdG9j6tZLWAkAybndOXTJg==", + "requires": { + "async": "^2.6.1", + "commander": "^2.18.0", + "email-addresses": "^3.0.1", + "filenamify": "^4.3.0", + "find-cache-dir": "^3.3.1", + "fs-extra": "^8.1.0", + "globby": "^6.1.0" + }, + "dependencies": { + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==", + "requires": { + "array-uniq": "^1.0.1" + } + }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==", + "requires": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" + } + } + }, "glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", @@ -24068,6 +24325,24 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==" + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==" + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", + "requires": { + "pinkie": "^2.0.0" + } + }, "pirates": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", @@ -25940,6 +26215,14 @@ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==" }, + "strip-outer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz", + "integrity": "sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==", + "requires": { + "escape-string-regexp": "^1.0.2" + } + }, "style-loader": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.1.tgz", @@ -26286,6 +26569,14 @@ "punycode": "^2.1.1" } }, + "trim-repeated": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz", + "integrity": "sha512-pkonvlKk8/ZuR0D5tLW8ljt5I8kmxp2XKymhepUeOdCEfKpZaktSArkLHZt76OB1ZvO9bssUsDty4SWhLvZpLg==", + "requires": { + "escape-string-regexp": "^1.0.2" + } + }, "tryer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz", diff --git a/package.json b/package.json index a100866d..295622a4 100644 --- a/package.json +++ b/package.json @@ -1,14 +1,18 @@ { "name": "project1-bootcamp", "version": "0.1.0", + "homepage": "https://lazyhazydaze.github.io/project1-bootcamp/", "private": true, "dependencies": { + "gh-pages": "^3.2.3", "react": "^18.1.0", "react-dom": "^18.1.0", "react-scripts": "5.0.1" }, "scripts": { - "start": "react-scripts start", + "predeploy": "npm run build", + "deploy": "gh-pages -b master -d build", + "start": "WATCHPACK_POLLING=true react-scripts start", "build": "react-scripts build" }, "eslintConfig": { diff --git a/public/index.html b/public/index.html index bd669818..29d61a8a 100644 --- a/public/index.html +++ b/public/index.html @@ -3,6 +3,10 @@ + - Rocket Bootcamp Project + 🅾💲🅿💲 app 💰 diff --git a/src/App.css b/src/App.css index 97b7c578..cbff1b74 100644 --- a/src/App.css +++ b/src/App.css @@ -1,19 +1,189 @@ -.App { - text-align: center; +body { + background-color: #ffc312; +} + +.flex-container { + display: flex; + flex-wrap: nowrap; + justify-content: space-evenly; + padding-top: 20px; } -.App-logo { - height: 40vmin; - pointer-events: none; +.flex-grouplist { + display: flex; + flex-wrap: wrap; + justify-content: space-evenly; + padding-top: 5px; + max-width: 300px; } -.App-header { - background-color: #282c34; - min-height: 100vh; +.flex-receipt { display: flex; flex-direction: column; + justify-content: space-evenly; align-items: center; - justify-content: center; - font-size: calc(10px + 2vmin); + padding-top: 0.375rem; + padding-bottom: 15px; +} + +.flex-arrow { + display: flex; + flex-direction: row; + justify-content: space-between; + font-weight: bold; + width: 200px; + color: #fffbc1; +} + +.flex-tax { + display: flex; + flex-flow: row wrap; + justify-content: space-between; + font-size: smaller; +} + +.green-container { + background-color: #004e00; + border: 5px solid #000000; + border-radius: 50px; + padding: 2.5rem; + color: white; + margin: 2px; +} + +.step { + padding: 5px 13px; + background-color: #ffc312; + border-radius: 50px; + font-size: 1.5rem; + font-weight: 500; + line-height: 1.2; + margin-bottom: 0.5rem; + color: #000000; + font-family: "Bangers"; +} + +.bangers { + font-family: "Bangers"; +} + +.trashcan { + width: 20px; + height: 20px; + fill: #ffc312; +} + +.bin-btn { + background-color: #004e00; + border: none; + cursor: pointer; +} + +.tooltip { + position: relative; + padding: 5px; +} + +.tooltip .tooltiptext { + visibility: hidden; + background-color: black; + color: #fff; + text-align: center; + border-radius: 6px; + padding: 5px 0; + + /* Position the tooltip */ + position: absolute; + z-index: 1; + top: -5px; + left: 105%; +} + +.tooltip:hover .tooltiptext { + visibility: visible; +} + +/* Styling for ExpenseForm.js and GroupForm.js */ + +.flex-spender { + display: flex; + justify-content: space-between; + flex-flow: row wrap; + max-width: 300px; +} + +.white-btn { + background: white; + font-size: 20px; + color: black; + border-radius: 7px; + box-shadow: 0 7px 0px grey; + display: inline-block; + transition: all 0.2s; + position: relative; + padding: 10px 15px; + position: relative; + top: 0; + cursor: pointer; + margin: 0 20px; +} + +.white-btn:active { + top: 3px; + box-shadow: 0 2px 0px darkgrey; + transition: all 0.2s; +} + +.input-field { + margin: 10px 1px; + display: block; + width: 100%; + height: calc(1.5em + 0.75em + 2px); + padding: 0.375rem 0.75rem; + font-size: 1rem; + font-weight: 400; + line-height: 1.5; + color: #495057; + background-color: white; + background-clip: padding-box; + border: 1px solid #ced4da; + border-radius: 0.25rem; + transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; +} + +/* Styling for button in SplitBill.js button */ + +.split_btn { + background: #51a3b8; + font-size: 22px; color: white; + border-radius: 7px; + box-shadow: 0 7px 0px #387796; + display: inline-block; + transition: all 0.2s; + position: relative; + padding: 20px 25px; + position: relative; + top: 0; + cursor: pointer; + margin: 0 20px; +} + +.split_btn:active { + top: 3px; + box-shadow: 0 2px 0px #387796; + transition: all 0.2s; } + +/* Responsive columns - one column layout (vertical) on small screens */ +@media screen and (max-width: 600px) { + .flex-container { + display: flex; + flex-wrap: wrap; + justify-content: space-evenly; + padding-top: 20px; + } +} + +/* https://webdeasy.de/en/top-css-buttons-en/ */ +/* https://www.geeksforgeeks.org/css-units-em-rem-px-vh-vw/ */ diff --git a/src/App.js b/src/App.js index 4a6f800f..11cd228c 100644 --- a/src/App.js +++ b/src/App.js @@ -1,17 +1,217 @@ import React from "react"; -import logo from "./logo.png"; import "./App.css"; +import DisplayExpense from "./components/DisplayExpense"; +import ExpenseForm from "./components/ExpenseForm"; +import GroupForm from "./components/GroupForm"; +import SplitBill from "./components/SplitBill"; +import TrashBinIcon from "./components/TrashBinIcon"; +import ReceiptDisplay from "./components/ReceiptDisplay"; class App extends React.Component { + constructor(props) { + super(props); + this.state = { + uniqueNames: [], + overallReceipt: {}, + group: [], + expenses: [], + hover: -1, + }; + } + + componentDidMount() { + let state = localStorage.getItem("state"); + state = JSON.parse(state); + this.setState(state); + } + + componentDidUpdate() { + localStorage.setItem("state", JSON.stringify(this.state)); + } + + addName = (name) => { + let newGroup = [...this.state.group, name]; + this.setState({ + group: newGroup, + }); + }; + + deleteName = (name) => { + let filteredGroup = this.state.group.filter((x) => x !== name); + + this.setState({ + group: filteredGroup, + }); + }; + + deleteRecord = (e) => { + // deleteRecord takes in a parameter (like addName or deleteName) + // e is the event, e.target is the button, the value in e.target.value is defined in Line 38 of dispay expense which takes its value from the id which is an index. + let key = e.target.value; + let newexpenses = [...this.state.expenses]; + newexpenses.splice(key, 1); + this.setState({ + expenses: newexpenses, + }); + }; + + addRecord = (record) => { + let newArray = [...this.state.expenses, record]; + this.setState({ + expenses: newArray, + }); + }; + + clearRecords = () => { + this.setState({ + expenses: [], + }); + }; + + splitBill = () => { + let expensesList = [...this.state.expenses]; + let spenderList = []; + for (let i = 0; i < expensesList.length; i++) { + spenderList = [...spenderList, ...expensesList[i].spenders]; + } + let uniqueNames = [...new Set(spenderList)]; + + let newReceipt = {}; + for (let k = 0; k < uniqueNames.length; k++) { + var purchase = []; + var cost = []; + var initialValue = 0; + for (let j = 0; j < expensesList.length; j++) { + if (expensesList[j]["spenders"].includes(uniqueNames[k])) { + purchase.push(expensesList[j]["item"]); + cost.push( + expensesList[j]["amount"] / expensesList[j]["spenders"].length + ); + } + + var record = { + purchases: purchase, + costprice: cost, + total: cost.reduce( + (previousValue, currentValue) => previousValue + currentValue, + initialValue + ), + }; + } + newReceipt[uniqueNames[k]] = record; + } + console.log("this.state.newReceipt", newReceipt); + console.log("this.state.uniqueNames", uniqueNames); + this.setState({ + overallReceipt: newReceipt, + uniqueNames: uniqueNames, + }); + }; + + onMouseEnter = (e) => { + this.setState({ hover: e.target.value }); + }; + + onMouseLeave = (e) => { + console.log(e.target); + this.setState({ hover: -1 }); + }; + render() { + let copyGroup = [...this.state.group]; + let copyExpenses = [...this.state.expenses]; + let copyUniqueNames = [...this.state.uniqueNames]; + let sortUniqueNames = copyUniqueNames.sort(); + let copyOverallReceipt = this.state.overallReceipt; + return ( -
-
- logo -

- Edit src/App.js and save to reload. -

-
+
+
+

Split âš¡ My âš¡ Bill

+
+
+
+
+

1. Add person

+
+ +
+ +
+
+

2. Add item

+
+ +
+ +
+
+

3. View Receipt

+
+
+ {sortUniqueNames.map((name) => ( + + ))} +
+ +
+ +
+
+

4. Edit Group List

+
+
+ {copyGroup.map((k, i) => ( +
+ {k}{" "} + +
+ ))} +
+
+
+
+ +
+
+ {this.state.expenses.length > 0 && ( +

📜RECORDS OF EXPENSES📜

+ )} +
+
+ {copyExpenses.map((entry, i) => ( + + ))} +
+
+
+ {this.state.expenses.length > 0 && ( +
+ +
+ )}
); } diff --git a/src/components/DisplayExpense.css b/src/components/DisplayExpense.css new file mode 100644 index 00000000..e0405bb1 --- /dev/null +++ b/src/components/DisplayExpense.css @@ -0,0 +1,48 @@ +* { + box-sizing: border-box; +} + +body { + font-family: Arial, Helvetica, sans-serif; +} + +/* Float four columns side by side */ +.column { + float: left; + width: 25%; + padding: 5px 5px; +} + +/* Remove extra left and right margins, due to padding in columns */ +.row-flex { + margin: 0 -5px; + display: flex; + flex-wrap: wrap; + justify-content: flex-start; + padding-top: 20px; +} + +/* Clear floats after the columns */ +.row-flex:after { + content: ""; + display: table; + clear: both; +} + +/* Style the counter cards */ +.card { + box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2); /* this adds the "card" effect */ + padding: 16px; + text-align: center; + background-color: #f1f1f1; + height: 100%; +} + +/* Responsive columns - one column layout (vertical) on small screens */ +@media screen and (max-width: 600px) { + .column { + width: 100%; + display: block; + margin-bottom: 20px; + } +} diff --git a/src/components/DisplayExpense.js b/src/components/DisplayExpense.js new file mode 100644 index 00000000..54428b7f --- /dev/null +++ b/src/components/DisplayExpense.js @@ -0,0 +1,45 @@ +import React from "react"; +import "./DisplayExpense.css"; + +const getFormattedPrice = (price) => { + const priceTwoDecimal = Number(price).toFixed(2); + return priceTwoDecimal; +}; + +const getRandomColor = () => { + var letters = "0123456789ABCDEF"; + var color = "#"; + for (var i = 0; i < 6; i++) { + color += letters[Math.floor(Math.random() * 16)]; + } + return color; +}; + +export default class DisplayExpense extends React.Component { + render() { + const pricePerPax = () => { + let output = (this.props.amount / this.props.spenders.length).toFixed(2); + return output; + }; + return ( +
+
+

+ {this.props.item.toUpperCase()} +

+

+ ${getFormattedPrice(this.props.amount)} (${pricePerPax()}/px) +

+

+

+ {this.props.spenders.join(" ")}{" "} +

+

+ +
+
+ ); + } +} diff --git a/src/components/ExpenseForm.js b/src/components/ExpenseForm.js new file mode 100644 index 00000000..32adb99a --- /dev/null +++ b/src/components/ExpenseForm.js @@ -0,0 +1,219 @@ +import React from "react"; + +const calcGST = (price) => { + let output = (price * 1.07).toFixed(2); + return output; +}; + +const calcSvcChrg = (price) => { + let output = (price * 1.1).toFixed(2); + return output; +}; + +const calcGstSc = (price) => { + let output = (price * 1.1 * 1.07).toFixed(2); + return output; +}; + +export default class ExpenseForm extends React.Component { + constructor(props) { + super(props); + this.state = { + item: "", + amount: "", + spenders: [], + start: true, + taxOption: "notax", + }; + this.handleChange = this.handleChange.bind(this); + this.handleSubmit = this.handleSubmit.bind(this); + this.handleChangeCheckBox = this.handleChangeCheckBox.bind(this); + } + + handleChange = (e) => { + const { name, value } = e.target; + + if (name === "taxOption") { + let baseprice = this.state.amount; + + if (this.state.taxOption === "gst") { + baseprice = baseprice / 1.07; + } else if (this.state.taxOption === "sc") { + baseprice = baseprice / 1.1; + } else if (this.state.taxOption === "gstsc") { + baseprice = baseprice / 1.177; + } + + if (value === "gst") { + this.setState({ + amount: calcGST(baseprice), + }); + } else if (value === "sc") { + this.setState({ + amount: calcSvcChrg(baseprice), + }); + } else if (value === "gstsc") { + this.setState({ + amount: calcGstSc(baseprice), + }); + } else { + this.setState({ + amount: baseprice.toFixed(2), + }); + } + } + this.setState({ + [name]: value, + }); + }; + + handleChangeCheckBox = (e) => { + const { checked, value } = e.target; + + if (checked) { + const newSpenderList = [...this.state.spenders, value]; + this.setState({ + spenders: newSpenderList, + start: false, + }); + } else { + this.setState({ + spenders: this.state.spenders.filter((e) => e !== value), + start: false, + }); + } + }; + + handleSubmit = (e) => { + e.preventDefault(); + + if (this.state.spenders.length < 1) { + alert("Please select spender field"); + return; + } + + const record = this.state; + this.props.action(record); + this.setState({ + item: "", + amount: "", + spenders: [], + start: true, + taxOption: "notax", + }); + }; + + render() { + let copyOfNameList = [...this.props.fullNameList]; + return ( +
+
+ this.handleChange(e)} + placeholder="Enter Item Name" + /> + this.handleChange(e)} + pattern="^\d*(\.\d{0,2})?$" + placeholder="Enter Price (Up 2 d.p.)" + /> + +
+ + + + + + + +
+ +
+
+ + + Split amongst: + + +
+
+
+ {copyOfNameList.map((name, i) => ( +
+ {this.state.start ? ( + this.handleChangeCheckBox(e)} + /> + ) : ( + this.handleChangeCheckBox(e)} + /> + )} + {name} +
+ ))}{" "} +
+
+
+
+ +
+
+
+ ); + } +} diff --git a/src/components/GroupForm.js b/src/components/GroupForm.js new file mode 100644 index 00000000..d016aedf --- /dev/null +++ b/src/components/GroupForm.js @@ -0,0 +1,56 @@ +import React from "react"; + +export default class GroupForm extends React.Component { + constructor(props) { + super(props); + this.state = { name: "" }; + } + + handleUserInput = (e) => { + this.setState({ + name: e.target.value, + }); + }; + + handleSubmit = (e) => { + e.preventDefault(); + if (!this.state.name) { + return; + } + + const newName = this.state.name.toLowerCase(); + + if (this.props.nameList.includes(newName)) { + this.setState({ + name: "", + }); + } else { + this.props.addName(newName); + this.setState({ + name: "", + }); + } + }; + + render() { + return ( +
+
+ +
+
+
+ +
+
+
+ ); + } +} diff --git a/src/components/ReceiptDisplay.js b/src/components/ReceiptDisplay.js new file mode 100644 index 00000000..e683d6f7 --- /dev/null +++ b/src/components/ReceiptDisplay.js @@ -0,0 +1,28 @@ +import React from "react"; + +export default class ReceiptDisplay extends React.Component { + render() { + return ( +
+
+
{this.props.name}
+
${this.props.receipt.total.toFixed(2)}
+
+ + + + + + + {this.props.receipt.purchases.map((purchase, i) => ( + + + + + ))} +
PurchaseCost
{purchase}${this.props.receipt.costprice[i].toFixed(2)}
+
+
+ ); + } +} diff --git a/src/components/SplitBill.js b/src/components/SplitBill.js new file mode 100644 index 00000000..fefc2b49 --- /dev/null +++ b/src/components/SplitBill.js @@ -0,0 +1,22 @@ +import React from "react"; + +export default class SplitBill extends React.Component { + render() { + return ( +
+
+ +
+
+ {this.props.uniqueName.length > 0 && ( + + (mouse over each person for breakdown) + + )} +
+
+ ); + } +} diff --git a/src/components/TrashBinIcon.js b/src/components/TrashBinIcon.js new file mode 100644 index 00000000..3fced788 --- /dev/null +++ b/src/components/TrashBinIcon.js @@ -0,0 +1,68 @@ +import React from "react"; + +export default class TrashBinIcon extends React.Component { + render() { + return ( + + {Number(this.props.hover) === this.props.value ? ( + + + + + + + ) : ( + + + + + + + )} + + ); + } +}