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
|
||||
|
||||
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