More things work now
All checks were successful
Build and Deploy Nuxt / build (push) Successful in 34s

This commit is contained in:
2026-04-27 00:42:14 +02:00
parent 2b07cc98a6
commit c7aac117c7
16 changed files with 607 additions and 59 deletions

View File

@@ -0,0 +1,61 @@
<script setup>
import { onMounted, ref, watch } from 'vue';
import { AddContextMenu, HideContextMenu } from '@/services/ContextMenu';
const props = defineProps(['options', 'onselect', 'selected']);
const options = props.options;
const selectCallback = props.onselect;
const initialSelect = props.selected;
const dropdown = ref(null);
const selected = ref(initialSelect);
onMounted(() => {
let context = [];
if(props.selected == undefined) selected.value = "undefined";
watch(() => props.selected, () => {
selected.value = props.selected;
});
options.forEach(name => {
context.push({
icon: selected.value == name ? 'icons/iconoir/regular/check.svg' : false,
name,
action: () => {
HideContextMenu();
selected.value = name;
if(selectCallback) selectCallback(name);
}
});
});
AddContextMenu(dropdown.value, context, {dropdown: true});
});
</script>
<template>
<div class="dropdown" ref="dropdown">
<span>{{ selected }}</span>
<img class="icon" src="/icons/iconoir/regular/nav-arrow-down.svg" draggable="false" ref="closeButton">
</div>
</template>
<style scoped lang="scss">
.dropdown {
flex-grow: 1;
display: flex;
background-color: var(--color-background-softer);
border: none;
padding: 4px 8px 4px 8px;
margin: 0 6px 0px 6px;
border-radius: 6px;
color: var(--color-text);
transition: 300ms background-color;
border: solid 1px var(--color-border);
.icon {
margin-left: auto;
justify-content: right;
}
}
</style>

View File

@@ -0,0 +1,101 @@
<script setup>
import { ref } from 'vue';
const props = defineProps(['rows']);
const rowDict = {}
for(let i = 0; i < props.rows.length; i++) rowDict[props.rows[i].id] = i;
let selectedTab = ref(props.rows[0].id);
function SelectTab(row){
selectedTab.value = row;
}
</script>
<template>
<div class="tab-container">
<div class="row">
<div class="toggler" :class="{ selected: row.id == selectedTab }" v-for="row in rows" v-on:click.prevent="SelectTab(row.id)">
{{ $t(row.value) }}
</div>
</div>
<div class="tab-container-outer">
<div v-for="row in rows" class="tab-content">
<TransitionGroup name="tab">
<div class="tab-content-inner" v-show="row.id == selectedTab" :key="row.id">
<slot :name="row.id" />
</div>
</TransitionGroup>
</div>
</div>
</div>
</template>
<style lang="scss">
.toggler.selected {
color: var(--color-text);
background-color: var(--color-background-softer);
}
</style>
<style scoped lang="scss">
.tab-container {
display: flex;
flex-direction: column;
height: 100%;
width: 100%;
}
.tab-container-outer {
flex-grow: 1;
display: flex;
flex-direction: column;
position: relative;
pointer-events: none;
}
.tab-enter-active,
.tab-leave-active {
transition: all 0.15s ease;
}
.tab-enter-from,
.tab-leave-to {
opacity: 0;
transform: translateY(15px);
}
.tab-content {
width: 100%;
height: 100%;
position: absolute;
overflow-y: auto;
}
.tab-content-inner {
width: 100%;
height: 100%;
pointer-events: auto;
}
.toggler {
flex-grow: 1;
flex-basis: 0;
font-weight: bold;
padding: 3px 12px 3px 12px;
font-size: 16px;
color: #9c9c9c;
border-left: 1px solid var(--color-border);
border-bottom: 1px solid var(--color-border);
border-top: 1px solid var(--color-border);
transition: color 0.2s, background-color 0.2s;
&:first-child {
border-left: none;
}
&.active {
color: var(--color-text);
}
}
</style>

View File

@@ -0,0 +1,72 @@
<script setup>
import { onMounted, watch, ref } from 'vue';
import { SetupContextMenu } from '../../services/ContextMenu';
onMounted(() => {
SetupContextMenu();
});
</script>
<template>
<div id="context-menu" class="context-menu">
<!--
<div class="context-menu-element">
<span>Hola</span> <img src="/icons/iconoir/regular/nav-arrow-right.svg">
</div>
<div class="context-menu-element">
<span>Holaa</span>
</div>
<div class="context-menu-element">
<span>Holaa</span>
</div>
<div class="context-menu-element">
<span>Holaaaaaaa</span>
</div>
-->
</div>
</template>
<style lang="scss">
.context-menu {
position: absolute;
z-index: 214748363;
flex-direction: column;
.context-menu-element {
&:last-child {
border-width: 1px 1px 1px 1px;
}
border: solid 1px var(--color-border);
border-width: 1px 1px 0px 1px;
padding: 3px 5px 3px 5px;
cursor: default;
user-select: none;
background-color: var(--tooltip-background);
transition: background-color 100ms;
display: flex;
align-items: center;
position: relative;
span {
flex-grow: 1;
padding-right: 20px;
white-space: nowrap;
}
img {
filter: invert(1);
width: 18px;
height: 18px;
}
&:hover {
background-color: var(--color-background-softest);
}
}
}
</style>

View File

@@ -55,12 +55,12 @@ function EditProfile(){
}
function EditSettings(){
ClearWindow('main_menu');
ClearWindow({type: 'main_menu'});
CreateWindow('settings', {
id: 'settings',
type: 'settings',
title: 'settings.title',
back: () => { ClearWindow('settings'); CreateWindow('main_menu'); }
back: () => { ClearWindow({type: 'settings'}); CreateWindow({type: 'main_menu'}); }
});
}

View File

@@ -1,6 +1,6 @@
<script setup>
import { onMounted, ref } from 'vue';
import { SetupHandle, SetSize, ResetPosition } from '@/services/Windows';
import { SetupHandle, SetSize, ResetPosition, Top } from '@/services/Windows';
import Server from '@/services/Server'
import { SetMinSize, SetResizable } from '@/services/Windows';
@@ -16,12 +16,14 @@ const data = props.data;
const userIcon = ref("");
const handle = ref(null);
const wrapper = ref(null);
const isAdmin = ref(false);
let id = data.id;
console.log(data);
onMounted(() => {
Top(wrapper);
SetupHandle(id, handle);
SetSize(id, {width: 500, height: 480});
ResetPosition(id, "center");
@@ -44,7 +46,7 @@ function RemoveUser(){
<template>
<div class="window-wrapper" :id="'window-wrapper-' + id">
<div class="window-wrapper" :id="'window-wrapper-' + id" ref="wrapper">
<WindowHandle :window="id" ref="handle"></WindowHandle>
<BigIconTemplate :title="data.user.username" :img="userIcon">

View File

@@ -1,10 +1,11 @@
<script setup>
import { onMounted, ref } from 'vue';
import { SetupHandle, SetSize, ResetPosition } from '@/services/Windows';
import { SetupHandle, SetSize, ResetPosition, Top } from '@/services/Windows';
import WindowHandle from './partials/WindowHandle.vue';
const handle = ref(null);
const wrapper = ref(null);
const props = defineProps(['data']);
const data = props.data;
@@ -12,6 +13,7 @@ const data = props.data;
let id = data.id;
onMounted(() => {
Top(wrapper);
SetupHandle(id, handle);
SetSize(id, {width: 500, height: 380});
ResetPosition(id, "center");
@@ -20,7 +22,7 @@ onMounted(() => {
<template>
<div class="window-wrapper" :id="'window-wrapper-' + id">
<div class="window-wrapper" :id="'window-wrapper-' + id" ref="wrapper">
<WindowHandle :window="id" ref="handle"></WindowHandle>
<!-- Body -->

View File

@@ -5,9 +5,9 @@ import {
SetSize,
ResetPosition,
SetResizable,
SetMovable,
ClearWindow,
CreateWindow,
Top,
} from '@/services/Windows';
import WindowHandle from './partials/WindowHandle.vue';
@@ -17,6 +17,7 @@ import { SetUser } from '~/services/User';
import Spinner from '../partials/Spinner.vue';
const handle = ref(null);
const wrapper = ref(null);
const props = defineProps(['data']);
const data = props.data;
@@ -29,6 +30,7 @@ const password = ref("");
const loading = ref(false);
onMounted(() => {
Top(wrapper);
SetupHandle(id, handle);
SetSize(id, {width: 450, height: 480});
SetResizable(id, false);
@@ -37,7 +39,7 @@ onMounted(() => {
function ShowMainMenu(){
CreateWindow('main_menu');
ClearWindow('login');
ClearWindow({type: 'login'});
}
function login() {
@@ -61,13 +63,13 @@ function login() {
function toRegister(){
CreateWindow('register');
ClearWindow('login');
ClearWindow({type: 'login'});
}
</script>
<template>
<div class="window-wrapper" :id="'window-wrapper-' + id">
<div class="window-wrapper" :id="'window-wrapper-' + id" ref="wrapper">
<WindowHandle :window="id" ref="handle"></WindowHandle>
<!-- Body -->

View File

@@ -1,12 +1,13 @@
<script setup>
import { onMounted, ref } from 'vue';
import { SetupHandle, SetSize, ResetPosition } from '@/services/Windows';
import { SetupHandle, SetSize, ResetPosition, Top } from '@/services/Windows';
import WindowHandle from './partials/WindowHandle.vue';
import VersionRender from '../partials/VersionRender.vue';
import EditUserPartial from '../partials/EditUserPartial.vue';
const handle = ref(null);
const wrapper = ref(null);
const props = defineProps(['data']);
const data = props.data;
@@ -14,6 +15,7 @@ const data = props.data;
let id = data.id;
onMounted(() => {
Top(wrapper);
SetupHandle(id, handle);
SetSize(id, {width: 500, height: 460});
ResetPosition(id, "center");
@@ -22,7 +24,7 @@ onMounted(() => {
<template>
<div class="window-wrapper" :id="'window-wrapper-' + id">
<div class="window-wrapper" :id="'window-wrapper-' + id" ref="wrapper">
<WindowHandle :window="id" ref="handle"></WindowHandle>
<EditUserPartial></EditUserPartial>
@@ -33,7 +35,6 @@ onMounted(() => {
<button class="btn-primary button-expand sound-click" v-on:click="OpenCampaigns" ref="campaignButton">{{ $t("main-menu.campaigns") }}</button>
</div>
<VersionRender></VersionRender>
</div>
</template>

View File

@@ -1,6 +1,6 @@
<script setup>
import { onMounted, ref } from 'vue';
import { SetupHandle, SetSize, ResetPosition, CreateWindow, ClearWindow } from '@/services/Windows';
import { SetupHandle, SetSize, ResetPosition, CreateWindow, ClearWindow, Top } from '@/services/Windows';
import WindowHandle from './partials/WindowHandle.vue';
import Spinner from '../partials/Spinner.vue';
@@ -9,6 +9,7 @@ import Server from '~/services/Server';
import { errorMessages } from 'vue/compiler-sfc';
const handle = ref(null);
const wrapper = ref(null);
const props = defineProps(['data']);
const data = props.data;
@@ -34,6 +35,7 @@ const images = [
const splashSource = ref("");
onMounted(() => {
Top(wrapper);
SetupHandle(id, handle);
SetSize(id, {width: 500});
ResetPosition(id, "center");
@@ -46,7 +48,7 @@ onMounted(() => {
function toLogin(){
CreateWindow('login');
ClearWindow('register');
ClearWindow({type: 'register'});
}
function register(){
@@ -93,7 +95,7 @@ function register(){
<template>
<div class="window-wrapper" :id="'window-wrapper-' + id">
<div class="window-wrapper" :id="'window-wrapper-' + id" ref="wrapper">
<WindowHandle :window="id" ref="handle"></WindowHandle>
<!-- Body -->

View File

@@ -0,0 +1,144 @@
<script setup>
import { onMounted, ref } from 'vue';
import WindowHandle from './partials/WindowHandle.vue';
import Tabs from '../layouts/Tabs.vue';
import Dropdown from '../layouts/Dropdown.vue';
import { GetUser, GetUserSetting, SetUserSetting } from '@/services/User';
import { SetupHandle, SetSize, ResetPosition, Top, ClearWindow, CreateWindow, SetMinSize, SetResizable } from '@/services/Windows';
const handle = ref(null);
const wrapper = ref(null);
const props = defineProps(['data']);
const data = props.data;
const id = data.id;
const rows = ref([{id: "account-settings", value: "settings.tabs.account-settings"}]);
/* TODO
const languageOptions = ref(["English", "Spanish", "Catalan"])
const langSelector = ref(null);
const currentLanguage = ref("");
*/
onBeforeMount(() => {
/*
let codes = {
"en-US": "English",
"es-ES": "Spanish",
"ca": "Catalan"
}
GetUserSetting('lang').then(value => {
currentLanguage.value = codes[value ?? 'en']
console.log(currentLanguage.value)
});
*/
if(GetUser().admin) rows.value.push({
id: "site-administration",
value: "settings.tabs.site-administration"
});
});
onMounted(() => {
Top(wrapper);
SetupHandle(id, handle);
SetSize(id, {width: 400, height: 480});
ResetPosition(id, "center");
SetResizable(id, true);
SetMinSize(id, {width: 350, height: 280});
});
function OpenManageAccounts(){
ClearWindow('settings');
CreateWindow('account_management', {
type: 'account_management',
title: 'settings.site-administration.manage-accounts.title',
id: 'account-management',
back: () => {
ClearWindow('account-management')
CreateWindow('settings', {
id: 'settings',
type: 'settings',
title: 'settings.title',
back: () => { ClearWindow('settings'); CreateWindow('main_menu'); }
});
}
})
}
function OpenManagePlugins(){
ClearWindow('settings');
CreateWindow('plugin_management', {
type: 'plugin_management',
title: 'settings.site-administration.manage-plugins.title',
id: 'plugin-management',
back: () => {
ClearWindow('plugin-management')
CreateWindow('settings', {
id: 'settings',
type: 'settings',
title: 'settings.title',
back: () => { ClearWindow('settings'); CreateWindow('main_menu'); }
});
}
})
}
</script>
<template>
<div class="window-wrapper" :id="'window-wrapper-' + id" ref="wrapper">
<WindowHandle :window="id" ref="handle"></WindowHandle>
<!-- Body -->
<Tabs :rows="rows">
<template #account-settings>
<div class="form-container">
<div class="form-element">
<label>{{ $t('settings.account.language') }}</label>
</div>
</div>
</template>
<template #site-administration>
<div class="form-element centered">
<button v-on:click.prevent="OpenManageAccounts">{{ $t('settings.site-administration.manage-accounts-button') }}</button>
</div>
<div class="form-element centered">
<button v-on:click.prevent="OpenManagePlugins">{{ $t('settings.site-administration.manage-plugins-button') }}</button>
</div>
</template>
</Tabs>
</div>
</template>
<style scoped>
.window-wrapper {
width: 100%;
display: flex;
align-items: center;
}
.splash-image {
width: 600px;
height: 250px;
}
.form-field {
padding: 10px;
display: flex;
align-items: left;
flex-direction: column;
justify-content: left;
width: 600px;
}
label {
text-align: left;
}
</style>

View File

@@ -74,13 +74,13 @@ defineExpose({
<div class="window-handle" :id="'window-handle-' + id">
<div class="left" v-if="def">
<img class="icon icon-add-margin" :src="ArrowLeftIcon" draggable="false" ref="backButton" v-if="hasBack" v-on:click="backFunction">
<img class="icon-handle icon-add-margin" :src="ArrowLeftIcon" draggable="false" ref="backButton" v-if="hasBack" v-on:click="backFunction">
</div>
<div class="center" v-if="def">
<span>{{ title }}</span>
</div>
<div class="right">
<img class="icon" :src="XMarkIcon" draggable="false" ref="closeButton" v-if="close" v-on:click="CloseButton">
<img class="icon-handle" :src="XMarkIcon" draggable="false" ref="closeButton" v-if="close" v-on:click="CloseButton">
</div>
<!-- span>{{ title }}</span>
@@ -139,4 +139,10 @@ defineExpose({
background-color: var(--color-window-handle-background);
}
.icon-handle {
width: 24px;
height: 24px;
filter: invert(var(--color-icon-invert));
}
</style>