feat: scaffold Next.js frontend with Tailwind, TanStack Query, RHF+Zod, Supabase client, Recharts

This commit is contained in:
Claude 2026-07-20 22:43:14 +02:00
parent bf127a9c6f
commit df897d4c15
17 changed files with 209 additions and 0 deletions

3
.env.example Normal file
View file

@ -0,0 +1,3 @@
NEXT_PUBLIC_SUPABASE_URL=http://supabasekong-to6mg3yxqit4ac9nl0otnvav.152.53.112.35.sslip.io:8000
NEXT_PUBLIC_SUPABASE_ANON_KEY=
NEXT_PUBLIC_API_URL=http://ceo-api:3000

4
.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
node_modules/
.next/
.env
*.log

15
Dockerfile Normal file
View file

@ -0,0 +1,15 @@
FROM node:22-alpine AS build
WORKDIR /app
COPY package.json ./
RUN npm install
COPY . .
RUN npm run build
FROM node:22-alpine
WORKDIR /app
ENV NODE_ENV=production
COPY --from=build /app/public ./public
COPY --from=build /app/.next/standalone ./
COPY --from=build /app/.next/static ./.next/static
EXPOSE 3000
CMD ["node", "server.js"]

View file

@ -1,2 +1,17 @@
# ceo-web
Frontend Next.js pentru CEO-OS.
## Stack
Next.js (App Router) + TypeScript, Tailwind CSS, TanStack Query, React Hook Form + Zod, Supabase JS, date-fns, Recharts.
## Dezvoltare
```bash
npm install
cp .env.example .env
npm run dev
```
`GET /api/health` returneaza statusul serviciului.

6
next.config.js Normal file
View file

@ -0,0 +1,6 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
output: 'standalone',
};
module.exports = nextConfig;

34
package.json Normal file
View file

@ -0,0 +1,34 @@
{
"name": "ceo-web",
"version": "0.1.0",
"description": "CEO-OS web frontend (Next.js)",
"private": true,
"license": "UNLICENSED",
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"@hookform/resolvers": "^3.9.1",
"@supabase/supabase-js": "^2.47.0",
"@tanstack/react-query": "^5.62.0",
"date-fns": "^4.1.0",
"next": "^15.1.0",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-hook-form": "^7.54.0",
"recharts": "^2.13.3",
"zod": "^3.23.8"
},
"devDependencies": {
"@types/node": "^22.0.0",
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"autoprefixer": "^10.4.20",
"postcss": "^8.4.49",
"tailwindcss": "^3.4.15",
"typescript": "^5.6.0"
}
}

6
postcss.config.js Normal file
View file

@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};

View file

@ -0,0 +1,5 @@
import { NextResponse } from 'next/server';
export function GET() {
return NextResponse.json({ status: 'ok', service: 'ceo-web' });
}

3
src/app/globals.css Normal file
View file

@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

18
src/app/layout.tsx Normal file
View file

@ -0,0 +1,18 @@
import type { Metadata } from 'next';
import { Providers } from './providers';
import './globals.css';
export const metadata: Metadata = {
title: 'CEO OS',
description: 'Sistem complex pentru antreprenori',
};
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="ro">
<body>
<Providers>{children}</Providers>
</body>
</html>
);
}

10
src/app/page.tsx Normal file
View file

@ -0,0 +1,10 @@
export default function HomePage() {
return (
<main className="max-w-3xl mx-auto p-8">
<h1 className="text-3xl font-bold">CEO OS</h1>
<p className="text-gray-500 mt-2">
Scaffold initial conectat la ceo-api si Supabase, stilizat cu Tailwind.
</p>
</main>
);
}

9
src/app/providers.tsx Normal file
View file

@ -0,0 +1,9 @@
'use client';
import { useState } from 'react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
export function Providers({ children }: { children: React.ReactNode }) {
const [queryClient] = useState(() => new QueryClient());
return <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>;
}

View file

@ -0,0 +1,18 @@
'use client';
import { LineChart, Line, XAxis, YAxis, Tooltip, ResponsiveContainer } from 'recharts';
type Point = { label: string; value: number };
export function ExampleChart({ data }: { data: Point[] }) {
return (
<ResponsiveContainer width="100%" height={240}>
<LineChart data={data}>
<XAxis dataKey="label" />
<YAxis />
<Tooltip />
<Line type="monotone" dataKey="value" stroke="#000" strokeWidth={2} />
</LineChart>
</ResponsiveContainer>
);
}

View file

@ -0,0 +1,29 @@
'use client';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
const schema = z.object({
name: z.string().min(2, 'Numele trebuie sa aiba minim 2 caractere'),
});
type FormValues = z.infer<typeof schema>;
export function ExampleForm({ onSubmit }: { onSubmit: (values: FormValues) => void }) {
const {
register,
handleSubmit,
formState: { errors },
} = useForm<FormValues>({ resolver: zodResolver(schema) });
return (
<form onSubmit={handleSubmit(onSubmit)} className="flex flex-col gap-2">
<input {...register('name')} className="border rounded px-3 py-2" placeholder="Nume organizatie" />
{errors.name && <span className="text-red-500 text-sm">{errors.name.message}</span>}
<button type="submit" className="bg-black text-white rounded px-3 py-2">
Salveaza
</button>
</form>
);
}

6
src/lib/supabase.ts Normal file
View file

@ -0,0 +1,6 @@
import { createClient } from '@supabase/supabase-js';
export const supabase = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL ?? '',
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY ?? '',
);

7
tailwind.config.ts Normal file
View file

@ -0,0 +1,7 @@
import type { Config } from 'tailwindcss';
export default {
content: ['./src/**/*.{ts,tsx}'],
theme: { extend: {} },
plugins: [],
} satisfies Config;

21
tsconfig.json Normal file
View file

@ -0,0 +1,21 @@
{
"compilerOptions": {
"target": "ES2022",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [{ "name": "next" }],
"paths": { "@/*": ["./src/*"] }
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}