Add futuristic landing page components#1
Conversation
Sprievodca pre recenzentovTento PR umožňuje podporu React JSX v TypeScript, integruje Tailwind s vlastnými CSS premennými s neónovou tematikou a animáciami a implementuje opakovane použiteľné UI primitívy spolu s piatimi komponentmi vstupnej stránky s tematikou, pričom ich zapája do hlavnej indexovej stránky. Diagram tried pre Core UI React komponentyclassDiagram
class Button {
<<React Component>>
+variant: "outline" | "default" = "default"
+size: "sm" | "md" | "lg" = "md"
+className: string = ""
+children: React.ReactNode
+ ...HTMLButtonAttributes
}
class Card {
<<React Component>>
+className: string = ""
+children: React.ReactNode
+ ...HTMLDivAttributes
}
class PhoenixLogo {
<<React Component>>
+size: "sm" | "md" | "lg" | "xl" = "md"
+animated: boolean = true
+className: string = ""
}
Zmeny na úrovni súborov
Tipy a príkazyInterakcia so Sourcery
Prispôsobenie vašej skúsenostiPrístup k vášmu dashboardu na:
Získanie pomoci
Original review guide in EnglishReviewer's GuideThis PR enables React JSX support in TypeScript, integrates Tailwind with custom neon-themed CSS variables and animations, and implements reusable UI primitives alongside five themed landing page components, wiring them into the main index page. Class Diagram for Core UI React ComponentsclassDiagram
class Button {
<<React Component>>
+variant: "outline" | "default" = "default"
+size: "sm" | "md" | "lg" = "md"
+className: string = ""
+children: React.ReactNode
+ ...HTMLButtonAttributes
}
class Card {
<<React Component>>
+className: string = ""
+children: React.ReactNode
+ ...HTMLDivAttributes
}
class PhoenixLogo {
<<React Component>>
+size: "sm" | "md" | "lg" | "xl" = "md"
+animated: boolean = true
+className: string = ""
}
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Ahoj @DDRCZ - skontroloval som tvoje zmeny - tu je nejaká spätná väzba:
- Tvoje importy pomocou '@/...' sa nevyriešia—pridaj položku paths mapujúcu '@/' na 'src/' (alebo prepni na relatívne importy) v tsconfig.json.
- tsconfig.json include by mal pokrývať .tsx súbory globálne (napr. "**/*.{ts,tsx}"), aby všetky React komponenty boli zachytené kompilátorom.
- Je tu veľa opakovaného neon-border a neon-glow markup—zvážte extrahovanie týchto do komponentov vyššej úrovne alebo Tailwind utility variantov na zníženie duplicity.
Tu je to, na čo som sa pozrel počas kontroly
- 🟡 Všeobecné problémy: Nájdených 6 problémov
- 🟢 Bezpečnosť: Všetko vyzerá dobre
- 🟢 Testovanie: Všetko vyzerá dobre
- 🟡 Zložitosť: Nájdené 2 problémy
- 🟢 Dokumentácia: Všetko vyzerá dobre
Sourcery je zadarmo pre open source - ak sa ti páčia naše recenzie, zváž zdieľanie ✨
Original comment in English
Hey @DDRCZ - I've reviewed your changes - here's some feedback:
- Your imports using '@/...' won’t resolve—add a paths entry mapping '@/' to 'src/' (or switch to relative imports) in tsconfig.json.
- tsconfig.json include should cover .tsx files globally (e.g. "**/*.{ts,tsx}") so that all React components are picked up by the compiler.
- There’s a lot of repeated neon-border and neon-glow markup—consider extracting those into higher-level components or Tailwind utility variants to reduce duplication.
Here's what I looked at during the review
- 🟡 General issues: 6 issues found
- 🟢 Security: all looks good
- 🟢 Testing: all looks good
- 🟡 Complexity: 2 issues found
- 🟢 Documentation: all looks good
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| } | ||
| }, | ||
| "include": ["*.ts", "controllers/*.ts", "tools/*.ts"], // Compile all TypeScript files | ||
| "include": ["*.ts", "controllers/*.ts", "tools/*.ts", "src/**/*"], // Compile all TypeScript and React files |
There was a problem hiding this comment.
návrh: Obmedzte vzory zahrnutia na rozšírenia .ts a .tsx
src/**/* zahŕňa všetky typy súborov. Použite src/**/*.{ts,tsx} na zacielenie iba na súbory TypeScript a TSX.
| "include": ["*.ts", "controllers/*.ts", "tools/*.ts", "src/**/*"], // Compile all TypeScript and React files | |
| "include": ["*.ts", "controllers/*.ts", "tools/*.ts", "src/**/*.{ts,tsx}"], // Kompilovať všetky súbory TypeScript a React |
Original comment in English
suggestion: Restrict include patterns to .ts and .tsx extensions
src/**/* includes all file types. Use src/**/*.{ts,tsx} to target only TypeScript and TSX files.
| "include": ["*.ts", "controllers/*.ts", "tools/*.ts", "src/**/*"], // Compile all TypeScript and React files | |
| "include": ["*.ts", "controllers/*.ts", "tools/*.ts", "src/**/*.{ts,tsx}"], // Compile all TypeScript and React files |
| className="w-full h-full" | ||
| > | ||
| <defs> | ||
| <linearGradient id="phoenixGradient" x1="0%" y1="0%" x2="100%" y2="100%"> |
There was a problem hiding this comment.
návrh (riziko_chyby): Používajte jedinečné ID pre SVG gradient/filter, aby ste sa vyhli kolíziám
Renderovanie viacerých komponentov s rovnakými SVG ID spôsobí konflikty. Použite React.useId() alebo pridajte jedinečnú príponu ku každému ID, aby ste tomu zabránili.
Navrhovaná implementácia:
const uniqueId = React.useId();
const gradientId = `phoenixGradient-${uniqueId}`;
const filterId = `glow-${uniqueId}`;
return (
<div className={`${sizeClasses[size]} ${animated ? 'animate-breathe' : ''} ${className}`}>
<svg
viewBox="0 0 200 200"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className="w-full h-full"
>
<defs>
<linearGradient id={gradientId} x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stopColor="#8b5cf6" />
<stop offset="50%" stopColor="#a855f7" />
<stop offset="100%" stopColor="#3b82f6" />
</linearGradient>
<filter id={filterId}>
<feGaussianBlur stdDeviation="3" result="coloredBlur"/>
<feMerge>
<feMergeNode in="coloredBlur"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
</defs>Mali by ste tiež aktualizovať akékoľvek použitie url(#phoenixGradient) a url(#glow) v SVG prvkoch (ako napríklad fill="url(#phoenixGradient)" alebo filter="url(#glow)") na použitie nových premenných: url(#${gradientId}) a url(#${filterId}). Ak sú tieto prítomné inde v súbore, aktualizujte ich zodpovedajúcim spôsobom.
Original comment in English
suggestion (bug_risk): Use unique IDs for SVG gradient/filter to avoid collisions
Rendering multiple components with the same SVG IDs will cause conflicts. Use React.useId() or add a unique suffix to each ID to prevent this.
Suggested implementation:
const uniqueId = React.useId();
const gradientId = `phoenixGradient-${uniqueId}`;
const filterId = `glow-${uniqueId}`;
return (
<div className={`${sizeClasses[size]} ${animated ? 'animate-breathe' : ''} ${className}`}>
<svg
viewBox="0 0 200 200"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className="w-full h-full"
>
<defs>
<linearGradient id={gradientId} x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stopColor="#8b5cf6" />
<stop offset="50%" stopColor="#a855f7" />
<stop offset="100%" stopColor="#3b82f6" />
</linearGradient>
<filter id={filterId}>
<feGaussianBlur stdDeviation="3" result="coloredBlur"/>
<feMerge>
<feMergeNode in="coloredBlur"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
</defs>You must also update any usage of url(#phoenixGradient) and url(#glow) in the SVG elements (such as fill="url(#phoenixGradient)" or filter="url(#glow)") to use the new variables: url(#${gradientId}) and url(#${filterId}). If these are present elsewhere in the file, update them accordingly.
| <svg | ||
| viewBox="0 0 200 200" | ||
| fill="none" | ||
| xmlns="http://www.w3.org/2000/svg" | ||
| className="w-full h-full" | ||
| > |
There was a problem hiding this comment.
návrh: Označte dekoratívne SVG ako aria-hidden alebo poskytnite prístupný názov
Ak je SVG dekoratívne, pridajte aria-hidden="true"; ak prenáša informácie, zahrňte <title> alebo aria-label.
| <svg | |
| viewBox="0 0 200 200" | |
| fill="none" | |
| xmlns="http://www.w3.org/2000/svg" | |
| className="w-full h-full" | |
| > | |
| <svg | |
| viewBox="0 0 200 200" | |
| fill="none" | |
| xmlns="http://www.w3.org/2000/svg" | |
| className="w-full h-full" | |
| aria-hidden="true" | |
| > |
Original comment in English
suggestion: Mark decorative SVG as aria-hidden or provide accessible title
If the SVG is decorative, add aria-hidden="true"; if it conveys information, include a <title> or aria-label.
| <svg | |
| viewBox="0 0 200 200" | |
| fill="none" | |
| xmlns="http://www.w3.org/2000/svg" | |
| className="w-full h-full" | |
| > | |
| <svg | |
| viewBox="0 0 200 200" | |
| fill="none" | |
| xmlns="http://www.w3.org/2000/svg" | |
| className="w-full h-full" | |
| aria-hidden="true" | |
| > |
| </div> | ||
|
|
||
| <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8"> | ||
| {benefits.map((benefit, index) => ( |
There was a problem hiding this comment.
návrh (riziko_chyby): Vyhnite sa používaniu indexu poľa ako kľúča v mape pre BenefitsSection
Použite stabilný kľúč, ako napríklad benefit.title alebo explicitné id namiesto indexu poľa, aby ste predišli problémom s renderovaním, keď sa zoznam zmení.
Navrhovaná implementácia:
{benefits.map((benefit) => (
<Card
key={benefit.title}Ak benefit.title nie je jedinečný alebo máte explicitnú vlastnosť id na každom benefite, použite benefit.id namiesto benefit.title pre kľúč.
Original comment in English
suggestion (bug_risk): Avoid using array index as key in map for BenefitsSection
Use a stable key such as benefit.title or an explicit id instead of the array index to prevent rendering issues when the list changes.
Suggested implementation:
{benefits.map((benefit) => (
<Card
key={benefit.title}If benefit.title is not unique or you have an explicit id property on each benefit, use benefit.id instead of benefit.title for the key.
| {index < flowSteps.length - 1 && ( | ||
| <ArrowRight className="w-8 h-8 text-neon-purple hidden lg:block animate-pulse" /> | ||
| )} |
There was a problem hiding this comment.
návrh: DRY duplicitné komponenty ArrowRight v AutomationFlow
Zvážte kombináciu desktopových a mobilných komponentov ArrowRight do jedného prvku pomocou podmienených tried pre rotáciu a zobrazenie na základe breakpointov.
| {index < flowSteps.length - 1 && ( | |
| <ArrowRight className="w-8 h-8 text-neon-purple hidden lg:block animate-pulse" /> | |
| )} | |
| {index < flowSteps.length - 1 && ( | |
| <ArrowRight | |
| className={` | |
| w-8 h-8 text-neon-purple animate-pulse | |
| ${/* Zobraziť na desktope, otočiť a zobraziť na mobile */''} | |
| hidden lg:block | |
| lg:hidden block rotate-90 lg:rotate-0 | |
| `} | |
| /> | |
| )} |
Original comment in English
suggestion: DRY duplicate ArrowRight components in AutomationFlow
Consider combining the desktop and mobile ArrowRight components into a single element by using conditional classes for rotation and display based on breakpoints.
| {index < flowSteps.length - 1 && ( | |
| <ArrowRight className="w-8 h-8 text-neon-purple hidden lg:block animate-pulse" /> | |
| )} | |
| {index < flowSteps.length - 1 && ( | |
| <ArrowRight | |
| className={` | |
| w-8 h-8 text-neon-purple animate-pulse | |
| ${/* Show on desktop, rotate and show on mobile */''} | |
| hidden lg:block | |
| lg:hidden block rotate-90 lg:rotate-0 | |
| `} | |
| /> | |
| )} |
| children, | ||
| ...props | ||
| }) => { | ||
| const base = 'rounded-md font-medium focus:outline-none focus:ring-2 focus:ring-offset-2'; |
There was a problem hiding this comment.
návrh: Použite nástroj ako clsx alebo twMerge na zlúčenie className
Priame zreťazenie reťazcov môže spôsobiť duplicitné alebo konfliktné triedy Tailwind. Použitie clsx alebo tailwind-merge zaisťuje bezpečnejšie, deduplikované kombinácie tried.
Original comment in English
suggestion: Use a utility like clsx or twMerge for className merging
Direct string concatenation may cause duplicate or conflicting Tailwind classes. Using clsx or tailwind-merge ensures safer, deduplicated class combinations.
| } | ||
| }, | ||
| extend: { | ||
| colors: { |
There was a problem hiding this comment.
issue (zložitosť): Zvážte zavedenie malých pomocných funkcií na generovanie hodnôt a objektov farieb HSL, čím sa zníži opakujúce sa reťazcové literály v konfigurácii farieb.
// ↑ na začiatku konfiguračného súboru pridajte dvoch malých pomocníkov:
const hslVar = (name: string) => `hsl(var(--${name}))`
const hslPair = (name: string) => ({
DEFAULT: hslVar(name),
foreground: hslVar(`${name}-foreground`),
})
export default {
// …
theme: {
extend: {
colors: {
// nahraďte opakujúce sa reťazce...
border: hslVar("border"),
input: hslVar("input"),
ring: hslVar("ring"),
background: hslVar("background"),
foreground: hslVar("foreground"),
// …a vnorené objekty
primary: hslPair("primary"),
secondary: hslPair("secondary"),
destructive: hslPair("destructive"),
muted: hslPair("muted"),
accent: hslPair("accent"),
popover: hslPair("popover"),
card: hslPair("card"),
// pre sidebar môžete stále kombinovať:
sidebar: {
background: hslVar("sidebar-background"),
foreground: hslVar("sidebar-foreground"),
primary: hslPair("sidebar-primary").DEFAULT,
"primary-foreground": hslPair("sidebar-primary").foreground,
// …
},
// neon zostáva taký, aký je
neon: {
purple: "#8b5cf6",
pink: "#a855f7",
blue: "#3b82f6",
},
},
// …
},
},
// …
} satisfies ConfigTýmto sa zachová všetko vaše HSL‐var theming, ale zredukuje desiatky literálnych reťazcov do dvoch veľmi malých pomocných funkcií. Môžete to ďalej DRY iterovaním cez pole kľúčov tém, ak je to potrebné.
Original comment in English
issue (complexity): Consider introducing small helper functions to generate HSL color values and objects, reducing repetitive string literals in the color configuration.
// ↑ at the top of your config file, add two small helpers:
const hslVar = (name: string) => `hsl(var(--${name}))`
const hslPair = (name: string) => ({
DEFAULT: hslVar(name),
foreground: hslVar(`${name}-foreground`),
})
export default {
// …
theme: {
extend: {
colors: {
// replace repetitive strings...
border: hslVar("border"),
input: hslVar("input"),
ring: hslVar("ring"),
background: hslVar("background"),
foreground: hslVar("foreground"),
// …and nested objects
primary: hslPair("primary"),
secondary: hslPair("secondary"),
destructive: hslPair("destructive"),
muted: hslPair("muted"),
accent: hslPair("accent"),
popover: hslPair("popover"),
card: hslPair("card"),
// for sidebar you can still mix & match:
sidebar: {
background: hslVar("sidebar-background"),
foreground: hslVar("sidebar-foreground"),
primary: hslPair("sidebar-primary").DEFAULT,
"primary-foreground": hslPair("sidebar-primary").foreground,
// …
},
// neon stays as-is
neon: {
purple: "#8b5cf6",
pink: "#a855f7",
blue: "#3b82f6",
},
},
// …
},
},
// …
} satisfies ConfigThis preserves all your HSL‐var theming but collapses dozens of literal strings into two very small helper functions. You can further DRY it by iterating over an array of theme keys if desired.
| </filter> | ||
| </defs> | ||
|
|
||
| {/* Phoenix body */} |
There was a problem hiding this comment.
issue (zložitosť): Zvážte extrahovanie opakovaných údajov cesty SVG do samostatného súboru a mapovanie cez neho na dynamické renderovanie ciest.
Tu je rýchly spôsob, ako zredukovať všetky tie opakované <path>s do údajov + mapy, bez toho, aby ste sa dotkli akýchkoľvek vizuálov:
- Extrahujte svoje údaje o ceste do niečoho ako
src/components/PhoenixLogo/paths.ts:
// paths.ts
export interface PathDef {
d: string
strokeWidth: number
opacity?: number
}
export const phoenixPaths: PathDef[] = [
{ d: 'M100 50 C90 60, 90 80, 100 90 C110 80, 110 60, 100 50 Z', strokeWidth: 3 },
{ d: 'M80 70 C60 60, 50 80, 60 100 C70 90, 85 85, 90 80', strokeWidth: 3 },
{ d: 'M120 70 C140 60, 150 80, 140 100 C130 90, 115 85, 110 80', strokeWidth: 3 },
{ d: 'M100 50 C105 45, 110 50, 100 55', strokeWidth: 3 },
{ d: 'M100 90 L95 120 L100 130 L105 120 Z', strokeWidth: 3 },
{ d: 'M75 75 C65 70, 55 85, 65 95', strokeWidth: 2, opacity: 0.7 },
{ d: 'M125 75 C135 70, 145 85, 135 95', strokeWidth: 2, opacity: 0.7 },
]- Importujte & mapujte vo vnútri vášho komponentu:
import { phoenixPaths } from './paths'
const PhoenixLogo: React.FC<PhoenixLogoProps> = ({ size, animated, className }) => {
const common = {
stroke: 'url(#phoenixGradient)',
fill: 'none',
filter: 'url(#glow)',
}
return (
<div className={`${sizeClasses[size]} ${animated ? 'animate-breathe' : ''} ${className}`}>
<svg …>
<defs>…</defs>
{phoenixPaths.map(({ d, strokeWidth, opacity }, i) => (
<path
key={i}
d={d}
strokeWidth={strokeWidth}
opacity={opacity}
{...common}
/>
))}
</svg>
</div>
)
}Tým sa zredukuje ~50 riadkov duplicitného JSX do niekoľkých riadkov údajov + mapy, pričom sa zachová váš gradient, filter a animácia presne tak, ako predtým.
Original comment in English
issue (complexity): Consider extracting the repeated SVG path data into a separate file and mapping over it to render paths dynamically.
Here’s a quick way to collapse all of those repeated <path>s into data + a map, without touching any visuals:
- Extract your path‐data into something like
src/components/PhoenixLogo/paths.ts:
// paths.ts
export interface PathDef {
d: string
strokeWidth: number
opacity?: number
}
export const phoenixPaths: PathDef[] = [
{ d: 'M100 50 C90 60, 90 80, 100 90 C110 80, 110 60, 100 50 Z', strokeWidth: 3 },
{ d: 'M80 70 C60 60, 50 80, 60 100 C70 90, 85 85, 90 80', strokeWidth: 3 },
{ d: 'M120 70 C140 60, 150 80, 140 100 C130 90, 115 85, 110 80', strokeWidth: 3 },
{ d: 'M100 50 C105 45, 110 50, 100 55', strokeWidth: 3 },
{ d: 'M100 90 L95 120 L100 130 L105 120 Z', strokeWidth: 3 },
{ d: 'M75 75 C65 70, 55 85, 65 95', strokeWidth: 2, opacity: 0.7 },
{ d: 'M125 75 C135 70, 145 85, 135 95', strokeWidth: 2, opacity: 0.7 },
]- Import & map inside your component:
import { phoenixPaths } from './paths'
const PhoenixLogo: React.FC<PhoenixLogoProps> = ({ size, animated, className }) => {
const common = {
stroke: 'url(#phoenixGradient)',
fill: 'none',
filter: 'url(#glow)',
}
return (
<div className={`${sizeClasses[size]} ${animated ? 'animate-breathe' : ''} ${className}`}>
<svg …>
<defs>…</defs>
{phoenixPaths.map(({ d, strokeWidth, opacity }, i) => (
<path
key={i}
d={d}
strokeWidth={strokeWidth}
opacity={opacity}
{...common}
/>
))}
</svg>
</div>
)
}That collapses ~50 lines of duplicate JSX into a few lines of data + map, while keeping your gradient, filter, and animation exactly as before.
Summary
Testing
npm run build(fails: cannot find modules)https://chatgpt.com/codex/tasks/task_e_6845ea3f66f88325b784250a263628f0
Zhrnutie od Sourcery
Nastavte futuristickú vstupnú stránku s neónovým štýlom konfiguráciou Tailwind, definovaním globálnych CSS premenných a animácií a implementáciou nových React komponentov pre sekcie obsahu a UI prvky.
Nové funkcie:
Vylepšenia:
Build:
Original summary in English
Summary by Sourcery
Set up a futuristic landing page with neon-themed styling by configuring Tailwind, defining global CSS variables and animations, and implementing new React components for content sections and UI elements.
New Features:
Enhancements:
Build: