39 Commits

Author SHA1 Message Date
danielvici123
94c14226dc Only minor adjustments 2025-04-09 09:31:20 +02:00
danielvici123
0f9284d515 Merge branch 'main' of https://github.com/danielvici/esp-projekt 2025-04-06 21:07:59 +02:00
danielvici123
4be4a26036 suche geht jetzt, special sachen für user 99 (danielvici gemacht) und in einstellungen werden daten angezeigt 2025-04-06 21:07:57 +02:00
deno-deploy[bot]
ed70ace33d [Deno Deploy] Update .github/workflows/deploy.yml 2025-04-04 06:49:22 +00:00
deno-deploy[bot]
dc29170a58 [Deno Deploy] Update .github/workflows/deploy.yml 2025-04-04 06:34:15 +00:00
deno-deploy[bot]
c2f56b2b5d [Deno Deploy] Update .github/workflows/deploy.yml 2025-04-04 06:25:06 +00:00
deno-deploy[bot]
86bc0347ca [Deno Deploy] Update .github/workflows/deploy.yml 2025-04-04 06:05:15 +00:00
deno-deploy[bot]
21c77cd34a [Deno Deploy] Add .github/workflows/deploy.yml 2025-04-04 06:04:01 +00:00
Lynixenn
8f27246234 Fixed Database creation, The error was that there were 2 simultaenous Database connections, causing an error when both attempted to connect at once. 2025-03-30 22:59:19 +02:00
Lynixenn
fb916e9e9f Merged API and Databank into Main branch 2025-03-30 22:02:56 +02:00
Esad Mustafoski
3cd92d7fff Update deno.json 2025-03-29 00:36:27 +01:00
danielvici123
915e91fbd1 +responsiv, +profile site, 2025-03-28 23:06:37 +01:00
danielvici123
9a8237de42 etwas responsible gemacht, posten und commentieren geht profile machen 2025-03-26 22:43:39 +01:00
danielvici123
5a5cf84197 ui responsive gemacht 2025-03-26 15:51:17 +01:00
danielvici123
7832267081 testphase posten 2025-03-25 22:23:57 +01:00
danielvici123
96f9071edd liken von posts geht (halb)
im feed gehs aber sonst nicht weil nach dem aktualisieren  geht es weg
2025-03-24 23:05:28 +01:00
danielvici123
f1b8dfe92f comments in posts sieht mann jetzt, link zum post copyn geht und kleine änderungen 2025-03-24 20:28:47 +01:00
danielvici123
834149ffb9 posts ansehen, posts im detial, scrollbar entfernt
scrollbar:  - nurnoch in bestimmten elementen
2025-03-23 21:20:43 +01:00
danielvici123
c2d5cc6a73 Delete api directory
verursacht eventuell probleme
2025-03-22 16:11:49 +01:00
danielvici123
2d29b9a3a4 Delete database directory
verursacht eventuelle probleme
2025-03-22 16:11:22 +01:00
danielvici123
a5fa8d840c Update profile_main.vue
problem gefixt das massive probleme verruscacht hat
2025-03-22 16:10:45 +01:00
danielvici123
932edc5a2b kleine änderung/anpassungen, login zwang, messages hinzugefügt, 2025-03-22 12:51:56 +01:00
danielvici123
7331b1b9b0 link zu icons8 hizugefügt 2025-03-12 15:24:13 +01:00
danielvici123
c7365703df quick chat entfernt, profil seiten hinzugefügt, chatt (contacts) verändert 2025-03-12 14:11:25 +01:00
danielvici123
4411c7def6 Navigationsbar angepasst und post view hinzugefügt (geht nicht wegen keine datenbank aanschluss) 2025-02-18 18:33:40 +01:00
danielvici123
eca851dc08 kleine veränderung und popup (bei kontakte) sowie testphase stay logged in 2025-02-18 17:48:10 +01:00
danielvici123
9af5b34658 suche geht jetzt 2025-02-06 17:46:18 +01:00
danielvici123
bb2cfedb84 suche (angefangen) und kleine änderungen
kleine veränderungen
suche anfangen -> filter und suche ansich geht nicht
2025-02-05 20:35:29 +01:00
danielvici123
8b4b99a015 Register gemacht. Kleine Änderung
Kleine Änderung:
- In Login:
    - Logo RICHTIG gefärbt
    - Remember me (keine Funktion) hinzufegüt
    - Weiterleitung auf register
Register:
Registrierung seite erstellt und registrieren möglich gemacht + API anbindung
2024-12-22 20:08:47 +01:00
danielvici123
1d8665d6ce small changes+ dokcer file added (NOT WORKING)
small changes
dockerfile added but its not working
2024-12-21 11:12:43 +01:00
danielvici123
aa690f0989 Merge branch 'main' of https://github.com/danielvici/esp-projekt 2024-12-18 21:29:14 +01:00
danielvici123
3683be8202 login updated
Mann kann jetzt text in den input rein schreiben und das wird dann an di api geschickt. (mit bugs)
2024-12-18 21:29:11 +01:00
danielvici123
d0182e7d5b small changes, login first version
small changes:
- Title changed -> ESP - Express, Share, Post
- Icon changed -> Logo
login:
connection to api :)
2024-12-17 22:24:23 +01:00
danielvici123
5bf7d62f71 Update README.md 2024-12-14 20:04:59 +01:00
danielvici123
f478198ff7 Merge branch 'main' of https://github.com/danielvici/esp-projekt 2024-12-14 19:56:11 +01:00
danielvici123
dc79759d38 Notifications gemacht. Settings, Login verändert
Nachrichten:
- Nachrichten anzeigen mit filter usw.
Login:
- Password ist sind jetzt punkte
Settings, WIP:
- Kleine Stylische anpassungen
2024-12-14 19:55:10 +01:00
danielvici123
17a23a992e kleine veränderungen
navbar
wip site
2024-11-14 10:12:58 +01:00
danielvici123
2352c25c05 Settings added (only one part), notification started and highlight added 2024-11-10 18:10:28 +01:00
danielvici123
860cd027cd some comments added 2024-11-05 11:14:24 +01:00
57 changed files with 2997 additions and 743 deletions

View File

@@ -1,9 +1,9 @@
name: Deploy
on:
push:
branches: api-dev
branches: main
pull_request:
branches: api-dev
branches: main
jobs:
deploy:
@@ -35,7 +35,7 @@ jobs:
uses: denoland/deployctl@v1
with:
project: "esp-projekt"
entrypoint: "api/main.ts"
entrypoint: "src/main.ts"
root: ""

2
.gitignore vendored
View File

@@ -13,8 +13,6 @@ dist-ssr
.vite
*.local
*.sqlite
*.idea
.env
# Editor directories and files
.vscode/*

View File

@@ -10,19 +10,22 @@ ESP is a simple social media platform built with [Deno](https://deno.land/), [Vi
We're building ESP to avoid the clutter of other Social Media websites.
It's build to be simple, fast and easy to use, all while keeping a Minimal feeling to it.
It's built to be simple, fast and easy to use, all while keeping a Minimal feeling to it.
---
# How do I run it?
The only dependency is Deno! Nothing else is required, Deno will install all the Modules needed and start it.
## Running
Dev server:
API + Website:
```bash
$ deno task dev
```
API only:
```bash
$ deno task dev:api
@@ -46,28 +49,22 @@ $ deno task build
# Resources
## Frameworks used
<ul>
<li><a href="https://deno.land/"><img src="https://upload.wikimedia.org/wikipedia/commons/thumb/8/84/Deno.svg/600px-Deno.svg.png" width="20" height="20"> Deno</a></li>
<li><a href="https://vitejs.dev/"><img src="https://vitejs.dev/logo.svg" width="20" height="20"> Vite</a></li>
<li><a href="https://www.typescriptlang.org/"><img src="https://upload.wikimedia.org/wikipedia/commons/thumb/4/4c/Typescript_logo_2020.svg/512px-Typescript_logo_2020.svg.png?20221110153201" width="20" height="20"> TypeScript</a></li>
<li><a href="https://tailwindcss.com/"><img src="https://tailwindcss.com/_next/static/media/tailwindcss-mark.3c5441fc7a190fb1800d4a5c7f07ba4b1345a9c8.svg" width="20" height="20"> TailwindCSS</a></li>
<li><a href="https://vuejs.org/"><img src="https://upload.wikimedia.org/wikipedia/commons/9/95/Vue.js_Logo_2.svg" width="20" height="20">VueJs</a></li>
<li><a href="https://www.sqlite.org/"><img src="https://www.vectorlogo.zone/logos/sqlite/sqlite-icon.svg" height="20", width="20">SQLite</a></li>
## Libs/Frameworks used
<ul>
<li><a href="https://deno.land/"><img src="https://upload.wikimedia.org/wikipedia/commons/thumb/8/84/Deno.svg/600px-Deno.svg.png" width="20" height="20"> Deno</a></li>
<li><a href="https://vitejs.dev/"><img src="https://vitejs.dev/logo.svg" width="20" height="20"> Vite</a></li>
<li><a href="https://www.typescriptlang.org/"><img src="https://upload.wikimedia.org/wikipedia/commons/thumb/4/4c/Typescript_logo_2020.svg/512px-Typescript_logo_2020.svg.png?20221110153201" width="20" height="20"> TypeScript</a></li>
<li><a href="https://oakserver.github.io/oak/"><img src="https://oakserver.org/oak_logo.svg?__frsh_c=2e345d91800d1e0a52aa35efcaab769fa1768888" width="20" height="20"> Oak</a></li>
<li><a href="https://www.sqlite.org/"><img src="https://upload.wikimedia.org/wikipedia/commons/thumb/3/38/SQLite370.svg/220px-SQLite370.svg.png" width="20" height="20"> SQLite</a></li>
<li><a href="https://tailwindcss.com/"><img src="https://tailwindcss.com/_next/static/media/tailwindcss-mark.3c5441fc7a190fb1800d4a5c7f07ba4b1345a9c8.svg" width="20" height="20"> TailwindCSS</a></li>
<li><a href="https://vuejs.org/"><img src="https://upload.wikimedia.org/wikipedia/commons/9/95/Vue.js_Logo_2.svg" width="20" height="20">Vue</a></li>
</ul>
## Tools used
<ul>
<li><a href="https://code.visualstudio.com/"><img src="https://upload.wikimedia.org/wikipedia/commons/9/9a/Visual_Studio_Code_1.35_icon.svg" width="20" height="20"> Visual Studio Code</a></li>
<li><a href="https://www.jetbrains.com/webstorm/"><img src="https://upload.wikimedia.org/wikipedia/commons/c/c0/WebStorm_Icon.svg" width="20" height="20"> JetBrains WebStorm</a></li>
<li><a href="https://www.jetbrains.com/datagrip/"><img src="https://seeklogo.com/images/D/datagrip-logo-295CA63255-seeklogo.com.png" width="20" height="20"> JetBrains DataGrip</a></li>
<li><a href="https://neovim.io/"><img src="https://upload.wikimedia.org/wikipedia/commons/thumb/0/07/Neovim-mark-flat.svg/640px-Neovim-mark-flat.svg.png" width="20" height="20"> Neovim</a></li>
<li><a href="https://code.visualstudio.com/"><img src="https://code.visualstudio.com/assets/images/code-stable.png" width="20" height="20"> Visual Studio Code</a></li>
</ul>
---
### Honorable mentions
<ul>
<li><a href="https://oakserver.github.io/oak/"><img src="https://oakserver.org/oak_logo.svg?__frsh_c=2e345d91800d1e0a52aa35efcaab769fa1768888" width="20" height="20"> Oak</a></li>
</ul>

View File

@@ -55,7 +55,7 @@ type ApiResponse = {
};
// database creation if missing, runs here because this is the main file executed by the API.
db_utils.ensureDatabaseExists();
await db_utils.ensureDatabaseExists();
// +++ ROUTER ------------------------------------------------------- //
// Creates the routes for the API server.

View File

@@ -7,18 +7,10 @@
// +++ IMPORTS ------------------------------------------------------ //
import { DB } from "https://deno.land/x/sqlite@v3.9.1/mod.ts";
import {
dirname,
fromFileUrl,
join,
} from "https://deno.land/std@0.224.0/path/mod.ts";
// +++ VARIABLES ---------------------------------------------------- //
const _dirname: string = dirname(fromFileUrl(import.meta.url));
const dbPath: string = join(_dirname, "../database/esp-projekt.sqlite");
const db = new DB(dbPath);
export function createDatabase(): void {
export function createDatabase(db: DB): void {
db.execute(`
CREATE TABLE IF NOT EXISTS accounts (
user_id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -72,12 +64,17 @@ export function createDatabase(): void {
chat_name TEXT,
participants TEXT,
created_at TEXT
)
);
CREATE TABLE IF NOT EXISTS marker (
id INTEGER PRIMARY KEY AUTOINCREMENT
);
`);
}
// Sample data generated using AI, does not work yet and will be adjusted
export function insertSampleData(): void {
export function insertSampleData(db: DB): void {
db.query(
`INSERT INTO accounts (user_group, bio, displayname, username, user_email, password, password_salt, firstname, surname, account_created, blocked_users, followers, following, contacts) VALUES
('admin', 'Admin bio', 'Admin User', 'admin', 'admin@example.com', 'pw1', 'salt1', 'Admin', 'User', '2024-01-01', '[]', '[]', '[]', '[]'),

BIN
database/sqlite3.exe Normal file

Binary file not shown.

View File

@@ -77,48 +77,53 @@ export function queryDatabase<T>(
// +++ DATABASE INITIALIZATION -------------------------------------- //
export async function ensureDatabaseExists(): Promise<void> {
const dbDir = dirname(dbPath);
let dbInstance: DB | null = null; // Avoids hard to decode errors because it Throws one if it cant continue.
try {
// Check if the database directory exists, create it if not
const dbDir = dirname(dbPath);
try {
await Deno.stat(dbDir);
} catch (error) {
if (error instanceof Deno.errors.NotFound) {
// Create the database directory
await Deno.mkdir(dbDir, { recursive: true });
if (error instanceof Deno.errors.NotFound) { // Deno.errors.NotFound is a type of error that is thrown when a file or directory is not found.
// In short, It's a type, and it makes sure that the "error" variable is of type Deno.errors.NotFound.
console.log(`Created database directory: ${dbDir}`);
} else {
throw error;
}
}
console.log(`Opening database connection: ${dbPath}`);
dbInstance = new DB(dbPath);
// Check if the database file exists
try {
await Deno.stat(dbPath);
console.log("Database file already exists");
dbInstance.query("SELECT 1 FROM marker LIMIT 1;");
console.log("Database already initialized (marker table found).");
} catch (error) {
if (error instanceof Deno.errors.NotFound) {
createDatabaseIfNotExist();
insertSamples();
// Nothing, file will be created below
if (error instanceof Error) {
console.log(
"Marker table not found or query failed. Initializing database tables.",
);
db_create.createDatabase(dbInstance);
db_create.insertSampleData(dbInstance);
console.log("Database initialization complete.");
} else {
throw error;
}
}
} catch (error) {
console.error("Error ensuring database exists:", error);
throw error;
console.error(
"Error during database existence check or initialization:",
error,
);
} finally {
if (dbInstance) {
dbInstance.close();
console.log("Database connection closed.");
} else {
console.log("Database connection was not opened.");
}
}
}
export function createDatabaseIfNotExist(): void {
db_create.createDatabase();
}
export function insertSamples(): void {
db_create.insertSampleData();
}
// +++ ACCOUNT FUNCTIONS -------------------------------------------- //
export const getAllUsersFromDB = () => getAllUsersFromDBInternal(db);
export const getUserByUsername = (username: string) =>

346
deno.lock generated
View File

@@ -23,12 +23,17 @@
"npm:autoprefixer@^10.4.20": "10.4.20_postcss@8.4.47",
"npm:path-to-regexp@6.2.1": "6.2.1",
"npm:postcss@^8.4.47": "8.4.47",
"npm:tailwind-scrollbar-hide@2": "2.0.0_tailwindcss@3.4.14__postcss@8.4.47",
"npm:tailwind-scrollbar@^4.0.1": "4.0.1_tailwindcss@4.0.15",
"npm:tailwindcss@*": "3.4.14_postcss@8.4.47",
"npm:tailwindcss@^3.4.14": "3.4.14_postcss@8.4.47",
"npm:typescript@^5.8.2": "5.8.2",
"npm:url@~0.11.4": "0.11.4",
"npm:vite-plugin-checker@~0.9.1": "0.9.1_vite@5.4.10_@types+node@22.5.4",
"npm:vite@*": "5.4.10",
"npm:vite@^5.4.10": "5.4.10",
"npm:vue-router@4": "4.4.5_vue@3.5.12",
"npm:vue-router@^4.5.0": "4.5.0_vue@3.5.12_typescript@5.8.2",
"npm:vue@*": "3.5.12",
"npm:vue@^3.5.12": "3.5.12"
},
@@ -79,6 +84,14 @@
"@alloc/quick-lru@5.2.0": {
"integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="
},
"@babel/code-frame@7.26.2": {
"integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==",
"dependencies": [
"@babel/helper-validator-identifier",
"js-tokens",
"picocolors"
]
},
"@babel/helper-string-parser@7.25.9": {
"integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA=="
},
@@ -101,7 +114,7 @@
"@deno/vite-plugin@1.0.0_vite@5.4.10": {
"integrity": "sha512-Q9UeWqs3s7B5lqzu1Z5QrzYAzqTj3+F9YW17tWobGRbT2G40ihwis6zK/+QgMgcG4fm3IqdIfXmpQYhkZpdMfw==",
"dependencies": [
"vite"
"vite@5.4.10"
]
},
"@esbuild/aix-ppc64@0.21.5": {
@@ -291,11 +304,14 @@
"undici-types"
]
},
"@types/prismjs@1.26.5": {
"integrity": "sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ=="
},
"@vitejs/plugin-vue@5.1.4_vite@5.4.10_vue@3.5.12": {
"integrity": "sha512-N2XSI2n3sQqp5w7Y/AN/L2XDjBIRGqXko+eDp42sydYSBeJuSm5a1sLf8zakmo8u7tA8NmBgoDLA1HeOESjp9A==",
"dependencies": [
"vite",
"vue"
"vite@5.4.10",
"vue@3.5.12"
]
},
"@vue/compiler-core@3.5.12": {
@@ -366,7 +382,15 @@
"dependencies": [
"@vue/compiler-ssr",
"@vue/shared",
"vue"
"vue@3.5.12"
]
},
"@vue/server-renderer@3.5.12_vue@3.5.12_vue@3.5.12__typescript@5.8.2": {
"integrity": "sha512-I3QoeDDeEPZm8yR28JtY+rk880Oqmj43hreIBVTicisFTx/Dl7JpG72g/X7YF8hnQD3IFhkky5i2bPonwrTVPg==",
"dependencies": [
"@vue/compiler-ssr",
"@vue/shared",
"vue@3.5.12_typescript@5.8.2"
]
},
"@vue/shared@3.5.12": {
@@ -394,7 +418,7 @@
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
"dependencies": [
"normalize-path",
"picomatch"
"picomatch@2.3.1"
]
},
"arg@5.0.2": {
@@ -465,9 +489,18 @@
"is-binary-path",
"is-glob",
"normalize-path",
"readdirp"
"readdirp@3.6.0"
]
},
"chokidar@4.0.3": {
"integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
"dependencies": [
"readdirp@4.1.1"
]
},
"clsx@2.1.1": {
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="
},
"color-convert@2.0.1": {
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dependencies": [
@@ -483,7 +516,7 @@
"cross-spawn@7.0.3": {
"integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
"dependencies": [
"path-key",
"path-key@3.1.1",
"shebang-command",
"which"
]
@@ -582,6 +615,12 @@
"reusify"
]
},
"fdir@6.4.3_picomatch@4.0.2": {
"integrity": "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==",
"dependencies": [
"picomatch@4.0.2"
]
},
"fill-range@7.1.1": {
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
"dependencies": [
@@ -701,6 +740,9 @@
"jiti@1.21.6": {
"integrity": "sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w=="
},
"js-tokens@4.0.0": {
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
},
"lilconfig@2.1.0": {
"integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ=="
},
@@ -726,7 +768,7 @@
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
"dependencies": [
"braces",
"picomatch"
"picomatch@2.3.1"
]
},
"minimatch@9.0.5": {
@@ -758,6 +800,13 @@
"normalize-range@0.1.2": {
"integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA=="
},
"npm-run-path@6.0.0": {
"integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==",
"dependencies": [
"path-key@4.0.0",
"unicorn-magic"
]
},
"object-assign@4.1.1": {
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="
},
@@ -773,6 +822,9 @@
"path-key@3.1.1": {
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="
},
"path-key@4.0.0": {
"integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="
},
"path-parse@1.0.7": {
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
},
@@ -792,6 +844,9 @@
"picomatch@2.3.1": {
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="
},
"picomatch@4.0.2": {
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg=="
},
"pify@2.3.0": {
"integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog=="
},
@@ -847,6 +902,14 @@
"source-map-js"
]
},
"prism-react-renderer@2.4.1_react@19.0.0": {
"integrity": "sha512-ey8Ls/+Di31eqzUxC46h8MksNuGx/n0AAC8uKpwFau4RPDYLuE3EXTp8N8G2vX2N7UC/+IXeNUnlWBGGcAG+Ig==",
"dependencies": [
"@types/prismjs",
"clsx",
"react"
]
},
"punycode@1.4.1": {
"integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ=="
},
@@ -859,6 +922,9 @@
"queue-microtask@1.2.3": {
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="
},
"react@19.0.0": {
"integrity": "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ=="
},
"read-cache@1.0.0": {
"integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==",
"dependencies": [
@@ -868,9 +934,12 @@
"readdirp@3.6.0": {
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
"dependencies": [
"picomatch"
"picomatch@2.3.1"
]
},
"readdirp@4.1.1": {
"integrity": "sha512-h80JrZu/MHUZCyHu5ciuoI0+WxsCxzxJTILn6Fs8rxSnFPh+UVHYfeIxK1nVGugMqkfC4vJcBOYbkfkwYK0+gw=="
},
"resolve@1.22.8": {
"integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
"dependencies": [
@@ -991,12 +1060,25 @@
"supports-preserve-symlinks-flag@1.0.0": {
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="
},
"tailwind-scrollbar-hide@2.0.0_tailwindcss@3.4.14__postcss@8.4.47": {
"integrity": "sha512-lqiIutHliEiODwBRHy4G2+Tcayo2U7+3+4frBmoMETD72qtah+XhOk5XcPzC1nJvXhXUdfl2ajlMhUc2qC6CIg==",
"dependencies": [
"tailwindcss@3.4.14_postcss@8.4.47"
]
},
"tailwind-scrollbar@4.0.1_tailwindcss@4.0.15": {
"integrity": "sha512-j2ZfUI7p8xmSQdlqaCxEb4Mha8ErvWjDVyu2Ke4IstWprQ/6TmIz1GSLE62vsTlXwnMLYhuvbFbIFzaJGOGtMg==",
"dependencies": [
"prism-react-renderer",
"tailwindcss@4.0.15"
]
},
"tailwindcss@3.4.14_postcss@8.4.47": {
"integrity": "sha512-IcSvOcTRcUtQQ7ILQL5quRDg7Xs93PdJEk1ZLbhhvJc7uj/OAhYOnruEiwnGgBvUtaUAJ8/mhSw1o8L2jCiENA==",
"dependencies": [
"@alloc/quick-lru",
"arg",
"chokidar",
"chokidar@3.6.0",
"didyoumean",
"dlv",
"fast-glob",
@@ -1018,6 +1100,9 @@
"sucrase"
]
},
"tailwindcss@4.0.15": {
"integrity": "sha512-6ZMg+hHdMJpjpeCCFasX7K+U615U9D+7k5/cDK/iRwl6GptF24+I/AbKgOnXhVKePzrEyIXutLv36n4cRsq3Sg=="
},
"thenify-all@1.6.0": {
"integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==",
"dependencies": [
@@ -1030,6 +1115,16 @@
"any-promise"
]
},
"tiny-invariant@1.3.3": {
"integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="
},
"tinyglobby@0.2.12_picomatch@4.0.2": {
"integrity": "sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==",
"dependencies": [
"fdir",
"picomatch@4.0.2"
]
},
"to-regex-range@5.0.1": {
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"dependencies": [
@@ -1039,9 +1134,15 @@
"ts-interface-checker@0.1.13": {
"integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="
},
"typescript@5.8.2": {
"integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ=="
},
"undici-types@6.19.8": {
"integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw=="
},
"unicorn-magic@0.3.0": {
"integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA=="
},
"update-browserslist-db@1.1.1_browserslist@4.24.2": {
"integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==",
"dependencies": [
@@ -1060,6 +1161,21 @@
"util-deprecate@1.0.2": {
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
},
"vite-plugin-checker@0.9.1_vite@5.4.10_@types+node@22.5.4": {
"integrity": "sha512-neH3CSNWdkZ+zi+WPt/0y5+IO2I0UAI0NX6MaXqU/KxN1Lz6np/7IooRB6VVAMBa4nigqm1GRF6qNa4+EL5jDQ==",
"dependencies": [
"@babel/code-frame",
"chokidar@4.0.3",
"npm-run-path",
"picocolors",
"picomatch@4.0.2",
"strip-ansi@7.1.0",
"tiny-invariant",
"tinyglobby",
"vite@5.4.10_@types+node@22.5.4",
"vscode-uri"
]
},
"vite@5.4.10": {
"integrity": "sha512-1hvaPshuPUtxeQ0hsVH3Mud0ZanOLwVTneA1EgbAM5LhaZEqyPWGRQ7BtaMvUrTDeEaC8pxtj6a6jku3x4z6SQ==",
"dependencies": [
@@ -1069,11 +1185,31 @@
"rollup"
]
},
"vite@5.4.10_@types+node@22.5.4": {
"integrity": "sha512-1hvaPshuPUtxeQ0hsVH3Mud0ZanOLwVTneA1EgbAM5LhaZEqyPWGRQ7BtaMvUrTDeEaC8pxtj6a6jku3x4z6SQ==",
"dependencies": [
"@types/node",
"esbuild",
"fsevents",
"postcss",
"rollup"
]
},
"vscode-uri@3.1.0": {
"integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ=="
},
"vue-router@4.4.5_vue@3.5.12": {
"integrity": "sha512-4fKZygS8cH1yCyuabAXGUAsyi1b2/o/OKgu/RUb+znIYOxPRxdkytJEx+0wGcpBE1pX6vUgh5jwWOKRGvuA/7Q==",
"dependencies": [
"@vue/devtools-api",
"vue"
"vue@3.5.12"
]
},
"vue-router@4.5.0_vue@3.5.12_typescript@5.8.2": {
"integrity": "sha512-HDuk+PuH5monfNuY+ct49mNmkCRK4xJAV9Ts4z9UFc4rzdDnxQLyCMGGc8pKhZhHTVzfanpNwB/lwqevcBwI4w==",
"dependencies": [
"@vue/devtools-api",
"vue@3.5.12_typescript@5.8.2"
]
},
"vue@3.5.12": {
@@ -1082,10 +1218,21 @@
"@vue/compiler-dom",
"@vue/compiler-sfc",
"@vue/runtime-dom",
"@vue/server-renderer",
"@vue/server-renderer@3.5.12_vue@3.5.12",
"@vue/shared"
]
},
"vue@3.5.12_typescript@5.8.2": {
"integrity": "sha512-CLVZtXtn2ItBIi/zHZ0Sg1Xkb7+PU32bJJ8Bmy7ts3jxXTcbfsEfBivFYYWz1Hur+lalqGAh65Coin0r+HRUfg==",
"dependencies": [
"@vue/compiler-dom",
"@vue/compiler-sfc",
"@vue/runtime-dom",
"@vue/server-renderer@3.5.12_vue@3.5.12_vue@3.5.12__typescript@5.8.2",
"@vue/shared",
"typescript"
]
},
"which@2.0.2": {
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
"dependencies": [
@@ -1113,101 +1260,15 @@
}
},
"redirects": {
"https://deno.land/std/assert/mod.ts": "https://deno.land/std@0.224.0/assert/mod.ts",
"https://deno.land/std/path/mod.ts": "https://deno.land/std@0.224.0/path/mod.ts",
"https://deno.land/std/testing/asserts.ts": "https://deno.land/std@0.224.0/testing/asserts.ts",
"https://deno.land/x/cors/mod.ts": "https://deno.land/x/cors@v1.2.2/mod.ts",
"https://deno.land/x/oak/mod.ts": "https://deno.land/x/oak@v17.1.2/mod.ts",
"https://deno.land/x/sqlite/mod.ts": "https://deno.land/x/sqlite@v3.9.1/mod.ts",
"https://deno.land/x/superoak/mod.ts": "https://deno.land/x/superoak@4.8.1/mod.ts"
"https://deno.land/x/sqlite/mod.ts": "https://deno.land/x/sqlite@v3.9.1/mod.ts"
},
"remote": {
"https://deno.land/std@0.115.0/_wasm_crypto/crypto.js": "1c565287b35f6eb1aa58499d0f4fbac99a9c30eb9a735c512d193a6493499e84",
"https://deno.land/std@0.115.0/_wasm_crypto/crypto.wasm.js": "e93d38b215c05c552669e9565654599b13e7898ebd3f10ac91e39413efda4b84",
"https://deno.land/std@0.115.0/_wasm_crypto/mod.ts": "9afe300945fe7e5bcec231b52b0016d8442d026b823f619bb5939b2cf66ff21b",
"https://deno.land/std@0.115.0/crypto/mod.ts": "8e1ec0ff94a4f08e3c4f72de5b88781566481602614e62291aa7ae7444ba11f0",
"https://deno.land/std@0.115.0/encoding/base64.ts": "0b58bd6477214838bf711eef43eac21e47ba9e5c81b2ce185fe25d9ecab3ebb3",
"https://deno.land/std@0.115.1/async/deadline.ts": "1d6ac7aeaee22f75eb86e4e105d6161118aad7b41ae2dd14f4cfd3bf97472b93",
"https://deno.land/std@0.115.1/async/debounce.ts": "b2f693e4baa16b62793fd618de6c003b63228db50ecfe3bd51fc5f6dc0bc264b",
"https://deno.land/std@0.115.1/async/deferred.ts": "ab60d46ba561abb3b13c0c8085d05797a384b9f182935f051dc67136817acdee",
"https://deno.land/std@0.115.1/async/delay.ts": "f2d8ccaa8ebc26594bd8b0989edfd8a96257a714c1dee2fb54d986e5bdd840ac",
"https://deno.land/std@0.115.1/async/mod.ts": "78425176fabea7bd1046ce3819fd69ce40da85c83e0f174d17e8e224a91f7d10",
"https://deno.land/std@0.115.1/async/mux_async_iterator.ts": "62abff3af9ff619e8f2adc96fc70d4ca020fa48a50c23c13f12d02ed2b760dbe",
"https://deno.land/std@0.115.1/async/pool.ts": "353ce4f91865da203a097aa6f33de8966340c91b6f4a055611c8c5d534afd12f",
"https://deno.land/std@0.115.1/async/tee.ts": "3e9f2ef6b36e55188de16a667c702ace4ad0cf84e3720379160e062bf27348ad",
"https://deno.land/std@0.115.1/fmt/colors.ts": "8368ddf2d48dfe413ffd04cdbb7ae6a1009cf0dccc9c7ff1d76259d9c61a0621",
"https://deno.land/std@0.115.1/http/http_status.ts": "2ff185827bff21c7be2807fcb09a6a2166464ba57fcd94afe805abab8e09070a",
"https://deno.land/std@0.115.1/http/server.ts": "46f616eac1ca0ea7b9fce97102d185a3d97ae7d7d3bbd635b74cefe05ed1cb37",
"https://deno.land/std@0.115.1/testing/_diff.ts": "e6a10d2aca8d6c27a9c5b8a2dbbf64353874730af539707b5b39d4128140642d",
"https://deno.land/std@0.115.1/testing/asserts.ts": "a1fef0239a2c343b0baa49c77dcdd7412613c46f3aba2887c331a2d7ed1f645e",
"https://deno.land/std@0.213.0/assert/_constants.ts": "a271e8ef5a573f1df8e822a6eb9d09df064ad66a4390f21b3e31f820a38e0975",
"https://deno.land/std@0.213.0/assert/_diff.ts": "dcc63d94ca289aec80644030cf88ccbf7acaa6fbd7b0f22add93616b36593840",
"https://deno.land/std@0.213.0/assert/_format.ts": "0ba808961bf678437fb486b56405b6fefad2cf87b5809667c781ddee8c32aff4",
"https://deno.land/std@0.213.0/assert/assert.ts": "bec068b2fccdd434c138a555b19a2c2393b71dfaada02b7d568a01541e67cdc5",
"https://deno.land/std@0.213.0/assert/assert_almost_equals.ts": "8b96b7385cc117668b0720115eb6ee73d04c9bcb2f5d2344d674918c9113688f",
"https://deno.land/std@0.213.0/assert/assert_array_includes.ts": "1688d76317fd45b7e93ef9e2765f112fdf2b7c9821016cdfb380b9445374aed1",
"https://deno.land/std@0.213.0/assert/assert_equals.ts": "4497c56fe7d2993b0d447926702802fc0becb44e319079e8eca39b482ee01b4e",
"https://deno.land/std@0.213.0/assert/assert_exists.ts": "24a7bf965e634f909242cd09fbaf38bde6b791128ece08e33ab08586a7cc55c9",
"https://deno.land/std@0.213.0/assert/assert_false.ts": "6f382568e5128c0f855e5f7dbda8624c1ed9af4fcc33ef4a9afeeedcdce99769",
"https://deno.land/std@0.213.0/assert/assert_greater.ts": "4945cf5729f1a38874d7e589e0fe5cc5cd5abe5573ca2ddca9d3791aa891856c",
"https://deno.land/std@0.213.0/assert/assert_greater_or_equal.ts": "573ed8823283b8d94b7443eb69a849a3c369a8eb9666b2d1db50c33763a5d219",
"https://deno.land/std@0.213.0/assert/assert_instance_of.ts": "72dc1faff1e248692d873c89382fa1579dd7b53b56d52f37f9874a75b11ba444",
"https://deno.land/std@0.213.0/assert/assert_is_error.ts": "6596f2b5ba89ba2fe9b074f75e9318cda97a2381e59d476812e30077fbdb6ed2",
"https://deno.land/std@0.213.0/assert/assert_less.ts": "2b4b3fe7910f65f7be52212f19c3977ecb8ba5b2d6d0a296c83cde42920bb005",
"https://deno.land/std@0.213.0/assert/assert_less_or_equal.ts": "b93d212fe669fbde959e35b3437ac9a4468f2e6b77377e7b6ea2cfdd825d38a0",
"https://deno.land/std@0.213.0/assert/assert_match.ts": "ec2d9680ed3e7b9746ec57ec923a17eef6d476202f339ad91d22277d7f1d16e1",
"https://deno.land/std@0.213.0/assert/assert_not_equals.ts": "f3edda73043bc2c9fae6cbfaa957d5c69bbe76f5291a5b0466ed132c8789df4c",
"https://deno.land/std@0.213.0/assert/assert_not_instance_of.ts": "8f720d92d83775c40b2542a8d76c60c2d4aeddaf8713c8d11df8984af2604931",
"https://deno.land/std@0.213.0/assert/assert_not_match.ts": "b4b7c77f146963e2b673c1ce4846473703409eb93f5ab0eb60f6e6f8aeffe39f",
"https://deno.land/std@0.213.0/assert/assert_not_strict_equals.ts": "da0b8ab60a45d5a9371088378e5313f624799470c3b54c76e8b8abeec40a77be",
"https://deno.land/std@0.213.0/assert/assert_object_match.ts": "e85e5eef62a56ce364c3afdd27978ccab979288a3e772e6855c270a7b118fa49",
"https://deno.land/std@0.213.0/assert/assert_rejects.ts": "e9e0c8d9c3e164c7ac962c37b3be50577c5a2010db107ed272c4c1afb1269f54",
"https://deno.land/std@0.213.0/assert/assert_strict_equals.ts": "0425a98f70badccb151644c902384c12771a93e65f8ff610244b8147b03a2366",
"https://deno.land/std@0.213.0/assert/assert_string_includes.ts": "dfb072a890167146f8e5bdd6fde887ce4657098e9f71f12716ef37f35fb6f4a7",
"https://deno.land/std@0.213.0/assert/assert_throws.ts": "edddd86b39606c342164b49ad88dd39a26e72a26655e07545d172f164b617fa7",
"https://deno.land/std@0.213.0/assert/assertion_error.ts": "9f689a101ee586c4ce92f52fa7ddd362e86434ffdf1f848e45987dc7689976b8",
"https://deno.land/std@0.213.0/assert/equal.ts": "fae5e8a52a11d3ac694bbe1a53e13a7969e3f60791262312e91a3e741ae519e2",
"https://deno.land/std@0.213.0/assert/fail.ts": "f310e51992bac8e54f5fd8e44d098638434b2edb802383690e0d7a9be1979f1c",
"https://deno.land/std@0.213.0/assert/mod.ts": "325df8c0683ad83a873b9691aa66b812d6275fc9fec0b2d180ac68a2c5efed3b",
"https://deno.land/std@0.213.0/assert/unimplemented.ts": "47ca67d1c6dc53abd0bd729b71a31e0825fc452dbcd4fde4ca06789d5644e7fd",
"https://deno.land/std@0.213.0/assert/unreachable.ts": "38cfecb95d8b06906022d2f9474794fca4161a994f83354fd079cac9032b5145",
"https://deno.land/std@0.213.0/async/delay.ts": "8e1d18fe8b28ff95885e2bc54eccec1713f57f756053576d8228e6ca110793ad",
"https://deno.land/std@0.213.0/fmt/colors.ts": "aeaee795471b56fc62a3cb2e174ed33e91551b535f44677f6320336aabb54fbb",
"https://deno.land/std@0.213.0/http/server.ts": "6dce295abc169d0956ae00432441331b3425afad4d79e8b3475739be2f04d614",
"https://deno.land/std@0.213.0/http/status.ts": "ed61b4882af2514a81aefd3245e8df4c47b9a8e54929a903577643d2d1ebf514",
"https://deno.land/std@0.224.0/assert/_constants.ts": "a271e8ef5a573f1df8e822a6eb9d09df064ad66a4390f21b3e31f820a38e0975",
"https://deno.land/std@0.224.0/assert/assert.ts": "09d30564c09de846855b7b071e62b5974b001bb72a4b797958fe0660e7849834",
"https://deno.land/std@0.224.0/assert/assert_almost_equals.ts": "9e416114322012c9a21fa68e187637ce2d7df25bcbdbfd957cd639e65d3cf293",
"https://deno.land/std@0.224.0/assert/assert_array_includes.ts": "14c5094471bc8e4a7895fc6aa5a184300d8a1879606574cb1cd715ef36a4a3c7",
"https://deno.land/std@0.224.0/assert/assert_equals.ts": "3bbca947d85b9d374a108687b1a8ba3785a7850436b5a8930d81f34a32cb8c74",
"https://deno.land/std@0.224.0/assert/assert_exists.ts": "43420cf7f956748ae6ed1230646567b3593cb7a36c5a5327269279c870c5ddfd",
"https://deno.land/std@0.224.0/assert/assert_false.ts": "3e9be8e33275db00d952e9acb0cd29481a44fa0a4af6d37239ff58d79e8edeff",
"https://deno.land/std@0.224.0/assert/assert_greater.ts": "5e57b201fd51b64ced36c828e3dfd773412c1a6120c1a5a99066c9b261974e46",
"https://deno.land/std@0.224.0/assert/assert_greater_or_equal.ts": "9870030f997a08361b6f63400273c2fb1856f5db86c0c3852aab2a002e425c5b",
"https://deno.land/std@0.224.0/assert/assert_instance_of.ts": "e22343c1fdcacfaea8f37784ad782683ec1cf599ae9b1b618954e9c22f376f2c",
"https://deno.land/std@0.224.0/assert/assert_is_error.ts": "f856b3bc978a7aa6a601f3fec6603491ab6255118afa6baa84b04426dd3cc491",
"https://deno.land/std@0.224.0/assert/assert_less.ts": "60b61e13a1982865a72726a5fa86c24fad7eb27c3c08b13883fb68882b307f68",
"https://deno.land/std@0.224.0/assert/assert_less_or_equal.ts": "d2c84e17faba4afe085e6c9123a63395accf4f9e00150db899c46e67420e0ec3",
"https://deno.land/std@0.224.0/assert/assert_match.ts": "ace1710dd3b2811c391946954234b5da910c5665aed817943d086d4d4871a8b7",
"https://deno.land/std@0.224.0/assert/assert_not_equals.ts": "78d45dd46133d76ce624b2c6c09392f6110f0df9b73f911d20208a68dee2ef29",
"https://deno.land/std@0.224.0/assert/assert_not_instance_of.ts": "3434a669b4d20cdcc5359779301a0588f941ffdc2ad68803c31eabdb4890cf7a",
"https://deno.land/std@0.224.0/assert/assert_not_match.ts": "df30417240aa2d35b1ea44df7e541991348a063d9ee823430e0b58079a72242a",
"https://deno.land/std@0.224.0/assert/assert_not_strict_equals.ts": "37f73880bd672709373d6dc2c5f148691119bed161f3020fff3548a0496f71b8",
"https://deno.land/std@0.224.0/assert/assert_object_match.ts": "411450fd194fdaabc0089ae68f916b545a49d7b7e6d0026e84a54c9e7eed2693",
"https://deno.land/std@0.224.0/assert/assert_rejects.ts": "4bee1d6d565a5b623146a14668da8f9eb1f026a4f338bbf92b37e43e0aa53c31",
"https://deno.land/std@0.224.0/assert/assert_strict_equals.ts": "b4f45f0fd2e54d9029171876bd0b42dd9ed0efd8f853ab92a3f50127acfa54f5",
"https://deno.land/std@0.224.0/assert/assert_string_includes.ts": "496b9ecad84deab72c8718735373feb6cdaa071eb91a98206f6f3cb4285e71b8",
"https://deno.land/std@0.224.0/assert/assert_throws.ts": "c6508b2879d465898dab2798009299867e67c570d7d34c90a2d235e4553906eb",
"https://deno.land/std@0.224.0/assert/assertion_error.ts": "ba8752bd27ebc51f723702fac2f54d3e94447598f54264a6653d6413738a8917",
"https://deno.land/std@0.224.0/assert/equal.ts": "bddf07bb5fc718e10bb72d5dc2c36c1ce5a8bdd3b647069b6319e07af181ac47",
"https://deno.land/std@0.224.0/assert/fail.ts": "0eba674ffb47dff083f02ced76d5130460bff1a9a68c6514ebe0cdea4abadb68",
"https://deno.land/std@0.224.0/assert/mod.ts": "48b8cb8a619ea0b7958ad7ee9376500fe902284bb36f0e32c598c3dc34cbd6f3",
"https://deno.land/std@0.224.0/assert/unimplemented.ts": "8c55a5793e9147b4f1ef68cd66496b7d5ba7a9e7ca30c6da070c1a58da723d73",
"https://deno.land/std@0.224.0/assert/unreachable.ts": "5ae3dbf63ef988615b93eb08d395dda771c96546565f9e521ed86f6510c29e19",
"https://deno.land/std@0.224.0/fmt/colors.ts": "508563c0659dd7198ba4bbf87e97f654af3c34eb56ba790260f252ad8012e1c5",
"https://deno.land/std@0.224.0/internal/diff.ts": "6234a4b493ebe65dc67a18a0eb97ef683626a1166a1906232ce186ae9f65f4e6",
"https://deno.land/std@0.224.0/internal/format.ts": "0a98ee226fd3d43450245b1844b47003419d34d210fa989900861c79820d21c2",
"https://deno.land/std@0.224.0/internal/mod.ts": "534125398c8e7426183e12dc255bb635d94e06d0f93c60a297723abe69d3b22e",
"https://deno.land/std@0.224.0/fs/exists.ts": "3d38cb7dcbca3cf313be343a7b8af18a87bddb4b5ca1bd2314be12d06533b50f",
"https://deno.land/std@0.224.0/path/_common/assert_path.ts": "dbdd757a465b690b2cc72fc5fb7698c51507dec6bfafce4ca500c46b76ff7bd8",
"https://deno.land/std@0.224.0/path/_common/basename.ts": "569744855bc8445f3a56087fd2aed56bdad39da971a8d92b138c9913aecc5fa2",
"https://deno.land/std@0.224.0/path/_common/common.ts": "ef73c2860694775fe8ffcbcdd387f9f97c7a656febf0daa8c73b56f4d8a7bd4c",
@@ -1285,7 +1346,6 @@
"https://deno.land/std@0.224.0/path/windows/resolve.ts": "8dae1dadfed9d46ff46cc337c9525c0c7d959fb400a6308f34595c45bdca1972",
"https://deno.land/std@0.224.0/path/windows/to_file_url.ts": "40e560ee4854fe5a3d4d12976cef2f4e8914125c81b11f1108e127934ced502e",
"https://deno.land/std@0.224.0/path/windows/to_namespaced_path.ts": "4ffa4fb6fae321448d5fe810b3ca741d84df4d7897e61ee29be961a6aac89a4c",
"https://deno.land/std@0.224.0/testing/asserts.ts": "d0cdbabadc49cc4247a50732ee0df1403fdcd0f95360294ad448ae8c240f3f5c",
"https://deno.land/x/cors@v1.2.2/abcCors.ts": "cdf83a7eaa69a1bf3ab910d18b9422217902fac47601adcaf0afac5a61845d48",
"https://deno.land/x/cors@v1.2.2/attainCors.ts": "7d6aba0f942495cc31119604e0895c9bb8edd8f8baa7fe78e6c655bd0b4cbf59",
"https://deno.land/x/cors@v1.2.2/cors.ts": "0e2d9167e3685f9bcf48f565e312b6e1883fa458f7337e5ce7bc2e3b29767980",
@@ -1294,7 +1354,6 @@
"https://deno.land/x/cors@v1.2.2/oakCors.ts": "1348dc7673c61b85d2e80559a7b44f8e0246eaa6bcc6ec744fafe5d9b13b5c71",
"https://deno.land/x/cors@v1.2.2/opineCors.ts": "fb5790115c26b7061d84b8d6c17d258a1e241bcab75b0bc3ca1fdb2e57bc5072",
"https://deno.land/x/cors@v1.2.2/types.ts": "97546633ccc7f0df7a29bacba5d91dc6f61decdd1b65258300244dba905d34b8",
"https://deno.land/x/free_port@v1.2.0/mod.ts": "512646732aaea41fbfd1f210f3ae82660f38251777d189d290da331d0235a58e",
"https://deno.land/x/oak@v17.1.2/application.ts": "2bcc73b3f22a193554c9958f7080ea635db25594d25ff7944021fca5bf74adba",
"https://deno.land/x/oak@v17.1.2/body.ts": "0eb7ab9df44d1b79933463d596b5e1a4f0991c94cff591861e58a413bda3f3db",
"https://deno.land/x/oak@v17.1.2/context.ts": "345cfdaa5a2310558ee0863f2fba5f9ba648188412b16ce342c33266c085f5d3",
@@ -1324,10 +1383,6 @@
"https://deno.land/x/oak@v17.1.2/utils/resolve_path.ts": "aa39d54a003b38fee55f340a0cba3f93a7af85b8ddd5fbfb049a98fc0109b36d",
"https://deno.land/x/oak@v17.1.2/utils/streams.ts": "3da73b94681f8d27a82cc67df3f91090ec0bd6c3e9ab957af588d41ab585d923",
"https://deno.land/x/oak@v17.1.2/utils/type_guards.ts": "a8dbb5ab7424f0355b121537d2454f927e0ca9949262fb67ac4fbefbd5880313",
"https://deno.land/x/opine@1.9.1/src/methods.ts": "0481daecc6068d24e9e5391818baddf555ab803d39a465dcd259161f8bd8ee49",
"https://deno.land/x/opine@1.9.1/src/utils/mergeDescriptors.ts": "1fe498d4a1a8dcfd3570f9ca5e0647590d86d029b3c340bfcfdb57002851e41b",
"https://deno.land/x/opine@2.3.4/src/methods.ts": "0481daecc6068d24e9e5391818baddf555ab803d39a465dcd259161f8bd8ee49",
"https://deno.land/x/opine@2.3.4/src/utils/mergeDescriptors.ts": "1fe498d4a1a8dcfd3570f9ca5e0647590d86d029b3c340bfcfdb57002851e41b",
"https://deno.land/x/sqlite@v3.2.0/build/sqlite.js": "16fe819f3b40c0d2e100014ec922b7dcef32bc9a0c799a9ecd4f1ae104217c88",
"https://deno.land/x/sqlite@v3.2.0/build/vfs.js": "baff72655c0916c906327fe6703c6a47daa1346e55c2eaa2629bcd879a673c8d",
"https://deno.land/x/sqlite@v3.2.0/mod.ts": "0b3e066f61a149d5aa99a50e2c41c687eaa0713350d3e9bfbe4025173ec1c9a9",
@@ -1344,77 +1399,7 @@
"https://deno.land/x/sqlite@v3.9.1/src/error.ts": "f7a15cb00d7c3797da1aefee3cf86d23e0ae92e73f0ba3165496c3816ab9503a",
"https://deno.land/x/sqlite@v3.9.1/src/function.ts": "bc778cab7a6d771f690afa27264c524d22fcb96f1bb61959ade7922c15a4ab8d",
"https://deno.land/x/sqlite@v3.9.1/src/query.ts": "d58abda928f6582d77bad685ecf551b1be8a15e8e38403e293ec38522e030cad",
"https://deno.land/x/sqlite@v3.9.1/src/wasm.ts": "e79d0baa6e42423257fb3c7cc98091c54399254867e0f34a09b5bdef37bd9487",
"https://deno.land/x/superdeno@4.7.0/deps.ts": "af7a0bc4c15710e0d7eb3e36b8ebf0f97debe394062ddf952c9f2b8160c6a590",
"https://deno.land/x/superdeno@4.7.0/mod.ts": "fa91c501867a4302a4bc92d63cbf934fe5475ebb7bf58335338e001147263c87",
"https://deno.land/x/superdeno@4.7.0/src/close.ts": "3d8bb8c24ab62cf6ce7aa9bff8ed35c0fcba5c9e4d90d0bf93fe2868a97b25e5",
"https://deno.land/x/superdeno@4.7.0/src/superagent.ts": "8f60187f9278b154ef6bccf09a5ff7d45f81103ad0ce02d45518a6bbe63ce764",
"https://deno.land/x/superdeno@4.7.0/src/superdeno.ts": "e27b490f34e170c5f103503c722293cefc351b1ac767246dfcb95d204e4a6953",
"https://deno.land/x/superdeno@4.7.0/src/test.ts": "d14892194d6911ba40de898884a50831468f47631a09178aef8fb61aa00ec1f6",
"https://deno.land/x/superdeno@4.7.0/src/types.ts": "0fc4a0a1acf4c3acba3e3d956a9e8917ce32cd47dbb8e75ffadec21a43a8e52f",
"https://deno.land/x/superdeno@4.7.0/src/utils.ts": "458c1699f73e348745b9b4d081e005dbc12d050fd2899ebf4f919515823ba1c0",
"https://deno.land/x/superdeno@4.7.0/src/xhrSham.js": "2db048613ca2fa2aa4ca9df2074411f8403ad4d88a8fb1e87313cee40e2c4a0d",
"https://deno.land/x/superdeno@4.7.0/version.ts": "4a24eb54df1fa633019ba3a4cae82032dc13c0e16bd9813a3aad95e1d5ea91e5",
"https://deno.land/x/superdeno@4.9.0/deps.ts": "acb88a5969aae0bcc82e053cb433cd183a10cc656495caa634b6e22a79156c4e",
"https://deno.land/x/superdeno@4.9.0/mod.ts": "fa91c501867a4302a4bc92d63cbf934fe5475ebb7bf58335338e001147263c87",
"https://deno.land/x/superdeno@4.9.0/src/close.ts": "8bd4ab602ebbb048d06697d0c48c30be5f78ab9ad673850965e8014d78cca7a8",
"https://deno.land/x/superdeno@4.9.0/src/superagent.ts": "8f60187f9278b154ef6bccf09a5ff7d45f81103ad0ce02d45518a6bbe63ce764",
"https://deno.land/x/superdeno@4.9.0/src/superdeno.ts": "2e2cd4898961ac7688f0c2a4b210bf560a338f6601bd231d74bf8a0956880311",
"https://deno.land/x/superdeno@4.9.0/src/test.ts": "1ab3c8c98160af8c3b30e097809d5c57bdd38d7b42c703f3f170f8452ad06c0f",
"https://deno.land/x/superdeno@4.9.0/src/types.ts": "9a48cdfafad3cea2212e1be29cdd2055e7d3d467437c9048012797323335abbb",
"https://deno.land/x/superdeno@4.9.0/src/utils.ts": "09a2e65cc5cc2a261b885f0e66ee84e96e978181975a0728636d20e48b67bd89",
"https://deno.land/x/superdeno@4.9.0/src/xhrSham.js": "6a35aed77bbe98324fe3b4d7430463b7cd6d3b43445ffdccd1fc327dc59dd3c6",
"https://deno.land/x/superdeno@4.9.0/version.ts": "4f8ba8f2a6b201e8e96818d3ab5c43aef1db751523c4b79160500664b72f87de",
"https://deno.land/x/superoak@4.5.0/deps.ts": "17bc881921ae2d3829d6e1168f29374de287c4d460d5288e6b5a6134e7508e5b",
"https://deno.land/x/superoak@4.5.0/mod.ts": "6d4ea8a5a48c9007f2e947934889c06259d3ebb5569515bcb0432036a22449cd",
"https://deno.land/x/superoak@4.5.0/src/superoak.ts": "9c08a3211c4d1f7bb89e88fc3f242536fce654c157aa6db52d3c24f033bb3d28",
"https://deno.land/x/superoak@4.5.0/version.ts": "af59786fbab2ab31ea2aa97927e98d8778407938bad4b531805b6166c064e847",
"https://deno.land/x/superoak@4.8.1/deps.ts": "d716c0b36fdac6458f6984ce80f69d0b645c7e0ac8461024a40ead5ed3fcd08d",
"https://deno.land/x/superoak@4.8.1/mod.ts": "6d4ea8a5a48c9007f2e947934889c06259d3ebb5569515bcb0432036a22449cd",
"https://deno.land/x/superoak@4.8.1/src/superoak.ts": "9c08a3211c4d1f7bb89e88fc3f242536fce654c157aa6db52d3c24f033bb3d28",
"https://deno.land/x/superoak@4.8.1/version.ts": "b9b71ac3596ff0a6aaad2bf9df8a54fb2925abd526800879e261de9c693812bc",
"https://jspm.dev/npm:call-bind@1.0.5!cjs": "09f8399c727fc1e9d58fdafc0a729b45bf37b7ee0c11d9d0b39abe37ac42ccf5",
"https://jspm.dev/npm:call-bind@1.0.5/callBound!cjs": "55fa05e2b115eeaef9ff684e3df12de253e6644a40ad09b5722f3a9a8df8f645",
"https://jspm.dev/npm:call-bind@1/callBound!cjs": "9cf2ef160025d392767618c2f0cb72d32cf14caa3fbeb493c6df9bde9d7fca8d",
"https://jspm.dev/npm:component-emitter@1!cjs": "26c2994a5fcac1cd9156b00be96c5e2f006dd76338095a96006ac3a47c6c327d",
"https://jspm.dev/npm:component-emitter@1.3.0!cjs": "757cafefb0bf5639f3f90b2267a7d168e03631e731c2a79fca847b735695e196",
"https://jspm.dev/npm:define-data-property@1!cjs": "37b65cb06c826730306a5f766de69da37b96076c96ea11a47667e9429623f937",
"https://jspm.dev/npm:define-data-property@1.1.1!cjs": "4ac6fa4b9d7ba7ccc83ffa350c58112ee878a450a97375217f66508d5673c822",
"https://jspm.dev/npm:fast-safe-stringify@2!cjs": "d8dd0803af23f037ffb44c13e18333131af27ebe582f55fd498b6e3c8f6d5a9d",
"https://jspm.dev/npm:fast-safe-stringify@2.1.1!cjs": "8a14a2de8a07a719c74aa63ffa5ff635fc55e9ee5d5a79fbc2e087dc4aa1940e",
"https://jspm.dev/npm:function-bind@1!cjs": "73fbc50bf85e8a6ca150609e98c396301c1ae5a1603e50ce8c64e95f646e0ce0",
"https://jspm.dev/npm:function-bind@1.1.2!cjs": "bbb663bc4e50f400a8ca0de9e0bfdaaa7022695f86b2806a48dc1afc5b4195a8",
"https://jspm.dev/npm:function-bind@1.1.2/implementation!cjs": "ebdc0ec85854db19d7e21081b368891394f86e21c6d786273c327762cb46ea6a",
"https://jspm.dev/npm:get-intrinsic@1!cjs": "f6d9266edc586632e8f6d8d6c5ca28fb2c0d5ee9c9d9252df9aafd57eda9fcea",
"https://jspm.dev/npm:get-intrinsic@1.2.2!cjs": "723fcebc493a45d5af8ecb366020a6cc2ce9bd4759bad699c1172015cb193f65",
"https://jspm.dev/npm:gopd@1!cjs": "c220469947b77de2c5e4b115beda16397bf6133c5b873b8e24e85b902ee6dc82",
"https://jspm.dev/npm:gopd@1.0.1!cjs": "b38da4f4b49cfef31e3aa8d62fdd136cf0fe99a5df6c603a426f97248f3cf4ab",
"https://jspm.dev/npm:has-property-descriptors@1!cjs": "b1a828f75a22a5614b136dd3da1be98cc744a2cd6bfed9bd8c338a8d51a570d1",
"https://jspm.dev/npm:has-property-descriptors@1.0.1!cjs": "f8da64823507f597f3cb44a2f3576e350df72e1033ef5e7a5b30d771e81c0819",
"https://jspm.dev/npm:has-proto@1!cjs": "78a2914e5525d531426c5d69fd5aa23671ec359c6c527b9791327f60ad1b6682",
"https://jspm.dev/npm:has-proto@1.0.1!cjs": "0a9d605f1d310f859265780011d6343a7869cadf3a9e02fd6cc949c2924b528c",
"https://jspm.dev/npm:has-symbols@1!cjs": "48faf647d225b64fa235ccc3e5a848e72221b0230935e421066a5de39aa89c3a",
"https://jspm.dev/npm:has-symbols@1.0.3!cjs": "36965f84e4e0ea1abeddb6928d0719a2648e61ceb9825df185b40d05cddb64df",
"https://jspm.dev/npm:has-symbols@1.0.3/shams!cjs": "669673e1dc7691c0b397580760121d57f3a5c5101dd70be2e8dd7d2a044de2e9",
"https://jspm.dev/npm:hasown@2!cjs": "9a39af846b167cae93b7a40f1ba4c97255bb5b07a1481da853a29bb68d24e603",
"https://jspm.dev/npm:hasown@2.0.0!cjs": "f52fd2477e345530f759465a984023f23d8261c4a54970e619daf1da6a2e85f5",
"https://jspm.dev/npm:object-inspect@1!cjs": "dc197b471ed55ecf2eabeb8da9aaee277e97831e65192531432a4ec2346211d9",
"https://jspm.dev/npm:object-inspect@1.13.1!cjs": "cec116e5c2b7d6b75e178d2541d70475d716ad912e3d5599e5c2d97284a9cb3e",
"https://jspm.dev/npm:qs@6!cjs": "210de1e090ac836c2495c19dfea88fc74b49de1b308241f8c9490d27ab6e0195",
"https://jspm.dev/npm:qs@6.11.2!cjs": "5da52fff60f7b1a6b1c73cdea2d9fc5d5588fa6c551b2a0ea2a1ebbb2a5e559f",
"https://jspm.dev/npm:qs@6.11.2/_/e71c21de.js": "cfe49eb949fb7291803f1ed2f4c0a244b8fca3b6936f5082fc97581a0663e427",
"https://jspm.dev/npm:qs@6.11.2/lib/stringify!cjs": "35d39c5871af151efe9ccca8e4ebecbf0282f97287b5fad56ebac369f69c2581",
"https://jspm.dev/npm:set-function-length@1!cjs": "b4c766d874ba261ff0c11aa18a6bf4510ecf8da09a7219da83a62772e0bc1b41",
"https://jspm.dev/npm:set-function-length@1.1.1!cjs": "f52607660d1f50e19e645ab49e6a4adf27fa4ae909867ec9950e993c430e4ca1",
"https://jspm.dev/npm:side-channel@1!cjs": "a07dfe7165af0d7f916d089490c38839397abcd8b218e4566b270858c9a0ea04",
"https://jspm.dev/npm:side-channel@1.0.4!cjs": "db65b31b6f9e67d57f04e26d71eb5b376306f5a89ab46fae1278c3ffefb19663",
"https://jspm.dev/npm:superagent@6.1.0!cjs": "fcf1c0b17cb3ff899b59ae178fc4ab74ad3b592d7fa8b44b16394001758e3176",
"https://jspm.dev/npm:superagent@6.1.0/lib/agent-base!cjs": "cfe465965a55d80114d835143717413945d0bbc46355d0f7f8200a89902ed006",
"https://jspm.dev/npm:superagent@6.1.0/lib/is-object!cjs": "95f67ff49b42fd5e82114b9d54a4b3fe1ac98813aed7ceaf53d314983f59820a",
"https://jspm.dev/npm:superagent@6.1.0/lib/request-base!cjs": "e361c341aa75d7417c918bc8fb697d0ccf96101e039dd2f00e5e45c01c534caa",
"https://jspm.dev/npm:superagent@6.1.0/lib/response-base!cjs": "00ac549f34d73c2753caa798aa7eb781051179013e3418ff0868a1e1904a8913",
"https://jspm.dev/npm:superagent@6.1.0/lib/utils!cjs": "ea706523553983c96ef4ab2f191c61c53fb8b78ad8ff2472b48f1385e896c030",
"https://jspm.dev/superagent@6.1.0": "4b3082d71252c42abd3930d85d1f3c4b2e937e0fab2b5f1c9d19eac20dea89a9"
"https://deno.land/x/sqlite@v3.9.1/src/wasm.ts": "e79d0baa6e42423257fb3c7cc98091c54399254867e0f34a09b5bdef37bd9487"
},
"workspace": {
"dependencies": [
@@ -1430,7 +1415,12 @@
"packageJson": {
"dependencies": [
"npm:@deno/vite-plugin@1",
"npm:@vitejs/plugin-vue@^5.1.4"
"npm:@vitejs/plugin-vue@^5.1.4",
"npm:tailwind-scrollbar-hide@2",
"npm:tailwind-scrollbar@^4.0.1",
"npm:typescript@^5.8.2",
"npm:vite-plugin-checker@~0.9.1",
"npm:vue-router@^4.5.0"
]
}
}

View File

@@ -1,13 +1,13 @@
<!doctype html>
<!Doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<link rel="icon" type="image/png" href="src/assets/esp-logo_no_text.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + Vue + TS</title>
<title>ESP - Express, Share, Post</title>
<link rel="stylesheet" href="/src/assets/main.css" />
</head>
<body>
<body class="bg-hintergrund-farbe sm:overflow-hidden">
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>

991
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,13 @@
{
"dependencies": {
"@deno/vite-plugin": "^1.0.0",
"@vitejs/plugin-vue": "^5.1.4"
"@vitejs/plugin-vue": "^5.1.4",
"tailwind-scrollbar": "^4.0.1",
"tailwind-scrollbar-hide": "^2.0.0",
"vue-router": "^4.5.0"
},
"devDependencies": {
"typescript": "^5.8.2",
"vite-plugin-checker": "^0.9.1"
}
}

6
postcss.config.cjs Normal file
View File

@@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

View File

@@ -1,3 +1,3 @@
<template>
<template >
<router-view />
</template>

BIN
src/assets/icons/logout.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 408 B

BIN
src/assets/icons/other.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 574 B

BIN
src/assets/icons/send.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 587 B

BIN
src/assets/icons/shield.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 501 B

BIN
src/assets/icons/trash.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 271 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 340 B

BIN
src/assets/icons/x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 538 B

View File

@@ -1,3 +1,5 @@
@plugin 'tailwind-scrollbar';
@tailwind base;
@tailwind components;
@tailwind utilities;

View File

@@ -4,21 +4,18 @@ import Contacts from "./home_components/contacts.vue";
import Legal from "./home_components/legal.vue";
import Navigationbar from "./home_components/navigationbar.vue";
import Trending from "./home_components/trending.vue";
import QuickSearch from "./home_components/quick_search.vue";
</script>
<template>
<div id="main" class="bg-hintergrund-farbe flex">
<div id="left" class="border border-b-grau w-72">
<div id="main" class="sm:flex overflow-y-auto sm:h-full sm:scrollbar">
<div id="left" class="sm:w-72 min-w-72">
<navigationbar></navigationbar>
<trending></trending>
</div>
<div class="w-100p">
<div class="sm:w-100p w-screen">
<feed></feed>
</div>
<div class="w-1/4">
<quick-search></quick-search>
<div class="sm:w-1/4 w-screen">
<contacts></contacts>
<legal></legal>
</div>

View File

@@ -2,15 +2,21 @@
import Legal from "./home_components/legal.vue";
import Login_comp from "./login_components/login_comp.vue";
import router from "../router";
function route_home() {
router.push("/");
}
</script>
<template>
<div id="main" class="bg-hintergrund-farbe flex p-2 ">
<div class="items-center pr-96 pl-2 pt-5">
<img src="../assets/esp-logo_no_text.png" alt="" class="rounded-lg h-12 w-24 mx-auto">
<div id="main" class="bg-hintergrund-farbe sm:flex sm:p-2 sm:justify-between ">
<div class="pt-5 pl-2">
<img src="../assets/esp-logo_no_text.png" alt="" class=" rounded-lg h-12 w-24 hover:shadow-2xl hover:shadow-grau-dunkel" @click="route_home">
</div>
<login_comp></login_comp>
<div class="p-5 ">
<login_comp class="inset-0"></login_comp>
<div class="md:inset-y-0 sm:right-0 sm:max-w-36 text-center">
<legal></legal>
</div>
</div>

View File

@@ -1,38 +0,0 @@
<script setup lang="ts">
import { ref } from 'vue'
defineProps<{ msg: string }>()
const count = ref(0)
</script>
<template>
<h1>{{ msg }}</h1>
<div class="card">
<button type="button" @click="count++">count is {{ count }}</button>
<p>
Edit
<code>components/HelloWorld.vue</code> to test HMR
</p>
</div>
<p>
Check out
<a href="https://vuejs.org/guide/quick-start.html#local" target="_blank"
>create-vue</a
>, the official Vue + Vite starter
</p>
<p>
Install
<a href="https://github.com/johnsoncodehk/volar" target="_blank">Volar</a>
in your IDE for a better DX
</p>
<p class="read-the-docs">Click on the Vite and Vue logos to learn more</p>
</template>
<style scoped>
.read-the-docs {
color: #888;
}
</style>

View File

@@ -1,9 +1,21 @@
<script setup lang="ts">
const contacts =[
{display_name: "Linux Enjoyer", username: "lunix"},
{display_name: "XBOX", username: "Xbox"},
{display_name: "JETBrains", username: "Jetbrains"},
]
import { ref } from 'vue';
import Popup_chat from "./popup_chat.vue";
const contacts = [
{ display_name: "Linux Enjoyer", username: "lunix" },
{ display_name: "XBOX", username: "xbox" },
{ display_name: "JETBrains", username: "jetbrains" },
{ display_name: "klopupser13", username: "theopampa",}
];
const selectedContact = ref(null);
const showChatPopup = ref(false);
function openChat(contact) {
selectedContact.value = contact;
showChatPopup.value = true;
}
</script>
<template>
@@ -13,19 +25,19 @@ const contacts =[
</div>
<div> <!--CONTENT-->
<ul class="space-y-1">
<li v-for="(contact) in contacts" :key="contact" class="flex rounded-lg">
<li v-for="(contact) in contacts" :key="contact.username" @click="openChat(contact)" class="flex rounded-lg p-2 cursor-pointer">
<!--CONTACT-->
<img src="../../assets/default_pp.png" alt="" class="w-12 h-12 mr-2">
<img src="../../assets/default_pp.png" alt="" class="w-12 h-12 mr-2"> <!--PROFILBILD-->
<div class="">
<label class=" font-bold m-1 text-weiss">{{ contact.display_name }}</label><br>
<p class="text-base m-1 text-grau2 underline-offset-3">@{{ contact.username }}</p>
<label class="font-bold m-1 text-weiss">{{ contact.display_name }}</label><br> <!-- display name-->
<p class="text-base m-1 text-grau2 underline-offset-3">@{{ contact.username }}</p> <!-- username-->
</div>
</li>
</ul>
</div>
<popup_chat v-if="showChatPopup" :contact="selectedContact" @close="showChatPopup = false" />
</div>
</template>
<style scoped>
</style>

View File

@@ -1,105 +1,176 @@
<script setup lang="ts">
import { ref } from "vue";
// import {valueOf} from "tailwindcss";
const post = ref([
{id: 1,
profile_picture: "../../assets/default_pp.png",
author_display_name: "Linux Enjoyer",author_username: "lunix",
content:"I love Linux. My Favorite Linux Distro is ARCH LINUX.",
comments_count: 1, likes: 5},
{id: 2,
profile_picture: "../../assets/default_pp.png",
author_display_name: "XBOX",author_username: "Xbox",
content: "Call of Duty: Black Ops 6 is OUT NOW.",
comments_count: 500000, likes: 100000},
{id: 3,
profile_picture: "../../assets/default_pp.png",
author_display_name: "JETBrains",author_username: "Jetbrains",
content: "BLI BLA BLUB. Jetbrains is the best IDE." ,
comments_count: 5000, likes: 1000},
{id: 4,
profile_picture: "../../assets/default_pp.png",
author_display_name: "GITHUB", author_username: "GitHub",
content: "GitHub Copilot got an massive update. Check out the new features.",
comments_count: 1500000, likes: 500000},
{id: 5,
profile_picture: "../../assets/danielvici_pp.png",
author_display_name: "danielvici123", author_username: "danielvici",
content: "I created this WebApp with VUE3 and TailwindCSS. It was a lot of fun.",
comments_count: undefined, likes: 532844},
{id: 6,
profile_picture: "../../assets/default_pp.png",
author_display_name: "Microsoft", author_username: "Microsoft",
content: "Windows 11 24H2 is out now. Learn more here: https://www.microsoft.com",
comments_count: 500000, likes: 100000},
{id: 7,
profile_picture: "../../assets/default_pp.png",
author_display_name: "Apple", author_username: "Apple",
content: "The new iPhone 16 is out now. Everything you need to know: https://www.apple.com",
comments_count: 500000, likes: 100000},
])
import {onMounted, ref} from "vue";
import router from "../../router";
// PLACEHOLDER
const upc = ref([]); // user post computed
let post_create_text = "";
let self_id ;
const addLike = (index: number) => {
post.value[index].likes += 1;
console.log("New Like Amount: ", post.value[index].likes);
onMounted(async () => {
self_id = localStorage.getItem("self_id");
await createFeed();
});
}
function post_publish_func(text:string) {
console.log("Post: ", text);
if (text === undefined || text === "") {
console.log("Post is empty");
alert("Post is empty");
async function createFeed() {
try {
// posts und user holen und schauen ob sie richtig sidn
const post_response = await fetch('http://localhost:8000/api/posts', { method: 'GET' });
const postsDATA = await post_response.json();
const user_response = await fetch('http://localhost:8000/api/users', { method: 'GET' });
const usersDATA = await user_response.json();
if(post_response.status === 404 || user_response.status === 404) {
console.error("ERRROR");
await router.push('/');
return;
} else {
console.log("Post is not empty");
post.value.push({id: post.value.length + 1, profile_picture: "../../assets/danielvici_pp.png", author_display_name: "ADMIN", author_username: "esp_admin", content: text, comments_count: undefined, likes: 0});
}
// posts und user kombinieren
const combinedPosts = postsDATA.map(post => {
const user = usersDATA.find(user => user.user_id === post.user_id);
return {
post_id: post.posts_uuid,
post_text: post.post_text,
likes: post.likes,
comments: post.comments,
displayname: user ? user.displayname : 'Unknown',
username: user ? user.username : 'unknown_user',
user_id: post.user_id,
};
});
//upc.value = combinedPosts;
upc.value = combinedPosts.sort((a, b) => b.post_id - a.post_id);;
} catch (e) {
console.error("An error has occurred. Please try again later.");
console.error(e);
}
console.log(upc.value);
}
async function addLike(post_id: string | number, user_id: number, index: number) {
try {
console.log("UPC: ", upc.value);
console.log("post_id: ", post_id);
upc.value[index].likes++;
const response = await fetch(`http://localhost:8000/api/post/${post_id}/like`, {
method: 'POST',
headers: {'content-type': 'application/json'},
body: `{"userId":${user_id}}`,
});
console.log('Antwort-Status:', response.status);
if (!response.ok) {
const errorText = await response.text();
console.error('Server-Fehlertext:', errorText);
//upc.value[index].likes--;
throw new Error(`HTTP error! status: ${response.status}, text: ${errorText}`);
}
const data = await response.json();
return data;
} catch (error) {
console.error('Fehler beim Liken des Posts:', error);
throw error;
}
}
async function post_create(post_text: string) {
if (post_text === "") {
alert("Please write something before posting.");
return;
}
console.log(self_id);
console.log(post_text);
const response = await fetch(`http://localhost:8000/api/post/create`, {
method: 'POST',
headers: {'content-type': 'application/json'},
body: `{"userId":${self_id},"postText":"${post_create_text}","postType":"text"}`});
const data = await response.json();
if (response.ok) {
console.log(response.text);
await createFeed();
}
console.log(data);
console.log(post_text);
}
function gotoPost(post_id: string | number) {
localStorage.setItem("viewedpost", post_id.toString());
router.push(`/post/${post_id}`);
}
function copyLink(post_id: string | number) {
const tocopy = `http://localhost:5173/post/${post_id}`;
navigator.clipboard.writeText(tocopy);
alert("Copied to clipboard");
}
function gotoProfile(user_id: string | number) {
router.push(`/profile/${user_id}`);
}
</script>
<template>
<div class="border-x-2 border-x-grau2"> <!-- MAIN -->
<div class="sm:border-x sm:border-x-grau2"> <!-- MAIN -->
<div> <!-- FEED HEADER -->
<h2 class="align-middle p-6 text-xl font-bold text-grau2 border-b-grau2 border-b ">Feed</h2>
<h2 class="align-middle p-6 text-3xl text-weiss border-b-grau2 border-b-2 ">Feed</h2>
<!-- POSTING-->
<div class="flex border-2 border-b-grau2">
<img src="../../assets/danielvici_pp.png" alt="" class="p-2 rounded-full w-16 h-16">
<form>
<img v-if="self_id != '99' " src="../../assets/default_pp.png" alt="" class="rounded-full w-16 h-16">
<img v-else src="../../assets/danielvici_pp.png" alt="" class="rounded-full w-16 h-16">
<form class="w-full pr-5">
<!-- post_publish ist richtig aber wird falsch angezeigt. File Input geht nicht-->
<textarea v-model="post_publish" name="post_text" class="bg-hintergrund-farbe rounded-lg m-2 p-1 focus:outline-none text-grau2 w-200p resize-none" rows="3" placeholder="Write something..."></textarea>
<textarea v-model="post_create_text" name="post_text" class="bg-hintergrund-farbe rounded-lg m-2 p-1 focus:outline-none text-grau2 w-full resize-none" rows="3" placeholder="Write something..."></textarea>
<div class="">
<input class="text-weiss" type="file" accept=".png, .jpg, .jpeg">
<button id="post_publish" name="post_publishss" class="text-weiss p-1 m-2 rounded-lg py-3 px-5 bg-button-farbe" @click.prevent="post_publish_func(post_publish)" type="button">Post</button>
<button id="post_publish" name="post_publishss" class="text-weiss p-1 m-2 rounded-lg py-3 px-5 bg-button-farbe" @click.prevent="post_create(post_create_text)" type="button">Post</button>
</div>
</form>
</form>
</div>
</div>
<div> <!-- CONTENT -->
<div class="sm:overflow-y-auto sm:h-[650px] sm:scrollbar"> <!-- CONTENT -->
<ul>
<li v-for="(postitem, indexus) in post" :key="post" class="border-2 border-b-grau2 p-3 flex">
<li v-for="(postitem, indexus) in upc" :key="upc" class="border-2 border-b-grau2 p-3 flex">
<!-- POST -->
<img src="../../assets/default_pp.png" alt="" class="rounded-full w-16 h-16">
<img v-if="postitem.username != 'danielvici' " src="../../assets/default_pp.png" alt="" class="rounded-full w-16 h-16">
<img v-else src="../../assets/danielvici_pp.png" alt="" class="rounded-full w-16 h-16">
<div>
<div> <!-- POST HEADER -->
<label class="text-lg font-bold m-1 text-weiss">{{postitem.author_display_name}}</label>
<label class="text-base m-1 underline-offset-3 text-grau2">@{{ postitem.author_username }}</label>
<label class="text-lg font-bold m-1 text-weiss">{{postitem.displayname}}</label>
<label class="text-base m-1 text-grau2">@{{ postitem.username }}</label>
</div>
<div class="m-2"> <!-- POST CONTENT -->
<p class="text-sm m-1 text-weiss">{{ postitem.content }}</p>
<p class="text-sm m-1 text-weiss">{{ postitem.post_text }}</p>
</div>
<div class="flex"> <!-- POST FOOTER -->
<div class="flex"> <!-- Comments -->
<img src="../../assets/icons/comment.png" alt="" class="rounded-full align-middle">
<label class="text-sm m-1 text-weiss" v-if="postitem.comments_count != undefined">{{ postitem.comments_count }}</label>
<label class="text-sm m-1 text-weiss" v-else>Comments disabled</label>
</div>
<div class="sm:flex"><!-- POST FOOTER -->
<div class="flex">
<div class="flex"> <!-- Comments -->
<img src="../../assets/icons/comment.png" alt="" class="rounded-full align-middle">
<label class="text-sm m-1 text-weiss" v-if="postitem.comments != undefined">{{ postitem.comments }}</label>
<label class="text-sm m-1 text-weiss" v-else>Comments disabled</label>
</div>
<div class="flex items-center" @click="addLike(indexus)"> <!-- Likes -->
<img alt="" src="../../assets/icons/herz.png" class="align-middle">
<label class="text-sm m-1 text-weiss">{{ postitem.likes }}</label>
<div class="flex items-center" @click="addLike(postitem.post_id, postitem.user_id, indexus)"> <!-- Likes -->
<img alt="" src="../../assets/icons/herz.png" class="align-middle">
<label class="text-sm m-1 text-weiss">{{ postitem.likes }}</label>
</div>
</div>
<br class="sm:hidden">
<div class="flex sm:tems-center mx-2"> <!-- View Post -->
<button @click="gotoPost(postitem.post_id)" class="text-schwarz mx-1 px-1 rounded-lg bg-button-farbe">View Post</button>
<button @click="copyLink(postitem.post_id)" class="text-schwarz pl-1 mx-1 px-1 rounded-lg bg-button-farbe">Share Post</button>
<button @click="gotoProfile(postitem.user_id)" class="text-schwarz pl-1 mx-1 px-1 rounded-lg bg-button-farbe"> Go to Profile</button>
</div><!-- ENDE -->
</div>
</div>

View File

@@ -1,18 +1,32 @@
<script setup lang="ts">
import {useRoute, useRouter} from "vue-router";
const route = useRoute();
const router = useRouter();
function gotop() {
location.reload();
console.log("PARAMS: "+ route.path);
console.log("Zum Seitenanfang gescrollt");
}
</script>
<template>
<div class="text-grau2 p-3">
<div class="text-grau2 p-3 text-center">
<a href="">Terms of Service</a>
-
<a href="">Privacy Policy</a><br>
<a href="">Imprint</a>
-
<a href="">Contact</a>
<a href="https://esp-projekt.notion.site/191fb990f26c808298dad302e97fb299?pvs=105">Contact</a>
-
<a href="">Support</a>
<a href="https://esp-projekt.notion.site/191fb990f26c808298dad302e97fb299?pvs=105">Support</a>
-
<a href="https://icons8.com"> Icons by icons8.com</a>
<div class="sm:hidden flex justify-center pt-8"><button class="text-weiss p-1 m-2 rounded-lg py-3 px-5 bg-button-farbe transition duration-300 ease-in-out" @click="gotop">Back to Top</button></div>
</div>
</template>

View File

@@ -1,43 +1,74 @@
<script setup lang="ts">
// Funktionen um die Seiten zu öffnen
// home -> app.vue
// PLACEHOLDER
import router from "../../router";
import { ref, onMounted, onUnmounted } from 'vue';
const sb_home = () => {
console.log("home");
router.push("/");
}
const sb_search = () => {
console.log("Search");
}
const sb_notifications = () => {
console.log("Notifications");
}
const sb_messages = () => {
console.log("Messages");
}
const sb_accounts = () => {
console.log("Accounts");
}
const sb_settings = () => {
console.log("Settings");
}
let self;
const isMobile = ref(false);
const show = ref(false);
const getShowMobileElements = () => {
const value = localStorage.getItem("mobile");
console.log("localStorage mobile:", value); // Debugging-Ausgabe
return value === 'true';
};
const setShowMobileElements = (value) => {
console.log("localStorage set mobile:", value.toString()); // Debugging-Ausgabe
localStorage.setItem("mobile", value.toString());
};
const toggleElements = () => {
if (isMobile.value){
show.value = !show.value;
setShowMobileElements(show.value);
}
};
const checkScreenWidth = () => {
isMobile.value = window.innerWidth < 640;
if(isMobile.value === false){
show.value = true;
} else {
show.value = false;
}
};
onMounted(() => {
checkScreenWidth();
window.addEventListener('resize', checkScreenWidth);
const show = ref(getShowMobileElements());
if(localStorage.getItem("mobile") === null){
show.value = false;
}
self = localStorage.getItem("self_id");
console.log("SELF NB: " + self);
});
onUnmounted(() => {
window.removeEventListener('resize', checkScreenWidth);
});
</script>
<template>
<div class="pt-4 border-b-2 border-b-grau2">
<div class="items-center flex justify-center">
<img src="../../assets/esp-logo_no_text.png" alt="" class="rounded-lg h-12 w-24 mx-auto" @click="sb_home">
<div class="items-center flex justify-center"><!-- BILD-->
<img src="../../assets/esp-logo_no_text.png" alt="" class="rounded-lg h-12 w-24 mx-auto hover:shadow-2xl hover:shadow-grau-dunkel" @click="router.push('/')">
</div>
<div class="align-middle space-y-3 pt-3 pl-3 pb-4 font-bold text-xl">
<label class="flex text-center text-grau2 hover:bg-logo-farbe-lila shadow-2xl rounded-lg" @click="sb_home"><img class="pr-2" src="../../assets/icons/home.png" alt=""> Home</label>
<label class="flex text-center text-grau2 hover:bg-logo-farbe-lila shadow-2xl rounded-lg" @click="sb_search"><img class="pr-2" src="../../assets/icons/lupe.png" alt="">Search</label>
<label class="flex text-center text-grau2 hover:bg-logo-farbe-rot shadow-2xl rounded-lg" @click="sb_notifications"><img class="pr-2" src="../../assets/icons/glocke.png" alt="">Notifications</label>
<label class="flex text-center text-grau2 hover:bg-logo-farbe-rot shadow-2xl rounded-lg" @click="sb_messages"><img class="pr-2" src="../../assets/icons/mail.png" alt="">Messages</label>
<label class="flex text-center text-grau2 hover:bg-logo-farbe-blau shadow-2xl rounded-lg" @click="sb_accounts"><img class="pr-2" src="../../assets/icons/user.png" alt="">Profile</label>
<label class="flex text-center text-grau2 hover:bg-logo-farbe-blau shadow-2xl rounded-lg" @click="sb_settings"><img class="pr-2" src="../../assets/icons/zahnrad.png" alt="">Settings</label>
<div class="sm:hidden text-weiss items-center flex justify-center mt-2 py-4 border-y-2 border-y-grau2 " v-if="isMobile">
<button @click="toggleElements"> {{ show ? 'close navbar' : 'expand navbar' }} </button>
</div>
<div class="align-middle space-y-3 pt-4 pl-3 pb-4 pr-4 font-bold text-xl" v-if="show || !isMobile"> <!-- Icons (Bild) und Text Damit der Text weiß ist muss zwei mal gedrückt werden manchmal-->
<label class="flex text-center text-grau2 hover:bg-logo-farbe-lila rounded-lg" @click="router.push('/')"><img class="mr-2 p-1 bg-logo-farbe-lila rounded-lg" src="../../assets/icons/home.png" alt=""> Home</label>
<label class="flex text-center text-grau2 hover:bg-logo-farbe-lila rounded-lg" @click="router.push('/search')"> <img class="mr-2 p-1 bg-logo-farbe-lila rounded-lg" src="../../assets/icons/lupe.png" alt="">Search</label>
<label class="flex text-center text-grau2 hover:bg-logo-farbe-rot rounded-lg" @click="router.push('/notifications')"> <img class="mr-2 p-1 bg-logo-farbe-rot rounded-lg" src="../../assets/icons/glocke.png" alt="">Notifications</label>
<label class="flex text-center text-grau2 hover:bg-logo-farbe-rot rounded-lg" @click="router.push('/messages')"> <img class="mr-2 p-1 bg-logo-farbe-rot rounded-lg" src="../../assets/icons/mail.png" alt="">Messages</label>
<label class="flex text-center text-grau2 hover:bg-logo-farbe-blau rounded-lg" @click="router.push(`/profile/${self}`)"> <img class="mr-2 p-1 bg-logo-farbe-blau rounded-lg" src="../../assets/icons/user.png" alt="">Profile</label>
<label class="flex text-center text-grau2 hover:bg-logo-farbe-blau rounded-lg" @click="router.push('/settings')"> <img class="mr-2 p-1 bg-logo-farbe-blau rounded-lg" src="../../assets/icons/zahnrad.png" alt="">Settings</label>
</div>
</div>
</template>

View File

@@ -0,0 +1,112 @@
<script setup lang="ts">
import {defineProps, defineEmits, onMounted, watch, ref} from 'vue';
import { nextTick } from 'vue';
let current_contact = null;
let self = "danielvici123";
const nachrichten = [
{ display_name: "Linux Enjoyer", username: "lunix", message: "Moin", msg_id: 1, chat_id: 16423 },
{ display_name: "XBOX",username: "xbox", message: "Hey, was geht?", msg_id: 2, chat_id: 29874 },
{ display_name: "danielvici223", username: "danielvici123", message: "Alles gut, und bei dir?", msg_id: 3, chat_id: 29874 },
{ display_name: "JETBrains",username: "jetbrains", message: "Hat jemand Erfahrung mit IntelliJ?", msg_id: 4, chat_id: 41235 },
{ display_name: "danielvici223",username: "danielvici123", message: "Ja, was brauchst du?", msg_id: 5, chat_id: 41235 },
{ display_name: "JETBrains",username: "jetbrains", message: "Wie kann ich Plugins effizient verwalten?", msg_id: 6, chat_id: 41235 },
{ display_name: "klopupser13", username: "theopampa", message: "ja ok können wir machen", msg_id: 7, chat_id: 41245 },
{ display_name: "danielvici223",username: "danielvici123", message: "willst du fornite", msg_id: 7, chat_id: 41245 },
];
let geladeneNachrichten = ref([]);
const send_message = ref("");
const props = defineProps({
contact: Object
});
const emit = defineEmits(['close']);
function closeChat() {
emit('close');
}
function openCHAT() {
geladeneNachrichten.value = [];
console.log("Chat geöffnet");
// conact ist null dann abruch
if(!props.contact) return;
// contact ist der aktuelle kontakt
current_contact = props.contact ? { ...props.contact } : null;
console.log(current_contact);
/*nachrichten.forEach((nachricht) => {
if ((nachricht.username == current_contact.username || nachricht.username == self) && nachricht.chat_id == current_contact.chat_id) {
geladeneNachrichten.value.push(nachricht);
}
});
*/
geladeneNachrichten.value = nachrichten.filter((nachricht) => {
console.log(`Nachricht von ${nachricht.username} mit Chat-ID ${nachricht.chat_id}`);
return (
(nachricht.username === current_contact.username || nachricht.username === self) &&
nachricht.chat_id == current_contact.chat_id
);
});
console.log("Nach dem Filtern:", geladeneNachrichten.value);
console.log(geladeneNachrichten);
}
function sendMessage(event){
event.preventDefault();
// API nachricht senden
console.log("Nachricht gesendet")
}
onMounted(() => {
openCHAT();
console.log("Current Contact:", current_contact);
});
watch(() => props.contact, () => {
openCHAT();
console.log("Current Contact:", current_contact);
});
</script>
<template>
<div class="fixed bottom-0 right-0 m-3 p-4 bg-schwarz text-weiss rounded-lg shadow-lg w-80">
<div class="flex justify-between items-center bborder-b-2 border-b-grau2">
<div class="flex">
<h3 class="text-lg font-bold">{{ contact.display_name }}</h3>
<a class="px-2">@{{ contact.username }}</a>
</div>
<button @click="closeChat"><img src="../../assets/icons/x-klein.png" alt=""></button>
</div>
<div class="mt-2">
<ul class="space-y-2">
<li v-for="(msg) in geladeneNachrichten" :key="msg.msg_id || msg.id">
<div class="space-x-2">
<a class="font-bold">{{ msg.display_name }}</a>
</div>
<div>
<p>{{ msg.message }}</p>
</div>
</li>
</ul>
<div>
<form class="flex mt-4" @submit="sendMessage">
<label><input v-model="send_message" class="bg-schwarz outline-none border-b-2 mt-2 py-2 px-1" placeholder="Message"></label>
<button type="submit"><img src="../../assets/icons/send.png" alt="senden" class="ml-4 mt-1"></button>
</form>
</div>
</div>
</div>
</template>
<style scoped>
</style>

View File

@@ -1,16 +0,0 @@
<script setup>
</script>
<template>
<div class="flex p-4 border-b-2 border-b-grau2 ">
<div class="w-12">
<img class="p-2 bg-grau rounded-l-lg" src="../../assets/icons/lupe.png" alt="">
</div>
<input type="text" class="bg-grau rounded-r-lg focus:outline-none text-weiss" placeholder="Search...">
</div>
</template>
<style scoped>
</style>

View File

@@ -1,5 +1,5 @@
<script setup lang="ts">
// PLACEHOLDER
const hashtags = [
{id: 1,name: "xbox", nr_posts: 553,category: "Gaming" } ,
{id: 2,name: "lol" , nr_posts: 16422, category: "Gaming"},

View File

@@ -1,21 +1,76 @@
<script setup>
<script setup lang="ts">
import router from "../../router";
import { ref, onMounted } from 'vue';
let input_username_mail = ref("");
let input_user_password = ref("");
let rememberMe = ref(false);
onMounted(() => {
if (localStorage.getItem('username')) {
input_username_mail.value = localStorage.getItem('username') || "";
input_user_password.value = localStorage.getItem('password') || "";
rememberMe.value = false;
}
});
async function login(event: Event) {
event.preventDefault();
const username = input_username_mail;
const password = input_user_password;
if (username.value === "" || password.value === "") {
alert("Please fill all fields");
return;
}
try {
const response = await fetch('http://localhost:8000/api/account/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ username: username.value, password: password.value }),
});
if (response.status === 200) {
const data = await response.json();
localStorage.setItem('isLoggedIn', 'true');
localStorage.setItem('username', username.value);
localStorage.setItem('self_id', data["userId"]);
console.log("SELF LOG: " + data["userId"]);
alert("You will be now redirected");
router.push('/');
} else {
alert("Username/E-Mail or Password are wrong");
}
} catch (e) {
console.log("An error has occurred. Please try again later.");
console.log(e)
}
}
</script>
<template>
<div class="px-20 border-x border-x-grau2 pb-35p">
<div class="text-3xl pt-32">
<p class="text-weiss text-center">Welcome to <span class="text-button-farbe">ESP</span>!</p>
<p class="text-weiss text-center">Login or create a new Account to continue</p>
<div class="px-20 sm:border-x sm:border-x-grau2 pb-32">
<div class="text-3xl pt-32"> <!-- ÜBERSCHRIFT-->
<p class="text-weiss text-center">Welcome to <label class="bg-schwarz p-1 rounded-lg mr-1"><span class="text-logo-farbe-lila">E</span><span class="text-logo-farbe-rot">S</span><span class="text-logo-farbe-blau">P</span></label>!</p>
<p class="text-weiss text-center">Login to continue</p>
</div>
<div class="px-20 pt-7">
<form class="flex flex-col items-center">
<input class="m-4 w-full max-w-xs bg-grau-dunkel p-4 text-weiss placeholder-grau2 focus:outline-none rounded-lg" type="text" placeholder="Username or E-Mail"><br>
<input class="m-4 w-full max-w-xs bg-grau-dunkel p-4 text-weiss placeholder-grau2 focus:outline-none rounded-lg" type="text" placeholder="Password"><br>
<button class="m-4 bg-grau-dunkel w-full max-w-xs p-4 text-weiss rounded-lg">Login</button>
<div class="px-20 pt-7"><!-- FORM --->
<form class="flex flex-col items-center" @submit.prevent="login">
<input class="m-4 md:w-full md:max-w-xs bg-grau-dunkel p-4 text-weiss placeholder-grau2 focus:outline-none rounded-lg" v-model="input_username_mail" type="text" placeholder="Username or E-Mail"><br>
<input class="m-4 md:w-full md:max-w-xs bg-grau-dunkel p-4 text-weiss placeholder-grau2 focus:outline-none rounded-lg" v-model="input_user_password" type="password" placeholder="Password"><br>
<button class="m-4 bg-button-farbe w-full md:max-w-xs p-4 text-schwarz rounded-lg">Login</button>
</form>
</div>
<div>
<p class="text-weiss text-center pb-2 ">No Account? <router-link to="/register" class="text-button-farbe">Register here</router-link></p>
<p class="text-weiss text-center pt-2"> <router-link to="/wip" class="text-button-farbe">Forgot password?</router-link> </p>
</div>
</div>
</template>
<style scoped>

View File

@@ -0,0 +1,27 @@
<script setup>
import Contacts from "./home_components/contacts.vue";
import Feed from "./home_components/feed.vue";
import Legal from "./home_components/legal.vue";
import Navigationbar from "./home_components/navigationbar.vue";
import Msg_main from "./messages_components/msg_main.vue";
</script>
<template>
<div id="main" class=" sm:flex">
<div id="left" class="sm:w-72 min-w-72">
<navigationbar></navigationbar>
</div>
<div class="w-100p">
<msg_main></msg_main>
</div>
<div class="sm:w-1/4">
<contacts></contacts>
<legal></legal>
</div>
</div>
</template>
<style scoped>
</style>

View File

@@ -0,0 +1,60 @@
<script setup lang="ts">
import {ref} from "vue";
import Popup_chat from "../home_components/popup_chat.vue";
const nachrichten = ref([
{id: 1,
profile_picture: "../../assets/default_pp.png", display_name: "Linux Enjoyer",username: "lunix",
content:"Moin!",
type:"message",date: new Date(2024,11,13).toDateString() },
{id: 2,
profile_picture: "../../assets/default_pp.png",
display_name: "XBOX",username: "xbox",
content: "Hey, was geht?",
type:"message",date: new Date(2024,9,13).toDateString()},
{id: 4,// Sollte im feed sein
profile_picture: "../../assets/default_pp.png",
display_name: "klopupser13", username: "theopampa",
content: "ja ok können wir machen",
type:"following",date: new Date(2025,1,15).toDateString()}
]);
const selectedContact = ref(null);
const showChatPopup = ref(false);
function openChat(contact) {
selectedContact.value = contact;
showChatPopup.value = true;
}
</script>
<template>
<div class="border-x-grau2 border-x-2">
<div class="border-b-grau2 border-b-2">
<h1 class="text-weiss text-3xl p-4">Messages</h1>
</div>
<div>
<ul>
<li v-for="nachricht in nachrichten" :key="nachricht.id" class="border-b-grau2 border-b-2" @click="openChat(nachricht)">
<div class="flex p-4">
<img src="../../assets/default_pp.png" alt="user profile picture" class="rounded-full w-16 h-16">
<div>
<div class="flex mb-1">
<label class="text-lg font-bold sm:m-1 ml-1 text-weiss">{{nachricht.display_name}}</label>
<label class="text-lg sm:m-1 ml-1 text-grau2">@{{nachricht.username}}</label>
<label class="sm:m-2 ml-1 text-grau2">{{nachricht.date}}</label>
</div>
<a class="ml-1 text-weiss">{{nachricht.content}}</a>
</div>
</div>
</li>
</ul>
</div>
<popup_chat v-if="showChatPopup" :contact="selectedContact" @close="showChatPopup = false" />
</div>
</template>
<style scoped>
</style>

View File

@@ -0,0 +1,26 @@
<script setup>
import Legal from "./home_components/legal.vue";
import Navigationbar from "./home_components/navigationbar.vue";
import Notifi_comp from "./notifications_components/notifi_main.vue";
import Contacts from "./home_components/contacts.vue";
</script>
<template>
<div class="text-weiss sm:flex">
<div id="left" class="border-1 border-b-grau sm:w-72 min-w-72">
<navigationbar></navigationbar>
</div>
<div class="w-100p border-x-2 border-x-grau2">
<notifi_comp></notifi_comp>
</div>
<div class="sm:w-1/4">
<contacts></contacts>
<legal></legal>
</div>
</div>
</template>
<style scoped>
</style>

View File

@@ -0,0 +1,124 @@
<script setup lang="ts">
console.log("SEITE WIRD AUSGEFÜHRT")
import {ref} from "vue";
const notifications = ref([
{id: 1, // Sollte im feed sein
profile_picture: "../../assets/default_pp.png",
author_display_name: "Linux Enjoyer",author_username: "lunix",
content:"I love Linux. My Favorite Linux Distro is ARCH LINUX.",
type:"message",date: new Date(2024,11,13).toDateString() }, // 11 = Dezember, 10 = Nov, 9 = okt., 8 = sep.
{id: 2,
profile_picture: "../../assets/default_pp.png",
author_display_name: "XBOX",author_username: "Xbox",
content: "Call of Duty: Black Ops 6 is OUT NOW. Check it out: https://xbox.com",
type:"message",date: new Date(2024,9,13).toDateString()},
{id: 33,// Sollte im feed sein
profile_picture: "../../assets/default_pp.png",
author_display_name: "ESP Support",author_username: "esp_support",
content: "You're post was against our TOS and got deleted. Additionally your account has been suspended for 30 Days." ,
type:"support",date: new Date(2024,9,13).toDateString()},
{id: 4,// Sollte im feed sein
profile_picture: "../../assets/default_pp.png",
author_display_name: "System", author_username: "esp_notifications",
content: "GitHub posted something new. Check it out",
type:"following",date: new Date(2024,10,2).toDateString()},
{id: 5, // Sollte im feed sein
profile_picture: "../../assets/danielvici_pp.png",
author_display_name: "danielvici123", author_username: "danielvici",
content: "I created this WebApp with VUE3 and TailwindCSS. It was a lot of fun.",
type:"message",date: new Date(2024,1,1).toDateString()}
]);
let feed = ref([]);
// wie enum in c bzw. arduino
interface sortting_definition {
f: boolean; // following
m: boolean; // messages
da: boolean; // date-asc
dd: boolean; // date-desc
u: boolean; // user
sorting: string;
}
// Standard filter und sorting
const check_type = ref<sortting_definition>({
f: true,
m: true,
da: false,
dd: false,
u: false,
sorting: "date-asc",
});
// Für jeden eintrag in notifications durschläuft die schleife einen durschlauf
for(let i =0; i < notifications.value.length; i++) {
console.log("Getting Notifications...");
feed.value.push(notifications.value[i]);
}
function go_filter(){
console.log("Filter applied");
console.log("These Filter applied: ", JSON.stringify(check_type.value));
feed.value = [];
notifications.value.forEach((notification) => {
if ((check_type.value.f == true && notification.type === "following") || (check_type.value.m == true && notification.type === "message")|| (notification.type === "support")){
feed.value.push(notification);
}
})
console.log("Feed:", feed.value);
}
</script>
<template>
<div>
<div class="border-b-grau2 border-b-2">
<h1 class="text-weiss text-3xl p-4">Notifications</h1>
</div>
<div class="text-grau2 p-5 border-b-2 "><!-- FILTER -->
<!-- Wenn das Form submited wurde wird die Seite nicht-->
<!-- neugeladen und die Funktion -->
<form @submit.prevent="go_filter">
<label class="p-2 text-xl text-weiss">Filter</label>
<label class="m-2 accent-logo-farbe-blau">You Following<input type="checkbox" class="m-1 mr-3" v-model="check_type.f"></label>
<label class="m-2 accent-logo-farbe-rot">Messages<input type="checkbox" class="m-1 mr-3" v-model="check_type.m"></label>
<!--<label class="m-2 accent-logo-farbe-rot">Other<input type="checkbox" class="m-1 mr-3" v-model="check_type.o"></label>-->
<label class="p2">Sort by <select name="sorting" id="notification_sorting" class="mr-3 bg-hintergrund-farbe text-weiss" v-model="check_type.sorting">
<option value="date-asc">Date (asc.)</option>
<option value="date-desc">Date (desc.)</option>
<option value="user">User</option>
</select></label>
<button type="submit" class="bg-button-farbe text-schwarz p-2 rounded-lg">Filter</button>
</form>
</div>
<div> <!-- MAIN NACHRICHTEN-->
<ul v-if="feed.length > 0">
<li v-for="(notification) in feed" :key="notifications.id" class="border-b-grau2 border-b-1 border-b p-3 flex">
<img src="../../assets/default_pp.png" alt="user profile picture" class="rounded-full w-16 h-16">
<div>
<div class="flex">
<label class="text-lg font-bold m-1 text-weiss">{{notification.author_display_name}}</label>
<label class="m-2 text-grau2">Type: {{notification.type}}</label>
<label class="m-2 text-grau2">Date: {{notification.date}}</label>
</div>
<a class="ml-1">{{notification.content}}</a>
</div>
</li>
</ul>
<a v-else>You have no Notifications!</a>
</div>
</div>
</template>
<style scoped>
</style>

327
src/components/posts.vue Normal file
View File

@@ -0,0 +1,327 @@
<script setup lang="ts">
import { onMounted, ref } from 'vue';
import {useRoute, useRouter} from 'vue-router';
import Navigationbar from './home_components/navigationbar.vue';
import Contacts from './home_components/contacts.vue';
import Legal from './home_components/legal.vue';
interface Post {
post_uuid: number;
user_id: number;
author_username: string;
post_text: string;
likes: number;
comments_count: number;
comments: any[];
created_at: string;
}
interface User {
user_id: number;
username: string;
displayname: string;
}
interface Comment {
comment_id: number;
author_user_id: number;
post_id: number;
text: string;
likes: number;
created_at: string;
displayname: string;
username: string;
}
const route = useRoute();
const router = useRouter();
const post = ref<Post | null>(null);
const user = ref<User | null>(null);
const comments = ref<Comment[] | null>(null);
let comment_text = ref();
let self_id;
const loading = ref(true);
let post_uuid = ref();
async function getPost(post_id: any) {
try {
const post_response = await fetch(`http://localhost:8000/api/post/${post_id}`, {method: 'GET'});
const fetchedPost: Post = await post_response.json();
if (post_response.status === 404) {
console.error("No comments found.");
alert("Post not found")
await router.push('/');
return;
}
const user_response = await fetch('http://localhost:8000/api/users', {method: 'GET'});
const usersDATA = await user_response.json();
const postAuthor = usersDATA.find(user => user.user_id === fetchedPost.user_id);
if (postAuthor) {
console.log("The post was written by:", postAuthor);
} else {
console.error("Author not found.");
}
user.value = postAuthor;
post.value = fetchedPost;
console.log(post.value);
} catch (e) {
console.error(e);
} finally {
loading.value = false;
}
}
async function getComment(post_id: any) {
try {
const comments_response = await fetch(`http://localhost:8000/api/post/${post_id}/comments`, { method: 'GET' });
const fetchedComments: Comment[] = await comments_response.json();
const user_response = await fetch(`http://localhost:8000/api/users`, { method: 'GET' });
const usersDATA: User[] = await user_response.json();
if(comments_response.status === 404 || user_response.status === 404) {
console.error("ERRROR");
alert("Error try it again later.");
await router.push('/');
return;
}
comments.value = fetchedComments.map(comment => {
const author = usersDATA.find(u => u.user_id === comment.author_user_id) || {
username: 'Unknown',
displayname: 'Unknown',
};
return {
...comment,
username: author.username,
displayname: author.displayname,
};
});
comments.value.sort((a, b) => b.post_id - a.post_id);
console.log(comments.value);
} catch (e) {
console.error(e);
}
}
function copyLink(post_id: string | number) {
const tocopy = `http://localhost:5173/post/${post_id}`;
navigator.clipboard.writeText(tocopy);
alert("Copied to clipboard");
}
async function addLike() { // Post liken
try {
post.value.likes++;
const response_like = await fetch(`http://localhost:8000/api/post/${post_uuid.value}/like`, {
method: 'POST',
headers: {'content-type': 'application/json'},
body: `{"userId":${self_id}}`,
});
if(response_like.status === 404) {
console.error("ERROR");
await router.push('/');
return;
}
const data = await response_like.json();
console.log("post_id 2: ", post_uuid);
return data;
} catch (error) {
console.error('Fehler beim Liken des Posts:', error);
throw error;
}
}
async function comment_create_text(comment_text: string) {
if (comment_text === null) {
alert("Please write something before commenting.");
return;
}
try {
const response = await fetch(`http://localhost:8000/api/post/${post_uuid.value}/comment`, {
method: 'POST',
headers: {'content-type': 'application/json'},
body: `{"userId":${self_id},"text":"${comment_text}"}`,});
const data = await response.json();
if(response.status === 404) {
console.error("ERROR");
await router.push('/');
return;
}
await getComment(parseInt(post_uuid.value));
return data;
} catch (error) {
console.error(error);
}
console.log(comment_text);
}
async function addLike_comment(comment_id: number | string) {
try {
post.value.likes++;
const response_like = await fetch(`http://localhost:8000/api/comment/${comment_id}/like`, {
method: 'POST',
headers: {'content-type': 'application/json'},
body: `{"userId":${self_id}}`,
});
if(response_like.status === 404) {
console.error("ERROR");
await router.push('/');
return;
}
const data = await response_like.json();
console.log("post_id: ", comment_id);
console.log(data);
getComment(parseInt(post_uuid.value));
return data;
} catch (error) {
console.error('Fehler beim Liken des Posts:', error);
throw error;
}
}
function gotoProfile(user_id: string | number) {
router.push(`/profile/${user_id}`);
}
onMounted(async () => {
console.log("PARAMS: "+ route.path);
const pathArray = route.path.split('/');
console.log(pathArray);
if (pathArray.length > 2) {
post_uuid.value = pathArray[2];
console.log("post_id 0: ", post_uuid.value);
}
if (!post_uuid) {
alert('No post selected. Redirecting to feed.');
await router.push('/');
return;
}
self_id = localStorage.getItem('self_id');
getPost(parseInt(post_uuid.value));
getComment(parseInt(post_uuid.value));
});
</script>
<template>
<div id="main" class="bg-hintergrund-farbe sm:flex overflow-y-auto sm:h-full sm:scrollbar">
<div id="left" class="sm:w-72 min-w-72">
<navigationbar></navigationbar>
</div>
<div class="text-weiss sm:w-100p w-screen sm:border-x sm:border-x-grau2" v-if="post">
<div> <!-- HEADER -->
<h2 class="align-middle p-6 text-3xl text-weiss border-b-grau2 border-b">Post</h2>
</div>
<div class="flex px-2 py-4 border-b-grau2 border-b">
<img v-if="user.username != 'danielvici' " src="../assets/default_pp.png" alt="" class="rounded-full w-16 h-16">
<img v-else src="../assets/danielvici_pp.png" alt="" class="rounded-full w-16 h-16">
<div>
<div> <!-- POST HEADER -->
<label class="text-lg font-bold m-1 text-weiss">{{user.displayname}}</label>
<label class="text-base m-1 text-grau2">@{{ user.username }} | </label>
<label class="text-base text-grau2"> Posted at {{ post.created_at }}</label>
</div>
<div class="m-2"> <!-- POST CONTENT -->
<p class="text-sm m-1 text-weiss">{{ post.post_text }}</p>
</div>
<div class="flex"> <!-- POST FOOTER -->
<div class="flex"> <!-- Comments -->
<img src="../assets/icons/comment.png" alt="" class="rounded-full align-middle">
<label class="text-sm m-1 text-weiss" v-if="post.comments != undefined">{{ post.comments }}</label>
<label class="text-sm m-1 text-weiss" v-else>Comments disabled</label>
</div>
<div class="flex items-center" @click="addLike()"> <!-- Likes -->
<img alt="" src="../assets/icons/herz.png" class="align-middle">
<label class="text-sm m-1 text-weiss">{{ post.likes }}</label>
</div>
<button @click="copyLink(post_uuid)" class="text-schwarz pl-1 mx-1 px-1 rounded-lg bg-button-farbe">Share Post</button>
</div>
</div>
</div>
<div> <!-- COMMENTS VIEW and WRITE -->
<div>
<h2 class="align-middle p-6 text-xl text-weiss border-b-grau2 border-b">Comments</h2>
</div>
<div> <!-- WRITE COMMENTS -->
<div class="flex border-b-2 border-b-grau2">
<img v-if="self_id != '99' " src="../assets/default_pp.png" alt="" class="p-2 rounded-full w-16 h-16">
<img v-else src="../assets/danielvici_pp.png" alt="" class="p-2 rounded-full w-16 h-16">
<!-- <img src="../assets/danielvici_pp.png" alt="" class="p-2 rounded-full w-16 h-16">-->
<form class="w-full pr-5">
<!-- post_publish ist richtig aber wird falsch angezeigt. File Input geht nicht-->
<textarea v-model="comment_text" name="post_text" class="bg-hintergrund-farbe rounded-lg m-2 p-1 focus:outline-none text-grau2 w-full resize-none" rows="3" placeholder="Write something..."></textarea>
<div class="">
<button id="post_publish" name="post_publishss" class="text-weiss p-1 m-2 rounded-lg py-3 px-5 bg-button-farbe" @click.prevent="comment_create_text(comment_text)" type="button">Post</button>
</div>
</form>
</div>
</div>
<div> <!-- VIEW COMMENTS -->
<div class="sm:overflow-y-scroll h-[450px]"> <!-- VIEW COMMENTS -->
<ul v-if="comments && comments.length > 0">
<li v-for="c in comments" :key="c.comment_id" class="p-4 border-b border-gray-700">
<div class="flex">
<img v-if="c.author_user_id != '99' " src="../assets/default_pp.png" alt="" class="p-2 rounded-full w-16 h-16">
<img v-else src="../assets/danielvici_pp.png" alt="" class="p-2 rounded-full w-16 h-16">
<div>
<div> <!-- POST HEADER -->
<label class="text-lg font-bold m-1 text-weiss">{{c.displayname}}</label>
<label class="text-base m-1 text-grau2">@{{ c.username }}</label>
</div>
<div class="m-2"> <!-- POST CONTENT -->
<p class="text-sm m-1 text-weiss">{{ c.text }}</p>
</div>
<div class="flex"> <!-- POST FOOTER -->
<div class="flex items-center" @click="addLike_comment(c.comment_id)"> <!-- Likes -->
<img alt="" src="../assets/icons/herz.png" class="align-middle">
<label class="text-sm m-1 text-weiss">{{ c.likes }}</label>
</div>
</div>
</div>
</div>
</li>
</ul>
<div v-else class="p-4 text-gray-400">No comments yet.</div>
</div>
</div>
</div>
</div>
<div class="sm:w-1/4 w-screen">
<contacts></contacts>
<legal></legal>
</div>
</div>
</template>
<style scoped>
</style>

View File

@@ -0,0 +1,26 @@
<script setup>
import Navigationbar from "./home_components/navigationbar.vue";
import Contacts from "./home_components/contacts.vue";
import Legal from "./home_components/legal.vue";
import Profile_main from "./profile_components/profile_main.vue";
</script>
<template>
<div id="main" class="bg-hintergrund-farbe sm:flex overflow-visible scrollbar sm:overflow-y-auto sm:h-full sm:scrollbar">
<div id="left" class="sm:w-72 min-w-72">
<navigationbar></navigationbar>
</div>
<div class="sm:w-100p w-screen sm:border-x sm:border-x-grau2">
<profile_main></profile_main>
</div>
<div id="right" class="sm:w-1/4 w-screen">
<contacts></contacts>
<legal></legal>
</div>
</div>
</template>
<style scoped>
</style>

View File

@@ -0,0 +1,205 @@
<script setup lang="ts">
import { onMounted, ref } from 'vue';
import { useRoute, useRouter } from 'vue-router';
const route = useRoute();
const router = useRouter();
const upc = ref([]);
const profile_id = ref<number | null>(null);
const userData = ref<any>(null);
onMounted(async () => {
const pathArray = route.path.split('/');
if (pathArray.length > 2) {
profile_id.value = parseInt(pathArray[2], 10);
} else {
console.warn("No profile ID found in the route.");
}
if (!profile_id.value) {
alert('No profile selected. Redirecting to feed.');
await router.push('/');
return;
}
await create_own_posts();
await getUser();
});
async function create_own_posts() {
try {
const post_response = await fetch('http://localhost:8000/api/posts', {
method: 'GET',
});
if (!post_response.ok) {
throw new Error(`HTTP error! status: ${post_response.status}`);
}
const postsDATA = await post_response.json();
upc.value = postsDATA.filter((post) => post.user_id === profile_id.value);
if (upc.value.length === 0) {
console.warn('No posts found for this user.');
return;
}
//console.log("upc: "+ JSON.stringify(upc.value, null, 2));
return upc.value;
} catch (error) {
console.error('Error fetching posts:', error);
upc.value = [];
}
}
async function addLike(post_id: string | number, user_id: number, index: number) {
try {
upc.value[index].likes++;
const response = await fetch(`http://localhost:8000/api/post/${post_id}/like`, {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: `{"userId":${user_id}}`,
});
if (!response.ok) {
const errorText = await response.text();
console.error('Server-Fehlertext:', errorText);
throw new Error(`HTTP error! status: ${response.status}, text: ${errorText}`);
}
const data = await response.json();
return data;
} catch (error) {
console.error('Fehler beim Liken des Posts:', error);
throw error;
}
}
function consoleLog() {
console.log("upc: ", upc.value);
console.log("profile_id: ", profile_id.value);
}
function gotoPost(post_id: string | number) {
localStorage.setItem("viewedpost", post_id.toString());
router.push(`/post/${post_id}`);
}
function copyLink(post_id: string | number) {
const tocopy = `http://localhost:5173/post/${post_id}`;
navigator.clipboard.writeText(tocopy);
alert("Copied to clipboard with");
}
async function getUser() {
try {
const response = await fetch('http://localhost:8000/api/users/');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const users = await response.json();
const user = users.find((u) => u.user_id === profile_id.value);
if (user) {
const followerCount = JSON.parse(user.followers).length;
const followingCount = JSON.parse(user.following).length;
userData.value = {
...user,
followerCount,
followingCount,
};
console.log("userData: ", userData.value);
return userData;
} else {
console.error('Benutzer nicht gefunden.');
userData.value = null;
}
} catch (error) {
console.error('Fehler beim Abrufen der Benutzerdaten:', error);
userData.value = null;
}
}
function copyUser(){
const tocopy = `http://localhost:5173/profile/${profile_id.value}`;
navigator.clipboard.writeText(tocopy);
alert("Copied to clipboard");
}
</script>
<template>
<div>
<h2 class="align-middle p-6 text-3xl text-weiss border-b-grau2 border-b ">Profile</h2>
<div class="mb-6" v-if="userData">
<div class="text-weiss p-2 flex justify-center">
<img v-if="userData.user_id != '99' " src="../../assets/default_pp.png" alt="" class="rounded-full size-36">
<img v-else src="../../assets/danielvici_pp.png" alt="" class="rounded-full size-36">
</div>
<div class="text-center p-2 flex flex-col">
<label class="text-xl font-bold m-1 text-weiss" @click="consoleLog()">{{ userData.displayname }}</label>
<label class="text-base m-1 text-grau2">@{{ userData.username }}</label>
</div>
<div class="text-center py-4">
<label class="text-base m-1 text-grau2">Bio:</label>
<p class="text-sm m-1 text-weiss italic">"{{ userData.bio }}"</p>
</div>
<div class="text-grau2 p2 text-center">
<label class="text-base m-1 p-2"> Followers <a class="text-weiss">{{ userData.followerCount }}</a></label>
<label class="text-base m-1 p-2"> Following <a class="text-weiss">{{ userData.followingCount }}</a></label>
</div>
<div class="flex justify-center pt-5">
<button @click="copyUser" class="text-schwarz mx-1 px-1 rounded-lg bg-button-farbe">Share Profile</button>
</div>
</div>
<div v-else class="flex-col justify-center p-5 text-center">
<a class="text-weiss text-3xl"> USER NOT FOUND</a> <br>
<router-link to="/" class="text-button-farbe text-3xl text-center"> Go to Feed</router-link>
</div>
<div>
<h2 class="align-middle p-6 text-xl text-weiss border-y-grau2 border-y ">Posts</h2>
</div>
<div class="sm:overflow-y-auto sm:h-[400px] sm:scrollbar">
<ul v-if="upc.length > 0">
<li v-for="(postitem, indexus) in upc" :key="postitem.user_id" class="border border-grau2 p-3 flex">
<img v-if="postitem.user_id != '99' " src="../../assets/default_pp.png" alt="" class="rounded-full w-16 h-16">
<img v-else src="../../assets/danielvici_pp.png" alt="" class="rounded-full w-16 h-16">
<div>
<div>
<label class="text-lg font-bold m-1 text-weiss">{{ userData.displayname }}</label>
<label class="text-base m-1 text-grau2">@{{ userData.username }}</label>
</div>
<div class="m-2">
<p class="text-sm m-1 text-weiss">{{ postitem.post_text }}</p>
</div>
<div class="sm:flex">
<div class="flex items-center">
<div class="flex">
<img src="../../assets/icons/comment.png" alt="" class="rounded-full align-middle">
<label class="text-sm m-1 text-weiss" v-if="postitem.comments != undefined">{{ postitem.comments }}</label>
<label class="text-sm m-1 text-weiss" v-else>Comments disabled or no comments</label>
</div>
<div class="flex items-center" @click="addLike(postitem.post_id, postitem.user_id, indexus)">
<img alt="" src="../../assets/icons/herz.png" class="align-middle">
<label class="text-sm m-1 text-weiss">{{ postitem.likes }}</label>
</div>
</div>
<br class="sm:hidden">
<div class="flex sm:items-center mx-2">
<button @click="gotoPost(postitem.posts_uuid)" class="text-schwarz mx-1 px-1 rounded-lg bg-button-farbe">View Post</button>
<button @click="copyLink(postitem.posts_uuid)" class="text-schwarz pl-1 mx-1 px-1 rounded-lg bg-button-farbe">Share Post</button>
</div>
</div>
</div>
</li>
</ul>
<p v-else class="text-weiss text-center justify-center text-lg pt-5"> This user has not posted anything yet. </p>
</div>
</div>
</template>
<style scoped>
</style>

View File

@@ -0,0 +1,25 @@
<script setup>
import Legal from "./home_components/legal.vue";
import Register_main from "./register_components/register_main.vue";
import router from "../router";
function route_home() {
router.push("/");
}
</script>
<template>
<div id="main" class="bg-hintergrund-farbe sm:flex p-2 sm:justify-between overflow-auto h-screen scrollbar">
<div class="pt-5">
<img src="../assets/esp-logo_no_text.png" alt="" class="rounded-lg h-12 w-24 hover:shadow-2xl hover:shadow-grau-dunkel" @click="route_home">
</div>
<register_main class="sm:inset-0"></register_main>
<div class="sm:inset-y-0 sm:right-0 sw:max-w-36 text-center">
<legal></legal>
</div>
</div>
</template>
<style scoped>
</style>

View File

@@ -0,0 +1,78 @@
<script setup lang="ts">
import {ref} from "vue";
import router from "../../router";
let register_input_username = ref("");
let register_input_displayname = ref("");
let register_input_password = ref("");
let register_input_email = ref("");
async function register() {
const username = register_input_username;
const displayname = register_input_displayname;
const email = register_input_email;
const password = register_input_password;
const std_text = "default";
if (username.value === "" || password.value === "" || displayname.value === "" || email.value === "") {
alert("Please fill all fields");
return;
}
console.log("Username: " + username.value + ", Password: " + password.value);
try {
const response = await fetch('http://localhost:8000/api/account/register', {
method: 'POST',
headers: {
'Content-Type': 'login/json',
},
body: JSON.stringify({username: username.value, password: password.value, userGroup: "user", displayname: displayname.value, user_email: email.value, firstname: std_text, surname: std_text}),
});
if (response["status"] == 200) {
const data = await response.json();
localStorage.setItem('isLoggedIn', 'true');
localStorage.setItem('username', username.value);
localStorage.setItem('self_id', data["userId"]);
console.log("SELF REG: " + data["userId"]);
alert("Account created! You will be now redirected");
router.push('/');
router.go(1);
}
const data = await response.json();
console.log(response);
} catch (e) {
console.log("An error has occurred. Please try again later.");
console.error(e);
}
}
</script>
<template>
<div id="main" class="bg-hintergrund-farbe p-2 sm:border-x sm:border-x-grau2 sm:px-20">
<div class="text-3xl pt-20px sm:pt-32"> <!-- ÜBERSCHRIFT-->
<p class="text-weiss text-center">Welcome to <label class="bg-schwarz p-1 rounded-lg mr-1"><span class="text-logo-farbe-lila">E</span><span class="text-logo-farbe-rot">S</span><span class="text-logo-farbe-blau">P</span></label>!</p>
<p class="text-weiss text-center pt-2">Join today!</p>
</div>
<div class="px-20 pt-7"> <!-- FORM --->
<form class="flex flex-col items-center" @submit.prevent="register">
<input class="m-4 sm:w-full max-w-xs bg-grau-dunkel p-4 text-weiss placeholder-grau2 focus:outline-none rounded-lg" v-model="register_input_username" type="text" placeholder="Username" required><br>
<input class="m-4 sm:w-full max-w-xs bg-grau-dunkel p-4 text-weiss placeholder-grau2 focus:outline-none rounded-lg" v-model="register_input_displayname" type="text" placeholder="Displayname" required><br>
<input class="m-4 sm:w-full max-w-xs bg-grau-dunkel p-4 text-weiss placeholder-grau2 focus:outline-none rounded-lg" v-model="register_input_email" type="email" placeholder="E-Mail" required><br>
<input class="m-4 sm:w-full max-w-xs bg-grau-dunkel p-4 text-weiss placeholder-grau2 focus:outline-none rounded-lg" v-model="register_input_password " type="password" placeholder="Password" required><br>
<button class="m-4 bg-button-farbe sm:w-full max-w-xs p-4 text-schwarz rounded-lg">Create Account</button>
</form>
<p class="text-weiss w-100p text-center">Already have an account? <router-link to="/login" class="text-button-farbe">Login</router-link></p>
</div>
</div>
</template>
<style scoped>
</style>

30
src/components/search.vue Normal file
View File

@@ -0,0 +1,30 @@
<script setup>
import Navigationbar from "./home_components/navigationbar.vue";
import Legal from "./home_components/legal.vue";
import Search_main from "./search_components/search_main.vue";
import Contacts from "./home_components/contacts.vue";
</script>
<template>
<div class="text-weiss sm:flex">
<div id="left" class="sm:w-72 sm:min-w-72 border-1 border-b-grau">
<navigationbar></navigationbar>
</div>
<div class="sm:w-100p w-screen border-x-2 border-x-grau2">
<div class="border-b-grau2 border-b-2">
<h1 class="text-weiss text-3xl p-4">Search</h1>
</div>
<search_main></search_main>
</div>
<div class="sm:w-1/4">
<contacts></contacts>
<legal></legal>
</div>
</div>
</template>
<style scoped>
</style>

View File

@@ -0,0 +1,221 @@
<script setup lang="ts">
import {ref, onMounted} from "vue";
import router from "../../router";
const allItems = ref<any[]>([]);
const feed = ref<any[]>([]);
const feed2 = ref<any[]>([]);
interface search_filter {
u: boolean;
p: boolean;
}
const search_filter_status = ref<search_filter>({
u: true,
p: true,
});
let searched = false;
const usr_search = ref<string | undefined>(undefined);
async function get_posts_user() {
try {
const post_response = await fetch('http://localhost:8000/api/posts');
const postsDATA = await post_response.json();
const user_response = await fetch('http://localhost:8000/api/users');
const usersDATA = await user_response.json();
usersDATA.forEach(user => {
allItems.value.push({...user, id: user.user_id, type: 'user'});
});
postsDATA.forEach(post => {
const author = usersDATA.find(user => user.user_id === post.user_id);
const postWithAuthorInfo = {
...post,
type: 'post',
username: author ? author.username : 'unknown',
displayname: author ? author.displayname : 'unknown',
};
allItems.value.push(postWithAuthorInfo);
});
feed.value = [...allItems.value];
feed2.value = [...allItems.value];
//console.log("Data Loaded:", allItems.value);
} catch (error) {
console.error("Error:", error);
}
}
onMounted(async () => {
get_posts_user();
});
function go_fs(){
searched = true;
feed.value = [];
feed2.value = [];
const combinedFilteredItems = allItems.value
.filter(item => (search_filter_status.value.u == true && item.type === "user") ||
(search_filter_status.value.p == true && item.type === "post"))
.map(item => {
if (item.type === 'post') {
const user = allItems.value.find(u => u.id === item.user_id && u.type === 'user');
return {
...item,
username: user ? user.username : 'unknown',
displayname: user ? user.displayname : 'unknown',
};
}
return item;
});
feed2.value = combinedFilteredItems;
const searchTerm = usr_search.value ? usr_search.value.toLowerCase() : '';
if (!searchTerm) {
feed.value = [...feed2.value];
return;
}
feed.value = feed2.value.filter(item => {
switch (item.type) {
case "user":
return (item.displayname && item.displayname.toLowerCase().includes(searchTerm)) || (item.username && item.username.toLowerCase().includes(searchTerm));
case "post":
return (item.post_text && item.post_text.toLowerCase().includes(searchTerm)) || (item.username && item.username.toLowerCase().includes(searchTerm) || item.displayname && item.displayname.toLowerCase().includes(searchTerm));
default:
return false;
}
});
}
async function addLike(post_id: string | number, user_id: number, index: number) {
try {
feed.value[index].likes++;
const response = await fetch(`http://localhost:8000/api/post/${post_id}/like`, {
method: 'POST',
headers: {'content-type': 'application/json'},
body: `{"userId":${user_id}}`,
});
if (!response.ok) {
const errorText = await response.text();
console.error('Server-Fehlertext:', errorText);
throw new Error(`HTTP error! status: ${response.status}, text: ${errorText}`);
}
const data = await response.json();
return data;
} catch (error) {
console.error('Fehler beim Liken des Posts:', error);
throw error;
}
}
function gotoPost(post_id: string ) {
localStorage.setItem("viewedpost", post_id);
router.push(`/post/${post_id}`);
console.log("goto post: " + post_id);
}
function copyLink(post_id: string | number) {
const tocopy = `http://localhost:5173/post/${post_id}`;
navigator.clipboard.writeText(tocopy);
alert("Copied to clipboard");
}
function gotoProfile(user_id: string | number) {
router.push(`/profile/${user_id}`);
}
</script>
<template>
<div>
<div class="flex justify-center">
<div class="w-1/2">
<form @submit.prevent="go_fs" class="flex flex-col">
<input type="text" placeholder="Search..." class="w-full m-2 mt-6 p-4 bg-grau-dunkel focus:outline-none rounded-xl placeholder:text-center text-center" v-model="usr_search">
<div class="flex justify-center text-grau2">
<label class="m-2 accent-button-farbe"><input type="checkbox" class="mr-1" v-model="search_filter_status.u">User</label>
<label class="m-2 accent-button-farbe"><input type="checkbox" class="mr-1" v-model="search_filter_status.p">Post</label>
</div>
<button class="text-schwarz pl-1 mx-1 px-1 rounded-lg bg-button-farbe w-1/2 place-self-center">Filter /Search </button>
</form>
</div>
</div>
<div>
<div>
<a class="text-2xl flex justify-center mt-4 pt-2 p-3 border-b-grau2 border-b">Result(s):</a>
</div>
<div v-if="feed.length > 0 && searched == true" class="sm:overflow-y-auto sm:h-[650px] sm:scrollbar">
<div v-for="(bing, i) in feed" :key="bing.id">
<div v-if="bing.type === 'user'" class="pt-2 p-3 border-b-grau2 border-b">
<div class="flex">
<img v-if="bing.user_id != '99' " src="../../assets/default_pp.png" alt="" class="rounded-full w-16 h-16">
<img v-else src="../../assets/danielvici_pp.png" alt="" class="rounded-full w-16 h-16">
<div class="">
<a class="text-lg m-1">{{ bing.displayname }}</a>
<a class="text-base m-1 text-grau2">@{{ bing.username }}</a>
<div>
<a class=" text-sm m-3 text-weiss" v-if="bing.bio">{{ bing.bio }}</a>
<a class=" text-sm m-3 text-grau2 italic" v-else>User has no bio.</a>
</div>
<button @click="gotoProfile(bing.user_id)" class="text-schwarz pl-1 mx-1 px-1 rounded-lg bg-button-farbe"> Go to Profile</button>
</div>
</div>
</div>
<div v-else-if="bing.type === 'post'" class="flex p-3 border-b-grau2 border-b "> <!-- POSTS -->
<img v-if="bing.user_id != '99' " src="../../assets/default_pp.png" alt="" class="rounded-full w-16 h-16">
<img v-else src="../../assets/danielvici_pp.png" alt="" class="rounded-full w-16 h-16">
<div>
<div>
<label class="text-lg m-1 text-weiss">{{ bing.displayname }}</label>
<label class="text-base m-1 text-grau2">@{{ bing.username }}</label>
</div>
<div class="m-2">
<p class="text-sm m-1 text-weiss">{{ bing.post_text }}</p>
</div>
<div class="sm:flex pt-2">
<div class="flex">
<div class="flex">
<img src="../../assets/icons/comment.png" alt="" class="rounded-full align-middle">
<label class="text-sm m-1 text-weiss" v-if="bing.comments != undefined">{{ bing.comments }}</label>
<label class="text-sm m-1 text-weiss" v-else>Comments disabled</label>
</div>
<div class="flex items-center" @click="addLike(bing.posts_uuid, bing.user_id, i)">
<img alt="" src="../../assets/icons/herz.png" class="align-middle">
<label class="text-sm m-1 text-weiss">{{ bing.likes }}</label>
</div>
</div>
<br class="sm:hidden">
<div class="flex sm:tems-center mx-2"> <!-- View Post -->
<button @click="gotoPost(bing.posts_uuid)" class="text-schwarz mx-1 px-1 rounded-lg bg-button-farbe">View Post</button>
<button @click="copyLink(bing.posts_uuid)" class="text-schwarz pl-1 mx-1 px-1 rounded-lg bg-button-farbe">Share Post</button>
<button @click="gotoProfile(bing.user_id)" class="text-schwarz pl-1 mx-1 px-1 rounded-lg bg-button-farbe"> Go to Profile</button>
</div><!-- ENDE -->
</div>
</div>
</div>
</div>
</div>
<div v-else-if="searched == false" class="flex justify-center">
<a class="text-2xl text-logo-farbe-rot p-10 text-center">To see results you have to search</a>
</div>
<div v-else class="flex justify-center">
<a class="text-xl text-logo-farbe-rot p-10 text-center">No Results Found</a>
</div>
</div>
</div>
</template>
<style scoped>
</style>

View File

@@ -0,0 +1,35 @@
<script setup lang="ts">
import { ref } from 'vue';
import Legal from "./home_components/legal.vue";
import Settings_sidebar from "./settings_components/settings_sidebar.vue";
import Settings_main from "./settings_components/settings_main.vue";
import Navigationbar from "./home_components/navigationbar.vue";
import Contacts from "./home_components/contacts.vue";
const selectedSetting = ref('');
// Aktualisieren der ausgewählten Einstellung
function handleUpdateSetting(setting: string) {
selectedSetting.value = setting;
console.log(`Setting got (S): ${setting}`);
}
</script>
<template>
<div id="main" class="bg-hintergrund-farbe sm:flex">
<div id="left" class="sm:w-72 min-w-72">
<navigationbar></navigationbar>
<settings_sidebar @updateSetting="handleUpdateSetting"></settings_sidebar>
</div>
<div class="w-100p sm:border-x-grau2 sm:border-x-2 my-2">
<settings_main :selectedSetting="selectedSetting"></settings_main>
</div>
<div class="sm:w-1/4">
<contacts></contacts>
<legal></legal>
</div>
</div>
</template>
<style scoped>
</style>

View File

@@ -0,0 +1,82 @@
<script setup>
import { ref, onMounted } from "vue";
let self = ref(null); // Initialisiere self mit null
let self_id = ref();
async function getSelf() {
try {
const response = await fetch('http://localhost:8000/api/users', { method: 'GET' });
if (!response.ok) {
console.error(`HTTP-Fehler! Status: ${response.status}`);
self.value = null;
return;
}
const users = await response.json();
const foundUser = users.find(user => {
return String(user.user_id) === self_id.value;
});
if (foundUser) {
self.value = foundUser;
} else {
self.value = null;
}
} catch (error) {
console.error('ERROR:', error);
self.value = null;
}
}
onMounted(() => {
self_id.value = localStorage.getItem('self_id');
getSelf();
console.log("SELF ID (onMounted):", self_id.value);
});
</script>
<template>
<div class="space-y-2 pl-2" v-if="self">
<p class="text-2xl text-weiss pt-2 items-center flex justify-center">{{ self.displayname }}</p>
<div>
<a class="text-lg text-weiss">Username</a><br>
<a class="text-grau2">{{ self.username }}</a>
</div>
<div>
<a class="text-lg text-weiss">User created at</a><br>
<a class="text-grau2">{{ self.account_created }}</a>
</div>
<div>
<a class="text-lg text-weiss">Email</a><br>
<a class="text-grau2">{{ self.user_email }}</a>
</div>
<div>
<a class="text-lg text-weiss">User ID</a><br>
<a class="text-grau2">{{ self.user_id }}</a>
</div>
<div>
<a class="text-lg text-weiss">Firstname</a><br>
<a class="text-grau2">{{ self.firstname }}</a>
</div>
<div>
<a class="text-lg text-weiss">Surname</a><br>
<a class="text-grau2">{{ self.surname }}</a>
</div>
<div>
<a class="text-lg text-weiss">Bio</a><br>
<a class="text-grau2">{{ self.bio }}</a>
</div>
</div>
<div v-else class="flex justify-center items-center">
<p class="text-2xl text-weiss pt-2 items-center flex justify-center">User data is being loaded or does not exist</p>
</div>
</template>
<style scoped>
</style>

View File

@@ -0,0 +1,37 @@
<script setup lang="ts">
import router from "../../router";
function deleteAccount() {
// alert("Account deleted. You will be redirected to the login page.");
router.push("/login");
}
function handleSubmit(event: Event) {
event.preventDefault();
deleteAccount();
}
</script>
<template>
<div class="flex-col flex items-center justify-center">
<div> <!-- HEADER -->
<h1 class="text-weiß text-2xl p-4">Delete Account</h1>
</div>
<div>
<form class="flex flex-col space-y-4 p-4" @submit.prevent="handleSubmit">
<div class="flex flex-col space-y-2 items-center justify-center">
<input type="password" name="password" id="password" placeholder="Password" required class="m-2 w-full max-w-xs bg-grau-dunkel p-4 text-weiss placeholder-grau2 focus:outline-none rounded-lg">
</div>
<div>
<input type="checkbox" name="verification" id="verification" required class="">
<label for="verification" class="p-4">I agree that my account will be deleted permanently.</label>
</div>
<button type="submit" class="bg-logo-farbe-rot text-schwarz text-xl font-bold p-4 mt-8 rounded-lg">Delete Account</button>
</form>
</div>
</div>
</template>
<style scoped>
</style>

View File

@@ -0,0 +1,39 @@
<script setup lang="ts">
import router from "../../router";
function reset_password() {
// alert("Account deleted. You will be redirected to the login page.");
router.push("/");
}
function handleSubmit(event: Event) {
event.preventDefault();
reset_password();
}
</script>
<template>
<div>
<div> <!--HEADER-->
<h1 class="text-weiss text-2xl p-3 items-center flex justify-center">Reset Password</h1>
</div>
<div>
<form class="flex flex-col space-y-4 p-4" @submit.prevent="handleSubmit">
<div class="flex flex-col space-y-2">
<input type="password" name="old-password" id="old-password" placeholder="Old Password" class="m-2 w-full max-w-xs bg-grau-dunkel p-4 text-weiss placeholder-grau2 focus:outline-none rounded-lg invalid:border-logo-farbe-rot invalid:border-2">
</div>
<div class="flex flex-col space-y-2">
<input type="password" name="new-password" id="new-password" placeholder="New Passoword" class="m-2 w-full max-w-xs bg-grau-dunkel p-4 text-weiss placeholder-grau2 focus:outline-none rounded-lg invalid:border-logo-farbe-rot invalid:border-2">
</div>
<div class="flex flex-col space-y-2">
<input type="password" name="new-password-repeat" id="new-password-repeat" placeholder=" Repeat new passoword" class="mt-4 m-2 w-full max-w-xs bg-grau-dunkel p-4 text-weiss placeholder-grau2 focus:outline-none rounded-lg invalid:border-logo-farbe-rot invalid:border-2">
</div>
<button type="submit" class="bg-button-farbe text-schwarz text-xl font-bold p-4 mt-8 rounded-lg">Change Password</button>
</form>
</div>
</div>
</template>
<style scoped>
</style>

View File

@@ -0,0 +1,25 @@
<script setup lang="ts">
import {defineEmits, ref} from 'vue';
const emit = defineEmits(['updateAccountSetting']);
const selectedSetting = ref('');
function updateAccountSetting(setting: string) {
emit('updateAccountSetting', setting);
selectedSetting.value = setting;
console.log(`Setting selected (SA): ${setting}`);
}
</script>
<template>
<div class=""> <!-- MAIN DIV -->
<div class="flex flex-col text-xl pl-4 pt-5 pr-4 space-y-5 text-grau2"> <!-- BODY -->
<label @click="updateAccountSetting('sa_account-information')" :class="{'text-weiss': selectedSetting === 'sa_account-information' || selectedSetting === ''}">Account Information</label>
<label @click="updateAccountSetting('sa_reset-password')" :class="{'text-weiss': selectedSetting === 'sa_reset-password'}">Reset Password</label>
<label @click="updateAccountSetting('sa_delete-account')" :class="{'text-weiss': selectedSetting === 'sa_delete-account'}">Delete Account</label>
</div>
</div>
</template>
<style scoped>
</style>

View File

@@ -0,0 +1,32 @@
<script setup lang="ts">
import {defineProps, ref} from 'vue';
import Sa_accountInformation_comp from "./sa_account-information_comp.vue";
import Sa_reset_password from "./sa_reset_password.vue";
import Sa_delete_account from "./sa_delete_account.vue";
const props2 = defineProps({
selectedAccountSetting: String
});
console.log(`Setting got (SAM): ${props2.selectedAccountSetting}`);
</script>
<template>
<div class="border-l-2 border-l-grau2"> <!-- MAIN DIV -->
<div v-if="props2.selectedAccountSetting === 'sa_account-information'">
<sa_account-information_comp></sa_account-information_comp>
</div>
<div v-else-if="props2.selectedAccountSetting === 'sa_reset-password'">
<sa_reset_password></sa_reset_password>
</div>
<div v-else-if="props2.selectedAccountSetting === 'sa_delete-account'">
<sa_delete_account></sa_delete_account>
</div>
<div v-else>
<sa_account-information_comp></sa_account-information_comp>
</div>
</div>
</template>
<style scoped>
</style>

View File

@@ -0,0 +1,77 @@
<script setup lang="ts">
import { ref } from 'vue';
import Settings_account from "./settings_account.vue";
import Settings_account_main from "./settings_account_main.vue";
import Sps_main from "./sps_main.vue";
import So_main from "./so_main.vue";
const props = defineProps({
selectedSetting: String
});
const selectedAccountSetting = ref('');
function handleUpdateAccountSetting(setting: string) {
selectedAccountSetting.value = setting;
}
console.log(`Setting got (SM): ${props.selectedSetting}`);
</script>
<template>
<div class="text-weiss">
<div v-if="props.selectedSetting === 'setting_account'"> <!-- ACCOUNT SETTINGS-->
<div class="border-r-grau2 border-r-1 border-b-grau2 border-b-2"> <!-- HEADER - ACCOUNT SETTINGS-->
<h1 class="text-weiss text-3xl p-4">Account</h1>
</div>
<div class="flex flex-row content-center justify-center autofill:"> <!-- BODY - ACCOUNT SETTINGS-->
<settings_account class="basis-1/2" @updateAccountSetting="handleUpdateAccountSetting"></settings_account>
<settings_account_main class="basis-1/2" :selectedAccountSetting="selectedAccountSetting"></settings_account_main>
</div>
</div>
<div v-else-if="props.selectedSetting === 'setting_privacy'"> <!-- PRIVACY AND SAFTEY-->
<div class="border-r-grau2 border-r-2 border-b-grau2 border-b-2"> <!-- HEADER PRIVACY-->
<h1 class="text-weiss text-3xl p-4">Privacy and Saftey</h1>
</div>
<div> <!-- BODY - ACCOUNT SETTINGS-->
<sps_main></sps_main>
</div>
</div>
<div v-else-if="props.selectedSetting === 'setting_messages'"> <!-- MESSAGES-->
<label class="text-center">Hey! So you found this setting? </label>
</div>
<div v-else-if="props.selectedSetting === 'setting_other'"> <!-- OTHER -->
<div class="border-r-grau2 border-r-1 border-b-grau2 border-b-2"> <!-- HEADER - ACCOUNT SETTINGS-->
<h1 class="text-weiss text-3xl p-4">Other</h1>
</div>
<so_main></so_main>
</div>
<div v-else-if="props.selectedSetting === undefined"> <!-- IF NOTHING SELECTED-->
<label>Select a Setting in the Sidebar</label>
</div>
<div v-else-if="props.selectedSetting === ''"> <!-- IF NOTHING SELECTED-->
<div class="border-r-grau2 border-r-1 border-b-grau2 border-b-2"> <!-- HEADER - ACCOUNT SETTINGS-->
<h1 class="text-weiss text-3xl p-4">Account</h1>
</div>
<div class="flex flex-row content-center justify-center"> <!-- BODY - ACCOUNT SETTINGS-->
<settings_account class="basis-1/2" @updateAccountSetting="handleUpdateAccountSetting"></settings_account>
<settings_account_main class="basis-1/2" :selectedAccountSetting="selectedAccountSetting"></settings_account_main>
</div>
</div>
</div>
</template>
<style scoped>
</style>

View File

@@ -0,0 +1,26 @@
<script setup lang="ts">
import { defineEmits } from 'vue';
import { ref } from 'vue';
const emit = defineEmits(['updateSetting']);
const selectedSetting = ref('');
function updateSetting(setting: string) {
selectedSetting.value = setting;
emit('updateSetting', setting);
console.log(`Setting selected (SS): ${setting}`);
}
</script>
<template>
<div class="text-grau2 font-bold space-y-4 pl-2 pt-5">
<label class="flex text-center rounded-lg" :class="{'text-weiss': selectedSetting === 'setting_account' || selectedSetting === ''}" @click="updateSetting('setting_account')"><img class="pr-2" src="../../assets/icons/user.png" alt="">Account</label>
<label class="flex text-center rounded-lg" :class="{'text-weiss': selectedSetting === 'setting_privacy'}" @click="updateSetting('setting_privacy')"><img class="pr-2" src="../../assets/icons/shield.png" alt="">Privacy and Saftey</label>
<label class="flex text-center rounded-lg" :class="{'text-weiss': selectedSetting === 'setting_message'}" @click="updateSetting('setting_messages')"><img class="pr-2" src="../../assets/icons/mail.png" alt="">Messages</label>
<label class="flex text-center rounded-lg" :class="{'text-weiss': selectedSetting === 'setting_other'}" @click="updateSetting('setting_other')"><img class="pr-2" src="../../assets/icons/other.png" alt="">Other</label>
</div>
</template>
<style scoped>
</style>

View File

@@ -0,0 +1,23 @@
<script setup lang="ts">
import router from "../../router";
function logout() {
console.log("Logout");
localStorage.setItem('isLoggedIn', 'false');
router.push("/login");
}
</script>
<template>
<div class="text-grau2 font-bold space-y-4 pl-2 pt-5 mb-4">
<label class="flex text-center shadow-2xl rounded-lg active:text-weiss" @click="logout"><img class="pr-2" src="../../assets/icons/logout.png" alt="">Logout</label>
</div>
</template>
<style scoped>
</style>

View File

@@ -0,0 +1,17 @@
<script setup>
</script>
<template>
<div class="m-3 flex flex-col">
<label class="text-xl text-grau2 m-2">Blocked Users</label>
<label class="text-xl text-grau2 m-2">Mailing</label>
</div>
</template>
<style scoped>
</style>

View File

@@ -1,10 +1,7 @@
<script setup>
import Navigationbar from "./home_components/navigationbar.vue";
import Feed from "./home_components/feed.vue";
import Contacts from "./home_components/contacts.vue";
import Legal from "./home_components/legal.vue";
import Trending from "./home_components/trending.vue";
import QuickSearch from "./home_components/quick_search.vue";
</script>
<template>
@@ -16,7 +13,6 @@ import QuickSearch from "./home_components/quick_search.vue";
<label class="text-weiss text-5xl align-middle mx-60 pt-60">This site is currently Work in Progress...</label>
</div>
<div id="right">
<quick-search> </quick-search>
<contacts></contacts>
<legal></legal>
</div>

View File

@@ -1,6 +1,5 @@
/// <reference lib="deno.ns" />
import './assets/main.css';
import { createApp } from 'vue';
import {createApp, onMounted} from 'vue';
// @ts-ignore:
import App from '../src/App.vue';
// @ts-ignore:
@@ -8,4 +7,4 @@ import router from "./router/index.ts";
const app = createApp(App);
app.use(router);
app.mount('#app');
app.mount('#app');

View File

@@ -1,37 +1,90 @@
/// <reference types="vite/client" />
// File: `src/router/index.ts`
import { createRouter, createWebHistory } from "vue-router";
// Vue components imported here.
// the vue components are the Pages that will be rendered
// at these URL's.
import Home from "../components/Home.vue";
import Home from "../../src/components/Home.vue";
import Login from "../components/Login.vue";
import wip from "../components/wip.vue";
import settings from "../components/settings.vue";
import notifications from "../components/notifications.vue";
import register from "../components/register.vue";
import search from "../components/search.vue";
import post from "../components/posts.vue";
import profile from "../components/profile.vue";
import messages from "../components/messages.vue";
// The routing does not happen automatically
// Each route has to be defined here, or it wont work.
const routes = [
{
path: "/",
name: "home",
component: Home,
meta: { requiresAuth: true }
},
{
path: "/login",
name: "login",
component: Login,
component: Login
},
{
path: "/wip",
name: "Work in Progress",
component: wip,
meta: { requiresAuth: true }
},
]
{
path: "/settings",
name: "Settings",
component: settings,
meta: { requiresAuth: true }
},
{
path: "/notifications",
name: "Notifications",
component: notifications,
meta: { requiresAuth: true }
},
{
path: "/register",
name: "Register",
component: register
},
{
path: "/search",
name: "Search",
component: search,
meta: { requiresAuth: true }
},
{
path: "/post/:id",
name: "PostDetail",
component: post,
meta: { requiresAuth: true }
},
{
path: "/profile/:username",
name: "Profile",
component: profile,
props: true,
meta: { requiresAuth: true }
},
{
path: "/messages",
name: "Messages",
component: messages,
meta: { requiresAuth: true }
}
];
const router = createRouter({
history: createWebHistory("/"),
routes,
});
export default router;
router.beforeEach((to, from, next) => {
if (to.meta.requiresAuth && localStorage.getItem("isLoggedIn") !== "true") {
alert("not logged in ");
next({ name: "login" });
} else {
next();
}
});
export default router;

View File

@@ -21,7 +21,8 @@ export default {
'35p': '30.825%',
}
},
colors: {
plugins: ['tailwind-scrollbar', 'tailwind-scrollbar-hide'],
colors: {
'logo-farbe-lila': '#5500a2',
'logo-farbe-rot': '#a2002b',
'logo-farbe-blau': '#0b1074',