classwork-68

parent 84c27494
...@@ -8,10 +8,13 @@ ...@@ -8,10 +8,13 @@
"name": "burger-builder-template", "name": "burger-builder-template",
"version": "0.0.0", "version": "0.0.0",
"dependencies": { "dependencies": {
"@reduxjs/toolkit": "^2.5.1",
"axios": "^1.7.9", "axios": "^1.7.9",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-router-dom": "^7.1.1" "react-redux": "^9.2.0",
"react-router-dom": "^7.1.1",
"redux": "^5.0.1"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^22.10.1", "@types/node": "^22.10.1",
...@@ -777,6 +780,30 @@ ...@@ -777,6 +780,30 @@
"@jridgewell/sourcemap-codec": "1.4.14" "@jridgewell/sourcemap-codec": "1.4.14"
} }
}, },
"node_modules/@reduxjs/toolkit": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.5.1.tgz",
"integrity": "sha512-UHhy3p0oUpdhnSxyDjaRDYaw8Xra75UiLbCiRozVPHjfDwNYkh0TsVm/1OmTW8Md+iDAJmYPWUKMvsMc2GtpNg==",
"license": "MIT",
"dependencies": {
"immer": "^10.0.3",
"redux": "^5.0.1",
"redux-thunk": "^3.1.0",
"reselect": "^5.1.0"
},
"peerDependencies": {
"react": "^16.9.0 || ^17.0.0 || ^18 || ^19",
"react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0"
},
"peerDependenciesMeta": {
"react": {
"optional": true
},
"react-redux": {
"optional": true
}
}
},
"node_modules/@types/cookie": { "node_modules/@types/cookie": {
"version": "0.6.0", "version": "0.6.0",
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz",
...@@ -797,16 +824,16 @@ ...@@ -797,16 +824,16 @@
"version": "15.7.5", "version": "15.7.5",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz",
"integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==", "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==",
"dev": true "devOptional": true
}, },
"node_modules/@types/react": { "node_modules/@types/react": {
"version": "18.0.28", "version": "18.3.18",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.28.tgz", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.18.tgz",
"integrity": "sha512-RD0ivG1kEztNBdoAK7lekI9M+azSnitIn85h4iOiaLjaTrMjzslhaqCGaI4IyCJ1RljWiLCEu4jyrLLgqxBTew==", "integrity": "sha512-t4yC+vtgnkYjNSKlFx1jkAhH8LgTo2N/7Qvi83kdEaUtMDiwpbLAktKDaAMlRcJ5eSxZkH74eEGt1ky31d7kfQ==",
"dev": true, "devOptional": true,
"license": "MIT",
"dependencies": { "dependencies": {
"@types/prop-types": "*", "@types/prop-types": "*",
"@types/scheduler": "*",
"csstype": "^3.0.2" "csstype": "^3.0.2"
} }
}, },
...@@ -819,11 +846,11 @@ ...@@ -819,11 +846,11 @@
"@types/react": "*" "@types/react": "*"
} }
}, },
"node_modules/@types/scheduler": { "node_modules/@types/use-sync-external-store": {
"version": "0.16.2", "version": "0.0.6",
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz",
"integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==", "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==",
"dev": true "license": "MIT"
}, },
"node_modules/@vitejs/plugin-react": { "node_modules/@vitejs/plugin-react": {
"version": "3.1.0", "version": "3.1.0",
...@@ -977,7 +1004,7 @@ ...@@ -977,7 +1004,7 @@
"version": "3.1.1", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz",
"integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==", "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==",
"dev": true "devOptional": true
}, },
"node_modules/debug": { "node_modules/debug": {
"version": "4.3.4", "version": "4.3.4",
...@@ -1159,6 +1186,16 @@ ...@@ -1159,6 +1186,16 @@
"node": ">=4" "node": ">=4"
} }
}, },
"node_modules/immer": {
"version": "10.1.1",
"resolved": "https://registry.npmjs.org/immer/-/immer-10.1.1.tgz",
"integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==",
"license": "MIT",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/immer"
}
},
"node_modules/is-core-module": { "node_modules/is-core-module": {
"version": "2.11.0", "version": "2.11.0",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz",
...@@ -1342,6 +1379,29 @@ ...@@ -1342,6 +1379,29 @@
"react": "^18.2.0" "react": "^18.2.0"
} }
}, },
"node_modules/react-redux": {
"version": "9.2.0",
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz",
"integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==",
"license": "MIT",
"dependencies": {
"@types/use-sync-external-store": "^0.0.6",
"use-sync-external-store": "^1.4.0"
},
"peerDependencies": {
"@types/react": "^18.2.25 || ^19",
"react": "^18.0 || ^19",
"redux": "^5.0.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"redux": {
"optional": true
}
}
},
"node_modules/react-refresh": { "node_modules/react-refresh": {
"version": "0.14.0", "version": "0.14.0",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz",
...@@ -1391,6 +1451,27 @@ ...@@ -1391,6 +1451,27 @@
"react-dom": ">=18" "react-dom": ">=18"
} }
}, },
"node_modules/redux": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz",
"integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==",
"license": "MIT"
},
"node_modules/redux-thunk": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz",
"integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==",
"license": "MIT",
"peerDependencies": {
"redux": "^5.0.0"
}
},
"node_modules/reselect": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz",
"integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==",
"license": "MIT"
},
"node_modules/resolve": { "node_modules/resolve": {
"version": "1.22.1", "version": "1.22.1",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
...@@ -1541,6 +1622,15 @@ ...@@ -1541,6 +1622,15 @@
"browserslist": ">= 4.21.0" "browserslist": ">= 4.21.0"
} }
}, },
"node_modules/use-sync-external-store": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz",
"integrity": "sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==",
"license": "MIT",
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
"node_modules/vite": { "node_modules/vite": {
"version": "4.2.1", "version": "4.2.1",
"resolved": "https://registry.npmjs.org/vite/-/vite-4.2.1.tgz", "resolved": "https://registry.npmjs.org/vite/-/vite-4.2.1.tgz",
...@@ -2054,6 +2144,17 @@ ...@@ -2054,6 +2144,17 @@
"@jridgewell/sourcemap-codec": "1.4.14" "@jridgewell/sourcemap-codec": "1.4.14"
} }
}, },
"@reduxjs/toolkit": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.5.1.tgz",
"integrity": "sha512-UHhy3p0oUpdhnSxyDjaRDYaw8Xra75UiLbCiRozVPHjfDwNYkh0TsVm/1OmTW8Md+iDAJmYPWUKMvsMc2GtpNg==",
"requires": {
"immer": "^10.0.3",
"redux": "^5.0.1",
"redux-thunk": "^3.1.0",
"reselect": "^5.1.0"
}
},
"@types/cookie": { "@types/cookie": {
"version": "0.6.0", "version": "0.6.0",
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz",
...@@ -2072,16 +2173,15 @@ ...@@ -2072,16 +2173,15 @@
"version": "15.7.5", "version": "15.7.5",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz",
"integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==", "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==",
"dev": true "devOptional": true
}, },
"@types/react": { "@types/react": {
"version": "18.0.28", "version": "18.3.18",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.28.tgz", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.18.tgz",
"integrity": "sha512-RD0ivG1kEztNBdoAK7lekI9M+azSnitIn85h4iOiaLjaTrMjzslhaqCGaI4IyCJ1RljWiLCEu4jyrLLgqxBTew==", "integrity": "sha512-t4yC+vtgnkYjNSKlFx1jkAhH8LgTo2N/7Qvi83kdEaUtMDiwpbLAktKDaAMlRcJ5eSxZkH74eEGt1ky31d7kfQ==",
"dev": true, "devOptional": true,
"requires": { "requires": {
"@types/prop-types": "*", "@types/prop-types": "*",
"@types/scheduler": "*",
"csstype": "^3.0.2" "csstype": "^3.0.2"
} }
}, },
...@@ -2094,11 +2194,10 @@ ...@@ -2094,11 +2194,10 @@
"@types/react": "*" "@types/react": "*"
} }
}, },
"@types/scheduler": { "@types/use-sync-external-store": {
"version": "0.16.2", "version": "0.0.6",
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz",
"integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==", "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg=="
"dev": true
}, },
"@vitejs/plugin-react": { "@vitejs/plugin-react": {
"version": "3.1.0", "version": "3.1.0",
...@@ -2204,7 +2303,7 @@ ...@@ -2204,7 +2303,7 @@
"version": "3.1.1", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz",
"integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==", "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==",
"dev": true "devOptional": true
}, },
"debug": { "debug": {
"version": "4.3.4", "version": "4.3.4",
...@@ -2323,6 +2422,11 @@ ...@@ -2323,6 +2422,11 @@
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
"dev": true "dev": true
}, },
"immer": {
"version": "10.1.1",
"resolved": "https://registry.npmjs.org/immer/-/immer-10.1.1.tgz",
"integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw=="
},
"is-core-module": { "is-core-module": {
"version": "2.11.0", "version": "2.11.0",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz",
...@@ -2451,6 +2555,15 @@ ...@@ -2451,6 +2555,15 @@
"scheduler": "^0.23.0" "scheduler": "^0.23.0"
} }
}, },
"react-redux": {
"version": "9.2.0",
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz",
"integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==",
"requires": {
"@types/use-sync-external-store": "^0.0.6",
"use-sync-external-store": "^1.4.0"
}
},
"react-refresh": { "react-refresh": {
"version": "0.14.0", "version": "0.14.0",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz",
...@@ -2476,6 +2589,22 @@ ...@@ -2476,6 +2589,22 @@
"react-router": "7.1.1" "react-router": "7.1.1"
} }
}, },
"redux": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz",
"integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w=="
},
"redux-thunk": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz",
"integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==",
"requires": {}
},
"reselect": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz",
"integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w=="
},
"resolve": { "resolve": {
"version": "1.22.1", "version": "1.22.1",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
...@@ -2569,6 +2698,12 @@ ...@@ -2569,6 +2698,12 @@
"picocolors": "^1.0.0" "picocolors": "^1.0.0"
} }
}, },
"use-sync-external-store": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz",
"integrity": "sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==",
"requires": {}
},
"vite": { "vite": {
"version": "4.2.1", "version": "4.2.1",
"resolved": "https://registry.npmjs.org/vite/-/vite-4.2.1.tgz", "resolved": "https://registry.npmjs.org/vite/-/vite-4.2.1.tgz",
......
...@@ -9,10 +9,13 @@ ...@@ -9,10 +9,13 @@
"preview": "vite preview" "preview": "vite preview"
}, },
"dependencies": { "dependencies": {
"@reduxjs/toolkit": "^2.5.1",
"axios": "^1.7.9", "axios": "^1.7.9",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-router-dom": "^7.1.1" "react-redux": "^9.2.0",
"react-router-dom": "^7.1.1",
"redux": "^5.0.1"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^22.10.1", "@types/node": "^22.10.1",
......
import { IngredientNames, Ingredients } from '@/interfaces/Ingredients'; import { IngredientNames } from '@/interfaces/Ingredients';
import BuildControl from './BuildControl/BuildControl'; import BuildControl from './BuildControl/BuildControl';
import { useAppDispatch, useAppSelector } from '@/store/hooks';
import { lessHandler, moreHandler, setOpen } from '@/store/slice/burger.slice';
import './BuildControls.css'; import './BuildControls.css';
interface Props {
price: number
purchasable: boolean
ingredients: Ingredients
lessHandler: (ingKey: IngredientNames) => void
moreHandler: (ingKey: IngredientNames) => void
setOpen: React.Dispatch<React.SetStateAction<boolean>>
}
const BuildControls = ({ingredients, purchasable, price, setOpen, lessHandler, moreHandler}: Props) => { const BuildControls = () => {
const dispatch = useAppDispatch()
const {ingredients, totalPrice, purchasable} = useAppSelector(state => state.burger)
const ingKeys = Object.keys(ingredients) const ingKeys = Object.keys(ingredients)
return ( return (
<div className='BuildControls'> <div className='BuildControls'>
<p>Current Price: <strong>{price}</strong></p> <p>Current Price: <strong>{totalPrice}</strong></p>
{ {
ingKeys.map((ingKey, index) => ( ingKeys.map((ingKey, index) => (
<BuildControl <BuildControl
key={ingKey + index} key={ingKey + index}
type={ingKey as IngredientNames} type={ingKey as IngredientNames}
moreHandler={() => moreHandler(ingKey as IngredientNames)} moreHandler={() => dispatch(moreHandler(ingKey as IngredientNames))}
lessHandler={() => lessHandler(ingKey as IngredientNames)} lessHandler={() => dispatch(lessHandler(ingKey as IngredientNames))}
disabled={!Boolean(ingredients[ingKey as IngredientNames])} disabled={!Boolean(ingredients[ingKey as IngredientNames])}
/> />
)) ))
...@@ -33,7 +31,7 @@ const BuildControls = ({ingredients, purchasable, price, setOpen, lessHandler, m ...@@ -33,7 +31,7 @@ const BuildControls = ({ingredients, purchasable, price, setOpen, lessHandler, m
<button <button
className="OrderButton" className="OrderButton"
disabled={!purchasable} disabled={!purchasable}
onClick={() => setOpen(true)} onClick={() => dispatch(setOpen(true))}
> >
ORDER NOW ORDER NOW
</button> </button>
......
import { ReactNode, useState } from 'react'; import { ReactNode } from 'react';
import { IngredientNames, Ingredients } from '@/interfaces/Ingredients'; import { IngredientNames } from '@/interfaces/Ingredients';
import Ingredient from './Ingredient/Ingredient'; import Ingredient from './Ingredient/Ingredient';
import { useAppSelector } from '@/store/hooks';
import './Burger.css'; import './Burger.css';
interface Props { const Burger = () => {
ingredients: Ingredients const {ingredients} = useAppSelector(state => state.burger)
}
const Burger = ({ingredients}: Props) => {
const ingredientKeys = Object.keys(ingredients) const ingredientKeys = Object.keys(ingredients)
let ingList: ReactNode[] = [] let ingList: ReactNode[] = []
......
import { IngredientNames, Ingredients } from '@/interfaces/Ingredients'; import { IngredientNames } from '@/interfaces/Ingredients';
import { useAppSelector } from '@/store/hooks';
import { useNavigate } from 'react-router-dom';
interface Props { const OrderSummary = () => {
ingredients: Ingredients; const navigate = useNavigate()
price: number;
onClick: VoidFunction const {ingredients, totalPrice} = useAppSelector(state => state.burger)
}
const OrderSummary = ({price, ingredients, onClick}: Props) => {
const ingredientSummary = Object.keys(ingredients) const ingredientSummary = Object.keys(ingredients)
.map((ingKey) => { .map((ingKey) => {
return ( return (
<li key={ingKey}> <li key={ingKey}>
<span style={{textTransform: 'capitalize'}}> <span style={{textTransform: 'capitalize'}}>
{ingKey} {ingKey}
</span>: {ingredients[ingKey as IngredientNames]} </span>: {ingredients[ingKey as IngredientNames]}
...@@ -25,8 +25,8 @@ const OrderSummary = ({price, ingredients, onClick}: Props) => { ...@@ -25,8 +25,8 @@ const OrderSummary = ({price, ingredients, onClick}: Props) => {
<ul> <ul>
{ingredientSummary} {ingredientSummary}
</ul> </ul>
<p><strong>Total Price: {price}</strong></p> <p><strong>Total Price: {totalPrice}</strong></p>
<button onClick={onClick}> <button onClick={() => navigate('/checkout')}>
Continue Continue
</button> </button>
</> </>
......
import Burger from "@/components/Burger/Burger"; import Burger from "@/components/Burger/Burger";
import Button from "@/components/UI/Button/Button"; import Button from "@/components/UI/Button/Button";
import type { Ingredients } from "@/interfaces/Ingredients"; import { useNavigate } from "react-router-dom";
import { MouseEventHandler } from "react";
import "./CheckoutSummary.css"; import "./CheckoutSummary.css";
interface Props {
ingredients: Ingredients;
checkoutContinued: MouseEventHandler<HTMLButtonElement>;
checkoutCancelled: MouseEventHandler<HTMLButtonElement>;
}
const CheckoutSummary = (props: Props) => { const CheckoutSummary = () => {
const navigate = useNavigate();
return ( return (
<div className="CheckoutSummary"> <div className="CheckoutSummary">
<h1>We hope it tastes well!</h1> <h1>We hope it tastes well!</h1>
<div style={{ width: "100%", margin: "auto" }}> <div style={{ width: "100%", margin: "auto" }}>
<Burger ingredients={props.ingredients} /> <Burger />
</div> </div>
<Button <Button
btnType="Danger" btnType="Danger"
click={props.checkoutCancelled} click={() => navigate(-1)}
> >
CANCEL CANCEL
</Button> </Button>
<Button <Button
btnType="Success" btnType="Success"
click={props.checkoutContinued} click={() => navigate('contact-data')}
> >
CONTINUE CONTINUE
</Button> </Button>
......
import { useAppDispatch } from '@/store/hooks'
import { setOpen } from '@/store/slice/burger.slice'
import './Backdrop.css' import './Backdrop.css'
interface Props { const Backdrop = () => {
setOpen: React.Dispatch<React.SetStateAction<boolean>> const dispatch = useAppDispatch()
}
const Backdrop = ({setOpen}: Props) => {
return ( return (
<div className='Backdrop' onClick={() => setOpen(false)}></div> <div className='Backdrop' onClick={() => dispatch(setOpen(false))}></div>
) )
} }
......
import { ReactNode } from "react" import { ReactNode } from "react"
import './Modal.css'
import Backdrop from "../Backdrop/Backdrop" import Backdrop from "../Backdrop/Backdrop"
import './Modal.css'
type TProps = { type TProps = {
children: ReactNode children: ReactNode
open: boolean open: boolean
setOpen: React.Dispatch<React.SetStateAction<boolean>>
} }
const Modal = ({open, setOpen, children}: TProps) => { const Modal = ({open, children}: TProps) => {
return open ? ( return open ? (
<> <>
<Backdrop setOpen={setOpen}/> <Backdrop/>
<div className="Modal"> <div className="Modal">
{children} {children}
</div> </div>
......
...@@ -2,101 +2,19 @@ import BuildControls from '@/components/BuildControls/BuildControls'; ...@@ -2,101 +2,19 @@ import BuildControls from '@/components/BuildControls/BuildControls';
import Burger from '@/components/Burger/Burger'; import Burger from '@/components/Burger/Burger';
import OrderSummary from '@/components/Burger/OrderSummary/OrderSummary'; import OrderSummary from '@/components/Burger/OrderSummary/OrderSummary';
import Modal from '@/components/UI/Modal/Modal'; import Modal from '@/components/UI/Modal/Modal';
import axiosBase from '@/Config/axiosBase'; import { useAppSelector } from '@/store/hooks';
import { IngredientNames, IngredientPrices, Ingredients } from '@/interfaces/Ingredients';
import { useEffect, useState } from 'react';
import { createSearchParams, URLSearchParamsInit, useNavigate } from 'react-router-dom'
const BurgerBuilder = () => { const BurgerBuilder = () => {
const navigate = useNavigate() const {open} = useAppSelector(state => state.burger)
const [totalPrice, setTotalPrice] = useState<number>(IngredientPrices.bread);
const [purchasable, setPurchasable] = useState<boolean>(false);
const [open, setOpen] = useState<boolean>(false);
const [ingredients, setIngredients] = useState<Ingredients>({
salad: 0,
cheese: 0,
chicken: 0,
meat: 0
});
const fetchOrders = async () => {
const {data} = await axiosBase.delete('orders/-OGAn20Ai4QF3lYhaufa')
console.log(data)
}
useEffect(() => {
fetchOrders()
}, [])
const updatePurschasable = (ingredients: Ingredients) => {
const sum = Object.keys(ingredients)
.map(ingKey => ingredients[ingKey as IngredientNames])
.reduce((sum, el) => sum + el, 0)
setPurchasable(sum > 0)
}
const moreHandler = (ingKey: IngredientNames) => {
const oldCount = ingredients[ingKey];
const updatedCount = oldCount + 1;
const updatedIngredients = { ...ingredients };
updatedIngredients[ingKey] = updatedCount;
setIngredients(updatedIngredients);
const priceAddition = IngredientPrices[ingKey];
const newPrice = totalPrice + priceAddition;
setTotalPrice(newPrice);
updatePurschasable(updatedIngredients);
}
const lessHandler = (ingKey: IngredientNames) => {
const oldCount = ingredients[ingKey];
if (oldCount <= 0) {
return;
}
const updatedCount = oldCount - 1;
const updatedIngredients = { ...ingredients };
updatedIngredients[ingKey] = updatedCount;
setIngredients(updatedIngredients);
const priceAddition = IngredientPrices[ingKey];
const newPrice = totalPrice - priceAddition;
setTotalPrice(newPrice);
updatePurschasable(updatedIngredients);
}
const onContinueHandler = () => {
const params = createSearchParams(ingredients as unknown as URLSearchParamsInit);
navigate({
pathname: '/checkout',
search: `?${params.toString()}`
})
}
return ( return (
<> <>
<Modal open={open} setOpen={setOpen}> <Modal open={open}>
<OrderSummary <OrderSummary/>
price={totalPrice}
ingredients={ingredients}
onClick={onContinueHandler}
/>
</Modal> </Modal>
<Burger ingredients={ingredients}/> <Burger/>
<BuildControls <BuildControls/>
ingredients={ingredients}
lessHandler={lessHandler}
moreHandler={moreHandler}
setOpen={setOpen}
price={totalPrice}
purchasable={purchasable}
/>
</> </>
) )
} }
......
import {useRef} from 'react';
import type {Ingredients} from '@/interfaces/Ingredients';
import CheckoutSummary from '@/components/Order/CheckoutSummary/CheckoutSummary'; import CheckoutSummary from '@/components/Order/CheckoutSummary/CheckoutSummary';
import { useNavigate, useSearchParams, Outlet } from 'react-router-dom'; import { Outlet } from 'react-router-dom';
const parseSearch = <T extends Object,>(searchParams: URLSearchParams): T => {
return Object.fromEntries(searchParams) as unknown as T;
}
const Checkout = () => { const Checkout = () => {
const navigate = useNavigate();
const [searchParams] = useSearchParams();
const ingredients = useRef(parseSearch<Ingredients>(searchParams));
const checkoutCancelledHandler = () => {
navigate(-1);
};
const checkoutContinuedHandler = () => {
navigate('contact-data', {state: {ingredients: ingredients.current}});
};
return ( return (
<> <>
<CheckoutSummary <CheckoutSummary/>
ingredients={ingredients.current}
checkoutCancelled={checkoutCancelledHandler}
checkoutContinued={checkoutContinuedHandler}
/>
<Outlet/> <Outlet/>
</> </>
) )
......
import Button from "@/components/UI/Button/Button"; import Button from "@/components/UI/Button/Button";
import axiosBase from "@/Config/axiosBase"; import axiosBase from "@/config/axiosBase";
import { useState } from "react"; import { useState } from "react";
import { ICustomer } from "@/interfaces/checkout"; import { ICustomer } from "@/interfaces/checkout";
import { useLocation } from "react-router-dom"; import { useLocation } from "react-router-dom";
......
...@@ -3,15 +3,19 @@ import { BrowserRouter, Route, Routes } from 'react-router-dom' ...@@ -3,15 +3,19 @@ import { BrowserRouter, Route, Routes } from 'react-router-dom'
import BurgerBuilder from './containers/BurgerBuilder/BurgerBuilder' import BurgerBuilder from './containers/BurgerBuilder/BurgerBuilder'
import Checkout from './containers/Checkout/Checkout' import Checkout from './containers/Checkout/Checkout'
import ContactData from './containers/Checkout/ContactData/ContactData' import ContactData from './containers/Checkout/ContactData/ContactData'
import { Provider } from 'react-redux'
import { store } from './store'
import './index.css' import './index.css'
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<BrowserRouter> <Provider store={store}>
<Routes> <BrowserRouter>
<Route path='/' element={<BurgerBuilder />}/> <Routes>
<Route path="/checkout" element={<Checkout/>}> <Route path='/' element={<BurgerBuilder />}/>
<Route path="contact-data" element={<ContactData/>}/> <Route path="/checkout" element={<Checkout/>}>
</Route> <Route path="contact-data" element={<ContactData/>}/>
</Routes> </Route>
</BrowserRouter> </Routes>
</BrowserRouter>
</Provider>
) )
import { useDispatch, useSelector } from 'react-redux'
import type { TypedUseSelectorHook } from 'react-redux'
import type { RootState, AppDispatch } from './index'
export const useAppDispatch: () => AppDispatch = useDispatch
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector
\ No newline at end of file
import { configureStore } from "@reduxjs/toolkit";
import { burgerSlice } from "./slice/burger.slice";
export const store = configureStore({
reducer: {
burger: burgerSlice.reducer
}
})
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
export default store;
\ No newline at end of file
import { IngredientNames, IngredientPrices, Ingredients } from "@/interfaces/Ingredients";
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
interface IState {
totalPrice: number
purchasable: boolean
open: boolean
ingredients: Ingredients
}
const initialState: IState = {
totalPrice: IngredientPrices.bread,
purchasable: false,
open: false,
ingredients: {
salad: 0,
cheese: 0,
chicken: 0,
meat: 0
}
}
export const burgerSlice = createSlice({
name: 'burger',
initialState,
reducers: {
setOpen(state, action: PayloadAction<boolean>) {
state.open = action.payload
},
moreHandler(state, action: PayloadAction<IngredientNames>) {
const ingKey = action.payload
const oldCount = state.ingredients[ingKey]
state.ingredients[ingKey] = oldCount + 1
const priceAddition = IngredientPrices[ingKey];
state.totalPrice += priceAddition
const sum = Object.keys(state.ingredients)
.map(ingKey => state.ingredients[ingKey as IngredientNames])
.reduce((sum, el) => sum + el, 0)
state.purchasable = sum > 0
},
lessHandler(state, action: PayloadAction<IngredientNames>) {
const ingKey = action.payload
const oldCount = state.ingredients[ingKey]
if (oldCount <= 0) return;
state.ingredients[ingKey] = oldCount - 1
const priceAddition = IngredientPrices[ingKey];
state.totalPrice -= priceAddition
const sum = Object.keys(state.ingredients)
.map(ingKey => state.ingredients[ingKey as IngredientNames])
.reduce((sum, el) => sum + el, 0)
state.purchasable = sum > 0
}
}
})
export const {setOpen, moreHandler, lessHandler} = burgerSlice.actions
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment