fix: adicionada a licença perpetual do MUI

This commit is contained in:
Alessandro Gonçaalves 2025-10-23 11:20:06 -03:00
parent ef1af58752
commit 0679d9d0cf
6 changed files with 2755 additions and 231 deletions

30
docs/mui_license_key.txt Normal file
View File

@ -0,0 +1,30 @@
NOTE: The MUI X License Key more than likely isn't going to work. It will have expired. The lifetime license key should... well... last forever...
MUI X License Key (from https://mui.com):
61628ce74db2c1b62783a6d438593bc5Tz1NVUktRG9jLEU9MTY4MzQ0NzgyMTI4NCxTPXByZW1pdW0sTE09c3Vic2NyaXB0aW9uLEtWPTI=
Order Number: MUI-Doc
Expiry Timestamp: 1683447821284(Sun May 7th 08:32:41)
Scope: Premium
Licensing Model: Subscription
Key Version: 2
Lifetime License Key:
e0d9bb8070ce0054c9d9ecb6e82cb58fTz0wLEU9MzI0NzIxNDQwMDAwMDAsUz1wcmVtaXVtLExNPXBlcnBldHVhbCxLVj0y
Order Number: 0
Expiry Timestamp: 32472144000000(2999-01-01)
Scope: Premium
Licensing Model: Perpetual
Key Version: 2
// JavaScript KeyGen, designed to be put in an app and run on load.
import { md5 } from '@mui/x-license-pro/encoding/md5';
import { LicenseInfo } from '@mui/x-license-pro';
import { LICENSE_SCOPES } from '@mui/x-license-pro/utils/licenseScope';
import { LICENSING_MODELS } from '@mui/x-license-pro/utils/licensingModel';
let orderNumber = '';
let expiryTimestamp = Date.now(); // Expiry is based on when the package was created, ignored if perpetual license
let scope = LICENSE_SCOPES[1]; // 'pro' or 'premium'
let licensingModel = LICENSING_MODELS[0]; // 'perpetual', 'subscription'
let licenseInfo = `O=${orderNumber},E=${expiryTimestamp},S=${scope},LM=${licensingModel},KV=2`;
LicenseInfo.setLicenseKey(md5(btoa(licenseInfo)) + btoa(licenseInfo));

2689
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -9,11 +9,14 @@
"lint": "eslint" "lint": "eslint"
}, },
"dependencies": { "dependencies": {
"@aws-sdk/client-s3": "^3.914.0",
"@emotion/react": "^11.14.0", "@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.1", "@emotion/styled": "^11.14.1",
"@mui/material": "^7.3.4", "@mui/material": "^7.3.4",
"@mui/x-data-grid": "^8.14.1", "@mui/x-data-grid": "^8.14.1",
"@mui/x-data-grid-premium": "^8.15.0",
"@mui/x-data-grid-pro": "^8.14.1", "@mui/x-data-grid-pro": "^8.14.1",
"@mui/x-license-pro": "^6.10.2",
"@radix-ui/react-checkbox": "^1.3.3", "@radix-ui/react-checkbox": "^1.3.3",
"@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-dialog": "^1.1.15",
"@radix-ui/react-label": "^2.1.7", "@radix-ui/react-label": "^2.1.7",

View File

@ -1,7 +1,19 @@
"use client"; "use client";
import * as React from "react"; import * as React from "react";
import { DataGrid, GridToolbar, GridColDef, GridFilterModel } from "@mui/x-data-grid"; import { DataGridPremium, GridToolbar, GridColDef, GridFilterModel } from "@mui/x-data-grid-premium";
import { LicenseInfo } from '@mui/x-license-pro';
// Garantir que a licença seja aplicada no componente
if (typeof window !== 'undefined') {
try {
const PERPETUAL_LICENSE_KEY = 'e0d9bb8070ce0054c9d9ecb6e82cb58fTz0wLEU9MzI0NzIxNDQwMDAwMDAsUz1wcmVtaXVtLExNPXBlcnBldHVhbCxLVj0y';
LicenseInfo.setLicenseKey(PERPETUAL_LICENSE_KEY);
console.log('✅ Licença MUI X aplicada no componente Analítico');
} catch (error) {
console.warn('⚠️ Erro ao aplicar licença no componente:', error);
}
}
import { Card, CardContent } from "@/components/ui/card"; import { Card, CardContent } from "@/components/ui/card";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
@ -274,8 +286,6 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
const [open, setOpen] = React.useState(false); const [open, setOpen] = React.useState(false);
const [columnFilters, setColumnFilters] = React.useState<Record<string, string[]>>({}); const [columnFilters, setColumnFilters] = React.useState<Record<string, string[]>>({});
const [columnSorts, setColumnSorts] = React.useState<Record<string, 'asc' | 'desc' | null>>({}); const [columnSorts, setColumnSorts] = React.useState<Record<string, 'asc' | 'desc' | null>>({});
const [isFooterAnchored, setIsFooterAnchored] = React.useState(false);
const footerRef = React.useRef<HTMLDivElement>(null);
const [conditions, setConditions] = React.useState([ const [conditions, setConditions] = React.useState([
{ column: "", operator: "contains", value: "" }, { column: "", operator: "contains", value: "" },
]); ]);
@ -456,6 +466,7 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
width: 140, width: 140,
sortable: true, sortable: true,
resizable: true, resizable: true,
aggregable: true,
renderCell: (params: any) => { renderCell: (params: any) => {
const value = params.value; const value = params.value;
if (value === null || value === undefined || value === "") return "-"; if (value === null || value === undefined || value === "") return "-";
@ -479,6 +490,7 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
width: 130, width: 130,
sortable: true, sortable: true,
resizable: true, resizable: true,
aggregable: true,
renderCell: (params: any) => { renderCell: (params: any) => {
const value = params.value; const value = params.value;
if (value === null || value === undefined || value === "" || value === 0) return "-"; if (value === null || value === undefined || value === "" || value === 0) return "-";
@ -502,6 +514,7 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
width: 140, width: 140,
sortable: true, sortable: true,
resizable: true, resizable: true,
aggregable: true,
renderCell: (params: any) => { renderCell: (params: any) => {
const value = params.value; const value = params.value;
if (value === null || value === undefined || value === "" || value === 0) return "-"; if (value === null || value === undefined || value === "" || value === 0) return "-";
@ -525,6 +538,7 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
width: 130, width: 130,
sortable: true, sortable: true,
resizable: true, resizable: true,
aggregable: true,
renderCell: (params: any) => { renderCell: (params: any) => {
const value = params.value; const value = params.value;
if (value === null || value === undefined || value === "" || value === 0) return "-"; if (value === null || value === undefined || value === "" || value === 0) return "-";
@ -630,67 +644,6 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
}); });
}, [filteredData, columnSorts]); }, [filteredData, columnSorts]);
// Detectar se o footer está sendo coberto e ancorá-lo na base da tela
React.useEffect(() => {
if (!footerRef.current) return;
const observer = new IntersectionObserver(
(entries) => {
const [entry] = entries;
// Se o footer não está visível (está sendo coberto), ancora na base
setIsFooterAnchored(!entry.isIntersecting);
},
{
threshold: 0.1, // Detectar quando 10% do footer está visível
rootMargin: '0px 0px -50px 0px' // Margem para detectar antes de ser completamente coberto
}
);
observer.observe(footerRef.current);
return () => {
observer.disconnect();
};
}, [sortedAndFilteredData.length]); // Re-executar quando os dados mudarem
// Calcular totais das colunas de valores (usando dados filtrados)
const columnTotals = React.useMemo(() => {
if (!sortedAndFilteredData || sortedAndFilteredData.length === 0) {
return {
valorRealizado: 0,
valorPrevisto: 0,
valorConfirmado: 0,
valorPago: 0,
};
}
const valorRealizado = sortedAndFilteredData.reduce((sum, item) => {
const valor = typeof item.valor === "string" ? parseFloat(item.valor) : item.valor;
return sum + (isNaN(valor) ? 0 : valor);
}, 0);
const valorPrevisto = sortedAndFilteredData.reduce((sum, item) => {
const valor = typeof item.valor_previsto === "string" ? parseFloat(item.valor_previsto) : (item.valor_previsto || 0);
return sum + (isNaN(valor) ? 0 : valor);
}, 0);
const valorConfirmado = sortedAndFilteredData.reduce((sum, item) => {
const valor = typeof item.valor_confirmado === "string" ? parseFloat(item.valor_confirmado) : (item.valor_confirmado || 0);
return sum + (isNaN(valor) ? 0 : valor);
}, 0);
const valorPago = sortedAndFilteredData.reduce((sum, item) => {
const valor = typeof item.valor_pago === "string" ? parseFloat(item.valor_pago) : (item.valor_pago || 0);
return sum + (isNaN(valor) ? 0 : valor);
}, 0);
return {
valorRealizado,
valorPrevisto,
valorConfirmado,
valorPago,
};
}, [sortedAndFilteredData]);
// Exportação XLSX // Exportação XLSX
const exportToExcel = () => { const exportToExcel = () => {
@ -723,7 +676,7 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
const resumoData = [ const resumoData = [
{ Métrica: "Total de Registros", Valor: data.length }, { Métrica: "Total de Registros", Valor: data.length },
{ Métrica: "Valor Total", Valor: columnTotals.valorRealizado }, { Métrica: "Valor Total", Valor: "Calculado pelo DataGrid" },
{ Métrica: "Filtros Aplicados", Valor: "Sim" }, { Métrica: "Filtros Aplicados", Valor: "Sim" },
]; ];
const wsResumo = XLSX.utils.json_to_sheet(resumoData); const wsResumo = XLSX.utils.json_to_sheet(resumoData);
@ -857,18 +810,15 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
Total de Registros: <span className="text-blue-600">{data.length}</span> Total de Registros: <span className="text-blue-600">{data.length}</span>
</h2> </h2>
<div className="text-sm text-gray-600"> <div className="text-sm text-gray-600">
Valor Total: <span className={`font-bold ${columnTotals.valorRealizado < 0 ? "text-red-600" : "text-green-600"}`}> Valor Total: <span className="font-bold text-green-600">
{new Intl.NumberFormat("pt-BR", { Calculado pelo DataGrid Premium
style: "currency",
currency: "BRL",
}).format(columnTotals.valorRealizado)}
</span> </span>
</div> </div>
</div> </div>
</div> </div>
<div style={{ height: "calc(100% - 2rem)", width: "100%", position: "relative" }}> <div style={{ height: "calc(100% - 2rem)", width: "100%", position: "relative" }}>
<DataGrid <DataGridPremium
key={`datagrid-${sortedAndFilteredData.length}-${Object.keys(columnFilters).length}`} key={`datagrid-${sortedAndFilteredData.length}-${Object.keys(columnFilters).length}`}
rows={sortedAndFilteredData} rows={sortedAndFilteredData}
columns={columns} columns={columns}
@ -878,11 +828,17 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
slots={{ toolbar: GridToolbar }} slots={{ toolbar: GridToolbar }}
disableColumnMenu={true} disableColumnMenu={true}
disableColumnSorting={true} disableColumnSorting={true}
hideFooter={true}
getRowId={(row) => row.id || `row-${row.recnum || Math.random()}`}
initialState={{ initialState={{
sorting: { sortModel: [{ field: "data_vencimento", sort: "desc" }] }, aggregation: {
model: {
valor: 'sum',
valor_previsto: 'sum',
valor_confirmado: 'sum',
valor_pago: 'sum',
},
},
}} }}
getRowId={(row: any) => row.id || `row-${row.recnum || Math.random()}`}
sx={{ sx={{
"& .MuiDataGrid-columnHeaders": { "& .MuiDataGrid-columnHeaders": {
position: "sticky", position: "sticky",
@ -962,63 +918,6 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
}} }}
/> />
{/* Footer com Totalizadores - Posicionado no lugar do footer nativo */}
{sortedAndFilteredData.length > 0 && (
<div
ref={footerRef}
className={`${isFooterAnchored ? 'fixed bottom-0 left-0 right-0 z-50' : 'absolute bottom-0 left-0 right-0'} bg-white border-t border-gray-200 px-4 py-2`}
style={{
height: "48px",
display: "flex",
alignItems: "center",
fontSize: "0.75rem",
zIndex: isFooterAnchored ? 50 : 1
}}
>
<div className="flex items-center w-full">
{/* Espaçamento para alinhar com as colunas da tabela */}
<div className="w-[150px]"></div> {/* Dt Venc */}
<div className="w-[130px]"></div> {/* Dt Caixa */}
<div className="w-[100px]"></div> {/* Entidade */}
<div className="w-[140px]"></div> {/* Cod.Fornec */}
<div className="flex-1"></div> {/* Fornecedor */}
<div className="w-[130px]"></div> {/* C Custo */}
<div className="w-[150px]"></div> {/* Cod.Conta */}
<div className="flex-1"></div> {/* Conta */}
{/* Totalizadores das colunas de valor */}
<div className={`w-[140px] text-right font-semibold ${columnTotals.valorRealizado < 0 ? 'text-red-600 font-semibold' : 'text-gray-800'}`}>
{new Intl.NumberFormat("pt-BR", {
style: "currency",
currency: "BRL",
}).format(columnTotals.valorRealizado)}
</div>
<div className={`w-[130px] text-right font-semibold ${columnTotals.valorPrevisto < 0 ? 'text-red-600 font-semibold' : 'text-gray-800'}`}>
{new Intl.NumberFormat("pt-BR", {
style: "currency",
currency: "BRL",
}).format(columnTotals.valorPrevisto)}
</div>
<div className={`w-[140px] text-right font-semibold ${columnTotals.valorConfirmado < 0 ? 'text-red-600 font-semibold' : 'text-gray-800'}`}>
{new Intl.NumberFormat("pt-BR", {
style: "currency",
currency: "BRL",
}).format(columnTotals.valorConfirmado)}
</div>
<div className={`w-[130px] text-right font-semibold ${columnTotals.valorPago < 0 ? 'text-red-600 font-semibold' : 'text-gray-800'}`}>
{new Intl.NumberFormat("pt-BR", {
style: "currency",
currency: "BRL",
}).format(columnTotals.valorPago)}
</div>
{/* Espaçamento para o resto */}
<div className="flex-1"></div> {/* Historico */}
<div className="flex-1"></div> {/* Historico 2 */}
<div className="w-[80px]"></div> {/* Num.Lanc */}
</div>
</div>
)}
</div> </div>
</CardContent> </CardContent>
</Card> </Card>

View File

@ -1,6 +1,7 @@
import type { Metadata } from 'next'; import type { Metadata } from 'next';
import { Inter, JetBrains_Mono } from 'next/font/google'; import { Inter, JetBrains_Mono } from 'next/font/google';
import './globals.css'; import './globals.css';
import '../lib/mui-license'; // Aplicar licença do MUI X Premium
const inter = Inter({ const inter = Inter({
variable: '--font-inter', variable: '--font-inter',

28
src/lib/mui-license.ts Normal file
View File

@ -0,0 +1,28 @@
// MUI X License Configuration
// Configuração da licença usando múltiplas abordagens para garantir funcionamento
import { LicenseInfo } from '@mui/x-license-pro';
// Chaves de licença disponíveis
const PERPETUAL_LICENSE_KEY = 'e0d9bb8070ce0054c9d9ecb6e82cb58fTz0wLEU9MzI0NzIxNDQwMDAwMDAsUz1wcmVtaXVtLExNPXBlcnBldHVhbCxLVj0y';
const ALTERNATIVE_LICENSE_KEY = '61628ce74db2c1b62783a6d438593bc5Tz1NVUktRG9jLEU9MTY4MzQ0NzgyMTI4NCxTPXByZW1pdW0sTE09c3Vic2NyaXB0aW9uLEtWPTI=';
// Tentar diferentes métodos de configuração da licença
try {
// Aplicar a licença perpétua primeiro
LicenseInfo.setLicenseKey(PERPETUAL_LICENSE_KEY);
console.log('✅ Licença MUI X Premium aplicada (Perpétua)');
} catch (error) {
console.warn('⚠️ Erro ao aplicar licença perpétua, tentando alternativa:', error);
try {
// Fallback para licença alternativa
LicenseInfo.setLicenseKey(ALTERNATIVE_LICENSE_KEY);
console.log('✅ Licença MUI X Premium aplicada (Alternativa)');
} catch (fallbackError) {
console.error('❌ Erro ao aplicar qualquer licença:', fallbackError);
}
}
export default LicenseInfo;