feat: Add video player modal and enhance search filters in Modify and Search components

This commit introduces a video player modal in the Modify component, allowing users to play selected videos directly within the application. Additionally, the Search component has been updated to include a collapsible filters section for media type selection, improving user experience when searching for images or videos. The layout adjustments ensure better responsiveness and usability across various screen sizes. Documentation has been updated to reflect these changes.
This commit is contained in:
tanyar09 2025-12-04 13:13:55 -05:00
parent e48b614b23
commit 6cc359f25a
2 changed files with 72 additions and 40 deletions

View File

@ -169,6 +169,7 @@ export default function Modify() {
const [busy, setBusy] = useState(false)
const [error, setError] = useState<string | null>(null)
const [success, setSuccess] = useState<string | null>(null)
const [selectedVideoToPlay, setSelectedVideoToPlay] = useState<PersonVideoItem | null>(null)
const gridRef = useRef<HTMLDivElement>(null)
@ -812,10 +813,10 @@ export default function Modify() {
<div
className="flex-1 cursor-pointer"
onClick={() => {
// Open video in new window or navigate to video page
window.open(`/api/v1/videos/${video.id}/thumbnail`, '_blank')
// Open video player modal
setSelectedVideoToPlay(video)
}}
title="Click to view video"
title="Click to play video"
>
<div className="font-medium text-sm truncate">{video.filename}</div>
{video.date_taken && (
@ -950,6 +951,37 @@ export default function Modify() {
</div>
</div>
)}
{/* Video player modal */}
{selectedVideoToPlay && (
<div className="fixed inset-0 bg-black bg-opacity-75 flex items-center justify-center z-50" onClick={() => setSelectedVideoToPlay(null)}>
<div className="bg-black rounded-lg shadow-xl w-full max-w-4xl p-4" onClick={(e) => e.stopPropagation()}>
<div className="flex items-center justify-between mb-4">
<h2 className="text-xl font-bold text-white">{selectedVideoToPlay.filename}</h2>
<button
onClick={() => setSelectedVideoToPlay(null)}
className="text-white hover:text-gray-300 text-2xl font-bold"
title="Close"
>
×
</button>
</div>
<video
src={videosApi.getVideoUrl(selectedVideoToPlay.id)}
controls
autoPlay
className="w-full max-h-[80vh] rounded"
>
Your browser does not support the video tag.
</video>
{selectedVideoToPlay.date_taken && (
<div className="text-sm text-gray-300 mt-2">
Date taken: {new Date(selectedVideoToPlay.date_taken).toLocaleDateString()}
</div>
)}
</div>
</div>
)}
</div>
)
}

View File

@ -588,7 +588,7 @@ export default function Search() {
return (
<div>
{/* Search Type Selector */}
<div className="bg-white rounded-lg shadow py-2 px-4 mb-2">
<div className="bg-white rounded-lg shadow py-2 px-4 mb-2 w-1/3">
<div className="flex items-center gap-2">
<label className="text-sm font-medium text-gray-700">
Search type:
@ -605,41 +605,9 @@ export default function Search() {
</div>
</div>
{/* Filters */}
<div className="bg-white rounded-lg shadow py-2 px-4 mb-2">
<div className="flex items-center gap-2">
<h2 className="text-sm font-medium text-gray-700">Filters</h2>
<button
onClick={() => setFiltersExpanded(!filtersExpanded)}
className="text-lg text-gray-600 hover:text-gray-800"
title={filtersExpanded ? 'Collapse' : 'Expand'}
>
{filtersExpanded ? '▼' : '▶'}
</button>
</div>
{filtersExpanded && (
<div className="mt-3 space-y-2">
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Media Type:
</label>
<select
value={mediaType}
onChange={(e) => setMediaType(e.target.value)}
className="w-48 border rounded px-3 py-2"
>
<option value="all">All</option>
<option value="image">Photos</option>
<option value="video">Videos</option>
</select>
</div>
</div>
)}
</div>
{/* Search Inputs */}
{(searchType !== 'no_faces' && searchType !== 'no_tags' && searchType !== 'processed' && searchType !== 'unprocessed') && (
<div className="bg-white rounded-lg shadow py-2 px-4 mb-2">
<div className="bg-white rounded-lg shadow py-2 px-4 mb-2 w-1/3">
{searchType === 'name' && (
<div className="flex items-center gap-2">
<label className="text-sm font-medium text-gray-700">
@ -649,7 +617,7 @@ export default function Search() {
type="text"
value={personName}
onChange={(e) => setPersonName(e.target.value)}
className="flex-1 border rounded px-3 py-2"
className="w-96 border rounded px-3 py-2"
onKeyDown={(e) => e.key === 'Enter' && handleSearch()}
placeholder="Enter person name(s), separated by commas (e.g., John, Jane, Bob)"
/>
@ -666,7 +634,7 @@ export default function Search() {
type="date"
value={dateFrom}
onChange={(e) => setDateFrom(e.target.value)}
className="w-full border rounded px-3 py-2"
className="w-48 border rounded px-3 py-2"
placeholder="YYYY-MM-DD"
/>
</div>
@ -678,7 +646,7 @@ export default function Search() {
type="date"
value={dateTo}
onChange={(e) => setDateTo(e.target.value)}
className="w-full border rounded px-3 py-2"
className="w-48 border rounded px-3 py-2"
placeholder="YYYY-MM-DD (optional)"
/>
</div>
@ -740,6 +708,38 @@ export default function Search() {
</div>
)}
{/* Filters */}
<div className="bg-white rounded-lg shadow py-2 px-4 mb-2 w-1/3">
<div className="flex items-center gap-2">
<h2 className="text-sm font-medium text-gray-700">Filters</h2>
<button
onClick={() => setFiltersExpanded(!filtersExpanded)}
className="text-lg text-gray-600 hover:text-gray-800"
title={filtersExpanded ? 'Collapse' : 'Expand'}
>
{filtersExpanded ? '▼' : '▶'}
</button>
</div>
{filtersExpanded && (
<div className="mt-3 space-y-2">
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Media Type:
</label>
<select
value={mediaType}
onChange={(e) => setMediaType(e.target.value)}
className="w-48 border rounded px-3 py-2"
>
<option value="all">All</option>
<option value="image">Photos</option>
<option value="video">Videos</option>
</select>
</div>
</div>
)}
</div>
{/* Search Button */}
<div className="mb-4">
<button