Skip to content

Commit

Permalink
Add verification button in place details view (#149)
Browse files Browse the repository at this point in the history
* Add verification button in place details view

* Display status when it is visited
  • Loading branch information
mauriciabad authored Nov 12, 2023
1 parent 62d87d1 commit f6237c8
Show file tree
Hide file tree
Showing 9 changed files with 202 additions and 81 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
'use client'

import { FC } from 'react'
import { shotConfettiStars } from '~/helpers/confetti'
import {
VerificateButton,
VerificateButtonProps,
} from '../../missions/_components/verificate-button'

export const PlaceDetailsVerificateButton: FC<VerificateButtonProps> = (
props
) => {
return (
<VerificateButton
{...props}
onVerificate={(verificated) => {
shotConfettiStars({ withStars: verificated })
}}
/>
)
}
18 changes: 18 additions & 0 deletions src/app/[locale]/(app)/explore/_components/place-details.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ import { MapPoint } from '~/helpers/spatial-data'
import { Features } from '~/server/db/constants/features'
import { VisitMission } from '~/server/db/constants/missions'
import { PlaceCategoryIcon as PlaceCategoryIconType } from '~/server/db/constants/places'
import { VerificationRequirements } from '~/server/db/constants/verifications'
import { PlaceCategoryTagList } from '../../../../../components/place-category-tags/place-category-tag-list'
import { VisitMissionsAcordion } from '../../missions/_components/visit-missions-acordion'
import { FeaturesBlock } from './features-block'
import { PlaceDetailsVerificateButton } from './place-details-verificate-button'
import { ViewMoreImagesButtonAndDialog } from './view-more-images-button-and-dialog'

export const PlaceDetails: FC<{
Expand All @@ -28,6 +30,7 @@ export const PlaceDetails: FC<{
icon: PlaceCategoryIconType | null
name: string
}
visited: boolean
categories: {
category: {
id: number
Expand All @@ -36,6 +39,11 @@ export const PlaceDetails: FC<{
}
}[]
features: Features | null
verificationRequirements: VerificationRequirements | null
verifications: {
id: number
validatedOn: Date
}[]
}
visitMissions: VisitMission[]
}> = ({ placeFullInfo: place, visitMissions }) => {
Expand All @@ -49,6 +57,16 @@ export const PlaceDetails: FC<{
<p className="text-stone-800">{place.description}</p>
)}

<PlaceDetailsVerificateButton
expectedLocation={place.location}
placeId={place.id}
verificationRequirements={place.verificationRequirements}
isAlreadyVisited={place.visited}
isAlreadyVerified={place.verifications.length > 0}
hideIfDone
className="mt-4"
/>

{place.images && place.images.length >= 1 ? (
<div className="mt-4 grid grid-cols-[2fr_1fr] grid-rows-2 gap-2">
<Image
Expand Down
62 changes: 15 additions & 47 deletions src/app/[locale]/(app)/missions/_components/place-prevew-modal.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Button } from '@nextui-org/button'
'use client'

import { Image } from '@nextui-org/image'
import {
Modal,
Expand All @@ -8,12 +9,7 @@ import {
ModalHeader,
ModalProps,
} from '@nextui-org/modal'
import { useDisclosure } from '@nextui-org/react'
import {
IconChevronRight,
IconDiscountCheckFilled,
IconInfoCircle,
} from '@tabler/icons-react'
import { IconChevronRight, IconInfoCircle } from '@tabler/icons-react'
import { useTranslations } from 'next-intl'
import Link from 'next-intl/link'
import { FC } from 'react'
Expand All @@ -23,7 +19,7 @@ import { PlaceCategoryTagList } from '~/components/place-category-tags/place-cat
import { shotConfettiStars } from '~/helpers/confetti'
import { makeImageUrl } from '~/helpers/images'
import { VisitMissionPlace } from '~/server/db/constants/missions'
import { VerificatePlaceVisitModal } from './verificate-place-visit-modal'
import { VerificateButton } from './verificate-button'

export const PlacePreviewModal: FC<
Omit<ModalProps, 'children'> & {
Expand All @@ -38,12 +34,6 @@ export const PlacePreviewModal: FC<
place.location.lat < 41.920697 ||
place.location.lat > 41.984129)

const {
isOpen: isVerificateModalOpen,
onOpen: onVerificateModalOpen,
onOpenChange: onVerificateModalOpenChange,
} = useDisclosure()

return (
<Modal isOpen={isOpen} onOpenChange={onOpenChange}>
<ModalContent>
Expand Down Expand Up @@ -125,40 +115,18 @@ export const PlacePreviewModal: FC<
</span>
</LinkButton>

{place.missionStatus.verified ? (
<Button
fullWidth
color="primary"
isDisabled
onPress={onVerificateModalOpen}
startContent={<IconDiscountCheckFilled size={20} />}
>
{t('visit-already-verified')}
</Button>
) : (
<Button
color="primary"
fullWidth
onPress={onVerificateModalOpen}
startContent={<IconDiscountCheckFilled size={20} />}
>
{t('verificate-visit')}
</Button>
)}
<VerificateButton
expectedLocation={place.location}
placeId={place.id}
isAlreadyVisited={place.visited}
isAlreadyVerified={place.verifications.length > 0}
verificationRequirements={place.verificationRequirements}
onVerificate={(verificated) => {
shotConfettiStars({ withStars: verificated })
onClose()
}}
/>
</ModalFooter>

<VerificatePlaceVisitModal
isOpen={isVerificateModalOpen}
onOpenChange={onVerificateModalOpenChange}
expectedLocation={place.location}
placeId={place.id}
onVerificate={(verificated) => {
shotConfettiStars({ withStars: verificated })
onClose()
}}
isAlreadyVisited={place.missionStatus.visited}
verificationRequirements={place.verificationRequirements}
/>
</>
) : (
<ModalBody className="">
Expand Down
81 changes: 81 additions & 0 deletions src/app/[locale]/(app)/missions/_components/verificate-button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
'use client'

import { Button, useDisclosure } from '@nextui-org/react'
import { IconDiscountCheckFilled } from '@tabler/icons-react'
import { useTranslations } from 'next-intl'
import { FC } from 'react'
import { MapPoint } from '~/helpers/spatial-data'
import { VerificationRequirements } from '~/server/db/constants/verifications'
import {
OnVerificate,
VerificatePlaceVisitModal,
} from './verificate-place-visit-modal'

export type VerificateButtonProps = {
expectedLocation: MapPoint
placeId: number
isAlreadyVisited: boolean
isAlreadyVerified: boolean
verificationRequirements: VerificationRequirements | null
className?: string
onVerificate?: OnVerificate
hideIfDone?: boolean
}

export const VerificateButton: FC<VerificateButtonProps> = ({
placeId,
expectedLocation,
isAlreadyVisited,
isAlreadyVerified,
verificationRequirements,
className,
onVerificate,
hideIfDone,
}) => {
const t = useTranslations('missions')
const {
isOpen: isVerificateModalOpen,
onOpen: onVerificateModalOpen,
onOpenChange: onVerificateModalOpenChange,
} = useDisclosure()
const isVerificationRequired =
verificationRequirements && verificationRequirements.isLocationRequired

return (
<>
{(isVerificationRequired ? isAlreadyVerified : isAlreadyVisited) ? (
!hideIfDone && (
<Button
fullWidth
color="primary"
isDisabled
startContent={<IconDiscountCheckFilled size={20} />}
className={className}
>
{t('visit-already-verified')}
</Button>
)
) : (
<Button
color="primary"
fullWidth
onPress={onVerificateModalOpen}
startContent={<IconDiscountCheckFilled size={20} />}
className={className}
>
{t('verificate-visit')}
</Button>
)}

<VerificatePlaceVisitModal
isOpen={isVerificateModalOpen}
onOpenChange={onVerificateModalOpenChange}
expectedLocation={expectedLocation}
placeId={placeId}
onVerificate={onVerificate}
isAlreadyVisited={isAlreadyVisited}
verificationRequirements={verificationRequirements}
/>
</>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import {
import { trpc } from '~/trpc'
import { useLocationValidator } from '../_hooks/useLocationValidator'

type OnVerificate = (
export type OnVerificate = (
hasBeenVerified: boolean,
verificationData: {
location: MapPoint | null
Expand All @@ -46,7 +46,7 @@ type OnVerificate = (

export const VerificatePlaceVisitModal: FC<
Omit<ModalProps, 'children'> & {
onVerificate: OnVerificate
onVerificate?: OnVerificate
expectedLocation: MapPoint
placeId: number
isAlreadyVisited: boolean
Expand Down Expand Up @@ -92,8 +92,8 @@ export const VerificatePlaceVisitModal: FC<
})
}

await onVerificate?.(hasBeenVerified, verificationData)
router.refresh()
await onVerificate(hasBeenVerified, verificationData)
}

const handleSubmit = async ({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,7 @@ export const VisitMissionsAcordion: FC<{
<PlaceCategoryIconWithProgress
icon={category.icon}
progress={
places.filter((place) => place.missionStatus.visited).length /
places.length
places.filter((place) => place.visited).length / places.length
}
color={category.color}
label={category.namePlural}
Expand Down Expand Up @@ -91,13 +90,13 @@ export const VisitMissionsAcordion: FC<{
onOpen()
}}
>
{place.missionStatus.verified ? (
{place.verifications.length > 0 ? (
<IconDiscountCheckFilled
size={24}
className="text-blue-400"
aria-label={t('verified')}
/>
) : place.missionStatus.visited ? (
) : place.visited ? (
<IconCircleCheckFilled
size={24}
className="text-blue-400"
Expand Down
47 changes: 26 additions & 21 deletions src/server/api/router/missions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,18 @@ const getVisitMissions = flattenTranslationsOnExecute(
},
}),
verifications: {
columns: {
id: true,
validatedOn: true,
},
orderBy: (verifications, { desc }) => [
desc(verifications.validatedOn),
],
where: (verification, { or, isNull, eq }) =>
or(
isNull(sql.placeholder('userId')),
eq(verification.userId, sql.placeholder('userId'))
),
limit: 1,
},
verificationRequirements: true,
Expand Down Expand Up @@ -129,9 +138,18 @@ const getVisitMissions = flattenTranslationsOnExecute(
},
}),
verifications: {
columns: {
id: true,
validatedOn: true,
},
orderBy: (verifications, { desc }) => [
desc(verifications.validatedOn),
],
where: (verification, { or, isNull, eq }) =>
or(
isNull(sql.placeholder('userId')),
eq(verification.userId, sql.placeholder('userId'))
),
limit: 1,
},
verificationRequirements: true,
Expand All @@ -150,6 +168,7 @@ export const missionsRouter = router({
const result = await getVisitMissions.execute({
locale: input.locale,
placeId: input.placeId,
userId: ctx.session?.user.id,
})

const visitedPlacesIds = await getVisitedPlacesIdsByUserId(
Expand All @@ -166,27 +185,13 @@ export const missionsRouter = router({
...places
.map(({ place }) => place)
.filter((place) => !mainPlacesIds.includes(place.id)),
].map(({ location, categories, verifications, ...place }) => {
const hasBeenVisited = visitedPlacesIds.has(place.id)
const lastVerification =
verifications.length > 0 ? verifications[0] : null
const isVerificationRequired =
place.verificationRequirements &&
place.verificationRequirements.isLocationRequired
return {
...place,
location: getPoint(location),
categories: categories.map(({ category }) => category),
images: [],
missionStatus: {
visited: hasBeenVisited,
verified: isVerificationRequired
? Boolean(lastVerification)
: hasBeenVisited,
},
lastVerification,
}
}),
].map(({ location, categories, ...place }) => ({
...place,
location: getPoint(location),
categories: categories.map(({ category }) => category),
images: [],
visited: visitedPlacesIds.has(place.id),
})),
}
})
.filter(({ places }) => places.length > 0)
Expand Down
Loading

0 comments on commit f6237c8

Please sign in to comment.