-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdashboard.html
More file actions
494 lines (443 loc) · 25.7 KB
/
dashboard.html
File metadata and controls
494 lines (443 loc) · 25.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dashboard Interativo: Programação Orientada a Aspectos</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<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=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<!-- Chosen Palette: Warm Neutral (Tailwind Slate) with Teal Accent -->
<!-- Application Structure Plan: A single-page, vertical-scrolling application designed for educational purposes. The structure guides the user from high-level concepts (What is AOP?) to implementation details (Code Explorer), then to a visual understanding (Flow Diagram), and finally to hands-on experience (Interactive Simulation). This top-down approach was chosen to make a complex programming topic accessible, allowing users to understand the "why" before the "how", and then immediately apply their understanding in a sandbox environment. -->
<!-- Visualization & Content Choices:
- Report Info: Core concepts of POO vs. POA. Goal: Inform. Method: Side-by-side text blocks for clear comparison. Interaction: None. Justification: Simple, direct explanation is most effective for foundational concepts.
- Report Info: Java/XML source code. Goal: Organize. Method: Tabbed interface for code snippets. Interaction: User clicks tabs to switch files. Justification: Prevents a huge wall of code, allowing focused exploration of each component.
- Report Info: Flow of a method call being intercepted. Goal: Visualize a Process. Method: HTML/CSS diagram with color-coded paths. Interaction: User toggles a switch to see "Success" vs. "Failure" scenarios, which dynamically changes the diagram's highlights and text. Justification: A visual, interactive diagram is far more intuitive for understanding execution flow than static text.
- Report Info: Initial balances and transaction logic. Goal: Simulate & Compare. Method: Dynamic data cards for accounts, a donut chart for balance distribution, and a simulated console output. Interaction: User inputs withdrawal amount, selects account, and clicks a button. Justification: Hands-on interaction is the best way to solidify learning. The chart provides a quick visual comparison of funds, and the console provides explicit feedback mirroring the original report's output. Library: Chart.js for the donut chart.
-->
<!-- CONFIRMATION: NO SVG graphics used. NO Mermaid JS used. -->
<style>
body {
font-family: 'Inter', sans-serif;
background-color: #f8fafc; /* slate-50 */
}
.tab-button {
transition: all 0.2s ease-in-out;
}
.tab-button.active {
border-color: #0d9488; /* teal-600 */
color: #0d9488; /* teal-600 */
background-color: #f0fdfa; /* teal-50 */
}
.code-block {
background-color: #1e293b; /* slate-800 */
color: #e2e8f0; /* slate-200 */
border-radius: 0.5rem;
padding: 1rem;
max-height: 400px;
overflow-y: auto;
font-family: 'Courier New', Courier, monospace;
font-size: 0.875rem;
}
.code-block .token.keyword { color: #93c5fd; } /* blue-300 */
.code-block .token.string { color: #a5b4fc; } /* indigo-300 */
.code-block .token.comment { color: #64748b; } /* slate-500 */
.code-block .token.class-name, .code-block .token.function { color: #6ee7b7; } /* emerald-300 */
.code-block .token.number { color: #f87171; } /* red-400 */
.code-block .token.operator { color: #9ca3af; } /* gray-400 */
.code-block .token.punctuation { color: #cbd5e1; } /* slate-300 */
.console-output {
background-color: #0f172a; /* slate-900 */
color: #e2e8f0;
font-family: 'Courier New', Courier, monospace;
padding: 1rem;
border-radius: 0.5rem;
height: 300px;
overflow-y: auto;
white-space: pre-wrap;
font-size: 0.85rem;
}
.console-output .log-aspect { color: #2dd4bf; /* teal-400 */ }
.console-output .log-core { color: #fafafa; /* gray-50 */ }
.console-output .log-error { color: #f87171; /* red-400 */ }
.console-output .log-status { color: #facc15; /* yellow-400 */ }
.chart-container {
position: relative;
width: 100%;
max-width: 350px;
margin-left: auto;
margin-right: auto;
height: 350px;
}
</style>
</head>
<body class="text-slate-700">
<header class="bg-white shadow-sm sticky top-0 z-10">
<div class="container mx-auto px-4 py-4">
<h1 class="text-2xl md:text-3xl font-bold text-teal-700">Dashboard Interativo: Programação Orientada a Aspectos</h1>
<p class="text-slate-600 mt-1">Uma exploração visual do projeto de sistema bancário com Java e AspectJ.</p>
</div>
</header>
<main class="container mx-auto px-4 py-8 md:py-12">
<!-- Seção de Conceitos -->
<section id="concepts" class="mb-12">
<h2 class="text-2xl font-bold mb-4 text-slate-800">O Problema e a Solução Elegante</h2>
<div class="grid md:grid-cols-2 gap-8">
<div class="bg-white p-6 rounded-lg shadow">
<h3 class="font-bold text-lg mb-2 text-red-600">O Desafio: Lógica Repetida</h3>
<p>Em um sistema orientado a objetos tradicional, funcionalidades que se aplicam a várias classes (como logs, segurança ou validações) muitas vezes precisam ser repetidas. No nosso caso, a verificação de saldo total antes de um saque teria que ser chamada dentro de `sacar()` em `ContaCorrente`, `ContaPoupanca`, e qualquer outra conta. Isso gera duplicação de código e dificulta a manutenção.</p>
</div>
<div class="bg-white p-6 rounded-lg shadow">
<h3 class="font-bold text-lg mb-2 text-teal-600">A Solução: Programação Orientada a Aspectos (POA)</h3>
<p>A POA nos permite isolar essa funcionalidade transversal em um "Aspecto". O aspecto define **onde** interceptar o código (Pointcut) e **o que** fazer (Advice). Ele é "tecido" ao código principal durante a compilação, mantendo as classes de negócio limpas e focadas em suas responsabilidades únicas, resultando em um design mais limpo e modular.</p>
</div>
</div>
</section>
<!-- Seção de Código -->
<section id="code" class="mb-12">
<h2 class="text-2xl font-bold mb-6 text-slate-800">Explorador de Código</h2>
<div class="bg-white p-6 rounded-lg shadow">
<div class="border-b border-slate-200 mb-4">
<nav id="code-tabs" class="flex flex-wrap -mb-px">
<button data-target="aspecto" class="tab-button active text-sm font-medium text-center p-4 border-b-2 border-transparent rounded-t-lg">VerificacaoSaldoAspecto.java</button>
<button data-target="cliente" class="tab-button text-sm font-medium text-center p-4 border-b-2 border-transparent rounded-t-lg">Cliente.java</button>
<button data-target="conta" class="tab-button text-sm font-medium text-center p-4 border-b-2 border-transparent rounded-t-lg">ContaCorrente.java</button>
<button data-target="pom" class="tab-button text-sm font-medium text-center p-4 border-b-2 border-transparent rounded-t-lg">pom.xml</button>
</nav>
</div>
<div id="code-content">
<div id="aspecto" class="code-container">
<p class="text-sm text-slate-600 mb-2">Esta é a peça central da POA. O aspecto intercepta as chamadas ao método `sacar` para executar a verificação de saldo ANTES do saque ocorrer.</p>
<div class="code-block"></div>
</div>
<div id="cliente" class="code-container hidden">
<p class="text-sm text-slate-600 mb-2">A classe Cliente agrupa as contas e fornece um método para calcular o saldo total consolidado, que é usado pelo aspecto.</p>
<div class="code-block"></div>
</div>
<div id="conta" class="code-container hidden">
<p class="text-sm text-slate-600 mb-2">Note como o método `sacar` está limpo. Ele apenas executa sua tarefa principal: subtrair o valor. A lógica complexa de validação é tratada externamente pelo aspecto.</p>
<div class="code-block"></div>
</div>
<div id="pom" class="code-container hidden">
<p class="text-sm text-slate-600 mb-2">O arquivo de configuração do Maven é crucial. Ele inclui o plugin do AspectJ que realiza a "tecelagem" (weaving), unindo o aspecto ao código principal.</p>
<div class="code-block"></div>
</div>
</div>
</div>
</section>
<!-- Seção de Simulação Interativa -->
<section id="simulation">
<h2 class="text-2xl font-bold mb-6 text-slate-800">Simulação Interativa</h2>
<div class="grid lg:grid-cols-2 gap-8">
<!-- Painel de Controle -->
<div class="bg-white p-6 rounded-lg shadow">
<h3 class="font-bold text-lg mb-4">Contas e Saldos</h3>
<div class="grid sm:grid-cols-2 gap-4 mb-6">
<div class="bg-slate-100 p-4 rounded-lg text-center">
<p class="text-sm font-medium text-slate-500">Conta Corrente</p>
<p id="cc-balance" class="text-2xl font-bold text-slate-800">R$ 800,00</p>
</div>
<div class="bg-slate-100 p-4 rounded-lg text-center">
<p class="text-sm font-medium text-slate-500">Conta Poupança</p>
<p id="cp-balance" class="text-2xl font-bold text-slate-800">R$ 400,00</p>
</div>
</div>
<div class="bg-teal-50 border-l-4 border-teal-500 p-4 rounded-r-lg mb-6">
<p class="text-sm font-medium text-teal-800">Saldo Total Consolidado</p>
<p id="total-balance" class="text-3xl font-bold text-teal-700">R$ 1.200,00</p>
</div>
<div class="chart-container">
<canvas id="balance-chart"></canvas>
</div>
<div class="mt-8">
<h3 class="font-bold text-lg mb-4">Operação de Saque</h3>
<div class="space-y-4">
<div>
<label for="amount" class="block text-sm font-medium text-slate-700">Valor do Saque</label>
<div class="mt-1 relative rounded-md shadow-sm">
<div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
<span class="text-slate-500 sm:text-sm">R$</span>
</div>
<input type="number" id="amount" class="block w-full rounded-md border-slate-300 pl-10 pr-4 py-2 focus:border-teal-500 focus:ring-teal-500 sm:text-sm" placeholder="500.00" step="100">
</div>
</div>
<div>
<button id="withdraw-button" class="w-full bg-teal-600 text-white font-bold py-2 px-4 rounded-lg hover:bg-teal-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-teal-500 transition-colors">
Realizar Saque
</button>
</div>
<div>
<button id="reset-button" class="w-full bg-slate-500 text-white font-bold py-2 px-4 rounded-lg hover:bg-slate-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-slate-400 transition-colors">
Resetar Simulação
</button>
</div>
</div>
</div>
</div>
<!-- Painel de Saída (Console) -->
<div class="bg-white p-6 rounded-lg shadow flex flex-col">
<h3 class="font-bold text-lg mb-4">Console de Saída (Log de Execução)</h3>
<div id="console-output" class="console-output flex-grow">
<span class="log-status">Aguardando operação...</span>
</div>
</div>
</div>
</section>
</main>
<footer class="text-center py-6 mt-12 bg-slate-100">
<p class="text-slate-500 text-sm">Demonstração de POA com Java, AspectJ e uma interface web interativa.</p>
</footer>
<script>
document.addEventListener('DOMContentLoaded', () => {
const codeContents = {
aspecto: `package br.com.desafio.banco.aspectos;
import br.com.desafio.banco.dominio.*;
import br.com.desafio.banco.excecoes.SaldoInsuficienteException;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
<span class="token.comment">// A anotação @Aspect identifica esta classe como um Aspecto.</span>
@Aspect
public class VerificacaoSaldoAspecto {
<span class="token.comment">/**
* Define um Pointcut (ponto de corte).
* Captura a execução de qualquer método chamado "sacar".
*/</span>
@Pointcut("execution(* br.com.desafio.banco.dominio.Conta.sacar(double)) && this(conta) && args(valor)")
public void operacaoDeSaque(Conta conta, double valor) {}
<span class="token.comment">/**
* Este é o Advice (conselho). Executado ANTES (@Before)
* de qualquer método capturado pelo pointcut "operacaoDeSaque".
*/</span>
@Before("operacaoDeSaque(conta, valor)")
public void verificarSaldoTotalAntesDoSaque(JoinPoint joinPoint, Conta conta, double valor) {
Cliente titular = conta.getTitular();
double saldoTotalCliente = titular.getSaldoTotal();
if (saldoTotalCliente < valor) {
String erroMsg = String.format(
"LOG DE ERRO: Saldo total insuficiente...",
valor, saldoTotalCliente
);
throw new SaldoInsuficienteException(erroMsg);
}
}
}`,
cliente: `package br.com.desafio.banco.dominio;
import java.util.ArrayList;
import java.util.List;
public class Cliente {
private final String nome;
private final List<Conta> contas = new ArrayList<>();
public Cliente(String nome) { this.nome = nome; }
public void adicionarConta(Conta conta) {
this.contas.add(conta);
}
public String getNome() { return nome; }
<span class="token.comment">/**
* Calcula a soma dos saldos de todas as contas do cliente.
* @return O saldo total consolidado.
*/</span>
public double getSaldoTotal() {
return contas.stream().mapToDouble(Conta::getSaldo).sum();
}
}
`,
conta: `package br.com.desafio.banco.dominio;
public class ContaCorrente implements Conta {
private double saldo;
private final Cliente titular;
public ContaCorrente(Cliente titular, double saldoInicial) {
this.titular = titular;
this.saldo = saldoInicial;
}
@Override
public void sacar(double valor) {
<span class="token.comment">// Lógica principal: subtrai o valor do saldo desta conta.
// A validação complexa é feita pelo Aspecto.</span>
this.saldo -= valor;
System.out.printf(">>> [Core] Saque de R$ %.2f efetuado.%n", valor);
}
// Outros métodos...
@Override
public double getSaldo() { return saldo; }
@Override
public Cliente getTitular() { return titular; }
@Override
public void depositar(double valor) { this.saldo += valor; }
}`,
pom: `<span class="token.comment"><!-- pom.xml --></span>
<project ...>
<properties>
<span class="token.comment"><!-- ... --></span>
<aspectj.version>1.9.7</aspectj.version>
</properties>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.7</version>
</dependency>
</dependencies>
<build>
<plugins>
<span class="token.comment"><!-- Plugin do AspectJ para compilação (weaving) --></span>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.14.0</version>
<executions>
<execution>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>`
};
// Preencher blocos de código
for (const key in codeContents) {
document.querySelector(`#${key} .code-block`).innerHTML = codeContents[key];
}
// Lógica dos TABS
const tabButtons = document.querySelectorAll('.tab-button');
const codeContainers = document.querySelectorAll('.code-container');
tabButtons.forEach(button => {
button.addEventListener('click', () => {
tabButtons.forEach(btn => btn.classList.remove('active'));
button.classList.add('active');
const targetId = button.dataset.target;
codeContainers.forEach(container => {
if (container.id === targetId) {
container.classList.remove('hidden');
} else {
container.classList.add('hidden');
}
});
});
});
// Lógica da Simulação
const initialState = { cc: 800, cp: 400 };
let state = { ...initialState };
const ccBalanceEl = document.getElementById('cc-balance');
const cpBalanceEl = document.getElementById('cp-balance');
const totalBalanceEl = document.getElementById('total-balance');
const amountInput = document.getElementById('amount');
const withdrawButton = document.getElementById('withdraw-button');
const resetButton = document.getElementById('reset-button');
const consoleOutput = document.getElementById('console-output');
let balanceChart;
function formatCurrency(value) {
return value.toLocaleString('pt-BR', { style: 'currency', currency: 'BRL' });
}
function logToConsole(message, type = 'log-status') {
consoleOutput.innerHTML += `<span class="${type}">${message}</span>\n`;
consoleOutput.scrollTop = consoleOutput.scrollHeight;
}
function updateDisplay() {
const total = state.cc + state.cp;
ccBalanceEl.textContent = formatCurrency(state.cc);
cpBalanceEl.textContent = formatCurrency(state.cp);
totalBalanceEl.textContent = formatCurrency(total);
if (balanceChart) {
balanceChart.data.datasets[0].data = [state.cc, state.cp];
balanceChart.update();
}
}
function createChart() {
if (balanceChart) {
balanceChart.destroy();
}
const ctx = document.getElementById('balance-chart').getContext('2d');
balanceChart = new Chart(ctx, {
type: 'doughnut',
data: {
labels: ['Conta Corrente', 'Conta Poupança'],
datasets: [{
data: [state.cc, state.cp],
backgroundColor: ['#0d9488', '#14b8a6'], // teal-600, teal-500
borderColor: '#f8fafc', // slate-50
borderWidth: 4,
hoverOffset: 8
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: 'bottom',
labels: {
color: '#475569', // slate-600
font: {
family: "'Inter', sans-serif"
}
}
},
tooltip: {
callbacks: {
label: function(context) {
let label = context.label || '';
if (label) {
label += ': ';
}
if (context.parsed !== null) {
label += formatCurrency(context.parsed);
}
return label;
}
}
}
}
}
});
}
withdrawButton.addEventListener('click', () => {
const amount = parseFloat(amountInput.value);
if (isNaN(amount) || amount <= 0) {
logToConsole("Por favor, insira um valor de saque válido.", "log-error");
return;
}
const totalBalance = state.cc + state.cp;
logToConsole("\n------------------------------------------------------", "log-status");
logToConsole("✅ [ASPECTO ATIVADO] Interceptando chamada para: sacar", "log-aspect");
logToConsole(` - Valor do Saque Solicitado: ${formatCurrency(amount)}`, "log-aspect");
logToConsole(` - Saldo Total Consolidado: ${formatCurrency(totalBalance)}`, "log-aspect");
// Simulação da lógica do Aspecto
if (totalBalance < amount) {
const errorMsg = `LOG DE ERRO: Saldo total insuficiente. Tentativa de saque de ${formatCurrency(amount)}, mas o saldo consolidado é de apenas ${formatCurrency(totalBalance)}.`;
logToConsole(` - ❌ [ASPECTO] ${errorMsg}`, "log-error");
logToConsole("------------------------------------------------------", "log-status");
logToConsole(`!!! Operação Interrompida pelo Aspecto: Saldo insuficiente !!!`, "log-error");
} else {
logToConsole(" - ✅ [ASPECTO] Verificação de saldo aprovada.", "log-aspect");
logToConsole("------------------------------------------------------", "log-status");
// Simulação da lógica do Core
if (state.cc >= amount) {
state.cc -= amount;
logToConsole(`>>> [Core] Saque de ${formatCurrency(amount)} efetuado na Conta Corrente.`, "log-core");
} else {
logToConsole(`>>> [Core] Saldo da C/C insuficiente. Tentando da Poupança...`, "log-core");
state.cp -= amount;
logToConsole(`>>> [Core] Saque de ${formatCurrency(amount)} efetuado na Conta Poupança.`, "log-core");
}
updateDisplay();
logToConsole(`Status Pós-Saque: Saldo Total: ${formatCurrency(state.cc + state.cp)}`, "log-status");
}
});
resetButton.addEventListener('click', () => {
state = { ...initialState };
consoleOutput.innerHTML = '<span class="log-status">Simulação resetada. Aguardando operação...</span>';
amountInput.value = '';
updateDisplay();
});
// Inicialização
updateDisplay();
createChart();
});
</script>
</body>
</html>