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}PageV2() { 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}PageV2.${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 |