From d6a26c0d09c6653d4039976cbc9bb754ace4691e Mon Sep 17 00:00:00 2001 From: eiiko6 Date: Mon, 15 Dec 2025 14:50:50 +0100 Subject: [PATCH] created frontend with login, room listing and creation, and message page with all features --- package.json | 10 +- src/App.vue | 158 +----------------------------- src/api/client.ts | 27 +++++ src/api/messages.ts | 17 ++++ src/api/rooms.ts | 14 +++ src/base.css | 73 ++++++++++++++ src/components/CreateRoomForm.vue | 21 ++++ src/components/MessageInput.vue | 58 +++++++++++ src/components/MessageList.vue | 31 ++++++ src/main.ts | 13 ++- src/pages/ChatPage.vue | 78 +++++++++++++++ src/pages/LoginPage.vue | 58 +++++++++++ src/pages/RoomsPage.vue | 83 ++++++++++++++++ src/router/index.ts | 25 +++++ src/stores/auth.ts | 32 ++++++ src/types/api.ts | 17 ++++ vite.config.ts | 8 +- yarn.lock | 95 ++++++++++++++++++ 18 files changed, 653 insertions(+), 165 deletions(-) create mode 100644 src/api/client.ts create mode 100644 src/api/messages.ts create mode 100644 src/api/rooms.ts create mode 100644 src/base.css create mode 100644 src/components/CreateRoomForm.vue create mode 100644 src/components/MessageInput.vue create mode 100644 src/components/MessageList.vue create mode 100644 src/pages/ChatPage.vue create mode 100644 src/pages/LoginPage.vue create mode 100644 src/pages/RoomsPage.vue create mode 100644 src/router/index.ts create mode 100644 src/stores/auth.ts create mode 100644 src/types/api.ts diff --git a/package.json b/package.json index b2e016b..174cb29 100644 --- a/package.json +++ b/package.json @@ -10,15 +10,17 @@ "tauri": "tauri" }, "dependencies": { - "vue": "^3.5.13", "@tauri-apps/api": "^2", - "@tauri-apps/plugin-opener": "^2" + "@tauri-apps/plugin-opener": "^2", + "pinia": "^3.0.4", + "vue": "^3.5.13", + "vue-router": "^4.6.4" }, "devDependencies": { + "@tauri-apps/cli": "^2", "@vitejs/plugin-vue": "^5.2.1", "typescript": "~5.6.2", "vite": "^6.0.3", - "vue-tsc": "^2.1.10", - "@tauri-apps/cli": "^2" + "vue-tsc": "^2.1.10" } } diff --git a/src/App.vue b/src/App.vue index b209212..5273d94 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,160 +1,10 @@ - - - \ No newline at end of file diff --git a/src/api/client.ts b/src/api/client.ts new file mode 100644 index 0000000..f0c094f --- /dev/null +++ b/src/api/client.ts @@ -0,0 +1,27 @@ +import { useAuthStore } from '../stores/auth' + +const BASE_URL = 'http://localhost:8080' + +export async function apiFetch( + path: string, + options: RequestInit = {} +): Promise { + const auth = useAuthStore() + + const res = await fetch(`${BASE_URL}${path}`, { + ...options, + headers: { + 'Content-Type': 'application/json', + ...(auth.token ? { Authorization: `Bearer ${auth.token}` } : {}), + ...options.headers, + }, + }) + + if (!res.ok) { + const text = await res.text() + throw new Error(text || res.statusText) + } + + return res.json() +} + diff --git a/src/api/messages.ts b/src/api/messages.ts new file mode 100644 index 0000000..2d9e116 --- /dev/null +++ b/src/api/messages.ts @@ -0,0 +1,17 @@ +import { apiFetch } from './client' +import type { Message } from '../types/api' + +export function fetchMessages(roomUuid: string) { + return apiFetch(`/messages/${roomUuid}`) +} + +export function sendMessage(roomUuid: string, content: string) { + return apiFetch(`/messages/${roomUuid}`, { + method: 'POST', + body: JSON.stringify({ + message_type: 'text', + content, + }), + }) +} + diff --git a/src/api/rooms.ts b/src/api/rooms.ts new file mode 100644 index 0000000..bf5abf9 --- /dev/null +++ b/src/api/rooms.ts @@ -0,0 +1,14 @@ +import { apiFetch } from './client' +import type { Room } from '../types/api' + +export function fetchRooms(userUuid: string) { + return apiFetch(`/rooms/${userUuid}`) +} + +export function createRoom(name: string) { + return apiFetch('/rooms', { + method: 'POST', + body: JSON.stringify({ name }), + }) +} + diff --git a/src/base.css b/src/base.css new file mode 100644 index 0000000..c02dbc6 --- /dev/null +++ b/src/base.css @@ -0,0 +1,73 @@ +/* ---- CSS reset (minimal, modern) ---- */ +*, +*::before, +*::after { + box-sizing: border-box; +} + +* { + margin: 0; + padding: 0; +} + +html, +body, +#app { + height: 100%; +} + +/* ---- Theme variables ---- */ +:root { + --bg: #0f1115; + --panel: #171923; + --text: #e6e6eb; + --muted: #9aa0a6; + --accent: #f27aa2; + --accent-hover: #ff91b4; + --border: #2a2f3a; + --radius: 8px; +} + +/* ---- Base ---- */ +body { + font-family: system-ui, -apple-system, BlinkMacSystemFont, + "Segoe UI", sans-serif; + background-color: var(--bg); + color: var(--text); + line-height: 1.5; +} + +/* ---- Inputs & buttons ---- */ +input, textarea { + background: var(--panel); + border: 1px solid var(--border); + color: var(--text); + padding: 0.6rem 0.7rem; + border-radius: var(--radius); + font-size: 0.95rem; +} + +input::placeholder, textarea::placeholder { + color: var(--muted); +} + +input:focus, textarea:focus { + outline: none; + border-color: var(--accent); +} + +button { + font-size: 1rem; + background: var(--accent); + color: #0b0d12; + border: none; + border-radius: var(--radius); + padding: 0.6rem 1rem; + font-weight: 600; + cursor: pointer; +} + +button:hover { + background: var(--accent-hover); +} + diff --git a/src/components/CreateRoomForm.vue b/src/components/CreateRoomForm.vue new file mode 100644 index 0000000..735b0e2 --- /dev/null +++ b/src/components/CreateRoomForm.vue @@ -0,0 +1,21 @@ + + + diff --git a/src/components/MessageInput.vue b/src/components/MessageInput.vue new file mode 100644 index 0000000..6712184 --- /dev/null +++ b/src/components/MessageInput.vue @@ -0,0 +1,58 @@ + + + + + + diff --git a/src/components/MessageList.vue b/src/components/MessageList.vue new file mode 100644 index 0000000..f590f4c --- /dev/null +++ b/src/components/MessageList.vue @@ -0,0 +1,31 @@ + + + + + diff --git a/src/main.ts b/src/main.ts index b670de8..a0d11fb 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,4 +1,11 @@ -import { createApp } from "vue"; -import App from "./App.vue"; +import { createApp } from 'vue' +import { createPinia } from 'pinia' +import router from './router' +import App from './App.vue' -createApp(App).mount("#app"); +import './base.css' + +createApp(App) + .use(createPinia()) + .use(router) + .mount('#app') diff --git a/src/pages/ChatPage.vue b/src/pages/ChatPage.vue new file mode 100644 index 0000000..38ed231 --- /dev/null +++ b/src/pages/ChatPage.vue @@ -0,0 +1,78 @@ + + + + + diff --git a/src/pages/LoginPage.vue b/src/pages/LoginPage.vue new file mode 100644 index 0000000..784178e --- /dev/null +++ b/src/pages/LoginPage.vue @@ -0,0 +1,58 @@ + + + + + diff --git a/src/pages/RoomsPage.vue b/src/pages/RoomsPage.vue new file mode 100644 index 0000000..c371d67 --- /dev/null +++ b/src/pages/RoomsPage.vue @@ -0,0 +1,83 @@ + + + + + + + diff --git a/src/router/index.ts b/src/router/index.ts new file mode 100644 index 0000000..0c5a2bb --- /dev/null +++ b/src/router/index.ts @@ -0,0 +1,25 @@ +import { createRouter, createWebHistory } from 'vue-router' +import { useAuthStore } from '../stores/auth' + +import LoginPage from '../pages/LoginPage.vue' +import RoomsPage from '../pages/RoomsPage.vue' +import ChatPage from '../pages/ChatPage.vue' + +const router = createRouter({ + history: createWebHistory(), + routes: [ + { path: '/login', component: LoginPage }, + { path: '/', component: RoomsPage }, + { path: '/rooms/:uuid', component: ChatPage, props: true }, + ], +}) + +router.beforeEach((to) => { + const auth = useAuthStore() + if (!auth.isAuthenticated && to.path !== '/login') { + return '/login' + } +}) + +export default router + diff --git a/src/stores/auth.ts b/src/stores/auth.ts new file mode 100644 index 0000000..85de3a3 --- /dev/null +++ b/src/stores/auth.ts @@ -0,0 +1,32 @@ +import { defineStore } from 'pinia' +import type { LoginResponse } from '../types/api' +import { apiFetch } from '../api/client' + +export const useAuthStore = defineStore('auth', { + state: () => ({ + token: null as string | null, + uuid: null as string | null, + }), + + getters: { + isAuthenticated: (s) => !!s.token, + }, + + actions: { + async login(email: string, username: string, password: string) { + const res = await apiFetch('/login', { + method: 'POST', + body: JSON.stringify({ email, username, password }), + }) + + this.token = res.token + this.uuid = res.uuid + }, + + logout() { + this.token = null + this.uuid = null + }, + }, +}) + diff --git a/src/types/api.ts b/src/types/api.ts new file mode 100644 index 0000000..9dde40e --- /dev/null +++ b/src/types/api.ts @@ -0,0 +1,17 @@ +export interface LoginResponse { + uuid: string + token: string +} + +export interface Room { + uuid: string + owner: number + name: string +} + +export interface Message { + sender: string + message_type: 'text' + content: string +} + diff --git a/vite.config.ts b/vite.config.ts index 812e61c..21cfd7e 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -19,10 +19,10 @@ export default defineConfig(async () => ({ host: host || false, hmr: host ? { - protocol: "ws", - host, - port: 1421, - } + protocol: "ws", + host, + port: 1421, + } : undefined, watch: { // 3. tell Vite to ignore watching `src-tauri` diff --git a/yarn.lock b/yarn.lock index 1b679a2..1916a13 100644 --- a/yarn.lock +++ b/yarn.lock @@ -437,6 +437,38 @@ de-indent "^1.0.2" he "^1.2.0" +"@vue/devtools-api@^6.6.4": + version "6.6.4" + resolved "https://registry.yarnpkg.com/@vue/devtools-api/-/devtools-api-6.6.4.tgz#cbe97fe0162b365edc1dba80e173f90492535343" + integrity sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g== + +"@vue/devtools-api@^7.7.7": + version "7.7.9" + resolved "https://registry.yarnpkg.com/@vue/devtools-api/-/devtools-api-7.7.9.tgz#999dbea50da6b00cf59a1336f11fdc2b43d9e063" + integrity sha512-kIE8wvwlcZ6TJTbNeU2HQNtaxLx3a84aotTITUuL/4bzfPxzajGBOoqjMhwZJ8L9qFYDU/lAYMEEm11dnZOD6g== + dependencies: + "@vue/devtools-kit" "^7.7.9" + +"@vue/devtools-kit@^7.7.9": + version "7.7.9" + resolved "https://registry.yarnpkg.com/@vue/devtools-kit/-/devtools-kit-7.7.9.tgz#bc218a815616e8987df7ab3e10fc1fb3b8706c58" + integrity sha512-PyQ6odHSgiDVd4hnTP+aDk2X4gl2HmLDfiyEnn3/oV+ckFDuswRs4IbBT7vacMuGdwY/XemxBoh302ctbsptuA== + dependencies: + "@vue/devtools-shared" "^7.7.9" + birpc "^2.3.0" + hookable "^5.5.3" + mitt "^3.0.1" + perfect-debounce "^1.0.0" + speakingurl "^14.0.1" + superjson "^2.2.2" + +"@vue/devtools-shared@^7.7.9": + version "7.7.9" + resolved "https://registry.yarnpkg.com/@vue/devtools-shared/-/devtools-shared-7.7.9.tgz#fa4c096b744927081a7dda5fcf05f34b1ae6ca14" + integrity sha512-iWAb0v2WYf0QWmxCGy0seZNDPdO3Sp5+u78ORnyeonS6MT4PC7VPrryX2BpMJrwlDeaZ6BD4vP4XKjK0SZqaeA== + dependencies: + rfdc "^1.4.1" + "@vue/language-core@2.2.12": version "2.2.12" resolved "https://registry.yarnpkg.com/@vue/language-core/-/language-core-2.2.12.tgz#d01f7e865f593f968cb65c12a13d8337e65641f0" @@ -499,6 +531,11 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +birpc@^2.3.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/birpc/-/birpc-2.9.0.tgz#b59550897e4cd96a223e2a6c1475b572236ed145" + integrity sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw== + brace-expansion@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.2.tgz#54fc53237a613d854c7bd37463aad17df87214e7" @@ -506,6 +543,13 @@ brace-expansion@^2.0.1: dependencies: balanced-match "^1.0.0" +copy-anything@^4: + version "4.0.5" + resolved "https://registry.yarnpkg.com/copy-anything/-/copy-anything-4.0.5.tgz#16cabafd1ea4bb327a540b750f2b4df522825aea" + integrity sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA== + dependencies: + is-what "^5.2.0" + csstype@^3.1.3: version "3.2.3" resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.2.3.tgz#ec48c0f3e993e50648c86da559e2610995cf989a" @@ -573,6 +617,16 @@ he@^1.2.0: resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== +hookable@^5.5.3: + version "5.5.3" + resolved "https://registry.yarnpkg.com/hookable/-/hookable-5.5.3.tgz#6cfc358984a1ef991e2518cb9ed4a778bbd3215d" + integrity sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ== + +is-what@^5.2.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/is-what/-/is-what-5.5.0.tgz#a3031815757cfe1f03fed990bf6355a2d3f628c4" + integrity sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw== + magic-string@^0.30.21: version "0.30.21" resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.21.tgz#56763ec09a0fa8091df27879fd94d19078c00d91" @@ -587,6 +641,11 @@ minimatch@^9.0.3: dependencies: brace-expansion "^2.0.1" +mitt@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/mitt/-/mitt-3.0.1.tgz#ea36cf0cc30403601ae074c8f77b7092cdab36d1" + integrity sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw== + muggle-string@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/muggle-string/-/muggle-string-0.4.1.tgz#3b366bd43b32f809dc20659534dd30e7c8a0d328" @@ -602,6 +661,11 @@ path-browserify@^1.0.1: resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd" integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g== +perfect-debounce@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/perfect-debounce/-/perfect-debounce-1.0.0.tgz#9c2e8bc30b169cc984a58b7d5b28049839591d2a" + integrity sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA== + picocolors@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" @@ -612,6 +676,13 @@ picomatch@^4.0.2, picomatch@^4.0.3: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.3.tgz#796c76136d1eead715db1e7bad785dedd695a042" integrity sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q== +pinia@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/pinia/-/pinia-3.0.4.tgz#75dde12784a61e34c1fa6abcd13c1a1061c360c0" + integrity sha512-l7pqLUFTI/+ESXn6k3nu30ZIzW5E2WZF/LaHJEpoq6ElcLD+wduZoB2kBN19du6K/4FDpPMazY2wJr+IndBtQw== + dependencies: + "@vue/devtools-api" "^7.7.7" + postcss@^8.5.3, postcss@^8.5.6: version "8.5.6" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.6.tgz#2825006615a619b4f62a9e7426cc120b349a8f3c" @@ -621,6 +692,11 @@ postcss@^8.5.3, postcss@^8.5.6: picocolors "^1.1.1" source-map-js "^1.2.1" +rfdc@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.4.1.tgz#778f76c4fb731d93414e8f925fbecf64cce7f6ca" + integrity sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA== + rollup@^4.34.9: version "4.53.4" resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.53.4.tgz#5517de2593624928ac18f041b269f3b79cb64e09" @@ -657,6 +733,18 @@ source-map-js@^1.2.1: resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== +speakingurl@^14.0.1: + version "14.0.1" + resolved "https://registry.yarnpkg.com/speakingurl/-/speakingurl-14.0.1.tgz#f37ec8ddc4ab98e9600c1c9ec324a8c48d772a53" + integrity sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ== + +superjson@^2.2.2: + version "2.2.6" + resolved "https://registry.yarnpkg.com/superjson/-/superjson-2.2.6.tgz#a223a3a988172a5f9656e2063fe5f733af40d099" + integrity sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA== + dependencies: + copy-anything "^4" + tinyglobby@^0.2.13: version "0.2.15" resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.15.tgz#e228dd1e638cea993d2fdb4fcd2d4602a79951c2" @@ -689,6 +777,13 @@ vscode-uri@^3.0.8: resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-3.1.0.tgz#dd09ec5a66a38b5c3fffc774015713496d14e09c" integrity sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ== +vue-router@^4.6.4: + version "4.6.4" + resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-4.6.4.tgz#a0a9cb9ef811a106d249e4bb9313d286718020d8" + integrity sha512-Hz9q5sa33Yhduglwz6g9skT8OBPii+4bFn88w6J+J4MfEo4KRRpmiNG/hHHkdbRFlLBOqxN8y8gf2Fb0MTUgVg== + dependencies: + "@vue/devtools-api" "^6.6.4" + vue-tsc@^2.1.10: version "2.2.12" resolved "https://registry.yarnpkg.com/vue-tsc/-/vue-tsc-2.2.12.tgz#5f719b08ef7390a763c1a20169ca5c9d09d55688"