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:
parent
e48b614b23
commit
6cc359f25a
@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user