modules/frontend/Write-Frontend-EntityPages.ps1

param(
    [string]$ProjectName,
    [string]$OrmMode,
    [string]$OutputPath,
    [string]$FrontendMode,
    [string[]]$Entities,
    [string]$Language = 'tr'
)

. (Join-Path (Split-Path $MyInvocation.MyCommand.Path) "..\core\Utils.ps1")
. (Join-Path (Split-Path $MyInvocation.MyCommand.Path) "..\core\Get-Locale.ps1")
$L = Get-Locale -Language $Language

$isTS  = ($FrontendMode -eq "ReactTS")
$ext   = if ($isTS) { "tsx" } else { "jsx" }
$extTs = if ($isTS) { "ts"  } else { "js"  }
$BT    = [char]96  # backtick

$base = Join-Path $OutputPath "$ProjectName-web"
function Write-File($rel, $rawContent) {
    Write-CoreFile (Join-Path $base $rel) $rawContent
}

# ─── QueryClientProvider wiring in main entry (T-26 support) ──
$mainContent = @"
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import './global.css';
import App from './App';
 
const queryClient = new QueryClient({
  defaultOptions: { queries: { staleTime: 30_000, retry: 1 } },
});
 
createRoot(document.getElementById('root')!).render(
  <StrictMode>
    <QueryClientProvider client={queryClient}>
      <App />
    </QueryClientProvider>
  </StrictMode>
);
"@

Write-File "main.$ext" $mainContent

# ─── Per-entity React Query hooks (T-26) ──────────────────────
$nonUserEntities = $Entities | Where-Object { $_ -ne "User" }

foreach ($e in $nonUserEntities) {
    $lowE  = $e.ToLower()
    $pE    = Get-Plural $e
    $lowPE = $pE.ToLower()

    if ($isTS) {
        $addVarsType = if ($e -eq "Product") { "{ name: string; price: number }" } else { "{ name: string }" }
        $addVarsDestructure = if ($e -eq "Product") { "vars.name, vars.price" } else { "vars.name" }
        $hookContent = @"
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { ${lowE}Service } from '../services/${lowE}Service';
 
const KEY = ['${lowPE}'];
 
export function use${pE}(page: number = 1, size: number = 20) {
  return useQuery({
    queryKey: [...KEY, page, size],
    queryFn: () => ${lowE}Service.getPaged(page, size),
  });
}
 
export function useAdd${e}() {
  const qc = useQueryClient();
  return useMutation({
    mutationFn: (vars: ${addVarsType}) => ${lowE}Service.add(${addVarsDestructure}),
    onSuccess: () => qc.invalidateQueries({ queryKey: KEY }),
  });
}
 
export function useDelete${e}() {
  const qc = useQueryClient();
  return useMutation({
    mutationFn: (id: number) => ${lowE}Service.remove(id),
    onSuccess: () => qc.invalidateQueries({ queryKey: KEY }),
  });
}
"@

    } else {
        $addSig = if ($e -eq "Product") { "name, price" } else { "name" }
        $hookContent = @"
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { ${lowE}Service } from '../services/${lowE}Service';
 
const KEY = ['${lowPE}'];
 
export function use${pE}(page, size) {
  if (page === undefined) page = 1;
  if (size === undefined) size = 20;
  return useQuery({
    queryKey: [...KEY, page, size],
    queryFn: () => ${lowE}Service.getPaged(page, size),
  });
}
 
export function useAdd${e}() {
  const qc = useQueryClient();
  return useMutation({
    mutationFn: (${addSig}) => ${lowE}Service.add(${addSig}),
    onSuccess: () => qc.invalidateQueries({ queryKey: KEY }),
  });
}
 
export function useDelete${e}() {
  const qc = useQueryClient();
  return useMutation({
    mutationFn: (id) => ${lowE}Service.remove(id),
    onSuccess: () => qc.invalidateQueries({ queryKey: KEY }),
  });
}
"@

    }
    Write-File "hooks\use${pE}.${extTs}" $hookContent
}

# ─── Per-entity paged page (T-25) ─────────────────────────────
foreach ($e in $nonUserEntities) {
    $lowE  = $e.ToLower()
    $pE    = Get-Plural $e
    $lowPE = $pE.ToLower()

    # Build nav links without ternary operator
    $navLinks = ""
    foreach ($nav in $nonUserEntities) {
        $navLow = $nav.ToLower()
        $castExpr = if ($isTS) { "('${nav}' as string)" } else { "'${nav}'" }
        $navLinks += " <button onClick={() => navigate('/${navLow}s')} style={{ color: 'rgba(255,255,255,0.85)', background: 'none', border: 'none', cursor: 'pointer', fontWeight: ($castExpr === '${e}') ? 700 : 400 }}>${nav}</button>`n"
    }

    $pageContent = @"
import { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { useQueryClient } from '@tanstack/react-query';
import { useAuth } from '../context/AuthContext';
import { ${e}Form } from '../components/${lowE}s/${e}Form';
import { ${e}Table } from '../components/${lowE}s/${e}Table';
import { Button } from '../components/common/Button';
import { use${pE} } from '../hooks/use${pE}';
import styles from './Page.module.css';
 
export function ${e}Page() {
  const { username, logout } = useAuth();
  const navigate = useNavigate();
  const qc = useQueryClient();
  const [page, setPage] = useState(1);
  const SIZE = 20;
 
  const { data, isLoading, isError } = use${pE}(page, SIZE);
  const items = (data && data.data && data.data.items) ? data.data.items : [];
  const totalPages = (data && data.data && data.data.totalPages) ? data.data.totalPages : 1;
 
  return (
    <div className={styles.layout}>
      <header className={styles.topbar}>
        <div className={styles.left}>
          <span className={styles.brand}>${ProjectName}</span>
          <nav className={styles.nav}>
${navLinks} </nav>
        </div>
        <div className={styles.user}>
          <span className={styles.username}>{username}</span>
          <Button variant="danger" onClick={() => { logout(); navigate('/login'); }}
            style={{ padding: '7px 16px', fontSize: '0.83rem' }}>$($L.uiLogout)</Button>
        </div>
      </header>
      <main className={styles.main}>
        <section className={styles.card}>
          <h2 className={styles.sectionTitle}>$($L.uiNewEntity -f ${e})</h2>
          <${e}Form onAdded={() => qc.invalidateQueries({ queryKey: ['${lowPE}'] })} />
        </section>
        <section className={styles.card}>
          <h2 className={styles.sectionTitle}>$($L.uiEntityList -f ${e})</h2>
          {isError && <p style={{ color: 'red' }}>$($L.uiLoadFailed)</p>}
          <${e}Table items={items} loading={isLoading} />
          <div className={styles.pagination}>
            <Button onClick={() => setPage(function(p){ return Math.max(1, p - 1); })} disabled={page === 1}>$($L.uiPrevious)</Button>
            <span className={styles.pageInfo}>$($L.uiPage -f '{page}', '{totalPages}')</span>
            <Button onClick={() => setPage(function(p){ return Math.min(totalPages, p + 1); })} disabled={page >= totalPages}>$($L.uiNext)</Button>
          </div>
        </section>
      </main>
    </div>
  );
}
"@

    Write-File "pages\${e}Page.${ext}" $pageContent
}

# ─── Page Layout CSS ──────────────────────────────────────────
$pageCssContent = @'
.container {
  display: flex;
  min-height: 100vh;
  background-color: #0f172a;
}
 
.mainContent {
  flex: 1;
  padding: 2rem;
}
 
.header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 2rem;
  padding-bottom: 1rem;
  border-bottom: 1px solid #334155;
}
 
.title {
  font-size: 1.5rem;
  font-weight: 600;
  color: #f8fafc;
}
 
.content {
  background-color: #1e293b;
  padding: 1.5rem;
  border-radius: 0.5rem;
  border: 1px solid #334155;
}
 
.pagination {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: 16px;
}
 
.pageInfo {
  font-size: 0.88rem;
  color: #94a3b8;
}
'@


Write-File "pages\Page.module.css" $pageCssContent