Added Main
This commit is contained in:
23
frontend/.gitignore
vendored
Normal file
23
frontend/.gitignore
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
node_modules
|
||||
|
||||
# Output
|
||||
.output
|
||||
.vercel
|
||||
.netlify
|
||||
.wrangler
|
||||
/.svelte-kit
|
||||
/build
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Env
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
!.env.test
|
||||
|
||||
# Vite
|
||||
vite.config.js.timestamp-*
|
||||
vite.config.ts.timestamp-*
|
||||
1
frontend/.npmrc
Normal file
1
frontend/.npmrc
Normal file
@@ -0,0 +1 @@
|
||||
engine-strict=true
|
||||
38
frontend/README.md
Normal file
38
frontend/README.md
Normal file
@@ -0,0 +1,38 @@
|
||||
# sv
|
||||
|
||||
Everything you need to build a Svelte project, powered by [`sv`](https://github.com/sveltejs/cli).
|
||||
|
||||
## Creating a project
|
||||
|
||||
If you're seeing this, you've probably already done this step. Congrats!
|
||||
|
||||
```bash
|
||||
# create a new project in the current directory
|
||||
npx sv create
|
||||
|
||||
# create a new project in my-app
|
||||
npx sv create my-app
|
||||
```
|
||||
|
||||
## Developing
|
||||
|
||||
Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
|
||||
# or start the server and open the app in a new browser tab
|
||||
npm run dev -- --open
|
||||
```
|
||||
|
||||
## Building
|
||||
|
||||
To create a production version of your app:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
You can preview the production build with `npm run preview`.
|
||||
|
||||
> To deploy your app, you may need to install an [adapter](https://svelte.dev/docs/kit/adapters) for your target environment.
|
||||
13
frontend/jsconfig.json
Normal file
13
frontend/jsconfig.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"extends": "./.svelte-kit/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"checkJs": false,
|
||||
"moduleResolution": "bundler"
|
||||
}
|
||||
// Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias
|
||||
// except $lib which is handled by https://svelte.dev/docs/kit/configuration#files
|
||||
//
|
||||
// If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
|
||||
// from the referenced tsconfig.json - TypeScript does not merge them in
|
||||
}
|
||||
1531
frontend/package-lock.json
generated
Normal file
1531
frontend/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
22
frontend/package.json
Normal file
22
frontend/package.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "frontend",
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sveltejs/adapter-auto": "^4.0.0",
|
||||
"@sveltejs/adapter-node": "^5.2.12",
|
||||
"@sveltejs/kit": "^2.0.0",
|
||||
"@sveltejs/vite-plugin-svelte": "^4.0.0",
|
||||
"svelte": "^5.0.0",
|
||||
"vite": "^5.4.11"
|
||||
},
|
||||
"dependencies": {
|
||||
"@picocss/pico": "^2.0.6"
|
||||
}
|
||||
}
|
||||
7
frontend/src/app.css
Normal file
7
frontend/src/app.css
Normal file
@@ -0,0 +1,7 @@
|
||||
.title{
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
textarea{
|
||||
height: 200px;
|
||||
}
|
||||
17
frontend/src/app.html
Normal file
17
frontend/src/app.html
Normal file
@@ -0,0 +1,17 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css">
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>NoteApp</title>
|
||||
%sveltekit.head%
|
||||
</head>
|
||||
|
||||
<body data-sveltekit-preload-data="hover">
|
||||
<div style="display: contents">%sveltekit.body%</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
51
frontend/src/lib/components/Note.svelte
Normal file
51
frontend/src/lib/components/Note.svelte
Normal file
@@ -0,0 +1,51 @@
|
||||
<script>
|
||||
import { goto } from "$app/navigation";
|
||||
import TimeAgo from "./TimeAgo.svelte";
|
||||
|
||||
export let data;
|
||||
|
||||
function gotoEdit(){
|
||||
goto(`editNote/${data.id}`);
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||
<div class="note" on:click={gotoEdit}>
|
||||
<h2>{data.title}</h2>
|
||||
<p>{data.content}</p>
|
||||
<TimeAgo timestamp={data.createdOn}></TimeAgo>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.note {
|
||||
background-color: rgba(0, 0, 0, 0.432);
|
||||
width: 400px;
|
||||
padding: 10px;
|
||||
border-radius: 25px;
|
||||
border: 2px solid rgba(255, 255, 255, 0.432);
|
||||
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
|
||||
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.note:hover {
|
||||
transform: scale(1.05);
|
||||
box-shadow: 0 0 16px rgba(255, 255, 255, 0.2);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.note h2 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.note p {
|
||||
text-align: left;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
</style>
|
||||
41
frontend/src/lib/components/TimeAgo.svelte
Normal file
41
frontend/src/lib/components/TimeAgo.svelte
Normal file
@@ -0,0 +1,41 @@
|
||||
<script>
|
||||
// Aufräumen beim Verlassen der Komponente
|
||||
import { onDestroy } from 'svelte';
|
||||
|
||||
export let timestamp;
|
||||
|
||||
let formattedTime;
|
||||
|
||||
// Funktion zur Berechnung von "Erstellt vor ..."
|
||||
function timeAgo(inputTime) {
|
||||
const date = new Date(inputTime);
|
||||
const now = new Date();
|
||||
const diff = Math.floor((now - date) / 1000); // Unterschied in Sekunden
|
||||
|
||||
if (diff < 60) return `Erstellt vor ${diff} Sekunden`;
|
||||
if (diff < 3600) return `Erstellt vor ${Math.floor(diff / 60)} Minuten`;
|
||||
if (diff < 86400) return `Erstellt vor ${Math.floor(diff / 3600)} Stunden`;
|
||||
if (diff < 2592000) return `Erstellt vor ${Math.floor(diff / 86400)} Tagen`;
|
||||
if (diff < 31536000) return `Erstellt vor ${Math.floor(diff / 2592000)} Monaten`;
|
||||
return `Erstellt vor ${Math.floor(diff / 31536000)} Jahren`;
|
||||
}
|
||||
|
||||
// Aktualisiere den Zeitstempel alle 1 Sekunde
|
||||
const interval = setInterval(() => {
|
||||
formattedTime = timeAgo(timestamp);
|
||||
}, 10000);
|
||||
|
||||
// Berechne sofort die aktuelle Zeit
|
||||
$: formattedTime = timeAgo(timestamp);
|
||||
|
||||
onDestroy(() => clearInterval(interval));
|
||||
</script>
|
||||
|
||||
<span class="timeStamp">{formattedTime}</span>
|
||||
|
||||
<style>
|
||||
.timeStamp{
|
||||
font-size: 15px;
|
||||
color: #b4b4b4; /* Optional: für eine schönere Darstellung */
|
||||
}
|
||||
</style>
|
||||
1
frontend/src/lib/index.js
Normal file
1
frontend/src/lib/index.js
Normal file
@@ -0,0 +1 @@
|
||||
// place files you want to import through the `$lib` alias in this folder.
|
||||
21
frontend/src/routes/+error.svelte
Normal file
21
frontend/src/routes/+error.svelte
Normal file
@@ -0,0 +1,21 @@
|
||||
<script>
|
||||
import { page } from "$app/state";
|
||||
</script>
|
||||
|
||||
<div class="container">
|
||||
<h1>{page.status}</h1>
|
||||
<h2>{page.error.message}</h2>
|
||||
<h3>Konkatiere uns, sollte der Fehler weiter Bestehen!</h3>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.container{
|
||||
text-align: center;
|
||||
height: 80vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
padding: -1000px;
|
||||
}
|
||||
</style>
|
||||
30
frontend/src/routes/+layout.svelte
Normal file
30
frontend/src/routes/+layout.svelte
Normal file
@@ -0,0 +1,30 @@
|
||||
<script>
|
||||
import "../app.css";
|
||||
</script>
|
||||
|
||||
<div id="header">
|
||||
<nav>
|
||||
<ul>
|
||||
<li id="appName"><strong>NoteApp</strong></li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li><a href="/">Startseite</a></li>
|
||||
<li><a href="/createNote">Neue Notiz</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<slot></slot>
|
||||
|
||||
<style>
|
||||
#header{
|
||||
padding: 15px;
|
||||
padding-bottom: 10px;
|
||||
padding-top: 10px;
|
||||
width: 99.14vw;
|
||||
}
|
||||
|
||||
#appName{
|
||||
font-size: 30px;
|
||||
}
|
||||
</style>
|
||||
19
frontend/src/routes/+page.js
Normal file
19
frontend/src/routes/+page.js
Normal file
@@ -0,0 +1,19 @@
|
||||
/** @type {import('./$types').LayoutServerLoad} */
|
||||
export async function load({ fetch }) {
|
||||
const response = await fetch("http://localhost:5165/api/note", {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
});
|
||||
|
||||
if(!response.ok){
|
||||
throw new Error(`Es gab einen Fehler beim Laden der Notizen: ${response.statusText}`)
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
return {
|
||||
notes: data
|
||||
};
|
||||
}
|
||||
34
frontend/src/routes/+page.svelte
Normal file
34
frontend/src/routes/+page.svelte
Normal file
@@ -0,0 +1,34 @@
|
||||
<script>
|
||||
import Note from "$lib/components/Note.svelte";
|
||||
export let data;
|
||||
let notes = data.notes;
|
||||
|
||||
let notesSorted = notes.sort(
|
||||
(a, b) => new Date(b.createdOn) - new Date(a.createdOn),
|
||||
);
|
||||
</script>
|
||||
|
||||
<div id="notes">
|
||||
{#if notes.length === 0}
|
||||
<h1 id="noNoteMsg">Es wurden noch keine Notizen geschrieben :(</h1>
|
||||
{/if}
|
||||
{#each notesSorted as note}
|
||||
<Note data={note}></Note>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
#notes {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 15px;
|
||||
flex: 1 1 auto;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
#noNoteMsg{
|
||||
position: absolute;
|
||||
top: 48%;
|
||||
}
|
||||
</style>
|
||||
28
frontend/src/routes/createNote/+page.server.js
Normal file
28
frontend/src/routes/createNote/+page.server.js
Normal file
@@ -0,0 +1,28 @@
|
||||
import { goto } from '$app/navigation';
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
|
||||
/** @satisfies {import('./$types').Actions} */
|
||||
export const actions = {
|
||||
create: async ({request}) => {
|
||||
const formData = await request.formData();
|
||||
const title = formData.get('title');
|
||||
const content = formData.get('content');
|
||||
|
||||
const response = await fetch('http://localhost:5165/api/note', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ title, content }),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.json();
|
||||
return { success: false, error: error.message || 'Failed to create note.' };
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
return goto("/")
|
||||
}
|
||||
}
|
||||
51
frontend/src/routes/createNote/+page.svelte
Normal file
51
frontend/src/routes/createNote/+page.svelte
Normal file
@@ -0,0 +1,51 @@
|
||||
<script>
|
||||
import { goto } from "$app/navigation";
|
||||
import { error } from "@sveltejs/kit";
|
||||
|
||||
let formData = { title: "", content: "" };
|
||||
|
||||
async function submitNote(event) {
|
||||
const payload = {
|
||||
title: formData.title,
|
||||
content: formData.content,
|
||||
};
|
||||
|
||||
const response = await fetch("http://localhost:5165/api/note", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
goto("/");
|
||||
} else {
|
||||
error(500, "Bad Request");
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="container">
|
||||
<h1 class="title">Erstelle eine Neue Notiz</h1>
|
||||
|
||||
<form on:submit|preventDefault={submitNote}>
|
||||
<fieldset>
|
||||
<label>
|
||||
Titel
|
||||
<input name="title" placeholder="Titel" bind:value={formData.title} />
|
||||
</label>
|
||||
<label>
|
||||
Inhalt
|
||||
<textarea
|
||||
name="content"
|
||||
placeholder="Inhalt"
|
||||
bind:value={formData.content}
|
||||
>
|
||||
</textarea>
|
||||
</label>
|
||||
</fieldset>
|
||||
|
||||
<input type="submit" value="Erstellen" />
|
||||
</form>
|
||||
</div>
|
||||
16
frontend/src/routes/editNote/[id]/+page.js
Normal file
16
frontend/src/routes/editNote/[id]/+page.js
Normal file
@@ -0,0 +1,16 @@
|
||||
import { error } from '@sveltejs/kit';
|
||||
|
||||
/** @type {import('./$types').PageLoad} */
|
||||
export async function load({ params, fetch }) {
|
||||
const request = await fetch(`http://localhost:5165/api/note/${params.id}`)
|
||||
|
||||
const note = await request.json();
|
||||
|
||||
if(request.status != 200){
|
||||
error(request.status, note.title);
|
||||
}
|
||||
|
||||
return {
|
||||
note
|
||||
}
|
||||
}
|
||||
94
frontend/src/routes/editNote/[id]/+page.svelte
Normal file
94
frontend/src/routes/editNote/[id]/+page.svelte
Normal file
@@ -0,0 +1,94 @@
|
||||
<script>
|
||||
import { goto } from "$app/navigation";
|
||||
import TimeAgo from "$lib/components/TimeAgo.svelte";
|
||||
import { error } from "@sveltejs/kit";
|
||||
|
||||
export let data;
|
||||
const note = data.note;
|
||||
|
||||
let formData = { title: note.title, content: note.content };
|
||||
|
||||
async function changeNote() {
|
||||
const payload = {
|
||||
title: formData.title,
|
||||
content: formData.content,
|
||||
};
|
||||
|
||||
const response = await fetch(`http://localhost:5165/api/note/${note.id}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
console.log(response.status)
|
||||
if (response.ok) {
|
||||
goto("/");
|
||||
} else {
|
||||
error(500, "Bad Request");
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteNote(){
|
||||
const response = await fetch(`http://localhost:5165/api/note/${note.id}`, {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
goto("/");
|
||||
} else {
|
||||
error(500, "Bad Request");
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="container">
|
||||
<h1 class="title">Ändere Notiz: {note.title}</h1>
|
||||
|
||||
<form on:submit|preventDefault={changeNote}>
|
||||
<fieldset>
|
||||
<label>
|
||||
Titel
|
||||
<input
|
||||
name="title"
|
||||
placeholder="Titel"
|
||||
bind:value={formData.title}
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
Inhalt
|
||||
<textarea
|
||||
name="content"
|
||||
placeholder="Inhalt"
|
||||
bind:value={formData.content}
|
||||
>
|
||||
</textarea>
|
||||
</label>
|
||||
</fieldset>
|
||||
|
||||
<input type="submit" value="Ändern" />
|
||||
</form>
|
||||
|
||||
<div class="button-container">
|
||||
<button class="secondary" on:click={deleteNote}>Notiz Löschen</button>
|
||||
</div>
|
||||
<br>
|
||||
<TimeAgo timestamp={note.createdOn}></TimeAgo>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.button-container {
|
||||
display: flex;
|
||||
justify-content: center; /* Centers the button horizontally */
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
button{
|
||||
width: 100%;
|
||||
background-color: rgb(194, 43, 43);
|
||||
border-color: rgba(255, 255, 255, 0);
|
||||
}
|
||||
</style>
|
||||
BIN
frontend/static/favicon.png
Normal file
BIN
frontend/static/favicon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.7 KiB |
13
frontend/svelte.config.js
Normal file
13
frontend/svelte.config.js
Normal file
@@ -0,0 +1,13 @@
|
||||
import adapter from '@sveltejs/adapter-node';
|
||||
|
||||
/** @type {import('@sveltejs/kit').Config} */
|
||||
const config = {
|
||||
kit: {
|
||||
// adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list.
|
||||
// If your environment is not supported, or you settled on a specific environment, switch out the adapter.
|
||||
// See https://svelte.dev/docs/kit/adapters for more information about adapters.
|
||||
adapter: adapter()
|
||||
}
|
||||
};
|
||||
|
||||
export default config;
|
||||
6
frontend/vite.config.js
Normal file
6
frontend/vite.config.js
Normal file
@@ -0,0 +1,6 @@
|
||||
import { sveltekit } from '@sveltejs/kit/vite';
|
||||
import { defineConfig } from 'vite';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [sveltekit()]
|
||||
});
|
||||
Reference in New Issue
Block a user