feat: scaffold Next.js frontend with Tailwind, TanStack Query, RHF+Zod, Supabase client, Recharts
This commit is contained in:
parent
bf127a9c6f
commit
df897d4c15
17 changed files with 209 additions and 0 deletions
3
.env.example
Normal file
3
.env.example
Normal 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
4
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
node_modules/
|
||||||
|
.next/
|
||||||
|
.env
|
||||||
|
*.log
|
||||||
15
Dockerfile
Normal file
15
Dockerfile
Normal 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"]
|
||||||
15
README.md
15
README.md
|
|
@ -1,2 +1,17 @@
|
||||||
# ceo-web
|
# 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
6
next.config.js
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
/** @type {import('next').NextConfig} */
|
||||||
|
const nextConfig = {
|
||||||
|
output: 'standalone',
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = nextConfig;
|
||||||
34
package.json
Normal file
34
package.json
Normal 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
6
postcss.config.js
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
module.exports = {
|
||||||
|
plugins: {
|
||||||
|
tailwindcss: {},
|
||||||
|
autoprefixer: {},
|
||||||
|
},
|
||||||
|
};
|
||||||
5
src/app/api/health/route.ts
Normal file
5
src/app/api/health/route.ts
Normal 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
3
src/app/globals.css
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
18
src/app/layout.tsx
Normal file
18
src/app/layout.tsx
Normal 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
10
src/app/page.tsx
Normal 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
9
src/app/providers.tsx
Normal 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>;
|
||||||
|
}
|
||||||
18
src/components/example-chart.tsx
Normal file
18
src/components/example-chart.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
29
src/components/example-form.tsx
Normal file
29
src/components/example-form.tsx
Normal 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
6
src/lib/supabase.ts
Normal 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
7
tailwind.config.ts
Normal 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
21
tsconfig.json
Normal 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"]
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue