enlighten-qalb / src /components /Search /SearchResults.jsx
eli02's picture
feat: Include userType in SearchResults component and update feedback data structure
a87bd5a
import React, { useState, useEffect } from 'react';
import {
Box,
Paper,
Typography,
IconButton,
Alert,
Grid
} from '@mui/material';
import styled from '@emotion/styled';
import { saveFeedback } from '../../services/search';
import { useAuth } from '../../hooks/useAuth';
const ResultCard = styled(Paper)(({ theme }) => ({
padding: theme.spacing(3),
height: '100%',
display: 'flex',
flexDirection: 'column',
position: 'relative',
}));
const EmojiButton = styled(IconButton)(({ theme }) => ({
fontSize: '1.5rem',
padding: theme.spacing(1),
}));
const SearchResults = ({ results, query, isLoading, error }) => {
const { user, authTokens, userType } = useAuth();
const [shuffledResults, setShuffledResults] = useState([]);
const [feedbackStatus, setFeedbackStatus] = useState({});
const [savingFeedback, setSavingFeedback] = useState(false);
// Shuffle and limit to 2 results when results change
useEffect(() => {
if (results && results.length) {
// Get one result from each model type if possible
const bgeResult = results.find(r => r.model_type?.includes('BGE'));
const uaeResult = results.find(r => r.model_type?.includes('UAE'));
let selectedResults = [];
if (bgeResult && uaeResult) {
selectedResults = [bgeResult, uaeResult];
} else {
// If we don't have both models, take top 2 (or all if less than 2)
selectedResults = results.slice(0, 2);
}
// Randomly shuffle the order
const shuffled = [...selectedResults].sort(() => Math.random() - 0.5);
// Assign labels (A and B)
setShuffledResults(shuffled.map((result, index) => ({
...result,
resultLabel: index === 0 ? 'A' : 'B'
})));
// Reset feedback status
setFeedbackStatus({});
} else {
setShuffledResults([]);
}
}, [results]);
if (error) {
return (
<Alert severity="error" sx={{ my: 2 }}>
{error}
</Alert>
);
}
if (!results || results.length === 0) {
return (
<Typography variant="body1" color="textSecondary" align="center" sx={{ my: 4 }}>
No results found. Try a different query.
</Typography>
);
}
const handleReaction = async (result, reaction) => {
// Prevent multiple submissions
if (feedbackStatus[result.resultLabel] || savingFeedback) return;
setSavingFeedback(true);
try {
// Prepare feedback data
const feedbackData = {
user_type: userType, // Use userType from auth context instead of hardcoded 'user'
username: user || 'anonymous',
query: query || '',
retrieved_text: result.content || result.text || JSON.stringify(result),
model_type: result.model_type || 'unknown',
result_label: result.resultLabel || '',
reaction: reaction, // Save the emoji directly instead of converting to text
confidence_score: result.similarity || 0, // Send the model confidence score
};
// Save the feedback
await saveFeedback(feedbackData, authTokens.access);
// Update UI to show feedback was saved
setFeedbackStatus(prev => ({
...prev,
[result.resultLabel]: { status: 'success', reaction }
}));
} catch (err) {
console.error("Feedback submission error:", err);
setFeedbackStatus(prev => ({
...prev,
[result.resultLabel]: { status: 'error', message: 'Failed to save feedback' }
}));
} finally {
setSavingFeedback(false);
}
};
return (
<Box>
<Typography variant="h6" gutterBottom>
Search Results
</Typography>
<Grid container spacing={3} sx={{ mb: 4 }}>
{shuffledResults.map((result) => (
<Grid item xs={12} md={6} key={result.resultLabel}>
<ResultCard elevation={3}>
<Typography variant="h6" gutterBottom>
Result {result.resultLabel}
</Typography>
<Typography variant="body1" paragraph sx={{ flexGrow: 1 }}>
{result.text}
</Typography>
<Box mt={2}>
{feedbackStatus[result.resultLabel]?.status === 'success' ? (
<Typography variant="body2" color="success.main" align="center">
Thank you for your feedback! You selected {feedbackStatus[result.resultLabel].reaction}
</Typography>
) : feedbackStatus[result.resultLabel]?.status === 'error' ? (
<Typography variant="body2" color="error" align="center">
{feedbackStatus[result.resultLabel].message}
</Typography>
) : (
<Box display="flex" justifyContent="center" gap={2}>
<EmojiButton
onClick={() => handleReaction(result, 'πŸ‘')}
disabled={savingFeedback}
aria-label="Good result"
>
πŸ‘
</EmojiButton>
<EmojiButton
onClick={() => handleReaction(result, 'πŸ€·β€β™‚οΈ')}
disabled={savingFeedback}
aria-label="Somewhat relevant"
>
πŸ€·β€β™‚οΈ
</EmojiButton>
<EmojiButton
onClick={() => handleReaction(result, 'πŸ‘Ž')}
disabled={savingFeedback}
aria-label="Not relevant"
>
πŸ‘Ž
</EmojiButton>
</Box>
)}
</Box>
</ResultCard>
</Grid>
))}
</Grid>
</Box>
);
};
export default SearchResults;