// SPDX-License-Identifier: Apache-2.0
import { AboutSlint, Button, CheckBox, ComboBox, GroupBox, LineEdit, ListView, StandardListView, TabWidget } from "std-widgets.slint";
export struct GuiUser {
id: int,
uname: string,
email: string,
fname: string,
lname: string,
active: bool,
readonly: bool,
}
export struct GuiGroup {
id: int,
name: string,
description: string,
parent: string,
files: int,
users: int,
sources: int,
}
export struct GuiSource {
id: int,
name: string,
description: string,
url: string,
parent: string,
files: int,
groups: int,
malicious: bool,
}
export struct GuiLabel {
id: int,
name: string,
parent: string,
}
component AdminTabs {
width: 440px;
height: 380px;
in-out property<int> users-index: UsersList.current-item;
in-out property<[GuiUser]> users;
in-out property<[StandardListViewItem]> users_list <=> UsersList.model;
in-out property<string> user-status <=> UserStatus.text;
out property<string> user-name: UNameEdit.text;
out property<string> user-fname: FNameEdit.text;
out property<string> user-lname: LNameEdit.text;
out property<string> user-email: EmailEdit.text;
out property<string> user-password: PasswordChange.text;
out property<bool> user-readonly: UserRO.checked;
callback users-save-button-pressed <=> UserSaveButton.clicked;
in-out property<int> groups-index: GroupsList.current-item;
in-out property<[GuiGroup]> groups;
in-out property<[string]> groups_strings;
in-out property<[string]> groups_strings_parent;
in-out property<[StandardListViewItem]> groups_list <=> GroupsList.model;
in-out property<string> group-status <=> GroupsStatus.text;
out property<string> group-name: GNameEdit.text;
out property<string> group-description: GroupDescriptionEdit.text;
out property<string> group-parent: GroupParentCombo.current-value;
callback groups-save-button-pressed <=> GroupSaveButton.clicked;
in-out property<int> sources-index: SourcesList.current-item;
in-out property<[GuiSource]> sources;
in-out property<[string]> sources_strings;
in-out property<[string]> sources_strings_parent;
in-out property<[StandardListViewItem]> sources_list <=> SourcesList.model;
in-out property<string> source-status <=> SourcesStatus.text;
in-out property<int> labels-index: LabelsList.current-item;
in-out property<[string]> labels_strings_parent;
in-out property<[GuiLabel]> labels;
in-out property<[StandardListViewItem]> labels_list <=> LabelsList.model;
in-out property<string> label-status <=> LabelsStatus.text;
out property<string> label-id <=> LabelIDEdit.text;
out property<string> label-name <=> LabelNameEdit.text;
out property<string> label-parent <=> LabelParentCombo.current-value;
callback label-save-button-pressed <=> LabelSaveButton.clicked;
TabWidget {
Tab {
title: "Users";
HorizontalLayout {
alignment: start;
spacing: 10px;
UsersList := StandardListView {
width: 150px;
height: 150px;
model: [ ];
current-item-changed => {
UNameEdit.text = "\{root.users[UsersList.current-item].uname}";
FNameEdit.text = "\{root.users[UsersList.current-item].fname}";
LNameEdit.text = "\{root.users[UsersList.current-item].lname}";
EmailEdit.text = "\{root.users[UsersList.current-item].email}";
UserRO.checked = root.users[UsersList.current-item].readonly;
PasswordChange.text = "";
PasswordConfirm.text = "";
}
}
GridLayout {
spacing: 8px;
Row {
Text {
text: "User ID";
height: 30px;
}
// Change to integer input type when this is released
// https://github.com/slint-ui/slint/pull/3377
UserIDEdit := LineEdit {
text: "\{root.users[UsersList.current-item].id}";
enabled: false;
height: 30px;
}
}
Row {
Text {
text: "User Name";
height: 30px;
}
UNameEdit := LineEdit {
text: "\{root.users[UsersList.current-item].uname}";
enabled: root.users[UsersList.current-item].id != 0;
placeholder-text: "user name";
height: 30px;
}
}
Row {
Text {
text: "First Name";
height: 30px;
}
FNameEdit := LineEdit {
text: "\{root.users[UsersList.current-item].fname}";
placeholder-text: "first name";
height: 30px;
}
}
Row {
Text {
text: "Last Name";
height: 30px;
}
LNameEdit := LineEdit {
text: "\{root.users[UsersList.current-item].lname}";
placeholder-text: "last name";
height: 30px;
}
}
Row {
Text {
text: "Email";
height: 30px;
}
EmailEdit := LineEdit {
text: "\{root.users[UsersList.current-item].email}";
placeholder-text: "user's email";
height: 30px;
}
}
Row {
Text {
text: "Change\npassword";
height: 40px;
}
PasswordChange := LineEdit {
placeholder-text: "user's password";
input-type: password;
height: 30px;
}
}
Row {
Text {
text: "Confirm\npassword";
height: 40px;
}
PasswordConfirm := LineEdit {
placeholder-text: "user's password";
input-type: password;
height: 30px;
}
}
Row {
UserRO := CheckBox {
text: "User is read-only";
}
}
Row {
Button {
text: "Reset";
clicked => {
UNameEdit.text = "\{root.users[UsersList.current-item].uname}";
FNameEdit.text = "\{root.users[UsersList.current-item].fname}";
LNameEdit.text = "\{root.users[UsersList.current-item].lname}";
EmailEdit.text = "\{root.users[UsersList.current-item].email}";
UserRO.checked = root.users[UsersList.current-item].readonly;
PasswordChange.text = "";
PasswordConfirm.text = "";
UserStatus.text = "";
}
height: 30px;
}
UserSaveButton := Button {
text: "Save";
enabled: PasswordChange.text == PasswordConfirm.text;
height: 30px;
}
}
UserStatus := Text {
text: "";
height: 20px;
font-size: 10pt;
color: #FF0000;
colspan: 2;
}
}
}
}
Tab {
title: "Groups";
HorizontalLayout {
alignment: start;
spacing: 10px;
GroupsList := StandardListView {
width: 146px;
height: 150px;
model: [ ];
current-item-changed => {
GroupIDEdit.text = "\{root.groups[GroupsList.current-item].id}";
GNameEdit.text = "\{root.groups[GroupsList.current-item].name}";
GroupDescriptionEdit.text = "\{root.groups[GroupsList.current-item].description}";
GroupParentCombo.current-value = "\{root.groups[GroupsList.current-item].parent}";
GNumFilesEdit.text = "\{root.groups[GroupsList.current-item].files}";
GNumUsersEdit.text = "\{root.groups[GroupsList.current-item].users}";
GNumSourcesEdit.text = "\{root.groups[GroupsList.current-item].sources}";
}
}
GridLayout {
spacing: 8px;
Row {
Text {
text: "ID";
height: 30px;
}
// Change to integer input type when this is released
// https://github.com/slint-ui/slint/pull/3377
GroupIDEdit := LineEdit {
text: "\{root.groups[GroupsList.current-item].id}";
enabled: false;
height: 30px;
}
}
Row {
Text {
text: "Name";
height: 30px;
}
GNameEdit := LineEdit {
text: "\{root.groups[GroupsList.current-item].name}";
enabled: root.groups[GroupsList.current-item].id != 0;
placeholder-text: "group name";
height: 30px;
}
}
Row {
Text {
text: "Description";
height: 30px;
}
GroupDescriptionEdit := LineEdit {
text: "\{root.groups[GroupsList.current-item].description}";
enabled: root.groups[GroupsList.current-item].id != 0;
placeholder-text: "group description";
height: 30px;
}
}
Row {
Text {
text: "Parent";
height: 30px;
}
GroupParentCombo := ComboBox {
enabled: root.groups[GroupsList.current-item].id != 0;
model: groups_strings_parent;
height: 30px;
}
}
Row {
Text {
text: "Files";
height: 30px;
}
GNumFilesEdit := LineEdit {
enabled: false;
height: 30px;
}
}
Row {
Text {
text: "Users";
height: 30px;
}
GNumUsersEdit := LineEdit {
enabled: false;
height: 30px;
}
}
Row {
Text {
text: "Sources";
height: 30px;
}
GNumSourcesEdit := LineEdit {
enabled: false;
height: 30px;
}
}
Row {
Button {
text: "Reset";
clicked => {
GNameEdit.text = "\{root.groups[GroupsList.current-item].name}";
GroupParentCombo.current-value = "\{root.groups[GroupsList.current-item].parent}";
GroupDescriptionEdit.text = "\{root.groups[GroupsList.current-item].description}";
GroupsStatus.text = "";
}
height: 30px;
}
GroupSaveButton := Button {
text: "Save";
enabled: GroupParentCombo.current-value != root.groups[GroupsList.current-item].name;
height: 30px;
}
}
GroupsStatus := Text {
text: "";
height: 20px;
font-size: 10pt;
color: #FF0000;
colspan: 2;
}
}
}
}
Tab {
title: "Sources";
HorizontalLayout {
SourcesList := StandardListView {
width: 150px;
height: 150px;
model: [ ];
current-item-changed => {
SourceParentCombo.current-value = "\{root.sources[SourcesList.current-item].parent}";
SNumFilesEdit.text = "\{root.sources[SourcesList.current-item].files}";
SNumGroupsEdit.text = "\{root.sources[SourcesList.current-item].groups}";
}
}
GridLayout {
spacing: 8px;
Row {
Text {
text: "ID";
height: 30px;
}
// Change to integer input type when this is released
// https://github.com/slint-ui/slint/pull/3377
SourceIDEdit := LineEdit {
text: "\{root.sources[SourcesList.current-item].id}";
enabled: false;
height: 30px;
}
}
Row {
Text {
text: "Name";
height: 30px;
}
SNameEdit := LineEdit {
text: "\{root.sources[SourcesList.current-item].name}";
enabled: root.sources[SourcesList.current-item].id != 0;
placeholder-text: "source name";
height: 30px;
}
}
Row {
Text {
text: "Description";
height: 30px;
}
SourceDescriptionEdit := LineEdit {
text: "\{root.sources[SourcesList.current-item].description}";
enabled: root.sources[SourcesList.current-item].id != 0;
placeholder-text: "source description";
height: 30px;
}
}
Row {
Text {
text: "URL";
height: 30px;
}
SourceURLEdit := LineEdit {
text: "\{root.sources[SourcesList.current-item].url}";
placeholder-text: "source URL";
height: 30px;
}
}
Row {
Text {
text: "Parent";
height: 30px;
}
SourceParentCombo := ComboBox {
enabled: root.sources[SourcesList.current-item].id != 0;
model: sources_strings_parent;
height: 30px;
}
}
Row {
Text {
text: "Files";
height: 30px;
}
SNumFilesEdit := LineEdit {
enabled: false;
height: 30px;
}
}
Row {
Text {
text: "Groups";
height: 30px;
}
SNumGroupsEdit := LineEdit {
enabled: false;
height: 30px;
}
}
SourcesStatus := Text {
text: "";
height: 20px;
font-size: 10pt;
color: #FF0000;
colspan: 2;
}
}
}
}
Tab {
title: "Labels";
HorizontalLayout {
LabelsList := StandardListView {
width: 150px;
height: 150px;
model: [ ];
current-item-changed => {
LabelParentCombo.current-value = "\{root.labels[LabelsList.current-item].parent}";
LabelIDEdit.text = "\{root.labels[LabelsList.current-item].id}";
LabelNameEdit.text = "\{root.labels[LabelsList.current-item].name}";
}
}
GridLayout {
spacing: 8px;
Row {
Text {
text: "ID";
height: 30px;
}
// Change to integer input type when this is released
// https://github.com/slint-ui/slint/pull/3377
LabelIDEdit := LineEdit {
text: "\{root.labels[LabelsList.current-item].id}";
enabled: false;
height: 30px;
}
}
Row {
Text {
text: "Name";
height: 30px;
}
LabelNameEdit := LineEdit {
text: "\{root.labels[LabelsList.current-item].name}";
placeholder-text: "label name";
height: 30px;
}
}
Row {
Text {
text: "Parent";
height: 30px;
}
LabelParentCombo := ComboBox {
model: labels_strings_parent;
height: 30px;
}
}
Row {
Button {
text: "Reset";
clicked => {
LabelIDEdit.text = "\{root.labels[LabelsList.current-item].id}";
LabelNameEdit.text = "\{root.labels[LabelsList.current-item].name}";
LabelParentCombo.current-value = "\{root.labels[LabelsList.current-item].parent}";
LabelsStatus.text = "";
}
height: 30px;
}
LabelSaveButton := Button {
text: "Save";
enabled: LabelParentCombo.current-value != root.labels[LabelsList.current-item].name;
height: 30px;
}
}
LabelsStatus := Text {
text: "";
height: 20px;
font-size: 10pt;
color: #FF0000;
colspan: 2;
}
}
}
}
Tab {
title: "Slint";
AboutSlint{}
}
}
}
export component AdminWindow inherits Window {
width: 440px;
height: 390px;
title: "MalwareDB Admin";
in-out property<int> users-index <=> gui.users-index;
in-out property<[GuiUser]> users <=> gui.users;
in-out property<[StandardListViewItem]> users_list <=> gui.users_list;
in-out property<string> user-status <=> gui.user-status;
out property<string> user-name: gui.user-name;
out property<string> user-fname: gui.user-fname;
out property<string> user-lname: gui.user-lname;
out property<string> user-email: gui.user-email;
out property<string> user-password: gui.user-password;
out property<bool> user-readonly: gui.user-readonly;
callback users-save-button-pressed <=> gui.users-save-button-pressed;
in-out property<int> groups-index <=> gui.groups-index;
in-out property<[GuiGroup]> groups <=> gui.groups;
in-out property<[string]> groups_strings <=> gui.groups_strings;
in-out property<[string]> groups_strings_parent <=> gui.groups_strings_parent;
in-out property<[StandardListViewItem]> groups_list <=> gui.groups_list;
in-out property<string> group-status <=> gui.group-status;
out property<string> group-name: gui.group-name;
out property<string> group-description: gui.group-description;
out property<string> group-parent: gui.group-parent;
callback groups-save-button-pressed <=> gui.groups-save-button-pressed;
in-out property<int> sources-index <=> gui.sources-index;
in-out property<[GuiSource]> sources <=> gui.sources;
in-out property<[string]> sources_strings <=> gui.sources_strings;
in-out property<[string]> sources_strings_parent <=> gui.sources_strings_parent;
in-out property<[StandardListViewItem]> sources_list <=> gui.sources_list;
in-out property<string> source-status <=> gui.source-status;
in-out property<int> labels-index <=> gui.labels-index;
in-out property<[string]> labels_strings_parent <=> gui.labels_strings_parent;
in-out property<[GuiLabel]> labels <=> gui.labels;
in-out property<[StandardListViewItem]> labels_list <=> gui.labels_list;
in-out property<string> label-status <=> gui.label-status;
callback label-save-button-pressed <=> gui.label-save-button-pressed;
out property<string> label-id <=> gui.label-id;
out property<string> label-name <=> gui.label-name;
out property<string> label-parent <=> gui.label-parent;
gui := AdminTabs {}
}