mirror of
https://github.com/CompassConnections/Compass.git
synced 2025-12-23 22:18:43 -05:00
205 lines
6.5 KiB
TypeScript
205 lines
6.5 KiB
TypeScript
import {useUser} from 'web/hooks/use-user'
|
|
import {useCompatibilityQuestionsWithAnswerCount, useUserCompatibilityAnswers} from 'web/hooks/use-questions'
|
|
import {useCallback, useEffect, useMemo, useState} from 'react'
|
|
import {Row} from 'common/supabase/utils'
|
|
import {Question} from 'web/lib/supabase/questions'
|
|
import {Col} from 'web/components/layout/col'
|
|
import {Title} from 'web/components/widgets/title'
|
|
import {PageBase} from "web/components/page-base";
|
|
import {UncontrolledTabs} from "web/components/layout/tabs";
|
|
import {CompatibilityAnswerBlock} from "web/components/answers/compatibility-questions-display";
|
|
import {User} from "common/user";
|
|
import {CompassLoadingIndicator} from "web/components/widgets/loading-indicator";
|
|
import {useIsMobile} from "web/hooks/use-is-mobile";
|
|
import {LoadMoreUntilNotVisible} from "web/components/widgets/visibility-observer";
|
|
|
|
type QuestionWithAnswer = Question & {
|
|
answer?: Row<'compatibility_answers'>
|
|
answer_count: number
|
|
score: number
|
|
}
|
|
|
|
export default function CompatibilityPage() {
|
|
const user = useUser()
|
|
const isMobile = useIsMobile()
|
|
const sep = isMobile ? '\n' : ''
|
|
const {compatibilityAnswers, refreshCompatibilityAnswers} = useUserCompatibilityAnswers(user?.id)
|
|
const {compatibilityQuestions, refreshCompatibilityQuestions} = useCompatibilityQuestionsWithAnswerCount()
|
|
const [isLoading, setIsLoading] = useState(true)
|
|
|
|
const questionsWithAnswers = useMemo(() => {
|
|
if (!compatibilityQuestions) return []
|
|
|
|
const answerMap = new Map(
|
|
compatibilityAnswers?.map((a) => [a.question_id, a]) ?? []
|
|
)
|
|
|
|
return compatibilityQuestions.map((q) => ({
|
|
...q,
|
|
answer: answerMap.get(q.id),
|
|
})).sort(
|
|
(a, b) => a.importance_score - b.importance_score
|
|
) as QuestionWithAnswer[]
|
|
}, [compatibilityQuestions, compatibilityAnswers])
|
|
|
|
const {answered, notAnswered, skipped} = useMemo(() => {
|
|
const answered: QuestionWithAnswer[] = []
|
|
const notAnswered: QuestionWithAnswer[] = []
|
|
const skipped: QuestionWithAnswer[] = []
|
|
|
|
questionsWithAnswers.forEach((q) => {
|
|
if (q.answer) {
|
|
if (q.answer.multiple_choice === -1) {
|
|
skipped.push(q)
|
|
} else {
|
|
answered.push(q)
|
|
}
|
|
} else {
|
|
notAnswered.push(q)
|
|
}
|
|
})
|
|
|
|
return {answered, notAnswered, skipped}
|
|
}, [questionsWithAnswers])
|
|
|
|
useEffect(() => {
|
|
if (user?.id) {
|
|
Promise.all([
|
|
refreshCompatibilityAnswers(),
|
|
refreshCompatibilityQuestions(),
|
|
]).finally(() => setIsLoading(false))
|
|
}
|
|
}, [user?.id])
|
|
|
|
const refreshCompatibilityAll = () => {
|
|
refreshCompatibilityAnswers()
|
|
refreshCompatibilityQuestions()
|
|
}
|
|
|
|
return (
|
|
<PageBase trackPageView={'compatibility'}>
|
|
{user ?
|
|
<Col className="w-full p-4">
|
|
<Title className="mb-4">Your Compatibility Questions</Title>
|
|
<UncontrolledTabs
|
|
trackingName={'compatibility page'}
|
|
name={'compatibility-page'}
|
|
tabs={[
|
|
{
|
|
title: `Answered ${sep}(${answered.length})`,
|
|
content: (
|
|
<QuestionList
|
|
questions={answered}
|
|
status="answered"
|
|
isLoading={isLoading}
|
|
user={user}
|
|
refreshCompatibilityAll={refreshCompatibilityAll}
|
|
/>
|
|
),
|
|
},
|
|
{
|
|
title: `To Answer ${sep}(${notAnswered.length})`,
|
|
content: (
|
|
<QuestionList
|
|
questions={notAnswered}
|
|
status="not-answered"
|
|
isLoading={isLoading}
|
|
user={user}
|
|
refreshCompatibilityAll={refreshCompatibilityAll}
|
|
/>
|
|
),
|
|
},
|
|
{
|
|
title: `Skipped ${sep}(${skipped.length})`,
|
|
content: (
|
|
<QuestionList
|
|
questions={skipped}
|
|
status="skipped"
|
|
isLoading={isLoading}
|
|
user={user}
|
|
refreshCompatibilityAll={refreshCompatibilityAll}
|
|
/>
|
|
),
|
|
},
|
|
]}
|
|
/>
|
|
</Col>
|
|
:
|
|
<div className="flex h-full flex-col items-center justify-center">
|
|
<div className="text-xl">Please sign in to view your compatibility questions</div>
|
|
</div>
|
|
}
|
|
</PageBase>
|
|
)
|
|
}
|
|
|
|
function QuestionList({
|
|
questions,
|
|
status,
|
|
isLoading,
|
|
user,
|
|
refreshCompatibilityAll,
|
|
}: {
|
|
questions: QuestionWithAnswer[]
|
|
status: 'answered' | 'not-answered' | 'skipped'
|
|
isLoading: boolean
|
|
user: User
|
|
refreshCompatibilityAll: () => void
|
|
}) {
|
|
const BATCH_SIZE = 100
|
|
const [visibleCount, setVisibleCount] = useState(BATCH_SIZE)
|
|
|
|
// Reset pagination when the questions list changes (e.g., switching tabs or refreshed data)
|
|
useEffect(() => {
|
|
console.log('resetting pagination')
|
|
setVisibleCount(BATCH_SIZE)
|
|
}, [questions])
|
|
|
|
const loadMore = useCallback(async () => {
|
|
console.log('start loadMore')
|
|
if (visibleCount >= questions.length) return false
|
|
console.log('loading more', visibleCount)
|
|
setVisibleCount((prev) => Math.min(prev + BATCH_SIZE, questions.length))
|
|
console.log('end loadMore')
|
|
return true
|
|
}, [visibleCount, questions.length]);
|
|
|
|
if (isLoading) {
|
|
return <CompassLoadingIndicator/>
|
|
}
|
|
|
|
if (questions.length === 0) {
|
|
return (
|
|
<div className="text-ink-500 p-4">
|
|
{status === 'answered' && 'You haven\'t answered any questions yet.'}
|
|
{status === 'not-answered' && 'All questions have been answered!'}
|
|
{status === 'skipped' && 'You haven\'t skipped any questions.'}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
const visibleQuestions = questions.slice(0, visibleCount)
|
|
|
|
return (
|
|
<div className="space-y-4 p-2">
|
|
{visibleQuestions.map((q) => (
|
|
<div
|
|
key={q.id}
|
|
className="bg-canvas-0 border-canvas-100 rounded-lg border px-2 pt-2 shadow-sm transition-colors"
|
|
>
|
|
<CompatibilityAnswerBlock
|
|
key={q.answer?.question_id}
|
|
question={q}
|
|
answer={q.answer}
|
|
yourQuestions={questions}
|
|
user={user}
|
|
isCurrentUser={true}
|
|
refreshCompatibilityAll={refreshCompatibilityAll}
|
|
/>
|
|
</div>
|
|
))}
|
|
<LoadMoreUntilNotVisible loadMore={loadMore}/>
|
|
</div>
|
|
)
|
|
}
|