@leonardofirme/deploy-nextjs16 1.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +99 -0
- package/bin/cli.js +36 -0
- package/eslint.config.mjs +18 -0
- package/next.config.ts +8 -0
- package/package.json +40 -0
- package/postcss.config.mjs +7 -0
- package/public/favicon.png +0 -0
- package/src/app/favicon.ico +0 -0
- package/src/app/globals.css +79 -0
- package/src/app/layout.tsx +39 -0
- package/src/app/page.tsx +96 -0
- package/src/components/ui/Alert.tsx +35 -0
- package/src/components/ui/Badge.tsx +25 -0
- package/src/components/ui/Breadcrumb.tsx +24 -0
- package/src/components/ui/Button.tsx +33 -0
- package/src/components/ui/Card.tsx +43 -0
- package/src/components/ui/Checkbox.tsx +34 -0
- package/src/components/ui/Dropdown.tsx +72 -0
- package/src/components/ui/FireworksBackground.tsx +202 -0
- package/src/components/ui/Index.tsx +56 -0
- package/src/components/ui/Input.tsx +34 -0
- package/src/components/ui/Modal.tsx +65 -0
- package/src/components/ui/Progress.tsx +27 -0
- package/src/components/ui/Provider.tsx +16 -0
- package/src/components/ui/Select.tsx +41 -0
- package/src/components/ui/Skeleton.tsx +10 -0
- package/src/components/ui/StarfieldBackground.tsx +161 -0
- package/src/components/ui/Table.tsx +53 -0
- package/src/components/ui/Textarea.tsx +34 -0
- package/src/components/ui/Toaster.tsx +24 -0
- package/src/components/ui/Toggle.tsx +36 -0
- package/src/components/ui/ToggleDarkmode.tsx +74 -0
- package/src/core/animations.ts +38 -0
- package/src/core/config.ts +21 -0
- package/src/core/constants.ts +38 -0
- package/src/core/legal.ts +11 -0
- package/src/core/providers/node-resolver.tsx +21 -0
- package/src/hooks/use-theme.tsx +27 -0
- package/src/layouts/default-layout.tsx +28 -0
- package/src/proxy.ts +26 -0
- package/src/types/common.ts +10 -0
- package/src/types/index.ts +22 -0
- package/src/utils/cn.ts +10 -0
- package/tsconfig.json +34 -0
package/README.md
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# Deploy NextJS 16+ | Leonardo Firme
|
|
2
|
+
|
|
3
|
+
Estrutura profissional (Scaffold) para desenvolvimento de SaaS e ERPs dinâmicos, utilizando NextJS 16+, React 19 e Tailwind CSS v4.
|
|
4
|
+
|
|
5
|
+

|
|
6
|
+

|
|
7
|
+

|
|
8
|
+

|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## 🚀 Instalação Rápida
|
|
13
|
+
|
|
14
|
+
Para iniciar um novo projeto utilizando este scaffold, execute o comando abaixo no seu terminal:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npx @LeonardoFirme/deploy-nextjs16 meu-novo-projeto
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
> **Nota:** Certifique-se de ter um arquivo `.npmrc` configurado com seu Token do GitHub para acessar os pacotes privados `@LeonardoFirme`.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 📂 Detalhamento da Estrutura
|
|
26
|
+
|
|
27
|
+
### 📁 Raiz (Root) & Configurações
|
|
28
|
+
|
|
29
|
+
* **`bin/lf-init`**: Executável do scaffold para automação da cópia de arquivos e setup inicial.
|
|
30
|
+
* **`package.json`**: Manifesto com dependências (Next 16.1.6, React 19.2.3, Tailwind v4). Define o binário `lf-init`.
|
|
31
|
+
* **`package-lock.json`**: Controle de integridade e versões das dependências.
|
|
32
|
+
* **`postcss.config.mjs`**: Configuração do motor PostCSS para processamento do Tailwind v4.
|
|
33
|
+
* **`tsconfig.json`**: Regras de tipagem estática e aliases de caminhos (paths) do projeto.
|
|
34
|
+
|
|
35
|
+
### 📁 `src/app` (Rotas e Estilo Global)
|
|
36
|
+
|
|
37
|
+
* **`layout.tsx`**: Layout principal da aplicação. Gerencia fontes, metadados e wrappers de contexto.
|
|
38
|
+
* **`page.tsx`**: View principal da rota raiz (Home).
|
|
39
|
+
* **`globals.css`**: Configuração global do Tailwind v4 e variáveis de cores dinâmicas.
|
|
40
|
+
|
|
41
|
+
### 📁 `src/components/ui` (Biblioteca Minimalista)
|
|
42
|
+
|
|
43
|
+
Componentes atômicos com suporte nativo a `dark:`.
|
|
44
|
+
|
|
45
|
+
* **`Alert.tsx`, `Badge.tsx`, `Breadcrumb.tsx**`: Sinalização, status e navegação.
|
|
46
|
+
* **`Button.tsx`**: Ação principal (`bg-gray-800` light / `bg-gray-50` dark).
|
|
47
|
+
* **`Card.tsx`, `Modal.tsx`, `Dropdown.tsx**`: Containers de conteúdo e sobreposição.
|
|
48
|
+
* **`Input.tsx`, `Select.tsx`, `Checkbox.tsx`, `Toggle.tsx`, `Textarea.tsx**`: Elementos de formulário (sem manipulação de `uppercase`).
|
|
49
|
+
* **`Table.tsx`**: Grid de dados avançado para sistemas ERP.
|
|
50
|
+
* **`StarfieldBackground.tsx` & `FireworksBackground.tsx**`: Backgrounds animados profissionais.
|
|
51
|
+
* **`ToggleDarkmode.tsx`**: Controle de alternância de tema.
|
|
52
|
+
* **`Provider.tsx`**: Wrapper centralizador de contextos de interface.
|
|
53
|
+
* **`Index.tsx`**: Ponto central de exportação dos componentes.
|
|
54
|
+
|
|
55
|
+
### 📁 `src/core` (Lógica e Regras)
|
|
56
|
+
|
|
57
|
+
* **`animations.ts`**: Configurações centralizadas de Framer Motion.
|
|
58
|
+
* **`config.ts`**: Variáveis de ambiente e constantes globais do sistema.
|
|
59
|
+
* **`constants.ts`**: Valores estáticos e Enums de negócio.
|
|
60
|
+
* **`legal.ts`**: Textos jurídicos e termos padronizados.
|
|
61
|
+
* **`providers/node-resolver.tsx`**: Gerenciador de hidratação para componentes Client/Server.
|
|
62
|
+
|
|
63
|
+
### 📁 `src/hooks` & `src/layouts`
|
|
64
|
+
|
|
65
|
+
* **`use-theme.tsx`**: Hook para controle lógico do estado Dark/Light.
|
|
66
|
+
* **`default-layout.tsx`**: Template base estrutural para dashboards e plataformas.
|
|
67
|
+
|
|
68
|
+
### 📁 `src/proxy.ts` (Segurança e Roteamento)
|
|
69
|
+
|
|
70
|
+
* **`proxy.ts`**: Camada de interceptação que substitui o middleware, controlando headers e redirecionamentos.
|
|
71
|
+
|
|
72
|
+
### 📁 `src/utils` & `src/types`
|
|
73
|
+
|
|
74
|
+
* **`cn.ts`**: Utilitário para mesclagem condicional de classes Tailwind.
|
|
75
|
+
* **`index.ts` & `common.ts**`: Contratos de interfaces e tipos globais do TypeScript.
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## 🛠️ Comandos de Desenvolvimento
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
# Instalar dependências localmente
|
|
83
|
+
npm install
|
|
84
|
+
|
|
85
|
+
# Iniciar ambiente de desenvolvimento
|
|
86
|
+
npm run dev
|
|
87
|
+
|
|
88
|
+
# Gerar build de produção
|
|
89
|
+
npm run build
|
|
90
|
+
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## 📄 Licença
|
|
96
|
+
|
|
97
|
+
Este projeto é de uso restrito para o ecossistema de plataformas desenvolvidas por [Leonardo Firme](https://github.com/LeonardoFirme).
|
|
98
|
+
|
|
99
|
+
---
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// bin/cli.js
|
|
2
|
+
#!/usr/bin/env node
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @file cli.js
|
|
6
|
+
* @description Script de inicialização de estruturas Leonardo Firme.
|
|
7
|
+
* @author Leonardo Firme
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const { execSync } = require('child_process');
|
|
11
|
+
const path = require('path');
|
|
12
|
+
|
|
13
|
+
function run() {
|
|
14
|
+
// Pegamos o nome do projeto ou definimos o diretório atual
|
|
15
|
+
const projectName = process.argv[2] || '.';
|
|
16
|
+
const repo = "LeonardoFirme/deploy-nextjs16";
|
|
17
|
+
|
|
18
|
+
console.log("\x1b[34m%s\x1b[0m", "--- Estruturas Leonardo Firme: Inicializando ---");
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
console.log(`Clonando estrutura de ${repo}...`);
|
|
22
|
+
// Usando degit para baixar a estrutura sem o histórico do git
|
|
23
|
+
execSync(`npx degit ${repo} ${projectName}`, { stdio: 'inherit' });
|
|
24
|
+
|
|
25
|
+
console.log("\x1b[32m%s\x1b[0m", `\nSucesso! Estrutura instalada.`);
|
|
26
|
+
console.log("\nPróximos passos:");
|
|
27
|
+
if (projectName !== '.') console.log(` cd ${projectName}`);
|
|
28
|
+
console.log(` npm install`);
|
|
29
|
+
console.log(` npm run dev`);
|
|
30
|
+
} catch (error) {
|
|
31
|
+
console.error("Erro ao clonar estrutura. Verifique se o diretório está vazio.");
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
run();
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { defineConfig, globalIgnores } from "eslint/config";
|
|
2
|
+
import nextVitals from "eslint-config-next/core-web-vitals";
|
|
3
|
+
import nextTs from "eslint-config-next/typescript";
|
|
4
|
+
|
|
5
|
+
const eslintConfig = defineConfig([
|
|
6
|
+
...nextVitals,
|
|
7
|
+
...nextTs,
|
|
8
|
+
// Override default ignores of eslint-config-next.
|
|
9
|
+
globalIgnores([
|
|
10
|
+
// Default ignores of eslint-config-next:
|
|
11
|
+
".next/**",
|
|
12
|
+
"out/**",
|
|
13
|
+
"build/**",
|
|
14
|
+
"next-env.d.ts",
|
|
15
|
+
]),
|
|
16
|
+
]);
|
|
17
|
+
|
|
18
|
+
export default eslintConfig;
|
package/next.config.ts
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@leonardofirme/deploy-nextjs16",
|
|
3
|
+
"version": "1.1.4",
|
|
4
|
+
"private": false,
|
|
5
|
+
"description": "Estruturas profissionais de ERP e SaaS - Leonardo Firme",
|
|
6
|
+
"bin": {
|
|
7
|
+
"lf-init": "bin/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"main": "src/app/page.tsx",
|
|
10
|
+
"publishConfig": {
|
|
11
|
+
"access": "public"
|
|
12
|
+
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"dev": "next dev --turbo",
|
|
15
|
+
"build": "next build",
|
|
16
|
+
"start": "next start",
|
|
17
|
+
"lint": "next lint"
|
|
18
|
+
},
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"@leonardofirme/package-npm": "^1.1.3",
|
|
21
|
+
"clsx": "^2.1.1",
|
|
22
|
+
"framer-motion": "^12.4.7",
|
|
23
|
+
"lucide-react": "^0.575.0",
|
|
24
|
+
"next": "16.1.6",
|
|
25
|
+
"react": "19.2.3",
|
|
26
|
+
"react-dom": "19.2.3",
|
|
27
|
+
"tailwind-merge": "^3.5.0"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@tailwindcss/postcss": "^4",
|
|
31
|
+
"@types/node": "^20",
|
|
32
|
+
"@types/react": "^19",
|
|
33
|
+
"@types/react-dom": "^19",
|
|
34
|
+
"babel-plugin-react-compiler": "1.0.0",
|
|
35
|
+
"eslint": "^9",
|
|
36
|
+
"eslint-config-next": "16.1.6",
|
|
37
|
+
"tailwindcss": "^4",
|
|
38
|
+
"typescript": "^5"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/* src/app/globals.css */
|
|
2
|
+
/**
|
|
3
|
+
* Estilização Global - Ecossistema Leonardo Firme
|
|
4
|
+
* Configuração de fontes (Instrument Sans & Orbitron), escala de cores v0 (Laravel Base)
|
|
5
|
+
* e scrollbar customizada profissional para NextJS 16+ e Tailwind v4.
|
|
6
|
+
*/
|
|
7
|
+
@import url('https://fonts.googleapis.com/css2?family=Instrument+Sans:ital,wght@0,400..700;1,400..700&display=swap');
|
|
8
|
+
@import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400..900&display=swap');
|
|
9
|
+
@import "tailwindcss";
|
|
10
|
+
|
|
11
|
+
@custom-variant dark (&:where(.dark, .dark *));
|
|
12
|
+
|
|
13
|
+
@theme {
|
|
14
|
+
/* definindo instrument sans como a fonte principal do projeto */
|
|
15
|
+
--font-sans: "Instrument Sans", ui-sans-serif, system-ui, sans-serif;
|
|
16
|
+
--font-orbitron: "Orbitron", sans-serif;
|
|
17
|
+
|
|
18
|
+
/* escala v0 digital corrigida - baseada na cor oficial #ff2d20 */
|
|
19
|
+
--color-v0-50: #fff1f0;
|
|
20
|
+
--color-v0-100: #ffdfe1;
|
|
21
|
+
--color-v0-200: #ffc2bd;
|
|
22
|
+
--color-v0-300: #ff9187;
|
|
23
|
+
--color-v0-400: #ff5445;
|
|
24
|
+
--color-v0-500: #f83525;
|
|
25
|
+
--color-v0-600: #ff2d20; /* cor base oficial laravel */
|
|
26
|
+
--color-v0-700: #d61f14;
|
|
27
|
+
--color-v0-800: #b11d15;
|
|
28
|
+
--color-v0-900: #931d18;
|
|
29
|
+
--color-v0-950: #500a08;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
@layer base {
|
|
33
|
+
body {
|
|
34
|
+
@apply bg-white text-gray-400 antialiased font-sans transition-colors duration-300 dark:bg-gray-950 dark:text-gray-300;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/* ajuste fino para headlines utilizando a mesma fonte */
|
|
38
|
+
h1, h2, h3, h4, h5, h6 {
|
|
39
|
+
@apply font-sans font-bold tracking-tighter;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
a {
|
|
43
|
+
@apply font-sans;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/* --- scrollbar customizada profissional --- */
|
|
47
|
+
::-webkit-scrollbar {
|
|
48
|
+
@apply w-2 h-2;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
::-webkit-scrollbar-track {
|
|
52
|
+
@apply bg-transparent;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
::-webkit-scrollbar-thumb {
|
|
56
|
+
@apply bg-v0-300 rounded-full border-2 border-solid border-white transition-colors;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
::-webkit-scrollbar-thumb:hover {
|
|
60
|
+
@apply bg-v0-600;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.dark ::-webkit-scrollbar-thumb {
|
|
64
|
+
@apply bg-gray-800 border-gray-950;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.dark ::-webkit-scrollbar-thumb:hover {
|
|
68
|
+
@apply bg-v0-600;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
* {
|
|
72
|
+
scrollbar-width: thin;
|
|
73
|
+
scrollbar-color: #ff2d20 transparent;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.dark * {
|
|
77
|
+
scrollbar-color: #ff2d20 transparent;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// src/app/layout.tsx
|
|
2
|
+
/**
|
|
3
|
+
* @file layout.tsx
|
|
4
|
+
* @description Root Layout Enterprise.
|
|
5
|
+
* @author Leonardo Firme
|
|
6
|
+
*/
|
|
7
|
+
import type { Metadata, Viewport } from "next";
|
|
8
|
+
import { NodeResolver } from "@/core/providers/node-resolver";
|
|
9
|
+
import { LEGAL } from "@/core/legal";
|
|
10
|
+
import "./globals.css";
|
|
11
|
+
|
|
12
|
+
export const metadata: Metadata = {
|
|
13
|
+
title: { default: "Enterprise Scaffold", template: `%s | ${LEGAL.owner}` },
|
|
14
|
+
description: LEGAL.notice,
|
|
15
|
+
other: { "creator": "Leonardo Firme" }
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const viewport: Viewport = {
|
|
19
|
+
themeColor: "#000000",
|
|
20
|
+
width: "device-width",
|
|
21
|
+
initialScale: 1,
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
25
|
+
return (
|
|
26
|
+
<html lang="pt-br" className="scroll-smooth dark" suppressHydrationWarning>
|
|
27
|
+
<body className="antialiased selection:bg-gray-800 selection:text-white dark:selection:bg-gray-50 dark:selection:text-gray-950" suppressHydrationWarning>
|
|
28
|
+
<NodeResolver>
|
|
29
|
+
{children}
|
|
30
|
+
</NodeResolver>
|
|
31
|
+
|
|
32
|
+
{/* Assinatura Técnica Oculta */}
|
|
33
|
+
<div style={{ display: 'none' }} aria-hidden="true">
|
|
34
|
+
System Auth: Leonardo Firme - ID: 2026-ENT
|
|
35
|
+
</div>
|
|
36
|
+
</body>
|
|
37
|
+
</html>
|
|
38
|
+
);
|
|
39
|
+
}
|
package/src/app/page.tsx
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// src/app/page.tsx
|
|
2
|
+
/**
|
|
3
|
+
* @file page.tsx
|
|
4
|
+
* @description Landing Page principal com animações e o novo efeito StarfieldBackground.
|
|
5
|
+
* @author Leonardo Firme
|
|
6
|
+
*/
|
|
7
|
+
"use client";
|
|
8
|
+
|
|
9
|
+
import React from 'react';
|
|
10
|
+
import { motion } from 'framer-motion';
|
|
11
|
+
import { DefaultLayout } from '@/layouts/default-layout';
|
|
12
|
+
import { Button, Badge } from '@/components/ui/Index';
|
|
13
|
+
import { ITEM_VARIANTS, CONTAINER_VARIANTS } from '@/core/animations';
|
|
14
|
+
import { StarfieldBackground } from '@/components/ui/StarfieldBackground';
|
|
15
|
+
|
|
16
|
+
export default function HomePage() {
|
|
17
|
+
return (
|
|
18
|
+
<DefaultLayout>
|
|
19
|
+
<StarfieldBackground>
|
|
20
|
+
<main className="max-w-7xl mx-auto relative flex min-h-screen flex-col items-center justify-center overflow-hidden bg-transparent p-6 transition-colors duration-300">
|
|
21
|
+
|
|
22
|
+
<motion.div
|
|
23
|
+
variants={CONTAINER_VARIANTS}
|
|
24
|
+
initial="hidden"
|
|
25
|
+
animate="visible"
|
|
26
|
+
className="z-10 flex w-full max-w-4xl flex-col items-center text-center space-y-12"
|
|
27
|
+
>
|
|
28
|
+
{/* Badge de Versão */}
|
|
29
|
+
<motion.div variants={ITEM_VARIANTS}>
|
|
30
|
+
<Badge className="bg-v0-50 px-5 py-1.5 font-orbitron tracking-[0.2em] text-v0-600 border-v0-200 dark:bg-v0-950/30 dark:border-v0-900">
|
|
31
|
+
ESTRUTURA V1.1.3 VALIDADA
|
|
32
|
+
</Badge>
|
|
33
|
+
</motion.div>
|
|
34
|
+
|
|
35
|
+
{/* Headline Principal */}
|
|
36
|
+
<motion.div variants={ITEM_VARIANTS} className="space-y-6">
|
|
37
|
+
<h1 className="font-orbitron text-6xl font-bold uppercase leading-[0.9] tracking-tight text-gray-800 md:text-8xl dark:text-gray-50">
|
|
38
|
+
FALA <span className="text-v0-600">DEV!</span>
|
|
39
|
+
</h1>
|
|
40
|
+
<p className="mx-auto max-w-2xl font-sans text-lg leading-relaxed text-gray-500 md:text-xl dark:text-gray-100">
|
|
41
|
+
Sua infraestrutura está pronta para escala. Desenvolva com a precisão e a suavidade do
|
|
42
|
+
<span className="font-bold text-gray-800 dark:text-gray-50"> Ecossistema Leonardo Firme</span>.
|
|
43
|
+
</p>
|
|
44
|
+
</motion.div>
|
|
45
|
+
|
|
46
|
+
{/* Botão com as cores exatas do seu padrão */}
|
|
47
|
+
<motion.div variants={ITEM_VARIANTS} className="flex flex-col items-center gap-6 pt-6">
|
|
48
|
+
<motion.div
|
|
49
|
+
whileHover={{ scale: 1.05 }}
|
|
50
|
+
whileTap={{ scale: 0.98 }}
|
|
51
|
+
transition={{ type: "spring", stiffness: 400, damping: 10 }}
|
|
52
|
+
>
|
|
53
|
+
<Button
|
|
54
|
+
className="rounded-full px-14 py-5 font-orbitron text-xs uppercase tracking-[0.3em] shadow-2xl transition-all bg-gray-800 text-white hover:bg-gray-950 dark:bg-gray-50 dark:text-v0-500 dark:hover:bg-gray-200"
|
|
55
|
+
>
|
|
56
|
+
Começar Desenvolvimento
|
|
57
|
+
</Button>
|
|
58
|
+
</motion.div>
|
|
59
|
+
|
|
60
|
+
<motion.span
|
|
61
|
+
animate={{ opacity: [0.3, 0.7, 0.3] }}
|
|
62
|
+
transition={{ repeat: Infinity, duration: 3, ease: "easeInOut" }}
|
|
63
|
+
className="font-orbitron text-[10px] uppercase tracking-[0.5em] text-gray-400"
|
|
64
|
+
>
|
|
65
|
+
System Online
|
|
66
|
+
</motion.span>
|
|
67
|
+
</motion.div>
|
|
68
|
+
|
|
69
|
+
{/* Footer Signature */}
|
|
70
|
+
<motion.div
|
|
71
|
+
variants={ITEM_VARIANTS}
|
|
72
|
+
className="flex items-center space-x-8 pt-20"
|
|
73
|
+
>
|
|
74
|
+
<div className="h-px w-16 bg-gray-200 dark:bg-gray-800" />
|
|
75
|
+
<div className="flex flex-col space-y-1">
|
|
76
|
+
<span className="font-orbitron text-[10px] uppercase tracking-[0.6em] text-gray-400">
|
|
77
|
+
Leonardo Firme
|
|
78
|
+
</span>
|
|
79
|
+
<span className="text-center font-sans text-[9px] font-bold uppercase tracking-[0.2em] text-v0-600 opacity-80">
|
|
80
|
+
FRAMEWORK V1 • 2026
|
|
81
|
+
</span>
|
|
82
|
+
</div>
|
|
83
|
+
<div className="h-px w-16 bg-gray-200 dark:bg-gray-800" />
|
|
84
|
+
</motion.div>
|
|
85
|
+
</motion.div>
|
|
86
|
+
|
|
87
|
+
{/* Efeito Visual de Background */}
|
|
88
|
+
<div className="pointer-events-none absolute inset-0 z-0">
|
|
89
|
+
<div className="absolute top-1/2 left-1/2 h-200 w-200 -translate-x-1/2 -translate-y-1/2 rounded-full bg-v0-600/5 blur-[120px] dark:bg-v0-600/10" />
|
|
90
|
+
</div>
|
|
91
|
+
|
|
92
|
+
</main>
|
|
93
|
+
</StarfieldBackground>
|
|
94
|
+
</DefaultLayout>
|
|
95
|
+
);
|
|
96
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// src/components/ui/Alert.tsx
|
|
2
|
+
/**
|
|
3
|
+
* @file Alert.tsx
|
|
4
|
+
* @description Componente de feedback visual para mensagens de status.
|
|
5
|
+
* Segue o padrão minimalista com suporte a variantes de informação e perigo.
|
|
6
|
+
*/
|
|
7
|
+
import React from 'react';
|
|
8
|
+
import { cn } from '@/utils/cn';
|
|
9
|
+
|
|
10
|
+
interface AlertProps {
|
|
11
|
+
title?: string;
|
|
12
|
+
children: React.ReactNode;
|
|
13
|
+
variant?: 'info' | 'danger';
|
|
14
|
+
className?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const Alert = ({ title, children, variant = 'info', className }: AlertProps): React.JSX.Element => {
|
|
18
|
+
const variants = {
|
|
19
|
+
info: "border-gray-200 bg-white text-gray-400 dark:border-gray-800 dark:bg-gray-950 dark:text-gray-200",
|
|
20
|
+
danger: "border-red-200 bg-red-50 text-red-900 dark:border-red-900/50 dark:bg-red-950/50 dark:text-red-200"
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<div className={cn("relative w-full rounded-xl border p-4 font-sans", variants[variant], className)}>
|
|
25
|
+
{title && (
|
|
26
|
+
<h5 className="mb-1 font-bold leading-none tracking-tight text-gray-800 dark:text-gray-50 font-sans">
|
|
27
|
+
{title}
|
|
28
|
+
</h5>
|
|
29
|
+
)}
|
|
30
|
+
<div className="text-sm opacity-90 leading-relaxed">
|
|
31
|
+
{children}
|
|
32
|
+
</div>
|
|
33
|
+
</div>
|
|
34
|
+
);
|
|
35
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// src/components/ui/Badge.tsx
|
|
2
|
+
/**
|
|
3
|
+
* @file Badge.tsx
|
|
4
|
+
* @description Indicador visual compacto para status, tags ou versões.
|
|
5
|
+
* Utiliza o padrão minimalista de bordas e tipografia do ecossistema.
|
|
6
|
+
*/
|
|
7
|
+
import React from 'react';
|
|
8
|
+
import { cn } from '@/utils/cn';
|
|
9
|
+
|
|
10
|
+
interface BadgeProps {
|
|
11
|
+
children: React.ReactNode;
|
|
12
|
+
className?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const Badge = ({ children, className }: BadgeProps): React.JSX.Element => {
|
|
16
|
+
return (
|
|
17
|
+
<span className={cn(
|
|
18
|
+
"inline-flex items-center rounded-full border border-gray-200 bg-gray-50 px-2.5 py-0.5 text-xs font-bold text-gray-800 transition-colors",
|
|
19
|
+
"dark:border-gray-800 dark:bg-gray-900 dark:text-gray-50 font-sans",
|
|
20
|
+
className
|
|
21
|
+
)}>
|
|
22
|
+
{children}
|
|
23
|
+
</span>
|
|
24
|
+
);
|
|
25
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
export interface BreadcrumbItem {
|
|
4
|
+
label: string;
|
|
5
|
+
href?: string;
|
|
6
|
+
active?: boolean;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const Breadcrumb = ({ items }: { items: BreadcrumbItem[] }): React.JSX.Element => {
|
|
10
|
+
return (
|
|
11
|
+
<nav className="flex" aria-label="Breadcrumb">
|
|
12
|
+
<ol className="inline-flex items-center space-x-1 md:space-x-3">
|
|
13
|
+
{items.map((item, index) => (
|
|
14
|
+
<li key={index} className="inline-flex items-center">
|
|
15
|
+
{index > 0 && <span className="mx-2 text-gray-400">/</span>}
|
|
16
|
+
<span className={`text-sm font-medium ${item.active ? 'text-gray-800 dark:text-gray-50' : 'text-gray-400 hover:text-gray-800 dark:text-gray-200 dark:hover:text-gray-50'}`}>
|
|
17
|
+
{item.label}
|
|
18
|
+
</span>
|
|
19
|
+
</li>
|
|
20
|
+
))}
|
|
21
|
+
</ol>
|
|
22
|
+
</nav>
|
|
23
|
+
);
|
|
24
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// src/components/ui/Button.tsx
|
|
2
|
+
/**
|
|
3
|
+
* @file Button.tsx
|
|
4
|
+
* @description Componente de botão atômico com suporte a variantes e children.
|
|
5
|
+
* Ajustado para aceitar conteúdo interno e manter o padrão de cores v0.
|
|
6
|
+
*/
|
|
7
|
+
import React from 'react';
|
|
8
|
+
import { cn } from '@/utils/cn';
|
|
9
|
+
|
|
10
|
+
export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
11
|
+
variant?: 'primary' | 'outline' | 'ghost';
|
|
12
|
+
children: React.ReactNode;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const Button = ({ children, variant = 'primary', className, ...props }: ButtonProps): React.JSX.Element => {
|
|
16
|
+
// Base minimalista seguindo as diretrizes de cores e bordas
|
|
17
|
+
const base = "inline-flex items-center justify-center rounded-lg px-4 py-2 text-sm font-medium transition-all focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50 border cursor-pointer active:scale-95";
|
|
18
|
+
|
|
19
|
+
const variants = {
|
|
20
|
+
primary: "bg-gray-800 text-gray-50 border-gray-200 hover:bg-gray-950 dark:bg-gray-50 dark:text-gray-950 dark:border-gray-800 dark:hover:bg-gray-200",
|
|
21
|
+
outline: "bg-transparent text-gray-800 border-gray-200 hover:bg-gray-100 dark:text-gray-50 dark:border-gray-800 dark:hover:bg-gray-900",
|
|
22
|
+
ghost: "bg-transparent text-gray-500 border-transparent hover:bg-gray-100 dark:text-gray-400 dark:hover:bg-gray-900"
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<button
|
|
27
|
+
{...props}
|
|
28
|
+
className={cn(base, variants[variant], className)}
|
|
29
|
+
>
|
|
30
|
+
{children}
|
|
31
|
+
</button>
|
|
32
|
+
);
|
|
33
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// src/components/ui/Card.tsx
|
|
2
|
+
/**
|
|
3
|
+
* @file Card.tsx
|
|
4
|
+
* @description Container estrutural para agrupamento de informações.
|
|
5
|
+
* Implementa o padrão de sombras sutis e bordas do ecossistema v0 Digital.
|
|
6
|
+
*/
|
|
7
|
+
import React from 'react';
|
|
8
|
+
import { cn } from '@/utils/cn';
|
|
9
|
+
|
|
10
|
+
interface CardProps {
|
|
11
|
+
title?: string;
|
|
12
|
+
subtitle?: string;
|
|
13
|
+
children: React.ReactNode;
|
|
14
|
+
className?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const Card = ({ title, subtitle, children, className }: CardProps): React.JSX.Element => {
|
|
18
|
+
return (
|
|
19
|
+
<div className={cn(
|
|
20
|
+
"rounded-xl border border-gray-200 bg-white p-6 shadow-xs",
|
|
21
|
+
"dark:border-gray-800 dark:bg-gray-950",
|
|
22
|
+
className
|
|
23
|
+
)}>
|
|
24
|
+
{(title || subtitle) && (
|
|
25
|
+
<div className="mb-4 space-y-1">
|
|
26
|
+
{title && (
|
|
27
|
+
<h3 className="text-xl font-bold tracking-tight text-gray-800 dark:text-gray-50 font-sans">
|
|
28
|
+
{title}
|
|
29
|
+
</h3>
|
|
30
|
+
)}
|
|
31
|
+
{subtitle && (
|
|
32
|
+
<p className="text-sm text-gray-500 dark:text-gray-100 font-sans">
|
|
33
|
+
{subtitle}
|
|
34
|
+
</p>
|
|
35
|
+
)}
|
|
36
|
+
</div>
|
|
37
|
+
)}
|
|
38
|
+
<div className="text-gray-400 dark:text-gray-200 font-sans leading-relaxed">
|
|
39
|
+
{children}
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
);
|
|
43
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// src/components/ui/Checkbox.tsx
|
|
2
|
+
/**
|
|
3
|
+
* @file Checkbox.tsx
|
|
4
|
+
* @description Seletor binário customizado.
|
|
5
|
+
* Mantém a precisão visual em formulários ERP com suporte nativo ao modo dark.
|
|
6
|
+
*/
|
|
7
|
+
import React from 'react';
|
|
8
|
+
import { cn } from '@/utils/cn';
|
|
9
|
+
|
|
10
|
+
export interface CheckboxProps extends React.InputHTMLAttributes<HTMLInputElement> {
|
|
11
|
+
label?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const Checkbox = ({ label, className, ...props }: CheckboxProps): React.JSX.Element => {
|
|
15
|
+
return (
|
|
16
|
+
<label className="inline-flex cursor-pointer items-center gap-2 group">
|
|
17
|
+
<input
|
|
18
|
+
type="checkbox"
|
|
19
|
+
{...props}
|
|
20
|
+
className={cn(
|
|
21
|
+
"h-4 w-4 rounded border border-gray-200 bg-white text-gray-800 transition-all",
|
|
22
|
+
"focus:ring-0 focus:ring-offset-0 cursor-pointer",
|
|
23
|
+
"dark:border-gray-800 dark:bg-gray-950 dark:checked:bg-gray-50",
|
|
24
|
+
className
|
|
25
|
+
)}
|
|
26
|
+
/>
|
|
27
|
+
{label && (
|
|
28
|
+
<span className="text-sm font-medium text-gray-500 dark:text-gray-100 font-sans group-hover:text-gray-800 dark:group-hover:text-gray-50 transition-colors">
|
|
29
|
+
{label}
|
|
30
|
+
</span>
|
|
31
|
+
)}
|
|
32
|
+
</label>
|
|
33
|
+
);
|
|
34
|
+
};
|