diff --git a/lang/en/posts.php b/lang/en/posts.php index 5653d9c1..785c5736 100644 --- a/lang/en/posts.php +++ b/lang/en/posts.php @@ -240,6 +240,7 @@ 'no_labels' => 'No labels created yet', 'schedule' => 'Schedule', 'pick_time' => 'Pick time', + 'pick_time_past' => 'Pick a future date and time.', 'post_now' => 'Post now', 'time' => 'Time', 'cancel' => 'Cancel', diff --git a/lang/es/posts.php b/lang/es/posts.php index 91a6d2c6..253c11e2 100644 --- a/lang/es/posts.php +++ b/lang/es/posts.php @@ -262,6 +262,7 @@ 'organize' => 'Organizar', 'no_labels' => 'Todavía no hay etiquetas creadas', 'pick_time' => 'Elegir hora', + 'pick_time_past' => 'Elige una fecha y hora en el futuro.', 'post_now' => 'Publicar ahora', 'time' => 'Hora', 'cancel' => 'Cancelar', diff --git a/lang/pt-BR/posts.php b/lang/pt-BR/posts.php index daaf75c7..f62548b4 100644 --- a/lang/pt-BR/posts.php +++ b/lang/pt-BR/posts.php @@ -262,6 +262,7 @@ 'organize' => 'Organizar', 'no_labels' => 'Nenhuma etiqueta criada ainda', 'pick_time' => 'Escolher horário', + 'pick_time_past' => 'Escolha uma data e hora no futuro.', 'post_now' => 'Publicar agora', 'time' => 'Horário', 'cancel' => 'Cancelar', diff --git a/resources/js/components/posts/PickTimePopover.vue b/resources/js/components/posts/PickTimePopover.vue index f367238b..a4bf3357 100644 --- a/resources/js/components/posts/PickTimePopover.vue +++ b/resources/js/components/posts/PickTimePopover.vue @@ -65,11 +65,14 @@ const buildDateTime = (): string => { return `${dateStr}T${selectedHour.value}:${selectedMinute.value}:00`; }; +const isPastDateTime = computed(() => dayjs(buildDateTime()).isBefore(dayjs())); + const cancel = () => { open.value = false; }; const confirm = () => { + if (isPastDateTime.value) return; const value = buildDateTime(); emit('update:modelValue', value); emit('confirm', value); @@ -112,6 +115,9 @@ const remove = () => { {{ timezoneAbbr }} +

+ {{ $t('posts.edit.pick_time_past') }} +

@@ -126,7 +132,7 @@ const remove = () => { {{ $t('posts.edit.unschedule') }} - +
diff --git a/resources/js/components/posts/editor/PinterestSettings.vue b/resources/js/components/posts/editor/PinterestSettings.vue index 4e294e03..3ec207bf 100644 --- a/resources/js/components/posts/editor/PinterestSettings.vue +++ b/resources/js/components/posts/editor/PinterestSettings.vue @@ -1,5 +1,4 @@ diff --git a/resources/js/components/posts/editor/PostEditorHeader.vue b/resources/js/components/posts/editor/PostEditorHeader.vue index 82c3b3bb..950c37fe 100644 --- a/resources/js/components/posts/editor/PostEditorHeader.vue +++ b/resources/js/components/posts/editor/PostEditorHeader.vue @@ -2,9 +2,11 @@ import { IconCalendar, IconCircleCheck, IconLoader2, IconTrash } from '@tabler/icons-vue'; import { computed } from 'vue'; +import InputError from '@/components/InputError.vue'; import PickTimePopover from '@/components/posts/PickTimePopover.vue'; import { Button } from '@/components/ui/button'; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'; +import { usePageErrors } from '@/composables/usePageErrors'; import { PostStatus } from '@/types/post'; interface Props { @@ -34,6 +36,9 @@ const PUBLISHED_STATUSES: readonly string[] = [PostStatus.Published, PostStatus. const isReadOnly = computed(() => READONLY_STATUSES.includes(props.post.status)); const isScheduled = computed(() => props.post.status === PostStatus.Scheduled); const isPublished = computed(() => PUBLISHED_STATUSES.includes(props.post.status)); + +const errors = usePageErrors(); +const scheduledAtError = computed(() => errors.value.scheduled_at); diff --git a/resources/js/components/posts/editor/ScheduleTab.vue b/resources/js/components/posts/editor/ScheduleTab.vue index b106c719..15a8a597 100644 --- a/resources/js/components/posts/editor/ScheduleTab.vue +++ b/resources/js/components/posts/editor/ScheduleTab.vue @@ -11,6 +11,7 @@ import TikTokSettings from '@/components/posts/editor/TikTokSettings.vue'; import { Avatar } from '@/components/ui/avatar'; import { Badge } from '@/components/ui/badge'; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'; +import { usePageErrors } from '@/composables/usePageErrors'; import { getPlatformLabel, getPlatformLogo } from '@/composables/usePlatformLogo'; import { Platform } from '@/enums/platform'; import type { PinterestBoard } from '@/types'; @@ -152,6 +153,24 @@ const getPlatformDisplayName = (pp: PostPlatform): string => const getPlatformAvatar = (pp: PostPlatform): string | null => pp.social_account?.avatar_url ?? pp.platform_avatar ?? null; +// Map pp.id → submit-array index, matching what Edit.vue sends as `platforms[i]`. +// Backend validation errors are keyed `platforms.{i}.content_type`, so we use this +// to surface the right error under each platform's variant picker. +const errors = usePageErrors(); +const submitIndexByPpId = computed>(() => { + const map: Record = {}; + props.postPlatforms + .filter((pp) => props.selectedPlatformIds.includes(pp.id)) + .forEach((pp, index) => { map[pp.id] = index; }); + return map; +}); + +const contentTypeErrorFor = (pp: PostPlatform): string | undefined => { + const index = submitIndexByPpId.value[pp.id]; + if (index === undefined) return undefined; + return errors.value[`platforms.${index}.content_type`]; +}; +