This repository has been archived on 2024-07-22. You can view files and clone it, but cannot push or open issues or pull requests.
Zaimki/routes/names.vue

213 lines
7.9 KiB
Vue

<template>
<NotFound v-if="!config.names || !config.names.enabled || (!config.names.published && !$isGranted('names'))"/>
<div v-else>
<CommunityNav/>
<h2>
<Icon v="signature"/>
<T>names.headerLong</T>
</h2>
<section>
<T>names.intro</T>
<NamesLinks/>
</section>
<section class="sticky-top">
<div class="input-group mb-3 bg-white">
<span class="input-group-text">
<Icon v="filter"/>
</span>
<input class="form-control border-primary" v-model="filter" :placeholder="$t('crud.filterLong')" ref="filter"/>
<button v-if="filter" class="btn btn-outline-danger" @click="filter = ''; $refs.filter.focus()">
<Icon v="times"/>
</button>
<button class="btn btn-outline-success" @click="$refs.form.$el.scrollIntoView({block: 'center'})">
<Icon v="plus-circle"/>
<T>nouns.submit.action</T>
</button>
</div>
</section>
<section>
<ul class="list-group small">
<template v-if="visibleNames().length">
<li v-for="name in visibleNames()" :class="['list-group-item', name.approved ? '' : 'marked']">
<Name :name="name">
<ul class="list-inline small">
<template v-if="$isGranted('names')">
<li v-if="name.author" class="list-inline-item small">
<nuxt-link :to="`/@${name.author}`" class="btn btn-outline-dark btn-sm">
<Icon v="user"/>
<span class="btn-label">
<T>crud.author</T><T>quotation.colon</T>
@{{name.author}}
</span>
</nuxt-link>
</li>
<li v-if="!name.approved" class="list-inline-item">
<button class="btn btn-success btn-sm" @click="approve(name)">
<Icon v="check"/>
<span class="btn-label"><T>crud.approve</T></span>
</button>
</li>
<li v-else @click="hide(name)" class="list-inline-item">
<button class="btn btn-outline-secondary btn-sm">
<Icon v="times"/>
<span class="btn-label"><T>crud.hide</T></span>
</button>
</li>
<li class="list-inline-item">
<button class="btn btn-outline-danger btn-sm" @click="remove(name)">
<Icon v="trash"/>
<span class="btn-label"><T>crud.remove</T></span>
</button>
</li>
</template>
<li class="list-inline-item">
<button class="btn btn-outline-primary btn-sm" @click="edit(name)">
<Icon v="pen"/>
<span class="btn-label">
<T v-if="$isGranted('names')">crud.edit</T>
<T v-else>nouns.edit</T>
</span>
</button>
</li>
</ul>
</Name>
<small v-if="name.base && names[name.base]">
<hr/>
<p><strong><T>nouns.edited</T><T>quotation.colon</T></strong></p>
<Diff switchable>
<template v-slot:before><Name :name="names[name.base]"/></template>
<template v-slot:after><Name :name="name"/></template>
</Diff>
</small>
</li>
</template>
<template v-else>
<li class="list-group-item text-center">
<Icon v="search"/>
<T>names.empty</T>
</li>
</template>
</ul>
</section>
<Separator icon="plus"/>
<section class="px-3">
<NameSubmitForm ref="form" style="scroll-padding-top: 2rem;"/>
</section>
</div>
</template>
<script>
import {head, buildDict} from '~/src/helpers';
import hash from "../plugins/hash";
import {Name} from "../src/classes";
export default {
mixins: [ hash ],
data() {
return {
filter: '',
}
},
async asyncData({app}) {
return {
namesRaw: await app.$axios.$get(`/names`),
}
},
mounted() {
this.handleHash('', filter => {
this.filter = filter;
if (filter) {
this.$refs.filter.focus();
this.$refs.filter.scrollIntoView();
setTimeout(_ => {
this.$refs.filter.scrollIntoView();
}, 1000);
}
});
},
computed: {
names() {
if (this.namesRaw === undefined) {
return {};
}
return buildDict(function* (that) {
const sorted = that.namesRaw.sort((a, b) => {
if (a.approved && !b.approved) {
return 1;
}
if (!a.approved && b.approved) {
return -1;
}
return a.name.toLowerCase().localeCompare(b.name.toLowerCase());
});
for (let n of sorted) {
yield [n.id, new Name(n)];
}
}, this);
},
},
methods: {
visibleNames() {
return Object.values(this.names).filter(n => n.matches(this.filter));
},
edit(name) {
this.$refs.form.edit(name);
},
async approve(name) {
await this.$post(`/names/approve/${name.id}`);
if (name.base) {
delete this.names[name.base];
}
name.approved = true;
name.base = null;
this.$forceUpdate();
},
async hide(name) {
await this.$post(`/names/hide/${name.id}`);
name.approved = false;
this.$forceUpdate();
},
async remove(name) {
await this.$confirm(this.$t('crud.removeConfirm'), 'danger');
await this.$post(`/names/remove/${name.id}`);
delete this.names[name.id];
this.$forceUpdate();
},
},
watch: {
filter() {
this.setHash('', this.filter);
}
},
head() {
return head({
title: this.$t('names.headerLong'),
description: this.$t('names.description'),
});
},
}
</script>
<style lang="scss">
@import "assets/variables";
@include media-breakpoint-up('md') {
.w-md-50 {
width: 50%;
}
}
.list-group-item.marked {
border-inline-start: 3px solid $primary;
}
</style>