|
|
@@ -1270,6 +1270,24 @@ class Transaction{
|
|
|
get recipes(){
|
|
|
return this._recipes;
|
|
|
}
|
|
|
+
|
|
|
+ /*
|
|
|
+ Gets the quantity for a given ingredient
|
|
|
+ */
|
|
|
+ getIngredientQuantity(ingredient){
|
|
|
+ let quantity = 0;
|
|
|
+
|
|
|
+ for(let i = 0; i < this._recipes.length; i++){
|
|
|
+ const recipe = this._recipes[i].recipe;
|
|
|
+ for(let j = 0; j < recipe.ingredients.length; j++){
|
|
|
+ if(recipe.ingredients[j].ingredient === ingredient){
|
|
|
+ quantity += recipe.ingredients[j].quantity;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return quantity;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
module.exports = Transaction;
|
|
|
@@ -1277,7 +1295,7 @@ module.exports = Transaction;
|
|
|
const home = require("./strands/home.js");
|
|
|
const ingredients = require("./strands/ingredients.js");
|
|
|
const recipeBook = require("./strands/recipeBook.js");
|
|
|
-const analytics = require("./strands/analytics.js");
|
|
|
+const analytics = require("./strands/analytics2.js");
|
|
|
const orders = require("./strands/orders.js");
|
|
|
const transactions = require("./strands/transactions.js");
|
|
|
|
|
|
@@ -1564,7 +1582,7 @@ document.getElementById("ordersBtn").onclick = async ()=>{
|
|
|
document.getElementById("transactionsBtn").onclick = ()=>{controller.openStrand("transactions", merchant.getTransactions())};
|
|
|
|
|
|
controller.openStrand("home");
|
|
|
-},{"./classes/Ingredient.js":1,"./classes/Merchant.js":2,"./classes/Order.js":3,"./classes/Recipe.js":4,"./classes/Transaction.js":5,"./sidebars/editIngredient.js":7,"./sidebars/editRecipe.js":8,"./sidebars/ingredientDetails.js":9,"./sidebars/newIngredient.js":10,"./sidebars/newOrder.js":11,"./sidebars/newRecipe.js":12,"./sidebars/newTransaction.js":13,"./sidebars/orderDetails.js":14,"./sidebars/orderFilter.js":15,"./sidebars/recipeDetails.js":16,"./sidebars/transactionDetails.js":17,"./sidebars/transactionFilter.js":18,"./strands/analytics.js":19,"./strands/home.js":20,"./strands/ingredients.js":21,"./strands/orders.js":22,"./strands/recipeBook.js":23,"./strands/transactions.js":24}],7:[function(require,module,exports){
|
|
|
+},{"./classes/Ingredient.js":1,"./classes/Merchant.js":2,"./classes/Order.js":3,"./classes/Recipe.js":4,"./classes/Transaction.js":5,"./sidebars/editIngredient.js":7,"./sidebars/editRecipe.js":8,"./sidebars/ingredientDetails.js":9,"./sidebars/newIngredient.js":10,"./sidebars/newOrder.js":11,"./sidebars/newRecipe.js":12,"./sidebars/newTransaction.js":13,"./sidebars/orderDetails.js":14,"./sidebars/orderFilter.js":15,"./sidebars/recipeDetails.js":16,"./sidebars/transactionDetails.js":17,"./sidebars/transactionFilter.js":18,"./strands/analytics2.js":19,"./strands/home.js":20,"./strands/ingredients.js":21,"./strands/orders.js":22,"./strands/recipeBook.js":23,"./strands/transactions.js":24}],7:[function(require,module,exports){
|
|
|
let editIngredient = {
|
|
|
display: function(ingredient){
|
|
|
let buttonList = document.getElementById("unitButtons");
|
|
|
@@ -2841,155 +2859,109 @@ let transactionFilter = {
|
|
|
module.exports = transactionFilter;
|
|
|
},{}],19:[function(require,module,exports){
|
|
|
let analytics = {
|
|
|
- newData: false,
|
|
|
- dateChange: false,
|
|
|
- transactions: [],
|
|
|
- ingredient: {},
|
|
|
- recipe: {},
|
|
|
-
|
|
|
- display: function(Transaction){
|
|
|
- document.getElementById("analDateBtn").onclick = ()=>{this.changeDates(Transaction)};
|
|
|
+ isPopulated: false,
|
|
|
+ recipesDisplay: false,
|
|
|
+ transactionsByDate: [],
|
|
|
|
|
|
- if(this.transactions.length === 0 || this.newData === true){
|
|
|
- let startDate = new Date();
|
|
|
- startDate.setMonth(startDate.getMonth() - 1);
|
|
|
- this.transactions = merchant.getTransactions(startDate);
|
|
|
- }
|
|
|
+ display: async function(Transaction){
|
|
|
+ if(!this.isPopulated){
|
|
|
+ let to = new Date()
|
|
|
+ let from = new Date(to.getFullYear(), to.getMonth() - 1, to.getDate());
|
|
|
+ await this.getData(from, to, Transaction);
|
|
|
|
|
|
- let slider = document.getElementById("analSlider");
|
|
|
- slider.onchange = ()=>{this.display(Transaction)};
|
|
|
+ let analSlider = document.getElementById("analSlider");
|
|
|
+ analSlider.onclick = ()=>{this.switchDisplay()};
|
|
|
+ analSlider.checked = false;
|
|
|
+ document.getElementById("analRecipeContent").style.display = "none";
|
|
|
|
|
|
- let ingredientContent = document.getElementById("analIngredientContent");
|
|
|
- let recipeContent = document.getElementById("analRecipeContent");
|
|
|
|
|
|
- if(slider.checked){
|
|
|
- ingredientContent.style.display = "none";
|
|
|
- recipeContent.style.display = "flex";
|
|
|
- this.displayRecipes();
|
|
|
- }else{
|
|
|
- ingredientContent.style.display = "flex";
|
|
|
- recipeContent.style.display = "none"
|
|
|
- this.displayIngredients();
|
|
|
+ this.populateButtons();
|
|
|
+ this.displayIngredient(merchant.ingredients[0].ingredient);
|
|
|
+ this.displayRecipe(merchant.recipes[0]);
|
|
|
+
|
|
|
+ this.isPopulated = true;
|
|
|
}
|
|
|
},
|
|
|
|
|
|
- displayIngredients: function(){
|
|
|
- const itemsList = document.getElementById("itemsList");
|
|
|
-
|
|
|
- while(itemsList.children.length > 0){
|
|
|
- itemsList.removeChild(itemsList.firstChild);
|
|
|
- }
|
|
|
+ populateButtons: function(){
|
|
|
+ let ingredientButtons = document.getElementById("analIngredientList");
|
|
|
+ let recipeButtons = document.getElementById("analRecipeList");
|
|
|
|
|
|
for(let i = 0; i < merchant.ingredients.length; i++){
|
|
|
- let li = document.createElement("li");
|
|
|
- li.classList.add("choosable");
|
|
|
- li.item = merchant.ingredients[i];
|
|
|
- li.innerText = merchant.ingredients[i].ingredient.name;
|
|
|
- li.onclick = ()=>{
|
|
|
- const itemsList = document.getElementById("itemsList");
|
|
|
- for(let i = 0; i < itemsList.children.length; i++){
|
|
|
- itemsList.children[i].classList.remove("active");
|
|
|
- }
|
|
|
-
|
|
|
- li.classList.add("active");
|
|
|
-
|
|
|
- this.ingredient = merchant.ingredients[i];
|
|
|
- this.ingredientDisplay();
|
|
|
- };
|
|
|
- itemsList.appendChild(li);
|
|
|
- }
|
|
|
-
|
|
|
- if(this.dateChange && Object.keys(this.ingredient).length !== 0){
|
|
|
- this.ingredientDisplay();
|
|
|
- }
|
|
|
- this.dateChange = false;
|
|
|
- },
|
|
|
-
|
|
|
- displayRecipes: function(){
|
|
|
- let recipeList = document.getElementById("analRecipeList");
|
|
|
- while(recipeList.children.length > 0){
|
|
|
- recipeList.removeChild(recipeList.firstChild);
|
|
|
+ let button = document.createElement("button");
|
|
|
+ button.innerText = merchant.ingredients[i].ingredient.name;
|
|
|
+ button.classList.add("choosable");
|
|
|
+ button.onclick = ()=>{this.displayIngredient(merchant.ingredients[i].ingredient)};
|
|
|
+ ingredientButtons.appendChild(button);
|
|
|
}
|
|
|
|
|
|
for(let i = 0; i < merchant.recipes.length; i++){
|
|
|
- let li = document.createElement("li");
|
|
|
- li.classList.add("choosable");
|
|
|
- li.recipe = merchant.recipes[i];
|
|
|
- li.innerText = merchant.recipes[i].name;
|
|
|
- li.onclick = ()=>{
|
|
|
- let recipeList = document.getElementById("analRecipeList");
|
|
|
- for(let i = 0; i < recipeList.children.length; i++){
|
|
|
- recipeList.children[i].classList.remove("active");
|
|
|
- }
|
|
|
- li.classList.add("active");
|
|
|
-
|
|
|
- this.recipe = merchant.recipes[i];
|
|
|
- this.recipeDisplay();
|
|
|
- }
|
|
|
-
|
|
|
- recipeList.appendChild(li);
|
|
|
- }
|
|
|
-
|
|
|
- if(this.dateChange && Object.keys(this.recipe).length !== 0){
|
|
|
- this.recipeDisplay();
|
|
|
+ let button = document.createElement("button");
|
|
|
+ button.innerText = merchant.recipes[i].name;
|
|
|
+ button.classList.add("choosable");
|
|
|
+ button.onclick = ()=>{this.displayRecipe(merchant.recipes[i])};
|
|
|
+ recipeButtons.appendChild(button);
|
|
|
}
|
|
|
- this.dateChange = false;
|
|
|
},
|
|
|
|
|
|
- ingredientDisplay: function(){
|
|
|
- //Get list of recipes that contain the ingredient
|
|
|
- let containingRecipes = [];
|
|
|
+ getData: function(from, to, Transaction){
|
|
|
+ let loader = document.getElementById("loaderContainer");
|
|
|
+ loader.style.display = "flex";
|
|
|
|
|
|
- for(let i = 0; i < merchant.recipes.length; i++){
|
|
|
- for(let j = 0; j < merchant.recipes[i].ingredients.length; j++){
|
|
|
- if(merchant.recipes[i].ingredients[j].ingredient === this.ingredient.ingredient){
|
|
|
- containingRecipes.push({
|
|
|
- recipe: merchant.recipes[i],
|
|
|
- quantity: merchant.recipes[i].ingredients[j].quantity
|
|
|
- });
|
|
|
+ return fetch(`/transactions/${from.toISOString()}/${to.toISOString()}`)
|
|
|
+ .then(response => response.json())
|
|
|
+ .then((response)=>{
|
|
|
+ if(typeof(response) === "string"){
|
|
|
+ banner.createError(response);
|
|
|
+ }else{
|
|
|
+ this.transactionsByDate = [];
|
|
|
|
|
|
- break;
|
|
|
+ for(let i = 0; i < response.length; i++){
|
|
|
+ const date = new Date(response[i].date);
|
|
|
+ let newDate = {
|
|
|
+ date: date,
|
|
|
+ transactions: []
|
|
|
+ };
|
|
|
+
|
|
|
+ for(let j = 0; j < response[i].transactions.length; j++){
|
|
|
+ newDate.transactions.push(new Transaction(
|
|
|
+ response[i].transactions[j]._id,
|
|
|
+ date,
|
|
|
+ response[i].transactions[j].recipes,
|
|
|
+ merchant
|
|
|
+ ));
|
|
|
+ }
|
|
|
+
|
|
|
+ this.transactionsByDate.push(newDate);
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
- }
|
|
|
+ })
|
|
|
+ .catch((err)=>{
|
|
|
+ banner.createError("UNABLE TO UPDATE THE PAGE");
|
|
|
+ })
|
|
|
+ .finally(()=>{
|
|
|
+ loader.style.display = "none";
|
|
|
+ });
|
|
|
+ },
|
|
|
|
|
|
- //Create Graph
|
|
|
- let quantities = [];
|
|
|
+ displayIngredient: function(ingredient){
|
|
|
+ //break down data into dates and quantities
|
|
|
let dates = [];
|
|
|
- let currentDate = (this.transactions.length > 0) ? this.transactions[0].date : undefined;
|
|
|
- let currentQuantity = 0;
|
|
|
-
|
|
|
- for(let i = 0; i < this.transactions.length; i++){
|
|
|
- if(currentDate.getDate() !== this.transactions[i].date.getDate()){
|
|
|
- quantities.push(currentQuantity);
|
|
|
- dates.push(currentDate);
|
|
|
- currentQuantity = 0;
|
|
|
- currentDate = this.transactions[i].date;
|
|
|
- }
|
|
|
-
|
|
|
- for(let j = 0; j < this.transactions[i].recipes.length; j++){
|
|
|
- for(let k = 0; k < containingRecipes.length; k++){
|
|
|
- if(this.transactions[i].recipes[j].recipe === containingRecipes[k].recipe){
|
|
|
- for(let l = 0; l < this.transactions[i].recipes[j].recipe.ingredients.length; l++){
|
|
|
- const transIngredient = this.transactions[i].recipes[j].recipe.ingredients[l];
|
|
|
-
|
|
|
- if(transIngredient.ingredient === this.ingredient.ingredient){
|
|
|
-
|
|
|
- currentQuantity += transIngredient.quantity * this.transactions[i].recipes[j].quantity;
|
|
|
+ let quantities = [];
|
|
|
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
+ for(let i = 0; i < this.transactionsByDate.length; i++){
|
|
|
+ dates.push(this.transactionsByDate[i].date);
|
|
|
|
|
|
- if(i === this.transactions.length - 1){
|
|
|
- quantities.push(currentQuantity);
|
|
|
- dates.push(currentDate);
|
|
|
+ let sum = 0;
|
|
|
+ for(let j = 0; j < this.transactionsByDate[i].transactions.length; j++){
|
|
|
+ let transactions = this.transactionsByDate[i].transactions[j];
|
|
|
+ sum += transactions.getIngredientQuantity(ingredient);
|
|
|
}
|
|
|
+
|
|
|
+ quantities.push(sum);
|
|
|
}
|
|
|
|
|
|
+ //create and display the graph
|
|
|
let trace = {
|
|
|
x: dates,
|
|
|
y: quantities,
|
|
|
@@ -3000,168 +2972,76 @@ let analytics = {
|
|
|
}
|
|
|
|
|
|
const layout = {
|
|
|
- title: this.ingredient.ingredient.name.toUpperCase(),
|
|
|
- xaxis: {
|
|
|
- title: "DATE"
|
|
|
- },
|
|
|
- yaxis: {
|
|
|
- title: `QUANTITY (${this.ingredient.ingredient.unit.toUpperCase()})`,
|
|
|
- }
|
|
|
+ title: ingredient.name.toUpperCase(),
|
|
|
+ xaxis: {title: "DATE"},
|
|
|
+ yaxis: {title: `QUANTITY (${ingredient.unit.toUpperCase()})`}
|
|
|
}
|
|
|
|
|
|
Plotly.newPlot("itemUseGraph", [trace], layout);
|
|
|
|
|
|
- //Create use cards
|
|
|
- let sum = 0;
|
|
|
- let max = 0;
|
|
|
- let min = (quantities.length > 0) ? quantities[0] : 0;
|
|
|
- for(let i = 0; i < quantities.length; i++){
|
|
|
- sum += quantities[i];
|
|
|
- if(quantities[i] > max){
|
|
|
- max = quantities[i];
|
|
|
- }else if(quantities[i] < min){
|
|
|
- min = quantities[i];
|
|
|
- }
|
|
|
- }
|
|
|
- document.getElementById("analMinUse").innerText = `${min.toFixed(2)} ${this.ingredient.ingredient.unit}`;
|
|
|
- document.getElementById("analAvgUse").innerText = `${(sum / quantities.length).toFixed(2)} ${this.ingredient.ingredient.unit}`;
|
|
|
- document.getElementById("analMaxUse").innerText = `${max.toFixed(2)} ${this.ingredient.ingredient.unit}`;
|
|
|
-
|
|
|
- let dayUse = [0, 0, 0, 0, 0, 0, 0];
|
|
|
- let dayCount = [0, 0, 0, 0, 0, 0, 0];
|
|
|
- for(let i = 0; i < quantities.length; i++){
|
|
|
- dayUse[dates[i].getDay()] += quantities[i];
|
|
|
- dayCount[dates[i].getDay()]++;
|
|
|
- }
|
|
|
-
|
|
|
- document.getElementById("analDayOne").innerText = `${(dayUse[0] / dayCount[0]).toFixed(2)} ${this.ingredient.ingredient.unit}`;
|
|
|
- document.getElementById("analDayTwo").innerText = `${(dayUse[1] / dayCount[1]).toFixed(2)} ${this.ingredient.ingredient.unit}`;
|
|
|
- document.getElementById("analDayThree").innerText = `${(dayUse[2] / dayCount[2]).toFixed(2)} ${this.ingredient.ingredient.unit}`;
|
|
|
- document.getElementById("analDayFour").innerText = `${(dayUse[3] / dayCount[3]).toFixed(2)} ${this.ingredient.ingredient.unit}`;
|
|
|
- document.getElementById("analDayFive").innerText = `${(dayUse[4] / dayCount[4]).toFixed(2)} ${this.ingredient.ingredient.unit}`;
|
|
|
- document.getElementById("analDaySix").innerText = `${(dayUse[5] / dayCount[5]).toFixed(2)} ${this.ingredient.ingredient.unit}`;
|
|
|
- document.getElementById("analDaySeven").innerText = `${(dayUse[6] / dayCount[6]).toFixed(2)} ${this.ingredient.ingredient.unit}`;
|
|
|
+ //display the boxes at the bottom
|
|
|
+
|
|
|
},
|
|
|
|
|
|
- recipeDisplay: function(){
|
|
|
- let quantities = [];
|
|
|
+ displayRecipe: function(recipe){
|
|
|
+ //break down data into dates and quantities
|
|
|
let dates = [];
|
|
|
- let currentDate;
|
|
|
- let quantity = 0;
|
|
|
- if(this.transactions.length > 0){
|
|
|
- currentDate = this.transactions[0].date;
|
|
|
- }
|
|
|
+ let quantities = [];
|
|
|
|
|
|
- for(let i = 0; i < this.transactions.length; i++){
|
|
|
- if(currentDate.getDate() !== this.transactions[i].date.getDate()){
|
|
|
- quantities.push(quantity);
|
|
|
- quantity = 0;
|
|
|
- dates.push(currentDate);
|
|
|
- currentDate = this.transactions[i].date;
|
|
|
- }
|
|
|
+ for(let i = 0; i < this.transactionsByDate.length; i++){
|
|
|
+ dates.push(this.transactionsByDate[i].date);
|
|
|
+ let sum = 0;
|
|
|
|
|
|
- for(let j = 0; j < this.transactions[i].recipes.length; j++){
|
|
|
- const recipe = this.transactions[i].recipes[j];
|
|
|
+ for(let j = 0; j < this.transactionsByDate[i].transactions.length; j++){
|
|
|
+ const transaction = this.transactionsByDate[i].transactions[j];
|
|
|
|
|
|
- if(recipe.recipe === this.recipe){
|
|
|
- quantity += recipe.quantity;
|
|
|
+ for(let k = 0; k < transaction.recipes.length; k++){
|
|
|
+ if(transaction.recipes[k].recipe === recipe){
|
|
|
+ sum += transaction.recipes[k].quantity;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- if(i === this.transactions.length - 1){
|
|
|
- quantities.push(quantity);
|
|
|
- dates.push(currentDate);
|
|
|
- }
|
|
|
+ quantities.push(sum);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
+ //create and display the graph
|
|
|
const trace = {
|
|
|
x: dates,
|
|
|
y: quantities,
|
|
|
mode: "lines+markers",
|
|
|
line: {
|
|
|
- color: "rgb(255, 99, 107"
|
|
|
+ color: "rgb(255, 99, 107)"
|
|
|
}
|
|
|
}
|
|
|
|
|
|
const layout = {
|
|
|
- title: this.recipe.name.toUpperCase(),
|
|
|
- xaxis: {
|
|
|
- title: "DATE"
|
|
|
- },
|
|
|
- yaxis: {
|
|
|
- title: "Quantity"
|
|
|
- }
|
|
|
+ title: recipe.name.toUpperCase(),
|
|
|
+ xaxis: {title: "DATE"},
|
|
|
+ yaxis: {title: "QUANTITY"}
|
|
|
}
|
|
|
|
|
|
Plotly.newPlot("recipeSalesGraph", [trace], layout);
|
|
|
|
|
|
- let sum = 0;
|
|
|
- for(let i = 0; i < quantities.length; i++){
|
|
|
- sum += quantities[i];
|
|
|
- }
|
|
|
+ //display the boxes at the bottom
|
|
|
|
|
|
- document.getElementById("recipeAvgUse").innerText = (sum / quantities.length).toFixed(2);
|
|
|
- document.getElementById("recipeAvgRevenue").innerText = `$${(((sum / quantities.length) * this.recipe.price) / 100).toFixed(2)}`;
|
|
|
},
|
|
|
|
|
|
- changeDates: function(Transaction){
|
|
|
- let data = {
|
|
|
- startDate: document.getElementById("analStartDate").valueAsDate,
|
|
|
- endDate: document.getElementById("analEndDate").valueAsDate,
|
|
|
- recipes: []
|
|
|
- }
|
|
|
+ switchDisplay: function(){
|
|
|
+ const checkbox = document.getElementById("analSlider");
|
|
|
+ let ingredient = document.getElementById("analIngredientContent");
|
|
|
+ let recipe = document.getElementById("analRecipeContent");
|
|
|
|
|
|
- if(data.from > data.to || data.from === "" || data.to === "" || data.to > new Date()){
|
|
|
- banner.createError("INVALID DATE");
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- for(let i = 0; i < merchant.recipes.length; i++){
|
|
|
- data.recipes.push(merchant.recipes[i].id);
|
|
|
+ if(checkbox.checked === true){
|
|
|
+ ingredient.style.display = "none";
|
|
|
+ recipe.style.display = "flex";
|
|
|
+ if(this.recipesDisplay === false){
|
|
|
+ this.displayRecipe(merchant.recipes[0]);
|
|
|
+ }
|
|
|
+ }else{
|
|
|
+ ingredient.style.display = "flex";
|
|
|
+ recipe.style.display = "none";
|
|
|
}
|
|
|
-
|
|
|
- let loader = document.getElementById("loaderContainer");
|
|
|
- loader.style.display = "flex";
|
|
|
-
|
|
|
- fetch("/transaction", {
|
|
|
- method: "post",
|
|
|
- headers: {
|
|
|
- "Content-Type": "application/json;charset=utf-8"
|
|
|
- },
|
|
|
- body: JSON.stringify(data)
|
|
|
- })
|
|
|
- .then(response => response.json())
|
|
|
- .then((response)=>{
|
|
|
- if(typeof(response) === "string"){
|
|
|
- banner.createError(response);
|
|
|
- }else{
|
|
|
- this.transactions = [];
|
|
|
-
|
|
|
- for(let i = 0; i < response.length; i++){
|
|
|
- this.transactions.push(new Transaction(
|
|
|
- response[i]._id,
|
|
|
- new Date(response[i].date),
|
|
|
- response[i].recipes,
|
|
|
- merchant
|
|
|
- ));
|
|
|
- }
|
|
|
-
|
|
|
- let isRecipe = document.getElementById("analSlider").checked;
|
|
|
- if(isRecipe && Object.keys(this.recipe).length !== 0){
|
|
|
- this.recipeDisplay();
|
|
|
- }else if(!isRecipe && Object.keys(this.ingredient).length !== 0){
|
|
|
- this.ingredientDisplay();
|
|
|
- }
|
|
|
-
|
|
|
- this.dateChange = true;
|
|
|
- }
|
|
|
- })
|
|
|
- .catch((err)=>{
|
|
|
- banner.createError("ERROR: UNABLE TO DISPLAY THE DATA");
|
|
|
- })
|
|
|
- .finally(()=>{
|
|
|
- loader.style.display = "none";
|
|
|
- });
|
|
|
}
|
|
|
}
|
|
|
|