mirror of
https://github.com/fallenbagel/jellyseerr.git
synced 2026-01-04 13:48:27 -05:00
feat(issue): add issue description preview (#1881)
* feat(issue): add issue description preview This PR adds a description preview to the issues list page, allowing users to quickly view issue details without navigating to individual issue pages. Signed-off-by: 0xsysr3ll <0xsysr3ll@pm.me> * fix(issue): remove unnecessary user join Signed-off-by: 0xsysr3ll <0xsysr3ll@pm.me> --------- Signed-off-by: 0xsysr3ll <0xsysr3ll@pm.me>
This commit is contained in:
@@ -54,6 +54,7 @@ issueRoutes.get<Record<string, string>, IssueResultsResponse>(
|
|||||||
.leftJoinAndSelect('issue.createdBy', 'createdBy')
|
.leftJoinAndSelect('issue.createdBy', 'createdBy')
|
||||||
.leftJoinAndSelect('issue.media', 'media')
|
.leftJoinAndSelect('issue.media', 'media')
|
||||||
.leftJoinAndSelect('issue.modifiedBy', 'modifiedBy')
|
.leftJoinAndSelect('issue.modifiedBy', 'modifiedBy')
|
||||||
|
.leftJoinAndSelect('issue.comments', 'comments')
|
||||||
.where('issue.status IN (:...issueStatus)', {
|
.where('issue.status IN (:...issueStatus)', {
|
||||||
issueStatus: statusFilter,
|
issueStatus: statusFilter,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import Badge from '@app/components/Common/Badge';
|
import Badge from '@app/components/Common/Badge';
|
||||||
import Button from '@app/components/Common/Button';
|
import Button from '@app/components/Common/Button';
|
||||||
import CachedImage from '@app/components/Common/CachedImage';
|
import CachedImage from '@app/components/Common/CachedImage';
|
||||||
|
import Tooltip from '@app/components/Common/Tooltip';
|
||||||
import { issueOptions } from '@app/components/IssueModal/constants';
|
import { issueOptions } from '@app/components/IssueModal/constants';
|
||||||
import { Permission, useUser } from '@app/hooks/useUser';
|
import { Permission, useUser } from '@app/hooks/useUser';
|
||||||
import globalMessages from '@app/i18n/globalMessages';
|
import globalMessages from '@app/i18n/globalMessages';
|
||||||
@@ -26,6 +27,7 @@ const messages = defineMessages('components.IssueList.IssueItem', {
|
|||||||
opened: 'Opened',
|
opened: 'Opened',
|
||||||
viewissue: 'View Issue',
|
viewissue: 'View Issue',
|
||||||
unknownissuetype: 'Unknown',
|
unknownissuetype: 'Unknown',
|
||||||
|
descriptionpreview: 'Issue Description',
|
||||||
});
|
});
|
||||||
|
|
||||||
const isMovie = (movie: MovieDetails | TvDetails): movie is MovieDetails => {
|
const isMovie = (movie: MovieDetails | TvDetails): movie is MovieDetails => {
|
||||||
@@ -107,8 +109,15 @@ const IssueItem = ({ issue }: IssueItemProps) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const description = issue.comments?.[0]?.message || '';
|
||||||
|
const maxDescriptionLength = 120;
|
||||||
|
const shouldTruncate = description.length > maxDescriptionLength;
|
||||||
|
const truncatedDescription = shouldTruncate
|
||||||
|
? description.substring(0, maxDescriptionLength) + '...'
|
||||||
|
: description;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative flex w-full flex-col justify-between overflow-hidden rounded-xl bg-gray-800 py-4 text-gray-400 shadow-md ring-1 ring-gray-700 xl:h-28 xl:flex-row">
|
<div className="relative flex w-full flex-col justify-between overflow-hidden rounded-xl bg-gray-800 py-4 text-gray-400 shadow-md ring-1 ring-gray-700 xl:flex-row">
|
||||||
{title.backdropPath && (
|
{title.backdropPath && (
|
||||||
<div className="absolute inset-0 z-0 w-full bg-cover bg-center xl:w-2/3">
|
<div className="absolute inset-0 z-0 w-full bg-cover bg-center xl:w-2/3">
|
||||||
<CachedImage
|
<CachedImage
|
||||||
@@ -168,8 +177,38 @@ const IssueItem = ({ issue }: IssueItemProps) => {
|
|||||||
>
|
>
|
||||||
{isMovie(title) ? title.title : title.name}
|
{isMovie(title) ? title.title : title.name}
|
||||||
</Link>
|
</Link>
|
||||||
|
{description && (
|
||||||
|
<div className="mt-1 max-w-full">
|
||||||
|
<div className="overflow-hidden text-sm text-gray-300">
|
||||||
|
{shouldTruncate ? (
|
||||||
|
<Tooltip
|
||||||
|
content={
|
||||||
|
<div className="max-w-sm p-3">
|
||||||
|
<div className="mb-1 text-sm font-medium text-gray-200">
|
||||||
|
Issue Description
|
||||||
|
</div>
|
||||||
|
<div className="whitespace-pre-wrap text-sm leading-relaxed text-gray-300">
|
||||||
|
{description}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
tooltipConfig={{
|
||||||
|
placement: 'top',
|
||||||
|
offset: [0, 8],
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span className="block cursor-help truncate transition-colors hover:text-gray-200">
|
||||||
|
{truncatedDescription}
|
||||||
|
</span>
|
||||||
|
</Tooltip>
|
||||||
|
) : (
|
||||||
|
<span className="block break-words">{description}</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
{problemSeasonEpisodeLine.length > 0 && (
|
{problemSeasonEpisodeLine.length > 0 && (
|
||||||
<div className="card-field">
|
<div className="card-field mt-1">
|
||||||
{problemSeasonEpisodeLine.map((t, k) => (
|
{problemSeasonEpisodeLine.map((t, k) => (
|
||||||
<span key={k}>{t}</span>
|
<span key={k}>{t}</span>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -180,6 +180,7 @@
|
|||||||
"components.IssueDetails.toaststatusupdated": "Issue status updated successfully!",
|
"components.IssueDetails.toaststatusupdated": "Issue status updated successfully!",
|
||||||
"components.IssueDetails.toaststatusupdatefailed": "Something went wrong while updating the issue status.",
|
"components.IssueDetails.toaststatusupdatefailed": "Something went wrong while updating the issue status.",
|
||||||
"components.IssueDetails.unknownissuetype": "Unknown",
|
"components.IssueDetails.unknownissuetype": "Unknown",
|
||||||
|
"components.IssueList.IssueItem.descriptionpreview": "Issue Description",
|
||||||
"components.IssueList.IssueItem.episodes": "{episodeCount, plural, one {Episode} other {Episodes}}",
|
"components.IssueList.IssueItem.episodes": "{episodeCount, plural, one {Episode} other {Episodes}}",
|
||||||
"components.IssueList.IssueItem.issuestatus": "Status",
|
"components.IssueList.IssueItem.issuestatus": "Status",
|
||||||
"components.IssueList.IssueItem.issuetype": "Type",
|
"components.IssueList.IssueItem.issuetype": "Type",
|
||||||
|
|||||||
Reference in New Issue
Block a user