mirror of
https://github.com/kestra-io/kestra.git
synced 2025-12-26 05:00:31 -05:00
Compare commits
15 Commits
fix-execut
...
fix/docker
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c322365dc2 | ||
|
|
a527271447 | ||
|
|
3346f3a3f9 | ||
|
|
4fc690b0d9 | ||
|
|
4fe5c665bc | ||
|
|
777bc36d01 | ||
|
|
e5b3bea4d1 | ||
|
|
6fad3eb14f | ||
|
|
8e91385080 | ||
|
|
b593c51659 | ||
|
|
824a7597cd | ||
|
|
7c292e2e70 | ||
|
|
7e6918cefa | ||
|
|
d50d5b3231 | ||
|
|
d6773e41ef |
2
.github/ISSUE_TEMPLATE/bug.yml
vendored
2
.github/ISSUE_TEMPLATE/bug.yml
vendored
@@ -4,7 +4,7 @@ body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for reporting an issue! Please provide a [Minimal Reproducible Example](https://stackoverflow.com/help/minimal-reproducible-example) and share any additional information that may help reproduce, troubleshoot, and hopefully fix the issue, including screenshots, error traceback, and your Kestra server logs. For quick questions, you can contact us directly on [Slack](https://kestra.io/slack).
|
||||
Thanks for reporting an issue! Please provide a [Minimal Reproducible Example](https://stackoverflow.com/help/minimal-reproducible-example) and share any additional information that may help reproduce, troubleshoot, and hopefully fix the issue, including screenshots, error traceback, and your Kestra server logs. For quick questions, you can contact us directly on [Slack](https://kestra.io/slack). Don't forget to give us a star! ⭐
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Describe the issue
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/feature.yml
vendored
2
.github/ISSUE_TEMPLATE/feature.yml
vendored
@@ -4,7 +4,7 @@ body:
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Feature description
|
||||
placeholder: Tell us more about your feature request
|
||||
placeholder: Tell us more about your feature request. Don't forget to give us a star! ⭐
|
||||
validations:
|
||||
required: true
|
||||
labels:
|
||||
|
||||
2
.github/pull_request_template.md
vendored
2
.github/pull_request_template.md
vendored
@@ -35,4 +35,4 @@ Remove this section if this change applies to all flows or to the documentation
|
||||
|
||||
If there are no setup requirements, you can remove this section.
|
||||
|
||||
Thank you for your contribution. ❤️ -->
|
||||
Thank you for your contribution. ❤️ Don't forget to give us a star! ⭐ -->
|
||||
|
||||
@@ -43,7 +43,7 @@ import java.util.concurrent.Callable;
|
||||
SysCommand.class,
|
||||
ConfigCommand.class,
|
||||
NamespaceCommand.class,
|
||||
MigrationCommand.class,
|
||||
MigrationCommand.class
|
||||
}
|
||||
)
|
||||
@Introspected
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
package io.kestra.cli.commands.configs.sys;
|
||||
import io.kestra.cli.commands.flows.FlowCreateCommand;
|
||||
import io.kestra.cli.commands.namespaces.kv.KvCommand;
|
||||
import io.micronaut.configuration.picocli.PicocliRunner;
|
||||
import io.micronaut.context.ApplicationContext;
|
||||
import io.micronaut.runtime.server.EmbeddedServer;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Objects;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
/**
|
||||
* Verifies CLI behavior without repository configuration:
|
||||
* - Repo-independent commands succeed (e.g. KV with no params).
|
||||
* - Repo-dependent commands fail with a clear error.
|
||||
*/
|
||||
class NoConfigCommandTest {
|
||||
|
||||
@Test
|
||||
void shouldSucceedWithNamespaceKVCommandWithoutParamsAndConfig() {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
System.setOut(new PrintStream(out));
|
||||
|
||||
try (ApplicationContext ctx = ApplicationContext.builder().deduceEnvironment(false).start()) {
|
||||
String[] args = {};
|
||||
Integer call = PicocliRunner.call(KvCommand.class, ctx, args);
|
||||
|
||||
assertThat(call).isZero();
|
||||
assertThat(out.toString()).contains("Usage: kestra namespace kv");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldFailWithCreateFlowCommandWithoutConfig() throws URISyntaxException {
|
||||
URL flowUrl = NoConfigCommandTest.class.getClassLoader().getResource("crudFlow/date.yml");
|
||||
Objects.requireNonNull(flowUrl, "Test flow resource not found");
|
||||
|
||||
Path flowPath = Paths.get(flowUrl.toURI());
|
||||
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
ByteArrayOutputStream err=new ByteArrayOutputStream();
|
||||
|
||||
System.setOut(new PrintStream(out));
|
||||
System.setErr(new PrintStream(err));
|
||||
|
||||
try (ApplicationContext ctx = ApplicationContext.builder()
|
||||
.deduceEnvironment(false)
|
||||
.start()) {
|
||||
|
||||
EmbeddedServer embeddedServer = ctx.getBean(EmbeddedServer.class);
|
||||
embeddedServer.start();
|
||||
|
||||
String[] createArgs = {
|
||||
"--server",
|
||||
embeddedServer.getURL().toString(),
|
||||
"--user",
|
||||
"myuser:pass:word",
|
||||
flowPath.toString(),
|
||||
};
|
||||
|
||||
Integer exitCode = PicocliRunner.call(FlowCreateCommand.class, ctx, createArgs);
|
||||
|
||||
|
||||
assertThat(exitCode).isNotZero();
|
||||
assertThat(out.toString()).isEmpty();
|
||||
assertThat(err.toString()).contains("No bean of type [io.kestra.core.repositories.FlowRepositoryInterface] exists");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -37,14 +37,14 @@ class PropertyTest {
|
||||
@Test
|
||||
void test() throws Exception {
|
||||
var task = DynamicPropertyExampleTask.builder()
|
||||
.number(new Property<>("{{numberValue}}"))
|
||||
.string(new Property<>("{{stringValue}}"))
|
||||
.level(new Property<>("{{levelValue}}"))
|
||||
.someDuration(new Property<>("{{durationValue}}"))
|
||||
.withDefault(new Property<>("{{defaultValue}}"))
|
||||
.items(new Property<>("""
|
||||
.number(Property.ofExpression("{{numberValue}}"))
|
||||
.string(Property.ofExpression("{{stringValue}}"))
|
||||
.level(Property.ofExpression("{{levelValue}}"))
|
||||
.someDuration(Property.ofExpression("{{durationValue}}"))
|
||||
.withDefault(Property.ofExpression("{{defaultValue}}"))
|
||||
.items(Property.ofExpression("""
|
||||
["{{item1}}", "{{item2}}"]"""))
|
||||
.properties(new Property<>("""
|
||||
.properties(Property.ofExpression("""
|
||||
{
|
||||
"key1": "{{value1}}",
|
||||
"key2": "{{value2}}"
|
||||
@@ -87,13 +87,13 @@ class PropertyTest {
|
||||
@Test
|
||||
void withDefaultsAndMessagesFromList() throws Exception {
|
||||
var task = DynamicPropertyExampleTask.builder()
|
||||
.number(new Property<>("{{numberValue}}"))
|
||||
.string(new Property<>("{{stringValue}}"))
|
||||
.level(new Property<>("{{levelValue}}"))
|
||||
.someDuration(new Property<>("{{durationValue}}"))
|
||||
.items(new Property<>("""
|
||||
.number(Property.ofExpression("{{numberValue}}"))
|
||||
.string(Property.ofExpression("{{stringValue}}"))
|
||||
.level(Property.ofExpression("{{levelValue}}"))
|
||||
.someDuration(Property.ofExpression("{{durationValue}}"))
|
||||
.items(Property.ofExpression("""
|
||||
["{{item1}}", "{{item2}}"]"""))
|
||||
.properties(new Property<>("""
|
||||
.properties(Property.ofExpression("""
|
||||
{
|
||||
"key1": "{{value1}}",
|
||||
"key2": "{{value2}}"
|
||||
@@ -156,14 +156,14 @@ class PropertyTest {
|
||||
}
|
||||
|
||||
var task = DynamicPropertyExampleTask.builder()
|
||||
.number(new Property<>("{{numberValue}}"))
|
||||
.string(new Property<>("{{stringValue}}"))
|
||||
.level(new Property<>("{{levelValue}}"))
|
||||
.someDuration(new Property<>("{{durationValue}}"))
|
||||
.withDefault(new Property<>("{{defaultValue}}"))
|
||||
.items(new Property<>("""
|
||||
.number(Property.ofExpression("{{numberValue}}"))
|
||||
.string(Property.ofExpression("{{stringValue}}"))
|
||||
.level(Property.ofExpression("{{levelValue}}"))
|
||||
.someDuration(Property.ofExpression("{{durationValue}}"))
|
||||
.withDefault(Property.ofExpression("{{defaultValue}}"))
|
||||
.items(Property.ofExpression("""
|
||||
["{{item1}}", "{{item2}}"]"""))
|
||||
.properties(new Property<>("""
|
||||
.properties(Property.ofExpression("""
|
||||
{
|
||||
"key1": "{{value1}}",
|
||||
"key2": "{{value2}}"
|
||||
@@ -202,12 +202,12 @@ class PropertyTest {
|
||||
@Test
|
||||
void failingToRender() throws Exception {
|
||||
var task = DynamicPropertyExampleTask.builder()
|
||||
.number(new Property<>("{{numberValue}}"))
|
||||
.string(new Property<>("{{stringValue}}"))
|
||||
.level(new Property<>("{{levelValue}}"))
|
||||
.someDuration(new Property<>("{{durationValue}}"))
|
||||
.withDefault(new Property<>("{{defaultValue}}"))
|
||||
.items(new Property<>("""
|
||||
.number(Property.ofExpression("{{numberValue}}"))
|
||||
.string(Property.ofExpression("{{stringValue}}"))
|
||||
.level(Property.ofExpression("{{levelValue}}"))
|
||||
.someDuration(Property.ofExpression("{{durationValue}}"))
|
||||
.withDefault(Property.ofExpression("{{defaultValue}}"))
|
||||
.items(Property.ofExpression("""
|
||||
["{{item1}}", "{{item2}}"]"""))
|
||||
.from(Map.of("key", "{{mapValue}}"))
|
||||
.build();
|
||||
@@ -221,13 +221,13 @@ class PropertyTest {
|
||||
var task = DynamicPropertyExampleTask.builder()
|
||||
.id("dynamic")
|
||||
.type(DynamicPropertyExampleTask.class.getName())
|
||||
.number(new Property<>("{{numberValue}}"))
|
||||
.string(new Property<>("{{stringValue}}"))
|
||||
.level(new Property<>("{{levelValue}}"))
|
||||
.someDuration(new Property<>("{{durationValue}}"))
|
||||
.items(new Property<>("""
|
||||
.number(Property.ofExpression("{{numberValue}}"))
|
||||
.string(Property.ofExpression("{{stringValue}}"))
|
||||
.level(Property.ofExpression("{{levelValue}}"))
|
||||
.someDuration(Property.ofExpression("{{durationValue}}"))
|
||||
.items(Property.ofExpression("""
|
||||
["{{item1}}", "{{item2}}"]"""))
|
||||
.properties(new Property<>("""
|
||||
.properties(Property.ofExpression("""
|
||||
{
|
||||
"key1": "{{value1}}",
|
||||
"key2": "{{value2}}"
|
||||
@@ -261,8 +261,8 @@ class PropertyTest {
|
||||
@Test
|
||||
void arrayAndMapToRender() throws Exception {
|
||||
var task = DynamicPropertyExampleTask.builder()
|
||||
.items(new Property<>("{{renderOnce(listToRender)}}"))
|
||||
.properties(new Property<>("{{renderOnce(mapToRender)}}"))
|
||||
.items(Property.ofExpression("{{renderOnce(listToRender)}}"))
|
||||
.properties(Property.ofExpression("{{renderOnce(mapToRender)}}"))
|
||||
.build();
|
||||
var runContext = runContextFactory.of(Map.ofEntries(
|
||||
entry("arrayValueToRender", "arrayValue1"),
|
||||
@@ -284,9 +284,9 @@ class PropertyTest {
|
||||
@Test
|
||||
void aListToRender() throws Exception {
|
||||
var task = DynamicPropertyExampleTask.builder()
|
||||
.items(new Property<>("""
|
||||
.items(Property.ofExpression("""
|
||||
["python test.py --input1 \\"{{ item1 }}\\" --input2 \\"{{ item2 }}\\"", "'gs://{{ renderOnce(\\"bucket\\") }}/{{ 'table' }}/{{ 'file' }}_*.csv.gz'"]"""))
|
||||
.properties(new Property<>("""
|
||||
.properties(Property.ofExpression("""
|
||||
{
|
||||
"key1": "{{value1}}",
|
||||
"key2": "{{value2}}"
|
||||
@@ -308,9 +308,9 @@ class PropertyTest {
|
||||
@Test
|
||||
void fromMessage() throws Exception {
|
||||
var task = DynamicPropertyExampleTask.builder()
|
||||
.items(new Property<>("""
|
||||
.items(Property.ofExpression("""
|
||||
["python test.py --input1 \\"{{ item1 }}\\" --input2 \\"{{ item2 }}\\"", "'gs://{{ renderOnce(\\"bucket\\") }}/{{ 'table' }}/{{ 'file' }}_*.csv.gz'"]"""))
|
||||
.properties(new Property<>("""
|
||||
.properties(Property.ofExpression("""
|
||||
{
|
||||
"key1": "{{value1}}",
|
||||
"key2": "{{value2}}"
|
||||
@@ -335,9 +335,9 @@ class PropertyTest {
|
||||
@Test
|
||||
void fromListOfMessages() throws Exception {
|
||||
var task = DynamicPropertyExampleTask.builder()
|
||||
.items(new Property<>("""
|
||||
.items(Property.ofExpression("""
|
||||
["python test.py --input1 \\"{{ item1 }}\\" --input2 \\"{{ item2 }}\\"", "'gs://{{ renderOnce(\\"bucket\\") }}/{{ 'table' }}/{{ 'file' }}_*.csv.gz'"]"""))
|
||||
.properties(new Property<>("""
|
||||
.properties(Property.ofExpression("""
|
||||
{
|
||||
"key1": "{{value1}}",
|
||||
"key2": "{{value2}}"
|
||||
|
||||
@@ -64,7 +64,7 @@ public class SkipExecutionCaseTest {
|
||||
.tasks(Collections.singletonList(Return.builder()
|
||||
.id("test")
|
||||
.type(Return.class.getName())
|
||||
.format(new Property<>("{{ inputs.testInputs }}"))
|
||||
.format(Property.ofExpression("{{ inputs.testInputs }}"))
|
||||
.build()))
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ class ExecutionOutputsTest {
|
||||
Map.of("test", "value"));
|
||||
|
||||
ExecutionOutputs build = ExecutionOutputs.builder()
|
||||
.expression(new Property<>("{{ trigger.outputs.test == 'value' }}"))
|
||||
.expression(Property.ofExpression("{{ trigger.outputs.test == 'value' }}"))
|
||||
.build();
|
||||
|
||||
boolean test = conditionService.isValid(build, flow, execution);
|
||||
@@ -44,7 +44,7 @@ class ExecutionOutputsTest {
|
||||
Map.of("test", "value"));
|
||||
|
||||
ExecutionOutputs build = ExecutionOutputs.builder()
|
||||
.expression(new Property<>("{{ unknown is defined }}"))
|
||||
.expression(Property.ofExpression("{{ unknown is defined }}"))
|
||||
.build();
|
||||
|
||||
boolean test = conditionService.isValid(build, flow, execution);
|
||||
@@ -58,7 +58,7 @@ class ExecutionOutputsTest {
|
||||
Execution execution = TestsUtils.mockExecution(flow, Map.of());
|
||||
|
||||
ExecutionOutputs build = ExecutionOutputs.builder()
|
||||
.expression(new Property<>("{{ not evaluated }}"))
|
||||
.expression(Property.ofExpression("{{ not evaluated }}"))
|
||||
.build();
|
||||
|
||||
boolean test = conditionService.isValid(build, flow, execution);
|
||||
|
||||
@@ -24,7 +24,7 @@ class ExpressionTest {
|
||||
Execution execution = TestsUtils.mockExecution(flow, ImmutableMap.of("test", "value"));
|
||||
|
||||
Expression build = Expression.builder()
|
||||
.expression(new Property<>("{{ flow.id }}"))
|
||||
.expression(Property.ofExpression("{{ flow.id }}"))
|
||||
.build();
|
||||
|
||||
boolean test = conditionService.isValid(build, flow, execution);
|
||||
@@ -38,7 +38,7 @@ class ExpressionTest {
|
||||
Execution execution = TestsUtils.mockExecution(flow, ImmutableMap.of("test", "value"));
|
||||
|
||||
Expression build = Expression.builder()
|
||||
.expression(new Property<>("{{ unknown is defined }}"))
|
||||
.expression(Property.ofExpression("{{ unknown is defined }}"))
|
||||
.build();
|
||||
|
||||
boolean test = conditionService.isValid(build, flow, execution);
|
||||
|
||||
@@ -37,7 +37,7 @@ class MultipleConditionTest {
|
||||
.in(Property.ofValue(Collections.singletonList(State.Type.SUCCESS)))
|
||||
.build(),
|
||||
"second", Expression.builder()
|
||||
.expression(new Property<>("{{ flow.id }}"))
|
||||
.expression(Property.ofExpression("{{ flow.id }}"))
|
||||
.build()
|
||||
))
|
||||
.build();
|
||||
|
||||
@@ -38,7 +38,7 @@ class WeekendTest {
|
||||
Execution execution = TestsUtils.mockExecution(flow, ImmutableMap.of());
|
||||
|
||||
Weekend build = Weekend.builder()
|
||||
.date(new Property<>(date))
|
||||
.date(date.startsWith("{{") ? Property.ofExpression(date) : Property.ofValue(date))
|
||||
.build();
|
||||
|
||||
boolean test = conditionService.isValid(build, flow, execution);
|
||||
|
||||
@@ -48,8 +48,8 @@ class CountTest {
|
||||
new Flow(AbstractExecutionRepositoryTest.NAMESPACE, "third")
|
||||
))
|
||||
.expression("{{ count >= 5 }}")
|
||||
.startDate(new Property<>("{{ now() | dateAdd (-30, 'DAYS') }}"))
|
||||
.endDate(new Property<>("{{ now() }}"))
|
||||
.startDate(Property.ofExpression("{{ now() | dateAdd (-30, 'DAYS') }}"))
|
||||
.endDate(Property.ofExpression("{{ now() }}"))
|
||||
.build();
|
||||
|
||||
RunContext runContext = runContextFactory.of("id", NAMESPACE, tenant);
|
||||
|
||||
@@ -35,8 +35,8 @@ class DeleteTest {
|
||||
Delete delete = Delete.builder()
|
||||
.id(Delete.class.getSimpleName())
|
||||
.type(Delete.class.getName())
|
||||
.namespace(new Property<>("{{ inputs.namespace }}"))
|
||||
.key(new Property<>("{{ inputs.key }}"))
|
||||
.namespace(Property.ofExpression("{{ inputs.namespace }}"))
|
||||
.key(Property.ofExpression("{{ inputs.key }}"))
|
||||
.build();
|
||||
|
||||
final KVStore kv = runContext.namespaceKv(namespaceId);
|
||||
@@ -61,8 +61,8 @@ class DeleteTest {
|
||||
Delete delete = Delete.builder()
|
||||
.id(Delete.class.getSimpleName())
|
||||
.type(Delete.class.getName())
|
||||
.namespace(new Property<>(namespaceId))
|
||||
.key(new Property<>("my-key"))
|
||||
.namespace(Property.ofValue(namespaceId))
|
||||
.key(Property.ofValue("my-key"))
|
||||
.build();
|
||||
|
||||
// When
|
||||
|
||||
@@ -54,7 +54,7 @@ class GetKeysTest {
|
||||
GetKeys getKeys = GetKeys.builder()
|
||||
.id(GetKeys.class.getSimpleName())
|
||||
.type(GetKeys.class.getName())
|
||||
.prefix(new Property<>("{{ inputs.prefix }}"))
|
||||
.prefix(Property.ofExpression("{{ inputs.prefix }}"))
|
||||
.build();
|
||||
|
||||
final KVStore kv = runContext.namespaceKv(namespace);
|
||||
@@ -79,7 +79,7 @@ class GetKeysTest {
|
||||
GetKeys getKeys = GetKeys.builder()
|
||||
.id(GetKeys.class.getSimpleName())
|
||||
.type(GetKeys.class.getName())
|
||||
.prefix(new Property<>("{{ inputs.prefix }}"))
|
||||
.prefix(Property.ofExpression("{{ inputs.prefix }}"))
|
||||
.build();
|
||||
|
||||
// When
|
||||
|
||||
@@ -41,8 +41,8 @@ class GetTest {
|
||||
Get get = Get.builder()
|
||||
.id(Get.class.getSimpleName())
|
||||
.type(Get.class.getName())
|
||||
.namespace(new Property<>("{{ inputs.namespace }}"))
|
||||
.key(new Property<>("{{ inputs.key }}"))
|
||||
.namespace(Property.ofExpression("{{ inputs.namespace }}"))
|
||||
.key(Property.ofExpression("{{ inputs.key }}"))
|
||||
.build();
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ class GetTest {
|
||||
Get get = Get.builder()
|
||||
.id(Get.class.getSimpleName())
|
||||
.type(Get.class.getName())
|
||||
.key(new Property<>("{{ inputs.key }}"))
|
||||
.key(Property.ofExpression("{{ inputs.key }}"))
|
||||
.build();
|
||||
|
||||
|
||||
@@ -99,8 +99,8 @@ class GetTest {
|
||||
Get get = Get.builder()
|
||||
.id(Get.class.getSimpleName())
|
||||
.type(Get.class.getName())
|
||||
.namespace(new Property<>(namespaceId))
|
||||
.key(new Property<>("my-key"))
|
||||
.namespace(Property.ofValue(namespaceId))
|
||||
.key(Property.ofValue("my-key"))
|
||||
.build();
|
||||
|
||||
// When
|
||||
|
||||
@@ -38,9 +38,9 @@ class SetTest {
|
||||
Set set = Set.builder()
|
||||
.id(Set.class.getSimpleName())
|
||||
.type(Set.class.getName())
|
||||
.key(new Property<>("{{ inputs.key }}"))
|
||||
.value(new Property<>("{{ inputs.value }}"))
|
||||
.kvDescription(new Property<>("{{ inputs.description }}"))
|
||||
.key(Property.ofExpression("{{ inputs.key }}"))
|
||||
.value(Property.ofExpression("{{ inputs.value }}"))
|
||||
.kvDescription(Property.ofExpression("{{ inputs.description }}"))
|
||||
.build();
|
||||
|
||||
var value = Map.of("date", Instant.now().truncatedTo(ChronoUnit.MILLIS), "int", 1, "string", "string");
|
||||
@@ -78,9 +78,9 @@ class SetTest {
|
||||
Set set = Set.builder()
|
||||
.id(Set.class.getSimpleName())
|
||||
.type(Set.class.getName())
|
||||
.key(new Property<>("{{ inputs.key }}"))
|
||||
.value(new Property<>("{{ inputs.value }}"))
|
||||
.namespace(new Property<>("io.kestra.test"))
|
||||
.key(Property.ofExpression("{{ inputs.key }}"))
|
||||
.value(Property.ofExpression("{{ inputs.value }}"))
|
||||
.namespace(Property.ofValue("io.kestra.test"))
|
||||
.build();
|
||||
|
||||
// When
|
||||
@@ -105,9 +105,9 @@ class SetTest {
|
||||
Set set = Set.builder()
|
||||
.id(Set.class.getSimpleName())
|
||||
.type(Set.class.getName())
|
||||
.key(new Property<>("{{ inputs.key }}"))
|
||||
.value(new Property<>("{{ inputs.value }}"))
|
||||
.namespace(new Property<>("io.kestra"))
|
||||
.key(Property.ofExpression("{{ inputs.key }}"))
|
||||
.value(Property.ofExpression("{{ inputs.value }}"))
|
||||
.namespace(Property.ofValue("io.kestra"))
|
||||
.build();
|
||||
// When
|
||||
set.run(runContext);
|
||||
@@ -131,9 +131,9 @@ class SetTest {
|
||||
Set set = Set.builder()
|
||||
.id(Set.class.getSimpleName())
|
||||
.type(Set.class.getName())
|
||||
.key(new Property<>("{{ inputs.key }}"))
|
||||
.value(new Property<>("{{ inputs.value }}"))
|
||||
.namespace(new Property<>("not-found"))
|
||||
.key(Property.ofExpression("{{ inputs.key }}"))
|
||||
.value(Property.ofExpression("{{ inputs.value }}"))
|
||||
.namespace(Property.ofValue("not-found"))
|
||||
.build();
|
||||
|
||||
// When - Then
|
||||
@@ -146,8 +146,8 @@ class SetTest {
|
||||
Set set = Set.builder()
|
||||
.id(Set.class.getSimpleName())
|
||||
.type(Set.class.getName())
|
||||
.key(new Property<>("{{ inputs.key }}"))
|
||||
.value(new Property<>("{{ inputs.value }}"))
|
||||
.key(Property.ofExpression("{{ inputs.key }}"))
|
||||
.value(Property.ofExpression("{{ inputs.value }}"))
|
||||
.ttl(Property.ofValue(Duration.ofMinutes(5)))
|
||||
.build();
|
||||
|
||||
@@ -174,8 +174,8 @@ class SetTest {
|
||||
Set set = Set.builder()
|
||||
.id(Set.class.getSimpleName())
|
||||
.type(Set.class.getName())
|
||||
.key(new Property<>("{{ inputs.key }}"))
|
||||
.value(new Property<>("{{ inputs.value }}"))
|
||||
.key(Property.ofExpression("{{ inputs.key }}"))
|
||||
.value(Property.ofExpression("{{ inputs.value }}"))
|
||||
.overwrite(Property.ofValue(false))
|
||||
.build();
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ class DeleteFilesTest {
|
||||
.id(DeleteFiles.class.getSimpleName())
|
||||
.type(DeleteFiles.class.getName())
|
||||
.files(List.of("**test1*"))
|
||||
.namespace(new Property<>("{{ inputs.namespace }}"))
|
||||
.namespace(Property.ofExpression("{{ inputs.namespace }}"))
|
||||
.build();
|
||||
|
||||
final RunContext runContext = TestsUtils.mockRunContext(this.runContextFactory, deleteFiles, Map.of("namespace", namespaceId));
|
||||
@@ -59,7 +59,7 @@ class DeleteFilesTest {
|
||||
.id(DeleteFiles.class.getSimpleName())
|
||||
.type(DeleteFiles.class.getName())
|
||||
.files(List.of("**/file.txt"))
|
||||
.namespace(new Property<>("{{ inputs.namespace }}"))
|
||||
.namespace(Property.ofExpression("{{ inputs.namespace }}"))
|
||||
.deleteParentFolder(Property.ofValue(true))
|
||||
.build();
|
||||
|
||||
@@ -87,7 +87,7 @@ class DeleteFilesTest {
|
||||
.id(DeleteFiles.class.getSimpleName())
|
||||
.type(DeleteFiles.class.getName())
|
||||
.files(List.of("**/file.txt"))
|
||||
.namespace(new Property<>("{{ inputs.namespace }}"))
|
||||
.namespace(Property.ofExpression("{{ inputs.namespace }}"))
|
||||
.deleteParentFolder(Property.ofValue(false))
|
||||
.build();
|
||||
|
||||
@@ -115,7 +115,7 @@ class DeleteFilesTest {
|
||||
.id(DeleteFiles.class.getSimpleName())
|
||||
.type(DeleteFiles.class.getName())
|
||||
.files(List.of("**/file1.txt"))
|
||||
.namespace(new Property<>("{{ inputs.namespace }}"))
|
||||
.namespace(Property.ofExpression("{{ inputs.namespace }}"))
|
||||
.deleteParentFolder(Property.ofValue(true))
|
||||
.build();
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ public class DownloadFilesTest {
|
||||
.id(DownloadFiles.class.getSimpleName())
|
||||
.type(DownloadFiles.class.getName())
|
||||
.files(List.of("**test1.txt"))
|
||||
.namespace(new Property<>("{{ inputs.namespace }}"))
|
||||
.namespace(Property.ofExpression("{{ inputs.namespace }}"))
|
||||
.build();
|
||||
|
||||
final RunContext runContext = TestsUtils.mockRunContext(this.runContextFactory, downloadFiles, Map.of("namespace", namespaceId));
|
||||
|
||||
@@ -76,7 +76,7 @@ public class UploadFilesTest {
|
||||
.id(UploadFiles.class.getSimpleName())
|
||||
.type(UploadFiles.class.getName())
|
||||
.filesMap(Map.of("/path/file.txt", fileStorage.toString()))
|
||||
.namespace(new Property<>("{{ inputs.namespace }}"))
|
||||
.namespace(Property.ofExpression("{{ inputs.namespace }}"))
|
||||
.destination(Property.ofValue("/folder"))
|
||||
.build();
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ class StateTest {
|
||||
Set set = Set.builder()
|
||||
.id(IdUtils.create())
|
||||
.type(Set.class.toString())
|
||||
.data(new Property<>(Map.of(
|
||||
.data(Property.ofValue(Map.of(
|
||||
"{{ inputs.key }}", "{{ inputs.inc }}"
|
||||
)))
|
||||
.build();
|
||||
@@ -56,7 +56,7 @@ class StateTest {
|
||||
set = Set.builder()
|
||||
.id(IdUtils.create())
|
||||
.type(Set.class.toString())
|
||||
.data(new Property<>(Map.of(
|
||||
.data(Property.ofValue(Map.of(
|
||||
"{{ inputs.key }}", "2",
|
||||
"test2", "3"
|
||||
)))
|
||||
@@ -100,7 +100,7 @@ class StateTest {
|
||||
Delete task = Delete.builder()
|
||||
.id(IdUtils.create())
|
||||
.type(Get.class.getName())
|
||||
.name(new Property<>(IdUtils.create()))
|
||||
.name(Property.ofValue(IdUtils.create()))
|
||||
.errorOnMissing(Property.ofValue(true))
|
||||
.build();
|
||||
|
||||
@@ -114,7 +114,7 @@ class StateTest {
|
||||
Get task = Get.builder()
|
||||
.id(IdUtils.create())
|
||||
.type(Get.class.getName())
|
||||
.name(new Property<>(IdUtils.create()))
|
||||
.name(Property.ofValue(IdUtils.create()))
|
||||
.errorOnMissing(Property.ofValue(true))
|
||||
.build();
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ class TemplatedTaskTest {
|
||||
TemplatedTask templatedTask = TemplatedTask.builder()
|
||||
.id("template")
|
||||
.type(TemplatedTask.class.getName())
|
||||
.spec(new Property<>("""
|
||||
.spec(Property.ofExpression("""
|
||||
type: {{ type }}
|
||||
format: It's alive!"""))
|
||||
.build();
|
||||
|
||||
@@ -20,6 +20,8 @@ services:
|
||||
retries: 10
|
||||
|
||||
kestra:
|
||||
env_file:
|
||||
- kestra.env
|
||||
image: kestra/kestra:latest
|
||||
pull_policy: always
|
||||
# Note that this setup with a root user is intended for development purpose.
|
||||
@@ -39,10 +41,10 @@ services:
|
||||
username: kestra
|
||||
password: k3str4
|
||||
kestra:
|
||||
# server:
|
||||
# basicAuth:
|
||||
# username: admin@kestra.io # it must be a valid email address
|
||||
# password: Admin1234 # it must be at least 8 characters long with uppercase letter and a number
|
||||
server:
|
||||
basicAuth:
|
||||
username: user@kestra.io
|
||||
password: DemoDemo1
|
||||
repository:
|
||||
type: postgres
|
||||
storage:
|
||||
|
||||
8
ui/package-lock.json
generated
8
ui/package-lock.json
generated
@@ -10,7 +10,7 @@
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@js-joda/core": "^5.6.5",
|
||||
"@kestra-io/ui-libs": "^0.0.250",
|
||||
"@kestra-io/ui-libs": "^0.0.253",
|
||||
"@vue-flow/background": "^1.3.2",
|
||||
"@vue-flow/controls": "^1.1.2",
|
||||
"@vue-flow/core": "^1.46.3",
|
||||
@@ -3201,9 +3201,9 @@
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@kestra-io/ui-libs": {
|
||||
"version": "0.0.250",
|
||||
"resolved": "https://registry.npmjs.org/@kestra-io/ui-libs/-/ui-libs-0.0.250.tgz",
|
||||
"integrity": "sha512-Y0ANjGn91f+3G6ZeH0niorf0ZCNe/BPWfur+yHni4AKHbyNUZjrE8UN9ETvOlYe5c2qQSyQdM9yK/LdG1Thtzw==",
|
||||
"version": "0.0.253",
|
||||
"resolved": "https://registry.npmjs.org/@kestra-io/ui-libs/-/ui-libs-0.0.253.tgz",
|
||||
"integrity": "sha512-Iixihy54osSAmQPlytQWokACcf2SzQjAu7k8KjuNAbgOyxp5XXXsozzA3YBoApS4cmTv3kdewXNr/Rgqg8/4Tg==",
|
||||
"dependencies": {
|
||||
"@nuxtjs/mdc": "^0.17.3",
|
||||
"@popperjs/core": "^2.11.8",
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@js-joda/core": "^5.6.5",
|
||||
"@kestra-io/ui-libs": "^0.0.250",
|
||||
"@kestra-io/ui-libs": "^0.0.253",
|
||||
"@vue-flow/background": "^1.3.2",
|
||||
"@vue-flow/controls": "^1.1.2",
|
||||
"@vue-flow/core": "^1.46.3",
|
||||
|
||||
@@ -14,21 +14,30 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
<script setup lang="ts">
|
||||
import {useRoute} from "vue-router";
|
||||
import {useDocStore} from "../../stores/doc";
|
||||
|
||||
const props = defineProps({
|
||||
pageUrl: {
|
||||
type: String,
|
||||
default: undefined
|
||||
},
|
||||
interface ReleaseMetadata {
|
||||
release: string;
|
||||
title: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
interface ResourcesWithMetadata {
|
||||
[key: string]: ReleaseMetadata;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
pageUrl?: string;
|
||||
}>(), {
|
||||
pageUrl: undefined
|
||||
});
|
||||
|
||||
const docStore = useDocStore();
|
||||
const route = useRoute();
|
||||
|
||||
let currentPage;
|
||||
let currentPage: string;
|
||||
|
||||
if (props.pageUrl) {
|
||||
currentPage = props.pageUrl;
|
||||
@@ -38,7 +47,7 @@
|
||||
|
||||
currentPage = currentPage.endsWith("/") ? currentPage.slice(0, -1) : currentPage;
|
||||
|
||||
const resourcesWithMetadata = await docStore.children(currentPage);
|
||||
const resourcesWithMetadata = await docStore.children(currentPage) as ResourcesWithMetadata;
|
||||
|
||||
const navigation = Object.entries(resourcesWithMetadata)
|
||||
.filter(([_, metadata]) => metadata.release !== undefined)
|
||||
|
||||
@@ -4,26 +4,25 @@
|
||||
</component>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
<script setup lang="ts">
|
||||
import {computed, toRef} from "vue";
|
||||
import {useRoute} from "vue-router";
|
||||
import {useDocsLink} from "../docs/useDocsLink";
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
const props = defineProps({
|
||||
href: {
|
||||
type: String,
|
||||
default: ""
|
||||
},
|
||||
target: {
|
||||
type: String,
|
||||
default: undefined,
|
||||
required: false
|
||||
}
|
||||
const props = withDefaults(defineProps<{
|
||||
href?: string;
|
||||
target?: string;
|
||||
}>(), {
|
||||
href: "",
|
||||
target: undefined
|
||||
});
|
||||
|
||||
const {href, isRemote} = useDocsLink(toRef(props.href), computed(() => route.path));
|
||||
const {href, isRemote} = useDocsLink(
|
||||
toRef(props, "href"),
|
||||
computed(() => route.path)
|
||||
);
|
||||
|
||||
const linkType = computed(() => {
|
||||
return isRemote.value ? "a" : "router-link";
|
||||
@@ -33,7 +32,7 @@
|
||||
if (isRemote.value) {
|
||||
return {
|
||||
href: href.value,
|
||||
target: "_blank"
|
||||
target: props.target ?? "_blank"
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -5,28 +5,60 @@
|
||||
<Badge v-if="isATestExecution" :label="$t('test-badge-text')" :tooltip="$t('test-badge-tooltip')" />
|
||||
</template>
|
||||
<template #additional-right v-if="canDelete || isAllowedTrigger || isAllowedEdit">
|
||||
<ul id="list">
|
||||
<li v-if="isAllowedEdit">
|
||||
<a :href="`${finalApiUrl}/executions/${execution.id}`" target="_blank">
|
||||
<el-button :icon="Api">
|
||||
{{ $t("api") }}
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<ul class="d-none d-xl-flex align-items-center">
|
||||
<li v-if="isAllowedEdit">
|
||||
<a :href="`${finalApiUrl}/executions/${execution.id}`" target="_blank">
|
||||
<el-button :icon="Api">
|
||||
{{ $t("api") }}
|
||||
</el-button>
|
||||
</a>
|
||||
</li>
|
||||
<li v-if="canDelete">
|
||||
<el-button :icon="Delete" @click="deleteExecution">
|
||||
{{ $t("delete") }}
|
||||
</el-button>
|
||||
</a>
|
||||
</li>
|
||||
<li v-if="canDelete">
|
||||
<el-button :icon="Delete" @click="deleteExecution">
|
||||
{{ $t("delete") }}
|
||||
</li>
|
||||
<li v-if="isAllowedEdit">
|
||||
<el-button :icon="Pencil" @click="editFlow">
|
||||
{{ $t("edit flow") }}
|
||||
</el-button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<el-dropdown class="d-flex d-xl-none align-items-center">
|
||||
<el-button>
|
||||
<el-icon><DotsVerticalIcon /></el-icon>
|
||||
<span class="d-none d-lg-inline-block">{{ $t("more_actions") }}</span>
|
||||
</el-button>
|
||||
</li>
|
||||
<li v-if="isAllowedEdit">
|
||||
<el-button :icon="Pencil" @click="editFlow">
|
||||
{{ $t("edit flow") }}
|
||||
</el-button>
|
||||
</li>
|
||||
<li v-if="isAllowedTrigger">
|
||||
<TriggerFlow type="primary" :flowId="$route.params.flowId" :namespace="$route.params.namespace" />
|
||||
</li>
|
||||
</ul>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item v-if="isAllowedEdit">
|
||||
<a :href="`${finalApiUrl}/executions/${execution.id}`" target="_blank">
|
||||
<el-icon><Api /></el-icon>
|
||||
{{ $t("api") }}
|
||||
</a>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item v-if="canDelete" @click="deleteExecution">
|
||||
<el-icon><Delete /></el-icon>
|
||||
{{ $t("delete") }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item v-if="isAllowedEdit" @click="editFlow">
|
||||
<el-icon><Pencil /></el-icon>
|
||||
{{ $t("edit flow") }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
|
||||
<div v-if="isAllowedTrigger">
|
||||
<TriggerFlow
|
||||
type="primary"
|
||||
:flowId="$route.params.flowId"
|
||||
:namespace="$route.params.namespace"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</TopNavBar>
|
||||
</template>
|
||||
@@ -35,6 +67,7 @@
|
||||
import Api from "vue-material-design-icons/Api.vue";
|
||||
import Delete from "vue-material-design-icons/Delete.vue";
|
||||
import Pencil from "vue-material-design-icons/Pencil.vue";
|
||||
import DotsVerticalIcon from "vue-material-design-icons/DotsVertical.vue";
|
||||
import Badge from "../global/Badge.vue";
|
||||
</script>
|
||||
|
||||
@@ -160,30 +193,11 @@
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
@media (max-width: 768px) {
|
||||
|
||||
|
||||
#list {
|
||||
display:contents;
|
||||
background-color: blue;
|
||||
}
|
||||
#list li:first-child {
|
||||
grid-row:1;
|
||||
grid-column:1;
|
||||
}
|
||||
|
||||
#list li:nth-child(2){
|
||||
grid-row:1;
|
||||
grid-column:2;
|
||||
}
|
||||
#list li:nth-child(3){
|
||||
grid-row:1;
|
||||
grid-column:3;
|
||||
}
|
||||
#list li:nth-child(4){
|
||||
grid-row:2;
|
||||
grid-column:1;
|
||||
}
|
||||
}
|
||||
@media (max-width: 575.98px) {
|
||||
.sm-extra-padding {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -19,7 +19,7 @@
|
||||
>
|
||||
<el-table-column type="expand">
|
||||
<template #default="props">
|
||||
<LogsWrapper class="m-3" :filters="{...props.row, triggerId: props.row.id}" purgeFilters :withCharts="false" embed />
|
||||
<LogsWrapper class="m-3" :filters="{...props.row, triggerId: props.row.id}" purgeFilters :withCharts="false" :reloadLogs embed />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="id" :label="$t('id')">
|
||||
@@ -305,7 +305,8 @@
|
||||
end: null,
|
||||
inputs: null,
|
||||
labels: []
|
||||
}
|
||||
},
|
||||
reloadLogs: undefined,
|
||||
}
|
||||
},
|
||||
created() {
|
||||
@@ -402,7 +403,8 @@
|
||||
|
||||
this.triggerStore
|
||||
.find({namespace: this.flowStore.flow.namespace, flowId: this.flowStore.flow.id, size: this.triggersWithType.length, q: this.query})
|
||||
.then(triggers => this.triggers = triggers.results);
|
||||
.then(triggers => this.triggers = triggers.results)
|
||||
.then(() => this.reloadLogs = Math.random());
|
||||
},
|
||||
setBackfillModal(trigger, bool) {
|
||||
this.isBackfillOpen = bool
|
||||
|
||||
@@ -77,120 +77,119 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup lang="ts">
|
||||
import {Duration, Period} from "@js-joda/core";
|
||||
export default {
|
||||
props: {
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: ""
|
||||
import {ref, watch, onMounted, onUpdated} from "vue";
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue?: string;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
"update:model-value": [value: string | null];
|
||||
}>();
|
||||
|
||||
const years = ref<number>(0);
|
||||
const months = ref<number>(0);
|
||||
const weeks = ref<number>(0);
|
||||
const days = ref<number>(0);
|
||||
const hours = ref<number>(0);
|
||||
const minutes = ref<number>(0);
|
||||
const seconds = ref<number>(0);
|
||||
const customDuration = ref<string>("");
|
||||
const durationIssue = ref<string | null>(null);
|
||||
|
||||
const updateDuration = () => {
|
||||
let duration = "P"
|
||||
if (years.value > 0) {
|
||||
duration += `${years.value}Y`;
|
||||
}
|
||||
if (months.value > 0) {
|
||||
duration += `${months.value}M`;
|
||||
}
|
||||
if (weeks.value > 0) {
|
||||
duration += `${weeks.value}W`;
|
||||
}
|
||||
if (days.value > 0) {
|
||||
duration += `${days.value}D`;
|
||||
}
|
||||
if (hours.value > 0 || minutes.value > 0 || seconds.value > 0) {
|
||||
duration += "T"
|
||||
if (hours.value > 0) {
|
||||
duration += `${hours.value}H`;
|
||||
}
|
||||
},
|
||||
emits: ["update:model-value"],
|
||||
mounted() {
|
||||
this.parseDuration(this.modelValue);
|
||||
this.updateDuration();
|
||||
},
|
||||
updated() {
|
||||
if (this.modelValue) {
|
||||
this.parseDuration(this.modelValue);
|
||||
this.updateDuration();
|
||||
if (minutes.value > 0) {
|
||||
duration += `${minutes.value}M`;
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
years: 0,
|
||||
months: 0,
|
||||
weeks: 0,
|
||||
days: 0,
|
||||
hours: 0,
|
||||
minutes: 0,
|
||||
seconds: 0,
|
||||
customDuration: "",
|
||||
durationIssue: null
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
years: "updateDuration",
|
||||
months: "updateDuration",
|
||||
weeks: "updateDuration",
|
||||
days: "updateDuration",
|
||||
hours: "updateDuration",
|
||||
minutes: "updateDuration",
|
||||
seconds: "updateDuration"
|
||||
},
|
||||
methods: {
|
||||
updateDuration() {
|
||||
let duration = "P"
|
||||
if (this.years > 0) {
|
||||
duration += `${this.years}Y`;
|
||||
}
|
||||
if (this.months > 0) {
|
||||
duration += `${this.months}M`;
|
||||
}
|
||||
if (this.weeks > 0) {
|
||||
duration += `${this.weeks}W`;
|
||||
}
|
||||
if (this.days > 0) {
|
||||
duration += `${this.days}D`;
|
||||
}
|
||||
if (this.hours > 0 || this.minutes > 0 || this.seconds > 0) {
|
||||
duration += "T"
|
||||
if (this.hours > 0) {
|
||||
duration += `${this.hours}H`;
|
||||
}
|
||||
if (this.minutes > 0) {
|
||||
duration += `${this.minutes}M`;
|
||||
}
|
||||
if (this.seconds > 0) {
|
||||
duration += `${this.seconds}S`;
|
||||
}
|
||||
}
|
||||
|
||||
if (duration === "P") {
|
||||
duration = null;
|
||||
}
|
||||
|
||||
this.customDuration = duration;
|
||||
this.durationIssue = null;
|
||||
this.$emit("update:model-value", duration);
|
||||
},
|
||||
parseDuration(durationString) {
|
||||
this.customDuration = durationString;
|
||||
const [datePart, timePart] = durationString.includes("T") ? durationString.split("T") : [durationString, null];
|
||||
let durationIssueMessage = null;
|
||||
|
||||
try {
|
||||
if (datePart && datePart !== "P") {
|
||||
const period = Period.parse(datePart);
|
||||
this.years = period.years();
|
||||
this.months = period.months();
|
||||
const days = period.days();
|
||||
|
||||
this.weeks = Math.floor(days / 7);
|
||||
this.days = days % 7;
|
||||
} else {
|
||||
this.years = 0; this.months = 0; this.weeks = 0; this.days = 0;
|
||||
}
|
||||
|
||||
if (timePart) {
|
||||
const timeDuration = Duration.parse(`PT${timePart}`);
|
||||
this.hours = timeDuration.toHours();
|
||||
this.minutes = timeDuration.toMinutes() % 60;
|
||||
this.seconds = timeDuration.seconds() % 60;
|
||||
} else {
|
||||
this.hours = 0; this.minutes = 0; this.seconds = 0;
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
durationIssueMessage = e.message;
|
||||
this.$emit("update:model-value", null);
|
||||
}
|
||||
|
||||
this.durationIssue = durationIssueMessage;
|
||||
if (seconds.value > 0) {
|
||||
duration += `${seconds.value}S`;
|
||||
}
|
||||
}
|
||||
|
||||
let finalDuration: string | null = duration;
|
||||
if (duration === "P") {
|
||||
finalDuration = null;
|
||||
}
|
||||
|
||||
customDuration.value = finalDuration ?? "";
|
||||
durationIssue.value = null;
|
||||
emit("update:model-value", finalDuration);
|
||||
};
|
||||
|
||||
const parseDuration = (durationString: string) => {
|
||||
customDuration.value = durationString;
|
||||
const [datePart, timePart] = durationString.includes("T") ? durationString.split("T") : [durationString, null];
|
||||
let durationIssueMessage: string | null = null;
|
||||
|
||||
try {
|
||||
if (datePart && datePart !== "P") {
|
||||
const period = Period.parse(datePart);
|
||||
years.value = period.years();
|
||||
months.value = period.months();
|
||||
const parsedDays = period.days();
|
||||
|
||||
weeks.value = Math.floor(parsedDays / 7);
|
||||
days.value = parsedDays % 7;
|
||||
} else {
|
||||
years.value = 0; months.value = 0; weeks.value = 0; days.value = 0;
|
||||
}
|
||||
|
||||
if (timePart) {
|
||||
const timeDuration = Duration.parse(`PT${timePart}`);
|
||||
hours.value = timeDuration.toHours();
|
||||
minutes.value = timeDuration.toMinutes() % 60;
|
||||
seconds.value = timeDuration.seconds() % 60;
|
||||
} else {
|
||||
hours.value = 0; minutes.value = 0; seconds.value = 0;
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
durationIssueMessage = (e as Error).message;
|
||||
emit("update:model-value", null);
|
||||
}
|
||||
|
||||
durationIssue.value = durationIssueMessage;
|
||||
};
|
||||
|
||||
watch(years, updateDuration);
|
||||
watch(months, updateDuration);
|
||||
watch(weeks, updateDuration);
|
||||
watch(days, updateDuration);
|
||||
watch(hours, updateDuration);
|
||||
watch(minutes, updateDuration);
|
||||
watch(seconds, updateDuration);
|
||||
|
||||
onMounted(() => {
|
||||
parseDuration(props.modelValue ?? "");
|
||||
updateDuration();
|
||||
});
|
||||
|
||||
onUpdated(() => {
|
||||
if (props.modelValue) {
|
||||
parseDuration(props.modelValue);
|
||||
updateDuration();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -24,51 +24,34 @@
|
||||
</el-select>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
const isValidLabel = (label) => {
|
||||
<script setup lang="ts">
|
||||
import {ref, watch} from "vue";
|
||||
|
||||
const isValidLabel = (label: string): boolean => {
|
||||
return label.match(".+:.+") !== null;
|
||||
};
|
||||
|
||||
const isValidLabels = (labels) => {
|
||||
return labels.every((label) => isValidLabel(label));
|
||||
const props = defineProps<{
|
||||
modelValue?: string | string[];
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
"update:modelValue": [value: string[]];
|
||||
}>();
|
||||
|
||||
const asArrayProp = (unknownValue: string | string[] | undefined): string[] => {
|
||||
return (!Array.isArray(unknownValue) && unknownValue !== undefined) ? [unknownValue] : (unknownValue ?? []);
|
||||
};
|
||||
|
||||
export default {
|
||||
props: {
|
||||
modelValue: {
|
||||
type: [Array, String],
|
||||
default: () => [],
|
||||
validator(value) {
|
||||
return typeof value === "string" ? isValidLabel(value) : isValidLabels(value);
|
||||
}
|
||||
}
|
||||
},
|
||||
emits: ["update:modelValue"],
|
||||
created() {
|
||||
this.labels = this.asArrayProp(this.modelValue);
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
hover: false,
|
||||
inputValue: undefined,
|
||||
labels: [],
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
modelValue: {
|
||||
handler (newValue) {
|
||||
this.labels = this.asArrayProp(newValue);
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
asArrayProp(unknownValue) {
|
||||
return (!Array.isArray(unknownValue) && unknownValue !== undefined) ? [unknownValue] : unknownValue;
|
||||
},
|
||||
onInput(value) {
|
||||
this.labels = value.filter((label) => isValidLabel(label));
|
||||
this.$emit("update:modelValue", this.labels)
|
||||
},
|
||||
}
|
||||
const hover = ref<boolean>(false);
|
||||
const labels = ref<string[]>(asArrayProp(props.modelValue));
|
||||
|
||||
watch(() => props.modelValue, (newValue) => {
|
||||
labels.value = asArrayProp(newValue);
|
||||
});
|
||||
|
||||
const onInput = (value: string[]) => {
|
||||
labels.value = value.filter((label) => isValidLabel(label));
|
||||
emit("update:modelValue", labels.value);
|
||||
};
|
||||
</script>
|
||||
@@ -52,6 +52,7 @@
|
||||
}>();
|
||||
|
||||
const route = useRoute();
|
||||
const PAGINATION_SIZE = `${storageKeys.PAGINATION_SIZE}__${String(route.name)}`;
|
||||
|
||||
const {t} = useI18n();
|
||||
|
||||
@@ -64,7 +65,7 @@
|
||||
|
||||
const internalSize = ref<number>(
|
||||
parseInt(
|
||||
localStorage.getItem(storageKeys.PAGINATION_SIZE) as string ||
|
||||
localStorage.getItem(PAGINATION_SIZE) as string ||
|
||||
(route.query.size as string) ||
|
||||
props.size?.toString() ||
|
||||
"25"
|
||||
@@ -83,7 +84,7 @@
|
||||
function pageSizeChange(value: number) {
|
||||
internalPage.value = 1;
|
||||
internalSize.value = value;
|
||||
localStorage.setItem(storageKeys.PAGINATION_SIZE, value.toString());
|
||||
localStorage.setItem(PAGINATION_SIZE, value.toString());
|
||||
emit("page-changed", {
|
||||
page: 1,
|
||||
size: internalSize.value,
|
||||
@@ -109,7 +110,7 @@
|
||||
() => route.query,
|
||||
() => {
|
||||
internalSize.value = parseInt(
|
||||
localStorage.getItem(storageKeys.PAGINATION_SIZE) as string ||
|
||||
localStorage.getItem(PAGINATION_SIZE) as string ||
|
||||
(route.query.size as string) ||
|
||||
props.size?.toString() ||
|
||||
"25"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<nav data-component="FILENAME_PLACEHOLDER" class="d-flex w-100 gap-3 top-bar">
|
||||
<nav data-component="FILENAME_PLACEHOLDER" class="d-flex align-items-center w-100 gap-3 top-bar">
|
||||
<div class="d-flex flex-column flex-grow-1 flex-shrink-1 overflow-hidden top-title">
|
||||
<div class="d-flex align-items-end gap-2">
|
||||
<SidebarToggleButton
|
||||
|
||||
@@ -83,6 +83,10 @@
|
||||
type: Object,
|
||||
default: null
|
||||
},
|
||||
reloadLogs: {
|
||||
type: Number,
|
||||
default: undefined
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -206,7 +210,6 @@
|
||||
load() {
|
||||
this.isLoading = true
|
||||
|
||||
|
||||
const data = {
|
||||
page: this.filters ? this.internalPageNumber : this.$route.query.page || this.internalPageNumber,
|
||||
size: this.filters ? this.internalPageSize : this.$route.query.size || this.internalPageSize,
|
||||
@@ -224,6 +227,11 @@
|
||||
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
reloadLogs(newValue) {
|
||||
if(newValue) this.refresh();
|
||||
},
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
#{--el-color-alert-#{$type}}: var(--ks-content-#{$type});
|
||||
}
|
||||
|
||||
--bs-code-color: $base-purple-600;
|
||||
#{--bs-code-color}: $base-purple-600;
|
||||
|
||||
#{--card-bg}: var(--ks-background-card);
|
||||
#{--input-bg}: var(--ks-background-input);
|
||||
|
||||
@@ -778,6 +778,7 @@
|
||||
"missingSource": "Fehlende Quelle",
|
||||
"monogram": "Monogramm",
|
||||
"months": "Monate",
|
||||
"more_actions": "Weitere Aktionen",
|
||||
"multi_panel_editor": {
|
||||
"close_all_panels": "Alle Panels schließen",
|
||||
"close_all_tabs": "Alle Registerkarten schließen",
|
||||
@@ -806,11 +807,13 @@
|
||||
"label": "Löschen"
|
||||
},
|
||||
"dialog": {
|
||||
"confirm": "Bestätigen",
|
||||
"file_deletion": "Datei-Löschung bestätigen",
|
||||
"file_deletion_description": "Sind Sie sicher, dass Sie diese Datei löschen möchten?",
|
||||
"folder_deletion": "Ordner-Löschung bestätigen",
|
||||
"folder_deletion_description": "Sind Sie sicher, dass Sie diesen Ordner und alle seine Inhalte löschen möchten?",
|
||||
"deletion": {
|
||||
"confirm": "Bestätigen",
|
||||
"files": "Möchten Sie wirklich <code>{count}</code> Datei(en) löschen?",
|
||||
"folders": "Möchten Sie wirklich <code>{count}</code> Ordner und alle seine Inhalte löschen?",
|
||||
"mixed": "Möchten Sie wirklich den Ordner <code>{folders}</code> und die Datei <code>{files}</code> löschen?",
|
||||
"title": "Bestätigen Sie das Löschen des Inhalts"
|
||||
},
|
||||
"name": {
|
||||
"file": "Name mit Erweiterung:",
|
||||
"folder": "Ordnername:"
|
||||
|
||||
@@ -576,6 +576,7 @@
|
||||
},
|
||||
"title": "Title",
|
||||
"api": "API",
|
||||
"more_actions": "More Actions",
|
||||
"expand error": "Expand only failed tasks",
|
||||
"expand all": "Expand all",
|
||||
"collapse all": "Collapse all",
|
||||
|
||||
@@ -778,6 +778,7 @@
|
||||
"missingSource": "Fuente faltante",
|
||||
"monogram": "Monograma",
|
||||
"months": "Meses",
|
||||
"more_actions": "Más acciones",
|
||||
"multi_panel_editor": {
|
||||
"close_all_panels": "Cerrar todos los paneles",
|
||||
"close_all_tabs": "Cerrar todas las pestañas",
|
||||
@@ -806,11 +807,13 @@
|
||||
"label": "Eliminar"
|
||||
},
|
||||
"dialog": {
|
||||
"confirm": "Confirmar",
|
||||
"file_deletion": "Confirmar eliminación de archivo",
|
||||
"file_deletion_description": "¿Estás seguro de que quieres eliminar este archivo?",
|
||||
"folder_deletion": "Confirmar eliminación de carpeta",
|
||||
"folder_deletion_description": "¿Estás seguro de que quieres eliminar esta carpeta y todo su contenido?",
|
||||
"deletion": {
|
||||
"confirm": "Confirmar",
|
||||
"files": "¿Está seguro de que desea eliminar <code>{count}</code> archivo(s)?",
|
||||
"folders": "¿Está seguro de que desea eliminar <code>{count}</code> carpeta(s) y todo su contenido?",
|
||||
"mixed": "¿Está seguro de que desea eliminar la(s) carpeta(s) <code>{folders}</code> y el/los archivo(s) <code>{files}</code>?",
|
||||
"title": "Confirmar eliminación de contenido"
|
||||
},
|
||||
"name": {
|
||||
"file": "Nombre con extensión:",
|
||||
"folder": "Nombre de la carpeta:"
|
||||
|
||||
@@ -778,6 +778,7 @@
|
||||
"missingSource": "Source manquant",
|
||||
"monogram": "Monogramme",
|
||||
"months": "Mois",
|
||||
"more_actions": "Plus d'actions",
|
||||
"multi_panel_editor": {
|
||||
"close_all_panels": "Fermer tous les panneaux",
|
||||
"close_all_tabs": "Fermer tous les onglets",
|
||||
@@ -806,11 +807,13 @@
|
||||
"label": "Supprimer"
|
||||
},
|
||||
"dialog": {
|
||||
"confirm": "Confirmer",
|
||||
"file_deletion": "Confirmer la suppression du fichier",
|
||||
"file_deletion_description": "Êtes-vous sûr de vouloir supprimer ce fichier ?",
|
||||
"folder_deletion": "Confirmer la suppression du dossier",
|
||||
"folder_deletion_description": "Êtes-vous sûr de vouloir supprimer ce dossier et tous ses contenus ?",
|
||||
"deletion": {
|
||||
"confirm": "Confirmer",
|
||||
"files": "Êtes-vous sûr de vouloir supprimer <code>{count}</code> fichier(s) ?",
|
||||
"folders": "Êtes-vous sûr de vouloir supprimer <code>{count}</code> dossier(s) et tout son contenu ?",
|
||||
"mixed": "Êtes-vous sûr de vouloir supprimer le(s) dossier(s) <code>{folders}</code> et le(s) fichier(s) <code>{files}</code> ?",
|
||||
"title": "Confirmer la suppression du contenu"
|
||||
},
|
||||
"name": {
|
||||
"file": "Nom avec extension :",
|
||||
"folder": "Nom du dossier :"
|
||||
|
||||
@@ -778,6 +778,7 @@
|
||||
"missingSource": "स्रोत अनुपलब्ध",
|
||||
"monogram": "मोनोग्राम",
|
||||
"months": "महीने",
|
||||
"more_actions": "अधिक क्रियाएँ",
|
||||
"multi_panel_editor": {
|
||||
"close_all_panels": "सभी पैनल बंद करें",
|
||||
"close_all_tabs": "सभी टैब बंद करें",
|
||||
@@ -806,11 +807,13 @@
|
||||
"label": "हटाएं"
|
||||
},
|
||||
"dialog": {
|
||||
"confirm": "पुष्टि करें",
|
||||
"file_deletion": "फ़ाइल हटाने की पुष्टि करें",
|
||||
"file_deletion_description": "क्या आप वाकई इस फ़ाइल को हटाना चाहते हैं?",
|
||||
"folder_deletion": "फ़ोल्डर हटाने की पुष्टि करें",
|
||||
"folder_deletion_description": "क्या आप वाकई इस फ़ोल्डर और इसकी सभी सामग्री को हटाना चाहते हैं?",
|
||||
"deletion": {
|
||||
"confirm": "पुष्टि करें",
|
||||
"files": "क्या आप वाकई <code>{count}</code> फ़ाइल(फ़ाइलें) हटाना चाहते हैं?",
|
||||
"folders": "क्या आप वाकई <code>{count}</code> फ़ोल्डर और उसकी सभी सामग्री को हटाना चाहते हैं?",
|
||||
"mixed": "क्या आप वाकई <code>{folders}</code> फ़ोल्डर(ओं) और <code>{files}</code> फ़ाइल(ओं) को हटाना चाहते हैं?",
|
||||
"title": "सामग्री को हटाने की पुष्टि करें"
|
||||
},
|
||||
"name": {
|
||||
"file": "एक्सटेंशन के साथ नाम:",
|
||||
"folder": "फ़ोल्डर का नाम:"
|
||||
|
||||
@@ -778,6 +778,7 @@
|
||||
"missingSource": "Sorgente mancante",
|
||||
"monogram": "Monogramma",
|
||||
"months": "Mesi",
|
||||
"more_actions": "Altre azioni",
|
||||
"multi_panel_editor": {
|
||||
"close_all_panels": "Chiudi tutti i pannelli",
|
||||
"close_all_tabs": "Chiudi tutte le tab",
|
||||
@@ -806,11 +807,13 @@
|
||||
"label": "Elimina"
|
||||
},
|
||||
"dialog": {
|
||||
"confirm": "Conferma",
|
||||
"file_deletion": "Conferma eliminazione file",
|
||||
"file_deletion_description": "Sei sicuro di voler eliminare questo file?",
|
||||
"folder_deletion": "Conferma eliminazione cartella",
|
||||
"folder_deletion_description": "Sei sicuro di voler eliminare questa cartella e tutto il suo contenuto?",
|
||||
"deletion": {
|
||||
"confirm": "Conferma",
|
||||
"files": "Sei sicuro di voler eliminare <code>{count}</code> file?",
|
||||
"folders": "Sei sicuro di voler eliminare <code>{count}</code> cartella/e e tutto il suo contenuto?",
|
||||
"mixed": "Sei sicuro di voler eliminare la/le cartella/e <code>{folders}</code> e il/i file <code>{files}</code>?",
|
||||
"title": "Conferma eliminazione del contenuto"
|
||||
},
|
||||
"name": {
|
||||
"file": "Nome con estensione:",
|
||||
"folder": "Nome della cartella:"
|
||||
|
||||
@@ -778,6 +778,7 @@
|
||||
"missingSource": "ソースが見つかりません",
|
||||
"monogram": "モノグラム",
|
||||
"months": "月",
|
||||
"more_actions": "その他のアクション",
|
||||
"multi_panel_editor": {
|
||||
"close_all_panels": "すべてのパネルを閉じる",
|
||||
"close_all_tabs": "すべてのタブを閉じる",
|
||||
@@ -806,11 +807,13 @@
|
||||
"label": "削除"
|
||||
},
|
||||
"dialog": {
|
||||
"confirm": "確認",
|
||||
"file_deletion": "ファイル削除の確認",
|
||||
"file_deletion_description": "このファイルを削除してもよろしいですか?",
|
||||
"folder_deletion": "フォルダー削除の確認",
|
||||
"folder_deletion_description": "このフォルダーとそのすべての内容を削除してもよろしいですか?",
|
||||
"deletion": {
|
||||
"confirm": "確認",
|
||||
"files": "<code>{count}</code> 個のファイルを削除してもよろしいですか?",
|
||||
"folders": "<code>{count}</code> 個のフォルダーとそのすべての内容を削除してもよろしいですか?",
|
||||
"mixed": "<code>{folders}</code> フォルダーと <code>{files}</code> ファイルを削除してもよろしいですか?",
|
||||
"title": "コンテンツの削除を確認"
|
||||
},
|
||||
"name": {
|
||||
"file": "拡張子付きの名前:",
|
||||
"folder": "フォルダー名:"
|
||||
|
||||
@@ -778,6 +778,7 @@
|
||||
"missingSource": "소스 누락",
|
||||
"monogram": "모노그램",
|
||||
"months": "개월",
|
||||
"more_actions": "추가 작업",
|
||||
"multi_panel_editor": {
|
||||
"close_all_panels": "모든 패널 닫기",
|
||||
"close_all_tabs": "모든 탭 닫기",
|
||||
@@ -806,11 +807,13 @@
|
||||
"label": "삭제"
|
||||
},
|
||||
"dialog": {
|
||||
"confirm": "확인",
|
||||
"file_deletion": "파일 삭제 확인",
|
||||
"file_deletion_description": "이 파일을 삭제하시겠습니까?",
|
||||
"folder_deletion": "폴더 삭제 확인",
|
||||
"folder_deletion_description": "이 폴더와 모든 내용을 삭제하시겠습니까?",
|
||||
"deletion": {
|
||||
"confirm": "확인",
|
||||
"files": "<code>{count}</code>개의 파일을 삭제하시겠습니까?",
|
||||
"folders": "<code>{count}</code>개의 폴더와 모든 내용을 삭제하시겠습니까?",
|
||||
"mixed": "<code>{folders}</code> 폴더와 <code>{files}</code> 파일을 삭제하시겠습니까?",
|
||||
"title": "콘텐츠 삭제 확인"
|
||||
},
|
||||
"name": {
|
||||
"file": "확장자를 포함한 이름:",
|
||||
"folder": "폴더 이름:"
|
||||
|
||||
@@ -778,6 +778,7 @@
|
||||
"missingSource": "Brak źródła",
|
||||
"monogram": "Monogram",
|
||||
"months": "Miesiące",
|
||||
"more_actions": "Więcej akcji",
|
||||
"multi_panel_editor": {
|
||||
"close_all_panels": "Zamknij wszystkie panele",
|
||||
"close_all_tabs": "Zamknij wszystkie karty",
|
||||
@@ -806,11 +807,13 @@
|
||||
"label": "Usuń"
|
||||
},
|
||||
"dialog": {
|
||||
"confirm": "Potwierdź",
|
||||
"file_deletion": "Potwierdź usunięcie pliku",
|
||||
"file_deletion_description": "Czy na pewno chcesz usunąć ten plik?",
|
||||
"folder_deletion": "Potwierdź usunięcie folderu",
|
||||
"folder_deletion_description": "Czy na pewno chcesz usunąć ten folder i całą jego zawartość?",
|
||||
"deletion": {
|
||||
"confirm": "Potwierdź",
|
||||
"files": "Czy na pewno chcesz usunąć <code>{count}</code> plik(ów)?",
|
||||
"folders": "Czy na pewno chcesz usunąć <code>{count}</code> folder(y) i całą jego zawartość?",
|
||||
"mixed": "Czy na pewno chcesz usunąć folder(y) <code>{folders}</code> i plik(i) <code>{files}</code>?",
|
||||
"title": "Potwierdź usunięcie zawartości"
|
||||
},
|
||||
"name": {
|
||||
"file": "Nazwa z rozszerzeniem:",
|
||||
"folder": "Nazwa folderu:"
|
||||
|
||||
@@ -778,6 +778,7 @@
|
||||
"missingSource": "Fonte ausente",
|
||||
"monogram": "Monograma",
|
||||
"months": "Meses",
|
||||
"more_actions": "Mais Ações",
|
||||
"multi_panel_editor": {
|
||||
"close_all_panels": "Fechar todos os painéis",
|
||||
"close_all_tabs": "Fechar todas as abas",
|
||||
@@ -806,11 +807,13 @@
|
||||
"label": "Deletar"
|
||||
},
|
||||
"dialog": {
|
||||
"confirm": "Confirmar",
|
||||
"file_deletion": "Confirmar deleção de arquivo",
|
||||
"file_deletion_description": "Tem certeza de que deseja deletar este arquivo?",
|
||||
"folder_deletion": "Confirmar deleção de pasta",
|
||||
"folder_deletion_description": "Tem certeza de que deseja deletar esta pasta e todo o seu conteúdo?",
|
||||
"deletion": {
|
||||
"confirm": "Confirmar",
|
||||
"files": "Tem certeza de que deseja excluir <code>{count}</code> arquivo(s)?",
|
||||
"folders": "Tem certeza de que deseja excluir <code>{count}</code> pasta(s) e todo o seu conteúdo?",
|
||||
"mixed": "Tem certeza de que deseja excluir a(s) pasta(s) <code>{folders}</code> e o(s) arquivo(s) <code>{files}</code>?",
|
||||
"title": "Confirmar exclusão de conteúdo"
|
||||
},
|
||||
"name": {
|
||||
"file": "Nome com extensão:",
|
||||
"folder": "Nome da pasta:"
|
||||
|
||||
@@ -778,6 +778,7 @@
|
||||
"missingSource": "Fonte ausente",
|
||||
"monogram": "Monograma",
|
||||
"months": "Meses",
|
||||
"more_actions": "Mais Ações",
|
||||
"multi_panel_editor": {
|
||||
"close_all_panels": "Fechar todos os painéis",
|
||||
"close_all_tabs": "Fechar todas as abas",
|
||||
@@ -806,11 +807,13 @@
|
||||
"label": "Excluir"
|
||||
},
|
||||
"dialog": {
|
||||
"confirm": "Confirmar",
|
||||
"file_deletion": "Confirmar deleção de arquivo",
|
||||
"file_deletion_description": "Tem certeza de que deseja excluir este arquivo?",
|
||||
"folder_deletion": "Confirmar deleção de pasta",
|
||||
"folder_deletion_description": "Tem certeza de que deseja excluir esta pasta e todo o seu conteúdo?",
|
||||
"deletion": {
|
||||
"confirm": "Confirmar",
|
||||
"files": "Tem certeza de que deseja excluir <code>{count}</code> arquivo(s)?",
|
||||
"folders": "Tem certeza de que deseja excluir <code>{count}</code> pasta(s) e todo o seu conteúdo?",
|
||||
"mixed": "Tem certeza de que deseja excluir a(s) pasta(s) <code>{folders}</code> e o(s) arquivo(s) <code>{files}</code>?",
|
||||
"title": "Confirmar exclusão de conteúdo"
|
||||
},
|
||||
"name": {
|
||||
"file": "Nome com extensão:",
|
||||
"folder": "Nome da pasta:"
|
||||
|
||||
@@ -778,6 +778,7 @@
|
||||
"missingSource": "Отсутствует источник",
|
||||
"monogram": "Монограмма",
|
||||
"months": "Месяцы",
|
||||
"more_actions": "Больше действий",
|
||||
"multi_panel_editor": {
|
||||
"close_all_panels": "Закрыть все панели",
|
||||
"close_all_tabs": "Закрыть все вкладки",
|
||||
@@ -806,11 +807,13 @@
|
||||
"label": "Удалить"
|
||||
},
|
||||
"dialog": {
|
||||
"confirm": "Подтвердить",
|
||||
"file_deletion": "Подтвердите удаление файла",
|
||||
"file_deletion_description": "Вы уверены, что хотите удалить этот файл?",
|
||||
"folder_deletion": "Подтвердите удаление папки",
|
||||
"folder_deletion_description": "Вы уверены, что хотите удалить эту папку и все ее содержимое?",
|
||||
"deletion": {
|
||||
"confirm": "Подтвердить",
|
||||
"files": "Вы уверены, что хотите удалить <code>{count}</code> файл(ы)?",
|
||||
"folders": "Вы уверены, что хотите удалить <code>{count}</code> папку(и) и все её содержимое?",
|
||||
"mixed": "Вы уверены, что хотите удалить папку(и) <code>{folders}</code> и файл(ы) <code>{files}</code>?",
|
||||
"title": "Подтвердите удаление содержимого"
|
||||
},
|
||||
"name": {
|
||||
"file": "Имя с расширением:",
|
||||
"folder": "Имя папки:"
|
||||
|
||||
@@ -778,6 +778,7 @@
|
||||
"missingSource": "缺少源",
|
||||
"monogram": "字母组合",
|
||||
"months": "月份",
|
||||
"more_actions": "更多操作",
|
||||
"multi_panel_editor": {
|
||||
"close_all_panels": "关闭所有面板",
|
||||
"close_all_tabs": "关闭所有标签页",
|
||||
@@ -806,11 +807,13 @@
|
||||
"label": "删除"
|
||||
},
|
||||
"dialog": {
|
||||
"confirm": "确认",
|
||||
"file_deletion": "确认删除文件",
|
||||
"file_deletion_description": "确定要删除此文件吗?",
|
||||
"folder_deletion": "确认删除文件夹",
|
||||
"folder_deletion_description": "确定要删除此文件夹及其所有内容吗?",
|
||||
"deletion": {
|
||||
"confirm": "确认",
|
||||
"files": "您确定要删除 <code>{count}</code> 个文件吗?",
|
||||
"folders": "您确定要删除 <code>{count}</code> 个文件夹及其所有内容吗?",
|
||||
"mixed": "您确定要删除 <code>{folders}</code> 个文件夹和 <code>{files}</code> 个文件吗?",
|
||||
"title": "确认删除内容"
|
||||
},
|
||||
"name": {
|
||||
"file": "文件名(带扩展名):",
|
||||
"folder": "文件夹名:"
|
||||
|
||||
@@ -106,6 +106,11 @@ export const InputTypes = {
|
||||
"Seventh value",
|
||||
"Eighth value"
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "duration_field",
|
||||
type: "DURATION",
|
||||
displayName: "Duration select input",
|
||||
}]}
|
||||
/>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user