diff --git a/backend/src/main/java/com/storycove/controller/StoryController.java b/backend/src/main/java/com/storycove/controller/StoryController.java index 1dfc059..272050c 100644 --- a/backend/src/main/java/com/storycove/controller/StoryController.java +++ b/backend/src/main/java/com/storycove/controller/StoryController.java @@ -837,7 +837,7 @@ public class StoryController { @RequestParam(required = false) String authorName, @RequestParam(required = false) UUID seriesId, @RequestParam(required = false) String seriesName, - @RequestParam(required = false) Integer seriesVolume, + @RequestParam(required = false) Double seriesVolume, @RequestParam(required = false) List tags, @RequestParam(defaultValue = "true") Boolean preserveReadingPosition, @RequestParam(defaultValue = "false") Boolean overwriteExisting, @@ -948,7 +948,7 @@ public class StoryController { @RequestParam(required = false) String authorName, @RequestParam(required = false) UUID seriesId, @RequestParam(required = false) String seriesName, - @RequestParam(required = false) Integer seriesVolume, + @RequestParam(required = false) Double seriesVolume, @RequestParam(required = false) List tags, @RequestParam(defaultValue = "true") Boolean createMissingAuthor, @RequestParam(defaultValue = "true") Boolean createMissingSeries, @@ -1082,7 +1082,7 @@ public class StoryController { private String description; private String contentHtml; private String sourceUrl; - private Integer volume; + private Double volume; private UUID authorId; private String authorName; private UUID seriesId; @@ -1100,8 +1100,8 @@ public class StoryController { public void setContentHtml(String contentHtml) { this.contentHtml = contentHtml; } public String getSourceUrl() { return sourceUrl; } public void setSourceUrl(String sourceUrl) { this.sourceUrl = sourceUrl; } - public Integer getVolume() { return volume; } - public void setVolume(Integer volume) { this.volume = volume; } + public Double getVolume() { return volume; } + public void setVolume(Double volume) { this.volume = volume; } public UUID getAuthorId() { return authorId; } public void setAuthorId(UUID authorId) { this.authorId = authorId; } public String getAuthorName() { return authorName; } @@ -1120,7 +1120,7 @@ public class StoryController { private String description; private String contentHtml; private String sourceUrl; - private Integer volume; + private Double volume; private UUID authorId; private String authorName; private UUID seriesId; @@ -1138,8 +1138,8 @@ public class StoryController { public void setContentHtml(String contentHtml) { this.contentHtml = contentHtml; } public String getSourceUrl() { return sourceUrl; } public void setSourceUrl(String sourceUrl) { this.sourceUrl = sourceUrl; } - public Integer getVolume() { return volume; } - public void setVolume(Integer volume) { this.volume = volume; } + public Double getVolume() { return volume; } + public void setVolume(Double volume) { this.volume = volume; } public UUID getAuthorId() { return authorId; } public void setAuthorId(UUID authorId) { this.authorId = authorId; } public String getAuthorName() { return authorName; } diff --git a/backend/src/main/java/com/storycove/dto/EPUBImportRequest.java b/backend/src/main/java/com/storycove/dto/EPUBImportRequest.java index 2783c12..a8ca060 100644 --- a/backend/src/main/java/com/storycove/dto/EPUBImportRequest.java +++ b/backend/src/main/java/com/storycove/dto/EPUBImportRequest.java @@ -19,7 +19,7 @@ public class EPUBImportRequest { private String seriesName; - private Integer seriesVolume; + private Double seriesVolume; private List tags; @@ -75,11 +75,11 @@ public class EPUBImportRequest { this.seriesName = seriesName; } - public Integer getSeriesVolume() { + public Double getSeriesVolume() { return seriesVolume; } - public void setSeriesVolume(Integer seriesVolume) { + public void setSeriesVolume(Double seriesVolume) { this.seriesVolume = seriesVolume; } diff --git a/backend/src/main/java/com/storycove/dto/PDFImportRequest.java b/backend/src/main/java/com/storycove/dto/PDFImportRequest.java index 6373a19..15b190b 100644 --- a/backend/src/main/java/com/storycove/dto/PDFImportRequest.java +++ b/backend/src/main/java/com/storycove/dto/PDFImportRequest.java @@ -19,7 +19,7 @@ public class PDFImportRequest { private String seriesName; - private Integer seriesVolume; + private Double seriesVolume; private List tags; @@ -71,11 +71,11 @@ public class PDFImportRequest { this.seriesName = seriesName; } - public Integer getSeriesVolume() { + public Double getSeriesVolume() { return seriesVolume; } - public void setSeriesVolume(Integer seriesVolume) { + public void setSeriesVolume(Double seriesVolume) { this.seriesVolume = seriesVolume; } diff --git a/backend/src/main/java/com/storycove/dto/StoryDto.java b/backend/src/main/java/com/storycove/dto/StoryDto.java index a52c65b..7f3ac36 100644 --- a/backend/src/main/java/com/storycove/dto/StoryDto.java +++ b/backend/src/main/java/com/storycove/dto/StoryDto.java @@ -26,7 +26,7 @@ public class StoryDto { private String coverPath; private Integer wordCount; private Integer rating; - private Integer volume; + private Double volume; // Reading progress fields private Boolean isRead; @@ -125,11 +125,11 @@ public class StoryDto { this.rating = rating; } - public Integer getVolume() { + public Double getVolume() { return volume; } - public void setVolume(Integer volume) { + public void setVolume(Double volume) { this.volume = volume; } diff --git a/backend/src/main/java/com/storycove/dto/StoryReadingDto.java b/backend/src/main/java/com/storycove/dto/StoryReadingDto.java index c7e8edb..733c71e 100644 --- a/backend/src/main/java/com/storycove/dto/StoryReadingDto.java +++ b/backend/src/main/java/com/storycove/dto/StoryReadingDto.java @@ -20,7 +20,7 @@ public class StoryReadingDto { private String coverPath; private Integer wordCount; private Integer rating; - private Integer volume; + private Double volume; // Reading progress fields private Boolean isRead; @@ -114,11 +114,11 @@ public class StoryReadingDto { this.rating = rating; } - public Integer getVolume() { + public Double getVolume() { return volume; } - public void setVolume(Integer volume) { + public void setVolume(Double volume) { this.volume = volume; } diff --git a/backend/src/main/java/com/storycove/dto/StorySearchDto.java b/backend/src/main/java/com/storycove/dto/StorySearchDto.java index ea8b6e1..239445f 100644 --- a/backend/src/main/java/com/storycove/dto/StorySearchDto.java +++ b/backend/src/main/java/com/storycove/dto/StorySearchDto.java @@ -13,7 +13,7 @@ public class StorySearchDto { private String coverPath; private Integer wordCount; private Integer rating; - private Integer volume; + private Double volume; // Reading status private Boolean isRead; @@ -103,11 +103,11 @@ public class StorySearchDto { this.rating = rating; } - public Integer getVolume() { + public Double getVolume() { return volume; } - public void setVolume(Integer volume) { + public void setVolume(Double volume) { this.volume = volume; } diff --git a/backend/src/main/java/com/storycove/dto/StorySummaryDto.java b/backend/src/main/java/com/storycove/dto/StorySummaryDto.java index 1ac0670..177fd64 100644 --- a/backend/src/main/java/com/storycove/dto/StorySummaryDto.java +++ b/backend/src/main/java/com/storycove/dto/StorySummaryDto.java @@ -18,7 +18,7 @@ public class StorySummaryDto { private String coverPath; private Integer wordCount; private Integer rating; - private Integer volume; + private Double volume; // Reading progress fields private Boolean isRead; @@ -105,11 +105,11 @@ public class StorySummaryDto { this.rating = rating; } - public Integer getVolume() { + public Double getVolume() { return volume; } - public void setVolume(Integer volume) { + public void setVolume(Double volume) { this.volume = volume; } diff --git a/backend/src/main/java/com/storycove/dto/ZIPImportRequest.java b/backend/src/main/java/com/storycove/dto/ZIPImportRequest.java index 6fa40d5..808116e 100644 --- a/backend/src/main/java/com/storycove/dto/ZIPImportRequest.java +++ b/backend/src/main/java/com/storycove/dto/ZIPImportRequest.java @@ -35,7 +35,7 @@ public class ZIPImportRequest { private String authorName; private UUID seriesId; private String seriesName; - private Integer seriesVolume; + private Double seriesVolume; private List tags; public UUID getAuthorId() { @@ -70,11 +70,11 @@ public class ZIPImportRequest { this.seriesName = seriesName; } - public Integer getSeriesVolume() { + public Double getSeriesVolume() { return seriesVolume; } - public void setSeriesVolume(Integer seriesVolume) { + public void setSeriesVolume(Double seriesVolume) { this.seriesVolume = seriesVolume; } diff --git a/backend/src/main/java/com/storycove/entity/Series.java b/backend/src/main/java/com/storycove/entity/Series.java index 6b2e037..722705c 100644 --- a/backend/src/main/java/com/storycove/entity/Series.java +++ b/backend/src/main/java/com/storycove/entity/Series.java @@ -8,6 +8,7 @@ import com.fasterxml.jackson.annotation.JsonManagedReference; import java.time.LocalDateTime; import java.util.ArrayList; +import java.util.Comparator; import java.util.List; import java.util.UUID; @@ -62,21 +63,21 @@ public class Series { public Story getNextStory(Story currentStory) { if (currentStory.getVolume() == null) return null; - + return stories.stream() .filter(story -> story.getVolume() != null) - .filter(story -> story.getVolume().equals(currentStory.getVolume() + 1)) - .findFirst() + .filter(story -> story.getVolume() > currentStory.getVolume()) + .min(Comparator.comparingDouble(Story::getVolume)) .orElse(null); } - + public Story getPreviousStory(Story currentStory) { - if (currentStory.getVolume() == null || currentStory.getVolume() <= 1) return null; - + if (currentStory.getVolume() == null) return null; + return stories.stream() .filter(story -> story.getVolume() != null) - .filter(story -> story.getVolume().equals(currentStory.getVolume() - 1)) - .findFirst() + .filter(story -> story.getVolume() < currentStory.getVolume()) + .max(Comparator.comparingDouble(Story::getVolume)) .orElse(null); } diff --git a/backend/src/main/java/com/storycove/entity/Story.java b/backend/src/main/java/com/storycove/entity/Story.java index 73975bc..dde96e5 100644 --- a/backend/src/main/java/com/storycove/entity/Story.java +++ b/backend/src/main/java/com/storycove/entity/Story.java @@ -52,8 +52,8 @@ public class Story { @Column(name = "rating") private Integer rating; - @Column(name = "volume") - private Integer volume; + @Column(name = "volume", columnDefinition = "float8") + private Double volume; @Column(name = "is_read") private Boolean isRead = false; @@ -216,11 +216,11 @@ public class Story { this.rating = rating; } - public Integer getVolume() { + public Double getVolume() { return volume; } - - public void setVolume(Integer volume) { + + public void setVolume(Double volume) { this.volume = volume; } diff --git a/backend/src/main/java/com/storycove/repository/StoryRepository.java b/backend/src/main/java/com/storycove/repository/StoryRepository.java index 2e532f6..2cfece2 100644 --- a/backend/src/main/java/com/storycove/repository/StoryRepository.java +++ b/backend/src/main/java/com/storycove/repository/StoryRepository.java @@ -37,7 +37,7 @@ public interface StoryRepository extends JpaRepository { List findBySeriesOrderByVolume(@Param("seriesId") UUID seriesId); @Query("SELECT s FROM Story s WHERE s.series.id = :seriesId AND s.volume = :volume") - Optional findBySeriesAndVolume(@Param("seriesId") UUID seriesId, @Param("volume") Integer volume); + Optional findBySeriesAndVolume(@Param("seriesId") UUID seriesId, @Param("volume") Double volume); @Query("SELECT s FROM Story s JOIN s.tags t WHERE t = :tag") diff --git a/backend/src/main/java/com/storycove/service/CollectionService.java b/backend/src/main/java/com/storycove/service/CollectionService.java index e05c246..024dbc9 100644 --- a/backend/src/main/java/com/storycove/service/CollectionService.java +++ b/backend/src/main/java/com/storycove/service/CollectionService.java @@ -420,7 +420,7 @@ public class CollectionService { .orElseThrow(() -> new ResourceNotFoundException("Series not found with id: " + seriesId)); List storyIds = series.getStories().stream() - .sorted(java.util.Comparator.comparingInt(s -> s.getVolume() != null ? s.getVolume() : Integer.MAX_VALUE)) + .sorted(java.util.Comparator.comparingDouble((Story s) -> s.getVolume() != null ? s.getVolume() : Double.MAX_VALUE)) .map(Story::getId) .collect(Collectors.toList()); diff --git a/backend/src/main/java/com/storycove/service/SolrService.java b/backend/src/main/java/com/storycove/service/SolrService.java index ef80940..4654885 100644 --- a/backend/src/main/java/com/storycove/service/SolrService.java +++ b/backend/src/main/java/com/storycove/service/SolrService.java @@ -1061,7 +1061,8 @@ public class SolrService { story.setCoverPath((String) doc.getFieldValue("coverPath")); story.setWordCount((Integer) doc.getFieldValue("wordCount")); story.setRating((Integer) doc.getFieldValue("rating")); - story.setVolume((Integer) doc.getFieldValue("volume")); + Number volumeVal = (Number) doc.getFieldValue("volume"); + story.setVolume(volumeVal != null ? volumeVal.doubleValue() : null); story.setIsRead((Boolean) doc.getFieldValue("isRead")); story.setReadingPosition((Integer) doc.getFieldValue("readingPosition")); story.setReadingProgressPercentage((Integer) doc.getFieldValue("readingProgressPercentage")); diff --git a/backend/src/main/java/com/storycove/service/StoryService.java b/backend/src/main/java/com/storycove/service/StoryService.java index 9514216..d113d27 100644 --- a/backend/src/main/java/com/storycove/service/StoryService.java +++ b/backend/src/main/java/com/storycove/service/StoryService.java @@ -139,7 +139,7 @@ public class StoryService { } @Transactional(readOnly = true) - public Optional findBySeriesAndVolume(UUID seriesId, Integer volume) { + public Optional findBySeriesAndVolume(UUID seriesId, Double volume) { return storyRepository.findBySeriesAndVolume(seriesId, volume); } @@ -499,7 +499,7 @@ public class StoryService { return storyRepository.save(story); } - public Story addToSeries(UUID storyId, UUID seriesId, Integer volume) { + public Story addToSeries(UUID storyId, UUID seriesId, Double volume) { Story story = findById(storyId); Series series = seriesService.findById(seriesId); @@ -550,7 +550,7 @@ public class StoryService { } } - private void validateSeriesVolume(Series series, Integer volume) { + private void validateSeriesVolume(Series series, Double volume) { if (volume != null) { Optional existingPart = storyRepository.findBySeriesAndVolume(series.getId(), volume); if (existingPart.isPresent()) { diff --git a/backend/src/test/java/com/storycove/entity/SeriesTest.java b/backend/src/test/java/com/storycove/entity/SeriesTest.java index bec0a7a..34bd993 100644 --- a/backend/src/test/java/com/storycove/entity/SeriesTest.java +++ b/backend/src/test/java/com/storycove/entity/SeriesTest.java @@ -104,11 +104,11 @@ class SeriesTest { @DisplayName("Should get next story correctly") void shouldGetNextStoryCorrectly() { Story story1 = new Story("Part 1"); - story1.setVolume(1); + story1.setVolume(1.0); Story story2 = new Story("Part 2"); - story2.setVolume(2); + story2.setVolume(2.0); Story story3 = new Story("Part 3"); - story3.setVolume(3); + story3.setVolume(3.0); series.addStory(story1); series.addStory(story2); @@ -123,11 +123,11 @@ class SeriesTest { @DisplayName("Should get previous story correctly") void shouldGetPreviousStoryCorrectly() { Story story1 = new Story("Part 1"); - story1.setVolume(1); + story1.setVolume(1.0); Story story2 = new Story("Part 2"); - story2.setVolume(2); + story2.setVolume(2.0); Story story3 = new Story("Part 3"); - story3.setVolume(3); + story3.setVolume(3.0); series.addStory(story1); series.addStory(story2); diff --git a/backend/src/test/java/com/storycove/entity/StoryTest.java b/backend/src/test/java/com/storycove/entity/StoryTest.java index ee0c3dc..3b1ee29 100644 --- a/backend/src/test/java/com/storycove/entity/StoryTest.java +++ b/backend/src/test/java/com/storycove/entity/StoryTest.java @@ -140,7 +140,7 @@ class StoryTest { story.setSeries(series); assertFalse(story.isPartOfSeries()); // Still false because no volume - story.setVolume(1); + story.setVolume(1.0); assertTrue(story.isPartOfSeries()); story.setSeries(null); diff --git a/backend/src/test/java/com/storycove/repository/StoryRepositoryTest.java b/backend/src/test/java/com/storycove/repository/StoryRepositoryTest.java index 1e11741..d2311b1 100644 --- a/backend/src/test/java/com/storycove/repository/StoryRepositoryTest.java +++ b/backend/src/test/java/com/storycove/repository/StoryRepositoryTest.java @@ -69,14 +69,14 @@ class StoryRepositoryTest extends BaseRepositoryTest { story2.setDescription("The sequel to the great adventure"); story2.setAuthor(author); story2.setSeries(series); - story2.setVolume(1); + story2.setVolume(1.0); story2.addTag(tag1); story3 = new Story("The Final Chapter"); story3.setDescription("The final chapter"); story3.setAuthor(author); story3.setSeries(series); - story3.setVolume(2); + story3.setVolume(2.0); storyRepository.saveAll(List.of(story1, story2, story3)); } @@ -126,11 +126,11 @@ class StoryRepositoryTest extends BaseRepositoryTest { @Test @DisplayName("Should find story by series and volume") void shouldFindStoryBySeriesAndVolume() { - Optional found = storyRepository.findBySeriesAndVolume(series.getId(), 1); + Optional found = storyRepository.findBySeriesAndVolume(series.getId(), 1.0); assertTrue(found.isPresent()); assertEquals("The Sequel", found.get().getTitle()); - found = storyRepository.findBySeriesAndVolume(series.getId(), 99); + found = storyRepository.findBySeriesAndVolume(series.getId(), 99.0); assertFalse(found.isPresent()); } diff --git a/frontend/src/app/add-story/AddStoryContent.tsx b/frontend/src/app/add-story/AddStoryContent.tsx index 6e6ce30..c79711a 100644 --- a/frontend/src/app/add-story/AddStoryContent.tsx +++ b/frontend/src/app/add-story/AddStoryContent.tsx @@ -285,7 +285,7 @@ export default function AddStoryContent() { summary: formData.summary || undefined, contentHtml: formData.contentHtml, sourceUrl: formData.sourceUrl || undefined, - volume: formData.seriesName ? parseInt(formData.volume) : undefined, + volume: formData.seriesName ? parseFloat(formData.volume) : undefined, // Send seriesId if we have it (existing series), otherwise send seriesName (new series) ...(formData.seriesId ? { seriesId: formData.seriesId } : { seriesName: formData.seriesName || undefined }), // Send authorId if we have it (existing author), otherwise send authorName (new author) @@ -308,7 +308,7 @@ export default function AddStoryContent() { summary: formData.summary || undefined, contentHtml: imageResult.processedContent, sourceUrl: formData.sourceUrl || undefined, - volume: formData.seriesName ? parseInt(formData.volume) : undefined, + volume: formData.seriesName ? parseFloat(formData.volume) : undefined, ...(formData.seriesId ? { seriesId: formData.seriesId } : { seriesName: formData.seriesName || undefined }), ...(formData.authorId ? { authorId: formData.authorId } : { authorName: formData.authorName }), tagNames: formData.tags.length > 0 ? formData.tags : undefined, diff --git a/frontend/src/app/stories/[id]/edit/page.tsx b/frontend/src/app/stories/[id]/edit/page.tsx index 78436f4..0b15c7f 100644 --- a/frontend/src/app/stories/[id]/edit/page.tsx +++ b/frontend/src/app/stories/[id]/edit/page.tsx @@ -199,7 +199,7 @@ export default function EditStoryPage() { summary: formData.summary || undefined, contentHtml: formData.contentHtml, sourceUrl: formData.sourceUrl || undefined, - volume: formData.seriesName && formData.volume ? parseInt(formData.volume) : undefined, + volume: formData.seriesName && formData.volume ? parseFloat(formData.volume) : undefined, // Send seriesId if we have it (existing series), otherwise send seriesName (new/changed series) ...(formData.seriesId ? { seriesId: formData.seriesId } : { seriesName: formData.seriesName }), // Send authorId if we have it (existing author), otherwise send authorName (new/changed author) @@ -222,7 +222,7 @@ export default function EditStoryPage() { summary: formData.summary || undefined, contentHtml: imageResult.processedContent, sourceUrl: formData.sourceUrl || undefined, - volume: formData.seriesName && formData.volume ? parseInt(formData.volume) : undefined, + volume: formData.seriesName && formData.volume ? parseFloat(formData.volume) : undefined, ...(formData.seriesId ? { seriesId: formData.seriesId } : { seriesName: formData.seriesName }), ...(formData.authorId ? { authorId: formData.authorId } : { authorName: formData.authorName }), tagNames: formData.tags,