modules/frontend/Write-Frontend-React-Pages.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" } $base = Join-Path $OutputPath "frontend" function Write-File($rel, $rawContent) { Write-CoreFile (Join-Path $base $rel) $rawContent } function Proj($t) { $t -replace '__PROJ__', $ProjectName } # ─── PAGES ──────────────────────────────────────────────── Write-File "pages\LoginPage.$ext" (Proj @" import { useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; import { LoginForm } from '../components/auth/LoginForm'; import { useAuth } from '../context/AuthContext'; import styles from './LoginPage.module.css'; export function LoginPage() { const { isAuthenticated } = useAuth(); const navigate = useNavigate(); useEffect(() => { if (isAuthenticated) navigate('/'); }, [isAuthenticated, navigate]); return ( <div className={styles.wrapper}> <div className={styles.card}> <div className={styles.header}> <h1 className={styles.title}>__PROJ__</h1> <p className={styles.subtitle}>$($L.uiLoginSubtitle)</p> </div> <LoginForm /> </div> </div> ); } "@) Write-File "pages\LoginPage.module.css" @' .wrapper { min-height: 100vh; display: flex; align-items: center; justify-content: center; background: linear-gradient(135deg, #1a1a2e 0%, #16213e 55%, #0f3460 100%); padding: 20px; } .card { background: #fff; border-radius: 16px; padding: 44px 40px; width: 100%; max-width: 400px; box-shadow: 0 24px 64px rgba(0,0,0,0.28); } .header { margin-bottom: 28px; } .title { font-size: 1.7rem; font-weight: 700; color: #0f3460; margin-bottom: 4px; } .subtitle { color: #6b7280; font-size: 0.88rem; } '@ foreach ($e in $Entities) { if ($e -eq "User") { continue } $lowE = $e.ToLower() $dirE = $lowE + "s" $stateType = if ($isTS) { "<${e}[]>" } else { "" } $importType = if ($isTS) { "`nimport type { $e } from '../types';" } else { "" } $catchClause = if ($isTS) { "catch (err) { if ((err as Error).message === 'UNAUTHORIZED') { logout(); navigate('/login'); } }" } else { "catch (err) { if (err.message === 'UNAUTHORIZED') { logout(); navigate('/login'); } }" } Write-File "pages\$e`Page.$ext" (Proj @" import { useState, useEffect, useCallback } from 'react'; import { useNavigate, Link } from 'react-router-dom'; import { useAuth } from '../context/AuthContext'; import { $e`Form } from '../components/$dirE/$e`Form'; import { $e`Table } from '../components/$dirE/$e`Table'; import { Button } from '../components/common/Button'; import { $lowE`Service } from '../services/$lowE`Service';$importType import styles from './Page.module.css'; export function $e`Page() { const { username, logout } = useAuth(); const navigate = useNavigate(); const [items, setItems] = useState$stateType([]); const [loading, setLoading] = useState(true); const loadData = useCallback(async () => { setLoading(true); try { setItems(await $lowE`Service.getAll()); } $catchClause finally { setLoading(false); } }, [logout, navigate]); useEffect(() => { loadData(); }, [loadData]); return ( <div className={styles.layout}> <header className={styles.topbar}> <div className={styles.left}> <span className={styles.brand}>__PROJ__</span> <nav className={styles.nav}> $( ($Entities | Where-Object { $_ -ne "User" } | ForEach-Object { "<Link to='/$($_.ToLower())s' className={styles.navLink}>$_</Link>" }) -join " " ) </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={loadData} /></section> <section className={styles.card}><h2 className={styles.sectionTitle}>$($L.uiEntityList -f $e)</h2><$e`Table items={items} loading={loading} /></section> </main> </div> ); } "@) } Write-File "pages\Page.module.css" @' .layout { display: flex; flex-direction: column; min-height: 100vh; background: #f0f2f5; } .topbar { background: #0f3460; color: #fff; padding: 0 32px; height: 58px; display: flex; align-items: center; justify-content: space-between; box-shadow: 0 2px 8px rgba(0,0,0,0.2); position: sticky; top: 0; z-index: 10; } .left { display: flex; align-items: center; gap: 32px; } .brand { font-size: 1.05rem; font-weight: 700; letter-spacing: 0.4px; } .nav { display: flex; gap: 16px; } .navLink { color: rgba(255,255,255,0.7); font-size: 0.88rem; transition: color 0.2s; } .navLink:hover { color: #fff; } .user { display: flex; align-items: center; gap: 14px; } .username { font-size: 0.88rem; opacity: 0.85; } .main { flex: 1; padding: 28px 32px; max-width: 960px; width: 100%; margin: 0 auto; display: flex; flex-direction: column; gap: 20px; } .card { background: #fff; border-radius: 12px; padding: 26px 28px; box-shadow: 0 2px 12px rgba(0,0,0,0.07); } .sectionTitle { font-size: 1.05rem; font-weight: 700; color: #0f3460; margin-bottom: 18px; } '@ Write-File "global.css" @' *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } body { font-family: 'Segoe UI', system-ui, -apple-system, sans-serif; color: #1a1a2e; -webkit-font-smoothing: antialiased; } a { color: inherit; text-decoration: none; } button, input { font-family: inherit; } '@ |