Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions src/pages/api/concierge-landing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,19 @@ import {

type LandingPayload = { text: string; suggestions: string[] };

/* Human backstop: the organic greeting is a paid call, gated client-side
on the panel being open (a real click). This rejects known crawlers and
header-less scripted requests so we never pay a machine that reaches the
route anyway. Greeting is non-critical β€” a blocked caller just gets no
line, never an error. */
const BOT_UA_RE =
/bot|crawl|spider|slurp|mediapartners|ahrefs|semrush|mj12|dotbot|bingpreview|facebookexternalhit|embedly|slackbot|telegrambot|whatsapp|headless|phantomjs|python-requests|curl\/|wget|go-http-client|scrapy|yandex(?:bot)?|baidu|duckduckbot/i;

function isBotUserAgent(ua: string | undefined): boolean {
if (!ua || !ua.trim()) return true;
return BOT_UA_RE.test(ua);
}

async function callClaude(
system: string,
user: string,
Expand Down Expand Up @@ -268,6 +281,14 @@ export default async function handler(
return res.status(200).json({ text: '' });
}

const ua =
typeof req.headers['user-agent'] === 'string'
? req.headers['user-agent']
: undefined;
if (isBotUserAgent(ua)) {
return res.status(200).json({ text: '' });
}

const { url, title, prevQuery, prevAnswer, lang, mode } = (req.body ??
{}) as {
url?: string;
Expand Down
70 changes: 22 additions & 48 deletions src/pages/uxcg/[slug].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import { getStrapiQuestions } from '@uxcore/api/questions';
import { getTags } from '@uxcore/api/tags';
import SeoGenerator from '@uxcore/components/SeoGenerator';
import UXCGModal from '@uxcore/components/UXCGModal';
import UXCGModalMobile from '@uxcore/components/UXCGModalMobile';
import useMobile from '@uxcore/hooks/useMobile';
import UXCGLayout from '@uxcore/layouts/UXCGLayout';
import { getUXCGRedirects } from '@uxcore/lib/getUXCGRedirects';
import {
Expand Down Expand Up @@ -67,11 +65,9 @@ const Slug: FC<UXCGIdProps> = ({
: typeof searchQuery === 'string'
? searchQuery
: undefined;
const { isMobile } = useMobile()[1];

const [isModalClosed, setIsModalClosed] = useState<boolean>(true);
const [questionId, setQuestionId] = useState<number>(id);
const [clickedQuestionId, setClickedQuestionId] = useState<number>(null);
const [answerId, setAnswerId] = useState<number>(null);
const [searchValue, setSearchValue] = useState<string>(searchTerm as string);
const [isCopyTooltipVisible, setIsCopyTooltipVisible] = useState(false);
Expand Down Expand Up @@ -112,7 +108,7 @@ const Slug: FC<UXCGIdProps> = ({
router.push(`/uxcg/${newSlug}`, undefined, { scroll: false });
setQuestionId(Number(newId));
},
[router, isMobile],
[router],
);

// Copy link to clipboard
Expand All @@ -130,7 +126,6 @@ const Slug: FC<UXCGIdProps> = ({
const handleQuestionClick = useCallback(
(number, slug) => {
handleSelectedQuestion(Number(number), slug);
setClickedQuestionId(Number(number));
},
[handleSelectedQuestion],
);
Expand Down Expand Up @@ -179,48 +174,27 @@ const Slug: FC<UXCGIdProps> = ({
createdDate={'2021-07-16'}
/>
<h1 className={styles.title}>{title}</h1>
{isMobile ? (
<UXCGModalMobile
tags={tags}
biases={biases?.[locale]}
answerId={answerId}
onClose={closeModal}
questions={questions}
questionId={questionId}
clickedQuestionId={clickedQuestionId}
closeModal={closeModal}
data={!!id && modalData}
handleCopyLink={handleCopyLink}
setIsModalClosed={setIsModalClosed}
onChangeQuestionId={handleSelectedQuestion}
isCopyTooltipVisible={isCopyTooltipVisible}
handleQuestionClick={handleQuestionClick}
nextQuestion={next}
prevQuestion={prev}
id={id}
slugs={languageSwitchSlugs}
/>
) : (
<UXCGModal
data={id && modalData}
setIsModalClosed={setIsModalClosed}
questionId={questionId}
answerId={answerId}
biases={biases?.[locale]}
tags={tags}
totalLength={questionsLength}
onChangeQuestionId={handleSelectedQuestion}
closeModal={closeModal}
handleQuestionClick={handleQuestionClick}
relatedQuestions={answerRelatedQuestions}
handleCopyLink={handleCopyLink}
isCopyTooltipVisible={isCopyTooltipVisible}
nextQuestion={next}
prevQuestion={prev}
id={id}
slugs={languageSwitchSlugs}
/>
)}
{/* UXCGModal carries its own mobile layout now (pills, dark theme,
loaders) β€” the legacy UXCGModalMobile swap is retired. */}
<UXCGModal
data={id && modalData}
setIsModalClosed={setIsModalClosed}
questionId={questionId}
answerId={answerId}
biases={biases?.[locale]}
tags={tags}
totalLength={questionsLength}
onChangeQuestionId={handleSelectedQuestion}
closeModal={closeModal}
handleQuestionClick={handleQuestionClick}
relatedQuestions={answerRelatedQuestions}
handleCopyLink={handleCopyLink}
isCopyTooltipVisible={isCopyTooltipVisible}
nextQuestion={next}
prevQuestion={prev}
id={id}
slugs={languageSwitchSlugs}
/>
<UXCGLayout
questions={allQuestions[locale]}
tags={tags}
Expand Down
60 changes: 20 additions & 40 deletions src/pages/uxcore/[slug].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ import { getStrapiQuestions } from '@uxcore/api/questions';
import { getTags } from '@uxcore/api/tags';
import SeoGenerator from '@uxcore/components/SeoGenerator';
import UXCoreModal from '@uxcore/components/UXCoreModal';
import UXCoreModalMobile from '@uxcore/components/UXCoreModalMobile';
import useMobile from '@uxcore/hooks/useMobile';
import useUXCoreGlobals from '@uxcore/hooks/useUXCoreGlobals';
import UXCoreLayout from '@uxcore/layouts/UXCoreLayout';
import { getRedirectMap } from '@uxcore/lib/getUXCoreRedirects';
Expand Down Expand Up @@ -44,9 +42,8 @@ const UXCoreIds: FC<UXCoreProps> = ({
}) => {
const [activeBiasNumber, setActiveBiasNumber] = useState<number>(null);
const [isModalClosed, setIsModalClosed] = useState<boolean>(true);
const [{ toggleIsProductView }, { isProductView }] = useUXCoreGlobals();
const [, { isProductView }] = useUXCoreGlobals();
const router = useRouter();
const { isMobile } = useMobile()[1];
const { locale } = router as TRouter;

const slugs = {
Expand Down Expand Up @@ -149,42 +146,25 @@ const UXCoreIds: FC<UXCoreProps> = ({
modifiedDate={currentActiveBias.updatedAt}
/>
<h1 className={styles.headingTitle}>{currentActiveBias.title}</h1>
{isMobile ? (
<UXCoreModalMobile
toggleIsCoreView={toggleIsProductView}
isProductView={!isProductView}
biasNumber={activeBiasNumber}
isSecondView={!isProductView}
secondViewLabel={'hr'}
defaultViewLabel="product"
questions={mentionedQuestions}
uxCoreData={biases[locale]}
tags={tags}
onClose={openPage}
onChangeBiasId={openSelectedBias}
nextBiasName={next}
prevBiasName={prev}
slugs={languageSwitchSlugs}
/>
) : (
<UXCoreModal
headingTitle={currentActiveBias.title}
isProductView={!isProductView}
setIsModalClosed={setIsModalClosed}
biasNumber={activeBiasNumber}
isSecondView={!isProductView}
secondViewLabel={'hr'}
defaultViewLabel="product"
questions={mentionedQuestions}
tags={tags}
data={Number(currentActiveBias.number) && currentActiveBias}
onClose={openPage}
onChangeBiasId={openSelectedBias}
nextBiasName={next}
prevBiasName={prev}
slugs={languageSwitchSlugs}
/>
)}
{/* UXCoreModal carries its own mobile layout now (pills, dark theme,
loaders) β€” the legacy UXCoreModalMobile swap is retired. */}
<UXCoreModal
headingTitle={currentActiveBias.title}
isProductView={!isProductView}
setIsModalClosed={setIsModalClosed}
biasNumber={activeBiasNumber}
isSecondView={!isProductView}
secondViewLabel={'hr'}
defaultViewLabel="product"
questions={mentionedQuestions}
tags={tags}
data={Number(currentActiveBias.number) && currentActiveBias}
onClose={openPage}
onChangeBiasId={openSelectedBias}
nextBiasName={next}
prevBiasName={prev}
slugs={languageSwitchSlugs}
/>
{biases[locale] ? (
<UXCoreLayout
strapiBiases={biases[locale]}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,72 +53,70 @@ $mobile-border-radius: 4px;
}

&.Mobile {
padding: 0 10px;
width: 100vw;
max-width: fit-content;
position: relative;
width: 100%;
max-width: none;
padding: 16px;
box-sizing: border-box;
background-color: #fff;
border-radius: 12px;
box-shadow: 0 16px 40px rgba(0, 0, 0, 0.35);

& .Title {
font-weight: 700;
width: 100%;
padding: $mobile-padding;
box-sizing: border-box;
text-align: center;
font-size: 16px;
margin: 10px 0;
background-color: #fff;
border-radius: $mobile-border-radius;
line-height: 140%;
text-align: left;
// Right margin clears the absolute close button.
margin: 0 36px 10px 0;
}

& .Description {
padding: $mobile-padding;
background-color: #fff;
border-radius: $mobile-border-radius;
margin-bottom: 10px;
height: fit-content !important;
margin-bottom: 14px;
max-height: 50vh;
overflow-y: auto;

& p {
font-size: 16px;
line-height: 150%;
}
}

& .LinkSection {
flex-direction: row;
flex-direction: column;
align-items: flex-start;
position: relative;
gap: 2px;
margin: 0;

& a {
height: 48px;
padding: 8px 16px 20px;
background-color: #fff;
border-radius: $mobile-border-radius;
margin-right: 10px;
width: calc(100% - 10px - 48px);
box-sizing: border-box;
margin-right: 0;
font-weight: 600;
text-decoration: none;
}

// The tip says "opens in a new tab" β€” on mobile the link opens in the
// same tab, so the hint would lie.
& .Tip {
position: absolute;
bottom: 4px;
left: 50px;
display: none;
}

& .CloseButton {
width: 48px;
height: 48px;
background-color: #fff;
border-radius: $mobile-border-radius;
position: relative;
position: absolute;
top: 10px;
right: 10px;
width: 32px;
height: 32px;

&::after,
&::before {
content: '';
height: 4px;
width: calc(100% - 8px);
height: 2px;
width: 18px;
position: absolute;
top: calc(50% - 2px);
right: 4px;
background-color: gray;
border-radius: 4px;
top: calc(50% - 1px);
right: 7px;
background-color: #9e9e9e;
border-radius: 2px;
}

&::after {
Expand All @@ -131,4 +129,34 @@ $mobile-border-radius: 4px;
}
}
}

// Dark theme: popup card repainted to match the modal palette.
:global(body.darkTheme) &.Mobile {
background-color: #1b1e26;
border: 1px solid #303338;
box-shadow: 0 16px 40px rgba(0, 0, 0, 0.6);

& .Title {
color: #dadada;
}

// ContentParser reuses UXCoreModal classes whose dark overrides are
// scoped to that modal β€” neutralize their light-theme highlight here.
& .Description,
& .Description * {
color: #dadada;
background: transparent !important;
}

& .Description a {
color: #7fb3d5;
}

& .LinkSection .CloseButton {
&::after,
&::before {
background-color: #8b919c;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import ContentParser from '@uxcore/components/ContentParser';
import biasPopupIntl from '@uxcore/data/biasPopup';
import useMobile from '@uxcore/hooks/useMobile';
import cn from 'classnames';
import Link from 'next/link';
import { FC } from 'react';

import useMobile from '@uxcore/hooks/useMobile';

import biasPopupIntl from '@uxcore/data/biasPopup';

import ContentParser from '@uxcore/components/ContentParser';

import UxCoreStyle from '../../UXCoreModal/UXCoreModal.module.scss';
import styles from './BiasPopupContent.module.scss';

Expand Down Expand Up @@ -49,7 +46,7 @@ const BiasPopupContent: FC<BiasPopupContentProps> = ({
>
{!customTip && (
<Link href={`/uxcore/${slug}`} legacyBehavior>
<a target="_blank" data-cy={'open-bias'}>
<a target={isMobile ? '_self' : '_blank'} data-cy={'open-bias'}>
{text}
</a>
</Link>
Expand Down
Loading
Loading