make details for Comic, Artist and Issue clickable, add CustomField to select Comic and Issue
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
from typing import Dict, List, Optional
|
||||
from typing import Dict, List, Optional, Any
|
||||
from natsort import natsorted
|
||||
from sqlalchemy import Column, ForeignKey, Integer, String, Boolean, func
|
||||
from sqlalchemy.orm import relationship, Mapped, mapped_column
|
||||
@@ -48,10 +48,10 @@ class Comic(Base, BaseMixin):
|
||||
def __str__(self):
|
||||
return f'{self.title}({self.id})'
|
||||
|
||||
def get_artists(self) -> Dict[str, List[str]]:
|
||||
works: Dict[str, List[str]] = {}
|
||||
def get_artists(self) -> Dict[Any, List[Any]]:
|
||||
works: Dict[Any, List[Any]] = {}
|
||||
for work in self.comic_works:
|
||||
work_type = work.work_type.name
|
||||
work_type = work.work_type
|
||||
artist = work.artist
|
||||
if work_type in works:
|
||||
works[work_type].append(artist)
|
||||
@@ -107,6 +107,18 @@ class Issue(Base, BaseMixin):
|
||||
story_arc = relationship("StoryArc", back_populates="issues")
|
||||
issue_works = relationship("IssueWork")
|
||||
|
||||
def get_artists(self) -> Dict[Any, List[Any]]:
|
||||
works: Dict[Any, List[Any]] = {}
|
||||
for work in self.issue_works:
|
||||
work_type = work.work_type
|
||||
artist = work.artist
|
||||
if work_type in works:
|
||||
works[work_type].append(artist)
|
||||
else:
|
||||
works[work_type] = [artist]
|
||||
return works
|
||||
|
||||
|
||||
class Artist(Base, BaseMixin):
|
||||
__tablename__ = "artist"
|
||||
name = Column(String, nullable=False)
|
||||
@@ -114,10 +126,10 @@ class Artist(Base, BaseMixin):
|
||||
comic_works = relationship("ComicWork")
|
||||
issue_works = relationship("IssueWork")
|
||||
|
||||
def get_comics(self) -> Dict[str, List[str]]:
|
||||
works: Dict[str, List[str]] = {}
|
||||
def get_comics(self) -> Dict[Any, List[Comic]]:
|
||||
works: Dict[Any, List[Comic]] = {}
|
||||
for work in self.comic_works:
|
||||
work_type = work.work_type.name
|
||||
work_type = work.work_type
|
||||
comic = work.comic
|
||||
if work_type in works:
|
||||
works[work_type].append(comic)
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
<td colspan="2">
|
||||
{% for work in artist.get_comics() %}
|
||||
<p>
|
||||
{{work}}:
|
||||
<a href="/comic/worktypes/{{work.id}}">{{work.name}}</a>
|
||||
<ul>
|
||||
{% for comic in artist.get_comics()[work] %}
|
||||
<li><a href="/comic/comics/{{comic.id}}">{{comic.title}}</a></li>
|
||||
@@ -47,6 +47,10 @@
|
||||
<th scope="row">Data Modified</th>
|
||||
<td colspan="2">{{artist.last_modified_date}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Data Version</th>
|
||||
<td colspan="2">{{artist.version}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
<td colspan="2">
|
||||
{% for work in comic.get_artists() %}
|
||||
<p>
|
||||
{{work}}:
|
||||
<a href="/comic/worktypes/{{work.id}}">{{work.name}}</a>
|
||||
<ul>
|
||||
{% for artist in comic.get_artists()[work] %}
|
||||
<li><a href="/comic/artists/{{artist.id}}">{{artist.name}}</a></li>
|
||||
|
||||
@@ -10,9 +10,15 @@
|
||||
{% endwith %}
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h1 class="display-5">Comics..</h1>
|
||||
<form class="d-flex" action="/comic/comics/">
|
||||
<input class="form-control me-2" name="query" id="autocomplete" type="search" placeholder="Search" aria-label="Search">
|
||||
Completed<input type="checkbox" name="completed" {% if request.query_params.get("completed")=="on" %}checked{% endif %} aria-label="Completed">
|
||||
Order<input type="checkbox" name="order" {% if request.query_params.get("order")=="on" %}checked{% endif %} aria-label="Order">
|
||||
<button class="btn btn-outline-success" type="submit">Search</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="row">
|
||||
<h1 class="display-5">Comics..</h1>
|
||||
</div>
|
||||
<div class="row">
|
||||
<table class="table table-hover">
|
||||
|
||||
@@ -50,12 +50,29 @@
|
||||
<a href="/comic/comics/{{issue.comic_id}}">{{issue.comic.title}}</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% if issue.volume %}
|
||||
<tr>
|
||||
<th scope="row">Volume</th>
|
||||
<td colspan="2">
|
||||
<a href="/comic/comics/{{issue.volume_id}}">{{issue.volume.name}}</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
<tr>
|
||||
<th scope="row">Works</th>
|
||||
<td colspan="2">
|
||||
{% for work in issue.get_artists() %}
|
||||
<p>
|
||||
<a href="/comic/worktypes/{{work.id}}">{{work.name}}</a>
|
||||
<ul>
|
||||
{% for artist in issue.get_artists()[work] %}
|
||||
<li><a href="/comic/artists/{{artist.id}}">{{artist.name}}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</p>
|
||||
{% endfor %}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Data Created</th>
|
||||
<td colspan="2">{{issue.created_date}}</td>
|
||||
|
||||
@@ -10,6 +10,23 @@ router = APIRouter(include_in_schema=False, prefix="/comic")
|
||||
|
||||
@router.get("/comics")
|
||||
def get_comics(db: SessionDep, request: Request, msg: str | None = None):
|
||||
params = request.query_params
|
||||
query = params.get("query")
|
||||
filter = {}
|
||||
completed = params.get('completed') == "on"
|
||||
if completed:
|
||||
filter['completed'] = True
|
||||
order = params.get("order") == "on"
|
||||
if order:
|
||||
filter['current_order'] = True
|
||||
if query is not None and len(query) > 0:
|
||||
filter['title'] = query
|
||||
if len(filter) > 0:
|
||||
if "title" in filter:
|
||||
comics = db.query(Comic).filter(Comic.title.ilike(f'%{query}%'))
|
||||
else:
|
||||
comics = db.query(Comic).filter_by(**filter).all()
|
||||
else:
|
||||
comics = db.query(Comic).all()
|
||||
return templates.TemplateResponse("comic/comics.html", {"request": request, "msg": msg, "comics": comics})
|
||||
|
||||
|
||||
@@ -70,6 +70,18 @@ public class Issue extends AbstractEntity {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getFullTitle() {
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
stringBuilder.append(this.getComic().getTitle());
|
||||
stringBuilder.append(" #");
|
||||
stringBuilder.append(this.getIssueNumber());
|
||||
if (this.title !=null && !this.title.isEmpty()) {
|
||||
stringBuilder.append(": ");
|
||||
stringBuilder.append(this.title);
|
||||
}
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder result = new StringBuilder();
|
||||
|
||||
@@ -16,7 +16,10 @@ import com.vaadin.flow.component.textfield.TextField;
|
||||
|
||||
import com.vaadin.flow.data.binder.BeanValidationBinder;
|
||||
import com.vaadin.flow.data.binder.Binder;
|
||||
import de.thpeetz.kontor.comics.data.*;
|
||||
import de.thpeetz.kontor.comics.data.Comic;
|
||||
import de.thpeetz.kontor.comics.data.Issue;
|
||||
import de.thpeetz.kontor.comics.data.IssueWork;
|
||||
import de.thpeetz.kontor.comics.data.Volume;
|
||||
import de.thpeetz.kontor.common.views.YearMonthField;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
|
||||
@@ -14,12 +14,13 @@ import com.vaadin.flow.data.binder.BeanValidationBinder;
|
||||
import com.vaadin.flow.data.binder.Binder;
|
||||
|
||||
import de.thpeetz.kontor.comics.data.Artist;
|
||||
import de.thpeetz.kontor.comics.data.Issue;
|
||||
import de.thpeetz.kontor.comics.data.Comic;
|
||||
import de.thpeetz.kontor.comics.data.IssueWork;
|
||||
import de.thpeetz.kontor.comics.data.Worktype;
|
||||
import de.thpeetz.kontor.common.views.ComicIssueField;
|
||||
|
||||
public class IssueWorkForm extends FormLayout {
|
||||
ComboBox<Issue> issue = new ComboBox<>("Issue");
|
||||
ComicIssueField issue = new ComicIssueField("Issue");
|
||||
ComboBox<Artist> artist = new ComboBox<>("Artist");
|
||||
ComboBox<Worktype> workType = new ComboBox<>("Worktype");
|
||||
|
||||
@@ -29,12 +30,13 @@ public class IssueWorkForm extends FormLayout {
|
||||
|
||||
Binder<IssueWork> binder = new BeanValidationBinder<>(IssueWork.class);
|
||||
|
||||
public IssueWorkForm(List<Issue> issues, List<Artist> artists, List<Worktype> workTypes) {
|
||||
public IssueWorkForm(List<Comic> comics, List<Artist> artists, List<Worktype> workTypes) {
|
||||
addClassName("issuework-form");
|
||||
binder.bindInstanceFields(this);
|
||||
|
||||
issue.setItems(issues);
|
||||
issue.setItemLabelGenerator(Issue::getIssueNumber);
|
||||
issue.setComics(comics);
|
||||
//issue.setItems(issues);
|
||||
//issue.setItemLabelGenerator(Issue::getIssueNumber);
|
||||
artist.setItems(artists);
|
||||
artist.setItemLabelGenerator(Artist::getName);
|
||||
workType.setItems(workTypes);
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package de.thpeetz.kontor.comics.views;
|
||||
|
||||
import de.thpeetz.kontor.comics.data.Issue;
|
||||
import de.thpeetz.kontor.comics.data.IssueWork;
|
||||
import lombok.Getter;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
|
||||
import com.vaadin.flow.component.Component;
|
||||
@@ -24,7 +26,9 @@ import jakarta.annotation.security.PermitAll;
|
||||
@PageTitle("IssueWork | Comics | Kontor")
|
||||
public class IssueWorkView extends VerticalLayout {
|
||||
|
||||
@Getter
|
||||
Grid<IssueWork> grid = new Grid<>(IssueWork.class);
|
||||
@Getter
|
||||
IssueWorkForm form;
|
||||
ComicService service;
|
||||
|
||||
@@ -39,24 +43,16 @@ public class IssueWorkView extends VerticalLayout {
|
||||
updateList();
|
||||
}
|
||||
|
||||
public Grid<IssueWork> getGrid() {
|
||||
return grid;
|
||||
}
|
||||
|
||||
private void configureGrid() {
|
||||
grid.addClassName("issue-grid");
|
||||
grid.setSizeFull();
|
||||
grid.setColumns("issue.title", "artist.name", "workType.name");
|
||||
grid.setColumns("issue.fullTitle", "artist.name", "workType.name");
|
||||
grid.getColumns().forEach(col -> col.setAutoWidth(true));
|
||||
grid.asSingleSelect().addValueChangeListener(event -> editIssueWork(event.getValue()));
|
||||
}
|
||||
|
||||
public IssueWorkForm getForm() {
|
||||
return form;
|
||||
}
|
||||
|
||||
private void configureForm() {
|
||||
form = new IssueWorkForm(service.findAllIssues(), service.findAllArtists(null),
|
||||
form = new IssueWorkForm(service.findAllComics(null), service.findAllArtists(null),
|
||||
service.findAllWorktypes(null));
|
||||
form.setWidth("25em");
|
||||
form.setVisible(false);
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
package de.thpeetz.kontor.common.views;
|
||||
|
||||
import com.vaadin.flow.component.Unit;
|
||||
import com.vaadin.flow.component.customfield.CustomField;
|
||||
import com.vaadin.flow.component.select.Select;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import de.thpeetz.kontor.comics.data.Comic;
|
||||
import de.thpeetz.kontor.comics.data.Issue;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
public class ComicIssueField extends CustomField<Issue> {
|
||||
|
||||
public final Select<Comic> comic = new Select<>();
|
||||
public final Select<Issue> issue = new Select<>();
|
||||
|
||||
public ComicIssueField(String caption) {
|
||||
comic.setEnabled(false);
|
||||
comic.setWidth(9, Unit.EM);
|
||||
comic.setItemLabelGenerator(Comic::getTitle);
|
||||
comic.addValueChangeListener(e -> {
|
||||
updateIssues();
|
||||
});
|
||||
issue.setEnabled(false);
|
||||
issue.setWidth(9, Unit.EM);
|
||||
issue.setItemLabelGenerator(Issue::getIssueNumber);
|
||||
add(comic, issue);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Issue generateModelValue() {
|
||||
log.info("ComicIssueField.generateModelValue: {}", issue.getValue());
|
||||
return issue.getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setPresentationValue(Issue newPresentationValue) {
|
||||
log.info("ComicIssueField.setPresentationValue({})", newPresentationValue);
|
||||
if (newPresentationValue == null) return;
|
||||
comic.setValue(newPresentationValue.getComic());
|
||||
log.info("setPresentationValue: set Comic select = {}", comic.getValue());
|
||||
issue.setValue(newPresentationValue);
|
||||
}
|
||||
|
||||
public void setComics(List<Comic> comics) {
|
||||
log.info("ComicIssueField.setComics");
|
||||
comic.setItems(comics);
|
||||
comic.setEnabled(true);
|
||||
}
|
||||
|
||||
private void updateIssues() {
|
||||
log.info("ComicIssueField.updateIssues");
|
||||
if (comic.getValue() == null) {
|
||||
issue.setValue(null);
|
||||
issue.setEnabled(false);
|
||||
return;
|
||||
}
|
||||
issue.setItems(comic.getValue().getIssues());
|
||||
issue.setEnabled(true);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user