Skip to content

Commit 017d8e6

Browse files
committed
improvement(landing): react-doctor health pass across the landing surface
- shared JsonLd server component with HTML-safe serialization (replaces 32 inline dangerouslySetInnerHTML JSON-LD sites; structured-data output semantically identical, XSS hardened) - state/effect fixes: derive-instead-of-sync, phase state machines, useSyncExternalStore for media query, handler-only state to refs - structural splits for one-component-per-file and non-component export isolation - mechanical: combined iterations, hoisted Intl formatters/static values, immutable sorts, stable keys, a11y labels, focus rings, SVG precision - ES2023 lib for apps/sim to allow toSorted/toReversed
1 parent b8e88b1 commit 017d8e6

78 files changed

Lines changed: 992 additions & 889 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apps/sim/app/(landing)/blog/[slug]/page.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { buildPostGraphJsonLd, buildPostMetadata } from '@/lib/blog/seo'
88
import { getBaseUrl } from '@/lib/core/utils/urls'
99
import { ShareButton } from '@/app/(landing)/blog/[slug]/share-button'
1010
import { BackLink } from '@/app/(landing)/components'
11+
import { JsonLd } from '@/app/(landing)/components/json-ld'
1112

1213
export const dynamicParams = false
1314

@@ -37,10 +38,7 @@ export default async function Page({ params }: { params: Promise<{ slug: string
3738

3839
return (
3940
<article className='w-full bg-[var(--bg)]' itemScope itemType='https://schema.org/TechArticle'>
40-
<script
41-
type='application/ld+json'
42-
dangerouslySetInnerHTML={{ __html: JSON.stringify(graphJsonLd) }}
43-
/>
41+
<JsonLd data={graphJsonLd} />
4442
<header className='mx-auto w-full max-w-[1446px] px-12 pt-[112px] max-sm:px-5 max-sm:pt-20 max-lg:px-8'>
4543
<div className='mb-6'>
4644
<BackLink href='/blog' label='Back to Blog' />

apps/sim/app/(landing)/blog/[slug]/share-button.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ export function ShareButton({ url, title }: ShareButtonProps) {
3838
<DropdownMenu>
3939
<DropdownMenuTrigger asChild>
4040
<button
41+
type='button'
4142
className='flex items-center gap-1.5 text-[var(--text-muted)] text-sm hover:text-[var(--text-primary)]'
4243
aria-label='Share this post'
4344
>

apps/sim/app/(landing)/blog/authors/[id]/page.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import Image from 'next/image'
33
import Link from 'next/link'
44
import { getAllPostMeta } from '@/lib/blog/registry'
55
import { SITE_URL } from '@/lib/core/utils/urls'
6+
import { JsonLd } from '@/app/(landing)/components/json-ld'
67

78
export const revalidate = 3600
89

@@ -82,10 +83,7 @@ export default async function AuthorPage({ params }: { params: Promise<{ id: str
8283
}
8384
return (
8485
<main className='mx-auto max-w-[900px] px-6 py-10 sm:px-8 md:px-12'>
85-
<script
86-
type='application/ld+json'
87-
dangerouslySetInnerHTML={{ __html: JSON.stringify(graphJsonLd) }}
88-
/>
86+
<JsonLd data={graphJsonLd} />
8987
<div className='mb-6 flex items-center gap-3'>
9088
{author.avatarUrl ? (
9189
<Image

apps/sim/app/(landing)/blog/page.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import Link from 'next/link'
55
import { getAllPostMeta } from '@/lib/blog/registry'
66
import { buildCollectionPageJsonLd } from '@/lib/blog/seo'
77
import { SITE_URL } from '@/lib/core/utils/urls'
8+
import { JsonLd } from '@/app/(landing)/components/json-ld'
89

910
export async function generateMetadata({
1011
searchParams,
@@ -91,10 +92,7 @@ export default async function BlogIndex({
9192

9293
return (
9394
<section className='bg-[var(--bg)]'>
94-
<script
95-
type='application/ld+json'
96-
dangerouslySetInnerHTML={{ __html: JSON.stringify(collectionJsonLd) }}
97-
/>
95+
<JsonLd data={collectionJsonLd} />
9896

9997
{/* Section header */}
10098
<div className='mx-auto w-full max-w-[1446px] px-12 pt-[112px] max-sm:px-5 max-sm:pt-20 max-lg:px-8'>

apps/sim/app/(landing)/blog/tags/page.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { ChipLink } from '@sim/emcn'
22
import type { Metadata } from 'next'
33
import { getAllTags } from '@/lib/blog/registry'
44
import { SITE_URL } from '@/lib/core/utils/urls'
5+
import { JsonLd } from '@/app/(landing)/components/json-ld'
56

67
export const metadata: Metadata = {
78
title: 'Tags',
@@ -37,10 +38,7 @@ export default async function TagsIndex() {
3738
const tags = await getAllTags()
3839
return (
3940
<section className='mx-auto max-w-[900px] px-6 py-10 sm:px-8 md:px-12'>
40-
<script
41-
type='application/ld+json'
42-
dangerouslySetInnerHTML={{ __html: JSON.stringify(breadcrumbJsonLd) }}
43-
/>
41+
<JsonLd data={breadcrumbJsonLd} />
4442
<h1 className='mb-6 text-[32px] text-[var(--text-primary)] leading-tight'>Browse by tag</h1>
4543
<div className='flex flex-wrap gap-3'>
4644
<ChipLink href='/blog' className='border border-[var(--border-1)]'>

apps/sim/app/(landing)/careers/careers.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,10 @@ interface CareersProps {
3535
* never flashes unfiltered before the client board hydrates.
3636
*/
3737
export default async function Careers({ searchParams }: CareersProps) {
38-
const { team, location } = await careersSearchParamsCache.parse(searchParams)
39-
const postings = await getAshbyJobs()
38+
const [{ team, location }, postings] = await Promise.all([
39+
careersSearchParamsCache.parse(searchParams),
40+
getAshbyJobs(),
41+
])
4042
const fallbackGroups = groupByDepartment(filterPostings(postings, team, location))
4143

4244
return (
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
export { JobBoard } from './job-board'
2-
export { filterPostings, groupByDepartment, hasActiveFilters, JobGroups } from './job-groups'
2+
export { JobGroups } from './job-groups'
3+
export { filterPostings, groupByDepartment, hasActiveFilters } from './utils'

apps/sim/app/(landing)/careers/components/job-board/job-board.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33
import { ChipSelect, type ChipSelectOption } from '@sim/emcn'
44
import { useQueryStates } from 'nuqs'
55
import type { CareerPosting } from '@/lib/ashby/jobs'
6+
import { JobGroups } from '@/app/(landing)/careers/components/job-board/job-groups'
67
import {
78
filterPostings,
89
groupByDepartment,
910
hasActiveFilters,
10-
JobGroups,
11-
} from '@/app/(landing)/careers/components/job-board/job-groups'
11+
} from '@/app/(landing)/careers/components/job-board/utils'
1212
import {
1313
ALL_FILTER_VALUE,
1414
careersParsers,
@@ -46,7 +46,7 @@ export function JobBoard({ postings }: JobBoardProps) {
4646

4747
const teamOptions = toFilterOptions(uniqueSorted(postings.map((p) => p.department)), 'All teams')
4848
const locationOptions = toFilterOptions(
49-
uniqueSorted(postings.map((p) => p.location).filter(Boolean)),
49+
uniqueSorted(postings.flatMap((p) => (p.location ? [p.location] : []))),
5050
'All locations'
5151
)
5252
const groups = groupByDepartment(filterPostings(postings, team, location))

apps/sim/app/(landing)/careers/components/job-board/job-groups.tsx

Lines changed: 1 addition & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,13 @@
11
import { cn } from '@sim/emcn'
22
import { ArrowRight } from '@sim/emcn/icons'
33
import type { CareerPosting } from '@/lib/ashby/jobs'
4-
import { ALL_FILTER_VALUE } from '@/app/(landing)/careers/search-params'
5-
6-
export interface DepartmentGroup {
7-
department: string
8-
postings: CareerPosting[]
9-
}
10-
11-
/**
12-
* Narrows postings to a selected Team and Location, treating {@link ALL_FILTER_VALUE}
13-
* as "any". Shared by the server-rendered fallback and the client board so a
14-
* deep-linked filter resolves to the exact same set on both sides.
15-
*/
16-
export function filterPostings(
17-
postings: CareerPosting[],
18-
team: string,
19-
location: string
20-
): CareerPosting[] {
21-
return postings.filter(
22-
(posting) =>
23-
(team === ALL_FILTER_VALUE || posting.department === team) &&
24-
(location === ALL_FILTER_VALUE || posting.location === location)
25-
)
26-
}
27-
28-
/** Whether either the Team or Location filter is narrowing the board. */
29-
export function hasActiveFilters(team: string, location: string): boolean {
30-
return team !== ALL_FILTER_VALUE || location !== ALL_FILTER_VALUE
31-
}
4+
import type { DepartmentGroup } from '@/app/(landing)/careers/components/job-board/utils'
325

336
/** Empty-state copy: distinguishes a truly empty board from a filtered-to-zero view. */
347
const NO_OPEN_ROLES_MESSAGE = 'No open roles right now — check back soon.'
358
const NO_MATCHING_ROLES_MESSAGE =
369
'No roles match these filters right now. Try clearing them, or check back soon.'
3710

38-
/**
39-
* Buckets postings by department, preserving their incoming order (the fetcher
40-
* pre-sorts by department then title). Shared by the interactive board and its
41-
* static Suspense fallback so the two can never render a different grouping.
42-
*/
43-
export function groupByDepartment(postings: CareerPosting[]): DepartmentGroup[] {
44-
const byDepartment = new Map<string, CareerPosting[]>()
45-
for (const posting of postings) {
46-
const bucket = byDepartment.get(posting.department)
47-
if (bucket) bucket.push(posting)
48-
else byDepartment.set(posting.department, [posting])
49-
}
50-
return Array.from(byDepartment, ([department, items]) => ({ department, postings: items }))
51-
}
52-
5311
interface JobGroupsProps {
5412
groups: DepartmentGroup[]
5513
/**
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import type { CareerPosting } from '@/lib/ashby/jobs'
2+
import { ALL_FILTER_VALUE } from '@/app/(landing)/careers/search-params'
3+
4+
export interface DepartmentGroup {
5+
department: string
6+
postings: CareerPosting[]
7+
}
8+
9+
/**
10+
* Narrows postings to a selected Team and Location, treating {@link ALL_FILTER_VALUE}
11+
* as "any". Shared by the server-rendered fallback and the client board so a
12+
* deep-linked filter resolves to the exact same set on both sides.
13+
*/
14+
export function filterPostings(
15+
postings: CareerPosting[],
16+
team: string,
17+
location: string
18+
): CareerPosting[] {
19+
return postings.filter(
20+
(posting) =>
21+
(team === ALL_FILTER_VALUE || posting.department === team) &&
22+
(location === ALL_FILTER_VALUE || posting.location === location)
23+
)
24+
}
25+
26+
/** Whether either the Team or Location filter is narrowing the board. */
27+
export function hasActiveFilters(team: string, location: string): boolean {
28+
return team !== ALL_FILTER_VALUE || location !== ALL_FILTER_VALUE
29+
}
30+
31+
/**
32+
* Buckets postings by department, preserving their incoming order (the fetcher
33+
* pre-sorts by department then title). Shared by the interactive board and its
34+
* static Suspense fallback so the two can never render a different grouping.
35+
*/
36+
export function groupByDepartment(postings: CareerPosting[]): DepartmentGroup[] {
37+
const byDepartment = new Map<string, CareerPosting[]>()
38+
for (const posting of postings) {
39+
const bucket = byDepartment.get(posting.department)
40+
if (bucket) bucket.push(posting)
41+
else byDepartment.set(posting.department, [posting])
42+
}
43+
return Array.from(byDepartment, ([department, items]) => ({ department, postings: items }))
44+
}

0 commit comments

Comments
 (0)