feat(journey-editor): JourneyEditor frontend — issue #753 #792

Open
marcel wants to merge 92 commits from feat/issue-753-journey-editor into feat/issue-750-lesereisen-data-model
2 changed files with 36 additions and 2 deletions
Showing only changes of commit ae0b143685 - Show all commits

View File

@@ -30,8 +30,18 @@ public interface JourneyItemRepository extends JpaRepository<JourneyItem, UUID>
/** COUNT for the 100-item cap check — COUNT(*)-based, never MAX(position)-derived. */
long countByGeschichteId(UUID geschichteId);
/** Dedup guard: true when the document is already linked to this journey. */
boolean existsByGeschichteIdAndDocumentId(UUID geschichteId, UUID documentId);
/**
* Dedup guard: true when the document is already linked to this journey.
* Explicit JPQL, not a derived query: the transient {@code getDocumentId()}
* getter on JourneyItem makes Spring Data resolve the derived path as a
* direct {@code documentId} attribute, which Hibernate cannot map.
*/
@Query("""
SELECT COUNT(i) > 0 FROM JourneyItem i
WHERE i.geschichte.id = :geschichteId AND i.document.id = :documentId
""")
boolean existsByGeschichteIdAndDocumentId(
@Param("geschichteId") UUID geschichteId, @Param("documentId") UUID documentId);
/**
* Loads journey items with their linked Document in a single JOIN FETCH query,

View File

@@ -258,6 +258,30 @@ class JourneyItemIntegrationTest {
assertThat(persisted.get(0).getNote()).isEqualTo("First stop");
}
@Test
void append_document_persists_and_rejects_duplicate() {
// Covers the document branch of append, including the duplicate guard —
// the derived exists query must resolve document.id, which the transient
// getDocumentId() getter on JourneyItem shadows for Spring Data.
authenticateAs(writer, Permission.BLOG_WRITE);
JourneyItemCreateDTO dto = new JourneyItemCreateDTO();
dto.setDocumentId(doc.getId());
JourneyItemView view = journeyItemService.append(journey.getId(), dto);
em.flush();
em.clear();
assertThat(view.document()).isNotNull();
assertThat(view.document().id()).isEqualTo(doc.getId());
JourneyItemCreateDTO duplicate = new JourneyItemCreateDTO();
duplicate.setDocumentId(doc.getId());
assertThatThrownBy(() -> journeyItemService.append(journey.getId(), duplicate))
.hasFieldOrPropertyWithValue("code",
org.raddatz.familienarchiv.exception.ErrorCode.JOURNEY_DOCUMENT_ALREADY_ADDED);
}
// ─── JourneyItemService.reorder — atomicity check ────────────────────────
@Test