Compare commits

..

80 Commits

Author SHA1 Message Date
avanzato dc697c8962 Release PROD: Aplicativos de Atrair Leads
Deploy PROD Avanzato / deploy-prod (push) Successful in 45s
2026-06-03 22:19:56 -03:00
avanzato 667447c389 Merge remote-tracking branch 'origin/develop' 2026-06-03 22:19:52 -03:00
avanzato e3f35d808d Log release HML: Aplicativos de Atrair Leads
Deploy HML Avanzato / deploy-hml (push) Successful in 37s
2026-06-03 22:07:59 -03:00
avanzato fdfb4606f0 Aplicativos de Atrair Leads 2026-06-03 22:07:59 -03:00
avanzato e5c18bb6fc Log release HML: Aplicativos de Atrair Leads
Deploy HML Avanzato / deploy-hml (push) Successful in 2m21s
2026-06-02 22:34:40 -03:00
avanzato 6dfdf01830 Aplicativos de Atrair Leads 2026-06-02 22:34:39 -03:00
avanzato cd6bcf5b3d Release PROD: Paginas e SEO master, correção javascript google
Deploy PROD Avanzato / deploy-prod (push) Successful in 35s
2026-06-02 14:21:08 -03:00
avanzato 35e5727e60 Merge remote-tracking branch 'origin/develop' 2026-06-02 12:50:56 -03:00
avanzato 96563b30e9 Log release HML: Paginas e SEO master
Deploy HML Avanzato / deploy-hml (push) Successful in 42s
2026-06-02 12:43:58 -03:00
avanzato 80dbb986f4 Paginas e SEO master 2026-06-02 12:43:57 -03:00
avanzato 6db7b5200b Log release HML: Paginas e SEO master
Deploy HML Avanzato / deploy-hml (push) Successful in 1m17s
2026-06-02 12:07:35 -03:00
avanzato 52cda4304b Paginas e SEO master 2026-06-02 12:07:34 -03:00
avanzato a85a6c9b3f Log release HML: Paginas e SEO master
Deploy HML Avanzato / deploy-hml (push) Successful in 46s
2026-06-02 11:45:37 -03:00
avanzato 2900b9afd9 Paginas e SEO master 2026-06-02 11:45:36 -03:00
avanzato 433773c4b8 Log release HML: Paginas e SEO master
Deploy HML Avanzato / deploy-hml (push) Successful in 47s
2026-06-02 10:42:23 -03:00
avanzato 6140142bab Paginas e SEO master 2026-06-02 10:42:23 -03:00
avanzato 529036bfe9 Release PROD: Paginas e SEO master
Deploy PROD Avanzato / deploy-prod (push) Successful in 34s
2026-06-01 20:28:34 -03:00
avanzato 52f86cbba5 Release PROD: Paginas e SEO master
Deploy PROD Avanzato / deploy-prod (push) Successful in 43s
2026-06-01 19:56:51 -03:00
avanzato 3eb41133f4 Merge remote-tracking branch 'origin/develop' 2026-06-01 19:56:36 -03:00
avanzato e8f15d63ed Log release HML: Paginas e SEO master
Deploy HML Avanzato / deploy-hml (push) Successful in 42s
2026-06-01 19:55:54 -03:00
avanzato 24786e3512 Paginas e SEO master 2026-06-01 19:55:53 -03:00
avanzato 7bcc582664 Release PROD: Paginas e SEO master
Deploy PROD Avanzato / deploy-prod (push) Successful in 42s
2026-06-01 19:44:45 -03:00
avanzato 4f15851f09 Merge remote-tracking branch 'origin/develop' 2026-06-01 19:43:55 -03:00
avanzato 15ce64e786 Log release HML: Paginas e SEO master
Deploy HML Avanzato / deploy-hml (push) Successful in 35s
2026-06-01 19:39:58 -03:00
avanzato 913e717a21 Paginas e SEO master 2026-06-01 19:39:58 -03:00
avanzato aa275ef538 Log release HML: Revisão Logo, Favicon, Robots
Deploy HML Avanzato / deploy-hml (push) Successful in 39s
2026-06-01 19:30:24 -03:00
avanzato 19f2848fec Revisão Logo, Favicon, Robots 2026-06-01 19:30:24 -03:00
avanzato 10e910eb6b Release PROD: Revisão Logo, Favicon, Robots
Deploy PROD Avanzato / deploy-prod (push) Successful in 34s
2026-05-31 00:58:08 -03:00
avanzato 9885f59b06 Merge remote-tracking branch 'origin/develop' 2026-05-31 00:58:01 -03:00
avanzato d79c9c48a9 Log release HML: Revisão Logo, Favicon, Robots
Deploy HML Avanzato / deploy-hml (push) Successful in 33s
2026-05-31 00:57:46 -03:00
avanzato 7f6c800ab0 Revisão Logo, Favicon, Robots 2026-05-31 00:57:45 -03:00
avanzato ec3e439973 Release PROD: Revisão Logo, Favicon, Robots
Deploy PROD Avanzato / deploy-prod (push) Successful in 33s
2026-05-31 00:29:03 -03:00
avanzato bfb022866b Merge remote-tracking branch 'origin/develop' 2026-05-31 00:29:00 -03:00
avanzato be22792b6d Log release HML: Revisão Logo, Favicon, Robots
Deploy HML Avanzato / deploy-hml (push) Successful in 37s
2026-05-31 00:28:42 -03:00
avanzato cb08c53b40 Revisão Logo, Favicon, Robots 2026-05-31 00:28:42 -03:00
avanzato 8c056b0c50 Release PROD: Revisão Logo, Favicon, Robots
Deploy PROD Avanzato / deploy-prod (push) Successful in 33s
2026-05-31 00:03:02 -03:00
avanzato b284c4d258 Merge remote-tracking branch 'origin/develop' 2026-05-31 00:02:57 -03:00
avanzato 3d36e98185 Log release HML: Revisão Logo, Favicon, Robots
Deploy HML Avanzato / deploy-hml (push) Successful in 59s
2026-05-30 23:54:49 -03:00
avanzato 4e26c13240 Revisão Logo, Favicon, Robots 2026-05-30 23:54:48 -03:00
avanzato 3e91269f12 Log release HML: Revisão logo
Deploy HML Avanzato / deploy-hml (push) Successful in 33s
2026-05-30 23:52:08 -03:00
avanzato b3b8917a96 Revisão logo 2026-05-30 23:52:07 -03:00
avanzato d15474365c Release PROD: Favivon - Correção
Deploy PROD Avanzato / deploy-prod (push) Successful in 33s
2026-05-30 23:15:12 -03:00
avanzato e9f47c5b1e Merge remote-tracking branch 'origin/develop' 2026-05-30 23:15:09 -03:00
avanzato 2dc62486d7 Log release HML: Favivon - Correção
Deploy HML Avanzato / deploy-hml (push) Successful in 34s
2026-05-30 23:14:49 -03:00
avanzato 643f4a3f10 Favivon - Correção 2026-05-30 23:14:48 -03:00
avanzato af8ef2693d Release PROD: Favivon - Correção
Deploy PROD Avanzato / deploy-prod (push) Successful in 34s
2026-05-30 20:36:09 -03:00
avanzato 5b729b23be Merge remote-tracking branch 'origin/develop' 2026-05-30 20:35:32 -03:00
avanzato 643f628ad7 Log release HML: Favivon - Correção
Deploy HML Avanzato / deploy-hml (push) Successful in 36s
2026-05-30 20:00:25 -03:00
avanzato d7dfd221f0 Favivon - Correção 2026-05-30 19:59:39 -03:00
avanzato 76ddaa815d Log release HML: Favivon - Correção
Deploy HML Avanzato / deploy-hml (push) Successful in 50s
2026-05-30 19:45:34 -03:00
avanzato 2529360958 Favivon - Correção 2026-05-30 19:45:34 -03:00
avanzato 41312945d9 Release PROD: Atualização e correção de diretorios
Deploy PROD Avanzato / deploy-prod (push) Successful in 30s
2026-05-30 19:30:29 -03:00
avanzato bb73783e9f Merge remote-tracking branch 'origin/develop' 2026-05-30 19:30:26 -03:00
avanzato 146652ab10 Log release HML: Correção de sub pastas
Deploy HML Avanzato / deploy-hml (push) Successful in 33s
2026-05-30 19:03:06 -03:00
avanzato 246d860ce4 Correção de sub pastas 2026-05-30 19:03:06 -03:00
avanzato 87fe58944d Log release HML: Commit V1.1.1
Deploy HML Avanzato / deploy-hml (push) Successful in 34s
2026-05-30 16:39:05 -03:00
avanzato 38723c5a61 Commit V1.1.1 2026-05-30 16:39:05 -03:00
avanzato d420717053 Log release HML: corrigindo scrpit
Deploy HML Avanzato / deploy-hml (push) Successful in 33s
2026-05-30 16:32:48 -03:00
avanzato 81598abe19 corrigindo scrpit 2026-05-30 16:32:48 -03:00
avanzato 56e8d9d0bf Log release HML: Commit V1.0.9
Deploy HML Avanzato / deploy-hml (push) Successful in 32s
2026-05-30 13:37:48 -03:00
avanzato b2a92d1a5b Commit V1.0.9 2026-05-30 13:37:48 -03:00
avanzato 57c4abf055 Merge branch 'develop'
Deploy PROD Avanzato / deploy-prod (push) Successful in 50s
2026-05-29 01:15:14 -03:00
avanzato eb39bd7867 Atualiza site corrigindo logo
Deploy HML Avanzato / deploy-hml (push) Successful in 34s
2026-05-29 01:09:08 -03:00
avanzato d0334004fd Ajusta gitignore e adiciona guia nginx
Deploy HML Avanzato / deploy-hml (push) Successful in 36s
2026-05-29 00:14:49 -03:00
avanzato 6ff7089647 Remove zips do versionamento
Deploy HML Avanzato / deploy-hml (push) Successful in 43s
2026-05-27 23:27:59 -03:00
avanzato f493c55040 Merge branch 'develop'
Deploy PROD Avanzato / deploy-prod (push) Successful in 31s
2026-05-27 22:45:17 -03:00
avanzato b8881c4172 Restaura workflow HML
Deploy HML Avanzato / deploy-hml (push) Successful in 36s
2026-05-27 22:32:47 -03:00
avanzato 5c5e39b866 Atualiza site via novo pacote 2026-05-27 22:11:48 -03:00
avanzato 0a0d2382d1 Merge branch 'develop'
Deploy PROD Avanzato / deploy-prod (push) Successful in 54s
2026-05-27 16:17:46 -03:00
avanzato 9e3082729c Atualiza sitemap, e logo rodapé
Deploy HML Avanzato / deploy-hml (push) Successful in 37s
2026-05-27 16:12:41 -03:00
avanzato 47fdcab3b4 Adiciona deploy automático produção
Deploy PROD Avanzato / deploy-prod (push) Successful in 58s
2026-05-26 22:07:42 -03:00
avanzato f66e745d87 Ajusta homepage e depoimentos
Deploy HML Avanzato / deploy-hml (push) Successful in 30s
2026-05-26 21:52:38 -03:00
avanzato 78c7f04a7e Configura HML para branch develop
Deploy HML Avanzato / deploy-hml (push) Successful in 42s
2026-05-26 21:00:32 -03:00
avanzato 9f5e31da0f Corrige indentação do workflow HML
Deploy HML Avanzato / deploy-hml (push) Successful in 30s
2026-05-26 01:09:14 -03:00
avanzato d79268de44 Força execução do pipeline 2026-05-26 00:56:24 -03:00
avanzato c8a1ba0ebe Ajusta deploy HML usando chave SSH direta 2026-05-26 00:43:41 -03:00
avanzato 0326ee548b Adiciona deploy automático para HML
Deploy HML Avanzato / deploy-hml (push) Failing after 24s
2026-05-26 00:25:13 -03:00
avanzato 65f7859f65 Adiciona workflow de build HML
Build HML Avanzato / build (push) Successful in 28s
2026-05-25 23:50:34 -03:00
avanzato ac32d91010 Merge repositório remoto com versão local
Build e Deploy / Build (push) Has been cancelled
Build e Deploy / Deploy - Producao (push) Has been cancelled
Build e Deploy / Deploy - Staging (push) Has been cancelled
Build e Deploy / Notificar Falha (push) Has been cancelled
2026-05-25 23:39:01 -03:00
avanzato 4eed31a206 Versão inicial do site Avanzato 2026-05-25 23:31:32 -03:00
33061 changed files with 5496540 additions and 1 deletions
Vendored
BIN
View File
Binary file not shown.
+37
View File
@@ -0,0 +1,37 @@
name: Deploy HML Avanzato
on:
push:
branches:
- develop
jobs:
deploy-hml:
runs-on: linux_amd64
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Instalar dependências
run: npm install
- name: Build projeto
run: npm run build
- name: Compactar dist
run: tar -czf dist.tar.gz dist
- name: Enviar para servidor HML
run: scp -o StrictHostKeyChecking=no -i /data/avz_hml_deploy dist.tar.gz root@191.252.100.174:/tmp/avz-site-hml-dist.tar.gz
- name: Publicar HML
run: |
ssh -o StrictHostKeyChecking=no -i /data/avz_hml_deploy root@191.252.100.174 '
rm -rf /opt/avanzato/site-hml/app/dist_old
mv /opt/avanzato/site-hml/app/dist /opt/avanzato/site-hml/app/dist_old
mkdir -p /opt/avanzato/site-hml/app/dist
tar -xzf /tmp/avz-site-hml-dist.tar.gz -C /opt/avanzato/site-hml/app
rm -f /tmp/avz-site-hml-dist.tar.gz
docker restart avanzato-site-hml
'
+37
View File
@@ -0,0 +1,37 @@
name: Deploy PROD Avanzato
on:
push:
branches:
- main
jobs:
deploy-prod:
runs-on: linux_amd64
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Instalar dependências
run: npm install
- name: Build projeto
run: npm run build
- name: Compactar dist
run: tar -czf dist.tar.gz dist
- name: Enviar para servidor PROD
run: scp -o StrictHostKeyChecking=no -i /data/avz_hml_deploy dist.tar.gz root@191.252.100.174:/tmp/avz-site-prod-dist.tar.gz
- name: Publicar PROD
run: |
ssh -o StrictHostKeyChecking=no -i /data/avz_hml_deploy root@191.252.100.174 '
rm -rf /opt/avanzato/site/app/dist_old
mv /opt/avanzato/site/app/dist /opt/avanzato/site/app/dist_old
mkdir -p /opt/avanzato/site/app/dist
tar -xzf /tmp/avz-site-prod-dist.tar.gz -C /opt/avanzato/site/app
rm -f /tmp/avz-site-prod-dist.tar.gz
docker restart avanzato-site
'
+135
View File
@@ -0,0 +1,135 @@
# CI/CD - Avanzato Tecnologia
## Estrutura do Pipeline
```
Push para main/master
|
v
+------------------+ +------------------+ +------------------+
| BUILD | --> | PRESERVAR ASSETS | --> | DEPLOY |
| | | | | |
| - Checkout | | - Logo | | - SCP para VPS |
| - npm install | | - Imagens | | - Limpar cache |
| - npm run build | | - Favicon | | - Notificar |
+------------------+ +------------------+ +------------------+
```
---
## Configurar Secrets no GitHub
Va em: **Settings > Secrets and variables > Actions > New repository secret**
| Secret | Descricao | Exemplo |
|--------|-----------|---------|
| `SSH_HOST` | IP ou dominio do servidor | `201.45.120.30` ou `avanzato.com.br` |
| `SSH_USER` | Usuario SSH | `root` ou `deploy` |
| `SSH_PRIVATE_KEY` | Chave SSH privada | `-----BEGIN OPENSSH PRIVATE KEY-----...` |
| `SSH_PORT` | Porta SSH (opcional) | `22` |
| `DEPLOY_PATH` | Caminho no servidor | `/var/www/avanzato` |
| `STAGING_PATH` | Caminho staging (opcional) | `/var/www/avanzato-staging` |
### Como gerar a chave SSH:
```bash
# No seu computador (NAO no servidor):
ssh-keygen -t ed25519 -C "github-actions" -f ~/.ssh/github_actions
# Copiar publica para o servidor:
ssh-copy-id -i ~/.ssh/github_actions.pub root@SEU_SERVIDOR
# Copiar privada para o GitHub Secret:
cat ~/.ssh/github_actions
# Cole o conteudo inteiro no secret SSH_PRIVATE_KEY
```
---
## Estrutura de Branches
```
main/master --> Deploy automatico para producao
staging --> Deploy automatico para staging (se existir)
feature/* --> Apenas build (sem deploy)
hotfix/* --> Apenas build (sem deploy)
```
---
## Como Funciona
### Cenario 1: Deploy Automatico
```
1. Voce faz push para main
2. GitHub Actions executa automaticamente
3. Build + Deploy em ~2 minutos
4. Site atualizado!
```
### Cenario 2: Deploy Manual
```
1. Va em Actions > Build e Deploy
2. Clique "Run workflow"
3. Escolha: production ou staging
4. Clique "Run"
```
### Cenario 3: Pull Request
```
1. Cria PR para main
2. CI executa build (testa se compila)
3. NAO faz deploy
4. Merge aprovado? Deploy automatico!
```
---
## Assets Customizados (Logo, Imagens)
Para preservar assets apos o deploy automatico:
### 1. Commitar assets no repositorio
```bash
git add custom-assets/logo/logo.png
git add custom-assets/images/
git commit -m "Adiciona logo e imagens customizadas"
git push origin main
```
### 2. O pipeline faz sozinho
O workflow executa automaticamente:
```bash
bash scripts/preserve-assets.sh dist/
```
---
## Monitore os Deploys
- **GitHub Actions**: https://github.com/SEU_USUARIO/avanzato-site/actions
- **Status**: Check verde = sucesso, X vermelho = falha
- **Logs**: Clique no workflow para ver detalhes
---
## Troubleshooting
### Deploy falhou?
1. **Verifique os secrets**: Settings > Secrets > Actions
2. **Verifique a chave SSH**: `ssh -i ~/.ssh/github_actions root@SEU_SERVIDOR`
3. **Verifique permissoes**: O usuario precisa de acesso de escrita no DEPLOY_PATH
4. **Verifique logs**: Actions > Clique no workflow > Veja o passo que falhou
### Assets nao preservados?
1. Verifique se `custom-assets/` esta no repositorio
2. Verifique se os arquivos estao comitados: `git ls-files custom-assets/`
3. Verifique os logs do passo "Preservar assets customizados"
---
*Configurado em: Maio 2026*
+162
View File
@@ -0,0 +1,162 @@
# ============================================================================
# CI/CD Pipeline - Avanzato Tecnologia
# ============================================================================
# Este workflow automatiza o build e deploy do site.
#
# TRIGGER: Push na branch main ou manual (workflow_dispatch)
# ============================================================================
name: Build e Deploy
on:
# Deploy automatico ao fazer push na main
push:
branches: [ main, master ]
# Deploy manual pelo botao "Run workflow" no GitHub
workflow_dispatch:
inputs:
environment:
description: 'Ambiente'
required: true
default: 'production'
type: choice
options:
- production
- staging
# Pull Request (apenas build, sem deploy)
pull_request:
branches: [ main, master ]
# Permissoes necessarias
permissions:
contents: read
jobs:
# ==========================================================================
# JOB 1: Build
# ==========================================================================
build:
name: Build
runs-on: ubuntu-latest
steps:
# Passo 1: Checkout do codigo
- name: Checkout codigo
uses: actions/checkout@v4
# Passo 2: Setup Node.js
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
# Passo 3: Instalar dependencias
- name: Instalar dependencias
run: npm ci
# Passo 4: Build
- name: Build
run: npm run build
# Passo 5: Upload do artefato (para usar no job de deploy)
- name: Upload artefato
uses: actions/upload-artifact@v4
with:
name: dist
path: dist/
retention-days: 1
# ==========================================================================
# JOB 2: Deploy para Servidor (Producao)
# ==========================================================================
deploy-production:
name: Deploy - Producao
needs: build
runs-on: ubuntu-latest
# So executa na branch main/master ou manual com environment=production
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master' || github.event.inputs.environment == 'production'
steps:
# Passo 1: Download do artefato buildado
- name: Download artefato
uses: actions/download-artifact@v4
with:
name: dist
path: dist/
# Passo 2: Preservar assets customizados (logo, imagens)
- name: Preservar assets customizados
run: |
if [ -d "custom-assets" ]; then
echo "Preservando assets customizados..."
bash scripts/preserve-assets.sh dist/
else
echo "Nenhum asset customizado encontrado."
fi
# Passo 3: Deploy via SCP (SSH)
- name: Deploy para servidor
uses: appleboy/scp-action@v0.1.7
with:
host: ${{ secrets.SSH_HOST }}
username: ${{ secrets.SSH_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
port: ${{ secrets.SSH_PORT || '22' }}
source: "dist/*"
target: ${{ secrets.DEPLOY_PATH || '/var/www/avanzato' }}
strip_components: 1
rm: true # Remove arquivos antigos antes de copiar
# Passo 4: Notificar (opcional - webhook Discord/Slack)
- name: Notificar sucesso
if: success()
run: |
echo "Deploy para producao concluido com sucesso!"
echo "Site: https://avanzato.com.br"
# ==========================================================================
# JOB 3: Deploy para Staging (Opcional)
# ==========================================================================
deploy-staging:
name: Deploy - Staging
needs: build
runs-on: ubuntu-latest
# So executa em push para branch staging ou manual
if: github.ref == 'refs/heads/staging' || github.event.inputs.environment == 'staging'
steps:
- name: Download artefato
uses: actions/download-artifact@v4
with:
name: dist
path: dist/
- name: Deploy para staging
uses: appleboy/scp-action@v0.1.7
with:
host: ${{ secrets.SSH_HOST }}
username: ${{ secrets.SSH_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
port: ${{ secrets.SSH_PORT || '22' }}
source: "dist/*"
target: ${{ secrets.STAGING_PATH || '/var/www/avanzato-staging' }}
strip_components: 1
rm: true
# ==========================================================================
# JOB 4: Notificacao de Falha
# ==========================================================================
notify-failure:
name: Notificar Falha
needs: [build, deploy-production]
runs-on: ubuntu-latest
if: failure()
steps:
- name: Notificar falha
run: |
echo "ALERTA: O deploy falhou!"
echo "Verifique os logs em: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
+46
View File
@@ -0,0 +1,46 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
# Build
dist
dist-ssr
*.local
# Cache
.cache
.vite
# Dependencias
node_modules
# Ambiente (NUNCA commite!)
.env
.env.local
.env.*.local
# Editor
.vscode/*
!.vscode/extensions.json
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
# OS
.DS_Store
Thumbs.db
# Scripts de deploy local
scripts/.env.local
# ZIPs internos (nunca commite!)
*.zip
avanzato-*.zip
+232
View File
@@ -0,0 +1,232 @@
# Como Usar a Configuracao Central - Avanzato
## O que mudou?
Agora **tudo** (logo, redes sociais, contatos, enderecos, menu, etc.) esta em um unico arquivo:
```
src/config/site.ts
```
**Nao precisa mais editar Navigation.tsx, Footer.tsx ou qualquer componente!**
---
## EDITAR APENAS ESTE ARQUIVO
### src/config/site.ts
### 1. Trocar o Logo
**Opcao A - SVG inline:**
```typescript
export const LOGO = {
svgInline: `<svg width="40" height="40">...COLE O SVG AQUI...</svg>`,
imageUrl: '', // Deixe vazio
text: 'avanzato',
textHighlight: '.',
};
```
**Opcao B - Imagem (PNG/SVG):**
```typescript
export const LOGO = {
svgInline: '', // Deixe vazio
imageUrl: '/assets/logo.png', // Ou .svg
text: '', // Deixe vazio se nao quiser texto
textHighlight: '',
};
```
### 2. Trocar Redes Sociais
```typescript
export const SOCIAL_LINKS = {
facebook: {
url: 'https://www.facebook.com/avanzatotec',
label: 'Facebook',
},
youtube: {
url: '', // Deixe vazio para OCULTAR
label: 'YouTube',
},
linkedin: {
url: 'https://www.linkedin.com/...',
label: 'LinkedIn',
},
instagram: {
url: 'https://www.instagram.com/...',
label: 'Instagram',
},
};
```
Para **adicionar uma rede nova**, adicione uma linha:
```typescript
tiktok: {
url: 'https://tiktok.com/...',
label: 'TikTok',
iconSvg: '', // Opcional: SVG customizado
},
```
### 3. Trocar Contatos
```typescript
export const CONTACT = {
phone: '(11) 4810-1704',
phoneRaw: '551148101704',
email: 'leads@avanzato.com.br',
supportUrl: 'https://suporte.avanzato.com.br/',
};
```
### 4. Trocar Enderecos
```typescript
export const ADDRESSES = [
{
label: 'Escritorio Principal',
street: 'Rua Juiz de Fora, 76',
city: 'Guarulhos',
state: 'SP',
cep: '',
},
// Adicione mais ou remova
];
```
### 5. Trocar Menu de Navegacao
```typescript
export const NAV_LINKS = [
{ name: 'Home', href: '/' },
{ name: 'Sobre', href: '/sobre' },
{ name: 'Servicos', href: '#', submenu: [...] },
];
```
### 6. Desabilitar WhatsApp Widget
```typescript
export const WHATSAPP_CONFIG = {
enabled: false, // Mude para false para ocultar
// ...
};
```
---
## APOS EDITAR
Sempre que editar `site.ts`, faca:
```bash
npm run build
```
O build vai usar os novos valores automaticamente.
---
## SISTEMA DE PRESERVACAO DE ASSETS
Para imagens que NAO devem ser sobrescritas no deploy:
### 1. Crie a pasta de assets customizados
```bash
mkdir -p custom-assets/logo
mkdir -p custom-assets/images
```
### 2. Coloque seus arquivos
```
custom-assets/
logo/
logo.png <- Seu logo (substitui o do build)
favicon.ico <- Favicon customizado
images/
hero-bg.jpg <- Imagem do hero
about.jpg <- Imagem da secao sobre
```
### 3. Execute o script apos o deploy
```bash
bash scripts/preserve-assets.sh /caminho/do/site/no/servidor
```
Ou se o site esta em `/var/www/avanzato`:
```bash
bash scripts/preserve-assets.sh /var/www/avanzato/dist
```
### O que o script faz?
- Copia `logo/logo.*` para a pasta do site
- Copia `images/*` para a pasta do site
- Copia `fonts/*` se houver
- **Nao altera** nenhum outro arquivo do build
---
## FLUXO DE TRABALHO RECOMENDADO
### Quando editar config (textos, links, etc.):
1. Edite `src/config/site.ts`
2. Rode `npm run build`
3. Deploy
### Quando trocar apenas imagens/logo:
1. Coloque a nova imagem em `custom-assets/`
2. Rode `bash scripts/preserve-assets.sh /caminho/do/site`
3. **Nao precisa rebuild!**
### Quando eu (dev) atualizar o site:
1. Eu faco as mudancas no codigo
2. Build + deploy
3. **Voce roda:** `bash scripts/preserve-assets.sh /caminho/do/site`
4. Seu logo e imagens sao restaurados automaticamente!
---
## EXEMPLO COMPLETO
### Cenario: Trocar apenas o logo
**Passo 1:** Coloque o novo logo em `custom-assets/logo/logo.png`
**Passo 2:** Execute:
```bash
bash scripts/preserve-assets.sh /var/www/avanzato/dist
```
**Resultado:**
```
[LOGO] Copiando logo.png...
Concluido! 1 arquivo(s) preservado(s).
```
Pronto! O logo foi trocado sem precisar rebuild.
---
## PERGUNTAS FREQUENTES
**Q: Preciso rebuild quando troco o logo via custom-assets?**
R: Nao! O script copia diretamente.
**Q: E se eu quiser trocar o texto do logo?**
R: Ai sim, edite `site.ts` e faca rebuild.
**Q: O script funciona em qualquer servidor?**
R: Sim! Basta passar o caminho correto como argumento.
**Q: Posso adicionar novas redes sociais?**
R: Sim! Basta adicionar em `SOCIAL_LINKS` no `site.ts`.
---
*Documento criado em: Maio 2026*
+96
View File
@@ -0,0 +1,96 @@
# Deploy com Docker - Avanzato
## O Problema
O Nginx no Docker nao vem configurado para SPA React. Ao acessar `/avaliacao` diretamente, da 404.
## A Solucao
### Opcao 1: Configurar Nginx dentro do container (RECOMENDADO)
**1. Criar um novo Dockerfile:**
```dockerfile
FROM nginx:alpine
# Copiar o site
COPY dist/ /usr/share/nginx/html/
# Copiar a configuracao customizada
COPY nginx-docker.conf /etc/nginx/conf.d/default.conf
# Expor porta 80
EXPOSE 80
# Iniciar Nginx
CMD ["nginx", "-g", "daemon off;"]
```
**2. Build e run:**
```bash
# Build
npm run build
bash scripts/build-static-routes.sh dist
docker build -t avanzato-site .
# Run
docker run -d -p 80:80 --name avanzato avanzato-site
```
### Opcao 2: Usar docker-compose
**docker-compose.yml:**
```yaml
version: '3.8'
services:
avanzato:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./dist:/usr/share/nginx/html:ro
- ./nginx-docker.conf:/etc/nginx/conf.d/default.conf:ro
container_name: avanzato-site
```
**Comandos:**
```bash
npm run build
bash scripts/build-static-routes.sh dist
docker-compose up -d
```
### Opcao 3: Container ja rodando (hotfix)
Se o container ja esta rodando:
```bash
# Copiar config para dentro do container
docker cp nginx-docker.conf avanzato-site:/etc/nginx/conf.d/default.conf
# Recarregar Nginx
docker exec avanzato-site nginx -s reload
```
## Verificar se funcionou
```bash
# Testar
curl -I http://localhost/avaliacao
# Deve retornar HTTP 200, nao 404
```
## A regra magica
A unica coisa que importa e esta linha no Nginx:
```nginx
location / {
try_files $uri $uri/ /index.html;
}
```
Isso diz ao Nginx: "se nao encontrar o arquivo, manda para index.html" — onde o React pega a rota.
+250
View File
@@ -0,0 +1,250 @@
# Fluxo de Desenvolvimento - Avanzato
## Visao Geral
```
+---------------------------------------------------------------+
| SEU MAC (Desenvolvimento) |
| |
| +-------------------+ bash scripts/dev.sh start |
| | Codigo Fonte | --> http://localhost:5173 |
| | app/src/ | |
| +-------------------+ |
| | |
| | bash scripts/dev.sh build-hom |
| | bash scripts/dev.sh deploy-hom |
| v |
+---------------------------------------------------------------+
|
v
+---------------------------------------------------------------+
| SERVIDOR - HOMOLOGACAO |
| |
| Gitea (git push) --> Hook --> Build --> Nginx |
| |
| https://homologacao.avanzato.com.br |
| - Mautic: desativado |
| - Google Ads: desativado |
| - Facebook Pixel: desativado |
| - noindex/nofollow (nao aparece no Google) |
| |
+---------------------------------------------------------------+
|
| Aprovado? bash scripts/dev.sh deploy-prod
v
+---------------------------------------------------------------+
| SERVIDOR - PRODUCAO |
| |
| https://avanzato.com.br |
| - Mautic: ativo (forms, tracking) |
| - Google Ads: ativo |
| - Facebook Pixel: ativo |
| - Indexado no Google |
| |
+---------------------------------------------------------------+
```
---
## 1. Primeira Configuracao (faca uma vez)
### 1.1 Instalar Node.js 20+
```bash
# Instalar via Homebrew
brew install node@20
# Verificar
node -v # v20.x.x
npm -v # 10.x.x
```
### 1.2 Instalar dependencias
```bash
cd /Users/avanzato/Projetos/Avanzato/avanzato-site/app
npm install
```
### 1.3 Configurar acesso aos servidores
```bash
# Copiar template
cp scripts/.env.local.example scripts/.env.local
# Editar com seus dados
nano scripts/.env.local
```
Conteudo do `.env.local`:
```bash
# Homologacao
SSH_HOM_HOST=192.168.1.100 # ou IP publico
SSH_HOM_USER=deploy
SSH_HOM_PATH=/var/www/avanzato-hom
# Producao
SSH_PROD_HOST=201.45.120.30 # ou IP publico
SSH_PROD_USER=deploy
SSH_PROD_PATH=/var/www/avanzato
```
### 1.4 Configurar chave SSH (se ainda nao tiver)
```bash
# Gerar chave
ssh-keygen -t ed25519 -C "dev@avanzato" -f ~/.ssh/id_ed25519
# Copiar para o servidor de homologacao
ssh-copy-id -i ~/.ssh/id_ed25519.pub deploy@SEU_SERVIDOR_HOM
# Copiar para o servidor de producao
ssh-copy-id -i ~/.ssh/id_ed25519.pub deploy@SEU_SERVIDOR_PROD
```
---
## 2. Desenvolvimento Diario
### 2.1 Iniciar servidor local
```bash
bash scripts/dev.sh start
# ou
npm run dev
```
Acesse: **http://localhost:5173**
- Hot reload funciona automaticamente
- Mautic/Google Ads/Facebook Pixel estao em modo simulacao
- Edite qualquer arquivo `.tsx` e veja a mudanca ao vivo
### 2.2 Comandos uteis durante desenvolvimento
```bash
# Ver ajuda completa
bash scripts/dev.sh help
# Build para homologacao
bash scripts/dev.sh build-hom
# Preview do build (para testar antes de deploy)
bash scripts/dev.sh preview
# Limpar caches quando algo estranho acontecer
bash scripts/dev.sh clean
```
---
## 3. Deploy para Homologacao
### Metodo 1: Script local (rapido)
```bash
# 1. Build
bash scripts/dev.sh build-hom
# 2. Deploy
bash scripts/dev.sh deploy-hom
```
### Metodo 2: Git push (CI/CD)
```bash
# Adicionar remote do Gitea (faca uma vez)
git remote add homolog ssh://git@SEU_SERVIDOR_HOM:22/gitea/avanzato-site.git
# Sempre que quiser deployar
git add .
git commit -m "descricao da mudanca"
git push homolog main
```
O Gitea vai executar o pipeline automaticamente e deployar.
---
## 4. Deploy para Producao
**ATENCAO: Sempre teste em homologacao primeiro!**
```bash
# 1. Build de producao
bash scripts/dev.sh build
# 2. Deploy (pede confirmacao)
bash scripts/dev.sh deploy-prod
# Digite 'sim' para confirmar
```
---
## 5. Estrutura de Comandos
| Comando | O que faz | URL |
|---------|-----------|-----|
| `bash scripts/dev.sh start` | Servidor dev local | http://localhost:5173 |
| `bash scripts/dev.sh build` | Build producao | - |
| `bash scripts/dev.sh build-hom` | Build homologacao | - |
| `bash scripts/dev.sh preview` | Preview do build | http://localhost:4173 |
| `bash scripts/dev.sh deploy-hom` | Deploy homologacao | https://homologacao.avanzato.com.br |
| `bash scripts/dev.sh deploy-prod` | Deploy producao | https://avanzato.com.br |
| `bash scripts/dev.sh clean` | Limpa caches | - |
| `bash scripts/dev.sh help` | Ajuda | - |
---
## 6. Checklist antes de cada deploy
### Homologacao
- [ ] Testei localmente (`npm run dev`)
- [ ] Build funciona (`bash scripts/dev.sh build-hom`)
- [ ] Nao ha erros no console do navegador
### Producao
- [ ] Testei em homologacao
- [ ] Aprovado pelo time
- [ ] Build de producao (`bash scripts/dev.sh build`)
- [ ] Backup esta funcionando no servidor
---
## 7. Solucao de Problemas
### Erro: "Cannot find module"
```bash
npm install
```
### Erro: "dist/ nao encontrado"
```bash
bash scripts/dev.sh build
```
### Erro: "Permission denied" no deploy
```bash
# Verificar SSH
ssh deploy@SEU_SERVIDOR "echo OK"
# Se nao funcionar, recopiar chave
ssh-copy-id -i ~/.ssh/id_ed25519.pub deploy@SEU_SERVIDOR
```
### Site nao carrega depois do deploy
```bash
# Verificar no servidor
ssh deploy@SEU_SERVIDOR "ls -la /var/www/avanzato"
ssh deploy@SEU_SERVIDOR "sudo systemctl status nginx"
```
---
## 8. Dicas
1. **Nunca edite diretamente no servidor** - Sempre edite local e deploy
2. **Sempre teste em homolog primeiro** - Antes de ir pra prod
3. **Commits frequentes** - Facilita rollback se algo der errado
4. **Mantenha o .env.local seguro** - Nunca commite este arquivo
+116
View File
@@ -0,0 +1,116 @@
# Guia Rapido - Configurar Nginx (SPA React)
## O Problema
URLs como `avanzato.com.br/avaliacao` dao 404 quando acessadas diretamente.
Funcionam apenas quando clica no menu (navegacao interna do React).
## A Solucao
O Nginx precisa redirecionar todas as rotas para `index.html`.
## Passo a Passo
### 1. Copiar a config do projeto para o servidor
No seu Mac, envie o arquivo `nginx.conf` do projeto para o servidor:
```bash
# Enviar o arquivo nginx.conf do projeto para o servidor
scp /Users/avanzato/Projetos/Avanzato/avanzato-site/app/nginx.conf root@SEU_SERVIDOR:/etc/nginx/sites-available/avanzato
```
Ou, se ja estiver logado no servidor:
```bash
# Logar no servidor
ssh root@SEU_SERVIDOR
# Criar o arquivo de config
cat > /etc/nginx/sites-available/avanzato << 'EOF'
server {
listen 80;
server_name avanzato.com.br;
root /var/www/avanzato;
index index.html;
# ESSA LINHA E A CHAVE - redireciona tudo para index.html
location / {
try_files $uri $uri/ /index.html;
}
# Cache para assets
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
expires 6M;
add_header Cache-Control "public";
}
# Seguranca
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
# Bloquear arquivos ocultos
location ~ /\. {
deny all;
}
}
EOF
```
### 2. Ativar o site
```bash
# Criar link simbolico (ativa o site)
ln -s /etc/nginx/sites-available/avanzato /etc/nginx/sites-enabled/avanzato
# Remover o default (se existir)
rm -f /etc/nginx/sites-enabled/default
```
### 3. Testar e aplicar
```bash
# Testar a configuracao (mostra erros se houver)
nginx -t
# Se der OK, recarregar o Nginx
systemctl reload nginx
```
### 4. Verificar se funcionou
```bash
# Testar uma rota direta
curl -I https://avanzato.com.br/avaliacao
# Deve retornar HTTP 200, nao 404
```
---
## Comandos Uteis
| Comando | O que faz |
|---------|-----------|
| `nginx -t` | Testa a configuracao (sem aplicar) |
| `systemctl reload nginx` | Recarrega config sem derrubar o site |
| `systemctl restart nginx` | Reinicia o Nginx |
| `systemctl status nginx` | Verifica se esta rodando |
| `tail -f /var/log/nginx/error.log` | Ver erros em tempo real |
---
## Importante
Se voce usa **Apache** em vez de Nginx, a config esta no arquivo `.htaccess` que ja vai no `dist/`:
```apache
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
RewriteRule ^ index.html [L]
```
Basta garantir que o `.htaccess` esta na raiz do site e o `mod_rewrite` esta ativado.
+244
View File
@@ -0,0 +1,244 @@
# 📁 Guia do .htaccess - Avanzato Tecnologia
## O que é o .htaccess?
O `.htaccess` é um arquivo de configuração do Apache (servidor web) que controla:
- ✅ Redirecionamentos
- ✅ Compressão de arquivos
- ✅ Cache do navegador
- ✅ Segurança
- ✅ Performance
---
## 📂 Onde colocar o arquivo?
```
/public_html/ ← Raiz do seu site
├── .htaccess ← COLOQUE AQUI
├── index.html
├── assets/
└── ...
```
**IMPORTANTE:** O arquivo deve estar na **mesma pasta** do `index.html`
---
## 📋 Arquivos disponíveis
| Arquivo | Quando usar |
|---------|-------------|
| `.htaccess` | Versão **completa** (recomendada) |
| `.htaccess-simples` | Se o servidor der erro 500 |
---
## 🚀 Como instalar
### Método 1: FTP/cPanel
1. Acesse seu servidor via FTP ou cPanel
2. Vá para a pasta `public_html/` (ou `www/`)
3. Faça upload do arquivo `.htaccess`
4. Pronto!
### Método 2: SSH
```bash
# Conecte ao servidor
ssh usuario@seudominio.com.br
# Vá para a pasta do site
cd /var/www/html
# ou
cd /home/usuario/public_html
# Crie o arquivo
nano .htaccess
# Cole o conteúdo do .htaccess
# Salve: Ctrl+O, Enter, Ctrl+X
```
---
## ⚙️ Configurações incluídas
### 1. **SPA (Single Page Application)**
```apache
RewriteRule ^ index.html [L]
```
→ Todas as URLs vão para `index.html` (necessário para React)
### 2. **Compressão GZIP**
```apache
AddOutputFilterByType DEFLATE text/css application/javascript
```
→ Reduz tamanho dos arquivos em ~70%
### 3. **Cache do Navegador**
```apache
ExpiresByType application/javascript "access plus 1 year"
```
→ Arquivos JS/CSS ficam no cache por 1 ano
### 4. **Segurança**
- Proteção contra clickjacking
- Proteção contra MIME sniffing
- Bloqueio de arquivos ocultos (.git, .env)
---
## 🔧 Configurações opcionais
### Ativar HTTPS (quando tiver SSL)
Descomente estas linhas no `.htaccess`:
```apache
# Forçar HTTPS
<IfModule mod_rewrite.c>
RewriteCond %{HTTPS} off
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
</IfModule>
# HSTS (segurança extra)
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
```
### Forçar SEM www
```apache
<IfModule mod_rewrite.c>
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
RewriteRule ^ https://%1%{REQUEST_URI} [R=301,L]
</IfModule>
```
### Forçar COM www
```apache
<IfModule mod_rewrite.c>
RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteRule ^ https://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
</IfModule>
```
---
## ❌ Problemas comuns
### Erro 500 (Internal Server Error)
**Causa:** Seu servidor não tem todos os módulos habilitados
**Solução:** Use o `.htaccess-simples`
```bash
# Renomeie o arquivo
mv .htaccess .htaccess-backup
mv .htaccess-simples .htaccess
```
### Página em branco
**Causa:** O mod_rewrite pode estar desativado
**Solução:** Contate seu provedor de hospedagem para ativar:
- `mod_rewrite`
- `mod_deflate`
- `mod_expires`
- `mod_headers`
### Rotas não funcionam (404 em /sobre, /contato)
**Causa:** O redirecionamento para index.html não está funcionando
**Solução:** Verifique se o arquivo .htaccess está na pasta correta
---
## 📊 Testar se está funcionando
### 1. Testar compressão GZIP
```bash
curl -H "Accept-Encoding: gzip" -I https://seudominio.com.br
```
Deve mostrar: `Content-Encoding: gzip`
### 2. Testar headers de segurança
```bash
curl -I https://seudominio.com.br
```
Deve mostrar:
- `X-Frame-Options: SAMEORIGIN`
- `X-Content-Type-Options: nosniff`
### 3. Testar cache
```bash
curl -I https://seudominio.com.br/assets/index-xxx.js
```
Deve mostrar: `Cache-Control: max-age=31536000`
---
## 🌐 Servidores específicos
### cPanel (Hostgator, Locaweb, etc)
1. Acesse: **Arquivos****Gerenciador de Arquivos**
2. Vá para `public_html/`
3. Clique em **Upload** e envie o `.htaccess`
### AWS S3 + CloudFront
O S3 não usa .htaccess. Configure:
- **Error Document:** `index.html`
- CloudFront: **Custom Error Response** 404 → 200 para `index.html`
### Vercel/Netlify
Não precisa de .htaccess. Use `vercel.json` ou `_redirects`:
```
/* /index.html 200
```
### Nginx (servidor diferente)
Se usar Nginx, crie um arquivo de configuração:
```nginx
location / {
try_files $uri $uri/ /index.html;
}
gzip on;
gzip_types text/css application/javascript;
```
---
## 📞 Suporte
Se tiver problemas, verifique:
1. O arquivo está na pasta correta?
2. O nome do arquivo é exatamente `.htaccess` (com ponto)?
3. As permissões do arquivo são 644?
```bash
# Corrigir permissões
chmod 644 .htaccess
```
---
## ✅ Checklist de instalação
- [ ] Arquivo `.htaccess` na pasta `public_html/`
- [ ] Site carrega normalmente (sem erro 500)
- [ ] Navegação entre páginas funciona
- [ ] Atualizar página (F5) não dá erro 404
- [ ] GZIP está ativo (teste com GTmetrix)
- [ ] Cache está funcionando
---
**Última atualização:** 2024
**Versão:** 1.0
+95
View File
@@ -0,0 +1,95 @@
# MAPA DE ATUACAO AVANZATO - Dados Reais
## Cidades Atendidas (com clientes)
### 1. Guarulhos (Matriz) - 11 clientes
| Cliente | Segmento | Servicos |
|---------|----------|----------|
| Dellacont | Contabilidade | Suporte + Servidor + Backup |
| Madri | Contabilidade | Suporte + Servidor Nuvem + Backup |
| Mouradi Naddi | Advocacia | Suporte + Servidor + Backup |
| EFLAW | Advocacia | Suporte + Servidor + Backup |
| Moraes Advogados | Advocacia | Suporte + Servidor + Backup |
| Thalls | Industria | Suporte + Servidor + Backup + VoIP |
| Ganiko | BPO | Suporte + Servidor Nuvem + Backup |
| DCarvalho | Embalagens | Suporte + Servidor Nuvem + Backup |
| Garuppy | Logistica | Suporte + Servidor Nuvem + Backup |
| Premier | Monitoramento | Suporte + Servidor + Backup |
| Condominio | VoIP | Interligacao aptos a portaria |
### 2. Mogi das Cruzes - 1 cliente
| Cliente | Segmento | Servicos |
|---------|----------|----------|
| Alamo | Monitoramento | Suporte + Servidor + Backup |
### 3. Ipiranga (Sao Paulo) - 1 cliente
| Cliente | Segmento | Servicos |
|---------|----------|----------|
| ONG/Terceiro Setor | Institucional | Suporte + Servidor + Backup + Cloud + Seguranca |
### 4. Cerqueira Cesar (Sao Paulo) - 1 cliente
| Cliente | Segmento | Servicos |
|---------|----------|----------|
| Museu de Faculdade Publica | Acervo Digital | Implantacao Tainacan + ICA-AtoM |
### 5. Vila Formosa (Sao Paulo) - 1 cliente
| Cliente | Segmento | Servicos |
|---------|----------|----------|
| Apoio 24 Horas | Monitoramento | Servidores + pfSense + VPN + Suporte |
### 6. Ribeirao Preto - 1 cliente
| Cliente | Segmento | Servicos |
|---------|----------|----------|
| RMA | Portaria Remota | VoIP em Condominios |
### 7. Bras (Sao Paulo) - 1 cliente
| Cliente | Segmento | Servicos |
|---------|----------|----------|
| Lucena Textil | Textil | Colocation + Rede + VPN + Backup |
### 8. Santana (Sao Paulo) - 1 cliente
| Cliente | Segmento | Servicos |
|---------|----------|----------|
| Quantica Eletrica | Eventos | Suporte + Servidor Nuvem + Backup |
### 9. Mongagua - 1 cliente
| Cliente | Segmento | Servicos |
|---------|----------|----------|
| GateOne e Fox | Monitoramento | Servidores + pfSense + VPN + Suporte |
### 10. Sao Paulo / Cangaiba - 1 cliente
| Cliente | Segmento | Servicos |
|---------|----------|----------|
| RollPrime | VoIP | 2 Ramais VoIP |
### 11. Sao Paulo / Jurubatuba - 1 cliente
| Cliente | Segmento | Servicos |
|---------|----------|----------|
| Piso.com | Piso de Borracha | Suporte + Servidor Nuvem + Backup (em negociacao) |
## Total: 20 clientes ativos em 11 localidades
## Segmentos Atendidos
- Contabilidade (2)
- Advocacia (3)
- Industria (1)
- BPO (1)
- Embalagens (1)
- Logistica (1)
- Monitoramento (4)
- Institucional (1)
- Acervo Digital (1)
- Portaria Remota (1)
- Textil (1)
- Eventos (1)
- VoIP (2)
- Piso de Borracha (1)
## Servicos Mais Vendidos (ranking)
1. Suporte a Computadores (100% dos clientes)
2. Administracao de Servidor (~80%)
3. Backup (~80%)
4. VPN (~40%)
5. VoIP (~25%)
6. Cloud (~15%)
7. Seguranca/Colocation (~10%)
+72 -1
View File
@@ -1,2 +1,73 @@
# avz-site
# React + TypeScript + Vite
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
Currently, two official plugins are available:
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) (or [oxc](https://oxc.rs) when used in [rolldown-vite](https://vite.dev/guide/rolldown)) for Fast Refresh
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
## React Compiler
The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation).
## Expanding the ESLint configuration
If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:
```js
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
// Other configs...
// Remove tseslint.configs.recommended and replace with this
tseslint.configs.recommendedTypeChecked,
// Alternatively, use this for stricter rules
tseslint.configs.strictTypeChecked,
// Optionally, add this for stylistic rules
tseslint.configs.stylisticTypeChecked,
// Other configs...
],
languageOptions: {
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: import.meta.dirname,
},
// other options...
},
},
])
```
You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:
```js
// eslint.config.js
import reactX from 'eslint-plugin-react-x'
import reactDom from 'eslint-plugin-react-dom'
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
// Other configs...
// Enable lint rules for React
reactX.configs['recommended-typescript'],
// Enable lint rules for React DOM
reactDom.configs.recommended,
],
languageOptions: {
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: import.meta.dirname,
},
// other options...
},
},
])
```
+185
View File
@@ -0,0 +1,185 @@
# Setup do Ambiente CI/CD - Avanzato
## Arquitetura
```
+------------------------------------------------------------------+
| SEU SERVIDOR |
| |
| +-------------------+ +-------------------+ +-----------+ |
| | Git Bare Repo | | Pipeline | | Site | |
| | /var/git/site |--->| /var/ci/ |--->| Nginx | |
| | | | | | | |
| | - hooks/post-receive | - build.sh | | /var/www | |
| | - hooks/pre-receive | - deploy.sh | | | |
| +-------------------+ +-------------------+ +-----------+ |
| ^ | |
| | v |
| +-------------------+ +-------------------+ |
| | Seu Computador | | Mautic | |
| | | | mkt.avanzato | |
| | git push | | | |
| +-------------------+ +-------------------+ |
+------------------------------------------------------------------+
```
## 1. Instalar dependencias no servidor
```bash
# Atualizar sistema
apt update && apt upgrade -y
# Instalar Git
apt install -y git curl
# Instalar Node.js 20
curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
apt install -y nodejs
# Verificar
node -v # v20.x.x
npm -v # 10.x.x
# Instalar Nginx
apt install -y nginx
# Instalar PM2 (opcional, para gerenciar processos)
npm install -g pm2
```
## 2. Criar usuario de deploy
```bash
# Criar usuario dedicado
useradd -m -s /bin/bash deploy
# Adicionar ao grupo www-data
usermod -aG www-data deploy
# Criar chave SSH (como usuario deploy)
su - deploy
ssh-keygen -t ed25519 -C "deploy" -f ~/.ssh/id_ed25519 -N ""
# Ver chave publica (para autorizar pushes)
cat ~/.ssh/id_ed25519.pub >> ~/.ssh/authorized_keys
```
## 3. Criar repositorio Git bare
```bash
# Criar pasta
mkdir -p /var/git/site.git
cd /var/git/site.git
git init --bare
# Permissoes
chown -R deploy:deploy /var/git/site.git
chmod -R 755 /var/git/site.git
```
## 4. Configurar hooks Git
```bash
# Copiar hooks
sudo su - deploy
cp /var/ci/hooks/post-receive /var/git/site.git/hooks/post-receive
chmod +x /var/git/site.git/hooks/post-receive
```
## 5. Configurar Nginx
```bash
# Criar config
nano /etc/nginx/sites-available/avanzato
# Ver link: CI/CD > nginx-config.conf
# Ativar
ln -s /etc/nginx/sites-available/avanzato /etc/nginx/sites-enabled/
nginx -t
systemctl reload nginx
# SSL com Let's Encrypt
certbot --nginx -d avanzato.com.br -d www.avanzato.com.br
```
## 6. Criar estrutura de pastas
```bash
mkdir -p /var/www/avanzato # Site em producao
mkdir -p /var/www/avanzato-staging # Site em staging (opcional)
mkdir -p /var/ci # Pipeline scripts
mkdir -p /var/ci/logs # Logs de build/deploy
mkdir -p /var/ci/backups # Backups automaticos
mkdir -p /var/ci/assets # Assets preservados
chown -R deploy:www-data /var/www
chmod -R 755 /var/www
```
## 7. Configurar pipeline
```bash
# Copiar scripts do pipeline
cp /var/ci/scripts/* /var/ci/
chmod +x /var/ci/*.sh
# Configurar variaveis
nano /var/ci/config.env
# Conteudo:
DEPLOY_PATH=/var/www/avanzato
STAGING_PATH=/var/www/avanzato-staging
BACKUP_PATH=/var/ci/backups
LOG_PATH=/var/ci/logs
MAUTIC_URL=https://mkt.avanzato.com.br
```
## 8. Configurar seu computador local
```bash
# Adicionar remote no seu projeto local
cd /caminho/do/projeto/avanzato-site
git remote add servidor ssh://deploy@SEU_SERVIDOR/var/git/site.git
# Testar conexao
ssh deploy@SEU_SERVIDOR "echo 'Conectado!'"
# Push inicial
git push servidor main
```
## Comandos de uso diario
| Comando | Resultado |
|---------|-----------|
| `git push servidor main` | Deploy automatico para producao |
| `git push servidor staging` | Deploy para staging |
| `ssh deploy@SERVIDOR "cat /var/ci/logs/last-deploy.log"` | Ver ultimo log |
| `ssh deploy@SERVIDOR "ls /var/ci/backups"` | Listar backups |
## Estrutura final no servidor
```
/var/
git/
site.git/ # Repo bare (recebe pushes)
hooks/
post-receive # Hook de deploy automatico
ci/
scripts/
build.sh # Compila o site
deploy.sh # Envia para producao
preserve-assets.sh # Preserva logo/imagens
backup.sh # Cria backup
hooks/
post-receive # Template do hook
logs/ # Logs de execucao
backups/ # Backups automaticos
assets/ # Assets preservados
config.env # Variaveis de ambiente
www/
avanzato/ # Site em producao
index.html
assets/
backups/ # Backups antigos
```
+57
View File
@@ -0,0 +1,57 @@
# ============================================================================
# Configuracao do Pipeline CI/CD - Avanzato
# ============================================================================
# Edite estas variaveis conforme seu ambiente.
# ============================================================================
# --- CAMINHOS ---
# Caminho onde o site fica em producao
DEPLOY_PATH=/var/www/avanzato
# Caminho do site em staging (opcional)
STAGING_PATH=/var/www/avanzato-staging
# Caminho para backups
BACKUP_PATH=/var/ci/backups
# Caminho para logs
LOG_PATH=/var/ci/logs
# Caminho para assets preservados (logo, imagens)
ASSETS_PATH=/var/ci/assets
# --- SERVIDOR ---
# URL do site
SITE_URL=https://avanzato.com.br
# URL do site em staging
STAGING_URL=https://staging.avanzato.com.br
# --- INTEGRACOES ---
# URL do Mautic
MAUTIC_URL=https://mkt.avanzato.com.br
# URL do EspoCRM
ESPOCRM_URL=https://crm.avanzato.com.br
# Chave API do Brevo (opcional)
BREVO_API_KEY=
# Google Ads ID
GOOGLE_ADS_ID=AW-16714813566
# --- E-MAIL ---
# E-mail para notificacoes
NOTIFY_EMAIL=leads@avanzato.com.br
# --- CONFIGURACOES ---
# Numero de backups a manter
MAX_BACKUPS=10
# Timeout do build (segundos)
BUILD_TIMEOUT=300
+240
View File
@@ -0,0 +1,240 @@
#!/bin/bash
# ============================================================================
# Git Hook: post-receive
# ============================================================================
# Este hook executa automaticamente apos cada git push.
# Ele faz build e deploy do site sem intervencao manual.
#
# Instalacao:
# cp ci/hooks/post-receive /var/git/site.git/hooks/
# chmod +x /var/git/site.git/hooks/post-receive
# ============================================================================
set -e
# Cores
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
BLUE='\033[0;34m'
NC='\033[0m'
# Configuracoes
REPO_DIR="$(pwd)"
DEPLOY_DIR="/var/tmp/site-deploy-$(date +%s)"
CI_DIR="/var/ci"
LOG_FILE="/var/ci/logs/deploy-$(date +%Y%m%d-%H%M%S).log"
LAST_LOG="/var/ci/logs/last-deploy.log"
CONFIG_FILE="/var/ci/config.env"
# Criar pasta de logs
mkdir -p "$(dirname "$LOG_FILE")"
# Funcao de log
log() {
echo -e "$1" | tee -a "$LOG_FILE"
}
# Redirect output to log
exec > >(tee -a "$LOG_FILE")
exec 2>&1
log "${BLUE}============================================================${NC}"
log "${BLUE} Avanzato CI/CD - Deploy Automatico${NC}"
log "${BLUE}============================================================${NC}"
log ""
log "Data: $(date)"
log "Branch recebida: $1"
log "Commit: $2"
log ""
# Carregar configuracoes
if [ -f "$CONFIG_FILE" ]; then
source "$CONFIG_FILE"
log "${GREEN}[OK]${NC} Configuracoes carregadas."
else
log "${YELLOW}[AVISO]${NC} Arquivo config.env nao encontrado. Usando defaults."
DEPLOY_PATH="/var/www/avanzato"
STAGING_PATH="/var/www/avanzato-staging"
BACKUP_PATH="/var/ci/backups"
fi
# Detectar branch
while read oldrev newrev refname; do
BRANCH=$(git rev-parse --symbolic --abbrev-ref "$refname")
log "Processando branch: ${YELLOW}${BRANCH}${NC}"
case "$BRANCH" in
main|master)
TARGET="production"
DEPLOY_TARGET="$DEPLOY_PATH"
;;
staging)
TARGET="staging"
DEPLOY_TARGET="$STAGING_PATH"
;;
*)
log "${YELLOW}[IGNORADO]${NC} Branch '${BRANCH}' nao configurada para deploy."
log "Branches validas: main, master, staging"
exit 0
;;
esac
done
log ""
log "${BLUE}------------------------------------------------------------${NC}"
log "Ambiente: ${YELLOW}${TARGET}${NC}"
log "Destino: ${DEPLOY_TARGET}"
log "${BLUE}------------------------------------------------------------${NC}"
log ""
# ==========================================================================
# PASSO 1: Checkout do codigo
# ==========================================================================
log "${BLUE}[1/6]${NC} Checkout do codigo..."
mkdir -p "$DEPLOY_DIR"
git --git-dir="$REPO_DIR" --work-tree="$DEPLOY_DIR" checkout -f "$BRANCH" 2>&1
log "${GREEN}[OK]${NC} Codigo extraido para ${DEPLOY_DIR}"
log ""
# ==========================================================================
# PASSO 2: Instalar dependencias
# ==========================================================================
log "${BLUE}[2/6]${NC} Instalando dependencias..."
cd "$DEPLOY_DIR"
if [ -f "package.json" ]; then
npm ci --silent 2>&1 || {
log "${RED}[ERRO]${NC} Falha ao instalar dependencias."
exit 1
}
log "${GREEN}[OK]${NC} Dependencias instaladas."
else
log "${YELLOW}[AVISO]${NC} package.json nao encontrado. Pulando."
fi
log ""
# ==========================================================================
# PASSO 3: Build
# ==========================================================================
log "${BLUE}[3/6]${NC} Executando build..."
cd "$DEPLOY_DIR"
if [ -f "package.json" ]; then
npm run build 2>&1 || {
log "${RED}[ERRO]${NC} Falha no build."
exit 1
}
# Verificar se a pasta dist foi criada
if [ ! -d "dist" ]; then
log "${RED}[ERRO]${NC} Pasta dist/ nao encontrada apos build."
exit 1
fi
log "${GREEN}[OK]${NC} Build concluido com sucesso."
log "Tamanho: $(du -sh dist/ | cut -f1)"
else
# Se nao tem package.json, usar o proprio diretorio
log "${YELLOW}[AVISO]${NC} Projeto estatico detectado. Usando arquivos diretamente."
fi
log ""
# ==========================================================================
# PASSO 4: Preservar assets
# ==========================================================================
log "${BLUE}[4/6]${NC} Preservando assets customizados..."
cd "$DEPLOY_DIR"
# Se existir pasta custom-assets, preservar
if [ -d "custom-assets" ]; then
log "Assets customizados encontrados. Preservando..."
bash "${DEPLOY_DIR}/scripts/preserve-assets.sh" "${DEPLOY_DIR}/dist" 2>&1 || {
log "${YELLOW}[AVISO]${NC} Erro ao preservar assets (nao critico)."
}
else
log "${YELLOW}[AVISO]${NC} Nenhum asset customizado encontrado."
fi
# Se existem assets preservados no /var/ci/assets, copiar
if [ -d "/var/ci/assets" ] && [ "$(ls -A /var/ci/assets)" ]; then
log "Copiando assets preservados..."
cp -r /var/ci/assets/* "${DEPLOY_DIR}/dist/" 2>/dev/null || true
fi
log "${GREEN}[OK]${NC} Assets processados."
log ""
# ==========================================================================
# PASSO 5: Backup do site atual
# ==========================================================================
log "${BLUE}[5/6]${NC} Criando backup..."
if [ -d "$DEPLOY_TARGET" ] && [ "$(ls -A "$DEPLOY_TARGET" 2>/dev/null)" ]; then
BACKUP_NAME="backup-$(date +%Y%m%d-%H%M%S)"
mkdir -p "$BACKUP_PATH"
# Criar backup (excluindo backups antigos para nao duplicar)
tar -czf "${BACKUP_PATH}/${BACKUP_NAME}.tar.gz" \
-C "$(dirname "$DEPLOY_TARGET")" \
--exclude="backups" \
"$(basename "$DEPLOY_TARGET")" 2>/dev/null || true
# Limpar backups antigos (manter 10)
ls -t "${BACKUP_PATH}"/*.tar.gz 2>/dev/null | tail -n +11 | xargs -r rm -f
log "${GREEN}[OK]${NC} Backup criado: ${BACKUP_NAME}.tar.gz"
else
log "${YELLOW}[AVISO]${NC} Nada para fazer backup (primeiro deploy)."
fi
log ""
# ==========================================================================
# PASSO 6: Deploy
# ==========================================================================
log "${BLUE}[6/6]${NC} Fazendo deploy..."
# Criar diretorio se nao existir
mkdir -p "$DEPLOY_TARGET"
# Limpar conteudo antigo (exceto backups)
find "$DEPLOY_TARGET" -mindepth 1 -not -path "${DEPLOY_TARGET}/backups/*" -not -name "backups" -delete 2>/dev/null || rm -rf "${DEPLOY_TARGET:?}"/*
# Copiar novos arquivos
if [ -d "${DEPLOY_DIR}/dist" ]; then
cp -r "${DEPLOY_DIR}/dist/"* "$DEPLOY_TARGET/"
else
# Se nao tem dist, copiar tudo
cp -r "${DEPLOY_DIR}/"* "$DEPLOY_TARGET/"
fi
# Permissoes
chown -R www-data:www-data "$DEPLOY_TARGET"
chmod -R 755 "$DEPLOY_TARGET"
# Limpar pasta temporaria
rm -rf "$DEPLOY_DIR"
log "${GREEN}[OK]${NC} Deploy concluido!"
log ""
# ==========================================================================
# FINAL
# ==========================================================================
log "${BLUE}============================================================${NC}"
log "${GREEN} Deploy concluido com sucesso!${NC}"
log "${BLUE}============================================================${NC}"
log ""
log "Ambiente: ${TARGET}"
log "Caminho: ${DEPLOY_TARGET}"
log "Backup: ${BACKUP_PATH}"
log "Log: ${LOG_FILE}"
log "Data: $(date)"
log ""
# Atualizar link do ultimo log
ln -sf "$LOG_FILE" "$LAST_LOG"
# Notificar via webhook (opcional)
# curl -X POST "https://hooks.slack.com/services/..." \
# -H 'Content-Type: application/json' \
# -d "{\"text\":\"Deploy ${TARGET} concluido!\"}" 2>/dev/null || true
exit 0
+22
View File
@@ -0,0 +1,22 @@
#!/bin/bash
# ============================================================================
# Git Hook: pre-receive
# ============================================================================
# Executado ANTES do push ser aceito.
# Pode bloquear pushes que nao atendem criterios.
# ============================================================================
set -e
# Rejeitar pushes diretos na main (forcar via PR)
# Descomente se quiser forcar code review:
# while read oldrev newrev refname; do
# BRANCH=$(git rev-parse --symbolic --abbrev-ref "$refname")
# if [ "$BRANCH" = "main" ] || [ "$BRANCH" = "master" ]; then
# echo "Push direto na main bloqueado. Use pull request."
# exit 1
# fi
# done
# Aceitar todos os pushes por padrao
exit 0
+56
View File
@@ -0,0 +1,56 @@
#!/bin/bash
# ============================================================================
# Script de Backup - Avanzato
# ============================================================================
# Uso: bash ci/scripts/backup.sh [ambiente]
#
# Faz backup manual do site.
# ============================================================================
set -e
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
NC='\033[0m'
# Carregar config
CONFIG_FILE="/var/ci/config.env"
if [ -f "$CONFIG_FILE" ]; then
source "$CONFIG_FILE"
fi
DEPLOY_PATH="${DEPLOY_PATH:-/var/www/avanzato}"
BACKUP_PATH="${BACKUP_PATH:-/var/ci/backups}"
AMBIENTE="${1:-production}"
# Determinar diretorio
if [ "$AMBIENTE" = "staging" ]; then
DEPLOY_PATH="${STAGING_PATH:-$DEPLOY_PATH-staging}"
fi
BACKUP_NAME="backup-$(date +%Y%m%d-%H%M%S)"
mkdir -p "$BACKUP_PATH"
if [ ! -d "$DEPLOY_PATH" ] || [ ! "$(ls -A "$DEPLOY_PATH" 2>/dev/null)" ]; then
echo -e "${RED}Nada para fazer backup.${NC}"
exit 1
fi
echo -e "${GREEN}Criando backup...${NC}"
echo "Origem: $DEPLOY_PATH"
echo "Destino: $BACKUP_PATH/${BACKUP_NAME}.tar.gz"
tar -czf "${BACKUP_PATH}/${BACKUP_NAME}.tar.gz" \
-C "$(dirname "$DEPLOY_PATH")" \
--exclude="backups" \
"$(basename "$DEPLOY_PATH")"
echo -e "${GREEN}Backup criado: ${BACKUP_NAME}.tar.gz${NC}"
# Limpar backups antigos
MAX_BACKUPS="${MAX_BACKUPS:-10}"
echo "Limpando backups antigos (manter $MAX_BACKUPS)..."
ls -t "${BACKUP_PATH}"/*.tar.gz 2>/dev/null | tail -n +$((MAX_BACKUPS + 1)) | xargs -r rm -f
echo -e "${GREEN}Concluido!${NC}"
+86
View File
@@ -0,0 +1,86 @@
#!/bin/bash
# ============================================================================
# Script de Rollback - Avanzato
# ============================================================================
# Uso: bash ci/scripts/rollback.sh [numero]
#
# Restaura um backup anterior.
# ============================================================================
set -e
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
BLUE='\033[0;34m'
NC='\033[0m'
CONFIG_FILE="/var/ci/config.env"
if [ -f "$CONFIG_FILE" ]; then
source "$CONFIG_FILE"
fi
DEPLOY_PATH="${DEPLOY_PATH:-/var/www/avanzato}"
BACKUP_PATH="${BACKUP_PATH:-/var/ci/backups}"
AMBIENTE="${1:-production}"
if [ "$AMBIENTE" = "staging" ]; then
DEPLOY_PATH="${STAGING_PATH:-$DEPLOY_PATH-staging}"
fi
echo -e "${BLUE}============================================================${NC}"
echo -e "${BLUE} Avanzato - Rollback${NC}"
echo -e "${BLUE}============================================================${NC}"
echo ""
# Listar backups disponiveis
echo "Backups disponiveis:"
echo ""
BACKUPS=()
i=1
for backup in $(ls -t "${BACKUP_PATH}"/*.tar.gz 2>/dev/null); do
nome=$(basename "$backup")
tamanho=$(du -h "$backup" | cut -f1)
data=$(stat -c %y "$backup" | cut -d'.' -f1)
printf " ${GREEN}%2d.${NC} %s (${tamanho}) %s\n" "$i" "$nome" "$data"
BACKUPS+=("$backup")
i=$((i + 1))
done
if [ ${#BACKUPS[@]} -eq 0 ]; then
echo -e "${RED}Nenhum backup encontrado.${NC}"
exit 1
fi
echo ""
read -p "Digite o numero do backup para restaurar: " NUM
if ! [[ "$NUM" =~ ^[0-9]+$ ]] || [ "$NUM" -lt 1 ] || [ "$NUM" -gt ${#BACKUPS[@]} ]; then
echo -e "${RED}Numero invalido.${NC}"
exit 1
fi
BACKUP="${BACKUPS[$((NUM - 1))]}"
echo ""
echo -e "Selecionado: ${YELLOW}$(basename "$BACKUP")${NC}"
echo "Destino: $DEPLOY_PATH"
read -p "Confirma? (s/N): " CONFIRM
if [ "$CONFIRM" != "s" ] && [ "$CONFIRM" != "S" ]; then
echo "Cancelado."
exit 0
fi
echo ""
echo -e "${YELLOW}Fazendo backup do estado atual antes do rollback...${NC}"
bash "$(dirname "$0")/backup.sh" "$AMBIENTE"
echo ""
echo -e "${YELLOW}Restaurando backup...${NC}"
rm -rf "${DEPLOY_PATH:?}"/*
tar -xzf "$BACKUP" -C "$(dirname "$DEPLOY_PATH")" --strip-components=1
echo -e "${GREEN}Rollback concluido!${NC}"
echo ""
echo "Verifique: ${DEPLOY_PATH}"
+75
View File
@@ -0,0 +1,75 @@
#!/bin/bash
# ============================================================================
# Script de Status - Avanzato
# ============================================================================
# Uso: bash ci/scripts/status.sh
#
# Mostra status do site, ultimos deploys e backups.
# ============================================================================
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
BLUE='\033[0;34m'
NC='\033[0m'
CONFIG_FILE="/var/ci/config.env"
if [ -f "$CONFIG_FILE" ]; then
source "$CONFIG_FILE"
fi
DEPLOY_PATH="${DEPLOY_PATH:-/var/www/avanzato}"
BACKUP_PATH="${BACKUP_PATH:-/var/ci/backups}"
LOG_PATH="${LOG_PATH:-/var/ci/logs}"
echo -e "${BLUE}============================================================${NC}"
echo -e "${BLUE} Avanzato - Status do Ambiente${NC}"
echo -e "${BLUE}============================================================${NC}"
echo ""
# Status do site
echo -e "${GREEN}SITUACAO DO SITE${NC}"
echo " URL: ${SITE_URL:-https://avanzato.com.br}"
echo " Caminho: $DEPLOY_PATH"
echo " Tamanho: $(du -sh "$DEPLOY_PATH" 2>/dev/null | cut -f1 || echo 'N/A')"
echo ""
# Status do Nginx
echo -e "${GREEN}NGINX${NC}"
systemctl is-active nginx 2>/dev/null && \
echo -e " Status: ${GREEN}Ativo${NC}" || \
echo -e " Status: ${RED}Inativo${NC}"
echo ""
# Ultimo deploy
echo -e "${GREEN}ULTIMOS DEPLOYS${NC}"
if [ -f "${LOG_PATH}/last-deploy.log" ]; then
echo " Log: ${LOG_PATH}/last-deploy.log"
echo " Data: $(stat -c %y "${LOG_PATH}/last-deploy.log" | cut -d'.' -f1)"
else
echo -e " ${YELLOW}Nenhum deploy registrado.${NC}"
fi
echo ""
# Backups
echo -e "${GREEN}BACKUPS${NC}"
echo " Caminho: $BACKUP_PATH"
echo " Quantidade: $(ls "${BACKUP_PATH}"/*.tar.gz 2>/dev/null | wc -l)"
echo " Tamanho total: $(du -sh "$BACKUP_PATH" 2>/dev/null | cut -f1 || echo '0')"
echo ""
# Ultimos logs
echo -e "${GREEN}ULTIMOS LOGS${NC}"
ls -t "${LOG_PATH}"/*.log 2>/dev/null | head -5 | while read log; do
echo " $(basename "$log") - $(stat -c %y "$log" | cut -d'.' -f1)"
done
echo ""
# Assets preservados
echo -e "${GREEN}ASSETS PRESERVADOS${NC}"
echo " Caminho: ${ASSETS_PATH:-/var/ci/assets}"
echo " Conteudo:"
ls -la "${ASSETS_PATH:-/var/ci/assets}" 2>/dev/null || echo " (vazio)"
echo ""
echo -e "${BLUE}============================================================${NC}"
+22
View File
@@ -0,0 +1,22 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "new-york",
"rsc": false,
"tsx": true,
"tailwind": {
"config": "postcss.config.js",
"css": "src/index.css",
"baseColor": "slate",
"cssVariables": true,
"prefix": ""
},
"iconLibrary": "lucide",
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
},
"registries": {}
}
+129
View File
@@ -0,0 +1,129 @@
# =============================================================================
# AVANZATO TECNOLOGIA - .htaccess para React SPA
# =============================================================================
# -----------------------------------------------------------------------------
# 1. REDIRECIONAMENTO PARA index.html (SPA - Single Page Application)
# -----------------------------------------------------------------------------
<IfModule mod_rewrite.c>
RewriteEngine On
# Não reescrever arquivos/diretórios reais
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
# Redirecionar tudo para index.html
RewriteRule ^ index.html [L]
</IfModule>
# -----------------------------------------------------------------------------
# 2. COMPRESSÃO GZIP (melhora performance)
# -----------------------------------------------------------------------------
<IfModule mod_deflate.c>
SetOutputFilter DEFLATE
SetEnvIfNoCase Request_URI \
\.(?:gif|jpe?g|png|webp|ico|svgz)$ no-gzip dont-vary
SetEnvIfNoCase Request_URI \
\.(?:exe|t?gz|zip|bz2|sit|rar)$ no-gzip dont-vary
AddOutputFilterByType DEFLATE text/html
AddOutputFilterByType DEFLATE text/css
AddOutputFilterByType DEFLATE text/javascript
AddOutputFilterByType DEFLATE text/plain
AddOutputFilterByType DEFLATE text/xml
AddOutputFilterByType DEFLATE application/javascript
AddOutputFilterByType DEFLATE application/json
AddOutputFilterByType DEFLATE application/xml
AddOutputFilterByType DEFLATE application/xhtml+xml
AddOutputFilterByType DEFLATE application/rss+xml
AddOutputFilterByType DEFLATE application/atom+xml
AddOutputFilterByType DEFLATE font/truetype
AddOutputFilterByType DEFLATE font/opentype
AddOutputFilterByType DEFLATE application/vnd.ms-fontobject
AddOutputFilterByType DEFLATE image/svg+xml
</IfModule>
# -----------------------------------------------------------------------------
# 3. CACHE DE BROWSER (performance)
# -----------------------------------------------------------------------------
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType text/html "access plus 0 seconds"
<FilesMatch "\.(js|css)$">
ExpiresDefault "access plus 1 year"
</FilesMatch>
ExpiresByType image/gif "access plus 6 months"
ExpiresByType image/png "access plus 6 months"
ExpiresByType image/jpeg "access plus 6 months"
ExpiresByType image/webp "access plus 6 months"
ExpiresByType image/svg+xml "access plus 6 months"
ExpiresByType image/x-icon "access plus 1 year"
ExpiresByType font/ttf "access plus 1 year"
ExpiresByType font/otf "access plus 1 year"
ExpiresByType font/woff "access plus 1 year"
ExpiresByType font/woff2 "access plus 1 year"
ExpiresByType application/vnd.ms-fontobject "access plus 1 year"
ExpiresByType application/json "access plus 1 hour"
ExpiresByType application/xml "access plus 1 hour"
</IfModule>
# -----------------------------------------------------------------------------
# 4. HEADERS DE SEGURANÇA
# -----------------------------------------------------------------------------
<IfModule mod_headers.c>
Header always set X-Frame-Options "SAMEORIGIN"
Header always set X-Content-Type-Options "nosniff"
Header always set X-XSS-Protection "1; mode=block"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
</IfModule>
# -----------------------------------------------------------------------------
# 5. TIPOS MIME CORRETOS
# -----------------------------------------------------------------------------
<IfModule mod_mime.c>
AddType application/javascript .js
AddType application/javascript .mjs
AddType text/css .css
AddType image/svg+xml .svg
AddType font/woff2 .woff2
AddType font/woff .woff
AddType font/ttf .ttf
AddType font/otf .otf
AddType application/json .json
AddType application/manifest+json .webmanifest
</IfModule>
# -----------------------------------------------------------------------------
# 6. PROTEÇÃO DE ARQUIVOS SENSÍVEIS
# -----------------------------------------------------------------------------
<IfModule mod_rewrite.c>
RewriteRule (^|/)\. - [F]
<FilesMatch "^\.">
Order allow,deny
Deny from all
</FilesMatch>
<FilesMatch "\.(git|gitignore|env|log|sql|sh|bash|zsh)$">
Order allow,deny
Deny from all
</FilesMatch>
</IfModule>
# -----------------------------------------------------------------------------
# 7. CHARSET PADRÃO
# -----------------------------------------------------------------------------
AddDefaultCharset UTF-8
# -----------------------------------------------------------------------------
# 8. DESATIVAR LISTAGEM DE DIRETÓRIOS
# -----------------------------------------------------------------------------
Options -Indexes
+244
View File
@@ -0,0 +1,244 @@
# 📁 Guia do .htaccess - Avanzato Tecnologia
## O que é o .htaccess?
O `.htaccess` é um arquivo de configuração do Apache (servidor web) que controla:
- ✅ Redirecionamentos
- ✅ Compressão de arquivos
- ✅ Cache do navegador
- ✅ Segurança
- ✅ Performance
---
## 📂 Onde colocar o arquivo?
```
/public_html/ ← Raiz do seu site
├── .htaccess ← COLOQUE AQUI
├── index.html
├── assets/
└── ...
```
**IMPORTANTE:** O arquivo deve estar na **mesma pasta** do `index.html`
---
## 📋 Arquivos disponíveis
| Arquivo | Quando usar |
|---------|-------------|
| `.htaccess` | Versão **completa** (recomendada) |
| `.htaccess-simples` | Se o servidor der erro 500 |
---
## 🚀 Como instalar
### Método 1: FTP/cPanel
1. Acesse seu servidor via FTP ou cPanel
2. Vá para a pasta `public_html/` (ou `www/`)
3. Faça upload do arquivo `.htaccess`
4. Pronto!
### Método 2: SSH
```bash
# Conecte ao servidor
ssh usuario@seudominio.com.br
# Vá para a pasta do site
cd /var/www/html
# ou
cd /home/usuario/public_html
# Crie o arquivo
nano .htaccess
# Cole o conteúdo do .htaccess
# Salve: Ctrl+O, Enter, Ctrl+X
```
---
## ⚙️ Configurações incluídas
### 1. **SPA (Single Page Application)**
```apache
RewriteRule ^ index.html [L]
```
→ Todas as URLs vão para `index.html` (necessário para React)
### 2. **Compressão GZIP**
```apache
AddOutputFilterByType DEFLATE text/css application/javascript
```
→ Reduz tamanho dos arquivos em ~70%
### 3. **Cache do Navegador**
```apache
ExpiresByType application/javascript "access plus 1 year"
```
→ Arquivos JS/CSS ficam no cache por 1 ano
### 4. **Segurança**
- Proteção contra clickjacking
- Proteção contra MIME sniffing
- Bloqueio de arquivos ocultos (.git, .env)
---
## 🔧 Configurações opcionais
### Ativar HTTPS (quando tiver SSL)
Descomente estas linhas no `.htaccess`:
```apache
# Forçar HTTPS
<IfModule mod_rewrite.c>
RewriteCond %{HTTPS} off
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
</IfModule>
# HSTS (segurança extra)
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
```
### Forçar SEM www
```apache
<IfModule mod_rewrite.c>
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
RewriteRule ^ https://%1%{REQUEST_URI} [R=301,L]
</IfModule>
```
### Forçar COM www
```apache
<IfModule mod_rewrite.c>
RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteRule ^ https://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
</IfModule>
```
---
## ❌ Problemas comuns
### Erro 500 (Internal Server Error)
**Causa:** Seu servidor não tem todos os módulos habilitados
**Solução:** Use o `.htaccess-simples`
```bash
# Renomeie o arquivo
mv .htaccess .htaccess-backup
mv .htaccess-simples .htaccess
```
### Página em branco
**Causa:** O mod_rewrite pode estar desativado
**Solução:** Contate seu provedor de hospedagem para ativar:
- `mod_rewrite`
- `mod_deflate`
- `mod_expires`
- `mod_headers`
### Rotas não funcionam (404 em /sobre, /contato)
**Causa:** O redirecionamento para index.html não está funcionando
**Solução:** Verifique se o arquivo .htaccess está na pasta correta
---
## 📊 Testar se está funcionando
### 1. Testar compressão GZIP
```bash
curl -H "Accept-Encoding: gzip" -I https://seudominio.com.br
```
Deve mostrar: `Content-Encoding: gzip`
### 2. Testar headers de segurança
```bash
curl -I https://seudominio.com.br
```
Deve mostrar:
- `X-Frame-Options: SAMEORIGIN`
- `X-Content-Type-Options: nosniff`
### 3. Testar cache
```bash
curl -I https://seudominio.com.br/assets/index-xxx.js
```
Deve mostrar: `Cache-Control: max-age=31536000`
---
## 🌐 Servidores específicos
### cPanel (Hostgator, Locaweb, etc)
1. Acesse: **Arquivos****Gerenciador de Arquivos**
2. Vá para `public_html/`
3. Clique em **Upload** e envie o `.htaccess`
### AWS S3 + CloudFront
O S3 não usa .htaccess. Configure:
- **Error Document:** `index.html`
- CloudFront: **Custom Error Response** 404 → 200 para `index.html`
### Vercel/Netlify
Não precisa de .htaccess. Use `vercel.json` ou `_redirects`:
```
/* /index.html 200
```
### Nginx (servidor diferente)
Se usar Nginx, crie um arquivo de configuração:
```nginx
location / {
try_files $uri $uri/ /index.html;
}
gzip on;
gzip_types text/css application/javascript;
```
---
## 📞 Suporte
Se tiver problemas, verifique:
1. O arquivo está na pasta correta?
2. O nome do arquivo é exatamente `.htaccess` (com ponto)?
3. As permissões do arquivo são 644?
```bash
# Corrigir permissões
chmod 644 .htaccess
```
---
## ✅ Checklist de instalação
- [ ] Arquivo `.htaccess` na pasta `public_html/`
- [ ] Site carrega normalmente (sem erro 500)
- [ ] Navegação entre páginas funciona
- [ ] Atualizar página (F5) não dá erro 404
- [ ] GZIP está ativo (teste com GTmetrix)
- [ ] Cache está funcionando
---
**Última atualização:** 2024
**Versão:** 1.0
+2
View File
@@ -0,0 +1,2 @@
# Netlify redirects for SPA
/* /index.html 200
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

+9
View File
@@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<rect width="100" height="100" rx="20" fill="#0e0e0e"/>
<path d="M50 15 L75 75 H60 L55 60 H45 L40 75 H25 L50 15 Z M48 48 H52 L50 40 L48 48 Z"
fill="#cbf400"
stroke="#cbf400"
stroke-width="3"
stroke-linejoin="round"
stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 358 B

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

+263
View File
@@ -0,0 +1,263 @@
<!doctype html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<!-- DETECAO DE AMBIENTE - Inline (executa antes de tudo) -->
<script>
(function() {
var h = location.hostname;
var IS_PROD = h === 'avanzato.com.br' || h === 'www.avanzato.com.br';
// Torna disponivel para outros scripts
window.__IS_PROD__ = IS_PROD;
// Se NAO for producao, desativa tracking globalmente
if (!IS_PROD) {
window.__AVANZATO_ENV__ = h === 'hml.avanzato.com.br' ? 'homologation' : 'development';
// Bloqueia gtag
window.dataLayer = { push: function(){} };
window.gtag = function(){};
// Bloqueia fbq (Facebook Pixel)
window.fbq = function(){};
// Bloqueia Mautic
window.MauticSDKLoaded = true;
console.log('[Avanzato] Ambiente: ' + window.__AVANZATO_ENV__ + ' | Tracking bloqueado');
} else {
window.__AVANZATO_ENV__ = 'production';
}
})();
</script>
<!-- Google tag (gtag.js) - Ads + Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=AW-16714813566"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
// LGPD/GDPR Consent Mode v2
// analytics_storage = 'denied' → GA4 coleta dados ANONIMIZADOS (sem cookies)
// Isso permite ver trafego SEM precisar de consentimento explicito
gtag('consent', 'default', {
ad_storage: 'denied',
analytics_storage: 'denied',
ad_user_data: 'denied',
ad_personalization: 'denied',
functionality_storage: 'granted', // Permite funcionalidade basica
security_storage: 'granted', // Permite seguranca
wait_for_update: 500
});
// IMPORTANTE: Ativar Consent Mode no painel do GA4
// GA4 → Admin → Account Settings → Consent Settings → Enable
gtag('js', new Date());
gtag('config', 'AW-16714813566');
gtag('config', 'G-LMHX8RJ3LH', { // GA4 - Avanzato
'send_page_view': true,
'cookie_expires': 63072000, // 2 anos
'cookie_update': true
});
</script>
<!-- Meta Pixel (Facebook) - ID: 1223271852059785 -->
<script>
!function(f,b,e,v,n,t,s)
{if(f.fbq)return;n=f.fbq=function(){n.callMethod?
n.callMethod.apply(n,arguments):n.queue.push(arguments)};
if(!f._fbq)f._fbq=n;n.push=n;n.loaded=!0;n.version='2.0';
n.queue=[];t=b.createElement(e);t.async=!0;
t.src=v;s=b.getElementsByTagName(e)[0];
s.parentNode.insertBefore(t,s)}(window, document,'script',
'https://connect.facebook.net/en_US/fbevents.js');
fbq('init', '1223271852059785');
fbq('track', 'PageView');
</script>
<!-- Mautic Tracking -->
<script>
(function(w,d,t,u,n,a,m){
w['MauticTrackingObject']=n;
w[n]=w[n]||function(){(w[n].q=w[n].q||[]).push(arguments)},a=d.createElement(t),
m=d.getElementsByTagName(t)[0];a.async=1;a.src=u;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://mkt.avanzato.com.br/mtc.js','mt');
mt('send', 'pageview');
</script>
<!-- FIM Mautic Tracking -->
<!-- Preconnect to Google Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Urbanist:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<!-- Primary Meta Tags -->
<title>Avanzato Tecnologia | TI Gerenciada, Cloud e Seguranca em Guarulhos/SP</title>
<meta name="title" content="Avanzato Tecnologia | TI Gerenciada, Cloud e Seguranca em Guarulhos/SP" />
<meta name="description" content="Solucoes em TI Gerenciada, Cloud Computing e Seguranca da Informacao. +23 anos de experiencia, +500 clientes atendidos. Suporte 24/7 em Guarulhos e regiao." />
<meta name="app-version" content="1.0.0" />
<meta name="keywords" content="TI Gerenciada, Cloud Computing, Seguranca da Informacao, Suporte Tecnico, Tecnologia da Informacao, Guarulhos, Sao Paulo, Infraestrutura de TI, Backup, Firewall, Consultoria TI" />
<meta name="author" content="Avanzato Tecnologia" />
<meta name="robots" content="index, follow" id="meta-robots" />
<meta name="googlebot" content="index, follow" id="meta-googlebot" />
<!-- BLOQUEAR INDEXACAO EM HOMOLOGACAO -->
<script>
(function() {
// Usa IS_PROD ja definido no inicio do documento
if (!window.__IS_PROD__) {
// Homologacao/Desenvolvimento: bloquear indexacao
document.getElementById('meta-robots').setAttribute('content', 'noindex, nofollow');
document.getElementById('meta-googlebot').setAttribute('content', 'noindex, nofollow');
// Adicionar tambem X-Robots-Tag via meta http-equiv
var meta = document.createElement('meta');
meta.httpEquiv = 'X-Robots-Tag';
meta.content = 'noindex, nofollow';
document.head.appendChild(meta);
console.log('%c[SEO] ' + (window.__AVANZATO_ENV__ || 'nao-producao') + ' - indexacao BLOQUEADA', 'background:#dc2626;color:#fff;font-weight:bold;padding:2px 6px;border-radius:3px');
} else {
console.log('%c[SEO] Producao - indexacao PERMITIDA (index, follow)', 'background:#16a34a;color:#fff;font-weight:bold;padding:2px 6px;border-radius:3px');
}
})();
</script>
<!-- Canonical URL -->
<link rel="canonical" href="https://avanzato.com.br" />
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website" />
<meta property="og:url" content="https://avanzato.com.br" />
<meta property="og:title" content="Avanzato Tecnologia | TI Gerenciada, Cloud e Seguranca" />
<meta property="og:description" content="Solucoes em TI Gerenciada, Cloud Computing e Seguranca da Informacao. +23 anos de experiencia, suporte 24/7." />
<meta property="og:image" content="https://avanzato.com.br/og-image.jpg" />
<meta property="og:locale" content="pt_BR" />
<meta property="og:site_name" content="Avanzato Tecnologia" />
<!-- Twitter -->
<meta property="twitter:card" content="summary_large_image" />
<meta property="twitter:url" content="https://avanzato.com.br" />
<meta property="twitter:title" content="Avanzato Tecnologia | TI Gerenciada, Cloud e Seguranca" />
<meta property="twitter:description" content="Solucoes em TI Gerenciada, Cloud Computing e Seguranca da Informacao. +23 anos de experiencia, suporte 24/7." />
<meta property="twitter:image" content="https://avanzato.com.br/og-image.jpg" />
<!-- Favicon -->
<link rel="icon" type="image/png" href="https://avanzato.com.br/favicon.png" />
<link rel="shortcut icon" type="image/png" href="https://avanzato.com.br/favicon.png" />
<link rel="manifest" href="/site.webmanifest" />
<!-- Theme Color -->
<meta name="theme-color" content="#cbf400" />
<meta name="msapplication-TileColor" content="#0e0e0e" />
<!-- Structured Data / Schema.org -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Organization",
"name": "Avanzato Tecnologia",
"alternateName": "Avanzato Tecnologia e Informatica",
"url": "https://avanzato.com.br",
"logo": "https://avanzato.com.br/logo.svg",
"description": "Solucoes em TI Gerenciada, Cloud Computing e Seguranca da Informacao. +23 anos de experiencia.",
"foundingDate": "2001",
"address": {
"@type": "PostalAddress",
"streetAddress": "Av. Dr. Carlos de Campos, 43 - Sl 05/06",
"addressLocality": "Guarulhos",
"addressRegion": "SP",
"postalCode": "07040-000",
"addressCountry": "BR"
},
"geo": {
"@type": "GeoCoordinates",
"latitude": "-23.4686",
"longitude": "-46.5326"
},
"contactPoint": {
"@type": "ContactPoint",
"telephone": "+55-11-4810-1704",
"contactType": "customer service",
"availableLanguage": ["Portuguese"],
"areaServed": "BR"
},
"sameAs": [
"https://www.facebook.com/avanzatotec",
"https://www.linkedin.com/company/avanzato-tecnologia-e-informatica/",
"https://www.instagram.com/avanzato.tecnologia/",
"https://www.youtube.com/@AvanzatoTecnologia"
],
"openingHoursSpecification": {
"@type": "OpeningHoursSpecification",
"dayOfWeek": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"],
"opens": "08:00",
"closes": "18:00"
}
}
</script>
<!-- Local Business Schema -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "LocalBusiness",
"name": "Avanzato Tecnologia",
"image": "https://avanzato.com.br/logo.svg",
"@id": "https://avanzato.com.br",
"url": "https://avanzato.com.br",
"telephone": "+55-11-4810-1704",
"priceRange": "$$",
"address": {
"@type": "PostalAddress",
"streetAddress": "Av. Dr. Carlos de Campos, 43 - Sl 05/06, Parque Renato Maia",
"addressLocality": "Guarulhos",
"addressRegion": "SP",
"postalCode": "07040-000",
"addressCountry": "BR"
},
"geo": {
"@type": "GeoCoordinates",
"latitude": -23.4686,
"longitude": -46.5326
},
"openingHoursSpecification": [
{
"@type": "OpeningHoursSpecification",
"dayOfWeek": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"],
"opens": "08:00",
"closes": "18:00"
}
],
"department": {
"@type": "ComputerStore",
"name": "Departamento de TI",
"openingHours": "24/7"
}
}
</script>
<script type="module" crossorigin src="/assets/index-DLMsdH5y.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-8ILJAHKZ.css">
</head>
<body>
<div id="root"></div>
<!-- Facebook Pixel noscript (moved from head to body to fix parse5 warning) -->
<noscript>
<img height="1" width="1" style="display:none"
src="https://www.facebook.com/tr?id=1223271852059785&ev=PageView&noscript=1"/>
</noscript>
<!-- Noscript content for SEO -->
<noscript>
<div style="padding: 20px; text-align: center; font-family: Urbanist, Arial, sans-serif; background: #0e0e0e; color: white; min-height: 100vh;">
<h1 style="color: #cbf400;">Avanzato Tecnologia</h1>
<p>Para visualizar nosso site completo, por favor habilite o JavaScript no seu navegador.</p>
<p>Entre em contato: (11) 4810-1704 | contato@avanzato.com.br</p>
</div>
</noscript>
</body>
</html>
+19
View File
@@ -0,0 +1,19 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 60">
<!-- Symbol -->
<path d="M30 5 L50 50 H40 L36 38 H24 L20 50 H10 L30 5 Z M28 32 H32 L30 25 L28 32 Z"
fill="#cbf400"
stroke="#cbf400"
stroke-width="2.5"
stroke-linejoin="round"
stroke-linecap="round"/>
<!-- Text: avanzato. -->
<text x="65" y="38"
font-family="Urbanist, Arial, sans-serif"
font-size="28"
font-weight="700"
fill="#ffffff">avanzato</text>
<!-- Dot -->
<circle cx="188" cy="38" r="3" fill="#cbf400"/>
</svg>

After

Width:  |  Height:  |  Size: 579 B

+168
View File
@@ -0,0 +1,168 @@
# =============================================================================
# AVANZATO TECNOLOGIA - Configuração Nginx
# =============================================================================
# Coloque este arquivo em: /etc/nginx/sites-available/avanzato
# Depois crie link: sudo ln -s /etc/nginx/sites-available/avanzato /etc/nginx/sites-enabled/
# =============================================================================
server {
# Portas HTTP e HTTPS
listen 80;
# listen 443 ssl http2; # Descomente quando tiver SSL
# Domínio
server_name avanzato.com.br www.avanzato.com.br;
# Pasta raiz do site
root /var/www/avanzato/dist;
index index.html;
# Charset
charset utf-8;
# -------------------------------------------------------------------------
# LOGS
# -------------------------------------------------------------------------
access_log /var/log/nginx/avanzato-access.log;
error_log /var/log/nginx/avanzato-error.log;
# -------------------------------------------------------------------------
# GZIP (compressão)
# -------------------------------------------------------------------------
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_types
text/plain
text/css
text/xml
text/javascript
application/json
application/javascript
application/xml+rss
application/atom+xml
image/svg+xml;
# -------------------------------------------------------------------------
# CACHE DE BROWSER
# -------------------------------------------------------------------------
# Assets com hash (JS/CSS) - cache longo
location ~* \.(js|css)$ {
expires 1y;
add_header Cache-Control "public, immutable";
add_header Vary "Accept-Encoding";
}
# Imagens - cache médio
location ~* \.(png|jpg|jpeg|gif|ico|svg|webp)$ {
expires 6M;
add_header Cache-Control "public";
add_header Vary "Accept-Encoding";
}
# Fontes - cache longo
location ~* \.(woff|woff2|ttf|otf|eot)$ {
expires 1y;
add_header Cache-Control "public";
add_header Vary "Accept-Encoding";
}
# HTML - sem cache
location ~* \.html$ {
expires -1;
add_header Cache-Control "no-store, no-cache, must-revalidate";
}
# -------------------------------------------------------------------------
# SEGURANÇA - HEADERS
# -------------------------------------------------------------------------
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# Content Security Policy (ajuste conforme necessário)
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://fonts.googleapis.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: https:; connect-src 'self'; frame-src https://api.whatsapp.com;" always;
# HSTS (descomente quando tiver SSL)
# add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
# -------------------------------------------------------------------------
# REGRAS DE SEGURANÇA
# -------------------------------------------------------------------------
# Bloquear arquivos ocultos
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
# Bloquear arquivos de backup/sensíveis
location ~* \.(git|gitignore|env|log|sql|sh|bash|zsh|bak|backup|swp|tmp)$ {
deny all;
access_log off;
log_not_found off;
}
# Proteger arquivos de configuração
location ~* \.(htaccess|htpasswd|ini|conf)$ {
deny all;
access_log off;
log_not_found off;
}
# -------------------------------------------------------------------------
# SPA - Single Page Application (React)
# -------------------------------------------------------------------------
# Tentar arquivo estático, senão vai para index.html
location / {
try_files $uri $uri/ /index.html;
}
# -------------------------------------------------------------------------
# REDIRECIONAMENTOS
# -------------------------------------------------------------------------
# Forçar HTTPS (descomente quando tiver SSL)
# if ($scheme != "https") {
# return 301 https://$host$request_uri;
# }
# Remover www (opcional)
# if ($host ~* ^www\.(.*)$) {
# return 301 https://$1$request_uri;
# }
# -------------------------------------------------------------------------
# PÁGINAS DE ERRO PERSONALIZADAS
# -------------------------------------------------------------------------
error_page 404 /index.html;
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /var/www/avanzato/dist;
internal;
}
# -------------------------------------------------------------------------
# LIMITE DE TAMANHO DE UPLOAD
# -------------------------------------------------------------------------
client_max_body_size 10M;
# -------------------------------------------------------------------------
# TIMEOUTS
# -------------------------------------------------------------------------
client_body_timeout 12;
client_header_timeout 12;
keepalive_timeout 15;
send_timeout 10;
}
# =============================================================================
# REDIRECIONAMENTO WWW (opcional)
# =============================================================================
# Se quiser forçar sem www:
# server {
# listen 80;
# server_name www.avanzato.com.br;
# return 301 $scheme://avanzato.com.br$request_uri;
# }
+11
View File
@@ -0,0 +1,11 @@
# =============================================================================
# ROBOTS.TXT - VERSAO PRODUCAO (avanzato.com.br)
# =============================================================================
# Substituir robots.txt por este arquivo ao fazer deploy em producao
# =============================================================================
User-agent: *
Allow: /
# Sitemap
Sitemap: https://avanzato.com.br/sitemap.xml
+13
View File
@@ -0,0 +1,13 @@
# ============================================================================
# AVANZATO TECNOLOGIA - ROBOTS.TXT (GENERICO)
# ============================================================================
# O controle real de indexacao e feito pelo <meta name="robots"> no HTML:
# - Producao (avanzato.com.br): index, follow
# - Homologacao (hml.avanzato.com.br): noindex, nofollow
# - Desenvolvimento (localhost): noindex, nofollow
# ============================================================================
User-agent: *
Allow: /
Sitemap: https://avanzato.com.br/sitemap.xml
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

+21
View File
@@ -0,0 +1,21 @@
{
"name": "Avanzato Tecnologia",
"short_name": "Avanzato",
"description": "Soluções em TI Gerenciada, Cloud Computing e Segurança da Informação",
"start_url": "/",
"display": "standalone",
"background_color": "#0e0e0e",
"theme_color": "#cbf400",
"orientation": "portrait-primary",
"icons": [
{
"src": "/favicon.svg",
"sizes": "any",
"type": "image/svg+xml",
"purpose": "any maskable"
}
],
"categories": ["business", "technology"],
"lang": "pt-BR",
"dir": "ltr"
}
+284
View File
@@ -0,0 +1,284 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://avanzato.com.br/</loc>
<changefreq>weekly</changefreq>
<priority>1.0</priority>
</url>
<url>
<loc>https://avanzato.com.br/sobre</loc>
<changefreq>monthly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://avanzato.com.br/servicos/ti-gerenciada</loc>
<changefreq>monthly</changefreq>
<priority>0.9</priority>
</url>
<url>
<loc>https://avanzato.com.br/servicos/cloud-computing</loc>
<changefreq>monthly</changefreq>
<priority>0.9</priority>
</url>
<url>
<loc>https://avanzato.com.br/servicos/seguranca</loc>
<changefreq>monthly</changefreq>
<priority>0.9</priority>
</url>
<url>
<loc>https://avanzato.com.br/servicos/suporte</loc>
<changefreq>monthly</changefreq>
<priority>0.9</priority>
</url>
<url>
<loc>https://avanzato.com.br/servicos/redes</loc>
<changefreq>monthly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://avanzato.com.br/servicos/pabx</loc>
<changefreq>monthly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://avanzato.com.br/servicos/backup</loc>
<changefreq>monthly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://avanzato.com.br/servicos/servidores</loc>
<changefreq>monthly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://avanzato.com.br/servicos/consultoria</loc>
<changefreq>monthly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://avanzato.com.br/servicos/noc</loc>
<changefreq>monthly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://avanzato.com.br/tecnologia-para-condominios</loc>
<changefreq>weekly</changefreq>
<priority>1.0</priority>
</url>
<url>
<loc>https://avanzato.com.br/cases</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://avanzato.com.br/blog</loc>
<changefreq>daily</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://avanzato.com.br/contato</loc>
<changefreq>monthly</changefreq>
<priority>0.9</priority>
</url>
<url>
<loc>https://avanzato.com.br/avaliacao</loc>
<changefreq>monthly</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://avanzato.com.br/privacidade</loc>
<changefreq>monthly</changefreq>
<priority>0.3</priority>
</url>
<url>
<loc>https://avanzato.com.br/termos</loc>
<changefreq>monthly</changefreq>
<priority>0.3</priority>
</url>
<url>
<loc>https://avanzato.com.br/tecnologias</loc>
<changefreq>monthly</changefreq>
<priority>0.6</priority>
</url>
<url>
<loc>https://avanzato.com.br/suporte-windows-server</loc>
<changefreq>monthly</changefreq>
<priority>0.6</priority>
</url>
<url>
<loc>https://avanzato.com.br/suporte-linux-empresas</loc>
<changefreq>monthly</changefreq>
<priority>0.6</priority>
</url>
<url>
<loc>https://avanzato.com.br/suporte-pfsense</loc>
<changefreq>monthly</changefreq>
<priority>0.6</priority>
</url>
<url>
<loc>https://avanzato.com.br/suporte-proxmox</loc>
<changefreq>monthly</changefreq>
<priority>0.6</priority>
</url>
<url>
<loc>https://avanzato.com.br/suporte-vmware</loc>
<changefreq>monthly</changefreq>
<priority>0.6</priority>
</url>
<url>
<loc>https://avanzato.com.br/suporte-sharepoint</loc>
<changefreq>monthly</changefreq>
<priority>0.6</priority>
</url>
<url>
<loc>https://avanzato.com.br/suporte-redes-cisco</loc>
<changefreq>monthly</changefreq>
<priority>0.6</priority>
</url>
<url>
<loc>https://avanzato.com.br/ti-para-contabilidades</loc>
<changefreq>weekly</changefreq>
<priority>0.9</priority>
</url>
<url>
<loc>https://avanzato.com.br/ti-para-advocacias</loc>
<changefreq>weekly</changefreq>
<priority>0.9</priority>
</url>
<url>
<loc>https://avanzato.com.br/monitoramento-empresarial</loc>
<changefreq>weekly</changefreq>
<priority>0.9</priority>
</url>
<url>
<loc>https://avanzato.com.br/firewall-pfsense-empresarial</loc>
<changefreq>weekly</changefreq>
<priority>0.9</priority>
</url>
<url>
<loc>https://avanzato.com.br/backup-corporativo</loc>
<changefreq>weekly</changefreq>
<priority>0.9</priority>
</url>
<url>
<loc>https://avanzato.com.br/voip-empresarial</loc>
<changefreq>weekly</changefreq>
<priority>0.9</priority>
</url>
<!-- SEO Local - Onde Atuamos -->
<url>
<loc>https://avanzato.com.br/onde-atuamos</loc>
<changefreq>weekly</changefreq>
<priority>0.9</priority>
</url>
<url>
<loc>https://avanzato.com.br/suporte-ti-guarulhos</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://avanzato.com.br/suporte-ti-mogi-das-cruzes</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://avanzato.com.br/suporte-ti-ipiranga</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://avanzato.com.br/suporte-ti-vila-formosa</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://avanzato.com.br/suporte-ti-cerqueira-cesar</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://avanzato.com.br/suporte-ti-santana</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://avanzato.com.br/suporte-ti-bras</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://avanzato.com.br/suporte-ti-ribeirao-preto</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<!-- Nichos adicionais -->
<url>
<loc>https://avanzato.com.br/suporte-ti-industria</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://avanzato.com.br/suporte-ti-portaria-remota</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://avanzato.com.br/suporte-ti-clinicas</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://avanzato.com.br/suporte-ti-imobiliarias</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://avanzato.com.br/suporte-ti-recrutamento-selecao</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<!-- Avanzato Tools -->
<url>
<loc>https://avanzato.com.br/ferramentas</loc>
<changefreq>weekly</changefreq>
<priority>0.9</priority>
</url>
<url>
<loc>https://avanzato.com.br/ferramentas/spf-checker</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://avanzato.com.br/ferramentas/dkim-checker</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://avanzato.com.br/ferramentas/dmarc-checker</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://avanzato.com.br/ferramentas/mx-lookup</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://avanzato.com.br/ferramentas/ssl-checker</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://avanzato.com.br/ferramentas/email-security-score</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://avanzato.com.br/ferramentas/microsoft-365-analyzer</loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
</urlset>
+5
View File
@@ -0,0 +1,5 @@
{
"rewrites": [
{ "source": "/(.*)", "destination": "/index.html" }
]
}
+23
View File
@@ -0,0 +1,23 @@
import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import tseslint from 'typescript-eslint'
import { defineConfig, globalIgnores } from 'eslint/config'
export default defineConfig([
globalIgnores(['dist']),
{
files: ['**/*.{ts,tsx}'],
extends: [
js.configs.recommended,
tseslint.configs.recommended,
reactHooks.configs.flat.recommended,
reactRefresh.configs.vite,
],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
},
},
])
+262
View File
@@ -0,0 +1,262 @@
<!doctype html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<!-- DETECAO DE AMBIENTE - Inline (executa antes de tudo) -->
<script>
(function() {
var h = location.hostname;
var IS_PROD = h === 'avanzato.com.br' || h === 'www.avanzato.com.br';
// Torna disponivel para outros scripts
window.__IS_PROD__ = IS_PROD;
// Se NAO for producao, desativa tracking globalmente
if (!IS_PROD) {
window.__AVANZATO_ENV__ = h === 'hml.avanzato.com.br' ? 'homologation' : 'development';
// Bloqueia gtag
window.dataLayer = { push: function(){} };
window.gtag = function(){};
// Bloqueia fbq (Facebook Pixel)
window.fbq = function(){};
// Bloqueia Mautic
window.MauticSDKLoaded = true;
console.log('[Avanzato] Ambiente: ' + window.__AVANZATO_ENV__ + ' | Tracking bloqueado');
} else {
window.__AVANZATO_ENV__ = 'production';
}
})();
</script>
<!-- Google tag (gtag.js) - Ads + Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=AW-16714813566"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
// LGPD/GDPR Consent Mode v2
// analytics_storage = 'denied' → GA4 coleta dados ANONIMIZADOS (sem cookies)
// Isso permite ver trafego SEM precisar de consentimento explicito
gtag('consent', 'default', {
ad_storage: 'denied',
analytics_storage: 'denied',
ad_user_data: 'denied',
ad_personalization: 'denied',
functionality_storage: 'granted', // Permite funcionalidade basica
security_storage: 'granted', // Permite seguranca
wait_for_update: 500
});
// IMPORTANTE: Ativar Consent Mode no painel do GA4
// GA4 → Admin → Account Settings → Consent Settings → Enable
gtag('js', new Date());
gtag('config', 'AW-16714813566');
gtag('config', 'G-LMHX8RJ3LH', { // GA4 - Avanzato
'send_page_view': true,
'cookie_expires': 63072000, // 2 anos
'cookie_update': true
});
</script>
<!-- Meta Pixel (Facebook) - ID: 1223271852059785 -->
<script>
!function(f,b,e,v,n,t,s)
{if(f.fbq)return;n=f.fbq=function(){n.callMethod?
n.callMethod.apply(n,arguments):n.queue.push(arguments)};
if(!f._fbq)f._fbq=n;n.push=n;n.loaded=!0;n.version='2.0';
n.queue=[];t=b.createElement(e);t.async=!0;
t.src=v;s=b.getElementsByTagName(e)[0];
s.parentNode.insertBefore(t,s)}(window, document,'script',
'https://connect.facebook.net/en_US/fbevents.js');
fbq('init', '1223271852059785');
fbq('track', 'PageView');
</script>
<!-- Mautic Tracking -->
<script>
(function(w,d,t,u,n,a,m){
w['MauticTrackingObject']=n;
w[n]=w[n]||function(){(w[n].q=w[n].q||[]).push(arguments)},a=d.createElement(t),
m=d.getElementsByTagName(t)[0];a.async=1;a.src=u;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://mkt.avanzato.com.br/mtc.js','mt');
mt('send', 'pageview');
</script>
<!-- FIM Mautic Tracking -->
<!-- Preconnect to Google Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Urbanist:wght@400;500;600;700;800;900&display=swap" rel="stylesheet">
<!-- Primary Meta Tags -->
<title>Avanzato Tecnologia | TI Gerenciada, Cloud e Seguranca em Guarulhos/SP</title>
<meta name="title" content="Avanzato Tecnologia | TI Gerenciada, Cloud e Seguranca em Guarulhos/SP" />
<meta name="description" content="Solucoes em TI Gerenciada, Cloud Computing e Seguranca da Informacao. +23 anos de experiencia, +500 clientes atendidos. Suporte 24/7 em Guarulhos e regiao." />
<meta name="app-version" content="1.0.0" />
<meta name="keywords" content="TI Gerenciada, Cloud Computing, Seguranca da Informacao, Suporte Tecnico, Tecnologia da Informacao, Guarulhos, Sao Paulo, Infraestrutura de TI, Backup, Firewall, Consultoria TI" />
<meta name="author" content="Avanzato Tecnologia" />
<meta name="robots" content="index, follow" id="meta-robots" />
<meta name="googlebot" content="index, follow" id="meta-googlebot" />
<!-- BLOQUEAR INDEXACAO EM HOMOLOGACAO -->
<script>
(function() {
// Usa IS_PROD ja definido no inicio do documento
if (!window.__IS_PROD__) {
// Homologacao/Desenvolvimento: bloquear indexacao
document.getElementById('meta-robots').setAttribute('content', 'noindex, nofollow');
document.getElementById('meta-googlebot').setAttribute('content', 'noindex, nofollow');
// Adicionar tambem X-Robots-Tag via meta http-equiv
var meta = document.createElement('meta');
meta.httpEquiv = 'X-Robots-Tag';
meta.content = 'noindex, nofollow';
document.head.appendChild(meta);
console.log('%c[SEO] ' + (window.__AVANZATO_ENV__ || 'nao-producao') + ' - indexacao BLOQUEADA', 'background:#dc2626;color:#fff;font-weight:bold;padding:2px 6px;border-radius:3px');
} else {
console.log('%c[SEO] Producao - indexacao PERMITIDA (index, follow)', 'background:#16a34a;color:#fff;font-weight:bold;padding:2px 6px;border-radius:3px');
}
})();
</script>
<!-- Canonical URL -->
<link rel="canonical" href="https://avanzato.com.br" />
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website" />
<meta property="og:url" content="https://avanzato.com.br" />
<meta property="og:title" content="Avanzato Tecnologia | TI Gerenciada, Cloud e Seguranca" />
<meta property="og:description" content="Solucoes em TI Gerenciada, Cloud Computing e Seguranca da Informacao. +23 anos de experiencia, suporte 24/7." />
<meta property="og:image" content="https://avanzato.com.br/og-image.jpg" />
<meta property="og:locale" content="pt_BR" />
<meta property="og:site_name" content="Avanzato Tecnologia" />
<!-- Twitter -->
<meta property="twitter:card" content="summary_large_image" />
<meta property="twitter:url" content="https://avanzato.com.br" />
<meta property="twitter:title" content="Avanzato Tecnologia | TI Gerenciada, Cloud e Seguranca" />
<meta property="twitter:description" content="Solucoes em TI Gerenciada, Cloud Computing e Seguranca da Informacao. +23 anos de experiencia, suporte 24/7." />
<meta property="twitter:image" content="https://avanzato.com.br/og-image.jpg" />
<!-- Favicon -->
<link rel="icon" type="image/png" href="https://avanzato.com.br/favicon.png" />
<link rel="shortcut icon" type="image/png" href="https://avanzato.com.br/favicon.png" />
<link rel="manifest" href="/site.webmanifest" />
<!-- Theme Color -->
<meta name="theme-color" content="#cbf400" />
<meta name="msapplication-TileColor" content="#0e0e0e" />
<!-- Structured Data / Schema.org -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Organization",
"name": "Avanzato Tecnologia",
"alternateName": "Avanzato Tecnologia e Informatica",
"url": "https://avanzato.com.br",
"logo": "https://avanzato.com.br/logo.svg",
"description": "Solucoes em TI Gerenciada, Cloud Computing e Seguranca da Informacao. +23 anos de experiencia.",
"foundingDate": "2001",
"address": {
"@type": "PostalAddress",
"streetAddress": "Av. Dr. Carlos de Campos, 43 - Sl 05/06",
"addressLocality": "Guarulhos",
"addressRegion": "SP",
"postalCode": "07040-000",
"addressCountry": "BR"
},
"geo": {
"@type": "GeoCoordinates",
"latitude": "-23.4686",
"longitude": "-46.5326"
},
"contactPoint": {
"@type": "ContactPoint",
"telephone": "+55-11-4810-1704",
"contactType": "customer service",
"availableLanguage": ["Portuguese"],
"areaServed": "BR"
},
"sameAs": [
"https://www.facebook.com/avanzatotec",
"https://www.linkedin.com/company/avanzato-tecnologia-e-informatica/",
"https://www.instagram.com/avanzato.tecnologia/",
"https://www.youtube.com/@AvanzatoTecnologia"
],
"openingHoursSpecification": {
"@type": "OpeningHoursSpecification",
"dayOfWeek": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"],
"opens": "08:00",
"closes": "18:00"
}
}
</script>
<!-- Local Business Schema -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "LocalBusiness",
"name": "Avanzato Tecnologia",
"image": "https://avanzato.com.br/logo.svg",
"@id": "https://avanzato.com.br",
"url": "https://avanzato.com.br",
"telephone": "+55-11-4810-1704",
"priceRange": "$$",
"address": {
"@type": "PostalAddress",
"streetAddress": "Av. Dr. Carlos de Campos, 43 - Sl 05/06, Parque Renato Maia",
"addressLocality": "Guarulhos",
"addressRegion": "SP",
"postalCode": "07040-000",
"addressCountry": "BR"
},
"geo": {
"@type": "GeoCoordinates",
"latitude": -23.4686,
"longitude": -46.5326
},
"openingHoursSpecification": [
{
"@type": "OpeningHoursSpecification",
"dayOfWeek": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"],
"opens": "08:00",
"closes": "18:00"
}
],
"department": {
"@type": "ComputerStore",
"name": "Departamento de TI",
"openingHours": "24/7"
}
}
</script>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
<!-- Facebook Pixel noscript (moved from head to body to fix parse5 warning) -->
<noscript>
<img height="1" width="1" style="display:none"
src="https://www.facebook.com/tr?id=1223271852059785&ev=PageView&noscript=1"/>
</noscript>
<!-- Noscript content for SEO -->
<noscript>
<div style="padding: 20px; text-align: center; font-family: Urbanist, Arial, sans-serif; background: #0e0e0e; color: white; min-height: 100vh;">
<h1 style="color: #cbf400;">Avanzato Tecnologia</h1>
<p>Para visualizar nosso site completo, por favor habilite o JavaScript no seu navegador.</p>
<p>Entre em contato: (11) 4810-1704 | contato@avanzato.com.br</p>
</div>
</noscript>
</body>
</html>
BIN
View File
Binary file not shown.
@@ -0,0 +1,24 @@
# Release PROD
Data: sáb 30 mai 2026 19:30:29 -03
Ambiente:
PROD
Descrição:
Atualização e correção de diretorios
Branch:
main
Commit:
bb73783
Último commit develop:
146652a
Arquivos alterados no último commit:
Status:
Preparado para publicação em produção
@@ -0,0 +1,24 @@
# Release PROD
Data: sáb 30 mai 2026 20:36:09 -03
Ambiente:
PROD
Descrição:
Favivon - Correção
Branch:
main
Commit:
5b729b23
Último commit develop:
643f628a
Arquivos alterados no último commit:
Status:
Preparado para publicação em produção
@@ -0,0 +1,24 @@
# Release PROD
Data: sáb 30 mai 2026 23:15:11 -03
Ambiente:
PROD
Descrição:
Favivon - Correção
Branch:
main
Commit:
e9f47c5b
Último commit develop:
2dc62486
Arquivos alterados no último commit:
Status:
Preparado para publicação em produção
@@ -0,0 +1,24 @@
# Release PROD
Data: dom 31 mai 2026 00:03:02 -03
Ambiente:
PROD
Descrição:
Revisão Logo, Favicon, Robots
Branch:
main
Commit:
b284c4d2
Último commit develop:
3d36e981
Arquivos alterados no último commit:
Status:
Preparado para publicação em produção
@@ -0,0 +1,24 @@
# Release PROD
Data: dom 31 mai 2026 00:29:03 -03
Ambiente:
PROD
Descrição:
Revisão Logo, Favicon, Robots
Branch:
main
Commit:
bfb02286
Último commit develop:
be22792b
Arquivos alterados no último commit:
Status:
Preparado para publicação em produção
@@ -0,0 +1,24 @@
# Release PROD
Data: dom 31 mai 2026 00:58:08 -03
Ambiente:
PROD
Descrição:
Revisão Logo, Favicon, Robots
Branch:
main
Commit:
9885f59b
Último commit develop:
d79c9c48
Arquivos alterados no último commit:
Status:
Preparado para publicação em produção
@@ -0,0 +1,24 @@
# Release PROD
Data: seg 1 jun 2026 19:44:45 -03
Ambiente:
PROD
Descrição:
Paginas e SEO master
Branch:
main
Commit:
4f15851f
Último commit develop:
15ce64e7
Arquivos alterados no último commit:
Status:
Preparado para publicação em produção
@@ -0,0 +1,24 @@
# Release PROD
Data: seg 1 jun 2026 19:56:50 -03
Ambiente:
PROD
Descrição:
Paginas e SEO master
Branch:
main
Commit:
3eb41133
Último commit develop:
e8f15d63
Arquivos alterados no último commit:
Status:
Preparado para publicação em produção
@@ -0,0 +1,24 @@
# Release PROD
Data: seg 1 jun 2026 20:28:34 -03
Ambiente:
PROD
Descrição:
Paginas e SEO master
Branch:
main
Commit:
52f86cbb
Último commit develop:
e8f15d63
Arquivos alterados no último commit:
infra/releases/2026-06-01_19-56-50_PROD.md
Status:
Preparado para publicação em produção
@@ -0,0 +1,24 @@
# Release PROD
Data: ter 2 jun 2026 14:21:07 -03
Ambiente:
PROD
Descrição:
Paginas e SEO master, correção javascript google
Branch:
main
Commit:
35e5727e
Último commit develop:
96563b30
Arquivos alterados no último commit:
Status:
Preparado para publicação em produção
+57
View File
@@ -0,0 +1,57 @@
# Release HML
Data: qua 3 jun 2026 22:07:59 -03
Ambiente:
HML
Descrição:
Aplicativos de Atrair Leads
Branch:
develop
Arquivos alterados antes do commit:
M dist/index.html
D infra/releases/2026-06-02_22-34-39_HML.md
M src/App.tsx
M src/sections/About.tsx
M src/sections/CondominiosCTA.tsx
M src/sections/Hero.tsx
M src/sections/Navigation.tsx
M src/sections/Services.tsx
?? infra/releases/2026-06-03_22-07-58_HML.md
?? nginx/
?? public/about-image.webp
?? public/hero-dashboard.webp
?? public/service-cloud.webp
?? public/service-security.webp
?? public/service-support.webp
?? public/service-ti.webp
Status:
Preparado para publicação
Commit:
fdfb4606
Arquivos no commit:
dist/index.html
infra/releases/2026-06-02_22-34-39_HML.md
infra/releases/2026-06-03_22-07-58_HML.md
nginx/avanzato.conf
public/about-image.webp
public/hero-dashboard.webp
public/service-cloud.webp
public/service-security.webp
public/service-support.webp
public/service-ti.webp
src/App.tsx
src/sections/About.tsx
src/sections/CondominiosCTA.tsx
src/sections/Hero.tsx
src/sections/Navigation.tsx
src/sections/Services.tsx
Status final:
Publicado no Gitea / aguardando pipeline HML
@@ -0,0 +1,24 @@
# Release PROD
Data: qua 3 jun 2026 22:19:56 -03
Ambiente:
PROD
Descrição:
Aplicativos de Atrair Leads
Branch:
main
Commit:
667447c3
Último commit develop:
e3f35d80
Arquivos alterados no último commit:
Status:
Preparado para publicação em produção
+30
View File
@@ -0,0 +1,30 @@
# ============================================================================
# AVANZATO - Configuracao Nginx para Docker
# ============================================================================
# Coloque este arquivo em: /etc/nginx/conf.d/default.conf
# dentro do container Docker
# ============================================================================
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
index index.html;
# === CORRECAO CRITICA: SPA React ===
# Redireciona TODAS as rotas para index.html
location / {
try_files $uri $uri/ /index.html;
}
# Cache para assets estaticos
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
expires 6M;
add_header Cache-Control "public, immutable";
}
# Seguranca
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
}
+168
View File
@@ -0,0 +1,168 @@
# =============================================================================
# AVANZATO TECNOLOGIA - Configuração Nginx
# =============================================================================
# Coloque este arquivo em: /etc/nginx/sites-available/avanzato
# Depois crie link: sudo ln -s /etc/nginx/sites-available/avanzato /etc/nginx/sites-enabled/
# =============================================================================
server {
# Portas HTTP e HTTPS
listen 80;
# listen 443 ssl http2; # Descomente quando tiver SSL
# Domínio
server_name avanzato.com.br www.avanzato.com.br;
# Pasta raiz do site
root /var/www/avanzato/dist;
index index.html;
# Charset
charset utf-8;
# -------------------------------------------------------------------------
# LOGS
# -------------------------------------------------------------------------
access_log /var/log/nginx/avanzato-access.log;
error_log /var/log/nginx/avanzato-error.log;
# -------------------------------------------------------------------------
# GZIP (compressão)
# -------------------------------------------------------------------------
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_types
text/plain
text/css
text/xml
text/javascript
application/json
application/javascript
application/xml+rss
application/atom+xml
image/svg+xml;
# -------------------------------------------------------------------------
# CACHE DE BROWSER
# -------------------------------------------------------------------------
# Assets com hash (JS/CSS) - cache longo
location ~* \.(js|css)$ {
expires 1y;
add_header Cache-Control "public, immutable";
add_header Vary "Accept-Encoding";
}
# Imagens - cache médio
location ~* \.(png|jpg|jpeg|gif|ico|svg|webp)$ {
expires 6M;
add_header Cache-Control "public";
add_header Vary "Accept-Encoding";
}
# Fontes - cache longo
location ~* \.(woff|woff2|ttf|otf|eot)$ {
expires 1y;
add_header Cache-Control "public";
add_header Vary "Accept-Encoding";
}
# HTML - sem cache
location ~* \.html$ {
expires -1;
add_header Cache-Control "no-store, no-cache, must-revalidate";
}
# -------------------------------------------------------------------------
# SEGURANÇA - HEADERS
# -------------------------------------------------------------------------
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# Content Security Policy (ajuste conforme necessário)
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://fonts.googleapis.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: https:; connect-src 'self'; frame-src https://api.whatsapp.com;" always;
# HSTS (descomente quando tiver SSL)
# add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
# -------------------------------------------------------------------------
# REGRAS DE SEGURANÇA
# -------------------------------------------------------------------------
# Bloquear arquivos ocultos
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
# Bloquear arquivos de backup/sensíveis
location ~* \.(git|gitignore|env|log|sql|sh|bash|zsh|bak|backup|swp|tmp)$ {
deny all;
access_log off;
log_not_found off;
}
# Proteger arquivos de configuração
location ~* \.(htaccess|htpasswd|ini|conf)$ {
deny all;
access_log off;
log_not_found off;
}
# -------------------------------------------------------------------------
# SPA - Single Page Application (React)
# -------------------------------------------------------------------------
# Tentar arquivo estático, senão vai para index.html
location / {
try_files $uri $uri/ /index.html;
}
# -------------------------------------------------------------------------
# REDIRECIONAMENTOS
# -------------------------------------------------------------------------
# Forçar HTTPS (descomente quando tiver SSL)
# if ($scheme != "https") {
# return 301 https://$host$request_uri;
# }
# Remover www (opcional)
# if ($host ~* ^www\.(.*)$) {
# return 301 https://$1$request_uri;
# }
# -------------------------------------------------------------------------
# PÁGINAS DE ERRO PERSONALIZADAS
# -------------------------------------------------------------------------
error_page 404 /index.html;
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /var/www/avanzato/dist;
internal;
}
# -------------------------------------------------------------------------
# LIMITE DE TAMANHO DE UPLOAD
# -------------------------------------------------------------------------
client_max_body_size 10M;
# -------------------------------------------------------------------------
# TIMEOUTS
# -------------------------------------------------------------------------
client_body_timeout 12;
client_header_timeout 12;
keepalive_timeout 15;
send_timeout 10;
}
# =============================================================================
# REDIRECIONAMENTO WWW (opcional)
# =============================================================================
# Se quiser forçar sem www:
# server {
# listen 80;
# server_name www.avanzato.com.br;
# return 301 $scheme://avanzato.com.br$request_uri;
# }
+132
View File
@@ -0,0 +1,132 @@
# ============================================================================
# Configuracao Nginx - Avanzato Tecnologia
# ============================================================================
# Inclui: CSP, HSTS, X-Frame-Options, cache headers, gzip, brotli
# Copiar para: /etc/nginx/sites-available/avanzato.com.br
# Ativar: ln -s /etc/nginx/sites-available/avanzato.com.br /etc/nginx/sites-enabled/
# ============================================================================
server {
listen 80;
listen [::]:80;
server_name avanzato.com.br www.avanzato.com.br;
# Redirecionar HTTP para HTTPS
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name avanzato.com.br www.avanzato.com.br;
# SSL (certbot gerencia automaticamente)
ssl_certificate /etc/letsencrypt/live/avanzato.com.br/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/avanzato.com.br/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/avanzato.com.br/chain.pem;
# SSL hardening
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;
# HSTS - HTTP Strict Transport Security
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
# Root do projeto
root /var/www/avanzato/dist;
index index.html;
# Gzip compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_proxied any;
gzip_comp_level 6;
gzip_types
text/plain
text/css
text/xml
text/javascript
application/json
application/javascript
application/xml+rss
application/atom+xml
image/svg+xml
font/woff2;
# ============================================================================
# CACHE HEADERS - Assets estaticos
# ============================================================================
# Imagens, fontes, videos - 1 mes
location ~* \.(jpg|jpeg|png|gif|ico|svg|webp|woff|woff2|ttf|eot|mp4|webm)$ {
expires 1M;
add_header Cache-Control "public, immutable" always;
add_header Vary "Accept-Encoding" always;
access_log off;
}
# CSS e JS - 1 ano (tem hash no nome)
location ~* \.(css|js)$ {
expires 1y;
add_header Cache-Control "public, immutable" always;
add_header Vary "Accept-Encoding" always;
access_log off;
}
# HTML - nao cachear
location ~* \.html$ {
expires -1;
add_header Cache-Control "no-cache, no-store, must-revalidate" always;
add_header Pragma "no-cache" always;
}
# ============================================================================
# SECURITY HEADERS
# ============================================================================
# Content Security Policy
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://www.googletagmanager.com https://connect.facebook.net https://static.cloudflareinsights.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: https:; connect-src 'self' https://dns.google https://crt.sh https://mkt.avanzato.com.br https://www.google-analytics.com https://www.facebook.com; frame-src 'self' https://mkt.avanzato.com.br; media-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'self'; upgrade-insecure-requests;" always;
# X-Frame-Options - protecao contra clickjacking
add_header X-Frame-Options "SAMEORIGIN" always;
# X-Content-Type-Options
add_header X-Content-Type-Options "nosniff" always;
# Referrer Policy
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# Permissions Policy
add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=(), usb=(), magnetometer=(), gyroscope=()" always;
# Cross-Origin Opener Policy
add_header Cross-Origin-Opener-Policy "same-origin" always;
# Cross-Origin Resource Policy
add_header Cross-Origin-Resource-Policy "cross-origin" always;
# ============================================================================
# REACT ROUTER - SPA fallback
# ============================================================================
location / {
try_files $uri $uri/ /index.html;
add_header Cache-Control "no-cache, no-store, must-revalidate" always;
}
# Sitemap e robots - acessiveis
location = /sitemap.xml {
try_files $uri =404;
add_header Content-Type "application/xml" always;
}
location = /robots.txt {
try_files $uri =404;
add_header Content-Type "text/plain" always;
}
}
Generated Vendored Symlink
+1
View File
@@ -0,0 +1 @@
../acorn/bin/acorn
Generated Vendored Symlink
+1
View File
@@ -0,0 +1 @@
../autoprefixer/bin/autoprefixer
+1
View File
@@ -0,0 +1 @@
../baseline-browser-mapping/dist/cli.js
Generated Vendored Symlink
+1
View File
@@ -0,0 +1 @@
../browserslist/cli.js
Generated Vendored Symlink
+1
View File
@@ -0,0 +1 @@
../cross-env/src/bin/cross-env.js
Generated Vendored Symlink
+1
View File
@@ -0,0 +1 @@
../cross-env/src/bin/cross-env-shell.js
Generated Vendored Symlink
+1
View File
@@ -0,0 +1 @@
../cssesc/bin/cssesc
Generated Vendored Symlink
+1
View File
@@ -0,0 +1 @@
../esbuild/bin/esbuild
Generated Vendored Symlink
+1
View File
@@ -0,0 +1 @@
../eslint/bin/eslint.js
Generated Vendored Symlink
+1
View File
@@ -0,0 +1 @@
../jiti/bin/jiti.js
Generated Vendored Symlink
+1
View File
@@ -0,0 +1 @@
../js-yaml/bin/js-yaml.js
Generated Vendored Symlink
+1
View File
@@ -0,0 +1 @@
../jsesc/bin/jsesc
Generated Vendored Symlink
+1
View File
@@ -0,0 +1 @@
../json5/lib/cli.js
Generated Vendored Symlink
+1
View File
@@ -0,0 +1 @@
../loose-envify/cli.js
Generated Vendored Symlink
+1
View File
@@ -0,0 +1 @@
../nanoid/bin/nanoid.cjs
Generated Vendored Symlink
+1
View File
@@ -0,0 +1 @@
../which/bin/node-which
Generated Vendored Symlink
+1
View File
@@ -0,0 +1 @@
../@babel/parser/bin/babel-parser.js
Generated Vendored Symlink
+1
View File
@@ -0,0 +1 @@
../regjsparser/bin/parser
Generated Vendored Symlink
+1
View File
@@ -0,0 +1 @@
../resolve/bin/resolve
Generated Vendored Symlink
+1
View File
@@ -0,0 +1 @@
../rollup/dist/bin/rollup
Generated Vendored Symlink
+1
View File
@@ -0,0 +1 @@
../semver/bin/semver.js
Generated Vendored Symlink
+1
View File
@@ -0,0 +1 @@
../sucrase/bin/sucrase
Generated Vendored Symlink
+1
View File
@@ -0,0 +1 @@
../sucrase/bin/sucrase-node
Generated Vendored Symlink
+1
View File
@@ -0,0 +1 @@
../tailwindcss/lib/cli.js
Generated Vendored Symlink
+1
View File
@@ -0,0 +1 @@
../tailwindcss/lib/cli.js
Generated Vendored Symlink
+1
View File
@@ -0,0 +1 @@
../typescript/bin/tsc
Generated Vendored Symlink
+1
View File
@@ -0,0 +1 @@
../typescript/bin/tsserver
+1
View File
@@ -0,0 +1 @@
../update-browserslist-db/cli.js
Generated Vendored Symlink
+1
View File
@@ -0,0 +1 @@
../vite/bin/vite.js
+8177
View File
File diff suppressed because it is too large Load Diff
+1
View File
File diff suppressed because one or more lines are too long
+1
View File
@@ -0,0 +1 @@
{"root":["../../vite.config.ts"],"version":"5.9.3"}
+457
View File
@@ -0,0 +1,457 @@
"use client";
import {
Presence
} from "./chunk-INCCO7S4.js";
import {
Combination_default,
DismissableLayer,
FocusScope,
Portal,
hideOthers,
useFocusGuards
} from "./chunk-LIWCVBQK.js";
import {
Primitive,
composeEventHandlers,
createContext2,
createContextScope,
useControllableState,
useId
} from "./chunk-MOFDO34C.js";
import {
composeRefs,
useComposedRefs
} from "./chunk-2VUH7NEY.js";
import "./chunk-YF4B4G2L.js";
import {
require_jsx_runtime
} from "./chunk-2YVA4HRZ.js";
import {
require_react
} from "./chunk-WUR7D6NS.js";
import {
__toESM
} from "./chunk-G3PMV62Z.js";
// node_modules/@radix-ui/react-dialog/dist/index.mjs
var React2 = __toESM(require_react(), 1);
// node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-slot/dist/index.mjs
var React = __toESM(require_react(), 1);
var import_jsx_runtime = __toESM(require_jsx_runtime(), 1);
function createSlot(ownerName) {
const SlotClone = createSlotClone(ownerName);
const Slot22 = React.forwardRef((props, forwardedRef) => {
const { children, ...slotProps } = props;
const childrenArray = React.Children.toArray(children);
const slottable = childrenArray.find(isSlottable);
if (slottable) {
const newElement = slottable.props.children;
const newChildren = childrenArray.map((child) => {
if (child === slottable) {
if (React.Children.count(newElement) > 1) return React.Children.only(null);
return React.isValidElement(newElement) ? newElement.props.children : null;
} else {
return child;
}
});
return (0, import_jsx_runtime.jsx)(SlotClone, { ...slotProps, ref: forwardedRef, children: React.isValidElement(newElement) ? React.cloneElement(newElement, void 0, newChildren) : null });
}
return (0, import_jsx_runtime.jsx)(SlotClone, { ...slotProps, ref: forwardedRef, children });
});
Slot22.displayName = `${ownerName}.Slot`;
return Slot22;
}
var Slot = createSlot("Slot");
function createSlotClone(ownerName) {
const SlotClone = React.forwardRef((props, forwardedRef) => {
const { children, ...slotProps } = props;
if (React.isValidElement(children)) {
const childrenRef = getElementRef(children);
const props2 = mergeProps(slotProps, children.props);
if (children.type !== React.Fragment) {
props2.ref = forwardedRef ? composeRefs(forwardedRef, childrenRef) : childrenRef;
}
return React.cloneElement(children, props2);
}
return React.Children.count(children) > 1 ? React.Children.only(null) : null;
});
SlotClone.displayName = `${ownerName}.SlotClone`;
return SlotClone;
}
var SLOTTABLE_IDENTIFIER = /* @__PURE__ */ Symbol("radix.slottable");
function createSlottable(ownerName) {
const Slottable2 = ({ children }) => {
return (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children });
};
Slottable2.displayName = `${ownerName}.Slottable`;
Slottable2.__radixId = SLOTTABLE_IDENTIFIER;
return Slottable2;
}
var Slottable = createSlottable("Slottable");
function isSlottable(child) {
return React.isValidElement(child) && typeof child.type === "function" && "__radixId" in child.type && child.type.__radixId === SLOTTABLE_IDENTIFIER;
}
function mergeProps(slotProps, childProps) {
const overrideProps = { ...childProps };
for (const propName in childProps) {
const slotPropValue = slotProps[propName];
const childPropValue = childProps[propName];
const isHandler = /^on[A-Z]/.test(propName);
if (isHandler) {
if (slotPropValue && childPropValue) {
overrideProps[propName] = (...args) => {
const result = childPropValue(...args);
slotPropValue(...args);
return result;
};
} else if (slotPropValue) {
overrideProps[propName] = slotPropValue;
}
} else if (propName === "style") {
overrideProps[propName] = { ...slotPropValue, ...childPropValue };
} else if (propName === "className") {
overrideProps[propName] = [slotPropValue, childPropValue].filter(Boolean).join(" ");
}
}
return { ...slotProps, ...overrideProps };
}
function getElementRef(element) {
let getter = Object.getOwnPropertyDescriptor(element.props, "ref")?.get;
let mayWarn = getter && "isReactWarning" in getter && getter.isReactWarning;
if (mayWarn) {
return element.ref;
}
getter = Object.getOwnPropertyDescriptor(element, "ref")?.get;
mayWarn = getter && "isReactWarning" in getter && getter.isReactWarning;
if (mayWarn) {
return element.props.ref;
}
return element.props.ref || element.ref;
}
// node_modules/@radix-ui/react-dialog/dist/index.mjs
var import_jsx_runtime2 = __toESM(require_jsx_runtime(), 1);
var DIALOG_NAME = "Dialog";
var [createDialogContext, createDialogScope] = createContextScope(DIALOG_NAME);
var [DialogProvider, useDialogContext] = createDialogContext(DIALOG_NAME);
var Dialog = (props) => {
const {
__scopeDialog,
children,
open: openProp,
defaultOpen,
onOpenChange,
modal = true
} = props;
const triggerRef = React2.useRef(null);
const contentRef = React2.useRef(null);
const [open, setOpen] = useControllableState({
prop: openProp,
defaultProp: defaultOpen ?? false,
onChange: onOpenChange,
caller: DIALOG_NAME
});
return (0, import_jsx_runtime2.jsx)(
DialogProvider,
{
scope: __scopeDialog,
triggerRef,
contentRef,
contentId: useId(),
titleId: useId(),
descriptionId: useId(),
open,
onOpenChange: setOpen,
onOpenToggle: React2.useCallback(() => setOpen((prevOpen) => !prevOpen), [setOpen]),
modal,
children
}
);
};
Dialog.displayName = DIALOG_NAME;
var TRIGGER_NAME = "DialogTrigger";
var DialogTrigger = React2.forwardRef(
(props, forwardedRef) => {
const { __scopeDialog, ...triggerProps } = props;
const context = useDialogContext(TRIGGER_NAME, __scopeDialog);
const composedTriggerRef = useComposedRefs(forwardedRef, context.triggerRef);
return (0, import_jsx_runtime2.jsx)(
Primitive.button,
{
type: "button",
"aria-haspopup": "dialog",
"aria-expanded": context.open,
"aria-controls": context.contentId,
"data-state": getState(context.open),
...triggerProps,
ref: composedTriggerRef,
onClick: composeEventHandlers(props.onClick, context.onOpenToggle)
}
);
}
);
DialogTrigger.displayName = TRIGGER_NAME;
var PORTAL_NAME = "DialogPortal";
var [PortalProvider, usePortalContext] = createDialogContext(PORTAL_NAME, {
forceMount: void 0
});
var DialogPortal = (props) => {
const { __scopeDialog, forceMount, children, container } = props;
const context = useDialogContext(PORTAL_NAME, __scopeDialog);
return (0, import_jsx_runtime2.jsx)(PortalProvider, { scope: __scopeDialog, forceMount, children: React2.Children.map(children, (child) => (0, import_jsx_runtime2.jsx)(Presence, { present: forceMount || context.open, children: (0, import_jsx_runtime2.jsx)(Portal, { asChild: true, container, children: child }) })) });
};
DialogPortal.displayName = PORTAL_NAME;
var OVERLAY_NAME = "DialogOverlay";
var DialogOverlay = React2.forwardRef(
(props, forwardedRef) => {
const portalContext = usePortalContext(OVERLAY_NAME, props.__scopeDialog);
const { forceMount = portalContext.forceMount, ...overlayProps } = props;
const context = useDialogContext(OVERLAY_NAME, props.__scopeDialog);
return context.modal ? (0, import_jsx_runtime2.jsx)(Presence, { present: forceMount || context.open, children: (0, import_jsx_runtime2.jsx)(DialogOverlayImpl, { ...overlayProps, ref: forwardedRef }) }) : null;
}
);
DialogOverlay.displayName = OVERLAY_NAME;
var Slot2 = createSlot("DialogOverlay.RemoveScroll");
var DialogOverlayImpl = React2.forwardRef(
(props, forwardedRef) => {
const { __scopeDialog, ...overlayProps } = props;
const context = useDialogContext(OVERLAY_NAME, __scopeDialog);
return (
// Make sure `Content` is scrollable even when it doesn't live inside `RemoveScroll`
// ie. when `Overlay` and `Content` are siblings
(0, import_jsx_runtime2.jsx)(Combination_default, { as: Slot2, allowPinchZoom: true, shards: [context.contentRef], children: (0, import_jsx_runtime2.jsx)(
Primitive.div,
{
"data-state": getState(context.open),
...overlayProps,
ref: forwardedRef,
style: { pointerEvents: "auto", ...overlayProps.style }
}
) })
);
}
);
var CONTENT_NAME = "DialogContent";
var DialogContent = React2.forwardRef(
(props, forwardedRef) => {
const portalContext = usePortalContext(CONTENT_NAME, props.__scopeDialog);
const { forceMount = portalContext.forceMount, ...contentProps } = props;
const context = useDialogContext(CONTENT_NAME, props.__scopeDialog);
return (0, import_jsx_runtime2.jsx)(Presence, { present: forceMount || context.open, children: context.modal ? (0, import_jsx_runtime2.jsx)(DialogContentModal, { ...contentProps, ref: forwardedRef }) : (0, import_jsx_runtime2.jsx)(DialogContentNonModal, { ...contentProps, ref: forwardedRef }) });
}
);
DialogContent.displayName = CONTENT_NAME;
var DialogContentModal = React2.forwardRef(
(props, forwardedRef) => {
const context = useDialogContext(CONTENT_NAME, props.__scopeDialog);
const contentRef = React2.useRef(null);
const composedRefs = useComposedRefs(forwardedRef, context.contentRef, contentRef);
React2.useEffect(() => {
const content = contentRef.current;
if (content) return hideOthers(content);
}, []);
return (0, import_jsx_runtime2.jsx)(
DialogContentImpl,
{
...props,
ref: composedRefs,
trapFocus: context.open,
disableOutsidePointerEvents: true,
onCloseAutoFocus: composeEventHandlers(props.onCloseAutoFocus, (event) => {
event.preventDefault();
context.triggerRef.current?.focus();
}),
onPointerDownOutside: composeEventHandlers(props.onPointerDownOutside, (event) => {
const originalEvent = event.detail.originalEvent;
const ctrlLeftClick = originalEvent.button === 0 && originalEvent.ctrlKey === true;
const isRightClick = originalEvent.button === 2 || ctrlLeftClick;
if (isRightClick) event.preventDefault();
}),
onFocusOutside: composeEventHandlers(
props.onFocusOutside,
(event) => event.preventDefault()
)
}
);
}
);
var DialogContentNonModal = React2.forwardRef(
(props, forwardedRef) => {
const context = useDialogContext(CONTENT_NAME, props.__scopeDialog);
const hasInteractedOutsideRef = React2.useRef(false);
const hasPointerDownOutsideRef = React2.useRef(false);
return (0, import_jsx_runtime2.jsx)(
DialogContentImpl,
{
...props,
ref: forwardedRef,
trapFocus: false,
disableOutsidePointerEvents: false,
onCloseAutoFocus: (event) => {
props.onCloseAutoFocus?.(event);
if (!event.defaultPrevented) {
if (!hasInteractedOutsideRef.current) context.triggerRef.current?.focus();
event.preventDefault();
}
hasInteractedOutsideRef.current = false;
hasPointerDownOutsideRef.current = false;
},
onInteractOutside: (event) => {
props.onInteractOutside?.(event);
if (!event.defaultPrevented) {
hasInteractedOutsideRef.current = true;
if (event.detail.originalEvent.type === "pointerdown") {
hasPointerDownOutsideRef.current = true;
}
}
const target = event.target;
const targetIsTrigger = context.triggerRef.current?.contains(target);
if (targetIsTrigger) event.preventDefault();
if (event.detail.originalEvent.type === "focusin" && hasPointerDownOutsideRef.current) {
event.preventDefault();
}
}
}
);
}
);
var DialogContentImpl = React2.forwardRef(
(props, forwardedRef) => {
const { __scopeDialog, trapFocus, onOpenAutoFocus, onCloseAutoFocus, ...contentProps } = props;
const context = useDialogContext(CONTENT_NAME, __scopeDialog);
const contentRef = React2.useRef(null);
const composedRefs = useComposedRefs(forwardedRef, contentRef);
useFocusGuards();
return (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
(0, import_jsx_runtime2.jsx)(
FocusScope,
{
asChild: true,
loop: true,
trapped: trapFocus,
onMountAutoFocus: onOpenAutoFocus,
onUnmountAutoFocus: onCloseAutoFocus,
children: (0, import_jsx_runtime2.jsx)(
DismissableLayer,
{
role: "dialog",
id: context.contentId,
"aria-describedby": context.descriptionId,
"aria-labelledby": context.titleId,
"data-state": getState(context.open),
...contentProps,
ref: composedRefs,
onDismiss: () => context.onOpenChange(false)
}
)
}
),
(0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
(0, import_jsx_runtime2.jsx)(TitleWarning, { titleId: context.titleId }),
(0, import_jsx_runtime2.jsx)(DescriptionWarning, { contentRef, descriptionId: context.descriptionId })
] })
] });
}
);
var TITLE_NAME = "DialogTitle";
var DialogTitle = React2.forwardRef(
(props, forwardedRef) => {
const { __scopeDialog, ...titleProps } = props;
const context = useDialogContext(TITLE_NAME, __scopeDialog);
return (0, import_jsx_runtime2.jsx)(Primitive.h2, { id: context.titleId, ...titleProps, ref: forwardedRef });
}
);
DialogTitle.displayName = TITLE_NAME;
var DESCRIPTION_NAME = "DialogDescription";
var DialogDescription = React2.forwardRef(
(props, forwardedRef) => {
const { __scopeDialog, ...descriptionProps } = props;
const context = useDialogContext(DESCRIPTION_NAME, __scopeDialog);
return (0, import_jsx_runtime2.jsx)(Primitive.p, { id: context.descriptionId, ...descriptionProps, ref: forwardedRef });
}
);
DialogDescription.displayName = DESCRIPTION_NAME;
var CLOSE_NAME = "DialogClose";
var DialogClose = React2.forwardRef(
(props, forwardedRef) => {
const { __scopeDialog, ...closeProps } = props;
const context = useDialogContext(CLOSE_NAME, __scopeDialog);
return (0, import_jsx_runtime2.jsx)(
Primitive.button,
{
type: "button",
...closeProps,
ref: forwardedRef,
onClick: composeEventHandlers(props.onClick, () => context.onOpenChange(false))
}
);
}
);
DialogClose.displayName = CLOSE_NAME;
function getState(open) {
return open ? "open" : "closed";
}
var TITLE_WARNING_NAME = "DialogTitleWarning";
var [WarningProvider, useWarningContext] = createContext2(TITLE_WARNING_NAME, {
contentName: CONTENT_NAME,
titleName: TITLE_NAME,
docsSlug: "dialog"
});
var TitleWarning = ({ titleId }) => {
const titleWarningContext = useWarningContext(TITLE_WARNING_NAME);
const MESSAGE = `\`${titleWarningContext.contentName}\` requires a \`${titleWarningContext.titleName}\` for the component to be accessible for screen reader users.
If you want to hide the \`${titleWarningContext.titleName}\`, you can wrap it with our VisuallyHidden component.
For more information, see https://radix-ui.com/primitives/docs/components/${titleWarningContext.docsSlug}`;
React2.useEffect(() => {
if (titleId) {
const hasTitle = document.getElementById(titleId);
if (!hasTitle) console.error(MESSAGE);
}
}, [MESSAGE, titleId]);
return null;
};
var DESCRIPTION_WARNING_NAME = "DialogDescriptionWarning";
var DescriptionWarning = ({ contentRef, descriptionId }) => {
const descriptionWarningContext = useWarningContext(DESCRIPTION_WARNING_NAME);
const MESSAGE = `Warning: Missing \`Description\` or \`aria-describedby={undefined}\` for {${descriptionWarningContext.contentName}}.`;
React2.useEffect(() => {
const describedById = contentRef.current?.getAttribute("aria-describedby");
if (descriptionId && describedById) {
const hasDescription = document.getElementById(descriptionId);
if (!hasDescription) console.warn(MESSAGE);
}
}, [MESSAGE, contentRef, descriptionId]);
return null;
};
var Root = Dialog;
var Trigger = DialogTrigger;
var Portal2 = DialogPortal;
var Overlay = DialogOverlay;
var Content = DialogContent;
var Title = DialogTitle;
var Description = DialogDescription;
var Close = DialogClose;
export {
Close,
Content,
Description,
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogOverlay,
DialogPortal,
DialogTitle,
DialogTrigger,
Overlay,
Portal2 as Portal,
Root,
Title,
Trigger,
WarningProvider,
createDialogScope
};
//# sourceMappingURL=@radix-ui_react-dialog.js.map
File diff suppressed because one or more lines are too long
+83
View File
@@ -0,0 +1,83 @@
"use client";
import {
createSlot
} from "./chunk-YWBEB5PG.js";
import "./chunk-2VUH7NEY.js";
import {
require_react_dom
} from "./chunk-YF4B4G2L.js";
import {
require_jsx_runtime
} from "./chunk-2YVA4HRZ.js";
import {
require_react
} from "./chunk-WUR7D6NS.js";
import {
__toESM
} from "./chunk-G3PMV62Z.js";
// node_modules/@radix-ui/react-label/dist/index.mjs
var React2 = __toESM(require_react(), 1);
// node_modules/@radix-ui/react-label/node_modules/@radix-ui/react-primitive/dist/index.mjs
var React = __toESM(require_react(), 1);
var ReactDOM = __toESM(require_react_dom(), 1);
var import_jsx_runtime = __toESM(require_jsx_runtime(), 1);
var NODES = [
"a",
"button",
"div",
"form",
"h2",
"h3",
"img",
"input",
"label",
"li",
"nav",
"ol",
"p",
"select",
"span",
"svg",
"ul"
];
var Primitive = NODES.reduce((primitive, node) => {
const Slot = createSlot(`Primitive.${node}`);
const Node = React.forwardRef((props, forwardedRef) => {
const { asChild, ...primitiveProps } = props;
const Comp = asChild ? Slot : node;
if (typeof window !== "undefined") {
window[/* @__PURE__ */ Symbol.for("radix-ui")] = true;
}
return (0, import_jsx_runtime.jsx)(Comp, { ...primitiveProps, ref: forwardedRef });
});
Node.displayName = `Primitive.${node}`;
return { ...primitive, [node]: Node };
}, {});
// node_modules/@radix-ui/react-label/dist/index.mjs
var import_jsx_runtime2 = __toESM(require_jsx_runtime(), 1);
var NAME = "Label";
var Label = React2.forwardRef((props, forwardedRef) => {
return (0, import_jsx_runtime2.jsx)(
Primitive.label,
{
...props,
ref: forwardedRef,
onMouseDown: (event) => {
const target = event.target;
if (target.closest("button, input, select, textarea")) return;
props.onMouseDown?.(event);
if (!event.defaultPrevented && event.detail > 1) event.preventDefault();
}
}
);
});
Label.displayName = NAME;
var Root = Label;
export {
Label,
Root
};
//# sourceMappingURL=@radix-ui_react-label.js.map
File diff suppressed because one or more lines are too long
+529
View File
@@ -0,0 +1,529 @@
"use client";
import {
Presence
} from "./chunk-INCCO7S4.js";
import {
createCollection,
useDirection,
usePrevious,
useSize
} from "./chunk-L2OGOWTU.js";
import {
Primitive,
composeEventHandlers,
createContextScope,
useCallbackRef,
useControllableState,
useId
} from "./chunk-MOFDO34C.js";
import {
useComposedRefs
} from "./chunk-2VUH7NEY.js";
import "./chunk-YF4B4G2L.js";
import {
require_jsx_runtime
} from "./chunk-2YVA4HRZ.js";
import {
require_react
} from "./chunk-WUR7D6NS.js";
import {
__toESM
} from "./chunk-G3PMV62Z.js";
// node_modules/@radix-ui/react-radio-group/dist/index.mjs
var React2 = __toESM(require_react(), 1);
// node_modules/@radix-ui/react-roving-focus/dist/index.mjs
var React = __toESM(require_react(), 1);
var import_jsx_runtime = __toESM(require_jsx_runtime(), 1);
var ENTRY_FOCUS = "rovingFocusGroup.onEntryFocus";
var EVENT_OPTIONS = { bubbles: false, cancelable: true };
var GROUP_NAME = "RovingFocusGroup";
var [Collection, useCollection, createCollectionScope] = createCollection(GROUP_NAME);
var [createRovingFocusGroupContext, createRovingFocusGroupScope] = createContextScope(
GROUP_NAME,
[createCollectionScope]
);
var [RovingFocusProvider, useRovingFocusContext] = createRovingFocusGroupContext(GROUP_NAME);
var RovingFocusGroup = React.forwardRef(
(props, forwardedRef) => {
return (0, import_jsx_runtime.jsx)(Collection.Provider, { scope: props.__scopeRovingFocusGroup, children: (0, import_jsx_runtime.jsx)(Collection.Slot, { scope: props.__scopeRovingFocusGroup, children: (0, import_jsx_runtime.jsx)(RovingFocusGroupImpl, { ...props, ref: forwardedRef }) }) });
}
);
RovingFocusGroup.displayName = GROUP_NAME;
var RovingFocusGroupImpl = React.forwardRef((props, forwardedRef) => {
const {
__scopeRovingFocusGroup,
orientation,
loop = false,
dir,
currentTabStopId: currentTabStopIdProp,
defaultCurrentTabStopId,
onCurrentTabStopIdChange,
onEntryFocus,
preventScrollOnEntryFocus = false,
...groupProps
} = props;
const ref = React.useRef(null);
const composedRefs = useComposedRefs(forwardedRef, ref);
const direction = useDirection(dir);
const [currentTabStopId, setCurrentTabStopId] = useControllableState({
prop: currentTabStopIdProp,
defaultProp: defaultCurrentTabStopId ?? null,
onChange: onCurrentTabStopIdChange,
caller: GROUP_NAME
});
const [isTabbingBackOut, setIsTabbingBackOut] = React.useState(false);
const handleEntryFocus = useCallbackRef(onEntryFocus);
const getItems = useCollection(__scopeRovingFocusGroup);
const isClickFocusRef = React.useRef(false);
const [focusableItemsCount, setFocusableItemsCount] = React.useState(0);
React.useEffect(() => {
const node = ref.current;
if (node) {
node.addEventListener(ENTRY_FOCUS, handleEntryFocus);
return () => node.removeEventListener(ENTRY_FOCUS, handleEntryFocus);
}
}, [handleEntryFocus]);
return (0, import_jsx_runtime.jsx)(
RovingFocusProvider,
{
scope: __scopeRovingFocusGroup,
orientation,
dir: direction,
loop,
currentTabStopId,
onItemFocus: React.useCallback(
(tabStopId) => setCurrentTabStopId(tabStopId),
[setCurrentTabStopId]
),
onItemShiftTab: React.useCallback(() => setIsTabbingBackOut(true), []),
onFocusableItemAdd: React.useCallback(
() => setFocusableItemsCount((prevCount) => prevCount + 1),
[]
),
onFocusableItemRemove: React.useCallback(
() => setFocusableItemsCount((prevCount) => prevCount - 1),
[]
),
children: (0, import_jsx_runtime.jsx)(
Primitive.div,
{
tabIndex: isTabbingBackOut || focusableItemsCount === 0 ? -1 : 0,
"data-orientation": orientation,
...groupProps,
ref: composedRefs,
style: { outline: "none", ...props.style },
onMouseDown: composeEventHandlers(props.onMouseDown, () => {
isClickFocusRef.current = true;
}),
onFocus: composeEventHandlers(props.onFocus, (event) => {
const isKeyboardFocus = !isClickFocusRef.current;
if (event.target === event.currentTarget && isKeyboardFocus && !isTabbingBackOut) {
const entryFocusEvent = new CustomEvent(ENTRY_FOCUS, EVENT_OPTIONS);
event.currentTarget.dispatchEvent(entryFocusEvent);
if (!entryFocusEvent.defaultPrevented) {
const items = getItems().filter((item) => item.focusable);
const activeItem = items.find((item) => item.active);
const currentItem = items.find((item) => item.id === currentTabStopId);
const candidateItems = [activeItem, currentItem, ...items].filter(
Boolean
);
const candidateNodes = candidateItems.map((item) => item.ref.current);
focusFirst(candidateNodes, preventScrollOnEntryFocus);
}
}
isClickFocusRef.current = false;
}),
onBlur: composeEventHandlers(props.onBlur, () => setIsTabbingBackOut(false))
}
)
}
);
});
var ITEM_NAME = "RovingFocusGroupItem";
var RovingFocusGroupItem = React.forwardRef(
(props, forwardedRef) => {
const {
__scopeRovingFocusGroup,
focusable = true,
active = false,
tabStopId,
children,
...itemProps
} = props;
const autoId = useId();
const id = tabStopId || autoId;
const context = useRovingFocusContext(ITEM_NAME, __scopeRovingFocusGroup);
const isCurrentTabStop = context.currentTabStopId === id;
const getItems = useCollection(__scopeRovingFocusGroup);
const { onFocusableItemAdd, onFocusableItemRemove, currentTabStopId } = context;
React.useEffect(() => {
if (focusable) {
onFocusableItemAdd();
return () => onFocusableItemRemove();
}
}, [focusable, onFocusableItemAdd, onFocusableItemRemove]);
return (0, import_jsx_runtime.jsx)(
Collection.ItemSlot,
{
scope: __scopeRovingFocusGroup,
id,
focusable,
active,
children: (0, import_jsx_runtime.jsx)(
Primitive.span,
{
tabIndex: isCurrentTabStop ? 0 : -1,
"data-orientation": context.orientation,
...itemProps,
ref: forwardedRef,
onMouseDown: composeEventHandlers(props.onMouseDown, (event) => {
if (!focusable) event.preventDefault();
else context.onItemFocus(id);
}),
onFocus: composeEventHandlers(props.onFocus, () => context.onItemFocus(id)),
onKeyDown: composeEventHandlers(props.onKeyDown, (event) => {
if (event.key === "Tab" && event.shiftKey) {
context.onItemShiftTab();
return;
}
if (event.target !== event.currentTarget) return;
const focusIntent = getFocusIntent(event, context.orientation, context.dir);
if (focusIntent !== void 0) {
if (event.metaKey || event.ctrlKey || event.altKey || event.shiftKey) return;
event.preventDefault();
const items = getItems().filter((item) => item.focusable);
let candidateNodes = items.map((item) => item.ref.current);
if (focusIntent === "last") candidateNodes.reverse();
else if (focusIntent === "prev" || focusIntent === "next") {
if (focusIntent === "prev") candidateNodes.reverse();
const currentIndex = candidateNodes.indexOf(event.currentTarget);
candidateNodes = context.loop ? wrapArray(candidateNodes, currentIndex + 1) : candidateNodes.slice(currentIndex + 1);
}
setTimeout(() => focusFirst(candidateNodes));
}
}),
children: typeof children === "function" ? children({ isCurrentTabStop, hasTabStop: currentTabStopId != null }) : children
}
)
}
);
}
);
RovingFocusGroupItem.displayName = ITEM_NAME;
var MAP_KEY_TO_FOCUS_INTENT = {
ArrowLeft: "prev",
ArrowUp: "prev",
ArrowRight: "next",
ArrowDown: "next",
PageUp: "first",
Home: "first",
PageDown: "last",
End: "last"
};
function getDirectionAwareKey(key, dir) {
if (dir !== "rtl") return key;
return key === "ArrowLeft" ? "ArrowRight" : key === "ArrowRight" ? "ArrowLeft" : key;
}
function getFocusIntent(event, orientation, dir) {
const key = getDirectionAwareKey(event.key, dir);
if (orientation === "vertical" && ["ArrowLeft", "ArrowRight"].includes(key)) return void 0;
if (orientation === "horizontal" && ["ArrowUp", "ArrowDown"].includes(key)) return void 0;
return MAP_KEY_TO_FOCUS_INTENT[key];
}
function focusFirst(candidates, preventScroll = false) {
const PREVIOUSLY_FOCUSED_ELEMENT = document.activeElement;
for (const candidate of candidates) {
if (candidate === PREVIOUSLY_FOCUSED_ELEMENT) return;
candidate.focus({ preventScroll });
if (document.activeElement !== PREVIOUSLY_FOCUSED_ELEMENT) return;
}
}
function wrapArray(array, startIndex) {
return array.map((_, index) => array[(startIndex + index) % array.length]);
}
var Root = RovingFocusGroup;
var Item = RovingFocusGroupItem;
// node_modules/@radix-ui/react-radio-group/dist/index.mjs
var React3 = __toESM(require_react(), 1);
var import_jsx_runtime2 = __toESM(require_jsx_runtime(), 1);
var import_jsx_runtime3 = __toESM(require_jsx_runtime(), 1);
var RADIO_NAME = "Radio";
var [createRadioContext, createRadioScope] = createContextScope(RADIO_NAME);
var [RadioProvider, useRadioContext] = createRadioContext(RADIO_NAME);
var Radio = React3.forwardRef(
(props, forwardedRef) => {
const {
__scopeRadio,
name,
checked = false,
required,
disabled,
value = "on",
onCheck,
form,
...radioProps
} = props;
const [button, setButton] = React3.useState(null);
const composedRefs = useComposedRefs(forwardedRef, (node) => setButton(node));
const hasConsumerStoppedPropagationRef = React3.useRef(false);
const isFormControl = button ? form || !!button.closest("form") : true;
return (0, import_jsx_runtime2.jsxs)(RadioProvider, { scope: __scopeRadio, checked, disabled, children: [
(0, import_jsx_runtime2.jsx)(
Primitive.button,
{
type: "button",
role: "radio",
"aria-checked": checked,
"data-state": getState(checked),
"data-disabled": disabled ? "" : void 0,
disabled,
value,
...radioProps,
ref: composedRefs,
onClick: composeEventHandlers(props.onClick, (event) => {
if (!checked) onCheck?.();
if (isFormControl) {
hasConsumerStoppedPropagationRef.current = event.isPropagationStopped();
if (!hasConsumerStoppedPropagationRef.current) event.stopPropagation();
}
})
}
),
isFormControl && (0, import_jsx_runtime2.jsx)(
RadioBubbleInput,
{
control: button,
bubbles: !hasConsumerStoppedPropagationRef.current,
name,
value,
checked,
required,
disabled,
form,
style: { transform: "translateX(-100%)" }
}
)
] });
}
);
Radio.displayName = RADIO_NAME;
var INDICATOR_NAME = "RadioIndicator";
var RadioIndicator = React3.forwardRef(
(props, forwardedRef) => {
const { __scopeRadio, forceMount, ...indicatorProps } = props;
const context = useRadioContext(INDICATOR_NAME, __scopeRadio);
return (0, import_jsx_runtime2.jsx)(Presence, { present: forceMount || context.checked, children: (0, import_jsx_runtime2.jsx)(
Primitive.span,
{
"data-state": getState(context.checked),
"data-disabled": context.disabled ? "" : void 0,
...indicatorProps,
ref: forwardedRef
}
) });
}
);
RadioIndicator.displayName = INDICATOR_NAME;
var BUBBLE_INPUT_NAME = "RadioBubbleInput";
var RadioBubbleInput = React3.forwardRef(
({
__scopeRadio,
control,
checked,
bubbles = true,
...props
}, forwardedRef) => {
const ref = React3.useRef(null);
const composedRefs = useComposedRefs(ref, forwardedRef);
const prevChecked = usePrevious(checked);
const controlSize = useSize(control);
React3.useEffect(() => {
const input = ref.current;
if (!input) return;
const inputProto = window.HTMLInputElement.prototype;
const descriptor = Object.getOwnPropertyDescriptor(
inputProto,
"checked"
);
const setChecked = descriptor.set;
if (prevChecked !== checked && setChecked) {
const event = new Event("click", { bubbles });
setChecked.call(input, checked);
input.dispatchEvent(event);
}
}, [prevChecked, checked, bubbles]);
return (0, import_jsx_runtime2.jsx)(
Primitive.input,
{
type: "radio",
"aria-hidden": true,
defaultChecked: checked,
...props,
tabIndex: -1,
ref: composedRefs,
style: {
...props.style,
...controlSize,
position: "absolute",
pointerEvents: "none",
opacity: 0,
margin: 0
}
}
);
}
);
RadioBubbleInput.displayName = BUBBLE_INPUT_NAME;
function getState(checked) {
return checked ? "checked" : "unchecked";
}
var ARROW_KEYS = ["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"];
var RADIO_GROUP_NAME = "RadioGroup";
var [createRadioGroupContext, createRadioGroupScope] = createContextScope(RADIO_GROUP_NAME, [
createRovingFocusGroupScope,
createRadioScope
]);
var useRovingFocusGroupScope = createRovingFocusGroupScope();
var useRadioScope = createRadioScope();
var [RadioGroupProvider, useRadioGroupContext] = createRadioGroupContext(RADIO_GROUP_NAME);
var RadioGroup = React2.forwardRef(
(props, forwardedRef) => {
const {
__scopeRadioGroup,
name,
defaultValue,
value: valueProp,
required = false,
disabled = false,
orientation,
dir,
loop = true,
onValueChange,
...groupProps
} = props;
const rovingFocusGroupScope = useRovingFocusGroupScope(__scopeRadioGroup);
const direction = useDirection(dir);
const [value, setValue] = useControllableState({
prop: valueProp,
defaultProp: defaultValue ?? null,
onChange: onValueChange,
caller: RADIO_GROUP_NAME
});
return (0, import_jsx_runtime3.jsx)(
RadioGroupProvider,
{
scope: __scopeRadioGroup,
name,
required,
disabled,
value,
onValueChange: setValue,
children: (0, import_jsx_runtime3.jsx)(
Root,
{
asChild: true,
...rovingFocusGroupScope,
orientation,
dir: direction,
loop,
children: (0, import_jsx_runtime3.jsx)(
Primitive.div,
{
role: "radiogroup",
"aria-required": required,
"aria-orientation": orientation,
"data-disabled": disabled ? "" : void 0,
dir: direction,
...groupProps,
ref: forwardedRef
}
)
}
)
}
);
}
);
RadioGroup.displayName = RADIO_GROUP_NAME;
var ITEM_NAME2 = "RadioGroupItem";
var RadioGroupItem = React2.forwardRef(
(props, forwardedRef) => {
const { __scopeRadioGroup, disabled, ...itemProps } = props;
const context = useRadioGroupContext(ITEM_NAME2, __scopeRadioGroup);
const isDisabled = context.disabled || disabled;
const rovingFocusGroupScope = useRovingFocusGroupScope(__scopeRadioGroup);
const radioScope = useRadioScope(__scopeRadioGroup);
const ref = React2.useRef(null);
const composedRefs = useComposedRefs(forwardedRef, ref);
const checked = context.value === itemProps.value;
const isArrowKeyPressedRef = React2.useRef(false);
React2.useEffect(() => {
const handleKeyDown = (event) => {
if (ARROW_KEYS.includes(event.key)) {
isArrowKeyPressedRef.current = true;
}
};
const handleKeyUp = () => isArrowKeyPressedRef.current = false;
document.addEventListener("keydown", handleKeyDown);
document.addEventListener("keyup", handleKeyUp);
return () => {
document.removeEventListener("keydown", handleKeyDown);
document.removeEventListener("keyup", handleKeyUp);
};
}, []);
return (0, import_jsx_runtime3.jsx)(
Item,
{
asChild: true,
...rovingFocusGroupScope,
focusable: !isDisabled,
active: checked,
children: (0, import_jsx_runtime3.jsx)(
Radio,
{
disabled: isDisabled,
required: context.required,
checked,
...radioScope,
...itemProps,
name: context.name,
ref: composedRefs,
onCheck: () => context.onValueChange(itemProps.value),
onKeyDown: composeEventHandlers((event) => {
if (event.key === "Enter") event.preventDefault();
}),
onFocus: composeEventHandlers(itemProps.onFocus, () => {
if (isArrowKeyPressedRef.current) ref.current?.click();
})
}
)
}
);
}
);
RadioGroupItem.displayName = ITEM_NAME2;
var INDICATOR_NAME2 = "RadioGroupIndicator";
var RadioGroupIndicator = React2.forwardRef(
(props, forwardedRef) => {
const { __scopeRadioGroup, ...indicatorProps } = props;
const radioScope = useRadioScope(__scopeRadioGroup);
return (0, import_jsx_runtime3.jsx)(RadioIndicator, { ...radioScope, ...indicatorProps, ref: forwardedRef });
}
);
RadioGroupIndicator.displayName = INDICATOR_NAME2;
var Root2 = RadioGroup;
var Item2 = RadioGroupItem;
var Indicator = RadioGroupIndicator;
export {
Indicator,
Item2 as Item,
RadioGroup,
RadioGroupIndicator,
RadioGroupItem,
Root2 as Root,
createRadioGroupScope
};
//# sourceMappingURL=@radix-ui_react-radio-group.js.map
File diff suppressed because one or more lines are too long
+3534
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
+17
View File
@@ -0,0 +1,17 @@
import {
Slot,
Slottable,
createSlot,
createSlottable
} from "./chunk-YWBEB5PG.js";
import "./chunk-2VUH7NEY.js";
import "./chunk-2YVA4HRZ.js";
import "./chunk-WUR7D6NS.js";
import "./chunk-G3PMV62Z.js";
export {
Slot as Root,
Slot,
Slottable,
createSlot,
createSlottable
};

Some files were not shown because too many files have changed in this diff Show More