diff --git a/app/Actions/Automation/Automation/GetAutomationEditorData.php b/app/Actions/Automation/Automation/GetAutomationEditorData.php index e633b77b..3db30dad 100644 --- a/app/Actions/Automation/Automation/GetAutomationEditorData.php +++ b/app/Actions/Automation/Automation/GetAutomationEditorData.php @@ -4,10 +4,10 @@ namespace App\Actions\Automation\Automation; +use App\Actions\SocialAccount\ListPinterestBoards; use App\Enums\SocialAccount\Platform; use App\Models\Automation; use App\Models\SocialAccount; -use App\Services\Social\PinterestPublisher; use App\Services\Social\TikTokCreatorInfo; use Illuminate\Database\Eloquent\Collection; use Illuminate\Support\Collection as SupportCollection; @@ -15,14 +15,13 @@ class GetAutomationEditorData { public function __construct( - private PinterestPublisher $pinterestPublisher, private TikTokCreatorInfo $tikTokCreatorInfo, ) {} /** * @return array{ * socialAccounts: Collection, - * pinterestBoards: SupportCollection>, + * pinterestBoards: SupportCollection, truncated: bool}>, * tiktokCreatorInfos: SupportCollection, * } */ @@ -34,12 +33,8 @@ public function __invoke(Automation $automation): array ->where('platform', Platform::Pinterest) ->mapWithKeys(fn ($account) => [ $account->id => rescue( - fn () => data_get( - $this->pinterestPublisher->getBoards($account), - 'boards', - [], - ), - [], + fn () => ListPinterestBoards::execute($account), + ['boards' => [], 'truncated' => false], report: false, ), ]); diff --git a/app/Http/Controllers/App/PostController.php b/app/Http/Controllers/App/PostController.php index 544d6dcc..c1ebc395 100644 --- a/app/Http/Controllers/App/PostController.php +++ b/app/Http/Controllers/App/PostController.php @@ -9,6 +9,7 @@ use App\Actions\Post\DuplicatePost; use App\Actions\Post\SyncPostPlatforms; use App\Actions\Post\UpdatePost; +use App\Actions\SocialAccount\ListPinterestBoards; use App\Ai\Templates\AiContentTemplate; use App\Ai\Templates\AiTemplateRegistry; use App\Enums\Post\Action as PostAction; @@ -23,7 +24,6 @@ use App\Models\Post; use App\Models\PostPlatform; use App\Services\Post\PostMetricsFetcher; -use App\Services\Social\PinterestPublisher; use App\Services\Social\TikTokCreatorInfo; use App\Support\PostStatusRules; use Carbon\Carbon; @@ -260,12 +260,8 @@ public function edit(Request $request, Post $post): Response|RedirectResponse ->where('platform', Platform::Pinterest) ->mapWithKeys(fn ($account) => [ $account->id => rescue( - fn () => data_get( - app(PinterestPublisher::class)->getBoards($account), - 'boards', - [], - ), - [], + fn () => ListPinterestBoards::execute($account), + ['boards' => [], 'truncated' => false], report: false, ), ]); diff --git a/lang/ar/posts.php b/lang/ar/posts.php index 8d5901e5..3f3b800f 100644 --- a/lang/ar/posts.php +++ b/lang/ar/posts.php @@ -154,6 +154,7 @@ 'board' => 'اللوحة', 'select_board' => 'اختر لوحة', 'no_boards' => 'لم يتم العثور على لوحات Pinterest. أنشئ واحدة في حسابك على Pinterest أولًا.', + 'boards_truncated' => 'تعذر تحميل بعض اللوحات. إذا كانت لوحتك مفقودة، افتح Pinterest وحاول مرة أخرى.', 'search_board' => 'البحث في اللوحات...', 'no_board_found' => 'لا توجد لوحة مطابقة لبحثك.', 'board_required' => 'اختر لوحة Pinterest لنشر هذا المنشور.', diff --git a/lang/de/posts.php b/lang/de/posts.php index 0aeef2b4..de87cbed 100644 --- a/lang/de/posts.php +++ b/lang/de/posts.php @@ -156,6 +156,7 @@ 'board' => 'Pinnwand', 'select_board' => 'Pinnwand auswählen', 'no_boards' => 'Keine Pinterest-Pinnwände gefunden. Erstelle zuerst eine in deinem Pinterest-Konto.', + 'boards_truncated' => 'Einige Pinnwände konnten nicht geladen werden. Fehlt deine, öffne Pinterest und versuche es erneut.', 'search_board' => 'Pinnwände suchen...', 'no_board_found' => 'Keine Pinnwand passt zu deiner Suche.', 'board_required' => 'Wähle eine Pinterest-Pinnwand, um diesen Beitrag zu veröffentlichen.', diff --git a/lang/el/posts.php b/lang/el/posts.php index 470ff022..03e36c60 100644 --- a/lang/el/posts.php +++ b/lang/el/posts.php @@ -154,6 +154,7 @@ 'board' => 'Πίνακας', 'select_board' => 'Επιλέξτε έναν πίνακα', 'no_boards' => 'Δεν βρέθηκαν πίνακες Pinterest. Δημιουργήστε πρώτα έναν στον λογαριασμό σας Pinterest.', + 'boards_truncated' => 'Ορισμένοι πίνακες δεν φορτώθηκαν. Αν λείπει ο δικός σας, ανοίξτε το Pinterest και δοκιμάστε ξανά.', 'search_board' => 'Αναζήτηση πινάκων...', 'no_board_found' => 'Κανένας πίνακας δεν ταιριάζει με την αναζήτησή σας.', 'board_required' => 'Επιλέξτε έναν πίνακα Pinterest για να δημοσιεύσετε αυτή τη δημοσίευση.', diff --git a/lang/en/posts.php b/lang/en/posts.php index f15f8f15..8878a707 100644 --- a/lang/en/posts.php +++ b/lang/en/posts.php @@ -154,6 +154,7 @@ 'board' => 'Board', 'select_board' => 'Select a board', 'no_boards' => 'No Pinterest boards found. Create one in your Pinterest account first.', + 'boards_truncated' => 'Some boards could not be loaded. If yours is missing, open Pinterest and try again.', 'search_board' => 'Search boards...', 'no_board_found' => 'No board matches your search.', 'board_required' => 'Select a Pinterest board to publish this post.', diff --git a/lang/es/posts.php b/lang/es/posts.php index 61b04344..9e00e325 100644 --- a/lang/es/posts.php +++ b/lang/es/posts.php @@ -154,6 +154,7 @@ 'board' => 'Tablero', 'select_board' => 'Selecciona un tablero', 'no_boards' => 'No se encontraron tableros de Pinterest. Crea uno en tu cuenta de Pinterest primero.', + 'boards_truncated' => 'No se pudieron cargar algunos tableros. Si falta el tuyo, abre Pinterest e inténtalo de nuevo.', 'search_board' => 'Buscar tableros...', 'no_board_found' => 'Ningún tablero coincide con tu búsqueda.', 'board_required' => 'Selecciona un tablero de Pinterest para publicar este post.', diff --git a/lang/fr/posts.php b/lang/fr/posts.php index 7cbd880e..79630288 100644 --- a/lang/fr/posts.php +++ b/lang/fr/posts.php @@ -154,6 +154,7 @@ 'board' => 'Tableau', 'select_board' => 'Sélectionner un tableau', 'no_boards' => 'Aucun tableau Pinterest trouvé. Créez-en un dans votre compte Pinterest d\'abord.', + 'boards_truncated' => 'Certains tableaux n’ont pas pu être chargés. S’il manque le vôtre, ouvrez Pinterest et réessayez.', 'search_board' => 'Rechercher des tableaux...', 'no_board_found' => 'Aucun tableau ne correspond à votre recherche.', 'board_required' => 'Sélectionnez un tableau Pinterest pour publier cette publication.', diff --git a/lang/it/posts.php b/lang/it/posts.php index ea9ca8de..3f0d43e0 100644 --- a/lang/it/posts.php +++ b/lang/it/posts.php @@ -154,6 +154,7 @@ 'board' => 'Bacheca', 'select_board' => 'Seleziona una bacheca', 'no_boards' => 'Nessuna bacheca Pinterest trovata. Creane una nel tuo account Pinterest prima.', + 'boards_truncated' => 'Alcune bacheche non sono state caricate. Se manca la tua, apri Pinterest e riprova.', 'search_board' => 'Cerca bacheche...', 'no_board_found' => 'Nessuna bacheca corrisponde alla ricerca.', 'board_required' => 'Seleziona una bacheca Pinterest per pubblicare questo post.', diff --git a/lang/ja/posts.php b/lang/ja/posts.php index 4c205ec4..53cc227a 100644 --- a/lang/ja/posts.php +++ b/lang/ja/posts.php @@ -154,6 +154,7 @@ 'board' => 'ボード', 'select_board' => 'ボードを選択', 'no_boards' => 'Pinterest ボードが見つかりません。先に Pinterest アカウントで作成してください。', + 'boards_truncated' => '一部のボードを読み込めませんでした。見つからない場合は Pinterest を開いて再試行してください。', 'search_board' => 'ボードを検索...', 'no_board_found' => '検索に一致するボードがありません。', 'board_required' => 'この投稿を公開する Pinterest ボードを選択してください。', diff --git a/lang/ko/posts.php b/lang/ko/posts.php index 2e58a2e7..8bb2ae08 100644 --- a/lang/ko/posts.php +++ b/lang/ko/posts.php @@ -154,6 +154,7 @@ 'board' => '보드', 'select_board' => '보드 선택', 'no_boards' => 'Pinterest 보드를 찾을 수 없습니다. 먼저 Pinterest 계정에서 보드를 만드세요.', + 'boards_truncated' => '일부 보드를 불러오지 못했습니다. 내 보드가 없다면 Pinterest를 연 뒤 다시 시도하세요.', 'search_board' => '보드 검색...', 'no_board_found' => '검색과 일치하는 보드가 없습니다.', 'board_required' => '이 게시물을 게시할 Pinterest 보드를 선택하세요.', diff --git a/lang/nl/posts.php b/lang/nl/posts.php index f13035b5..eaa2335d 100644 --- a/lang/nl/posts.php +++ b/lang/nl/posts.php @@ -154,6 +154,7 @@ 'board' => 'Bord', 'select_board' => 'Selecteer een bord', 'no_boards' => 'Geen Pinterest-borden gevonden. Maak er eerst een aan in je Pinterest-account.', + 'boards_truncated' => 'Sommige borden konden niet worden geladen. Ontbreekt die van jou, open Pinterest en probeer opnieuw.', 'search_board' => 'Borden zoeken...', 'no_board_found' => 'Geen bord komt overeen met je zoekopdracht.', 'board_required' => 'Selecteer een Pinterest-bord om deze post te publiceren.', diff --git a/lang/pl/posts.php b/lang/pl/posts.php index 3af64e34..74271f4a 100644 --- a/lang/pl/posts.php +++ b/lang/pl/posts.php @@ -154,6 +154,7 @@ 'board' => 'Tablica', 'select_board' => 'Wybierz tablicę', 'no_boards' => 'Nie znaleziono tablic Pinterest. Najpierw utwórz jedną na swoim koncie Pinterest.', + 'boards_truncated' => 'Nie udało się wczytać niektórych tablic. Jeśli brakuje twojej, otwórz Pinterest i spróbuj ponownie.', 'search_board' => 'Szukaj tablic...', 'no_board_found' => 'Brak tablic pasujących do wyszukiwania.', 'board_required' => 'Wybierz tablicę Pinterest, aby opublikować ten post.', diff --git a/lang/pt-BR/posts.php b/lang/pt-BR/posts.php index d7d8856a..8d40e961 100644 --- a/lang/pt-BR/posts.php +++ b/lang/pt-BR/posts.php @@ -154,6 +154,7 @@ 'board' => 'Quadro', 'select_board' => 'Selecione um quadro', 'no_boards' => 'Nenhum quadro do Pinterest encontrado. Crie um na sua conta do Pinterest primeiro.', + 'boards_truncated' => 'Alguns boards não puderam ser carregados. Se o seu não aparecer, abra o Pinterest e tente de novo.', 'search_board' => 'Pesquisar quadros...', 'no_board_found' => 'Nenhum quadro encontrado.', 'board_required' => 'Selecione um quadro do Pinterest para publicar este post.', diff --git a/lang/ru/posts.php b/lang/ru/posts.php index c180e074..a2efc84e 100644 --- a/lang/ru/posts.php +++ b/lang/ru/posts.php @@ -154,6 +154,7 @@ 'board' => 'Доска', 'select_board' => 'Выберите доску', 'no_boards' => 'Доски Pinterest не найдены. Сначала создайте доску в своём аккаунте Pinterest.', + 'boards_truncated' => 'Некоторые доски не удалось загрузить. Если вашей нет, откройте Pinterest и попробуйте снова.', 'search_board' => 'Поиск досок...', 'no_board_found' => 'Нет досок по вашему запросу.', 'board_required' => 'Выберите доску Pinterest, чтобы опубликовать этот пост.', diff --git a/lang/tr/posts.php b/lang/tr/posts.php index 2babf59f..a7024788 100644 --- a/lang/tr/posts.php +++ b/lang/tr/posts.php @@ -156,6 +156,7 @@ 'board' => 'Pano', 'select_board' => 'Bir pano seçin', 'no_boards' => 'Pinterest panosu bulunamadı. Önce Pinterest hesabınızda bir tane oluşturun.', + 'boards_truncated' => 'Bazı panolar yüklenemedi. Sizinki eksikse Pinterest’i açıp tekrar deneyin.', 'search_board' => 'Pano ara...', 'no_board_found' => 'Aramanızla eşleşen pano yok.', 'board_required' => 'Bu gönderiyi yayınlamak için bir Pinterest panosu seçin.', diff --git a/lang/zh/posts.php b/lang/zh/posts.php index 0fec3de5..9cf3a6a7 100644 --- a/lang/zh/posts.php +++ b/lang/zh/posts.php @@ -154,6 +154,7 @@ 'board' => '图板', 'select_board' => '选择一个图板', 'no_boards' => '未找到 Pinterest 图板。请先在你的 Pinterest 账号中创建一个。', + 'boards_truncated' => '部分图板未能加载。如果没有看到你的图板,请打开 Pinterest 后重试。', 'search_board' => '搜索图板…', 'no_board_found' => '没有与搜索匹配的图板。', 'board_required' => '请选择一个 Pinterest 图板以发布此帖子。', diff --git a/resources/js/components/ChannelConfigurator.vue b/resources/js/components/ChannelConfigurator.vue index 1feb31e2..ce3d00e8 100644 --- a/resources/js/components/ChannelConfigurator.vue +++ b/resources/js/components/ChannelConfigurator.vue @@ -153,6 +153,7 @@ const selectedChannels = computed(() => props.channels.filter((channel) => isSel :content-type="channel.contentType" :media="media" :boards="channel.boards ?? []" + :boards-truncated="channel.boardsTruncated ?? false" :meta="channel.meta" :disabled="disabled" :preview-only="previewOnly" diff --git a/resources/js/components/automations/config/GenerateNodeConfig.vue b/resources/js/components/automations/config/GenerateNodeConfig.vue index 1d73d91b..17a35e87 100644 --- a/resources/js/components/automations/config/GenerateNodeConfig.vue +++ b/resources/js/components/automations/config/GenerateNodeConfig.vue @@ -13,7 +13,7 @@ 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'; +import type { PinterestBoard, PinterestBoardsPayload } from '@/types'; import type { Channel } from '@/types/channel'; import { ContentType } from '@/types/content-type'; import type { MediaItem } from '@/types/media'; @@ -91,8 +91,8 @@ const platformConfigs = computed>(() => { return raw ?? {}; }); -const pinterestBoards = computed>(() => { - const raw = page.props.pinterestBoards as Record | undefined; +const pinterestBoards = computed>(() => { + const raw = page.props.pinterestBoards as Record | undefined; return raw ?? {}; }); @@ -209,7 +209,10 @@ const getCreatorInfo = (account: SocialAccount): TikTokCreatorInfo | null => tiktokCreatorInfos.value[account.id] ?? null; const getBoards = (account: SocialAccount): PinterestBoard[] => - pinterestBoards.value[account.id] ?? []; + pinterestBoards.value[account.id]?.boards ?? []; + +const boardsTruncated = (account: SocialAccount): boolean => + pinterestBoards.value[account.id]?.truncated ?? false; // Image-capability is derived from the SAME media rules the post editor uses // (per content type), never a hardcoded list — facebook_post, tiktok_photo, @@ -279,6 +282,7 @@ const channels = computed(() => publishConfig: getPublishConfig(account), creatorInfo: getCreatorInfo(account), boards: getBoards(account), + boardsTruncated: boardsTruncated(account), }; }), ); diff --git a/resources/js/components/posts/editor/PinterestSettings.vue b/resources/js/components/posts/editor/PinterestSettings.vue index ea6e6479..09e65e00 100644 --- a/resources/js/components/posts/editor/PinterestSettings.vue +++ b/resources/js/components/posts/editor/PinterestSettings.vue @@ -39,6 +39,7 @@ interface Props { contentType: string; media: MediaItem[]; boards: PinterestBoard[]; + boardsTruncated?: boolean; meta: Record; disabled?: boolean; previewOnly?: boolean; @@ -47,6 +48,7 @@ interface Props { const props = withDefaults(defineProps(), { disabled: false, previewOnly: false, + boardsTruncated: false, }); const emit = defineEmits<{ @@ -187,6 +189,13 @@ const boardError = computed(() => { +

+ + {{ $t('posts.form.pinterest.boards_truncated') }} +

diff --git a/resources/js/components/posts/editor/PostEditorTabs.vue b/resources/js/components/posts/editor/PostEditorTabs.vue index c5ef7555..45ad8a6a 100644 --- a/resources/js/components/posts/editor/PostEditorTabs.vue +++ b/resources/js/components/posts/editor/PostEditorTabs.vue @@ -5,7 +5,7 @@ import CommentsTab from '@/components/posts/editor/CommentsTab.vue'; import PreviewTab from '@/components/posts/editor/PreviewTab.vue'; import ScheduleTab from '@/components/posts/editor/ScheduleTab.vue'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; -import type { PinterestBoard } from '@/types'; +import type { PinterestBoardsPayload } from '@/types'; import type { MediaItem } from '@/types/media'; interface SocialAccount { @@ -62,7 +62,7 @@ const props = defineProps<{ labels: { id: string; name: string; color: string }[]; selectedLabelIds: string[]; tiktokCreatorInfos?: Record | null; - pinterestBoards?: Record | null; + pinterestBoards?: Record | null; isReadOnly: boolean; authUserId: string; initialHighlightCommentId: string | null; diff --git a/resources/js/components/posts/editor/ScheduleTab.vue b/resources/js/components/posts/editor/ScheduleTab.vue index 2bb93c23..f05e90f5 100644 --- a/resources/js/components/posts/editor/ScheduleTab.vue +++ b/resources/js/components/posts/editor/ScheduleTab.vue @@ -8,7 +8,7 @@ import { Badge } from '@/components/ui/badge'; import { usePageErrors } from '@/composables/usePageErrors'; import { getPlatformLogo } from '@/composables/usePlatformLogo'; import { isVideo } from '@/lib/mediaType'; -import type { PinterestBoard } from '@/types'; +import type { PinterestBoard, PinterestBoardsPayload } from '@/types'; import type { Channel } from '@/types/channel'; import type { MediaItem } from '@/types/media'; import { PostPlatformStatus } from '@/types/post'; @@ -77,7 +77,7 @@ const props = defineProps<{ platformContentTypes: Record; platformIssues?: Record; tiktokCreatorInfos?: Record | null; - pinterestBoards?: Record | null; + pinterestBoards?: Record | null; media?: MediaItem[]; }>(); @@ -94,8 +94,14 @@ const getPublishConfig = (pp: PostPlatform): Record | null => const getCreatorInfo = (pp: PostPlatform): TikTokCreatorInfo | null => pp.social_account_id ? props.tiktokCreatorInfos?.[pp.social_account_id] ?? null : null; -const getBoards = (pp: PostPlatform): PinterestBoard[] => - pp.social_account_id ? props.pinterestBoards?.[pp.social_account_id] ?? [] : []; +const boardsPayload = (pp: PostPlatform): PinterestBoardsPayload => + pp.social_account_id + ? props.pinterestBoards?.[pp.social_account_id] ?? { boards: [], truncated: false } + : { boards: [], truncated: false }; + +const getBoards = (pp: PostPlatform): PinterestBoard[] => boardsPayload(pp).boards; + +const boardsTruncated = (pp: PostPlatform): boolean => boardsPayload(pp).truncated; const videoDurationSec = computed(() => { const video = props.media?.find((m) => isVideo(m)); @@ -143,6 +149,7 @@ const channels = computed(() => publishConfig: getPublishConfig(pp), creatorInfo: getCreatorInfo(pp), boards: getBoards(pp), + boardsTruncated: boardsTruncated(pp), })), ); diff --git a/resources/js/pages/posts/Edit.vue b/resources/js/pages/posts/Edit.vue index 2b6e2f3c..667a43f0 100644 --- a/resources/js/pages/posts/Edit.vue +++ b/resources/js/pages/posts/Edit.vue @@ -25,7 +25,7 @@ import dayjs from '@/dayjs'; import debounce from '@/debounce'; import AppLayout from '@/layouts/AppLayout.vue'; import { destroy as destroyPost, update as updatePost } from '@/routes/app/posts'; -import type { PinterestBoard } from '@/types'; +import type { PinterestBoardsPayload } from '@/types'; import type { MediaItem } from '@/types/media'; import { PostStatus } from '@/types/post'; @@ -86,7 +86,7 @@ const props = defineProps<{ post: Post; socialAccounts: SocialAccount[]; platformConfigs: Record; - pinterestBoards: Record; + pinterestBoards: Record; tiktokCreatorInfos?: Record | null; labels: { id: string; name: string; color: string }[]; signatures: { id: string; name: string; content: string }[]; diff --git a/resources/js/types/channel.ts b/resources/js/types/channel.ts index d2c58ebf..81565fff 100644 --- a/resources/js/types/channel.ts +++ b/resources/js/types/channel.ts @@ -40,4 +40,5 @@ export interface Channel { publishConfig?: Record | null; creatorInfo?: ChannelTikTokCreatorInfo | null; boards?: PinterestBoard[]; + boardsTruncated?: boolean; } diff --git a/resources/js/types/index.d.ts b/resources/js/types/index.d.ts index 97030452..897ce048 100644 --- a/resources/js/types/index.d.ts +++ b/resources/js/types/index.d.ts @@ -116,6 +116,12 @@ export interface PinterestBoard { name: string; } +/** Per-account payload from ListPinterestBoards (Inertia + API/MCP). */ +export interface PinterestBoardsPayload { + boards: PinterestBoard[]; + truncated: boolean; +} + export interface ContentLanguageOption { value: string; label: string; diff --git a/tests/Feature/Automation/Automation/ReadActionsTest.php b/tests/Feature/Automation/Automation/ReadActionsTest.php index 24b6e4c2..d4d77856 100644 --- a/tests/Feature/Automation/Automation/ReadActionsTest.php +++ b/tests/Feature/Automation/Automation/ReadActionsTest.php @@ -136,8 +136,8 @@ it('maps pinterest boards for pinterest accounts', function () { $this->mock(PinterestPublisher::class, fn ($mock) => $mock->shouldReceive('getBoards')->andReturn([ - 'boards' => [['id' => 'b1']], - 'truncated' => false, + 'boards' => [['id' => 'b1', 'name' => 'Ideas']], + 'truncated' => true, ])); $this->mock(TikTokCreatorInfo::class); @@ -147,5 +147,8 @@ $result = app(GetAutomationEditorData::class)($automation); - expect($result['pinterestBoards']->get($pinterest->id))->toBe([['id' => 'b1']]); + expect($result['pinterestBoards']->get($pinterest->id))->toBe([ + 'boards' => [['id' => 'b1', 'name' => 'Ideas']], + 'truncated' => true, + ]); });