-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathBankingApp.java
More file actions
463 lines (390 loc) · 20.2 KB
/
BankingApp.java
File metadata and controls
463 lines (390 loc) · 20.2 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
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.chart.PieChart;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.stage.Stage;
// Import your custom packages
import models.*;
import accountTypes.*;
import database.FileHandler;
import java.util.List;
import java.util.Optional;
public class BankingApp extends Application {
// 1. GLOBAL VARIABLES & COLORS (Must be at top!)
private List<Account> approvedAccounts;
private List<Account> pendingAccounts;
private int accountCounter = 1001;
private Stage window;
private Scene loginScene;
// COLORS (Defined here so ALL methods can see them)
final String COLOR_PRIMARY = "#2980b9"; // Blue
final String COLOR_DARK = "#2c3e50"; // Navy
final String COLOR_BG = "#ecf0f1"; // Light Grey
final String COLOR_BTN = "-fx-background-color: #2980b9; -fx-text-fill: white; -fx-background-radius: 5; -fx-cursor: hand;";
final String COLOR_BTN_RED = "-fx-background-color: #c0392b; -fx-text-fill: white; -fx-background-radius: 5; -fx-cursor: hand;";
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
window = primaryStage;
window.setTitle("Smart Student Banking Management System ");
// Load Data
loadData();
// Initialize Scenes
initLoginScene();
// Show Window
window.setScene(loginScene);
window.show();
// Save on Exit
window.setOnCloseRequest(e -> saveData());
}
// 2. DATA LOADING
private void loadData() {
approvedAccounts = FileHandler.loadAccounts(false);
pendingAccounts = FileHandler.loadAccounts(true);
// Sync Counter
for(Account a : approvedAccounts) if(a.getAccountNumber() >= accountCounter) accountCounter = a.getAccountNumber() + 1;
for(Account a : pendingAccounts) if(a.getAccountNumber() >= accountCounter) accountCounter = a.getAccountNumber() + 1;
}
private void saveData() {
FileHandler.saveAccounts(approvedAccounts, false);
FileHandler.saveAccounts(pendingAccounts, true);
}
// 3. LOGIN SCENE (With Logo & Tabs)
private void initLoginScene() {
VBox layout = new VBox(20);
layout.setAlignment(Pos.CENTER);
layout.setStyle("-fx-background-color: " + COLOR_BG + ";");
layout.setPadding(new Insets(40));
// --- THE LOGO ---
StackPane logo = createLogo();
// Header
Label title = new Label("Student Banking Portal");
title.setFont(Font.font("Arial", FontWeight.BOLD, 28));
title.setTextFill(Color.web(COLOR_DARK));
// Tabs
TabPane tabs = new TabPane();
tabs.setTabClosingPolicy(TabPane.TabClosingPolicy.UNAVAILABLE);
tabs.setStyle("-fx-background-color: white; -fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.1), 10, 0, 0, 0);");
tabs.getTabs().addAll(
new Tab("Student Login", createStudentLoginForm()),
new Tab("Admin Login", createAdminLoginForm())
);
// Register Button
Button btnRegister = new Button("Create New Account");
btnRegister.setStyle("-fx-background-color: transparent; -fx-text-fill: #2980b9; -fx-underline: true; -fx-cursor: hand;");
btnRegister.setOnAction(e -> showRegisterWindow());
layout.getChildren().addAll(logo, title, tabs, btnRegister);
loginScene = new Scene(layout, 500, 680);
}
// --- Student Login Form (With Smart Checks) ---
private VBox createStudentLoginForm() {
VBox box = new VBox(15);
box.setPadding(new Insets(30));
box.setAlignment(Pos.CENTER);
TextField txtId = new TextField(); txtId.setPromptText("Account ID");
PasswordField txtPin = new PasswordField(); txtPin.setPromptText("4-Digit PIN");
Button btnLogin = new Button("Login");
btnLogin.setStyle(COLOR_BTN);
btnLogin.setPrefWidth(200);
btnLogin.setOnAction(e -> {
try {
int id = Integer.parseInt(txtId.getText());
int pin = Integer.parseInt(txtPin.getText());
// 1. Check Approved
Account activeUser = findApprovedAccount(id);
if (activeUser != null) {
if (activeUser.validatePin(pin)) {
window.setScene(createStudentDashboard(activeUser));
} else {
showAlert(Alert.AlertType.ERROR, "Login Failed", "Wrong Password (PIN).");
}
}
// 2. Check Pending (The Logic You Wanted)
else {
Account pendingUser = findPendingAccount(id);
if (pendingUser != null) {
showAlert(Alert.AlertType.WARNING, "Pending Approval", "Admin has not approved this account yet.\nPlease check back later.");
} else {
showAlert(Alert.AlertType.ERROR, "Login Failed", "Invalid Account Number.");
}
}
} catch (Exception ex) {
showAlert(Alert.AlertType.ERROR, "Input Error", "Please enter numeric ID and PIN.");
}
});
box.getChildren().addAll(new Label("Account ID"), txtId, new Label("PIN"), txtPin, btnLogin);
return box;
}
// --- Admin Login Form ---
private VBox createAdminLoginForm() {
VBox box = new VBox(15);
box.setPadding(new Insets(30));
box.setAlignment(Pos.CENTER);
TextField txtUser = new TextField(); txtUser.setPromptText("Username");
PasswordField txtPass = new PasswordField(); txtPass.setPromptText("Password");
Button btnLogin = new Button("Admin Login");
btnLogin.setStyle(COLOR_BTN_RED);
btnLogin.setPrefWidth(200);
btnLogin.setOnAction(e -> {
Admin admin = new Admin();
if (admin.login(txtUser.getText(), txtPass.getText())) {
window.setScene(createAdminDashboard());
} else {
showAlert(Alert.AlertType.ERROR, "Access Denied", "Invalid Admin Credentials");
}
});
box.getChildren().addAll(new Label("Username"), txtUser, new Label("Password"), txtPass, btnLogin);
return box;
}
// 4. STUDENT DASHBOARD
private Scene createStudentDashboard(Account user) {
BorderPane layout = new BorderPane();
layout.setStyle("-fx-background-color: " + COLOR_BG + ";");
HBox top = new HBox(20);
top.setPadding(new Insets(15));
top.setAlignment(Pos.CENTER_LEFT);
top.setStyle("-fx-background-color: " + COLOR_PRIMARY + ";");
Label lblName = new Label("Welcome, " + user.getName());
lblName.setTextFill(Color.WHITE);
lblName.setFont(Font.font(18));
Button btnLogout = new Button("Logout");
btnLogout.setOnAction(e -> window.setScene(loginScene));
Region spacer = new Region(); HBox.setHgrow(spacer, Priority.ALWAYS);
top.getChildren().addAll(lblName, spacer, btnLogout);
layout.setTop(top);
VBox center = new VBox(20);
center.setPadding(new Insets(30));
center.setAlignment(Pos.TOP_CENTER);
Label lblBal = new Label("$ " + user.getBalance());
lblBal.setFont(Font.font("Arial", FontWeight.BOLD, 40));
lblBal.setTextFill(Color.web(COLOR_DARK));
GridPane grid = new GridPane();
grid.setHgap(20); grid.setVgap(20);
grid.setAlignment(Pos.CENTER);
Button btnDep = createActionButton("Deposit", "#27ae60");
Button btnWit = createActionButton("Withdraw", "#c0392b");
Button btnTra = createActionButton("Transfer", "#e67e22");
Button btnHis = createActionButton("History", "#2980b9");
btnDep.setOnAction(e -> {
String input = showInput("Deposit", "Amount:");
if(input != null) {
try {
double amt = Double.parseDouble(input);
user.deposit(amt);
lblBal.setText("$ " + user.getBalance());
saveData();
} catch(Exception ex) { showAlert(Alert.AlertType.ERROR, "Error", "Invalid Amount"); }
}
});
btnWit.setOnAction(e -> {
String input = showInput("Withdraw", "Amount:");
if(input != null) {
try {
double amt = Double.parseDouble(input);
if(amt > user.getBalance()) { showAlert(Alert.AlertType.ERROR, "Error", "Insufficient Funds"); return;}
user.withdraw(amt);
lblBal.setText("$ " + user.getBalance());
saveData();
} catch(Exception ex) { showAlert(Alert.AlertType.ERROR, "Error", ex.getMessage()); }
}
});
btnTra.setOnAction(e -> showTransferDialog(user, lblBal));
btnHis.setOnAction(e -> showHistoryWindow(user));
grid.add(btnDep, 0, 0); grid.add(btnWit, 1, 0);
grid.add(btnTra, 0, 1); grid.add(btnHis, 1, 1);
center.getChildren().addAll(new Label("Current Balance"), lblBal, new Separator(), grid);
layout.setCenter(center);
return new Scene(layout, 600, 500);
}
// 5. ADMIN DASHBOARD
private Scene createAdminDashboard() {
BorderPane layout = new BorderPane();
HBox top = new HBox(20);
top.setPadding(new Insets(15));
top.setStyle("-fx-background-color: #2c3e50;");
Label title = new Label("ADMIN DASHBOARD");
title.setTextFill(Color.WHITE);
title.setFont(Font.font("Arial", FontWeight.BOLD, 18));
Button btnLogout = new Button("Logout");
btnLogout.setOnAction(e -> window.setScene(loginScene));
Region spacer = new Region(); HBox.setHgrow(spacer, Priority.ALWAYS);
top.getChildren().addAll(title, spacer, btnLogout);
layout.setTop(top);
TabPane tabs = new TabPane();
// TAB 1: CHART
VBox chartBox = new VBox(20);
chartBox.setAlignment(Pos.CENTER);
PieChart chart = new PieChart();
int s=0, sil=0, g=0, p=0;
for(Account a : approvedAccounts) {
if(a.getCardType().equals("Student")) s++;
else if(a.getCardType().equals("Silver")) sil++;
else if(a.getCardType().equals("Gold")) g++;
else p++;
}
ObservableList<PieChart.Data> pieData = FXCollections.observableArrayList(
new PieChart.Data("Student", s), new PieChart.Data("Silver", sil),
new PieChart.Data("Gold", g), new PieChart.Data("Premium", p)
);
chart.setData(pieData);
chart.setTitle("Accounts by Type");
chartBox.getChildren().addAll(chart, new Label("Total Active Users: " + approvedAccounts.size()));
// TAB 2: PENDING
ListView<String> pendingList = new ListView<>();
refreshPendingList(pendingList);
Button btnApprove = new Button("Approve Selected"); btnApprove.setStyle("-fx-background-color: #27ae60; -fx-text-fill: white;");
btnApprove.setOnAction(e -> {
String sel = pendingList.getSelectionModel().getSelectedItem();
if(sel != null) processPending(sel, true, pendingList, pieData);
});
Button btnReject = new Button("Reject Selected"); btnReject.setStyle("-fx-background-color: #c0392b; -fx-text-fill: white;");
btnReject.setOnAction(e -> {
String sel = pendingList.getSelectionModel().getSelectedItem();
if(sel != null) processPending(sel, false, pendingList, pieData);
});
HBox pendingActions = new HBox(10, btnApprove, btnReject); pendingActions.setAlignment(Pos.CENTER);
VBox pendingBox = new VBox(10, new Label("Pending Requests"), pendingList, pendingActions); pendingBox.setPadding(new Insets(20));
// TAB 3: ACTIVE
ListView<String> approvedList = new ListView<>();
refreshApprovedList(approvedList);
Button btnViewTrans = new Button("View User Transactions");
btnViewTrans.setStyle(COLOR_BTN);
btnViewTrans.setOnAction(e -> {
String sel = approvedList.getSelectionModel().getSelectedItem();
if(sel != null) {
int id = Integer.parseInt(sel.split(" \\| ")[0]);
Account target = findApprovedAccount(id);
if(target != null) showHistoryWindow(target);
}
});
VBox approvedBox = new VBox(10, new Label("Active Accounts"), approvedList, btnViewTrans); approvedBox.setPadding(new Insets(20));
tabs.getTabs().addAll(new Tab("Analytics", chartBox), new Tab("Pending", pendingBox), new Tab("Active", approvedBox));
layout.setCenter(tabs);
return new Scene(layout, 700, 550);
}
// 6. HELPER METHODS (Logo, Dialogs, Utils)
// --- DRAW LOGO METHOD ---
private StackPane createLogo() {
StackPane logoContainer = new StackPane();
javafx.scene.shape.Polygon roof = new javafx.scene.shape.Polygon();
roof.getPoints().addAll(0.0, 30.0, 40.0, 0.0, 80.0, 30.0);
roof.setFill(Color.web(COLOR_PRIMARY)); // Uses Global Color
HBox columns = new HBox(5);
columns.setAlignment(Pos.CENTER);
for(int i=0; i<3; i++) {
javafx.scene.shape.Rectangle col = new javafx.scene.shape.Rectangle(15, 30);
col.setFill(Color.web(COLOR_DARK)); // Uses Global Color
columns.getChildren().add(col);
}
javafx.scene.shape.Rectangle base = new javafx.scene.shape.Rectangle(80, 10);
base.setFill(Color.web(COLOR_PRIMARY));
VBox bankShape = new VBox(0, roof, columns, base);
bankShape.setAlignment(Pos.CENTER);
javafx.scene.shape.Circle bg = new javafx.scene.shape.Circle(60, Color.web("#bdc3c7"));
logoContainer.getChildren().addAll(bg, bankShape);
return logoContainer;
}
// --- OTHER HELPERS ---
private void processPending(String sel, boolean approve, ListView<String> list, ObservableList<PieChart.Data> chartData) {
int id = Integer.parseInt(sel.split(" \\| ")[0]);
Account target = findPendingAccount(id);
if(target != null) {
if(approve) {
target.setStatus("ACTIVE");
approvedAccounts.add(target);
for(PieChart.Data d : chartData) if(d.getName().equals(target.getCardType())) d.setPieValue(d.getPieValue() + 1);
}
pendingAccounts.remove(target);
saveData();
refreshPendingList(list);
refreshApprovedList(new ListView<>()); // dummy refresh
showAlert(Alert.AlertType.INFORMATION, "Done", approve ? "Approved!" : "Rejected.");
}
}
private void refreshPendingList(ListView<String> list) {
list.getItems().clear();
for(Account a : pendingAccounts) list.getItems().add(a.getAccountNumber() + " | " + a.getName() + " | " + a.getCardType());
}
private void refreshApprovedList(ListView<String> list) {
list.getItems().clear();
for(Account a : approvedAccounts) list.getItems().add(a.getAccountNumber() + " | " + a.getName() + " | $" + a.getBalance());
}
private void showRegisterWindow() {
Stage regStage = new Stage();
VBox layout = new VBox(15); layout.setPadding(new Insets(20)); layout.setAlignment(Pos.CENTER);
TextField txtName = new TextField(); txtName.setPromptText("Name");
TextField txtEmail = new TextField(); txtEmail.setPromptText("Email");
TextField txtUid = new TextField(); txtUid.setPromptText("Uni ID");
PasswordField txtPin = new PasswordField(); txtPin.setPromptText("PIN");
ComboBox<String> comboType = new ComboBox<>();
comboType.getItems().addAll("1. Student", "2. Silver", "3. Gold", "4. Premium");
comboType.setPromptText("Select Card Type");
Button btnSub = new Button("Register"); btnSub.setStyle(COLOR_BTN);
btnSub.setOnAction(e -> {
try {
int id = accountCounter++;
String sel = comboType.getValue();
if(sel == null) throw new Exception("Select Type");
Account newAcc = null;
if(sel.contains("Student")) newAcc = new StudentAccount(id, txtName.getText(), txtEmail.getText(), txtUid.getText(), Integer.parseInt(txtPin.getText()));
else if(sel.contains("Silver")) newAcc = new SilverAccount(id, txtName.getText(), txtEmail.getText(), txtUid.getText(), Integer.parseInt(txtPin.getText()));
else if(sel.contains("Gold")) newAcc = new GoldAccount(id, txtName.getText(), txtEmail.getText(), txtUid.getText(), Integer.parseInt(txtPin.getText()));
else newAcc = new PremiumAccount(id, txtName.getText(), txtEmail.getText(), txtUid.getText(), Integer.parseInt(txtPin.getText()));
pendingAccounts.add(newAcc);
saveData();
showAlert(Alert.AlertType.INFORMATION, "Success", "Registered! ID: " + id);
regStage.close();
} catch(Exception ex) { showAlert(Alert.AlertType.ERROR, "Error", "Invalid Input."); }
});
layout.getChildren().addAll(new Label("New Account"), txtName, txtEmail, txtUid, txtPin, comboType, btnSub);
regStage.setScene(new Scene(layout, 350, 450));
regStage.show();
}
private void showTransferDialog(Account from, Label lblBalToUpdate) {
TextInputDialog idDialog = new TextInputDialog();
idDialog.setTitle("Transfer"); idDialog.setHeaderText("Enter Target Account ID:");
Optional<String> resId = idDialog.showAndWait();
if(resId.isPresent()) {
try {
int tid = Integer.parseInt(resId.get());
if(tid == from.getAccountNumber()) { showAlert(Alert.AlertType.ERROR, "Error", "Cannot transfer to yourself."); return; }
Account target = findApprovedAccount(tid);
if(target == null) throw new Exception("Target not found.");
String amtStr = showInput("Transfer Amount", "Enter amount:");
if(amtStr != null) {
double amt = Double.parseDouble(amtStr);
if(amt > from.getBalance()) throw new Exception("Insufficient Funds");
from.transfer(target, amt);
saveData();
lblBalToUpdate.setText("$ " + from.getBalance());
showAlert(Alert.AlertType.INFORMATION, "Success", "Transfer Complete!");
}
} catch (Exception ex) { showAlert(Alert.AlertType.ERROR, "Transfer Failed", ex.getMessage()); }
}
}
private void showHistoryWindow(Account user) {
Stage hStage = new Stage();
ListView<String> list = new ListView<>();
List<Transaction> trans = user.readTransactionFile();
for(Transaction t : trans) list.getItems().add(t.toString());
VBox box = new VBox(10, new Label("History for " + user.getName()), list);
box.setPadding(new Insets(20));
hStage.setScene(new Scene(box, 400, 300));
hStage.show();
}
private Account findApprovedAccount(int id) { for(Account a : approvedAccounts) if(a.getAccountNumber() == id) return a; return null; }
private Account findPendingAccount(int id) { for(Account a : pendingAccounts) if(a.getAccountNumber() == id) return a; return null; }
private String showInput(String title, String content) { TextInputDialog d = new TextInputDialog(); d.setTitle(title); d.setContentText(content); return d.showAndWait().orElse(null); }
private void showAlert(Alert.AlertType type, String title, String content) { Alert a = new Alert(type); a.setTitle(title); a.setHeaderText(null); a.setContentText(content); a.showAndWait(); }
private Button createActionButton(String text, String color) { Button b = new Button(text); b.setPrefSize(120, 80); b.setStyle("-fx-background-color: " + color + "; -fx-text-fill: white; -fx-font-weight: bold; -fx-background-radius: 8;"); return b; }
}