fix: adds preview content modal to monitor tables

This commit is contained in:
2025-08-04 14:35:27 -05:00
parent 62aa0f4554
commit cb50007208
12 changed files with 182 additions and 17 deletions

2
assets/bootstrap.js vendored
View File

@@ -3,6 +3,7 @@ import PreviewContentDialog from "./components/preview-content-dialog.js";
import EpisodeContainer from './components/episode-container.js'; import EpisodeContainer from './components/episode-container.js';
import DownloadOptionTr from './components/download-option-tr.js'; import DownloadOptionTr from './components/download-option-tr.js';
import DownloadListRow from './components/download-list-row.js'; import DownloadListRow from './components/download-list-row.js';
import MonitorListRow from './components/monitor-list-row.js';
import MovieContainer from "./components/movie-container.js"; import MovieContainer from "./components/movie-container.js";
import { startStimulusApp } from '@symfony/stimulus-bundle'; import { startStimulusApp } from '@symfony/stimulus-bundle';
@@ -22,3 +23,4 @@ customElements.define('episode-container', EpisodeContainer);
customElements.define('movie-container', MovieContainer); customElements.define('movie-container', MovieContainer);
customElements.define('dl-tr', DownloadOptionTr, {extends: 'tr'}); customElements.define('dl-tr', DownloadOptionTr, {extends: 'tr'});
customElements.define('download-list-row', DownloadListRow, {extends: 'tr'}); customElements.define('download-list-row', DownloadListRow, {extends: 'tr'});
customElements.define('monitor-list-row', MonitorListRow, {extends: 'tr'});

View File

@@ -0,0 +1,115 @@
export default class MonitorListRow extends HTMLTableRowElement {
constructor() {
super();
this.downloadId = this.getAttribute('monitor-id');
this.imdbId = this.getAttribute('imdb-id');
this.mediaTitle = this.getAttribute('media-title');
this.url = this.getAttribute('url');
this.filename = this.getAttribute('filename');
this.status = this.getAttribute('status');
this.progress = this.getAttribute('progress');
this.mediaType = this.getAttribute('media-type');
this.episodeId = this.getAttribute('episode-id');
this.createdAt = this.getAttribute('created-at');
this.updatedAt = this.getAttribute('updated-at');
}
static get observedAttributes() {
return ['download-id', 'imdb-id', 'media-title', 'url', 'filename', 'status', 'progress', 'media-type', 'episode-id', 'created-at', 'updated-at'];
}
attributeChangedCallback(name, oldValue, newValue) {
if (oldValue !== newValue) {
this[name] = newValue;
this.setAttribute(name, newValue);
this.setPreviewContent();
}
}
setPreviewContent() {
this.previewContent = `
<table class="table-auto flex flex-row">
<thead>
<tr class="flex flex-col">
<th class="px-4 py-2">
<div class="dark:text-orange-500 text-right whitespace-nowrap ">ID</div>
</th>
<th class="px-4 py-2">
<div class="dark:text-orange-500 text-right whitespace-nowrap ">IMDB ID</div>
</th>
<th class="px-4 py-2">
<div class="dark:text-orange-500 text-right whitespace-nowrap ">Title</div>
</th>
<th class="px-4 py-2">
<div class="dark:text-orange-500 text-right whitespace-nowrap ">Season</div>
</th>
<th class="px-4 py-2">
<div class="dark:text-orange-500 text-right whitespace-nowrap ">Episode</div>
</th>
<th class="px-4 py-2">
<div class="dark:text-orange-500 text-right whitespace-nowrap ">Status</div>
</th>
<th class="px-4 py-2">
<div class="dark:text-orange-500 text-right whitespace-nowrap ">Search Count</div>
</th>
<th class="px-4 py-2">
<div class="dark:text-orange-500 text-right whitespace-nowrap ">Media Type</div>
</th>
<th class="px-4 py-2">
<div class="dark:text-orange-500 text-right whitespace-nowrap ">Episode ID</div>
</th>
<th class="px-4 py-2">
<div class="dark:text-orange-500 text-right whitespace-nowrap ">Created At</div>
</th>
<th class="px-4 py-2">
<div class="dark:text-orange-500 text-right whitespace-nowrap ">Updated At</div>
</th>
<th class="px-4 py-2">
<div class="dark:text-orange-500 text-right whitespace-nowrap ">Downloaded At</div>
</th>
</tr>
</thead>
<tbody>
<tr class="flex flex-col">
<td class="px-4 py-2">
<div class="text-left dark:text-white whitespace-nowrap font-normal">${this.getAttribute('monitor-id') ?? "-"}</div>
</td>
<td class="px-4 py-2">
<div class="text-left dark:text-white whitespace-nowrap font-normal">${this.getAttribute('imdb-id') ?? "-"}</div>
</td>
<td class="px-4 py-2">
<div class="text-left dark:text-white whitespace-nowrap font-normal">${this.getAttribute('media-title') ?? "-"}</div>
</td>
<td class="px-4 py-2">
<div class="text-left dark:text-white whitespace-nowrap font-normal">${this.getAttribute('season') ?? "-"}</div>
</td>
<td class="px-4 py-2">
<div class="text-left dark:text-white whitespace-nowrap font-normal">${this.getAttribute('episode') ?? "-"}</div>
</td>
<td class="px-4 py-2">
<div class="text-left dark:text-white whitespace-nowrap font-normal">${this.getAttribute('status') ?? "-"}</div>
</td>
<td class="px-4 py-2">
<div class="text-left dark:text-white whitespace-nowrap font-normal">${this.getAttribute('search-count') ?? "-"}</div>
</td>
<td class="px-4 py-2">
<div class="text-left dark:text-white whitespace-nowrap font-normal">${this.getAttribute('media-type') ?? "-"}</div>
</td>
<td class="px-4 py-2">
<div class="text-left dark:text-white whitespace-nowrap font-normal">${this.getAttribute('episode-id') ?? "-"}</div>
</td>
<td class="px-4 py-2">
<div class="text-left dark:text-white whitespace-nowrap font-normal">${this.getAttribute('created-at') ?? "-"}</div>
</td>
<td class="px-4 py-2">
<div class="text-left dark:text-white whitespace-nowrap font-normal">${this.getAttribute('last-search') ?? "-"}</div>
</td>
<td class="px-4 py-2">
<div class="text-left dark:text-white whitespace-nowrap font-normal">${this.getAttribute('downloaded-at') ?? "-"}</div>
</td>
</tr>
</tbody>
</table>
`;
}
}

View File

@@ -12,11 +12,11 @@ export default class PreviewContentDialog extends HTMLDialogElement {
this.setHeading = this.setHeading.bind(this); this.setHeading = this.setHeading.bind(this);
this.setContent = this.setContent.bind(this); this.setContent = this.setContent.bind(this);
this.#closeBtnEl.addEventListener('click', () => this.close());
document.addEventListener('hidePreviewContentModal', () => this.close());
document.addEventListener('showPreviewContentModal', (event) => { document.addEventListener('showPreviewContentModal', (event) => {
this.display(event.detail); this.display(event.detail);
}); });
document.addEventListener('hidePreviewContentModal', (e) => this.close());
this.#closeBtnEl.addEventListener('click', () => this.close());
} }
setHeading(heading) { setHeading(heading) {

View File

@@ -41,23 +41,20 @@ export default class extends Controller {
downloadTargetConnected(target) { downloadTargetConnected(target) {
let downloads = this.element.querySelectorAll('tbody tr'); let downloads = this.element.querySelectorAll('tbody tr');
console.log(target)
downloads.forEach(download => { downloads.forEach(download => {
download.addEventListener('click', (event) => { download.addEventListener('click', (event) => {
// let previewContentModal = document.querySelector('#previewContentModal');
let content, heading = "" let content, heading = ""
if (event.target.tagName !== "TR") { if (event.target.tagName !== "TR") {
content = event.target.parentElement.previewContent; content = event.target.parentElement.previewContent;
heading = event.target.parentElement.mediaTitle; heading = "Download # " + event.target.parentElement.downloadId + " - \"" + event.target.parentElement.mediaTitle + "\"";
} else { } else {
content = event.target.previewContent; content = event.target.previewContent;
heading = event.target.mediaTitle; heading = "Download for # " + event.target.downloadId + " - \"" + event.target.mediaTitle + "\"";
} }
console.log(content) if (null !== content && undefined !== content && "" !== content) {
document.dispatchEvent(new CustomEvent('showPreviewContentModal', {detail: {heading: heading, content: content}}))
document.dispatchEvent(new CustomEvent('showPreviewContentModal', {detail: {heading: heading, content: content}})) }
}) })
}) })
} }

View File

@@ -6,6 +6,29 @@ import { Controller } from '@hotwired/stimulus';
*/ */
/* stimulusFetch: 'lazy' */ /* stimulusFetch: 'lazy' */
export default class extends Controller { export default class extends Controller {
static targets = ['monitorList']
monitorListTargetConnected(target) {
let monitors = this.element.querySelectorAll('tbody tr');
monitors.forEach(monitor => {
monitor.addEventListener('click', (event) => {
let content, heading = ""
if (event.target.tagName !== "TR") {
content = event.target.parentElement.previewContent;
heading = "Monitor for \"" + event.target.parentElement.mediaTitle+ "\"";
} else {
content = event.target.previewContent;
heading = "Monitor for \"" + event.target.mediaTitle + "\"";
}
if (null !== content && undefined !== content && "" !== content) {
document.dispatchEvent(new CustomEvent('showPreviewContentModal', {detail: {heading: heading, content: content}}))
}
})
})
}
deleteMonitor(data) { deleteMonitor(data) {
fetch(`/api/monitor/${data.params.id}`, {method: 'DELETE'}) fetch(`/api/monitor/${data.params.id}`, {method: 'DELETE'})
.then(res => res.json()) .then(res => res.json())

View File

@@ -55,6 +55,10 @@ dialog {
} }
} }
dialog[open] {
animation: fade-in 100ms ease-in forwards;
}
/* Add animations */ /* Add animations */
dialog[data-dialog-target="dialog"][open] { dialog[data-dialog-target="dialog"][open] {
animation: fade-in 200ms forwards; animation: fade-in 200ms forwards;

View File

@@ -30,5 +30,6 @@
{% block body %}{% endblock %} {% block body %}{% endblock %}
</div> </div>
</div> </div>
<twig:PreviewModal id="previewModal" />
</body> </body>
</html> </html>

View File

@@ -1,5 +1,15 @@
<tr{{ attributes }} is="download-list-row" class="hover:bg-gray-200" id="ad_download_{{ download.id }}" data-title="{{ download.title }}" <tr{{ attributes }} is="download-list-row" class="hover:bg-gray-200" id="ad_download_{{ download.id }}" data-title="{{ download.title }}"
download-id="{{ download.id }}" imdb-id="{{ download.imdbId }}" media-title="{{ download.title }}" url="{{ download.url }}" filename="{{ download.filename }}" status="{{ download.status }}" progress="{{ download.progress }}" media-type="{{ download.mediaType }}" episode-id="{{ download.episodeId }}" created-at="{{ download.createdAt|date('m/d/Y g:i a') }}" updated-at="{{ download.updatedAt|date }}" download-id="{{ download.id }}"
imdb-id="{{ download.imdbId }}"
media-title="{{ download.title }}"
url="{{ download.url }}"
filename="{{ download.filename }}"
status="{{ download.status }}"
progress="{{ download.progress }}"
media-type="{{ download.mediaType }}"
episode-id="{{ download.episodeId }}"
created-at="{{ download.createdAt|date('m/d/Y g:i a') }}"
updated-at="{{ download.updatedAt|date('m/d/Y g:i a') }}"
data-filename="{{ download.filename }}" data-media-type="{{ download.mediaType }}" data-status="{{ download.status }}" data-progress="{{ download.progress }}" data-filename="{{ download.filename }}" data-media-type="{{ download.mediaType }}" data-status="{{ download.status }}" data-progress="{{ download.progress }}"
> >
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-800 dark:text-stone-800 truncate"> <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-800 dark:text-stone-800 truncate">

View File

@@ -4,7 +4,9 @@
<twig:DownloadSearch search_path="app_search" placeholder="Find {{ type == "complete" ? "a" : "an" }} {{ type }} monitor..." /> <twig:DownloadSearch search_path="app_search" placeholder="Find {{ type == "complete" ? "a" : "an" }} {{ type }} monitor..." />
</div> </div>
{% endif %} {% endif %}
<table id="monitor_list" class="divide-y divide-gray-200 bg-gray-50 overflow-hidden rounded-lg table-auto w-full" {{ turbo_stream_listen('App\\Monitor\\Framework\\Entity\\Monitor') }}> <table id="monitor_list" class="divide-y divide-gray-200 bg-gray-50 overflow-hidden rounded-lg table-auto w-full" {{ turbo_stream_listen('App\\Monitor\\Framework\\Entity\\Monitor') }}
{{ stimulus_target('monitor_list', 'monitorList') }}
>
<thead> <thead>
<tr class="bg-orange-500 bg-filter bg-blur-lg bg-opacity-80 text-gray-950"> <tr class="bg-orange-500 bg-filter bg-blur-lg bg-opacity-80 text-gray-950">
<th scope="col" <th scope="col"

View File

@@ -1,4 +1,17 @@
<tr{{ attributes }} id="monitor_{{ monitor.id }}" class="hover:bg-gray-200"> <tr{{ attributes }} is="monitor-list-row" id="monitor_{{ monitor.id }}" class="hover:bg-gray-200"
monitor-id="{{ monitor.id }}"
imdb-id="{{ monitor.imdbId }}"
media-title="{{ monitor.title }}"
season="{{ monitor.season }}"
episode="{{ monitor.episode }}"
status="{{ monitor.status }}"
search-count="{{ monitor.searchCount }}"
media-type="{{ monitor.monitorType|monitor_type }}"
episode-id="{{ monitor|monitor_media_id }}"
created-at="{{ monitor.createdAt|date('m/d/Y g:i a') }}"
last-search="{{ monitor.lastSearch|date('m/d/Y g:i a') }}"
downloaded-at="{{ monitor.downloadedAt|date('m/d/Y g:i a') }}"
>
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-stone-800 truncate"> <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-stone-800 truncate">
<a href="{{ path('app_search_result', {imdbId: monitor.imdbId, mediaType: monitor.monitorType|as_download_type}) }}" <a href="{{ path('app_search_result', {imdbId: monitor.imdbId, mediaType: monitor.monitorType|as_download_type}) }}"
class="mr-1 hover:underline rounded-md" class="mr-1 hover:underline rounded-md"

View File

@@ -1,8 +1,8 @@
<dialog{{ attributes }} is="preview-content-dialog" class="py-3 px-4 w-full md:w-[50rem] rounded-md dark:bg-gray-950/80 dark:border-2 dark:border-orange-500 dark:text-white backdrop-filter backdrop-blur-md"> <dialog{{ attributes }} is="preview-content-dialog" class="py-3 px-4 w-full md:w-[50rem] rounded-md dark:bg-gray-950/80 dark:border-2 dark:border-orange-500 dark:text-white backdrop-filter backdrop-blur-3xl">
<div class="flex flex-row justify-end"> <div class="flex flex-row justify-end">
<twig:ux:icon name="ic:twotone-cancel" width="16.75px" height="16.75px" class="modal-close rounded-full align-middle text-red-600 hover:text-red-700" /> <twig:ux:icon name="ic:twotone-cancel" width="16.75px" height="16.75px" class="modal-close rounded-full align-middle text-red-600 hover:text-red-700" />
</div> </div>
<h2 class="modal-heading mb-4 text-2xl font-bold">{{ heading|default('') }}</h2> <h2 class="modal-heading mb-4 text-2xl font-bold text-orange-500">{{ heading|default('') }}</h2>
<div class="modal-content mb-4"> <div class="modal-content mb-4">
{% block content %}{% endblock %} {% block content %}{% endblock %}

View File

@@ -15,6 +15,4 @@
<twig:DownloadList type="complete" :isWidget="false" :perPage="10"></twig:DownloadList> <twig:DownloadList type="complete" :isWidget="false" :perPage="10"></twig:DownloadList>
</twig:Card> </twig:Card>
</div> </div>
<twig:PreviewModal id="previewModal" />
{% endblock %} {% endblock %}