Skip to content

Commit

Permalink
Release 2024-04-08 (#97)
Browse files Browse the repository at this point in the history
  • Loading branch information
JohannaPeanut authored Apr 8, 2024
2 parents 2026576 + 5ef5dd4 commit a8775e6
Show file tree
Hide file tree
Showing 11 changed files with 400 additions and 24 deletions.
12 changes: 10 additions & 2 deletions src/core/components/Map/SurveyStaticPin.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
import clsx from "clsx"
import * as React from "react"

const SurveyStaticPin: React.FC = () => {
type Props = {
light?: boolean
}

const SurveyStaticPin: React.FC<Props> = ({ light }) => {
return (
<svg width="38" height="48" viewBox="0 0 38 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M19 0C29.4934 0 38 8.82644 38 19.7144C38 22.3298 36.7984 25.629 34.5493 29.4168C32.8863 32.2175 30.7033 35.2017 28.1269 38.2825C25.9191 40.9225 23.5498 43.484 21.1804 45.8647L19.984 47.0508L19 48L18.016 47.0508C17.6338 46.6771 17.2342 46.2812 16.8196 45.8647C14.4502 43.484 12.0809 40.9225 9.87312 38.2825C7.2967 35.2017 5.11365 32.2175 3.4507 29.4168C1.20161 25.629 0 22.3298 0 19.7144C0 8.82644 8.50659 0 19 0Z"
className="fill-[var(--survey-primary-color)]"
className={clsx(
light ? "fill-[var(--survey-light-color)]" : "fill-[var(--survey-primary-color)]",
light && "opacity-70 stroke-white",
)}
/>
<path
d="M14 20.3846L17.4737 25L25 15"
Expand Down
7 changes: 6 additions & 1 deletion src/core/layouts/Navigation/NavigationProject/menuItems.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Routes } from "@blitzjs/next"
import { Project } from "@prisma/client"

type Props = {
projectSlug: string
Expand All @@ -25,6 +24,12 @@ export const menuItems = ({ projectSlug, projectName }: Props) => {
alsoHighlightPathnames: [
// Any survey page gets highlighted
Routes.SurveyPage({ projectSlug: projectSlug!, surveyId: 999 }).pathname,
Routes.SurveyResponsePage({ projectSlug: projectSlug!, surveyId: 999 }).pathname,
Routes.SurveyResponseWithLocationPage({
projectSlug: projectSlug!,
surveyId: 999,
surveyResponseId: 9999,
}).pathname,
],
},
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export const SurveyResponse = () => {
const params = useRouterQuery()
const paramsStakeholderDetails = parseInt(String(params.responseDetails))
const accordionRefs = useRef<Array<HTMLDivElement | null>>([])

useEffect(() => {
if (paramsStakeholderDetails) {
const currentRef = accordionRefs.current?.at(paramsStakeholderDetails)
Expand Down Expand Up @@ -84,6 +85,8 @@ export const SurveyResponse = () => {
ref={(element) => (accordionRefs.current[response.id] = element)}
>
<EditableSurveyResponseListItem
showMap
isAccordion
response={response}
operators={operators}
topics={topics}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import { BlitzPage, useParam } from "@blitzjs/next"
import { useQuery } from "@blitzjs/rpc"
import { Suspense } from "react"
import { Spinner } from "src/core/components/Spinner"
import { PageHeader } from "src/core/components/pages/PageHeader"
import { H2 } from "src/core/components/text"
import { useSlugs } from "src/core/hooks"
import { LayoutRs, MetaTags } from "src/core/layouts"
import getOperatorsWithCount from "src/operators/queries/getOperatorsWithCount"
import getSubsections from "src/subsections/queries/getSubsections"
import { TMapProps } from "src/survey-public/components/types"
import {
getFeedbackDefinitionBySurveySlug,
getResponseConfigBySurveySlug,
} from "src/survey-public/utils/getConfigBySurveySlug"
import getSurveyResponseTopicsByProject from "src/survey-response-topics/queries/getSurveyResponseTopicsByProject"
import EditableSurveyResponseListItem from "src/survey-responses/components/feedback/EditableSurveyResponseListItem"
import { SurveyFeedbackWithLocationOverviewMap } from "src/survey-responses/components/feedback/SurveyFeedbackWithLocationOverviewMap"

import getFeedbackResponsesWithLocation from "src/survey-responses/queries/getFeedbackSurveyResponsesWithLocation"
import { SurveyTabs } from "src/surveys/components/SurveyTabs"
import getSurvey from "src/surveys/queries/getSurvey"

export const SurveyResponseWithLocation = () => {
const { projectSlug, subsectionSlug } = useSlugs()
const surveyId = useParam("surveyId", "number")
const surveyResponseId = useParam("surveyResponseId", "number")

const [survey] = useQuery(getSurvey, { id: surveyId })
const [{ surveyResponsesFeedbackPartWithLocation }] = useQuery(getFeedbackResponsesWithLocation, {
projectSlug,
surveyId: survey.id,
})
const [{ operators }] = useQuery(getOperatorsWithCount, { projectSlug })
const [{ surveyResponseTopics: topics }, { refetch: refetchTopics }] = useQuery(
getSurveyResponseTopicsByProject,
{
projectSlug: projectSlug!,
},
)
const [{ subsections }] = useQuery(getSubsections, {
projectSlug: projectSlug!,
subsectionSlug: subsectionSlug!,
})

const { evaluationRefs } = getResponseConfigBySurveySlug(survey.slug)
const feedbackDefinition = getFeedbackDefinitionBySurveySlug(survey.slug)

const locationRef = evaluationRefs["feedback-location"]

const mapProps = feedbackDefinition!.pages[0]!.questions.find((q) => q.id === locationRef)!
.props as TMapProps

const maptilerStyleUrl = mapProps.maptilerStyleUrl
const defaultViewState = mapProps?.config?.bounds

if (!surveyResponsesFeedbackPartWithLocation?.length) return

const selectedSurveyResponse = surveyResponsesFeedbackPartWithLocation.find(
(r) => r.id === surveyResponseId,
)

if (!selectedSurveyResponse) return

return (
<>
<MetaTags noindex title={`Beteiligung ${survey.title}`} />
<PageHeader title={survey.title} className="mt-12" description={<SurveyTabs />} />

<div className="mt-12 space-y-4">
<H2>Beiträge mit Ortsangabe </H2>

<div className="flex flex-col lg:flex-row gap-2">
<section className="lg:w-[46%] shrink-0">
<SurveyFeedbackWithLocationOverviewMap
maptilerStyleUrl={maptilerStyleUrl}
defaultViewState={defaultViewState}
selectedSurveyResponse={selectedSurveyResponse}
surveyResponsesFeedbackPartWithLocation={surveyResponsesFeedbackPartWithLocation}
locationRef={locationRef!}
/>
</section>
<section className="rounded-md drop-shadow-md">
<EditableSurveyResponseListItem
key={selectedSurveyResponse?.id}
response={selectedSurveyResponse!}
operators={operators}
topics={topics}
subsections={subsections}
refetchResponsesAndTopics={refetchTopics}
/>
</section>
</div>
</div>
</>
)
}

const SurveyResponseWithLocationPage: BlitzPage = () => {
return (
<LayoutRs>
<Suspense fallback={<Spinner page />}>
<SurveyResponseWithLocation />
</Suspense>
</LayoutRs>
)
}

SurveyResponseWithLocationPage.authenticate = true

export default SurveyResponseWithLocationPage
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ export function EditableSurveyResponseFilterForm<S extends z.ZodType<any, any>>(
onBlur={async () => await methods.handleSubmit(handleSubmit)()}
>
<div className="w-[300px]">
<p className="font-semibold mb-3">Freitextsuche</p>
<LabeledTextField
name="searchterm"
label=""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
LabeledTextField,
LabeledTextareaField,
} from "src/core/components/forms"
import { blueButtonStyles } from "src/core/components/links"
import { Link, blueButtonStyles } from "src/core/components/links"
import { useSlugs } from "src/core/hooks"
import createSurveyResponseTopicsOnSurveyResponses from "src/survey-response-topics-on-survey-responses/mutations/createSurveyResponseTopicsOnSurveyResponses"
import deleteSurveyResponseTopicsOnSurveyResponses from "src/survey-response-topics-on-survey-responses/mutations/deleteSurveyResponseTopicsOnSurveyResponses"
Expand All @@ -22,6 +22,7 @@ import updateSurveyResponse from "../../mutations/updateSurveyResponse"
import { EditableSurveyResponseFormMap } from "./EditableSurveyResponseFormMap"
import { EditableSurveyResponseListItemProps } from "./EditableSurveyResponseListItem"
import { surveyResponseStatus } from "./surveyResponseStatus"
import { Routes, useParam } from "@blitzjs/next"

type FormProps<S extends z.ZodType<any, any>> = Omit<
PropsWithoutRef<JSX.IntrinsicElements["form"]>,
Expand All @@ -33,6 +34,7 @@ type FormProps<S extends z.ZodType<any, any>> = Omit<
userLocationQuestionId: number | undefined
maptilerStyleUrl: string
defaultViewState: LngLatBoundsLike
showMap?: boolean
} & Pick<EditableSurveyResponseListItemProps, "response" | "operators" | "topics" | "subsections">

export const FORM_ERROR = "FORM_ERROR"
Expand All @@ -48,6 +50,7 @@ export function EditableSurveyResponseForm<S extends z.ZodType<any, any>>({
userLocationQuestionId,
initialValues,
refetchResponsesAndTopics,
showMap,
}: FormProps<S>) {
const router = useRouter()
const methods = useForm<z.infer<S>>({
Expand All @@ -57,6 +60,7 @@ export function EditableSurveyResponseForm<S extends z.ZodType<any, any>>({
})

const { projectSlug } = useSlugs()
const surveyId = useParam("surveyId", "string")

const [updateSurveyResponseMutation] = useMutation(updateSurveyResponse)
const [surveyResponseTopicsOnSurveyResponsesMutation] = useMutation(
Expand Down Expand Up @@ -157,7 +161,7 @@ export function EditableSurveyResponseForm<S extends z.ZodType<any, any>>({

return (
<FormProvider {...methods}>
<div className="grid grid-cols-2 gap-8">
<div className={clsx(showMap ? "grid-cols-2" : "grid-cols-1", "grid gap-8")}>
<div>
<div className="flex flex-col justify-between col-span-2">
<div className="flex flex-col w-full gap-10">
Expand Down Expand Up @@ -203,16 +207,34 @@ export function EditableSurveyResponseForm<S extends z.ZodType<any, any>>({
</div>
</div>

<div>
<EditableSurveyResponseFormMap
marker={
{showMap && (
<div>
<EditableSurveyResponseFormMap
marker={
// @ts-expect-error `data` is unkown
response.data[userLocationQuestionId] as { lat: number; lng: number } | undefined
}
maptilerStyleUrl={maptilerStyleUrl}
defaultViewState={defaultViewState}
/>
{
// @ts-expect-error `data` is unkown
response.data[userLocationQuestionId] as { lat: number; lng: number } | undefined
response.data[userLocationQuestionId] && (
<div className="pt-4">
<Link
href={Routes.SurveyResponseWithLocationPage({
projectSlug: projectSlug!,
surveyId: surveyId!,
surveyResponseId: response.id,
})}
>
Alle verorteten Beiträge öffnen
</Link>
</div>
)
}
maptilerStyleUrl={maptilerStyleUrl}
defaultViewState={defaultViewState}
/>
</div>
</div>
)}

<form
className="flex"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,23 +32,29 @@ export type EditableSurveyResponseListItemProps = {
topics: Prettify<
Awaited<ReturnType<typeof getSurveyResponseTopicsByProject>>["surveyResponseTopics"]
>
isAccordion?: boolean
subsections: SubsectionWithPosition[]
refetchResponsesAndTopics: () => void
showMap?: boolean
}

const EditableSurveyResponseListItem: React.FC<EditableSurveyResponseListItemProps> = ({
response,
operators,
topics,
subsections,
isAccordion,
refetchResponsesAndTopics,
showMap,
}) => {
const router = useRouter()

const params = useRouterQuery()
const open = parseInt(String(params.responseDetails)) === response.id
const open = !isAccordion ? true : parseInt(String(params.responseDetails)) === response.id
const surveyId = useParam("surveyId", "string")
const [survey] = useQuery(getSurvey, { id: Number(surveyId) })

const [deleteCalendarEntryMutation] = useMutation(deleteSurveyResponse)

useEffect(() => {
const surveyDefinition = getSurveyDefinitionBySurveySlug(survey.slug)
const root = document.documentElement
Expand All @@ -57,8 +63,6 @@ const EditableSurveyResponseListItem: React.FC<EditableSurveyResponseListItemPro
root.style.setProperty("--survey-light-color", surveyDefinition.lightColor)
}, [survey.slug])

const [deleteCalendarEntryMutation] = useMutation(deleteSurveyResponse)

const handleOpen = () => {
router.query.responseDetails = String(response.id)
void router.push({ query: router.query }, undefined, { scroll: false })
Expand Down Expand Up @@ -125,13 +129,13 @@ const EditableSurveyResponseListItem: React.FC<EditableSurveyResponseListItemPro
}

return (
<article data-open={open}>
<article data-open={open} className="bg-white">
<button
className={clsx(
"py-4 text-left text-sm text-gray-900 hover:bg-gray-50 group flex w-full items-center justify-between pr-4 focus:outline-none focus-visible:ring focus-visible:ring-gray-500 focus-visible:ring-opacity-75 sm:pr-6",
open ? "bg-gray-50" : "border-b border-gray-300",
)}
onClick={() => (open ? handleClose() : handleOpen())}
onClick={isAccordion ? () => (open ? handleClose() : handleOpen()) : undefined}
>
<div className="gap-4 flex items-center px-6 pb-2 pt-3">
<h3 className="text-gray-700">{response.id} </h3>
Expand All @@ -148,11 +152,12 @@ const EditableSurveyResponseListItem: React.FC<EditableSurveyResponseListItemPro
<Markdown className="ml-4 line-clamp-2" markdown={userTextPreview} />
</div>

{open ? (
<ChevronUpIcon className="h-5 w-5 text-gray-700 group-hover:text-black flex-shrink-0" />
) : (
<ChevronDownIcon className="h-5 w-5 text-gray-700 group-hover:text-black flex-shrink-0" />
)}
{isAccordion &&
(open ? (
<ChevronUpIcon className="h-5 w-5 text-gray-700 group-hover:text-black flex-shrink-0" />
) : (
<ChevronDownIcon className="h-5 w-5 text-gray-700 group-hover:text-black flex-shrink-0" />
))}
</button>

{open && (
Expand Down Expand Up @@ -185,6 +190,7 @@ const EditableSurveyResponseListItem: React.FC<EditableSurveyResponseListItemPro
</div>
</div>
<EditableSurveyResponseForm
showMap={showMap}
initialValues={{
...response,
// Mapping to string is required so the ReactHookForm and our Radiobutton can compare the data to find what is selected
Expand Down
Loading

0 comments on commit a8775e6

Please sign in to comment.