Manage Group UI + backend implemented.
This commit is contained in:
@@ -12,10 +12,12 @@ import javafx.scene.layout.*;
|
||||
import org.json.JSONObject;
|
||||
import org.to.telegramfinalproject.Client.ActionHandler;
|
||||
import org.to.telegramfinalproject.Client.AvatarLocalResolver;
|
||||
import org.to.telegramfinalproject.Client.Session;
|
||||
import org.to.telegramfinalproject.Models.ChatEntry;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.UUID;
|
||||
|
||||
public class GroupInfoController {
|
||||
|
||||
@@ -55,6 +57,10 @@ public class GroupInfoController {
|
||||
if (infoMoreMenu != null) infoMoreMenu.show(infoMoreButton, javafx.geometry.Side.BOTTOM, 0, 0);
|
||||
});
|
||||
|
||||
if (manageGroupItem != null) {
|
||||
manageGroupItem.setOnAction(e -> openManageGroupScene());
|
||||
}
|
||||
|
||||
deleteGroupItem.setOnAction(e -> handleDeleteGroup());
|
||||
|
||||
Platform.runLater(() -> {
|
||||
@@ -100,15 +106,26 @@ public class GroupInfoController {
|
||||
groupName.setText(data.optString("group_name", entry.getName()));
|
||||
memberCount.setText(data.optInt("member_count", 0) + " members");
|
||||
|
||||
// show/hide Delete button if user is owner
|
||||
String myRole = data.optString("my_role", "member");
|
||||
if ("owner".equalsIgnoreCase(myRole)) {
|
||||
deleteGroupItem.setVisible(true);
|
||||
} else {
|
||||
deleteGroupItem.setVisible(false);
|
||||
// --- Role-based UI ---
|
||||
String myRole = data.optString("my_role", "member").toLowerCase();
|
||||
|
||||
// Delete group → only owner
|
||||
deleteGroupItem.setVisible("owner".equals(myRole));
|
||||
|
||||
// Add member (button + menu item) → owner or admin
|
||||
boolean canAdd = "owner".equals(myRole) || "admin".equals(myRole);
|
||||
addMemberButton.setVisible(canAdd);
|
||||
addMemberButton.setManaged(canAdd);
|
||||
if (addMemberItem != null) {
|
||||
addMemberItem.setVisible(canAdd);
|
||||
}
|
||||
|
||||
// Group picture
|
||||
// More menu (3-dot) → hide entirely for plain members
|
||||
boolean showMore = "owner".equals(myRole) || "admin".equals(myRole);
|
||||
infoMoreButton.setVisible(showMore);
|
||||
infoMoreButton.setManaged(showMore);
|
||||
|
||||
// --- Group picture ---
|
||||
String imgUrl = data.optString("image_url", "");
|
||||
if (!imgUrl.isBlank()) {
|
||||
try {
|
||||
@@ -122,7 +139,7 @@ public class GroupInfoController {
|
||||
);
|
||||
}
|
||||
|
||||
// Members
|
||||
// --- Members list ---
|
||||
membersList.getChildren().clear();
|
||||
var arr = data.optJSONArray("members");
|
||||
if (arr != null) {
|
||||
@@ -191,6 +208,66 @@ public class GroupInfoController {
|
||||
membersList.getChildren().add(row);
|
||||
}
|
||||
|
||||
private void openManageGroupScene() {
|
||||
new Thread(() -> {
|
||||
try {
|
||||
JSONObject req = new JSONObject()
|
||||
.put("action", "view_group")
|
||||
.put("group_id", groupId)
|
||||
.put("viewer_id", Session.getUserUUID());
|
||||
|
||||
JSONObject resp = ActionHandler.sendWithResponse(req);
|
||||
|
||||
if (resp == null || !"success".equalsIgnoreCase(resp.optString("status"))) {
|
||||
Platform.runLater(() -> MainController.getInstance().showAlert(
|
||||
"Error",
|
||||
resp != null ? resp.optString("message") : "Server not responding.",
|
||||
Alert.AlertType.ERROR
|
||||
));
|
||||
return;
|
||||
}
|
||||
|
||||
JSONObject data = resp.optJSONObject("data");
|
||||
if (data == null) {
|
||||
Platform.runLater(() -> MainController.getInstance().showAlert(
|
||||
"Error",
|
||||
"Malformed server response.",
|
||||
Alert.AlertType.ERROR
|
||||
));
|
||||
return;
|
||||
}
|
||||
|
||||
Platform.runLater(() -> {
|
||||
try {
|
||||
FXMLLoader loader = new FXMLLoader(getClass().getResource(
|
||||
"/org/to/telegramfinalproject/Fxml/manage_group.fxml"));
|
||||
Node overlay = loader.load();
|
||||
|
||||
ManageGroupController controller = loader.getController();
|
||||
controller.setGroupData(data); // pass the full JSON
|
||||
|
||||
MainController.getInstance().showOverlay(overlay);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
MainController.getInstance().showAlert(
|
||||
"Error",
|
||||
"Could not load Manage Group scene.",
|
||||
Alert.AlertType.ERROR
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Platform.runLater(() -> MainController.getInstance().showAlert(
|
||||
"Error",
|
||||
"Error while fetching group info: " + e.getMessage(),
|
||||
Alert.AlertType.ERROR
|
||||
));
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
private void openAddMemberScene() {
|
||||
try {
|
||||
FXMLLoader loader = new FXMLLoader(getClass().getResource(
|
||||
@@ -198,8 +275,7 @@ public class GroupInfoController {
|
||||
Node overlay = loader.load();
|
||||
|
||||
AddMembersController controller = loader.getController();
|
||||
// 👇 Pass group id so AddMember scene knows which group to add to
|
||||
controller.setGroupForAdd(groupId, groupName);
|
||||
controller.setGroupForAdd(UUID.fromString(groupId), String.valueOf(groupName));
|
||||
|
||||
MainController.getInstance().showOverlay(overlay);
|
||||
} catch (IOException e) {
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
package org.to.telegramfinalproject.UI;
|
||||
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.fxml.FXMLLoader;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.layout.*;
|
||||
import javafx.stage.FileChooser;
|
||||
import org.json.JSONObject;
|
||||
import org.to.telegramfinalproject.Client.ActionHandler;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
|
||||
public class ManageGroupController {
|
||||
|
||||
@FXML private VBox manageGroupCard;
|
||||
@FXML private Pane overlayBackground;
|
||||
@FXML private Button closeButton, changePicButton, cancelButton, saveButton;
|
||||
@FXML private ImageView groupImage;
|
||||
@FXML private TextField groupNameField;
|
||||
@FXML private Label adminCount, memberCount;
|
||||
@FXML private TextField groupIdField;
|
||||
@FXML private Button manageAdminsButton;
|
||||
@FXML private Button manageMembersButton;
|
||||
|
||||
private String groupId; // Internal UUID of the current group
|
||||
private String originalName;
|
||||
private String originalGroupId;
|
||||
private String originalImageUrl;
|
||||
private File selectedImageFile;
|
||||
|
||||
private JSONObject data;
|
||||
|
||||
@FXML
|
||||
public void initialize() {
|
||||
closeButton.setOnAction(e -> MainController.getInstance().closeOverlay(manageGroupCard.getParent()));
|
||||
cancelButton.setOnAction(e -> MainController.getInstance().closeOverlay(manageGroupCard.getParent()));
|
||||
overlayBackground.setOnMouseClicked(e -> MainController.getInstance().closeOverlay(manageGroupCard.getParent()));
|
||||
|
||||
changePicButton.setOnAction(e -> choosePicture());
|
||||
saveButton.setOnAction(e -> saveChanges());
|
||||
|
||||
manageAdminsButton.setOnAction(e -> openManageAdminsScene());
|
||||
manageMembersButton.setOnAction(e -> openManageMembersScene(data));
|
||||
}
|
||||
|
||||
private void openManageAdminsScene() {
|
||||
try {
|
||||
FXMLLoader loader = new FXMLLoader(getClass().getResource(
|
||||
"/org/to/telegramfinalproject/Fxml/manage_admins.fxml"));
|
||||
Node overlay = loader.load();
|
||||
|
||||
// ManageAdminsController controller = loader.getController();
|
||||
// controller.setGroupId(UUID.fromString(groupId));
|
||||
|
||||
MainController.getInstance().showOverlay(overlay);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
MainController.getInstance().showAlert(
|
||||
"Error", "Could not load Manage Admins scene.", Alert.AlertType.ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
private void openManageMembersScene(JSONObject data) {
|
||||
try {
|
||||
FXMLLoader loader = new FXMLLoader(getClass().getResource(
|
||||
"/org/to/telegramfinalproject/Fxml/manage_members.fxml"));
|
||||
Node overlay = loader.load();
|
||||
|
||||
ManageMembersController controller = loader.getController();
|
||||
// Pass groupId and members list from the JSON data
|
||||
controller.setGroupData(
|
||||
data.optString("internal_uuid"),
|
||||
data.optJSONArray("members")
|
||||
);
|
||||
|
||||
MainController.getInstance().showOverlay(overlay);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
MainController.getInstance().showAlert(
|
||||
"Error", "Could not load Manage Members scene.", Alert.AlertType.ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
public void setGroupData(JSONObject data) {
|
||||
this.data = data;
|
||||
this.groupId = data.optString("internal_uuid");
|
||||
|
||||
originalName = data.optString("group_name", "");
|
||||
originalGroupId = data.optString("group_id", "");
|
||||
originalImageUrl = data.optString("image_url", "");
|
||||
|
||||
groupNameField.setText(originalName);
|
||||
groupIdField.setText(originalGroupId);
|
||||
|
||||
if (!originalImageUrl.isBlank()) {
|
||||
groupImage.setImage(new Image(originalImageUrl, true));
|
||||
}
|
||||
|
||||
adminCount.setText(String.valueOf(countRole(data, "admin")));
|
||||
memberCount.setText(String.valueOf(countRole(data, "member")));
|
||||
}
|
||||
|
||||
private int countRole(JSONObject groupData, String role) {
|
||||
var arr = groupData.optJSONArray("members");
|
||||
if (arr == null) return 0;
|
||||
int count = 0;
|
||||
for (int i = 0; i < arr.length(); i++) {
|
||||
if (role.equalsIgnoreCase(arr.getJSONObject(i).optString("role"))) count++;
|
||||
}
|
||||
return count + 1;
|
||||
}
|
||||
|
||||
private void choosePicture() {
|
||||
FileChooser fc = new FileChooser();
|
||||
fc.setTitle("Select group picture");
|
||||
selectedImageFile = fc.showOpenDialog(manageGroupCard.getScene().getWindow());
|
||||
if (selectedImageFile != null) {
|
||||
groupImage.setImage(new Image(selectedImageFile.toURI().toString()));
|
||||
}
|
||||
}
|
||||
|
||||
private void saveChanges() {
|
||||
String newName = groupNameField.getText().trim();
|
||||
String newGroupId = groupIdField.getText().trim(); // make sure you added this field
|
||||
String newImageUrl = (selectedImageFile != null)
|
||||
? selectedImageFile.toURI().toString()
|
||||
: originalImageUrl;
|
||||
|
||||
// Check if anything actually changed
|
||||
boolean changed =
|
||||
!Objects.equals(originalName, newName) ||
|
||||
!Objects.equals(originalGroupId, newGroupId) ||
|
||||
!Objects.equals(originalImageUrl, newImageUrl);
|
||||
|
||||
if (!changed) {
|
||||
// Nothing changed → just close overlay
|
||||
MainController.getInstance().closeOverlay(manageGroupCard.getParent());
|
||||
return;
|
||||
}
|
||||
|
||||
// Build request with all fields (server expects them)
|
||||
JSONObject req = new JSONObject()
|
||||
.put("action", "edit_group_info")
|
||||
.put("group_id", groupId) // internal_uuid
|
||||
.put("new_group_id", newGroupId) // display id
|
||||
.put("name", newName);
|
||||
|
||||
if (newImageUrl != null && !newImageUrl.isBlank()) {
|
||||
req.put("image_url", newImageUrl);
|
||||
} else {
|
||||
req.put("image_url", JSONObject.NULL);
|
||||
}
|
||||
|
||||
JSONObject resp = ActionHandler.sendWithResponse(req);
|
||||
if (resp != null && "success".equalsIgnoreCase(resp.optString("status"))) {
|
||||
MainController.getInstance().closeOverlay(manageGroupCard.getParent());
|
||||
} else {
|
||||
String msg = (resp != null)
|
||||
? resp.optString("message", "Failed to update group")
|
||||
: "No response from server";
|
||||
Alert a = new Alert(Alert.AlertType.ERROR, msg, ButtonType.OK);
|
||||
a.show();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -135,7 +135,8 @@ public class NewChannelController {
|
||||
if (resp == null || !"success".equalsIgnoreCase(resp.optString("status"))) {
|
||||
Platform.runLater(() -> {
|
||||
createButton.setDisable(false);
|
||||
showToast("Create failed: " + (resp == null ? "no response" : resp.optString("message","")));
|
||||
channelIdField.getStyleClass().add("error");
|
||||
channelIdLabel.getStyleClass().add("error");
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -110,8 +110,10 @@ public class NewGroupController {
|
||||
new Thread(() -> {
|
||||
JSONObject resp = ActionHandler.sendWithResponse(req);
|
||||
if (resp == null || !"success".equalsIgnoreCase(resp.optString("status"))) {
|
||||
Platform.runLater(() -> showToast("Create failed: " +
|
||||
(resp == null ? "no response" : resp.optString("message",""))));
|
||||
Platform.runLater(() -> {
|
||||
groupIdField.getStyleClass().add("error");
|
||||
groupIdLabel.getStyleClass().add("error");
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -182,7 +184,4 @@ public class NewGroupController {
|
||||
a.initOwner(overlayRoot.getScene().getWindow());
|
||||
a.show();
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1188,3 +1188,99 @@
|
||||
-fx-font-size: 13px;
|
||||
-fx-text-fill: #4fa8f0; /* same Telegram blue */
|
||||
}
|
||||
|
||||
/* Inputs */
|
||||
.input-field, .input-area {
|
||||
-fx-background-color: transparent;
|
||||
-fx-border-width: 0 0 1 0;
|
||||
-fx-border-color: #444; /* subtle underline */
|
||||
-fx-text-fill: #e6e6e6; /* light text */
|
||||
-fx-padding: 4 0 4 0;
|
||||
-fx-font-size: 14px;
|
||||
}
|
||||
.input-area {
|
||||
-fx-font-size: 12px;
|
||||
}
|
||||
|
||||
/* Info rows */
|
||||
.info-row-label {
|
||||
-fx-font-size: 14px;
|
||||
-fx-text-fill: #e6e6e6;
|
||||
}
|
||||
.info-row-value {
|
||||
-fx-font-size: 14px;
|
||||
-fx-text-fill: #4fa8f0; /* Telegram blue */
|
||||
-fx-font-weight: bold;
|
||||
}
|
||||
|
||||
/* Buttons */
|
||||
.primary-btn {
|
||||
-fx-background-color: #4fa8f0;
|
||||
-fx-text-fill: white;
|
||||
-fx-font-weight: bold;
|
||||
-fx-background-radius: 6;
|
||||
-fx-padding: 6 14;
|
||||
}
|
||||
.secondary-btn {
|
||||
-fx-background-color: transparent;
|
||||
-fx-border-color: #8ea1b2;
|
||||
-fx-border-radius: 6;
|
||||
-fx-text-fill: #e6e6e6;
|
||||
-fx-padding: 6 14;
|
||||
}
|
||||
.circle-pic-btn {
|
||||
-fx-background-color: transparent;
|
||||
-fx-padding: 0;
|
||||
}
|
||||
|
||||
.manage-link-btn {
|
||||
-fx-background-color: transparent;
|
||||
-fx-text-fill: #4fa8f0; /* brighter blue for dark mode */
|
||||
-fx-underline: false;
|
||||
-fx-cursor: hand;
|
||||
-fx-font-size: 13px;
|
||||
-fx-padding: 0 4 0 4;
|
||||
}
|
||||
|
||||
.manage-link-btn:hover {
|
||||
-fx-underline: true;
|
||||
}
|
||||
.manage-link-btn {
|
||||
-fx-background-color: transparent;
|
||||
-fx-text-fill: #4fa8f0; /* brighter blue for dark mode */
|
||||
-fx-underline: false;
|
||||
-fx-cursor: hand;
|
||||
-fx-font-size: 13px;
|
||||
-fx-padding: 0 4 0 4;
|
||||
}
|
||||
|
||||
.manage-link-btn:hover {
|
||||
-fx-underline: true;
|
||||
}
|
||||
|
||||
/* Shared */
|
||||
.member-row {
|
||||
-fx-alignment: CENTER_LEFT;
|
||||
-fx-padding: 6 0;
|
||||
}
|
||||
|
||||
.member-name {
|
||||
-fx-font-size: 14px;
|
||||
-fx-font-weight: bold;
|
||||
}
|
||||
|
||||
.member-status {
|
||||
-fx-font-size: 12px;
|
||||
-fx-text-fill: #8ea1b2;
|
||||
}
|
||||
|
||||
.link-btn {
|
||||
-fx-background-color: transparent;
|
||||
-fx-text-fill: -fx-accent;
|
||||
-fx-cursor: hand;
|
||||
-fx-padding: 0 6 0 6;
|
||||
}
|
||||
|
||||
.link-btn:hover {
|
||||
-fx-underline: true;
|
||||
}
|
||||
@@ -1178,3 +1178,87 @@
|
||||
-fx-font-size: 13px;
|
||||
-fx-text-fill: #4fa8f0; /* Telegram blue */
|
||||
}
|
||||
|
||||
/* Inputs */
|
||||
.input-field, .input-area {
|
||||
-fx-background-color: transparent;
|
||||
-fx-border-width: 0 0 1 0;
|
||||
-fx-border-color: #d0d0d0; /* light underline */
|
||||
-fx-text-fill: #2c2c2c; /* dark text */
|
||||
-fx-padding: 4 0 4 0;
|
||||
-fx-font-size: 14px;
|
||||
}
|
||||
.input-area {
|
||||
-fx-font-size: 12px;
|
||||
}
|
||||
|
||||
/* Info rows */
|
||||
.info-row-label {
|
||||
-fx-font-size: 14px;
|
||||
-fx-text-fill: #2c2c2c;
|
||||
}
|
||||
.info-row-value {
|
||||
-fx-font-size: 14px;
|
||||
-fx-text-fill: #0078d7; /* Telegram blue */
|
||||
-fx-font-weight: bold;
|
||||
}
|
||||
|
||||
/* Buttons */
|
||||
.primary-btn {
|
||||
-fx-background-color: #0078d7;
|
||||
-fx-text-fill: white;
|
||||
-fx-font-weight: bold;
|
||||
-fx-background-radius: 6;
|
||||
-fx-padding: 6 14;
|
||||
}
|
||||
.secondary-btn {
|
||||
-fx-background-color: transparent;
|
||||
-fx-border-color: #8ea1b2;
|
||||
-fx-border-radius: 6;
|
||||
-fx-text-fill: #2c2c2c;
|
||||
-fx-padding: 6 14;
|
||||
}
|
||||
.circle-pic-btn {
|
||||
-fx-background-color: transparent;
|
||||
-fx-padding: 0;
|
||||
}
|
||||
|
||||
.manage-link-btn {
|
||||
-fx-background-color: transparent;
|
||||
-fx-text-fill: #0078d7; /* Telegram blue */
|
||||
-fx-underline: false;
|
||||
-fx-cursor: hand;
|
||||
-fx-font-size: 13px;
|
||||
-fx-padding: 0 4 0 4;
|
||||
}
|
||||
|
||||
.manage-link-btn:hover {
|
||||
-fx-underline: true;
|
||||
}
|
||||
|
||||
/* Shared */
|
||||
.member-row {
|
||||
-fx-alignment: CENTER_LEFT;
|
||||
-fx-padding: 6 0;
|
||||
}
|
||||
|
||||
.member-name {
|
||||
-fx-font-size: 14px;
|
||||
-fx-font-weight: bold;
|
||||
}
|
||||
|
||||
.member-status {
|
||||
-fx-font-size: 12px;
|
||||
-fx-text-fill: #8ea1b2;
|
||||
}
|
||||
|
||||
.link-btn {
|
||||
-fx-background-color: transparent;
|
||||
-fx-text-fill: -fx-accent;
|
||||
-fx-cursor: hand;
|
||||
-fx-padding: 0 6 0 6;
|
||||
}
|
||||
|
||||
.link-btn:hover {
|
||||
-fx-underline: true;
|
||||
}
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
<!-- ===== Header ===== -->
|
||||
<HBox alignment="CENTER_LEFT" spacing="10" styleClass="contacts-header">
|
||||
<Label text="Add Members" styleClass="contacts-title"/>
|
||||
<Label text="Add Subscribers" styleClass="contacts-title"/>
|
||||
<!-- Member count -->
|
||||
<Label fx:id="memberCountLabel" text="0 / 200000" styleClass="member-count"/>
|
||||
<Pane HBox.hgrow="ALWAYS"/> <!-- pushes elements to right -->
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.scene.layout.*?>
|
||||
<?import javafx.scene.image.ImageView?>
|
||||
<?import javafx.scene.image.Image?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
|
||||
<StackPane xmlns:fx="http://javafx.com/fxml"
|
||||
fx:controller="org.to.telegramfinalproject.UI.ManageGroupController"
|
||||
styleClass="overlay-root">
|
||||
|
||||
<!-- Background -->
|
||||
<Pane fx:id="overlayBackground" styleClass="overlay-background"/>
|
||||
|
||||
<!-- Card -->
|
||||
<VBox fx:id="manageGroupCard" styleClass="profile-card" spacing="16"
|
||||
prefWidth="360" maxWidth="360"
|
||||
prefHeight="520" maxHeight="520">
|
||||
|
||||
<!-- ===== Header ===== -->
|
||||
<HBox alignment="CENTER_LEFT" spacing="10">
|
||||
<Label text="Edit group" styleClass="profile-title" HBox.hgrow="ALWAYS"/>
|
||||
<Pane HBox.hgrow="ALWAYS"/>
|
||||
<Button fx:id="closeButton" styleClass="icon-button" text="✕"/>
|
||||
</HBox>
|
||||
|
||||
<!-- ===== Group picture + name + description ===== -->
|
||||
<VBox alignment="CENTER" spacing="12">
|
||||
<!-- Group picture -->
|
||||
<Button fx:id="changePicButton" styleClass="circle-pic-btn">
|
||||
<graphic>
|
||||
<ImageView fx:id="groupImage" fitWidth="80" fitHeight="80" preserveRatio="true">
|
||||
<image>
|
||||
<Image url="@/org/to/telegramfinalproject/Avatars/default_group_profile.png"/>
|
||||
</image>
|
||||
</ImageView>
|
||||
</graphic>
|
||||
</Button>
|
||||
|
||||
<!-- Group name -->
|
||||
<TextField fx:id="groupNameField" promptText="Group name" styleClass="input-field"/>
|
||||
|
||||
<!-- Group ID -->
|
||||
<TextField fx:id="groupIdField" promptText="Group ID (unique)" styleClass="input-field"/>
|
||||
</VBox>
|
||||
|
||||
|
||||
<!-- ===== Options ===== -->
|
||||
<VBox spacing="10" styleClass="info-blocks" VBox.vgrow="ALWAYS">
|
||||
<!-- Administrators -->
|
||||
<HBox alignment="CENTER_LEFT" spacing="10">
|
||||
<Label text="Administrators" styleClass="info-row-label"/>
|
||||
<Pane HBox.hgrow="ALWAYS"/>
|
||||
<Label fx:id="adminCount" text="0" styleClass="info-row-value"/>
|
||||
<Button fx:id="manageAdminsButton" text="Manage" styleClass="manage-link-btn"/>
|
||||
</HBox>
|
||||
|
||||
<!-- Members -->
|
||||
<HBox alignment="CENTER_LEFT" spacing="10">
|
||||
<Label text="Members" styleClass="info-row-label"/>
|
||||
<Pane HBox.hgrow="ALWAYS"/>
|
||||
<Label fx:id="memberCount" text="0" styleClass="info-row-value"/>
|
||||
<Button fx:id="manageMembersButton" text="Manage" styleClass="manage-link-btn"/>
|
||||
</HBox>
|
||||
</VBox>
|
||||
|
||||
<!-- ===== Footer ===== -->
|
||||
<HBox spacing="10" alignment="CENTER_RIGHT">
|
||||
<Button fx:id="cancelButton" text="Cancel" styleClass="secondary-btn"/>
|
||||
<Button fx:id="saveButton" text="Save" styleClass="primary-btn"/>
|
||||
</HBox>
|
||||
</VBox>
|
||||
</StackPane>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 980 B |
Binary file not shown.
|
After Width: | Height: | Size: 1021 B |
Reference in New Issue
Block a user