transcriptor-web/resources/js/Pages/Clips.tsx
2025-04-05 15:58:14 +02:00

234 lines
11 KiB
TypeScript

import React, { useEffect, useRef, useState } from 'react';
import axios from "axios";
import AppLayout from '@/Layouts/AppLayout';
import { Channel, Clip } from "@/types";
import { usePage } from "@inertiajs/react";
import {MultiSelect} from "@/Components/MultiSelect";
import {Typography} from "@material-tailwind/react";
import {format} from "date-fns";
export default function Clips() {
// Initialize videoId state from the Inertia prop
const { video_id } = usePage().props;
const [videoId, setVideoId] = useState<string>(video_id || '');
const [clips, setClips] = useState<Clip[]>([]);
const [channels, setChannels] = useState<Channel[]>([]);
const [distinctLanguages, setDistinctLanguages] = useState<string[]>([]);
const [loadingChannels, setLoadingChannels] = useState<boolean>(true);
const [loadingVideos, setLoadingVideos] = useState<boolean>(true);
// Filter states for multi-select options
const [selectedChannelIds, setSelectedChannelIds] = useState<string[]>([]);
const [selectedLanguages, setSelectedLanguages] = useState<string[]>([]);
const [startDate, setStartDate] = useState<string>('');
const [endDate, setEndDate] = useState<string>('');
// (1) Initial fetch on mount to get all channels and compute distinct languages (runs only once)
useEffect(() => {
setLoadingChannels(true);
axios.get('/api/channels/get')
.then(response => {
const channelsData = response.data as Channel[];
setChannels(channelsData);
// Compute distinct languages once (from the full unfiltered list)
const langs = [...new Set(channelsData.map(channel => channel.language))];
setDistinctLanguages(langs);
setLoadingChannels(false);
})
.catch(error => {
console.error('Error fetching channels:', error);
setLoadingChannels(false);
});
}, []);
// (2) Fetch channels based on language selection changes
useEffect(() => {
setLoadingChannels(true);
const params: Record<string, string | string[]> = {};
if (selectedLanguages.length > 0) {
params.languages = selectedLanguages.filter(lang => lang !== '');
}
axios.get('/api/channels/get', { params })
.then(response => {
setChannels(response.data);
setLoadingChannels(false);
})
.catch(error => {
console.error('Error fetching channels:', error);
setLoadingChannels(false);
});
}, [selectedLanguages]);
// Fetch clips when filters change (including videoId)
useEffect(() => {
setLoadingVideos(true);
const params: Record<string, string | string[]> = {};
if (selectedChannelIds.length > 0) {
params.channel_ids = selectedChannelIds.filter(id => id !== '');
}
if (selectedLanguages.length > 0) {
params.languages = selectedLanguages.filter(lang => lang !== '');
}
if (startDate) params.start_date = startDate;
if (endDate) params.end_date = endDate;
if (videoId) params.video_id = videoId;
axios.get('/api/clips/get', { params })
.then(response => {
setClips(response.data);
setLoadingVideos(false);
})
.catch(error => {
console.error('Error fetching clips:', error);
setLoadingVideos(false);
});
}, [selectedChannelIds, selectedLanguages, startDate, endDate, videoId]);
function openInGoogleDrive(googleDriveFileId: string | null) {
if (googleDriveFileId != null) {
const url = `https://drive.google.com/file/d/${googleDriveFileId}/view?usp=sharing`;
window.open(url, '_blank');
}
}
return (
<AppLayout
title="Videos"
renderHeader={() => (
<h2 className="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
Clips
</h2>
)}
>
<div className="py-12">
<div className="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div className="bg-white dark:bg-gray-800 overflow-hidden shadow-xl sm:rounded-lg p-6">
{/* Filter Options */}
<div className="mb-6 grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 gap-4">
{/* Channel Filter */}
<div className="form-control">
<label className="label">
<span className="label-text">Channel</span>
</label>
{loadingChannels ? (
<span className="loading loading-dots loading-md"></span>
) : (
<MultiSelect
label="Channels"
options={channels.map(ch => ({
value: ch.id.toString(),
label: ch.channel_name,
}))}
selected={selectedChannelIds}
onChange={setSelectedChannelIds}
/>
)}
</div>
{/* Language Filter */}
<div className="form-control">
<label className="label">
<span className="label-text">Language</span>
</label>
{loadingChannels ? (
<span className="loading loading-dots loading-md"></span>
) : (
<MultiSelect
label="Languages"
options={distinctLanguages.map(ln => ({
value: ln,
label: ln,
}))}
selected={selectedLanguages}
onChange={setSelectedLanguages}
/>
)}
</div>
{/* Start Date Filter */}
<div className="form-control">
<label className="label">
<span className="label-text">Start Date</span>
</label>
<input
type="date"
className="input input-bordered"
value={startDate}
onChange={(e) => setStartDate(e.target.value)}
/>
</div>
{/* End Date Filter */}
<div className="form-control">
<label className="label">
<span className="label-text">End Date</span>
</label>
<input
type="date"
className="input input-bordered"
value={endDate}
onChange={(e) => setEndDate(e.target.value)}
/>
</div>
</div>
{/* Selected Video ID Filter Display */}
{videoId && (
<div className="badge badge-ghost badge-sm mb-4">
<span className="mr-1">Video ID: {videoId}</span>
<button
className="text-red-500 hover:text-red-700"
onClick={() => setVideoId('')}
>
X
</button>
</div>
)}
{/* Clips Grid */}
{loadingVideos ? (
<div className="flex justify-center items-center">
<span className="loading loading-dots loading-lg"></span>
</div>
) : (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{clips.map((clip) => (
<div
key={clip.id}
className={`card bg-base-100 shadow-sm image-full w-full ${clip.gdrive_file_id ? "cursor-pointer" : "cursor-not-allowed"}`}
onClick={() => {
if (clip.gdrive_file_id) openInGoogleDrive(clip.gdrive_file_id);
}}
>
<figure>
<img
src="https://img.daisyui.com/images/stock/photo-1606107557195-0e29a4b5b4aa.webp"
alt="Video Thumbnail"
/>
</figure>
<div className="card-body">
<h2 className="card-title">
<div>
{clip.video.channel.channel_name}
</div>
<Typography className="opacity-50">
{clip.filename.split('/').pop() || 'No filename'}
</Typography>
</h2>
<div>Stream: {clip.video.external_date ? format(clip.video.external_date, 'dd-MM-yy') : ''}</div>
<div>Clip ID: {clip.id}</div>
<div>Video ID: {clip.video.id}</div>
{/* Additional clip information can be added here */}
</div>
</div>
))}
</div>
)}
</div>
</div>
</div>
</AppLayout>
);
}