78 lines
3.1 KiB
TypeScript
78 lines
3.1 KiB
TypeScript
import type { CourseView } from '../types';
|
|
import React, { useState } from 'react';
|
|
import { Loader2, Camera } from 'lucide-react';
|
|
import { base44 } from '@/api/base44Client';
|
|
import { Image } from '@/components/ui/image';
|
|
import { useTranslation } from 'react-i18next';
|
|
|
|
// Photo or video proof — capture/upload, then submit for AI evaluation.
|
|
interface MediaChallengeProps {
|
|
course: CourseView;
|
|
onSubmit?: (...args: any[]) => void;
|
|
submitting: boolean;
|
|
}
|
|
|
|
export default function MediaChallenge({ course, onSubmit, submitting }: MediaChallengeProps) {
|
|
const { t } = useTranslation();
|
|
const type = course?.challenge?.type || 'photo';
|
|
const [mediaUrl, setMediaUrl] = useState('');
|
|
const [uploading, setUploading] = useState(false);
|
|
|
|
const accept = type === 'video' ? 'video/*' : 'image/*';
|
|
const capture = type === 'photo' ? 'environment' : undefined;
|
|
|
|
const handleFile = async (e) => {
|
|
const file = e.target.files?.[0];
|
|
if (!file) return;
|
|
setUploading(true);
|
|
try {
|
|
const { file_url } = await base44.integrations.Core.UploadFile({ file });
|
|
setMediaUrl(file_url);
|
|
} catch {
|
|
setMediaUrl('');
|
|
} finally {
|
|
setUploading(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div>
|
|
<p className="text-[13px] text-ink-3 mb-3">
|
|
{type === 'video' ? 'Record or upload a short video demonstrating the challenge.' : 'Take or upload a photo showing your work.'}
|
|
</p>
|
|
{mediaUrl ? (
|
|
<div className="mb-3 rounded-xl overflow-hidden border border-border">
|
|
{type === 'video' ? (
|
|
<video src={mediaUrl} controls className="w-full max-h-64 object-cover" />
|
|
) : (
|
|
<Image src={mediaUrl} alt={t('mediaChallenge.yourProof')} className="w-full max-h-64 object-cover" fittingType="fill" />
|
|
)}
|
|
</div>
|
|
) : (
|
|
<label className="flex flex-col items-center justify-center h-40 rounded-xl border-2 border-dashed border-border cursor-pointer hover:border-krow-blue transition-colors">
|
|
{uploading ? (
|
|
<Loader2 className="w-6 h-6 animate-spin text-krow-blue" />
|
|
) : (
|
|
<>
|
|
<Camera className="w-6 h-6 text-ink-4 mb-2" />
|
|
<span className="text-[13px] text-ink-3">{t('mediaChallenge.tapTo')} {type === 'video' ? 'record' : 'capture'}</span>
|
|
</>
|
|
)}
|
|
<input type="file" accept={accept} capture={capture} onChange={handleFile} className="hidden" disabled={uploading || submitting} />
|
|
</label>
|
|
)}
|
|
<div className="flex gap-2">
|
|
{mediaUrl && (
|
|
<button onClick={() => setMediaUrl('')} className="h-10 px-4 rounded-xl border border-border text-[13px] font-medium text-ink-3">{t('mediaChallenge.retake')}</button>
|
|
)}
|
|
<button
|
|
onClick={() => onSubmit(mediaUrl)}
|
|
disabled={!mediaUrl || uploading || submitting}
|
|
className="flex-1 h-10 rounded-xl bg-krow-navy text-white text-[13px] font-semibold disabled:opacity-40"
|
|
>
|
|
{t('mediaChallenge.submitProofForAiEvaluation')}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
);
|
|
} |