Tidy automation frontend: extract CSS, use the Label component
- Move the automation-node and json-viewer styles out of app.css into their own imported stylesheets. - Replace raw <label> elements across the node config panels and the test panel with the shared ui/label component.
This commit is contained in:
parent
730cb9d156
commit
ff8fd9de40
17 changed files with 174 additions and 172 deletions
|
|
@ -1,6 +1,8 @@
|
|||
@import 'tailwindcss';
|
||||
@import 'tw-animate-css';
|
||||
@import 'vue-sonner/style.css';
|
||||
@import './automations.css';
|
||||
@import './json-viewer.css';
|
||||
@plugin '@tailwindcss/typography';
|
||||
@source '../../vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php';
|
||||
@source '../../storage/framework/views/*.php';
|
||||
|
|
@ -165,128 +167,3 @@ .trypost-container {
|
|||
@apply relative max-w-7xl w-full flex-1 mx-auto flex flex-col justify-center;
|
||||
}
|
||||
|
||||
/* Automations canvas nodes — TryPost brutalist identity:
|
||||
thick ink borders, hard offset shadow, warm card background, colored header
|
||||
strip that fills the top of the card with the accent tint. */
|
||||
.automation-node {
|
||||
position: relative;
|
||||
min-width: 230px;
|
||||
max-width: 260px;
|
||||
background: var(--card);
|
||||
border: 2px solid var(--foreground);
|
||||
border-radius: 14px;
|
||||
box-shadow: 3px 3px 0 var(--foreground);
|
||||
transition: transform 120ms ease, box-shadow 120ms ease;
|
||||
}
|
||||
|
||||
.automation-node:hover {
|
||||
transform: translate(-1px, -1px);
|
||||
box-shadow: 4px 4px 0 var(--foreground);
|
||||
}
|
||||
|
||||
.automation-node--wide {
|
||||
min-width: 260px;
|
||||
max-width: 260px;
|
||||
}
|
||||
|
||||
.automation-node.is-selected {
|
||||
transform: translate(-2px, -2px);
|
||||
box-shadow: 5px 5px 0 #7c3aed;
|
||||
}
|
||||
|
||||
.automation-node__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.625rem;
|
||||
padding: 0.625rem 0.875rem;
|
||||
border-bottom: 2px solid var(--foreground);
|
||||
border-top-left-radius: 12px;
|
||||
border-top-right-radius: 12px;
|
||||
}
|
||||
|
||||
.automation-node__icon-tile {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border: 2px solid var(--foreground);
|
||||
border-radius: 8px;
|
||||
flex-shrink: 0;
|
||||
transform: rotate(-3deg);
|
||||
}
|
||||
|
||||
/* Header accent tint mirrors the icon tile color, applied to the whole header
|
||||
strip so the node type is identifiable at a glance from the canvas. */
|
||||
.automation-node__icon-tile--violet { background: #ede9fe; color: #5b21b6; }
|
||||
.automation-node__icon-tile--blue { background: #dbeafe; color: #1d4ed8; }
|
||||
.automation-node__icon-tile--amber { background: #fef3c7; color: #92400e; }
|
||||
.automation-node__icon-tile--rose { background: #ffe4e6; color: #be123c; }
|
||||
.automation-node__icon-tile--emerald { background: #d1fae5; color: #047857; }
|
||||
.automation-node__icon-tile--slate { background: #e2e8f0; color: #334155; }
|
||||
.automation-node__icon-tile--zinc { background: #e4e4e7; color: #27272a; }
|
||||
.automation-node__icon-tile--cyan { background: #cffafe; color: #155e75; }
|
||||
|
||||
.automation-node--accent-violet .automation-node__header { background: #f5f3ff; }
|
||||
.automation-node--accent-blue .automation-node__header { background: #eff6ff; }
|
||||
.automation-node--accent-amber .automation-node__header { background: #fffbeb; }
|
||||
.automation-node--accent-rose .automation-node__header { background: #fff1f2; }
|
||||
.automation-node--accent-emerald .automation-node__header { background: #ecfdf5; }
|
||||
.automation-node--accent-slate .automation-node__header { background: #f1f5f9; }
|
||||
.automation-node--accent-zinc .automation-node__header { background: #f4f4f5; }
|
||||
.automation-node--accent-cyan .automation-node__header { background: #ecfeff; }
|
||||
|
||||
.automation-node__title {
|
||||
min-width: 0;
|
||||
font-weight: 700;
|
||||
font-size: 0.875rem;
|
||||
color: var(--foreground);
|
||||
line-height: 1.2;
|
||||
letter-spacing: -0.005em;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.automation-node__summary {
|
||||
padding: 0.625rem 0.875rem 0.75rem 0.875rem;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
color: color-mix(in srgb, var(--foreground) 70%, transparent);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
border-bottom-left-radius: 12px;
|
||||
border-bottom-right-radius: 12px;
|
||||
}
|
||||
|
||||
.automation-node__branches {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0.375rem 0.875rem 0.625rem 0.875rem;
|
||||
font-size: 0.625rem;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.06em;
|
||||
}
|
||||
|
||||
.automation-node__branch--yes { color: #047857; }
|
||||
.automation-node__branch--no { color: #be123c; }
|
||||
|
||||
/* JsonViewer — GitHub Light palette for highlight.js JSON output. */
|
||||
|
||||
.json-viewer__body {
|
||||
font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;
|
||||
margin: 0;
|
||||
background: #ffffff;
|
||||
color: #24292f;
|
||||
}
|
||||
|
||||
.json-viewer .hljs-attr { color: #0550ae; }
|
||||
.json-viewer .hljs-string { color: #0a3069; }
|
||||
.json-viewer .hljs-number { color: #0550ae; }
|
||||
.json-viewer .hljs-literal { color: #cf222e; }
|
||||
.json-viewer .hljs-punctuation { color: #57606a; }
|
||||
.json-viewer .hljs-comment { color: #6e7781; font-style: italic; }
|
||||
|
||||
|
|
|
|||
103
resources/css/automations.css
Normal file
103
resources/css/automations.css
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
.automation-node {
|
||||
position: relative;
|
||||
min-width: 230px;
|
||||
max-width: 260px;
|
||||
background: var(--card);
|
||||
border: 2px solid var(--foreground);
|
||||
border-radius: 14px;
|
||||
box-shadow: 3px 3px 0 var(--foreground);
|
||||
transition: transform 120ms ease, box-shadow 120ms ease;
|
||||
}
|
||||
|
||||
.automation-node:hover {
|
||||
transform: translate(-1px, -1px);
|
||||
box-shadow: 4px 4px 0 var(--foreground);
|
||||
}
|
||||
|
||||
.automation-node--wide {
|
||||
min-width: 260px;
|
||||
max-width: 260px;
|
||||
}
|
||||
|
||||
.automation-node.is-selected {
|
||||
transform: translate(-2px, -2px);
|
||||
box-shadow: 5px 5px 0 #7c3aed;
|
||||
}
|
||||
|
||||
.automation-node__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.625rem;
|
||||
padding: 0.625rem 0.875rem;
|
||||
border-bottom: 2px solid var(--foreground);
|
||||
border-top-left-radius: 12px;
|
||||
border-top-right-radius: 12px;
|
||||
}
|
||||
|
||||
.automation-node__icon-tile {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border: 2px solid var(--foreground);
|
||||
border-radius: 8px;
|
||||
flex-shrink: 0;
|
||||
transform: rotate(-3deg);
|
||||
}
|
||||
|
||||
.automation-node__icon-tile--violet { background: #ede9fe; color: #5b21b6; }
|
||||
.automation-node__icon-tile--blue { background: #dbeafe; color: #1d4ed8; }
|
||||
.automation-node__icon-tile--amber { background: #fef3c7; color: #92400e; }
|
||||
.automation-node__icon-tile--rose { background: #ffe4e6; color: #be123c; }
|
||||
.automation-node__icon-tile--emerald { background: #d1fae5; color: #047857; }
|
||||
.automation-node__icon-tile--slate { background: #e2e8f0; color: #334155; }
|
||||
.automation-node__icon-tile--zinc { background: #e4e4e7; color: #27272a; }
|
||||
.automation-node__icon-tile--cyan { background: #cffafe; color: #155e75; }
|
||||
|
||||
.automation-node--accent-violet .automation-node__header { background: #f5f3ff; }
|
||||
.automation-node--accent-blue .automation-node__header { background: #eff6ff; }
|
||||
.automation-node--accent-amber .automation-node__header { background: #fffbeb; }
|
||||
.automation-node--accent-rose .automation-node__header { background: #fff1f2; }
|
||||
.automation-node--accent-emerald .automation-node__header { background: #ecfdf5; }
|
||||
.automation-node--accent-slate .automation-node__header { background: #f1f5f9; }
|
||||
.automation-node--accent-zinc .automation-node__header { background: #f4f4f5; }
|
||||
.automation-node--accent-cyan .automation-node__header { background: #ecfeff; }
|
||||
|
||||
.automation-node__title {
|
||||
min-width: 0;
|
||||
font-weight: 700;
|
||||
font-size: 0.875rem;
|
||||
color: var(--foreground);
|
||||
line-height: 1.2;
|
||||
letter-spacing: -0.005em;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.automation-node__summary {
|
||||
padding: 0.625rem 0.875rem 0.75rem 0.875rem;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
color: color-mix(in srgb, var(--foreground) 70%, transparent);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
border-bottom-left-radius: 12px;
|
||||
border-bottom-right-radius: 12px;
|
||||
}
|
||||
|
||||
.automation-node__branches {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0.375rem 0.875rem 0.625rem 0.875rem;
|
||||
font-size: 0.625rem;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.06em;
|
||||
}
|
||||
|
||||
.automation-node__branch--yes { color: #047857; }
|
||||
.automation-node__branch--no { color: #be123c; }
|
||||
13
resources/css/json-viewer.css
Normal file
13
resources/css/json-viewer.css
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
.json-viewer__body {
|
||||
font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;
|
||||
margin: 0;
|
||||
background: #ffffff;
|
||||
color: #24292f;
|
||||
}
|
||||
|
||||
.json-viewer .hljs-attr { color: #0550ae; }
|
||||
.json-viewer .hljs-string { color: #0a3069; }
|
||||
.json-viewer .hljs-number { color: #0550ae; }
|
||||
.json-viewer .hljs-literal { color: #cf222e; }
|
||||
.json-viewer .hljs-punctuation { color: #57606a; }
|
||||
.json-viewer .hljs-comment { color: #6e7781; font-style: italic; }
|
||||
|
|
@ -8,6 +8,7 @@ import { showRun as showRunRoute } from '@/actions/App/Http/Controllers/App/Auto
|
|||
import JsonViewer from '@/components/JsonViewer.vue';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { useAutomationEcho } from '@/composables/echo/useAutomationEcho';
|
||||
import { test as testAutomation } from '@/routes/app/automations';
|
||||
|
||||
|
|
@ -152,10 +153,10 @@ const isZeroFetchResult = (nodeRun: NodeRun): boolean => {
|
|||
</div>
|
||||
|
||||
<div class="flex items-center justify-between gap-3 rounded-xl border-2 border-foreground bg-card p-3 shadow-[3px_3px_0_var(--foreground)]">
|
||||
<label class="flex cursor-pointer items-center gap-2 text-sm font-semibold text-foreground/70">
|
||||
<Label class="cursor-pointer font-semibold text-foreground/70">
|
||||
<Checkbox v-model="realData" :disabled="isStarting" />
|
||||
{{ $t('automations.test.with_real_data') }}
|
||||
</label>
|
||||
</Label>
|
||||
<Button size="sm" :disabled="isStarting || !!configIssue" @click="runTest">
|
||||
<IconLoader2 v-if="isStarting" class="size-4 animate-spin" />
|
||||
<IconPlayerPlay v-else class="size-4" />
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { ref, watch } from 'vue';
|
|||
|
||||
import InputError from '@/components/InputError.vue';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
|
|
@ -36,13 +37,13 @@ watch(local, (val) => emit('update', val), { deep: true });
|
|||
<template>
|
||||
<div class="space-y-3">
|
||||
<div>
|
||||
<label class="mb-1 block text-sm font-medium">{{ $t('automations.config.condition.field') }}</label>
|
||||
<Label class="mb-1 block">{{ $t('automations.config.condition.field') }}</Label>
|
||||
<Input v-model="local.field" placeholder="{{ trigger.title }}" />
|
||||
<InputError :message="errors?.field" class="mt-1" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="mb-1 block text-sm font-medium">{{ $t('automations.config.condition.operator') }}</label>
|
||||
<Label class="mb-1 block">{{ $t('automations.config.condition.operator') }}</Label>
|
||||
<Select v-model="local.operator">
|
||||
<SelectTrigger class="w-full">
|
||||
<SelectValue :placeholder="$t('automations.config.select_placeholder')" />
|
||||
|
|
@ -61,7 +62,7 @@ watch(local, (val) => emit('update', val), { deep: true });
|
|||
</div>
|
||||
|
||||
<div>
|
||||
<label class="mb-1 block text-sm font-medium">{{ $t('automations.config.condition.value') }}</label>
|
||||
<Label class="mb-1 block">{{ $t('automations.config.condition.value') }}</Label>
|
||||
<Input v-model="local.value" placeholder="keyword" />
|
||||
<InputError :message="errors?.value" class="mt-1" />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { ref, watch } from 'vue';
|
|||
|
||||
import InputError from '@/components/InputError.vue';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
|
|
@ -34,13 +35,13 @@ watch(local, (val) => emit('update', val), { deep: true });
|
|||
<template>
|
||||
<div class="space-y-3">
|
||||
<div>
|
||||
<label class="mb-1 block text-sm font-medium">{{ $t('automations.config.delay.duration') }}</label>
|
||||
<Label class="mb-1 block">{{ $t('automations.config.delay.duration') }}</Label>
|
||||
<Input type="number" v-model.number="local.duration" placeholder="1" />
|
||||
<InputError :message="errors?.duration" class="mt-1" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="mb-1 block text-sm font-medium">{{ $t('automations.config.delay.unit') }}</label>
|
||||
<Label class="mb-1 block">{{ $t('automations.config.delay.unit') }}</Label>
|
||||
<Select v-model="local.unit">
|
||||
<SelectTrigger class="w-full">
|
||||
<SelectValue :placeholder="$t('automations.config.select_placeholder')" />
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
import { ref, watch } from 'vue';
|
||||
|
||||
import InputError from '@/components/InputError.vue';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Textarea } from '@/components/ui/textarea';
|
||||
|
||||
interface EndConfig {
|
||||
|
|
@ -24,7 +25,7 @@ watch(local, (val) => emit('update', val), { deep: true });
|
|||
<template>
|
||||
<div class="space-y-3">
|
||||
<div>
|
||||
<label class="mb-1 block text-sm font-medium">{{ $t('automations.config.end.reason') }}</label>
|
||||
<Label class="mb-1 block">{{ $t('automations.config.end.reason') }}</Label>
|
||||
<Textarea v-model="local.reason" :placeholder="$t('automations.config.end.reason_placeholder')" :rows="3" />
|
||||
<InputError :message="errors?.reason" class="mt-1" />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { ref, watch } from 'vue';
|
|||
|
||||
import InputError from '@/components/InputError.vue';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
|
||||
interface FetchRssConfig {
|
||||
feed_url: string;
|
||||
|
|
@ -24,7 +25,7 @@ watch(local, (val) => emit('update', val), { deep: true });
|
|||
<template>
|
||||
<div class="space-y-3">
|
||||
<div>
|
||||
<label class="mb-1 block text-sm font-medium">{{ $t('automations.config.fetch_rss.feed_url') }}</label>
|
||||
<Label class="mb-1 block">{{ $t('automations.config.fetch_rss.feed_url') }}</Label>
|
||||
<Input v-model="local.feed_url" placeholder="https://example.com/feed.xml" />
|
||||
<InputError :message="errors?.feed_url" class="mt-1" />
|
||||
<p class="mt-1 text-xs text-foreground/50">{{ $t('automations.config.fetch_rss.feed_url_hint') }}</p>
|
||||
|
|
|
|||
|
|
@ -5,10 +5,10 @@ import { computed, ref, watch } from 'vue';
|
|||
import ChannelConfigurator from '@/components/ChannelConfigurator.vue';
|
||||
import CodeEditor from '@/components/CodeEditor.vue';
|
||||
import InputError from '@/components/InputError.vue';
|
||||
import { useExpandedEditor } from '@/composables/useExpandedEditor';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Switch } from '@/components/ui/switch';
|
||||
import { useExpandedEditor } from '@/composables/useExpandedEditor';
|
||||
import { getMediaRulesForContentType } from '@/composables/useMediaRules';
|
||||
import { getMediaIncompatibilityReason, getPlatformMetaIssue } from '@/composables/usePostCompliance';
|
||||
import type { PinterestBoard } from '@/types';
|
||||
|
|
@ -316,7 +316,7 @@ const channels = computed<Channel[]>(() =>
|
|||
</div>
|
||||
|
||||
<div v-show="!editorExpanded">
|
||||
<label class="mb-1 block text-sm font-medium">{{ $t('automations.config.generate.prompt_template') }}</label>
|
||||
<Label class="mb-1 block">{{ $t('automations.config.generate.prompt_template') }}</Label>
|
||||
<div class="h-40">
|
||||
<CodeEditor
|
||||
v-model="local.prompt_template"
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import CodeEditor from '@/components/CodeEditor.vue';
|
|||
import InputError from '@/components/InputError.vue';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
|
|
@ -115,7 +116,7 @@ const isBodyJsonInvalid = computed(() => {
|
|||
<div class="space-y-4">
|
||||
<div class="grid grid-cols-[110px_1fr] gap-2">
|
||||
<div>
|
||||
<label class="mb-1 block text-sm font-medium">{{ $t('automations.config.http_request.method') }}</label>
|
||||
<Label class="mb-1 block">{{ $t('automations.config.http_request.method') }}</Label>
|
||||
<Select v-model="local.method">
|
||||
<SelectTrigger class="w-full">
|
||||
<SelectValue />
|
||||
|
|
@ -127,14 +128,14 @@ const isBodyJsonInvalid = computed(() => {
|
|||
<InputError :message="errors?.method" class="mt-1" />
|
||||
</div>
|
||||
<div>
|
||||
<label class="mb-1 block text-sm font-medium">{{ $t('automations.config.http_request.url') }}</label>
|
||||
<Label class="mb-1 block">{{ $t('automations.config.http_request.url') }}</Label>
|
||||
<Input v-model="local.url" placeholder="https://api.example.com/items" />
|
||||
<InputError :message="errors?.url" class="mt-1" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="mb-1 block text-sm font-medium">{{ $t('automations.config.http_request.auth_type') }}</label>
|
||||
<Label class="mb-1 block">{{ $t('automations.config.http_request.auth_type') }}</Label>
|
||||
<Select v-model="local.auth_type">
|
||||
<SelectTrigger class="w-full">
|
||||
<SelectValue />
|
||||
|
|
@ -149,19 +150,19 @@ const isBodyJsonInvalid = computed(() => {
|
|||
</div>
|
||||
|
||||
<div v-if="local.auth_type === AuthType.Bearer">
|
||||
<label class="mb-1 block text-sm font-medium">{{ $t('automations.config.http_request.bearer_token') }}</label>
|
||||
<Label class="mb-1 block">{{ $t('automations.config.http_request.bearer_token') }}</Label>
|
||||
<Input v-model="local.auth_token" type="password" autocomplete="off" placeholder="sk-…" />
|
||||
<InputError :message="errors?.auth_token" class="mt-1" />
|
||||
</div>
|
||||
|
||||
<template v-if="local.auth_type === AuthType.Basic">
|
||||
<div>
|
||||
<label class="mb-1 block text-sm font-medium">{{ $t('automations.config.http_request.basic_username') }}</label>
|
||||
<Label class="mb-1 block">{{ $t('automations.config.http_request.basic_username') }}</Label>
|
||||
<Input v-model="local.auth_username" autocomplete="off" />
|
||||
<InputError :message="errors?.auth_username" class="mt-1" />
|
||||
</div>
|
||||
<div>
|
||||
<label class="mb-1 block text-sm font-medium">{{ $t('automations.config.http_request.basic_password') }}</label>
|
||||
<Label class="mb-1 block">{{ $t('automations.config.http_request.basic_password') }}</Label>
|
||||
<Input v-model="local.auth_password" type="password" autocomplete="off" />
|
||||
<InputError :message="errors?.auth_password" class="mt-1" />
|
||||
</div>
|
||||
|
|
@ -169,18 +170,18 @@ const isBodyJsonInvalid = computed(() => {
|
|||
|
||||
<template v-if="local.auth_type === AuthType.ApiKey">
|
||||
<div>
|
||||
<label class="mb-1 block text-sm font-medium">{{ $t('automations.config.http_request.api_key_header') }}</label>
|
||||
<Label class="mb-1 block">{{ $t('automations.config.http_request.api_key_header') }}</Label>
|
||||
<Input v-model="local.auth_header_name" placeholder="X-API-Key" />
|
||||
</div>
|
||||
<div>
|
||||
<label class="mb-1 block text-sm font-medium">{{ $t('automations.config.http_request.api_key_value') }}</label>
|
||||
<Label class="mb-1 block">{{ $t('automations.config.http_request.api_key_value') }}</Label>
|
||||
<Input v-model="local.auth_token" type="password" autocomplete="off" />
|
||||
<InputError :message="errors?.auth_token" class="mt-1" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div v-if="supportsBody" v-show="!editorExpanded">
|
||||
<label class="mb-1 block text-sm font-medium">{{ $t('automations.config.http_request.body_template') }}</label>
|
||||
<Label class="mb-1 block">{{ $t('automations.config.http_request.body_template') }}</Label>
|
||||
<div class="h-36">
|
||||
<CodeEditor
|
||||
v-model="local.body_template"
|
||||
|
|
@ -197,7 +198,7 @@ const isBodyJsonInvalid = computed(() => {
|
|||
</div>
|
||||
|
||||
<div>
|
||||
<label class="mb-1 block text-sm font-medium">{{ $t('automations.config.http_request.headers') }}</label>
|
||||
<Label class="mb-1 block">{{ $t('automations.config.http_request.headers') }}</Label>
|
||||
<div class="space-y-2">
|
||||
<div v-for="(row, index) in headerRows" :key="index" class="flex items-center gap-2">
|
||||
<Input
|
||||
|
|
@ -237,19 +238,19 @@ const isBodyJsonInvalid = computed(() => {
|
|||
|
||||
<div class="space-y-3">
|
||||
<div>
|
||||
<label class="mb-1 block text-sm font-medium">{{ $t('automations.config.http_request.items_path') }}</label>
|
||||
<Label class="mb-1 block">{{ $t('automations.config.http_request.items_path') }}</Label>
|
||||
<Input v-model="local.items_path" placeholder="data.items" />
|
||||
<p class="mt-1 text-xs text-foreground/50">{{ $t('automations.config.http_request.items_path_hint') }}</p>
|
||||
<InputError :message="errors?.items_path" class="mt-1" />
|
||||
</div>
|
||||
<div>
|
||||
<label class="mb-1 block text-sm font-medium">{{ $t('automations.config.http_request.item_date_path') }}</label>
|
||||
<Label class="mb-1 block">{{ $t('automations.config.http_request.item_date_path') }}</Label>
|
||||
<Input v-model="local.item_date_path" placeholder="published_at" />
|
||||
<p class="mt-1 text-xs text-foreground/50">{{ $t('automations.config.http_request.item_date_path_hint') }}</p>
|
||||
<InputError :message="errors?.item_date_path" class="mt-1" />
|
||||
</div>
|
||||
<div>
|
||||
<label class="mb-1 block text-sm font-medium">{{ $t('automations.config.http_request.item_key_path') }}</label>
|
||||
<Label class="mb-1 block">{{ $t('automations.config.http_request.item_key_path') }}</Label>
|
||||
<Input v-model="local.item_key_path" placeholder="id" />
|
||||
<p class="mt-1 text-xs text-foreground/50">{{ $t('automations.config.http_request.item_key_path_hint') }}</p>
|
||||
<InputError :message="errors?.item_key_path" class="mt-1" />
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { ref, watch } from 'vue';
|
|||
|
||||
import InputError from '@/components/InputError.vue';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
|
|
@ -34,7 +35,7 @@ watch(local, (val) => emit('update', val), { deep: true });
|
|||
<template>
|
||||
<div class="space-y-3">
|
||||
<div>
|
||||
<label class="mb-1 block text-sm font-medium">{{ $t('automations.config.publish.mode') }}</label>
|
||||
<Label class="mb-1 block">{{ $t('automations.config.publish.mode') }}</Label>
|
||||
<Select v-model="local.mode">
|
||||
<SelectTrigger class="w-full">
|
||||
<SelectValue :placeholder="$t('automations.config.select_placeholder')" />
|
||||
|
|
@ -49,7 +50,7 @@ watch(local, (val) => emit('update', val), { deep: true });
|
|||
</div>
|
||||
|
||||
<div v-if="local.mode === PublishMode.Scheduled">
|
||||
<label class="mb-1 block text-sm font-medium">{{ $t('automations.config.publish.scheduled_offset') }}</label>
|
||||
<Label class="mb-1 block">{{ $t('automations.config.publish.scheduled_offset') }}</Label>
|
||||
<Input type="number" v-model.number="local.scheduled_offset" placeholder="60" />
|
||||
<InputError :message="errors?.scheduled_offset" class="mt-1" />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import {
|
|||
} from '@/components/automations/schedule-summary';
|
||||
import InputError from '@/components/InputError.vue';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
|
|
@ -113,7 +114,7 @@ watch(local, (val) => emit('update', val), { deep: true });
|
|||
<template>
|
||||
<div class="space-y-3">
|
||||
<div>
|
||||
<label class="mb-1 block text-sm font-medium">{{ $t('automations.config.trigger.type') }}</label>
|
||||
<Label class="mb-1 block">{{ $t('automations.config.trigger.type') }}</Label>
|
||||
<Select v-model="local.trigger_type">
|
||||
<SelectTrigger class="w-full">
|
||||
<SelectValue :placeholder="$t('automations.config.select_placeholder')" />
|
||||
|
|
@ -141,7 +142,7 @@ watch(local, (val) => emit('update', val), { deep: true });
|
|||
|
||||
<template v-if="local.trigger_type === TriggerType.Schedule">
|
||||
<div>
|
||||
<label class="mb-1 block text-sm font-medium">{{ $t('automations.config.trigger.schedule.field') }}</label>
|
||||
<Label class="mb-1 block">{{ $t('automations.config.trigger.schedule.field') }}</Label>
|
||||
<Select v-model="local.schedule_field">
|
||||
<SelectTrigger class="w-full">
|
||||
<SelectValue :placeholder="$t('automations.config.select_placeholder')" />
|
||||
|
|
@ -157,17 +158,17 @@ watch(local, (val) => emit('update', val), { deep: true });
|
|||
</div>
|
||||
|
||||
<div v-if="local.schedule_field === ScheduleField.Minutes">
|
||||
<label class="mb-1 block text-sm font-medium">{{ $t('automations.config.trigger.schedule.minutes_interval') }}</label>
|
||||
<Label class="mb-1 block">{{ $t('automations.config.trigger.schedule.minutes_interval') }}</Label>
|
||||
<Input type="number" v-model.number="local.schedule_minutes_interval" min="1" max="59" />
|
||||
</div>
|
||||
|
||||
<template v-if="local.schedule_field === ScheduleField.Hours">
|
||||
<div>
|
||||
<label class="mb-1 block text-sm font-medium">{{ $t('automations.config.trigger.schedule.hours_interval') }}</label>
|
||||
<Label class="mb-1 block">{{ $t('automations.config.trigger.schedule.hours_interval') }}</Label>
|
||||
<Input type="number" v-model.number="local.schedule_hours_interval" min="1" max="23" />
|
||||
</div>
|
||||
<div>
|
||||
<label class="mb-1 block text-sm font-medium">{{ $t('automations.config.trigger.schedule.minute') }}</label>
|
||||
<Label class="mb-1 block">{{ $t('automations.config.trigger.schedule.minute') }}</Label>
|
||||
<Select v-model="scheduleMinuteStr">
|
||||
<SelectTrigger class="w-full">
|
||||
<SelectValue />
|
||||
|
|
@ -181,12 +182,12 @@ watch(local, (val) => emit('update', val), { deep: true });
|
|||
|
||||
<template v-if="local.schedule_field === ScheduleField.Days">
|
||||
<div>
|
||||
<label class="mb-1 block text-sm font-medium">{{ $t('automations.config.trigger.schedule.days_interval') }}</label>
|
||||
<Label class="mb-1 block">{{ $t('automations.config.trigger.schedule.days_interval') }}</Label>
|
||||
<Input type="number" v-model.number="local.schedule_days_interval" min="1" max="31" />
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-3">
|
||||
<div>
|
||||
<label class="mb-1 block text-sm font-medium">{{ $t('automations.config.trigger.schedule.hour') }}</label>
|
||||
<Label class="mb-1 block">{{ $t('automations.config.trigger.schedule.hour') }}</Label>
|
||||
<Select v-model="scheduleHourStr">
|
||||
<SelectTrigger class="w-full">
|
||||
<SelectValue />
|
||||
|
|
@ -197,7 +198,7 @@ watch(local, (val) => emit('update', val), { deep: true });
|
|||
</Select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="mb-1 block text-sm font-medium">{{ $t('automations.config.trigger.schedule.minute') }}</label>
|
||||
<Label class="mb-1 block">{{ $t('automations.config.trigger.schedule.minute') }}</Label>
|
||||
<Select v-model="scheduleMinuteStr">
|
||||
<SelectTrigger class="w-full">
|
||||
<SelectValue />
|
||||
|
|
@ -212,7 +213,7 @@ watch(local, (val) => emit('update', val), { deep: true });
|
|||
|
||||
<template v-if="local.schedule_field === ScheduleField.Weeks">
|
||||
<div>
|
||||
<label class="mb-1 block text-sm font-medium">{{ $t('automations.config.trigger.schedule.weekdays') }}</label>
|
||||
<Label class="mb-1 block">{{ $t('automations.config.trigger.schedule.weekdays') }}</Label>
|
||||
<div class="flex flex-wrap gap-1.5">
|
||||
<button
|
||||
v-for="day in weekdays"
|
||||
|
|
@ -230,7 +231,7 @@ watch(local, (val) => emit('update', val), { deep: true });
|
|||
</div>
|
||||
<div class="grid grid-cols-2 gap-3">
|
||||
<div>
|
||||
<label class="mb-1 block text-sm font-medium">{{ $t('automations.config.trigger.schedule.hour') }}</label>
|
||||
<Label class="mb-1 block">{{ $t('automations.config.trigger.schedule.hour') }}</Label>
|
||||
<Select v-model="scheduleHourStr">
|
||||
<SelectTrigger class="w-full">
|
||||
<SelectValue />
|
||||
|
|
@ -241,7 +242,7 @@ watch(local, (val) => emit('update', val), { deep: true });
|
|||
</Select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="mb-1 block text-sm font-medium">{{ $t('automations.config.trigger.schedule.minute') }}</label>
|
||||
<Label class="mb-1 block">{{ $t('automations.config.trigger.schedule.minute') }}</Label>
|
||||
<Select v-model="scheduleMinuteStr">
|
||||
<SelectTrigger class="w-full">
|
||||
<SelectValue />
|
||||
|
|
@ -256,12 +257,12 @@ watch(local, (val) => emit('update', val), { deep: true });
|
|||
|
||||
<template v-if="local.schedule_field === ScheduleField.Months">
|
||||
<div>
|
||||
<label class="mb-1 block text-sm font-medium">{{ $t('automations.config.trigger.schedule.day_of_month') }}</label>
|
||||
<Label class="mb-1 block">{{ $t('automations.config.trigger.schedule.day_of_month') }}</Label>
|
||||
<Input type="number" v-model.number="local.schedule_day_of_month" min="1" max="31" />
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-3">
|
||||
<div>
|
||||
<label class="mb-1 block text-sm font-medium">{{ $t('automations.config.trigger.schedule.hour') }}</label>
|
||||
<Label class="mb-1 block">{{ $t('automations.config.trigger.schedule.hour') }}</Label>
|
||||
<Select v-model="scheduleHourStr">
|
||||
<SelectTrigger class="w-full">
|
||||
<SelectValue />
|
||||
|
|
@ -272,7 +273,7 @@ watch(local, (val) => emit('update', val), { deep: true });
|
|||
</Select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="mb-1 block text-sm font-medium">{{ $t('automations.config.trigger.schedule.minute') }}</label>
|
||||
<Label class="mb-1 block">{{ $t('automations.config.trigger.schedule.minute') }}</Label>
|
||||
<Select v-model="scheduleMinuteStr">
|
||||
<SelectTrigger class="w-full">
|
||||
<SelectValue />
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { isPayloadTemplateValid } from '@/components/automations/config-validati
|
|||
import CodeEditor from '@/components/CodeEditor.vue';
|
||||
import InputError from '@/components/InputError.vue';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
|
|
@ -45,13 +46,13 @@ const isPayloadJsonInvalid = computed(() => !isPayloadTemplateValid(local.value.
|
|||
<template>
|
||||
<div class="space-y-3">
|
||||
<div>
|
||||
<label class="mb-1 block text-sm font-medium">{{ $t('automations.config.webhook.url') }}</label>
|
||||
<Label class="mb-1 block">{{ $t('automations.config.webhook.url') }}</Label>
|
||||
<Input v-model="local.url" placeholder="https://hooks.example.com/…" />
|
||||
<InputError :message="errors?.url" class="mt-1" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="mb-1 block text-sm font-medium">{{ $t('automations.config.webhook.method') }}</label>
|
||||
<Label class="mb-1 block">{{ $t('automations.config.webhook.method') }}</Label>
|
||||
<Select v-model="local.method">
|
||||
<SelectTrigger class="w-full">
|
||||
<SelectValue :placeholder="$t('automations.config.select_placeholder')" />
|
||||
|
|
@ -64,7 +65,7 @@ const isPayloadJsonInvalid = computed(() => !isPayloadTemplateValid(local.value.
|
|||
</div>
|
||||
|
||||
<div v-show="!editorExpanded">
|
||||
<label class="mb-1 block text-sm font-medium">{{ $t('automations.config.webhook.payload_template') }}</label>
|
||||
<Label class="mb-1 block">{{ $t('automations.config.webhook.payload_template') }}</Label>
|
||||
<div class="h-40">
|
||||
<CodeEditor
|
||||
v-model="local.payload_template"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import { Handle, Position } from '@vue-flow/core';
|
||||
import { IconCircleX } from '@tabler/icons-vue';
|
||||
import { Handle, Position } from '@vue-flow/core';
|
||||
import { computed } from 'vue';
|
||||
|
||||
const props = defineProps<{
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import { Handle, Position } from '@vue-flow/core';
|
||||
import { IconRss } from '@tabler/icons-vue';
|
||||
import { Handle, Position } from '@vue-flow/core';
|
||||
import { computed } from 'vue';
|
||||
|
||||
const props = defineProps<{
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import { Handle, Position } from '@vue-flow/core';
|
||||
import { IconSparkles } from '@tabler/icons-vue';
|
||||
import { Handle, Position } from '@vue-flow/core';
|
||||
import { computed } from 'vue';
|
||||
|
||||
const props = defineProps<{
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import { Handle, Position } from '@vue-flow/core';
|
||||
import { IconBolt } from '@tabler/icons-vue';
|
||||
import { Handle, Position } from '@vue-flow/core';
|
||||
import { computed } from 'vue';
|
||||
|
||||
import { triggerSummary } from '@/components/automations/schedule-summary';
|
||||
|
|
|
|||
Loading…
Reference in a new issue