Added simple page transition.

This commit is contained in:
Abdulmhsen B. A. A.
2024-07-22 21:31:46 +03:00
parent 109d133837
commit 2fb9435367
28 changed files with 3807 additions and 3735 deletions

View File

@@ -1,267 +1,269 @@
<template>
<div class="columns is-multiline">
<div class="column is-12 is-clearfix is-unselectable">
<span class="title is-4 ">
<span class="icon"><i class="fas fa-database"></i></span>
Data Parity
</span>
<div class="is-pulled-right">
<div class="field is-grouped">
<div class="control has-icons-left" v-if="showFilter">
<input type="search" v-model.lazy="filter" class="input" id="filter"
placeholder="Filter displayed results.">
<span class="icon is-left">
<i class="fas fa-filter"></i>
</span>
</div>
<div>
<div class="columns is-multiline">
<div class="column is-12 is-clearfix is-unselectable">
<span class="title is-4 ">
<span class="icon"><i class="fas fa-database"></i></span>
Data Parity
</span>
<div class="is-pulled-right">
<div class="field is-grouped">
<div class="control has-icons-left" v-if="showFilter">
<input type="search" v-model.lazy="filter" class="input" id="filter"
placeholder="Filter displayed results.">
<span class="icon is-left">
<i class="fas fa-filter"></i>
</span>
</div>
<div class="control">
<button class="button is-danger is-light" @click="toggleFilter">
<span class="icon"><i class="fas fa-filter"></i></span>
<div class="control">
<button class="button is-danger is-light" @click="toggleFilter">
<span class="icon"><i class="fas fa-filter"></i></span>
</button>
</div>
<div class="control" v-if="min && max" v-tooltip.bottom="'Minimum number of backends'">
<div class="select">
<select v-model="min" :disabled="isDeleting || isLoading">
<option v-for="i in numberRange(1,max+1)" :key="`min-${i}`" :value="i">
{{ i }}
</option>
</select>
</div>
</div>
<p class="control">
<button class="button is-danger" @click="deleteData" v-tooltip.bottom="'Delete The reported records'"
:disabled="isDeleting || isLoading || items.length<1" :class="{'is-loading':isDeleting}">
<span class="icon"><i class="fas fa-trash"></i></span>
</button>
</p>
<div class="control">
<button class="button is-info is-light" @click="selectAll = !selectAll"
data-tooltip="Toggle select all">
<span class="icon">
<i class="fas fa-check-square"
:class="{'fa-check-square': !selectAll,'fa-square':selectAll}"></i>
</span>
</button>
</div>
<p class="control">
<button class="button is-info" @click.prevent="loadContent(page, true, true)" :disabled="isLoading"
:class="{'is-loading':isLoading}">
<span class="icon"><i class="fas fa-sync"></i></span>
</button>
</p>
</div>
</div>
<div class="is-hidden-mobile">
<span class="subtitle">This page shows local database records not being reported by the specified number of
backends.</span>
</div>
</div>
<div class="column is-12" v-if="total && last_page > 1">
<div class="field is-grouped">
<div class="control" v-if="page !== 1">
<button rel="first" class="button" @click="loadContent(1)" :disabled="isLoading"
:class="{'is-loading':isLoading}">
<span><<</span>
</button>
</div>
<div class="control" v-if="min && max" v-tooltip.bottom="'Minimum number of backends'">
<div class="control" v-if="page > 1 && (page-1) !== 1">
<button rel="prev" class="button" @click="loadContent(page-1)" :disabled="isLoading"
:class="{'is-loading':isLoading}">
<span><</span>
</button>
</div>
<div class="control">
<div class="select">
<select v-model="min" :disabled="isDeleting || isLoading">
<option v-for="i in numberRange(1,max+1)" :key="`min-${i}`" :value="i">
{{ i }}
<select v-model="page" @change="loadContent(page)" :disabled="isLoading">
<option v-for="(item, index) in makePagination(page, last_page)" :key="index" :value="item.page">
{{ item.text }}
</option>
</select>
</div>
</div>
<p class="control">
<button class="button is-danger" @click="deleteData" v-tooltip.bottom="'Delete The reported records'"
:disabled="isDeleting || isLoading || items.length<1" :class="{'is-loading':isDeleting}">
<span class="icon"><i class="fas fa-trash"></i></span>
</button>
</p>
<div class="control">
<button class="button is-info is-light" @click="selectAll = !selectAll"
data-tooltip="Toggle select all">
<span class="icon">
<i class="fas fa-check-square"
:class="{'fa-check-square': !selectAll,'fa-square':selectAll}"></i>
</span>
</button>
</div>
<p class="control">
<button class="button is-info" @click.prevent="loadContent(page, true, true)" :disabled="isLoading"
<div class="control" v-if="page !== last_page && (page+1) !== last_page">
<button rel="next" class="button" @click="loadContent(page+1)" :disabled="isLoading"
:class="{'is-loading':isLoading}">
<span class="icon"><i class="fas fa-sync"></i></span>
<span>></span>
</button>
</div>
<div class="control" v-if="page !== last_page">
<button rel="last" class="button" @click="loadContent(last_page)" :disabled="isLoading"
:class="{'is-loading':isLoading}">
<span>>></span>
</button>
</p>
</div>
</div>
<div class="is-hidden-mobile">
<span class="subtitle">This page shows local database records not being reported by the specified number of
backends.</span>
</div>
</div>
<div class="column is-12" v-if="total && last_page > 1">
<div class="field is-grouped">
<div class="control" v-if="page !== 1">
<button rel="first" class="button" @click="loadContent(1)" :disabled="isLoading"
:class="{'is-loading':isLoading}">
<span><<</span>
</button>
</div>
<div class="control" v-if="page > 1 && (page-1) !== 1">
<button rel="prev" class="button" @click="loadContent(page-1)" :disabled="isLoading"
:class="{'is-loading':isLoading}">
<span><</span>
</button>
</div>
<div class="control">
<div class="select">
<select v-model="page" @change="loadContent(page)" :disabled="isLoading">
<option v-for="(item, index) in makePagination(page, last_page)" :key="index" :value="item.page">
{{ item.text }}
</option>
</select>
</div>
</div>
<div class="control" v-if="page !== last_page && (page+1) !== last_page">
<button rel="next" class="button" @click="loadContent(page+1)" :disabled="isLoading"
:class="{'is-loading':isLoading}">
<span>></span>
</button>
</div>
<div class="control" v-if="page !== last_page">
<button rel="last" class="button" @click="loadContent(last_page)" :disabled="isLoading"
:class="{'is-loading':isLoading}">
<span>>></span>
</button>
</div>
<div class="column is-12" v-if="selected_ids.length > 0">
<div class="field is-grouped is-justify-content-center">
<div class="control">
<button class="button is-danger" @click="massDelete()" :disabled="massActionInProgress"
:class="{'is-loading':massActionInProgress}">
<span class="icon"><i class="fas fa-trash"></i></span>
<span class="is-hidden-mobile">Delete '{{ selected_ids.length }}' selected item/s</span>
</button>
</div>
</div>
</div>
</div>
<div class="column is-12" v-if="selected_ids.length > 0">
<div class="field is-grouped is-justify-content-center">
<div class="control">
<button class="button is-danger" @click="massDelete()" :disabled="massActionInProgress"
:class="{'is-loading':massActionInProgress}">
<span class="icon"><i class="fas fa-trash"></i></span>
<span class="is-hidden-mobile">Delete '{{ selected_ids.length }}' selected item/s</span>
</button>
</div>
</div>
</div>
<div class="column is-12">
<div class="columns is-multiline" v-if="filteredRows(items)?.length>0">
<template v-for="item in items" :key="item.id">
<Lazy :unrender="true" :min-height="343" class="column is-6-tablet" v-if="filterItem(item)">
<div class="card" :class="{ 'is-success': item.watched }">
<header class="card-header">
<p class="card-header-title is-text-overflow pr-1">
<span class="icon">
<label class="checkbox">
<input type="checkbox" :value="item.id" v-model="selected_ids">
</label>&nbsp;
<div class="column is-12">
<div class="columns is-multiline" v-if="filteredRows(items)?.length>0">
<template v-for="item in items" :key="item.id">
<Lazy :unrender="true" :min-height="343" class="column is-6-tablet" v-if="filterItem(item)">
<div class="card" :class="{ 'is-success': item.watched }">
<header class="card-header">
<p class="card-header-title is-text-overflow pr-1">
<span class="icon">
<label class="checkbox">
<input type="checkbox" :value="item.id" v-model="selected_ids">
</label>&nbsp;
</span>
<NuxtLink :to="'/history/'+item.id" v-text="makeName(item)"/>
</p>
<span class="card-header-icon" @click="item.showRawData = !item?.showRawData">
<span class="icon">
<i class="fas"
:class="{ 'fa-tv': 'episode' === item.type.toLowerCase(), 'fa-film': 'movie' === item.type.toLowerCase()}"></i>
</span>
</span>
<NuxtLink :to="'/history/'+item.id" v-text="makeName(item)"/>
</p>
<span class="card-header-icon" @click="item.showRawData = !item?.showRawData">
<span class="icon">
<i class="fas"
:class="{ 'fa-tv': 'episode' === item.type.toLowerCase(), 'fa-film': 'movie' === item.type.toLowerCase()}"></i>
</span>
</span>
</header>
<div class="card-content">
<div class="columns is-multiline is-mobile">
<div class="column is-12">
<div class="field is-grouped">
<div class="control is-clickable"
:class="{'is-text-overflow': !item?.expand_title, 'is-text-contents': item?.expand_title}"
@click="item.expand_title = !item?.expand_title">
<span class="icon"><i class="fas fa-heading"></i>&nbsp;</span>
<template v-if="item?.content_title">
<NuxtLink :to="makeSearchLink('subtitle', item.content_title)" v-text="item.content_title"/>
</template>
<template v-else>
<NuxtLink :to="makeSearchLink('subtitle', item.title)" v-text="item.title"/>
</template>
</div>
<div class="control">
<span class="icon is-clickable"
@click="copyText(item?.content_title ?? item.title, false)">
<i class="fas fa-copy"></i></span>
</header>
<div class="card-content">
<div class="columns is-multiline is-mobile">
<div class="column is-12">
<div class="field is-grouped">
<div class="control is-clickable"
:class="{'is-text-overflow': !item?.expand_title, 'is-text-contents': item?.expand_title}"
@click="item.expand_title = !item?.expand_title">
<span class="icon"><i class="fas fa-heading"></i>&nbsp;</span>
<template v-if="item?.content_title">
<NuxtLink :to="makeSearchLink('subtitle', item.content_title)" v-text="item.content_title"/>
</template>
<template v-else>
<NuxtLink :to="makeSearchLink('subtitle', item.title)" v-text="item.title"/>
</template>
</div>
<div class="control">
<span class="icon is-clickable"
@click="copyText(item?.content_title ?? item.title, false)">
<i class="fas fa-copy"></i></span>
</div>
</div>
</div>
</div>
<div class="column is-12">
<div class="field is-grouped">
<div class="control is-clickable"
:class="{'is-text-overflow': !item?.expand_path, 'is-text-contents': item?.expand_path}"
@click="item.expand_path = !item?.expand_path">
<span class="icon"><i class="fas fa-file"></i>&nbsp;</span>
<NuxtLink v-if="item?.content_path" :to="makeSearchLink('path', item.content_path)"
v-text="item.content_path"/>
<span v-else>No path found.</span>
</div>
<div class="control">
<span class="icon is-clickable"
@click="copyText(item?.content_path ?item.content_path : null, false)">
<i class="fas fa-copy"></i></span>
<div class="column is-12">
<div class="field is-grouped">
<div class="control is-clickable"
:class="{'is-text-overflow': !item?.expand_path, 'is-text-contents': item?.expand_path}"
@click="item.expand_path = !item?.expand_path">
<span class="icon"><i class="fas fa-file"></i>&nbsp;</span>
<NuxtLink v-if="item?.content_path" :to="makeSearchLink('path', item.content_path)"
v-text="item.content_path"/>
<span v-else>No path found.</span>
</div>
<div class="control">
<span class="icon is-clickable"
@click="copyText(item?.content_path ?item.content_path : null, false)">
<i class="fas fa-copy"></i></span>
</div>
</div>
</div>
</div>
<div class="column is-12">
<div class="field is-grouped">
<div class="control is-expanded is-unselectable">
<span class="icon"><i class="fas fa-info"></i>&nbsp;</span>
<span>Has metadata from</span>
</div>
<div class="control">
<NuxtLink v-for="backend in item.reported_by" :key="`${item.id}-rb-${backend}`"
:to="'/backend/'+backend" v-text="backend" class="tag is-primary ml-1"/>
<NuxtLink v-for="backend in item.not_reported_by" :key="`${item.id}-nrb-${backend}`"
:to="'/backend/'+backend" v-text="backend" class="tag is-danger ml-1"/>
<div class="column is-12">
<div class="field is-grouped">
<div class="control is-expanded is-unselectable">
<span class="icon"><i class="fas fa-info"></i>&nbsp;</span>
<span>Has metadata from</span>
</div>
<div class="control">
<NuxtLink v-for="backend in item.reported_by" :key="`${item.id}-rb-${backend}`"
:to="'/backend/'+backend" v-text="backend" class="tag is-primary ml-1"/>
<NuxtLink v-for="backend in item.not_reported_by" :key="`${item.id}-nrb-${backend}`"
:to="'/backend/'+backend" v-text="backend" class="tag is-danger ml-1"/>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="card-content p-0 m-0" v-if="item?.showRawData">
<div class="card-content p-0 m-0" v-if="item?.showRawData">
<pre style="position: relative; max-height: 343px;"><code>{{ JSON.stringify(item, null, 2) }}</code>
<button class="button is-small m-4" @click="() => copyText(JSON.stringify(item, null, 2))"
style="position: absolute; top:0; right:0;">
<span class="icon"><i class="fas fa-copy"></i></span>
</button>
</pre>
</div>
<div class="card-footer">
<div class="card-footer-item">
<span class="icon">
<i class="fas" :class="{'fa-eye':item.watched,'fa-eye-slash':!item.watched}"></i>&nbsp;
</span>
<span class="has-text-success" v-if="item.watched">Played</span>
<span class="has-text-danger" v-else>Unplayed</span>
</div>
<div class="card-footer-item">
<span class="icon"><i class="fas fa-calendar"></i>&nbsp;</span>
<span class="has-tooltip"
v-tooltip="`Record updated at: ${moment.unix(item.updated_at).format(TOOLTIP_DATE_FORMAT)}`">
{{ moment.unix(item.updated_at).fromNow() }}
</span>
<div class="card-footer">
<div class="card-footer-item">
<span class="icon">
<i class="fas" :class="{'fa-eye':item.watched,'fa-eye-slash':!item.watched}"></i>&nbsp;
</span>
<span class="has-text-success" v-if="item.watched">Played</span>
<span class="has-text-danger" v-else>Unplayed</span>
</div>
<div class="card-footer-item">
<span class="icon"><i class="fas fa-calendar"></i>&nbsp;</span>
<span class="has-tooltip"
v-tooltip="`Record updated at: ${moment.unix(item.updated_at).format(TOOLTIP_DATE_FORMAT)}`">
{{ moment.unix(item.updated_at).fromNow() }}
</span>
</div>
</div>
</div>
</div>
</lazy>
</template>
</div>
</lazy>
</template>
</div>
<div class="column is-12" v-else>
<Message v-if="isLoading" message_class="has-background-info-90 has-text-dark" title="Loading"
icon="fas fa-spinner fa-spin" message="Loading data. Please wait..."/>
<template v-else>
<Message message_class="has-background-warning-80 has-text-dark" v-if="filter && items.length > 1"
title="Information"
icon="fas fa-check">
The filter <code>{{ filter }}</code> did not match any records.
</Message>
<Message message_class="has-background-success-90 has-text-dark" v-if="!filter || items.length < 1"
title="Success"
icon="fas fa-check">
WatchState did not find any records matching the criteria. All records has at least <code>{{ min }}</code>
backends reporting it.
</Message>
</template>
</div>
<div class="column is-12" v-else>
<Message v-if="isLoading" message_class="has-background-info-90 has-text-dark" title="Loading"
icon="fas fa-spinner fa-spin" message="Loading data. Please wait..."/>
<template v-else>
<Message message_class="has-background-warning-80 has-text-dark" v-if="filter && items.length > 1"
title="Information"
icon="fas fa-check">
The filter <code>{{ filter }}</code> did not match any records.
</Message>
<Message message_class="has-background-success-90 has-text-dark" v-if="!filter || items.length < 1"
title="Success"
icon="fas fa-check">
WatchState did not find any records matching the criteria. All records has at least <code>{{ min }}</code>
backends reporting it.
</Message>
</template>
</div>
<div class="column is-12">
<Message message_class="has-background-info-90 has-text-dark" :toggle="show_page_tips"
@toggle="show_page_tips = !show_page_tips" :use-toggle="true" title="Tips" icon="fas fa-info-circle">
<ul>
<li>
You can specify the minimum number of backends that need to report the record to be considered valid.
</li>
<li>
By clicking the <span class="fa fa-trash"></span> icon you will delete the the reported items from the
local database. If the items are not fixed by the time <code>import</code> is run, they will re-appear.
</li>
<li>
Deleting records works by deleting everything at or below the specified number of backends. For example,
if you set the minimum to <code>3</code>, all records that are reported by <code>3</code> or fewer
backends will be deleted.
</li>
<li>
Records showing here most likely means your backends, are not reporting same data. This could be due to
many reasons, including using different external databases i.e. <code>TheMovieDB</code> vs
<code>TheTVDB</code>.
</li>
<li>
The results are cached in your browser temporarily to provide faster response, as the operation to
generate the report is quite intensive. If you want to refresh the data, click the <span
class="fa fa-sync"></span> icon.
</li>
</ul>
</Message>
<div class="column is-12">
<Message message_class="has-background-info-90 has-text-dark" :toggle="show_page_tips"
@toggle="show_page_tips = !show_page_tips" :use-toggle="true" title="Tips" icon="fas fa-info-circle">
<ul>
<li>
You can specify the minimum number of backends that need to report the record to be considered valid.
</li>
<li>
By clicking the <span class="fa fa-trash"></span> icon you will delete the the reported items from the
local database. If the items are not fixed by the time <code>import</code> is run, they will re-appear.
</li>
<li>
Deleting records works by deleting everything at or below the specified number of backends. For example,
if you set the minimum to <code>3</code>, all records that are reported by <code>3</code> or fewer
backends will be deleted.
</li>
<li>
Records showing here most likely means your backends, are not reporting same data. This could be due to
many reasons, including using different external databases i.e. <code>TheMovieDB</code> vs
<code>TheTVDB</code>.
</li>
<li>
The results are cached in your browser temporarily to provide faster response, as the operation to
generate the report is quite intensive. If you want to refresh the data, click the <span
class="fa fa-sync"></span> icon.
</li>
</ul>
</Message>
</div>
</div>
</div>
</div>