diff --git a/packages/shared/src/graphql/guessWhoQuiz.ts b/packages/shared/src/graphql/guessWhoQuiz.ts new file mode 100644 index 0000000000..d75f0a0545 --- /dev/null +++ b/packages/shared/src/graphql/guessWhoQuiz.ts @@ -0,0 +1,48 @@ +import { gql } from 'graphql-request'; +import { gqlClient } from './common'; + +export interface GuessWhoQuizQAInput { + question: string; + answer: string; +} + +export interface GuessWhoQuizNextQuestion { + question: string; + options: string[]; +} + +export interface GuessWhoQuizFinalPersona { + name: string; + description: string; + tags: string[]; +} + +export interface GuessWhoQuizStepResult { + nextQuestion: GuessWhoQuizNextQuestion | null; + finalPersona: GuessWhoQuizFinalPersona | null; +} + +export const GUESS_WHO_QUIZ_STEP_MUTATION = gql` + mutation GuessWhoQuizStep($history: [GuessWhoQuizQAInput!]!) { + guessWhoQuizStep(history: $history) { + nextQuestion { + question + options + } + finalPersona { + name + description + tags + } + } + } +`; + +export const requestGuessWhoQuizStep = async ( + history: GuessWhoQuizQAInput[], +): Promise => { + const res = await gqlClient.request<{ + guessWhoQuizStep: GuessWhoQuizStepResult; + }>(GUESS_WHO_QUIZ_STEP_MUTATION, { history }); + return res.guessWhoQuizStep; +}; diff --git a/packages/shared/src/lib/log.ts b/packages/shared/src/lib/log.ts index 27c92db001..50ee443fa1 100644 --- a/packages/shared/src/lib/log.ts +++ b/packages/shared/src/lib/log.ts @@ -97,6 +97,7 @@ export enum Origin { ProfileStack = 'profile stack', BrandedTag = 'branded tag', MentionedTool = 'mentioned tool', + GuessWhoQuiz = 'guess who quiz', } export enum LogEvent { @@ -307,6 +308,11 @@ export enum LogEvent { StartAiFluencyQuiz = 'start ai fluency quiz', CompleteAiFluencyQuiz = 'complete ai fluency quiz', ShareAiFluencyQuiz = 'share ai fluency quiz', + // Guess who quiz + StartGuessWhoQuiz = 'start guess who quiz', + AnswerGuessWhoQuestion = 'answer guess who question', + AnswerGuessWhoLlmQuestion = 'answer guess who llm question', + CompleteGuessWhoQuiz = 'complete guess who quiz', // Plus subscription UpgradeSubscription = 'upgrade subscription', ManageSubscription = 'manage subscription', diff --git a/packages/webapp/components/guess-who/GuessWhoQuiz.tsx b/packages/webapp/components/guess-who/GuessWhoQuiz.tsx new file mode 100644 index 0000000000..98eb1b1e83 --- /dev/null +++ b/packages/webapp/components/guess-who/GuessWhoQuiz.tsx @@ -0,0 +1,131 @@ +import type { ReactElement } from 'react'; +import React, { + useCallback, + useEffect, + useMemo, + useRef, + useState, +} from 'react'; +import { AnimatePresence } from 'framer-motion'; +import { useLogContext } from '@dailydotdev/shared/src/contexts/LogContext'; +import { LogEvent, Origin } from '@dailydotdev/shared/src/lib/log'; +import type { GuessWhoQuizQAInput } from '@dailydotdev/shared/src/graphql/guessWhoQuiz'; +import { LlmPhase } from './LlmPhase'; +import { QuestionCard } from './QuestionCard'; +import { + FIRST_QUESTION_ID, + TOTAL_VISIBLE_STEPS, + getNextQuestionId, + questions, +} from './questions'; + +const buildInitialHistory = ( + orderedQuestionIds: string[], + answers: Record, +): GuessWhoQuizQAInput[] => + orderedQuestionIds + .map((qid) => { + const question = questions[qid]; + const optionId = answers[qid]; + const option = question?.options.find((opt) => opt.id === optionId); + if (!question || !option) { + return null; + } + return { question: question.prompt, answer: option.label }; + }) + .filter((entry): entry is GuessWhoQuizQAInput => entry !== null); + +export const GuessWhoQuiz = (): ReactElement => { + const { logEvent } = useLogContext(); + const [currentId, setCurrentId] = useState(FIRST_QUESTION_ID); + const [answers, setAnswers] = useState>({}); + const [history, setHistory] = useState([]); + const [isComplete, setIsComplete] = useState(false); + const didLogStart = useRef(false); + + useEffect(() => { + if (didLogStart.current) { + return; + } + didLogStart.current = true; + logEvent({ + event_name: LogEvent.StartGuessWhoQuiz, + origin: Origin.GuessWhoQuiz, + }); + }, [logEvent]); + + const reset = useCallback(() => { + setCurrentId(FIRST_QUESTION_ID); + setAnswers({}); + setHistory([]); + setIsComplete(false); + }, []); + + const handleSelect = useCallback( + (optionId: string) => { + const question = questions[currentId]; + if (!question) { + throw new Error(`Unknown question id "${currentId}"`); + } + logEvent({ + event_name: LogEvent.AnswerGuessWhoQuestion, + target_id: currentId, + origin: Origin.GuessWhoQuiz, + extra: JSON.stringify({ optionId }), + }); + const nextId = getNextQuestionId(question, optionId); + setAnswers((prev) => ({ ...prev, [currentId]: optionId })); + + if (!nextId) { + setIsComplete(true); + return; + } + + setHistory((prev) => [...prev, currentId]); + setCurrentId(nextId); + }, + [currentId, logEvent], + ); + + const handleBack = useCallback(() => { + setHistory((prev) => { + if (prev.length === 0) { + return prev; + } + const next = prev.slice(0, -1); + const previousId = prev[prev.length - 1]; + setCurrentId(previousId); + setIsComplete(false); + return next; + }); + }, []); + + const initialLlmHistory = useMemo( + () => + isComplete ? buildInitialHistory([...history, currentId], answers) : [], + [isComplete, history, currentId, answers], + ); + + if (isComplete) { + return ; + } + + const currentQuestion = questions[currentId]; + if (!currentQuestion) { + throw new Error(`Unknown question id "${currentId}"`); + } + + return ( + + 0 ? handleBack : undefined} + /> + + ); +}; diff --git a/packages/webapp/components/guess-who/LlmPhase.tsx b/packages/webapp/components/guess-who/LlmPhase.tsx new file mode 100644 index 0000000000..e4cc107c10 --- /dev/null +++ b/packages/webapp/components/guess-who/LlmPhase.tsx @@ -0,0 +1,104 @@ +import type { ReactElement } from 'react'; +import React, { useCallback } from 'react'; +import { motion } from 'framer-motion'; +import { + Button, + ButtonSize, + ButtonVariant, +} from '@dailydotdev/shared/src/components/buttons/Button'; +import { Loader } from '@dailydotdev/shared/src/components/Loader'; +import { useLogContext } from '@dailydotdev/shared/src/contexts/LogContext'; +import { LogEvent, Origin } from '@dailydotdev/shared/src/lib/log'; +import type { GuessWhoQuizQAInput } from '@dailydotdev/shared/src/graphql/guessWhoQuiz'; +import { useGuessWhoQuiz } from './useGuessWhoQuiz'; +import { LlmQuestionCard } from './LlmQuestionCard'; +import { PersonaResult } from './PersonaResult'; + +interface LlmPhaseProps { + initialHistory: GuessWhoQuizQAInput[]; + onRestart: () => void; +} + +export const LlmPhase = ({ + initialHistory, + onRestart, +}: LlmPhaseProps): ReactElement => { + const { logEvent } = useLogContext(); + const { nextQuestion, persona, isPending, error, submitAnswer, retry } = + useGuessWhoQuiz(initialHistory); + + const onSelectAnswer = useCallback( + (question: string, answer: string) => { + logEvent({ + event_name: LogEvent.AnswerGuessWhoLlmQuestion, + origin: Origin.GuessWhoQuiz, + extra: JSON.stringify({ answer }), + }); + submitAnswer(question, answer); + }, + [logEvent, submitAnswer], + ); + + if (persona) { + return ; + } + + if (error) { + return ( + +

+ Something glitched +

+

+ We couldn't reach our developer-vibes oracle. Try again? +

+
+ + +
+
+ ); + } + + if (nextQuestion && !isPending) { + return ( + onSelectAnswer(nextQuestion.question, answer)} + /> + ); + } + + return ( + + +

+ Cooking up your developer personaโ€ฆ +

+
+ ); +}; diff --git a/packages/webapp/components/guess-who/LlmQuestionCard.tsx b/packages/webapp/components/guess-who/LlmQuestionCard.tsx new file mode 100644 index 0000000000..14e438b511 --- /dev/null +++ b/packages/webapp/components/guess-who/LlmQuestionCard.tsx @@ -0,0 +1,41 @@ +import type { ReactElement } from 'react'; +import React from 'react'; +import { motion } from 'framer-motion'; + +interface LlmQuestionCardProps { + question: string; + options: string[]; + onSelect: (answer: string) => void; +} + +export const LlmQuestionCard = ({ + question, + options, + onSelect, +}: LlmQuestionCardProps): ReactElement => ( + +

One more thingโ€ฆ

+

+ {question} +

+
+ {options.map((option) => ( + + ))} +
+
+); diff --git a/packages/webapp/components/guess-who/PersonaResult.tsx b/packages/webapp/components/guess-who/PersonaResult.tsx new file mode 100644 index 0000000000..e203346383 --- /dev/null +++ b/packages/webapp/components/guess-who/PersonaResult.tsx @@ -0,0 +1,77 @@ +import type { ReactElement } from 'react'; +import React, { useEffect, useRef } from 'react'; +import { motion } from 'framer-motion'; +import { + Button, + ButtonSize, + ButtonVariant, +} from '@dailydotdev/shared/src/components/buttons/Button'; +import { useLogContext } from '@dailydotdev/shared/src/contexts/LogContext'; +import { LogEvent } from '@dailydotdev/shared/src/lib/log'; +import type { GuessWhoQuizFinalPersona } from '@dailydotdev/shared/src/graphql/guessWhoQuiz'; + +interface PersonaResultProps { + persona: GuessWhoQuizFinalPersona; + onRestart: () => void; +} + +export const PersonaResult = ({ + persona, + onRestart, +}: PersonaResultProps): ReactElement => { + const { logEvent } = useLogContext(); + const didLog = useRef(false); + + useEffect(() => { + if (didLog.current) { + return; + } + didLog.current = true; + logEvent({ + event_name: LogEvent.CompleteGuessWhoQuiz, + extra: JSON.stringify({ + persona: persona.name, + tagCount: persona.tags.length, + }), + }); + }, [logEvent, persona.name, persona.tags.length]); + + return ( + +
+

You are aโ€ฆ

+

+ {persona.name} +

+

{persona.description}

+
+ + {persona.tags.length > 0 && ( +
    + {persona.tags.map((tag) => ( +
  • + #{tag} +
  • + ))} +
+ )} + + +
+ ); +}; diff --git a/packages/webapp/components/guess-who/QuestionCard.tsx b/packages/webapp/components/guess-who/QuestionCard.tsx new file mode 100644 index 0000000000..0d25fd16bf --- /dev/null +++ b/packages/webapp/components/guess-who/QuestionCard.tsx @@ -0,0 +1,103 @@ +import type { ReactElement } from 'react'; +import React from 'react'; +import classNames from 'classnames'; +import { motion } from 'framer-motion'; +import { + Button, + ButtonSize, + ButtonVariant, +} from '@dailydotdev/shared/src/components/buttons/Button'; +import { ArrowIcon } from '@dailydotdev/shared/src/components/icons/Arrow'; +import type { Question } from './questions'; + +interface QuestionCardProps { + question: Question; + selectedOptionId?: string; + step: number; + totalSteps: number; + onSelect: (optionId: string) => void; + onBack?: () => void; +} + +export const QuestionCard = ({ + question, + selectedOptionId, + step, + totalSteps, + onSelect, + onBack, +}: QuestionCardProps): ReactElement => ( + +
+

+ Question {step} of {totalSteps} +

+
+ {Array.from({ length: totalSteps }, (_, index) => index).map( + (index) => ( + + ), + )} +
+
+ +

+ {question.prompt} +

+ +
+ {question.options.map((option) => { + const selected = selectedOptionId === option.id; + return ( + + ); + })} +
+ + {onBack && ( +
+ +
+ )} +
+); diff --git a/packages/webapp/components/guess-who/questions.ts b/packages/webapp/components/guess-who/questions.ts new file mode 100644 index 0000000000..4a8f49fab7 --- /dev/null +++ b/packages/webapp/components/guess-who/questions.ts @@ -0,0 +1,381 @@ +export type PersonaTag = + | 'frontend' + | 'backend' + | 'systems' + | 'mobile' + | 'junior' + | 'mid' + | 'senior' + | 'veteran' + | 'tailwind' + | 'css-purist' + | 'type-safe-styles' + | 'pragmatic' + | 'systems-leaning' + | 'enterprise' + | 'classic-stack' + | 'whiteboard' + | 'excalidraw' + | 'mermaid' + | 'powerpoint' + | 'ios' + | 'android' + | 'cross-platform' + | 'game-engines' + | 'ai-curious' + | 'ai-bff' + | 'ai-skeptic' + | 'ai-allergic'; + +export interface QuestionOption { + id: string; + label: string; + emoji?: string; + tags: PersonaTag[]; + next?: string | null; +} + +export interface Question { + id: string; + prompt: string; + options: QuestionOption[]; + defaultNext?: string | null; +} + +export const FIRST_QUESTION_ID = 'q1_domain'; +export const TOTAL_VISIBLE_STEPS = 5; + +export const questions: Record = { + q1_domain: { + id: 'q1_domain', + prompt: 'Where do you spend most of your dev hours?', + options: [ + { + id: 'frontend', + emoji: '๐ŸŽจ', + label: 'Pixel-perfecting UIs and arguing about CSS', + tags: ['frontend'], + next: 'q2_frontend', + }, + { + id: 'backend', + emoji: 'โš™๏ธ', + label: 'Wrangling APIs, queues, and angry databases', + tags: ['backend'], + next: 'q2_backend', + }, + { + id: 'systems', + emoji: '๐Ÿงฑ', + label: 'Drawing boxes and arrows nobody reads', + tags: ['systems'], + next: 'q2_systems', + }, + { + id: 'mobile', + emoji: '๐Ÿ“ฑ', + label: 'Building things that move (mobile, games, embedded)', + tags: ['mobile'], + next: 'q2_mobile', + }, + ], + }, + q2_frontend: { + id: 'q2_frontend', + prompt: 'Pick your CSS personality', + defaultNext: 'q3_experience', + options: [ + { + id: 'tailwind', + emoji: '๐Ÿ’จ', + label: 'Tailwind utility maximalist', + tags: ['tailwind'], + }, + { + id: 'vanilla', + emoji: '๐ŸŒฟ', + label: 'Vanilla CSS โ€” hand-crafted, organic', + tags: ['css-purist'], + }, + { + id: 'css-in-js', + emoji: '๐Ÿงช', + label: "CSS-in-JS, types or it didn't happen", + tags: ['type-safe-styles'], + }, + { + id: 'whatever-works', + emoji: '๐Ÿคท', + label: '"It works in my browser"', + tags: ['pragmatic'], + }, + ], + }, + q2_backend: { + id: 'q2_backend', + prompt: 'Pick your weapon of choice', + defaultNext: 'q3_experience', + options: [ + { + id: 'go-rust', + emoji: '๐Ÿฆ€', + label: 'Go / Rust โ€” fast and angry', + tags: ['systems-leaning'], + }, + { + id: 'node-python', + emoji: '๐Ÿ', + label: 'Node / Python โ€” gets stuff done', + tags: ['pragmatic'], + }, + { + id: 'java-csharp', + emoji: 'โ˜•', + label: 'Java / C# โ€” enterprise mode activated', + tags: ['enterprise'], + }, + { + id: 'php-ruby', + emoji: '๐Ÿ’Ž', + label: 'PHP / Ruby โ€” vintage but vibing', + tags: ['classic-stack'], + }, + ], + }, + q2_systems: { + id: 'q2_systems', + prompt: 'Your favorite diagram tool?', + defaultNext: 'q3_experience', + options: [ + { + id: 'whiteboard', + emoji: '๐Ÿ–Š๏ธ', + label: 'Whiteboard markers and prayer', + tags: ['whiteboard'], + }, + { + id: 'excalidraw', + emoji: 'โœ๏ธ', + label: 'Excalidraw โ€” beautiful chaos', + tags: ['excalidraw'], + }, + { + id: 'mermaid', + emoji: '๐Ÿงœ', + label: 'Mermaid in markdown โ€” text-based fundamentalist', + tags: ['mermaid'], + }, + { + id: 'powerpoint', + emoji: '๐Ÿ“Š', + label: 'PowerPoint, because someone has to read it', + tags: ['powerpoint'], + }, + ], + }, + q2_mobile: { + id: 'q2_mobile', + prompt: 'Pick your battlefield', + defaultNext: 'q3_experience', + options: [ + { + id: 'ios', + emoji: '๐ŸŽ', + label: 'iOS โ€” Swift and stubborn', + tags: ['ios'], + }, + { + id: 'android', + emoji: '๐Ÿค–', + label: 'Android โ€” Kotlin and chaos', + tags: ['android'], + }, + { + id: 'cross-platform', + emoji: '๐ŸŒ', + label: 'Cross-platform (Flutter / React Native)', + tags: ['cross-platform'], + }, + { + id: 'game-engines', + emoji: '๐ŸŽฎ', + label: 'Game engines (Unity / Unreal)', + tags: ['game-engines'], + }, + ], + }, + q3_experience: { + id: 'q3_experience', + prompt: 'How long have you been at this?', + options: [ + { + id: 'junior', + emoji: '๐Ÿฃ', + label: 'Under a year โ€” still wide-eyed', + tags: ['junior'], + next: 'q4_junior', + }, + { + id: 'mid', + emoji: '๐Ÿง‘โ€๐Ÿ’ป', + label: 'A few years deep', + tags: ['mid'], + next: 'q4_mid', + }, + { + id: 'senior', + emoji: '๐Ÿง™', + label: 'Senior โ€” saw the rise of microservices', + tags: ['senior'], + next: 'q4_senior', + }, + { + id: 'veteran', + emoji: '๐Ÿฆ–', + label: 'I have war stories', + tags: ['veteran'], + next: 'q4_senior', + }, + ], + }, + q4_junior: { + id: 'q4_junior', + prompt: "What saves you when you're stuck?", + defaultNext: 'q5_ai', + options: [ + { + id: 'stack-overflow', + emoji: '๐Ÿชœ', + label: 'Stack Overflow + four browser tabs', + tags: [], + }, + { + id: 'llm-therapy', + emoji: '๐Ÿค–', + label: "An LLM chat that's basically my therapist", + tags: ['ai-bff'], + }, + { + id: 'senior-pity', + emoji: '๐Ÿง‘โ€๐Ÿซ', + label: 'A senior teammate pity-coding with me', + tags: [], + }, + { + id: 'actual-docs', + emoji: '๐Ÿ“š', + label: 'Reading the actual docs (rare flex)', + tags: [], + }, + ], + }, + q4_mid: { + id: 'q4_mid', + prompt: "What's your kryptonite at work?", + defaultNext: 'q5_ai', + options: [ + { + id: 'vague-tickets', + emoji: '๐ŸŽซ', + label: 'Vague Jira tickets', + tags: [], + }, + { + id: 'long-meetings', + emoji: '๐Ÿ“ž', + label: '"Quick" meetings that take 90 minutes', + tags: [], + }, + { + id: 'pr-comments', + emoji: '๐Ÿ’ฌ', + label: "That one teammate's PR comments", + tags: [], + }, + { + id: 'friday-deploys', + emoji: '๐Ÿš€', + label: 'Friday afternoon deploys', + tags: [], + }, + ], + }, + q4_senior: { + id: 'q4_senior', + prompt: "What's your guilty engineering pleasure?", + defaultNext: 'q5_ai', + options: [ + { + id: 'sneaky-rewrite', + emoji: '๐Ÿ› ๏ธ', + label: 'Rewriting a legacy module nobody asked you to touch', + tags: [], + }, + { + id: 'cache-problem', + emoji: '๐Ÿง ', + label: 'Saying "actually, that\'s a cache problem" in every meeting', + tags: [], + }, + { + id: 'rubber-stamp', + emoji: 'โœ…', + label: 'Approving PRs without reading them (then regretting it)', + tags: [], + }, + { + id: 'mentor-escape', + emoji: '๐Ÿง‘โ€๐Ÿซ', + label: "Mentoring juniors so you don't have to fix the bug yourself", + tags: [], + }, + ], + }, + q5_ai: { + id: 'q5_ai', + prompt: 'Pick your AI relationship status', + defaultNext: null, + options: [ + { + id: 'complicated', + emoji: '๐Ÿ˜ฌ', + label: "It's complicated โ€” we copy/paste", + tags: ['ai-curious'], + }, + { + id: 'best-friends', + emoji: '๐Ÿ’ž', + label: 'Best friends โ€” pair-programming daily', + tags: ['ai-bff'], + }, + { + id: 'skeptical', + emoji: '๐Ÿง', + label: 'Skeptical โ€” I review every line', + tags: ['ai-skeptic'], + }, + { + id: 'allergic', + emoji: '๐Ÿšซ', + label: 'Allergic โ€” I write all my code by hand', + tags: ['ai-allergic'], + }, + ], + }, +}; + +export const getNextQuestionId = ( + question: Question, + optionId: string, +): string | null => { + const option = question.options.find((opt) => opt.id === optionId); + if (!option) { + throw new Error( + `Option "${optionId}" not found on question "${question.id}"`, + ); + } + if (option.next !== undefined) { + return option.next; + } + return question.defaultNext ?? null; +}; diff --git a/packages/webapp/components/guess-who/useGuessWhoQuiz.ts b/packages/webapp/components/guess-who/useGuessWhoQuiz.ts new file mode 100644 index 0000000000..4deebd835a --- /dev/null +++ b/packages/webapp/components/guess-who/useGuessWhoQuiz.ts @@ -0,0 +1,59 @@ +import { useCallback, useEffect, useRef, useState } from 'react'; +import { useMutation } from '@tanstack/react-query'; +import type { + GuessWhoQuizFinalPersona, + GuessWhoQuizNextQuestion, + GuessWhoQuizQAInput, +} from '@dailydotdev/shared/src/graphql/guessWhoQuiz'; +import { requestGuessWhoQuizStep } from '@dailydotdev/shared/src/graphql/guessWhoQuiz'; + +interface UseGuessWhoQuiz { + nextQuestion: GuessWhoQuizNextQuestion | null; + persona: GuessWhoQuizFinalPersona | null; + isPending: boolean; + error: Error | null; + submitAnswer: (question: string, answer: string) => void; + retry: () => void; +} + +export const useGuessWhoQuiz = ( + initialHistory: GuessWhoQuizQAInput[], +): UseGuessWhoQuiz => { + const [history, setHistory] = useState(initialHistory); + const [nextQuestion, setNextQuestion] = + useState(null); + const [persona, setPersona] = useState(null); + const didKickoff = useRef(false); + + const { mutate, isPending, error } = useMutation({ + mutationFn: (h: GuessWhoQuizQAInput[]) => requestGuessWhoQuizStep(h), + onSuccess: (data) => { + setNextQuestion(data.nextQuestion); + setPersona(data.finalPersona); + }, + }); + + useEffect(() => { + if (didKickoff.current) { + return; + } + didKickoff.current = true; + mutate(initialHistory); + }, [initialHistory, mutate]); + + const submitAnswer = useCallback( + (question: string, answer: string) => { + const updated = [...history, { question, answer }]; + setHistory(updated); + setNextQuestion(null); + mutate(updated); + }, + [history, mutate], + ); + + const retry = useCallback(() => { + mutate(history); + }, [history, mutate]); + + return { nextQuestion, persona, isPending, error, submitAnswer, retry }; +}; diff --git a/packages/webapp/pages/guess-who.tsx b/packages/webapp/pages/guess-who.tsx new file mode 100644 index 0000000000..2f8242d5dc --- /dev/null +++ b/packages/webapp/pages/guess-who.tsx @@ -0,0 +1,39 @@ +import type { ReactElement } from 'react'; +import React from 'react'; +import type { NextSeoProps } from 'next-seo'; +import { GuessWhoQuiz } from '../components/guess-who/GuessWhoQuiz'; +import { getLayout as getMainLayout } from '../components/layouts/MainLayout'; +import { getPageSeoTitles } from '../components/layouts/utils'; +import { defaultOpenGraph, defaultSeo } from '../next-seo'; + +const seo: NextSeoProps = { + ...defaultSeo, + ...getPageSeoTitles('Guess Who'), + description: + 'Answer five quick questions and let daily.dev guess your developer persona.', + openGraph: { + ...defaultOpenGraph, + type: 'website', + site_name: 'daily.dev', + }, +}; + +const GuessWhoPage = (): ReactElement => ( +
+
+

+ Guess who you are +

+

+ Five quick questions and we'll take a (very humble) guess at your + developer persona. +

+
+ +
+); + +GuessWhoPage.getLayout = getMainLayout; +GuessWhoPage.layoutProps = { screenCentered: false, seo }; + +export default GuessWhoPage;