From df897d4c159ee043844bbe77e8896ba021f34178 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 20 Jul 2026 22:43:14 +0200 Subject: [PATCH] feat: scaffold Next.js frontend with Tailwind, TanStack Query, RHF+Zod, Supabase client, Recharts --- .env.example | 3 +++ .gitignore | 4 ++++ Dockerfile | 15 ++++++++++++++ README.md | 15 ++++++++++++++ next.config.js | 6 ++++++ package.json | 34 ++++++++++++++++++++++++++++++++ postcss.config.js | 6 ++++++ src/app/api/health/route.ts | 5 +++++ src/app/globals.css | 3 +++ src/app/layout.tsx | 18 +++++++++++++++++ src/app/page.tsx | 10 ++++++++++ src/app/providers.tsx | 9 +++++++++ src/components/example-chart.tsx | 18 +++++++++++++++++ src/components/example-form.tsx | 29 +++++++++++++++++++++++++++ src/lib/supabase.ts | 6 ++++++ tailwind.config.ts | 7 +++++++ tsconfig.json | 21 ++++++++++++++++++++ 17 files changed, 209 insertions(+) create mode 100644 .env.example create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 next.config.js create mode 100644 package.json create mode 100644 postcss.config.js create mode 100644 src/app/api/health/route.ts create mode 100644 src/app/globals.css create mode 100644 src/app/layout.tsx create mode 100644 src/app/page.tsx create mode 100644 src/app/providers.tsx create mode 100644 src/components/example-chart.tsx create mode 100644 src/components/example-form.tsx create mode 100644 src/lib/supabase.ts create mode 100644 tailwind.config.ts create mode 100644 tsconfig.json diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..3764ddb --- /dev/null +++ b/.env.example @@ -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 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b7cfe70 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +node_modules/ +.next/ +.env +*.log diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..701a321 --- /dev/null +++ b/Dockerfile @@ -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"] diff --git a/README.md b/README.md index 1c88d6f..ee1c27d 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/next.config.js b/next.config.js new file mode 100644 index 0000000..b408897 --- /dev/null +++ b/next.config.js @@ -0,0 +1,6 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = { + output: 'standalone', +}; + +module.exports = nextConfig; diff --git a/package.json b/package.json new file mode 100644 index 0000000..642c049 --- /dev/null +++ b/package.json @@ -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" + } +} diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 0000000..12a703d --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/src/app/api/health/route.ts b/src/app/api/health/route.ts new file mode 100644 index 0000000..a34781a --- /dev/null +++ b/src/app/api/health/route.ts @@ -0,0 +1,5 @@ +import { NextResponse } from 'next/server'; + +export function GET() { + return NextResponse.json({ status: 'ok', service: 'ceo-web' }); +} diff --git a/src/app/globals.css b/src/app/globals.css new file mode 100644 index 0000000..b5c61c9 --- /dev/null +++ b/src/app/globals.css @@ -0,0 +1,3 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; diff --git a/src/app/layout.tsx b/src/app/layout.tsx new file mode 100644 index 0000000..7753352 --- /dev/null +++ b/src/app/layout.tsx @@ -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 ( + + + {children} + + + ); +} diff --git a/src/app/page.tsx b/src/app/page.tsx new file mode 100644 index 0000000..0ff7219 --- /dev/null +++ b/src/app/page.tsx @@ -0,0 +1,10 @@ +export default function HomePage() { + return ( +
+

CEO OS

+

+ Scaffold initial — conectat la ceo-api si Supabase, stilizat cu Tailwind. +

+
+ ); +} diff --git a/src/app/providers.tsx b/src/app/providers.tsx new file mode 100644 index 0000000..a2e609d --- /dev/null +++ b/src/app/providers.tsx @@ -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 {children}; +} diff --git a/src/components/example-chart.tsx b/src/components/example-chart.tsx new file mode 100644 index 0000000..8a7b67d --- /dev/null +++ b/src/components/example-chart.tsx @@ -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 ( + + + + + + + + + ); +} diff --git a/src/components/example-form.tsx b/src/components/example-form.tsx new file mode 100644 index 0000000..48921c2 --- /dev/null +++ b/src/components/example-form.tsx @@ -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; + +export function ExampleForm({ onSubmit }: { onSubmit: (values: FormValues) => void }) { + const { + register, + handleSubmit, + formState: { errors }, + } = useForm({ resolver: zodResolver(schema) }); + + return ( +
+ + {errors.name && {errors.name.message}} + +
+ ); +} diff --git a/src/lib/supabase.ts b/src/lib/supabase.ts new file mode 100644 index 0000000..a47f4d2 --- /dev/null +++ b/src/lib/supabase.ts @@ -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 ?? '', +); diff --git a/tailwind.config.ts b/tailwind.config.ts new file mode 100644 index 0000000..a553a08 --- /dev/null +++ b/tailwind.config.ts @@ -0,0 +1,7 @@ +import type { Config } from 'tailwindcss'; + +export default { + content: ['./src/**/*.{ts,tsx}'], + theme: { extend: {} }, + plugins: [], +} satisfies Config; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..334fafd --- /dev/null +++ b/tsconfig.json @@ -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"] +}