log editing

This commit is contained in:
Dan Elbert 2018-04-13 23:32:34 -05:00
parent 46072422d4
commit 642a9b362c
11 changed files with 203 additions and 66 deletions

View File

@ -3,10 +3,11 @@ class LogsController < ApplicationController
before_action :ensure_valid_user
before_action :set_log, only: [:show, :update, :destroy]
before_action :set_recipe, only: [:new, :create]
before_action :require_recipe, only: [:new, :create]
def index
@logs = Log.for_user(current_user).order(:date).page(params[:page]).per(params[:per])
@logs = Log.for_user(current_user).order(date: :desc).page(params[:page]).per(params[:per])
end
def show

View File

@ -0,0 +1,41 @@
<template>
<app-text-field :value="stringValue" @input="input" :label="label" type="date"></app-text-field>
</template>
<script>
import DateTimeUtils from "../lib/DateTimeUtils";
export default {
props: {
value: {
required: false,
type: Date
},
label: {
required: false,
type: String,
default: null
}
},
computed: {
stringValue() {
return DateTimeUtils.formatDateForEdit(this.value);
}
},
methods: {
input(val) {
let d = DateTimeUtils.toDate(val + " 00:00");
this.$emit("input", d);
}
}
}
</script>
<style lang="scss" scoped>
</style>

View File

@ -4,6 +4,8 @@
<script>
import DateTimeUtils from "../lib/DateTimeUtils";
export default {
props: {
dateTime: {
@ -26,64 +28,22 @@
computed: {
dateObj() {
if (this.dateTime instanceof Date) {
return this.dateTime;
} else if (this.dateTime === null || this.dateTime.length === 0) {
return null;
} else {
return new Date(this.dateTime);
}
return DateTimeUtils.toDate(this.dateTime);
},
friendlyString() {
const parts = [];
if (this.showDate) {
parts.push(this.formatDate());
parts.push(DateTimeUtils.formatDate(this.dateObj));
}
if (this.showTime) {
parts.push(this.formatTime(true));
parts.push(DateTimeUtils.formatTime(this.dateObj, true));
}
return parts.join(" ");
},
fullString() {
return this.formatDate() + " " + this.formatTime(false);
}
},
methods: {
formatDate() {
if (this.dateObj) {
return [this.dateObj.getMonth() + 1, this.dateObj.getDate(), this.dateObj.getFullYear() % 100].join("/");
} else {
return "";
}
},
formatTime(use12hour) {
if (this.dateObj) {
let h = this.dateObj.getHours();
const m = this.dateObj.getMinutes().toString().padStart(2, "0");
let meridiem = "";
if (use12hour) {
meridiem = " am";
if (h === 0) {
h = 12;
} else if (h > 12) {
h = h - 12;
meridiem = " pm";
}
} else {
h = h.toString().padStart(2, "0");
}
return h + ":" + m + meridiem;
} else {
return "";
}
return DateTimeUtils.formatTimestamp(this.dateObj);
}
}
}

View File

@ -19,7 +19,20 @@
type: Number,
default: 5
},
rating: {
readonly: {
required: false,
type: Boolean,
default: false
},
step: {
required: false,
type: Number,
default: 0.5
},
value: {
required: false,
type: Number,
default: 0
@ -28,17 +41,25 @@
data() {
return {
temporaryWidth: null
temporaryValue: null
};
},
computed: {
ratingPercent() {
return (this.rating / this.starCount) * 100.0;
return ((this.value || 0) / this.starCount) * 100.0;
},
temporaryPercent() {
if (this.temporaryValue !== null) {
return (this.temporaryValue / this.starCount) * 100.0;
} else {
return null;
}
},
filledStyle() {
const width = this.temporaryWidth === null ? this.ratingPercent : this.temporaryWidth;
const width = this.temporaryPercent || this.ratingPercent;
return {
width: width + "%"
};
@ -47,21 +68,32 @@
methods: {
handleClick(evt) {
console.log("click");
if (this.temporaryValue !== null) {
this.$emit("input", this.temporaryValue);
}
},
handleMousemove(evt) {
if (this.readonly) {
return;
}
const wrapperBox = this.$refs.wrapper.getBoundingClientRect();
const wrapperWidth = wrapperBox.right - wrapperBox.left;
const mousePosition = evt.clientX;
if (mousePosition > wrapperBox.left && mousePosition < wrapperBox.right) {
this.temporaryWidth = ((mousePosition - wrapperBox.left) / wrapperWidth) * 100.0;
const filledRatio = ((mousePosition - wrapperBox.left) / wrapperWidth);
const totalSteps = this.starCount / this.step;
const filledSteps = Math.round(totalSteps * filledRatio);
this.temporaryValue = filledSteps * this.step;
}
},
handleMouseleave(evt) {
this.temporaryWidth = null;
this.temporaryValue = null;
}
},

View File

@ -4,11 +4,24 @@
<h1 class="title">Creating Log for {{ log.recipe.name }}</h1>
<p>Edit Rating</p>
<div class="columns">
<div class="column">
<app-date-picker v-model="log.date" label="Date"></app-date-picker>
</div>
<p>Edit Date</p>
<div class="column">
<div class="field">
<label class="label is-small-mobile">Rating</label>
<div class="control">
<app-rating v-model="log.rating" :step="1"></app-rating>
</div>
</div>
</div>
</div>
<app-text-field label="Notes" :value="log.notes" type="textarea"></app-text-field>
<app-text-field label="Notes" v-model="log.notes" type="textarea"></app-text-field>
<slot></slot>
<recipe-edit :recipe="log.recipe" :for-logging="true"></recipe-edit>

View File

@ -1,10 +1,17 @@
<template>
<div>
<log-edit v-if="log.recipe !== null" :log="log"></log-edit>
<button type="button" class="button is-primary" @click="save">Save</button>
<log-edit v-if="log.recipe !== null" :log="log">
<div class="buttons">
<button type="button" class="button is-primary" @click="save">Save Log</button>
<router-link class="button is-secondary" to="/">Cancel</router-link>
</div>
</log-edit>
<div class="buttons">
<button type="button" class="button is-primary" @click="save">Save Log</button>
<router-link class="button is-secondary" to="/">Cancel</router-link>
</div>
</div>
</template>
@ -37,6 +44,8 @@
methods: {
save() {
this.log.original_recipe_id = this.recipeId;
this.loadResource(
api.postLog(this.log)
.then(() => this.$router.push('/'))

View File

@ -14,9 +14,9 @@
<tr v-for="l in logs" :key="l.id">
<td>{{l.recipe.name}}</td>
<td>l.date</td>
<td>l.rating</td>
<td>l.notes</td>
<td><app-date-time :date-time="l.date" :show-time="false"></app-date-time> </td>
<td><app-rating :value="l.rating" readonly></app-rating></td>
<td>{{l.notes}}</td>
</tr>
</table>

View File

@ -45,7 +45,7 @@
</div>
</td>
<td>
<app-rating v-if="r.rating !== null" :rating="r.rating" ></app-rating>
<app-rating v-if="r.rating !== null" :value="r.rating" readonly></app-rating>
<span v-else>--</span>
</td>
<td>{{ r.yields }}</td>

View File

@ -283,19 +283,25 @@ class Api {
}
buildLogParams(log) {
const recParams = this.buildRecipeParams(log.recipe);
if (recParams.recipe && recParams.recipe.recipe_ingredients_attributes) {
recParams.recipe.recipe_ingredients_attributes.forEach(ri => ri.id = null);
}
return {
log: {
date: log.date,
rating: log.rating,
notes: log.notes,
source_recipe_id: log.source_recipe_id,
recipe_attributes: this.buildRecipeParams(log.recipe)
recipe_attributes: recParams.recipe
}
};
}
postLog(log) {
return this.post("/logs/", this.buildLogParams(log));
return this.post("/recipes/" + log.original_recipe_id + "/logs/", this.buildLogParams(log));
}
patchLog(log) {

View File

@ -0,0 +1,73 @@
function zeroPad(val, length) {
return val.toString().padStart(length, "0");
}
// Ensure the given date is a Date object.
function toDate(date) {
if (date instanceof Date) {
return date;
} else if (date === null || date.length === 0) {
return null;
} else {
return new Date(date);
}
}
function formatDate(dateObj) {
if (dateObj) {
return [dateObj.getMonth() + 1, dateObj.getDate(), dateObj.getFullYear() % 100].join("/");
} else {
return "";
}
}
function formatDateForEdit(dateObj) {
if (dateObj) {
return [dateObj.getFullYear(), zeroPad(dateObj.getMonth() + 1, 2), zeroPad(dateObj.getDate(), 2)].join("-");
} else {
return "";
}
}
function formatTimestamp(dateObj) {
if (dateObj) {
return formatDateForEdit(dateObj) + " " + formatTime(dateObj, false);
} else {
return "";
}
}
function formatTime(dateObj, use12hour) {
if (dateObj) {
let h = dateObj.getHours();
const m = zeroPad(dateObj.getMinutes(), 2);
let meridiem = "";
if (use12hour) {
meridiem = " am";
if (h === 0) {
h = 12;
} else if (h > 12) {
h = h - 12;
meridiem = " pm";
}
} else {
h = h.toString().padStart(2, "0");
}
return h + ":" + m + meridiem;
} else {
return "";
}
}
export default {
toDate,
formatDate,
formatDateForEdit,
formatTimestamp,
formatTime
}

View File

@ -11,6 +11,7 @@ import App from '../components/App';
import AppAutocomplete from "../components/AppAutocomplete";
import AppDateTime from "../components/AppDateTime";
import AppDatePicker from "../components/AppDatePicker";
import AppIcon from "../components/AppIcon";
import AppModal from "../components/AppModal";
import AppNavbar from "../components/AppNavbar";
@ -21,6 +22,7 @@ import AppTextField from "../components/AppTextField";
Vue.component("AppAutocomplete", AppAutocomplete);
Vue.component("AppDateTime", AppDateTime);
Vue.component("AppDatePicker", AppDatePicker);
Vue.component("AppIcon", AppIcon);
Vue.component("AppModal", AppModal);
Vue.component("AppNavbar", AppNavbar);