chore: removed useless editor

This commit is contained in:
Andrey Terentev 2025-08-19 22:28:16 +07:00
parent 5e8c357deb
commit 499029d456
30 changed files with 1 additions and 1329 deletions

3
editor/.gitignore vendored
View File

@ -1,3 +0,0 @@
node_modules
**/frontend/generated/
.vaadin-node-tasks.lock

View File

@ -1,39 +0,0 @@
plugins {
id 'org.springframework.boot' version '3.4.1'
id 'io.spring.dependency-management' version '1.1.7'
id 'com.vaadin' version '24.6.1'
}
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
ext {
set('vaadinVersion', "24.6.1")
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework:spring-aspects'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
runtimeOnly 'org.postgresql:postgresql'
implementation 'com.vaadin:vaadin-spring-boot-starter'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
dependencyManagement {
imports {
mavenBom "com.vaadin:vaadin-bom:${vaadinVersion}"
}
}
sourcesJar.dependsOn({
vaadinPrepareFrontend
})

View File

@ -1,23 +0,0 @@
-- Table definitions
create domain dialogue_package_id as
varchar(32) not null;
create domain dialogue_id as
varchar(64) not null;
create table dialogue_packages (
id dialogue_package_id primary key,
comment text not null
);
create table dialogues (
id dialogue_id primary key,
package dialogue_package_id,
comment text not null,
ctx json not null,
foreign key (package) references dialogue_packages (id)
);

View File

@ -1 +0,0 @@
-- Default inserts

View File

@ -1,2 +0,0 @@
FROM postgres:latest
COPY *.sql /docker-entrypoint-initdb.d/

View File

@ -1,13 +0,0 @@
version: "3"
services:
postgres:
image: msb3_postgresql
build:
context: ./database
restart: always
environment:
POSTGRES_DB: 'postgres'
POSTGRES_USER: 'postgres'
POSTGRES_PASSWORD: 'some_password'
ports: [ '5432:5432' ]

View File

@ -1,20 +0,0 @@
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style>
body, #outlet {
height: 100vh;
width: 100%;
margin: 0;
}
</style>
<title>MSB3 Control Panel</title>
</head>
<body>
<div id="outlet"></div>
</body>
</html>

View File

@ -1,12 +0,0 @@
package ru.dragonestia.editor;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class EditorApplication {
public static void main(String[] args) {
SpringApplication.run(EditorApplication.class, args);
}
}

View File

@ -1,158 +0,0 @@
package ru.dragonestia.editor.component;
import com.vaadin.flow.component.Unit;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.button.ButtonVariant;
import com.vaadin.flow.component.details.Details;
import com.vaadin.flow.component.html.H2;
import com.vaadin.flow.component.html.H3;
import com.vaadin.flow.component.html.Hr;
import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.Autocomplete;
import com.vaadin.flow.component.textfield.TextArea;
import com.vaadin.flow.component.textfield.TextField;
import ru.dragonestia.editor.model.DialogueContext;
import java.util.ArrayList;
import java.util.UUID;
import java.util.function.Consumer;
public class DialogEditor extends VerticalLayout {
private final DialogueContext ctx;
private final TextArea fieldComment;
private final TextArea fieldText;
private final Button buttonNewAnswer;
private final AnswersLayout layoutAnswers;
private final ArrayList<AnswerComponent> answers = new ArrayList<>();
public DialogEditor(DialogueContext ctx) {
this.ctx = ctx;
add(new H2("Редактор диалога"));
var commentDetail = new Details("Комментарий");
commentDetail.setWidth("100%");
commentDetail.add(fieldComment = createFieldComment());
commentDetail.setOpened(!(ctx.getComment() == null || ctx.getComment().isEmpty()));
add(commentDetail);
add(new Hr());
add(fieldText = createFieldText());
add(new H3("Ответы диалога"));
add(buttonNewAnswer = createButtonNewAnswer());
add(layoutAnswers = new AnswersLayout());
updateAnswers();
}
private TextArea createFieldComment() {
var field = new TextArea("Комментарий");
field.setAutocomplete(Autocomplete.OFF);
field.setWidth(100, Unit.PERCENTAGE);
field.setMinHeight(10, Unit.REM);
field.setHelperText("Здесь можно описать какой-нибудь комментарий по поводу диалога. Эта информация имеет роль заметки для разработчиков");
field.setPlaceholder("Какая-то заметка для разработчика");
if (ctx.getComment() != null) field.setValue(ctx.getComment());
return field;
}
private TextArea createFieldText() {
var field = new TextArea("Текст диалога");
field.setAutocomplete(Autocomplete.OFF);
field.setWidth(100, Unit.PERCENTAGE);
field.setMinHeight(10, Unit.REM);
if (ctx.getText() != null) field.setValue(ctx.getText());
return field;
}
private Button createButtonNewAnswer() {
var button = new Button("Добавить новый ответ", e -> {
if (answers.size() >= 4) {
Notification.show("Ответов у диалога не может быть больше 4");
return;
}
var component = new AnswerComponent();
component.onDelete = answer -> {
answers.removeIf(target -> target.uuid.equals(answer.uuid));
updateAnswers();
};
answers.add(component);
updateAnswers();
});
button.addThemeVariants(ButtonVariant.LUMO_SUCCESS, ButtonVariant.LUMO_PRIMARY);
button.setPrefixComponent(VaadinIcon.PLUS.create());
button.setWidth(100, Unit.PERCENTAGE);
return button;
}
private void updateAnswers() {
layoutAnswers.removeAll();
for (var component: answers) {
layoutAnswers.add(component);
}
}
private static class AnswersLayout extends VerticalLayout {
private AnswersLayout() {
getStyle().set("border", "2px solid #1C6EA4");
getStyle().set("border-radius", "26px");
}
}
private static class AnswerComponent extends VerticalLayout {
private final UUID uuid = UUID.randomUUID();
private final TextField fieldText;
private final TextArea fieldComment;
private Consumer<AnswerComponent> onDelete;
private AnswerComponent() {
add(fieldText = createFieldText());
var commentDetail = new Details("Комментарий");
commentDetail.setWidth("100%");
commentDetail.add(fieldComment = createFieldComment());
add(commentDetail);
add(createButtonDelete());
getStyle().set("background-color", "#EEEEEE");
getStyle().set("border-radius", "26px");
}
private TextField createFieldText() {
var field = new TextField("Текст");
field.setAutocomplete(Autocomplete.OFF);
field.setWidth(100, Unit.PERCENTAGE);
return field;
}
private TextArea createFieldComment() {
var field = new TextArea("Комментарий");
field.setAutocomplete(Autocomplete.OFF);
field.setWidth(100, Unit.PERCENTAGE);
field.setMinHeight(10, Unit.REM);
field.setHelperText("Здесь можно описать какой-нибудь комментарий по поводу ответа диалога. Эта информация имеет роль заметки для разработчиков");
field.setPlaceholder("Какая-то заметка для разработчика");
return field;
}
private Button createButtonDelete() {
var button = new Button("Удалить ответ", e -> {
if (onDelete != null) onDelete.accept(this);
});
button.addThemeVariants(ButtonVariant.LUMO_ERROR, ButtonVariant.LUMO_PRIMARY);
button.setPrefixComponent(VaadinIcon.TRASH.create());
button.setWidth(100, Unit.PERCENTAGE);
return button;
}
}
}

View File

@ -1,16 +0,0 @@
package ru.dragonestia.editor.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import ru.dragonestia.editor.controller.mapper.DialogueMapper;
import java.util.List;
@RestController
public class DialogueController {
@GetMapping("/api/dialogues")
List<DialogueMapper> allDialogues() {
return List.of(); // TODO
}
}

View File

@ -1,51 +0,0 @@
package ru.dragonestia.editor.controller.mapper;
import ru.dragonestia.editor.model.DialogueContext;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public record DialogueMapper(
String groupId,
String id,
String text,
List<Answer> answers
) {
public static DialogueMapper fromEntity(DialogueContext context) {
var buttons = new ArrayList<Answer>();
for (var button: context.getAnswers()) {
var actionData = new HashMap<String, String>();
for (var actionParam: button.getAction().getFields()) {
actionData.put(actionParam.getIdentifier(), actionParam.getValue());
}
var conditions = new ArrayList<Condition>();
for (var condition: button.getConditions()) {
var conditionData = new HashMap<String, String>();
for (var param: condition.getFields()) {
conditionData.put(param.getIdentifier(), param.getValue());
}
conditions.add(new Condition(condition.getIdentifier(), conditionData));
}
buttons.add(new Answer(button.getIdentifier(), button.getText(), button.getAction().getIdentifier(), actionData, conditions));
}
return new DialogueMapper(context.getGroupId(), context.getGroupId(), context.getText(), buttons);
}
public record Answer(
String id,
String text,
String actionId,
Map<String, String> actionData,
List<Condition> conditions
) {}
public record Condition(
String id,
Map<String, String> data
) {}
}

View File

@ -1,32 +0,0 @@
package ru.dragonestia.editor.model;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import ru.dragonestia.editor.model.converter.DialogueContextConverter;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "dialogues")
public class Dialogue {
@Id
@Column(name = "id", columnDefinition = "dialogue_id")
private String id;
@JoinColumn(name = "package")
@ManyToOne(fetch = FetchType.EAGER)
private DialoguePackage dialoguePackage;
@Column(name = "comment")
private String comment;
@Convert(converter = DialogueContextConverter.class)
@Column(name = "ctx", columnDefinition = "json")
private DialogueContext ctx;
}

View File

@ -1,35 +0,0 @@
package ru.dragonestia.editor.model;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.util.ArrayList;
import java.util.List;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class DialogueAction {
private String identifier;
private String name;
private String description;
private List<Field> fields = new ArrayList<>();
private boolean builtIn;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class Field {
private String identifier;
private String name;
private String description;
private FieldType type;
private String defaultValue;
}
}

View File

@ -1,31 +0,0 @@
package ru.dragonestia.editor.model;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.util.ArrayList;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class DialogueCondition {
private String identifier;
private String name;
private String description;
private ArrayList<Field> fields = new ArrayList<>();
@Getter
@Setter
public static class Field {
private String identifier;
private String name;
private String description;
private FieldType type;
private String defaultValue;
}
}

View File

@ -1,56 +0,0 @@
package ru.dragonestia.editor.model;
import lombok.Getter;
import lombok.Setter;
import java.util.ArrayList;
@Getter
@Setter
public class DialogueContext {
private String groupId;
private String id;
private String text;
private String comment;
private ArrayList<Answer> answers = new ArrayList<>();
@Getter
@Setter
public static class Answer {
private String identifier;
private String text;
private String comment;
private Action action;
private ArrayList<Condition> conditions = new ArrayList<>();
}
@Getter
@Setter
public static class Action {
private String identifier;
private String name;
private ArrayList<Field> fields = new ArrayList<>();
}
@Getter
@Setter
public static class Condition {
private String identifier;
private String name;
private ArrayList<Field> fields = new ArrayList<>();
}
@Getter
@Setter
public static class Field {
private String identifier;
private String name;
private FieldType type;
private String value;
}
}

View File

@ -1,26 +0,0 @@
package ru.dragonestia.editor.model;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "dialogue_packages")
public class DialoguePackage {
@Id
@Column(name = "id", columnDefinition = "dialogue_package_id")
private String id;
@Column(name = "comment")
private String comment;
}

View File

@ -1,36 +0,0 @@
package ru.dragonestia.editor.model;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import java.util.function.Predicate;
@Getter
@RequiredArgsConstructor
public enum FieldType {
STRING("Строка", input -> true),
TEXT("Текст", input -> true),
INTEGER("Целое число", input -> {
try {
Integer.parseInt(input);
return true;
} catch (Exception e) {
return false;
}
}),
BOOLEAN("Булево значение", input -> switch (input.toLowerCase()) {
case "true", "false" -> true;
default -> false;
}),
DOUBLE("Число с плавающей точкой", input -> {
try {
Double.parseDouble(input);
return true;
} catch (Exception e) {
return false;
}
});
private final String name;
private final Predicate<String> validator;
}

View File

@ -1,20 +0,0 @@
package ru.dragonestia.editor.model.converter;
import jakarta.persistence.AttributeConverter;
import jakarta.persistence.Converter;
import ru.dragonestia.editor.model.DialogueContext;
import ru.dragonestia.editor.util.JsonUtils;
@Converter
public class DialogueContextConverter implements AttributeConverter<DialogueContext, String> {
@Override
public String convertToDatabaseColumn(DialogueContext ctx) {
return JsonUtils.toJson(ctx);
}
@Override
public DialogueContext convertToEntityAttribute(String json) {
return JsonUtils.fromJson(json);
}
}

View File

@ -1,97 +0,0 @@
package ru.dragonestia.editor.page;
import com.vaadin.flow.component.Unit;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.button.ButtonVariant;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.html.H2;
import com.vaadin.flow.component.html.Paragraph;
import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.data.value.ValueChangeMode;
import ru.dragonestia.editor.page.browser.BrowserPage;
import ru.dragonestia.editor.page.browser.TabContainer;
import java.util.ArrayList;
import java.util.List;
public class DialogueGroupPage extends TabContainer {
private final String groupIdentifier;
private final TextField fieldSearch;
private final Grid<DialogueEntry> grid;
public DialogueGroupPage(BrowserPage browser, String groupIdentifier) {
super(browser, "Группа: " + groupIdentifier);
this.groupIdentifier = groupIdentifier;
add(new H2(groupIdentifier));
add(new Paragraph(
"Здесь содержатся диалоги, которые связаны с данной группой. " +
"Нажмите на диалог в таблице чтобы редактировать или удалить его. " +
"Если хотите создать новый диалог, то нажмите кнопку для создания ниже. "
));
var buttonsLayout = new HorizontalLayout();
buttonsLayout.setWidth("100%");
add(buttonsLayout);
buttonsLayout.add(createButtonNewDialogue());
buttonsLayout.add(createButtonUpdateDialogues());
add(fieldSearch = createFieldSearch());
add(grid = createGrid());
// TODO: init grid data
grid.setItems(List.of(
new DialogueEntry("test1", ""),
new DialogueEntry("test2", "а ывп выпыв пку пукфпкаырп ыварукып куруке авыфп авып"),
new DialogueEntry("fsdfdsfds", "111"),
new DialogueEntry("hello", "Приветственный диалог с игроком")
));
}
private Button createButtonUpdateDialogues() {
var button = new Button("Обновить список диалогов", VaadinIcon.REFRESH.create(), event -> {
// TODO
});
button.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
return button;
}
private Button createButtonNewDialogue() {
var button = new Button("Создать новый диалог", VaadinIcon.PLUS.create(), e -> {
// TODO
});
button.addThemeVariants(ButtonVariant.LUMO_PRIMARY, ButtonVariant.LUMO_SUCCESS);
return button;
}
private TextField createFieldSearch() {
var field = new TextField();
field.setPlaceholder("Поиск группы по идентификатору");
field.setPrefixComponent(VaadinIcon.SEARCH.create());
field.setValueChangeMode(ValueChangeMode.EAGER);
field.setWidth(100, Unit.PERCENTAGE);
field.addValueChangeListener(e -> {
var input = e.getValue().trim().toLowerCase();
// TODO
});
return field;
}
private Grid<DialogueEntry> createGrid() {
var grid = new Grid<DialogueEntry>();
grid.addColumn(DialogueEntry::identifier).setHeader("Идентификатор").setWidth("15%").setFlexGrow(0);
grid.addColumn(DialogueEntry::comment).setHeader("Комментарий");
grid.addItemClickListener(e -> {
var dialogueIdentifier = e.getItem().identifier();
// TODO
browser.openTab(new DialoguePage(browser, groupIdentifier, "test_id"));
});
return grid;
}
public record DialogueEntry(String identifier, String comment) {}
}

View File

@ -1,325 +0,0 @@
package ru.dragonestia.editor.page;
import com.vaadin.flow.component.Text;
import com.vaadin.flow.component.Unit;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.button.ButtonVariant;
import com.vaadin.flow.component.checkbox.Checkbox;
import com.vaadin.flow.component.combobox.ComboBox;
import com.vaadin.flow.component.dialog.Dialog;
import com.vaadin.flow.component.html.H3;
import com.vaadin.flow.component.html.ListItem;
import com.vaadin.flow.component.html.Paragraph;
import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.Scroller;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.IntegerField;
import com.vaadin.flow.component.textfield.NumberField;
import com.vaadin.flow.component.textfield.TextArea;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.data.value.ValueChangeMode;
import ru.dragonestia.editor.model.DialogueAction;
import ru.dragonestia.editor.model.DialogueContext;
import ru.dragonestia.editor.model.FieldType;
import ru.dragonestia.editor.page.browser.BrowserPage;
import ru.dragonestia.editor.page.browser.TabContainer;
import java.util.*;
import java.util.function.Supplier;
public class DialoguePage extends TabContainer {
private final static Random random = new Random();
private final String groupIdentifier;
private final String dialogueIdentifier;
private final TextArea fieldText;
private final TextArea fieldComment;
private final Button buttonNewAnswer;
private final VerticalLayout layoutAnswers;
private final List<Answer> answers = new ArrayList<>();
private DialogueContext dialogueContext = new DialogueContext();
public DialoguePage(BrowserPage browser, String groupIdentifier, String dialogueIdentifier) {
super(browser, "Диалог: %s/%s".formatted(groupIdentifier, dialogueIdentifier));
this.groupIdentifier = groupIdentifier;
this.dialogueIdentifier = dialogueIdentifier;
var layout = new HorizontalLayout();
layout.setPadding(false);
layout.setWidth(100, Unit.PERCENTAGE);
add(layout);
var leftLayout = new VerticalLayout();
leftLayout.setWidth(45, Unit.PERCENTAGE);
leftLayout.setPadding(false);
layout.add(leftLayout);
var rightLayout = new VerticalLayout();
rightLayout.setPadding(false);
layout.add(rightLayout);
leftLayout.add(createFieldIdentifier());
leftLayout.add(fieldText = createFieldText());
leftLayout.add(createTextHelping());
var controlLayout = new HorizontalLayout();
controlLayout.setWidth(100, Unit.PERCENTAGE);
controlLayout.setPadding(false);
leftLayout.add(controlLayout);
controlLayout.add(createButtonSave());
controlLayout.add(createButtonDelete());
leftLayout.add(fieldComment = createFieldComment());
rightLayout.add(new H3("Ответы диалогов"));
rightLayout.add(buttonNewAnswer = createButtonNewAnswer());
layoutAnswers = new VerticalLayout();
layoutAnswers.setPadding(false);
rightLayout.add(layoutAnswers);
updateAnswers();
//add(new DialogEditor(new DialogueContext()));
}
private TextField createFieldIdentifier() {
var field = new TextField("Идентификатор группы/идентификатор диалога");
field.setHelperText("Идентификатор диалога изменять нельзя");
field.setValue("%s/%s".formatted(groupIdentifier, dialogueIdentifier));
field.setReadOnly(true);
field.setWidth(100, Unit.PERCENTAGE);
return field;
}
private TextArea createFieldText() {
var field = new TextArea("Текст диалога");
field.setHeight(20, Unit.REM);
field.setWidth(100, Unit.PERCENTAGE);
return field;
}
private VerticalLayout createTextHelping() {
var layout = new VerticalLayout();
layout.setPadding(false);
layout.add(new Text("Здесь описаны подсказки с плейсхолдерами для диалога:")); // TODO
layout.add(new ListItem("Плейсхолдер 1"));
layout.add(new ListItem("Плейсхолдер 2"));
layout.add(new ListItem("Плейсхолдер 3"));
return layout;
}
private TextArea createFieldComment() {
var field = new TextArea("Комментарий");
field.setHelperText("Комментарием может являться любая пометка с кратким описанием диалога. Этот комментарий видно в списке диалогов на сайте.");
field.setMinHeight(10, Unit.REM);
field.setWidth(100, Unit.PERCENTAGE);
return field;
}
private Button createButtonNewAnswer() {
var button = new Button("Добавить ответ диалога", VaadinIcon.PLUS.create());
button.addThemeVariants(ButtonVariant.LUMO_PRIMARY, ButtonVariant.LUMO_SUCCESS);
button.addClickListener(e -> {
var answer = new Answer();
answer.deletion = () -> deleteAnswer(answer);
answers.add(answer);
updateAnswers();
});
button.setWidth(100, Unit.PERCENTAGE);
return button;
}
private Button createButtonSave() {
var button = new Button("Сохранить", VaadinIcon.DATABASE.create(), e -> {
// TODO
});
button.addThemeVariants(ButtonVariant.LUMO_PRIMARY, ButtonVariant.LUMO_SUCCESS);
button.setWidth(49, Unit.PERCENTAGE);
return button;
}
private Button createButtonDelete() {
var button = new Button("Удалить", VaadinIcon.TRASH.create(), e -> sendDeletionConfirm());
button.addThemeVariants(ButtonVariant.LUMO_PRIMARY, ButtonVariant.LUMO_ERROR);
button.setWidth(49, Unit.PERCENTAGE);
return button;
}
private void sendDeletionConfirm() {
var dialog = new Dialog("Удаление диалога");
dialog.setWidth(30, Unit.REM);
dialog.getFooter().add(new Button("Отмена", e -> dialog.close()));
var randomNumber = Integer.toString(random.nextInt(100, 999));
var layout = new VerticalLayout();
dialog.add(layout);
layout.add(new Paragraph("Подтвердите что вы хотите удалить диалог. Введите код ниже в поле для ввода чтобы подтвердить удаление."));
var codeLayout = new H3(randomNumber);
layout.add(codeLayout);
var buttonConfirm = new Button("Подтвердить удаление", e -> {
// TODO
dialog.close();
});
buttonConfirm.setEnabled(false);
buttonConfirm.addThemeVariants(ButtonVariant.LUMO_PRIMARY, ButtonVariant.LUMO_ERROR);
buttonConfirm.setWidth(100, Unit.PERCENTAGE);
var fieldConfirmation = new TextField();
fieldConfirmation.setPlaceholder("Введите код, написанный выше");
fieldConfirmation.setWidth(100, Unit.PERCENTAGE);
fieldConfirmation.setValueChangeMode(ValueChangeMode.EAGER);
fieldConfirmation.addValueChangeListener(e -> {
buttonConfirm.setEnabled(randomNumber.equals(e.getValue()));
});
layout.add(fieldConfirmation);
layout.add(buttonConfirm);
dialog.open();
}
private void updateAnswers() {
buttonNewAnswer.setEnabled(answers.size() < 4);
layoutAnswers.removeAll();
for (var answer: answers) {
layoutAnswers.add(answer);
}
}
private void deleteAnswer(Answer answer) {
answers.removeIf(target -> answer.uuid.equals(target.uuid));
updateAnswers();
}
public static class Answer extends HorizontalLayout {
private final UUID uuid = UUID.randomUUID();
private Runnable deletion;
private final VerticalLayout layoutAction;
public Answer() {
var layoutLeft = new VerticalLayout();
add(layoutLeft);
var layoutRight = new VerticalLayout();
add(layoutRight);
layoutAction = new VerticalLayout();
layoutAction.setPadding(false);
layoutLeft.add(createFieldText());
layoutLeft.add(createSelectAction());
layoutLeft.add(layoutAction);
layoutRight.add(createButtonDeleteAnswer());
layoutRight.add(createFieldIdentifier());
layoutRight.add(createFieldComment());
setWidth(100, Unit.PERCENTAGE);
getStyle().set("background-color", "#EEEEEE");
getStyle().set("border-radius", "26px");
}
private TextField createFieldText() {
var field = new TextField("Текст кнопки");
field.setWidth(100, Unit.PERCENTAGE);
return field;
}
private Button createButtonDeleteAnswer() {
var button = new Button("Удалить ответ", VaadinIcon.TRASH.create(), e -> {
if (deletion == null) return;
deletion.run();
});
button.addThemeVariants(ButtonVariant.LUMO_PRIMARY, ButtonVariant.LUMO_ERROR);
button.setWidth(100, Unit.PERCENTAGE);
return button;
}
private TextField createFieldIdentifier() {
var field = new TextField("Идентификатор кнопки");
field.setHelperText("Может быть пустым. Этот параметр нужен чтобы запоминать какие кнопки нажимал игрок чтобы потом делать различные проверки на это.");
field.setWidth(100, Unit.PERCENTAGE);
return field;
}
private TextArea createFieldComment() {
var field = new TextArea("Комментарий");
field.setHelperText("Комментарием может являться любая пометка с кратким описанием ответа диалога.");
field.setWidth(100, Unit.PERCENTAGE);
field.setHeight(10, Unit.REM);
return field;
}
private ComboBox<DialogueAction> createSelectAction() {
var comboBox = new ComboBox<DialogueAction>("Действие ответа диалога");
comboBox.setHelperText("Выполняется, когда игрок нажимает на кнопку выбора ответа внутри диалога");
comboBox.setWidth(100, Unit.PERCENTAGE);
comboBox.setItemLabelGenerator(action -> "%s [%s]".formatted(action.getName(), action.getIdentifier()));
var items = List.of(
new DialogueAction("close", "Закрыть диалог", "Просто закрывает диалог", new ArrayList<>(), true),
new DialogueAction("dialogue", "Перейти к диалогу", "Перейти к другому диалогу", List.of(
new DialogueAction.Field("groupId", "Id-группы диалога", "", FieldType.STRING, ""),
new DialogueAction.Field("dialogueId", "Id диалога", "", FieldType.STRING, "")
), true),
new DialogueAction("test_params", "Тест всех полей", "", List.of(
new DialogueAction.Field("param1", "Строка", "", FieldType.STRING, ""),
new DialogueAction.Field("param2", "Текст", "", FieldType.TEXT, ""),
new DialogueAction.Field("param3", "Булево значение", "", FieldType.BOOLEAN, ""),
new DialogueAction.Field("param4", "Целое число", "", FieldType.INTEGER, ""),
new DialogueAction.Field("param5", "Число с плавающей точкой", "", FieldType.DOUBLE, "")
), true)
);
comboBox.setItems(items);
comboBox.setValue(items.getFirst());
comboBox.addValueChangeListener(e -> {
updateAction(e.getValue(), new HashMap<>());
});
return comboBox;
}
private void updateAction(DialogueAction action, Map<String, String> params) {
layoutAction.removeAll();
var fieldMapper = new HashMap<String, Supplier<String>>();
for (var param: action.getFields()) {
var component = switch (param.getType()) {
case STRING -> {
var field = new TextField(param.getName());
field.setValue(param.getDefaultValue());
fieldMapper.put(param.getIdentifier(), field::getValue);
yield field;
}
case TEXT -> {
var field = new TextArea(param.getName());
field.setValue(param.getDefaultValue());
fieldMapper.put(param.getIdentifier(), field::getValue);
yield field;
}
case BOOLEAN -> {
var field = new Checkbox(param.getName());
field.setValue(Boolean.parseBoolean(param.getDefaultValue()));
fieldMapper.put(param.getIdentifier(), () -> Boolean.toString(field.getValue()));
yield field;
}
case INTEGER -> {
var field = new IntegerField(param.getName());
fieldMapper.put(param.getIdentifier(), () -> Integer.toString(field.getValue()));
yield field;
}
case DOUBLE -> {
var field = new NumberField(param.getName());
fieldMapper.put(param.getIdentifier(), () -> Double.toString(field.getValue()));
yield field;
}
};
component.setWidth(100, Unit.PERCENTAGE);
layoutAction.add(component);
}
}
}
}

View File

@ -1,166 +0,0 @@
package ru.dragonestia.editor.page;
import com.vaadin.flow.component.Html;
import com.vaadin.flow.component.Unit;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.button.ButtonVariant;
import com.vaadin.flow.component.dialog.Dialog;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.html.*;
import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.notification.NotificationVariant;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.data.value.ValueChangeMode;
import ru.dragonestia.editor.page.browser.BrowserPage;
import ru.dragonestia.editor.page.browser.TabContainer;
import java.util.ArrayList;
import java.util.function.BiConsumer;
public class HomePage extends TabContainer {
public HomePage(BrowserPage browser) {
super(browser, "Домашняя страница");
add(new H3("Диалоги"));
addLink("Управление группами диалогов", this::findOrCreateDialog);
addLink("Список всех групп диалогов", this::listAllDialogueGroups);
add(new H3("Действия ответов диалога"));
addLink("Создание/редактирование действий ответов диалога", this::findOrCreateDialogAction);
addLink("Список всех действий ответов диалога", this::listAllDialogueActions);
add(new H3("Условия ответов диалога"));
addLink("Создание/редактирование условий ответов диалога", this::findOrCreateDialogCondition);
addLink("Список всех ответов диалога", this::listAllDialogueConditions);
}
private void addLink(String label, BiConsumer<VerticalLayout, Runnable> content) {
var component = new Span(new Html("<a href='#'>" + label + "</a>"));
component.addClickListener(event -> {
var dialog = new Dialog();
dialog.setMinHeight(30, Unit.PERCENTAGE);
dialog.setWidth(40, Unit.PERCENTAGE);
dialog.getHeader().add(new H2(label));
var layout = new VerticalLayout();
layout.setPadding(false);
content.accept(layout, dialog::close);
dialog.add(layout);
var closeButton = new Button("Close");
closeButton.addThemeVariants();
closeButton.addClickListener(e -> dialog.close());
dialog.getFooter().add(closeButton);
dialog.open();
});
add(new ListItem(component));
}
private void findOrCreateDialog(VerticalLayout layout, Runnable closeWindow) {
layout.add(new Paragraph(
"Введите идентификатор группы диалога чтобы перейти к созданию или редактированию группы диалога. " +
"Группы диалогов нужны чтобы упростить группировку диалогов. "
));
var fieldIdentifier = new TextField("Идентификатор группы");
fieldIdentifier.setHelperText("Идентификатор может содержать только символы английского алфавита, цифры и символ нижнего подчеркивания");
fieldIdentifier.setWidth(100, Unit.PERCENTAGE);
layout.add(fieldIdentifier);
var buttons = new HorizontalLayout();
buttons.setWidth(100, Unit.PERCENTAGE);
buttons.setJustifyContentMode(JustifyContentMode.CENTER);
layout.add(buttons);
var buttonCheckGroup = new Button("Проверить существование группы", e -> {
// TODO
});
buttonCheckGroup.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
buttonCheckGroup.setWidth(49, Unit.PERCENTAGE);
buttons.add(buttonCheckGroup);
var createOrFind = new Button("Создать/редактировать", e -> {
var groupIdentifier = fieldIdentifier.getValue().trim().toLowerCase();
if (groupIdentifier.isEmpty()) return;
if (!groupIdentifier.matches("^[aA-zZ\\d_]+$")) {
Notification.show("Идентификатор группы содержит недопустимые символы.")
.addThemeVariants(NotificationVariant.LUMO_ERROR);
return;
}
// TODO
browser.openTab(new DialogueGroupPage(browser, groupIdentifier));
closeWindow.run();
});
createOrFind.addThemeVariants(ButtonVariant.LUMO_PRIMARY, ButtonVariant.LUMO_SUCCESS);
createOrFind.setWidth(49, Unit.PERCENTAGE);
buttons.add(createOrFind);
}
private void listAllDialogueGroups(VerticalLayout layout, Runnable closeWindow) {
var buttonRefresh = new Button("Обновить список", VaadinIcon.REFRESH.create());
layout.add(buttonRefresh);
var fieldSearch = new TextField();
fieldSearch.setPlaceholder("Поиск группы по идентификатору");
fieldSearch.setPrefixComponent(VaadinIcon.SEARCH.create());
fieldSearch.setValueChangeMode(ValueChangeMode.EAGER);
fieldSearch.setWidth(100, Unit.PERCENTAGE);
layout.add(fieldSearch);
var grid = new Grid<DialogGroupEntry>();
var data = new ArrayList<DialogGroupEntry>();
// TODO: initial data loading
grid.setItems(data);
grid.addColumn(DialogGroupEntry::id).setHeader("Id группы").setWidth("25%").setFlexGrow(0);
grid.addColumn(DialogGroupEntry::dialoguesInside).setHeader("Кол-во диалогов").setWidth("20%").setFlexGrow(0);
grid.addComponentColumn(obj -> new Paragraph(obj.comment())).setHeader("Комментарий");
grid.addItemClickListener(e -> {
var item = e.getItem();
// TODO: open dialogue group
closeWindow.run();
});
layout.add(grid);
fieldSearch.addValueChangeListener(e -> {
var input = e.getValue().trim().toLowerCase();
var newList = new ArrayList<>(data);
newList.removeIf(entry -> !entry.id().toLowerCase().startsWith(input));
grid.setItems(newList);
});
buttonRefresh.addClickListener(e -> {
// TODO
});
layout.add(new Paragraph(
"В этом списке отображаются группы, которые имеют хоть какое-то количество диалогов внутри. " +
"Нажмите на строчку с диалогом чтобы заглянуть внутрь. "
));
}
private void findOrCreateDialogAction(VerticalLayout layout, Runnable closeWindow) {
}
private void listAllDialogueActions(VerticalLayout layout, Runnable closeWindow) {
}
private void findOrCreateDialogCondition(VerticalLayout layout, Runnable closeWindow) {
}
private void listAllDialogueConditions(VerticalLayout layout, Runnable closeWindow) {
}
private record DialogGroupEntry(String id, int dialoguesInside, String comment) {}
}

View File

@ -1,57 +0,0 @@
package ru.dragonestia.editor.page.browser;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.Unit;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.html.Span;
import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.tabs.Tab;
import com.vaadin.flow.component.tabs.TabSheet;
import com.vaadin.flow.router.Route;
import ru.dragonestia.editor.page.HomePage;
@Route("/")
public class BrowserPage extends VerticalLayout {
private final TabSheet tabs;
public BrowserPage() {
tabs = new TabSheet();
tabs.setWidth(100, Unit.PERCENTAGE);
//tabs.setSuffixComponent(createNewTabButton());
add(tabs);
openHomePage();
}
private Component createNewTabButton() {
return new Button(VaadinIcon.PLUS.create(), event -> openHomePage());
}
private void openHomePage() {
var tab = new Tab(new Span(VaadinIcon.HOME.create()));
var style = tab.getStyle();
style.setPaddingTop("0rem");
style.setPaddingBottom("0rem");
tabs.add(tab, new HomePage(this));
}
public void openTab(TabContainer container) {
var tab = new Tab();
var style = tab.getStyle();
style.setPaddingTop("0rem");
style.setPaddingBottom("0rem");
tab.setLabel(container.getTitle());
container.setTab(tab);
var closeTabButton = new Span(VaadinIcon.CLOSE_SMALL.create());
closeTabButton.getStyle().setMarginLeft("1rem");
closeTabButton.addClickListener(event -> tabs.remove(tab));
tab.add(closeTabButton);
tabs.add(tab, container);
tabs.setSelectedTab(tab);
}
}

View File

@ -1,25 +0,0 @@
package ru.dragonestia.editor.page.browser;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.tabs.Tab;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public abstract class TabContainer extends VerticalLayout {
protected final BrowserPage browser;
private String title;
private Tab tab;
public TabContainer(BrowserPage browser, String title) {
this.browser = browser;
this.title = title;
}
public void setTitle(String title) {
this.title = title;
tab.setLabel(title);
}
}

View File

@ -1,6 +0,0 @@
package ru.dragonestia.editor.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import ru.dragonestia.editor.model.DialoguePackage;
public interface DialoguePackageRepository extends JpaRepository<DialoguePackage, String> {}

View File

@ -1,6 +0,0 @@
package ru.dragonestia.editor.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import ru.dragonestia.editor.model.Dialogue;
public interface DialogueRepository extends JpaRepository<Dialogue, String> {}

View File

@ -1,9 +0,0 @@
package ru.dragonestia.editor.service;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class DialoguePackageService {
}

View File

@ -1,30 +0,0 @@
package ru.dragonestia.editor.util;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectReader;
import com.fasterxml.jackson.databind.ObjectWriter;
import lombok.experimental.UtilityClass;
@UtilityClass
public class JsonUtils {
private final ObjectMapper mapper = new ObjectMapper();
private final ObjectWriter writer = mapper.writer();
private final ObjectReader reader = mapper.reader();
public String toJson(Object object) {
try {
return writer.writeValueAsString(object);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
public <T> T fromJson(String json) {
try {
return reader.readValue(json);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
}

View File

@ -1,21 +0,0 @@
spring:
application:
name: 'editor'
datasource:
url: ${BFF_POSTGRES_URL:jdbc:postgresql://localhost:5432/postgres}
username: ${BFF_POSTGRES_USERNAME:postgres}
password: ${BFF_POSTGRES_PASSWORD:some_password}
jpa:
properties:
hibernate:
dialect: org.hibernate.dialect.PostgreSQLDialect
hibernate:
ddl-auto: validate
mvc:
static-path-pattern: '/static/**'
server:
port: 8080

View File

@ -1,11 +0,0 @@
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<meta name="design-properties" content="{&quot;RULERS_VISIBLE&quot;:true,&quot;GUIDELINES_VISIBLE&quot;:false,&quot;SNAP_TO_OBJECTS&quot;:true,&quot;SNAP_TO_GRID&quot;:true,&quot;SNAPPING_DISTANCE&quot;:10,&quot;GENERATE_GETTERS&quot;:false,&quot;JAVA_SOURCES_ROOT&quot;:&quot;../../src/main/java&quot;,&quot;THEME&quot;:&quot;valo&quot;}">
<meta name="vaadin-version" content="8.1.5">
</head>
<body>
<vaadin-vertical-layout size-full></vaadin-vertical-layout>
</body>
</html>

View File

@ -1,4 +1,3 @@
rootProject.name = 'msb3'
include 'resource-compiler', 'api', 'editor'
include 'resource-compiler', 'api'