fix: les 6

This commit is contained in:
2026-03-11 14:07:00 +01:00
parent d5066021ab
commit 9ffdecf2c4
117 changed files with 13198 additions and 5194 deletions

View File

@@ -0,0 +1,6 @@
You are a Next.js 15 expert using App Router with TypeScript.
Use server components by default.
Use "use client" only when needed for interactivity.
Always define TypeScript interfaces for props, params, and API bodies.
Use Tailwind CSS for styling.
Use the @/ import alias for all local imports.

View 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

View 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.

View File

@@ -0,0 +1,7 @@
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
/* config options here */
};
export default nextConfig;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,23 @@
{
"name": "quickpoll",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start"
},
"dependencies": {
"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",
"typescript": "^5"
}
}

View File

@@ -0,0 +1,7 @@
const config = {
plugins: {
"@tailwindcss/postcss": {},
},
};
export default config;

View File

@@ -0,0 +1 @@
<svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>

After

Width:  |  Height:  |  Size: 391 B

View File

@@ -0,0 +1 @@
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -0,0 +1 @@
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1155 1000"><path d="m577.3 0 577.4 1000H0z" fill="#fff"/></svg>

After

Width:  |  Height:  |  Size: 128 B

View File

@@ -0,0 +1 @@
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>

After

Width:  |  Height:  |  Size: 385 B

View File

@@ -0,0 +1,24 @@
import { NextResponse } from "next/server";
import { getPollById } from "@/lib/data";
interface RouteParams {
params: Promise<{ id: string }>;
}
// STAP 3: GET /api/polls/[id] — enkele poll ophalen
//
// Wat moet je doen?
// 1. Haal het id op uit params (let op: params is een Promise!)
// 2. Zoek de poll met getPollById(id)
// 3. Als de poll niet bestaat, return een 404 JSON response
// 4. Als de poll wel bestaat, return de poll als JSON
//
// Hint: kijk naar /api/polls/route.ts voor een voorbeeld van NextResponse.json()
export async function GET(
request: Request,
{ params }: RouteParams
): Promise<NextResponse> {
// Jouw code hier...
return NextResponse.json({ error: "Nog niet geimplementeerd" }, { status: 501 });
}

View File

@@ -0,0 +1,28 @@
import { NextResponse } from "next/server";
import { votePoll } from "@/lib/data";
interface RouteParams {
params: Promise<{ id: string }>;
}
interface VoteBody {
optionIndex: number;
}
// STAP 4: POST /api/polls/[id]/vote — stem uitbrengen
//
// Wat moet je doen?
// 1. Haal het id op uit params
// 2. Lees de request body (request.json()) en cast naar VoteBody
// 3. Valideer: is optionIndex een number?
// 4. Roep votePoll(id, body.optionIndex) aan
// 5. Als het resultaat undefined is: return 404
// 6. Anders: return de geüpdatete poll als JSON
export async function POST(
request: Request,
{ params }: RouteParams
): Promise<NextResponse> {
// Jouw code hier...
return NextResponse.json({ error: "Nog niet geimplementeerd" }, { status: 501 });
}

View File

@@ -0,0 +1,24 @@
import { NextResponse } from "next/server";
import { getPolls, createPoll } from "@/lib/data";
import type { Poll, CreatePollBody } from "@/types";
// GET /api/polls — alle polls ophalen
export async function GET(): Promise<NextResponse<Poll[]>> {
const polls = getPolls();
return NextResponse.json(polls);
}
// POST /api/polls — nieuwe poll aanmaken
export async function POST(request: Request): Promise<NextResponse> {
const body: CreatePollBody = await request.json();
if (!body.question || !body.options || body.options.length < 2) {
return NextResponse.json(
{ error: "Vraag en minstens 2 opties zijn verplicht" },
{ status: 400 }
);
}
const newPoll = createPoll(body.question, body.options);
return NextResponse.json(newPoll, { status: 201 });
}

View File

@@ -0,0 +1,32 @@
"use client";
import { useState } from "react";
import { useRouter } from "next/navigation";
// BONUS: Maak een formulier om een nieuwe poll aan te maken
//
// Benodigde state:
// - question: string
// - options: string[] (start met ["", ""])
// - isSubmitting: boolean
// - error: string | null
//
// Wat moet je bouwen?
// 1. Een input voor de vraag
// 2. Inputs voor de opties (minimaal 2, maximaal 6)
// 3. Knoppen om opties toe te voegen/verwijderen
// 4. Een submit knop die POST naar /api/polls
// 5. Na success: redirect naar / met router.push("/")
export default function CreatePollPage() {
return (
<div className="max-w-2xl mx-auto">
<h1 className="text-2xl font-bold text-gray-900 mb-6">
Nieuwe Poll Aanmaken
</h1>
<p className="text-gray-400 italic">
Bonus: bouw hier het create formulier
</p>
</div>
);
}

View File

@@ -0,0 +1,30 @@
"use client";
// STAP 7: Error boundary
//
// Dit bestand vangt fouten op in de route.
// MOET een client component zijn ("use client" staat al bovenaan).
//
// Props die je krijgt:
// - error: Error — het error object met .message
// - reset: () => void — functie om de pagina opnieuw te proberen
//
// Bouw een nette error pagina met:
// - Een titel "Er ging iets mis!"
// - De error message
// - Een "Probeer opnieuw" knop die reset() aanroept
export default function Error({
error,
reset,
}: {
error: Error;
reset: () => void;
}) {
return (
<div className="text-center py-16">
<p>Er ging iets mis: {error.message}</p>
<button onClick={() => reset()}>Probeer opnieuw</button>
</div>
);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View File

@@ -0,0 +1 @@
@import "tailwindcss";

View File

@@ -0,0 +1,36 @@
import type { Metadata } from "next";
import Link from "next/link";
import "./globals.css";
export const metadata: Metadata = {
title: "QuickPoll — Stem op alles",
description: "Maak en deel polls met je vrienden",
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="nl">
<body className="min-h-screen bg-gray-50 text-gray-900">
{/*
STAP 1: Bouw hier een navigatiebalk met:
- Logo/titel "QuickPoll" (links) die linkt naar /
- Een link naar / ("Polls")
- Een link naar /create ("Nieuwe Poll")
Tip: gebruik <Link> van "next/link", niet <a>
Tip: gebruik Tailwind classes voor styling
*/}
<main className="max-w-4xl mx-auto px-4 py-8">{children}</main>
<footer className="text-center text-gray-400 text-sm py-8">
© 2025 QuickPoll NOVI Hogeschool Les 5
</footer>
</body>
</html>
);
}

View File

@@ -0,0 +1,17 @@
// STAP 7: Loading state
//
// Dit bestand wordt automatisch getoond terwijl een pagina laadt.
// Bouw een skeleton loader met Tailwind's animate-pulse class.
//
// Voorbeeld:
// <div className="animate-pulse">
// <div className="h-8 bg-gray-200 rounded w-1/3 mb-4" />
// </div>
export default function Loading() {
return (
<div>
<p>Laden...</p>
</div>
);
}

View File

@@ -0,0 +1,18 @@
import Link from "next/link";
// STAP 7: Not found pagina
//
// Wordt getoond als een pagina niet bestaat.
// Bouw een nette 404 pagina met een link terug naar home.
export default function NotFound() {
return (
<div className="text-center py-16">
<h2 className="text-4xl font-bold text-gray-900 mb-4">404</h2>
<p className="text-gray-600 mb-6">Deze pagina bestaat niet.</p>
<Link href="/" className="text-purple-600 hover:underline">
Terug naar home
</Link>
</div>
);
}

View File

@@ -0,0 +1,31 @@
import Link from "next/link";
import { getPolls } from "@/lib/data";
import type { Poll } from "@/types";
export const dynamic = "force-dynamic";
export default function HomePage() {
// STAP 2: Haal alle polls op met getPolls()
// Dit is een Server Component — je kunt gewoon functies aanroepen!
return (
<div>
<h1 className="text-3xl font-bold text-gray-900 mb-2">Actieve Polls</h1>
<p className="text-gray-500 mb-8">Klik op een poll om te stemmen</p>
<div className="grid gap-4">
{/*
STAP 2: Map over de polls en toon voor elke poll:
- De vraag (poll.question)
- Het aantal opties en stemmen
- De opties als tags/badges
- Wrap het in een <Link> naar /poll/{poll.id}
Tip: maak een helper functie voor het totaal aantal stemmen:
const totalVotes = (poll: Poll): number =>
poll.votes.reduce((sum, v) => sum + v, 0);
*/}
</div>
</div>
);
}

View File

@@ -0,0 +1,17 @@
import Link from "next/link";
export default function PollNotFound() {
return (
<div className="text-center py-16">
<h2 className="text-2xl font-bold text-gray-900 mb-4">
Poll niet gevonden
</h2>
<p className="text-gray-600 mb-6">
Deze poll bestaat niet of is verwijderd.
</p>
<Link href="/" className="text-purple-600 hover:underline">
Bekijk alle polls
</Link>
</div>
);
}

View File

@@ -0,0 +1,41 @@
import { notFound } from "next/navigation";
import { getPollById } from "@/lib/data";
import VoteForm from "@/components/VoteForm";
import type { Metadata } from "next";
interface PageProps {
params: Promise<{ id: string }>;
}
// STAP 5: generateMetadata — dynamische pagina titel
//
// Deze functie genereert de <title> tag voor SEO.
// Haal de poll op en return de vraag als titel.
// Als de poll niet bestaat, return "Poll niet gevonden".
export async function generateMetadata({ params }: PageProps): Promise<Metadata> {
const { id } = await params;
const poll = getPollById(id);
return {
title: poll ? `${poll.question} — QuickPoll` : "Poll niet gevonden",
};
}
// STAP 5: PollPage — de poll detail pagina
//
// Wat moet je doen?
// 1. Haal het id op uit params
// 2. Zoek de poll met getPollById(id)
// 3. Als de poll niet bestaat: roep notFound() aan
// 4. Render de poll vraag als <h1>
// 5. Render de <VoteForm poll={poll} /> component
export default async function PollPage({ params }: PageProps) {
// Jouw code hier...
return (
<div className="max-w-2xl mx-auto">
<p>Implementeer deze pagina (zie stap 5 in de opdracht)</p>
</div>
);
}

View File

@@ -0,0 +1,89 @@
"use client";
import { useState } from "react";
import { useRouter } from "next/navigation";
import type { Poll } from "@/types";
interface VoteFormProps {
poll: Poll;
}
// STAP 6: VoteForm — de stem interface
//
// Dit is een CLIENT component ("use client" staat bovenaan).
// Hier mag je wel useState en onClick gebruiken!
//
// Benodigde state:
// - selectedOption: number | null (welke optie is geselecteerd)
// - hasVoted: boolean (heeft de gebruiker al gestemd)
// - isSubmitting: boolean (wordt het formulier verstuurd)
// - currentPoll: Poll (de huidige poll data, update na stemmen)
//
// Wat moet je bouwen?
// 1. Toon alle opties als klikbare knoppen
// 2. Highlight de geselecteerde optie
// 3. Een "Stem!" knop die een POST doet naar /api/polls/{id}/vote
// 4. Na het stemmen: toon de resultaten met percentages
//
// De fetch call voor stemmen:
// const response = await fetch(`/api/polls/${currentPoll.id}/vote`, {
// method: "POST",
// headers: { "Content-Type": "application/json" },
// body: JSON.stringify({ optionIndex: selectedOption }),
// });
// if (response.ok) {
// const updatedPoll: Poll = await response.json();
// setCurrentPoll(updatedPoll);
// setHasVoted(true);
// }
export default function VoteForm({ poll }: VoteFormProps) {
const [selectedOption, setSelectedOption] = useState<number | null>(null);
const [hasVoted, setHasVoted] = useState<boolean>(false);
const [isSubmitting, setIsSubmitting] = useState<boolean>(false);
const [currentPoll, setCurrentPoll] = useState<Poll>(poll);
const router = useRouter();
const totalVotes: number = currentPoll.votes.reduce(
(sum, v) => sum + v,
0
);
function getPercentage(votes: number): number {
if (totalVotes === 0) return 0;
return Math.round((votes / totalVotes) * 100);
}
async function handleVote(): Promise<void> {
if (selectedOption === null || isSubmitting) return;
setIsSubmitting(true);
// STAP 6: Doe hier de fetch call naar de vote API
// Zie de beschrijving hierboven voor de code
setIsSubmitting(false);
}
return (
<div className="space-y-3">
{/*
STAP 6: Bouw hier de voting interface
ALS de gebruiker nog NIET gestemd heeft:
- Toon elke optie als een klikbare button
- Highlight de geselecteerde optie (purple border)
- Toon een radio-achtige indicator (gevuld/leeg rondje)
- Toon een "Stem!" knop onderaan
ALS de gebruiker WEL gestemd heeft:
- Toon elke optie met een percentage balk
- Toon het percentage en aantal stemmen
- Toon "Bedankt voor je stem!"
- Toon een "Terug" link naar /
*/}
<p className="text-gray-400 italic">
Bouw hier de voting interface (zie stap 6)
</p>
</div>
);
}

View File

@@ -0,0 +1,55 @@
import { Poll } from "@/types";
export const polls: Poll[] = [
{
id: "1",
question: "Wat is de beste code editor?",
options: ["VS Code", "Cursor", "Vim", "WebStorm"],
votes: [12, 25, 5, 3],
},
{
id: "2",
question: "Wat is de beste programmeertaal?",
options: ["TypeScript", "Python", "Rust", "Go"],
votes: [18, 15, 8, 4],
},
{
id: "3",
question: "Welk framework heeft de toekomst?",
options: ["Next.js", "Remix", "Astro", "SvelteKit"],
votes: [22, 6, 10, 7],
},
];
let nextId = 4;
export function getPolls(): Poll[] {
return polls;
}
export function getPollById(id: string): Poll | undefined {
return polls.find((poll) => poll.id === id);
}
export function createPoll(question: string, options: string[]): Poll {
const newPoll: Poll = {
id: String(nextId++),
question,
options,
votes: new Array(options.length).fill(0),
};
polls.push(newPoll);
return newPoll;
}
export function votePoll(
pollId: string,
optionIndex: number
): Poll | undefined {
const poll = polls.find((p) => p.id === pollId);
if (!poll || optionIndex < 0 || optionIndex >= poll.options.length) {
return undefined;
}
poll.votes[optionIndex]++;
return poll;
}

View File

@@ -0,0 +1,17 @@
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
export function middleware(request: NextRequest): NextResponse {
const start = Date.now();
console.log(`[${request.method}] ${request.nextUrl.pathname}`);
const response = NextResponse.next();
response.headers.set("x-request-time", String(Date.now() - start));
return response;
}
export const config = {
matcher: ["/api/:path*", "/poll/:path*"],
};

View File

@@ -0,0 +1,11 @@
export interface Poll {
id: string;
question: string;
options: string[];
votes: number[];
}
export interface CreatePollBody {
question: string;
options: string[];
}

View 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": {
"@/*": ["./src/*"]
}
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts",
".next/dev/types/**/*.ts",
"**/*.mts"
],
"exclude": ["node_modules"]
}