feat(automations): mobile-safe builder with desktop notice

The node builder is gated to lg+ (its fixed side panels no longer overflow a phone); below lg it shows a 'works best on a larger screen' notice. Automation tabs scroll instead of cutting off.
This commit is contained in:
Paulo Castellano 2026-07-17 18:38:20 -03:00
parent 49282d2acb
commit 5118640a9c
18 changed files with 63 additions and 4 deletions

View file

@ -3,6 +3,7 @@
declare(strict_types=1);
return [
'mobile_notice' => 'يعمل محرر الأتمتة بشكل أفضل على شاشة أكبر. افتحه على جهاز كمبيوتر مكتبي لإنشاء سير عملك.',
'title' => 'الأتمتة',
'default_name' => 'أتمتة جديدة',

View file

@ -3,6 +3,7 @@
declare(strict_types=1);
return [
'mobile_notice' => 'Der Automatisierungs-Editor funktioniert am besten auf einem größeren Bildschirm. Öffne ihn auf einem Desktop, um deinen Workflow zu erstellen.',
'title' => 'Automatisierungen',
'default_name' => 'Neue Automatisierung',

View file

@ -3,6 +3,7 @@
declare(strict_types=1);
return [
'mobile_notice' => 'Ο επεξεργαστής αυτοματισμών λειτουργεί καλύτερα σε μεγαλύτερη οθόνη. Άνοιξέ τον σε υπολογιστή για να δημιουργήσεις τη ροή εργασίας σου.',
'title' => 'Αυτοματισμοί',
'default_name' => 'Νέος αυτοματισμός',

View file

@ -3,6 +3,7 @@
declare(strict_types=1);
return [
'mobile_notice' => 'The automation editor works best on a larger screen. Open it on a desktop to build your workflow.',
'title' => 'Automations',
'default_name' => 'New automation',

View file

@ -3,6 +3,7 @@
declare(strict_types=1);
return [
'mobile_notice' => 'El editor de automatizaciones funciona mejor en una pantalla más grande. Ábrelo en un ordenador para crear tu flujo de trabajo.',
'title' => 'Automatizaciones',
'default_name' => 'Nueva automatización',

View file

@ -3,6 +3,7 @@
declare(strict_types=1);
return [
'mobile_notice' => 'L\'éditeur d\'automatisation fonctionne mieux sur un grand écran. Ouvrez-le sur un ordinateur pour créer votre workflow.',
'title' => 'Automatisations',
'default_name' => 'Nouvelle automatisation',

View file

@ -3,6 +3,7 @@
declare(strict_types=1);
return [
'mobile_notice' => 'L\'editor delle automazioni funziona meglio su uno schermo più grande. Aprilo su un desktop per creare il tuo flusso di lavoro.',
'title' => 'Automazioni',
'default_name' => 'Nuova automazione',

View file

@ -3,6 +3,7 @@
declare(strict_types=1);
return [
'mobile_notice' => 'オートメーションエディタは大きな画面での利用に最適です。ワークフローを作成するにはデスクトップで開いてください。',
'title' => 'オートメーション',
'default_name' => '新しいオートメーション',

View file

@ -3,6 +3,7 @@
declare(strict_types=1);
return [
'mobile_notice' => '자동화 편집기는 큰 화면에서 가장 잘 작동합니다. 워크플로를 만들려면 데스크톱에서 열어주세요.',
'title' => '자동화',
'default_name' => '새 자동화',

View file

@ -3,6 +3,7 @@
declare(strict_types=1);
return [
'mobile_notice' => 'De automatiserings-editor werkt het best op een groter scherm. Open het op een desktop om je workflow te bouwen.',
'title' => 'Automatiseringen',
'default_name' => 'Nieuwe automatisering',

View file

@ -3,6 +3,7 @@
declare(strict_types=1);
return [
'mobile_notice' => 'Edytor automatyzacji działa najlepiej na większym ekranie. Otwórz go na komputerze, aby zbudować swój workflow.',
'title' => 'Automatyzacje',
'default_name' => 'Nowa automatyzacja',

View file

@ -3,6 +3,7 @@
declare(strict_types=1);
return [
'mobile_notice' => 'O editor de automações funciona melhor em uma tela maior. Abra no desktop para montar seu fluxo de trabalho.',
'title' => 'Automações',
'default_name' => 'Nova automação',

View file

@ -3,6 +3,7 @@
declare(strict_types=1);
return [
'mobile_notice' => 'Редактор автоматизаций лучше всего работает на большом экране. Откройте его на компьютере, чтобы создать рабочий процесс.',
'title' => 'Автоматизации',
'default_name' => 'Новая автоматизация',

View file

@ -3,6 +3,7 @@
declare(strict_types=1);
return [
'mobile_notice' => 'Otomasyon düzenleyicisi daha büyük bir ekranda en iyi şekilde çalışır. İş akışınızı oluşturmak için masaüstünde açın.',
'title' => 'Otomasyonlar',
'default_name' => 'Yeni otomasyon',

View file

@ -3,6 +3,7 @@
declare(strict_types=1);
return [
'mobile_notice' => '自动化编辑器在更大的屏幕上体验最佳。请在电脑上打开以搭建你的工作流。',
'title' => '自动化',
'default_name' => '新建自动化',

View file

@ -18,13 +18,13 @@ const tabs = computed(() => [
</script>
<template>
<nav class="flex items-center gap-6 border-b-2 border-foreground/10 px-4">
<nav class="flex items-center gap-4 overflow-x-auto border-b-2 border-foreground/10 px-4 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden">
<Link
v-for="tab in tabs"
:key="tab.key"
:href="tab.href"
:dusk="`automation-tab-${tab.key}`"
class="-mb-0.5 border-b-2 py-2.5 text-sm font-medium transition-colors"
class="-mb-0.5 shrink-0 whitespace-nowrap border-b-2 py-2.5 text-sm font-medium transition-colors"
:class="
tab.key === current
? 'border-primary text-foreground'

View file

@ -1,6 +1,6 @@
<script setup lang="ts">
import { Head, router } from '@inertiajs/vue3';
import { IconBolt, IconLifebuoy } from '@tabler/icons-vue';
import { IconBolt, IconDeviceDesktop, IconLifebuoy } from '@tabler/icons-vue';
import { Background } from '@vue-flow/background';
import { Controls } from '@vue-flow/controls';
import {
@ -475,7 +475,7 @@ const defaultEdgeOptions = {
</template>
</AutomationHeader>
<div class="flex flex-1 overflow-hidden">
<div class="hidden flex-1 overflow-hidden lg:flex">
<main
class="automations-canvas-host relative flex-1"
@drop="onDrop"
@ -547,6 +547,16 @@ const defaultEdgeOptions = {
/>
</aside>
</div>
<div
data-testid="automation-mobile-notice"
class="flex flex-1 flex-col items-center justify-center gap-3 p-8 text-center lg:hidden"
>
<div class="inline-flex size-14 items-center justify-center rounded-2xl border-2 border-foreground bg-violet-200 shadow-2xs">
<IconDeviceDesktop class="size-7 text-foreground" stroke-width="2" />
</div>
<p class="max-w-sm text-sm text-foreground/70">{{ $t('automations.mobile_notice') }}</p>
</div>
</div>
</AppLayout>
</template>

View file

@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
use App\Enums\UserWorkspace\Role;
use App\Models\Automation;
use App\Models\User;
use App\Models\Workspace;
test('the automation builder shows a desktop-recommended notice on a phone', function () {
$user = User::factory()->create();
$workspace = Workspace::factory()->create(['user_id' => $user->id]);
$workspace->members()->attach($user->id, ['role' => Role::Member->value]);
$user->update(['current_workspace_id' => $workspace->id]);
$automation = Automation::factory()->create(['workspace_id' => $workspace->id]);
$this->actingAs($user);
$page = visit(route('app.automations.workflow', $automation))->resize(375, 812);
$page->script(<<<'JS'
(async () => {
const sel = '[data-testid="automation-mobile-notice"]';
for (let i = 0; i < 100; i++) {
const el = document.querySelector(sel);
if (el && el.getBoundingClientRect().height > 0) return;
await new Promise((r) => setTimeout(r, 50));
}
})();
JS);
$page->assertVisible('@automation-mobile-notice');
});