first commit
This commit is contained in:
commit
a10d92abac
12
.dockerignore
Normal file
12
.dockerignore
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
Dockerfile
|
||||||
|
.dockerignore
|
||||||
|
node_modules
|
||||||
|
npm-debug.log
|
||||||
|
README.md
|
||||||
|
.next
|
||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
*.md
|
||||||
|
.env*.local
|
||||||
|
.DS_Store
|
||||||
|
*.pem
|
||||||
41
.gitignore
vendored
Normal file
41
.gitignore
vendored
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
/node_modules
|
||||||
|
/.pnp
|
||||||
|
.pnp.*
|
||||||
|
.yarn/*
|
||||||
|
!.yarn/patches
|
||||||
|
!.yarn/plugins
|
||||||
|
!.yarn/releases
|
||||||
|
!.yarn/versions
|
||||||
|
|
||||||
|
# testing
|
||||||
|
/coverage
|
||||||
|
|
||||||
|
# next.js
|
||||||
|
/.next/
|
||||||
|
/out/
|
||||||
|
|
||||||
|
# production
|
||||||
|
/build
|
||||||
|
|
||||||
|
# misc
|
||||||
|
.DS_Store
|
||||||
|
*.pem
|
||||||
|
|
||||||
|
# debug
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
.pnpm-debug.log*
|
||||||
|
|
||||||
|
# env files (can opt-in for committing if needed)
|
||||||
|
.env*
|
||||||
|
|
||||||
|
# vercel
|
||||||
|
.vercel
|
||||||
|
|
||||||
|
# typescript
|
||||||
|
*.tsbuildinfo
|
||||||
|
next-env.d.ts
|
||||||
47
Dockerfile
Normal file
47
Dockerfile
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
# Multi-stage build for Next.js application
|
||||||
|
FROM node:20-alpine AS base
|
||||||
|
|
||||||
|
# Install dependencies only when needed
|
||||||
|
FROM base AS deps
|
||||||
|
RUN apk add --no-cache libc6-compat
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy package files
|
||||||
|
COPY package.json package-lock.json* ./
|
||||||
|
RUN npm ci
|
||||||
|
|
||||||
|
# Rebuild the source code only when needed
|
||||||
|
FROM base AS builder
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=deps /app/node_modules ./node_modules
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Set environment variables for build
|
||||||
|
ENV NEXT_TELEMETRY_DISABLED=1
|
||||||
|
|
||||||
|
# Build the application
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
# Production image, copy all the files and run next
|
||||||
|
FROM base AS runner
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
ENV NODE_ENV=production
|
||||||
|
ENV NEXT_TELEMETRY_DISABLED=1
|
||||||
|
|
||||||
|
RUN addgroup --system --gid 1001 nodejs
|
||||||
|
RUN adduser --system --uid 1001 nextjs
|
||||||
|
|
||||||
|
# Copy built application
|
||||||
|
COPY --from=builder /app/public ./public
|
||||||
|
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
|
||||||
|
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
|
||||||
|
|
||||||
|
USER nextjs
|
||||||
|
|
||||||
|
EXPOSE 3000
|
||||||
|
|
||||||
|
ENV PORT=3000
|
||||||
|
ENV HOSTNAME="0.0.0.0"
|
||||||
|
|
||||||
|
CMD ["node", "server.js"]
|
||||||
17
Dockerfile.dev
Normal file
17
Dockerfile.dev
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# Development Dockerfile for Next.js with hot reload
|
||||||
|
FROM node:20-alpine
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
COPY package.json package-lock.json* ./
|
||||||
|
RUN npm ci
|
||||||
|
|
||||||
|
# Copy application code
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Expose development port
|
||||||
|
EXPOSE 3000
|
||||||
|
|
||||||
|
# Start development server
|
||||||
|
CMD ["npm", "run", "dev"]
|
||||||
36
README.md
Normal file
36
README.md
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
First, run the development server:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run dev
|
||||||
|
# or
|
||||||
|
yarn dev
|
||||||
|
# or
|
||||||
|
pnpm dev
|
||||||
|
# or
|
||||||
|
bun dev
|
||||||
|
```
|
||||||
|
|
||||||
|
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||||
|
|
||||||
|
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
|
||||||
|
|
||||||
|
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
|
||||||
|
|
||||||
|
## Learn More
|
||||||
|
|
||||||
|
To learn more about Next.js, take a look at the following resources:
|
||||||
|
|
||||||
|
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
||||||
|
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
||||||
|
|
||||||
|
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
|
||||||
|
|
||||||
|
## Deploy on Vercel
|
||||||
|
|
||||||
|
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
||||||
|
|
||||||
|
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
|
||||||
55
app/about/page.tsx
Normal file
55
app/about/page.tsx
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import type { Metadata } from "next";
|
||||||
|
import AboutHero from "@/components/AboutHero";
|
||||||
|
import Mission from "@/components/Mission";
|
||||||
|
import Values from "@/components/Values";
|
||||||
|
import Team from "@/components/Team";
|
||||||
|
import CTA from "@/components/CTA";
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: "O nas — Specjaliści Sztucznej Inteligencji | SZMYT AI Labs",
|
||||||
|
description:
|
||||||
|
"Zespół ekspertów AI ✓ Doświadczenie w projektach dla różnych branż ✓ Transparentność i efektywność ✓ Kompleksowe wdrożenia AI. Poznaj naszą misję!",
|
||||||
|
keywords: [
|
||||||
|
"zespół AI",
|
||||||
|
"eksperci sztucznej inteligencji",
|
||||||
|
"specjaliści AI Polska",
|
||||||
|
"doświadczenie AI",
|
||||||
|
"firma AI",
|
||||||
|
"konsultanci sztucznej inteligencji",
|
||||||
|
],
|
||||||
|
openGraph: {
|
||||||
|
title: "O nas — Specjaliści Sztucznej Inteligencji | SZMYT AI Labs",
|
||||||
|
description:
|
||||||
|
"Zespół ekspertów AI ✓ Doświadczenie w projektach dla różnych branż ✓ Transparentność i efektywność ✓ Kompleksowe wdrożenia AI.",
|
||||||
|
url: "https://sztucznainteligencjadlafirm.pl/about/",
|
||||||
|
},
|
||||||
|
alternates: { canonical: "https://sztucznainteligencjadlafirm.pl/about/" },
|
||||||
|
};
|
||||||
|
|
||||||
|
const pageSchema = {
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "WebPage",
|
||||||
|
name: "O nas — Specjaliści Sztucznej Inteligencji",
|
||||||
|
url: "https://sztucznainteligencjadlafirm.pl/about/",
|
||||||
|
description: "Zespół ekspertów AI z doświadczeniem w projektach dla różnych branż. Transparentność, efektywność i kompleksowe wdrożenia AI.",
|
||||||
|
breadcrumb: {
|
||||||
|
"@type": "BreadcrumbList",
|
||||||
|
itemListElement: [
|
||||||
|
{ "@type": "ListItem", position: 1, name: "Strona główna", item: "https://sztucznainteligencjadlafirm.pl/" },
|
||||||
|
{ "@type": "ListItem", position: 2, name: "O nas", item: "https://sztucznainteligencjadlafirm.pl/about/" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function About() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(pageSchema) }} />
|
||||||
|
<AboutHero />
|
||||||
|
<Mission />
|
||||||
|
<Values />
|
||||||
|
<Team />
|
||||||
|
<CTA />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
23
app/api/blog/[slug]/route.ts
Normal file
23
app/api/blog/[slug]/route.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { NextResponse } from "next/server";
|
||||||
|
import { getPostBySlug } from "@/lib/blog";
|
||||||
|
|
||||||
|
export const dynamic = "force-dynamic";
|
||||||
|
|
||||||
|
export async function GET(request: Request, { params }: { params: Promise<{ slug: string }> }) {
|
||||||
|
try {
|
||||||
|
const { slug } = await params;
|
||||||
|
const post = await getPostBySlug(slug);
|
||||||
|
|
||||||
|
if (!post) {
|
||||||
|
return NextResponse.json({ success: false, error: "Post not found" }, { status: 404 });
|
||||||
|
}
|
||||||
|
|
||||||
|
return NextResponse.json({
|
||||||
|
success: true,
|
||||||
|
post,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error("API Error:", error);
|
||||||
|
return NextResponse.json({ success: false, error: "Failed to fetch post" }, { status: 500 });
|
||||||
|
}
|
||||||
|
}
|
||||||
22
app/api/blog/route.ts
Normal file
22
app/api/blog/route.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { NextResponse } from "next/server";
|
||||||
|
import { getAllPosts } from "@/lib/blog";
|
||||||
|
|
||||||
|
export const dynamic = "force-dynamic";
|
||||||
|
|
||||||
|
export async function GET(request: Request) {
|
||||||
|
try {
|
||||||
|
const { searchParams } = new URL(request.url);
|
||||||
|
const category = searchParams.get("category") as "case-study" | "blog" | null;
|
||||||
|
|
||||||
|
const posts = await getAllPosts(category || undefined);
|
||||||
|
|
||||||
|
return NextResponse.json({
|
||||||
|
success: true,
|
||||||
|
posts,
|
||||||
|
count: posts.length,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error("API Error:", error);
|
||||||
|
return NextResponse.json({ success: false, error: "Failed to fetch posts" }, { status: 500 });
|
||||||
|
}
|
||||||
|
}
|
||||||
34
app/api/contact/route.ts
Normal file
34
app/api/contact/route.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import { NextResponse } from "next/server";
|
||||||
|
import { readFileSync, writeFileSync } from "fs";
|
||||||
|
import { resolve } from "path";
|
||||||
|
|
||||||
|
const SUBMISSIONS_PATH = resolve(process.cwd(), "data/submissions.json");
|
||||||
|
|
||||||
|
export async function POST(request: Request) {
|
||||||
|
try {
|
||||||
|
const { name, email, message } = await request.json();
|
||||||
|
|
||||||
|
if (!name || !email || !message) {
|
||||||
|
return NextResponse.json({ error: "All fields are required" }, { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||||
|
if (!emailRegex.test(email)) {
|
||||||
|
return NextResponse.json({ error: "Invalid email" }, { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
const submissions = JSON.parse(readFileSync(SUBMISSIONS_PATH, "utf-8"));
|
||||||
|
submissions.push({
|
||||||
|
name,
|
||||||
|
email,
|
||||||
|
message,
|
||||||
|
createdAt: new Date().toISOString(),
|
||||||
|
});
|
||||||
|
writeFileSync(SUBMISSIONS_PATH, JSON.stringify(submissions, null, 2), "utf-8");
|
||||||
|
|
||||||
|
return NextResponse.json({ success: true }, { status: 200 });
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Contact form error:", err);
|
||||||
|
return NextResponse.json({ error: "Server error" }, { status: 500 });
|
||||||
|
}
|
||||||
|
}
|
||||||
8
app/api/health/route.ts
Normal file
8
app/api/health/route.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { NextResponse } from "next/server";
|
||||||
|
|
||||||
|
export async function GET() {
|
||||||
|
return NextResponse.json({
|
||||||
|
status: "ok",
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
});
|
||||||
|
}
|
||||||
92
app/blog/[slug]/page.tsx
Normal file
92
app/blog/[slug]/page.tsx
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
import type { Metadata } from "next";
|
||||||
|
import { notFound } from "next/navigation";
|
||||||
|
import { getAllPosts, getPostBySlug } from "@/lib/blog";
|
||||||
|
import BlogPost from "@/components/BlogPost";
|
||||||
|
|
||||||
|
// ISR: Revalidate every 60 seconds
|
||||||
|
export const revalidate = 60;
|
||||||
|
|
||||||
|
// Generate static params for all blog posts
|
||||||
|
export async function generateStaticParams() {
|
||||||
|
const posts = await getAllPosts();
|
||||||
|
return posts.map((post) => ({
|
||||||
|
slug: post.slug,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate metadata for each post
|
||||||
|
export async function generateMetadata({ params }: { params: Promise<{ slug: string }> }): Promise<Metadata> {
|
||||||
|
const { slug } = await params;
|
||||||
|
const post = await getPostBySlug(slug);
|
||||||
|
|
||||||
|
if (!post) {
|
||||||
|
return {
|
||||||
|
title: "Post nie znaleziony — SZMYT AI Labs",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
title: post.seo?.title || `${post.title} — SZMYT AI Labs`,
|
||||||
|
description: post.seo?.description || post.excerpt,
|
||||||
|
keywords: post.seo?.keywords || post.tags,
|
||||||
|
openGraph: {
|
||||||
|
title: post.seo?.title || post.title,
|
||||||
|
description: post.seo?.description || post.excerpt,
|
||||||
|
url: `https://sztucznainteligencjadlafirm.pl/blog/${post.slug}/`,
|
||||||
|
type: "article",
|
||||||
|
publishedTime: post.publishedAt.toISOString(),
|
||||||
|
modifiedTime: post.updatedAt.toISOString(),
|
||||||
|
authors: [post.author],
|
||||||
|
images: post.coverImage ? [{ url: post.coverImage }] : [],
|
||||||
|
},
|
||||||
|
alternates: { canonical: `https://sztucznainteligencjadlafirm.pl/blog/${post.slug}/` },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function BlogPostPage({ params }: { params: Promise<{ slug: string }> }) {
|
||||||
|
const { slug } = await params;
|
||||||
|
const post = await getPostBySlug(slug);
|
||||||
|
|
||||||
|
if (!post) {
|
||||||
|
notFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
const articleSchema = {
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": post.category === "case-study" ? "Case Study" : "BlogPosting",
|
||||||
|
headline: post.title,
|
||||||
|
description: post.excerpt,
|
||||||
|
image: post.coverImage,
|
||||||
|
datePublished: post.publishedAt.toISOString(),
|
||||||
|
dateModified: post.updatedAt.toISOString(),
|
||||||
|
author: {
|
||||||
|
"@type": "Person",
|
||||||
|
name: post.author,
|
||||||
|
},
|
||||||
|
publisher: {
|
||||||
|
"@type": "Organization",
|
||||||
|
name: "SZMYT AI Labs",
|
||||||
|
url: "https://sztucznainteligencjadlafirm.pl",
|
||||||
|
},
|
||||||
|
mainEntityOfPage: {
|
||||||
|
"@type": "WebPage",
|
||||||
|
"@id": `https://sztucznainteligencjadlafirm.pl/blog/${post.slug}/`,
|
||||||
|
},
|
||||||
|
keywords: post.tags.join(", "),
|
||||||
|
breadcrumb: {
|
||||||
|
"@type": "BreadcrumbList",
|
||||||
|
itemListElement: [
|
||||||
|
{ "@type": "ListItem", position: 1, name: "Strona główna", item: "https://sztucznainteligencjadlafirm.pl/" },
|
||||||
|
{ "@type": "ListItem", position: 2, name: "Blog", item: "https://sztucznainteligencjadlafirm.pl/blog/" },
|
||||||
|
{ "@type": "ListItem", position: 3, name: post.title, item: `https://sztucznainteligencjadlafirm.pl/blog/${post.slug}/` },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(articleSchema) }} />
|
||||||
|
<BlogPost post={post} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
74
app/blog/page.tsx
Normal file
74
app/blog/page.tsx
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
import type { Metadata } from "next";
|
||||||
|
import { getAllPosts } from "@/lib/blog";
|
||||||
|
import BlogList from "@/components/BlogList";
|
||||||
|
|
||||||
|
// Revalidate every 60 seconds
|
||||||
|
export const revalidate = 60;
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: "Blog AI i Case Studies — SZMYT AI Labs",
|
||||||
|
description:
|
||||||
|
"✓ Case studies wdrożeń AI ✓ Blog o sztucznej inteligencji dla firm ✓ Praktyczne porady i przykłady ✓ Najnowsze trendy w AI dla biznesu",
|
||||||
|
keywords: [
|
||||||
|
"blog AI",
|
||||||
|
"case studies AI",
|
||||||
|
"wdrożenia AI przykłady",
|
||||||
|
"sztuczna inteligencja blog",
|
||||||
|
"AI dla firm blog",
|
||||||
|
"przykłady automatyzacji AI",
|
||||||
|
],
|
||||||
|
openGraph: {
|
||||||
|
title: "Blog AI i Case Studies — SZMYT AI Labs",
|
||||||
|
description:
|
||||||
|
"✓ Case studies wdrożeń AI ✓ Blog o sztucznej inteligencji dla firm ✓ Praktyczne porady i przykłady",
|
||||||
|
url: "https://sztucznainteligencjadlafirm.pl/blog/",
|
||||||
|
},
|
||||||
|
alternates: { canonical: "https://sztucznainteligencjadlafirm.pl/blog/" },
|
||||||
|
};
|
||||||
|
|
||||||
|
const pageSchema = {
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "Blog",
|
||||||
|
name: "Blog SZMYT AI Labs",
|
||||||
|
url: "https://sztucznainteligencjadlafirm.pl/blog/",
|
||||||
|
description: "Blog o sztucznej inteligencji dla firm, case studies wdrożeń AI i praktyczne porady",
|
||||||
|
publisher: {
|
||||||
|
"@type": "Organization",
|
||||||
|
name: "SZMYT AI Labs",
|
||||||
|
url: "https://sztucznainteligencjadlafirm.pl",
|
||||||
|
},
|
||||||
|
breadcrumb: {
|
||||||
|
"@type": "BreadcrumbList",
|
||||||
|
itemListElement: [
|
||||||
|
{ "@type": "ListItem", position: 1, name: "Strona główna", item: "https://sztucznainteligencjadlafirm.pl/" },
|
||||||
|
{ "@type": "ListItem", position: 2, name: "Blog", item: "https://sztucznainteligencjadlafirm.pl/blog/" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default async function BlogPage() {
|
||||||
|
const posts = await getAllPosts();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(pageSchema) }} />
|
||||||
|
<section className="relative min-h-screen py-20 px-4 pt-24 bg-gray-950">
|
||||||
|
<div className="absolute inset-0 overflow-hidden">
|
||||||
|
<div className="absolute top-1/4 left-1/2 -translate-x-1/2 w-[600px] h-[600px] bg-cyan-500/5 rounded-full blur-3xl" />
|
||||||
|
</div>
|
||||||
|
<div className="relative z-10 max-w-6xl mx-auto">
|
||||||
|
<div className="text-center mb-14">
|
||||||
|
<p className="text-cyan-400 font-medium tracking-widest uppercase text-sm mb-4">Blog & Case Studies</p>
|
||||||
|
<h1 className="text-4xl md:text-6xl font-bold text-white mb-6">
|
||||||
|
Wiedza z pierwszej ręki o <span className="text-cyan-400">AI dla firm</span>
|
||||||
|
</h1>
|
||||||
|
<p className="text-gray-400 text-lg max-w-2xl mx-auto">
|
||||||
|
Przykłady wdrożeń, praktyczne porady i najnowsze trendy w automatyzacji biznesu z AI
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<BlogList posts={posts} />
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
BIN
app/favicon.ico
Normal file
BIN
app/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 25 KiB |
69
app/globals.css
Normal file
69
app/globals.css
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
@import "tailwindcss";
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--background: #030712;
|
||||||
|
--foreground: #f9fafb;
|
||||||
|
}
|
||||||
|
|
||||||
|
@theme inline {
|
||||||
|
--color-background: var(--background);
|
||||||
|
--color-foreground: var(--foreground);
|
||||||
|
--font-sans: var(--font-geist-sans);
|
||||||
|
--font-mono: var(--font-geist-mono);
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background: var(--background);
|
||||||
|
color: var(--foreground);
|
||||||
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Glow utilities */
|
||||||
|
.glow-cyan {
|
||||||
|
box-shadow: 0 0 20px rgba(6, 182, 212, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.glow-cyan-hover:hover {
|
||||||
|
box-shadow: 0 0 28px rgba(6, 182, 212, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.border-glow-left {
|
||||||
|
border-left: 3px solid rgba(6, 182, 212, 0.8);
|
||||||
|
box-shadow: -4px 0 15px rgba(6, 182, 212, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-glow {
|
||||||
|
text-shadow: 0 0 24px rgba(6, 182, 212, 0.6);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Carousel scroll animations */
|
||||||
|
@keyframes carousel-left {
|
||||||
|
0% { transform: translateX(0); }
|
||||||
|
100% { transform: translateX(-50%); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes carousel-right {
|
||||||
|
0% { transform: translateX(-50%); }
|
||||||
|
100% { transform: translateX(0); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Animated gradient background */
|
||||||
|
@keyframes gradient-shift {
|
||||||
|
0%, 100% { background-position: 0% 50%; }
|
||||||
|
50% { background-position: 100% 50%; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate-gradient-bg {
|
||||||
|
background-size: 200% 200%;
|
||||||
|
animation: gradient-shift 8s ease infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Icon glow pulse */
|
||||||
|
@keyframes icon-glow-pulse {
|
||||||
|
0%, 100% { filter: drop-shadow(0 0 3px rgba(6, 182, 212, 0.3)); }
|
||||||
|
50% { filter: drop-shadow(0 0 12px rgba(6, 182, 212, 0.7)); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-pulse {
|
||||||
|
animation: icon-glow-pulse 3s ease-in-out infinite;
|
||||||
|
}
|
||||||
81
app/layout.tsx
Normal file
81
app/layout.tsx
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
import type { Metadata } from "next";
|
||||||
|
import { Geist } from "next/font/google";
|
||||||
|
import "./globals.css";
|
||||||
|
import Nav from "@/components/Nav";
|
||||||
|
import Footer from "@/components/Footer";
|
||||||
|
import GoogleAnalytics from "@/components/GoogleAnalytics";
|
||||||
|
|
||||||
|
const geistSans = Geist({
|
||||||
|
variable: "--font-geist-sans",
|
||||||
|
subsets: ["latin"],
|
||||||
|
});
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: "SZMYT AI Labs — Sztuczna Inteligencja dla Firm | AI Agenty",
|
||||||
|
description:
|
||||||
|
"✓ Sztuczna inteligencja dla firm ✓ Automatyzacja procesów z AI ✓ Dedykowane agenty AI ✓ Integracja z systemami firmowymi ✓ Wdrożenie w 40h. Zwiększ efektywność!",
|
||||||
|
keywords: [
|
||||||
|
"sztuczna inteligencja dla firm",
|
||||||
|
"AI dla biznesu",
|
||||||
|
"agenty AI",
|
||||||
|
"automatyzacja AI",
|
||||||
|
"wdrożenie AI",
|
||||||
|
"ChatGPT dla firm",
|
||||||
|
"Claude AI",
|
||||||
|
"AI w procesach biznesowych",
|
||||||
|
"inteligentna automatyzacja",
|
||||||
|
"AI chatbot firmowy",
|
||||||
|
"machine learning dla firm",
|
||||||
|
"AI konsultacje",
|
||||||
|
],
|
||||||
|
robots: { index: true, follow: true },
|
||||||
|
icons: { icon: "/favicon.svg" },
|
||||||
|
openGraph: {
|
||||||
|
type: "website",
|
||||||
|
locale: "pl_PL",
|
||||||
|
siteName: "SZMYT AI Labs",
|
||||||
|
url: "https://sztucznainteligencjadlafirm.pl",
|
||||||
|
title: "SZMYT AI Labs — Sztuczna Inteligencja dla Firm | AI Agenty",
|
||||||
|
description:
|
||||||
|
"✓ Sztuczna inteligencja dla firm ✓ Automatyzacja procesów z AI ✓ Dedykowane agenty AI ✓ Integracja z systemami firmowymi ✓ Wdrożenie w 40h.",
|
||||||
|
images: [{ url: "https://sztucznainteligencjadlafirm.pl/og-image.png", width: 1200, height: 630 }],
|
||||||
|
},
|
||||||
|
twitter: { card: "summary_large_image", images: ["https://sztucznainteligencjadlafirm.pl/og-image.png"] },
|
||||||
|
alternates: { canonical: "https://sztucznainteligencjadlafirm.pl" },
|
||||||
|
};
|
||||||
|
|
||||||
|
const jsonLd = {
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "Organization",
|
||||||
|
name: "SZMYT AI Labs",
|
||||||
|
url: "https://sztucznainteligencjadlafirm.pl",
|
||||||
|
description:
|
||||||
|
"Budujemy dedykowane agenty AI dla firm. Automatyzacja biznesu z siłą sztucznej inteligencji.",
|
||||||
|
contactPoint: {
|
||||||
|
"@type": "ContactPoint",
|
||||||
|
email: "kontakt@sztucznainteligencjadlafirm.pl",
|
||||||
|
contactType: "customer service",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function RootLayout({
|
||||||
|
children,
|
||||||
|
}: Readonly<{
|
||||||
|
children: React.ReactNode;
|
||||||
|
}>) {
|
||||||
|
return (
|
||||||
|
<html lang="pl">
|
||||||
|
<head>
|
||||||
|
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }} />
|
||||||
|
</head>
|
||||||
|
<body className={`${geistSans.variable} antialiased bg-gray-950 text-white min-h-screen`}>
|
||||||
|
{process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID && (
|
||||||
|
<GoogleAnalytics GA_MEASUREMENT_ID={process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID} />
|
||||||
|
)}
|
||||||
|
<Nav />
|
||||||
|
<main>{children}</main>
|
||||||
|
<Footer />
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
);
|
||||||
|
}
|
||||||
53
app/oferta/page.tsx
Normal file
53
app/oferta/page.tsx
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import type { Metadata } from "next";
|
||||||
|
import OfertaHero from "@/components/OfertaHero";
|
||||||
|
import OfertaServices from "@/components/OfertaServices";
|
||||||
|
import CTA from "@/components/CTA";
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: "Oferta Sztucznej Inteligencji dla Firm — AI na Wyciągnięcie Ręki | SZMYT AI Labs",
|
||||||
|
description:
|
||||||
|
"Kompleksowa oferta AI ✓ Analiza i optymalizacja procesów ✓ Budowa agentów AI ✓ Integracje systemowe ✓ Monitoring 24/7 ✓ Wsparcie techniczne. Bezpłatna konsultacja!",
|
||||||
|
keywords: [
|
||||||
|
"oferta AI",
|
||||||
|
"sztuczna inteligencja oferta",
|
||||||
|
"wdrożenie AI cena",
|
||||||
|
"usługi AI dla firm",
|
||||||
|
"konsultacje AI",
|
||||||
|
"analiza procesów AI",
|
||||||
|
"integracja AI",
|
||||||
|
"cennik AI",
|
||||||
|
],
|
||||||
|
openGraph: {
|
||||||
|
title: "Oferta Sztucznej Inteligencji dla Firm — AI na Wyciągnięcie Ręki | SZMYT AI Labs",
|
||||||
|
description:
|
||||||
|
"Kompleksowa oferta AI ✓ Analiza i optymalizacja procesów ✓ Budowa agentów AI ✓ Integracje systemowe ✓ Monitoring 24/7.",
|
||||||
|
url: "https://sztucznainteligencjadlafirm.pl/oferta/",
|
||||||
|
},
|
||||||
|
alternates: { canonical: "https://sztucznainteligencjadlafirm.pl/oferta/" },
|
||||||
|
};
|
||||||
|
|
||||||
|
const pageSchema = {
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "WebPage",
|
||||||
|
name: "Oferta Sztucznej Inteligencji dla Firm",
|
||||||
|
url: "https://sztucznainteligencjadlafirm.pl/oferta/",
|
||||||
|
description: "Kompleksowa oferta AI: analiza i optymalizacja procesów, budowa agentów AI, integracje systemowe, monitoring 24/7. Bezpłatna konsultacja.",
|
||||||
|
breadcrumb: {
|
||||||
|
"@type": "BreadcrumbList",
|
||||||
|
itemListElement: [
|
||||||
|
{ "@type": "ListItem", position: 1, name: "Strona główna", item: "https://sztucznainteligencjadlafirm.pl/" },
|
||||||
|
{ "@type": "ListItem", position: 2, name: "Oferta", item: "https://sztucznainteligencjadlafirm.pl/oferta/" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function Oferta() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(pageSchema) }} />
|
||||||
|
<OfertaHero />
|
||||||
|
<OfertaServices />
|
||||||
|
<CTA />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
166
app/page.tsx
Normal file
166
app/page.tsx
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
import type { Metadata } from "next";
|
||||||
|
import Hero from "@/components/Hero";
|
||||||
|
import WhatWeDo from "@/components/WhatWeDo";
|
||||||
|
import HowItWorks from "@/components/HowItWorks";
|
||||||
|
import WhyUs from "@/components/WhyUs";
|
||||||
|
import ContactForm from "@/components/ContactForm";
|
||||||
|
import LogoCarousel, { AI_TOOLS, PLATFORMS } from "@/components/LogoCarousel";
|
||||||
|
import Stats from "@/components/Stats";
|
||||||
|
import ScrollReveal from "@/components/ScrollReveal";
|
||||||
|
import FeaturedBlog from "@/components/FeaturedBlog";
|
||||||
|
import { getFeaturedPosts } from "@/lib/blog";
|
||||||
|
|
||||||
|
// Revalidate every 60 seconds
|
||||||
|
export const revalidate = 60;
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: "Sztuczna Inteligencja dla Firm — Wdrożenie AI w Biznesie | SZMYT AI Labs",
|
||||||
|
description:
|
||||||
|
"Wdróż sztuczną inteligencję w swojej firmie ✓ AI agenty ✓ Automatyzacja procesów ✓ Integracja z CRM, Slack, Teams ✓ 98% zadowolonych klientów. Bezpłatna konsultacja!",
|
||||||
|
keywords: [
|
||||||
|
"sztuczna inteligencja dla firm",
|
||||||
|
"wdrożenie AI",
|
||||||
|
"AI w biznesie",
|
||||||
|
"automatyzacja AI",
|
||||||
|
"agenty AI dla firm",
|
||||||
|
"ChatGPT dla biznesu",
|
||||||
|
"AI chatbot",
|
||||||
|
"inteligentna automatyzacja",
|
||||||
|
"machine learning firma",
|
||||||
|
],
|
||||||
|
openGraph: {
|
||||||
|
title: "Sztuczna Inteligencja dla Firm — Wdrożenie AI w Biznesie | SZMYT AI Labs",
|
||||||
|
description:
|
||||||
|
"Wdróż sztuczną inteligencję w swojej firmie ✓ AI agenty ✓ Automatyzacja procesów ✓ Integracja z CRM, Slack, Teams ✓ 98% zadowolonych.",
|
||||||
|
url: "https://sztucznainteligencjadlafirm.pl/",
|
||||||
|
},
|
||||||
|
alternates: { canonical: "https://sztucznainteligencjadlafirm.pl/" },
|
||||||
|
};
|
||||||
|
|
||||||
|
const pageSchema = [
|
||||||
|
{
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "WebSite",
|
||||||
|
name: "SZMYT AI Labs",
|
||||||
|
url: "https://sztucznainteligencjadlafirm.pl",
|
||||||
|
potentialAction: {
|
||||||
|
"@type": "SearchAction",
|
||||||
|
target: "https://sztucznainteligencjadlafirm.pl/?q={search_term_string}",
|
||||||
|
"query-input": "required name=search_term_string",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "WebPage",
|
||||||
|
name: "Sztuczna Inteligencja dla Firm — Wdrożenie AI w Biznesie",
|
||||||
|
url: "https://sztucznainteligencjadlafirm.pl/",
|
||||||
|
description: "Wdróż sztuczną inteligencję w swojej firmie. AI agenty, automatyzacja procesów, integracja z systemami. 98% zadowolonych klientów.",
|
||||||
|
breadcrumb: {
|
||||||
|
"@type": "BreadcrumbList",
|
||||||
|
itemListElement: [{ "@type": "ListItem", position: 1, name: "Strona główna", item: "https://sztucznainteligencjadlafirm.pl/" }],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "Service",
|
||||||
|
name: "Analiza procesów biznesowych AI",
|
||||||
|
provider: { "@type": "Organization", name: "SZMYT AI Labs", url: "https://sztucznainteligencjadlafirm.pl" },
|
||||||
|
description: "Mapujemy procesy biznesowe i identyfikujemy obszary idealne do automatyzacji z wykorzystaniem sztucznej inteligencji.",
|
||||||
|
serviceType: "Konsultacje AI",
|
||||||
|
areaServed: "PL",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "Service",
|
||||||
|
name: "Wdrożenie sztucznej inteligencji",
|
||||||
|
provider: { "@type": "Organization", name: "SZMYT AI Labs", url: "https://sztucznainteligencjadlafirm.pl" },
|
||||||
|
description: "Tworzymy i wdrażamy agenty AI dopasowane do potrzeb firmy. Pełna integracja z istniejącymi systemami.",
|
||||||
|
serviceType: "Wdrożenie AI",
|
||||||
|
areaServed: "PL",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "Service",
|
||||||
|
name: "Monitoring i optymalizacja systemów AI",
|
||||||
|
provider: { "@type": "Organization", name: "SZMYT AI Labs", url: "https://sztucznainteligencjadlafirm.pl" },
|
||||||
|
description: "Śledzimy i optymalizujemy wydajność systemów AI w czasie rzeczywistym. Wsparcie techniczne 24/7.",
|
||||||
|
serviceType: "Monitoring AI",
|
||||||
|
areaServed: "PL",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@context": "https://schema.org",
|
||||||
|
"@type": "FAQPage",
|
||||||
|
mainEntity: [
|
||||||
|
{
|
||||||
|
"@type": "Question",
|
||||||
|
name: "Jak sztuczna inteligencja może pomóc mojej firmie?",
|
||||||
|
acceptedAnswer: {
|
||||||
|
"@type": "Answer",
|
||||||
|
text: "Sztuczna inteligencja automatyzuje rutynowe zadania, analizuje dane w czasie rzeczywistym, obsługuje klientów 24/7 i zwiększa efektywność procesów biznesowych nawet 3-krotnie. AI może być wdrożone w obsłudze klienta, analizie danych, automatyzacji procesów czy personalizacji oferty.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "Question",
|
||||||
|
name: "Ile czasu zajmuje wdrożenie AI w firmie?",
|
||||||
|
acceptedAnswer: {
|
||||||
|
"@type": "Answer",
|
||||||
|
text: "Typowe wdrożenie zajmuje około 40 godzin roboczych i obejmuje analizę procesów, konfigurację systemu AI, testy oraz integrację z istniejącymi narzędziami. Złożone projekty mogą wymagać więcej czasu.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "Question",
|
||||||
|
name: "Z jakimi narzędziami integruje się sztuczna inteligencja?",
|
||||||
|
acceptedAnswer: {
|
||||||
|
"@type": "Answer",
|
||||||
|
text: "Systemy AI można zintegrować z Slack, Microsoft Teams, systemami CRM (Salesforce, HubSpot), platformami e-commerce, narzędziami projektowymi (Jira, Asana), bazami danych oraz własnym API firmy.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "Question",
|
||||||
|
name: "Czy wdrożenie AI jest bezpieczne dla danych firmy?",
|
||||||
|
acceptedAnswer: {
|
||||||
|
"@type": "Answer",
|
||||||
|
text: "Tak, bezpieczeństwo danych jest priorytetem. Wszystkie wdrożenia AI są zgodne z RODO, dane są szyfrowane, a systemy projektowane z uwzględnieniem najlepszych praktyk cyberbezpieczeństwa. Możliwe jest również wdrożenie rozwiązań on-premise.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "Question",
|
||||||
|
name: "Ile kosztuje wdrożenie sztucznej inteligencji?",
|
||||||
|
acceptedAnswer: {
|
||||||
|
"@type": "Answer",
|
||||||
|
text: "Koszt wdrożenia AI zależy od zakresu projektu, liczby integracji i złożoności procesów. Oferujemy bezpłatną konsultację, podczas której oszacujemy potrzeby i przedstawimy ofertę dopasowaną do budżetu firmy.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default async function Home() {
|
||||||
|
const featuredPosts = await getFeaturedPosts(3);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(pageSchema) }} />
|
||||||
|
<Hero />
|
||||||
|
<WhatWeDo />
|
||||||
|
<Stats />
|
||||||
|
<LogoCarousel
|
||||||
|
title="Narzędzia, które znamy"
|
||||||
|
subtitle="Technologie AI, z którymi pracujemy na co dzień"
|
||||||
|
logos={AI_TOOLS}
|
||||||
|
/>
|
||||||
|
<HowItWorks />
|
||||||
|
<LogoCarousel
|
||||||
|
title="Platformy, z którymi się integrujemy"
|
||||||
|
subtitle="Podłączamy AI do narzędzi, które już masz"
|
||||||
|
logos={PLATFORMS}
|
||||||
|
reverse
|
||||||
|
/>
|
||||||
|
<WhyUs />
|
||||||
|
<FeaturedBlog posts={featuredPosts} />
|
||||||
|
<ScrollReveal>
|
||||||
|
<ContactForm />
|
||||||
|
</ScrollReveal>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
51
app/sitemap.ts
Normal file
51
app/sitemap.ts
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import { MetadataRoute } from "next";
|
||||||
|
import { getAllPosts } from "@/lib/blog";
|
||||||
|
|
||||||
|
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
|
||||||
|
const baseUrl = "https://sztucznainteligencjadlafirm.pl";
|
||||||
|
|
||||||
|
// Static pages
|
||||||
|
const routes = [
|
||||||
|
{
|
||||||
|
url: `${baseUrl}/`,
|
||||||
|
lastModified: new Date(),
|
||||||
|
changeFrequency: "monthly" as const,
|
||||||
|
priority: 1.0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
url: `${baseUrl}/about/`,
|
||||||
|
lastModified: new Date(),
|
||||||
|
changeFrequency: "monthly" as const,
|
||||||
|
priority: 0.8,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
url: `${baseUrl}/oferta/`,
|
||||||
|
lastModified: new Date(),
|
||||||
|
changeFrequency: "monthly" as const,
|
||||||
|
priority: 0.9,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
url: `${baseUrl}/blog/`,
|
||||||
|
lastModified: new Date(),
|
||||||
|
changeFrequency: "weekly" as const,
|
||||||
|
priority: 0.9,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// Dynamic blog posts
|
||||||
|
try {
|
||||||
|
const posts = await getAllPosts();
|
||||||
|
const blogPosts = posts.map((post) => ({
|
||||||
|
url: `${baseUrl}/blog/${post.slug}/`,
|
||||||
|
lastModified: new Date(post.updatedAt),
|
||||||
|
changeFrequency: "weekly" as const,
|
||||||
|
priority: 0.7,
|
||||||
|
}));
|
||||||
|
|
||||||
|
return [...routes, ...blogPosts];
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error generating sitemap:", error);
|
||||||
|
// Return static routes if blog posts fail to load
|
||||||
|
return routes;
|
||||||
|
}
|
||||||
|
}
|
||||||
14
components/AboutHero.tsx
Normal file
14
components/AboutHero.tsx
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
export default function AboutHero() {
|
||||||
|
return (
|
||||||
|
<section className="relative py-32 px-4 pt-28 bg-gray-950">
|
||||||
|
<div className="absolute inset-0 overflow-hidden">
|
||||||
|
<div className="absolute top-0 right-1/4 w-[500px] h-[500px] bg-cyan-500/5 rounded-full blur-3xl" />
|
||||||
|
</div>
|
||||||
|
<div className="relative z-10 max-w-4xl mx-auto text-center">
|
||||||
|
<p className="text-cyan-400 font-medium tracking-widest uppercase text-sm mb-4">SZMYT AI Labs</p>
|
||||||
|
<h1 className="text-5xl md:text-6xl font-bold text-white mb-4">O nas</h1>
|
||||||
|
<p className="text-gray-400 text-xl max-w-2xl mx-auto">Poznaj zespół za SZMYT AI Labs</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
52
components/BlogCard.tsx
Normal file
52
components/BlogCard.tsx
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import Link from "next/link";
|
||||||
|
import Image from "next/image";
|
||||||
|
import type { BlogPost } from "@/lib/blog";
|
||||||
|
|
||||||
|
export default function BlogCard({ post }: { post: BlogPost }) {
|
||||||
|
const formattedDate = new Date(post.publishedAt).toLocaleDateString("pl-PL", {
|
||||||
|
year: "numeric",
|
||||||
|
month: "long",
|
||||||
|
day: "numeric",
|
||||||
|
});
|
||||||
|
|
||||||
|
const categoryLabel = post.category === "case-study" ? "Case Study" : "Blog";
|
||||||
|
const categoryColor = post.category === "case-study" ? "bg-purple-500/10 text-purple-400" : "bg-cyan-500/10 text-cyan-400";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Link href={`/blog/${post.slug}/`}>
|
||||||
|
<article className="bg-gray-800 border border-gray-700 rounded-2xl p-6 hover:border-cyan-500/50 hover:-translate-y-1 hover:shadow-lg hover:shadow-cyan-500/10 transition-all duration-300 h-full flex flex-col group">
|
||||||
|
<div className="mb-4 rounded-xl overflow-hidden bg-gray-700 relative h-48">
|
||||||
|
<Image
|
||||||
|
src={post.coverImage || '/blog/ai-automation.jpg'}
|
||||||
|
alt={post.title}
|
||||||
|
fill
|
||||||
|
className="object-cover group-hover:scale-105 transition-transform duration-300"
|
||||||
|
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-3 mb-3">
|
||||||
|
<span className={`px-3 py-1 rounded-full text-xs font-medium ${categoryColor}`}>{categoryLabel}</span>
|
||||||
|
<time className="text-gray-500 text-sm" dateTime={post.publishedAt.toISOString()}>
|
||||||
|
{formattedDate}
|
||||||
|
</time>
|
||||||
|
</div>
|
||||||
|
<h3 className="text-xl font-semibold text-white mb-3 group-hover:text-cyan-400 transition-colors">
|
||||||
|
{post.title}
|
||||||
|
</h3>
|
||||||
|
<p className="text-gray-400 leading-relaxed mb-4 flex-grow">{post.excerpt}</p>
|
||||||
|
<div className="flex items-center justify-between pt-4 border-t border-gray-700">
|
||||||
|
<div className="flex flex-wrap gap-2">
|
||||||
|
{post.tags.slice(0, 3).map((tag) => (
|
||||||
|
<span key={tag} className="text-xs text-gray-500">
|
||||||
|
#{tag}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<span className="text-cyan-400 text-sm font-medium group-hover:translate-x-1 transition-transform">
|
||||||
|
Czytaj więcej →
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
}
|
||||||
65
components/BlogList.tsx
Normal file
65
components/BlogList.tsx
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useState } from "react";
|
||||||
|
import type { BlogPost } from "@/lib/blog";
|
||||||
|
import BlogCard from "./BlogCard";
|
||||||
|
import ScrollReveal from "./ScrollReveal";
|
||||||
|
|
||||||
|
export default function BlogList({ posts }: { posts: BlogPost[] }) {
|
||||||
|
const [filter, setFilter] = useState<"all" | "blog" | "case-study">("all");
|
||||||
|
|
||||||
|
const filteredPosts = filter === "all" ? posts : posts.filter((p) => p.category === filter);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{/* Filter Tabs */}
|
||||||
|
<div className="flex justify-center gap-4 mb-12">
|
||||||
|
<button
|
||||||
|
onClick={() => setFilter("all")}
|
||||||
|
className={`px-6 py-3 rounded-full font-medium transition-all ${
|
||||||
|
filter === "all"
|
||||||
|
? "bg-cyan-600 text-white shadow-lg shadow-cyan-500/30"
|
||||||
|
: "bg-gray-800 text-gray-400 hover:bg-gray-700"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
Wszystkie ({posts.length})
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => setFilter("blog")}
|
||||||
|
className={`px-6 py-3 rounded-full font-medium transition-all ${
|
||||||
|
filter === "blog"
|
||||||
|
? "bg-cyan-600 text-white shadow-lg shadow-cyan-500/30"
|
||||||
|
: "bg-gray-800 text-gray-400 hover:bg-gray-700"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
Blog ({posts.filter((p) => p.category === "blog").length})
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => setFilter("case-study")}
|
||||||
|
className={`px-6 py-3 rounded-full font-medium transition-all ${
|
||||||
|
filter === "case-study"
|
||||||
|
? "bg-cyan-600 text-white shadow-lg shadow-cyan-500/30"
|
||||||
|
: "bg-gray-800 text-gray-400 hover:bg-gray-700"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
Case Studies ({posts.filter((p) => p.category === "case-study").length})
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Posts Grid */}
|
||||||
|
{filteredPosts.length === 0 ? (
|
||||||
|
<div className="text-center py-20">
|
||||||
|
<p className="text-gray-500 text-lg">Brak postów w tej kategorii</p>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||||
|
{filteredPosts.map((post, i) => (
|
||||||
|
<ScrollReveal key={post.slug} delay={i * 100}>
|
||||||
|
<BlogCard post={post} />
|
||||||
|
</ScrollReveal>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
199
components/BlogPost.tsx
Normal file
199
components/BlogPost.tsx
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
"use client";
|
||||||
|
import Link from "next/link";
|
||||||
|
import type { BlogPost as BlogPostType } from "@/lib/blog";
|
||||||
|
|
||||||
|
export default function BlogPost({ post }: { post: BlogPostType }) {
|
||||||
|
const formattedDate = new Date(post.publishedAt).toLocaleDateString("pl-PL", {
|
||||||
|
year: "numeric",
|
||||||
|
month: "long",
|
||||||
|
day: "numeric",
|
||||||
|
});
|
||||||
|
|
||||||
|
const categoryLabel = post.category === "case-study" ? "Case Study" : "Blog";
|
||||||
|
const categoryColor = post.category === "case-study" ? "bg-purple-500/10 text-purple-400" : "bg-cyan-500/10 text-cyan-400";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<article className="relative min-h-screen py-20 px-4 pt-24 bg-gray-950">
|
||||||
|
<div className="absolute inset-0 overflow-hidden">
|
||||||
|
<div className="absolute top-1/4 left-1/2 -translate-x-1/2 w-[600px] h-[600px] bg-cyan-500/5 rounded-full blur-3xl" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="relative z-10 max-w-4xl mx-auto">
|
||||||
|
{/* Back Link */}
|
||||||
|
<Link
|
||||||
|
href="/blog/"
|
||||||
|
className="inline-flex items-center gap-2 text-cyan-400 hover:text-cyan-300 mb-8 transition-colors"
|
||||||
|
>
|
||||||
|
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
|
||||||
|
</svg>
|
||||||
|
Wróć do bloga
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
{/* Category & Date */}
|
||||||
|
<div className="flex items-center gap-4 mb-6">
|
||||||
|
<span className={`px-4 py-2 rounded-full text-sm font-medium ${categoryColor}`}>{categoryLabel}</span>
|
||||||
|
<time className="text-gray-500" dateTime={post.publishedAt.toISOString()}>
|
||||||
|
{formattedDate}
|
||||||
|
</time>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Title */}
|
||||||
|
<h1 className="text-4xl md:text-6xl font-bold text-white mb-6 leading-tight">{post.title}</h1>
|
||||||
|
|
||||||
|
{/* Author & Tags */}
|
||||||
|
<div className="flex flex-wrap items-center gap-4 mb-8 pb-8 border-b border-gray-800">
|
||||||
|
<p className="text-gray-400">
|
||||||
|
Autor: <span className="text-white font-medium">{post.author}</span>
|
||||||
|
</p>
|
||||||
|
<div className="flex flex-wrap gap-2">
|
||||||
|
{post.tags.map((tag) => (
|
||||||
|
<span key={tag} className="text-sm text-gray-500">
|
||||||
|
#{tag}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Cover Image */}
|
||||||
|
{post.coverImage && (
|
||||||
|
<div className="mb-10 rounded-2xl overflow-hidden">
|
||||||
|
<img src={post.coverImage} alt={post.title} className="w-full h-auto object-cover" />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Excerpt */}
|
||||||
|
<div className="bg-cyan-500/5 border-l-4 border-cyan-500 p-6 mb-10 rounded-r-xl">
|
||||||
|
<p className="text-gray-300 text-lg leading-relaxed">{post.excerpt}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Content */}
|
||||||
|
<div
|
||||||
|
className="blog-content"
|
||||||
|
dangerouslySetInnerHTML={{ __html: post.content }}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<style jsx>{`
|
||||||
|
.blog-content {
|
||||||
|
color: #d1d5db;
|
||||||
|
line-height: 1.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog-content :global(h2) {
|
||||||
|
color: #22d3ee;
|
||||||
|
font-size: 2rem;
|
||||||
|
font-weight: 700;
|
||||||
|
margin-top: 3rem;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog-content :global(h3) {
|
||||||
|
color: white;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-top: 2rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog-content :global(p) {
|
||||||
|
color: #d1d5db;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
line-height: 1.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog-content :global(strong) {
|
||||||
|
color: white;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog-content :global(ul) {
|
||||||
|
margin: 1.5rem 0;
|
||||||
|
padding-left: 0;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog-content :global(ul li) {
|
||||||
|
color: #d1d5db;
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
padding-left: 2rem;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog-content :global(ul li::before) {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
left: 0.5rem;
|
||||||
|
top: 0.7rem;
|
||||||
|
width: 0.5rem;
|
||||||
|
height: 0.5rem;
|
||||||
|
background: #22d3ee;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog-content :global(ol) {
|
||||||
|
margin: 1.5rem 0;
|
||||||
|
padding-left: 0;
|
||||||
|
list-style: none;
|
||||||
|
counter-reset: item;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog-content :global(ol li) {
|
||||||
|
color: #d1d5db;
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
padding-left: 2rem;
|
||||||
|
position: relative;
|
||||||
|
counter-increment: item;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog-content :global(ol li::before) {
|
||||||
|
content: counter(item) '.';
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
color: #22d3ee;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog-content :global(a) {
|
||||||
|
color: #22d3ee;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog-content :global(a:hover) {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog-content :global(code) {
|
||||||
|
color: #22d3ee;
|
||||||
|
background: #111827;
|
||||||
|
padding: 0.25rem 0.5rem;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blog-content :global(pre) {
|
||||||
|
background: #111827;
|
||||||
|
border: 1px solid #374151;
|
||||||
|
border-radius: 0.75rem;
|
||||||
|
padding: 1.5rem;
|
||||||
|
overflow-x: auto;
|
||||||
|
margin: 1.5rem 0;
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
|
|
||||||
|
{/* CTA */}
|
||||||
|
<div className="mt-16 p-8 bg-gray-800 border border-gray-700 rounded-2xl text-center">
|
||||||
|
<h3 className="text-2xl font-bold text-white mb-4">Zainteresowany wdrożeniem AI w swojej firmie?</h3>
|
||||||
|
<p className="text-gray-400 mb-6">
|
||||||
|
Skontaktuj się z nami i poznaj możliwości automatyzacji biznesu z AI
|
||||||
|
</p>
|
||||||
|
<Link
|
||||||
|
href="/#kontakt"
|
||||||
|
className="inline-block bg-cyan-600 hover:bg-cyan-500 text-white font-semibold px-8 py-4 rounded-full transition-all"
|
||||||
|
>
|
||||||
|
Bezpłatna Konsultacja
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
);
|
||||||
|
}
|
||||||
24
components/CTA.tsx
Normal file
24
components/CTA.tsx
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
|
export default function CTA() {
|
||||||
|
return (
|
||||||
|
<section className="py-20 px-4 bg-gray-900">
|
||||||
|
<div className="max-w-4xl mx-auto">
|
||||||
|
<div className="bg-gray-800 border border-gray-700 rounded-2xl p-12 text-center relative overflow-hidden">
|
||||||
|
<div className="absolute inset-0 overflow-hidden">
|
||||||
|
<div className="absolute bottom-0 left-1/2 -translate-x-1/2 w-[600px] h-[300px] bg-cyan-500/5 rounded-full blur-2xl" />
|
||||||
|
</div>
|
||||||
|
<div className="relative z-10">
|
||||||
|
<h2 className="text-4xl font-bold text-white mb-4">Gotowi do współpracy?</h2>
|
||||||
|
<p className="text-gray-300 text-lg mb-8 max-w-xl mx-auto">
|
||||||
|
Skontaktuj się z nami i zobaczmy, jak AI może pomóc Twojej firmie.
|
||||||
|
</p>
|
||||||
|
<Link href="/#kontakt" className="inline-block bg-cyan-600 hover:bg-cyan-500 text-white font-semibold px-8 py-4 rounded-full glow-cyan glow-cyan-hover transition-all text-lg">
|
||||||
|
Porozmawiajmy
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
23
components/ContactForm.tsx
Normal file
23
components/ContactForm.tsx
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
export default function ContactForm() {
|
||||||
|
return (
|
||||||
|
<section id="kontakt" className="py-20 px-4 bg-gray-900">
|
||||||
|
<div className="max-w-2xl mx-auto">
|
||||||
|
<div className="text-center mb-10">
|
||||||
|
<h2 className="text-4xl font-bold text-white mb-4">Skontaktuj się z nami</h2>
|
||||||
|
<p className="text-gray-300 text-lg">Masz pytania? Chętnie porozmawiam.</p>
|
||||||
|
</div>
|
||||||
|
<div className="bg-gray-800 border border-gray-700 rounded-2xl p-10 text-center">
|
||||||
|
<p className="text-gray-200 text-lg mb-8 max-w-lg mx-auto leading-relaxed">
|
||||||
|
Napisz do nas e-mail i opowiedz o Twoim biznesie. Odpowiemy jak najszybciej.
|
||||||
|
</p>
|
||||||
|
<a
|
||||||
|
href="mailto:kontakt@sztucznainteligencjadlafirm.pl"
|
||||||
|
className="inline-block bg-cyan-600 hover:bg-cyan-500 text-white font-semibold px-8 py-4 rounded-full glow-cyan glow-cyan-hover transition-all text-lg"
|
||||||
|
>
|
||||||
|
Napisz do nas
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
97
components/FeaturedBlog.tsx
Normal file
97
components/FeaturedBlog.tsx
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
"use client";
|
||||||
|
import Link from "next/link";
|
||||||
|
import Image from "next/image";
|
||||||
|
import { BlogPost } from "@/lib/blog";
|
||||||
|
|
||||||
|
interface FeaturedBlogProps {
|
||||||
|
posts: BlogPost[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const CalendarIcon = () => (
|
||||||
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<rect x="3" y="4" width="18" height="18" rx="2" ry="2" />
|
||||||
|
<line x1="16" y1="2" x2="16" y2="6" />
|
||||||
|
<line x1="8" y1="2" x2="8" y2="6" />
|
||||||
|
<line x1="3" y1="10" x2="21" y2="10" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
|
||||||
|
const ArrowRightIcon = () => (
|
||||||
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<line x1="5" y1="12" x2="19" y2="12" />
|
||||||
|
<polyline points="12 5 19 12 12 19" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default function FeaturedBlog({ posts }: FeaturedBlogProps) {
|
||||||
|
if (!posts || posts.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className="py-20 bg-gray-900">
|
||||||
|
<div className="max-w-6xl mx-auto px-4">
|
||||||
|
<div className="text-center mb-12">
|
||||||
|
<h2 className="text-3xl md:text-4xl font-bold text-white mb-4">
|
||||||
|
Najnowsze wpisy z bloga
|
||||||
|
</h2>
|
||||||
|
<p className="text-gray-400 text-lg">
|
||||||
|
Dowiedz się więcej o AI, automatyzacji i innowacjach technologicznych
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid md:grid-cols-3 gap-8 mb-12">
|
||||||
|
{posts.slice(0, 3).map((post) => (
|
||||||
|
<Link
|
||||||
|
key={post.slug}
|
||||||
|
href={`/blog/${post.slug}`}
|
||||||
|
className="group bg-gray-800 rounded-lg overflow-hidden border border-gray-700 hover:border-cyan-500 transition-all duration-300"
|
||||||
|
>
|
||||||
|
<div className="aspect-video overflow-hidden bg-gray-700 relative">
|
||||||
|
<Image
|
||||||
|
src={post.coverImage || '/blog/ai-automation.jpg'}
|
||||||
|
alt={post.title}
|
||||||
|
fill
|
||||||
|
className="object-cover group-hover:scale-105 transition-transform duration-300"
|
||||||
|
sizes="(max-width: 768px) 100vw, 33vw"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="p-6">
|
||||||
|
<div className="flex items-center gap-2 text-sm text-gray-400 mb-3">
|
||||||
|
<CalendarIcon />
|
||||||
|
{new Date(post.publishedAt).toLocaleDateString("pl-PL", {
|
||||||
|
year: "numeric",
|
||||||
|
month: "long",
|
||||||
|
day: "numeric",
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
<h3 className="text-xl font-bold text-white mb-2 group-hover:text-cyan-400 transition-colors">
|
||||||
|
{post.title}
|
||||||
|
</h3>
|
||||||
|
<p className="text-gray-400 text-sm line-clamp-2 mb-4">
|
||||||
|
{post.excerpt}
|
||||||
|
</p>
|
||||||
|
<div className="flex items-center text-cyan-400 text-sm font-medium">
|
||||||
|
Czytaj więcej
|
||||||
|
<span className="ml-2 group-hover:translate-x-1 transition-transform inline-block">
|
||||||
|
<ArrowRightIcon />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="text-center">
|
||||||
|
<Link
|
||||||
|
href="/blog"
|
||||||
|
className="inline-flex items-center gap-2 px-6 py-3 bg-cyan-500 hover:bg-cyan-600 text-white font-medium rounded-lg transition-colors"
|
||||||
|
>
|
||||||
|
Zobacz wszystkie wpisy
|
||||||
|
<ArrowRightIcon />
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
22
components/Footer.tsx
Normal file
22
components/Footer.tsx
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
|
export default function Footer() {
|
||||||
|
return (
|
||||||
|
<footer className="bg-gray-950 border-t border-gray-800 py-12 mt-16">
|
||||||
|
<div className="max-w-6xl mx-auto px-4 flex flex-col md:flex-row items-center justify-between gap-6">
|
||||||
|
<div>
|
||||||
|
<p className="text-xl font-bold text-white text-glow">SZMYT AI Labs</p>
|
||||||
|
<p className="text-gray-500 text-sm mt-1">AI Agenty dla Twojej Firmy</p>
|
||||||
|
</div>
|
||||||
|
<div className="flex gap-8">
|
||||||
|
<Link href="/" className="text-gray-400 hover:text-cyan-400 transition-colors text-sm">Strona główna</Link>
|
||||||
|
<Link href="/oferta" className="text-gray-400 hover:text-cyan-400 transition-colors text-sm">Oferta</Link>
|
||||||
|
<Link href="/blog" className="text-gray-400 hover:text-cyan-400 transition-colors text-sm">Blog</Link>
|
||||||
|
<Link href="/about" className="text-gray-400 hover:text-cyan-400 transition-colors text-sm">O nas</Link>
|
||||||
|
<Link href="/#kontakt" className="text-gray-400 hover:text-cyan-400 transition-colors text-sm">Kontakt</Link>
|
||||||
|
</div>
|
||||||
|
<p className="text-gray-600 text-sm">© 2026 SZMYT AI Labs. Wszystkie prawa zastrzeżone.</p>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
);
|
||||||
|
}
|
||||||
28
components/GoogleAnalytics.tsx
Normal file
28
components/GoogleAnalytics.tsx
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import Script from "next/script";
|
||||||
|
|
||||||
|
export default function GoogleAnalytics({ GA_MEASUREMENT_ID }: { GA_MEASUREMENT_ID: string }) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Script
|
||||||
|
strategy="afterInteractive"
|
||||||
|
src={`https://www.googletagmanager.com/gtag/js?id=${GA_MEASUREMENT_ID}`}
|
||||||
|
/>
|
||||||
|
<Script
|
||||||
|
id="google-analytics"
|
||||||
|
strategy="afterInteractive"
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: `
|
||||||
|
window.dataLayer = window.dataLayer || [];
|
||||||
|
function gtag(){dataLayer.push(arguments);}
|
||||||
|
gtag('js', new Date());
|
||||||
|
gtag('config', '${GA_MEASUREMENT_ID}', {
|
||||||
|
page_path: window.location.pathname,
|
||||||
|
});
|
||||||
|
`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
30
components/Hero.tsx
Normal file
30
components/Hero.tsx
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
|
export default function Hero() {
|
||||||
|
return (
|
||||||
|
<section className="relative min-h-screen flex items-center justify-center px-4 pt-16">
|
||||||
|
<div className="absolute inset-0 overflow-hidden">
|
||||||
|
<div className="absolute inset-0 animate-gradient-bg" style={{ background: "linear-gradient(270deg, rgba(6,182,212,0.1), transparent 50%, rgba(139,92,246,0.07))" }} />
|
||||||
|
<div className="absolute top-1/4 left-1/2 -translate-x-1/2 w-[800px] h-[800px] bg-cyan-500/5 rounded-full blur-3xl" />
|
||||||
|
<div className="absolute top-1/3 left-1/4 w-[400px] h-[400px] bg-cyan-600/5 rounded-full blur-2xl" />
|
||||||
|
<div className="absolute bottom-1/4 right-1/3 w-[350px] h-[350px] bg-purple-600/4 rounded-full blur-2xl" />
|
||||||
|
</div>
|
||||||
|
<div className="relative z-10 max-w-4xl mx-auto text-center">
|
||||||
|
<p className="text-cyan-400 font-medium tracking-widest uppercase text-sm mb-6">Sztuczna Inteligencja w Twojej Firmie</p>
|
||||||
|
<h1 className="text-5xl md:text-7xl font-bold text-white mb-6 leading-tight">
|
||||||
|
Wdrażamy AI<br />
|
||||||
|
<span className="text-cyan-400 text-glow">które naprawdę działa</span>
|
||||||
|
</h1>
|
||||||
|
<p className="text-gray-300 text-xl md:text-2xl max-w-2xl mx-auto mb-10 leading-relaxed">
|
||||||
|
Kompleksowe rozwiązania sztucznej inteligencji dla firm. Od analizy procesów po pełną automatyzację. Zwiększ wydajność o 300%.
|
||||||
|
</p>
|
||||||
|
<Link
|
||||||
|
href="#kontakt"
|
||||||
|
className="inline-block bg-cyan-600 hover:bg-cyan-500 text-white font-semibold px-8 py-4 rounded-full glow-cyan glow-cyan-hover transition-all text-lg"
|
||||||
|
>
|
||||||
|
Bezpłatna Konsultacja
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
50
components/HowItWorks.tsx
Normal file
50
components/HowItWorks.tsx
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import ScrollReveal from "@/components/ScrollReveal";
|
||||||
|
|
||||||
|
export default function HowItWorks() {
|
||||||
|
const steps = [
|
||||||
|
{
|
||||||
|
number: "01",
|
||||||
|
title: "Konsultacja",
|
||||||
|
description: "Poznamy Twoje potrzeby, procesy i cele biznesowe.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
number: "02",
|
||||||
|
title: "Implementacja",
|
||||||
|
description: "Zbudujemy dedykowane agenty AI skrojone na Twoją firmę.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
number: "03",
|
||||||
|
title: "Automatyzacja",
|
||||||
|
description: "Twoja firma działa szybciej i efektywniej — automatycznie.",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className="py-20 px-4 bg-gray-900">
|
||||||
|
<div className="max-w-6xl mx-auto">
|
||||||
|
<ScrollReveal>
|
||||||
|
<div className="text-center mb-14">
|
||||||
|
<h2 className="text-4xl font-bold text-white mb-4">Jak to działa</h2>
|
||||||
|
<p className="text-gray-400 text-lg">Trzy kroki do automatyzacji Twojego biznesu</p>
|
||||||
|
</div>
|
||||||
|
</ScrollReveal>
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
|
||||||
|
{steps.map((step, i) => (
|
||||||
|
<ScrollReveal key={step.title} delay={i * 200}>
|
||||||
|
<div className="relative flex flex-col items-center text-center cursor-pointer group">
|
||||||
|
{i < steps.length - 1 && (
|
||||||
|
<div className="hidden md:block absolute top-10 left-1/2 w-full h-0.5 bg-gradient-to-r from-cyan-500/50 to-transparent" />
|
||||||
|
)}
|
||||||
|
<div className="relative z-10 w-20 h-20 rounded-full bg-gray-800 border-2 border-cyan-500 shadow-lg shadow-cyan-500/20 group-hover:shadow-xl group-hover:shadow-cyan-500/40 group-hover:scale-110 flex items-center justify-center mb-6 transition-all duration-300">
|
||||||
|
<span className="text-cyan-400 font-bold text-xl">{step.number}</span>
|
||||||
|
</div>
|
||||||
|
<h3 className="text-xl font-semibold text-white group-hover:text-cyan-400 mb-2 transition-colors duration-300">{step.title}</h3>
|
||||||
|
<p className="text-gray-300 leading-relaxed max-w-xs">{step.description}</p>
|
||||||
|
</div>
|
||||||
|
</ScrollReveal>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
80
components/Icons.tsx
Normal file
80
components/Icons.tsx
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
export function SearchIcon() {
|
||||||
|
return (
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
|
||||||
|
<circle cx="11" cy="11" r="8" />
|
||||||
|
<line x1="21" y1="21" x2="16.65" y2="16.65" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function CpuIcon() {
|
||||||
|
return (
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
|
||||||
|
<rect x="4" y="4" width="16" height="16" rx="2" />
|
||||||
|
<rect x="9" y="9" width="6" height="6" />
|
||||||
|
<line x1="9" y1="1" x2="9" y2="4" />
|
||||||
|
<line x1="15" y1="1" x2="15" y2="4" />
|
||||||
|
<line x1="9" y1="20" x2="9" y2="23" />
|
||||||
|
<line x1="15" y1="20" x2="15" y2="23" />
|
||||||
|
<line x1="20" y1="9" x2="23" y2="9" />
|
||||||
|
<line x1="1" y1="9" x2="4" y2="9" />
|
||||||
|
<line x1="20" y1="15" x2="23" y2="15" />
|
||||||
|
<line x1="1" y1="15" x2="4" y2="15" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function TrendingUpIcon() {
|
||||||
|
return (
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
|
||||||
|
<polyline points="23 6 13.5 15.5 8.5 10.5 1 18" />
|
||||||
|
<polyline points="17 6 23 6 23 12" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function LightbulbIcon() {
|
||||||
|
return (
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
|
||||||
|
<path d="M12 2a7 7 0 00-7 7c0 2.38 1.19 4.47 3 5.74V17a1 1 0 001 1h6a1 1 0 001-1v-2.26C17.81 13.47 19 11.38 19 9a7 7 0 00-7-7z" />
|
||||||
|
<line x1="10" y1="22" x2="14" y2="22" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ShieldCheckIcon() {
|
||||||
|
return (
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
|
||||||
|
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" />
|
||||||
|
<polyline points="9 12 11 14 15 10" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ZapIcon() {
|
||||||
|
return (
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
|
||||||
|
<polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function UsersIcon() {
|
||||||
|
return (
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
|
||||||
|
<path d="M17 21v-2a4 4 0 00-4-4H5a4 4 0 00-4 4v2" />
|
||||||
|
<circle cx="9" cy="7" r="4" />
|
||||||
|
<path d="M23 21v-2a4 4 0 00-3-3.87" />
|
||||||
|
<path d="M16 3.13a4 4 0 010 7.75" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function UserIcon() {
|
||||||
|
return (
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
|
||||||
|
<path d="M20 21v-2a4 4 0 00-4-4H8a4 4 0 00-4 4v2" />
|
||||||
|
<circle cx="12" cy="8" r="4" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
74
components/LogoCarousel.tsx
Normal file
74
components/LogoCarousel.tsx
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
interface LogoItem {
|
||||||
|
name: string;
|
||||||
|
color: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const AI_TOOLS: LogoItem[] = [
|
||||||
|
{ name: "ChatGPT", color: "bg-green-400" },
|
||||||
|
{ name: "Claude", color: "bg-purple-400" },
|
||||||
|
{ name: "Gemini", color: "bg-blue-400" },
|
||||||
|
{ name: "n8n", color: "bg-orange-400" },
|
||||||
|
{ name: "Make", color: "bg-violet-400" },
|
||||||
|
{ name: "Zapier", color: "bg-yellow-400" },
|
||||||
|
{ name: "LangChain", color: "bg-emerald-400" },
|
||||||
|
{ name: "OpenAI", color: "bg-cyan-400" },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const PLATFORMS: LogoItem[] = [
|
||||||
|
{ name: "HubSpot", color: "bg-orange-400" },
|
||||||
|
{ name: "Salesforce", color: "bg-sky-400" },
|
||||||
|
{ name: "Monday", color: "bg-red-400" },
|
||||||
|
{ name: "Notion", color: "bg-gray-300" },
|
||||||
|
{ name: "Sheets", color: "bg-green-400" },
|
||||||
|
{ name: "Slack", color: "bg-purple-400" },
|
||||||
|
{ name: "Airtable", color: "bg-cyan-400" },
|
||||||
|
{ name: "Jira", color: "bg-blue-400" },
|
||||||
|
];
|
||||||
|
|
||||||
|
function LogoBadge({ name, color }: LogoItem) {
|
||||||
|
return (
|
||||||
|
<div className="flex-shrink-0 h-14 px-6 rounded-full border border-gray-700/50 bg-gray-800/70 backdrop-blur-sm flex items-center gap-3">
|
||||||
|
<div className={`w-2.5 h-2.5 rounded-full ${color}`} />
|
||||||
|
<span className="text-gray-200 font-medium text-sm whitespace-nowrap">{name}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function LogoCarousel({
|
||||||
|
title,
|
||||||
|
subtitle,
|
||||||
|
logos,
|
||||||
|
reverse = false,
|
||||||
|
}: {
|
||||||
|
title: string;
|
||||||
|
subtitle: string;
|
||||||
|
logos: LogoItem[];
|
||||||
|
reverse?: boolean;
|
||||||
|
}) {
|
||||||
|
const doubled = [...logos, ...logos];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className="py-14 overflow-hidden bg-gray-950">
|
||||||
|
<div className="max-w-5xl mx-auto text-center mb-8 px-4">
|
||||||
|
<h3 className="text-xl font-semibold text-white mb-1">{title}</h3>
|
||||||
|
<p className="text-gray-500 text-sm">{subtitle}</p>
|
||||||
|
</div>
|
||||||
|
<div className="relative">
|
||||||
|
<div className="absolute left-0 top-0 bottom-0 w-32 bg-gradient-to-r from-gray-950 via-gray-950/70 to-transparent z-10 pointer-events-none" />
|
||||||
|
<div className="absolute right-0 top-0 bottom-0 w-32 bg-gradient-to-l from-gray-950 via-gray-950/70 to-transparent z-10 pointer-events-none" />
|
||||||
|
<div
|
||||||
|
className="flex gap-4 w-max"
|
||||||
|
style={{
|
||||||
|
animation: `${reverse ? "carousel-right" : "carousel-left"} 40s linear infinite`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{doubled.map((logo, i) => (
|
||||||
|
<LogoBadge key={i} name={logo.name} color={logo.color} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
16
components/Mission.tsx
Normal file
16
components/Mission.tsx
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
export default function Mission() {
|
||||||
|
return (
|
||||||
|
<section className="py-20 px-4 bg-gray-950">
|
||||||
|
<div className="max-w-4xl mx-auto">
|
||||||
|
<div className="border-glow-left bg-gray-800 rounded-xl p-8">
|
||||||
|
<h2 className="text-3xl font-bold text-white mb-4">Nasza misja</h2>
|
||||||
|
<p className="text-gray-300 text-lg leading-relaxed">
|
||||||
|
Naszą misją jest pomóc firmom wykorzystać potęgę sztucznej inteligencji.
|
||||||
|
Budujemy dedykowane agenty AI, które automatyzują procesy i zwiększają
|
||||||
|
wydajność — tak, żeby ludzie mogą skupiać się na tym, co naprawdę ważne.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
46
components/Nav.tsx
Normal file
46
components/Nav.tsx
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
"use client";
|
||||||
|
import { useState } from "react";
|
||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
|
export default function Nav() {
|
||||||
|
const [menuOpen, setMenuOpen] = useState(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<nav className="fixed top-0 left-0 right-0 z-50 bg-gray-950/90 backdrop-blur-sm border-b border-gray-800">
|
||||||
|
<div className="max-w-6xl mx-auto px-4 py-4 flex items-center justify-between">
|
||||||
|
<Link href="/" className="text-xl font-bold text-white text-glow">
|
||||||
|
SZMYT AI Labs
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<div className="hidden md:flex gap-8">
|
||||||
|
<Link href="/" className="text-gray-300 hover:text-cyan-400 transition-colors">Strona główna</Link>
|
||||||
|
<Link href="/oferta" className="text-gray-300 hover:text-cyan-400 transition-colors">Oferta</Link>
|
||||||
|
<Link href="/blog" className="text-gray-300 hover:text-cyan-400 transition-colors">Blog</Link>
|
||||||
|
<Link href="/about" className="text-gray-300 hover:text-cyan-400 transition-colors">O nas</Link>
|
||||||
|
<Link href="/#kontakt" className="text-gray-300 hover:text-cyan-400 transition-colors">Kontakt</Link>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
className="md:hidden text-gray-300"
|
||||||
|
onClick={() => setMenuOpen(!menuOpen)}
|
||||||
|
aria-label={menuOpen ? "Zamknij menu" : "Otwórz menu"}
|
||||||
|
aria-expanded={menuOpen}
|
||||||
|
>
|
||||||
|
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d={menuOpen ? "M6 18L18 6M6 6l12 12" : "M3 6h18M3 12h18M3 18h18"} />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{menuOpen && (
|
||||||
|
<div className="md:hidden bg-gray-950 border-t border-gray-800 px-4 py-4 flex flex-col gap-4">
|
||||||
|
<Link href="/" onClick={() => setMenuOpen(false)} className="text-gray-300 hover:text-cyan-400 transition-colors">Strona główna</Link>
|
||||||
|
<Link href="/oferta" onClick={() => setMenuOpen(false)} className="text-gray-300 hover:text-cyan-400 transition-colors">Oferta</Link>
|
||||||
|
<Link href="/blog" onClick={() => setMenuOpen(false)} className="text-gray-300 hover:text-cyan-400 transition-colors">Blog</Link>
|
||||||
|
<Link href="/about" onClick={() => setMenuOpen(false)} className="text-gray-300 hover:text-cyan-400 transition-colors">O nas</Link>
|
||||||
|
<Link href="/#kontakt" onClick={() => setMenuOpen(false)} className="text-gray-300 hover:text-cyan-400 transition-colors">Kontakt</Link>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</nav>
|
||||||
|
);
|
||||||
|
}
|
||||||
18
components/OfertaHero.tsx
Normal file
18
components/OfertaHero.tsx
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
export default function OfertaHero() {
|
||||||
|
return (
|
||||||
|
<section className="relative py-32 px-4 pt-28 bg-gray-950">
|
||||||
|
<div className="absolute inset-0 overflow-hidden">
|
||||||
|
<div className="absolute inset-0 animate-gradient-bg" style={{ background: "linear-gradient(270deg, rgba(6,182,212,0.1), transparent 50%, rgba(139,92,246,0.07))" }} />
|
||||||
|
<div className="absolute top-0 left-1/4 w-[500px] h-[500px] bg-cyan-500/5 rounded-full blur-3xl" />
|
||||||
|
<div className="absolute top-1/3 right-1/4 w-[300px] h-[300px] bg-purple-500/5 rounded-full blur-2xl" />
|
||||||
|
</div>
|
||||||
|
<div className="relative z-10 max-w-4xl mx-auto text-center">
|
||||||
|
<p className="text-cyan-400 font-medium tracking-widest uppercase text-sm mb-4">SZMYT AI Labs</p>
|
||||||
|
<h1 className="text-5xl md:text-6xl font-bold text-white mb-4">Oferta</h1>
|
||||||
|
<p className="text-gray-400 text-xl max-w-2xl mx-auto">
|
||||||
|
Kompleksowe rozwiązania AI skrojone pod Twoją firmę
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
99
components/OfertaServices.tsx
Normal file
99
components/OfertaServices.tsx
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
import ScrollReveal from "@/components/ScrollReveal";
|
||||||
|
import { SearchIcon, CpuIcon, ZapIcon, TrendingUpIcon } from "@/components/Icons";
|
||||||
|
|
||||||
|
const services = [
|
||||||
|
{
|
||||||
|
Icon: SearchIcon,
|
||||||
|
title: "Analiza Procesów Biznesowych",
|
||||||
|
description:
|
||||||
|
"Przeprowadzamy szczegółową analizę Twoich procesów biznesowych. Mapujemy przepływy danych, identyfikujemy powtarzalne zadania i wyznaczamy obszary, w których AI przyniesie największą wartość. Wynik to konkretny plan wdrożenia.",
|
||||||
|
features: [
|
||||||
|
"Mapowanie procesów end-to-end",
|
||||||
|
"Identyfikacja powtarzalnych zadań",
|
||||||
|
"Analiza kosztu vs zysk automatyzacji",
|
||||||
|
"Raport z rekomendacjami",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Icon: CpuIcon,
|
||||||
|
title: "Budowa Dedykowanych Agentów AI",
|
||||||
|
description:
|
||||||
|
"Projektujemy i budujemy agenty AI skrojone na potrzeby Twojej firmy. Od prostych asystentów po złożone systemy wieloagentowe — każde rozwiązanie jest tworzone od zera, nie ze standardowego szablonika.",
|
||||||
|
features: [
|
||||||
|
"Agenty dedykowane dla firmy",
|
||||||
|
"Systemy wieloagentowe",
|
||||||
|
"Integracja z LLM (GPT, Claude, Gemini)",
|
||||||
|
"Testowanie i walidacja",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Icon: ZapIcon,
|
||||||
|
title: "Integracja z Twoimi Narzędziami",
|
||||||
|
description:
|
||||||
|
"Podłączamy agenty AI do narzędzi, które już masz — CRM, zarządzanie projektem, komunikacja, bazy danych. Nie trzeba rezygnować z tego, co działa. AI pracuje dla Ciebie w dotychczasowym ekosystemie.",
|
||||||
|
features: [
|
||||||
|
"Integracja z CRM i ERP",
|
||||||
|
"Automatyzacja przepływów danych",
|
||||||
|
"Webhooks i API",
|
||||||
|
"Synchronizacja w czasie rzeczywistym",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Icon: TrendingUpIcon,
|
||||||
|
title: "Monitoring & Ciągła Optymalizacja",
|
||||||
|
description:
|
||||||
|
"Po wdrożeniu nie kończy się nasza praca. Monitorujemy wydajność agentów 24/7, analizujemy wyniki i stale poprawiamy procesy. Twoja automatyzacja ewoluuje razem z Twoim biznisem.",
|
||||||
|
features: [
|
||||||
|
"Dashboard monitoringu",
|
||||||
|
"Alerting i raportowanie",
|
||||||
|
"Kwartalny przegląd efektów",
|
||||||
|
"Iteracyjne ulepszenia",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function OfertaServices() {
|
||||||
|
return (
|
||||||
|
<section className="py-20 px-4 bg-gray-950">
|
||||||
|
<div className="max-w-4xl mx-auto">
|
||||||
|
<ScrollReveal>
|
||||||
|
<div className="text-center mb-14">
|
||||||
|
<h2 className="text-4xl font-bold text-white mb-4">Nasze usługi</h2>
|
||||||
|
<p className="text-gray-400 text-lg max-w-2xl mx-auto">
|
||||||
|
Każde rozwiązanie jest tworzone indywidualnie — dopasowane do Twojej firmy i jej celów
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</ScrollReveal>
|
||||||
|
<div className="flex flex-col gap-6">
|
||||||
|
{services.map((service, i) => (
|
||||||
|
<ScrollReveal key={service.title} delay={i * 120}>
|
||||||
|
<div className="bg-gray-800 border border-gray-700 rounded-2xl p-8 hover:border-cyan-500/40 hover:shadow-lg hover:shadow-cyan-500/5 transition-all duration-300 group">
|
||||||
|
<div className="flex items-start gap-5">
|
||||||
|
<div className="w-12 h-12 flex-shrink-0 bg-cyan-500/10 rounded-xl p-3 text-cyan-400 icon-pulse">
|
||||||
|
<service.Icon />
|
||||||
|
</div>
|
||||||
|
<div className="flex-1">
|
||||||
|
<h3 className="text-xl font-semibold text-white mb-2 group-hover:text-cyan-400 transition-colors duration-300">
|
||||||
|
{service.title}
|
||||||
|
</h3>
|
||||||
|
<p className="text-gray-300 leading-relaxed mb-4">{service.description}</p>
|
||||||
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-2">
|
||||||
|
{service.features.map((f) => (
|
||||||
|
<div key={f} className="flex items-center gap-2 text-gray-400 text-sm">
|
||||||
|
<svg className="w-4 h-4 text-cyan-400 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
|
||||||
|
</svg>
|
||||||
|
<span>{f}</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ScrollReveal>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
41
components/ScrollReveal.tsx
Normal file
41
components/ScrollReveal.tsx
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
"use client";
|
||||||
|
import { useEffect, useRef, useState } from "react";
|
||||||
|
|
||||||
|
export default function ScrollReveal({
|
||||||
|
children,
|
||||||
|
delay = 0,
|
||||||
|
}: {
|
||||||
|
children: React.ReactNode;
|
||||||
|
delay?: number;
|
||||||
|
}) {
|
||||||
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
|
const [visible, setVisible] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const el = ref.current;
|
||||||
|
if (!el) return;
|
||||||
|
const observer = new IntersectionObserver(
|
||||||
|
([entry]) => {
|
||||||
|
if (entry.isIntersecting) {
|
||||||
|
const timer = setTimeout(() => setVisible(true), delay);
|
||||||
|
observer.unobserve(el);
|
||||||
|
return () => clearTimeout(timer);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ threshold: 0.12 }
|
||||||
|
);
|
||||||
|
observer.observe(el);
|
||||||
|
return () => observer.disconnect();
|
||||||
|
}, [delay]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
ref={ref}
|
||||||
|
className={`transition-all duration-700 ease-out ${
|
||||||
|
visible ? "opacity-100 translate-y-0" : "opacity-0 translate-y-8"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
74
components/Stats.tsx
Normal file
74
components/Stats.tsx
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
"use client";
|
||||||
|
import { useEffect, useRef, useState } from "react";
|
||||||
|
|
||||||
|
const stats = [
|
||||||
|
{ value: 40, suffix: "h", label: "Oszczędności miesięczne" },
|
||||||
|
{ value: 98, suffix: "%", label: "Satysfakcja klientów" },
|
||||||
|
{ value: 3, suffix: "x", label: "Wzrost efektywności" },
|
||||||
|
{ value: 24, suffix: "/7", label: "Monitor agentów" },
|
||||||
|
];
|
||||||
|
|
||||||
|
function useCountUp(target: number, duration = 1500) {
|
||||||
|
const [count, setCount] = useState(0);
|
||||||
|
const [started, setStarted] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!started) return;
|
||||||
|
const startTime = performance.now();
|
||||||
|
const step = (now: number) => {
|
||||||
|
const elapsed = now - startTime;
|
||||||
|
const progress = Math.min(elapsed / duration, 1);
|
||||||
|
const eased = 1 - Math.pow(1 - progress, 3);
|
||||||
|
setCount(Math.round(target * eased));
|
||||||
|
if (progress < 1) requestAnimationFrame(step);
|
||||||
|
};
|
||||||
|
requestAnimationFrame(step);
|
||||||
|
}, [started, target, duration]);
|
||||||
|
|
||||||
|
return { count, setStarted };
|
||||||
|
}
|
||||||
|
|
||||||
|
function StatCard({ value, suffix, label }: { value: number; suffix: string; label: string }) {
|
||||||
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
|
const { count, setStarted } = useCountUp(value);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const el = ref.current;
|
||||||
|
if (!el) return;
|
||||||
|
const observer = new IntersectionObserver(
|
||||||
|
([entry]) => {
|
||||||
|
if (entry.isIntersecting) {
|
||||||
|
setStarted(true);
|
||||||
|
observer.unobserve(el);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ threshold: 0.3 }
|
||||||
|
);
|
||||||
|
observer.observe(el);
|
||||||
|
return () => observer.disconnect();
|
||||||
|
}, [setStarted]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div ref={ref} className="text-center px-4">
|
||||||
|
<div className="text-5xl md:text-6xl font-bold">
|
||||||
|
<span className="text-cyan-400">{count}</span>
|
||||||
|
<span className="text-cyan-300">{suffix}</span>
|
||||||
|
</div>
|
||||||
|
<p className="text-gray-400 mt-3 text-sm tracking-wide uppercase">{label}</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Stats() {
|
||||||
|
return (
|
||||||
|
<section className="py-16 px-4 bg-gray-900 border-y border-gray-800">
|
||||||
|
<div className="max-w-4xl mx-auto">
|
||||||
|
<div className="grid grid-cols-2 md:grid-cols-4 gap-8">
|
||||||
|
{stats.map((s) => (
|
||||||
|
<StatCard key={s.label} value={s.value} suffix={s.suffix} label={s.label} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
33
components/Team.tsx
Normal file
33
components/Team.tsx
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import { UserIcon } from "@/components/Icons";
|
||||||
|
|
||||||
|
export default function Team() {
|
||||||
|
const members = [
|
||||||
|
{ name: "Jan Kowalski", role: "CEO" },
|
||||||
|
{ name: "Anna Nowak", role: "CTO" },
|
||||||
|
{ name: "Piotr Wiśniewski", role: "Lead Developer" },
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className="py-20 px-4 bg-gray-950">
|
||||||
|
<div className="max-w-6xl mx-auto">
|
||||||
|
<div className="text-center mb-14">
|
||||||
|
<h2 className="text-4xl font-bold text-white mb-4">Nasz zespół</h2>
|
||||||
|
<p className="text-gray-400 text-lg">Ludzie za SZMYT AI Labs</p>
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-1 sm:grid-cols-3 gap-6 max-w-4xl mx-auto">
|
||||||
|
{members.map((m) => (
|
||||||
|
<div key={m.name} className="bg-gray-800 border border-gray-700 rounded-xl p-6 text-center">
|
||||||
|
<div className="w-24 h-24 mx-auto mb-4 rounded-full bg-gray-700 border-2 border-gray-600 flex items-center justify-center">
|
||||||
|
<div className="w-12 h-12 text-gray-500">
|
||||||
|
<UserIcon />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<h3 className="text-lg font-semibold text-white">{m.name}</h3>
|
||||||
|
<p className="text-cyan-400 text-sm mt-1">{m.role}</p>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
31
components/Values.tsx
Normal file
31
components/Values.tsx
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import { LightbulbIcon, ShieldCheckIcon, ZapIcon, UsersIcon } from "@/components/Icons";
|
||||||
|
|
||||||
|
export default function Values() {
|
||||||
|
const values = [
|
||||||
|
{ Icon: LightbulbIcon, title: "Innowacja", description: "Zawsze szukamy nowoczesnych i sprawdzonych rozwiązań." },
|
||||||
|
{ Icon: ShieldCheckIcon, title: "Zaufanie", description: "Transparentność i rzetelność w każdym kroku współpracy." },
|
||||||
|
{ Icon: ZapIcon, title: "Efektywność", description: "Maksymalizujemy wyniki przy minimalnym wysiłku." },
|
||||||
|
{ Icon: UsersIcon, title: "Partnerstwo", description: "Jesteśmy Twoim partnerem — nie tylko dostawcą." },
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className="py-20 px-4 bg-gray-900">
|
||||||
|
<div className="max-w-6xl mx-auto">
|
||||||
|
<div className="text-center mb-14">
|
||||||
|
<h2 className="text-4xl font-bold text-white mb-4">Nasze wartości</h2>
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||||
|
{values.map((v) => (
|
||||||
|
<div key={v.title} className="bg-gray-800 border border-gray-700 rounded-xl p-6 text-center hover:border-cyan-500/50 transition-colors">
|
||||||
|
<div className="w-8 h-8 text-cyan-400 mx-auto mb-4">
|
||||||
|
<v.Icon />
|
||||||
|
</div>
|
||||||
|
<h3 className="text-lg font-semibold text-white mb-2">{v.title}</h3>
|
||||||
|
<p className="text-gray-300 text-sm leading-relaxed">{v.description}</p>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
50
components/WhatWeDo.tsx
Normal file
50
components/WhatWeDo.tsx
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import { SearchIcon, CpuIcon, TrendingUpIcon } from "@/components/Icons";
|
||||||
|
import ScrollReveal from "@/components/ScrollReveal";
|
||||||
|
|
||||||
|
export default function WhatWeDo() {
|
||||||
|
const services = [
|
||||||
|
{
|
||||||
|
Icon: SearchIcon,
|
||||||
|
title: "Analiza procesów",
|
||||||
|
description: "Mapujemy Twoje procesy biznesowe i identyfikujemy obszary idealne do automatyzacji.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Icon: CpuIcon,
|
||||||
|
title: "Wdrożenie agentów AI",
|
||||||
|
description: "Tworzymy i wdrażamy agenty AI w pełni dopasowane do potrzeb Twojej firmy.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Icon: TrendingUpIcon,
|
||||||
|
title: "Monitor & Optymalizacja",
|
||||||
|
description: "Śledzi i optymalizujemy wydajność agentów w czasie rzeczywistym, non-stop.",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className="py-20 px-4 bg-gray-950">
|
||||||
|
<div className="max-w-6xl mx-auto">
|
||||||
|
<ScrollReveal>
|
||||||
|
<div className="text-center mb-14">
|
||||||
|
<h2 className="text-4xl font-bold text-white mb-4">Co oferujemy</h2>
|
||||||
|
<p className="text-gray-400 text-lg max-w-2xl mx-auto">
|
||||||
|
Kompleksowe rozwiązania AI dopasowane do każdej firmy
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</ScrollReveal>
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||||
|
{services.map((s, i) => (
|
||||||
|
<ScrollReveal key={s.title} delay={i * 150}>
|
||||||
|
<div className="bg-gray-800 border border-gray-700 rounded-xl p-6 cursor-pointer hover:border-cyan-500/50 hover:-translate-y-1 hover:shadow-lg hover:shadow-cyan-500/10 transition-all duration-300 h-full">
|
||||||
|
<div className="w-8 h-8 text-cyan-400 mb-4 icon-pulse">
|
||||||
|
<s.Icon />
|
||||||
|
</div>
|
||||||
|
<h3 className="text-xl font-semibold text-white mb-2">{s.title}</h3>
|
||||||
|
<p className="text-gray-300 leading-relaxed">{s.description}</p>
|
||||||
|
</div>
|
||||||
|
</ScrollReveal>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
35
components/WhyUs.tsx
Normal file
35
components/WhyUs.tsx
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import ScrollReveal from "@/components/ScrollReveal";
|
||||||
|
|
||||||
|
export default function WhyUs() {
|
||||||
|
const reasons = [
|
||||||
|
"Dedykowane rozwiązania dla każdej firmy — nikt nie dostanie tego samego produktu.",
|
||||||
|
"Wsparcie techniczne i merytoryczne na każdym etapie współpracy.",
|
||||||
|
"Sprawdzone technologie AI wdrożone w rzeczywistych biznesach.",
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className="py-20 px-4 bg-gray-950">
|
||||||
|
<div className="max-w-4xl mx-auto">
|
||||||
|
<ScrollReveal>
|
||||||
|
<div className="text-center mb-14">
|
||||||
|
<h2 className="text-4xl font-bold text-white mb-4">Dlaczego SZMYT AI Labs</h2>
|
||||||
|
</div>
|
||||||
|
</ScrollReveal>
|
||||||
|
<div className="flex flex-col gap-6">
|
||||||
|
{reasons.map((reason, i) => (
|
||||||
|
<ScrollReveal key={i} delay={i * 150}>
|
||||||
|
<div className="bg-gray-800 border border-gray-700 rounded-xl p-6 border-glow-left flex items-start gap-4 cursor-pointer hover:-translate-y-1 hover:shadow-[0_8px_30px_rgb(6,182,212,0.3)] transition-all duration-300 group">
|
||||||
|
<div className="w-8 h-8 rounded-full bg-cyan-500/20 border border-cyan-500/50 flex items-center justify-center flex-shrink-0 mt-0.5" aria-hidden="true">
|
||||||
|
<svg className="w-4 h-4 text-cyan-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<p className="text-gray-200 text-lg leading-relaxed">{reason}</p>
|
||||||
|
</div>
|
||||||
|
</ScrollReveal>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
1
data/submissions.json
Normal file
1
data/submissions.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
[]
|
||||||
136
lib/blog.ts
Normal file
136
lib/blog.ts
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
import clientPromise from "./mongodb";
|
||||||
|
import { ObjectId } from "mongodb";
|
||||||
|
|
||||||
|
export interface BlogPost {
|
||||||
|
_id?: ObjectId;
|
||||||
|
title: string;
|
||||||
|
slug: string;
|
||||||
|
excerpt: string;
|
||||||
|
content: string;
|
||||||
|
author: string;
|
||||||
|
category: "case-study" | "blog";
|
||||||
|
tags: string[];
|
||||||
|
coverImage?: string;
|
||||||
|
publishedAt: Date;
|
||||||
|
updatedAt: Date;
|
||||||
|
featured: boolean;
|
||||||
|
seo?: {
|
||||||
|
title?: string;
|
||||||
|
description?: string;
|
||||||
|
keywords?: string[];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const DB_NAME = "mo1124_ai_web";
|
||||||
|
const COLLECTION_NAME = "posts";
|
||||||
|
|
||||||
|
// Helper function to serialize MongoDB documents for client components
|
||||||
|
function serializePost(post: any): BlogPost {
|
||||||
|
return {
|
||||||
|
...post,
|
||||||
|
_id: post._id?.toString(),
|
||||||
|
// Normalize author to always be a string
|
||||||
|
author:
|
||||||
|
typeof post.author === "string"
|
||||||
|
? post.author
|
||||||
|
: post.author?.name || "Unknown",
|
||||||
|
// Normalize tags to always be an array
|
||||||
|
tags: Array.isArray(post.tags)
|
||||||
|
? post.tags
|
||||||
|
: typeof post.tags === "string"
|
||||||
|
? post.tags.trim().startsWith("[")
|
||||||
|
? JSON.parse(post.tags)
|
||||||
|
: [post.tags]
|
||||||
|
: [],
|
||||||
|
publishedAt:
|
||||||
|
post.publishedAt instanceof Date
|
||||||
|
? post.publishedAt
|
||||||
|
: new Date(post.publishedAt),
|
||||||
|
updatedAt:
|
||||||
|
post.updatedAt instanceof Date
|
||||||
|
? post.updatedAt
|
||||||
|
: new Date(post.updatedAt),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getAllPosts(
|
||||||
|
category?: "case-study" | "blog",
|
||||||
|
): Promise<BlogPost[]> {
|
||||||
|
try {
|
||||||
|
const client = await clientPromise;
|
||||||
|
const db = client.db(DB_NAME);
|
||||||
|
|
||||||
|
const query = category ? { category } : {};
|
||||||
|
const posts = await db
|
||||||
|
.collection<BlogPost>(COLLECTION_NAME)
|
||||||
|
.find(query)
|
||||||
|
.sort({ publishedAt: -1 })
|
||||||
|
.toArray();
|
||||||
|
|
||||||
|
return posts.map(serializePost);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Database Error:", error);
|
||||||
|
console.warn("MongoDB connection failed, returning empty posts array");
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getPostBySlug(slug: string): Promise<BlogPost | null> {
|
||||||
|
try {
|
||||||
|
const client = await clientPromise;
|
||||||
|
const db = client.db(DB_NAME);
|
||||||
|
|
||||||
|
const post = await db
|
||||||
|
.collection<BlogPost>(COLLECTION_NAME)
|
||||||
|
.findOne({ slug });
|
||||||
|
|
||||||
|
return post ? serializePost(post) : null;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Database Error:", error);
|
||||||
|
console.warn("MongoDB connection failed, returning null for post");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getFeaturedPosts(limit: number = 3): Promise<BlogPost[]> {
|
||||||
|
try {
|
||||||
|
const client = await clientPromise;
|
||||||
|
const db = client.db(DB_NAME);
|
||||||
|
|
||||||
|
const posts = await db
|
||||||
|
.collection<BlogPost>(COLLECTION_NAME)
|
||||||
|
.find({ featured: true })
|
||||||
|
.sort({ publishedAt: -1 })
|
||||||
|
.limit(limit)
|
||||||
|
.toArray();
|
||||||
|
|
||||||
|
return posts.map(serializePost);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Database Error:", error);
|
||||||
|
console.warn("MongoDB connection failed, returning empty featured posts");
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getPostsByCategory(
|
||||||
|
category: "case-study" | "blog",
|
||||||
|
): Promise<BlogPost[]> {
|
||||||
|
try {
|
||||||
|
const client = await clientPromise;
|
||||||
|
const db = client.db(DB_NAME);
|
||||||
|
|
||||||
|
const posts = await db
|
||||||
|
.collection<BlogPost>(COLLECTION_NAME)
|
||||||
|
.find({ category })
|
||||||
|
.sort({ publishedAt: -1 })
|
||||||
|
.toArray();
|
||||||
|
|
||||||
|
return posts.map(serializePost);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Database Error:", error);
|
||||||
|
console.warn(
|
||||||
|
"MongoDB connection failed, returning empty posts by category",
|
||||||
|
);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
21
lib/mongodb.ts
Normal file
21
lib/mongodb.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { MongoClient } from "mongodb";
|
||||||
|
|
||||||
|
// Production MongoDB connection
|
||||||
|
const uri =
|
||||||
|
"mongodb://mo1124_ai_web:~4nfR5EA)3%C2%A3%40@mongo76.mydevil.net:27017/mo1124_ai_web?authSource=mo1124_ai_web";
|
||||||
|
|
||||||
|
// Use global variable to preserve connection across HMR in development
|
||||||
|
let globalWithMongo = global as typeof globalThis & {
|
||||||
|
_mongoClientPromise?: Promise<MongoClient>;
|
||||||
|
};
|
||||||
|
|
||||||
|
let clientPromise: Promise<MongoClient>;
|
||||||
|
|
||||||
|
if (!globalWithMongo._mongoClientPromise) {
|
||||||
|
const client = new MongoClient(uri);
|
||||||
|
globalWithMongo._mongoClientPromise = client.connect();
|
||||||
|
}
|
||||||
|
|
||||||
|
clientPromise = globalWithMongo._mongoClientPromise;
|
||||||
|
|
||||||
|
export default clientPromise;
|
||||||
37
next.config.ts
Normal file
37
next.config.ts
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import type { NextConfig } from "next";
|
||||||
|
|
||||||
|
const nextConfig: NextConfig = {
|
||||||
|
trailingSlash: true,
|
||||||
|
output: "standalone",
|
||||||
|
// Removed output: "export" to enable API routes and ISR for blog functionality
|
||||||
|
|
||||||
|
// Force new build ID to bust cache
|
||||||
|
generateBuildId: async () => {
|
||||||
|
return `build-${Date.now()}`;
|
||||||
|
},
|
||||||
|
|
||||||
|
// Prevent nginx from caching HTML pages
|
||||||
|
async headers() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
source: "/:path*",
|
||||||
|
headers: [
|
||||||
|
{
|
||||||
|
key: "Cache-Control",
|
||||||
|
value: "no-cache, no-store, must-revalidate",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "Pragma",
|
||||||
|
value: "no-cache",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "Expires",
|
||||||
|
value: "0",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default nextConfig;
|
||||||
2355
package-lock.json
generated
Normal file
2355
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
26
package.json
Normal file
26
package.json
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"name": "aiagentdlafirm",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"dev": "next dev",
|
||||||
|
"build": "next build",
|
||||||
|
"start": "next start -p 56693 -H localhost",
|
||||||
|
"seed:blog": "tsx scripts/seed-blog.ts"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"mongodb": "^7.1.0",
|
||||||
|
"next": "16.1.6",
|
||||||
|
"react": "19.2.3",
|
||||||
|
"react-dom": "19.2.3"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@tailwindcss/postcss": "^4",
|
||||||
|
"@types/node": "^20",
|
||||||
|
"@types/react": "^19",
|
||||||
|
"@types/react-dom": "^19",
|
||||||
|
"tailwindcss": "^4",
|
||||||
|
"tsx": "^4.21.0",
|
||||||
|
"typescript": "^5"
|
||||||
|
}
|
||||||
|
}
|
||||||
7
postcss.config.mjs
Normal file
7
postcss.config.mjs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
const config = {
|
||||||
|
plugins: {
|
||||||
|
"@tailwindcss/postcss": {},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
||||||
15
public/.htaccess
Normal file
15
public/.htaccess
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# Cache static assets
|
||||||
|
<FilesMatch "\.(jpg|jpeg|png|gif|svg|webp|ico|css|js|woff|woff2|ttf|eot)$">
|
||||||
|
Header set Cache-Control "public, max-age=31536000, immutable"
|
||||||
|
</FilesMatch>
|
||||||
|
|
||||||
|
# Cache HTML with short lifetime
|
||||||
|
<FilesMatch "\.(html|htm)$">
|
||||||
|
Header set Cache-Control "public, max-age=3600, must-revalidate"
|
||||||
|
</FilesMatch>
|
||||||
|
|
||||||
|
# Security headers
|
||||||
|
Header set X-Content-Type-Options "nosniff"
|
||||||
|
Header set X-Frame-Options "SAMEORIGIN"
|
||||||
|
Header set X-XSS-Protection "1; mode=block"
|
||||||
|
Header set Referrer-Policy "strict-origin-when-cross-origin"
|
||||||
BIN
public/blog/ai-automation.jpg
Normal file
BIN
public/blog/ai-automation.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 38 KiB |
BIN
public/blog/ecommerce-ai.jpg
Normal file
BIN
public/blog/ecommerce-ai.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 227 KiB |
BIN
public/blog/salesforce-integration.jpg
Normal file
BIN
public/blog/salesforce-integration.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 98 KiB |
4
public/favicon.svg
Normal file
4
public/favicon.svg
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="32" height="32">
|
||||||
|
<rect width="32" height="32" rx="6" fill="#030712"/>
|
||||||
|
<text x="16" y="23" text-anchor="middle" font-family="Arial, sans-serif" font-weight="bold" font-size="22" fill="#06b6d4">S</text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 279 B |
BIN
public/og-image.png
Normal file
BIN
public/og-image.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
5
public/robots.txt
Normal file
5
public/robots.txt
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
User-agent: *
|
||||||
|
Allow: /
|
||||||
|
Disallow: /api/
|
||||||
|
|
||||||
|
Sitemap: https://sztucznainteligencjadlafirm.pl/sitemap.xml
|
||||||
229
scripts/seed-blog.ts
Normal file
229
scripts/seed-blog.ts
Normal file
@ -0,0 +1,229 @@
|
|||||||
|
import clientPromise from "@/lib/mongodb";
|
||||||
|
|
||||||
|
const samplePosts = [
|
||||||
|
{
|
||||||
|
title: "Jak AI może zautomatyzować obsługę klienta w e-commerce",
|
||||||
|
slug: "ai-obsluga-klienta-ecommerce",
|
||||||
|
excerpt:
|
||||||
|
"Dowiedz się jak agenty AI mogą odpowiadać na 80% zapytań klientów 24/7, skracając czas odpowiedzi z godzin do sekund.",
|
||||||
|
content: `
|
||||||
|
<h2>Wyzwanie: Zalewające zapytania klientów</h2>
|
||||||
|
<p>Sklepy e-commerce otrzymują setki zapytań dziennie: "Kiedy dostanę paczkę?", "Jak zwrócić produkt?", "Czy macie rozmiar XL?".</p>
|
||||||
|
|
||||||
|
<h2>Rozwiązanie: AI Agent obsługi klienta</h2>
|
||||||
|
<p>Agent AI może:</p>
|
||||||
|
<ul>
|
||||||
|
<li>Odpowiadać na FAQ w czasie rzeczywistym</li>
|
||||||
|
<li>Sprawdzać status zamówień w systemie</li>
|
||||||
|
<li>Inicjować zwroty i reklamacje</li>
|
||||||
|
<li>Przekazywać złożone sprawy do człowieka</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>Efekty</h2>
|
||||||
|
<p>Nasi klienci z e-commerce widzą:</p>
|
||||||
|
<ul>
|
||||||
|
<li><strong>80% automatyzacji</strong> - większość zapytań obsługiwana bez człowieka</li>
|
||||||
|
<li><strong>Czas odpowiedzi: <10 sekund</strong> - zamiast kilku godzin</li>
|
||||||
|
<li><strong>Zadowolenie klientów: +35%</strong> - szybsze odpowiedzi = szczęśliwsi klienci</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>Implementacja</h2>
|
||||||
|
<p>Wdrożenie zajmuje 2-3 tygodnie:</p>
|
||||||
|
<ol>
|
||||||
|
<li>Analiza najczęstszych zapytań</li>
|
||||||
|
<li>Integracja z systemem zamówień</li>
|
||||||
|
<li>Trening AI na historycznych rozmowach</li>
|
||||||
|
<li>Testowanie i deploy</li>
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
<p><strong>Zainteresowany?</strong> <a href="/#kontakt">Skontaktuj się z nami</a> po bezpłatną konsultację.</p>
|
||||||
|
`,
|
||||||
|
author: "Adrian Miesikowski",
|
||||||
|
category: "case-study",
|
||||||
|
tags: ["e-commerce", "obsługa klienta", "automatyzacja", "chatbot"],
|
||||||
|
coverImage: "/blog/ecommerce-ai.jpg",
|
||||||
|
publishedAt: new Date("2026-01-15"),
|
||||||
|
updatedAt: new Date("2026-01-15"),
|
||||||
|
featured: true,
|
||||||
|
seo: {
|
||||||
|
title: "AI w obsłudze klienta e-commerce - Case Study | SZMYT AI Labs",
|
||||||
|
description:
|
||||||
|
"Zobacz jak AI agent zautomatyzował 80% obsługi klienta w sklepie e-commerce. Czas odpowiedzi spadł z godzin do 10 sekund.",
|
||||||
|
keywords: [
|
||||||
|
"AI e-commerce",
|
||||||
|
"chatbot obsługa klienta",
|
||||||
|
"automatyzacja e-commerce",
|
||||||
|
"AI customer service",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "5 procesów biznesowych idealnych do automatyzacji AI w 2026",
|
||||||
|
slug: "5-procesow-do-automatyzacji-ai",
|
||||||
|
excerpt:
|
||||||
|
"Nie każdy proces nadaje się do automatyzacji AI. Sprawdź 5 obszarów, gdzie AI przynosi największy ROI.",
|
||||||
|
content: `
|
||||||
|
<h2>Wprowadzenie</h2>
|
||||||
|
<p>AI to potężne narzędzie, ale nie do wszystkiego. Oto 5 procesów, gdzie automatyzacja AI ma największy sens biznesowy:</p>
|
||||||
|
|
||||||
|
<h2>1. Obsługa zapytań klientów (Customer Support)</h2>
|
||||||
|
<p><strong>Dlaczego:</strong> Powtarzalne pytania, 24/7 availability, skalowalność</p>
|
||||||
|
<p><strong>ROI:</strong> 60-80% redukcja kosztów obsługi</p>
|
||||||
|
|
||||||
|
<h2>2. Analiza dokumentów i faktur</h2>
|
||||||
|
<p><strong>Dlaczego:</strong> Czasochłonne, podatne na błędy ludzkie, strukturalne dane</p>
|
||||||
|
<p><strong>ROI:</strong> 90% redukcja czasu przetwarzania</p>
|
||||||
|
|
||||||
|
<h2>3. Kwalifikacja leadów (Lead Scoring)</h2>
|
||||||
|
<p><strong>Dlaczego:</strong> AI analizuje setki sygnałów szybciej niż człowiek</p>
|
||||||
|
<p><strong>ROI:</strong> +40% konwersja leadów na klientów</p>
|
||||||
|
|
||||||
|
<h2>4. Content generation dla e-commerce</h2>
|
||||||
|
<p><strong>Dlaczego:</strong> Opisy produktów, SEO content, email marketing</p>
|
||||||
|
<p><strong>ROI:</strong> 10x szybsza produkcja contentu</p>
|
||||||
|
|
||||||
|
<h2>5. Monitoring i alerty</h2>
|
||||||
|
<p><strong>Dlaczego:</strong> AI wykrywa anomalie i przewiduje problemy</p>
|
||||||
|
<p><strong>ROI:</strong> Zapobieganie przestojom = miliony oszczędności</p>
|
||||||
|
|
||||||
|
<h2>Jak zacząć?</h2>
|
||||||
|
<ol>
|
||||||
|
<li>Wybierz 1 proces z listy</li>
|
||||||
|
<li>Zmierz current state (czas, koszt, błędy)</li>
|
||||||
|
<li>Pilot z AI na małą skalę</li>
|
||||||
|
<li>Mierz rezultaty i skaluj</li>
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
<p><a href="/#kontakt">Umów bezpłatną konsultację</a> - pomożemy wybrać najlepszy proces do automatyzacji w Twojej firmie.</p>
|
||||||
|
`,
|
||||||
|
author: "SZMYT AI Labs Team",
|
||||||
|
category: "blog",
|
||||||
|
tags: ["automatyzacja", "AI w biznesie", "ROI", "strategie AI"],
|
||||||
|
publishedAt: new Date("2026-01-20"),
|
||||||
|
updatedAt: new Date("2026-01-20"),
|
||||||
|
featured: true,
|
||||||
|
seo: {
|
||||||
|
title:
|
||||||
|
"5 procesów biznesowych do automatyzacji AI w 2026 | SZMYT AI Labs",
|
||||||
|
description:
|
||||||
|
"Dowiedz się które procesy biznesowe przynoszą największy ROI z automatyzacji AI. Praktyczny przewodnik z case studies.",
|
||||||
|
keywords: [
|
||||||
|
"automatyzacja AI",
|
||||||
|
"procesy biznesowe",
|
||||||
|
"AI ROI",
|
||||||
|
"AI w firmie",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Jak zintegrowaliśmy AI z Salesforce dla firmy logistycznej",
|
||||||
|
slug: "integracja-ai-salesforce-logistyka",
|
||||||
|
excerpt:
|
||||||
|
"Case study: AI agent automatyzujący wprowadzanie leadów do Salesforce skrócił proces z 15 minut do 30 sekund.",
|
||||||
|
content: `
|
||||||
|
<h2>Klient: Firma logistyczna, 200+ pracowników</h2>
|
||||||
|
|
||||||
|
<h2>Problem</h2>
|
||||||
|
<p>Sales team otrzymywał zapytania ofertowe emailem i ręcznie przepisywał dane do Salesforce:</p>
|
||||||
|
<ul>
|
||||||
|
<li>15-20 minut na jedno zapytanie</li>
|
||||||
|
<li>50-70 zapytań dziennie</li>
|
||||||
|
<li>Błędy w przepisywaniu danych</li>
|
||||||
|
<li>Opóźnienia w odpowiedziach</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>Rozwiązanie: AI Agent + Salesforce Integration</h2>
|
||||||
|
<p>Stworzyliśmy agenta AI który:</p>
|
||||||
|
<ol>
|
||||||
|
<li>Monitoruje skrzynkę zapytań ofertowych</li>
|
||||||
|
<li>Ekstrahuje kluczowe informacje (trasa, waga, termin)</li>
|
||||||
|
<li>Automatycznie tworzy Lead w Salesforce</li>
|
||||||
|
<li>Kategoryzuje i scoruje lead</li>
|
||||||
|
<li>Notyfikuje odpowiedniego sales repa</li>
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
<h2>Architektura</h2>
|
||||||
|
<pre><code>Email → AI Parser → Salesforce API → Slack Notification</code></pre>
|
||||||
|
|
||||||
|
<h2>Technologie</h2>
|
||||||
|
<ul>
|
||||||
|
<li>Claude 3.5 Sonnet (extraction)</li>
|
||||||
|
<li>Salesforce REST API</li>
|
||||||
|
<li>Gmail API</li>
|
||||||
|
<li>Slack Webhooks</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>Rezultaty po 3 miesiącach</h2>
|
||||||
|
<table>
|
||||||
|
<tr><th>Metryka</th><th>Przed</th><th>Po</th><th>Zmiana</th></tr>
|
||||||
|
<tr><td>Czas przetwarzania</td><td>15 min</td><td>30 sek</td><td>-96%</td></tr>
|
||||||
|
<tr><td>Błędy w danych</td><td>12%</td><td>0.5%</td><td>-96%</td></tr>
|
||||||
|
<tr><td>Leadów/dzień</td><td>50</td><td>120</td><td>+140%</td></tr>
|
||||||
|
<tr><td>Konwersja</td><td>22%</td><td>31%</td><td>+41%</td></tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<h2>ROI</h2>
|
||||||
|
<p><strong>Oszczędności:</strong> 12.5 godziny/dzień × 20 dni × 150 PLN/h = <strong>37,500 PLN/miesiąc</strong></p>
|
||||||
|
<p><strong>Koszt wdrożenia:</strong> 25,000 PLN (one-time) + 2,000 PLN/miesiąc (utrzymanie)</p>
|
||||||
|
<p><strong>Payback period:</strong> <1 miesiąc</p>
|
||||||
|
|
||||||
|
<h2>Lessons Learned</h2>
|
||||||
|
<ul>
|
||||||
|
<li>AI nie musi być perfekcyjne - 99.5% accuracy wystarczy</li>
|
||||||
|
<li>Integracja z istniejącymi narzędziami > nowe platformy</li>
|
||||||
|
<li>Human-in-the-loop dla edge cases jest OK</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p><strong>Podobne wyzwanie?</strong> <a href="/#kontakt">Napisz do nas</a> - pokażemy jak możemy pomóc.</p>
|
||||||
|
`,
|
||||||
|
author: "Adrian Miesikowski",
|
||||||
|
category: "case-study",
|
||||||
|
tags: ["Salesforce", "integracja", "logistyka", "email automation"],
|
||||||
|
coverImage: "/blog/salesforce-integration.jpg",
|
||||||
|
publishedAt: new Date("2026-01-10"),
|
||||||
|
updatedAt: new Date("2026-01-10"),
|
||||||
|
featured: false,
|
||||||
|
seo: {
|
||||||
|
title:
|
||||||
|
"Integracja AI z Salesforce - Case Study Logistyka | SZMYT AI Labs",
|
||||||
|
description:
|
||||||
|
"Jak AI agent skrócił proces wprowadzania leadów do Salesforce z 15 minut do 30 sekund. ROI w miesiąc.",
|
||||||
|
keywords: [
|
||||||
|
"Salesforce AI",
|
||||||
|
"automatyzacja CRM",
|
||||||
|
"AI logistyka",
|
||||||
|
"integracja Salesforce",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
async function seed() {
|
||||||
|
try {
|
||||||
|
console.log("🌱 Łączenie z MongoDB...");
|
||||||
|
const client = await clientPromise;
|
||||||
|
const db = client.db("mo1124_ai_web");
|
||||||
|
const collection = db.collection("posts");
|
||||||
|
|
||||||
|
console.log("🗑️ Czyszczenie istniejących postów...");
|
||||||
|
await collection.deleteMany({});
|
||||||
|
|
||||||
|
console.log("📝 Dodawanie przykładowych postów...");
|
||||||
|
const result = await collection.insertMany(samplePosts);
|
||||||
|
|
||||||
|
console.log(`✅ Dodano ${result.insertedCount} postów do bazy!`);
|
||||||
|
console.log("\n📊 Posty:");
|
||||||
|
samplePosts.forEach((post, i) => {
|
||||||
|
console.log(`${i + 1}. [${post.category}] ${post.title}`);
|
||||||
|
console.log(` URL: /blog/${post.slug}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("\n🎉 Seed completed!");
|
||||||
|
process.exit(0);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("❌ Błąd podczas seed:", error);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
seed();
|
||||||
34
tsconfig.json
Normal file
34
tsconfig.json
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2017",
|
||||||
|
"lib": ["dom", "dom.iterable", "esnext"],
|
||||||
|
"allowJs": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"strict": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"module": "esnext",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"incremental": true,
|
||||||
|
"plugins": [
|
||||||
|
{
|
||||||
|
"name": "next"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["./*"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"next-env.d.ts",
|
||||||
|
"**/*.ts",
|
||||||
|
"**/*.tsx",
|
||||||
|
".next/types/**/*.ts",
|
||||||
|
".next/dev/types/**/*.ts",
|
||||||
|
"**/*.mts"
|
||||||
|
],
|
||||||
|
"exclude": ["node_modules"]
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user