fix series number and improve author view
This commit is contained in:
@@ -1394,6 +1394,10 @@ public class SolrService {
|
||||
}
|
||||
|
||||
public void recreateCores() throws IOException {
|
||||
if (!isAvailable()) {
|
||||
throw new IOException("Solr is not available");
|
||||
}
|
||||
|
||||
List<String> coreNames = Arrays.asList(
|
||||
properties.getCores().getStories(),
|
||||
properties.getCores().getAuthors(),
|
||||
@@ -1403,20 +1407,13 @@ public class SolrService {
|
||||
for (String coreName : coreNames) {
|
||||
try {
|
||||
logger.info("Clearing Lucene index for core: {}", coreName);
|
||||
|
||||
// Unload the core and delete only the Lucene index files.
|
||||
// deleteIndex=true removes data/index/ only; conf/ and core.properties are preserved.
|
||||
// deleteDataDir=false keeps the data directory so the core can be reloaded in place.
|
||||
org.apache.solr.client.solrj.request.CoreAdminRequest.unloadCore(
|
||||
coreName, true, false, solrClient);
|
||||
logger.info("Unloaded core (index deleted): {}", coreName);
|
||||
|
||||
// Reload the core from its existing conf/ directory with a fresh empty index.
|
||||
org.apache.solr.client.solrj.request.CoreAdminRequest.reloadCore(coreName, solrClient);
|
||||
logger.info("Reloaded core with fresh index: {}", coreName);
|
||||
|
||||
} catch (SolrServerException e) {
|
||||
logger.warn("Could not clear/reload core {} (may not exist yet): {}", coreName, e.getMessage());
|
||||
// Delete all documents and hard-commit so the index is empty immediately.
|
||||
// Using deleteByQuery keeps the core registered in Solr — no restart needed.
|
||||
solrClient.deleteByQuery(coreName, "*:*");
|
||||
solrClient.commit(coreName);
|
||||
logger.info("Successfully cleared index for core: {}", coreName);
|
||||
} catch (Exception e) {
|
||||
logger.warn("Could not clear core {} (may not exist yet): {}", coreName, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -489,7 +489,8 @@ export default function AddStoryContent() {
|
||||
<Input
|
||||
label="Volume/Part (optional)"
|
||||
type="number"
|
||||
min="1"
|
||||
min="0.1"
|
||||
step="any"
|
||||
value={formData.volume}
|
||||
onChange={handleInputChange('volume')}
|
||||
placeholder="Enter volume/part number"
|
||||
|
||||
@@ -11,6 +11,39 @@ import Button from '../../../components/ui/Button';
|
||||
import StoryCard from '../../../components/stories/StoryCard';
|
||||
import LoadingSpinner from '../../../components/ui/LoadingSpinner';
|
||||
|
||||
type DisplayItem =
|
||||
| { type: 'standalone'; story: Story }
|
||||
| { type: 'series'; seriesId: string; seriesName: string; stories: Story[] };
|
||||
|
||||
function buildDisplayItems(stories: Story[], authorId: string): DisplayItem[] {
|
||||
const authorStories = stories.filter(s => s.authorId === authorId);
|
||||
const sorted = [...authorStories].sort(
|
||||
(a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime()
|
||||
);
|
||||
|
||||
const items: DisplayItem[] = [];
|
||||
const addedSeries = new Set<string>();
|
||||
|
||||
for (const story of sorted) {
|
||||
if (story.seriesId) {
|
||||
if (!addedSeries.has(story.seriesId)) {
|
||||
const seriesStories = sorted.filter(s => s.seriesId === story.seriesId);
|
||||
items.push({
|
||||
type: 'series',
|
||||
seriesId: story.seriesId,
|
||||
seriesName: story.seriesName!,
|
||||
stories: seriesStories,
|
||||
});
|
||||
addedSeries.add(story.seriesId);
|
||||
}
|
||||
} else {
|
||||
items.push({ type: 'standalone', story });
|
||||
}
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
export default function AuthorDetailPage() {
|
||||
const params = useParams();
|
||||
const router = useRouter();
|
||||
@@ -220,18 +253,38 @@ export default function AuthorDetailPage() {
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-4">
|
||||
{stories.map((story) => (
|
||||
{buildDisplayItems(stories, authorId).map((item) => {
|
||||
if (item.type === 'standalone') {
|
||||
return (
|
||||
<StoryCard
|
||||
key={item.story.id}
|
||||
story={item.story}
|
||||
viewMode="list"
|
||||
onUpdate={() => window.location.reload()}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div key={item.seriesId}>
|
||||
<div className="flex items-center gap-2 mb-2 px-1">
|
||||
<span className="text-xs font-semibold theme-text uppercase tracking-wide opacity-60">Series</span>
|
||||
<span className="font-semibold theme-header">{item.seriesName}</span>
|
||||
<span className="text-xs theme-text opacity-60">({item.stories.length} {item.stories.length === 1 ? 'part' : 'parts'})</span>
|
||||
</div>
|
||||
<div className="space-y-4 pl-4 border-l-2 border-gray-300 dark:border-gray-600">
|
||||
{item.stories.map((story) => (
|
||||
<StoryCard
|
||||
key={story.id}
|
||||
story={story}
|
||||
viewMode="list"
|
||||
onUpdate={() => {
|
||||
// Reload stories after update
|
||||
window.location.reload();
|
||||
}}
|
||||
onUpdate={() => window.location.reload()}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -439,7 +439,8 @@ export default function EditStoryPage() {
|
||||
<Input
|
||||
label="Volume/Part (optional)"
|
||||
type="number"
|
||||
min="1"
|
||||
min="0.1"
|
||||
step="any"
|
||||
value={formData.volume}
|
||||
onChange={handleInputChange('volume')}
|
||||
placeholder="Enter volume/part number"
|
||||
|
||||
Reference in New Issue
Block a user