feat: scaffold frontend (Vite + React + Vitest)
- Añade scaffold de frontend con Vite y React - Configura Vitest y tests básicos (App, Navbar) - Añade QueryClient y hooks/plantillas iniciales
This commit is contained in:
36
frontend/index.html
Normal file
36
frontend/index.html
Normal file
@@ -0,0 +1,36 @@
|
||||
<!doctype html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Quasar</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Quasar</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Quasar</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
34
frontend/package.json
Normal file
34
frontend/package.json
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"name": "quasar-frontend",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"packageManager": "yarn@4.12.0",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"test": "vitest",
|
||||
"test:run": "vitest run",
|
||||
"lint": "echo \"No lint configured\"",
|
||||
"format": "prettier --write ."
|
||||
},
|
||||
"dependencies": {
|
||||
"@tanstack/react-query": "^4.34.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@testing-library/jest-dom": "^6.0.0",
|
||||
"@testing-library/react": "^14.0.0",
|
||||
"@types/react": "^18.2.21",
|
||||
"@types/react-dom": "^18.2.7",
|
||||
"@vitejs/plugin-react": "^4.0.0",
|
||||
"autoprefixer": "^10.4.14",
|
||||
"jsdom": "^22.1.0",
|
||||
"postcss": "^8.4.24",
|
||||
"tailwindcss": "^3.4.7",
|
||||
"typescript": "^5.2.2",
|
||||
"vite": "^5.1.0",
|
||||
"vitest": "^0.34.1"
|
||||
}
|
||||
}
|
||||
18
frontend/postcss.config.cjs
Normal file
18
frontend/postcss.config.cjs
Normal file
@@ -0,0 +1,18 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
};
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
};
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
};
|
||||
13
frontend/src/App.tsx
Normal file
13
frontend/src/App.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import React from 'react';
|
||||
import Navbar from './components/layout/Navbar';
|
||||
|
||||
export default function App(): JSX.Element {
|
||||
return (
|
||||
<div>
|
||||
<Navbar />
|
||||
<main>
|
||||
<h1>Quasar</h1>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
12
frontend/src/components/layout/Navbar.tsx
Normal file
12
frontend/src/components/layout/Navbar.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
import React from 'react';
|
||||
|
||||
export default function Navbar(): JSX.Element {
|
||||
return (
|
||||
<nav style={{ padding: 12 }}>
|
||||
<a href="/roms" style={{ marginRight: 12 }}>
|
||||
ROMs
|
||||
</a>
|
||||
<a href="/games">Games</a>
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
9
frontend/src/components/layout/Sidebar.tsx
Normal file
9
frontend/src/components/layout/Sidebar.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import React from 'react';
|
||||
|
||||
export default function Sidebar(): JSX.Element {
|
||||
return (
|
||||
<aside style={{ padding: 12 }}>
|
||||
<div>Sidebar (placeholder)</div>
|
||||
</aside>
|
||||
);
|
||||
}
|
||||
4
frontend/src/hooks/useGames.ts
Normal file
4
frontend/src/hooks/useGames.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export function useGames() {
|
||||
// placeholder stub for tests and future implementation
|
||||
return { data: [], isLoading: false };
|
||||
}
|
||||
3
frontend/src/lib/api.ts
Normal file
3
frontend/src/lib/api.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export const api = {
|
||||
// placeholder for future HTTP client
|
||||
};
|
||||
3
frontend/src/lib/queryClient.ts
Normal file
3
frontend/src/lib/queryClient.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import { QueryClient } from '@tanstack/react-query';
|
||||
|
||||
export const queryClient = new QueryClient();
|
||||
32
frontend/src/main.tsx
Normal file
32
frontend/src/main.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
import React from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import { QueryClientProvider } from '@tanstack/react-query';
|
||||
import { queryClient } from './lib/queryClient';
|
||||
import App from './App';
|
||||
import './styles.css';
|
||||
|
||||
const rootEl = document.getElementById('root');
|
||||
|
||||
if (rootEl) {
|
||||
createRoot(rootEl).render(
|
||||
<React.StrictMode>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<App />
|
||||
</QueryClientProvider>
|
||||
</React.StrictMode>
|
||||
);
|
||||
}
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import { QueryClientProvider } from '@tanstack/react-query';
|
||||
import { queryClient } from './lib/queryClient';
|
||||
import App from './App';
|
||||
import './index.css';
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||
<React.StrictMode>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<App />
|
||||
</QueryClientProvider>
|
||||
</React.StrictMode>
|
||||
);
|
||||
9
frontend/src/routes/games.tsx
Normal file
9
frontend/src/routes/games.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import React from 'react';
|
||||
|
||||
export default function Games(): JSX.Element {
|
||||
return (
|
||||
<div>
|
||||
<h2>Games</h2>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
9
frontend/src/routes/index.tsx
Normal file
9
frontend/src/routes/index.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import React from 'react';
|
||||
|
||||
export default function Home(): JSX.Element {
|
||||
return (
|
||||
<div>
|
||||
<h2>Home</h2>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
9
frontend/src/routes/roms.tsx
Normal file
9
frontend/src/routes/roms.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import React from 'react';
|
||||
|
||||
export default function Roms(): JSX.Element {
|
||||
return (
|
||||
<div>
|
||||
<h2>ROMs</h2>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
2
frontend/src/setupTests.ts
Normal file
2
frontend/src/setupTests.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
import '@testing-library/jest-dom';
|
||||
import '@testing-library/jest-dom';
|
||||
8
frontend/src/styles.css
Normal file
8
frontend/src/styles.css
Normal file
@@ -0,0 +1,8 @@
|
||||
/* Minimal global styles */
|
||||
html, body, #root {
|
||||
height: 100%;
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial;
|
||||
}
|
||||
21
frontend/tailwind.config.cjs
Normal file
21
frontend/tailwind.config.cjs
Normal file
@@ -0,0 +1,21 @@
|
||||
module.exports = {
|
||||
content: ['./index.html', './src/**/*.{ts,tsx}'],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
};
|
||||
module.exports = {
|
||||
content: ['./index.html', './src/**/*.{ts,tsx}'],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
};
|
||||
module.exports = {
|
||||
content: ['./index.html', './src/**/*.{ts,tsx,js,jsx}'],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
};
|
||||
19
frontend/tests/App.spec.tsx
Normal file
19
frontend/tests/App.spec.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import React from 'react';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import App from '../src/App';
|
||||
|
||||
describe('App', () => {
|
||||
it('renderiza el título Quasar', () => {
|
||||
render(<App />);
|
||||
expect(screen.getByText('Quasar')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import App from '../src/App';
|
||||
|
||||
describe('App', () => {
|
||||
it('renders Quasar', () => {
|
||||
render(<App />);
|
||||
expect(screen.getByText(/Quasar/i)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
21
frontend/tests/components/Navbar.spec.tsx
Normal file
21
frontend/tests/components/Navbar.spec.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
import React from 'react';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import Navbar from '../../src/components/layout/Navbar';
|
||||
|
||||
describe('Navbar', () => {
|
||||
it('muestra enlaces ROMs y Games', () => {
|
||||
render(<Navbar />);
|
||||
expect(screen.getByText('ROMs')).toBeInTheDocument();
|
||||
expect(screen.getByText('Games')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import Navbar from '../../src/components/layout/Navbar';
|
||||
|
||||
describe('Navbar', () => {
|
||||
it('renders ROMs and Games links', () => {
|
||||
render(<Navbar />);
|
||||
expect(screen.getByText(/ROMs/)).toBeInTheDocument();
|
||||
expect(screen.getByText(/Games/)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
19
frontend/tsconfig.json
Normal file
19
frontend/tsconfig.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"useDefineForClassFields": true,
|
||||
"lib": ["DOM", "DOM.Iterable", "ESNext"],
|
||||
"jsx": "react-jsx",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Bundler",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"types": ["vite/client", "vitest/globals"]
|
||||
},
|
||||
"include": ["src", "tests"]
|
||||
}
|
||||
28
frontend/vite.config.ts
Normal file
28
frontend/vite.config.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
server: {
|
||||
port: 5173,
|
||||
},
|
||||
});
|
||||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
test: {
|
||||
environment: 'jsdom',
|
||||
globals: true,
|
||||
setupFiles: ['./src/setupTests.ts'],
|
||||
include: ['tests/**/*.spec.tsx'],
|
||||
},
|
||||
});
|
||||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
server: { port: 5173 },
|
||||
});
|
||||
10
frontend/vitest.config.ts
Normal file
10
frontend/vitest.config.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { defineConfig } from 'vitest/config';
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
environment: 'jsdom',
|
||||
globals: true,
|
||||
setupFiles: './src/setupTests.ts',
|
||||
include: ['tests/**/*.spec.tsx'],
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user