Enough for today
All checks were successful
Build and Deploy Nuxt / build (push) Successful in 49s

This commit is contained in:
2026-04-26 00:52:59 +02:00
parent c3e5448597
commit b69e571202
89 changed files with 2727 additions and 58 deletions

View File

@@ -0,0 +1,39 @@
name: Build and Deploy Nuxt
on:
push:
branches: [master]
jobs:
build:
runs-on: docker
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Build frontend
run: |
docker build -t git.aranroig.com/${{ secrets.REGISTRY_USER }}/dragonroll-frontend:latest ./frontend
docker push git.aranroig.com/${{ secrets.REGISTRY_USER }}/dragonroll-frontend:latest
- name: Build backend
run: |
docker build -t git.aranroig.com/${{ secrets.REGISTRY_USER }}/dragonroll-backend:latest ./backend
docker push git.aranroig.com/${{ secrets.REGISTRY_USER }}/dragonroll-backend:latest
# - name: Copy files
# run: |
# scp docker-compose.yml deploy@${{ secrets.DEPLOY_HOST}}:/var/www/app/
# scp nginx.conf deploy@${{ secrets.DEPLOY_HOST }}:/var/www/app/nginx.conf
#- name: Deploy
# run: |
# ssh deploy@${{ secrets.DEPLOY_HOST }} << 'EOF'
# echo "${{ secrets.REGISTRY_PASSWORD }}" | docker login git.aranroig.com -u "${{ secrets.REGISTRY_USER }}" --password-stdin
# cd /var/www/app/
# docker-compose pull
# docker-compose up -d
# EOF

6
frontend/18n.config.ts Normal file
View File

@@ -0,0 +1,6 @@
// i18n.config.ts
export default defineI18nConfig(() => ({
fallbackLocale: 'en',
missingWarn: true,
fallbackWarn: true
}))

View File

@@ -1,6 +1,6 @@
<script setup> <script setup>
import { TransitionGroup } from 'vue' import { TransitionGroup } from 'vue'
import { Windows, ReloadRef, WindowMap } from '@/services/Windows'; import { Windows, ReloadRef, WindowMap, getComponent } from '@/services/Windows';
// Gestionem ventanas // Gestionem ventanas
const reload = ReloadRef(); const reload = ReloadRef();
@@ -11,7 +11,7 @@ const windows = Windows();
<template> <template>
<div class="window-container" :key="reload"> <div class="window-container" :key="reload">
<TransitionGroup name="window"> <TransitionGroup name="window">
<component v-for="win in windows" :is="WindowMap()[win.type]" :key="win.id" :data="win"></component> <component v-for="win in windows" :is="getComponent(win.type)" :key="win.id" :data="win"></component>
</TransitionGroup> </TransitionGroup>
</div> </div>
</template> </template>

View File

@@ -9,7 +9,7 @@ const handle = ref(null);
const props = defineProps(['data']); const props = defineProps(['data']);
const data = props.data; const data = props.data;
let id = data.id; let id = data.type;
const test = ref(null) const test = ref(null)

View File

@@ -20,7 +20,7 @@ const handle = ref(null);
const props = defineProps(['data']); const props = defineProps(['data']);
const data = props.data; const data = props.data;
let id = data.id; let id = data.type;
const username = ref(""); const username = ref("");
const password = ref(""); const password = ref("");
@@ -28,7 +28,6 @@ const password = ref("");
onMounted(() => { onMounted(() => {
SetupHandle(id, handle); SetupHandle(id, handle);
SetSize(id, {width: 450, height: 480}); SetSize(id, {width: 450, height: 480});
SetMovable(id, false);
SetResizable(id, false); SetResizable(id, false);
ResetPosition(id, "center"); ResetPosition(id, "center");
}); });
@@ -77,18 +76,18 @@ function toRegister(){
<form v-on:submit.prevent="login"> <form v-on:submit.prevent="login">
<div class="form-field"> <div class="form-field">
<label for="username">Username</label> <label for="username">{{$t('login.username')}}</label>
<input id="username-field" type="text" placeholder="Enter your username here..." name="username" v-model="username" autocomplete="off" > <input id="username-field" type="text" :placeholder="$t('login.username-placeholder')" name="username" v-model="username" autocomplete="off" >
</div> </div>
<div class="form-field"> <div class="form-field">
<label for="password">Password</label> <label for="password">{{$t('login.password')}}</label>
<input id="password-field" type="password" placeholder="Enter your password..." name="password" v-model="password" autocomplete="off" > <input id="password-field" type="password" :placeholder="$t('login.password-placeholder')" name="password" v-model="password" autocomplete="off" >
</div> </div>
<div class="form-field"> <div class="form-field">
<button class="btn-primary sound-click">Log in</button> <button class="btn-primary sound-click">{{$t('login.log-in')}}</button>
</div> </div>
<div class="form-field center"> <div class="form-field center">
<p>You don't have an account? <a href="#" @click.prevent="toRegister">Register</a></p> <p>{{$t('login.no-account')}} <a href="#" @click.prevent="toRegister">{{$t('login.register')}}</a></p>
</div> </div>
</form> </form>

View File

@@ -9,7 +9,7 @@ const handle = ref(null);
const props = defineProps(['data']); const props = defineProps(['data']);
const data = props.data; const data = props.data;
let id = data.id; let id = data.type;
const test = ref(null) const test = ref(null)

View File

@@ -5,6 +5,7 @@ import { GetWindowWithId, ClearWindow, Windows } from '@/services/Windows';
import ArrowLeftIcon from '/icons/iconoir/regular/arrow-left.svg'; import ArrowLeftIcon from '/icons/iconoir/regular/arrow-left.svg';
import XMarkIcon from '/icons/iconoir/regular/xmark.svg'; import XMarkIcon from '/icons/iconoir/regular/xmark.svg';
import ResizeHandleIcon from '/icons/ui/resize-handle.svg'; import ResizeHandleIcon from '/icons/ui/resize-handle.svg';
import { AddSound } from '~/services/Sound';
const props = defineProps(['window', 'handleHeight', 'custom', 'color']); const props = defineProps(['window', 'handleHeight', 'custom', 'color']);
const id = props.window; const id = props.window;
@@ -25,7 +26,7 @@ let backFunction;
function setupHandle() { function setupHandle() {
let win = GetWindowWithId(id); let win = GetWindowWithId(id);
if(win.title) title.value = win.title; if(win.title) title.value = $t(win.title);
if(win.close){ if(win.close){
close.value = true; close.value = true;
closeAction = win.close; closeAction = win.close;
@@ -43,6 +44,8 @@ function setupHandle() {
// Setup sounds // Setup sounds
let currentWindowId = "window-wrapper-" + id; let currentWindowId = "window-wrapper-" + id;
let currentWindow = document.getElementById(currentWindowId); let currentWindow = document.getElementById(currentWindowId);
AddSound(currentWindow);
} }
function CloseButton(){ function CloseButton(){

View File

@@ -0,0 +1,15 @@
function AddSound(element){
let soundClicks = element.getElementsByClassName("sound-click");
for (let i = 0; i < soundClicks.length; i++) {
soundClicks[i].addEventListener("click", async (event) => {
const audio = new Audio('/sounds/snap.wav');
audio.type = "audio/wav"
audio.play().catch((e)=>{});
})
}
}
export {
AddSound
};

View File

@@ -0,0 +1,24 @@
/*
Put here all dragonroll windows
*/
const defWindows = {
login: {
title: 'Login',
movable: false,
component: () => import('~/components/windows/LoginWindow.vue'),
},
register: {
title: 'Register',
movable: false,
component: () => import('~/components/windows/RegisterWindow.vue'),
},
example: {
title: 'Example',
component: () => import('~/components/windows/ExampleWindow.vue'),
}
}
export {
defWindows
}

View File

@@ -1,33 +1,9 @@
import { ref } from 'vue' import { ref } from 'vue'
import { defWindows } from './WindowDefinitions';
const windows = ref([]); const windows = ref([]);
import LoginWindow from '~/components/windows/LoginWindow.vue'; const getComponent = (type) => defineAsyncComponent(defWindows[type]?.component)
import RegisterWindow from '~/components/windows/RegisterWindow.vue';
import ExampleWindow from '~/components/windows/ExampleWindow.vue';
let windowMap = {
login: LoginWindow,
register: RegisterWindow,
example: ExampleWindow
};
// Presets
const defValues = {
'example': {
id: "example",
title: "Example",
close: () => ClearWindow('example')
},
'login': {
id: 'login',
title: 'Login',
},
'register': {
id: 'register',
title: 'Register'
}
}
const reload = ref(0); const reload = ref(0);
@@ -36,11 +12,12 @@ let Windows = () => { return windows };
let WindowMap = () => { return windowMap }; let WindowMap = () => { return windowMap };
let currentIndex = 10; let currentIndex = 10;
let currentId = 0;
function SetupHandle(id, handle) { function SetupHandle(id, handle) {
// Update window info with handle info // Update window info with handle info
console.log(id);
let win = GetWindowWithId(id); let win = GetWindowWithId(id);
let currentWindowId = "window-wrapper-" + id; let currentWindowId = "window-wrapper-" + id;
@@ -68,7 +45,7 @@ function SetupHandle(id, handle) {
// Move window listeners // Move window listeners
handler.addEventListener("mousedown", (event) => { handler.addEventListener("mousedown", (event) => {
if(win.noMove) return; if(!win.movable) return;
draggingWindow = true; draggingWindow = true;
let windowRect = currentWindow.getBoundingClientRect(); let windowRect = currentWindow.getBoundingClientRect();
@@ -77,7 +54,7 @@ function SetupHandle(id, handle) {
}) })
document.addEventListener("mousemove", (event) => { document.addEventListener("mousemove", (event) => {
if(win.noMove) return; if(!win.movable) return;
if (!draggingWindow) return; if (!draggingWindow) return;
if (event.clientX + offsetX < -currentWindow.getBoundingClientRect().width + 20) currentWindow.style.left = (-currentWindow.getBoundingClientRect().width + 20) + "px"; if (event.clientX + offsetX < -currentWindow.getBoundingClientRect().width + 20) currentWindow.style.left = (-currentWindow.getBoundingClientRect().width + 20) + "px";
@@ -90,7 +67,7 @@ function SetupHandle(id, handle) {
}) })
document.addEventListener("mouseup", (event) => { document.addEventListener("mouseup", (event) => {
if(win.noMove) return; if(!win.movable) return;
draggingWindow = false; draggingWindow = false;
// ummm suposo que no pots tancar mentres mous? // ummm suposo que no pots tancar mentres mous?
SaveWindowPos({ id, x: parseInt(currentWindow.style.left, 10), y: parseInt(currentWindow.style.top, 10) }); SaveWindowPos({ id, x: parseInt(currentWindow.style.left, 10), y: parseInt(currentWindow.style.top, 10) });
@@ -128,6 +105,13 @@ function SetupHandle(id, handle) {
win.height = parseInt(currentWindow.style.height, 10); win.height = parseInt(currentWindow.style.height, 10);
}); });
// Should move eventually?
window.addEventListener('resize', (event) => {
if(!win.movable){
ResetPosition(id, "center");
}
})
handle.value.setupHandle(); handle.value.setupHandle();
} }
@@ -138,7 +122,7 @@ function SetResizable(id, resizable) {
function SetMovable(id, movable) { function SetMovable(id, movable) {
let win = GetWindowWithId(id); let win = GetWindowWithId(id);
win.noMove = !movable; win.movable = movable;
} }
function SetSize(id, size) { function SetSize(id, size) {
@@ -211,35 +195,38 @@ function ResetPosition(id, pos) {
function CreateWindow(type, data = {}) { function CreateWindow(type, data = {}) {
let finalData = { ...{ type }, ...defValues[type], ...data } let finalData = { ...{ type, id: currentId }, ...defWindows[type], ...data }
console.log(finalData); console.log(finalData);
let contains = false; let contains = false;
for (let i = 0; i < windows.value.length; i++) { for (let i = 0; i < windows.value.length; i++) {
if (windows.value[i].id == finalData.id) { if (windows.value[i].type == finalData.type) {
contains = true; contains = true;
console.log("It contains")
break; break;
} }
} }
if (!contains) { if (!contains) {
windows.value.push(finalData); windows.value.push(finalData);
console.log("Pushed ", finalData.id); currentId++;
console.log(finalData);
console.log("Pushed ", finalData.type);
// reload.value += 1; // reload.value += 1;
console.log(windows.value); console.log(windows.value);
setTimeout(() => { setTimeout(() => {
SetOnTop(finalData.id); SetOnTop(finalData.type);
if (finalData.create) finalData.create(); if (finalData.create) finalData.create();
}, 0); }, 0);
} }
} }
function CreateChildWindow(parentId, type, data = {}) { function CreateChildWindow(parentId, type, data = {}) {
let finalData = { ...{ type }, ...defValues[type], ...data } let finalData = { ...{ type }, ...defWindows[type], ...data }
let parent = GetWindowWithId(parentId); let parent = GetWindowWithId(parentId);
if (parent.children) parent.children.push(finalData.id); if (parent.children) parent.children.push(finalData.type);
else parent.children = [finalData.id]; else parent.children = [finalData.type];
CreateWindow(type, data); CreateWindow(type, data);
} }
@@ -251,22 +238,23 @@ function ClearAll() {
function ClearWindows(data) { function ClearWindows(data) {
for (let i = 0; i < windows.value.length; i++) { for (let i = 0; i < windows.value.length; i++) {
ClearWindow(windows.value[i].id); ClearWindow(windows.value[i].type);
} }
// reload.value += 1; // reload.value += 1;
} }
function ClearWindow(id) { function ClearWindow(id) {
let win = GetWindowWithId(id); let win = GetWindowWithId(id);
console.log(win);
if (!win) return; if (!win) return;
if (win.children) for (let i = 0; i < win.children.length; i++) ClearWindow(win.children[i]); if (win.children) for (let i = 0; i < win.children.length; i++) ClearWindow(win.children[i]);
windows.value = windows.value.filter((e) => { return e.id !== id }); windows.value = windows.value.filter((e) => { return e.type !== id });
// reload.value += 1; // reload.value += 1;
} }
function GetWindowWithId(id) { function GetWindowWithId(id) {
for (let i = 0; i < windows.value.length; i++) { for (let i = 0; i < windows.value.length; i++) {
if (windows.value[i].id == id) { if (windows.value[i].type == id) {
return windows.value[i]; return windows.value[i];
} }
} }
@@ -288,8 +276,10 @@ function SetOnTop(id) {
let currentWindowId = "window-wrapper-" + id; let currentWindowId = "window-wrapper-" + id;
let currentWindow = document.getElementById(currentWindowId); let currentWindow = document.getElementById(currentWindowId);
try {
currentIndex += 1; currentIndex += 1;
currentWindow.style.zIndex = currentIndex; currentWindow.style.zIndex = currentIndex;
} catch(e) {}
} }
export { export {
@@ -312,5 +302,6 @@ export {
SaveWindowPos, SaveWindowPos,
GetPosition, GetPosition,
ClearWindow, ClearWindow,
ClearAll ClearAll,
getComponent
} }

View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1,16 @@
{
"windows": {
"login": "Login",
"register": "Register",
"example": "Example Window"
},
"login": {
"username": "Username",
"username-placeholder": "Enter your username here...",
"password": "Password",
"password-placeholder": "Enter your password...",
"log-in": "Log in",
"no-account": "You don't have an account?",
"register": "Register"
}
}

View File

@@ -0,0 +1 @@
{}

View File

@@ -5,7 +5,8 @@ export default defineNuxtConfig({
include: [ include: [
'@vue/devtools-core', '@vue/devtools-core',
'@vue/devtools-kit', '@vue/devtools-kit',
'axios' 'axios',
'mitt'
] ]
} }
}, },
@@ -24,4 +25,16 @@ export default defineNuxtConfig({
apiBaseUrl: process.env.API_BASE_URL || 'http://localhost:5000/api' apiBaseUrl: process.env.API_BASE_URL || 'http://localhost:5000/api'
} }
}, },
i18n: {
locales: [
{ code: 'en', iso: 'en-US', name: '🇺🇸 English', file: 'en.json' },
{ code: 'es', iso: 'es-ES', name: '🇪🇸 Spanish', file: 'es.json' },
{ code: 'ca', iso: 'ca-ES', name: '🇦🇩 Catalan', file: 'ca.json' }
],
defaultLocale: 'en',
vueI18n: './i18n.config.ts',
langDir: 'locales/'
},
modules: ['@nuxtjs/i18n']
}) })

File diff suppressed because it is too large Load Diff

View File

@@ -10,6 +10,7 @@
"postinstall": "nuxt prepare" "postinstall": "nuxt prepare"
}, },
"dependencies": { "dependencies": {
"@nuxtjs/i18n": "^10.3.0",
"axios": "^1.15.2", "axios": "^1.15.2",
"mitt": "^3.0.1", "mitt": "^3.0.1",
"nuxt": "^4.4.2", "nuxt": "^4.4.2",

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.