1. TOP
  2. やる気スイッチグループのお仕事
  3. テストhtml
import React, { useState, useRef, useEffect } from 'react'; import { Play, Square, Video, ShieldCheck, Activity, Award, ChevronRight, RefreshCw, Volume2, User } from 'lucide-react'; import { initializeApp } from 'firebase/app'; import { getAuth, signInAnonymously } from 'firebase/auth'; import { getFirestore, collection, addDoc, doc, setDoc } from 'firebase/firestore'; // --- API Configuration --- const apiKey = ""; // Runtime provides the key const App = () => { const [status, setStatus] = useState('idle'); // idle, speaking, recording, analyzing, results const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0); const [evaluation, setEvaluation] = useState(null); const [isRecording, setIsRecording] = useState(false); const [videoBlobURL, setVideoBlobURL] = useState(null); const [error, setError] = useState(null); const mediaRecorderRef = useRef(null); const videoChunksRef = useRef([]); const liveVideoRef = useRef(null); const streamRef = useRef(null); const questions = [ { id: 1, text: "Please introduce yourself and tell us why you want to be an English teacher in Japan.", focus: "Fluency, Intonation & Overall Impression" }, { id: 2, text: "Read the following sentence with emotion: 'The sophisticated synchronization of educational technology and pedagogical expertise creates a superior learning environment.'", focus: "Complex Pronunciation, Clarity & Expressiveness" }, { id: 3, text: "How would you handle a student who is very shy and doesn't want to speak in class? Show empathy in your response.", focus: "Communication Style, Empathy & Non-verbal Cues" } ]; // --- Utilities --- // pcmToWav is not needed for video analysis const playTTS = async (text) => { setStatus('speaking'); try { const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-preview-tts:generateContent?key=${apiKey}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ contents: [{ parts: [{ text: `Say professionally: ${text}` }] }], generationConfig: { responseModalities: ["AUDIO"], speechConfig: { voiceConfig: { prebuiltVoiceConfig: { voiceName: "Kore" } } } } }) }); const result = await response.json(); const audioData = result.candidates[0].content.parts[0].inlineData.data; const binaryString = atob(audioData); const arrayBuffer = new ArrayBuffer(binaryString.length); const uint8Array = new Uint8Array(arrayBuffer); for (let i = 0; i < binaryString.length; i++) { uint8Array[i] = binaryString.charCodeAt(i); } const blob = new Blob([uint8Array], { type: 'audio/wav' }); const url = URL.createObjectURL(blob); const audio = new Audio(url); audio.onended = () => setStatus('idle'); audio.play(); } catch (err) { console.error(err); setStatus('idle'); setError("TTS playback failed."); } }; const startRecording = async () => { try { // Request both audio and video const stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: { width: 640, height: 480, facingMode: "user" } }); streamRef.current = stream; // Show live video preview if (liveVideoRef.current) { liveVideoRef.current.srcObject = stream; } mediaRecorderRef.current = new MediaRecorder(stream, { mimeType: 'video/webm' }); videoChunksRef.current = []; mediaRecorderRef.current.ondataavailable = (event) => { if (event.data.size > 0) { videoChunksRef.current.push(event.data); } }; mediaRecorderRef.current.onstop = async () => { // Stop all tracks to turn off camera/mic streamRef.current.getTracks().forEach(track => track.stop()); if (liveVideoRef.current) { liveVideoRef.current.srcObject = null; } const videoBlob = new Blob(videoChunksRef.current, { type: 'video/webm' }); setVideoBlobURL(URL.createObjectURL(videoBlob)); const reader = new FileReader(); reader.readAsDataURL(videoBlob); reader.onloadend = () => { const base64Video = reader.result.split(',')[1]; evaluatePerformance(base64Video); }; }; mediaRecorderRef.current.start(); setIsRecording(true); setStatus('recording'); setError(null); } catch (err) { console.error(err); setError("Camera/Microphone access denied. Please check permissions."); } }; const stopRecording = () => { if (mediaRecorderRef.current && mediaRecorderRef.current.state !== 'inactive') { mediaRecorderRef.current.stop(); setIsRecording(false); setStatus('analyzing'); } }; const evaluatePerformance = async (base64Video) => { const prompt = ` As a professional linguistic expert and ALT recruiter, evaluate this applicant's video response for the following question: "${questions[currentQuestionIndex].text}". Analyze BOTH the audio (pronunciation, fluency) AND the visual elements (facial expressions, eye contact, confidence, professional demeanor). Provide a structured JSON response with: - overall_score (0-100) - nativeness_score (0-100: audio focus) - clarity_score (0-100: audio focus) - fluency_score (0-100: audio focus) - expression_score (0-100: visual focus - confidence, engagement, appropriate facial cues) - detailed_feedback (Japanese, professional tone, covering both speech and non-verbal aspects) - transcription (The text you heard) - areas_for_improvement (Array of specific points, mixed audio/visual) `; try { const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-preview-09-2025:generateContent?key=${apiKey}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ contents: [{ parts: [ { text: prompt }, { inlineData: { mimeType: "video/webm", data: base64Video } } ] }], generationConfig: { responseMimeType: "application/json" } }) }); const data = await response.json(); if (!data.candidates || !data.candidates[0].content.parts[0].text) { throw new Error("No analysis result generated."); } const evalData = JSON.parse(data.candidates[0].content.parts[0].text); setEvaluation(evalData); setStatus('results'); } catch (err) { console.error(err); setError("Analysis failed. The video might be too long or unclear. Please try again."); setStatus('idle'); setVideoBlobURL(null); } }; const nextQuestion = () => { if (currentQuestionIndex < questions.length - 1) { setCurrentQuestionIndex(prev => prev + 1); setEvaluation(null); setVideoBlobURL(null); setStatus('idle'); } else { setStatus('finished'); } }; // Cleanup stream on unmount useEffect(() => { return () => { if (streamRef.current) { streamRef.current.getTracks().forEach(track => track.stop()); } }; }, []); return (
{/* Header */}

SmartRecruit AI Video Interviewer

Candidate: #ALT-PRO-0293
{/* Main Content */}
{/* Left Sidebar - Progress & Notes */}

Interview Progress

{questions.map((q, idx) => (
{idx + 1}

Section {idx + 1}

{q.focus}

))}

"Assess not just WHAT they say, but HOW they say it. Look for confidence, natural engagement, and professional demeanor in their video response."

{/* Right Content - Interview Interaction Area */}
{status === 'finished' ? (

Interview Completed

Video responses and AI evaluations have been finalized for review.

) : ( <> {/* Question Section */}
Question {currentQuestionIndex + 1} of {questions.length}

{questions[currentQuestionIndex].text}

{/* Video/Action Section */}
{status === 'recording' ? ( // Recording State: Live Preview
RECORDING
) : status === 'analyzing' ? ( // Analyzing State

AI Analyzing Video...

Evaluating speech clarity, fluency, and non-verbal communication cues.

) : status === 'results' && evaluation ? ( // Results State: Recorded Video & Scores
{videoBlobURL &&

Nativeness

{evaluation.nativeness_score}

Clarity

{evaluation.clarity_score}

Fluency

{evaluation.fluency_score}

{/* New Metric */}

Expression

{evaluation.expression_score}

AI Feedback (Audio & Visual)

{evaluation.detailed_feedback}

{evaluation.areas_for_improvement.map((item, i) => ( {item} ))}
) : ( // Idle State: Record Button

START VIDEO RESPONSE

)}
{status === 'recording' && ( )} )} {error && (
{error}
)}

© 2026 Ujiie AI Talent Solutions. All rights reserved.

); }; export default App;

ご応募はこちらから

ご登録のメールアドレスへご連絡させていただきます

求
人
を
探
す