feat: complete website implementation

This commit is contained in:
Radosław Smoliński
2026-06-21 21:53:15 +02:00
parent b147112bdc
commit dfd3888517
110 changed files with 18276 additions and 217 deletions
+79
View File
@@ -0,0 +1,79 @@
import Link from "next/link";
import {
Wind, Wrench, ThermometerSun, Paintbrush, Droplet, Tv, HelpCircle, ArrowRight,
PaintBucket, Refrigerator, Hammer, Key, Bath, PaintRoller, Grid, Layers,
AlertTriangle, Waves, Droplets, WashingMachine, Snowflake, Store, ScanLine, Settings
} from "lucide-react";
import { cn } from "@/lib/utils";
interface IconCardProps {
title: string;
description: string;
iconName: string;
href?: string;
className?: string;
}
const iconMap: Record<string, React.ComponentType<{ className?: string }>> = {
Wind,
Wrench,
ThermometerSun,
Paintbrush,
Droplet,
Tv,
PaintBucket,
Refrigerator,
Hammer,
Key,
Bath,
PaintRoller,
Grid,
Layers,
AlertTriangle,
Waves,
Droplets,
WashingMachine,
Snowflake,
Store,
ScanLine,
Settings,
};
export function IconCard({ title, description, iconName, href, className }: IconCardProps) {
const Icon = iconMap[iconName] || HelpCircle;
const cardContent = (
<>
<div className="p-3 rounded-lg bg-slate-100 text-primary w-fit group-hover:bg-accent-light group-hover:text-accent transition-colors duration-300">
<Icon className="size-6" />
</div>
<h3 className="text-lg font-semibold text-primary font-heading mt-4 group-hover:text-accent transition-colors">
{title}
</h3>
<p className="text-sm text-text-muted mt-2 leading-relaxed flex-grow">
{description}
</p>
{href && (
<div className="flex items-center gap-1 text-xs font-semibold text-primary group-hover:text-accent mt-4 transition-colors">
<span>Dowiedz się więcej</span>
<ArrowRight className="size-3.5 group-hover:translate-x-1 transition-transform" />
</div>
)}
</>
);
const cardClasses = cn(
"group border border-border rounded-xl p-6 bg-white hover:shadow-md transition-all duration-300 flex flex-col justify-between h-full",
className
);
if (href) {
return (
<Link href={href} className={cardClasses}>
{cardContent}
</Link>
);
}
return <div className={cardClasses}>{cardContent}</div>;
}
+35
View File
@@ -0,0 +1,35 @@
import { Image as ImageIcon } from "lucide-react";
import { cn } from "@/lib/utils";
interface PlaceholderProps {
text: string;
className?: string;
aspect?: "video" | "square" | "portrait" | "auto" | "banner";
}
export function Placeholder({ text, className, aspect = "video" }: PlaceholderProps) {
const aspectClasses = {
video: "aspect-video",
square: "aspect-square",
portrait: "aspect-[3/4]",
banner: "aspect-[21/9] min-h-[300px]",
auto: "h-full w-full",
};
return (
<div
className={cn(
"relative w-full bg-slate-100 dark:bg-slate-800 border-2 border-dashed border-slate-200 dark:border-slate-700 rounded-xl flex flex-col items-center justify-center text-center p-6 text-slate-400 dark:text-slate-500 overflow-hidden group hover:bg-slate-150 transition-colors duration-250",
aspectClasses[aspect],
className
)}
>
{/* TODO: docelowe zdjęcie — {text} */}
<ImageIcon className="size-10 mb-3 group-hover:scale-110 transition-transform duration-300" />
<span className="text-sm font-medium px-4 leading-normal">{text}</span>
<span className="text-[10px] text-slate-400 mt-2 uppercase tracking-widest font-semibold font-heading opacity-70">
Placeholder Zdjęcia
</span>
</div>
);
}
+58
View File
@@ -0,0 +1,58 @@
import { cn } from "@/lib/utils";
interface SectionHeadingProps {
title: string;
subtitle?: string;
eyebrow?: string;
align?: "left" | "center";
dark?: boolean;
className?: string;
}
export function SectionHeading({
title,
subtitle,
eyebrow,
align = "center",
dark = false,
className,
}: SectionHeadingProps) {
return (
<div
className={cn(
"flex flex-col gap-2 mb-10 md:mb-14",
align === "center" ? "items-center text-center" : "items-start text-left",
className
)}
>
{eyebrow && (
<span
className={cn(
"text-xs md:text-sm font-semibold uppercase tracking-wider font-heading",
dark ? "text-accent" : "text-cta"
)}
>
{eyebrow}
</span>
)}
<h2
className={cn(
"text-3xl md:text-4xl font-bold leading-tight font-heading max-w-3xl",
dark ? "text-white" : "text-primary"
)}
>
{title}
</h2>
{subtitle && (
<p
className={cn(
"text-base md:text-lg max-w-2xl leading-relaxed mt-1",
dark ? "text-slate-300" : "text-text-muted"
)}
>
{subtitle}
</p>
)}
</div>
);
}