Upgrade shakapacker, vue; switch to pinia
This commit is contained in:
parent
bb2e29f25c
commit
f246f71aa9
@ -4,4 +4,6 @@ db/*.sqlite*
|
|||||||
tmp/*.*
|
tmp/*.*
|
||||||
public/assets
|
public/assets
|
||||||
public/packs
|
public/packs
|
||||||
node_modules/
|
node_modules/
|
||||||
|
.yarn
|
||||||
|
.pnp.*
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -35,3 +35,5 @@ yarn-debug.log*
|
|||||||
/yarn-error.log
|
/yarn-error.log
|
||||||
yarn-debug.log*
|
yarn-debug.log*
|
||||||
.yarn-integrity
|
.yarn-integrity
|
||||||
|
.yarn
|
||||||
|
.pnp.*
|
@ -31,7 +31,7 @@ WORKDIR /parsley
|
|||||||
COPY Gemfile* ./
|
COPY Gemfile* ./
|
||||||
RUN bundle install
|
RUN bundle install
|
||||||
|
|
||||||
COPY package.json yarn.lock ./
|
COPY package.json.org yarn.lock ./
|
||||||
RUN yarn install --production=true --frozen-lockfile
|
RUN yarn install --production=true --frozen-lockfile
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
3
Gemfile
3
Gemfile
@ -3,10 +3,11 @@ source 'https://rubygems.org'
|
|||||||
gem 'rails', '7.2.1'
|
gem 'rails', '7.2.1'
|
||||||
gem 'pg', '~> 1.5.8'
|
gem 'pg', '~> 1.5.8'
|
||||||
|
|
||||||
gem 'shakapacker', '6.5.4'
|
gem 'shakapacker', '8.0.2'
|
||||||
gem 'bootsnap', '>= 1.1.0', require: false
|
gem 'bootsnap', '>= 1.1.0', require: false
|
||||||
|
|
||||||
gem 'oj', '~> 3.16.6'
|
gem 'oj', '~> 3.16.6'
|
||||||
|
gem 'csv', '~> 3.3'
|
||||||
|
|
||||||
gem 'kaminari', '~> 1.2.2'
|
gem 'kaminari', '~> 1.2.2'
|
||||||
gem 'unitwise', '~> 2.3.0'
|
gem 'unitwise', '~> 2.3.0'
|
||||||
|
@ -81,6 +81,7 @@ GEM
|
|||||||
concurrent-ruby (1.3.4)
|
concurrent-ruby (1.3.4)
|
||||||
connection_pool (2.4.1)
|
connection_pool (2.4.1)
|
||||||
crass (1.0.6)
|
crass (1.0.6)
|
||||||
|
csv (3.3.0)
|
||||||
dalli (3.2.8)
|
dalli (3.2.8)
|
||||||
database_cleaner (2.0.2)
|
database_cleaner (2.0.2)
|
||||||
database_cleaner-active_record (>= 2, < 3)
|
database_cleaner-active_record (>= 2, < 3)
|
||||||
@ -176,6 +177,7 @@ GEM
|
|||||||
bigdecimal (>= 3.0)
|
bigdecimal (>= 3.0)
|
||||||
ostruct (>= 0.2)
|
ostruct (>= 0.2)
|
||||||
ostruct (0.6.0)
|
ostruct (0.6.0)
|
||||||
|
package_json (0.1.0)
|
||||||
parslet (2.0.0)
|
parslet (2.0.0)
|
||||||
pg (1.5.8)
|
pg (1.5.8)
|
||||||
pry (0.14.2)
|
pry (0.14.2)
|
||||||
@ -261,8 +263,9 @@ GEM
|
|||||||
rspec-support (3.13.1)
|
rspec-support (3.13.1)
|
||||||
securerandom (0.3.1)
|
securerandom (0.3.1)
|
||||||
semantic_range (3.0.0)
|
semantic_range (3.0.0)
|
||||||
shakapacker (6.5.4)
|
shakapacker (8.0.2)
|
||||||
activesupport (>= 5.2)
|
activesupport (>= 5.2)
|
||||||
|
package_json
|
||||||
rack-proxy (>= 0.6.1)
|
rack-proxy (>= 0.6.1)
|
||||||
railties (>= 5.2)
|
railties (>= 5.2)
|
||||||
semantic_range (>= 2.3.0)
|
semantic_range (>= 2.3.0)
|
||||||
@ -296,6 +299,7 @@ PLATFORMS
|
|||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
bcrypt (~> 3.1.18)
|
bcrypt (~> 3.1.18)
|
||||||
bootsnap (>= 1.1.0)
|
bootsnap (>= 1.1.0)
|
||||||
|
csv (~> 3.3)
|
||||||
dalli (~> 3.2.8)
|
dalli (~> 3.2.8)
|
||||||
database_cleaner (~> 2.0.2)
|
database_cleaner (~> 2.0.2)
|
||||||
factory_bot_rails (~> 6.4.3)
|
factory_bot_rails (~> 6.4.3)
|
||||||
@ -309,7 +313,7 @@ DEPENDENCIES
|
|||||||
rails-controller-testing
|
rails-controller-testing
|
||||||
redcarpet (~> 3.6.0)
|
redcarpet (~> 3.6.0)
|
||||||
rspec-rails (~> 7.0.1)
|
rspec-rails (~> 7.0.1)
|
||||||
shakapacker (= 6.5.4)
|
shakapacker (= 8.0.2)
|
||||||
sqlite3 (~> 2.1.0)
|
sqlite3 (~> 2.1.0)
|
||||||
tzinfo-data
|
tzinfo-data
|
||||||
unitwise (~> 2.3.0)
|
unitwise (~> 2.3.0)
|
||||||
|
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
|||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2020 Dan Elbert
|
Copyright (c) 2024 Dan Elbert
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
2
Procfile
2
Procfile
@ -1,2 +1,2 @@
|
|||||||
rails: bundle exec rails s -b 0.0.0.0
|
rails: bundle exec rails s -b 0.0.0.0
|
||||||
webpacker: bin/webpack-dev-server
|
shakapacker: bin/shakapacker-dev-server
|
||||||
|
@ -5,7 +5,7 @@ A self hosted cookbook
|
|||||||
|
|
||||||
Parsley is released under the MIT License.
|
Parsley is released under the MIT License.
|
||||||
|
|
||||||
Copyright (C) 2020 Dan Elbert
|
Copyright (C) 2024 Dan Elbert
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
@ -4,13 +4,15 @@
|
|||||||
<app-navbar></app-navbar>
|
<app-navbar></app-navbar>
|
||||||
<section id="main" class="">
|
<section id="main" class="">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<transition name="fade" mode="out-in">
|
<router-view v-slot="{ Component }">
|
||||||
<router-view v-if="!hasError"></router-view>
|
<transition name="fade" mode="out-in">
|
||||||
<div v-else>
|
<component v-if="!hasError" :is="Component" />
|
||||||
<h1>Error!</h1>
|
<div v-else>
|
||||||
<p>{{error}}</p>
|
<h1>Error!</h1>
|
||||||
</div>
|
<p>{{ error }}</p>
|
||||||
</transition>
|
</div>
|
||||||
|
</transition>
|
||||||
|
</router-view>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
@ -18,31 +20,31 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
import { mapMutations, mapState } from "vuex";
|
import { mapState } from "pinia";
|
||||||
import api from "../lib/Api";
|
|
||||||
import TWEEN from '@tweenjs/tween.js';
|
import TWEEN from '@tweenjs/tween.js';
|
||||||
|
import { useAppConfigStore } from "../stores/appConfig";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
api: api
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
...mapState({
|
...mapState(useAppConfigStore, {
|
||||||
hasError: state => state.error !== null,
|
hasError: store => store.error !== null,
|
||||||
error: state => state.error,
|
error: store => store.error,
|
||||||
authChecked: state => state.authChecked,
|
authChecked: store => store.authChecked,
|
||||||
initialLoad: state => state.initialLoad
|
initialLoad: store => store.initialLoad
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
isLoading(val) {
|
isLoading(val) {
|
||||||
if (val) {
|
if (val) {
|
||||||
this.$Progress.start();
|
// this.$Progress.start();
|
||||||
} else {
|
} else {
|
||||||
this.$Progress.finish();
|
// this.$Progress.finish();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
value: String,
|
modelValue: String,
|
||||||
id: String,
|
id: String,
|
||||||
placeholder: String,
|
placeholder: String,
|
||||||
name: String,
|
name: String,
|
||||||
@ -70,12 +70,12 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
created() {
|
created() {
|
||||||
this.rawValue = this.value;
|
this.rawValue = this.modelValue;
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
value(newValue) {
|
modelValue(newValue) {
|
||||||
this.rawValue = newValue;
|
this.rawValue = newValue;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -156,7 +156,7 @@
|
|||||||
|
|
||||||
this.rawValue = newValue;
|
this.rawValue = newValue;
|
||||||
|
|
||||||
this.$emit("input", newValue);
|
this.$emit("update:modelValue", newValue);
|
||||||
|
|
||||||
if (newValue.length >= Math.max(1, this.minLength)) {
|
if (newValue.length >= Math.max(1, this.minLength)) {
|
||||||
this.debouncedUpdateOptions(newValue);
|
this.debouncedUpdateOptions(newValue);
|
||||||
@ -192,7 +192,7 @@
|
|||||||
|
|
||||||
selectOption(opt) {
|
selectOption(opt) {
|
||||||
this.rawValue = this.optionValue(opt);
|
this.rawValue = this.optionValue(opt);
|
||||||
this.$emit("input", this.rawValue);
|
this.$emit("update:modelValue", this.rawValue);
|
||||||
this.$emit("optionSelected", opt);
|
this.$emit("optionSelected", opt);
|
||||||
this.isListOpen = false;
|
this.isListOpen = false;
|
||||||
},
|
},
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
value: {
|
modelValue: {
|
||||||
required: false,
|
required: false,
|
||||||
type: [Date, String]
|
type: [Date, String]
|
||||||
},
|
},
|
||||||
@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
stringValue() {
|
stringValue() {
|
||||||
const d = DateTimeUtils.toDate(this.value);
|
const d = DateTimeUtils.toDate(this.modelValue);
|
||||||
return DateTimeUtils.formatDateForEdit(d);
|
return DateTimeUtils.formatDateForEdit(d);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -30,7 +30,7 @@
|
|||||||
methods: {
|
methods: {
|
||||||
input(val) {
|
input(val) {
|
||||||
let d = DateTimeUtils.toDate(val + " 00:00");
|
let d = DateTimeUtils.toDate(val + " 00:00");
|
||||||
this.$emit("input", d);
|
this.$emit("update:modelValue", d);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -132,7 +132,13 @@
|
|||||||
created() {
|
created() {
|
||||||
if (LOADED_APIS[this.svgName] !== true) {
|
if (LOADED_APIS[this.svgName] !== true) {
|
||||||
for (let sb of this.svgData.scriptBlocks) {
|
for (let sb of this.svgData.scriptBlocks) {
|
||||||
new Function(sb)(window);
|
try {
|
||||||
|
new Function(sb)(window);
|
||||||
|
} catch (e) {
|
||||||
|
console.log(sb);
|
||||||
|
console.log(e);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
LOADED_APIS[this.svgName] = true;
|
LOADED_APIS[this.svgName] = true;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div ref="container">
|
<Teleport to="body">
|
||||||
<div ref="modal" :class="['popup', 'modal', { 'is-wide': wide, 'is-active': open && error === null }]">
|
<div :class="['popup', 'modal', { 'is-wide': wide, 'is-active': open && error === null }]">
|
||||||
<div class="modal-background" @click="close"></div>
|
<div class="modal-background" @click="close"></div>
|
||||||
<div class="modal-card">
|
<div class="modal-card">
|
||||||
<header class="modal-card-head">
|
<header class="modal-card-head">
|
||||||
@ -15,14 +15,17 @@
|
|||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Teleport>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
import { mapState } from "vuex";
|
import { mapState } from "pinia";
|
||||||
|
import { useAppConfigStore } from "../stores/appConfig";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
emits: ["dismiss"],
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
open: {
|
open: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
@ -35,16 +38,8 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted() {
|
|
||||||
this.$root.$el.appendChild(this.$refs.modal);
|
|
||||||
},
|
|
||||||
|
|
||||||
beforeDestroy() {
|
|
||||||
this.$refs.container.appendChild(this.$refs.modal);
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
...mapState([
|
...mapState(useAppConfigStore, [
|
||||||
'error'
|
'error'
|
||||||
])
|
])
|
||||||
},
|
},
|
||||||
|
@ -54,7 +54,8 @@
|
|||||||
<script>
|
<script>
|
||||||
|
|
||||||
import UserLogin from "./UserLogin";
|
import UserLogin from "./UserLogin";
|
||||||
import { mapState } from "vuex";
|
import { mapState } from "pinia";
|
||||||
|
import { useAppConfigStore } from "../stores/appConfig";
|
||||||
import { swUpdate } from "../lib/ServiceWorker";
|
import { swUpdate } from "../lib/ServiceWorker";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@ -65,7 +66,7 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
...mapState([
|
...mapState(useAppConfigStore, [
|
||||||
'route',
|
'route',
|
||||||
'user',
|
'user',
|
||||||
'updateAvailable'
|
'updateAvailable'
|
||||||
|
@ -32,7 +32,7 @@
|
|||||||
default: 0.5
|
default: 0.5
|
||||||
},
|
},
|
||||||
|
|
||||||
value: {
|
modelValue: {
|
||||||
required: false,
|
required: false,
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 0
|
default: 0
|
||||||
@ -47,7 +47,7 @@
|
|||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
ratingPercent() {
|
ratingPercent() {
|
||||||
return ((this.value || 0) / this.starCount) * 100.0;
|
return ((this.modelValue || 0) / this.starCount) * 100.0;
|
||||||
},
|
},
|
||||||
|
|
||||||
temporaryPercent() {
|
temporaryPercent() {
|
||||||
@ -69,7 +69,7 @@
|
|||||||
methods: {
|
methods: {
|
||||||
handleClick(evt) {
|
handleClick(evt) {
|
||||||
if (this.temporaryValue !== null) {
|
if (this.temporaryValue !== null) {
|
||||||
this.$emit("input", this.temporaryValue);
|
this.$emit("update:modelValue", this.temporaryValue);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ export default {
|
|||||||
default: ""
|
default: ""
|
||||||
},
|
},
|
||||||
|
|
||||||
value: {
|
modelValue: {
|
||||||
required: false,
|
required: false,
|
||||||
type: String,
|
type: String,
|
||||||
default: ""
|
default: ""
|
||||||
@ -37,7 +37,7 @@ export default {
|
|||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
triggerInput: debounce(function() {
|
triggerInput: debounce(function() {
|
||||||
this.$emit("input", this.text);
|
this.$emit("update:modelValue", this.text);
|
||||||
}, 250, {leading: false, trailing: true}),
|
}, 250, {leading: false, trailing: true}),
|
||||||
|
|
||||||
userUpdateText(text) {
|
userUpdateText(text) {
|
||||||
@ -55,7 +55,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
created() {
|
created() {
|
||||||
this.$watch("value",
|
this.$watch("modelValue",
|
||||||
val => this.propUpdateText(val),
|
val => this.propUpdateText(val),
|
||||||
{
|
{
|
||||||
immediate: true
|
immediate: true
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<div class="tag-editor control">
|
<div class="tag-editor control">
|
||||||
<input ref="input" type="text" class="input" :value="tagText" @input="inputHandler" @focus="getFocus" @blur="loseFocus">
|
<input ref="input" type="text" class="input" :value="tagText" @input="inputHandler" @focus="getFocus" @blur="loseFocus">
|
||||||
<div class="tags">
|
<div class="tags">
|
||||||
<span v-for="t in value" :key="t" class="tag">{{t}}</span>
|
<span v-for="t in modelValue" :key="t" class="tag">{{t}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
value: {
|
modelValue: {
|
||||||
required: true,
|
required: true,
|
||||||
type: Array
|
type: Array
|
||||||
}
|
}
|
||||||
@ -25,7 +25,7 @@
|
|||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
tagText() {
|
tagText() {
|
||||||
return this.value.join(" ");
|
return this.modelValue.join(" ");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -54,8 +54,8 @@
|
|||||||
|
|
||||||
const newTags = [...new Set(str.toString().split(/\s+/).filter(t => t.length > 0))];
|
const newTags = [...new Set(str.toString().split(/\s+/).filter(t => t.length > 0))];
|
||||||
|
|
||||||
if (!this.arraysEqual(newTags, this.value)) {
|
if (!this.arraysEqual(newTags, this.modelValue)) {
|
||||||
this.$emit("input", newTags);
|
this.$emit("update:modelValue", newTags);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
<div class="field">
|
<div class="field">
|
||||||
<label v-if="label.length" class="label is-small-mobile">{{ label }}</label>
|
<label v-if="label.length" class="label is-small-mobile">{{ label }}</label>
|
||||||
<div :class="controlClasses">
|
<div :class="controlClasses">
|
||||||
<textarea v-if="isTextarea" :class="inputClasses" :value="value" @input="input" :disabled="disabled"></textarea>
|
<textarea v-if="isTextarea" :class="inputClasses" :value="modelValue" @input="input" :disabled="disabled"></textarea>
|
||||||
<input v-else :type="type" :class="inputClasses" :value="value" @input="input" :disabled="disabled">
|
<input v-else :type="type" :class="inputClasses" :value="modelValue" @input="input" :disabled="disabled">
|
||||||
<app-icon class="is-right" icon="warning" v-if="validationError !== null"></app-icon>
|
<app-icon class="is-right" icon="warning" v-if="validationError !== null"></app-icon>
|
||||||
</div>
|
</div>
|
||||||
<p v-if="helpMessage !== null" :class="helpClasses">
|
<p v-if="helpMessage !== null" :class="helpClasses">
|
||||||
@ -21,7 +21,7 @@
|
|||||||
type: String,
|
type: String,
|
||||||
default: ""
|
default: ""
|
||||||
},
|
},
|
||||||
value: {
|
modelValue: {
|
||||||
required: false,
|
required: false,
|
||||||
type: [String, Number],
|
type: [String, Number],
|
||||||
default: ""
|
default: ""
|
||||||
@ -83,7 +83,7 @@
|
|||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
input(evt) {
|
input(evt) {
|
||||||
this.$emit("input", evt.target.value);
|
this.$emit("update:modelValue", evt.target.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,11 +62,14 @@
|
|||||||
<button class="button" type="button" @click="addUnit">Add Unit</button>
|
<button class="button" type="button" @click="addUnit">Add Unit</button>
|
||||||
|
|
||||||
<table class="table">
|
<table class="table">
|
||||||
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Name</th>
|
<th>Name</th>
|
||||||
<th>Grams</th>
|
<th>Grams</th>
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
<tr v-for="unit in visibleFoodUnits" :key="unit.id">
|
<tr v-for="unit in visibleFoodUnits" :key="unit.id">
|
||||||
<td>
|
<td>
|
||||||
<div class="control">
|
<div class="control">
|
||||||
@ -82,6 +85,7 @@
|
|||||||
<button type="button" class="button is-danger" @click="removeUnit(unit)">X</button>
|
<button type="button" class="button is-danger" @click="removeUnit(unit)">X</button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -96,14 +100,18 @@
|
|||||||
<div class="message-body">
|
<div class="message-body">
|
||||||
|
|
||||||
<table class="table">
|
<table class="table">
|
||||||
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Name</th>
|
<th>Name</th>
|
||||||
<th>Grams</th>
|
<th>Grams</th>
|
||||||
</tr>
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
<tr v-for="unit in food.ndbn_units">
|
<tr v-for="unit in food.ndbn_units">
|
||||||
<td>{{unit.description}}</td>
|
<td>{{unit.description}}</td>
|
||||||
<td>{{unit.gram_weight}}</td>
|
<td>{{unit.gram_weight}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@ -139,7 +147,8 @@
|
|||||||
<script>
|
<script>
|
||||||
|
|
||||||
import api from "../lib/Api";
|
import api from "../lib/Api";
|
||||||
import { mapState } from "vuex";
|
import { mapState } from "pinia";
|
||||||
|
import { useNutrientStore } from "../stores/nutrient";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
@ -166,7 +175,7 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
...mapState({
|
...mapState(useNutrientStore, {
|
||||||
nutrients: 'nutrientList'
|
nutrients: 'nutrientList'
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
@ -55,7 +55,8 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
import { mapState } from "vuex";
|
import { mapState } from "pinia";
|
||||||
|
import { useNutrientStore } from "../stores/nutrient";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
@ -66,7 +67,7 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
...mapState({
|
...mapState(useNutrientStore, {
|
||||||
nutrients: 'nutrientList'
|
nutrients: 'nutrientList'
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
@ -71,6 +71,7 @@
|
|||||||
<th>Result</th>
|
<th>Result</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Heading</td>
|
<td>Heading</td>
|
||||||
<td>
|
<td>
|
||||||
@ -155,6 +156,7 @@ _underline_
|
|||||||
</p>
|
</p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<h3 class="title is-3">Basic Example</h3>
|
<h3 class="title is-3">Basic Example</h3>
|
||||||
@ -206,7 +208,7 @@ _underline_
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
import autosize from "autosize";
|
//import autosize from "autosize";
|
||||||
import debounce from "lodash/debounce";
|
import debounce from "lodash/debounce";
|
||||||
import api from "../lib/Api";
|
import api from "../lib/Api";
|
||||||
|
|
||||||
|
@ -12,18 +12,22 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="column is-half">
|
<div class="column is-half">
|
||||||
<table class="table is-bordered is-narrow is-size-7">
|
<table class="table is-bordered is-narrow is-size-7">
|
||||||
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>#</th>
|
<th>#</th>
|
||||||
<th>Unit</th>
|
<th>Unit</th>
|
||||||
<th>Name</th>
|
<th>Name</th>
|
||||||
<th>Prep</th>
|
<th>Prep</th>
|
||||||
</tr>
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
<tr v-for="i in bulkIngredientPreview">
|
<tr v-for="i in bulkIngredientPreview">
|
||||||
<td>{{i.quantity}}</td>
|
<td>{{i.quantity}}</td>
|
||||||
<td>{{i.units}}</td>
|
<td>{{i.units}}</td>
|
||||||
<td>{{i.name}}</td>
|
<td>{{i.name}}</td>
|
||||||
<td>{{i.preparation}}</td>
|
<td>{{i.preparation}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -43,7 +47,8 @@
|
|||||||
|
|
||||||
import RecipeEditIngredientItem from "./RecipeEditIngredientItem";
|
import RecipeEditIngredientItem from "./RecipeEditIngredientItem";
|
||||||
|
|
||||||
import { mapState } from "vuex";
|
import { mapState } from "pinia";
|
||||||
|
import { useMediaQueryStore } from "../stores/mediaQuery";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
@ -61,8 +66,8 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
...mapState({
|
...mapState(useMediaQueryStore, {
|
||||||
isMobile: state => state.mediaQueries.mobile
|
isMobile: store => store.mobile
|
||||||
}),
|
}),
|
||||||
bulkIngredientPreview() {
|
bulkIngredientPreview() {
|
||||||
if (this.bulkEditText === null) {
|
if (this.bulkEditText === null) {
|
||||||
|
@ -65,14 +65,18 @@
|
|||||||
<div class="message-header" @click="showNutrition = !showNutrition">Nutrition Data</div>
|
<div class="message-header" @click="showNutrition = !showNutrition">Nutrition Data</div>
|
||||||
<div class="message-body" v-show="showNutrition">
|
<div class="message-body" v-show="showNutrition">
|
||||||
<table class="table">
|
<table class="table">
|
||||||
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Item</th>
|
<th>Item</th>
|
||||||
<th>Value</th>
|
<th>Value</th>
|
||||||
</tr>
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
<tr v-for="nutrient in recipe.nutrition_data.nutrients" :key="nutrient.name">
|
<tr v-for="nutrient in recipe.nutrition_data.nutrients" :key="nutrient.name">
|
||||||
<td>{{nutrient.label}}</td>
|
<td>{{nutrient.label}}</td>
|
||||||
<td>{{ roundValue(nutrient.value) }}</td>
|
<td>{{ roundValue(nutrient.value) }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<h3 class="title is-5">Nutrition Calculation Warnings</h3>
|
<h3 class="title is-5">Nutrition Calculation Warnings</h3>
|
||||||
@ -149,7 +153,8 @@
|
|||||||
<script>
|
<script>
|
||||||
|
|
||||||
import api from "../lib/Api";
|
import api from "../lib/Api";
|
||||||
import { mapActions, mapMutations, mapState } from "vuex";
|
import { mapActions, mapState } from "pinia";
|
||||||
|
import { useTaskStore } from "../stores/task";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
@ -185,7 +190,7 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
...mapState([
|
...mapState(useTaskStore, [
|
||||||
'taskLists'
|
'taskLists'
|
||||||
]),
|
]),
|
||||||
|
|
||||||
@ -235,11 +240,8 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions([
|
...mapActions(useTaskStore, [
|
||||||
'ensureTaskLists'
|
'ensureTaskLists',
|
||||||
]),
|
|
||||||
|
|
||||||
...mapMutations([
|
|
||||||
'setCurrentTaskList'
|
'setCurrentTaskList'
|
||||||
]),
|
]),
|
||||||
|
|
||||||
|
@ -63,8 +63,8 @@
|
|||||||
<script>
|
<script>
|
||||||
|
|
||||||
import * as Errors from '../lib/Errors';
|
import * as Errors from '../lib/Errors';
|
||||||
import { mapActions } from "vuex";
|
import { mapActions } from "pinia";
|
||||||
import cloneDeep from "lodash/cloneDeep";
|
import { useTaskStore } from "../stores/task";
|
||||||
|
|
||||||
import TaskItemEdit from "./TaskItemEdit";
|
import TaskItemEdit from "./TaskItemEdit";
|
||||||
|
|
||||||
@ -115,7 +115,7 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions([
|
...mapActions(useTaskStore, [
|
||||||
'createTaskItem',
|
'createTaskItem',
|
||||||
'updateTaskItem',
|
'updateTaskItem',
|
||||||
'deleteTaskItems',
|
'deleteTaskItems',
|
||||||
|
@ -53,19 +53,20 @@
|
|||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
||||||
@import "~styles/variables";
|
@use "bulma/sass" as bulma;
|
||||||
|
@use 'sass:color';
|
||||||
|
|
||||||
div.dropdown-item {
|
div.dropdown-item {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
&.hovered {
|
&.hovered {
|
||||||
color: $black;
|
color: bulma.$black;
|
||||||
background-color: $background;
|
background-color: bulma.$background;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.is-active {
|
&.is-active {
|
||||||
color: $link-invert;
|
color: color.invert(bulma.$link);
|
||||||
background-color: $link;
|
background-color: bulma.$link;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Parsley is released under the MIT License. All code © Dan Elbert 2020.
|
Parsley is released under the MIT License. All code © Dan Elbert 2024.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
|
@ -15,7 +15,6 @@
|
|||||||
<script>
|
<script>
|
||||||
|
|
||||||
import FoodShow from "./FoodShow";
|
import FoodShow from "./FoodShow";
|
||||||
import { mapState } from "vuex";
|
|
||||||
import api from "../lib/Api";
|
import api from "../lib/Api";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@ -26,9 +25,9 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
...mapState({
|
foodId() {
|
||||||
foodId: state => state.route.params.id,
|
return this.$route.params.id;
|
||||||
})
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
created() {
|
created() {
|
||||||
|
@ -12,7 +12,6 @@
|
|||||||
<script>
|
<script>
|
||||||
|
|
||||||
import FoodEdit from "./FoodEdit";
|
import FoodEdit from "./FoodEdit";
|
||||||
import { mapState } from "vuex";
|
|
||||||
import api from "../lib/Api";
|
import api from "../lib/Api";
|
||||||
import * as Errors from '../lib/Errors';
|
import * as Errors from '../lib/Errors';
|
||||||
|
|
||||||
|
@ -17,7 +17,6 @@
|
|||||||
<script>
|
<script>
|
||||||
|
|
||||||
import FoodEdit from "./FoodEdit";
|
import FoodEdit from "./FoodEdit";
|
||||||
import { mapState } from "vuex";
|
|
||||||
import api from "../lib/Api";
|
import api from "../lib/Api";
|
||||||
import * as Errors from '../lib/Errors';
|
import * as Errors from '../lib/Errors';
|
||||||
|
|
||||||
@ -30,9 +29,9 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
...mapState({
|
foodId() {
|
||||||
foodId: state => state.route.params.id,
|
return this.$route.params.id;
|
||||||
})
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -17,7 +17,6 @@
|
|||||||
<script>
|
<script>
|
||||||
|
|
||||||
import LogShow from "./LogShow";
|
import LogShow from "./LogShow";
|
||||||
import { mapState } from "vuex";
|
|
||||||
import api from "../lib/Api";
|
import api from "../lib/Api";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@ -28,9 +27,9 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
...mapState({
|
logId() {
|
||||||
logId: state => state.route.params.id,
|
return this.$route.params.id;
|
||||||
})
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
created() {
|
created() {
|
||||||
|
@ -19,7 +19,6 @@
|
|||||||
<script>
|
<script>
|
||||||
|
|
||||||
import LogEdit from "./LogEdit";
|
import LogEdit from "./LogEdit";
|
||||||
import { mapState } from "vuex";
|
|
||||||
import api from "../lib/Api";
|
import api from "../lib/Api";
|
||||||
import * as Errors from '../lib/Errors';
|
import * as Errors from '../lib/Errors';
|
||||||
|
|
||||||
@ -37,9 +36,9 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
...mapState({
|
recipeId() {
|
||||||
recipeId: state => state.route.params.recipeId,
|
return this.$route.params.recipeId;
|
||||||
})
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -18,7 +18,6 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
import { mapState } from "vuex";
|
|
||||||
import api from "../lib/Api";
|
import api from "../lib/Api";
|
||||||
import * as Errors from "../lib/Errors";
|
import * as Errors from "../lib/Errors";
|
||||||
import LogEdit from "./LogEdit";
|
import LogEdit from "./LogEdit";
|
||||||
@ -32,9 +31,9 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
...mapState({
|
logId() {
|
||||||
logId: state => state.route.params.id,
|
return this.$route.params.id;
|
||||||
})
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -5,19 +5,23 @@
|
|||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<table class="table">
|
<table class="table">
|
||||||
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Recipe</th>
|
<th>Recipe</th>
|
||||||
<th>Date</th>
|
<th>Date</th>
|
||||||
<th>Rating</th>
|
<th>Rating</th>
|
||||||
<th>Notes</th>
|
<th>Notes</th>
|
||||||
</tr>
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
<tr v-for="l in logs" :key="l.id">
|
<tr v-for="l in logs" :key="l.id">
|
||||||
<td> <router-link :to="{name: 'log', params: {id: l.id}}">{{l.recipe.name}}</router-link></td>
|
<td> <router-link :to="{name: 'log', params: {id: l.id}}">{{l.recipe.name}}</router-link></td>
|
||||||
<td><app-date-time :date-time="l.date" :show-time="false"></app-date-time> </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><app-rating :value="l.rating" readonly></app-rating></td>
|
||||||
<td>{{l.notes}}</td>
|
<td>{{l.notes}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
@ -9,12 +9,15 @@
|
|||||||
</app-modal>
|
</app-modal>
|
||||||
|
|
||||||
<table class="table">
|
<table class="table">
|
||||||
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Note</th>
|
<th>Note</th>
|
||||||
<th>Date</th>
|
<th>Date</th>
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
<tr v-for="n in notes" :key="n.id">
|
<tr v-for="n in notes" :key="n.id">
|
||||||
<td>
|
<td>
|
||||||
{{ n.content }}
|
{{ n.content }}
|
||||||
@ -28,6 +31,7 @@
|
|||||||
</button>
|
</button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
@ -25,7 +25,6 @@
|
|||||||
<script>
|
<script>
|
||||||
|
|
||||||
import RecipeShow from "./RecipeShow";
|
import RecipeShow from "./RecipeShow";
|
||||||
import { mapState } from "vuex";
|
|
||||||
import api from "../lib/Api";
|
import api from "../lib/Api";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@ -37,13 +36,11 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
...mapState({
|
recipeId() { return this.$route.params.id },
|
||||||
recipeId: state => state.route.params.id,
|
routeQuery() { return this.$route.query },
|
||||||
routeQuery: state => state.route.query,
|
scale() { return this.$route.query.scale || null },
|
||||||
scale: state => state.route.query.scale || null,
|
system() { return this.$route.query.system || null },
|
||||||
system: state => state.route.query.system || null,
|
unit() { return this.$route.query.unit || null },
|
||||||
unit: state => state.route.query.unit || null
|
|
||||||
}),
|
|
||||||
|
|
||||||
isScaled() {
|
isScaled() {
|
||||||
return this.recipe.converted_scale !== null && this.recipe.converted_scale.length > 0 && this.recipe.converted_scale !== "1";
|
return this.recipe.converted_scale !== null && this.recipe.converted_scale.length > 0 && this.recipe.converted_scale !== "1";
|
||||||
|
@ -21,7 +21,6 @@
|
|||||||
<script>
|
<script>
|
||||||
|
|
||||||
import RecipeEdit from "./RecipeEdit";
|
import RecipeEdit from "./RecipeEdit";
|
||||||
import { mapState } from "vuex";
|
|
||||||
import api from "../lib/Api";
|
import api from "../lib/Api";
|
||||||
import * as Errors from '../lib/Errors';
|
import * as Errors from '../lib/Errors';
|
||||||
|
|
||||||
@ -34,9 +33,9 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
...mapState({
|
recipeId() {
|
||||||
recipeId: state => state.route.params.id,
|
return this.$route.params.id;
|
||||||
})
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<table class="table is-fullwidth" :class="{ small: mediaQueries.touch }">
|
<table class="table is-fullwidth" :class="{ small: isTouch }">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th v-for="h in tableHeader" :key="h.name">
|
<th v-for="h in tableHeader" :key="h.name">
|
||||||
@ -91,9 +91,10 @@
|
|||||||
<script>
|
<script>
|
||||||
|
|
||||||
import api from "../lib/Api";
|
import api from "../lib/Api";
|
||||||
import debounce from "lodash/debounce";
|
import { mapWritableState, mapState } from "pinia";
|
||||||
import { mapMutations, mapState } from "vuex";
|
|
||||||
import AppLoading from "./AppLoading";
|
import AppLoading from "./AppLoading";
|
||||||
|
import { useAppConfigStore } from "../stores/appConfig";
|
||||||
|
import { useMediaQueryStore } from "../stores/mediaQuery";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
@ -112,8 +113,9 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
...mapState([
|
...mapState(useMediaQueryStore, { isTouch: store => store.touch }),
|
||||||
"mediaQueries"
|
...mapWritableState(useAppConfigStore, [
|
||||||
|
"initialLoad"
|
||||||
]),
|
]),
|
||||||
|
|
||||||
search() {
|
search() {
|
||||||
@ -174,10 +176,6 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
...mapMutations([
|
|
||||||
"setInitialLoad"
|
|
||||||
]),
|
|
||||||
|
|
||||||
buildQueryParams() {
|
buildQueryParams() {
|
||||||
return {
|
return {
|
||||||
name: this.searchQuery.name,
|
name: this.searchQuery.name,
|
||||||
@ -302,7 +300,7 @@
|
|||||||
created() {
|
created() {
|
||||||
this.$watch("search",
|
this.$watch("search",
|
||||||
() => {
|
() => {
|
||||||
this.getList().then(() => this.setInitialLoad(true));
|
this.getList().then(() => this.initialLoad = true);
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
deep: true,
|
deep: true,
|
||||||
|
@ -24,9 +24,9 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
import api from "../lib/Api";
|
|
||||||
import * as Errors from '../lib/Errors';
|
import * as Errors from '../lib/Errors';
|
||||||
import { mapActions, mapMutations, mapState } from "vuex";
|
import { mapActions, mapState } from "pinia";
|
||||||
|
import { useTaskStore } from "../stores/task";
|
||||||
|
|
||||||
import TaskListMiniForm from "./TaskListMiniForm";
|
import TaskListMiniForm from "./TaskListMiniForm";
|
||||||
import TaskListDropdownItem from "./TaskListDropdownItem";
|
import TaskListDropdownItem from "./TaskListDropdownItem";
|
||||||
@ -48,7 +48,7 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
...mapState([
|
...mapState(useTaskStore, [
|
||||||
'taskLists',
|
'taskLists',
|
||||||
'currentTaskList'
|
'currentTaskList'
|
||||||
]),
|
]),
|
||||||
@ -62,14 +62,12 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions([
|
...mapActions(useTaskStore, [
|
||||||
'refreshTaskLists',
|
'refreshTaskLists',
|
||||||
'createTaskList',
|
'createTaskList',
|
||||||
'deleteTaskList',
|
'deleteTaskList',
|
||||||
'deleteTaskItems',
|
'deleteTaskItems',
|
||||||
'completeTaskItems'
|
'completeTaskItems',
|
||||||
]),
|
|
||||||
...mapMutations([
|
|
||||||
'setCurrentTaskList'
|
'setCurrentTaskList'
|
||||||
]),
|
]),
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@
|
|||||||
<button type="submit" class="button is-primary" :disabled="!enableSubmit">Login</button>
|
<button type="submit" class="button is-primary" :disabled="!enableSubmit">Login</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<button class="button is-secondary" @click="showLogin = false">Cancel</button>
|
<button type="button" class="button is-secondary" @click="showLogin = false">Cancel</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -47,8 +47,8 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
import api from "../lib/Api";
|
import { mapActions, mapState } from "pinia";
|
||||||
import { mapActions, mapState } from "vuex";
|
import { useAppConfigStore } from "../stores/appConfig";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
@ -61,7 +61,7 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
...mapState([
|
...mapState(useAppConfigStore, [
|
||||||
'loginMessage'
|
'loginMessage'
|
||||||
]),
|
]),
|
||||||
enableSubmit() {
|
enableSubmit() {
|
||||||
@ -70,7 +70,7 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions([
|
...mapActions(useAppConfigStore, [
|
||||||
'login'
|
'login'
|
||||||
]),
|
]),
|
||||||
|
|
||||||
@ -86,6 +86,7 @@
|
|||||||
this.loadResource(
|
this.loadResource(
|
||||||
this.login(params)
|
this.login(params)
|
||||||
.then(data => {
|
.then(data => {
|
||||||
|
console.log(data);
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
this.showLogin = false;
|
this.showLogin = false;
|
||||||
}
|
}
|
||||||
|
@ -1,54 +1,6 @@
|
|||||||
|
|
||||||
import Vue from 'vue';
|
import { mapActions, mapState } from "pinia";
|
||||||
import { mapActions, mapGetters, mapMutations, mapState } from 'vuex';
|
import { useAppConfigStore } from "../stores/appConfig";
|
||||||
import api from "../lib/Api";
|
|
||||||
|
|
||||||
Vue.mixin({
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
localLoadingCount: 0
|
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
...mapGetters([
|
|
||||||
"isLoading",
|
|
||||||
"isLoggedIn",
|
|
||||||
"isAdmin"
|
|
||||||
]),
|
|
||||||
...mapState([
|
|
||||||
"user"
|
|
||||||
]),
|
|
||||||
localLoading() {
|
|
||||||
return this.localLoadingCount > 0;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
...mapActions([
|
|
||||||
'updateCurrentUser'
|
|
||||||
]),
|
|
||||||
...mapMutations([
|
|
||||||
'setError',
|
|
||||||
'setLoading'
|
|
||||||
]),
|
|
||||||
|
|
||||||
loadResource(promise) {
|
|
||||||
this.setLoading(true);
|
|
||||||
this.localLoadingCount = this.localLoadingCount + 1;
|
|
||||||
|
|
||||||
return promise
|
|
||||||
.catch(err => this.setError(err))
|
|
||||||
.then(res => {
|
|
||||||
this.setLoading(false);
|
|
||||||
this.localLoadingCount = this.localLoadingCount - 1;
|
|
||||||
return res;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
checkAuthentication() {
|
|
||||||
return this.loadResource(this.updateCurrentUser());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function clickStrikeClick(evt) {
|
function clickStrikeClick(evt) {
|
||||||
const isStrikable = el => el && el.tagName === "LI";
|
const isStrikable = el => el && el.tagName === "LI";
|
||||||
@ -73,12 +25,59 @@ function clickStrikeClick(evt) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Vue.directive('click-strike', {
|
export function installMixins(app) {
|
||||||
bind(el) {
|
app.directive('click-strike', {
|
||||||
el.addEventListener("click", clickStrikeClick);
|
beforeMount(el) {
|
||||||
},
|
el.addEventListener("click", clickStrikeClick);
|
||||||
|
},
|
||||||
|
|
||||||
unbind(el) {
|
unmounted(el) {
|
||||||
el.removeEventListener("click", clickStrikeClick);
|
el.removeEventListener("click", clickStrikeClick);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
app.mixin({
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
localLoadingCount: 0
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
...mapState(useAppConfigStore, [
|
||||||
|
"isLoading",
|
||||||
|
"isLoggedIn",
|
||||||
|
"isAdmin",
|
||||||
|
"user"
|
||||||
|
]),
|
||||||
|
|
||||||
|
localLoading() {
|
||||||
|
return this.localLoadingCount > 0;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
...mapActions(useAppConfigStore, [
|
||||||
|
'setError',
|
||||||
|
'setLoading',
|
||||||
|
'updateCurrentUser'
|
||||||
|
]),
|
||||||
|
|
||||||
|
loadResource(promise) {
|
||||||
|
this.setLoading(true);
|
||||||
|
this.localLoadingCount = this.localLoadingCount + 1;
|
||||||
|
|
||||||
|
return promise
|
||||||
|
.catch(err => this.setError(err))
|
||||||
|
.then(res => {
|
||||||
|
this.setLoading(false);
|
||||||
|
this.localLoadingCount = this.localLoadingCount - 1;
|
||||||
|
return res;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
checkAuthentication() {
|
||||||
|
return this.loadResource(this.updateCurrentUser());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
|
|
||||||
|
import { useAppConfigStore } from '../stores/appConfig';
|
||||||
|
|
||||||
function trackInstall(worker, cb) {
|
function trackInstall(worker, cb) {
|
||||||
worker.addEventListener('statechange', function() {
|
worker.addEventListener('statechange', function() {
|
||||||
if (worker.state == 'installed') {
|
if (worker.state == 'installed') {
|
||||||
@ -18,7 +20,9 @@ function trackActive(worker, cb) {
|
|||||||
export function swUpdate() {
|
export function swUpdate() {
|
||||||
navigator.serviceWorker.getRegistration().then(reg => {
|
navigator.serviceWorker.getRegistration().then(reg => {
|
||||||
if (reg && reg.waiting) {
|
if (reg && reg.waiting) {
|
||||||
trackActive(reg.waiting, () => window.location.reload(true));
|
trackActive(reg.waiting, () => {
|
||||||
|
window.location.reload(true)
|
||||||
|
});
|
||||||
reg.waiting.postMessage("skipWaiting");
|
reg.waiting.postMessage("skipWaiting");
|
||||||
} else {
|
} else {
|
||||||
window.location.reload(true);
|
window.location.reload(true);
|
||||||
@ -27,10 +31,17 @@ export function swUpdate() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function swInit(store) {
|
export function swInit() {
|
||||||
|
|
||||||
const updateReady = () => store.commit("setUpdateAvailable", true);
|
const updateReady = () => {
|
||||||
const clearUpdateReady = () => store.commit("setUpdateAvailable", false);
|
const store = useAppConfigStore();
|
||||||
|
store.updateAvailable = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const clearUpdateReady = () => {
|
||||||
|
const store = useAppConfigStore();
|
||||||
|
store.updateAvailable = false;
|
||||||
|
}
|
||||||
|
|
||||||
if ('serviceWorker' in navigator) {
|
if ('serviceWorker' in navigator) {
|
||||||
navigator.serviceWorker.register('/sw.js')
|
navigator.serviceWorker.register('/sw.js')
|
||||||
@ -53,6 +64,4 @@ export function swInit(store) {
|
|||||||
console.log('Registration failed with ' + error);
|
console.log('Registration failed with ' + error);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
54
app/javascript/packs/application.js
Normal file
54
app/javascript/packs/application.js
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import '../styles';
|
||||||
|
import { createApp } from 'vue';
|
||||||
|
import { createPinia } from 'pinia';
|
||||||
|
import { swInit } from "../lib/ServiceWorker";
|
||||||
|
import config from '../lib/config';
|
||||||
|
import { installMixins } from "../lib/GlobalMixins";
|
||||||
|
import router from '../router';
|
||||||
|
|
||||||
|
import AppAutocomplete from "../components/AppAutocomplete";
|
||||||
|
import AppConfirm from "../components/AppConfirm";
|
||||||
|
import AppDateTime from "../components/AppDateTime";
|
||||||
|
import AppDatePicker from "../components/AppDatePicker";
|
||||||
|
import AppDropdown from "../components/AppDropdown";
|
||||||
|
import AppExpandTransition from "../components/AppExpandTransition";
|
||||||
|
import AppIcon from "../components/AppIcon";
|
||||||
|
import AppIconicIcon from "../components/AppIconicIcon";
|
||||||
|
import AppModal from "../components/AppModal";
|
||||||
|
import AppNavbar from "../components/AppNavbar";
|
||||||
|
import AppPager from "../components/AppPager";
|
||||||
|
import AppRating from "../components/AppRating";
|
||||||
|
import AppSearchText from "../components/AppSearchText";
|
||||||
|
import AppTagEditor from "../components/AppTagEditor";
|
||||||
|
import AppTextField from "../components/AppTextField";
|
||||||
|
import AppValidationErrors from "../components/AppValidationErrors";
|
||||||
|
|
||||||
|
import App from '../components/App.vue'
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
const app = createApp(App);
|
||||||
|
const pinia = createPinia();
|
||||||
|
app.use(pinia);
|
||||||
|
app.use(router);
|
||||||
|
swInit();
|
||||||
|
installMixins(app);
|
||||||
|
|
||||||
|
app.component("AppAutocomplete", AppAutocomplete);
|
||||||
|
app.component("AppConfirm", AppConfirm);
|
||||||
|
app.component("AppDateTime", AppDateTime);
|
||||||
|
app.component("AppDatePicker", AppDatePicker);
|
||||||
|
app.component("AppDropdown", AppDropdown);
|
||||||
|
app.component("AppExpandTransition", AppExpandTransition);
|
||||||
|
app.component("AppIcon", AppIcon);
|
||||||
|
app.component("AppIconicIcon", AppIconicIcon);
|
||||||
|
app.component("AppModal", AppModal);
|
||||||
|
app.component("AppNavbar", AppNavbar);
|
||||||
|
app.component("AppPager", AppPager);
|
||||||
|
app.component("AppRating", AppRating);
|
||||||
|
app.component("AppSearchText", AppSearchText);
|
||||||
|
app.component("AppTagEditor", AppTagEditor);
|
||||||
|
app.component("AppTextField", AppTextField);
|
||||||
|
app.component("AppValidationErrors", AppValidationErrors);
|
||||||
|
|
||||||
|
app.mount('#app');
|
||||||
|
});
|
@ -1,5 +1,5 @@
|
|||||||
import Vue from 'vue';
|
import { createRouter, createWebHashHistory } from "vue-router";
|
||||||
import Router from 'vue-router';
|
import { nextTick } from "vue";
|
||||||
|
|
||||||
import The404Page from '../components/The404Page';
|
import The404Page from '../components/The404Page';
|
||||||
import TheAboutPage from '../components/TheAboutPage';
|
import TheAboutPage from '../components/TheAboutPage';
|
||||||
@ -28,26 +28,12 @@ import TheUserEditor from '../components/TheUserEditor';
|
|||||||
import TheAdminUserList from '../components/TheAdminUserList';
|
import TheAdminUserList from '../components/TheAdminUserList';
|
||||||
import TheAdminUserEditor from '../components/TheAdminUserEditor';
|
import TheAdminUserEditor from '../components/TheAdminUserEditor';
|
||||||
|
|
||||||
import $store from '../store';
|
import { useAppConfigStore } from "../stores/appConfig";
|
||||||
|
|
||||||
Vue.use(Router);
|
|
||||||
|
|
||||||
const router = new Router({
|
const router = createRouter({
|
||||||
routes: []
|
history: createWebHashHistory(),
|
||||||
});
|
routes: [
|
||||||
|
|
||||||
router.afterEach((to, from) => {
|
|
||||||
if (to.meta.handleInitialLoad !== true && $store.state.initialLoad === false) {
|
|
||||||
$store.commit("setInitialLoad", true);
|
|
||||||
}
|
|
||||||
|
|
||||||
Vue.nextTick(() => {
|
|
||||||
document.title = to.meta.title || 'Parsley';
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
router.addRoutes(
|
|
||||||
[
|
|
||||||
{
|
{
|
||||||
path: '/',
|
path: '/',
|
||||||
redirect: '/recipes'
|
redirect: '/recipes'
|
||||||
@ -148,10 +134,10 @@ router.addRoutes(
|
|||||||
{
|
{
|
||||||
path: "/logout",
|
path: "/logout",
|
||||||
name: "logout",
|
name: "logout",
|
||||||
beforeEnter: (to, from, next) => {
|
beforeEnter: async (to, from, next) => {
|
||||||
const $store = router.app.$store;
|
const appConfig = useAppConfigStore();
|
||||||
$store.dispatch("logout")
|
await appConfig.logout();
|
||||||
.then(() => next("/"));
|
return next("/");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -179,10 +165,22 @@ router.addRoutes(
|
|||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
path: '*',
|
path: '/:pathMatch(.*)*',
|
||||||
|
name: 'NotFound',
|
||||||
component: The404Page
|
component: The404Page
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
);
|
});
|
||||||
|
|
||||||
|
router.afterEach((to, from) => {
|
||||||
|
const appConfigStore = useAppConfigStore();
|
||||||
|
if (to.meta.handleInitialLoad !== true && appConfigStore.initialLoad === false) {
|
||||||
|
appConfigStore.initialLoad = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
nextTick(() => {
|
||||||
|
document.title = to.meta.title || 'Parsley';
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
102
app/javascript/stores/appConfig.js
Normal file
102
app/javascript/stores/appConfig.js
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
import { defineStore } from 'pinia';
|
||||||
|
import { computed, ref } from 'vue';
|
||||||
|
import { useRoute } from "vue-router";
|
||||||
|
import api from "../lib/Api";
|
||||||
|
|
||||||
|
export const useAppConfigStore = defineStore('appConfig', () => {
|
||||||
|
const authChecked = ref(false);
|
||||||
|
const errorObject = ref(null);
|
||||||
|
const initialLoad = ref(false);
|
||||||
|
const loadingCount = ref(0);
|
||||||
|
const loginMessage = ref(null);
|
||||||
|
const updateAvailable = ref(false);
|
||||||
|
const userObject = ref(null);
|
||||||
|
const route = useRoute();
|
||||||
|
|
||||||
|
const isLoading = computed(() => {
|
||||||
|
return loadingCount.value > 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
const isLoggedIn = computed(() => {
|
||||||
|
return userObject.value !== null;
|
||||||
|
});
|
||||||
|
|
||||||
|
const isAdmin = computed(() => {
|
||||||
|
return userObject.value?.admin === true;
|
||||||
|
});
|
||||||
|
|
||||||
|
const error = computed({
|
||||||
|
get: () => errorObject.value,
|
||||||
|
set: (val) => {
|
||||||
|
console.log(val);
|
||||||
|
errorObject.value = val;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const user = computed({
|
||||||
|
get: () => userObject.value,
|
||||||
|
set: (val) => {
|
||||||
|
userObject.value = val;
|
||||||
|
authChecked.value = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function setError(value) {
|
||||||
|
error.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setLoading(value) {
|
||||||
|
if (value) {
|
||||||
|
loadingCount.value = loadingCount.value + 1;
|
||||||
|
} else {
|
||||||
|
loadingCount.value = loadingCount.value - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateCurrentUser() {
|
||||||
|
user.value = await api.getCurrentUser();
|
||||||
|
return user.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function login(authData) {
|
||||||
|
return api.postLogin(authData.username, authData.password)
|
||||||
|
.then(data => {
|
||||||
|
if (data.success) {
|
||||||
|
user.value = data.user;
|
||||||
|
loginMessage.value = null;
|
||||||
|
} else {
|
||||||
|
user.value = null;
|
||||||
|
loginMessage.value = data.message;
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function logout() {
|
||||||
|
return api.getLogout()
|
||||||
|
.then(() => {
|
||||||
|
user.value = null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
authChecked,
|
||||||
|
error,
|
||||||
|
initialLoad,
|
||||||
|
loadingCount,
|
||||||
|
loginMessage,
|
||||||
|
updateAvailable,
|
||||||
|
user,
|
||||||
|
route,
|
||||||
|
|
||||||
|
isAdmin,
|
||||||
|
isLoading,
|
||||||
|
isLoggedIn,
|
||||||
|
|
||||||
|
login,
|
||||||
|
logout,
|
||||||
|
setError,
|
||||||
|
setLoading,
|
||||||
|
updateCurrentUser
|
||||||
|
};
|
||||||
|
});
|
35
app/javascript/stores/mediaQuery.js
Normal file
35
app/javascript/stores/mediaQuery.js
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import { defineStore } from 'pinia';
|
||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
|
export const useMediaQueryStore = defineStore('mediaQuery', () => {
|
||||||
|
// Hard coded values taken directly from Bulma css
|
||||||
|
const mobileBp = 768;
|
||||||
|
const desktopBp = 1024;
|
||||||
|
const widscreenBp = 1216;
|
||||||
|
const fullHdBp = 1408;
|
||||||
|
|
||||||
|
const mediaQueries = {
|
||||||
|
mobile: `screen and (max-width: ${mobileBp}px)`,
|
||||||
|
tablet: `screen and (min-width: ${mobileBp + 1}px)`,
|
||||||
|
tabletOnly: `screen and (min-width: ${mobileBp + 1}px) and (max-width: ${desktopBp - 1}px)`,
|
||||||
|
touch: `screen and (max-width: ${desktopBp - 1}px)`,
|
||||||
|
desktop: `screen and (min-width: ${desktopBp}px)`,
|
||||||
|
desktopOnly: `screen and (min-width: ${desktopBp}px) and (max-width: ${widscreenBp - 1}px)`,
|
||||||
|
widescreen: `screen and (min-width: ${widscreenBp}px)`,
|
||||||
|
widescreenOnly: `screen and (min-width: ${widscreenBp}px) and (max-width: ${fullHdBp - 1}px)`,
|
||||||
|
fullhd: `screen and (min-width: ${fullHdBp}px)`
|
||||||
|
};
|
||||||
|
|
||||||
|
const store = {};
|
||||||
|
|
||||||
|
for (let device in mediaQueries) {
|
||||||
|
const query = window.matchMedia(mediaQueries[device]);
|
||||||
|
store[device] = ref(query.matches);
|
||||||
|
|
||||||
|
query.onchange = (q) => {
|
||||||
|
store[device].value = q.matches;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return store;
|
||||||
|
});
|
36
app/javascript/stores/nutrient.js
Normal file
36
app/javascript/stores/nutrient.js
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import { defineStore } from 'pinia';
|
||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
|
export const useNutrientStore = defineStore('nutrient', () => {
|
||||||
|
const nutrientList = ref({
|
||||||
|
kcal: { label: "Calories", unit: "kcal" },
|
||||||
|
protein: { label: "Protein", unit: "g" },
|
||||||
|
lipids: { label: "Fat", unit: "g" },
|
||||||
|
carbohydrates: { label: "Carbohydrates", unit: "g" },
|
||||||
|
water: { label: "Water", unit: "g" },
|
||||||
|
sugar: { label: "Sugar", unit: "g" },
|
||||||
|
fiber: { label: "Fiber", unit: "g" },
|
||||||
|
cholesterol: { label: "Cholesterol", unit: "mg" },
|
||||||
|
sodium: { label: "Sodium", unit: "mg" },
|
||||||
|
calcium: { label: "Calcium", unit: "mg" },
|
||||||
|
iron: { label: "Iron", unit: "mg" },
|
||||||
|
magnesium: { label: "Magnesium", unit: "mg" },
|
||||||
|
phosphorus: { label: "Phosphorus", unit: "mg" },
|
||||||
|
potassium: { label: "Potassium", unit: "mg" },
|
||||||
|
zinc: { label: "Zinc", unit: "mg" },
|
||||||
|
copper: { label: "Copper", unit: "mg" },
|
||||||
|
manganese: { label: "Manganese", unit: "mg" },
|
||||||
|
vit_a: { label: "Vitamin A", unit: "μg" },
|
||||||
|
vit_b6: { label: "Vitamin B6", unit: "mg" },
|
||||||
|
vit_b12: { label: "Vitamin B12", unit: "μg" },
|
||||||
|
vit_c: { label: "Vitamin C", unit: "mg" },
|
||||||
|
vit_d: { label: "Vitamin D", unit: "μg" },
|
||||||
|
vit_e: { label: "Vitamin E", unit: "mg" },
|
||||||
|
vit_k: { label: "Vitamin K", unit: "μg" },
|
||||||
|
ash: { label: "ash", unit: "g" }
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
nutrientList
|
||||||
|
};
|
||||||
|
});
|
114
app/javascript/stores/task.js
Normal file
114
app/javascript/stores/task.js
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
import { defineStore } from 'pinia';
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { useAppConfigStore } from "./appConfig";
|
||||||
|
import api from "../lib/Api";
|
||||||
|
import { createChannel } from "../lib/ActionCable";
|
||||||
|
|
||||||
|
export const useTaskStore = defineStore('task', () => {
|
||||||
|
const taskLists = ref([]);
|
||||||
|
const currentTaskList = ref(null);
|
||||||
|
let taskChannel = null;
|
||||||
|
|
||||||
|
function replaceTaskList(list) {
|
||||||
|
if (taskLists.value) {
|
||||||
|
const listIdx = taskLists.value.findIndex(l => l.id === list.id);
|
||||||
|
if (listIdx >= 0) {
|
||||||
|
taskLists.value.splice(listIdx, 1, list);
|
||||||
|
}
|
||||||
|
if (currentTaskList.value && currentTaskList.value.id === list.id) {
|
||||||
|
currentTaskList.value = list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureTaskListChannel() {
|
||||||
|
if (taskChannel === null) {
|
||||||
|
taskChannel = createChannel(null, "TaskChannel", {
|
||||||
|
received(data) {
|
||||||
|
if (data && data.action === 'updated') {
|
||||||
|
replaceTaskList(data.task_list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function refreshTaskLists() {
|
||||||
|
const cb = function(data) {
|
||||||
|
taskLists.value = data || [];
|
||||||
|
let ctl = null;
|
||||||
|
|
||||||
|
if (currentTaskList.value) {
|
||||||
|
ctl = data.find(l => l.id === currentTaskList.value.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctl = ctl || data[0] || null;
|
||||||
|
setCurrentTaskList(ctl);
|
||||||
|
ensureTaskListChannel();
|
||||||
|
};
|
||||||
|
|
||||||
|
return api.getTaskLists(cb)
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureTaskLists() {
|
||||||
|
const appConfig = useAppConfigStore();
|
||||||
|
|
||||||
|
if (appConfig.user && taskLists.value.length === 0) {
|
||||||
|
return refreshTaskLists();
|
||||||
|
} else {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setCurrentTaskList(taskList) {
|
||||||
|
currentTaskList.value = taskList || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createTaskList(newList) {
|
||||||
|
currentTaskList.value = await api.postTaskList(newList);
|
||||||
|
return refreshTaskLists();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deleteTaskList(taskList) {
|
||||||
|
await api.deleteTaskList(taskList);
|
||||||
|
return refreshTaskLists();
|
||||||
|
}
|
||||||
|
|
||||||
|
function createTaskItem(taskItem) {
|
||||||
|
return api.postTaskItem(taskItem.task_list_id, taskItem)
|
||||||
|
.then(data => {
|
||||||
|
return data;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateTaskItem(taskItem) {
|
||||||
|
return api.patchTaskItem(taskItem.task_list_id, taskItem)
|
||||||
|
.then(data => {
|
||||||
|
return data;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteTaskItems(payload) {
|
||||||
|
return api.deleteTaskItems(payload.taskList.id, payload.taskItems);
|
||||||
|
}
|
||||||
|
|
||||||
|
function completeTaskItems(payload) {
|
||||||
|
return api.completeTaskItems(payload.taskList.id, payload.taskItems, !payload.completed);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
currentTaskList,
|
||||||
|
taskLists,
|
||||||
|
|
||||||
|
createTaskList,
|
||||||
|
deleteTaskList,
|
||||||
|
ensureTaskLists,
|
||||||
|
refreshTaskLists,
|
||||||
|
setCurrentTaskList,
|
||||||
|
|
||||||
|
createTaskItem,
|
||||||
|
updateTaskItem,
|
||||||
|
deleteTaskItems,
|
||||||
|
completeTaskItems
|
||||||
|
};
|
||||||
|
});
|
@ -1,21 +1,25 @@
|
|||||||
@import "./variables";
|
// coolors.co pallet
|
||||||
|
$coolors-dark: rgba(29, 30, 24, 1);
|
||||||
|
$coolors-blue: rgba(67, 127, 151, 1);
|
||||||
|
$coolors-green: rgba(121, 167, 54, 1);
|
||||||
|
$coolors-red: #ab4c34;
|
||||||
|
$coolors-yellow: rgba(240, 162, 2, 1);
|
||||||
|
|
||||||
@import "~bulma/sass/utilities/_all";
|
$family-serif: Georgia, "Times New Roman", Times, serif;
|
||||||
@import "~bulma/sass/base/_all";
|
|
||||||
@import "~bulma/sass/components/dropdown";
|
|
||||||
@import "~bulma/sass/components/navbar";
|
|
||||||
@import "~bulma/sass/components/level";
|
|
||||||
@import "~bulma/sass/components/message";
|
|
||||||
@import "~bulma/sass/components/modal";
|
|
||||||
@import "~bulma/sass/components/pagination";
|
|
||||||
@import "~bulma/sass/components/panel";
|
|
||||||
@import "~bulma/sass/elements/_all";
|
|
||||||
@import "~bulma/sass/form/_all";
|
|
||||||
@import "~bulma/sass/grid/columns";
|
|
||||||
@import "~bulma/sass/layout/section";
|
|
||||||
|
|
||||||
@import "./responsive_controls";
|
@use "bulma/sass" as bulma with (
|
||||||
@import "./wide_modal";
|
$family-primary: $family-serif,
|
||||||
|
$green: $coolors-green,
|
||||||
|
$blue: $coolors-blue,
|
||||||
|
$red: $coolors-red,
|
||||||
|
$yellow: $coolors-yellow,
|
||||||
|
$dark: $coolors-dark,
|
||||||
|
$primary: $coolors-green,
|
||||||
|
$modal-content-width: 750px
|
||||||
|
);
|
||||||
|
|
||||||
|
// @import "./responsive_controls";
|
||||||
|
// @import "./wide_modal";
|
||||||
@import "./iconic";
|
@import "./iconic";
|
||||||
@import "./transitions";
|
@import "./transitions";
|
||||||
|
|
||||||
@ -28,7 +32,7 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
background-color: $grey-dark;
|
background-color: bulma.$grey-dark;
|
||||||
padding-bottom: 2rem;
|
padding-bottom: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,13 +41,13 @@ body {
|
|||||||
|
|
||||||
.container {
|
.container {
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
background-color: $white;
|
background-color: bulma.$white;
|
||||||
min-height: 75vh;
|
min-height: 75vh;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.title, .subtitle, .navbar, .button, .pagination, .modal-card-title, th {
|
.title, .subtitle, .navbar, .button, .pagination, .modal-card-title, th {
|
||||||
font-family: $family-sans-serif;
|
font-family: bulma.$family-sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pagination:not(:last-child) {
|
.pagination:not(:last-child) {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
<%
|
<%
|
||||||
manifest_data = Webpacker::manifest.refresh
|
manifest_data = Shakapacker::manifest.refresh
|
||||||
manifest_timestamp = File.mtime(Webpacker::config.public_manifest_path).to_i
|
manifest_timestamp = File.mtime(Shakapacker::config.public_manifest_path).to_i
|
||||||
pack_assets = manifest_data.select { |name, asset| name !~ /\.map$/ && name != 'entrypoints' }.values
|
pack_assets = manifest_data.select { |name, asset| name !~ /\.map$/ && name != 'entrypoints' }.values
|
||||||
%>
|
%>
|
||||||
|
|
||||||
|
@ -17,6 +17,9 @@ FileUtils.chdir APP_ROOT do
|
|||||||
system! "gem install bundler --conservative"
|
system! "gem install bundler --conservative"
|
||||||
system("bundle check") || system!("bundle install")
|
system("bundle check") || system!("bundle install")
|
||||||
|
|
||||||
|
# Install JavaScript dependencies
|
||||||
|
system!("yarn install --no-immutable")
|
||||||
|
|
||||||
# puts "\n== Copying sample files =="
|
# puts "\n== Copying sample files =="
|
||||||
# unless File.exist?("config/database.yml")
|
# unless File.exist?("config/database.yml")
|
||||||
# FileUtils.cp "config/database.yml.sample", "config/database.yml"
|
# FileUtils.cp "config/database.yml.sample", "config/database.yml"
|
||||||
|
@ -4,10 +4,10 @@ ENV["RAILS_ENV"] ||= "development"
|
|||||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", __FILE__)
|
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", __FILE__)
|
||||||
|
|
||||||
require "bundler/setup"
|
require "bundler/setup"
|
||||||
require "webpacker"
|
require "shakapacker"
|
||||||
require "webpacker/webpack_runner"
|
require "shakapacker/webpack_runner"
|
||||||
|
|
||||||
APP_ROOT = File.expand_path("..", __dir__)
|
APP_ROOT = File.expand_path("..", __dir__)
|
||||||
Dir.chdir(APP_ROOT) do
|
Dir.chdir(APP_ROOT) do
|
||||||
Webpacker::WebpackRunner.run(ARGV)
|
Shakapacker::WebpackRunner.run(ARGV)
|
||||||
end
|
end
|
@ -4,10 +4,10 @@ ENV["RAILS_ENV"] ||= "development"
|
|||||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", __FILE__)
|
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", __FILE__)
|
||||||
|
|
||||||
require "bundler/setup"
|
require "bundler/setup"
|
||||||
require "webpacker"
|
require "shakapacker"
|
||||||
require "webpacker/dev_server_runner"
|
require "shakapacker/dev_server_runner"
|
||||||
|
|
||||||
APP_ROOT = File.expand_path("..", __dir__)
|
APP_ROOT = File.expand_path("..", __dir__)
|
||||||
Dir.chdir(APP_ROOT) do
|
Dir.chdir(APP_ROOT) do
|
||||||
Webpacker::DevServerRunner.run(ARGV)
|
Shakapacker::DevServerRunner.run(ARGV)
|
||||||
end
|
end
|
19
bin/webpack
19
bin/webpack
@ -1,19 +0,0 @@
|
|||||||
#!/usr/bin/env ruby
|
|
||||||
|
|
||||||
ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development"
|
|
||||||
ENV["NODE_ENV"] ||= "development"
|
|
||||||
|
|
||||||
require "pathname"
|
|
||||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
|
||||||
Pathname.new(__FILE__).realpath)
|
|
||||||
|
|
||||||
require "rubygems"
|
|
||||||
require "bundler/setup"
|
|
||||||
|
|
||||||
require "webpacker"
|
|
||||||
require "webpacker/webpack_runner"
|
|
||||||
|
|
||||||
APP_ROOT = File.expand_path("..", __dir__)
|
|
||||||
Dir.chdir(APP_ROOT) do
|
|
||||||
Webpacker::WebpackRunner.run(ARGV)
|
|
||||||
end
|
|
@ -1,19 +0,0 @@
|
|||||||
#!/usr/bin/env ruby
|
|
||||||
|
|
||||||
ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development"
|
|
||||||
ENV["NODE_ENV"] ||= "development"
|
|
||||||
|
|
||||||
require "pathname"
|
|
||||||
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
|
||||||
Pathname.new(__FILE__).realpath)
|
|
||||||
|
|
||||||
require "rubygems"
|
|
||||||
require "bundler/setup"
|
|
||||||
|
|
||||||
require "webpacker"
|
|
||||||
require "webpacker/dev_server_runner"
|
|
||||||
|
|
||||||
APP_ROOT = File.expand_path("..", __dir__)
|
|
||||||
Dir.chdir(APP_ROOT) do
|
|
||||||
Webpacker::DevServerRunner.run(ARGV)
|
|
||||||
end
|
|
18
bin/yarn
18
bin/yarn
@ -1,18 +0,0 @@
|
|||||||
#!/usr/bin/env ruby
|
|
||||||
|
|
||||||
APP_ROOT = File.expand_path("..", __dir__)
|
|
||||||
Dir.chdir(APP_ROOT) do
|
|
||||||
yarn = ENV["PATH"].split(File::PATH_SEPARATOR).
|
|
||||||
select { |dir| File.expand_path(dir) != __dir__ }.
|
|
||||||
product(["yarn", "yarnpkg", "yarn.cmd", "yarn.ps1"]).
|
|
||||||
map { |dir, file| File.expand_path(file, dir) }.
|
|
||||||
find { |file| File.executable?(file) }
|
|
||||||
|
|
||||||
if yarn
|
|
||||||
exec yarn, *ARGV
|
|
||||||
else
|
|
||||||
$stderr.puts "Yarn executable was not detected in the system."
|
|
||||||
$stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install"
|
|
||||||
exit 1
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,15 +1,17 @@
|
|||||||
# Note: You must restart bin/webpacker-dev-server for changes to take effect
|
# Note: You must restart bin/shakapacker-dev-server for changes to take effect
|
||||||
|
# This file contains the defaults used by shakapacker.
|
||||||
|
|
||||||
default: &default
|
default: &default
|
||||||
source_path: app/javascript
|
source_path: app/javascript
|
||||||
|
|
||||||
# You can have a subdirectory of the source_path, like 'packs' (recommended).
|
# You can have a subdirectory of the source_path, like 'packs' (recommended).
|
||||||
# Alternatively, you can use '/' to use the whole source_path directory.
|
# Alternatively, you can use '/' to use the whole source_path directory.
|
||||||
source_entry_path: /
|
# Notice that this is a relative path to source_path
|
||||||
|
source_entry_path: packs
|
||||||
|
|
||||||
# If nested_entries is true, then we'll pick up subdirectories within the source_entry_path.
|
# If nested_entries is true, then we'll pick up subdirectories within the source_entry_path.
|
||||||
# You cannot set this option to true if you set source_entry_path to '/'
|
# You cannot set this option to true if you set source_entry_path to '/'
|
||||||
nested_entries: false
|
nested_entries: true
|
||||||
|
|
||||||
# While using a File-System-based automated bundle generation feature, miscellaneous warnings suggesting css order
|
# While using a File-System-based automated bundle generation feature, miscellaneous warnings suggesting css order
|
||||||
# conflicts may arise due to the mini-css-extract-plugin. For projects where css ordering has been mitigated through
|
# conflicts may arise due to the mini-css-extract-plugin. For projects where css ordering has been mitigated through
|
||||||
@ -19,10 +21,10 @@ default: &default
|
|||||||
|
|
||||||
public_root_path: public
|
public_root_path: public
|
||||||
public_output_path: packs
|
public_output_path: packs
|
||||||
cache_path: tmp/webpacker
|
cache_path: tmp/shakapacker
|
||||||
webpack_compile_output: true
|
webpack_compile_output: true
|
||||||
# See https://github.com/shakacode/shakapacker#deployment
|
# See https://github.com/shakacode/shakapacker#deployment
|
||||||
webpacker_precompile: true
|
shakapacker_precompile: true
|
||||||
|
|
||||||
# Location for manifest.json, defaults to {public_output_path}/manifest.json if unset
|
# Location for manifest.json, defaults to {public_output_path}/manifest.json if unset
|
||||||
# manifest_path: public/packs/manifest.json
|
# manifest_path: public/packs/manifest.json
|
||||||
@ -35,48 +37,63 @@ default: &default
|
|||||||
cache_manifest: false
|
cache_manifest: false
|
||||||
|
|
||||||
# Select loader to use, available options are 'babel' (default), 'swc' or 'esbuild'
|
# Select loader to use, available options are 'babel' (default), 'swc' or 'esbuild'
|
||||||
webpack_loader: 'babel'
|
webpack_loader: 'swc'
|
||||||
|
|
||||||
# Set to true to enable check for matching versions of shakapacker gem and NPM package - will raise an error if there is a mismatch or wildcard versioning is used
|
# Raises an error if there is a mismatch in the shakapacker gem and npm package being used
|
||||||
ensure_consistent_versioning: false
|
ensure_consistent_versioning: true
|
||||||
|
|
||||||
# Select whether the compiler will use SHA digest ('digest' option) or most most recent modified timestamp ('mtime') to determine freshness
|
# Select whether the compiler will use SHA digest ('digest' option) or most most recent modified timestamp ('mtime') to determine freshness
|
||||||
compiler_strategy: digest
|
compiler_strategy: digest
|
||||||
|
|
||||||
|
# Select whether the compiler will always use a content hash and not just in production
|
||||||
|
# Don't use contentHash except for production for performance
|
||||||
|
# https://webpack.js.org/guides/build-performance/#avoid-production-specific-tooling
|
||||||
|
useContentHash: false
|
||||||
|
|
||||||
|
# Setting the asset host here will override Rails.application.config.asset_host.
|
||||||
|
# Here, you can set different asset_host per environment. Note that
|
||||||
|
# SHAKAPACKER_ASSET_HOST will override both configurations.
|
||||||
|
# asset_host: custom-path
|
||||||
|
|
||||||
development:
|
development:
|
||||||
<<: *default
|
<<: *default
|
||||||
compile: true
|
compile: true
|
||||||
compiler_strategy: mtime
|
compiler_strategy: mtime
|
||||||
|
|
||||||
# Reference: https://webpack.js.org/configuration/dev-server/
|
# Reference: https://webpack.js.org/configuration/dev-server/
|
||||||
|
# Keys not described there are documented inline and in https://github.com/shakacode/shakapacker/
|
||||||
dev_server:
|
dev_server:
|
||||||
https: false
|
# For running dev server with https, set `server: https`.
|
||||||
|
# server: https
|
||||||
|
|
||||||
host: localhost
|
host: localhost
|
||||||
port: 3035
|
port: 3035
|
||||||
# Hot Module Replacement updates modules while the application is running without a full reload
|
# Hot Module Replacement updates modules while the application is running without a full reload
|
||||||
|
# Used instead of the `hot` key in https://webpack.js.org/configuration/dev-server/#devserverhot
|
||||||
hmr: false
|
hmr: false
|
||||||
# If HMR is on, CSS will by inlined by delivering it as part of the script payload via style-loader. Be sure
|
# If HMR is on, CSS will by inlined by delivering it as part of the script payload via style-loader. Be sure
|
||||||
# that you add style-loader to your project dependencies.
|
# that you add style-loader to your project dependencies.
|
||||||
#
|
#
|
||||||
# If you want to instead deliver CSS via <link> with the mini-extract-css-plugin, set inline_css to false.
|
# If you want to instead deliver CSS via <link> with the mini-css-extract-plugin, set inline_css to false.
|
||||||
# In that case, style-loader is not needed as a dependency.
|
# In that case, style-loader is not needed as a dependency.
|
||||||
#
|
#
|
||||||
# mini-extract-css-plugin is a required dependency in both cases.
|
# mini-css-extract-plugin is a required dependency in both cases.
|
||||||
inline_css: true
|
inline_css: true
|
||||||
# Defaults to the inverse of hmr. Uncomment to manually set this.
|
# Defaults to the inverse of hmr. Uncomment to manually set this.
|
||||||
# live_reload: true
|
live_reload: false
|
||||||
client:
|
client:
|
||||||
# Should we show a full-screen overlay in the browser when there are compiler errors or warnings?
|
# Should we show a full-screen overlay in the browser when there are compiler errors or warnings?
|
||||||
overlay: true
|
overlay: true
|
||||||
# May also be a string
|
# May also be a string
|
||||||
# webSocketURL:
|
# webSocketURL:
|
||||||
# hostname: "0.0.0.0"
|
# hostname: '0.0.0.0'
|
||||||
# pathname: "/ws"
|
# pathname: '/ws'
|
||||||
# port: 8080
|
# port: 8080
|
||||||
# Should we use gzip compression?
|
# Should we use gzip compression?
|
||||||
compress: true
|
compress: true
|
||||||
# Note that apps that do not check the host are vulnerable to DNS rebinding attacks
|
# Note that apps that do not check the host are vulnerable to DNS rebinding attacks
|
||||||
allowed_hosts: "all"
|
allowed_hosts: 'auto'
|
||||||
|
# Shows progress and colorizes output of bin/shakapacker[-dev-server]
|
||||||
pretty: true
|
pretty: true
|
||||||
headers:
|
headers:
|
||||||
'Access-Control-Allow-Origin': '*'
|
'Access-Control-Allow-Origin': '*'
|
||||||
@ -91,17 +108,14 @@ test:
|
|||||||
# Compile test packs to a separate directory
|
# Compile test packs to a separate directory
|
||||||
public_output_path: packs-test
|
public_output_path: packs-test
|
||||||
|
|
||||||
production: &production
|
production:
|
||||||
<<: *default
|
<<: *default
|
||||||
|
|
||||||
# Production depends on precompilation of packs prior to booting for performance.
|
# Production depends on precompilation of packs prior to booting for performance.
|
||||||
compile: false
|
compile: false
|
||||||
|
|
||||||
|
# Use content hash for naming assets. Cannot be overridden by for production.
|
||||||
|
useContentHash: true
|
||||||
|
|
||||||
# Cache manifest.json for performance
|
# Cache manifest.json for performance
|
||||||
cache_manifest: true
|
cache_manifest: true
|
||||||
|
|
||||||
docker:
|
|
||||||
<<: *production
|
|
||||||
|
|
||||||
beta:
|
|
||||||
<<: *production
|
|
@ -13,9 +13,11 @@ const iriElementsAndProperties = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
module.exports = function(content) {
|
module.exports = function(content) {
|
||||||
|
|
||||||
this.cacheable && this.cacheable();
|
this.cacheable && this.cacheable();
|
||||||
|
|
||||||
const $ = cheerio.load(content, {
|
const $ = cheerio.load(content, {
|
||||||
|
xmlMode: true,
|
||||||
lowerCaseTags: false
|
lowerCaseTags: false
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -71,6 +73,7 @@ module.exports = function(content) {
|
|||||||
const svgMarkup = $("svg").html();
|
const svgMarkup = $("svg").html();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return `module.exports = ${JSON.stringify({
|
return `module.exports = ${JSON.stringify({
|
||||||
attributes: attributes,
|
attributes: attributes,
|
||||||
content: svgMarkup,
|
content: svgMarkup,
|
||||||
|
@ -2,4 +2,4 @@ module.exports = {
|
|||||||
resolve: {
|
resolve: {
|
||||||
extensions: ['.css', '.scss']
|
extensions: ['.css', '.scss']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,10 +6,9 @@ module.exports = {
|
|||||||
{
|
{
|
||||||
test: /\/iconic\/svg\/.*\.svg$/,
|
test: /\/iconic\/svg\/.*\.svg$/,
|
||||||
use: [
|
use: [
|
||||||
'vue-loader',
|
{ loader: path.join(__dirname, '../loaders/iconic_svg_loader.js') }
|
||||||
path.join(__dirname, '../loaders/iconic_svg_loader.js')
|
|
||||||
],
|
],
|
||||||
type: 'asset/source'
|
type: 'javascript/auto'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -1,16 +1,25 @@
|
|||||||
|
const { env } = require('shakapacker')
|
||||||
|
const { DefinePlugin } = require('webpack')
|
||||||
const { VueLoaderPlugin } = require('vue-loader')
|
const { VueLoaderPlugin } = require('vue-loader')
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
module: {
|
module: {
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
test: /\.vue$/,
|
test: /\.vue$/,
|
||||||
loader: 'vue-loader'
|
loader: 'vue-loader'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
plugins: [new VueLoaderPlugin()],
|
plugins: [
|
||||||
resolve: {
|
new VueLoaderPlugin(),
|
||||||
extensions: ['.vue']
|
new DefinePlugin({
|
||||||
}
|
__VUE_OPTIONS_API__: true,
|
||||||
|
__VUE_PROD_DEVTOOLS__: env.isDevelopment,
|
||||||
|
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__: env.isDevelopment
|
||||||
|
})
|
||||||
|
],
|
||||||
|
resolve: {
|
||||||
|
extensions: ['.vue']
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,32 +1,34 @@
|
|||||||
const { merge, mergeWithRules, webpackConfig } = require('shakapacker')
|
// See the shakacode/shakapacker README and docs directory for advice on customizing your webpackConfig.
|
||||||
const cssConfig = require('./rules/css');
|
const { generateWebpackConfig, merge, mergeWithRules } = require('shakapacker')
|
||||||
const vueConfig = require('./rules/vue');
|
const cssConfig = require('./rules/css')
|
||||||
const svgConfig = require('./rules/svg');
|
const svgConfig = require('./rules/svg')
|
||||||
|
const vueConfig = require('./rules/vue')
|
||||||
|
|
||||||
|
const webpackConfig = generateWebpackConfig()
|
||||||
|
|
||||||
let conf = merge(vueConfig, svgConfig);
|
let conf = merge(vueConfig, svgConfig);
|
||||||
conf = merge(cssConfig, conf);
|
conf = merge(cssConfig, conf);
|
||||||
conf = merge(conf, webpackConfig);
|
conf = merge(conf, webpackConfig);
|
||||||
|
|
||||||
const updateFileLoaderConf = {
|
const updateFileLoaderConf = {
|
||||||
module: {
|
module: {
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
type: 'asset/resource',
|
type: 'asset/resource',
|
||||||
// This version does not include svg. See https://github.com/shakacode/shakapacker/blob/v6.2.0/package/rules/file.js
|
// This version does not include svg. See https://github.com/shakacode/shakapacker/blob/v6.2.0/package/rules/file.js
|
||||||
test: /\.(bmp|gif|jpe?g|png|tiff|ico|avif|webp|eot|otf|ttf|woff|woff2)$/
|
test: /\.(bmp|gif|jpe?g|png|tiff|ico|avif|webp|eot|otf|ttf|woff|woff2)$/
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
conf = mergeWithRules({
|
conf = mergeWithRules({
|
||||||
module: {
|
module: {
|
||||||
rules: {
|
rules: {
|
||||||
type: "match",
|
type: "match",
|
||||||
test: "replace"
|
test: "replace"
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
})(conf, updateFileLoaderConf);
|
})(conf, updateFileLoaderConf);
|
||||||
|
|
||||||
|
|
||||||
module.exports = conf
|
module.exports = conf
|
||||||
|
24
index.html
24
index.html
@ -1,24 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<meta name ="theme-color" content="rgba(121, 167, 54, 1)">
|
|
||||||
<link rel="manifest" href="/manifest.json">
|
|
||||||
|
|
||||||
<title>Parsley</title>
|
|
||||||
|
|
||||||
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<div id="app" data-url="http://localhost:3000/">
|
|
||||||
<div id="app-placeholder">
|
|
||||||
Loading...
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script src="/packs/application-3067a90281a92d120ca6.js"></script>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1,54 +0,0 @@
|
|||||||
namespace :dev do
|
|
||||||
|
|
||||||
desc 'Run both rails server and webpack dev server'
|
|
||||||
task :run do
|
|
||||||
|
|
||||||
rails_pid = fork do
|
|
||||||
exec 'rails s'
|
|
||||||
end
|
|
||||||
|
|
||||||
webpack_pid = fork do
|
|
||||||
exec 'bin/webpack-dev-server'
|
|
||||||
end
|
|
||||||
|
|
||||||
running = true
|
|
||||||
shutdown = false
|
|
||||||
shutdown_start = nil
|
|
||||||
|
|
||||||
Signal.trap('SIGINT') do
|
|
||||||
shutdown = true
|
|
||||||
end
|
|
||||||
|
|
||||||
while running
|
|
||||||
rails_check ||= Process.waitpid(rails_pid, Process::WNOHANG)
|
|
||||||
webpack_check ||= Process.waitpid(webpack_pid, Process::WNOHANG)
|
|
||||||
running = rails_check.nil? || webpack_check.nil?
|
|
||||||
|
|
||||||
if shutdown
|
|
||||||
if shutdown_start.nil?
|
|
||||||
puts "Shutting down..."
|
|
||||||
shutdown_start = Time.now
|
|
||||||
#Process.kill("SIGINT", rails_pid) rescue Errno::ESRCH
|
|
||||||
#Process.kill("SIGINT", webpack_pid) rescue Errno::ESRCH
|
|
||||||
end
|
|
||||||
|
|
||||||
if (Time.now - shutdown_start) > 5
|
|
||||||
if rails_check.nil?
|
|
||||||
puts "Force killing rails..."
|
|
||||||
Process.kill("KILL", rails_pid)
|
|
||||||
end
|
|
||||||
|
|
||||||
if webpack_check.nil?
|
|
||||||
puts "Force killing webpack..."
|
|
||||||
Process.kill("KILL", webpack_pid)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
sleep 0.25
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
80
package.json
80
package.json
@ -1,44 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "parsley",
|
"packageManager": "yarn@4.5.0",
|
||||||
|
"name": "app",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
|
||||||
"@babel/core": "7",
|
|
||||||
"@babel/plugin-transform-runtime": "7",
|
|
||||||
"@babel/preset-env": "7",
|
|
||||||
"@babel/runtime": "7",
|
|
||||||
"@rails/actioncable": "^7.0.4",
|
|
||||||
"@tweenjs/tween.js": "^18.6.4",
|
|
||||||
"autosize": "^5.0.2",
|
|
||||||
"babel-loader": "8",
|
|
||||||
"bulma": "0.9.4",
|
|
||||||
"cheerio": "^1.0.0-rc.12",
|
|
||||||
"compression-webpack-plugin": "9",
|
|
||||||
"css-loader": "^6.7.2",
|
|
||||||
"css-minimizer-webpack-plugin": "^4.2.2",
|
|
||||||
"lodash": "^4.17.21",
|
|
||||||
"mini-css-extract-plugin": "^2.7.1",
|
|
||||||
"sass": "^1.56.1",
|
|
||||||
"sass-loader": "^13.2.0",
|
|
||||||
"shakapacker": "6.5.4",
|
|
||||||
"style-loader": "^3.3.1",
|
|
||||||
"svg-loader": "^0.0.2",
|
|
||||||
"terser-webpack-plugin": "5",
|
|
||||||
"url-loader": "^4.1.1",
|
|
||||||
"vue": "^2.6.14",
|
|
||||||
"vue-loader": "^15.10.1",
|
|
||||||
"vue-progressbar": "^0.7.5",
|
|
||||||
"vue-resize": "^1.0.1",
|
|
||||||
"vue-router": "^3.6.5",
|
|
||||||
"vue-template-compiler": "^2.6.14",
|
|
||||||
"vuex": "^3.6.2",
|
|
||||||
"vuex-router-sync": "^5.0.0",
|
|
||||||
"webpack": "5",
|
|
||||||
"webpack-assets-manifest": "5",
|
|
||||||
"webpack-cli": "4",
|
|
||||||
"webpack-dev-server": "^4.11.1",
|
|
||||||
"webpack-merge": "5"
|
|
||||||
},
|
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
|
"scripts": {
|
||||||
|
"console": "node"
|
||||||
|
},
|
||||||
"babel": {
|
"babel": {
|
||||||
"presets": [
|
"presets": [
|
||||||
"./node_modules/shakapacker/package/babel/preset.js"
|
"./node_modules/shakapacker/package/babel/preset.js"
|
||||||
@ -47,5 +14,40 @@
|
|||||||
"browserslist": [
|
"browserslist": [
|
||||||
"defaults"
|
"defaults"
|
||||||
],
|
],
|
||||||
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
|
"dependencies": {
|
||||||
|
"@babel/core": "7",
|
||||||
|
"@babel/plugin-transform-runtime": "7",
|
||||||
|
"@babel/preset-env": "7",
|
||||||
|
"@babel/runtime": "7",
|
||||||
|
"@rails/actioncable": "7.2.100",
|
||||||
|
"@swc/core": "1.7.26",
|
||||||
|
"@tweenjs/tween.js": "^25.0.0",
|
||||||
|
"@types/babel__core": "7",
|
||||||
|
"@types/webpack": "5",
|
||||||
|
"babel-loader": "8",
|
||||||
|
"bulma": "^1.0.2",
|
||||||
|
"cheerio": "^1.0.0",
|
||||||
|
"compression-webpack-plugin": "9",
|
||||||
|
"css-loader": "^7.1.2",
|
||||||
|
"css-minimizer-webpack-plugin": "^7.0.0",
|
||||||
|
"lodash": "^4.17.21",
|
||||||
|
"mini-css-extract-plugin": "^2.9.1",
|
||||||
|
"pinia": "^2.2.2",
|
||||||
|
"sass": "~1.78.0",
|
||||||
|
"sass-loader": "^16.0.2",
|
||||||
|
"shakapacker": "8.0.2",
|
||||||
|
"style-loader": "^4.0.0",
|
||||||
|
"swc-loader": "^0.2.6",
|
||||||
|
"terser-webpack-plugin": "5",
|
||||||
|
"vue": "^3.5.10",
|
||||||
|
"vue-loader": "^17.4.2",
|
||||||
|
"vue-router": "^4.4.5",
|
||||||
|
"webpack": "5",
|
||||||
|
"webpack-assets-manifest": "5",
|
||||||
|
"webpack-cli": "4",
|
||||||
|
"webpack-merge": "5"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"webpack-dev-server": "4"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
plugins: [
|
|
||||||
require('postcss-import'),
|
|
||||||
require('postcss-flexbugs-fixes'),
|
|
||||||
require('postcss-preset-env')({
|
|
||||||
autoprefixer: {
|
|
||||||
flexbox: 'no-2009'
|
|
||||||
},
|
|
||||||
stage: 3
|
|
||||||
})
|
|
||||||
]
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user