mirror of https://github.com/kcal-app/kcal.git
Fix edit link in Foods list
This commit is contained in:
parent
c29c88776f
commit
2a6549c6d2
|
|
@ -19,8 +19,7 @@ class FoodController extends Controller
|
||||||
*/
|
*/
|
||||||
public function index(): View
|
public function index(): View
|
||||||
{
|
{
|
||||||
return view('foods.index')
|
return view('foods.index');
|
||||||
->with('foods', Food::all()->sortBy('name'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -86,8 +85,13 @@ class FoodController extends Controller
|
||||||
$attributes['serving_size'] = Number::floatFromString($attributes['serving_size']);
|
$attributes['serving_size'] = Number::floatFromString($attributes['serving_size']);
|
||||||
$attributes['name'] = Str::lower($attributes['name']);
|
$attributes['name'] = Str::lower($attributes['name']);
|
||||||
$food->fill(array_filter($attributes))->save();
|
$food->fill(array_filter($attributes))->save();
|
||||||
return redirect(route('foods.show', $food))
|
|
||||||
->with('message', 'Changes saved!');
|
// Sync tags.
|
||||||
|
$tags = explode(',', $request->get('tags'));
|
||||||
|
$food->syncTags($tags);
|
||||||
|
|
||||||
|
session()->flash('message', "Food {$food->name} updated!");
|
||||||
|
return redirect()->route('foods.show', $food);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -186,6 +186,10 @@ class RecipeController extends Controller
|
||||||
'servings' => (int) $input['servings'],
|
'servings' => (int) $input['servings'],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// Sync tags.
|
||||||
|
$tags = explode(',', $request->get('tags'));
|
||||||
|
$recipe->syncTags($tags);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
DB::transaction(function () use ($recipe, $input) {
|
DB::transaction(function () use ($recipe, $input) {
|
||||||
if (!$recipe->save()) {
|
if (!$recipe->save()) {
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,12 @@ class TagAdapter extends AbstractAdapter
|
||||||
*/
|
*/
|
||||||
protected function filter($query, Collection $filters)
|
protected function filter($query, Collection $filters)
|
||||||
{
|
{
|
||||||
$this->filterWithScopes($query, $filters);
|
if ($term = $filters->get('name')) {
|
||||||
|
$query->where('tags.name', 'like', "%{$term}%");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$this->filterWithScopes($query, $filters);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ class FoodSchema extends SchemaProvider
|
||||||
public function getAttributes($resource): array
|
public function getAttributes($resource): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
|
'slug' => $resource->slug,
|
||||||
'name' => $resource->name,
|
'name' => $resource->name,
|
||||||
'detail' => $resource->detail,
|
'detail' => $resource->detail,
|
||||||
'brand' => $resource->brand,
|
'brand' => $resource->brand,
|
||||||
|
|
@ -41,6 +42,8 @@ class FoodSchema extends SchemaProvider
|
||||||
'servingWeight' => $resource->serving_weight,
|
'servingWeight' => $resource->serving_weight,
|
||||||
'createdAt' => $resource->created_at,
|
'createdAt' => $resource->created_at,
|
||||||
'updatedAt' => $resource->updated_at,
|
'updatedAt' => $resource->updated_at,
|
||||||
|
'showUrl' => route('foods.show', $resource),
|
||||||
|
'editUrl' => route('foods.edit', $resource),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ class RecipeSchema extends SchemaProvider
|
||||||
public function getAttributes($resource): array
|
public function getAttributes($resource): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
|
'slug' => $resource->slug,
|
||||||
'name' => $resource->name,
|
'name' => $resource->name,
|
||||||
'description' => $resource->description,
|
'description' => $resource->description,
|
||||||
'source' => $resource->source,
|
'source' => $resource->source,
|
||||||
|
|
@ -44,6 +45,8 @@ class RecipeSchema extends SchemaProvider
|
||||||
'sodiumTotal' => $resource->sodiumTotal(),
|
'sodiumTotal' => $resource->sodiumTotal(),
|
||||||
'createdAt' => $resource->created_at,
|
'createdAt' => $resource->created_at,
|
||||||
'updatedAt' => $resource->updated_at,
|
'updatedAt' => $resource->updated_at,
|
||||||
|
'showUrl' => route('recipes.show', $resource),
|
||||||
|
'editUrl' => route('recipes.edit', $resource),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,98 @@
|
||||||
|
<div x-data data-tags="{{ $defaultTags ?? [] }}">
|
||||||
|
<div x-data="tagSelect()" x-init="init('parentEl')" @click.away="clearSearch()" @keydown.escape="clearSearch()">
|
||||||
|
<div class="relative" @keydown.enter.prevent="addTag(searchTerm)">
|
||||||
|
<x-inputs.input type="hidden"
|
||||||
|
name="tags"
|
||||||
|
value=""
|
||||||
|
x-model="tags"/>
|
||||||
|
<x-inputs.label for="tag_picker" value="Tags"/>
|
||||||
|
<div class="flex flex-row items-center">
|
||||||
|
<x-inputs.input
|
||||||
|
type="text"
|
||||||
|
name="tag_picker"
|
||||||
|
class="mr-2"
|
||||||
|
x-model="searchTerm"
|
||||||
|
x-ref="searchTerm"
|
||||||
|
@input="search($event.target.value)"
|
||||||
|
placeholder="Enter some tags..." />
|
||||||
|
<div x-show="open">
|
||||||
|
<div class="absolute z-40 left-0 mt-2">
|
||||||
|
<div class="py-1 text-sm bg-white rounded shadow-lg border border-gray-300">
|
||||||
|
<template x-for="result in results">
|
||||||
|
<a @click.prevent="addTag(result)"
|
||||||
|
x-text="result"
|
||||||
|
class="block py-1 px-5 cursor-pointer hover:bg-indigo-600 hover:text-white"></a>
|
||||||
|
</template>
|
||||||
|
<a @click.prevent="addTag(searchTerm)"
|
||||||
|
class="block py-1 px-5 cursor-pointer hover:bg-indigo-600 hover:text-white"
|
||||||
|
x-text="searchTerm"></a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<template x-for="(tag, index) in tags">
|
||||||
|
<div class="bg-indigo-100 inline-flex items-center text-sm rounded mr-1">
|
||||||
|
<span class="ml-2 mr-1 leading-relaxed truncate max-w-xs" x-text="tag"></span>
|
||||||
|
<button @click.prevent="removeTag(index)" class="w-6 h-8 inline-block align-middle text-gray-500 hover:text-gray-600 focus:outline-none">
|
||||||
|
<svg class="w-6 h-6 fill-current mx-auto" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill-rule="evenodd" d="M15.78 14.36a1 1 0 0 1-1.42 1.42l-2.82-2.83-2.83 2.83a1 1 0 1 1-1.42-1.42l2.83-2.82L7.3 8.7a1 1 0 0 1 1.42-1.42l2.83 2.83 2.82-2.83a1 1 0 0 1 1.42 1.42l-2.83 2.83 2.83 2.82z"/></svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@once
|
||||||
|
@push('scripts')
|
||||||
|
<script type="text/javascript">
|
||||||
|
function tagSelect() {
|
||||||
|
return {
|
||||||
|
open: false,
|
||||||
|
searchTerm: '',
|
||||||
|
tags: [],
|
||||||
|
results: [],
|
||||||
|
init() {
|
||||||
|
this.tags = JSON.parse(this.$el.parentNode.getAttribute('data-tags'));
|
||||||
|
},
|
||||||
|
addTag(tag) {
|
||||||
|
tag = tag.trim()
|
||||||
|
if (tag !== "" && !this.hasTag(tag)) {
|
||||||
|
this.tags.push( tag )
|
||||||
|
}
|
||||||
|
this.clearSearch()
|
||||||
|
this.$refs.searchTerm.focus()
|
||||||
|
},
|
||||||
|
hasTag(tag) {
|
||||||
|
let foundTag = this.tags.find(e => {
|
||||||
|
return e.toLowerCase() === tag.toLowerCase()
|
||||||
|
})
|
||||||
|
return foundTag !== undefined
|
||||||
|
},
|
||||||
|
removeTag(index) {
|
||||||
|
this.tags.splice(index, 1)
|
||||||
|
},
|
||||||
|
search(q) {
|
||||||
|
if ( q.includes(",") ) {
|
||||||
|
q.split(",").forEach((val) => {
|
||||||
|
this.addTag(val)
|
||||||
|
}, this)
|
||||||
|
}
|
||||||
|
fetch('{{ route('api:v1:tags.index') }}?filter[name]=' + q)
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
this.results = data.data.map(tag => tag.attributes.name);
|
||||||
|
});
|
||||||
|
this.toggleSearch();
|
||||||
|
},
|
||||||
|
clearSearch() {
|
||||||
|
this.searchTerm = ''
|
||||||
|
this.toggleSearch();
|
||||||
|
},
|
||||||
|
toggleSearch() {
|
||||||
|
this.open = this.searchTerm !== '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
@endpush
|
||||||
|
@endonce
|
||||||
|
|
@ -110,7 +110,12 @@
|
||||||
</div>
|
</div>
|
||||||
@endforeach
|
@endforeach
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Tags -->
|
||||||
|
<x-tagger :defaultTags="$food->tags->pluck('name')"/>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex items-center justify-end mt-4">
|
<div class="flex items-center justify-end mt-4">
|
||||||
<x-inputs.button class="ml-3">
|
<x-inputs.button class="ml-3">
|
||||||
{{ ($food->exists ? 'Save' : 'Add') }}
|
{{ ($food->exists ? 'Save' : 'Add') }}
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@
|
||||||
<template x-for="food in foods" :key="food">
|
<template x-for="food in foods" :key="food">
|
||||||
<div class="p-2 font-light rounded-t border-2 border-gray-400">
|
<div class="p-2 font-light rounded-t border-2 border-gray-400">
|
||||||
<a class="h-6 w-6 text-gray-500 hover:text-gray-700 hover:border-gray-300 float-right text-sm"
|
<a class="h-6 w-6 text-gray-500 hover:text-gray-700 hover:border-gray-300 float-right text-sm"
|
||||||
href="/TODO">
|
x-bind:href="food.editUrl">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
|
||||||
<path d="M17.414 2.586a2 2 0 00-2.828 0L7 10.172V13h2.828l7.586-7.586a2 2 0 000-2.828z" />
|
<path d="M17.414 2.586a2 2 0 00-2.828 0L7 10.172V13h2.828l7.586-7.586a2 2 0 000-2.828z" />
|
||||||
<path fill-rule="evenodd" d="M2 6a2 2 0 012-2h4a1 1 0 010 2H4v10h10v-4a1 1 0 112 0v4a2 2 0 01-2 2H4a2 2 0 01-2-2V6z" clip-rule="evenodd" />
|
<path fill-rule="evenodd" d="M2 6a2 2 0 012-2h4a1 1 0 010 2H4v10h10v-4a1 1 0 112 0v4a2 2 0 01-2 2H4a2 2 0 01-2-2V6z" clip-rule="evenodd" />
|
||||||
|
|
@ -76,7 +76,7 @@
|
||||||
number: 1,
|
number: 1,
|
||||||
size: 12,
|
size: 12,
|
||||||
morePages: true,
|
morePages: true,
|
||||||
searchTerm: null,
|
searchTerm: '{{ $defaultSearch ?? null }}',
|
||||||
reset() {
|
reset() {
|
||||||
this.foods = [];
|
this.foods = [];
|
||||||
this.number = 1;
|
this.number = 1;
|
||||||
|
|
@ -88,7 +88,6 @@
|
||||||
if (this.searchTerm) {
|
if (this.searchTerm) {
|
||||||
url += `&filter[search]=${this.searchTerm}`;
|
url += `&filter[search]=${this.searchTerm}`;
|
||||||
}
|
}
|
||||||
console.log(url);
|
|
||||||
fetch(url)
|
fetch(url)
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1 @@
|
||||||
@php
|
@include('foods.index', ['defaultSearch' => $food->name])
|
||||||
$foods = [$food];
|
|
||||||
@endphp
|
|
||||||
@include('foods.index')
|
|
||||||
|
|
|
||||||
|
|
@ -39,16 +39,6 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Description -->
|
|
||||||
<div>
|
|
||||||
<x-inputs.label for="description" :value="__('Description')" />
|
|
||||||
|
|
||||||
<x-inputs.textarea id="description"
|
|
||||||
class="block mt-1 w-full"
|
|
||||||
name="description"
|
|
||||||
:value="old('description', $recipe->description)" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Source -->
|
<!-- Source -->
|
||||||
<div>
|
<div>
|
||||||
<x-inputs.label for="source" :value="__('Source')" />
|
<x-inputs.label for="source" :value="__('Source')" />
|
||||||
|
|
@ -59,6 +49,19 @@
|
||||||
name="source"
|
name="source"
|
||||||
:value="old('source', $recipe->source)" />
|
:value="old('source', $recipe->source)" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Description -->
|
||||||
|
<div>
|
||||||
|
<x-inputs.label for="description" :value="__('Description')" />
|
||||||
|
|
||||||
|
<x-inputs.textarea id="description"
|
||||||
|
class="block mt-1 w-full"
|
||||||
|
name="description"
|
||||||
|
:value="old('description', $recipe->description)" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Tags -->
|
||||||
|
<x-tagger :defaultTags="$recipe->tags->pluck('name')"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Ingredients -->
|
<!-- Ingredients -->
|
||||||
|
|
|
||||||
|
|
@ -15,10 +15,18 @@
|
||||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
||||||
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
|
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
|
||||||
<div class="p-6 bg-white border-b border-gray-200">
|
<div class="p-6 bg-white border-b border-gray-200">
|
||||||
<div class="mb-2 text-gray-500 text-sm">
|
@if($recipe->tags)
|
||||||
<span class="font-extrabold">Source:</span>
|
<div class="mb-2 text-gray-700 text-sm">
|
||||||
{{ $recipe->source }}
|
<span class="font-extrabold">Tags:</span>
|
||||||
</div>
|
{{ implode(', ', $recipe->tags->pluck('name')->all()) }}
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
@if($recipe->source)
|
||||||
|
<div class="mb-2 text-gray-500 text-sm">
|
||||||
|
<span class="font-extrabold">Source:</span>
|
||||||
|
{{ $recipe->source }}
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
<h3 class="mb-2 font-bold">Description</h3>
|
<h3 class="mb-2 font-bold">Description</h3>
|
||||||
<div class="mb-2 text-gray-800">{{ $recipe->description }}</div>
|
<div class="mb-2 text-gray-800">{{ $recipe->description }}</div>
|
||||||
<h3 class="mb-2 font-bold">Ingredients</h3>
|
<h3 class="mb-2 font-bold">Ingredients</h3>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue