[web] interviews/learning-list: deprecate focus area methods

This commit is contained in:
Yangshun 2024-10-27 20:19:38 +08:00
parent bc9b18fbde
commit f2e505d0d8
99 changed files with 867 additions and 753 deletions

View File

@ -1,56 +1,5 @@
// @ts-check
import { makeSource } from 'contentlayer/source-files';
import remarkFrontmatter from 'remark-frontmatter';
import remarkGfm from 'remark-gfm';
import remarkMdxFrontmatter from 'remark-mdx-frontmatter';
import config from './src/content/contentlayerConfig';
import { BlogAuthorDocument } from './src/components/blog/contentlayer/BlogAuthorDocument';
import { BlogCategoryDocument } from './src/components/blog/contentlayer/BlogCategoryDocument';
import { BlogPostDocument } from './src/components/blog/contentlayer/BlogPostDocument';
import { BlogSeriesDocument } from './src/components/blog/contentlayer/BlogSeriesDocument';
import { BlogSubseriesDocument } from './src/components/blog/contentlayer/BlogSubseriesDocument';
import { ProjectsCommonGuideDocument } from './src/components/projects/contentlayer/ProjectsCommonGuideDocument';
import { ProjectsChallengeAppendixDocument } from './src/components/projects/contentlayer/ProjectsChallengeAppendixDocument';
import { ProjectsChallengeBriefDocument } from './src/components/projects/contentlayer/ProjectsChallengeBriefDocument';
import { ProjectsChallengeInfoDocument } from './src/components/projects/contentlayer/ProjectsChallengeInfoDocument';
import { ProjectsChallengeMetadataDocument } from './src/components/projects/contentlayer/ProjectsChallengeMetadataDocument';
import { ProjectsChallengeStyleGuideDocument } from './src/components/projects/contentlayer/ProjectsChallengeStyleGuideDocument';
import { ProjectsChallengeGuideDocument } from './src/components/projects/contentlayer/ProjectsChallengeGuideDocument';
import { ProjectsChallengeAPIWriteupDocument } from './src/components/projects/contentlayer/ProjectsChallengeAPIWriteupDocument';
import { ProjectsTrackMetadataDocument } from './src/components/projects/contentlayer/ProjectsTrackMetadataDocument';
import { ProjectsTrackInfoDocument } from './src/components/projects/contentlayer/ProjectsTrackInfoDocument';
import { ProjectsSkillMetadataDocument } from './src/components/projects/contentlayer/ProjectsSkillMetadataDocument';
import { ProjectsSkillInfoDocument } from './src/components/projects/contentlayer/ProjectsSkillInfoDocument';
import { JobsPostingDocument } from './src/components/hiring/contentlayer/JobsPostingDocument.ts';
import { InterviewsLearningListDocument } from './src/components/interviews/questions/listings/learning/InterviewsLearningListDocument.ts';
import { InterviewsListingBottomContentDocument } from './src/components/interviews/company/contentlayer/InterviewsListingBottomContentDocument.ts';
export default makeSource({
contentDirPath: 'src/content',
documentTypes: [
BlogAuthorDocument,
BlogCategoryDocument,
BlogPostDocument,
BlogSeriesDocument,
BlogSubseriesDocument,
InterviewsLearningListDocument,
InterviewsListingBottomContentDocument,
ProjectsCommonGuideDocument,
ProjectsChallengeAppendixDocument,
ProjectsChallengeBriefDocument,
ProjectsChallengeGuideDocument,
ProjectsChallengeInfoDocument,
ProjectsChallengeMetadataDocument,
ProjectsChallengeStyleGuideDocument,
ProjectsChallengeAPIWriteupDocument,
ProjectsSkillMetadataDocument,
ProjectsSkillInfoDocument,
ProjectsTrackMetadataDocument,
ProjectsTrackInfoDocument,
JobsPostingDocument,
],
mdx: {
remarkPlugins: [remarkGfm, remarkFrontmatter, remarkMdxFrontmatter],
},
});
export default config;

View File

@ -1,6 +1,6 @@
'use client';
import type { InterviewsLearningList } from 'contentlayer/generated';
import type { InterviewsStudyList } from 'contentlayer/generated';
import { useInView } from 'framer-motion';
import dynamic from 'next/dynamic';
import { useRef } from 'react';
@ -15,7 +15,7 @@ const InterviewsMarketingHomePageBottomNew = dynamic(
);
type Props = Readonly<{
companyGuides: ReadonlyArray<InterviewsLearningList>;
companyGuides: ReadonlyArray<InterviewsStudyList>;
questions: QuestionBankDataType;
}>;

View File

@ -1,4 +1,4 @@
import type { InterviewsLearningList } from 'contentlayer/generated';
import type { InterviewsStudyList } from 'contentlayer/generated';
import { useUserProfile } from '~/components/global/UserProfileProvider';
import InterviewsMarketingCompaniesSection from '~/components/interviews/marketing/InterviewsMarketingCompaniesSection';
@ -15,7 +15,7 @@ import InterviewsPricingSectionLocalizedContainer from '~/components/interviews/
import MarketingCommunitySection from '~/components/marketing/contact/MarketingCommunitySection';
type Props = Readonly<{
companyGuides: ReadonlyArray<InterviewsLearningList>;
companyGuides: ReadonlyArray<InterviewsStudyList>;
questions: QuestionBankDataType;
}>;

View File

@ -1,4 +1,4 @@
import type { InterviewsLearningList } from 'contentlayer/generated';
import type { InterviewsStudyList } from 'contentlayer/generated';
import InterviewsMarketingEmbedSection from '~/components/interviews/marketing/embed/InterviewsMarketingEmbedSection';
import type { EmbedUIQuestion } from '~/components/interviews/marketing/embed/InterviewsMarketingEmbedUIQuestion';
@ -15,7 +15,7 @@ import Section from '~/components/ui/Heading/HeadingContext';
import InterviewsMarketingHomePageBottomContainer from './InterviewsMarketingHomePageBottomContainer';
type Props = Readonly<{
companyGuides: ReadonlyArray<InterviewsLearningList>;
companyGuides: ReadonlyArray<InterviewsStudyList>;
javaScriptEmbedExample: QuestionJavaScript;
questions: QuestionBankDataType;
testimonials: ReadonlyArray<InterviewsMarketingTestimonial>;

View File

@ -17,7 +17,7 @@ import {
} from '~/components/interviews/questions/listings/filters/QuestionsProcessor';
import { QuestionCount } from '~/components/interviews/questions/listings/stats/QuestionCount';
import { fetchInterviewsLearningLists } from '~/db/contentlayer/InterviewsLearningListReader';
import { fetchInterviewsStudyLists } from '~/db/contentlayer/InterviewsStudyListReader';
import {
readQuestionJavaScriptContents,
readQuestionUserInterface,
@ -220,7 +220,7 @@ export default async function Page({ params }: Props) {
fetchQuestionsListQuiz(locale),
fetchQuestionsListSystemDesign(locale),
// Company guides
fetchInterviewsLearningLists('company'),
fetchInterviewsStudyLists('company'),
]);
const testimonials = InterviewsMarketingTestimonialsDict();

View File

@ -1,175 +0,0 @@
'use client';
import clsx from 'clsx';
import { RiArrowRightLine } from 'react-icons/ri';
import { trpc } from '~/hooks/trpc';
import type {
FocusArea,
FocusAreas,
FocusAreaType,
} from '~/data/focus-areas/FocusAreas';
import { getFocusAreaTheme } from '~/data/focus-areas/FocusAreas';
import type { QuestionDifficulty } from '~/components/interviews/questions/common/QuestionsTypes';
import CompletionCountSummary from '~/components/interviews/questions/listings/stats/CompletionCountSummary';
import QuestionCountLabel from '~/components/interviews/questions/metadata/QuestionCountLabel';
import QuestionDifficultySummary from '~/components/interviews/questions/metadata/QuestionDifficultySummary';
import { useIntl } from '~/components/intl';
import Anchor from '~/components/ui/Anchor';
import Badge from '~/components/ui/Badge';
import Container from '~/components/ui/Container';
import Heading from '~/components/ui/Heading';
import Section from '~/components/ui/Heading/HeadingContext';
import Text from '~/components/ui/Text';
import { themeGlassyBorder } from '~/components/ui/theme';
import { countNumberOfQuestionsInList } from '~/db/QuestionsUtils';
import { useUser } from '@supabase/auth-helpers-react';
function FocusAreaCard({
difficultySummary,
area: { type, name, shortDescription, questions, href },
isStarted = false,
completionCount = 0,
}: {
area: FocusArea;
completionCount?: number;
difficultySummary: Record<QuestionDifficulty, number>;
isStarted?: boolean;
}) {
const intl = useIntl();
const questionCount = countNumberOfQuestionsInList(questions);
const theme = getFocusAreaTheme(type);
return (
<div
className={clsx(
'group relative flex flex-1 items-center gap-6 rounded-lg p-4',
'bg-white transition dark:bg-neutral-800/70 dark:hover:bg-neutral-800/80',
themeGlassyBorder,
)}>
<div className="flex flex-1 flex-col gap-3 self-stretch">
<div className="flex items-center gap-3">
<div
className={clsx(
'size-10 flex items-center justify-center rounded',
theme.gradient.className,
)}>
<theme.iconOutline className="size-6 text-white" />
</div>
<Anchor href={href} variant="unstyled">
<span aria-hidden={true} className="absolute inset-0" />
<Heading level="heading6">{name}</Heading>
</Anchor>
{isStarted && (
<span>
<Badge
label={intl.formatMessage({
defaultMessage: 'Started',
description: 'Started on study plan label',
id: 'cKn3cK',
})}
size="sm"
variant="info"
/>
</span>
)}
</div>
<Text className="grow" color="secondary" size="body2">
{shortDescription}
</Text>
<div className="flex flex-wrap items-center gap-x-8 gap-y-2">
<QuestionCountLabel count={questionCount} showIcon={true} />
<QuestionDifficultySummary
easy={difficultySummary.easy}
hard={difficultySummary.hard}
medium={difficultySummary.medium}
showIcon={true}
/>
{isStarted && (
<CompletionCountSummary
completed={completionCount}
total={questionCount}
/>
)}
</div>
</div>
<RiArrowRightLine className="group-hover:text-brand-dark dark:group-hover:text-brand size-6 text-neutral-400 transition-colors" />
</div>
);
}
export type FocusAreaDifficultySummary = Record<
FocusAreaType,
Record<QuestionDifficulty, number>
>;
type Props = Readonly<{
difficultySummary: FocusAreaDifficultySummary;
focusAreas: FocusAreas;
}>;
export default function InterviewsFocusAreaListPage({
focusAreas,
difficultySummary,
}: Props) {
const intl = useIntl();
const user = useUser();
const { data: questionListSessions } =
trpc.questionLists.getActiveSessions.useQuery(undefined, {
enabled: !!user,
});
const sessions = questionListSessions ?? [];
const areas = Object.values(focusAreas);
return (
<Container
className={clsx(
'flex flex-col',
'py-4 md:py-6 lg:py-8 xl:py-16',
'gap-y-8 md:gap-y-10 2xl:gap-y-12',
)}>
<div className="flex flex-col gap-3">
<Heading level="heading5">
{intl.formatMessage({
defaultMessage: 'Focus areas',
description: 'Title of focus areas page',
id: 'Zui1cu',
})}
</Heading>
<Text className="block" color="secondary" size="body2">
{intl.formatMessage({
defaultMessage:
'Discover focus areas tailored to your needs to help you prepare for your upcoming technical interviews.',
description: 'Description for focus areas page',
id: 'Qe4ww/',
})}
</Text>
</div>
<Section>
<div className="grid gap-4 lg:grid-cols-2">
{areas.map((area) => {
const session = sessions.find(
(session_) => session_.key === area.type,
);
const completionCount = session?._count.progress;
return (
<FocusAreaCard
key={area.type}
area={area}
completionCount={completionCount}
difficultySummary={difficultySummary[area.type]}
isStarted={session != null}
/>
);
})}
</div>
</Section>
</Container>
);
}

View File

@ -1,119 +0,0 @@
import type { Metadata } from 'next/types';
import { CourseJsonLd } from 'next-seo';
import type { IntlShape } from 'react-intl';
import type { FocusAreaType } from '~/data/focus-areas/FocusAreas';
import { getFocusAreas } from '~/data/focus-areas/FocusAreas';
import type { QuestionMetadata } from '~/components/interviews/questions/common/QuestionsTypes';
import { sortQuestions } from '~/components/interviews/questions/listings/filters/QuestionsProcessor';
import { fetchInterviewListingBottomContent } from '~/db/contentlayer/InterviewsListingBottomContentReader';
import { fetchQuestionsBySlug } from '~/db/QuestionsListReader';
import { getIntlServerOnly } from '~/i18n';
import { generateStaticParamsWithLocale } from '~/next-i18nostic/src';
import defaultMetadata from '~/seo/defaultMetadata';
import { getSiteOrigin } from '~/seo/siteUrl';
import InterviewsFocusAreaPage from './InterviewsFocusAreaPage';
import { INTERVIEWS_REVAMP_BOTTOM_CONTENT } from '../../../../../../../../data/FeatureFlags';
async function getFocusAreaSEO(focusAreaType: FocusAreaType, locale: string) {
const intl = await getIntlServerOnly(locale);
// TODO: Remove this IntlShape typecast.
const focusAreas = getFocusAreas(intl as IntlShape);
const focusArea = focusAreas[focusAreaType];
return { ...focusArea.seo, href: focusArea.href };
}
export async function generateStaticParams() {
const focusAreas: Record<FocusAreaType, null> = {
accessibility: null,
'async-operations': null,
'data-structures-algorithms': null,
'design-system-components': null,
'dom-manipulation': null,
forms: null,
'javascript-polyfills': null,
lodash: null,
'state-management': null,
};
return generateStaticParamsWithLocale(
Object.keys(focusAreas).map((focusArea) => ({ focusArea })),
);
}
type Props = Readonly<{
params: {
focusArea: FocusAreaType;
locale: string;
};
}>;
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const { locale, focusArea: focusAreaType } = params;
const { title, description, href, socialTitle } = await getFocusAreaSEO(
focusAreaType,
locale,
);
return defaultMetadata({
description,
locale,
pathname: href,
socialTitle,
title,
});
}
export default async function Page({ params }: Props) {
const { locale, focusArea: focusAreaType } = params;
const intl = await getIntlServerOnly(locale);
// TODO: Remove this IntlShape typecast.
const focusAreas = getFocusAreas(intl as IntlShape);
const focusArea = focusAreas[focusAreaType];
const [{ title, description }, questions, bottomContent] = await Promise.all([
getFocusAreaSEO(focusAreaType, locale),
fetchQuestionsBySlug(focusArea.questions, locale),
fetchInterviewListingBottomContent(`${focusAreaType}-focus-area`),
]);
const codingQuestionsForPlan = [
...questions.javascript,
...questions['user-interface'],
...questions.algo,
];
const systemDesignQuestionsForPlan = questions['system-design'];
const quizQuestionsForPlan = questions.quiz;
return (
<>
<CourseJsonLd
courseName={title}
description={description}
provider={{
name: 'GreatFrontEnd',
url: getSiteOrigin(),
}}
useAppDir={true}
/>
<InterviewsFocusAreaPage
bottomContent={
INTERVIEWS_REVAMP_BOTTOM_CONTENT ? bottomContent : undefined
}
codingQuestions={codingQuestionsForPlan}
focusArea={focusArea}
quizQuestions={sortQuestions(
quizQuestionsForPlan as ReadonlyArray<QuestionMetadata>,
'importance',
false,
)}
systemDesignQuestions={systemDesignQuestionsForPlan}
/>
</>
);
}

View File

@ -1,146 +0,0 @@
import type { Metadata } from 'next/types';
import type { IntlShape } from 'react-intl';
import { INTERVIEWS_REVAMP_2024 } from '~/data/FeatureFlags';
import { getFocusAreas } from '~/data/focus-areas/FocusAreas';
import type {
QuestionDifficulty,
QuestionList_DEPRECATED,
} from '~/components/interviews/questions/common/QuestionsTypes';
import { countQuestionsByDifficulty } from '~/components/interviews/questions/listings/filters/QuestionsProcessor';
import { fetchInterviewListingBottomContent } from '~/db/contentlayer/InterviewsListingBottomContentReader';
import { fetchQuestionsBySlug } from '~/db/QuestionsListReader';
import { getIntlServerOnly } from '~/i18n';
import defaultMetadata from '~/seo/defaultMetadata';
import InterviewsFocusAreaListPage from './InterviewsFocusAreaListPage';
import InterviewsRevampFocusAreaListPage from './InterviewsRevampFocusAreaListPage';
export const dynamic = 'force-static';
type Props = Readonly<{
params: Readonly<{
locale: string;
}>;
}>;
async function getPageSEOMetadata({ params }: Props) {
const { locale } = params;
const intl = await getIntlServerOnly(locale);
const focusAreas = getFocusAreas(intl as IntlShape);
return {
description: intl.formatMessage(
{
defaultMessage:
'Explore {topicsCount} critical topics for front end interviews. Master them with targeted practice questions—each with detailed solutions and tests to learn from.',
description: 'Page description for focus areas listing',
id: 'UISwUX',
},
{
topicsCount: Object.keys(focusAreas).length,
},
),
href: '/focus-areas',
socialTitle: intl.formatMessage({
defaultMessage: 'Practice Questions by Focus Area | GreatFrontEnd',
description: 'Social title for focus areas listing',
id: 'DUKB3u',
}),
title: intl.formatMessage({
defaultMessage:
'Front End Interview Focus Areas—Accessibility, Forms and more',
description: 'Page title for focus areas listing',
id: 'UET3FT',
}),
};
}
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const { locale } = params;
const { title, description, socialTitle, href } = await getPageSEOMetadata({
params,
});
return defaultMetadata({
description,
locale,
pathname: href,
socialTitle,
title,
});
}
async function getDifficultySummaryForList(
list: QuestionList_DEPRECATED,
locale: string,
): Promise<Record<QuestionDifficulty, number>> {
const questions = await fetchQuestionsBySlug(list.questions, locale);
return countQuestionsByDifficulty([
...questions.javascript,
...questions['user-interface'],
...questions['system-design'],
]);
}
export default async function Page({ params }: Props) {
const { locale } = params;
const [intl] = await Promise.all([
getIntlServerOnly(locale),
getPageSEOMetadata({ params }),
]);
// TODO: Remove this IntlShape typecast.
const focusAreas = getFocusAreas(intl as IntlShape);
const [
difficultySummaryA11y,
difficultySummaryAsync,
difficultySummaryDSA,
difficultySummaryDSC,
difficultySummaryDOM,
difficultySummaryForms,
difficultySummaryLodash,
difficultySummaryJavaScriptPolyfills,
difficultySummaryStateManagement,
] = await Promise.all(
[
focusAreas.accessibility,
focusAreas['async-operations'],
focusAreas['data-structures-algorithms'],
focusAreas['design-system-components'],
focusAreas['dom-manipulation'],
focusAreas.forms,
focusAreas.lodash,
focusAreas['javascript-polyfills'],
focusAreas['state-management'],
].map((area) => getDifficultySummaryForList(area, locale)),
);
const bottomContent = await fetchInterviewListingBottomContent('focus-areas');
return INTERVIEWS_REVAMP_2024 ? (
<InterviewsRevampFocusAreaListPage
bottomContent={bottomContent}
focusAreas={focusAreas}
/>
) : (
<InterviewsFocusAreaListPage
difficultySummary={{
accessibility: difficultySummaryA11y,
'async-operations': difficultySummaryAsync,
'data-structures-algorithms': difficultySummaryDSA,
'design-system-components': difficultySummaryDSC,
'dom-manipulation': difficultySummaryDOM,
forms: difficultySummaryForms,
'javascript-polyfills': difficultySummaryJavaScriptPolyfills,
lodash: difficultySummaryLodash,
'state-management': difficultySummaryStateManagement,
}}
focusAreas={focusAreas}
/>
);
}

View File

@ -1,7 +1,10 @@
'use client';
import clsx from 'clsx';
import type { InterviewsListingBottomContent } from 'contentlayer/generated';
import type {
InterviewsListingBottomContent,
InterviewsStudyList,
} from 'contentlayer/generated';
import {
RiListCheck3,
RiVerifiedBadgeLine,
@ -11,13 +14,12 @@ import {
import { trpc } from '~/hooks/trpc';
import {
categorizeFocusAreas,
type FocusAreas,
getFocusAreaTheme,
categorizeFocusAreas_DEPRECATED,
getFocusAreaTheme_DEPRECATED,
} from '~/data/focus-areas/FocusAreas';
import InterviewsListPageHeader from '~/components/interviews/common/InterviewsListPageHeader';
import InterviewsLearningListCard from '~/components/interviews/questions/listings/learning/InterviewsLearningListCard';
import InterviewsStudyListCard from '~/components/interviews/questions/listings/learning/InterviewsStudyListCard';
import { useIntl } from '~/components/intl';
import MDXContent from '~/components/mdx/MDXContent';
import Container from '~/components/ui/Container';
@ -30,7 +32,7 @@ import { useUser } from '@supabase/auth-helpers-react';
type Props = Readonly<{
bottomContent?: InterviewsListingBottomContent;
focusAreas: FocusAreas;
focusAreas: ReadonlyArray<InterviewsStudyList>;
}>;
export default function InterviewsRevampFocusAreaListPage({
@ -45,7 +47,7 @@ export default function InterviewsRevampFocusAreaListPage({
});
const sessions = questionListSessions ?? [];
const focusAreasCategories = categorizeFocusAreas(intl);
const focusAreasCategories = categorizeFocusAreas_DEPRECATED(intl);
const features = [
{
@ -124,10 +126,10 @@ export default function InterviewsRevampFocusAreaListPage({
(session_) => session_.key === focusArea.type,
);
const completionCount = session?._count.progress;
const theme = getFocusAreaTheme(focusArea.type);
const theme = getFocusAreaTheme_DEPRECATED(focusArea.type);
return (
<InterviewsLearningListCard
<InterviewsStudyListCard
key={focusArea.type}
completionCount={completionCount}
isStarted={session != null}

View File

@ -1,7 +1,10 @@
'use client';
import clsx from 'clsx';
import type { InterviewsListingBottomContent } from 'contentlayer/generated';
import type {
InterviewsListingBottomContent,
InterviewsStudyList,
} from 'contentlayer/generated';
import {
RiArrowLeftLine,
RiQuestionnaireLine,
@ -12,13 +15,16 @@ import {
import { trpc } from '~/hooks/trpc';
import { INTERVIEWS_REVAMP_2024 } from '~/data/FeatureFlags';
import type { FocusArea } from '~/data/focus-areas/FocusAreas';
import { getFocusAreaTheme } from '~/data/focus-areas/FocusAreas';
import { getFocusAreaTheme_DEPRECATED } from '~/data/focus-areas/FocusAreas';
import type { QuestionMetadata } from '~/components/interviews/questions/common/QuestionsTypes';
import QuestionsLearningList from '~/components/interviews/questions/listings/learning/QuestionsLearningList';
import QuestionsLearningListPageTitleSection from '~/components/interviews/questions/listings/learning/QuestionsLearningListPageTitleSection';
import QuestionsLearningListTitleSection from '~/components/interviews/questions/listings/learning/QuestionsLearningListTitleSection';
import type {
QuestionFormat,
QuestionMetadata,
QuestionSlug,
} from '~/components/interviews/questions/common/QuestionsTypes';
import QuestionsLearningList from '~/components/interviews/questions/listings/learning/QuestionsStudyList';
import QuestionsLearningListPageTitleSection from '~/components/interviews/questions/listings/learning/QuestionsStudyListPageTitleSection';
import QuestionsStudyListTitleSection_DEPRECATED from '~/components/interviews/questions/listings/learning/QuestionsStudyListTitleSection_DEPRECATED';
import { useIntl } from '~/components/intl';
import MDXContent from '~/components/mdx/MDXContent';
import Button from '~/components/ui/Button';
@ -30,24 +36,23 @@ import {
categorizeQuestionsProgress,
countNumberOfQuestionsInList,
filterQuestionsProgressByList,
flattenQuestionFormatMetadata,
} from '~/db/QuestionsUtils';
import { useUser } from '@supabase/auth-helpers-react';
type Props = Readonly<{
bottomContent?: InterviewsListingBottomContent;
codingQuestions: ReadonlyArray<QuestionMetadata>;
focusArea: FocusArea;
quizQuestions: ReadonlyArray<QuestionMetadata>;
systemDesignQuestions: ReadonlyArray<QuestionMetadata>;
focusArea: InterviewsStudyList;
questionsMetadata: Record<QuestionFormat, ReadonlyArray<QuestionMetadata>>;
questionsSlugs: Record<QuestionFormat, ReadonlyArray<QuestionSlug>>;
}>;
export default function InterviewsFocusAreaPage({
quizQuestions,
codingQuestions,
systemDesignQuestions,
focusArea,
bottomContent,
focusArea,
questionsMetadata,
questionsSlugs,
}: Props) {
const intl = useIntl();
const user = useUser();
@ -63,10 +68,10 @@ export default function InterviewsFocusAreaPage({
const questionsOverallProgress = filterQuestionsProgressByList(
questionsProgressAll,
focusArea.questions,
questionsSlugs,
);
const focusAreaTheme = getFocusAreaTheme(focusArea.type);
const questionCount = countNumberOfQuestionsInList(focusArea.questions);
const focusAreaTheme = getFocusAreaTheme_DEPRECATED(focusArea.slug);
const questionCount = countNumberOfQuestionsInList(questionsSlugs);
const features = [
{
@ -124,11 +129,7 @@ export default function InterviewsFocusAreaPage({
features={features}
icon={focusAreaTheme.iconOutline}
overallProgress={questionProgressParam ?? []}
questions={[
...quizQuestions,
...codingQuestions,
...systemDesignQuestions,
]}
questions={flattenQuestionFormatMetadata(questionsMetadata)}
questionsSessionKey={focusArea.type}
themeBackgroundClass={focusAreaTheme.gradient.className}
title={focusArea.longName}
@ -136,18 +137,14 @@ export default function InterviewsFocusAreaPage({
<Divider />
</>
) : (
<QuestionsLearningListTitleSection
<QuestionsStudyListTitleSection_DEPRECATED
description={focusArea.description}
icon={focusAreaTheme.iconOutline}
overallProgress={questionProgressParam ?? []}
progressTrackingAvailableToNonPremiumUsers={true}
questionCount={questionCount}
questionListKey={focusArea.type}
questions={[
...codingQuestions,
...quizQuestions,
...systemDesignQuestions,
]}
questions={flattenQuestionFormatMetadata(questionsMetadata)}
themeBackgroundClass={focusAreaTheme.gradient.className}
title={focusArea.longName}
/>
@ -155,14 +152,17 @@ export default function InterviewsFocusAreaPage({
</div>
<Section>
<QuestionsLearningList
codingQuestions={codingQuestions}
listKey={focusArea.type}
codingQuestions={[
...questionsMetadata.javascript,
...questionsMetadata['user-interface'],
...questionsMetadata.algo,
]}
listKey={focusArea.slug}
overallProgress={questionsOverallProgress}
quizQuestions={quizQuestions}
systemDesignQuestions={systemDesignQuestions}
quizQuestions={questionsMetadata.quiz}
systemDesignQuestions={questionsMetadata['system-design']}
/>
</Section>
{bottomContent && (
<>
<Divider className="my-8" />

View File

@ -0,0 +1,112 @@
import { notFound } from 'next/navigation';
import type { Metadata } from 'next/types';
import { CourseJsonLd } from 'next-seo';
import { INTERVIEWS_REVAMP_BOTTOM_CONTENT } from '~/data/FeatureFlags';
import { sortQuestions } from '~/components/interviews/questions/listings/filters/QuestionsProcessor';
import { fetchInterviewListingBottomContent } from '~/db/contentlayer/InterviewsListingBottomContentReader';
import {
fetchInterviewsStudyList,
fetchInterviewsStudyLists,
} from '~/db/contentlayer/InterviewsStudyListReader';
import { fetchQuestionsBySlug } from '~/db/QuestionsListReader';
import { generateStaticParamsWithLocale } from '~/next-i18nostic/src';
import defaultMetadata from '~/seo/defaultMetadata';
import { getSiteOrigin } from '~/seo/siteUrl';
import InterviewsFocusAreaPage from './InterviewsFocusAreaPage';
type Props = Readonly<{
params: {
focusArea: string;
locale: string;
};
}>;
async function getPageSEOMetadata({ focusArea }: Props['params']) {
const focusAreaDocument = await fetchInterviewsStudyList(focusArea);
if (focusAreaDocument == null) {
return notFound();
}
return {
description: focusAreaDocument.seoDescription,
href: focusAreaDocument.href,
socialTitle: focusAreaDocument.socialTitle,
title: focusAreaDocument.seoTitle,
};
}
export async function generateStaticParams() {
const focusAreas = await fetchInterviewsStudyLists('focus-area');
return generateStaticParamsWithLocale(
focusAreas.map((focusArea) => ({ focusArea: focusArea.slug })),
);
}
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const { locale } = params;
const { title, description, href, socialTitle } =
await getPageSEOMetadata(params);
return defaultMetadata({
description,
locale,
pathname: href,
socialTitle,
title,
});
}
export default async function Page({ params }: Props) {
const { locale, focusArea: focusAreaSlug } = params;
const focusArea = await fetchInterviewsStudyList(focusAreaSlug);
if (focusArea == null) {
return notFound();
}
const questionsSlugs = {
algo: focusArea.questionsAlgo ?? [],
javascript: focusArea.questionsJavaScript ?? [],
quiz: focusArea.questionsQuiz ?? [],
'system-design': focusArea.questionsSystemDesign ?? [],
'user-interface': focusArea.questionsUserInterface ?? [],
};
const [questionsMetadata, bottomContent] = await Promise.all([
fetchQuestionsBySlug(questionsSlugs, locale),
fetchInterviewListingBottomContent(`${focusAreaSlug}-focus-area`),
]);
return (
<>
<CourseJsonLd
courseName={focusArea.seoTitle}
description={focusArea.seoDescription}
provider={{
name: 'GreatFrontEnd',
url: getSiteOrigin(),
}}
useAppDir={true}
/>
<InterviewsFocusAreaPage
bottomContent={
INTERVIEWS_REVAMP_BOTTOM_CONTENT ? bottomContent : undefined
}
focusArea={focusArea}
questionsMetadata={{
...questionsMetadata,
quiz: sortQuestions(questionsMetadata.quiz, 'importance', false),
}}
questionsSlugs={questionsSlugs}
/>
</>
);
}

View File

@ -0,0 +1,80 @@
import type { Metadata } from 'next/types';
import { fetchInterviewListingBottomContent } from '~/db/contentlayer/InterviewsListingBottomContentReader';
import { fetchInterviewsStudyLists } from '~/db/contentlayer/InterviewsStudyListReader';
import { getIntlServerOnly } from '~/i18n';
import defaultMetadata from '~/seo/defaultMetadata';
import InterviewsFocusAreaListPage from './InterviewsFocusAreaListPage';
export const dynamic = 'force-static';
type Props = Readonly<{
params: Readonly<{
locale: string;
}>;
}>;
async function getPageSEOMetadata({ params }: Props) {
const { locale } = params;
const [intl, focusAreas] = await Promise.all([
getIntlServerOnly(locale),
fetchInterviewsStudyLists('focus-area'),
]);
return {
description: intl.formatMessage(
{
defaultMessage:
'Explore {topicsCount} critical topics for front end interviews. Master them with targeted practice questions—each with detailed solutions and tests to learn from.',
description: 'Page description for focus areas listing',
id: 'UISwUX',
},
{
topicsCount: focusAreas.length,
},
),
href: '/focus-areas',
socialTitle: intl.formatMessage({
defaultMessage: 'Practice Questions by Focus Area | GreatFrontEnd',
description: 'Social title for focus areas listing',
id: 'DUKB3u',
}),
title: intl.formatMessage({
defaultMessage:
'Front End Interview Focus Areas — Accessibility, Forms and more',
description: 'Page title for focus areas listing',
id: 'j/2Zyj',
}),
};
}
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const { locale } = params;
const { title, description, socialTitle, href } = await getPageSEOMetadata({
params,
});
return defaultMetadata({
description,
locale,
pathname: href,
socialTitle,
title,
});
}
export default async function Page() {
const [focusAreas, bottomContent] = await Promise.all([
fetchInterviewsStudyLists('focus-area'),
fetchInterviewListingBottomContent('focus-areas'),
]);
return (
<InterviewsFocusAreaListPage
bottomContent={bottomContent}
focusAreas={focusAreas}
/>
);
}

View File

@ -10,7 +10,7 @@ import type { PreparationPlan } from '~/data/plans/PreparationPlans';
import { getPreparationPlanTheme } from '~/data/plans/PreparationPlans';
import type { QuestionMetadata } from '~/components/interviews/questions/common/QuestionsTypes';
import QuestionsLearningList from '~/components/interviews/questions/listings/learning/QuestionsLearningList';
import QuestionsLearningList from '~/components/interviews/questions/listings/learning/QuestionsStudyList';
import InterviewsRecommendedPrepStrategyPageTitleSection from '~/components/interviews/recommended/InterviewsRecommendedPrepStrategyPageTitleSection';
import { FormattedMessage, useIntl } from '~/components/intl';
import MDXContent from '~/components/mdx/MDXContent';

View File

@ -7,13 +7,12 @@ import InterviewsCompanyGuidePage from '~/components/interviews/company/Intervie
import type { QuestionMetadata } from '~/components/interviews/questions/common/QuestionsTypes';
import { sortQuestions } from '~/components/interviews/questions/listings/filters/QuestionsProcessor';
import { fetchInterviewsLearningList } from '~/db/contentlayer/InterviewsLearningListReader';
import { fetchInterviewListingBottomContent } from '~/db/contentlayer/InterviewsListingBottomContentReader';
import { fetchInterviewsStudyList } from '~/db/contentlayer/InterviewsStudyListReader';
import { fetchQuestionsBySlug } from '~/db/QuestionsListReader';
import { getIntlServerOnly } from '~/i18n';
import defaultMetadata from '~/seo/defaultMetadata';
// TODO(companies)
// TODO(interviews/companies)
// export async function generateStaticParams() {
// return [];
// }
@ -26,11 +25,8 @@ type Props = Readonly<{
}>;
async function getPageSEOMetadata({ params }: Props) {
const { locale, slug } = params;
const [intl, companyGuide] = await Promise.all([
getIntlServerOnly(locale),
fetchInterviewsLearningList(slug),
]);
const { slug } = params;
const companyGuide = await fetchInterviewsStudyList(slug);
if (companyGuide == null) {
return notFound();
@ -62,13 +58,13 @@ export async function generateMetadata({ params }: Props): Promise<Metadata> {
export default async function Page({ params }: Props) {
const { locale, slug } = params;
const companyGuide = await fetchInterviewsLearningList(slug);
const companyGuide = await fetchInterviewsStudyList(slug);
if (companyGuide == null) {
return notFound();
}
const companyQuestions = {
const companyGuideQuestions = {
algo: companyGuide.questionsAlgo ?? [],
javascript: companyGuide.questionsJavaScript ?? [],
quiz: companyGuide.questionsQuiz ?? [],
@ -77,7 +73,7 @@ export default async function Page({ params }: Props) {
};
const [questions, bottomContent] = await Promise.all([
fetchQuestionsBySlug(companyQuestions, locale),
fetchQuestionsBySlug(companyGuideQuestions, locale),
fetchInterviewListingBottomContent('company-detail'),
]);
const codingQuestions = [
@ -95,7 +91,7 @@ export default async function Page({ params }: Props) {
}
codingQuestions={codingQuestions}
companyGuide={companyGuide}
companyQuestions={companyQuestions}
companyQuestions={companyGuideQuestions}
quizQuestions={sortQuestions(
quizQuestions as ReadonlyArray<QuestionMetadata>,
'importance',

View File

@ -4,8 +4,8 @@ import { INTERVIEWS_REVAMP_BOTTOM_CONTENT } from '~/data/FeatureFlags';
import InterviewsCompanyGuideListPage from '~/components/interviews/company/InterviewsCompanyGuideListPage';
import { fetchInterviewsLearningLists } from '~/db/contentlayer/InterviewsLearningListReader';
import { fetchInterviewListingBottomContent } from '~/db/contentlayer/InterviewsListingBottomContentReader';
import { fetchInterviewsStudyLists } from '~/db/contentlayer/InterviewsStudyListReader';
import { getIntlServerOnly } from '~/i18n';
import defaultMetadata from '~/seo/defaultMetadata';
@ -57,7 +57,7 @@ export async function generateMetadata({ params }: Props): Promise<Metadata> {
export default async function Page() {
const [companyGuides, bottomContent] = await Promise.all([
fetchInterviewsLearningLists('company'),
fetchInterviewsStudyLists('company'),
fetchInterviewListingBottomContent('company'),
]);
const sortedGuides = companyGuides

View File

@ -14,7 +14,7 @@ import { getPreparationPlanTheme } from '~/data/plans/PreparationPlans';
import FeedbackDialog from '~/components/global/feedback/FeedbackDialog';
import { useUserPreferences } from '~/components/global/UserPreferencesProvider';
import type { QuestionMetadata } from '~/components/interviews/questions/common/QuestionsTypes';
import QuestionsLearningList from '~/components/interviews/questions/listings/learning/QuestionsLearningList';
import QuestionsLearningList from '~/components/interviews/questions/listings/learning/QuestionsStudyList';
import InterviewsRecommendedPrepStrategyPageTitleSection from '~/components/interviews/recommended/InterviewsRecommendedPrepStrategyPageTitleSection';
import { FormattedMessage, useIntl } from '~/components/intl';
import MDXContent from '~/components/mdx/MDXContent';

View File

@ -22,9 +22,9 @@ import type {
QuestionMetadata,
} from '~/components/interviews/questions/common/QuestionsTypes';
import QuestionsList from '~/components/interviews/questions/listings/items/QuestionsList';
import QuestionsLearningList from '~/components/interviews/questions/listings/learning/QuestionsLearningList';
import QuestionsLearningListPageTitleSection from '~/components/interviews/questions/listings/learning/QuestionsLearningListPageTitleSection';
import QuestionsLearningListTitleSection from '~/components/interviews/questions/listings/learning/QuestionsLearningListTitleSection';
import QuestionsLearningList from '~/components/interviews/questions/listings/learning/QuestionsStudyList';
import QuestionsLearningListPageTitleSection from '~/components/interviews/questions/listings/learning/QuestionsStudyListPageTitleSection';
import QuestionsStudyListTitleSection_DEPRECATED from '~/components/interviews/questions/listings/learning/QuestionsStudyListTitleSection_DEPRECATED';
import { useIntl } from '~/components/intl';
import MDXContent from '~/components/mdx/MDXContent';
import Button from '~/components/ui/Button';
@ -147,7 +147,7 @@ export default function InterviewsStudyPlanPage({
<Divider />
</>
) : (
<QuestionsLearningListTitleSection
<QuestionsStudyListTitleSection_DEPRECATED
description={plan.description}
difficultySummary={difficultySummary}
feature="study-plans"

View File

@ -2,6 +2,7 @@ import type { Metadata } from 'next/types';
import { CourseJsonLd } from 'next-seo';
import type { IntlShape } from 'react-intl';
import { INTERVIEWS_REVAMP_BOTTOM_CONTENT } from '~/data/FeatureFlags';
import type { PreparationPlanType } from '~/data/plans/PreparationPlans';
import { getPreparationPlan } from '~/data/plans/PreparationPlans';
@ -20,7 +21,6 @@ import defaultMetadata from '~/seo/defaultMetadata';
import { getSiteOrigin } from '~/seo/siteUrl';
import InterviewsStudyPlanPage from './InterviewsStudyPlanPage';
import { INTERVIEWS_REVAMP_BOTTOM_CONTENT } from '../../../../../../../../data/FeatureFlags';
async function getPreparationPlansSEO(
planType: PreparationPlanType,

View File

@ -13,7 +13,7 @@ import {
} from '~/data/plans/PreparationPlans';
import InterviewsListPageHeader from '~/components/interviews/common/InterviewsListPageHeader';
import InterviewsLearningListCard from '~/components/interviews/questions/listings/learning/InterviewsLearningListCard';
import InterviewsStudyListCard from '~/components/interviews/questions/listings/learning/InterviewsStudyListCard';
import InterviewsStudyPlanTestimonialsSection from '~/components/interviews/questions/listings/learning/study-plan/InterviewsStudyPlanTestimonialsSection';
import { useIntl } from '~/components/intl';
import MDXContent from '~/components/mdx/MDXContent';
@ -118,7 +118,7 @@ export default function InterviewsRevampStudyPlansPage({
const theme = getPreparationPlanTheme(studyPlan.type);
return (
<InterviewsLearningListCard
<InterviewsStudyListCard
key={studyPlan.type}
completionCount={completionCount}
isStarted={session != null}

View File

@ -9,8 +9,8 @@ import {
import InterviewsDashboardPage from '~/components/interviews/revamp-dashboard/InterviewsDashboardPage';
import { fetchInterviewsLearningLists } from '~/db/contentlayer/InterviewsLearningListReader';
import { fetchInterviewListingBottomContent } from '~/db/contentlayer/InterviewsListingBottomContentReader';
import { fetchInterviewsStudyLists } from '~/db/contentlayer/InterviewsStudyListReader';
import { fetchPreparationPlans } from '~/db/PreparationPlansReader';
import {
fetchQuestionsListCoding,
@ -65,7 +65,7 @@ export default async function Page({ params }: Props) {
fetchQuestionsListCoding(locale),
fetchQuestionsListSystemDesign(locale),
fetchInterviewListingBottomContent('dashboard'),
fetchInterviewsLearningLists('company'),
fetchInterviewsStudyLists('company'),
]);
const { framework, language } = categorizeQuestionsByFrameworkAndLanguage({
codingQuestions,

View File

@ -80,7 +80,7 @@ export default async function Page({ params }: Props) {
const isQuestionLockedForUser =
question.metadata.premium && !canViewPremiumContent;
// TODO(interviews/playlist): fetch playlist questions instead.
// TODO(interviews/learning-list): fetch list questions instead.
const { questions: codingQuestions } = await fetchQuestionsListCoding(locale);
const nextQuestions = sortQuestionsMultiple(
codingQuestions.filter((questionItem) =>

View File

@ -80,7 +80,7 @@ export default async function Page({ params }: Props) {
const isQuestionLockedForUser =
question.metadata.premium && !canViewPremiumContent;
// TODO(interviews/playlist): fetch playlist questions instead.
// TODO(interviews/learning-list): fetch list questions instead.
const { questions: codingQuestions } = await fetchQuestionsListCoding(locale);
const nextQuestions = sortQuestionsMultiple(
codingQuestions.filter((questionItem) =>

View File

@ -171,7 +171,7 @@ export default async function Page({ params }: Props) {
question.metadata.premium && !canViewPremiumContent;
const { url } = frameworkAgnosticLinks(question, mode);
// TODO(interviews/playlist): fetch playlist questions instead.
// TODO(interviews/learning-list): fetch list questions instead.
const { questions: codingQuestions } = await fetchQuestionsListCoding(locale);
const nextQuestions = sortQuestionsMultiple(
codingQuestions.filter((questionItem) =>

View File

@ -21,7 +21,7 @@ import InterviewsMarketingQuestionCardMarquee from '~/components/interviews/mark
import InterviewsPaymentFailureDialog from '~/components/interviews/purchase/InterviewsPaymentFailureDialog';
import type { QuestionMetadata } from '~/components/interviews/questions/common/QuestionsTypes';
import QuestionListingTopicFilters from '~/components/interviews/questions/listings/filters/QuestionListingTopicFilters';
import QuestionsLearningListTitleSection from '~/components/interviews/questions/listings/learning/QuestionsLearningListTitleSection';
import QuestionsStudyListTitleSection_DEPRECATED from '~/components/interviews/questions/listings/learning/QuestionsStudyListTitleSection_DEPRECATED';
import QuestionListingDifficultySummary from '~/components/interviews/questions/listings/stats/QuestionListingDifficultySummary';
import QuestionsProgressPanel from '~/components/interviews/questions/listings/stats/QuestionsProgressPanel';
import QuestionCountLabel from '~/components/interviews/questions/metadata/QuestionCountLabel';
@ -372,7 +372,7 @@ export default function ScrapbookPage() {
<Section>
<div>
<UIExamplesGroup>
<QuestionsLearningListTitleSection
<QuestionsStudyListTitleSection_DEPRECATED
difficultySummary={{
easy: 30,
hard: 10,

View File

@ -22,7 +22,7 @@ const GuidesContext = createContext<GuidesContextType>({
export function useGuidesContext() {
const context = useContext(GuidesContext);
// TODO(interviews/playlist): define GuideContext for playlist mode.
// TODO(interviews/learning-list): define GuideContext for learning-list mode.
// if (context === undefined) {
// throw new Error(
// 'useGuidesContext must be used within a GuidesContextProvider',

View File

@ -1,5 +1,5 @@
import clsx from 'clsx';
import type { InterviewsLearningList } from 'contentlayer/generated';
import type { InterviewsStudyList } from 'contentlayer/generated';
import { RiArrowRightLine } from 'react-icons/ri';
import { INTERVIEWS_REVAMP_2024 } from '~/data/FeatureFlags';
@ -21,7 +21,7 @@ import CompletionCountSummary from '../questions/listings/stats/CompletionCountS
import QuestionCountLabel from '../questions/metadata/QuestionCountLabel';
type Props = Readonly<{
companyGuide: InterviewsLearningList;
companyGuide: InterviewsStudyList;
completionCount?: number;
isStarted?: boolean;
showProgressBar?: boolean;

View File

@ -2,8 +2,8 @@
import clsx from 'clsx';
import type {
InterviewsLearningList,
InterviewsListingBottomContent,
InterviewsStudyList,
} from 'contentlayer/generated';
import {
RiThumbUpLine,
@ -26,10 +26,10 @@ import InterviewsCompanyGuideListWithFilters from './InterviewsCompanyGuideListW
type Props = Readonly<{
bottomContent?: InterviewsListingBottomContent;
companyGuides: Array<InterviewsLearningList>;
companyGuides: Array<InterviewsStudyList>;
}>;
export default function InterviewsLearningListListPage({
export default function InterviewsStudyListListPage({
companyGuides,
bottomContent,
}: Props) {

View File

@ -1,5 +1,5 @@
import clsx from 'clsx';
import type { InterviewsLearningList } from 'contentlayer/generated';
import type { InterviewsStudyList } from 'contentlayer/generated';
import { useState } from 'react';
import { RiSearchLine } from 'react-icons/ri';
@ -21,10 +21,10 @@ import { InterviewsCompanyGuideCard } from './InterviewsCompanyGuideCard';
import { useUser } from '@supabase/auth-helpers-react';
type Props = Readonly<{
companyGuides: Array<InterviewsLearningList>;
companyGuides: Array<InterviewsStudyList>;
}>;
export default function InterviewsLearningListListWithFilters({
export default function InterviewsStudyListListWithFilters({
companyGuides,
}: Props) {
const intl = useIntl();

View File

@ -2,8 +2,8 @@
import clsx from 'clsx';
import type {
InterviewsLearningList,
InterviewsListingBottomContent,
InterviewsStudyList,
} from 'contentlayer/generated';
import { useMemo } from 'react';
import {
@ -27,9 +27,9 @@ import type {
QuestionTopic,
} from '~/components/interviews/questions/common/QuestionsTypes';
import QuestionsList from '~/components/interviews/questions/listings/items/QuestionsList';
import QuestionsLearningList from '~/components/interviews/questions/listings/learning/QuestionsLearningList';
import QuestionsLearningListPageTitleSection from '~/components/interviews/questions/listings/learning/QuestionsLearningListPageTitleSection';
import QuestionsLearningListTitleSection from '~/components/interviews/questions/listings/learning/QuestionsLearningListTitleSection';
import QuestionsLearningList from '~/components/interviews/questions/listings/learning/QuestionsStudyList';
import QuestionsLearningListPageTitleSection from '~/components/interviews/questions/listings/learning/QuestionsStudyListPageTitleSection';
import QuestionsStudyListTitleSection_DEPRECATED from '~/components/interviews/questions/listings/learning/QuestionsStudyListTitleSection_DEPRECATED';
import { useIntl } from '~/components/intl';
import MDXContent from '~/components/mdx/MDXContent';
import Button from '~/components/ui/Button';
@ -53,7 +53,7 @@ import { useUser } from '@supabase/auth-helpers-react';
type Props = Readonly<{
bottomContent?: InterviewsListingBottomContent;
codingQuestions: ReadonlyArray<QuestionMetadata>;
companyGuide: InterviewsLearningList;
companyGuide: InterviewsStudyList;
companyQuestions: Record<QuestionFormat, ReadonlyArray<QuestionSlug>>;
quizQuestions: ReadonlyArray<QuestionMetadata>;
systemDesignQuestions: ReadonlyArray<QuestionMetadata>;
@ -200,7 +200,7 @@ export default function InterviewsCompanyGuidePage({
)}
</>
) : (
<QuestionsLearningListTitleSection
<QuestionsStudyListTitleSection_DEPRECATED
description={<MDXContent mdxCode={companyGuide.body.code} />}
feature="company-guides"
icon={({ className, ...props }) => (

View File

@ -1,6 +1,6 @@
import {
getQuestionListThemes,
useQuestionLists,
useQuestionLists_DEPRECATED,
} from '~/data/question-lists/QuestionListsHooks';
import InterviewsDashboardContinueLearning from '~/components/interviews/dashboard/InterviewsDashboardContinueLearning';
@ -17,7 +17,7 @@ type Props = Readonly<{
export default function InterviewsDashboardContinueLearningContainer({
items,
}: Props) {
const questionLists = useQuestionLists();
const questionLists = useQuestionLists_DEPRECATED();
const themes = getQuestionListThemes();
return (

View File

@ -1,4 +1,4 @@
import { useFocusAreas } from '~/data/focus-areas/FocusAreasHooks';
import { useFocusAreas_DEPRECATED } from '~/data/focus-areas/FocusAreasHooks';
import { useIntl } from '~/components/intl';
@ -12,7 +12,7 @@ export default function InterviewsDashboardFeaturedFocusAreas({
limit = Infinity,
}: Props) {
const intl = useIntl();
const focusAreas = useFocusAreas();
const focusAreas = useFocusAreas_DEPRECATED();
const areas = [
focusAreas['async-operations'],
focusAreas['data-structures-algorithms'],

View File

@ -1,8 +1,8 @@
import clsx from 'clsx';
import { RiArrowRightLine, RiQuestionFill } from 'react-icons/ri';
import type { FocusArea } from '~/data/focus-areas/FocusAreas';
import { getFocusAreaTheme } from '~/data/focus-areas/FocusAreas';
import type { FocusArea_DEPRECATED } from '~/data/focus-areas/FocusAreas';
import { getFocusAreaTheme_DEPRECATED } from '~/data/focus-areas/FocusAreas';
import { useIntl } from '~/components/intl';
import Anchor from '~/components/ui/Anchor';
@ -25,7 +25,7 @@ import QuestionCountLabel from '../questions/metadata/QuestionCountLabel';
type Props = Readonly<{
description: string;
focusAreas: ReadonlyArray<FocusArea>;
focusAreas: ReadonlyArray<FocusArea_DEPRECATED>;
title: string;
}>;
@ -65,7 +65,7 @@ export default function InterviewsDashboardFocusAreasSection({
</div>
<CardContainer className="@4xl:grid-cols-4 @md:grid-cols-2 grid grid-cols-1 grid-rows-1 gap-3 md:gap-4 lg:gap-6">
{focusAreas.map(({ href, name, type, shortDescription, questions }) => {
const Icon = getFocusAreaTheme(type).iconSolid;
const Icon = getFocusAreaTheme_DEPRECATED(type).iconSolid;
return (
<Anchor key={type} href={href} variant="unstyled">

View File

@ -1,5 +1,5 @@
import clsx from 'clsx';
import type { InterviewsLearningList } from 'contentlayer/generated';
import type { InterviewsStudyList } from 'contentlayer/generated';
import { RiArrowRightLine } from 'react-icons/ri';
import { useMediaQuery } from 'usehooks-ts';
@ -13,7 +13,7 @@ import Text from '~/components/ui/Text';
import { themeGradientHeading } from '~/components/ui/theme';
type Props = Readonly<{
companyGuides: ReadonlyArray<InterviewsLearningList>;
companyGuides: ReadonlyArray<InterviewsStudyList>;
}>;
export default function InterviewsMarketingCompaniesSection({

View File

@ -171,7 +171,7 @@ export type QuestionQuiz = QuestionBase;
/**
* @deprecated
*
* Use InterviewsLearningListDocument instead.
* Use InterviewsStudyListDocument instead.
*/
export type QuestionList_DEPRECATED = Readonly<{
description: string;
@ -187,7 +187,7 @@ export type QuestionList_DEPRECATED = Readonly<{
shortDescription: string;
}>;
export type QuestionListTheme = Readonly<{
export type QuestionListTheme_DEPRECATED = Readonly<{
customIcon?: (props: { size?: 'lg' | 'md' | 'sm' }) => JSX.Element;
gradient: ThemeGradient;
iconOutline: (props: React.ComponentProps<'svg'>) => JSX.Element;

View File

@ -16,7 +16,7 @@ export function questionHrefWithList(
urlObject.searchParams.append('list', listKey);
return (
`/interviews/l/${listKey}` +
`/interviews/study/${listKey}` +
urlObject.pathname +
urlObject.search +
urlObject.hash

View File

@ -6,7 +6,7 @@ import type { PreparationPlanSchedule } from '~/data/plans/PreparationPlans';
import InterviewsEntityProgress from '~/components/interviews/common/InterviewsEntityProgress';
import type {
QuestionList_DEPRECATED,
QuestionListTheme,
QuestionListTheme_DEPRECATED,
} from '~/components/interviews/questions/common/QuestionsTypes';
import QuestionStudyAllocationLabel from '~/components/interviews/questions/metadata/QuestionStudyAllocationLabel';
import { useIntl } from '~/components/intl';
@ -30,10 +30,10 @@ type Props = Readonly<{
isStarted?: boolean;
metadata: QuestionList_DEPRECATED;
schedule?: PreparationPlanSchedule;
theme: QuestionListTheme;
theme: QuestionListTheme_DEPRECATED;
}>;
export default function InterviewsLearningListCard({
export default function InterviewsStudyListCard({
completionCount = 0,
metadata,
schedule,

View File

@ -9,7 +9,7 @@ function parseSlug(sourceFilePath: string) {
return sourceFilePath.split(path.posix.sep)[3].replace(/\.mdx$/, '');
}
export const InterviewsLearningListDocument = defineDocumentType(() => ({
export const InterviewsStudyListDocument = defineDocumentType(() => ({
computedFields: {
category: {
description: 'Type of list: study plan / focus area / company',
@ -18,7 +18,7 @@ export const InterviewsLearningListDocument = defineDocumentType(() => ({
type: 'enum',
},
href: {
description: 'Link to learning list page',
description: 'Link to study list page',
resolve: (doc) => {
const category = parseCategory(doc._raw.sourceFilePath);
@ -28,6 +28,9 @@ export const InterviewsLearningListDocument = defineDocumentType(() => ({
doc._raw.sourceFilePath,
)}/questions-guides`;
}
case 'focus-area': {
return `/focus-area/${parseSlug(doc._raw.sourceFilePath)}`;
}
}
},
type: 'string',
@ -112,6 +115,6 @@ export const InterviewsLearningListDocument = defineDocumentType(() => ({
type: 'string',
},
},
filePathPattern: 'interviews/learning-list/**/*.mdx',
name: 'InterviewsLearningList',
filePathPattern: 'interviews/study-list/**/*.mdx',
name: 'InterviewsStudyList',
}));

View File

@ -34,7 +34,10 @@ type Props = Readonly<{
title: string;
}>;
export default function QuestionsLearningListTitleSection({
/**
* @deprecated
*/
export default function QuestionsStudyListTitleSection_DEPRECATED({
description,
difficultySummary,
icon: Icon,

View File

@ -29,7 +29,7 @@ import type { QuestionProgress } from '~/db/QuestionsProgressTypes';
import InterviewsPageHeaderActions from '../common/InterviewsPageHeaderActions';
import type { QuestionMetadata } from '../questions/common/QuestionsTypes';
import QuestionsLearningListPageTitleSection from '../questions/listings/learning/QuestionsLearningListPageTitleSection';
import QuestionsLearningListPageTitleSection from '../questions/listings/learning/QuestionsStudyListPageTitleSection';
import QuestionListingQuestionCount from '../questions/listings/stats/QuestionListingQuestionCount';
import { useUser } from '@supabase/auth-helpers-react';

View File

@ -2,7 +2,7 @@ import clsx from 'clsx';
import {
getQuestionListThemes,
useQuestionLists,
useQuestionLists_DEPRECATED,
} from '~/data/question-lists/QuestionListsHooks';
import getProgressBarGradient from '~/components/interviews/common/utils';
@ -35,7 +35,7 @@ export default function InterviewsDashboardContinueLearningSection({
questionListSessions,
}: Props) {
const intl = useIntl();
const questionLists = useQuestionLists();
const questionLists = useQuestionLists_DEPRECATED();
const themes = getQuestionListThemes();
const items = questionListSessions

View File

@ -1,5 +1,5 @@
import clsx from 'clsx';
import type { InterviewsLearningList } from 'contentlayer/generated';
import type { InterviewsStudyList } from 'contentlayer/generated';
import type { PreparationPlans } from '~/data/plans/PreparationPlans';
@ -25,7 +25,7 @@ import InterviewsDashboardPracticeQuestionsSection from './practice/InterviewsDa
import type { LearningSession } from '@prisma/client';
type Props = Readonly<{
companyGuides: Array<InterviewsLearningList>;
companyGuides: Array<InterviewsStudyList>;
guidesProgress: ReadonlyArray<
Readonly<{ id: string; slug: string; type: GuideCategory }>
>;

View File

@ -2,8 +2,8 @@
import clsx from 'clsx';
import type {
InterviewsLearningList,
InterviewsListingBottomContent,
InterviewsStudyList,
} from 'contentlayer/generated';
import { useMemo } from 'react';
@ -33,7 +33,7 @@ import { useUser } from '@supabase/auth-helpers-react';
type Props = Readonly<{
bottomContent?: InterviewsListingBottomContent;
companyGuides: Array<InterviewsLearningList>;
companyGuides: Array<InterviewsStudyList>;
preparationPlans: PreparationPlans;
questions: {
codingQuestions: ReadonlyArray<QuestionMetadata>;

View File

@ -1,4 +1,4 @@
import type { InterviewsLearningList } from 'contentlayer/generated';
import type { InterviewsStudyList } from 'contentlayer/generated';
import { useIntl } from '~/components/intl';
@ -8,7 +8,7 @@ import { InterviewsCompanyGuideCard } from '../company/InterviewsCompanyGuideCar
import type { LearningSession } from '@prisma/client';
type Props = Readonly<{
companyGuides: Array<InterviewsLearningList>;
companyGuides: Array<InterviewsStudyList>;
questionListSessions: Array<
LearningSession & { _count: { progress: number } }
>;

View File

@ -3,7 +3,7 @@ import {
type PreparationPlans,
} from '~/data/plans/PreparationPlans';
import InterviewsLearningListCard from '~/components/interviews/questions/listings/learning/InterviewsLearningListCard';
import InterviewsStudyListCard from '~/components/interviews/questions/listings/learning/InterviewsStudyListCard';
import { useIntl } from '~/components/intl';
import InterviewsDashboardLearningSection from './InterviewsDashboardLearningSection';
@ -48,7 +48,7 @@ export default function InterviewsDashboardStudyPlansSection({
const theme = getPreparationPlanTheme(studyPlan.type);
return (
<InterviewsLearningListCard
<InterviewsStudyListCard
key={studyPlan.type}
completionCount={completionCount}
isStarted={session != null}

View File

@ -1,9 +1,9 @@
import {
categorizeFocusAreas,
getFocusAreaTheme,
categorizeFocusAreas_DEPRECATED,
getFocusAreaTheme_DEPRECATED,
} from '~/data/focus-areas/FocusAreas';
import InterviewsLearningListCard from '~/components/interviews/questions/listings/learning/InterviewsLearningListCard';
import InterviewsStudyListCard from '~/components/interviews/questions/listings/learning/InterviewsStudyListCard';
import InterviewsDashboardLearningSection from '~/components/interviews/revamp-dashboard/InterviewsDashboardLearningSection';
import { useIntl } from '~/components/intl';
import Text from '~/components/ui/Text';
@ -20,7 +20,7 @@ export default function InterviewsDashboardPracticeByFocusAreasSection({
questionListSessions,
}: Props) {
const intl = useIntl();
const focusAreasCategories = categorizeFocusAreas(intl);
const focusAreasCategories = categorizeFocusAreas_DEPRECATED(intl);
return (
<InterviewsDashboardLearningSection
@ -48,10 +48,10 @@ export default function InterviewsDashboardPracticeByFocusAreasSection({
(session_) => session_.key === focusArea.type,
);
const completionCount = session?._count.progress;
const theme = getFocusAreaTheme(focusArea.type);
const theme = getFocusAreaTheme_DEPRECATED(focusArea.type);
return (
<InterviewsLearningListCard
<InterviewsStudyListCard
key={focusArea.type}
completionCount={completionCount}
isStarted={session != null}

View File

@ -0,0 +1,56 @@
// @ts-check
import { makeSource } from 'contentlayer/source-files';
import remarkFrontmatter from 'remark-frontmatter';
import remarkGfm from 'remark-gfm';
import remarkMdxFrontmatter from 'remark-mdx-frontmatter';
import { BlogAuthorDocument } from '../components/blog/contentlayer/BlogAuthorDocument';
import { BlogCategoryDocument } from '../components/blog/contentlayer/BlogCategoryDocument';
import { BlogPostDocument } from '../components/blog/contentlayer/BlogPostDocument';
import { BlogSeriesDocument } from '../components/blog/contentlayer/BlogSeriesDocument';
import { BlogSubseriesDocument } from '../components/blog/contentlayer/BlogSubseriesDocument';
import { JobsPostingDocument } from '../components/hiring/contentlayer/JobsPostingDocument';
import { InterviewsListingBottomContentDocument } from '../components/interviews/questions/listings/InterviewsListingBottomContentDocument';
import { InterviewsStudyListDocument } from '../components/interviews/questions/listings/learning/InterviewsStudyListDocument';
import { ProjectsChallengeAPIWriteupDocument } from '../components/projects/contentlayer/ProjectsChallengeAPIWriteupDocument';
import { ProjectsChallengeAppendixDocument } from '../components/projects/contentlayer/ProjectsChallengeAppendixDocument';
import { ProjectsChallengeBriefDocument } from '../components/projects/contentlayer/ProjectsChallengeBriefDocument';
import { ProjectsChallengeGuideDocument } from '../components/projects/contentlayer/ProjectsChallengeGuideDocument';
import { ProjectsChallengeInfoDocument } from '../components/projects/contentlayer/ProjectsChallengeInfoDocument';
import { ProjectsChallengeMetadataDocument } from '../components/projects/contentlayer/ProjectsChallengeMetadataDocument';
import { ProjectsChallengeStyleGuideDocument } from '../components/projects/contentlayer/ProjectsChallengeStyleGuideDocument';
import { ProjectsCommonGuideDocument } from '../components/projects/contentlayer/ProjectsCommonGuideDocument';
import { ProjectsSkillInfoDocument } from '../components/projects/contentlayer/ProjectsSkillInfoDocument';
import { ProjectsSkillMetadataDocument } from '../components/projects/contentlayer/ProjectsSkillMetadataDocument';
import { ProjectsTrackInfoDocument } from '../components/projects/contentlayer/ProjectsTrackInfoDocument';
import { ProjectsTrackMetadataDocument } from '../components/projects/contentlayer/ProjectsTrackMetadataDocument';
export default makeSource({
contentDirPath: 'src/content',
documentTypes: [
BlogAuthorDocument,
BlogCategoryDocument,
BlogPostDocument,
BlogSeriesDocument,
BlogSubseriesDocument,
InterviewsStudyListDocument,
InterviewsListingBottomContentDocument,
ProjectsCommonGuideDocument,
ProjectsChallengeAppendixDocument,
ProjectsChallengeBriefDocument,
ProjectsChallengeGuideDocument,
ProjectsChallengeInfoDocument,
ProjectsChallengeMetadataDocument,
ProjectsChallengeStyleGuideDocument,
ProjectsChallengeAPIWriteupDocument,
ProjectsSkillMetadataDocument,
ProjectsSkillInfoDocument,
ProjectsTrackMetadataDocument,
ProjectsTrackInfoDocument,
JobsPostingDocument,
],
mdx: {
remarkPlugins: [remarkGfm, remarkFrontmatter, remarkMdxFrontmatter],
},
});

View File

@ -0,0 +1,31 @@
---
name: Accessibility
longName: Accessibility
description: Targeted practice on Accessibility interview questions
shortDescription: Practice developing inclusive and accessible web experiences.
seoTitle: Practice Accessibility Interview Questions with Solutions
seoDescription: Practice front end Accessibility interview questions on semantic HTML, ARIA, and screen readers. Code in-browser with curated solutions from ex-interviewers.
socialTitle: Accessibility Interview Questions | GreatFrontEnd
ranking: 999
questionsAlgo: []
questionsJavaScript: []
questionsQuiz: []
questionsSystemDesign: []
questionsUserInterface:
- accordion
- accordion-ii
- accordion-iii
- auth-code-input
- file-explorer-ii
- modal-dialog
- modal-dialog-ii
- modal-dialog-iii
- modal-dialog-iv
- tabs
- tabs-ii
- tabs-iii
---
```
```

View File

@ -0,0 +1,48 @@
---
name: Async Operations
longName: Async Operations
description: Targeted practice on Async Operations interview questions
shortDescription: Sharpen your skills in asynchronous programming by practicing the use of async/await, Promises, and callback functions.
seoTitle: Practice Async Operations Interview Questions with Solutions
seoDescription: Practice async operations interview questions on async / await, Promises, and callbacks. Code in-browser with quality solutions and tests from ex-interviewers.
socialTitle: Async Operations Interview Questions | GreatFrontEnd
ranking: 999
questionsAlgo: []
questionsJavaScript:
- cancellable-interval
- cancellable-timeout
- debounce
- debounce-ii
- map-async
- map-async-limit
- promise-all
- promise-all-settled
- promise-any
- promise-merge
- promise-race
- promise-reject
- promise-resolve
- promise-timeout
- promise-with-resolvers
- promisify
- promisify-ii
- resumable-interval
- sleep
- throttle
questionsQuiz: []
questionsSystemDesign: []
questionsUserInterface:
- analog-clock
- birth-year-histogram
- digital-clock
- grid-lights
- progress-bars
- progress-bars-ii
- progress-bars-iii
- progress-bars-iv
- job-board
- like-button
- stopwatch
- traffic-light
- whack-a-mole
---

View File

@ -0,0 +1,37 @@
---
name: Data Structures & Algorithms
longName: Data Structures & Algorithms
description: Targeted practice on Data Structures & Algorithms interview questions
shortDescription: Hone your computer science fundamentals by implementing important data structures and algorithms from scratch.
seoTitle: Practice Data Structures and Algorithms Interview Questions
seoDescription: Practice data structures and algorithms interview questions in-browser with JavaScript / TypeScript solutions from ex-interviewers.
socialTitle: Data Structures and Algorithms Interview Questions | GreatFrontEnd
ranking: 999
questionsAlgo:
- stack
- queue
- merge-sort
- quick-sort
- heap-sort
- topological-sort
- insertion-sort
- selection-sort
- binary-search
- depth-first-search
- breadth-first-search
questionsJavaScript:
- data-merging
- data-selection
- event-emitter
- event-emitter-ii
- backbone-model
- table-of-contents
- unique-array
questionsQuiz: []
questionsSystemDesign: []
questionsUserInterface:
- transfer-list
- transfer-list-ii
- undoable-counter
- wordle
---

View File

@ -0,0 +1,30 @@
---
name: Design System Components
longName: Design System Components
description: Targeted practice on Design System Components interview questions
shortDescription: Elevate your front-end skills by practicing the creation of front end design system components.
seoTitle: Practice Design System Components Interview Questions
seoDescription: Practice design system components interview questions, including Tabs, Modals, Accordions, and Progress Bars. Code in-browser with solutions from ex-interviewers.
socialTitle: Design System Components Interview Questions | GreatFrontEnd
ranking: 999
questionsAlgo: []
questionsJavaScript: []
questionsQuiz: []
questionsSystemDesign:
- image-carousel
- dropdown-menu
- modal-dialog
questionsUserInterface:
- accordion
- accordion-ii
- accordion-iii
- modal-dialog
- modal-dialog-ii
- modal-dialog-iii
- modal-dialog-iv
- tabs
- tabs-ii
- tabs-iii
- progress-bar
- star-rating
---

View File

@ -0,0 +1,25 @@
---
name: DOM Manipulation
longName: DOM Manipulation
description: Targeted practice on DOM Manipulation interview questions
shortDescription: Familiarize with selecting elements using CSS selectors, traverse the DOM hierarchy, and manipulate their properties, content, and styles.
seoTitle: Practice DOM Manipulation Interview Questions with Solutions
seoDescription: Practice DOM manipulation interview questions on CSS selectors, DOM traversal, and element manipulation. Code in-browser with solutions from ex-interviewers.
socialTitle: DOM Manipulation Interview Questions | GreatFrontEnd
ranking: 999
questionsAlgo: []
questionsJavaScript:
- get-elements-by-class-name
- get-elements-by-style
- get-elements-by-tag-name
- get-elements-by-tag-name-hierarchy
- jquery-class-manipulation
- jquery-css
- html-serializer
- identical-dom-trees
- table-of-contents
- text-search
questionsQuiz: []
questionsSystemDesign: []
questionsUserInterface: []
---

View File

@ -0,0 +1,25 @@
---
name: Forms
longName: Forms
description: Targeted practice on Forms interview questions
shortDescription: Master the art of building interactive and user-friendly forms.
seoTitle: Practice Forms Interview Questions with Solutions
seoDescription: Practice form-related interview questions on form components, validation, and submission handling. Code in-browser with solutions from ex-interviewers.
socialTitle: Forms Interview Questions | GreatFrontEnd
ranking: 999
questionsAlgo: []
questionsJavaScript: []
questionsQuiz: []
questionsSystemDesign:
- e-commerce-amazon
questionsUserInterface:
- auth-code-input
- contact-form
- flight-booker
- mortgage-calculator
- nested-checkboxes
- signup-form
- todo-list
- temperature-converter
- transfer-list-ii
---

View File

@ -0,0 +1,41 @@
---
name: JavaScript Polyfills
longName: JavaScript Polyfills
description: Targeted practice on JavaScript Polyfills interview questions
shortDescription: Gain proficiency in front end fundamentals by implementing JavaScript and DOM APIs from scratch
seoTitle: Practice JavaScript Polyfills Interview Questions with Solutions
seoDescription: Practice JavaScript Polyfills interview questions by implementing JS and DOM APIs from scratch. Code in-browser with solutions by ex-interviewers
socialTitle: JavaScript Polyfills Interview Questions | GreatFrontEnd
ranking: 999
questionsAlgo: []
questionsJavaScript:
- array-at
- array-concat
- array-filter
- array-map
- array-reduce
- event-emitter
- event-emitter-ii
- find-index
- find-last-index
- flatten
- function-apply
- function-bind
- function-call
- get-elements-by-class-name
- get-elements-by-tag-name
- get-elements-by-tag-name-hierarchy
- json-stringify
- promise-all
- promise-all-settled
- promise-any
- promise-race
- promise-reject
- promise-resolve
- promise-with-resolvers
- type-utilities
- type-utilities-ii
questionsQuiz: []
questionsSystemDesign: []
questionsUserInterface: []
---

View File

@ -0,0 +1,43 @@
---
name: Lodash Functions
longName: Lodash Functions
description: Targeted practice on Lodash Functions interview questions
shortDescription: Strengthen your proficiency in JavaScript through writing Lodash functions from scratch.
seoTitle: Practice Lodash Interview Questions with Solutions
seoDescription: Practice Lodash interview questions, implementing functions to manipulate and transform data efficiently. Code in-browser with solutions from ex-interviewers.
socialTitle: Lodash Functions Interview Questions | GreatFrontEnd
ranking: 999
questionsAlgo: []
questionsJavaScript:
- chunk
- clamp
- compact
- count-by
- curry
- debounce
- deep-clone
- deep-equal
- difference
- drop-right-while
- drop-while
- fill
- find-index
- find-last-index
- flatten
- from-pairs
- get
- group-by
- in-range
- intersection
- intersection-by
- intersection-with
- is-empty
- limit
- once
- size
- throttle
- unique-array
questionsQuiz: []
questionsSystemDesign: []
questionsUserInterface: []
---

View File

@ -0,0 +1,32 @@
---
name: State Management
longName: State Management
description: Targeted practice on State Management interview questions
shortDescription: Train your skills in designing complex state and implementing operations to manipulate state.
seoTitle: Practice State Management Interview Questions with Solutions
seoDescription: Practice State Management interview questions that train your skills in complex state design and state manipulation. Code in-browser with solutions from ex-interviewers.
socialTitle: State Management Interview Questions | GreatFrontEnd
ranking: 999
questionsAlgo: []
questionsJavaScript: []
questionsQuiz: []
questionsSystemDesign: []
questionsUserInterface:
- connect-four
- data-table
- data-table-ii
- data-table-iii
- data-table-iv
- grid-lights
- image-carousel-ii
- image-carousel-iii
- memory-game
- nested-checkboxes
- pixel-art
- transfer-list
- transfer-list-ii
- undoable-counter
- users-database
- whack-a-mole
- wordle
---

View File

@ -2,7 +2,7 @@ import type { IntlShape } from 'react-intl';
import type {
QuestionList_DEPRECATED,
QuestionListTheme,
QuestionListTheme_DEPRECATED,
} from '~/components/interviews/questions/common/QuestionsTypes';
import {
@ -42,7 +42,8 @@ import {
getFocusAreaThemeStateManagement,
} from './items/FocusAreaStateManagement';
export type FocusAreaType =
/** @deprecated */
export type FocusAreaType_DEPRECATED =
| 'accessibility'
| 'async-operations'
| 'data-structures-algorithms'
@ -53,37 +54,51 @@ export type FocusAreaType =
| 'lodash'
| 'state-management';
// Can only contain serializable values as it's passed between the server-client boundary.
export type FocusArea = QuestionList_DEPRECATED &
/** @deprecated Can only contain serializable values as it's passed between the server-client boundary. */
export type FocusArea_DEPRECATED = QuestionList_DEPRECATED &
Readonly<{
type: FocusAreaType;
type: FocusAreaType_DEPRECATED;
}>;
export type FocusAreas = Record<FocusAreaType, FocusArea>;
/** @deprecated */
export type FocusAreas_DEPRECATED = Record<
FocusAreaType_DEPRECATED,
FocusArea_DEPRECATED
>;
export function getFocusAreas(intl: IntlShape): FocusAreas {
const focusAreas: FocusAreas = {
accessibility: getFocusArea('accessibility', intl),
'async-operations': getFocusArea('async-operations', intl),
'data-structures-algorithms': getFocusArea(
/** @deprecated */
export function getFocusAreas_DEPRECATED(
intl: IntlShape,
): FocusAreas_DEPRECATED {
const focusAreas: FocusAreas_DEPRECATED = {
accessibility: getFocusArea_DEPRECATED('accessibility', intl),
'async-operations': getFocusArea_DEPRECATED('async-operations', intl),
'data-structures-algorithms': getFocusArea_DEPRECATED(
'data-structures-algorithms',
intl,
),
'design-system-components': getFocusArea('design-system-components', intl),
'dom-manipulation': getFocusArea('dom-manipulation', intl),
forms: getFocusArea('forms', intl),
'javascript-polyfills': getFocusArea('javascript-polyfills', intl),
lodash: getFocusArea('lodash', intl),
'state-management': getFocusArea('state-management', intl),
'design-system-components': getFocusArea_DEPRECATED(
'design-system-components',
intl,
),
'dom-manipulation': getFocusArea_DEPRECATED('dom-manipulation', intl),
forms: getFocusArea_DEPRECATED('forms', intl),
'javascript-polyfills': getFocusArea_DEPRECATED(
'javascript-polyfills',
intl,
),
lodash: getFocusArea_DEPRECATED('lodash', intl),
'state-management': getFocusArea_DEPRECATED('state-management', intl),
};
return focusAreas;
}
export function getFocusArea(
focusArea: FocusAreaType,
/** @deprecated */
export function getFocusArea_DEPRECATED(
focusArea: FocusAreaType_DEPRECATED,
intl: IntlShape,
): FocusArea {
): FocusArea_DEPRECATED {
switch (focusArea) {
case 'accessibility':
return getFocusAreaAccessibility(intl);
@ -106,23 +121,33 @@ export function getFocusArea(
}
}
export function getFocusAreaThemes(): Record<FocusAreaType, QuestionListTheme> {
export function getFocusAreaThemes_DEPRECATED(): Record<
FocusAreaType_DEPRECATED,
QuestionListTheme_DEPRECATED
> {
return {
accessibility: getFocusAreaTheme('accessibility'),
'async-operations': getFocusAreaTheme('async-operations'),
'data-structures-algorithms': getFocusAreaTheme(
accessibility: getFocusAreaTheme_DEPRECATED('accessibility'),
'async-operations': getFocusAreaTheme_DEPRECATED('async-operations'),
'data-structures-algorithms': getFocusAreaTheme_DEPRECATED(
'data-structures-algorithms',
),
'design-system-components': getFocusAreaTheme('design-system-components'),
'dom-manipulation': getFocusAreaTheme('dom-manipulation'),
forms: getFocusAreaTheme('forms'),
'javascript-polyfills': getFocusAreaTheme('javascript-polyfills'),
lodash: getFocusAreaTheme('lodash'),
'state-management': getFocusAreaTheme('state-management'),
'design-system-components': getFocusAreaTheme_DEPRECATED(
'design-system-components',
),
'dom-manipulation': getFocusAreaTheme_DEPRECATED('dom-manipulation'),
forms: getFocusAreaTheme_DEPRECATED('forms'),
'javascript-polyfills': getFocusAreaTheme_DEPRECATED(
'javascript-polyfills',
),
lodash: getFocusAreaTheme_DEPRECATED('lodash'),
'state-management': getFocusAreaTheme_DEPRECATED('state-management'),
};
}
export function getFocusAreaTheme(focusArea: FocusAreaType): QuestionListTheme {
/** @deprecated */
export function getFocusAreaTheme_DEPRECATED(
focusArea: string,
): QuestionListTheme_DEPRECATED {
switch (focusArea) {
case 'accessibility':
return getFocusAreaThemeAccessibility();
@ -142,11 +167,14 @@ export function getFocusAreaTheme(focusArea: FocusAreaType): QuestionListTheme {
return getFocusAreaThemeJavaScriptPolyfills();
case 'state-management':
return getFocusAreaThemeStateManagement();
default:
throw Error('No such focus area');
}
}
export function categorizeFocusAreas(intl: IntlShape) {
const focusAreas = getFocusAreas(intl);
/** @deprecated */
export function categorizeFocusAreas_DEPRECATED(intl: IntlShape) {
const focusAreas = getFocusAreas_DEPRECATED(intl);
return [
{

View File

@ -1,17 +1,12 @@
import { useIntl } from '~/components/intl';
import type { FocusAreaType } from './FocusAreas';
import { getFocusArea } from './FocusAreas';
import { getFocusAreas } from './FocusAreas';
import { getFocusAreas_DEPRECATED } from './FocusAreas';
export function useFocusAreas() {
/**
* @deprecated
*/
export function useFocusAreas_DEPRECATED() {
const intl = useIntl();
return getFocusAreas(intl);
}
export function useFocusArea(focusArea: FocusAreaType) {
const intl = useIntl();
return getFocusArea(focusArea, intl);
return getFocusAreas_DEPRECATED(intl);
}

View File

@ -1,12 +1,14 @@
import { BiUniversalAccess } from 'react-icons/bi';
import type { IntlShape } from 'react-intl';
import type { QuestionListTheme } from '~/components/interviews/questions/common/QuestionsTypes';
import type { QuestionListTheme_DEPRECATED } from '~/components/interviews/questions/common/QuestionsTypes';
import type { ThemeGradient } from '~/components/ui/theme';
import type { FocusArea } from '../FocusAreas';
import type { FocusArea_DEPRECATED } from '../FocusAreas';
export function getFocusAreaAccessibility(intl: IntlShape): FocusArea {
export function getFocusAreaAccessibility(
intl: IntlShape,
): FocusArea_DEPRECATED {
return {
description: intl.formatMessage({
defaultMessage: 'Targeted practice on Accessibility interview questions',
@ -79,7 +81,7 @@ const gradient: ThemeGradient<'#f953c6', '#b91d73'> = {
startColor: '#f953c6',
};
export function getFocusAreaThemeAccessibility(): QuestionListTheme {
export function getFocusAreaThemeAccessibility(): QuestionListTheme_DEPRECATED {
return {
gradient,
iconOutline: BiUniversalAccess,

View File

@ -1,12 +1,14 @@
import { RiRefreshLine } from 'react-icons/ri';
import type { IntlShape } from 'react-intl';
import type { QuestionListTheme } from '~/components/interviews/questions/common/QuestionsTypes';
import type { QuestionListTheme_DEPRECATED } from '~/components/interviews/questions/common/QuestionsTypes';
import type { ThemeGradient } from '~/components/ui/theme';
import type { FocusArea } from '../FocusAreas';
import type { FocusArea_DEPRECATED } from '../FocusAreas';
export function getFocusAreaAsyncOperations(intl: IntlShape): FocusArea {
export function getFocusAreaAsyncOperations(
intl: IntlShape,
): FocusArea_DEPRECATED {
return {
description: intl.formatMessage({
defaultMessage:
@ -102,7 +104,7 @@ const gradient: ThemeGradient<'#8E2DE2', '#4A00E0'> = {
startColor: '#8E2DE2',
};
export function getFocusAreaThemeAsyncOperations(): QuestionListTheme {
export function getFocusAreaThemeAsyncOperations(): QuestionListTheme_DEPRECATED {
return {
gradient,
iconOutline: RiRefreshLine,

View File

@ -1,12 +1,14 @@
import { RiWindowFill } from 'react-icons/ri';
import type { IntlShape } from 'react-intl';
import type { QuestionListTheme } from '~/components/interviews/questions/common/QuestionsTypes';
import type { QuestionListTheme_DEPRECATED } from '~/components/interviews/questions/common/QuestionsTypes';
import type { ThemeGradient } from '~/components/ui/theme';
import type { FocusArea } from '../FocusAreas';
import type { FocusArea_DEPRECATED } from '../FocusAreas';
export function getFocusAreaDOMManipulation(intl: IntlShape): FocusArea {
export function getFocusAreaDOMManipulation(
intl: IntlShape,
): FocusArea_DEPRECATED {
return {
description: intl.formatMessage({
defaultMessage:
@ -78,7 +80,7 @@ const gradient: ThemeGradient<'#bc4e9c', '#f80759'> = {
startColor: '#bc4e9c',
};
export function getFocusAreaThemeDOMManipulation(): QuestionListTheme {
export function getFocusAreaThemeDOMManipulation(): QuestionListTheme_DEPRECATED {
return {
gradient,
iconOutline: RiWindowFill,

View File

@ -1,15 +1,15 @@
import { TbBinaryTree } from 'react-icons/tb';
import type { IntlShape } from 'react-intl';
import type { QuestionListTheme } from '~/components/interviews/questions/common/QuestionsTypes';
import type { QuestionListTheme_DEPRECATED } from '~/components/interviews/questions/common/QuestionsTypes';
import { QuestionCount } from '~/components/interviews/questions/listings/stats/QuestionCount';
import type { ThemeGradient } from '~/components/ui/theme';
import type { FocusArea } from '../FocusAreas';
import type { FocusArea_DEPRECATED } from '../FocusAreas';
export function getFocusAreaDataStructuresAlgorithms(
intl: IntlShape,
): FocusArea {
): FocusArea_DEPRECATED {
return {
description: intl.formatMessage(
{
@ -101,7 +101,7 @@ const gradient: ThemeGradient<'#50C9C3', '#96DEDA'> = {
startColor: '#50C9C3',
};
export function getFocusAreaThemeDataStructuresAlgorithms(): QuestionListTheme {
export function getFocusAreaThemeDataStructuresAlgorithms(): QuestionListTheme_DEPRECATED {
return {
gradient,
iconOutline: TbBinaryTree,

View File

@ -1,12 +1,14 @@
import { RiDashboardLine } from 'react-icons/ri';
import type { IntlShape } from 'react-intl';
import type { QuestionListTheme } from '~/components/interviews/questions/common/QuestionsTypes';
import type { QuestionListTheme_DEPRECATED } from '~/components/interviews/questions/common/QuestionsTypes';
import type { ThemeGradient } from '~/components/ui/theme';
import type { FocusArea } from '../FocusAreas';
import type { FocusArea_DEPRECATED } from '../FocusAreas';
export function getFocusAreaDesignSystemComponents(intl: IntlShape): FocusArea {
export function getFocusAreaDesignSystemComponents(
intl: IntlShape,
): FocusArea_DEPRECATED {
return {
description: intl.formatMessage({
defaultMessage:
@ -80,7 +82,7 @@ const gradient: ThemeGradient<'#FF5F6D', '#FFC371'> = {
startColor: '#FF5F6D',
};
export function getFocusAreaThemeDesignSystemComponents(): QuestionListTheme {
export function getFocusAreaThemeDesignSystemComponents(): QuestionListTheme_DEPRECATED {
return {
gradient,
iconOutline: RiDashboardLine,

View File

@ -1,12 +1,12 @@
import { TbForms } from 'react-icons/tb';
import type { IntlShape } from 'react-intl';
import type { QuestionListTheme } from '~/components/interviews/questions/common/QuestionsTypes';
import type { QuestionListTheme_DEPRECATED } from '~/components/interviews/questions/common/QuestionsTypes';
import type { ThemeGradient } from '~/components/ui/theme';
import type { FocusArea } from '../FocusAreas';
import type { FocusArea_DEPRECATED } from '../FocusAreas';
export function getFocusAreaForms(intl: IntlShape): FocusArea {
export function getFocusAreaForms(intl: IntlShape): FocusArea_DEPRECATED {
return {
description: intl.formatMessage({
defaultMessage: 'Targeted practice on Forms interview questions',
@ -75,7 +75,7 @@ const gradient: ThemeGradient<'#56ab2f', '#a8e063'> = {
startColor: '#56ab2f',
};
export function getFocusAreaThemeForms(): QuestionListTheme {
export function getFocusAreaThemeForms(): QuestionListTheme_DEPRECATED {
return {
gradient,
iconOutline: TbForms,

View File

@ -1,12 +1,14 @@
import { RiJavascriptFill } from 'react-icons/ri';
import type { IntlShape } from 'react-intl';
import type { QuestionListTheme } from '~/components/interviews/questions/common/QuestionsTypes';
import type { QuestionListTheme_DEPRECATED } from '~/components/interviews/questions/common/QuestionsTypes';
import type { ThemeGradient } from '~/components/ui/theme';
import type { FocusArea } from '../FocusAreas';
import type { FocusArea_DEPRECATED } from '../FocusAreas';
export function getFocusAreaJavaScriptPolyfills(intl: IntlShape): FocusArea {
export function getFocusAreaJavaScriptPolyfills(
intl: IntlShape,
): FocusArea_DEPRECATED {
return {
description: intl.formatMessage({
defaultMessage:
@ -95,7 +97,7 @@ const gradient: ThemeGradient<'#f7df1e', '#f7df1e'> = {
startColor: '#f7df1e',
};
export function getFocusAreaThemeJavaScriptPolyfills(): QuestionListTheme {
export function getFocusAreaThemeJavaScriptPolyfills(): QuestionListTheme_DEPRECATED {
return {
gradient,
iconOutline: RiJavascriptFill,

View File

@ -1,12 +1,12 @@
import { SiLodash } from 'react-icons/si';
import type { IntlShape } from 'react-intl';
import type { QuestionListTheme } from '~/components/interviews/questions/common/QuestionsTypes';
import type { QuestionListTheme_DEPRECATED } from '~/components/interviews/questions/common/QuestionsTypes';
import type { ThemeGradient } from '~/components/ui/theme';
import type { FocusArea } from '../FocusAreas';
import type { FocusArea_DEPRECATED } from '../FocusAreas';
export function getFocusAreaLodash(intl: IntlShape): FocusArea {
export function getFocusAreaLodash(intl: IntlShape): FocusArea_DEPRECATED {
return {
description: intl.formatMessage({
defaultMessage:
@ -96,7 +96,7 @@ const gradient: ThemeGradient<'#7474BF', '#348AC7'> = {
startColor: '#7474BF',
};
export function getFocusAreaThemeLodash(): QuestionListTheme {
export function getFocusAreaThemeLodash(): QuestionListTheme_DEPRECATED {
return {
gradient,
iconOutline: SiLodash,

View File

@ -1,12 +1,14 @@
import { RiFlowChart } from 'react-icons/ri';
import type { IntlShape } from 'react-intl';
import type { QuestionListTheme } from '~/components/interviews/questions/common/QuestionsTypes';
import type { QuestionListTheme_DEPRECATED } from '~/components/interviews/questions/common/QuestionsTypes';
import type { ThemeGradient } from '~/components/ui/theme';
import type { FocusArea } from '../FocusAreas';
import type { FocusArea_DEPRECATED } from '../FocusAreas';
export function getFocusAreaStateManagement(intl: IntlShape): FocusArea {
export function getFocusAreaStateManagement(
intl: IntlShape,
): FocusArea_DEPRECATED {
return {
description: intl.formatMessage({
defaultMessage:
@ -85,7 +87,7 @@ const gradient: ThemeGradient<'#4e54c8', '#8f94fb'> = {
startColor: '#4e54c8',
};
export function getFocusAreaThemeStateManagement(): QuestionListTheme {
export function getFocusAreaThemeStateManagement(): QuestionListTheme_DEPRECATED {
return {
gradient,
iconOutline: RiFlowChart,

View File

@ -2,7 +2,7 @@ import type { IntlShape } from 'react-intl';
import type {
QuestionList_DEPRECATED,
QuestionListTheme,
QuestionListTheme_DEPRECATED,
} from '~/components/interviews/questions/common/QuestionsTypes';
import {
@ -79,7 +79,7 @@ export function getPreparationPlan(
export function getPreparationPlanThemes(): Record<
PreparationPlanType,
QuestionListTheme
QuestionListTheme_DEPRECATED
> {
return {
blind75: getPreparationPlanTheme('greatfrontend75'),
@ -92,7 +92,7 @@ export function getPreparationPlanThemes(): Record<
export function getPreparationPlanTheme(
planType: PreparationPlanType,
): QuestionListTheme {
): QuestionListTheme_DEPRECATED {
switch (planType) {
case 'one-week':
return getPreparationPlanThemeOneWeek();

View File

@ -1,7 +1,7 @@
import { RiEye2Fill, RiEye2Line } from 'react-icons/ri';
import type { IntlShape } from 'react-intl';
import type { QuestionListTheme } from '~/components/interviews/questions/common/QuestionsTypes';
import type { QuestionListTheme_DEPRECATED } from '~/components/interviews/questions/common/QuestionsTypes';
import { type ThemeGradient } from '~/components/ui/theme';
import { PreparationPlanQuizImportanceHighJavaScript } from './PreparationPlanQuizQuestions';
@ -99,7 +99,7 @@ const gradient: ThemeGradient<'#B7B1FF', '#4B468B'> = {
startColor: '#B7B1FF',
};
export function getPreparationPlanThemeBlind75(): QuestionListTheme {
export function getPreparationPlanThemeBlind75(): QuestionListTheme_DEPRECATED {
return {
gradient,
iconOutline: RiEye2Line,

View File

@ -1,7 +1,7 @@
import { RiStarFill, RiStarLine } from 'react-icons/ri';
import type { IntlShape } from 'react-intl';
import type { QuestionListTheme } from '~/components/interviews/questions/common/QuestionsTypes';
import type { QuestionListTheme_DEPRECATED } from '~/components/interviews/questions/common/QuestionsTypes';
import { type ThemeGradient } from '~/components/ui/theme';
import { PreparationPlanQuizImportanceHighJavaScript } from './PreparationPlanQuizQuestions';
@ -98,7 +98,7 @@ const gradient: ThemeGradient<'#B7B1FF', '#4B468B'> = {
startColor: '#B7B1FF',
};
export function getPreparationPlanThemeGFE75(): QuestionListTheme {
export function getPreparationPlanThemeGFE75(): QuestionListTheme_DEPRECATED {
return {
customIcon: PreparationGFE75Logo,
gradient,

View File

@ -1,7 +1,7 @@
import { RiFireFill, RiFireLine } from 'react-icons/ri';
import type { IntlShape } from 'react-intl';
import type { QuestionListTheme } from '~/components/interviews/questions/common/QuestionsTypes';
import type { QuestionListTheme_DEPRECATED } from '~/components/interviews/questions/common/QuestionsTypes';
import type { ThemeGradient } from '~/components/ui/theme';
import {
@ -115,7 +115,7 @@ const gradient: ThemeGradient<'#bc4e9c', '#f80759'> = {
startColor: '#bc4e9c',
};
export function getPreparationPlanThemeOneMonth(): QuestionListTheme {
export function getPreparationPlanThemeOneMonth(): QuestionListTheme_DEPRECATED {
return {
gradient,
iconOutline: RiFireLine,

View File

@ -1,7 +1,7 @@
import { RiFlashlightFill, RiFlashlightLine } from 'react-icons/ri';
import type { IntlShape } from 'react-intl';
import type { QuestionListTheme } from '~/components/interviews/questions/common/QuestionsTypes';
import type { QuestionListTheme_DEPRECATED } from '~/components/interviews/questions/common/QuestionsTypes';
import type { ThemeGradient } from '~/components/ui/theme';
import {
@ -92,7 +92,7 @@ const gradient: ThemeGradient<'#f7ff00', '#db36a4'> = {
startColor: '#f7ff00',
};
export function getPreparationPlanThemeOneWeek(): QuestionListTheme {
export function getPreparationPlanThemeOneWeek(): QuestionListTheme_DEPRECATED {
return {
gradient,
iconOutline: RiFlashlightLine,

View File

@ -1,7 +1,7 @@
import { RiStarFill, RiStarLine } from 'react-icons/ri';
import type { IntlShape } from 'react-intl';
import type { QuestionListTheme } from '~/components/interviews/questions/common/QuestionsTypes';
import type { QuestionListTheme_DEPRECATED } from '~/components/interviews/questions/common/QuestionsTypes';
import type { ThemeGradient } from '~/components/ui/theme';
import {
@ -138,7 +138,7 @@ const gradient: ThemeGradient<'#7F00FF', '#E100FF'> = {
startColor: '#7F00FF',
};
export function getPreparationPlanThemeThreeMonths(): QuestionListTheme {
export function getPreparationPlanThemeThreeMonths(): QuestionListTheme_DEPRECATED {
return {
gradient,
iconOutline: RiStarLine,

View File

@ -1,23 +1,29 @@
import type {
QuestionList_DEPRECATED,
QuestionListTheme,
QuestionListTheme_DEPRECATED,
} from '~/components/interviews/questions/common/QuestionsTypes';
import { getFocusAreaThemes } from '../focus-areas/FocusAreas';
import { useFocusAreas } from '../focus-areas/FocusAreasHooks';
import { getFocusAreaThemes_DEPRECATED } from '../focus-areas/FocusAreas';
import { useFocusAreas_DEPRECATED } from '../focus-areas/FocusAreasHooks';
import { getPreparationPlanThemes } from '../plans/PreparationPlans';
import { usePreparationPlans } from '../plans/PreparationPlansHooks';
export function useQuestionLists(): Record<string, QuestionList_DEPRECATED> {
export function useQuestionLists_DEPRECATED(): Record<
string,
QuestionList_DEPRECATED
> {
const plans = usePreparationPlans();
const focusAreas = useFocusAreas();
const focusAreas = useFocusAreas_DEPRECATED();
return { ...plans, ...focusAreas };
}
export function getQuestionListThemes(): Record<string, QuestionListTheme> {
export function getQuestionListThemes(): Record<
string,
QuestionListTheme_DEPRECATED
> {
const planThemes = getPreparationPlanThemes();
const focusAreaThemes = getFocusAreaThemes();
const focusAreaThemes = getFocusAreaThemes_DEPRECATED();
return { ...planThemes, ...focusAreaThemes };
}

View File

@ -266,6 +266,12 @@ export function roundQuestionCountToNearestTen(count: number) {
return Math.floor(count / 10) * 10;
}
export function flattenQuestionFormatMetadata(
questions: Record<QuestionFormat, ReadonlyArray<QuestionMetadata>>,
): ReadonlyArray<QuestionMetadata> {
return Object.values(questions).flat();
}
export function countNumberOfQuestionsInList(
questions: Record<QuestionFormat, ReadonlyArray<QuestionSlug>>,
): number {

View File

@ -1,17 +0,0 @@
import type { InterviewsLearningList } from 'contentlayer/generated';
import { allInterviewsLearningLists } from '~/../.contentlayer/generated/InterviewsLearningList/_index.mjs';
export async function fetchInterviewsLearningList(slug: string) {
return allInterviewsLearningLists.find((content) => content.slug === slug) as
| InterviewsLearningList
| undefined;
}
export async function fetchInterviewsLearningLists(
categoryParam: InterviewsLearningList['category'],
): Promise<ReadonlyArray<InterviewsLearningList>> {
return (
allInterviewsLearningLists as ReadonlyArray<InterviewsLearningList>
).filter(({ category }) => categoryParam === category);
}

View File

@ -0,0 +1,19 @@
import type { InterviewsStudyList } from 'contentlayer/generated';
import { allInterviewsStudyLists } from '~/../.contentlayer/generated/InterviewsStudyList/_index.mjs';
export async function fetchInterviewsStudyList(
slug: string,
): Promise<InterviewsStudyList | undefined> {
return (allInterviewsStudyLists as ReadonlyArray<InterviewsStudyList>).find(
(content) => content.slug === slug,
);
}
export async function fetchInterviewsStudyLists(
categoryParam: InterviewsStudyList['category'],
): Promise<ReadonlyArray<InterviewsStudyList>> {
return (allInterviewsStudyLists as ReadonlyArray<InterviewsStudyList>).filter(
({ category }) => categoryParam === category,
);
}

View File

@ -1163,10 +1163,6 @@
"defaultMessage": "Annual plan",
"description": "Title of annual pricing plan"
},
"6TG+jF": {
"defaultMessage": "The one-stop to prepare well for your {company} front end interviews.",
"description": "Description for company guides detail page"
},
"6U52IB": {
"defaultMessage": "FAQ",
"description": "Title for FAQ section on Projects project page"
@ -4411,10 +4407,6 @@
"defaultMessage": "Holistic front end skill roadmap",
"description": "Title of 'Roadmap of front end skills' sub-feature in Projects marketing page"
},
"Qe4ww/": {
"defaultMessage": "Discover focus areas tailored to your needs to help you prepare for your upcoming technical interviews.",
"description": "Description for focus areas page"
},
"QeQWqZ": {
"defaultMessage": "Explore component tracks",
"description": "Recommended action for new projects platform user"
@ -5064,7 +5056,7 @@
"description": "Coding questions long title"
},
"UET3FT": {
"defaultMessage": "Front End Interview Focus Areas—Accessibility, Forms and more",
"defaultMessage": "Front End Interview Focus Areas Accessibility, Forms and more",
"description": "Page title for focus areas listing"
},
"UISwUX": {