simplify structure by remove (rezepte)

This commit is contained in:
2024-01-20 16:39:27 +01:00
parent 36e0abb26d
commit c4c72bd8f0
26 changed files with 2 additions and 2 deletions

View File

@ -0,0 +1,5 @@
import { get_username } from '$lib/js/get_username';;
export const load = (async ({cookies}) => {
return { user: await get_username(cookies) }
});

View File

@ -0,0 +1,21 @@
<script>
import Header from '$lib/components/Header.svelte'
import UserHeader from '$lib/components/UserHeader.svelte';
export let data
let username = ""
if(data.user){
username = data.user.username
}
</script>
<Header>
<ul class=site_header slot=links>
<li><a href="/rezepte">Alle Rezepte</a></li>
<li><a href="/rezepte/season">In Saison</a></li>
<li><a href="/rezepte/category">Kategorie</a></li>
<li><a href="/rezepte/icon">Icon</a></li>
<li><a href="/rezepte/tag">Stichwörter</a></li>
</ul>
<UserHeader slot=right_side {username}></UserHeader>
<slot></slot>
</Header>

View File

@ -0,0 +1,13 @@
import type { PageServerLoad } from "./$types";
export async function load({ fetch }) {
let current_month = new Date().getMonth() + 1
const res_season = await fetch(`/api/rezepte/items/in_season/` + current_month);
const res_all_brief = await fetch(`/api/rezepte/items/all_brief`);
const item_season = await res_season.json();
const item_all_brief = await res_all_brief.json();
return {
season: item_season,
all_brief: item_all_brief,
};
};

View File

@ -0,0 +1,44 @@
<script lang="ts">
import type { PageData } from './$types';
import MediaScroller from '$lib/components/MediaScroller.svelte';
import AddButton from '$lib/components/AddButton.svelte';
import Card from '$lib/components/Card.svelte';
import Search from '$lib/components/Search.svelte';
export let data: PageData;
export let current_month = new Date().getMonth() + 1
const categories = ["Hauptspeise", "Nudel", "Brot", "Dessert", "Suppe", "Beilage", "Salat", "Kuchen", "Frühstück", "Sauce", "Zutat", "Getränk", "Aufstrich", "Guetzli", "Unterwegs"]
</script>
<style>
h1{
text-align: center;
margin-bottom: 0;
font-size: 4rem;
}
</style>
<svelte:head>
<title>Bocken Rezepte</title>
<meta name="description" content="Eine stetig wachsende Ansammlung an Rezepten aus der Bockenschen Küche." />
<meta property="og:image" content="https://bocken.org/static/rezepte/thumb/ragu_aus_rindsrippen.webp" />
<meta property="og:image:secure_url" content="https://bocken.org/static/rezepte/thumb/ragu_aus_rindsrippen.webp" />
<meta property="og:image:type" content="image/webp" />
<meta property="og:image:alt" content="Pasta al Ragu mit Linguine" />
</svelte:head>
<h1>Rezepte</h1>
<Search></Search>
<MediaScroller title="In Saison">
{#each data.season as recipe}
<Card {recipe} {current_month}></Card>
{/each}
</MediaScroller>
{#each categories as category}
<MediaScroller title={category}>
{#each data.all_brief.filter(recipe => recipe.category == category) as recipe}
<Card {recipe} {current_month}></Card>
{/each}
</MediaScroller>
{/each}
<AddButton></AddButton>

View File

@ -0,0 +1,327 @@
<script lang="ts">
import { writable } from 'svelte/store';
export const multiplier = writable(0);
import type { PageData } from './$types';
import "$lib/css/nordtheme.css"
import EditButton from '$lib/components/EditButton.svelte';
import InstructionsPage from '$lib/components/InstructionsPage.svelte';
import IngredientsPage from '$lib/components/IngredientsPage.svelte';
import TitleImgParallax from '$lib/components/TitleImgParallax.svelte';
import { afterNavigate } from '$app/navigation';
import {season} from '$lib/js/season_store';
import RecipeNote from '$lib/components/RecipeNote.svelte';
import {stripHtmlTags} from '$lib/js/stripHtmlTags';
export let data: PageData;
let hero_img_src = "https://bocken.org/static/rezepte/full/" + data.short_name + ".webp"
let placeholder_src = "https://bocken.org/static/rezepte/placeholder/" + data.short_name + ".webp"
export let months = ["Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember"]
function season_intervals() {
let interval_arr = []
let start_i = 0
for(var i = 12; i > 0; i--){
if(data.season.includes(i)){
start_i = data.season.indexOf(i);
}
else{
break
}
}
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();
afterNavigate(() => {
hero_img_src = "https://bocken.org/static/rezepte/full/" + data.short_name + ".webp"
placeholder_src = "https://bocken.org/static/rezepte/placeholder/" + data.short_name + ".webp"
season_iv = season_intervals();
})
let display_date = new Date(data.dateCreated);
if (data.updatedAt){
display_date = new Date(data.updatedAt);
}
const options = {
day: '2-digit',
month: 'short', // German abbreviation for the month
year: 'numeric',
hour: '2-digit',
minute: '2-digit',
};
const formatted_display_date = display_date.toLocaleDateString('de-DE', options)
</script>
<style>
*{
font-family: sans-serif;
}
h1{
text-align: center;
padding-block: 0.5em;
border-radius: 10000px;
margin:0;
font-size: 3rem;
overflow-wrap: break-word;
hyphens: auto;
text-wrap: balance;
}
.category{
--size: 1.75rem;
position: absolute;
top: calc(-1* var(--size) );
left:calc(-3/2 * var(--size));
background-color: var(--nord0);
color: var(--nord6);
text-decoration: none;
font-size: var(--size);
padding: calc(var(--size) * 2/3);
border-radius: 1000px;
transition: 100ms;
box-shadow: 0em 0em 1em 0.3em rgba(0,0,0,0.4);
}
.category:hover,
.category:focus-visible{
background-color: var(--nord1);
scale: 1.1;
}
.tags{
margin-block: 1rem;
display: flex;
flex-direction: row;
align-items: center;
flex-wrap: wrap;
gap: 1em;
}
.center{
justify-content: center;
}
.tag{
all:unset;
color: var(--nord0);
font-size: 1.1rem;
background-color: var(--nord5);
border-radius: 10000px;
padding: 0.25em 1em;
transition: 100ms;
box-shadow: 0em 0em 0.5em 0.05em rgba(0,0,0,0.3);
}
@media (prefers-color-scheme: dark) {
.tag{
background-color: var(--nord0);
color: white;
}
}
.tag:hover,
.tag:focus-visible
{
cursor: pointer;
transform: scale(1.1,1.1);
background-color: var(--orange);
box-shadow: 0.1em 0.1em 0.5em 0.1em rgba(0,0,0,0.3);
}
.wrapper_wrapper{
background-color: #fbf9f3;
padding-top: 10rem;
display: flex;
flex-direction: column;
align-items: center;
margin-bottom: 3rem;
transform: translateY(-7rem);
z-index: -2;
}
@media (prefers-color-scheme: dark) {
.wrapper_wrapper{
background-color: var(--background-dark);
}
}
.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{
position: relative;
width: min(800px, 80vw);
margin-inline: auto;
background-color: var(--nord6);
padding: 1rem 2rem;
translate: 0 1px; /*bruh*/
z-index: 1;
}
@media (prefers-color-scheme: dark) {
.title{
background-color: var(--nord6-dark);
}
}
.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.3em rgba(0,0,0,0.4);
}
@media (prefers-color-scheme: dark) {
.icon{
background-color: var(--accent-dark);
}
}
.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;
top: -1.75rem;
}
.category{
left: 1rem;
top: calc(var(--size) * -1.5);
}
}
@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);
}
}
.description{
text-align: center;
margin-bottom: 2em;
margin-top: -0.5em;
}
.date{
margin-bottom: 0;
}
</style>
<svelte:head>
<title>{stripHtmlTags(data.name)} - Bocken'sche Rezepte</title>
<meta name="description" content="{stripHtmlTags(data.description)}" />
<meta property="og:image" content="https://bocken.org/static/rezepte/thumb/{data.short_name}.webp" />
<meta property="og:image:secure_url" content="https://bocken.org/static/rezepte/thumb/{data.short_name}.webp" />
<meta property="og:image:type" content="image/webp" />
<meta property="og:image:alt" content="{stripHtmlTags(data.name)}" />
</svelte:head>
<TitleImgParallax src={hero_img_src} {placeholder_src}>
<div class=title>
<a class="category" href='/rezepte/category/{data.category}'>{data.category}</a>
<a class="icon" href='/rezepte/icon/{data.icon}'>{data.icon}</a>
<h1>{@html data.name}</h1>
{#if data.description && ! data.preamble}
<p class=description>{data.description}</p>
{/if}
{#if data.preamble}
<p>{@html data.preamble}</p>
{/if}
<div class=tags>
<h4>Saison:</h4>
{#each season_iv as season}
<a class=tag href="/rezepte/season/{season[0]}">
{months[season[0] - 1]}
{#if season[1]}
- {months[season[1] - 1]}
{/if}
</a>
{/each}
</div>
<h4>Stichwörter:</h4>
<div class="tags center">
{#each data.tags as tag}
<a class=tag href="/rezepte/tag/{tag}">{tag}</a>
{/each}
</div>
{#if data.note}
<RecipeNote note={data.note}></RecipeNote>
{/if}
</div>
<div class=wrapper_wrapper>
<div class=wrapper>
<IngredientsPage {data}></IngredientsPage>
<InstructionsPage {data}></InstructionsPage>
</div>
<div class=addendum>
{#if data.addendum}
{@html data.addendum}
{/if}
</div>
<p class=date>Letzte Änderung: {formatted_display_date}</p>
</div>
</TitleImgParallax>
<EditButton href="/rezepte/edit/{data.short_name}"></EditButton>

View File

@ -0,0 +1,10 @@
import { error } from "@sveltejs/kit";
export async function load({ fetch, params}) {
const res = await fetch(`/api/rezepte/items/${params.name}`);
let item = await res.json();
if(!res.ok){
throw error(res.status, item.message)
}
return item;
}

View File

@ -0,0 +1,5 @@
export async function load({locals}) {
return {
user: locals.user
};
};

View File

@ -0,0 +1,287 @@
<script lang="ts">
import Check from '$lib/assets/icons/Check.svelte';
import SeasonSelect from '$lib/components/SeasonSelect.svelte';
import '$lib/css/action_button.css'
import '$lib/css/nordtheme.css'
let preamble = ""
let addendum = ""
import { season } from '$lib/js/season_store';
import { portions } from '$lib/js/portions_store';
import { img } from '$lib/js/img_store';
season.update(() => [])
let season_local
season.subscribe((s) => {
season_local = s
});
let portions_local
portions.update(() => "")
portions.subscribe((p) => {
portions_local = p});
let img_local
img.update(() => "")
img.subscribe((i) => {
img_local = i});
export let card_data ={
icon: "",
category: "",
name: "",
description: "",
tags: [],
}
export let add_info ={
preparation: "",
fermentation: {
bulk: "",
final: "",
},
baking: {
length: "",
temperature: "",
mode: "",
},
total_time: "",
cooking: "",
}
let images = []
let short_name = ""
let datecreated = new Date()
let datemodified = datecreated
import type { PageData } from './$types';
import CardAdd from '$lib/components/CardAdd.svelte';
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 upload_img(){
console.log("uploading...")
console.log(img_local)
const data = {
image: img_local,
name: short_name,
}
await fetch(`/api/rezepte/img/add`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
credentials: 'include',
},
body: JSON.stringify(data)
});
}
async function doPost () {
upload_img()
console.log(add_info.total_time)
const res = await fetch('/api/rezepte/add', {
method: 'POST',
body: JSON.stringify({
recipe: {
...card_data,
...add_info,
images: {mediapath: short_name + '.webp', alt: "", caption: ""}, // TODO
season: season_local,
short_name,
portions: portions_local,
datecreated,
datemodified,
instructions,
ingredients,
preamble,
addendum,
},
headers: {
'content-type': 'application/json',
}
})
});
if(res.status === 200){
const url = location.href.split('/')
url.splice(url.length -1, 1);
url.push(short_name)
location.assign(url.join('/'))
}
else{
const item = await res.json();
alert(item.message)
}
}
</script>
<style>
input{
display: block;
border: unset;
margin: 1rem auto;
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;
}
}
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;
}
button.action_button{
animation: unset !important;
font-size: 1.3rem;
color: white;
}
.submit_buttons{
display: flex;
margin-inline: auto;
max-width: 1000px;
margin-block: 1rem;
justify-content: center;
align-items: center;
gap: 2rem;
}
.submit_buttons p{
padding: 0;
padding-right: 0.5em;
margin: 0;
}
@media (prefers-color-scheme: dark){
.title{
background-color: var(--nord6-dark);
}
}
</style>
<svelte:head>
<title>Rezept erstellen</title>
<meta name="description" content="Hier können neue Rezepte hinzugefügt werden" />
</svelte:head>
<h1>Rezept erstellen</h1>
<CardAdd {card_data}></CardAdd>
<h3>Kurzname (für URL):</h3>
<input bind:value={short_name} placeholder="Kurzname"/>
<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}></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_buttons>
<button class=action_button on:click={doPost}><p>Hinzufügen</p><Check fill=white width=2rem height=2rem></Check></button>
</div>

View File

@ -0,0 +1,22 @@
<script lang="ts">
import type { PageData } from './$types';
import "$lib/css/nordtheme.css";
export let data: PageData;
import TagCloud from '$lib/components/TagCloud.svelte';
import TagBall from '$lib/components/TagBall.svelte';
</script>
<style>
h1 {
text-align: center;
font-size: 3rem;
}
</style>
<h1>Kategorien</h1>
<section>
<TagCloud>
{#each data.categories as tag}
<TagBall {tag} ref="/rezepte/category">
</TagBall>
{/each}
</TagCloud>
</section>

View File

@ -0,0 +1,7 @@
import type { PageLoad } from "./$types";
export async function load({ fetch}) {
const res = await fetch(`/api/rezepte/items/category`);
const categories= await res.json();
return {categories}
};

View File

@ -0,0 +1,24 @@
<script lang="ts">
import type { PageData } from './$types';
import Recipes from '$lib/components/Recipes.svelte';
import Search from '$lib/components/Search.svelte';
export let data: PageData;
export let current_month = new Date().getMonth() + 1;
import Card from '$lib/components/Card.svelte'
import { rand_array } from '$lib/js/randomize';
</script>
<style>
h1 {
text-align: center;
font-size: 3em;
}
</style>
<h1>Rezepte in Kategorie <q>{data.category}</q>:</h1>
<Search></Search>
<section>
<Recipes>
{#each rand_array(data.recipes) as recipe}
<Card {recipe} {current_month}></Card>
{/each}
</Recipes>
</section>

View File

@ -0,0 +1,10 @@
import type { PageLoad } from "./$types";
export async function load({ fetch, params }) {
const res = await fetch(`/api/rezepte/items/category/${params.category}`);
const items = await res.json();
return {
category: params.category,
recipes: items
}
};

View File

@ -0,0 +1,10 @@
import type { PageLoad } from "./$types";
export async function load({ fetch, params, locals}) {
let current_month = new Date().getMonth() + 1
const res = await fetch(`/api/rezepte/items/${params.name}`);
const recipe = await res.json();
return {recipe: recipe,
user: locals.user
};
};

View File

@ -0,0 +1,387 @@
<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 '$lib/css/nordtheme.css'
import { redirect } from '@sveltejs/kit';
import EditRecipeNote from '$lib/components/EditRecipeNote.svelte';
export let data: PageData;
let preamble = data.recipe.preamble
let addendum = data.recipe.addendum
let image_preview_url="https://bocken.org/static/rezepte/thumb/" + data.recipe.short_name + ".webp"
let note = data.recipe.note
import { season } from '$lib/js/season_store';
import { portions } from '$lib/js/portions_store';
portions.update(() => data.recipe.portions)
let portions_local
portions.subscribe((p) => {
portions_local = p
});
season.update(() => data.recipe.season)
let season_local
season.subscribe((s) => {
season_local = s
});
import { img } from '$lib/js/img_store';
let img_local
img.update(() => "")
img.subscribe((i) => {
img_local = i});
let old_short_name = data.recipe.short_name
export let card_data ={
icon: data.recipe.icon,
category: data.recipe.category,
name: data.recipe.name,
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,
cooking: data.recipe.cooking,
}
let images = data.recipe.images
let short_name = data.recipe.short_name
let datecreated = data.recipe.datecreated
let datemodified = new Date()
import type { PageData } from './$types';
import CardAdd from '$lib/components/CardAdd.svelte';
import CreateIngredientList from '$lib/components/CreateIngredientList.svelte';
export let ingredients = data.recipe.ingredients
import CreateStepList from '$lib/components/CreateStepList.svelte';
export let instructions = data.recipe.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 doDelete(){
const response = confirm("Bist du dir sicher, dass du das Rezept löschen willst?")
if(!response){
return
}
const res_img = await fetch('/api/rezepte/img/delete', {
method: 'POST',
body: JSON.stringify({
name: old_short_name,
}),
headers : {
'content-type': 'application/json',
credentials: 'include',
}
})
if(!res_img.ok){
const item = await res_img.json();
//alert(item.message)
return
}
return
const res = await fetch('/api/rezepte/delete', {
method: 'POST',
body: JSON.stringify({
old_short_name,
headers: {
'content-type': 'application/json',
}
})
})
if(res.ok){
const url = location.href.split('/')
url.splice(url.length -2, 2);
location.assign(url.join('/'))
}
else{
const item = await res.json();
// alert(item.message)
}
}
async function doEdit() {
// two cases:
//new image uploaded (not implemented yet)
// new short_name -> move images as well
// if new image
console.log("img_local", img_local)
if(img_local != ""){
async function delete_img(){
const res = await fetch('/api/rezepte/img/delete', {
method: 'POST',
body: JSON.stringify({
name: old_short_name,
}),
headers : {
'content-type': 'application/json',
credentials: 'include',
}
})
if(!res.ok){
const item = await res.json();
// alert(item.message)
}
}
async function upload_img(){
const data = {
image: img_local,
name: short_name,
}
const res = await fetch(`/api/rezepte/img/add`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
credentials: 'include',
},
body: JSON.stringify(data)
});
if(!res.ok){
const item = await res.json();
// alert(item.message)
}
}
delete_img()
upload_img()
}
// case new short_name:
else if(short_name != old_short_name){
console.log("MOVING")
const res_img = await fetch('/api/rezepte/img/mv', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
credentials: 'include',
},
body: JSON.stringify({
old_name: old_short_name,
new_name: short_name,
})
})
if(!res_img.ok){
const item = await res_img.json();
//alert(item.message)
return
}
}
const res = await fetch('/api/rezepte/edit', {
method: 'POST',
body: JSON.stringify({
recipe: {
...card_data,
...add_info,
images, // TODO
season: season_local,
short_name,
datecreated,
portions: portions_local,
datemodified,
instructions,
ingredients,
addendum,
preamble,
note,
},
old_short_name,
headers: {
'content-type': 'application/json',
credentials: 'include',
}
})
})
if(res.ok){
const url = location.href.split('/');
url.splice(url.length -2, 2);
url.push(short_name);
location.assign(url.join('/'))
}
else{
const item = await res.json()
//alert(item.message)
}
}
</script>
<style>
input{
display: block;
border: unset;
margin: 1rem auto;
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;
}
}
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;
}
@media (prefers-color-scheme: dark){
.title{
background-color: var(--nord6-dark);
}
}
.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;
}
button.action_button{
animation: unset !important;
font-size: 1.3rem;
color: white;
}
.submit_buttons{
display: flex;
margin-inline: auto;
max-width: 1000px;
margin-block: 1rem;
justify-content: center;
align-items: center;
gap: 2rem;
}
.submit_buttons p{
padding: 0;
padding-right: 0.5em;
margin: 0;
}
@media (prefers-color-scheme: dark){
:global(body){
background-color: var(--background-dark);
}
}
</style>
<h1>Rezept editieren</h1>
<CardAdd {card_data} {image_preview_url} ></CardAdd>
<h3>Kurzname (für URL):</h3>
<input bind:value={short_name} placeholder="Kurzname"/>
<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>
<EditRecipeNote><p contenteditable bind:innerText={note}></p></EditRecipeNote>
</div>
</div>
</div>
<div class=list_wrapper>
<div>
<CreateIngredientList {ingredients}></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_buttons>
<button class=action_button on:click={doDelete}><p>Löschen</p><Cross fill=white width=2rem height=2rem></Cross></button>
<button class=action_button on:click={doEdit}><p>Speichern</p><Check fill=white width=2rem height=2rem></Check></button>
</div>

View File

@ -0,0 +1,79 @@
<script lang="ts">
import type { PageData } from './$types';
import '$lib/components/nordtheme.css';
import Recipes from '$lib/components/Recipes.svelte';
import MediaScroller from '$lib/components/MediaScroller.svelte';
import SeasonLayout from '$lib/components/SeasonLayout.svelte'
import Card from '$lib/components/Card.svelte';
import Search from '$lib/components/Search.svelte';
export let data: PageData;
</script>
<style>
a{
--padding: 0.5em;
font-size: 3rem;
text-decoration: none;
padding: var(--padding);
background-color: var(--nord4);
border-radius: 1000px;
box-shadow: 0em 0em 0.5em 0.2em rgba(0, 0, 0, 0.2);
text-align: center;
--width: calc(1.2em + var(--padding) * 2);
width: var(--width);
line-height: calc(var(--width) - 2*var(--padding));
height: var(--width);
}
a:hover,
a:focus-visible
{
--angle: 15deg;
animation: shake 0.5s ease forwards;
}
.flex{
display:flex;
flex-wrap:wrap;
gap: 1rem;
max-width: 500px;
justify-content: center;
margin:4rem auto;
}
@keyframes shake{
0%{
transform: rotate(0)
scale(1,1);
}
25%{
box-shadow: 0em 0em 0.6em 0.3em rgba(0, 0, 0, 0.2);
transform: rotate(var(--angle))
scale(1.2,1.2)
;
}
50%{
box-shadow: 0em 0em 0.6em 0.3em rgba(0, 0, 0, 0.2);
transform: rotate(calc(-1* var(--angle)))
scale(1.2,1.2);
}
74%{
box-shadow: 0em 0em 0.6em 0.3em rgba(0, 0, 0, 0.2);
transform: rotate(var(--angle))
scale(1.2, 1.2);
}
100%{
transform: rotate(0)
scale(1.2,1.2);
box-shadow: 0em 0em 0.6em 0.3em rgba(0, 0, 0, 0.2);
}
}
</style>
<div class=flex>
{#each data.icons as icon}
<a href="/rezepte/icon/{icon}">{icon}</a>
{/each}
</div>

View File

@ -0,0 +1,10 @@
import type { PageLoad } from "./$types";
export async function load({ fetch }) {
let current_month = new Date().getMonth() + 1
const res_icons = await fetch(`/api/rezepte/items/icon`);
const item = await res_icons.json();
return {
icons: item,
};
};

View File

@ -0,0 +1,17 @@
<script lang="ts">
import type { PageData } from './$types';
import Recipes from '$lib/components/Recipes.svelte';
import IconLayout from '$lib/components/IconLayout.svelte';
import MediaScroller from '$lib/components/MediaScroller.svelte';
import Card from '$lib/components/Card.svelte';
import Search from '$lib/components/Search.svelte';
export let data: PageData;
import { rand_array } from '$lib/js/randomize';
</script>
<IconLayout icons={data.icons} active_icon={data.icon} >
<Recipes slot=recipes>
{#each rand_array(data.season) as recipe}
<Card {recipe} icon_override=true></Card>
{/each}
</Recipes>
</IconLayout>

View File

@ -0,0 +1,13 @@
import type { PageLoad } from "./$types";
export async function load({ fetch, params }) {
const res_season = await fetch(`/api/rezepte/items/icon/` + params.icon);
const res_icons = await fetch(`/api/rezepte/items/icon`);
const icons = await res_icons.json();
const item_season = await res_season.json();
return {
icons: icons,
icon: params.icon,
season: item_season,
};
};

View File

@ -0,0 +1,20 @@
<script lang="ts">
import type { PageData } from './$types';
import '$lib/components/nordtheme.css';
import Recipes from '$lib/components/Recipes.svelte';
import MediaScroller from '$lib/components/MediaScroller.svelte';
import SeasonLayout from '$lib/components/SeasonLayout.svelte'
import Card from '$lib/components/Card.svelte';
import Search from '$lib/components/Search.svelte';
export let data: PageData;
export let current_month = new Date().getMonth() + 1
import { rand_array } from '$lib/js/randomize';
</script>
<SeasonLayout active_index={current_month-1}>
<Recipes slot=recipes>
{#each rand_array(data.season) as recipe}
<Card {recipe} {current_month}></Card>
{/each}
</Recipes>
</SeasonLayout>

View File

@ -0,0 +1,10 @@
import type { PageLoad } from "./$types";
export async function load({ fetch }) {
let current_month = new Date().getMonth() + 1
const res_season = await fetch(`/api/rezepte/items/in_season/` + current_month);
const item_season = await res_season.json();
return {
season: item_season,
};
};

View File

@ -0,0 +1,18 @@
<script lang="ts">
import type { PageData } from './$types';
import Recipes from '$lib/components/Recipes.svelte';
import SeasonLayout from '$lib/components/SeasonLayout.svelte';
import MediaScroller from '$lib/components/MediaScroller.svelte';
import Card from '$lib/components/Card.svelte';
import Search from '$lib/components/Search.svelte';
export let data: PageData;
let months = ["Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember"]
import { rand_array } from '$lib/js/randomize';
</script>
<SeasonLayout active_index={data.month -1}>
<Recipes slot=recipes>
{#each rand_array(data.season) as recipe}
<Card {recipe} icon_override=true></Card>
{/each}
</Recipes>
</SeasonLayout>

View File

@ -0,0 +1,12 @@
import type { PageLoad } from "./$types";
export async function load({ fetch, params }) {
const res_season = await fetch(`/api/rezepte/items/in_season/` + params.month);
const res_all_brief = await fetch(`/api/rezepte/items/all_brief`);
const item_season = await res_season.json();
const item_all_brief = await res_all_brief.json();
return {
month: params.month,
season: item_season,
};
};

View File

@ -0,0 +1,22 @@
<script lang="ts">
import type { PageData } from './$types';
export let data: PageData;
import "$lib/css/nordtheme.css";
import TagCloud from '$lib/components/TagCloud.svelte';
import TagBall from '$lib/components/TagBall.svelte';
</script>
<style>
h1 {
font-size: 3rem;
text-align: center;
}
</style>
<h1>Stichwörter</h1>
<section>
<TagCloud>
{#each data.tags as tag}
<TagBall {tag} ref="/rezepte/tag">
</TagBall>
{/each}
</TagCloud>
</section>

View File

@ -0,0 +1,7 @@
import type { PageLoad } from "./$types";
export async function load({ fetch}) {
const res = await fetch(`/api/rezepte/items/tag`);
const tags = await res.json();
return {tags}
};

View File

@ -0,0 +1,24 @@
<script lang="ts">
import type { PageData } from './$types';
import Recipes from '$lib/components/Recipes.svelte';
export let data: PageData;
export let current_month = new Date().getMonth() + 1;
import Card from '$lib/components/Card.svelte'
import Search from '$lib/components/Search.svelte';
import { rand_array } from '$lib/js/randomize';
</script>
<style>
h1 {
text-align: center;
font-size: 2em;
}
</style>
<h1>Rezepte mit Stichwort <q>{data.tag}</q>:</h1>
<Search></Search>
<section>
<Recipes>
{#each rand_array(data.recipes) as recipe}
<Card {recipe} {current_month}></Card>
{/each}
</Recipes>
</section>

View File

@ -0,0 +1,10 @@
import type { PageLoad } from "./$types";
export async function load({ fetch, params }) {
const res_tag = await fetch(`/api/rezepte/items/tag/${params.tag}`);
const items_tag = await res_tag.json();
return {
tag: params.tag,
recipes: items_tag
}
};