Does not work: uploading images
Adding/Editing/Deleting works SeasonsSelect works Nice recipe layout
This commit is contained in:
parent
3d0d3f41e2
commit
9392ff6ada
@ -19,7 +19,11 @@ background-color: var(--red);
|
||||
display: grid;
|
||||
justify-content: center;
|
||||
align-content: center;
|
||||
|
||||
}
|
||||
@media screen and (max-width: 500px) {
|
||||
.container{
|
||||
margin: 1rem;
|
||||
}
|
||||
}
|
||||
:global(.icon_svg){
|
||||
width: 2rem;
|
||||
|
@ -215,12 +215,19 @@ input::placeholder{
|
||||
color:var(--nord0);
|
||||
}
|
||||
.card .description{
|
||||
box-sizing:border-box;
|
||||
border: 2px solid var(--nord5);
|
||||
border-radius: 30px;
|
||||
padding-inline: 1em;
|
||||
padding-block: 0.5em;
|
||||
margin-inline: 1em;
|
||||
margin-top: 0;
|
||||
color: var(--nord4);
|
||||
width: calc(300px - 2em); /*??*/
|
||||
}
|
||||
.card .description:hover{
|
||||
color: var(--nord0);
|
||||
border: 2px solid var(--nord0);
|
||||
}
|
||||
.card .tags{
|
||||
display: flex;
|
||||
@ -354,7 +361,7 @@ input::placeholder{
|
||||
<input class=category placeholder=Kategorie... bind:value={card_data.category}/>
|
||||
<div>
|
||||
<input class=name placeholder=Name... bind:value={card_data.name}/>
|
||||
<input class=description placeholder=Kurzbeschreibung... bind:value={card_data.description}/>
|
||||
<p contenteditable class=description placeholder=Kurzbeschreibung... bind:innerText={card_data.description}></p>
|
||||
</div>
|
||||
<div class=tags>
|
||||
{#each card_data.tags as tag}
|
||||
|
@ -10,6 +10,7 @@ import "$lib/css/action_button.css"
|
||||
import { do_on_key } from '$lib/components/do_on_key.js'
|
||||
|
||||
export let ingredients
|
||||
export let portions
|
||||
|
||||
let new_ingredient = {
|
||||
amount: "",
|
||||
@ -107,7 +108,6 @@ export function show_keys(event){
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
input::placeholder{
|
||||
all:unset;
|
||||
}
|
||||
@ -117,6 +117,7 @@ input{
|
||||
|
||||
input.heading{
|
||||
all: unset;
|
||||
box-sizing: border-box;
|
||||
background-color: var(--nord0);
|
||||
padding: 1rem;
|
||||
padding-inline: 2rem;
|
||||
@ -146,13 +147,14 @@ input.heading:hover{
|
||||
.heading_wrapper button{
|
||||
position: absolute;
|
||||
bottom: -1.5rem;
|
||||
right: -5rem;
|
||||
right: -2rem;
|
||||
}
|
||||
.adder{
|
||||
box-sizing: border-box;
|
||||
margin-inline: auto;
|
||||
position: relative;
|
||||
margin-block: 3rem;
|
||||
width: 400px;
|
||||
width: 90%;
|
||||
border-radius: 20px;
|
||||
transition: 200ms;
|
||||
}
|
||||
@ -269,32 +271,11 @@ dialog h2{
|
||||
drop-shadow(0 0 1em black)
|
||||
;
|
||||
}
|
||||
ul{
|
||||
width: fit-content;
|
||||
margin-inline: auto;
|
||||
}
|
||||
li{
|
||||
font-size: 1.2rem;
|
||||
max-width: 1000px;
|
||||
align-items: center;
|
||||
}
|
||||
.li_wrapper{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.mod_icons{
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin-left: 2rem;
|
||||
}
|
||||
li:nth-child(2n){
|
||||
background-color: var(--nord4);
|
||||
|
||||
}
|
||||
li:nth-child(2n+1){
|
||||
background-color: var(--nord6);
|
||||
}
|
||||
|
||||
.button_subtle{
|
||||
padding: 0em;
|
||||
animation: unset;
|
||||
@ -306,16 +287,64 @@ li:nth-child(2n+1){
|
||||
scale: 1.2 1.2;
|
||||
}
|
||||
h3{
|
||||
margin-inline: auto;
|
||||
width: fit-content;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
max-width: 1000px;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.ingredients_grid{
|
||||
box-sizing: border-box;
|
||||
display: grid;
|
||||
font-size: 1.1em;
|
||||
grid-template-columns: 2fr 3fr 1fr;
|
||||
grid-template-rows: auto;
|
||||
grid-auto-flow: row;
|
||||
align-items: center;
|
||||
row-gap: 0.5em;
|
||||
column-gap: 0.5em;
|
||||
}
|
||||
|
||||
.ingredients_grid > *{
|
||||
cursor: pointer;
|
||||
}
|
||||
.ingredients_grid>*:nth-child(3n+1){
|
||||
min-width: 5ch;
|
||||
}
|
||||
|
||||
.list_wrapper{
|
||||
padding-inline: 2em;
|
||||
padding-block: 1em;
|
||||
}
|
||||
.list_wrapper p[contenteditable]{
|
||||
border: 2px solid grey;
|
||||
border-radius: 1000px;
|
||||
padding: 0.25em 1em;
|
||||
background-color: white;
|
||||
transition: 200ms;
|
||||
}
|
||||
.list_wrapper p[contenteditable]:hover,
|
||||
.list_wrapper p[contenteditable]:focus-within{
|
||||
scale: 1.05 1.05;
|
||||
}
|
||||
@media screen and (max-width: 500px){
|
||||
dialog h2{
|
||||
margin-top: 2rem;
|
||||
}
|
||||
dialog .heading_wrapper{
|
||||
width: 80%;
|
||||
}
|
||||
.ingredients_grid .mod_icons{
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class=list_wrapper>
|
||||
<h4>Portionen:</h4>
|
||||
<p contenteditable type="text" bind:innerText={portions}></p>
|
||||
|
||||
<h2>Zutaten</h2>
|
||||
{#each ingredients as list, list_index}
|
||||
<h3>
|
||||
<div>
|
||||
@ -329,23 +358,20 @@ h3{
|
||||
<button class="action_button button_subtle" on:click="{() => show_modal_edit_subheading_ingredient(list_index)}">
|
||||
<Pen fill=var(--nord1)></Pen> </button>
|
||||
<button class="action_button button_subtle" on:click="{() => remove_list(list_index)}">
|
||||
<Cross fill=var(--nord1)></Cross>
|
||||
</button>
|
||||
<Cross fill=var(--nord1)></Cross></button>
|
||||
</div>
|
||||
</h3>
|
||||
<ul>
|
||||
<div class=ingredients_grid>
|
||||
{#each list.list as ingredient, ingredient_index}
|
||||
<li><div class=li_wrapper><div>{ingredient.amount} {ingredient.unit} {ingredient.name}</div>
|
||||
<div on:click={() => show_modal_edit_ingredient(list_index, ingredient_index)} >{ingredient.amount} {ingredient.unit}</div>
|
||||
<div on:click={() => show_modal_edit_ingredient(list_index, ingredient_index)} >{ingredient.name}</div>
|
||||
<div class=mod_icons><button class="action_button button_subtle" on:click={() => show_modal_edit_ingredient(list_index, ingredient_index)}>
|
||||
<Pen fill=var(--nord1) height=1em width=1em></Pen>
|
||||
</button>
|
||||
<button class="action_button button_subtle" on:click="{() => remove_ingredient(list_index, ingredient_index)}">
|
||||
<Cross fill=var(--nord1) height=1em width=1em></Cross>
|
||||
</button></div></div>
|
||||
</li>
|
||||
<Pen fill=var(--nord1) height=1em width=1em></Pen></button>
|
||||
<button class="action_button button_subtle" on:click="{() => remove_ingredient(list_index, ingredient_index)}"><Cross fill=var(--nord1) height=1em width=1em></Cross></button></div>
|
||||
{/each}
|
||||
</ul>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<div class="adder shadow">
|
||||
<input class=category type="text" bind:value={new_ingredient.sublist} placeholder="Kategorie (optional)" on:keypress={(event) => do_on_key(event, 'Enter', false, add_new_ingredient)}>
|
||||
@ -358,7 +384,6 @@ h3{
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<dialog id=edit_ingredient_modal>
|
||||
<h2>Zutat verändern</h2>
|
||||
<div class=adder>
|
||||
|
@ -12,6 +12,7 @@ import { do_on_key } from '$lib/components/do_on_key.js'
|
||||
|
||||
const step_placeholder = "Kartoffeln schälen..."
|
||||
export let instructions
|
||||
export let add_info
|
||||
|
||||
let new_step = {
|
||||
name: "",
|
||||
@ -32,6 +33,12 @@ function get_sublist_index(sublist_name, list){
|
||||
return -1
|
||||
}
|
||||
export function remove_list(list_index){
|
||||
if(instructions[list_index].steps.length > 1){
|
||||
const response = confirm("Bist du dir sicher, dass du diese Liste löschen möchtest? Alle Zubereitungsschritte der Liste werden hiermit auch gelöscht.");
|
||||
if(!response){
|
||||
return
|
||||
}
|
||||
}
|
||||
instructions.splice(list_index, 1);
|
||||
instructions = instructions //tells svelte to update dom
|
||||
}
|
||||
@ -117,9 +124,23 @@ input::placeholder{
|
||||
all:unset;
|
||||
}
|
||||
|
||||
|
||||
li > div{
|
||||
display:flex;
|
||||
flex-direction: row;
|
||||
justify-items: space-between;
|
||||
align-items:center;
|
||||
}
|
||||
li > div > div:first-child{
|
||||
flex-grow: 1;
|
||||
cursor: pointer;
|
||||
}
|
||||
li > div > div:last-child{
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
input.heading{
|
||||
all: unset;
|
||||
box-sizing: border-box;
|
||||
background-color: var(--nord0);
|
||||
padding: 1rem;
|
||||
padding-inline: 2rem;
|
||||
@ -139,7 +160,7 @@ input.heading:focus-visible
|
||||
|
||||
.heading_wrapper{
|
||||
position: relative;
|
||||
width: 300px;
|
||||
width: min(300px, 95dvw);
|
||||
margin-inline: auto;
|
||||
transition: 200ms;
|
||||
}
|
||||
@ -152,15 +173,20 @@ input.heading:focus-visible
|
||||
.heading_wrapper button{
|
||||
position: absolute;
|
||||
bottom: -1.5rem;
|
||||
right: -5rem;
|
||||
right: -1.5rem;
|
||||
}
|
||||
.adder{
|
||||
margin-inline: auto;
|
||||
position: relative;
|
||||
margin-block: 3rem;
|
||||
width: 50ch;
|
||||
width: 90%;
|
||||
border-radius: 20px;
|
||||
transition: 200ms;
|
||||
background-color: var(--blue);
|
||||
padding: 1.5rem 2rem;
|
||||
}
|
||||
dialog .adder{
|
||||
width: 400px;
|
||||
}
|
||||
.shadow{
|
||||
box-shadow: 0 0 1em 0.2em rgba(0,0,0,0.3);
|
||||
@ -202,17 +228,18 @@ input.heading:focus-visible
|
||||
font-family: sans-serif;
|
||||
width: 100%;
|
||||
font-size: 1.2rem;
|
||||
padding: 2rem;
|
||||
padding-top: 2.5rem;
|
||||
border-radius: 20px;
|
||||
background-color: var(--blue);
|
||||
color: #bbb;
|
||||
transition: 200ms;
|
||||
border: 2px solid var(--nord4);
|
||||
border-radius: 30px;
|
||||
padding: 0.5em 1em;
|
||||
color: var(--nord4);
|
||||
transition: 100ms;
|
||||
}
|
||||
.add_step p:hover,
|
||||
.add_step p:focus-visible
|
||||
{
|
||||
color: white;
|
||||
scale: 1.02 1.02;
|
||||
}
|
||||
|
||||
dialog{
|
||||
@ -239,6 +266,21 @@ dialog h2{
|
||||
drop-shadow(0 0 1em black)
|
||||
;
|
||||
}
|
||||
@media screen and (max-width: 500px){
|
||||
dialog h2{
|
||||
margin-top: 2rem;
|
||||
}
|
||||
dialog .adder{
|
||||
width: 85%;
|
||||
padding-inline: 0.5em;
|
||||
}
|
||||
dialog .adder .category{
|
||||
width: 70%;
|
||||
}
|
||||
dialog .adder input::placeholder{
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
}
|
||||
dialog[open]{
|
||||
animation: show 200ms ease forwards;
|
||||
}
|
||||
@ -250,10 +292,122 @@ dialog[open]{
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
}
|
||||
ol li::marker{
|
||||
font-weight: bold;
|
||||
color: var(--blue);
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
.instructions{
|
||||
flex-basis: 0;
|
||||
flex-grow: 2;
|
||||
background-color: var(--nord5);
|
||||
padding-block: 1rem;
|
||||
padding-inline: 2rem;
|
||||
}
|
||||
.instructions ol{
|
||||
padding-left: 1em;
|
||||
}
|
||||
.instructions li{
|
||||
margin-block: 0.5em;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.additional_info{
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 1em;
|
||||
}
|
||||
.additional_info > *{
|
||||
flex-grow: 0;
|
||||
overflow: hidden;
|
||||
padding: 1em;
|
||||
background-color: #FAFAFE;
|
||||
box-shadow: 0.3em 0.3em 1em 0.2em rgba(0,0,0,0.3);
|
||||
/*max-width: 30%*/
|
||||
}
|
||||
.additional_info > div > *:not(h4){
|
||||
line-height: 2em;
|
||||
}
|
||||
h4{
|
||||
line-height: 1em;
|
||||
margin-block: 0;
|
||||
}
|
||||
.button_subtle{
|
||||
padding: 0em;
|
||||
animation: unset;
|
||||
margin: 0.2em 0.1em;
|
||||
background-color: transparent;
|
||||
box-shadow: unset;
|
||||
display:inline;
|
||||
}
|
||||
.button_subtle:hover{
|
||||
scale: 1.2 1.2;
|
||||
}
|
||||
h3{
|
||||
display:flex;
|
||||
gap: 1rem;
|
||||
}
|
||||
.additional_info input{
|
||||
all:unset;
|
||||
display: inline;
|
||||
width: 10ch;
|
||||
border-radius: 1000px;
|
||||
border: 2px solid grey;
|
||||
padding: 0em 0.5em;
|
||||
}
|
||||
.additional_info input::placeholder{
|
||||
color: grey;
|
||||
}
|
||||
.additional_info p[contenteditable]{
|
||||
display: inline;
|
||||
padding: 0.25em 1em;
|
||||
border: 2px solid grey;
|
||||
border-radius: 1000px;
|
||||
}
|
||||
.additional_info div:has(p[contenteditable]){
|
||||
transition: 200ms;
|
||||
display: inline;
|
||||
}
|
||||
.additional_info div:has(p[contenteditable]):hover,
|
||||
.additional_info div:has(p[contenteditable]):focus-within
|
||||
{
|
||||
transform: scale(1.1, 1.1);
|
||||
}
|
||||
@media screen and (max-width: 500px){
|
||||
dialog h2{
|
||||
margin-top: 2rem;
|
||||
}
|
||||
dialog .heading_wrapper{
|
||||
width: 80%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class=instructions>
|
||||
<div class=additional_info>
|
||||
|
||||
<div><h4>Vorbereitung:</h4>
|
||||
<p contenteditable type="text" bind:innerText={add_info.preparation}></p>
|
||||
</div>
|
||||
|
||||
|
||||
<div><h4>Stockgare:</h4>
|
||||
<p contenteditable type="text" bind:innerText={add_info.fermentation.bulk}></p>
|
||||
</div>
|
||||
|
||||
<div><h4>Stückgare:</h4>
|
||||
<p contenteditable type="text" bind:innerText={add_info.fermentation.final}></p>
|
||||
</div>
|
||||
|
||||
<div><h4>Backen:</h4>
|
||||
<div><p type="text" bind:innerText={add_info.baking.length} contenteditable placeholder="40 min..."></p></div> bei <div><p type="text" bind:innerText={add_info.baking.temperature} contenteditable placeholder=200...></p></div> °C <div><p type="text" bind:innerText={add_info.baking.mode} contenteditable placeholder="Ober-/Unterhitze..."></p></div></div>
|
||||
|
||||
<div><h4>Auf dem Teller:</h4>
|
||||
<div><p contenteditable type="text" bind:innerText={add_info.total_time}></p></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>Zubereitung</h2>
|
||||
{#each instructions as list, list_index}
|
||||
<h3>
|
||||
{#if list.name}
|
||||
@ -261,31 +415,34 @@ dialog[open]{
|
||||
{:else}
|
||||
Leer
|
||||
{/if}
|
||||
<button class=edit on:click="{() => show_modal_edit_subheading_step(list_index)}">
|
||||
<Pen></Pen> </button>
|
||||
<button class=remove on:click="{() => remove_list(list_index)}">
|
||||
<Cross></Cross>
|
||||
<div>
|
||||
<button class="action_button button_subtle" on:click="{() => show_modal_edit_subheading_step(list_index)}">
|
||||
<Pen fill=var(--nord1)></Pen> </button>
|
||||
<button class="action_button button_subtle" on:click="{() => remove_list(list_index)}">
|
||||
<Cross fill=var(--nord1)></Cross>
|
||||
</button>
|
||||
</div>
|
||||
</h3>
|
||||
<ol>
|
||||
{#each list.steps as step, step_index}
|
||||
<li>{step}
|
||||
<button class=edit on:click={() => show_modal_edit_step(list_index, step_index)}>
|
||||
<Pen></Pen>
|
||||
<li><div><div on:click={() => show_modal_edit_step(list_index, step_index)}>{step}</div>
|
||||
<div><button class="action_button button_subtle" on:click={() => show_modal_edit_step(list_index, step_index)}>
|
||||
<Pen fill=var(--nord1)></Pen>
|
||||
</button>
|
||||
<button class=remove on:click="{() => remove_step(list_index, step_index)}">
|
||||
<Cross></Cross>
|
||||
<button class="action_button button_subtle" on:click="{() => remove_step(list_index, step_index)}">
|
||||
<Cross fill=var(--nord1)></Cross>
|
||||
</button>
|
||||
|
||||
</div></div>
|
||||
</li>
|
||||
{/each}
|
||||
</ol>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<div class='adder shadow'>
|
||||
<input class=category type="text" bind:value={new_step.name} placeholder="Kategorie (optional)"on:keypress={(event) => do_on_key(event, 'Enter', false , add_new_step)} >
|
||||
<div class=add_step>
|
||||
<p id=step contenteditable on:focus='{clear_step}' on:blur={add_placeholder} bind:innerHTML={new_step.step} on:keypress={(event) => do_on_key(event, 'Enter', true , add_new_step)}></p>
|
||||
<p id=step contenteditable on:focus='{clear_step}' on:blur={add_placeholder} bind:innerText={new_step.step} on:keypress={(event) => do_on_key(event, 'Enter', true , add_new_step)}></p>
|
||||
<button on:click={() => add_new_step()} class=action_button>
|
||||
<Plus fill=white style="height: 2rem; width: 2rem"></Plus>
|
||||
</button>
|
||||
|
45
src/lib/components/IngredientsPage.svelte
Normal file
45
src/lib/components/IngredientsPage.svelte
Normal file
@ -0,0 +1,45 @@
|
||||
<script>
|
||||
export let data
|
||||
</script>
|
||||
<style>
|
||||
*{
|
||||
font-family: sans-serif;
|
||||
}
|
||||
.ingredients{
|
||||
flex-basis: 0;
|
||||
flex-grow: 1;
|
||||
padding-block: 1rem;
|
||||
padding-inline: 2rem;
|
||||
}
|
||||
.ingredients_grid{
|
||||
display: grid;
|
||||
font-size: 1.1rem;
|
||||
grid-template-columns: 1fr 3fr;
|
||||
grid-template-rows: auto;
|
||||
grid-auto-flow: row;
|
||||
row-gap: 0.5em;
|
||||
column-gap: 0.5em;
|
||||
}
|
||||
h4{
|
||||
margin-block: 0;
|
||||
}
|
||||
</style>
|
||||
{#if data.ingredients}
|
||||
<div class=ingredients>
|
||||
{#if data.portions}
|
||||
<h4>Portionen:</h4>
|
||||
{data.portions}
|
||||
{/if}
|
||||
<h2>Zutaten</h2>
|
||||
{#each data.ingredients as list}
|
||||
{#if list.name}
|
||||
<h3>{list.name}</h3>
|
||||
{/if}
|
||||
<div class=ingredients_grid>
|
||||
{#each list.list as item}
|
||||
<div class=amount>{item.amount} {item.unit}</div><div class=name>{@html item.name}</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
90
src/lib/components/InstructionsPage.svelte
Normal file
90
src/lib/components/InstructionsPage.svelte
Normal file
@ -0,0 +1,90 @@
|
||||
<script>
|
||||
export let data
|
||||
</script>
|
||||
<style>
|
||||
*{
|
||||
font-family: sans-serif;
|
||||
}
|
||||
ol li::marker{
|
||||
font-weight: bold;
|
||||
color: var(--blue);
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
.instructions{
|
||||
flex-basis: 0;
|
||||
flex-grow: 2;
|
||||
background-color: var(--nord5);
|
||||
padding-block: 1rem;
|
||||
padding-inline: 2rem;
|
||||
}
|
||||
.instructions ol{
|
||||
padding-left: 1em;
|
||||
}
|
||||
.instructions li{
|
||||
margin-block: 0.5em;
|
||||
font-size: 1.1rem;
|
||||
|
||||
}
|
||||
|
||||
.additional_info{
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 1em;
|
||||
}
|
||||
.additional_info > *{
|
||||
flex-grow: 0;
|
||||
padding: 1em;
|
||||
background-color: #FAFAFE;
|
||||
box-shadow: 0.3em 0.3em 1em 0.2em rgba(0,0,0,0.3);
|
||||
max-width: 30%
|
||||
}
|
||||
@media screen and (max-width: 500px){
|
||||
.additional_info > *{
|
||||
max-width: 60%;
|
||||
}
|
||||
}
|
||||
|
||||
h4{
|
||||
margin-block: 0;
|
||||
}
|
||||
</style>
|
||||
<div class=instructions>
|
||||
<div class=additional_info>
|
||||
{#if data.preparation}
|
||||
<div><h4>Vorbereitung:</h4>{data.preparation}</div>
|
||||
{/if}
|
||||
|
||||
|
||||
{#if data.fermentation}
|
||||
{#if data.fermentation.bulk}
|
||||
<div><h4>Stockgare:</h4>{data.fermentation.bulk}</div>
|
||||
{/if}
|
||||
|
||||
{#if data.fermentation.final}
|
||||
<div><h4>Stückgare:</h4> {data.fermentation.final}</div>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
{#if data.baking.temperature}
|
||||
<div><h4>Backen:</h4> {data.baking.length} bei {data.baking.temperature} °C {data.baking.mode}</div>
|
||||
{/if}
|
||||
|
||||
{#if data.total_time}
|
||||
<div><h4>Auf dem Teller:</h4>{data.total_time}</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{#if data.instructions}
|
||||
<h2>Zubereitung</h2>
|
||||
{#each data.instructions as list}
|
||||
{#if list.name}
|
||||
<h3>{list.name}</h3>
|
||||
{/if}
|
||||
<ol>
|
||||
{#each list.steps as step}
|
||||
<li>{@html step}</li>
|
||||
{/each}
|
||||
</ol>
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
@ -5,7 +5,6 @@
|
||||
|
||||
onMount(() => {
|
||||
const recipes = document.querySelectorAll(".search_me");
|
||||
console.log("######", recipes)
|
||||
const search = document.getElementById("search");
|
||||
const clearSearch = document.getElementById("clear-search");
|
||||
|
||||
|
@ -1,9 +1,16 @@
|
||||
<script lang=ts>
|
||||
import "$lib/components/nordtheme.css"
|
||||
import { season } from '$lib/js/season_store.js'
|
||||
import {onMount} from "svelte";
|
||||
let months = ["Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember"]
|
||||
|
||||
export let season : Number[]
|
||||
|
||||
|
||||
let season_local
|
||||
|
||||
season.subscribe((s) => {
|
||||
season_local = s
|
||||
});
|
||||
|
||||
export function set_season(){
|
||||
let temp = []
|
||||
@ -13,18 +20,18 @@ export function set_season(){
|
||||
temp.push(i+1)
|
||||
}
|
||||
}
|
||||
season = temp
|
||||
season.update((s) => temp)
|
||||
}
|
||||
|
||||
function write_season(season){
|
||||
const el = document.getElementById("labels");
|
||||
for(var i = 0; i < season.length; i++){
|
||||
el.children[i].children[0].children[0].checked = true
|
||||
el.children[season[i]-1].children[0].children[0].checked = true
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
write_season(season)
|
||||
write_season(season_local)
|
||||
});
|
||||
|
||||
</script>
|
||||
@ -75,6 +82,3 @@ input[type=checkbox]::after
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
|
||||
<button on:click={() => console.log("season", season)}> PRINT SEASON FROM SEASON_SELECT</button>
|
||||
|
3
src/lib/js/season_store.js
Normal file
3
src/lib/js/season_store.js
Normal file
@ -0,0 +1,3 @@
|
||||
import { writable } from 'svelte/store';
|
||||
|
||||
export const season = writable([]);
|
@ -16,16 +16,17 @@ const RecipeSchema = new mongoose.Schema(
|
||||
description: {type: String, required: true},
|
||||
tags : [String],
|
||||
season : [Number],
|
||||
baking: { temperature: String,
|
||||
length: String,
|
||||
mode: String,
|
||||
baking: { temperature: {type:String, default: ""},
|
||||
length: {type:String, default: ""},
|
||||
mode: {type:String, default: ""},
|
||||
},
|
||||
preparation : String,
|
||||
fermentation: {bulk: String,
|
||||
final: String,
|
||||
preparation : {type:String, default: ""},
|
||||
fermentation: { bulk: {type:String, default: ""},
|
||||
final: {type:String, default: ""},
|
||||
|
||||
},
|
||||
portions : String,
|
||||
total_time : String,
|
||||
portions :{type:String, default: ""},
|
||||
total_time : {type:String, default: ""},
|
||||
ingredients : [ { name: {type: String, default: ""},
|
||||
list: [{name: {type: String, default: ""},
|
||||
unit: String,
|
||||
@ -34,6 +35,7 @@ const RecipeSchema = new mongoose.Schema(
|
||||
}],
|
||||
instructions : [{name: {type: String, default: ""},
|
||||
steps: [String]}],
|
||||
preamble : String,
|
||||
addendum : String,
|
||||
},
|
||||
);
|
||||
|
@ -4,10 +4,12 @@ import "$lib/components/nordtheme.css"
|
||||
<style>
|
||||
:global(*){
|
||||
box-sizing: border-box;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
:global(body){
|
||||
margin:0;
|
||||
padding:0;
|
||||
background-color: #fbf9f3;
|
||||
}
|
||||
li{
|
||||
list-style-type:none;
|
||||
@ -17,9 +19,10 @@ li{
|
||||
li>a{
|
||||
text-decoration: none;
|
||||
font-family: sans-serif;
|
||||
font-size: 1.5rem;
|
||||
font-size: 1.2rem;
|
||||
color: inherit
|
||||
}
|
||||
|
||||
li:hover,
|
||||
li:focus-within
|
||||
{
|
||||
@ -27,7 +30,6 @@ li:focus-within
|
||||
color: var(--red);
|
||||
transform: scale(1.1,1.1);
|
||||
}
|
||||
|
||||
ul {
|
||||
padding-block: 2rem;
|
||||
display: flex;
|
||||
@ -38,6 +40,16 @@ ul {
|
||||
margin: 0;
|
||||
margin-inline: auto;
|
||||
}
|
||||
@media screen and (max-width: 500px) {
|
||||
ul{
|
||||
flex-direction: column;
|
||||
}
|
||||
li:hover,
|
||||
li:focus-within{
|
||||
transform: unset;
|
||||
}
|
||||
}
|
||||
|
||||
nav{
|
||||
background-color: var(--nord0);
|
||||
}
|
||||
@ -49,7 +61,7 @@ nav{
|
||||
footer{
|
||||
padding-block: 1rem;
|
||||
text-align: center;
|
||||
margin-top: auto;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
</style>
|
||||
<div class=wrapper style="">
|
1
src/routes/.jukit/.jukit_info.json
Normal file
1
src/routes/.jukit/.jukit_info.json
Normal file
@ -0,0 +1 @@
|
||||
{"terminal": "nvimterm"}
|
@ -16,7 +16,7 @@ export const POST: RequestHandler = async ({request}) => {
|
||||
await dbConnect();
|
||||
await Recipe.create(recipe_json);
|
||||
await dbDisconnect();
|
||||
return {status: 400} //TODO: cleanup error throwing
|
||||
return {status: 200} //TODO: cleanup error throwing
|
||||
}
|
||||
else{
|
||||
console.log("PASSWORD INCORRECT")
|
||||
|
23
src/routes/api/delete/+server.ts
Normal file
23
src/routes/api/delete/+server.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
import { Recipe } from '../../../models/Recipe';
|
||||
import { dbConnect, dbDisconnect } from '../../../utils/db';
|
||||
import type {RecipeModelType} from '../../../types/types';
|
||||
import { BEARER_TOKEN } from '$env/static/private'
|
||||
// header: use for bearer token for now
|
||||
// recipe json in body
|
||||
export const POST: RequestHandler = async ({request}) => {
|
||||
let message = await request.json()
|
||||
const short_name = message.old_short_name
|
||||
const bearer_token = message.headers.bearer
|
||||
if(bearer_token === BEARER_TOKEN){
|
||||
console.log("PASSWORD CORRECT")
|
||||
await dbConnect();
|
||||
await Recipe.findOneAndDelete({short_name: short_name});
|
||||
await dbDisconnect();
|
||||
return {status: 400} //TODO: cleanup error throwing
|
||||
}
|
||||
else{
|
||||
console.log("PASSWORD INCORRECT")
|
||||
return {status: 403}
|
||||
}
|
||||
};
|
@ -3,6 +3,7 @@ import { Recipe } from '../../../models/Recipe';
|
||||
import { dbConnect, dbDisconnect } from '../../../utils/db';
|
||||
import type {RecipeModelType} from '../../../types/types';
|
||||
import { BEARER_TOKEN } from '$env/static/private'
|
||||
import { error } from '@sveltejs/kit';
|
||||
// header: use for bearer token for now
|
||||
// recipe json in body
|
||||
export const POST: RequestHandler = async ({request}) => {
|
||||
@ -10,16 +11,15 @@ export const POST: RequestHandler = async ({request}) => {
|
||||
let message = await request.json()
|
||||
const recipe_json = message.recipe
|
||||
const bearer_token = message.headers.bearer
|
||||
console.log("RECIPE:", recipe_json)
|
||||
console.log("BEARER:", bearer_token)
|
||||
if(bearer_token === BEARER_TOKEN){
|
||||
await dbConnect();
|
||||
await Recipe.findOneAndUpdate({short_name: message.old_short_name }, recipe_json);
|
||||
await dbDisconnect();
|
||||
return {status: 400} //TODO: cleanup error throwing
|
||||
const res = new Response(JSON.stringify({ message: "Updated Recipe successfully"}), { status: 200 })
|
||||
return res
|
||||
}
|
||||
else{
|
||||
console.log("PASSWORD INCORRECT")
|
||||
return {status: 403}
|
||||
console.log("INCORRECT PASSWORD")
|
||||
throw error(403, "Password incorrect")
|
||||
}
|
||||
};
|
||||
|
13
src/routes/api/items/icon/+server.ts
Normal file
13
src/routes/api/items/icon/+server.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { json, type RequestHandler } from '@sveltejs/kit';
|
||||
import { Recipe } from '../../../../models/Recipe';
|
||||
import { dbConnect, dbDisconnect } from '../../../../utils/db';
|
||||
import type {BriefRecipeType} from '../../../../types/types';
|
||||
|
||||
export const GET: RequestHandler = async ({params}) => {
|
||||
await dbConnect();
|
||||
let icons = (await Recipe.distinct('icon').lean());
|
||||
await dbDisconnect();
|
||||
|
||||
icons = JSON.parse(JSON.stringify(icons));
|
||||
return json(icons);
|
||||
};
|
13
src/routes/api/items/icon/[icon]/+server.ts
Normal file
13
src/routes/api/items/icon/[icon]/+server.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { json, type RequestHandler } from '@sveltejs/kit';
|
||||
import { Recipe } from '../../../../../models/Recipe';
|
||||
import { dbConnect, dbDisconnect } from '../../../../../utils/db';
|
||||
import type {BriefRecipeType} from '../../../../../types/types';
|
||||
|
||||
export const GET: RequestHandler = async ({params}) => {
|
||||
await dbConnect();
|
||||
let recipes = (await Recipe.find({tags: params.icon}, 'name short_name images tags category icon description season').lean()) as BriefRecipeType[];
|
||||
await dbDisconnect();
|
||||
|
||||
recipes = JSON.parse(JSON.stringify(recipes));
|
||||
return json(recipes);
|
||||
};
|
@ -16,10 +16,12 @@ const test_json = [
|
||||
caption: "",
|
||||
}],
|
||||
description: "Alles was das Bauernherz erfreuen lässt in einer Mahlzeit.",
|
||||
tags: ["Schweiz", "Käse", "Speck", "Nudeln", "Apfelmuß", "Kartoffeln"],
|
||||
season: [6,7,8,9,10,11,12,1],
|
||||
preamble: "Dieser Schweizer Klassiker ist wohl das beste Essen nach einem langen Tag von Skifahren. Die Beilage aus Apfelmus ist ein Muss.",
|
||||
tags: ["Schweiz", "Käse", "Speck", "Nudeln", "Apfelmuß", "Kartoffeln", "Fleisch"],
|
||||
season: [10,11,12,1],
|
||||
portions: "4 Hauptspeisen",
|
||||
total_time: "30 Minuten",
|
||||
preparation: "10 min",
|
||||
total_time: "30 min",
|
||||
ingredients: [ {
|
||||
name: "",
|
||||
list: [
|
||||
|
77
src/routes/api/seed/json/aelplermagronen.js
Normal file
77
src/routes/api/seed/json/aelplermagronen.js
Normal file
@ -0,0 +1,77 @@
|
||||
const obj =
|
||||
{
|
||||
short_name: "aelplermagronen",
|
||||
name : "Älplermagronen",
|
||||
category: "Hauptspeise",
|
||||
icon: "🍂",
|
||||
datecreated: 20230619,
|
||||
datemodified: 20230619,
|
||||
images: [{
|
||||
mediapath: "aelplermagronen.webp",
|
||||
alt: "Älplermagronen serviert mit Apfelmuß",
|
||||
caption: "",
|
||||
}],
|
||||
description: "Alles was das Bauernherz erfreuen lässt in einer Mahlzeit.",
|
||||
tags: ["Schweiz", "Käse", "Speck", "Nudeln", "Apfelmuß", "Kartoffeln"],
|
||||
season: [6,7,8,9,10,11,12,1],
|
||||
portions: "4 Hauptspeisen",
|
||||
total_time: "30 Minuten",
|
||||
ingredients: [ {
|
||||
name: "",
|
||||
list: [
|
||||
{ name: "Speckwürfel",
|
||||
unit: "g",
|
||||
amount: "150"
|
||||
},
|
||||
{
|
||||
name: "mittelgroße Zwiebeln",
|
||||
unit: "",
|
||||
amount: "3",
|
||||
},
|
||||
{
|
||||
name: "Kartoffeln, festkochend",
|
||||
unit: "g",
|
||||
amount: "400",
|
||||
},
|
||||
{
|
||||
name: "Milch",
|
||||
unit: "L",
|
||||
amount: "1-2",
|
||||
},
|
||||
{
|
||||
name: "Maccaroni",
|
||||
unit: "g",
|
||||
amount: "400",
|
||||
},
|
||||
{
|
||||
name: "Appenzeller",
|
||||
unit: "g",
|
||||
amount: "150",
|
||||
},
|
||||
{
|
||||
name: "<a href=apfelmuss>Apfelmuß</a>",
|
||||
unit: "",
|
||||
amount: "",
|
||||
},
|
||||
|
||||
]},
|
||||
],
|
||||
instructions: [
|
||||
{name: "",
|
||||
steps: [
|
||||
"In einem großen Topf oder tiefer Pfanne Speckwürfel anbraten.",
|
||||
"Zwiebel in Halbringe schneiden und im gleichen Topf schwitzen lassen.",
|
||||
"Kartoffeln schälen und in ~1 cm<sup>3</sup> schneiden.",
|
||||
"Wenn Ziwebeln genügend gekocht sind die Kartoffeln hinzufügen und Milch hinzufügen, sodass alles bedeckt ist. Ca. 10 Minuten kochen lassen.",
|
||||
"Ca. 1 L Milch hinzugeben. Für den nächsten Schritt wollen wir die Maccaroni hinzufügen. Damit diese nicht zu breiig werden geben wir erst die Milch zu und lassen sie aufkochen.",
|
||||
"Wenn die der Topf wieder kocht jetzt die Maccaroni hinzugeben.",
|
||||
"Den Käse zerreiben oder in kleine Würfel schneiden.",
|
||||
"Ein bis zwei Minuten bevor die Nudeln durchgekocht sind den Käse hinzugeben und schmelzen lassen.",
|
||||
"Mit Salz und Muskat würzen.",
|
||||
"Den Topf ein bisschen zu früh vom Herd nehmen und ein bisschen auskühlen lassen.",
|
||||
"Mit <a href=apfelkompott>Apfelmuß oder Apfelkompott</a> servieren."
|
||||
]
|
||||
}
|
||||
],
|
||||
addendum: "<p>Man kann das Gericht noch dekanter machen indem man zu Teilen Rahm an Stelle von Milch verwendet. Zudem kann man das ganze auch noch in eine Auflaufform geben und im Ofen eine Kruste anbacken</p>",
|
||||
}
|
50
src/routes/api/seed/json/template.js
Normal file
50
src/routes/api/seed/json/template.js
Normal file
@ -0,0 +1,50 @@
|
||||
const obj =
|
||||
{
|
||||
short_name: "<++>",
|
||||
name : "<++>",
|
||||
category: "<++>",
|
||||
icon: "<++>",
|
||||
datecreated: 20230619,
|
||||
datemodified: 20230619,
|
||||
images: [{
|
||||
mediapath: "<++>.webp",
|
||||
alt: "<++>",
|
||||
caption: "<++>",
|
||||
}],
|
||||
description: "<++>",
|
||||
tags: [<++>],
|
||||
season: [<++>],
|
||||
baking: {
|
||||
temperature: "<++>",
|
||||
length: "<++>",
|
||||
mode: "<++>",
|
||||
},
|
||||
preparation: "<++>",
|
||||
fermentation: {
|
||||
bulk: "<++>",
|
||||
final: "<++>"
|
||||
},
|
||||
portions: "<++>",
|
||||
total_time: "<++>",
|
||||
ingredients: [ {
|
||||
name: "<++>",
|
||||
list: [
|
||||
{ name: "<++>",
|
||||
unit: "<++>",
|
||||
amount: <++>,
|
||||
},
|
||||
{
|
||||
name: "<++>",
|
||||
unit: "<++>",
|
||||
amount: <++>,
|
||||
},
|
||||
]},
|
||||
],
|
||||
instructions: [
|
||||
{name: "<++>",
|
||||
steps: [
|
||||
"<++>",
|
||||
"<++>"
|
||||
]
|
||||
}
|
||||
]}
|
@ -8,7 +8,6 @@
|
||||
export let data: PageData;
|
||||
export let current_month = new Date().getMonth() + 1
|
||||
</script>
|
||||
|
||||
<h1>Rezepte</h1>
|
||||
<h2>In Saison</h2>
|
||||
<section>
|
||||
|
@ -6,19 +6,42 @@
|
||||
import "$lib/components/nordtheme.css"
|
||||
import MultiImgWrapper from './MultiImgWrapper.svelte'
|
||||
import EditButton from '$lib/components/EditButton.svelte';
|
||||
import InstructionsPage from '$lib/components/InstructionsPage.svelte';
|
||||
import IngredientsPage from '$lib/components/IngredientsPage.svelte';
|
||||
export let data: PageData;
|
||||
let hero_img_src = "/images/" + data.images[0].mediapath
|
||||
export let months = ["Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember"]
|
||||
function season_intervals() {
|
||||
let interval_arr = []
|
||||
let start = 0
|
||||
for(var i = 0; i < data.season.length - 1; i++)
|
||||
{
|
||||
if(Math.abs(data.season[i] - data.season[i + 1])%11 > 1){
|
||||
interval_arr.push([data.season[start], data.season[i]])
|
||||
start=i+1
|
||||
|
||||
|
||||
let start_i = 0
|
||||
for(var i = 12; i > 0; i--){
|
||||
if(data.season.includes(i)){
|
||||
start_i = data.season.indexOf(i);
|
||||
}
|
||||
else{
|
||||
break
|
||||
}
|
||||
}
|
||||
interval_arr.push([data.season[start], data.season[data.season.length -1]])
|
||||
|
||||
var start = data.season[start_i]
|
||||
var end_i
|
||||
const len = data.season.length
|
||||
for(var i = 0; i < len -1; i++){
|
||||
if(data.season.includes((start + i) %12 + 1)){
|
||||
end_i = (start_i + i + 1) % len
|
||||
}
|
||||
else{
|
||||
interval_arr.push([start, data.season[end_i]])
|
||||
start = data.season[(start + i + 1) % len]
|
||||
}
|
||||
|
||||
}
|
||||
if(interval_arr.length == 0){
|
||||
interval_arr.push([start, data.season[end_i]])
|
||||
}
|
||||
|
||||
return interval_arr
|
||||
}
|
||||
export let season_iv = season_intervals();
|
||||
@ -29,20 +52,16 @@ font-family: sans-serif;
|
||||
}
|
||||
h1{
|
||||
text-align: center;
|
||||
padding: 0.5em 2em;
|
||||
padding-block: 0.5em;
|
||||
border-radius: 10000px;
|
||||
margin:0;
|
||||
font-size: 3rem;
|
||||
}
|
||||
.wrapper{
|
||||
margin-inline: auto;
|
||||
max-width: 700px;
|
||||
padding-inline: 2rem;
|
||||
}
|
||||
.tags{
|
||||
margin-block: 1rem;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 1em;
|
||||
}
|
||||
@ -64,12 +83,109 @@ h1{
|
||||
background-color: var(--orange);
|
||||
box-shadow: 0.1em 0.1em 0.2em 0.2em rgba(0,0,0,0.3);
|
||||
}
|
||||
|
||||
.wrapper{
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
max-width: 1000px;
|
||||
justify-content: center;
|
||||
margin-inline: auto;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 700px){
|
||||
.wrapper{
|
||||
flex-direction:column;
|
||||
}
|
||||
}
|
||||
|
||||
.title_container{
|
||||
max-width: 1000px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-inline: auto;
|
||||
}
|
||||
.title{
|
||||
position: relative;
|
||||
width: min(800px, 80vw);
|
||||
margin-inline: auto;
|
||||
transform: translateY(-4rem);
|
||||
background-color: var(--nord6);
|
||||
padding: 1rem 2rem;
|
||||
}
|
||||
.title_container .img{
|
||||
width: 100%;
|
||||
height: 700px;
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
.icon{
|
||||
position: absolute;
|
||||
top: -1em;
|
||||
right: -0.75em;
|
||||
text-decoration: unset;
|
||||
background-color: #FAFAFE;
|
||||
padding: 0.5em;
|
||||
font-size: 1.5rem;
|
||||
border-radius: 100000px;
|
||||
transition: 100ms;
|
||||
box-shadow: 0em 0em 1em 0.5em rgba(0,0,0,0.5);
|
||||
}
|
||||
.icon:hover,
|
||||
.icon:focus-visible{
|
||||
scale: 1.2 1.2;
|
||||
animation: shake 0.5s ease forwards;
|
||||
}
|
||||
|
||||
h4{
|
||||
margin-block: 0;
|
||||
}
|
||||
.addendum{
|
||||
max-width: 800px;
|
||||
margin-inline: auto;
|
||||
padding-inline: 2rem;
|
||||
}
|
||||
@media screen and (max-width: 800px){
|
||||
.title{
|
||||
width: 100%;
|
||||
}
|
||||
.icon{
|
||||
right: 1rem;
|
||||
}
|
||||
}
|
||||
@keyframes shake{
|
||||
0%{
|
||||
transform: rotate(0)
|
||||
scale(1,1);
|
||||
}
|
||||
25%{
|
||||
box-shadow: 0em 0em 1em 0.2em rgba(0, 0, 0, 0.6);
|
||||
transform: rotate(var(--angle))
|
||||
scale(1.2,1.2)
|
||||
;
|
||||
}
|
||||
50%{
|
||||
|
||||
box-shadow: 0em 0em 1em 0.2em rgba(0, 0, 0, 0.6);
|
||||
transform: rotate(calc(-1* var(--angle)))
|
||||
scale(1.2,1.2);
|
||||
}
|
||||
74%{
|
||||
|
||||
box-shadow: 0em 0em 1em 0.2em rgba(0, 0, 0, 0.6);
|
||||
transform: rotate(var(--angle))
|
||||
scale(1.2, 1.2);
|
||||
}
|
||||
100%{
|
||||
transform: rotate(0)
|
||||
scale(1.2,1.2);
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<div class="wrapper">
|
||||
<h1>{data.name}</h1>
|
||||
|
||||
<MultiImgWrapper wrap={data.images.length>1} class=double>
|
||||
<!--<MultiImgWrapper wrap={data.images.length>1} class=double>
|
||||
{#each data.images as img}
|
||||
<img width=100% src="/images/{img.mediapath}" alt="{img.alt}">
|
||||
<figure>
|
||||
@ -78,76 +194,40 @@ h1{
|
||||
{/if}
|
||||
</figure>
|
||||
{/each}
|
||||
</MultiImgWrapper>
|
||||
</MultiImgWrapper>-->
|
||||
|
||||
|
||||
<div class=title_container>
|
||||
<div class=img style="background-image: url({hero_img_src})"></div>
|
||||
<div class=title>
|
||||
<a class="icon" href='/rezepte/season/{data.season[0]}'>{data.icon}</a>
|
||||
<h1>{data.name}</h1>
|
||||
{#if data.preamble}
|
||||
<p>{data.preamble}</p>
|
||||
{/if}
|
||||
<div class=tags>
|
||||
Saison:
|
||||
<h4>Saison:</h4>
|
||||
{#each season_iv as season}
|
||||
<a class=tag href="/rezepte/season/{season[0]}">{months[season[0] - 1]}-{months[season[1] - 1]}</a>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
|
||||
<h4>Stichwörter:</h4>
|
||||
<div class=tags>
|
||||
Stichwörter:
|
||||
{#each data.tags as tag}
|
||||
<a href="/rezepte/tag/{tag}" class=tag>{tag}</a>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if data.preparation}
|
||||
<div>Vorbereitung: {data.preparation}</div>
|
||||
{/if}
|
||||
|
||||
|
||||
{#if data.fermentation}
|
||||
{#if data.fermentation.bulk}
|
||||
<div>Stockgare: {data.fermentation.bulk}</div>
|
||||
{/if}
|
||||
|
||||
{#if data.fermentation.final}
|
||||
<div>Stückgare: {data.fermentation.final}</div>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
{#if data.baking}
|
||||
<div>Backen: {data.baking.length} bei {data.baking.temperature} °C {data.baking.mode}</div>
|
||||
{/if}
|
||||
|
||||
{#if data.total_time}
|
||||
<div>Gesamtzeit: {data.total_time}</div>
|
||||
{/if}
|
||||
|
||||
|
||||
{#if data.ingredients}
|
||||
<h2>Zutaten</h2>
|
||||
{#each data.ingredients as list}
|
||||
{#if list.name}
|
||||
<h3>{list.name}</h3>
|
||||
{/if}
|
||||
<ul>
|
||||
{#each list.list as item}
|
||||
<li>{item.amount} {item.unit} {@html item.name}</li>
|
||||
{/each}
|
||||
</ul>
|
||||
{/each}
|
||||
{/if}
|
||||
|
||||
|
||||
{#if data.instructions}
|
||||
<h2>Zubereitung</h2>
|
||||
{#each data.instructions as list}
|
||||
{#if list.name}
|
||||
<h3>{list.name}</h3>
|
||||
{/if}
|
||||
<ol>
|
||||
{#each list.steps as step}
|
||||
<li>{@html step}</li>
|
||||
{/each}
|
||||
</ol>
|
||||
{/each}
|
||||
{/if}
|
||||
<div class=wrapper>
|
||||
<IngredientsPage {data}></IngredientsPage>
|
||||
<InstructionsPage {data}></InstructionsPage>
|
||||
</div>
|
||||
<div class=addendum>
|
||||
{#if data.addendum}
|
||||
{@html data.addendum}
|
||||
{/if}
|
||||
|
@ -1,39 +1,92 @@
|
||||
<script lang="ts">
|
||||
import Check from '$lib/assets/icons/Check.svelte';
|
||||
import SeasonSelect from '$lib/components/SeasonSelect.svelte';
|
||||
import '$lib/css/action_button.css'
|
||||
|
||||
export let data: PageData;
|
||||
let preamble = ""
|
||||
let addendum = ""
|
||||
|
||||
import { season } from '$lib/js/season_store';
|
||||
season.update(() => [])
|
||||
let season_local
|
||||
season.subscribe((s) => {
|
||||
season_local = s
|
||||
});
|
||||
|
||||
export let card_data ={
|
||||
icon: "",
|
||||
category: "",
|
||||
name: "",
|
||||
description: "",
|
||||
tags: [],
|
||||
}
|
||||
let short_name
|
||||
export let add_info ={
|
||||
preparation: "",
|
||||
fermentation: {
|
||||
bulk: "",
|
||||
final: "",
|
||||
},
|
||||
baking: {
|
||||
length: "",
|
||||
temperature: "",
|
||||
mode: "",
|
||||
},
|
||||
total_time: "",
|
||||
}
|
||||
|
||||
let images = []
|
||||
export let portions = ""
|
||||
|
||||
let short_name = ""
|
||||
let password
|
||||
let datecreated = new Date()
|
||||
let datemodified = datecreated
|
||||
|
||||
import type { PageData } from './$types';
|
||||
import CardAdd from '$lib/components/CardAdd.svelte';
|
||||
|
||||
import SeasonSelect from '$lib/components/SeasonSelect.svelte';
|
||||
export let season = []
|
||||
|
||||
import CreateIngredientList from '$lib/components/CreateIngredientList.svelte';
|
||||
export let ingredients = []
|
||||
|
||||
import CreateStepList from '$lib/components/CreateStepList.svelte';
|
||||
export let instructions = []
|
||||
|
||||
|
||||
function get_season(){
|
||||
let season = []
|
||||
const el = document.getElementById("labels");
|
||||
for(var i = 0; i < el.children.length; i++){
|
||||
if(el.children[i].children[0].children[0].checked){
|
||||
season.push(i+1)
|
||||
}
|
||||
}
|
||||
return season
|
||||
}
|
||||
function write_season(season){
|
||||
const el = document.getElementById("labels");
|
||||
for(var i = 0; i < season.length; i++){
|
||||
el.children[i].children[0].children[0].checked = true
|
||||
}
|
||||
}
|
||||
|
||||
async function doPost () {
|
||||
console.log(add_info.total_time)
|
||||
const res = await fetch('/api/add', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
recipe: {
|
||||
season: get_season(),
|
||||
...card_data,
|
||||
images: [{
|
||||
mediapath: short_name + '.webp',
|
||||
alt: "",
|
||||
caption: ""
|
||||
}],
|
||||
...add_info,
|
||||
images: {mediapath: short_name + '.webp', alt: "", caption: ""}, // TODO
|
||||
season: season_local,
|
||||
short_name,
|
||||
datecreated,
|
||||
datemodified,
|
||||
instructions,
|
||||
ingredients,
|
||||
preamble,
|
||||
addendum,
|
||||
},
|
||||
headers: {
|
||||
'content-type': 'application/json',
|
||||
@ -42,35 +95,146 @@
|
||||
})
|
||||
})
|
||||
|
||||
const json = await res.json()
|
||||
result = JSON.stringify(json)
|
||||
console.log(result)
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
input.temp{
|
||||
all: unset;
|
||||
input{
|
||||
display: block;
|
||||
border: unset;
|
||||
margin: 1rem auto;
|
||||
padding: 0.2em 1em;
|
||||
padding: 0.5em 1em;
|
||||
border-radius: 1000px;
|
||||
background-color: var(--nord4);
|
||||
font-size: 1.1rem;
|
||||
transition: 100ms;
|
||||
|
||||
}
|
||||
input:hover,
|
||||
input:focus-visible
|
||||
{
|
||||
scale: 1.05 1.05;
|
||||
}
|
||||
.list_wrapper{
|
||||
margin-inline: auto;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
max-width: 1000px;
|
||||
gap: 2rem;
|
||||
justify-content: center;
|
||||
}
|
||||
@media screen and (max-width: 700px){
|
||||
.list_wrapper{
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
input[type=password]{
|
||||
box-sizing: border-box;
|
||||
font-size: 1.5rem;
|
||||
padding-block: 0.5em;
|
||||
display: inline;
|
||||
width: 100%;
|
||||
}
|
||||
.submit_wrapper{
|
||||
position: relative;
|
||||
margin-inline: auto;
|
||||
width: max(300px, 50vw)
|
||||
}
|
||||
.submit_wrapper button{
|
||||
position: absolute;
|
||||
right:-1em;
|
||||
bottom: -0.5em;
|
||||
}
|
||||
.submit_wrapper h2{
|
||||
margin-bottom: 0;
|
||||
}
|
||||
h1{
|
||||
text-align: center;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
.title_container{
|
||||
max-width: 1000px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-inline: auto;
|
||||
}
|
||||
.title{
|
||||
position: relative;
|
||||
width: min(800px, 80vw);
|
||||
margin-block: 2rem;
|
||||
margin-inline: auto;
|
||||
background-color: var(--nord6);
|
||||
padding: 1rem 2rem;
|
||||
}
|
||||
.title p{
|
||||
border: 2px solid var(--nord1);
|
||||
border-radius: 10000px;
|
||||
padding: 0.5em 1em;
|
||||
font-size: 1.1rem;
|
||||
transition: 200ms;
|
||||
}
|
||||
.title p:hover,
|
||||
.title p:focus-within{
|
||||
scale: 1.02 1.02;
|
||||
}
|
||||
.addendum{
|
||||
font-size: 1.1rem;
|
||||
max-width: 90%;
|
||||
margin-inline: auto;
|
||||
border: 2px solid var(--nord1);
|
||||
border-radius: 45px;
|
||||
padding: 1em 1em;
|
||||
transition: 100ms;
|
||||
}
|
||||
.addendum:hover,
|
||||
.addendum:focus-within
|
||||
{
|
||||
scale: 1.02 1.02;
|
||||
}
|
||||
.addendum_wrapper{
|
||||
max-width: 1000px;
|
||||
margin-inline: auto;
|
||||
}
|
||||
h3{
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
<h1>Rezept hinzufügen</h1>
|
||||
<h1>Rezept erstellen</h1>
|
||||
|
||||
<CardAdd {card_data}></CardAdd>
|
||||
|
||||
<input class=temp bind:value={short_name} placeholder="Kurzname"/>
|
||||
<h3>Kurzname (für URL):</h3>
|
||||
<input bind:value={short_name} placeholder="Kurzname"/>
|
||||
|
||||
<SeasonSelect {season}></SeasonSelect>
|
||||
<div class=title_container>
|
||||
<div class=title>
|
||||
<h4>Eine etwas längere Beschreibung:</h4>
|
||||
<p bind:innerText={preamble} contenteditable></p>
|
||||
<div class=tags>
|
||||
<h4>Saison:</h4>
|
||||
<SeasonSelect></SeasonSelect>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>Zutaten</h2>
|
||||
<CreateIngredientList {ingredients}></CreateIngredientList>
|
||||
<h2>Zubereitung</h2>
|
||||
<CreateStepList {instructions} ></CreateStepList>
|
||||
<input class=temp type="password" placeholder=Passwort bind:value={password}>
|
||||
<button on:click={doPost}>ADD RECIPE</button>
|
||||
<div class=list_wrapper>
|
||||
<div>
|
||||
<CreateIngredientList {ingredients} {portions}></CreateIngredientList>
|
||||
</div>
|
||||
<div>
|
||||
<CreateStepList {instructions} {add_info}></CreateStepList>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class=addendum_wrapper>
|
||||
<h3>Nachtrag:</h3>
|
||||
<div class=addendum bind:innerText={addendum} contenteditable></div>
|
||||
</div>
|
||||
|
||||
<div class=submit_wrapper>
|
||||
<h2>Neues Rezept hinzufügen:</h2>
|
||||
<input type="password" placeholder=Passwort bind:value={password}>
|
||||
<button class=action_button on:click={doPost}><Check fill=white width=2rem height=2rem></Check></button>
|
||||
</div>
|
||||
|
@ -1,5 +1,21 @@
|
||||
<script lang="ts">
|
||||
import Check from '$lib/assets/icons/Check.svelte';
|
||||
import Cross from '$lib/assets/icons/Cross.svelte';
|
||||
import SeasonSelect from '$lib/components/SeasonSelect.svelte';
|
||||
import '$lib/css/action_button.css'
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
|
||||
export let data: PageData;
|
||||
let preamble = data.recipe.preamble
|
||||
let addendum = data.recipe.addendum
|
||||
|
||||
import { season } from '$lib/js/season_store';
|
||||
season.update(() => data.recipe.season)
|
||||
let season_local
|
||||
season.subscribe((s) => {
|
||||
season_local = s
|
||||
});
|
||||
|
||||
let old_short_name = data.recipe.short_name
|
||||
|
||||
export let card_data ={
|
||||
@ -9,8 +25,22 @@
|
||||
description: data.recipe.description,
|
||||
tags: data.recipe.tags,
|
||||
}
|
||||
export let add_info ={
|
||||
preparation: data.recipe.preparation,
|
||||
fermentation: {
|
||||
bulk: data.recipe.fermentation.bulk,
|
||||
final: data.recipe.fermentation.final,
|
||||
},
|
||||
baking: {
|
||||
length: data.recipe.baking.length,
|
||||
temperature: data.recipe.baking.temperature,
|
||||
mode: data.recipe.baking.mode,
|
||||
},
|
||||
total_time: data.recipe.total_time,
|
||||
}
|
||||
|
||||
let images = data.recipe.images
|
||||
let season = data.recipe.season
|
||||
export let portions = data.recipe.portions
|
||||
|
||||
let short_name = data.recipe.short_name
|
||||
let password
|
||||
@ -19,9 +49,6 @@
|
||||
|
||||
import type { PageData } from './$types';
|
||||
import CardAdd from '$lib/components/CardAdd.svelte';
|
||||
import MediaScroller from '$lib/components/MediaScroller.svelte';
|
||||
import Card from '$lib/components/Card.svelte';
|
||||
import Search from '$lib/components/Search.svelte';
|
||||
|
||||
import CreateIngredientList from '$lib/components/CreateIngredientList.svelte';
|
||||
export let ingredients = data.recipe.ingredients
|
||||
@ -29,6 +56,7 @@
|
||||
import CreateStepList from '$lib/components/CreateStepList.svelte';
|
||||
export let instructions = data.recipe.instructions
|
||||
|
||||
|
||||
function get_season(){
|
||||
let season = []
|
||||
const el = document.getElementById("labels");
|
||||
@ -46,19 +74,39 @@
|
||||
}
|
||||
}
|
||||
|
||||
async function doPost () {
|
||||
async function doDelete(){
|
||||
const response = confirm("Bist du dir sicher, dass du das Rezept löschen willst?")
|
||||
if(!response){
|
||||
return
|
||||
}
|
||||
const res = await fetch('/api/delete', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
old_short_name,
|
||||
headers: {
|
||||
'content-type': 'application/json',
|
||||
bearer: password,
|
||||
}
|
||||
})
|
||||
|
||||
})
|
||||
}
|
||||
async function doEdit() {
|
||||
const res = await fetch('/api/edit', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
recipe: {
|
||||
...card_data,
|
||||
...add_info,
|
||||
images, // TODO
|
||||
season: get_season(),
|
||||
season: season_local,
|
||||
short_name,
|
||||
datecreated,
|
||||
datemodified,
|
||||
instructions,
|
||||
ingredients,
|
||||
addendum,
|
||||
preamble
|
||||
},
|
||||
old_short_name,
|
||||
headers: {
|
||||
@ -67,36 +115,158 @@
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
const json = await res.json()
|
||||
result = JSON.stringify(json)
|
||||
console.log(result)
|
||||
const item = await res.json();
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
input{
|
||||
all: unset;
|
||||
display: block;
|
||||
border: unset;
|
||||
margin: 1rem auto;
|
||||
padding: 0.2em 1em;
|
||||
padding: 0.5em 1em;
|
||||
border-radius: 1000px;
|
||||
background-color: var(--nord4);
|
||||
font-size: 1.1rem;
|
||||
transition: 100ms;
|
||||
|
||||
}
|
||||
input:hover,
|
||||
input:focus-visible
|
||||
{
|
||||
scale: 1.05 1.05;
|
||||
}
|
||||
.list_wrapper{
|
||||
margin-inline: auto;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
max-width: 1000px;
|
||||
gap: 2rem;
|
||||
justify-content: center;
|
||||
}
|
||||
@media screen and (max-width: 700px){
|
||||
.list_wrapper{
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
input[type=password]{
|
||||
box-sizing: border-box;
|
||||
font-size: 1.5rem;
|
||||
padding-block: 0.5em;
|
||||
display: inline;
|
||||
width: 100%;
|
||||
}
|
||||
.submit_wrapper{
|
||||
position: relative;
|
||||
margin-inline: auto;
|
||||
width: max(300px, 50vw)
|
||||
}
|
||||
.submit_wrapper button{
|
||||
position: absolute;
|
||||
right:-1em;
|
||||
bottom: -0.5em;
|
||||
}
|
||||
.submit_wrapper h2{
|
||||
margin-bottom: 0;
|
||||
}
|
||||
h1{
|
||||
text-align: center;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
.title_container{
|
||||
max-width: 1000px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-inline: auto;
|
||||
}
|
||||
.title{
|
||||
position: relative;
|
||||
width: min(800px, 80vw);
|
||||
margin-block: 2rem;
|
||||
margin-inline: auto;
|
||||
background-color: var(--nord6);
|
||||
padding: 1rem 2rem;
|
||||
}
|
||||
.title p{
|
||||
border: 2px solid var(--nord1);
|
||||
border-radius: 10000px;
|
||||
padding: 0.5em 1em;
|
||||
font-size: 1.1rem;
|
||||
transition: 200ms;
|
||||
}
|
||||
.title p:hover,
|
||||
.title p:focus-within{
|
||||
scale: 1.02 1.02;
|
||||
}
|
||||
.addendum{
|
||||
font-size: 1.1rem;
|
||||
max-width: 90%;
|
||||
margin-inline: auto;
|
||||
border: 2px solid var(--nord1);
|
||||
border-radius: 45px;
|
||||
padding: 1em 1em;
|
||||
transition: 100ms;
|
||||
}
|
||||
.addendum:hover,
|
||||
.addendum:focus-within
|
||||
{
|
||||
scale: 1.02 1.02;
|
||||
}
|
||||
.addendum_wrapper{
|
||||
max-width: 1000px;
|
||||
margin-inline: auto;
|
||||
}
|
||||
h3{
|
||||
text-align: center;
|
||||
}
|
||||
.delete{
|
||||
position: fixed;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
margin: 2rem;
|
||||
}
|
||||
</style>
|
||||
<h1>Rezept hinzufügen</h1>
|
||||
<h1>Rezept editieren</h1>
|
||||
|
||||
<CardAdd {card_data}></CardAdd>
|
||||
<button on:click={console.log(JSON.stringify(ingredients, null, 4))}>Printout Ingredients</button>
|
||||
<button on:click={console.log(JSON.stringify(instructions, null, 4))}>Printout Instructions</button>
|
||||
<button on:click={console.log(JSON.stringify(card_data, null, 4))}>Prinout Card Data</button>
|
||||
|
||||
<h3>Kurzname (für URL):</h3>
|
||||
<input bind:value={short_name} placeholder="Kurzname"/>
|
||||
<h2>Zutaten</h2>
|
||||
<CreateIngredientList {ingredients}></CreateIngredientList>
|
||||
<h2>Zubereitung</h2>
|
||||
<CreateStepList {instructions} ></CreateStepList>
|
||||
|
||||
<div class=title_container>
|
||||
<div class=title>
|
||||
<h4>Eine etwas längere Beschreibung:</h4>
|
||||
<p bind:innerText={preamble} contenteditable></p>
|
||||
<div class=tags>
|
||||
<h4>Saison:</h4>
|
||||
<SeasonSelect></SeasonSelect>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class=list_wrapper>
|
||||
<div>
|
||||
<CreateIngredientList {ingredients} {portions}></CreateIngredientList>
|
||||
</div>
|
||||
<div>
|
||||
<CreateStepList {instructions} {add_info}></CreateStepList>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class=addendum_wrapper>
|
||||
<h3>Nachtrag:</h3>
|
||||
<div class=addendum bind:innerText={addendum} contenteditable></div>
|
||||
</div>
|
||||
|
||||
<div class=submit_wrapper>
|
||||
<h2>Editiertes Rezept abspeichern:</h2>
|
||||
<input type="password" placeholder=Passwort bind:value={password}>
|
||||
<button on:click={doPost}>EDIT RECIPE</button>
|
||||
<button class=action_button on:click={doEdit}><Check fill=white width=2rem height=2rem></Check></button>
|
||||
</div>
|
||||
|
||||
<div class=submit_wrapper>
|
||||
<h2>Rezept löschen:</h2>
|
||||
<input type="password" placeholder=Passwort bind:value={password}>
|
||||
<button class=action_button on:click={doDelete}><Cross fill=white width=2rem height=2rem></Cross></button>
|
||||
</div>
|
||||
|
@ -38,6 +38,7 @@ export type RecipeModelType = {
|
||||
name?: string;
|
||||
steps: [string]
|
||||
}]
|
||||
preamble?: String
|
||||
addendum?: string
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user