Enable update and create food
This commit is contained in:
16
app.go
16
app.go
@@ -26,4 +26,20 @@ func (a *App) GetFood() WailsFood {
|
|||||||
return WailsFood{Success: false, Error: err.Error()}
|
return WailsFood{Success: false, Error: err.Error()}
|
||||||
}
|
}
|
||||||
return WailsFood{Data: data, Success: true}
|
return WailsFood{Data: data, Success: true}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) CreateFood(food Food) WailsFood1 {
|
||||||
|
data, err := foodService.Create(food)
|
||||||
|
if err != nil {
|
||||||
|
return WailsFood1{Success: false, Error: err.Error()}
|
||||||
|
}
|
||||||
|
return WailsFood1{Data: data, Success: true}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) UpdateFood(food Food) WailsFood1 {
|
||||||
|
data, err := foodService.Update(food)
|
||||||
|
if err != nil {
|
||||||
|
return WailsFood1{Success: false, Error: err.Error()}
|
||||||
|
}
|
||||||
|
return WailsFood1{Data: data, Success: true}
|
||||||
}
|
}
|
95
frontend/src/lib/components/Energy/Food/EmptyFoodComp.svelte
Normal file
95
frontend/src/lib/components/Energy/Food/EmptyFoodComp.svelte
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { type Food, FoodService } from '$lib/database/food'
|
||||||
|
import { toast } from 'svelte-sonner'
|
||||||
|
import type { Err } from '$lib/types'
|
||||||
|
import { foodStore } from '$lib/store/energy/foodStore'
|
||||||
|
import { RefreshStores } from '$lib/utils'
|
||||||
|
|
||||||
|
let item: Food = {
|
||||||
|
food: '',
|
||||||
|
amount: 0,
|
||||||
|
description: ''
|
||||||
|
}
|
||||||
|
let name: string = ''
|
||||||
|
let amount: string = ''
|
||||||
|
let description: string = ''
|
||||||
|
let per100: string = ''
|
||||||
|
let per100Edited: boolean = false
|
||||||
|
let per100Element: HTMLTableCellElement
|
||||||
|
|
||||||
|
async function update(event: KeyboardEvent & { currentTarget: (EventTarget & HTMLTableCellElement) }) {
|
||||||
|
name = name.trim()
|
||||||
|
amount = amount.trim()
|
||||||
|
description = description.trim()
|
||||||
|
per100 = per100.trim()
|
||||||
|
|
||||||
|
if (!per100Edited && event.currentTarget === per100Element)
|
||||||
|
per100Edited = true
|
||||||
|
|
||||||
|
if (event.key == 'Enter') {
|
||||||
|
event.preventDefault()
|
||||||
|
item.food = name
|
||||||
|
item.description = description
|
||||||
|
item.amount = parseInt(amount)
|
||||||
|
item.per100 = parseInt(per100)
|
||||||
|
|
||||||
|
const [dbFood, err]: [Food, Err] = await FoodService.Create(item)
|
||||||
|
name = ''
|
||||||
|
amount = ''
|
||||||
|
// description = ''
|
||||||
|
per100 = ''
|
||||||
|
per100Edited = false
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
toast.error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
RefreshStores()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!per100Edited)
|
||||||
|
FoodService.GetLatestPer100(name.trim()).then((res) => {
|
||||||
|
if (res[1])
|
||||||
|
// toast.error(res[1])
|
||||||
|
return
|
||||||
|
|
||||||
|
per100 = res[0].toString()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<tr class="border-b border-gray-200 dark:border-gray-700 text-lg font-bold">
|
||||||
|
<th class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap bg-gray-50 dark:text-white dark:bg-gray-800"
|
||||||
|
scope="row">
|
||||||
|
</th>
|
||||||
|
<td bind:innerText={name}
|
||||||
|
class:border-[3px]={!name}
|
||||||
|
class:border-red-600={!name}
|
||||||
|
class="px-6 py-4 overflow-hidden"
|
||||||
|
contenteditable="true"
|
||||||
|
autofocus
|
||||||
|
on:keydown={update}>
|
||||||
|
</td>
|
||||||
|
<td bind:innerText={description}
|
||||||
|
class="px-6 py-4 bg-gray-50 dark:bg-gray-800 overflow-hidden"
|
||||||
|
contenteditable="true"
|
||||||
|
on:keydown={update}>
|
||||||
|
</td>
|
||||||
|
<td bind:innerText={amount}
|
||||||
|
class:border-[3px]={!amount}
|
||||||
|
class:border-red-600={!amount}
|
||||||
|
class="px-6 py-4 overflow-hidden"
|
||||||
|
contenteditable="true"
|
||||||
|
on:keydown={update}>
|
||||||
|
</td>
|
||||||
|
<td bind:this={per100Element}
|
||||||
|
bind:innerText={per100}
|
||||||
|
class="px-6 py-4 bg-gray-50 dark:bg-gray-800 overflow-hidden"
|
||||||
|
class:border-[3px]={!per100}
|
||||||
|
class:border-orange-600={!per100}
|
||||||
|
contenteditable="true"
|
||||||
|
on:keydown={update}>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</template>
|
84
frontend/src/lib/components/Energy/Food/FoodComp.svelte
Normal file
84
frontend/src/lib/components/Energy/Food/FoodComp.svelte
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { type Food, FoodService } from '$lib/database/food'
|
||||||
|
|
||||||
|
export let item: Food
|
||||||
|
export let energyColor: string
|
||||||
|
export let nameColor: string
|
||||||
|
export let dateColor: string
|
||||||
|
|
||||||
|
let amount: string = item.amount.toString()
|
||||||
|
let per100: string = item.per100?.toString() ?? ''
|
||||||
|
let description: string = item.description ?? ''
|
||||||
|
let name: string = item.food
|
||||||
|
|
||||||
|
async function update(event: KeyboardEvent & { currentTarget: (EventTarget & HTMLTableCellElement) }) {
|
||||||
|
if (event.key == 'Enter') {
|
||||||
|
event.preventDefault()
|
||||||
|
await updateItem()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function focusOutUpdate() {
|
||||||
|
await updateItem()
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateItem() {
|
||||||
|
amount = amount.trim()
|
||||||
|
per100 = per100.trim()
|
||||||
|
description = description.trim()
|
||||||
|
name = name.trim()
|
||||||
|
|
||||||
|
item.food = name
|
||||||
|
item.description = description
|
||||||
|
item.amount = parseInt(amount)
|
||||||
|
item.per100 = parseInt(per100)
|
||||||
|
|
||||||
|
const [newItem, err] = await FoodService.Update(item)
|
||||||
|
|
||||||
|
if (newItem && !err) {
|
||||||
|
item = newItem
|
||||||
|
}
|
||||||
|
name = item.food
|
||||||
|
description = item.description ?? ''
|
||||||
|
amount = item.amount.toString()
|
||||||
|
per100 = item.per100?.toString() ?? ''
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<tr class="border-b border-gray-200 dark:border-gray-700 font-bold text-lg">
|
||||||
|
<th class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap bg-gray-50 dark:text-white dark:bg-gray-800"
|
||||||
|
style="color: {dateColor}"
|
||||||
|
scope="row">
|
||||||
|
{item.date}
|
||||||
|
</th>
|
||||||
|
<td class="px-6 py-4"
|
||||||
|
style="color: {nameColor}"
|
||||||
|
contenteditable="true"
|
||||||
|
bind:innerText={name}
|
||||||
|
on:focusout={focusOutUpdate}
|
||||||
|
on:keydown={update}>
|
||||||
|
</td>
|
||||||
|
<td class="px-6 py-4 bg-gray-50 dark:bg-gray-800"
|
||||||
|
contenteditable="true"
|
||||||
|
bind:innerText={description}
|
||||||
|
on:focusout={focusOutUpdate}
|
||||||
|
on:keydown={update}>
|
||||||
|
</td>
|
||||||
|
<td class="px-6 py-4"
|
||||||
|
contenteditable="true"
|
||||||
|
bind:innerText={amount}
|
||||||
|
on:focusout={focusOutUpdate}
|
||||||
|
on:keydown={update}>
|
||||||
|
</td>
|
||||||
|
<td class="px-6 py-4 bg-gray-50 dark:bg-gray-800"
|
||||||
|
contenteditable="true"
|
||||||
|
bind:innerText={per100}
|
||||||
|
on:focusout={focusOutUpdate}
|
||||||
|
on:keydown={update}>
|
||||||
|
</td>
|
||||||
|
<td class="px-6 py-4" style="color: {energyColor}">
|
||||||
|
{item.energy}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</template>
|
95
frontend/src/lib/components/Energy/Food/FoodTable.svelte
Normal file
95
frontend/src/lib/components/Energy/Food/FoodTable.svelte
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import EmptyFoodComp from '$components/Energy/Food/EmptyFoodComp.svelte'
|
||||||
|
import FoodComp from '$components/Energy/Food/FoodComp.svelte'
|
||||||
|
import type { Food } from '$lib/database/food'
|
||||||
|
import { GenerateColor } from '$lib/utils'
|
||||||
|
|
||||||
|
export let items: Food[] = []
|
||||||
|
|
||||||
|
let minCal = 1e5
|
||||||
|
let maxCal = 0
|
||||||
|
for (let item of items) {
|
||||||
|
if (!item.energy) continue
|
||||||
|
if (item.energy > maxCal) maxCal = item.energy
|
||||||
|
if (item.energy < minCal) minCal = item.energy
|
||||||
|
}
|
||||||
|
|
||||||
|
const start = '#99ff99'
|
||||||
|
const end = '#ff9999'
|
||||||
|
|
||||||
|
function lerp(item: Food) {
|
||||||
|
if (!item) return start
|
||||||
|
if (!item.energy) return start
|
||||||
|
const t = (item.energy - minCal) / (maxCal - minCal)
|
||||||
|
const r = parseInt(start.slice(1, 3), 16) * (1 - t) + parseInt(end.slice(1, 3), 16) * t
|
||||||
|
const g = parseInt(start.slice(3, 5), 16) * (1 - t) + parseInt(end.slice(3, 5), 16) * t
|
||||||
|
const b = parseInt(start.slice(5, 7), 16) * (1 - t) + parseInt(end.slice(5, 7), 16) * t
|
||||||
|
return `rgb(${r}, ${g}, ${b})`
|
||||||
|
}
|
||||||
|
|
||||||
|
const itemColors: Map<string, string> = new Map<string, string>()
|
||||||
|
|
||||||
|
function getNameColor(item: Food): string {
|
||||||
|
if (!item) return GenerateColor()
|
||||||
|
if (!item.food) return GenerateColor()
|
||||||
|
if (!itemColors.has(item.food)) itemColors.set(item.food, GenerateColor())
|
||||||
|
// THERE'S NOTHING UNDEFINED HERE
|
||||||
|
// WE GOT RID OF UNDEFINED ON LINE 33
|
||||||
|
// ASSHOLE
|
||||||
|
// @ts-ignore
|
||||||
|
return itemColors.get(item.food)
|
||||||
|
}
|
||||||
|
|
||||||
|
const dateColors: Map<string, string> = new Map<string, string>()
|
||||||
|
function getDateColor(item: Food): string {
|
||||||
|
if (!item) return GenerateColor()
|
||||||
|
if (!item.date) return GenerateColor()
|
||||||
|
const date = item.date.toString().split(" ")[0]
|
||||||
|
if (!date) return GenerateColor()
|
||||||
|
if (!dateColors.has(date)) dateColors.set(date, GenerateColor())
|
||||||
|
// THERE'S NOTHING UNDEFINED HERE
|
||||||
|
// WE GOT RID OF UNDEFINED ON LINE 33
|
||||||
|
// ASSHOLE
|
||||||
|
// @ts-ignore
|
||||||
|
return dateColors.get(date)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="relative flex flex-col flex-grow h-[95vh]" data-vaul-drawer-wrapper id="page">
|
||||||
|
<div class="relative overflow-auto h-full shadow-md sm:rounded-lg">
|
||||||
|
<table class="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400">
|
||||||
|
<thead class="text-xs text-gray-700 uppercase dark:text-gray-400">
|
||||||
|
<tr>
|
||||||
|
<th class="px-6 py-3 bg-gray-50 dark:bg-gray-800 w-2/12" scope="col">
|
||||||
|
Date
|
||||||
|
</th>
|
||||||
|
<th class="px-6 py-3 w-3/12" scope="col">
|
||||||
|
Food
|
||||||
|
</th>
|
||||||
|
<th class="px-6 py-3 bg-gray-50 dark:bg-gray-800 w-4/12" scope="col">
|
||||||
|
Description
|
||||||
|
</th>
|
||||||
|
<th class="px-6 py-3 w-1/12" scope="col">
|
||||||
|
Amount
|
||||||
|
</th>
|
||||||
|
<th class="px-6 py-3 bg-gray-50 dark:bg-gray-800 w-1/12" scope="col">
|
||||||
|
Cal Per 100
|
||||||
|
</th>
|
||||||
|
<th class="px-6 py-3 w-1/12" scope="col">
|
||||||
|
Energy
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<EmptyFoodComp />
|
||||||
|
{#each items as f}
|
||||||
|
<FoodComp item="{f}" energyColor="{lerp(f)}" nameColor="{getNameColor(f)}" dateColor="{getDateColor(f)}" />
|
||||||
|
{/each}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
@@ -15,6 +15,12 @@ type (
|
|||||||
Success bool `json:"success"`
|
Success bool `json:"success"`
|
||||||
Error string `json:"error,omitempty"`
|
Error string `json:"error,omitempty"`
|
||||||
}
|
}
|
||||||
|
WailsFood1 struct {
|
||||||
|
Data Food `json:"data"`
|
||||||
|
Success bool `json:"success"`
|
||||||
|
Error string `json:"error,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
WailsAggregateFood struct {
|
WailsAggregateFood struct {
|
||||||
Data []AggregatedFood `json:"data"`
|
Data []AggregatedFood `json:"data"`
|
||||||
Success bool `json:"success"`
|
Success bool `json:"success"`
|
||||||
|
Reference in New Issue
Block a user