diff --git a/app/cennik-montazu/page.tsx b/app/cennik-montazu/page.tsx index 1f30a4e..b665ff7 100644 --- a/app/cennik-montazu/page.tsx +++ b/app/cennik-montazu/page.tsx @@ -3,6 +3,8 @@ import Link from 'next/link'; import Image from 'next/image'; import { Check, Phone, ArrowRight, Wrench, Zap, Wind } from 'lucide-react'; import { companyDetails } from '@/content/company'; +import { buttonClasses, Button } from '@/components/ui/Button'; + export const metadata: Metadata = { title: 'Cennik montażu klimatyzacji Wrocław 2026 — ile kosztuje montaż?', @@ -136,11 +138,11 @@ export default function CennikMontazuPage() {

- + - + Formularz kontaktowy @@ -247,11 +249,11 @@ export default function CennikMontazuPage() {

- + - + Napisz do nas diff --git a/app/klimatyzacja-do-biura/page.tsx b/app/klimatyzacja-do-biura/page.tsx index 2a95f42..9be6059 100644 --- a/app/klimatyzacja-do-biura/page.tsx +++ b/app/klimatyzacja-do-biura/page.tsx @@ -3,6 +3,8 @@ import Link from 'next/link'; import Image from 'next/image'; import { Check, Phone, Wrench, ArrowRight, Building2, Thermometer, Wind, Zap, Clock } from 'lucide-react'; import { companyDetails } from '@/content/company'; +import { buttonClasses, Button } from '@/components/ui/Button'; + export const metadata: Metadata = { title: 'Klimatyzacja do biura Wrocław — montaż, serwis', @@ -135,11 +137,11 @@ export default function KlimatyzacjaDoBiuraPage() {
- + - + Zapytaj o wycenę dla firmy @@ -273,11 +275,11 @@ export default function KlimatyzacjaDoBiuraPage() {

- + - + Napisz do nas diff --git a/app/klimatyzacja-do-domu/page.tsx b/app/klimatyzacja-do-domu/page.tsx index 38c3157..6afc8d2 100644 --- a/app/klimatyzacja-do-domu/page.tsx +++ b/app/klimatyzacja-do-domu/page.tsx @@ -3,6 +3,8 @@ import Link from 'next/link'; import Image from 'next/image'; import { Check, Phone, Wrench, ArrowRight, Home, Thermometer, Wind, Zap } from 'lucide-react'; import { companyDetails } from '@/content/company'; +import { buttonClasses, Button } from '@/components/ui/Button'; + export const metadata: Metadata = { title: 'Klimatyzacja do domu Wrocław — montaż split i multisplit', @@ -113,11 +115,11 @@ export default function KlimatyzacjaDoDomuPage() {
- + - + Dobierz urządzenie @@ -242,11 +244,11 @@ export default function KlimatyzacjaDoDomuPage() {

- + - + Kalkulator doboru diff --git a/app/koszyk/layout.tsx b/app/koszyk/layout.tsx new file mode 100644 index 0000000..d15a797 --- /dev/null +++ b/app/koszyk/layout.tsx @@ -0,0 +1,17 @@ +import type { Metadata } from 'next'; + +export const metadata: Metadata = { + title: 'Koszyk', + robots: { + index: false, + follow: true, + }, +}; + +export default function KoszykLayout({ + children, +}: { + children: React.ReactNode; +}) { + return <>{children}; +} diff --git a/app/koszyk/page.tsx b/app/koszyk/page.tsx index eb8e98f..f9eb48a 100644 --- a/app/koszyk/page.tsx +++ b/app/koszyk/page.tsx @@ -4,6 +4,8 @@ import { useCartStore } from '@/lib/cart-store'; import Image from 'next/image'; import Link from 'next/link'; import { Trash2, Plus, Minus, ShoppingBag, ArrowRight } from 'lucide-react'; +import { buttonClasses } from '@/components/ui/Button'; + export default function KoszykPage() { const { items, removeItem, updateQuantity, getTotal, clearCart } = useCartStore(); @@ -14,9 +16,9 @@ export default function KoszykPage() {
-

Koszyk jest pusty

+

Koszyk jest pusty

Dodaj produkty ze sklepu aby złożyć zamówienie.

- + Przejdź do sklepu
diff --git a/app/montaz-klimatyzacji/page.tsx b/app/montaz-klimatyzacji/page.tsx index 4470e0c..f8641dd 100644 --- a/app/montaz-klimatyzacji/page.tsx +++ b/app/montaz-klimatyzacji/page.tsx @@ -3,6 +3,8 @@ import Link from 'next/link'; import Image from 'next/image'; import { Check, Phone, Wrench, ArrowRight, ShieldCheck, Clock, Star } from 'lucide-react'; import { companyDetails } from '@/content/company'; +import { buttonClasses, Button } from '@/components/ui/Button'; + export const metadata: Metadata = { title: 'Montaż klimatyzacji Wrocław — split, multisplit', @@ -113,11 +115,11 @@ export default function MontazKlimatyzacjiPage() {
- + - + Dobierz urządzenie @@ -250,9 +252,9 @@ export default function MontazKlimatyzacjiPage() { Zadzwoń po bezpłatną wycenę — odpowiadamy szybko i przyjeżdżamy w całym Dolnym Śląsku.

- +
diff --git a/app/not-found.tsx b/app/not-found.tsx index ecd488e..deeee7b 100644 --- a/app/not-found.tsx +++ b/app/not-found.tsx @@ -1,4 +1,6 @@ import Link from 'next/link'; +import { buttonClasses } from '@/components/ui/Button'; + export default function NotFound() { return ( @@ -9,7 +11,7 @@ export default function NotFound() {

Szukana strona została usunięta lub nigdy nie istniała.

- + Wróć na stronę główną
diff --git a/app/page.tsx b/app/page.tsx index 6afc822..4d004aa 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -9,6 +9,7 @@ import { CtaBanner } from "@/components/sections/CtaBanner"; import { companyDetails } from "@/content/company"; import { Wrench, Phone, ArrowRight, Thermometer, Wind, Zap, Calendar, Check } from "lucide-react"; import Link from "next/link"; +import { Button, buttonClasses } from "@/components/ui/Button"; export default function Home() { const baseUrl = process.env.NEXT_PUBLIC_BASE_URL || 'https://thermcool.pl'; @@ -160,16 +161,16 @@ export default function Home() { na terenie całego Dolnego Śląska.

-
- +
+ Dowiedz się więcej - - +
diff --git a/app/porownaj/page.tsx b/app/porownaj/page.tsx index a96f472..367c94f 100644 --- a/app/porownaj/page.tsx +++ b/app/porownaj/page.tsx @@ -5,6 +5,8 @@ import Image from 'next/image'; import Link from 'next/link'; import { X, ShoppingCart } from 'lucide-react'; import { useCartStore } from '@/lib/cart-store'; +import { buttonClasses } from '@/components/ui/Button'; + export default function PorownajPage() { const { items, removeItem } = useCompareStore(); @@ -15,7 +17,7 @@ export default function PorownajPage() {

Nie masz żadnych produktów do porównania.

- + Wróć do sklepu
diff --git a/app/serwis-daikin/page.tsx b/app/serwis-daikin/page.tsx index e1dd5dc..c940bb1 100644 --- a/app/serwis-daikin/page.tsx +++ b/app/serwis-daikin/page.tsx @@ -2,6 +2,8 @@ import type { Metadata } from 'next'; import Link from 'next/link'; import { Check, Phone, Wrench, AlertTriangle, ArrowRight } from 'lucide-react'; import { companyDetails } from '@/content/company'; +import { buttonClasses, Button } from '@/components/ui/Button'; + export const metadata: Metadata = { title: 'Serwis klimatyzacji Daikin Wrocław — naprawa i przegląd', @@ -88,13 +90,13 @@ export default function SerwisDaikinPage() { Serwisujemy wszystkie serie — od Sensiry po systemy VRV — z użyciem oryginalnych części i pełną dokumentacją przeglądową.

-
- - + - + Zgłoś awarię online @@ -193,11 +195,11 @@ export default function SerwisDaikinPage() {

- + - + Zgłoś online diff --git a/app/serwis-klimatyzacji/page.tsx b/app/serwis-klimatyzacji/page.tsx index 3c13e8c..7d7d0bc 100644 --- a/app/serwis-klimatyzacji/page.tsx +++ b/app/serwis-klimatyzacji/page.tsx @@ -3,6 +3,8 @@ import Link from 'next/link'; import Image from 'next/image'; import { Check, Phone, Wrench, ArrowRight, Thermometer, Wind, Zap, ShieldCheck } from 'lucide-react'; import { companyDetails } from '@/content/company'; +import { buttonClasses, Button } from '@/components/ui/Button'; + export const metadata: Metadata = { title: 'Serwis klimatyzacji Wrocław — przegląd, czyszczenie, naprawa', @@ -146,11 +148,11 @@ export default function SerwisKlimatyzacjiPage() {
- + - + Zgłoś awarię online @@ -231,11 +233,11 @@ export default function SerwisKlimatyzacjiPage() {

- + - + Zgłoś online diff --git a/app/serwis-lg/page.tsx b/app/serwis-lg/page.tsx index 8ab3d1d..2ffbbe3 100644 --- a/app/serwis-lg/page.tsx +++ b/app/serwis-lg/page.tsx @@ -2,6 +2,8 @@ import type { Metadata } from 'next'; import Link from 'next/link'; import { Check, Phone, Wrench, AlertTriangle, ArrowRight } from 'lucide-react'; import { companyDetails } from '@/content/company'; +import { buttonClasses, Button } from '@/components/ui/Button'; + export const metadata: Metadata = { title: 'Serwis klimatyzacji LG Wrocław — naprawa i przegląd', @@ -84,13 +86,13 @@ export default function SerwisLGPage() { My mamy odpowiednie doświadczenie i narzędzia, żeby skutecznie diagnozować i naprawiać te urządzenia na terenie Dolnego Śląska.

-
- - + - + Napisz do nas @@ -188,9 +190,9 @@ export default function SerwisLGPage() { Zadzwoń lub napisz — odpowiadamy szybko i przyjeżdżamy na Dolny Śląsk.

- +
diff --git a/app/serwis-mitsubishi/page.tsx b/app/serwis-mitsubishi/page.tsx index 45c7cfd..d630797 100644 --- a/app/serwis-mitsubishi/page.tsx +++ b/app/serwis-mitsubishi/page.tsx @@ -2,6 +2,8 @@ import type { Metadata } from 'next'; import Link from 'next/link'; import { Check, Phone, Wrench, AlertTriangle, ArrowRight } from 'lucide-react'; import { companyDetails } from '@/content/company'; +import { buttonClasses, Button } from '@/components/ui/Button'; + export const metadata: Metadata = { title: 'Serwis klimatyzacji Mitsubishi Electric Wrocław — naprawa i przegląd', @@ -88,13 +90,13 @@ export default function SerwisMitsubishiPage() { wszystkie serie — MSZ, MXZ, MLZ, SEZ — z pełną dokumentacją przeglądową i oryginalnymi częściami zamiennymi.

-
- - + - + Zgłoś awarię online @@ -197,11 +199,11 @@ export default function SerwisMitsubishiPage() {

- + - + Zgłoś online diff --git a/app/serwis-samsung/page.tsx b/app/serwis-samsung/page.tsx index c984c70..16bbe81 100644 --- a/app/serwis-samsung/page.tsx +++ b/app/serwis-samsung/page.tsx @@ -2,6 +2,8 @@ import type { Metadata } from 'next'; import Link from 'next/link'; import { Check, Phone, Wrench, AlertTriangle, ArrowRight } from 'lucide-react'; import { companyDetails } from '@/content/company'; +import { buttonClasses, Button } from '@/components/ui/Button'; + export const metadata: Metadata = { title: 'Serwis klimatyzacji Samsung Wrocław — naprawa i przegląd', @@ -88,13 +90,13 @@ export default function SerwisSamsungPage() { Serwisujemy wszystkie serie — WindFree, Luzon, Maldives i inne — z szybkim terminem i precyzyjną diagnostyką elektroniczną.

-
- - + - + Zgłoś awarię online @@ -193,11 +195,11 @@ export default function SerwisSamsungPage() {

- + - + Zgłoś online diff --git a/app/zamowienie/layout.tsx b/app/zamowienie/layout.tsx new file mode 100644 index 0000000..8579daa --- /dev/null +++ b/app/zamowienie/layout.tsx @@ -0,0 +1,17 @@ +import type { Metadata } from 'next'; + +export const metadata: Metadata = { + title: 'Zamówienie', + robots: { + index: false, + follow: true, + }, +}; + +export default function ZamowienieLayout({ + children, +}: { + children: React.ReactNode; +}) { + return <>{children}; +} diff --git a/app/zamowienie/page.tsx b/app/zamowienie/page.tsx index c06b1d1..b883868 100644 --- a/app/zamowienie/page.tsx +++ b/app/zamowienie/page.tsx @@ -5,6 +5,8 @@ import { useActionState, useEffect } from 'react'; import Link from 'next/link'; import { submitOrderForm } from './actions'; import { ArrowLeft, CheckCircle } from 'lucide-react'; +import { buttonClasses } from '@/components/ui/Button'; + const initialState = { success: false, message: '' }; @@ -22,7 +24,7 @@ export default function ZamowieniePage() {

Koszyk jest pusty.

- + Wróć do sklepu
@@ -35,9 +37,9 @@ Wróć do sklepu
-

Zamówienie przyjęte!

+

Zamówienie przyjęte!

{state.message}

- + Wróć do sklepu
diff --git a/audit_headings.js b/audit_headings.js new file mode 100644 index 0000000..ffc09b1 --- /dev/null +++ b/audit_headings.js @@ -0,0 +1,109 @@ +const fs = require('fs'); +const path = require('path'); + +function walkDir(dir, callback) { + if (!fs.existsSync(dir)) return; + fs.readdirSync(dir).forEach(f => { + const dirPath = path.join(dir, f); + const isDirectory = fs.statSync(dirPath).isDirectory(); + if (isDirectory) { + walkDir(dirPath, callback); + } else if (dirPath.endsWith('.tsx')) { + callback(dirPath); + } + }); +} + +const appFiles = []; +const componentsFiles = []; + +walkDir('app', f => appFiles.push(f)); +walkDir('components', f => componentsFiles.push(f)); + +const allFiles = [...appFiles, ...componentsFiles]; +const results = {}; + +allFiles.forEach(file => { + const content = fs.readFileSync(file, 'utf8'); + const lines = content.split('\n'); + const headings = []; + + lines.forEach((line, index) => { + const regex = /]/g; + let match; + while ((match = regex.exec(line)) !== null) { + headings.push({ level: parseInt(match[1]), line: index + 1, content: line.trim() }); + } + }); + + if (headings.length > 0 || file.endsWith('page.tsx')) { + results[file] = headings; + } +}); + +// Analysis +console.log("=== HEADINGS AUDIT ==="); + +// 1. Pages with >1 H1 +console.log("\n1. Strony z więcej niż jednym

:"); +let foundMultipleH1 = false; +for (const [file, headings] of Object.entries(results)) { + if (file.includes('page.tsx')) { + const h1s = headings.filter(h => h.level === 1); + if (h1s.length > 1) { + console.log(`- ${file} (Liczba

: ${h1s.length})`); + foundMultipleH1 = true; + } + } +} +if (!foundMultipleH1) console.log("Brak."); + +// 2. Pages with 0 H1 +console.log("\n2. Strony (page.tsx) bez

:"); +let foundZeroH1 = false; +for (const [file, headings] of Object.entries(results)) { + if (file.includes('page.tsx') && !file.includes('api/')) { // ignoring api if any + const h1s = headings.filter(h => h.level === 1); + if (h1s.length === 0) { + console.log(`- ${file}`); + foundZeroH1 = true; + } + } +} +if (!foundZeroH1) console.log("Brak."); + +// 3. Components rendering H1 +console.log("\n3. Komponenty współdzielone (w components/) z

:"); +let foundComponentH1 = false; +for (const [file, headings] of Object.entries(results)) { + if (file.startsWith('components/')) { + const h1s = headings.filter(h => h.level === 1); + if (h1s.length > 0) { + console.log(`- ${file} (${h1s.length} wystąpień

)`); + foundComponentH1 = true; + } + } +} +if (!foundComponentH1) console.log("Brak."); + +// 4. Heading Skips +console.log("\n4. Skoki poziomów nagłówków w plikach:"); +let foundSkips = false; +for (const [file, headings] of Object.entries(results)) { + if (headings.length === 0) continue; + + let currentLevel = null; + + for (const h of headings) { + if (currentLevel !== null) { + // A skip happens if the new level is strictly greater than currentLevel + 1 + if (h.level > currentLevel + 1) { + console.log(`- ${file}: Skok z na w linii ${h.line}`); + foundSkips = true; + } + } + currentLevel = h.level; + } +} +if (!foundSkips) console.log("Brak oczywistych skoków."); + diff --git a/components/awaria/AwariaClient.tsx b/components/awaria/AwariaClient.tsx index d0c9617..2a43ce6 100644 --- a/components/awaria/AwariaClient.tsx +++ b/components/awaria/AwariaClient.tsx @@ -152,9 +152,9 @@ export function AwariaClient() {
-

+

Zgłoszenie przyjęte! -

+

{isUrgent ? 'Twoje zgłoszenie oznaczono jako PILNE — skontaktujemy się z Tobą w ciągu 2 godzin.' diff --git a/components/layout/Header.tsx b/components/layout/Header.tsx index 4098cf2..64a1a2b 100644 --- a/components/layout/Header.tsx +++ b/components/layout/Header.tsx @@ -9,6 +9,8 @@ import { MobileMenu } from "./MobileMenu"; import { useCartStore } from "@/lib/cart-store"; import { companyDetails } from "@/content/company"; import { cn } from "@/lib/utils"; +import { buttonClasses } from '@/components/ui/Button'; + export function Header() { const [isScrolled, setIsScrolled] = useState(false); @@ -240,13 +242,13 @@ export function Header() { {/* CTA Button */}

- + Zgłoś awarię - + Darmowa wycena diff --git a/components/sections/ConfiguratorTeaser.tsx b/components/sections/ConfiguratorTeaser.tsx index 99b3750..68f8c4c 100644 --- a/components/sections/ConfiguratorTeaser.tsx +++ b/components/sections/ConfiguratorTeaser.tsx @@ -50,7 +50,7 @@ export const ConfiguratorTeaser = () => {
-

{step.label}

+

{step.label}

Krok {idx + 1}

@@ -62,7 +62,7 @@ export const ConfiguratorTeaser = () => {
-
Porada eksperta
+

Porada eksperta

Szybki przelicznik mocy: 10 m² = 1 kW mocy chłodniczej. Dla pokoju 25 m² szukaj urządzenia o mocy ok. 2.5 kW.

diff --git a/components/sections/FundingSection.tsx b/components/sections/FundingSection.tsx index cf2f053..2530286 100644 --- a/components/sections/FundingSection.tsx +++ b/components/sections/FundingSection.tsx @@ -55,7 +55,7 @@ export const FundingSection = () => {

Twój kosztorys z dotacją

-

Przykładowa kalkulacja

+

Przykładowa kalkulacja

diff --git a/components/sections/PricingTable.tsx b/components/sections/PricingTable.tsx index 38393f0..6a9b06f 100644 --- a/components/sections/PricingTable.tsx +++ b/components/sections/PricingTable.tsx @@ -66,10 +66,10 @@ export const PricingTable = () => {
-

+

Co obejmuje montaż podstawowy? -

+
  • Wykonanie przewiertu przez ścianę
  • Instalacja chłodnicza do 3 mb
  • diff --git a/components/shop/ProductPageClient.tsx b/components/shop/ProductPageClient.tsx index f20d5e3..a11aad7 100644 --- a/components/shop/ProductPageClient.tsx +++ b/components/shop/ProductPageClient.tsx @@ -361,7 +361,7 @@ export function ProductPageClient({ product, seriesProducts, companyPhone, compa
    -

    Kalkulator kosztów energii

    +

    Kalkulator kosztów energii

    diff --git a/components/ui/Button.tsx b/components/ui/Button.tsx new file mode 100644 index 0000000..8f00c91 --- /dev/null +++ b/components/ui/Button.tsx @@ -0,0 +1,54 @@ +import React from 'react'; +import { cn } from '@/lib/utils'; + +export type ButtonVariant = 'primary' | 'outline' | 'error'; +export type ButtonSize = 'sm' | 'md' | 'lg'; + +export interface ButtonClassesProps { + variant?: ButtonVariant; + size?: ButtonSize; + className?: string; +} + +export function buttonClasses({ variant = 'primary', size = 'md', className }: ButtonClassesProps = {}) { + const baseClasses = 'inline-flex items-center justify-center gap-2 whitespace-nowrap transition-colors'; + + const variantClasses = { + primary: 'btn btn-primary text-white font-bold', + outline: 'btn bg-white/10 text-white border border-white/20 hover:bg-white/20 font-bold', + error: 'btn btn-error text-white font-bold', + }; + + const sizeClasses = { + sm: 'btn-sm text-sm', + md: '', // default DaisyUI btn size + lg: 'btn-lg text-lg', + }; + + return cn( + baseClasses, + variantClasses[variant], + sizeClasses[size], + className + ); +} + +export interface ButtonProps extends React.ButtonHTMLAttributes, ButtonClassesProps { + children: React.ReactNode; +} + +export const Button = React.forwardRef( + ({ className, variant, size, children, ...props }, ref) => { + return ( + + ); + } +); + +Button.displayName = 'Button'; diff --git a/find_buttons.py b/find_buttons.py new file mode 100644 index 0000000..317ee5a --- /dev/null +++ b/find_buttons.py @@ -0,0 +1,43 @@ +import os +import re +from collections import Counter + +dirs_to_search = ['app', 'components'] +button_classes = [] + +class_regex = re.compile(r'className=["\']([^"\']+)["\']') +dynamic_class_regex = re.compile(r'className=\{`([^`]+)`\}') + +for d in dirs_to_search: + for root, _, files in os.walk(d): + for file in files: + if file.endswith(('.tsx', '.jsx', '.ts', '.js')): + filepath = os.path.join(root, file) + with open(filepath, 'r', encoding='utf-8') as f: + content = f.read() + + # Find all classNames + matches = class_regex.findall(content) + dynamic_class_regex.findall(content) + + for m in matches: + # Check if it looks like a button + if 'btn' in m or ('px-' in m and 'py-' in m and 'rounded' in m): + # Normalize spacing + normalized = ' '.join(m.split()) + button_classes.append((normalized, filepath)) + +# Count frequencies +counts = Counter([c[0] for c in button_classes]) +most_common = counts.most_common(10) + +print("Top variants:") +for variant, count in most_common: + print(f"\nVariant ({count} times): {variant}") + # Find up to 3 files where it appears + files_seen = set() + for c, fpath in button_classes: + if c == variant and fpath not in files_seen: + files_seen.add(fpath) + print(f" - {fpath}") + if len(files_seen) >= 3: + break diff --git a/refactor_buttons.js b/refactor_buttons.js new file mode 100644 index 0000000..9f89c1a --- /dev/null +++ b/refactor_buttons.js @@ -0,0 +1,133 @@ +const fs = require('fs'); +const path = require('path'); + +const directories = ['app', 'components']; +const patterns = [ + { + regex: /className=["']flex items-center gap-2 px-6 py-3 bg-accent text-white rounded-xl font-bold hover:bg-accent\/90 transition-colors["']/g, + replace: "className={buttonClasses({ variant: 'primary', size: 'md' })}" + }, + { + regex: /className=["']flex items-center gap-2 px-6 py-3 bg-white\/10 text-white rounded-xl font-bold hover:bg-white\/20 transition-colors border border-white\/20["']/g, + replace: "className={buttonClasses({ variant: 'outline', size: 'md' })}" + }, + { + regex: /className=["']flex items-center gap-2 px-8 py-4 bg-accent text-white rounded-xl font-bold text-lg hover:bg-accent\/90 transition-colors["']/g, + replace: "className={buttonClasses({ variant: 'primary', size: 'lg' })}" + }, + { + regex: /className=["']flex items-center gap-2 px-8 py-4 bg-white\/10 text-white rounded-xl font-bold text-lg hover:bg-white\/20 transition-colors border border-white\/20["']/g, + replace: "className={buttonClasses({ variant: 'outline', size: 'lg' })}" + }, + { + regex: /className=["']btn btn-primary text-white font-bold["']/g, + replace: "className={buttonClasses({ variant: 'primary', size: 'md' })}" + }, + { + regex: /className=["']btn btn-primary font-bold["']/g, + replace: "className={buttonClasses({ variant: 'primary', size: 'md' })}" + }, + { + regex: /className=["']flex items-center gap-1\.5 px-4 py-2 bg-red-500 text-white rounded-\[var\(--radius-field\)\] font-bold text-sm hover:bg-red-600 transition-colors whitespace-nowrap["']/g, + replace: "className={buttonClasses({ variant: 'error', size: 'sm' })}" + } +]; + +// In case there are some dynamic classNames (template literals): +const dynamicPatterns = [ + { + regex: /className=\{`flex items-center gap-2 px-6 py-3 bg-accent text-white rounded-xl font-bold hover:bg-accent\/90 transition-colors (.*?)`\}/g, + replace: "className={buttonClasses({ variant: 'primary', size: 'md', className: `$1` })}" + }, + { + regex: /className=\{`btn btn-primary font-bold (.*?)`\}/g, + replace: "className={buttonClasses({ variant: 'primary', size: 'md', className: `$1` })}" + } +]; + +function processDirectory(dir) { + const files = fs.readdirSync(dir); + + for (const file of files) { + const fullPath = path.join(dir, file); + const stat = fs.statSync(fullPath); + + if (stat.isDirectory()) { + processDirectory(fullPath); + } else if (file.endsWith('.tsx') || file.endsWith('.jsx')) { + // Don't process the Button.tsx itself + if (fullPath.includes('Button.tsx')) continue; + + let content = fs.readFileSync(fullPath, 'utf8'); + let originalContent = content; + + let changed = false; + let usedButtonClasses = false; + let usedButtonComponent = false; + + // 1. Tag conversion: '); + } + + if (changed) { + // We still might have used buttonClasses for tags + const needsButtonClasses = content.includes('buttonClasses('); + + // Inject imports + const imports = []; + if (needsButtonClasses) imports.push('buttonClasses'); + if (usedButtonComponent) imports.push('Button'); + + if (imports.length > 0) { + const importStr = `import { ${imports.join(', ')} } from '@/components/ui/Button';\n`; + // Find the last import + const importRegex = /^import.*$/gm; + let match; + let lastImportIndex = -1; + while ((match = importRegex.exec(content)) !== null) { + lastImportIndex = match.index + match[0].length; + } + + if (lastImportIndex !== -1) { + content = content.slice(0, lastImportIndex) + '\n' + importStr + content.slice(lastImportIndex); + } else { + content = importStr + content; + } + } + + fs.writeFileSync(fullPath, content, 'utf8'); + console.log(`Updated ${fullPath}`); + } + } + } +} + +for (const dir of directories) { + processDirectory(path.join(__dirname, dir)); +}