From 284df442096f12de2386467a85fc3c87148df985 Mon Sep 17 00:00:00 2001 From: Hayden <64056131+hay-kot@users.noreply.github.com> Date: Wed, 21 Apr 2021 21:52:12 -0800 Subject: [PATCH] feature/editor-improvements (#289) * pin editor buttons on scroll * scaler scratch * fix langauge assignment 1st pass * set lang on navigate * refactor/breakup router * unify style for language selectro * refactor/code-cleanup * refactor/page specific components to page folder * Fix time card layout issue * fix timecard display * update mobile cards / fix overflow errors Co-authored-by: hay-kot --- dev/ingredientScaler/index.js | 42 +++++ dev/ingredientScaler/recipeIngredient.js | 75 ++++++++ dev/ingredientScaler/recipeNumber.js | 166 ++++++++++++++++++ dev/scripts/publish-release-branch.sh | 11 ++ frontend/src/App.vue | 9 +- frontend/src/api/siteSettings.js | 4 +- frontend/src/components/Debug/LastRecipe.vue | 38 ---- frontend/src/components/Debug/LogFile.vue | 37 ---- .../ColorPickerDialog.vue | 0 .../{UI => FormHelpers}/DatePicker.vue | 0 .../FormHelpers/LanguageSelector.vue | 48 +++++ .../TimePickerDialog.vue | 0 .../ImportSummaryDialog/DataTable.vue | 0 .../Backup => }/ImportSummaryDialog/index.vue | 2 +- .../src/components/MealPlan/MealPlanNew.vue | 14 +- .../src/components/Recipe/EditorButtonRow.vue | 100 +++++++---- .../components/Recipe/MobileRecipeCard.vue | 56 ++++-- .../Recipe/RecipeEditor/ImageUploadBtn.vue | 6 +- .../components/Recipe/RecipeEditor/index.vue | 86 +++------ .../src/components/Recipe/RecipeTimeCard.vue | 121 +++++-------- .../UI/{ => Buttons}/TheDownloadBtn.vue | 0 .../TheUploadBtn.vue} | 0 frontend/src/components/UI/CardSection.vue | 1 + .../BaseDialog.vue} | 0 .../ConfirmationDialog.vue} | 6 +- frontend/src/components/UI/LanguageMenu.vue | 87 --------- .../src/components/UI/SuccessFailureAlert.vue | 66 ------- frontend/src/components/UI/TheAppBar.vue | 8 +- .../UI/{AddRecipeFab.vue => TheRecipeFab.vue} | 0 .../UI/{SiteMenu.vue => TheSiteMenu.vue} | 0 frontend/src/main.js | 24 +-- frontend/src/mixins/validators.js | 12 +- frontend/src/pages/Admin/About/index.vue | 2 +- .../Admin/Backup/AvailableBackupCard.vue | 0 .../Admin/Backup/BackupCard.vue | 0 .../Admin/Backup/ImportDialog.vue | 4 +- .../Admin/Backup/ImportOptions.vue | 0 .../Admin/Backup/NewBackupCard.vue | 2 +- frontend/src/pages/Admin/Backup/index.vue | 23 +-- .../Admin/ManageUsers/GroupCard.vue | 6 +- .../Admin/ManageUsers/GroupDashboard.vue | 2 +- .../Admin/ManageUsers/TheSignUpTable.vue | 6 +- .../Admin/ManageUsers/TheUserTable.vue | 6 +- .../src/pages/Admin/ManageUsers/index.vue | 12 +- .../src/pages/Admin/MealPlanner/index.vue | 2 +- .../Admin/Migration/MigrationCard.vue | 8 +- .../Admin/Migration/MigrationDialog.vue | 2 +- frontend/src/pages/Admin/Migration/index.vue | 12 +- frontend/src/pages/Admin/Profile/index.vue | 6 +- .../Admin/Settings}/CreatePageDialog.vue | 0 .../Admin/Settings}/CustomPageCreator.vue | 2 +- .../Admin/Settings}/HomePageSettings.vue | 50 +++--- frontend/src/pages/Admin/Settings/index.vue | 4 +- .../Admin/Theme/NewThemeDialog.vue | 0 .../Admin/Theme/ThemeCard.vue | 6 +- frontend/src/pages/Admin/Theme/index.vue | 6 +- frontend/src/routes/admin.js | 2 +- frontend/src/routes/auth.js | 18 ++ frontend/src/routes/general.js | 15 ++ frontend/src/routes/index.js | 123 +++++-------- frontend/src/routes/meal.js | 34 ++++ frontend/src/routes/recipes.js | 29 +++ frontend/src/store/index.js | 2 +- frontend/src/store/modules/language.js | 26 +-- frontend/src/store/modules/siteSettings.js | 11 +- frontend/src/store/modules/userSettings.js | 2 +- 66 files changed, 778 insertions(+), 664 deletions(-) create mode 100644 dev/ingredientScaler/index.js create mode 100644 dev/ingredientScaler/recipeIngredient.js create mode 100644 dev/ingredientScaler/recipeNumber.js create mode 100644 dev/scripts/publish-release-branch.sh delete mode 100644 frontend/src/components/Debug/LastRecipe.vue delete mode 100644 frontend/src/components/Debug/LogFile.vue rename frontend/src/components/{Admin/Theme => FormHelpers}/ColorPickerDialog.vue (100%) rename frontend/src/components/{UI => FormHelpers}/DatePicker.vue (100%) create mode 100644 frontend/src/components/FormHelpers/LanguageSelector.vue rename frontend/src/components/{Admin/MealPlanner => FormHelpers}/TimePickerDialog.vue (100%) rename frontend/src/components/{Admin/Backup => }/ImportSummaryDialog/DataTable.vue (100%) rename frontend/src/components/{Admin/Backup => }/ImportSummaryDialog/index.vue (97%) rename frontend/src/components/UI/{ => Buttons}/TheDownloadBtn.vue (100%) rename frontend/src/components/UI/{UploadBtn.vue => Buttons/TheUploadBtn.vue} (100%) rename frontend/src/components/UI/{BasicModal.vue => Dialogs/BaseDialog.vue} (100%) rename frontend/src/components/UI/{Confirmation.vue => Dialogs/ConfirmationDialog.vue} (93%) delete mode 100644 frontend/src/components/UI/LanguageMenu.vue delete mode 100644 frontend/src/components/UI/SuccessFailureAlert.vue rename frontend/src/components/UI/{AddRecipeFab.vue => TheRecipeFab.vue} (100%) rename frontend/src/components/UI/{SiteMenu.vue => TheSiteMenu.vue} (100%) rename frontend/src/{components => pages}/Admin/Backup/AvailableBackupCard.vue (100%) rename frontend/src/{components => pages}/Admin/Backup/BackupCard.vue (100%) rename frontend/src/{components => pages}/Admin/Backup/ImportDialog.vue (95%) rename frontend/src/{components => pages}/Admin/Backup/ImportOptions.vue (100%) rename frontend/src/{components => pages}/Admin/Backup/NewBackupCard.vue (97%) rename frontend/src/{components => pages}/Admin/ManageUsers/GroupCard.vue (95%) rename frontend/src/{components => pages}/Admin/ManageUsers/GroupDashboard.vue (97%) rename frontend/src/{components => pages}/Admin/ManageUsers/TheSignUpTable.vue (97%) rename frontend/src/{components => pages}/Admin/ManageUsers/TheUserTable.vue (98%) rename frontend/src/{components => pages}/Admin/Migration/MigrationCard.vue (94%) rename frontend/src/{components => pages}/Admin/Migration/MigrationDialog.vue (96%) rename frontend/src/{components/Admin/General => pages/Admin/Settings}/CreatePageDialog.vue (100%) rename frontend/src/{components/Admin/General => pages/Admin/Settings}/CustomPageCreator.vue (97%) rename frontend/src/{components/Admin/General => pages/Admin/Settings}/HomePageSettings.vue (84%) rename frontend/src/{components => pages}/Admin/Theme/NewThemeDialog.vue (100%) rename frontend/src/{components => pages}/Admin/Theme/ThemeCard.vue (94%) create mode 100644 frontend/src/routes/auth.js create mode 100644 frontend/src/routes/general.js create mode 100644 frontend/src/routes/meal.js create mode 100644 frontend/src/routes/recipes.js diff --git a/dev/ingredientScaler/index.js b/dev/ingredientScaler/index.js new file mode 100644 index 00000000..f7ba9fd9 --- /dev/null +++ b/dev/ingredientScaler/index.js @@ -0,0 +1,42 @@ +import { recipeIngredient } from "./recipeIngredient"; +import { recipeNumber } from "./recipeNumber"; + +export const ingredientScaler = { + process(ingredientArray, scale) { + console.log(scale); + let workingArray = ingredientArray.map(x => + ingredientScaler.markIngredient(x) + ); + return workingArray.map(x => ingredientScaler.adjustIngredients(x, scale)); + }, + + adjustIngredients(ingredient, scale) { + var scaledQuantity = new recipeNumber(ingredient.quantity).multiply(scale); + const newText = ingredient.text.replace( + ingredient.quantity, + scaledQuantity + ); + return { ...ingredient, quantity: scaledQuantity, text: newText }; + }, + + markIngredient(ingredient) { + console.log(ingredient); + const returnVar = ingredient.replace( + /^([\d/?[^\s&]*)(?: |\s)(\w*)/g, + (match, quantity, unit) => { + return `${unit}${quantity},${match}`; + } + ); + const split = returnVar.split(","); + const [unit, quantity, match] = split; + console.log("Split", unit, quantity, match); + const n = new recipeNumber(quantity); + const i = new recipeIngredient(n, unit); + const serializedQuantity = n.isFraction() ? n.toImproperFraction() : n; + return { + unit: i, + quantity: serializedQuantity.toString(), + text: match, + }; + }, +}; diff --git a/dev/ingredientScaler/recipeIngredient.js b/dev/ingredientScaler/recipeIngredient.js new file mode 100644 index 00000000..8bc3e2f1 --- /dev/null +++ b/dev/ingredientScaler/recipeIngredient.js @@ -0,0 +1,75 @@ +export const recipeIngredient = function(quantity, unit) { + this.quantity = quantity; + this.unit = unit; +}; + +recipeIngredient.prototype.isSingular = function() { + return this.quantity > 0 && this.quantity <= 1; +}; + +recipeIngredient.prototype.pluralize = function() { + if (this.isSingular()) { + return this.unit; + } else { + return `${this.unit}s`; + } +}; + +recipeIngredient.prototype.getSingularUnit = function() { + if (this.isSingular()) { + return this.unit; + } else { + return this.unit.replace(/s$/, ""); + } +}; + +recipeIngredient.prototype.toString = function() { + return `${this.quantity.toString()} ${this.pluralize()}`; +}; + +recipeIngredient.prototype.convertUnits = function() { + const conversion = recipeIngredient.CONVERSIONS[this.unit] || {}; + if (conversion.min && this.quantity < conversion.min.value) { + this.unit = conversion.min.next; + this.quantity.multiply(conversion.to[this.unit]); + } else if (conversion.max && this.quantity >= conversion.max.value) { + this.unit = conversion.max.next; + this.quantity.multiply(conversion.to[this.unit]); + } + return this; +}; + +recipeIngredient.CONVERSIONS = { + cup: { + to: { + tablespoon: 16, + }, + min: { + value: 1 / 4, + next: "tablespoon", + }, + }, + tablespoon: { + to: { + teaspoon: 3, + cup: 1 / 16, + }, + min: { + value: 1, + next: "teaspoon", + }, + max: { + value: 4, + next: "cup", + }, + }, + teaspoon: { + to: { + tablespoon: 1 / 3, + }, + max: { + value: 3, + next: "tablespoon", + }, + }, +}; diff --git a/dev/ingredientScaler/recipeNumber.js b/dev/ingredientScaler/recipeNumber.js new file mode 100644 index 00000000..74579aeb --- /dev/null +++ b/dev/ingredientScaler/recipeNumber.js @@ -0,0 +1,166 @@ +export const recipeNumber = function(number) { + const match = number.match( + /^(?:(\d+)|(?:(\d+)(?: | ))?(?:(\d+)\/(\d+))?)$/ + ); + if (!match || !match[0] || match[4] == "0") { + throw `Invalid number: "${number}".`; + } + this.wholeNumber = +(match[1] || match[2]); + this.numerator = +match[3]; + this.denominator = +match[4]; +}; + +/** + * Determines if the number is a fraction. + * @this {recipeNumber} + * @return {boolean} If the number is a fraction. + */ +recipeNumber.prototype.isFraction = function() { + return !!(this.numerator && this.denominator); +}; + +/** + * Determines if the fraction is proper, which is defined as + * the numerator being strictly less than the denominator. + * @this {recipeNumber} + * @return {boolean} If the fraction is proper. + */ +recipeNumber.prototype.isProperFraction = function() { + return this.numerator < this.denominator; +}; + +/** + * Determines if the fraction is improper, which is defined as + * the numerator being greater than or equal to the denominator. + * @this {recipeNumber} + * @return {boolean} If the fraction is improper. + */ +recipeNumber.prototype.isImproperFraction = function() { + return this.numerator >= this.denominator; +}; + +/** + * Determines if the fraction is mixed, which is defined as + * a whole number with a proper fraction. + * @this {recipeNumber} + * @return {boolean} If the fraction is mixed. + */ +recipeNumber.prototype.isMixedFraction = function() { + return this.isProperFraction() && !isNaN(this.wholeNumber); +}; + +/** + * Simplifies fractions. Examples: + * 3/2 = 1 1/2 + * 4/2 = 2 + * 1 3/2 = 2 1/2 + * 0/1 = 0 + * 1 0/1 = 1 + * @this {recipeNumber} + * @return {recipeNumber} The instance. + */ +recipeNumber.prototype.simplifyFraction = function() { + if (this.isImproperFraction()) { + this.wholeNumber |= 0; + this.wholeNumber += Math.floor(this.numerator / this.denominator); + const modulus = this.numerator % this.denominator; + if (modulus) { + this.numerator = modulus; + } else { + this.numerator = this.denominator = NaN; + } + } else if (this.numerator == 0) { + this.wholeNumber |= 0; + this.numerator = this.denominator = NaN; + } + return this; +}; + +/** + * Reduces a fraction. Examples: + * 2/6 = 1/3 + * 6/2 = 3/1 + * @this {recipeNumber} + * @return {recipeNumber} The instance. + */ +recipeNumber.prototype.reduceFraction = function() { + if (this.isFraction()) { + const gcd = recipeNumber.gcd(this.numerator, this.denominator); + this.numerator /= gcd; + this.denominator /= gcd; + } + return this; +}; + +/** + * Converts proper fractions to improper fractions. Examples: + * 1 1/2 = 3/2 + * 3/2 = 3/2 + * 1/2 = 1/2 + * 2 = 2 + * + * @this {recipeNumber} + * @return {recipeNumber} The instance. + */ +recipeNumber.prototype.toImproperFraction = function() { + if (!isNaN(this.wholeNumber)) { + this.numerator |= 0; + this.denominator = this.denominator || 1; + this.numerator += this.wholeNumber * this.denominator; + this.wholeNumber = NaN; + } + return this; +}; + +/** + * Multiplies the number by some decimal value. + * @param {number} multiplier The multiplier. + * @this {recipeNumber} + * @return {recipeNumber} The instance. + */ +recipeNumber.prototype.multiply = function(multiplier) { + this.toImproperFraction(); + this.numerator *= multiplier; + return this.reduceFraction().simplifyFraction(); +}; + +/** + * Gets a string representation of the number. + * @this {recipeNumber} + * @return {string} The string representation of the number. + */ +recipeNumber.prototype.toString = function() { + let number = ""; + let fraction = ""; + if (!isNaN(this.wholeNumber)) { + number += this.wholeNumber; + } + if (this.isFraction()) { + fraction = `${this.numerator}/${this.denominator}`; + } + if (number && fraction) { + number += ` ${fraction}`; + } + return number || fraction; +}; + +/** + * Gets a numeric representation of the number. + * @this {recipeNumber} + * @return {number} The numeric representation of the number. + */ +recipeNumber.prototype.valueOf = function() { + let value = this.wholeNumber || 0; + value += this.numerator / this.denominator || 0; + return value; +}; + +/** + * Euclid's algorithm to find the greatest common divisor of two numbers. + * @param {number} a One number. + * @param {number} b Another number. + * @return {number} The GCD of the numbers. + */ +recipeNumber.gcd = function gcd(a, b) { + return b ? recipeNumber.gcd(b, a % b) : a; +}; diff --git a/dev/scripts/publish-release-branch.sh b/dev/scripts/publish-release-branch.sh new file mode 100644 index 00000000..22bd2aa8 --- /dev/null +++ b/dev/scripts/publish-release-branch.sh @@ -0,0 +1,11 @@ +git checkout dev +git merge --strategy=ours master # keep the content of this branch, but record a merge +git checkout master +git merge dev # fast-forward master up to the merge + + +## TODOs + +# Create New Branch v0.x.x +# Push Branch Version to Github +# Create Pull Request \ No newline at end of file diff --git a/frontend/src/App.vue b/frontend/src/App.vue index bb545577..d9df8bf2 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -9,7 +9,7 @@ > - + @@ -19,7 +19,7 @@ - - \ No newline at end of file diff --git a/frontend/src/components/Debug/LogFile.vue b/frontend/src/components/Debug/LogFile.vue deleted file mode 100644 index 47f30949..00000000 --- a/frontend/src/components/Debug/LogFile.vue +++ /dev/null @@ -1,37 +0,0 @@ - - - - - \ No newline at end of file diff --git a/frontend/src/components/Admin/Theme/ColorPickerDialog.vue b/frontend/src/components/FormHelpers/ColorPickerDialog.vue similarity index 100% rename from frontend/src/components/Admin/Theme/ColorPickerDialog.vue rename to frontend/src/components/FormHelpers/ColorPickerDialog.vue diff --git a/frontend/src/components/UI/DatePicker.vue b/frontend/src/components/FormHelpers/DatePicker.vue similarity index 100% rename from frontend/src/components/UI/DatePicker.vue rename to frontend/src/components/FormHelpers/DatePicker.vue diff --git a/frontend/src/components/FormHelpers/LanguageSelector.vue b/frontend/src/components/FormHelpers/LanguageSelector.vue new file mode 100644 index 00000000..79715717 --- /dev/null +++ b/frontend/src/components/FormHelpers/LanguageSelector.vue @@ -0,0 +1,48 @@ + + + \ No newline at end of file diff --git a/frontend/src/components/Admin/MealPlanner/TimePickerDialog.vue b/frontend/src/components/FormHelpers/TimePickerDialog.vue similarity index 100% rename from frontend/src/components/Admin/MealPlanner/TimePickerDialog.vue rename to frontend/src/components/FormHelpers/TimePickerDialog.vue diff --git a/frontend/src/components/Admin/Backup/ImportSummaryDialog/DataTable.vue b/frontend/src/components/ImportSummaryDialog/DataTable.vue similarity index 100% rename from frontend/src/components/Admin/Backup/ImportSummaryDialog/DataTable.vue rename to frontend/src/components/ImportSummaryDialog/DataTable.vue diff --git a/frontend/src/components/Admin/Backup/ImportSummaryDialog/index.vue b/frontend/src/components/ImportSummaryDialog/index.vue similarity index 97% rename from frontend/src/components/Admin/Backup/ImportSummaryDialog/index.vue rename to frontend/src/components/ImportSummaryDialog/index.vue index 4c2f8597..c41aa912 100644 --- a/frontend/src/components/Admin/Backup/ImportSummaryDialog/index.vue +++ b/frontend/src/components/ImportSummaryDialog/index.vue @@ -45,7 +45,7 @@ \ No newline at end of file diff --git a/frontend/src/components/UI/SuccessFailureAlert.vue b/frontend/src/components/UI/SuccessFailureAlert.vue deleted file mode 100644 index f9ea6836..00000000 --- a/frontend/src/components/UI/SuccessFailureAlert.vue +++ /dev/null @@ -1,66 +0,0 @@ - - - - - \ No newline at end of file diff --git a/frontend/src/components/UI/TheAppBar.vue b/frontend/src/components/UI/TheAppBar.vue index 1c556feb..72947116 100644 --- a/frontend/src/components/UI/TheAppBar.vue +++ b/frontend/src/components/UI/TheAppBar.vue @@ -35,7 +35,7 @@ mdi-magnify - + mdi-magnify - +