mirror of
https://github.com/CompassConnections/Compass.git
synced 2026-02-24 02:46:11 -05:00
Add option to delete an answered compatibility prompt
This commit is contained in:
@@ -1,22 +1,22 @@
|
||||
import { RadioGroup } from '@headlessui/react'
|
||||
import { UserIcon } from '@heroicons/react/solid'
|
||||
import {RadioGroup} from '@headlessui/react'
|
||||
import {UserIcon} from '@heroicons/react/solid'
|
||||
import clsx from 'clsx'
|
||||
import { Row as rowFor, run } from 'common/supabase/utils'
|
||||
import { User } from 'common/user'
|
||||
import { shortenNumber } from 'common/util/format'
|
||||
import { sortBy } from 'lodash'
|
||||
import { useState } from 'react'
|
||||
import { Button } from 'web/components/buttons/button'
|
||||
import { Col } from 'web/components/layout/col'
|
||||
import { SCROLLABLE_MODAL_CLASS } from 'web/components/layout/modal'
|
||||
import { Row } from 'web/components/layout/row'
|
||||
import { ExpandingInput } from 'web/components/widgets/expanding-input'
|
||||
import { RadioToggleGroup } from 'web/components/widgets/radio-toggle-group'
|
||||
import { Tooltip } from 'web/components/widgets/tooltip'
|
||||
import { QuestionWithCountType } from 'web/hooks/use-questions'
|
||||
import { track } from 'web/lib/service/analytics'
|
||||
import { db } from 'web/lib/supabase/db'
|
||||
import { filterKeys } from '../questions-form'
|
||||
import {Row as rowFor, run} from 'common/supabase/utils'
|
||||
import {User} from 'common/user'
|
||||
import {shortenNumber} from 'common/util/format'
|
||||
import {sortBy} from 'lodash'
|
||||
import {useState} from 'react'
|
||||
import {Button} from 'web/components/buttons/button'
|
||||
import {Col} from 'web/components/layout/col'
|
||||
import {SCROLLABLE_MODAL_CLASS} from 'web/components/layout/modal'
|
||||
import {Row} from 'web/components/layout/row'
|
||||
import {ExpandingInput} from 'web/components/widgets/expanding-input'
|
||||
import {RadioToggleGroup} from 'web/components/widgets/radio-toggle-group'
|
||||
import {Tooltip} from 'web/components/widgets/tooltip'
|
||||
import {QuestionWithCountType} from 'web/hooks/use-questions'
|
||||
import {track} from 'web/lib/service/analytics'
|
||||
import {db} from 'web/lib/supabase/db'
|
||||
import {filterKeys} from '../questions-form'
|
||||
import toast from "react-hot-toast";
|
||||
|
||||
export type CompatibilityAnswerSubmitType = Omit<
|
||||
@@ -72,7 +72,25 @@ export const submitCompatibilityAnswer = async (
|
||||
console.error('Failed to upsert love_compatibility_answers:', error);
|
||||
toast.error('Error submitting. Try again?')
|
||||
}
|
||||
}
|
||||
|
||||
export const deleteCompatibilityAnswer = async (
|
||||
id: number,
|
||||
userId: string
|
||||
) => {
|
||||
if (!userId || !id) return
|
||||
try {
|
||||
await run(
|
||||
db
|
||||
.from('love_compatibility_answers')
|
||||
.delete()
|
||||
.match({id: id, creator_id: userId})
|
||||
)
|
||||
await track('delete compatibility question', {id});
|
||||
} catch (error) {
|
||||
console.error('Failed to delete prompt answer:', error);
|
||||
toast.error('Error deleting. Try again?')
|
||||
}
|
||||
}
|
||||
|
||||
function getEmptyAnswer(userId: string, questionId: number) {
|
||||
@@ -85,6 +103,7 @@ function getEmptyAnswer(userId: string, questionId: number) {
|
||||
importance: -1,
|
||||
}
|
||||
}
|
||||
|
||||
export function AnswerCompatibilityQuestionContent(props: {
|
||||
compatibilityQuestion: QuestionWithCountType
|
||||
user: User
|
||||
@@ -108,7 +127,7 @@ export function AnswerCompatibilityQuestionContent(props: {
|
||||
} = props
|
||||
const [answer, setAnswer] = useState<CompatibilityAnswerSubmitType>(
|
||||
(props.answer as CompatibilityAnswerSubmitType) ??
|
||||
getEmptyAnswer(user.id, compatibilityQuestion.id)
|
||||
getEmptyAnswer(user.id, compatibilityQuestion.id)
|
||||
)
|
||||
|
||||
const [loading, setLoading] = useState(false)
|
||||
@@ -158,7 +177,7 @@ export function AnswerCompatibilityQuestionContent(props: {
|
||||
>
|
||||
{shortenedPopularity}
|
||||
</Tooltip>
|
||||
<UserIcon className="h-4 w-4" />
|
||||
<UserIcon className="h-4 w-4"/>
|
||||
</Row>
|
||||
)}
|
||||
</Col>
|
||||
@@ -173,7 +192,7 @@ export function AnswerCompatibilityQuestionContent(props: {
|
||||
<SelectAnswer
|
||||
value={answer.multiple_choice}
|
||||
setValue={(choice) =>
|
||||
setAnswer({ ...answer, multiple_choice: choice })
|
||||
setAnswer({...answer, multiple_choice: choice})
|
||||
}
|
||||
options={optionOrder}
|
||||
/>
|
||||
@@ -183,7 +202,7 @@ export function AnswerCompatibilityQuestionContent(props: {
|
||||
<MultiSelectAnswers
|
||||
values={answer.pref_choices ?? []}
|
||||
setValue={(choice) =>
|
||||
setAnswer({ ...answer, pref_choices: choice })
|
||||
setAnswer({...answer, pref_choices: choice})
|
||||
}
|
||||
options={optionOrder}
|
||||
/>
|
||||
@@ -194,7 +213,7 @@ export function AnswerCompatibilityQuestionContent(props: {
|
||||
currentChoice={answer.importance ?? -1}
|
||||
choicesMap={IMPORTANCE_CHOICES}
|
||||
setChoice={(choice: number) =>
|
||||
setAnswer({ ...answer, importance: choice })
|
||||
setAnswer({...answer, importance: choice})
|
||||
}
|
||||
indexColors={IMPORTANCE_RADIO_COLORS}
|
||||
/>
|
||||
@@ -208,14 +227,14 @@ export function AnswerCompatibilityQuestionContent(props: {
|
||||
rows={3}
|
||||
value={answer.explanation ?? ''}
|
||||
onChange={(e) =>
|
||||
setAnswer({ ...answer, explanation: e.target.value })
|
||||
setAnswer({...answer, explanation: e.target.value})
|
||||
}
|
||||
/>
|
||||
</Col>
|
||||
</Col>
|
||||
<Row className="w-full justify-between gap-4">
|
||||
{noSkip ? (
|
||||
<div />
|
||||
<div/>
|
||||
) : (
|
||||
<button
|
||||
disabled={loading || skipLoading}
|
||||
@@ -252,15 +271,15 @@ export function AnswerCompatibilityQuestionContent(props: {
|
||||
loading={loading}
|
||||
onClick={() => {
|
||||
setLoading(true)
|
||||
submitCompatibilityAnswer(answer)
|
||||
.then(() => {
|
||||
if (isLastQuestion) {
|
||||
onSubmit()
|
||||
} else if (onNext) {
|
||||
onNext()
|
||||
}
|
||||
})
|
||||
.finally(() => setLoading(false))
|
||||
submitCompatibilityAnswer(answer)
|
||||
.then(() => {
|
||||
if (isLastQuestion) {
|
||||
onSubmit()
|
||||
} else if (onNext) {
|
||||
onNext()
|
||||
}
|
||||
})
|
||||
.finally(() => setLoading(false))
|
||||
}}
|
||||
>
|
||||
{isLastQuestion ? 'Finish' : 'Next'}
|
||||
@@ -275,7 +294,7 @@ export const SelectAnswer = (props: {
|
||||
setValue: (value: number) => void
|
||||
options: string[]
|
||||
}) => {
|
||||
const { value, setValue, options } = props
|
||||
const {value, setValue, options} = props
|
||||
return (
|
||||
<RadioGroup
|
||||
className={
|
||||
@@ -288,7 +307,7 @@ export const SelectAnswer = (props: {
|
||||
<RadioGroup.Option
|
||||
key={i}
|
||||
value={i}
|
||||
className={({ disabled }) =>
|
||||
className={({disabled}) =>
|
||||
clsx(
|
||||
disabled
|
||||
? 'text-ink-300 aria-checked:bg-ink-300 aria-checked:text-ink-0 cursor-not-allowed'
|
||||
@@ -310,7 +329,7 @@ export const MultiSelectAnswers = (props: {
|
||||
setValue: (value: number[]) => void
|
||||
options: string[]
|
||||
}) => {
|
||||
const { values, setValue, options } = props
|
||||
const {values, setValue, options} = props
|
||||
|
||||
return (
|
||||
<Col
|
||||
|
||||
@@ -1,52 +1,43 @@
|
||||
import { PencilIcon } from '@heroicons/react/outline'
|
||||
import {
|
||||
getAnswerCompatibility,
|
||||
getScoredAnswerCompatibility,
|
||||
} from 'common/love/compatibility-score'
|
||||
import { Profile } from 'common/love/profile'
|
||||
import { Row as rowFor } from 'common/supabase/utils'
|
||||
import { User } from 'common/user'
|
||||
import { partition, sortBy, keyBy } from 'lodash'
|
||||
import { useProfile } from 'web/hooks/use-profile'
|
||||
import {PencilIcon, TrashIcon} from '@heroicons/react/outline'
|
||||
import {getAnswerCompatibility, getScoredAnswerCompatibility,} from 'common/love/compatibility-score'
|
||||
import {Profile} from 'common/love/profile'
|
||||
import {Row as rowFor} from 'common/supabase/utils'
|
||||
import {User} from 'common/user'
|
||||
import {keyBy, partition, sortBy} from 'lodash'
|
||||
import {useProfile} from 'web/hooks/use-profile'
|
||||
import {
|
||||
QuestionWithCountType,
|
||||
useCompatibilityQuestionsWithAnswerCount,
|
||||
useUserCompatibilityAnswers,
|
||||
} from 'web/hooks/use-questions'
|
||||
import { useEffect, useState } from 'react'
|
||||
import {useEffect, useState} from 'react'
|
||||
import DropdownMenu from 'web/components/comments/dropdown-menu'
|
||||
import { Col } from 'web/components/layout/col'
|
||||
import {
|
||||
MODAL_CLASS,
|
||||
Modal,
|
||||
SCROLLABLE_MODAL_CLASS,
|
||||
} from 'web/components/layout/modal'
|
||||
import { Row } from 'web/components/layout/row'
|
||||
import { Linkify } from 'web/components/widgets/linkify'
|
||||
import { Pagination } from 'web/components/widgets/pagination'
|
||||
import { db } from 'web/lib/supabase/db'
|
||||
import { Subtitle } from '../widgets/profile-subtitle'
|
||||
import { AddCompatibilityQuestionButton } from './add-compatibility-question-button'
|
||||
import {Col} from 'web/components/layout/col'
|
||||
import {Modal, MODAL_CLASS, SCROLLABLE_MODAL_CLASS,} from 'web/components/layout/modal'
|
||||
import {Row} from 'web/components/layout/row'
|
||||
import {Linkify} from 'web/components/widgets/linkify'
|
||||
import {Pagination} from 'web/components/widgets/pagination'
|
||||
import {db} from 'web/lib/supabase/db'
|
||||
import {Subtitle} from '../widgets/profile-subtitle'
|
||||
import {AddCompatibilityQuestionButton} from './add-compatibility-question-button'
|
||||
import {
|
||||
AnswerCompatibilityQuestionButton,
|
||||
AnswerSkippedCompatibilityQuestionsButton,
|
||||
} from './answer-compatibility-question-button'
|
||||
import {
|
||||
AnswerCompatibilityQuestionContent,
|
||||
deleteCompatibilityAnswer,
|
||||
IMPORTANCE_CHOICES,
|
||||
IMPORTANCE_DISPLAY_COLORS,
|
||||
} from './answer-compatibility-question-content'
|
||||
import clsx from 'clsx'
|
||||
import { shortenName } from 'web/components/widgets/user-link'
|
||||
import {
|
||||
PreferredList,
|
||||
PreferredListNoComparison,
|
||||
} from './compatibility-question-preferred-list'
|
||||
import { useUser } from 'web/hooks/use-user'
|
||||
import { usePersistentInMemoryState } from 'web/hooks/use-persistent-in-memory-state'
|
||||
import { useIsLooking } from 'web/hooks/use-is-looking'
|
||||
import { DropdownButton } from '../filters/desktop-filters'
|
||||
import { buildArray } from 'common/util/array'
|
||||
import {shortenName} from 'web/components/widgets/user-link'
|
||||
import {PreferredList, PreferredListNoComparison,} from './compatibility-question-preferred-list'
|
||||
import {useUser} from 'web/hooks/use-user'
|
||||
import {usePersistentInMemoryState} from 'web/hooks/use-persistent-in-memory-state'
|
||||
import {useIsLooking} from 'web/hooks/use-is-looking'
|
||||
import {DropdownButton} from '../filters/desktop-filters'
|
||||
import {buildArray} from 'common/util/array'
|
||||
|
||||
const NUM_QUESTIONS_TO_SHOW = 8
|
||||
|
||||
@@ -69,7 +60,7 @@ function separateQuestionsArray(
|
||||
}
|
||||
})
|
||||
|
||||
return { skippedQuestions, answeredQuestions, otherQuestions }
|
||||
return {skippedQuestions, answeredQuestions, otherQuestions}
|
||||
}
|
||||
|
||||
type CompatibilitySort =
|
||||
@@ -85,12 +76,12 @@ export function CompatibilityQuestionsDisplay(props: {
|
||||
fromSignup?: boolean
|
||||
fromProfilePage?: Profile
|
||||
}) {
|
||||
const { isCurrentUser, user, fromSignup, fromProfilePage, profile } = props
|
||||
const {isCurrentUser, user, fromSignup, fromProfilePage, profile} = props
|
||||
|
||||
const { refreshCompatibilityQuestions, compatibilityQuestions } =
|
||||
const {refreshCompatibilityQuestions, compatibilityQuestions} =
|
||||
useCompatibilityQuestionsWithAnswerCount()
|
||||
|
||||
const { refreshCompatibilityAnswers, compatibilityAnswers } =
|
||||
const {refreshCompatibilityAnswers, compatibilityAnswers} =
|
||||
useUserCompatibilityAnswers(user.id)
|
||||
|
||||
const [skippedAnswers, answers] = partition(
|
||||
@@ -106,7 +97,7 @@ export function CompatibilityQuestionsDisplay(props: {
|
||||
skippedAnswers.map((answer) => answer.question_id)
|
||||
)
|
||||
|
||||
const { skippedQuestions, answeredQuestions, otherQuestions } =
|
||||
const {skippedQuestions, answeredQuestions, otherQuestions} =
|
||||
separateQuestionsArray(
|
||||
compatibilityQuestions,
|
||||
skippedAnswerQuestionIds,
|
||||
@@ -126,7 +117,7 @@ export function CompatibilityQuestionsDisplay(props: {
|
||||
|
||||
const currentUser = useUser()
|
||||
const comparedUserId = fromProfilePage?.user_id ?? currentUser?.id
|
||||
const { compatibilityAnswers: comparedAnswers } =
|
||||
const {compatibilityAnswers: comparedAnswers} =
|
||||
useUserCompatibilityAnswers(comparedUserId)
|
||||
const questionIdToComparedAnswer = keyBy(comparedAnswers, 'question_id')
|
||||
|
||||
@@ -206,9 +197,9 @@ export function CompatibilityQuestionsDisplay(props: {
|
||||
</span>
|
||||
) : (
|
||||
<span className="text-ink-600 text-sm">
|
||||
Answer more questions to increase your compatibility scores—or
|
||||
Answer more questions to increase your compatibility scores—or{' '}
|
||||
</span>
|
||||
)}{' '}
|
||||
)}
|
||||
<AddCompatibilityQuestionButton
|
||||
refreshCompatibilityAll={refreshCompatibilityAll}
|
||||
/>
|
||||
@@ -269,7 +260,7 @@ function CompatibilitySortWidget(props: {
|
||||
fromProfilePage: Profile | undefined
|
||||
className?: string
|
||||
}) {
|
||||
const { sort, setSort, user, fromProfilePage, className } = props
|
||||
const {sort, setSort, user, fromProfilePage, className} = props
|
||||
const currentUser = useUser()
|
||||
|
||||
const sortToDisplay = {
|
||||
@@ -286,7 +277,7 @@ function CompatibilitySortWidget(props: {
|
||||
'their-important',
|
||||
'disagree',
|
||||
(!fromProfilePage || fromProfilePage.user_id === currentUser?.id) &&
|
||||
'your-unanswered'
|
||||
'your-unanswered'
|
||||
)
|
||||
|
||||
return (
|
||||
@@ -301,7 +292,7 @@ function CompatibilitySortWidget(props: {
|
||||
closeOnClick
|
||||
buttonClass={'!text-ink-600 !hover:!text-ink-600'}
|
||||
buttonContent={(open: boolean) => (
|
||||
<DropdownButton content={sortToDisplay[sort]} open={open} />
|
||||
<DropdownButton content={sortToDisplay[sort]} open={open}/>
|
||||
)}
|
||||
menuItemsClass={'bg-canvas-0'}
|
||||
menuWidth="w-48"
|
||||
@@ -335,8 +326,8 @@ function CompatibilityAnswerBlock(props: {
|
||||
const comparedProfile = isCurrentUser
|
||||
? null
|
||||
: !!fromProfilePage
|
||||
? fromProfilePage
|
||||
: { ...currentProfile, user: currentUser }
|
||||
? fromProfilePage
|
||||
: {...currentProfile, user: currentUser}
|
||||
|
||||
if (
|
||||
!question ||
|
||||
@@ -393,9 +384,16 @@ function CompatibilityAnswerBlock(props: {
|
||||
items={[
|
||||
{
|
||||
name: 'Edit',
|
||||
icon: <PencilIcon className="h-5 w-5" />,
|
||||
icon: <PencilIcon className="h-5 w-5"/>,
|
||||
onClick: () => setEditOpen(true),
|
||||
},
|
||||
{
|
||||
name: 'Delete',
|
||||
icon: <TrashIcon className="h-5 w-5"/>,
|
||||
onClick: () => {
|
||||
deleteCompatibilityAnswer(answer.id, user.id).then(() => refreshCompatibilityAll())
|
||||
},
|
||||
},
|
||||
]}
|
||||
closeOnClick
|
||||
menuWidth="w-40"
|
||||
@@ -409,7 +407,7 @@ function CompatibilityAnswerBlock(props: {
|
||||
</Row>
|
||||
<Row className="px-2 -mt-4">
|
||||
{answer.explanation && (
|
||||
<Linkify className="" text={answer.explanation} />
|
||||
<Linkify className="" text={answer.explanation}/>
|
||||
)}
|
||||
</Row>
|
||||
{distinctPreferredAnswersText.length > 0 && (
|
||||
@@ -509,6 +507,7 @@ function CompatibilityDisplay(props: {
|
||||
setAnswer2(res.data[0] ?? null)
|
||||
})
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
getComparedProfileAnswer()
|
||||
}, [])
|
||||
@@ -527,7 +526,7 @@ function CompatibilityDisplay(props: {
|
||||
const answerCompatibility = answer2
|
||||
? getAnswerCompatibility(answer1, answer2)
|
||||
: //getScoredAnswerCompatibility(answer1, answer2)
|
||||
undefined
|
||||
undefined
|
||||
const user1 = profile1.user
|
||||
const user2 = profile2.user
|
||||
|
||||
@@ -572,11 +571,11 @@ function CompatibilityDisplay(props: {
|
||||
<div className="text-ink-500 text-sm">
|
||||
{shortenName(user1.name)} marked this as{' '}
|
||||
<span className="font-semibold">
|
||||
<ImportanceDisplay importance={answer1.importance} />
|
||||
<ImportanceDisplay importance={answer1.importance}/>
|
||||
</span>
|
||||
</div>
|
||||
{!answer2 && (
|
||||
<PreferredListNoComparison question={question} answer={answer1} />
|
||||
<PreferredListNoComparison question={question} answer={answer1}/>
|
||||
)}
|
||||
{answer2 && (
|
||||
<>
|
||||
@@ -597,7 +596,7 @@ function CompatibilityDisplay(props: {
|
||||
{isCurrentUser ? 'You' : shortenName(user2.name)} marked this
|
||||
as{' '}
|
||||
<span className="font-semibold">
|
||||
<ImportanceDisplay importance={answer2.importance} />
|
||||
<ImportanceDisplay importance={answer2.importance}/>
|
||||
</span>
|
||||
</div>
|
||||
<PreferredList
|
||||
@@ -616,7 +615,7 @@ function CompatibilityDisplay(props: {
|
||||
}
|
||||
|
||||
function ImportanceDisplay(props: { importance: number }) {
|
||||
const { importance } = props
|
||||
const {importance} = props
|
||||
return (
|
||||
<span className={clsx('w-fit')}>
|
||||
{getStringKeyFromNumValue(importance, IMPORTANCE_CHOICES)}
|
||||
@@ -629,7 +628,7 @@ function ImportanceButton(props: {
|
||||
onClick: () => void
|
||||
className?: string
|
||||
}) {
|
||||
const { importance, onClick, className } = props
|
||||
const {importance, onClick, className} = props
|
||||
return (
|
||||
<button
|
||||
onClick={onClick}
|
||||
@@ -641,10 +640,11 @@ function ImportanceButton(props: {
|
||||
className
|
||||
)}
|
||||
>
|
||||
<ImportanceDisplay importance={importance} />
|
||||
<ImportanceDisplay importance={importance}/>
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
function getStringKeyFromNumValue(
|
||||
value: number,
|
||||
map: Record<string, number>
|
||||
|
||||
@@ -21,7 +21,7 @@ import {api, updateProfile} from 'web/lib/api'
|
||||
import React, {useState} from 'react'
|
||||
import {VisibilityConfirmationModal} from './visibility-confirmation-modal'
|
||||
|
||||
export default function ProfileProfileHeader(props: {
|
||||
export default function ProfileHeader(props: {
|
||||
user: User
|
||||
profile: Profile
|
||||
simpleView?: boolean
|
||||
@@ -1,5 +1,5 @@
|
||||
import {ProfileCommentSection} from 'web/components/profile-comment-section'
|
||||
import ProfileProfileHeader from 'web/components/profile/profile-profile-header'
|
||||
import ProfileHeader from 'web/components/profile/profile-header'
|
||||
import ProfileCarousel from 'web/components/profile-carousel'
|
||||
import {Col} from 'web/components/layout/col'
|
||||
import {Row} from 'web/components/layout/row'
|
||||
@@ -16,7 +16,7 @@ import {Content} from "web/components/widgets/editor";
|
||||
import {JSONContent} from "@tiptap/core";
|
||||
import React from "react";
|
||||
|
||||
export function ProfileProfile(props: {
|
||||
export function ProfileInfo(props: {
|
||||
profile: Profile
|
||||
user: User
|
||||
refreshProfile: () => void
|
||||
@@ -63,7 +63,7 @@ export function ProfileProfile(props: {
|
||||
|
||||
return (
|
||||
<>
|
||||
<ProfileProfileHeader
|
||||
<ProfileHeader
|
||||
user={user}
|
||||
profile={profile}
|
||||
simpleView={!!fromProfilePage}
|
||||
@@ -15,7 +15,7 @@ import {useSaveReferral} from 'web/hooks/use-save-referral'
|
||||
import {getLoveOgImageUrl} from 'common/love/og-image'
|
||||
import {getProfileRow, ProfileRow} from 'common/love/profile'
|
||||
import {db} from 'web/lib/supabase/db'
|
||||
import {ProfileProfile} from 'web/components/profile/profile-profile'
|
||||
import {ProfileInfo} from 'web/components/profile/profile-info'
|
||||
import {User} from 'common/user'
|
||||
import {getUserForStaticProps} from 'common/supabase/users'
|
||||
import {type GetStaticProps} from 'next'
|
||||
@@ -141,7 +141,7 @@ function UserPageInner(props: ActiveUserPageProps) {
|
||||
{currentUser !== undefined && (
|
||||
<Col className={'gap-4'}>
|
||||
{profile ? (
|
||||
<ProfileProfile
|
||||
<ProfileInfo
|
||||
key={profile.user_id}
|
||||
profile={profile}
|
||||
user={user}
|
||||
|
||||
Reference in New Issue
Block a user