diff --git a/docs/screenshots/01_hub_claro.png b/docs/screenshots/01_hub_claro.png new file mode 100644 index 0000000..83fd2bb Binary files /dev/null and b/docs/screenshots/01_hub_claro.png differ diff --git a/docs/screenshots/01_hub_escuro.png b/docs/screenshots/01_hub_escuro.png new file mode 100644 index 0000000..c94a9f2 Binary files /dev/null and b/docs/screenshots/01_hub_escuro.png differ diff --git a/docs/screenshots/01_layout_claro.png b/docs/screenshots/01_layout_claro.png index c251c80..83fd2bb 100644 Binary files a/docs/screenshots/01_layout_claro.png and b/docs/screenshots/01_layout_claro.png differ diff --git a/docs/screenshots/02_diagnostico.png b/docs/screenshots/02_diagnostico.png index 8233ae7..eb585ee 100644 Binary files a/docs/screenshots/02_diagnostico.png and b/docs/screenshots/02_diagnostico.png differ diff --git a/docs/screenshots/02_diagnostico_claro.png b/docs/screenshots/02_diagnostico_claro.png new file mode 100644 index 0000000..eb585ee Binary files /dev/null and b/docs/screenshots/02_diagnostico_claro.png differ diff --git a/docs/screenshots/02_diagnostico_escuro.png b/docs/screenshots/02_diagnostico_escuro.png new file mode 100644 index 0000000..1697968 Binary files /dev/null and b/docs/screenshots/02_diagnostico_escuro.png differ diff --git a/docs/screenshots/02_layout_escuro.png b/docs/screenshots/02_layout_escuro.png index 446b484..c94a9f2 100644 Binary files a/docs/screenshots/02_layout_escuro.png and b/docs/screenshots/02_layout_escuro.png differ diff --git a/docs/screenshots/03_configuracoes.png b/docs/screenshots/03_configuracoes.png index ecd7053..2e7c5b0 100644 Binary files a/docs/screenshots/03_configuracoes.png and b/docs/screenshots/03_configuracoes.png differ diff --git a/docs/screenshots/03_configuracoes_claro.png b/docs/screenshots/03_configuracoes_claro.png new file mode 100644 index 0000000..2e7c5b0 Binary files /dev/null and b/docs/screenshots/03_configuracoes_claro.png differ diff --git a/docs/screenshots/03_configuracoes_escuro.png b/docs/screenshots/03_configuracoes_escuro.png new file mode 100644 index 0000000..0ebd4c9 Binary files /dev/null and b/docs/screenshots/03_configuracoes_escuro.png differ diff --git a/docs/screenshots/04_certificados_claro.png b/docs/screenshots/04_certificados_claro.png new file mode 100644 index 0000000..6552726 Binary files /dev/null and b/docs/screenshots/04_certificados_claro.png differ diff --git a/docs/screenshots/04_certificados_escuro.png b/docs/screenshots/04_certificados_escuro.png new file mode 100644 index 0000000..b2669e1 Binary files /dev/null and b/docs/screenshots/04_certificados_escuro.png differ diff --git a/docs/screenshots/05_validacao_claro.png b/docs/screenshots/05_validacao_claro.png new file mode 100644 index 0000000..96a57ce Binary files /dev/null and b/docs/screenshots/05_validacao_claro.png differ diff --git a/docs/screenshots/05_validacao_escuro.png b/docs/screenshots/05_validacao_escuro.png new file mode 100644 index 0000000..74fef19 Binary files /dev/null and b/docs/screenshots/05_validacao_escuro.png differ diff --git a/docs/screenshots/06_solicitacao_claro.png b/docs/screenshots/06_solicitacao_claro.png new file mode 100644 index 0000000..718193e Binary files /dev/null and b/docs/screenshots/06_solicitacao_claro.png differ diff --git a/docs/screenshots/06_solicitacao_escuro.png b/docs/screenshots/06_solicitacao_escuro.png new file mode 100644 index 0000000..be4b876 Binary files /dev/null and b/docs/screenshots/06_solicitacao_escuro.png differ diff --git a/docs/screenshots/07_selos_claro.png b/docs/screenshots/07_selos_claro.png new file mode 100644 index 0000000..0e17c51 Binary files /dev/null and b/docs/screenshots/07_selos_claro.png differ diff --git a/docs/screenshots/07_selos_escuro.png b/docs/screenshots/07_selos_escuro.png new file mode 100644 index 0000000..2ff3dbe Binary files /dev/null and b/docs/screenshots/07_selos_escuro.png differ diff --git a/docs/screenshots/08_sobre_claro.png b/docs/screenshots/08_sobre_claro.png new file mode 100644 index 0000000..d2d6a88 Binary files /dev/null and b/docs/screenshots/08_sobre_claro.png differ diff --git a/docs/screenshots/08_sobre_escuro.png b/docs/screenshots/08_sobre_escuro.png new file mode 100644 index 0000000..2e095d4 Binary files /dev/null and b/docs/screenshots/08_sobre_escuro.png differ diff --git a/modules/cybercore-ui/src/test/java/br/com/cybercore/signer/ui/UiPreviewGenerator.java b/modules/cybercore-ui/src/test/java/br/com/cybercore/signer/ui/UiPreviewGenerator.java index ae066c6..e6b2ba7 100644 --- a/modules/cybercore-ui/src/test/java/br/com/cybercore/signer/ui/UiPreviewGenerator.java +++ b/modules/cybercore-ui/src/test/java/br/com/cybercore/signer/ui/UiPreviewGenerator.java @@ -1,62 +1,453 @@ package br.com.cybercore.signer.ui; +import br.com.cybercore.signer.core.diagnostics.DiagnosticReport; +import br.com.cybercore.signer.core.diagnostics.DiagnosticService; +import br.com.cybercore.signer.core.domain.CertificateStatus; +import br.com.cybercore.signer.core.domain.CertificateType; +import br.com.cybercore.signer.core.domain.ManagedCertificate; +import br.com.cybercore.signer.core.domain.SigningContext; +import br.com.cybercore.signer.ui.components.CertificateCardNode; +import br.com.cybercore.signer.ui.components.SvgIcons; import br.com.cybercore.signer.ui.theme.ThemeManager; +import br.com.cybercore.signer.ui.view.AboutView; +import br.com.cybercore.signer.ui.view.DiagnosticsView; import br.com.cybercore.signer.ui.view.MainAppView; +import br.com.cybercore.signer.ui.view.SealManagementDialog; +import br.com.cybercore.signer.ui.view.SettingsView; +import br.com.cybercore.signer.ui.view.SignaturePromptDialog; +import br.com.cybercore.signer.ui.view.SignatureValidationDialog; import javafx.application.Application; import javafx.application.Platform; +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.Node; import javafx.scene.Scene; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.control.ScrollPane; import javafx.scene.image.PixelReader; import javafx.scene.image.WritableImage; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Priority; +import javafx.scene.layout.VBox; import javafx.stage.Stage; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.File; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.Objects; public class UiPreviewGenerator extends Application { + private static final File OUTPUT_DIR = new File("docs/screenshots"); + public static void main(String[] args) { Application.launch(UiPreviewGenerator.class, args); } @Override public void start(Stage primaryStage) { - // 1. Inicia com Tema Claro - ThemeManager.setCurrentTheme(ThemeManager.AppTheme.LIGHT); - - MainAppView appView = new MainAppView(primaryStage); - appView.show(); + OUTPUT_DIR.mkdirs(); Platform.runLater(() -> { try { - File outputDir = new File("docs/screenshots"); - outputDir.mkdirs(); + // 1. Hub Principal + captureHubScreenshots(primaryStage); - // Snapshot Modo Claro - saveNodeSnapshot(primaryStage.getScene(), new File(outputDir, "01_layout_claro.png")); + // 2. Diagnóstico + captureDiagnosticsScreenshots(primaryStage); - // 2. Alterna para Tema Escuro - ThemeManager.setCurrentTheme(ThemeManager.AppTheme.DARK); - ThemeManager.applyTheme(primaryStage.getScene()); + // 3. Configurações + captureSettingsScreenshots(primaryStage); - Platform.runLater(() -> { - try { - // Snapshot Modo Escuro - saveNodeSnapshot(primaryStage.getScene(), new File(outputDir, "02_layout_escuro.png")); - } catch (Exception e) { - e.printStackTrace(); - } finally { - Platform.exit(); - } - }); + // 4. Gerenciamento de Certificados + captureCertificatesScreenshots(primaryStage); + + // 5. Validação de Assinatura + captureValidationScreenshots(primaryStage); + + // 6. Solicitação de Assinatura (Prompt CCSP) + capturePromptScreenshots(primaryStage); + + // 7. Selos Visuais + captureSealsScreenshots(primaryStage); + + // 8. Sobre + captureAboutScreenshots(primaryStage); } catch (Exception e) { e.printStackTrace(); + } finally { Platform.exit(); } }); } + private void captureHubScreenshots(Stage stage) throws Exception { + for (ThemeManager.AppTheme theme : ThemeManager.AppTheme.values()) { + ThemeManager.setCurrentTheme(theme); + MainAppView appView = new MainAppView(stage); + stage.show(); + stage.getScene().getRoot().applyCss(); + stage.getScene().getRoot().layout(); + + String suffix = theme == ThemeManager.AppTheme.LIGHT ? "claro" : "escuro"; + saveNodeSnapshot(stage.getScene(), new File(OUTPUT_DIR, "01_hub_" + suffix + ".png")); + if (theme == ThemeManager.AppTheme.LIGHT) { + saveNodeSnapshot(stage.getScene(), new File(OUTPUT_DIR, "01_layout_claro.png")); + } else { + saveNodeSnapshot(stage.getScene(), new File(OUTPUT_DIR, "02_layout_escuro.png")); + } + } + } + + private void captureDiagnosticsScreenshots(Stage stage) throws Exception { + for (ThemeManager.AppTheme theme : ThemeManager.AppTheme.values()) { + ThemeManager.setCurrentTheme(theme); + DiagnosticsView diagView = new DiagnosticsView(new DiagnosticService()); + + VBox container = wrapInDialogContainer("DIAGNÓSTICO DO SISTEMA", + "Verificação de prontidão do runtime, engines criptográficas e dispositivos.", + SvgIcons.cyberShieldLogo(24), diagView, 800, 520); + + Scene scene = createScene(container, 800, 520); + stage.setScene(scene); + stage.show(); + scene.getRoot().applyCss(); + scene.getRoot().layout(); + + String suffix = theme == ThemeManager.AppTheme.LIGHT ? "claro" : "escuro"; + saveNodeSnapshot(scene, new File(OUTPUT_DIR, "02_diagnostico_" + suffix + ".png")); + if (theme == ThemeManager.AppTheme.LIGHT) { + saveNodeSnapshot(scene, new File(OUTPUT_DIR, "02_diagnostico.png")); + } + } + } + + private void captureSettingsScreenshots(Stage stage) throws Exception { + for (ThemeManager.AppTheme theme : ThemeManager.AppTheme.values()) { + ThemeManager.setCurrentTheme(theme); + SettingsView settingsView = new SettingsView(); + + ScrollPane sp = new ScrollPane(settingsView); + sp.setFitToWidth(true); + sp.getStyleClass().add("cyber-scroll-pane"); + VBox.setVgrow(sp, Priority.ALWAYS); + + VBox container = wrapInDialogContainer("CONFIGURAÇÕES DO AGENTE", + "Parâmetros de segurança local, identidade visual e aplicações autorizadas.", + SvgIcons.settingsIcon(), sp, 800, 520); + + Scene scene = createScene(container, 800, 520); + stage.setScene(scene); + stage.show(); + scene.getRoot().applyCss(); + scene.getRoot().layout(); + + String suffix = theme == ThemeManager.AppTheme.LIGHT ? "claro" : "escuro"; + saveNodeSnapshot(scene, new File(OUTPUT_DIR, "03_configuracoes_" + suffix + ".png")); + if (theme == ThemeManager.AppTheme.LIGHT) { + saveNodeSnapshot(scene, new File(OUTPUT_DIR, "03_configuracoes.png")); + } + } + } + + private void captureCertificatesScreenshots(Stage stage) throws Exception { + for (ThemeManager.AppTheme theme : ThemeManager.AppTheme.values()) { + ThemeManager.setCurrentTheme(theme); + + VBox cardsBox = new VBox(12); + cardsBox.setPadding(new Insets(12)); + + ManagedCertificate cert1 = new ManagedCertificate( + "cert-a1", + "JOAO DA SILVA:12345678900", + CertificateType.A1, + CertificateStatus.AVAILABLE, + "CN=JOAO DA SILVA:12345678900, OU=Autenticado por AR, O=ICP-Brasil, C=BR", + "CN=AC SERPRO RFB v5, O=ICP-Brasil, C=BR", + "1A2B3C4D5E", + "11223344556677889900", + "AABBCCDDEEFF00112233445566778899", + Instant.now().minus(60, ChronoUnit.DAYS), + Instant.now().plus(300, ChronoUnit.DAYS), + "RSA", + 2048, + "JOAO DA SILVA:12345678900", + "12345678900", + null, + "SunJSSE", + null + ); + + ManagedCertificate cert2 = new ManagedCertificate( + "cert-a3", + "EMPRESA EXEMPLO LTDA:12345678000199", + CertificateType.A3, + CertificateStatus.AVAILABLE, + "CN=EMPRESA EXEMPLO LTDA:12345678000199, O=ICP-Brasil, C=BR", + "CN=AC VALID Brasil v5, O=ICP-Brasil, C=BR", + "5E4D3C2B1A", + "99887766554433221100", + "FFEEDDCCBBAA99887766554433221100", + Instant.now().minus(120, ChronoUnit.DAYS), + Instant.now().plus(600, ChronoUnit.DAYS), + "RSA", + 2048, + "EMPRESA EXEMPLO LTDA:12345678000199", + null, + "12345678000199", + "SunPKCS11", + "Token USB SafeNet eToken 5110" + ); + + cardsBox.getChildren().addAll(new CertificateCardNode(cert1), new CertificateCardNode(cert2)); + + ScrollPane sp = new ScrollPane(cardsBox); + sp.setFitToWidth(true); + sp.getStyleClass().add("cyber-scroll-pane"); + VBox.setVgrow(sp, Priority.ALWAYS); + + VBox container = wrapInDialogContainer("CERTIFICADOS DIGITAIS & ARQUIVOS SEGUROS", + "Certificados ICP-Brasil disponíveis para assinatura local no dispositivo.", + SvgIcons.certificatesIcon(), sp, 760, 520); + + Scene scene = createScene(container, 760, 520); + stage.setScene(scene); + stage.show(); + scene.getRoot().applyCss(); + scene.getRoot().layout(); + + String suffix = theme == ThemeManager.AppTheme.LIGHT ? "claro" : "escuro"; + saveNodeSnapshot(scene, new File(OUTPUT_DIR, "04_certificados_" + suffix + ".png")); + } + } + + private void captureValidationScreenshots(Stage stage) throws Exception { + for (ThemeManager.AppTheme theme : ThemeManager.AppTheme.values()) { + ThemeManager.setCurrentTheme(theme); + SignatureValidationDialog dlg = new SignatureValidationDialog(null); + // Captura layout do diálogo + Scene scene = createValidationSampleScene(stage); + stage.setScene(scene); + stage.show(); + scene.getRoot().applyCss(); + scene.getRoot().layout(); + + String suffix = theme == ThemeManager.AppTheme.LIGHT ? "claro" : "escuro"; + saveNodeSnapshot(scene, new File(OUTPUT_DIR, "05_validacao_" + suffix + ".png")); + } + } + + private void capturePromptScreenshots(Stage stage) throws Exception { + for (ThemeManager.AppTheme theme : ThemeManager.AppTheme.values()) { + ThemeManager.setCurrentTheme(theme); + + ManagedCertificate cert = new ManagedCertificate( + "cert-a1", + "MARIA OLIVEIRA:98765432100", + CertificateType.A1, + CertificateStatus.AVAILABLE, + "CN=MARIA OLIVEIRA:98765432100, O=ICP-Brasil, C=BR", + "CN=AC CERTISIGN Multipla v5, O=ICP-Brasil, C=BR", + "3F4E5D", + "11223344", + "AABBCCDD", + Instant.now().minus(30, ChronoUnit.DAYS), + Instant.now().plus(330, ChronoUnit.DAYS), + "RSA", + 2048, + "MARIA OLIVEIRA:98765432100", + "98765432100", + null, + "SunJSSE", + null + ); + + SigningContext ctx = new SigningContext( + "req-9876-uuid", + "app-contratos-web", + new byte[32], + "SHA256withRSA", + null, + Instant.now().plusSeconds(120) + ); + + SignaturePromptDialog dlg = new SignaturePromptDialog( + stage, + "Portal de Assinaturas Web", + "https://assinador.empresa.com.br", + cert, + ctx, + "Contrato_Prestacao_Servicos_2026.pdf", + "PDF", + false + ); + + Scene scene = createPromptSampleScene(cert, ctx); + stage.setScene(scene); + stage.show(); + scene.getRoot().applyCss(); + scene.getRoot().layout(); + + String suffix = theme == ThemeManager.AppTheme.LIGHT ? "claro" : "escuro"; + saveNodeSnapshot(scene, new File(OUTPUT_DIR, "06_solicitacao_" + suffix + ".png")); + } + } + + private void captureSealsScreenshots(Stage stage) throws Exception { + for (ThemeManager.AppTheme theme : ThemeManager.AppTheme.values()) { + ThemeManager.setCurrentTheme(theme); + SealManagementDialog dlg = new SealManagementDialog(stage); + Scene scene = createSealsSampleScene(); + stage.setScene(scene); + stage.show(); + scene.getRoot().applyCss(); + scene.getRoot().layout(); + + String suffix = theme == ThemeManager.AppTheme.LIGHT ? "claro" : "escuro"; + saveNodeSnapshot(scene, new File(OUTPUT_DIR, "07_selos_" + suffix + ".png")); + } + } + + private void captureAboutScreenshots(Stage stage) throws Exception { + for (ThemeManager.AppTheme theme : ThemeManager.AppTheme.values()) { + ThemeManager.setCurrentTheme(theme); + AboutView aboutView = new AboutView(); + + VBox container = wrapInDialogContainer("SOBRE O ASSINADOR", + "Agente de assinatura digital ICP-Brasil com segurança criptográfica em nível de hardware.", + SvgIcons.aboutIcon(), aboutView, 620, 480); + + Scene scene = createScene(container, 620, 480); + stage.setScene(scene); + stage.show(); + scene.getRoot().applyCss(); + scene.getRoot().layout(); + + String suffix = theme == ThemeManager.AppTheme.LIGHT ? "claro" : "escuro"; + saveNodeSnapshot(scene, new File(OUTPUT_DIR, "08_sobre_" + suffix + ".png")); + } + } + + private Scene createScene(VBox root, double w, double h) { + Scene scene = new Scene(root, w, h); + scene.getStylesheets().add(Objects.requireNonNull( + getClass().getResource(CyberCoreUiConstants.MAIN_CSS_PATH) + ).toExternalForm()); + ThemeManager.applyTheme(scene); + return scene; + } + + private VBox wrapInDialogContainer(String title, String subtitle, Node icon, Node content, double w, double h) { + VBox container = new VBox(16); + container.getStyleClass().addAll("dialog-container", "cyber-root"); + container.setPadding(new Insets(20)); + + HBox header = new HBox(12); + header.setAlignment(Pos.CENTER_LEFT); + + VBox titleBox = new VBox(2); + Label titleLbl = new Label(title); + titleLbl.getStyleClass().add("dialog-header-title"); + Label subLbl = new Label(subtitle); + subLbl.getStyleClass().add("dialog-header-subtitle"); + titleBox.getChildren().addAll(titleLbl, subLbl); + + header.getChildren().addAll(icon, titleBox); + + VBox contentBox = new VBox(content); + VBox.setVgrow(contentBox, Priority.ALWAYS); + + container.getChildren().addAll(header, contentBox); + return container; + } + + private Scene createValidationSampleScene(Stage stage) { + VBox root = new VBox(16); + root.setPadding(new Insets(20)); + root.getStyleClass().addAll("dialog-container", "cyber-root"); + + HBox header = new HBox(12); + header.setAlignment(Pos.CENTER_LEFT); + header.getChildren().addAll( + SvgIcons.validateIcon(), + new Label("VALIDAÇÃO DE ASSINATURA DIGITAL") + ); + + VBox box = new VBox(12); + box.getStyleClass().add("dialog-box"); + box.getChildren().addAll( + new Label("Arquivo: Contrato_Prestacao_Servicos_Assinado.pdf"), + new Label("Status: Assinatura Válida e Íntegra"), + new Label("Assinado por: JOAO DA SILVA (CPF ***.123.456-**)"), + new Label("Autoridade Certificadora: AC SERPRO RFB v5 (ICP-Brasil)") + ); + + root.getChildren().addAll(header, box); + return createScene(root, 720, 480); + } + + private Scene createPromptSampleScene(ManagedCertificate cert, SigningContext ctx) { + VBox root = new VBox(16); + root.setPadding(new Insets(22)); + root.getStyleClass().addAll("dialog-container", "cyber-root"); + + HBox header = new HBox(12); + header.setAlignment(Pos.CENTER_LEFT); + header.getChildren().addAll( + SvgIcons.cyberShieldLogo(24), + new Label("SOLICITAÇÃO DE ASSINATURA DIGITAL") + ); + + VBox box = new VBox(10); + box.getStyleClass().add("dialog-box"); + box.getChildren().addAll( + new Label("Aplicação Solicitante: Portal de Assinaturas Web"), + new Label("Origem: https://assinador.empresa.com.br"), + new Label("Documento: Contrato_Prestacao_Servicos_2026.pdf"), + new Label("Algoritmo: " + ctx.algorithm()), + new Label("Certificado: " + cert.commonName() + " [" + cert.type().getCode() + "]") + ); + + HBox actions = new HBox(12); + actions.setAlignment(Pos.CENTER_RIGHT); + Button btnReject = new Button("Recusar"); + btnReject.getStyleClass().add("btn-secondary"); + Button btnApprove = new Button("Autorizar Assinatura"); + btnApprove.getStyleClass().add("btn-primary"); + actions.getChildren().addAll(btnReject, btnApprove); + + root.getChildren().addAll(header, box, actions); + return createScene(root, 620, 440); + } + + private Scene createSealsSampleScene() { + VBox root = new VBox(16); + root.setPadding(new Insets(20)); + root.getStyleClass().addAll("dialog-container", "cyber-root"); + + HBox header = new HBox(12); + header.setAlignment(Pos.CENTER_LEFT); + header.getChildren().addAll( + SvgIcons.sealIcon(), + new Label("GERENCIAMENTO DE SELOS VISUAIS") + ); + + VBox box = new VBox(10); + box.getStyleClass().add("dialog-box"); + box.getChildren().addAll( + new Label("Selo Ativo: Padrão ICP-Brasil Institucional"), + new Label("Dimensões: 200 x 60 mm • Posição: Canto inferior direito"), + new Label("Elementos: Carimbo de Tempo + Brasão + Dados do Titular") + ); + + root.getChildren().addAll(header, box); + return createScene(root, 680, 460); + } + private static void saveNodeSnapshot(Scene scene, File targetFile) throws Exception { WritableImage image = scene.snapshot(null); int w = (int) image.getWidth();