Source: frontend/menu/Menu.js

  1. //Brennan Wilkes
  2. import React from "react";
  3. import "../bootstrap-import.js";
  4. import axios from "axios";
  5. import moment from "moment";
  6. import barImage from "../../../assets/bar-stock.jpg";
  7. import "./menu.css";
  8. import Nav from "../nav/Nav.js";
  9. import DrinkDetails from "../detailedViews/DrinkDetails.js";
  10. import DrinkIcon from "../iconViews/DrinkIcon.js";
  11. import IngredientDetails from "../detailedViews/IngredientDetails.js";
  12. import IngredientIcon from "../iconViews/IngredientIcon.js";
  13. import DetailedViewController from "../detailedViews/DetailedViewController.js";
  14. //capitalization one liner using regex
  15. const capitalize = s => String(s).toLowerCase().replace(/(?:^|\s|["'([{])+\S/g, l => l.toUpperCase());
  16. /**
  17. Customer menu component.
  18. Connects all other customer visible frontend components together including
  19. navigation, search and advanced search, handling search results and displaying
  20. them as icons, expanding those icons into detailed views, and handling
  21. ordering and deleting of items.
  22. @class
  23. @memberof frontend
  24. @extends {@link DetailedViewController}
  25. */
  26. class Menu extends DetailedViewController {
  27. /**
  28. Initializes state, binds methods, and queries database for initial data.
  29. @param {any[]} props Should contain a username
  30. */
  31. constructor(props){
  32. super(props);
  33. this.search = this.search.bind(this);
  34. this.advSearch = this.advSearch.bind(this);
  35. this.advancedSearchToggle = this.advancedSearchToggle.bind(this);
  36. this.orderDrink = this.orderDrink.bind(this);
  37. this.state = {
  38. drinks: [],
  39. glasses: [],
  40. detailedDrink: undefined,
  41. detailedIngredient: undefined,
  42. orderButtonMd: "success",
  43. orderButtonContent: "ORDER"
  44. };
  45. //Query for drinks
  46. axios.get("/drinks").then(res => this.setState({drinks:res.data}));
  47. //Query for glasses
  48. axios.get("/glasses").then(res => this.setState({glasses:res.data}));
  49. }
  50. /**
  51. Callback for drink ordering events.
  52. Updates state, runs queries and updates ORDER button
  53. @param {number} id ID of drink to order
  54. */
  55. orderDrink(id){
  56. //Update order button
  57. this.setState({
  58. orderButtonMd: "secondary",
  59. orderButtonContent: "PROCESSING"
  60. });
  61. //Query database
  62. axios.post('/purchase',{
  63. drinkId: id,
  64. userName: this.props.user
  65. }).then(res => {
  66. //On a slight delay, update button. This delay is for a more responsive feeling interface
  67. setTimeout(() => {
  68. this.setState({
  69. orderButtonMd: "success",
  70. orderButtonContent: "SUCCESS"
  71. });
  72. },500);
  73. //Reset button
  74. setTimeout(() => {
  75. if(this.state.orderButtonContent === "SUCCESS"){
  76. this.setState({orderButtonContent: "ORDER"})
  77. }
  78. },3000);
  79. }).catch(err => {
  80. //Reflect insertion failure
  81. this.setState({
  82. orderButtonMd: "danger",
  83. orderButtonContent: "OUT OF STOCK"
  84. });
  85. });
  86. }
  87. /**
  88. Updates the main application background image to ensure it's set correctly
  89. */
  90. componentDidMount(){
  91. $("main").css("backgroundImage",`url(${barImage})`);
  92. $("main").css("background-color","#00000080");
  93. $("main").css("background-blend-mode","overlay");
  94. $("#menu").css("marginTop","7.5%");
  95. //Trigger margin-top autoadjustment
  96. this.advancedSearchToggle();
  97. }
  98. /**
  99. Basic search callback
  100. @param {string} name Name of drink to search for
  101. */
  102. search(query){
  103. axios.post('/drinks',{
  104. name: query
  105. }).then(res => {
  106. //Fixes a weird react bug
  107. this.setState({drinks:[]});
  108. //Update state data
  109. this.setState({drinks:res.data});
  110. });
  111. }
  112. /**
  113. Callback to be run on component mount and advanced search toggle.
  114. Updates the page's top margin to accurately reflect the change in
  115. fixed navigation size
  116. */
  117. advancedSearchToggle(){
  118. setTimeout(()=>{
  119. $("#menu").animate({
  120. marginTop: `${$("#nav-wrapper").height() + 50}px`
  121. },200);
  122. },250);
  123. //Auto hides detailed views on advanced search toggle
  124. this.setState({
  125. detailedDrink: undefined,
  126. detailedIngredient: undefined
  127. });
  128. }
  129. /**
  130. Advanced search callback
  131. @param {object} query Contains all the data from AdvancedSearch to send to the database
  132. */
  133. advSearch(query){
  134. axios.post('/drinks/advanced',query).then(res => {
  135. this.setState({drinks:[]});
  136. this.setState({drinks:res.data});
  137. });
  138. }
  139. /**
  140. Renders out the menu
  141. Renders a navigation bar, rows of 6 drink icons, and hidden drink and
  142. ingredient detailed view components.
  143. */
  144. render() {
  145. //Split the drinks list into subgroups of 6 to insert into rows.
  146. let splitDrinks = [];
  147. for(let i=0;i<this.state.drinks.length;i+=6){
  148. splitDrinks.push(this.state.drinks.slice(i,i+6));
  149. }
  150. return <>
  151. <Nav user={this.props.user}
  152. searchCallback={this.search}
  153. advSearchCallback={this.advSearch}
  154. advancedSearchToggleCallback={this.advancedSearchToggle}
  155. glasses={this.state.glasses} />
  156. <div className="container-fluid" id="menu">{
  157. splitDrinks.map(r => {
  158. return <>
  159. <div className="row">{
  160. r.map(d => {
  161. return <>
  162. <div className="col-sm-6 col-md-4 col-xl-2 d-flex justify-content-center mb-4">
  163. <div className="menuIconWrapper">
  164. <DrinkIcon drinkInfo={d} clickCallback={id=>{
  165. $("#advancedOptions").removeClass("show");
  166. this.advancedSearchToggle();
  167. this.updateDetailedDrink(id);
  168. }} />
  169. </div>
  170. </div>
  171. </>
  172. })
  173. }</div>
  174. </>;
  175. })
  176. }</div>
  177. <DrinkDetails
  178. drinkId={this.state.detailedDrink}
  179. changeIngredient={this.updateDetailedIngrident}
  180. changeDrink={id => {
  181. this.setState({
  182. orderButtonMd: "success",
  183. orderButtonContent: "ORDER"
  184. });
  185. this.updateDetailedDrink(id);
  186. }}
  187. orderCallback={this.orderDrink}
  188. orderButtonMd={this.state.orderButtonMd}
  189. orderButtonContent={this.state.orderButtonContent} />
  190. <IngredientDetails
  191. ingredientId={this.state.detailedIngredient}
  192. changeIngredient={this.updateDetailedIngrident} />
  193. </>
  194. }
  195. }
  196. export default Menu;