Compare commits

...

6 Commits

19 changed files with 642 additions and 490 deletions

View File

@@ -23,28 +23,28 @@
"php-tmdb/api": "^4.1", "php-tmdb/api": "^4.1",
"predis/predis": "^2.4", "predis/predis": "^2.4",
"runtime/frankenphp-symfony": "^0.2.0", "runtime/frankenphp-symfony": "^0.2.0",
"symfony/asset": "7.2.*", "symfony/asset": "7.3.*",
"symfony/console": "7.2.*", "symfony/console": "7.3.*",
"symfony/doctrine-messenger": "7.2.*", "symfony/doctrine-messenger": "7.3.*",
"symfony/dotenv": "7.2.*", "symfony/dotenv": "7.3.*",
"symfony/filesystem": "7.2.*", "symfony/filesystem": "7.3.*",
"symfony/finder": "7.2.*", "symfony/finder": "7.3.*",
"symfony/flex": "^2", "symfony/flex": "^2",
"symfony/form": "7.2.*", "symfony/form": "7.3.*",
"symfony/framework-bundle": "7.2.*", "symfony/framework-bundle": "7.3.*",
"symfony/ldap": "7.2.*", "symfony/ldap": "7.3.*",
"symfony/mercure-bundle": "^0.3.9", "symfony/mercure-bundle": "^0.3.9",
"symfony/messenger": "7.2.*", "symfony/messenger": "7.3.*",
"symfony/runtime": "7.2.*", "symfony/runtime": "7.3.*",
"symfony/scheduler": "7.2.*", "symfony/scheduler": "7.3.*",
"symfony/security-bundle": "7.2.*", "symfony/security-bundle": "7.3.*",
"symfony/stimulus-bundle": "^2.24", "symfony/stimulus-bundle": "^2.24",
"symfony/twig-bundle": "7.2.*", "symfony/twig-bundle": "7.3.*",
"symfony/ux-icons": "^2.24", "symfony/ux-icons": "^2.24",
"symfony/ux-live-component": "^2.24", "symfony/ux-live-component": "^2.24",
"symfony/ux-turbo": "^2.24", "symfony/ux-turbo": "^2.24",
"symfony/ux-twig-component": "^2.24", "symfony/ux-twig-component": "^2.24",
"symfony/yaml": "7.2.*", "symfony/yaml": "7.3.*",
"symfonycasts/tailwind-bundle": "^0.10.0", "symfonycasts/tailwind-bundle": "^0.10.0",
"twig/extra-bundle": "^2.12|^3.0", "twig/extra-bundle": "^2.12|^3.0",
"twig/twig": "^2.12|^3.0" "twig/twig": "^2.12|^3.0"
@@ -98,13 +98,13 @@
"extra": { "extra": {
"symfony": { "symfony": {
"allow-contrib": false, "allow-contrib": false,
"require": "7.2.*" "require": "7.3.*"
} }
}, },
"require-dev": { "require-dev": {
"phpstan/phpstan": "^2.1", "phpstan/phpstan": "^2.1",
"symfony/maker-bundle": "^1.62", "symfony/maker-bundle": "^1.62",
"symfony/stopwatch": "7.2.*", "symfony/stopwatch": "7.3.*",
"symfony/web-profiler-bundle": "7.2.*" "symfony/web-profiler-bundle": "7.3.*"
} }
} }

769
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,3 @@
framework:
property_info:
with_constructor_extractor: true

View File

@@ -2,34 +2,25 @@
namespace App\Controller; namespace App\Controller;
use App\Util\Broadcaster;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Mercure\HubInterface;
use Symfony\Component\Mercure\Update;
use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Routing\Attribute\Route;
use Twig\Environment;
final class AlertController extends AbstractController final class AlertController extends AbstractController
{ {
public function __construct( public function __construct(
#[Autowire(service: 'twig')] private readonly Environment $renderer, private readonly Broadcaster $broadcaster,
private readonly HubInterface $hub,
) {} ) {}
#[Route('/alert', name: 'app_alert')] #[Route('/alert', name: 'app_alert')]
public function index(): Response public function index(): Response
{ {
$update = new Update( $this->broadcaster->alert(
'alerts', 'Added to queue',
$this->renderer->render('Alert.stream.html.twig', [ 'This is a testy test!'
'alert_id' => 1,
'title' => 'Added to queue',
'message' => 'This is a testy test!',
])
); );
$this->hub->publish($update);
return $this->json([ return $this->json([
'Success' => 'Published' 'Success' => 'Published'
]); ]);

View File

@@ -6,12 +6,11 @@ use App\Torrentio\Action\Handler\GetMovieOptionsHandler;
use App\Torrentio\Action\Handler\GetTvShowOptionsHandler; use App\Torrentio\Action\Handler\GetTvShowOptionsHandler;
use App\Torrentio\Action\Input\GetMovieOptionsInput; use App\Torrentio\Action\Input\GetMovieOptionsInput;
use App\Torrentio\Action\Input\GetTvShowOptionsInput; use App\Torrentio\Action\Input\GetTvShowOptionsInput;
use App\Util\Broadcaster;
use Carbon\Carbon; use Carbon\Carbon;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Mercure\HubInterface;
use Symfony\Component\Mercure\Update;
use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Routing\Attribute\Route;
use Symfony\Contracts\Cache\CacheInterface; use Symfony\Contracts\Cache\CacheInterface;
use Symfony\Contracts\Cache\ItemInterface; use Symfony\Contracts\Cache\ItemInterface;
@@ -21,8 +20,7 @@ final class TorrentioController extends AbstractController
public function __construct( public function __construct(
private readonly GetMovieOptionsHandler $getMovieOptionsHandler, private readonly GetMovieOptionsHandler $getMovieOptionsHandler,
private readonly GetTvShowOptionsHandler $getTvShowOptionsHandler, private readonly GetTvShowOptionsHandler $getTvShowOptionsHandler,
private readonly HubInterface $hub, private readonly Broadcaster $broadcaster,
private readonly \Twig\Environment $renderer,
) {} ) {}
#[Route('/torrentio/movies/{tmdbId}/{imdbId}', name: 'app_torrentio_movies')] #[Route('/torrentio/movies/{tmdbId}/{imdbId}', name: 'app_torrentio_movies')]
@@ -75,14 +73,10 @@ final class TorrentioController extends AbstractController
); );
$cache->delete($cacheId); $cache->delete($cacheId);
$this->hub->publish(new Update( $this->broadcaster->alert(
$request->getSession()->get('mercure_alert_topic'), title: 'Success',
$this->renderer->render('Alert.stream.html.twig', [ message: 'Torrentio cache Cleared.'
'alert_id' => uniqid(), );
'title' => 'Success',
'message' => 'Torrentio cache Cleared.',
])
));
return $cache->get($cacheId, function (ItemInterface $item) use ($input) { return $cache->get($cacheId, function (ItemInterface $item) use ($input) {
$item->expiresAt(Carbon::now()->addHour()->setMinute(0)->setSecond(0)); $item->expiresAt(Carbon::now()->addHour()->setMinute(0)->setSecond(0));

View File

@@ -6,11 +6,9 @@ use App\Download\Action\Handler\DeleteDownloadHandler;
use App\Download\Action\Input\DeleteDownloadInput; use App\Download\Action\Input\DeleteDownloadInput;
use App\Download\Action\Input\DownloadMediaInput; use App\Download\Action\Input\DownloadMediaInput;
use App\Download\Framework\Repository\DownloadRepository; use App\Download\Framework\Repository\DownloadRepository;
use App\Util\Broadcaster;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Mercure\HubInterface;
use Symfony\Component\Mercure\Update;
use Symfony\Component\Messenger\MessageBusInterface; use Symfony\Component\Messenger\MessageBusInterface;
use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Routing\Attribute\Route;
@@ -19,12 +17,11 @@ class ApiController extends AbstractController
public function __construct( public function __construct(
private DownloadRepository $downloadRepository, private DownloadRepository $downloadRepository,
private MessageBusInterface $bus, private MessageBusInterface $bus,
private readonly HubInterface $hub, private readonly Broadcaster $broadcaster,
) {} ) {}
#[Route('/api/download', name: 'api_download', methods: ['POST'])] #[Route('/api/download', name: 'api_download', methods: ['POST'])]
public function download( public function download(
Request $request,
DownloadMediaInput $input, DownloadMediaInput $input,
): Response { ): Response {
$download = $this->downloadRepository->insert( $download = $this->downloadRepository->insert(
@@ -47,33 +44,24 @@ class ApiController extends AbstractController
return $this->json(['error' => $exception->getMessage()], Response::HTTP_INTERNAL_SERVER_ERROR); return $this->json(['error' => $exception->getMessage()], Response::HTTP_INTERNAL_SERVER_ERROR);
} }
$this->hub->publish(new Update( $this->broadcaster->alert(
$request->getSession()->get('mercure_alert_topic'), title: 'Success',
$this->renderView('broadcast/Alert.stream.html.twig', [ message: "$input->title added to Queue."
'alert_id' => uniqid(), );
'title' => 'Success',
'message' => '"' . $input->title . '" added to Queue',
])
));
return $this->json(['status' => 200, 'message' => 'Added to Queue']); return $this->json(['status' => 200, 'message' => 'Added to Queue']);
} }
#[Route('/api/download/{downloadId}', name: 'api_download_delete', methods: ['DELETE'])] #[Route('/api/download/{downloadId}', name: 'api_download_delete', methods: ['DELETE'])]
public function deleteDownload( public function deleteDownload(
Request $request,
DeleteDownloadInput $input, DeleteDownloadInput $input,
DeleteDownloadHandler $handler, DeleteDownloadHandler $handler,
): Response { ): Response {
$result = $handler->handle($input->toCommand()); $result = $handler->handle($input->toCommand());
$this->hub->publish(new Update( $this->broadcaster->alert(
$request->getSession()->get('mercure_alert_topic'), title: 'Success',
$this->renderView('broadcast/Alert.stream.html.twig', [ message: "{$result->download->getTitle()} has been deleted.",
'alert_id' => uniqid(), );
'title' => 'Success',
'message' => '"' . $result->download->getTitle() . '" has been deleted.',
])
));
return $this->json(['status' => 200, 'message' => 'Download Deleted']); return $this->json(['status' => 200, 'message' => 'Download Deleted']);
} }

View File

@@ -7,19 +7,18 @@ use App\Monitor\Action\Handler\AddMonitorHandler;
use App\Monitor\Action\Handler\DeleteMonitorHandler; use App\Monitor\Action\Handler\DeleteMonitorHandler;
use App\Monitor\Action\Input\AddMonitorInput; use App\Monitor\Action\Input\AddMonitorInput;
use App\Monitor\Action\Input\DeleteMonitorInput; use App\Monitor\Action\Input\DeleteMonitorInput;
use App\Util\Broadcaster;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Bundle\SecurityBundle\Security; use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\DependencyInjection\Attribute\Autowire; use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\Mercure\HubInterface; use Symfony\Component\Mercure\HubInterface;
use Symfony\Component\Mercure\Update; use Symfony\Component\Mercure\Update;
use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Routing\Attribute\Route;
use Twig\Environment;
class ApiController extends AbstractController class ApiController extends AbstractController
{ {
public function __construct( public function __construct(
#[Autowire(service: 'twig')] private readonly Broadcaster $broadcaster,
private readonly Environment $renderer,
) {} ) {}
#[Route('/api/monitor', name: 'api_monitor', methods: ['POST'])] #[Route('/api/monitor', name: 'api_monitor', methods: ['POST'])]
@@ -32,14 +31,10 @@ class ApiController extends AbstractController
$command->userId = $this->getUser()->getId(); $command->userId = $this->getUser()->getId();
$response = $handler->handle($command); $response = $handler->handle($command);
$hub->publish(new Update( $this->broadcaster->alert(
'alerts', title: 'Success',
$this->renderer->render('broadcast/Alert.stream.html.twig', [ message: "New monitor added for {$input->title}",
'alert_id' => uniqid(), );
'title' => 'Success',
'message' => "New monitor added for {$input->title}",
])
));
return $this->json([ return $this->json([
'status' => 200, 'status' => 200,

View File

@@ -3,6 +3,7 @@
namespace App\Twig\Components; namespace App\Twig\Components;
use App\Monitor\Framework\Repository\MonitorRepository; use App\Monitor\Framework\Repository\MonitorRepository;
use Doctrine\ORM\Query\Expr\OrderBy;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\UX\LiveComponent\Attribute\AsLiveComponent; use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
use Symfony\UX\LiveComponent\Attribute\LiveAction; use Symfony\UX\LiveComponent\Attribute\LiveAction;
@@ -16,6 +17,9 @@ final class MonitorList extends AbstractController
use PaginateTrait; use PaginateTrait;
#[LiveProp(writable: true)]
public string $type;
#[LiveProp(writable: true)] #[LiveProp(writable: true)]
public bool $isWidget = true; public bool $isWidget = true;
@@ -24,8 +28,36 @@ final class MonitorList extends AbstractController
) {} ) {}
#[LiveAction] #[LiveAction]
public function getUserMonitors() public function getMonitors()
{ {
return $this->monitorRepository->getUserMonitorsPaginated($this->getUser(), $this->pageNumber, $this->perPage); if ($this->type === "active") {
return $this->getActiveUserMonitors();
} elseif ($this->type === "complete") {
return $this->getCompleteUserMonitors();
}
return [];
}
#[LiveAction]
public function getActiveUserMonitors()
{
return $this->asPaginator($this->monitorRepository->createQueryBuilder('m')
->andWhere('m.status IN (:statuses)')
->setParameter('statuses', ['New', 'In Progress'])
->orderBy('m.id', 'DESC')
->getQuery()
);
}
#[LiveAction]
public function getCompleteUserMonitors()
{
return $this->asPaginator($this->monitorRepository->createQueryBuilder('m')
->andWhere('m.status = :status')
->setParameter('status', 'Complete')
->orderBy('m.id', 'DESC')
->getQuery()
);
} }
} }

View File

@@ -0,0 +1,36 @@
<?php
namespace App\Twig\Extensions;
use App\Monitor\Framework\Entity\Monitor;
use Twig\Attribute\AsTwigFilter;
class MonitorExtension
{
#[AsTwigFilter('monitor_type')]
public function type(string $type)
{
$types = [
'tvshows' => 'Show',
'tvseason' => 'Season',
'tvepisode' => 'Episode',
];
return $types[$type] ?? '-';
}
#[AsTwigFilter('monitor_media_id')]
public function mediaId(Monitor $monitor)
{
if ($monitor->getMonitorType() === "tvseason") {
return "S". str_pad($monitor->getSeason(), 2, "0", STR_PAD_LEFT);
}
if ($monitor->getMonitorType() === "tvepisode") {
return "S". str_pad($monitor->getSeason(), 2, "0", STR_PAD_LEFT) .
"E". str_pad($monitor->getEpisode(), 2, "0", STR_PAD_LEFT);
}
return "-";
}
}

View File

@@ -9,23 +9,20 @@ use App\User\Action\Handler\SaveUserMediaPreferencesHandler;
use App\User\Action\Input\SaveUserDownloadPreferencesInput; use App\User\Action\Input\SaveUserDownloadPreferencesInput;
use App\User\Action\Input\SaveUserMediaPreferencesInput; use App\User\Action\Input\SaveUserMediaPreferencesInput;
use App\User\Framework\Repository\PreferencesRepository; use App\User\Framework\Repository\PreferencesRepository;
use App\Util\Broadcaster;
use App\Util\CountryLanguages; use App\Util\CountryLanguages;
use App\Util\ProviderList; use App\Util\ProviderList;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Mercure\HubInterface;
use Symfony\Component\Mercure\Update;
use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Routing\Attribute\Route;
class PreferencesController extends AbstractController class PreferencesController extends AbstractController
{ {
public function __construct( public function __construct(
private readonly PreferencesRepository $preferencesRepository, private readonly PreferencesRepository $preferencesRepository,
private readonly SaveUserMediaPreferencesHandler $saveUserMediaPreferencesHandler, private readonly Broadcaster $broadcaster,
private readonly HubInterface $hub,
private readonly SaveUserDownloadPreferencesHandler $saveUserDownloadPreferencesHandler,
) {} ) {}
#[Route('/user/preferences', 'app_user_preferences', methods: ['GET'])] #[Route('/user/preferences', 'app_user_preferences', methods: ['GET'])]
public function mediaPreferences(): Response public function mediaPreferences(): Response
{ {
@@ -48,25 +45,21 @@ class PreferencesController extends AbstractController
#[Route('/user/preferences/media', 'app_save_media_preferences', methods: ['POST'])] #[Route('/user/preferences/media', 'app_save_media_preferences', methods: ['POST'])]
public function saveMediaPreferences( public function saveMediaPreferences(
Request $request,
SaveUserMediaPreferencesInput $input, SaveUserMediaPreferencesInput $input,
SaveUserMediaPreferencesHandler $saveUserMediaPreferencesHandler,
): Response ): Response
{ {
$this->saveUserMediaPreferencesHandler->handle($input->toCommand()); $saveUserMediaPreferencesHandler->handle($input->toCommand());
$mediaPreferences = $this->getUser()->getMediaPreferences(); $mediaPreferences = $this->getUser()->getMediaPreferences();
$downloadPreferences = $this->getUser()->getDownloadPreferences(); $downloadPreferences = $this->getUser()->getDownloadPreferences();
$languages = CountryLanguages::$languages; $languages = CountryLanguages::$languages;
sort($languages); sort($languages);
$this->hub->publish(new Update( $this->broadcaster->alert(
$request->getSession()->get('mercure_alert_topic'), title: 'Success',
$this->renderView('broadcast/Alert.stream.html.twig', [ message: 'Your media preferences have been saved.'
'alert_id' => uniqid(), );
'title' => 'Success',
'message' => 'Your media preferences have been saved.',
])
));
return $this->render( return $this->render(
'user/preferences.html.twig', 'user/preferences.html.twig',
@@ -82,24 +75,20 @@ class PreferencesController extends AbstractController
#[Route('/user/preferences/download', 'app_save_download_preferences', methods: ['POST'])] #[Route('/user/preferences/download', 'app_save_download_preferences', methods: ['POST'])]
public function saveDownloadPreferences( public function saveDownloadPreferences(
Request $request,
SaveUserDownloadPreferencesInput $input, SaveUserDownloadPreferencesInput $input,
SaveUserDownloadPreferencesHandler $saveUserDownloadPreferencesHandler,
): Response ): Response
{ {
$downloadPreferences = $this->saveUserDownloadPreferencesHandler->handle($input->toCommand())->downloadPreferences; $downloadPreferences = $saveUserDownloadPreferencesHandler->handle($input->toCommand())->downloadPreferences;
$mediaPreferences = $this->getUser()->getMediaPreferences(); $mediaPreferences = $this->getUser()->getMediaPreferences();
$languages = CountryLanguages::$languages; $languages = CountryLanguages::$languages;
sort($languages); sort($languages);
$this->hub->publish(new Update( $this->broadcaster->alert(
$request->getSession()->get('mercure_alert_topic'), title: 'Success',
$this->renderView('broadcast/Alert.stream.html.twig', [ message: 'Your download preferences have been saved.'
'alert_id' => uniqid(), );
'title' => 'Success',
'message' => 'Your download preferences have been saved.',
])
));
return $this->render( return $this->render(
'user/preferences.html.twig', 'user/preferences.html.twig',

33
src/Util/Broadcaster.php Normal file
View File

@@ -0,0 +1,33 @@
<?php
namespace App\Util;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Mercure\HubInterface;
use Symfony\Component\Mercure\Update;
use Twig\Environment;
readonly class Broadcaster
{
public function __construct(
#[Autowire(service: 'twig')]
private Environment $renderer,
private HubInterface $hub,
private RequestStack $requestStack,
) {}
public function alert(string $title, string $message): void
{
$userAlertTopic = $this->requestStack->getCurrentRequest()->getSession()->get('mercure_alert_topic');
$update = new Update(
$userAlertTopic,
$this->renderer->render('broadcast/Alert.stream.html.twig', [
'alert_id' => uniqid(),
'title' => $title,
'message' => $message,
])
);
$this->hub->publish($update);
}
}

View File

@@ -2,6 +2,15 @@
"1tomany/rich-bundle": { "1tomany/rich-bundle": {
"version": "v1.8.3" "version": "v1.8.3"
}, },
"doctrine/deprecations": {
"version": "1.1",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "1.0",
"ref": "87424683adc81d7dc305eefec1fced883084aab9"
}
},
"doctrine/doctrine-bundle": { "doctrine/doctrine-bundle": {
"version": "2.14", "version": "2.14",
"recipe": { "recipe": {
@@ -169,6 +178,18 @@
"config/packages/messenger.yaml" "config/packages/messenger.yaml"
] ]
}, },
"symfony/property-info": {
"version": "7.3",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "7.3",
"ref": "dae70df71978ae9226ae915ffd5fad817f5ca1f7"
},
"files": [
"config/packages/property_info.yaml"
]
},
"symfony/routing": { "symfony/routing": {
"version": "7.2", "version": "7.2",
"recipe": { "recipe": {

View File

@@ -4,6 +4,14 @@ module.exports = {
"./assets/**/*.js", "./assets/**/*.js",
"./templates/**/*.html.twig", "./templates/**/*.html.twig",
], ],
safelist: [
"bg-blue-300",
"bg-orange-300",
"bg-rose-300",
"bg-green-400",
"bg-purple-400",
"bg-orange-400",
],
theme: { theme: {
extend: {}, extend: {},
}, },

View File

@@ -82,4 +82,10 @@
{% block remove %} {% block remove %}
<turbo-stream action="remove" target="monitor_{{ id }}"></turbo-stream> <turbo-stream action="remove" target="monitor_{{ id }}"></turbo-stream>
<turbo-stream action="prepend" target="alert_list">
<template>
<twig:Alert title="Success" message="Your Monitor for '{{ entity.title }}' has been removed." alert_id="monitor_alert_{{ entity.id }}" data-controller="alert" />
</template>
</turbo-stream>
{% endblock %} {% endblock %}

View File

@@ -70,7 +70,7 @@
{% endfor %} {% endfor %}
{% if this.isWidget == true and this.downloads.items|length > this.perPage %} {% if this.isWidget == true and this.downloads.items|length > this.perPage %}
<tr id="download_view_all"> <tr id="download_view_all">
<td class="py-2 whitespace-nowrap bg-orange-500 uppercase text-sm font-medium text-center text-white truncate" colspan="100%"> <td class="py-2 whitespace-nowrap bg-orange-300 uppercase text-xs font-medium text-center text-black truncate" colspan="100%">
<a href="{{ path('app_downloads') }}">View All Downloads</a> <a href="{{ path('app_downloads') }}">View All Downloads</a>
</td> </td>
</tr> </tr>

View File

@@ -3,9 +3,13 @@
<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"
class="px-6 py-3 text-start text-xs font-medium uppercase min-w-[45ch] max-w-[45ch] truncate"> class="px-6 py-3 text-start text-xs font-medium uppercase truncate">
Title Title
</th> </th>
<th scope="col"
class="px-6 py-3 text-start text-xs font-medium uppercase">
ID
</th>
<th scope="col" <th scope="col"
class="px-6 py-3 text-start text-xs font-medium uppercase"> class="px-6 py-3 text-start text-xs font-medium uppercase">
Search Count Search Count
@@ -18,6 +22,10 @@
class="px-6 py-3 text-start text-xs font-medium uppercase"> class="px-6 py-3 text-start text-xs font-medium uppercase">
Last Search Date Last Search Date
</th> </th>
<th scope="col"
class="px-6 py-3 text-start text-xs font-medium uppercase">
Type
</th>
<th scope="col" <th scope="col"
class="px-6 py-3 text-start text-xs font-medium uppercase"> class="px-6 py-3 text-start text-xs font-medium uppercase">
Status Status
@@ -26,12 +34,15 @@
</tr> </tr>
</thead> </thead>
<tbody id="monitors" class="divide-y divide-gray-50"> <tbody id="monitors" class="divide-y divide-gray-50">
{% if this.userMonitors.items|length > 0 %} {% if this.monitors.items|length > 0 %}
{% for monitor in this.userMonitors.items %} {% for monitor in this.monitors.items %}
<tr id="monitor_{{ monitor.id }}"> <tr id="monitor_{{ monitor.id }}">
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-stone-800 min-w-[50ch] max-w-[50ch] truncate"> <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-stone-800 truncate">
{{ monitor.title }} {{ monitor.title }}
</td> </td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-800">
{{ monitor|monitor_media_id }}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-800"> <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-800">
{{ monitor.searchCount }} {{ monitor.searchCount }}
</td> </td>
@@ -41,6 +52,15 @@
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-800"> <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-800">
{{ monitor.lastSearch|date('m/d/Y h:i a') }} {{ monitor.lastSearch|date('m/d/Y h:i a') }}
</td> </td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-800">
{% if monitor.monitorType == "tvshow" %}
<twig:StatusBadge color="blue" number="300" text="black" status="{{ monitor.monitorType|monitor_type }}" />
{% elseif monitor.monitorType == "tvseason" %}
<twig:StatusBadge color="orange" number="300" text="black" status="{{ monitor.monitorType|monitor_type }}" />
{% else %}
<twig:StatusBadge color="rose" number="300" text="black" status="{{ monitor.monitorType|monitor_type }}" />
{% endif %}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-800"> <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-800">
{% if monitor.status == "New" %} {% if monitor.status == "New" %}
<twig:StatusBadge color="orange" status="{{ monitor.status }}" /> <twig:StatusBadge color="orange" status="{{ monitor.status }}" />
@@ -61,9 +81,9 @@
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}
{% if this.isWidget and this.userMonitors.items|length > 5 %} {% if this.isWidget and this.monitors.items|length > 5 %}
<tr id="monitor_view_all"> <tr id="monitor_view_all">
<td colspan="100%" class="py-2 whitespace-nowrap bg-orange-500 uppercase text-sm font-medium text-center text-white min-w-[50ch] max-w-[50ch] truncate"> <td colspan="100%" class="py-2 whitespace-nowrap bg-orange-300 uppercase text-xs font-medium text-center text-black min-w-[50ch] max-w-[50ch] truncate">
<a href="{{ path('app_monitors') }}">View All Monitors</a> <a href="{{ path('app_monitors') }}">View All Monitors</a>
</td> </td>
</tr> </tr>
@@ -79,8 +99,8 @@
</table> </table>
{% if this.isWidget == false %} {% if this.isWidget == false %}
{% if this.userMonitors.items|length > 0 %} {% if this.monitors.items|length > 0 %}
{% set paginator = this.userMonitors %} {% set paginator = this.monitors %}
{% include 'partial/paginator.html.twig' %} {% include 'partial/paginator.html.twig' %}
{% endif %} {% endif %}
{% endif %} {% endif %}

View File

@@ -1,3 +1,8 @@
<span {{ attributes }} class="py-[3px] px-[7px] bg-{{ color|default('green') }}-600 rounded-lg inline-block text-center text-xs text-white"> <span {{ attributes }}
class="py-[3px] px-[7px]
bg-{{ color|default('green') }}-{{ number|default(400) }}
text-{{ text|default('black') }}
rounded-lg inline-block text-center text-xs"
>
{{ status }} {{ status }}
</span> </span>

View File

@@ -16,7 +16,7 @@
</div> </div>
<div class="flex flex-row gap-4"> <div class="flex flex-row gap-4">
<twig:Card title="Monitors" class="w-full"> <twig:Card title="Monitors" class="w-full">
<twig:MonitorList /> <twig:MonitorList :type="'active'" :isWidget="true" />
</twig:Card> </twig:Card>
</div> </div>
<div class="flex flex-col gap-4"> <div class="flex flex-col gap-4">

View File

@@ -5,8 +5,14 @@
{% block body %} {% block body %}
<div class="p-4"> <div class="p-4">
<twig:Card title="Monitors"> <twig:Card title="Active Monitors">
<twig:MonitorList :isWidget="false" :perPage="10"></twig:MonitorList> <twig:MonitorList :type="'active'" :isWidget="false" :perPage="10"></twig:MonitorList>
</twig:Card>
</div>
<div class="p-4">
<twig:Card title="Complete Monitors">
<twig:MonitorList :type="'complete'" :isWidget="false" :perPage="10"></twig:MonitorList>
</twig:Card> </twig:Card>
</div> </div>
{% endblock %} {% endblock %}