First commit

This commit is contained in:
2026-03-27 10:14:29 +03:00
commit ad29150770
10404 changed files with 962562 additions and 0 deletions

View File

@@ -0,0 +1,64 @@
<template>
<div
@click="changeValue()"
class="select-none flex flex-row flex-nowrap items-center"
:class="getClasses"
>
<!-- Box -->
<fa-icon v-if="value === true" :icon="on" :class="iconSize"></fa-icon>
<fa-icon v-else :icon="off" :class="iconSize"></fa-icon>
<!-- Label -->
<span class="ml-1 my-1 text-primary">
<slot></slot>
</span>
</div>
</template>
<script>
export default {
props: {
value: {
type: Boolean,
required: true
},
disabled: {
type: Boolean,
default: false,
},
on: {
type: Array,
default: () => ['fa', 'toggle-on']
},
off: {
type: Array,
default: () => ['fa', 'toggle-off']
},
iconSize: {
type: String,
default: 'text-4xl'
}
},
methods: {
changeValue() {
if ( ! this.disabled) {
this.$emit("input", ! this.value);
}
}
},
computed: {
getClasses() {
let classes = this.value ? 'text-blue-300 ' : 'text-rose-400 ';
if (this.disabled) {
classes += ' cursor-not-allowed grayscale-70 opacity-70 ';
}
else {
classes += 'cursor-pointer hover:underline hover:decoration-2 ';
classes += this.value ?
'hover:text-blue-400 hover:decoration-blue-400 ' :
'hover:text-rose-600 hover:decoration-rose-600 ';
}
return classes;
}
}
}
</script>

View File

@@ -0,0 +1,69 @@
<template>
<!-- <dropdown :value="value" :index="index" @change="changeValue" ref="select"> -->
<dropdown :value="value" @change="changeValue" class="w-52" :disabled="disabled">
<option v-if="all" :value="-1">--- All Content ---</option>
<optgroup
v-for="(step, index) in content"
:key="index"
:label="step.name"
>
<option v-for="raid in filterContent(step.content)" :key="raid.id" :value="raid.id">
{{ raid.name }}
</option>
</optgroup>
</dropdown>
</template>
<script>
import Content from '@/js/content'
import Dropdown from '@/components/common/Dropdown.vue'
export default {
components: {
Dropdown,
},
props: {
value: {
//required: true, Cannot uncomment, since it can be null
type: Number
},
all: {
required: false,
type: Boolean,
default: false
},
showPrivateCategories: {
required: false,
type: Boolean,
default: true
},
disabled: {
type: Boolean,
default: false,
},
},
methods: {
filterContent(content) {
if ( ! this.showPrivateCategories) {
return content.filter(raid => raid.private !== true);
}
return content;
},
changeValue(e) {
this.$emit('change', e);
if (e.target.value === '') {
this.$emit('input', null);
}
else {
this.$emit('input', e.target.value);
}
}
},
computed: {
content() {
return Content;
}
}
}
</script>

View File

@@ -0,0 +1,115 @@
<template>
<div class="flex flex-row items-center">
<span class="pr-2">{{ category }}</span>
<span class="inline-flex flex-row flex-wrap btn-group">
<button
v-if="hasAll"
class="btn btn-sm"
:class="all ? 'btn-blue' : 'btn-white'"
@click="clickAll()"
>
All
</button>
<button
class="btn btn-sm relative"
:class="getClassesForItem(item)"
v-for="(item, index) in data_view"
:key="index"
@click="clickItem(index)"
>
{{ item.name }}
<div
v-if="count"
class="absolute w-5 -top-2 -right-1 z-10 rounded-full text-xs leading-5 tracking-tight bg-tertiary text-primary"
>
{{ count[index] > 0 ? count[index] : '-' }}
</div>
</button>
</span>
</div>
</template>
<script>
import Utils from '@/js/utils.js'
export default {
props: {
data: {
type: Array,
required: true,
},
count: {
type: Array,
default: undefined
},
category: {
type: String,
required: true,
},
hasAll: {
type: Boolean,
default: true,
}
},
data() {
return {
all: true,
data_view: [],
}
},
methods: {
clickAll() {
// check
if ( ! this.all) {
this.all = true;
this.data_view.forEach(e => e.checked = false);
this.data.forEach(e => e.checked = true);
}
},
clickItem(index) {
this.data_view[index].checked = ! this.data_view[index].checked;
if (this.all === true) {
this.all = false;
// Propagate change
for (let i=0; i<this.data.length; i++) {
this.$set(this.data[i], 'checked', this.data_view[i].checked);
}
}
else {
this.$set(this.data[index], 'checked', this.data_view[index].checked);
}
},
getClassesForItem(item) {
let classes = item.checked ? 'btn-blue ' : 'btn-white ';
if (this.count) {
classes += 'pr-3 ';
}
return classes;
},
},
created() {
if (this.hasAll === true) {
// Copy data locally to deal with the All button
this.data_view = Utils.copy(this.data);
if (this.data_view.some(e => ! e.checked)) {
this.all = false;
}
else {
this.data_view.forEach(e => e.checked = false);
this.all = true;
}
}
else {
// Data view is the same as the data
this.data_view = this.data;
// Disable All button
this.all = false;
}
},
}
</script>

View File

@@ -0,0 +1,43 @@
<template>
<div class="inline-block relative">
<select :value="value" @change="changeValue" class="block select select-sm w-full" ref="select" :disabled="disabled">
<slot></slot>
</select>
<div v-if="! disabled" class="pointer-events-none absolute inset-y-0 right-0 rounded-md flex items-center px-2 text-gray-700">
<fa-icon :icon="['fas', 'angle-down']" class="text-xl"></fa-icon>
</div>
</div>
</template>
<script>
export default {
props: {
value: {
required: false
},
index: {
type: Number,
required: false,
},
disabled: {
type: Boolean,
default: false,
},
},
methods: {
changeValue(e) {
this.$emit('change', e);
this.$emit('input', this.$refs.select.value);
}
},
watch: {
value() {
// Selected index takes some time to update. Don't batch it in changeValue
this.$nextTick().then(() => {
this.$emit('update:index', this.$refs.select.selectedIndex);
});
}
}
}
</script>

View File

@@ -0,0 +1,76 @@
<template>
<ins
class="adsbygoogle responsive_ad"
style="display:block"
:style="$isDebug ? 'border: 1px solid grey;' : ''"
data-ad-client="ca-pub-2769716391947040"
:data-ad-slot="adSlot"
:data-ad-region="ad_region"
:key="ad_region"
data-ad-format="fluid"
data-full-width-responsive="true"
></ins>
</template>
<script>
export default {
props: {
adSlot: {
required: true
},
},
data() {
return {
ad_region: null,
}
},
methods: {
getNewAds() {
if (VUE_ENV === 'server') {
return;
}
this.ad_region = 'page-' + Math.random();
this.$nextTick(() => {
try {
(window.adsbygoogle = window.adsbygoogle || []).push({})
} catch (error) {
//console.error(error)
}
});
},
},
mounted() {
this.getNewAds();
},
watch: {
'$route.path'(to, from) {
if (to !== from) {
this.getNewAds();
}
},
}
}
</script>
<style scoped>
.responsive_ad {
width: 320px;
height: 100px;
}
@media(min-width: 500px) {
.responsive_ad {
width: 468px;
height: 60px;
}
}
@media(min-width: 800px) {
.responsive_ad {
width: 728px;
height: 90px;
}
}
</style>

View File

@@ -0,0 +1,66 @@
<template>
<checkbox
v-show="team_owner && ! isMyParty"
:value="liked"
@input="clickLike"
:disabled="team_owner && ! isUserLogged"
:title="isUserLogged ? '' : 'Log in to like this team'"
:off="['far', 'circle']"
:on="['far', 'face-grin-wide']"
iconSize="text-3xl"
>
{{ liked ? 'Liked' : 'Like this team' }}
</checkbox>
</template>
<script>
import { mapState } from 'vuex'
import Checkbox from '@/components/common/Checkbox.vue'
export default {
components: {
Checkbox,
},
props: {
teamId: {
type: Number
}
},
methods: {
clickLike(e) {
if (e) {
this.axios.get('/party/like/' + this.teamId)
.then(_ => {
this.$store.dispatch('addMessage', {message: 'You liked this party'});
this.liked = e;
})
.catch(error => this.$store.dispatch('addAxiosErrorMessage', error));
}
else {
this.axios.get('/party/unlike/' + this.teamId)
.then(_ => {
this.$store.dispatch('addMessage', {message: 'You stopped liking this party'});
this.liked = e;
})
.catch(error => this.$store.dispatch('addAxiosErrorMessage', error));
}
},
},
computed: {
...mapState({
team_owner: state => state.party_builder.team_owner,
}),
liked: {
get() { return this.$store.state.party_builder.liked },
set(value) { this.$store.commit('setLiked', value) }
},
isUserLogged() {
return this.$store.getters.getUserId !== null;
},
isMyParty() {
return this.team_owner === this.$store.getters.getUserId;
},
},
}
</script>

View File

@@ -0,0 +1,64 @@
<template>
<div v-if="show" class="fixed inset-0 z-40 flex justify-center items-start bg-black/80" @click.self="close()">
<div
class="w-screen h-auto mt-8 bg-primary md:rounded flex flex-col overflow-hidden"
:class="large ? 'md:w-11/12' : 'md:w-3/4 xl:w-3/5 2xl:1/2'"
style="max-height: calc(100vh - 4rem);"
>
<div class="px-4 py-2 flex flex-col-reverse sm:flex-row sm:justify-between">
<slot name="header"></slot>
<button @click.prevent="close()" class="self-end sm:self-start">
<fa-icon :icon="['fas', 'times-circle']" class="text-link-primary hover:text-link-hover text-2xl"></fa-icon>
</button>
</div>
<div class="overflow-auto break-all px-4" >
<slot></slot>
</div>
<div class="px-4 py-2 flex shrink-0">
<slot name="footer"></slot>
</div>
</div>
</div>
</template>
<script>
export default {
model: {
prop: 'show',
event: 'close'
},
props: {
show: {
type: Boolean,
required: true
},
large: {
type: Boolean,
default: false,
}
},
methods: {
close() {
this.$emit("close", ! this.show);
},
toggleOverflow() {
if (this.show === true) {
document.querySelector("HTML").classList.add("overflow-hidden");
}
else {
document.querySelector("HTML").classList.remove("overflow-hidden");
}
}
},
watch: {
show() {
this.toggleOverflow();
}
},
mounted() {
this.toggleOverflow();
}
}
</script>

View File

@@ -0,0 +1,49 @@
<template>
<label :title="longName">
{{ shortName }}
<input
class="appearance-none text-primary bg-transparent"
:class="alignRight ? 'text-right' : ''"
type="tel"
:style="'width: ' + length + 'ch;'"
v-model.number.lazy="localProp"
@keydown.arrow-up="incrementProp()"
@keydown.arrow-down="decrementProp()"
>
</label>
</template>
<script>
export default {
props: {
prop: Number,
shortName: String,
longName: String,
length: Number,
max: Number,
alignRight: Boolean,
},
methods: {
incrementProp() {
if (this.max === undefined || this.max > this.localProp) {
this.localProp++;
}
},
decrementProp() {
if (this.localProp > 0) {
this.localProp--;
}
},
},
computed: {
localProp: {
get() {
return this.prop;
},
set(value) {
this.$emit('update:prop', value);
}
}
}
}
</script>