Skip to content

Commit

Permalink
Add settings page for locations
Browse files Browse the repository at this point in the history
  • Loading branch information
leepeuker committed Aug 21, 2024
1 parent 440920a commit 9124979
Show file tree
Hide file tree
Showing 14 changed files with 342 additions and 4 deletions.
1 change: 1 addition & 0 deletions public/css/bootstrap-icons-1.10.2.css
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,4 @@ url("../fonts/bootstrap-icons.woff?24e3eb84d0bcaf83d77f904c78ac1f47") format("wo
.bi-chevron-contract::before { content: "\f27d"; }
.bi-question-lg::before { content: "\f64e"; }
.bi-filter::before{content:"\f3ca"}
.bi-pin-map-fill::before{content:"\f64b"}
5 changes: 4 additions & 1 deletion public/js/movie.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ function loadWatchDateModal(watchDateListElement) {
document.getElementById('editWatchDateModalPlaysInput').value = watchDateListElement.dataset.plays;
document.getElementById('editWatchDateModalCommentInput').value = watchDateListElement.dataset.comment;
document.getElementById('editWatchDateModalPositionInput').value = watchDateListElement.dataset.position;
document.getElementById('editWatchDateModalLocationInput').value = watchDateListElement.dataset.location;

document.getElementById('originalWatchDate').value = watchDateListElement.dataset.watchDate;
document.getElementById('originalWatchDatePlays').value = watchDateListElement.dataset.plays;
Expand All @@ -125,6 +126,7 @@ function editWatchDate() {
const newWatchDatePlays = document.getElementById('editWatchDateModalPlaysInput').value;
const newPositionPlays = document.getElementById('editWatchDateModalPositionInput').value;
const comment = document.getElementById('editWatchDateModalCommentInput').value;
const location = document.getElementById('editWatchDateModalLocationInput').value;

const apiUrl = '/users/' + getRouteUsername() + '/movies/' + getMovieId() + '/history'

Expand All @@ -137,7 +139,8 @@ function editWatchDate() {
'plays': newWatchDatePlays,
'comment': comment,
'position': newPositionPlays,
'dateFormat': document.getElementById('dateFormatPhp').value
'dateFormat': document.getElementById('dateFormatPhp').value,
'location': location
}),
success: function (data, textStatus, xhr) {
window.location.reload()
Expand Down
145 changes: 145 additions & 0 deletions public/js/settings-account-location.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
const locationModal = new bootstrap.Modal('#locationModal', {keyboard: false})

const table = document.getElementById('locationsTable');
const rows = table.getElementsByTagName('tr');

reloadTable()

async function reloadTable() {
table.getElementsByTagName('tbody')[0].innerHTML = ''
document.getElementById('locationsTableLoadingSpinner').classList.remove('d-none')

const response = await fetch('/settings/locations');

console.log(response.status)
if (response.status !== 200) {
setLocationsAlert('Could not load locations', 'danger')
document.getElementById('locationsTableLoadingSpinner').classList.add('d-none')

return
}

const locations = await response.json();

document.getElementById('locationsTableLoadingSpinner').classList.add('d-none')

locations.forEach((location) => {
let row = document.createElement('tr');
row.innerHTML = '<td>' + location.id + '</td>';
row.innerHTML += '<td>' + location.name + '</td>';
row.style.cursor = 'pointer'

table.getElementsByTagName('tbody')[0].appendChild(row);
})

registerTableRowClickEvent()
}

function setLocationsAlert(message, type = 'success') {
const locationManagementAlerts = document.getElementById('locationAlerts');
locationManagementAlerts.classList.remove('d-none')
locationManagementAlerts.innerHTML = ''
locationManagementAlerts.innerHTML = '<div class="alert alert-' + type + ' alert-dismissible" role="alert">' + message + '<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button></div>'
locationManagementAlerts.style.textAlign = 'center'
}

function registerTableRowClickEvent() {
for (let i = 0; i < rows.length; i++) {
if (i === 0) continue

rows[i].onclick = function () {
prepareEditLocationsModal(
this.cells[0].innerHTML,
this.cells[1].innerHTML,
this.cells[2].innerHTML,
this.cells[3].innerHTML === '1'
)

locationModal.show()
};
}
}

function prepareEditLocationsModal(id, name) {
document.getElementById('locationModalHeaderTitle').innerHTML = 'Edit User'

document.getElementById('locationModalFooterCreateButton').classList.add('d-none')
document.getElementById('locationModalFooterButtons').classList.remove('d-none')

document.getElementById('locationModalNameInput').value = name

document.getElementById('locationModalAlerts').innerHTML = ''

// Remove class invalid-input from all (input) elements
Array.from(document.querySelectorAll('.invalid-input')).forEach((el) => el.classList.remove('invalid-input'));
}

function showCreateLocationModal() {
prepareCreateLocationModal()
locationModal.show()
}

function prepareCreateLocationModal(name) {
document.getElementById('locationModalHeaderTitle').innerHTML = 'Create Location'

document.getElementById('locationModalFooterCreateButton').classList.remove('d-none')
document.getElementById('locationModalFooterButtons').classList.add('d-none')

document.getElementById('locationModalIdInput').value = ''
document.getElementById('locationModalNameInput').value = ''

document.getElementById('locationModalAlerts').innerHTML = ''

// Remove class invalid-input from all (input) elements
Array.from(document.querySelectorAll('.invalid-input')).forEach((el) => el.classList.remove('invalid-input'));
}

document.getElementById('createLocationButton').addEventListener('click', async () => {
if (validateCreateLocationInput() === true) {
return;
}

const response = await fetch('/settings/locations', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
'name': document.getElementById('locationModalNameInput').value,
})
})

if (response.status !== 200) {
setLocationModalAlertServerError(await response.text())
return
}

setLocationsAlert('Location was created: ' + document.getElementById('locationModalNameInput').value)

reloadTable()
locationModal.hide()
})

function setLocationModalAlertServerError(message = "Server error, please try again.") {
document.getElementById('locationModalAlerts').innerHTML = '<div class="alert alert-danger alert-dismissible" role="alert">' + message + '<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button></div>'
}


function validateCreateLocationInput() {
let error = false

const nameInput = document.getElementById('locationModalNameInput');

let mustNotBeEmptyInputs = [nameInput]

mustNotBeEmptyInputs.forEach((input) => {
input.classList.remove('invalid-input');
if (input.value.toString() === '') {
input.classList.add('invalid-input');

error = true
}
})

return error
}
1 change: 1 addition & 0 deletions settings/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ function addWebRoutes(RouterService $routerService, FastRoute\RouteCollector $ro
$routes->add('DELETE', '/settings/account/general/api-token', [Web\SettingsController::class, 'deleteApiToken'], [Web\Middleware\UserIsAuthenticated::class]);
$routes->add('PUT', '/settings/account/general/api-token', [Web\SettingsController::class, 'regenerateApiToken'], [Web\Middleware\UserIsAuthenticated::class]);
$routes->add('GET', '/settings/account/dashboard', [Web\SettingsController::class, 'renderDashboardAccountPage'], [Web\Middleware\UserIsAuthenticated::class]);
$routes->add('GET', '/settings/account/locations', [Web\SettingsController::class, 'renderLocationsAccountPage'], [Web\Middleware\UserIsAuthenticated::class]);
$routes->add('GET', '/settings/account/security', [Web\SettingsController::class, 'renderSecurityAccountPage'], [Web\Middleware\UserIsAuthenticated::class]);
$routes->add('GET', '/settings/account/data', [Web\SettingsController::class, 'renderDataAccountPage'], [Web\Middleware\UserIsAuthenticated::class]);
$routes->add('GET', '/settings/server/general', [Web\SettingsController::class, 'renderServerGeneralPage'], [Web\Middleware\UserIsAuthenticated::class]);
Expand Down
15 changes: 15 additions & 0 deletions src/Domain/Movie/History/Location/MovieHistoryLocationApi.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php declare(strict_types=1);

namespace Movary\Domain\Movie\History\Location;

class MovieHistoryLocationApi
{
public function __construct()
{
}

public function findLocationsByUserId(int $userId) : MovieHistoryLocationEntityList
{
return MovieHistoryLocationEntityList::create();
}
}
29 changes: 29 additions & 0 deletions src/Domain/Movie/History/Location/MovieHistoryLocationEntity.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php declare(strict_types=1);

namespace Movary\Domain\Movie\History\Location;

use Movary\ValueObject\DateTime;

class MovieHistoryLocationEntity
{
private function __construct(
private readonly int $id,
private readonly int $userId,
private readonly string $name,
private readonly DateTime $createdAt,
private readonly DateTime $updatedAt,
) {
}

public static function createFromArray(array $data) : self
{
return new self(
(int)$data['id'],
(int)$data['user_id'],
(string)$data['name'],
DateTime::createFromString((string)$data['created_at']),
DateTime::createFromString((string)$data['updated_at']),
);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php declare(strict_types=1);

namespace Movary\Domain\Movie\History\Location;

use Movary\ValueObject\AbstractList;

/**
* @method MovieHistoryLocationEntity[] getIterator()
* @psalm-suppress ImplementedReturnTypeMismatch
*/
class MovieHistoryLocationEntityList extends AbstractList
{
public static function create() : self
{
return new self();
}

public static function createFromArray(array $data) : self
{
$list = new self();

foreach ($data as $historyEntry) {
$list->add(MovieHistoryLocationEntity::createFromArray($historyEntry));
}

return $list;
}

private function add(MovieHistoryLocationEntity $dto) : void
{
$this->data[] = $dto;
}
}
16 changes: 16 additions & 0 deletions src/HttpController/Web/SettingsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Movary\Api\Tmdb\Cache\TmdbIsoCountryCache;
use Movary\Api\Trakt\TraktApi;
use Movary\Domain\Movie;
use Movary\Domain\Movie\History\Location\MovieHistoryLocationApi;
use Movary\Domain\User;
use Movary\Domain\User\Service\Authentication;
use Movary\Domain\User\Service\TwoFactorAuthenticationApi;
Expand Down Expand Up @@ -52,6 +53,7 @@ public function __construct(
private readonly DashboardFactory $dashboardFactory,
private readonly EmailService $emailService,
private readonly TmdbIsoCountryCache $countryCache,
private readonly MovieHistoryLocationApi $historyLocationApi,
) {
}

Expand Down Expand Up @@ -176,6 +178,20 @@ public function renderDashboardAccountPage() : Response
);
}

public function renderLocationsAccountPage() : Response
{
$user = $this->authenticationService->getCurrentUser();

$locations = $this->historyLocationApi->findLocationsByUserId($user->getId());

return Response::create(
StatusCode::createOk(),
$this->twig->render('page/settings-account-locations.html.twig', [
'locations' => $locations,
]),
);
}

public function renderDataAccountPage() : Response
{
$userId = $this->authenticationService->getCurrentUserId();
Expand Down
6 changes: 5 additions & 1 deletion templates/component/modal-edit-watch-date.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,11 @@
</div>
<div class="input-group" style="margin-top: 1rem;margin-bottom: 1rem">
<i class="input-group-text"><i class="bi bi-chat-square-text"></i></i>
<textarea class="form-control" rows="1" aria-label="Comment" id="editWatchDateModalCommentInput" placeholder="Comment"></textarea>
<textarea class="form-control" rows="1" aria-label="Comment" id="editWatchDateModalCommentInput"></textarea>
</div>
<div class="input-group" style="margin-top: 1rem;margin-bottom: 1rem">
<i class="input-group-text"><i class="bi bi-pin-map-fill"></i></i>
<select class="form-select" style="text-align: center;padding-right:0;" id="editWatchDateModalLocationInput" aria-describedby="location" required></select>
</div>
<div id="advancedWatchDateDetails" class="d-none">
<div class="input-group" style="margin-top: 1rem">
Expand Down
30 changes: 30 additions & 0 deletions templates/component/modal-location.html.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<div class="modal fade" id="locationModal" tabindex="-1" aria-labelledby="locationModal" aria-hidden="true" data-bs-keyboard="false">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="locationModalHeaderTitle"></h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body" style="padding-bottom: 0!important;">
<form>
<input type="hidden" id="locationModalIdInput">

<div class="mb-3">
<label for="locationModalNameInput" class="form-label">Name <span class="text-danger">*</span></label>
<input type="text" class="form-control" id="locationModalNameInput" required>
</div>
</form>
<div id="locationModalAlerts"></div>
</div>
<div class="modal-footer">
<div class="d-none" id="locationModalFooterCreateButton">
<button type="submit" form="locationCreateModal" class="btn btn-primary" id="createLocationButton">Create</button>
</div>
<div class="d-none" id="locationModalFooterButtons">
<button type="button" class="btn btn-danger" id="deleteLocationButton">Delete</button>
<button type="button" class="btn btn-primary" id="updateLocationButton">Save</button>
</div>
</div>
</div>
</div>
</div>
7 changes: 6 additions & 1 deletion templates/component/modal-log-play.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,12 @@
</div>
<div class="input-group" style="margin-top: 0.5rem;">
<i class="input-group-text"><i class="bi bi-chat-square-text"></i></i>
<textarea class="form-control" rows="1" aria-label="Comment" id="logPlayModalCommentInput" placeholder="Comment"></textarea>
<textarea class="form-control" rows="1" aria-label="Comment" id="logPlayModalCommentInput"></textarea>
</div>
<div class="input-group" style="margin-top: 0.5rem;">
<i class="input-group-text"><i class="bi bi-pin-map-fill"></i></i>
<select class="form-select" style="" id="editWatchDateModalLocationInput" aria-describedby="location" required>
</select>
</div>
</div>

Expand Down
3 changes: 3 additions & 0 deletions templates/component/settings-nav.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
<li class="dropdown-item">
<a class="nav-link {% if requestUrlPath starts with '/settings/account/security' %}active{% endif %}" href="/settings/account/security">Security</a>
</li>
<li class="dropdown-item">
<a class="nav-link {% if requestUrlPath starts with '/settings/account/locations' %}active{% endif %}" href="/settings/account/locations">Locations</a>
</li>
<li class="dropdown-item">
<a class="nav-link {% if requestUrlPath starts with '/settings/account/dashboard' %}active{% endif %}" href="/settings/account/dashboard">Dashboard</a>
</li>
Expand Down
3 changes: 2 additions & 1 deletion templates/page/movie.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,8 @@
data-watch-date="{{ watchDate.watched_at is null ? '' : watchDate.watched_at|date(dateFormatPhp) }}"
data-plays="{{ watchDate.plays }}"
data-comment="{{ watchDate.comment }}"
data-position="{{ watchDate.position }}">
data-position="{{ watchDate.position }}"
data-location="{{ watchDate.location_id }}">
{{ watchDate.watched_at is null ? 'Unkown date' : watchDate.watched_at|date(dateFormatPhp) }} {% if watchDate.plays > 1 %}({{ watchDate.plays }}x){% endif %}
{% if watchDate.comment != '' %}<i class="bi bi-chat-square-text" style="position: absolute; right:.7rem; top:25%;">{% endif %}</i>
</li>
Expand Down
Loading

0 comments on commit 9124979

Please sign in to comment.