Files
exodevices/hotel/src/components/Icons.tsx
T
Yuriy PanovandClaude Opus 5 35f713a267 Convert hotel landing to React + Tailwind v4 with amoCRM lead capture
Ports the single-file ЭкзоОтель page to the same architecture as the fitness
landing: Vite + React 19 + TypeScript + Tailwind v4 on the client, Express 5 for
the /api/leads/hotels endpoint, zod schema shared between the two.

The original page is kept in legacy/index.html as the visual reference. Its 15
inlined base64 images are extracted to files (the 1 MB HTML becomes ~318 KB of
JS plus assets loaded on demand), and its text is reproduced line for line —
verified with an innerText diff. Section heights stay within 0.6% at 375 and
1440 px; the drift comes from Inter actually loading, which the original asked
for but never served.

Three deliberate departures, documented in the README:
  * eyebrow and lead in the CTA block were dark teal on navy (3.4:1); they now
    match the other dark sections
  * hero fact values overflowed their 80px column into the label below 430px
  * "2025–2026г.." typo in the market source note

Leads reuse the fitness amoCRM integration: contact lookup by phone in every
spelling, deal in the first stage of pipeline 10980758, account fields matched
automatically with the rest written to a note. Rate limit, honeypot, and a
localStorage fallback so a lead survives the CRM being down. Vite runs on 5174
and the API on 3001 so both landings can run at once.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 14:17:10 +06:00

100 lines
3.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { ReactNode } from 'react'
/** Every icon on the page is a 24×24 outline drawn in `currentColor`. */
function Icon({ children }: { children: ReactNode }) {
return (
<svg aria-hidden="true" viewBox="0 0 24 24" fill="none">
{children}
</svg>
)
}
export function ArrowRightIcon() {
return (
<Icon>
<path d="M5 12h14M13 6l6 6-6 6" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" />
</Icon>
)
}
export function PhoneIcon() {
return (
<Icon>
<path
d="M22 16.92v3a2 2 0 0 1-2.18 2 19.86 19.86 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6A19.86 19.86 0 0 1 2.12 4.18 2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72c.12.9.35 1.78.68 2.62a2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.46-1.25a2 2 0 0 1 2.11-.45c.84.33 1.72.56 2.62.68A2 2 0 0 1 22 16.92Z"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="1.8"
/>
</Icon>
)
}
export function ChevronsIcon() {
return (
<Icon>
<path d="M8 7l-5 5 5 5M16 7l5 5-5 5" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.8" />
</Icon>
)
}
/** Icons of the guest portraits, keyed by `guestPortraits[].icon`. */
export const guestIcons = {
road: (
<Icon>
<path d="M3 12h18M5 8h4l2-4h2l2 4h4l2 4-2 4h-4l-2 4h-2l-2-4H5l-2-4 2-4Z" stroke="currentColor" strokeLinejoin="round" strokeWidth="1.7" />
</Icon>
),
active: (
<Icon>
<path
d="M13 5.5a2 2 0 1 0 0-4 2 2 0 0 0 0 4ZM10 22l1-6-3-3 2-5 4 1 2 3h4M5 22l2-6"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="1.7"
/>
</Icon>
),
wellness: (
<Icon>
<path d="M12 21c4.5-3 7-6.2 7-10a7 7 0 0 0-14 0c0 3.8 2.5 7 7 10Z" stroke="currentColor" strokeWidth="1.7" />
<path
d="M9 11c1.8.1 3.2-.9 4-3 1.8 1.4 2.4 3.2 1.5 5.3-1 2.2-3.3 3.1-5.5 2"
stroke="currentColor"
strokeLinecap="round"
strokeWidth="1.7"
/>
</Icon>
),
business: (
<Icon>
<path d="M8 7V5a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2M3 9h18v10a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V9Z" stroke="currentColor" strokeWidth="1.7" />
<path d="M3 13h18M10 13v2h4v-2" stroke="currentColor" strokeWidth="1.7" />
</Icon>
),
}
/** Icons of the "почему Экзо" list, keyed by `whyItems[].icon`. */
export const whyIcons = {
tech: (
<Icon>
<path d="M12 3 3 7.5 12 12l9-4.5L12 3Z" stroke="currentColor" strokeLinejoin="round" strokeWidth="1.7" />
<path d="M5 11v5.5L12 21l7-4.5V11" stroke="currentColor" strokeLinejoin="round" strokeWidth="1.7" />
</Icon>
),
factory: (
<Icon>
<path d="M3 21V9l5 3V7l5 3V3h8v18H3Z" stroke="currentColor" strokeLinejoin="round" strokeWidth="1.7" />
<path d="M16 7h2M16 11h2M7 16h2M12 16h2M17 16h2" stroke="currentColor" strokeLinecap="round" strokeWidth="1.7" />
</Icon>
),
launch: (
<Icon>
<path d="M4 4h16v16H4V4Z" stroke="currentColor" strokeWidth="1.7" />
<path d="m8 12 2.5 2.5L16 9" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.7" />
</Icon>
),
}