log editing
This commit is contained in:
parent
46072422d4
commit
642a9b362c
@ -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
|
||||
|
41
app/javascript/components/AppDatePicker.vue
Normal file
41
app/javascript/components/AppDatePicker.vue
Normal 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>
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -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>
|
||||
|
||||
|
@ -1,10 +1,17 @@
|
||||
<template>
|
||||
<div>
|
||||
|
||||
<log-edit v-if="log.recipe !== null" :log="log"></log-edit>
|
||||
<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>
|
||||
|
||||
<button type="button" class="button is-primary" @click="save">Save</button>
|
||||
<router-link class="button is-secondary" to="/">Cancel</router-link>
|
||||
<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('/'))
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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) {
|
||||
|
73
app/javascript/lib/DateTimeUtils.js
Normal file
73
app/javascript/lib/DateTimeUtils.js
Normal 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
|
||||
}
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user