refactor(posts): replace status string literals with PostStatus enum

Introduce resources/js/types/post.ts mirroring App\Enums\Post\Status
and App\Enums\PostPlatform\Status. Replace inline string comparisons
across Edit.vue, Show.vue, PostEditorHeader.vue and ScheduleTab.vue.
This commit is contained in:
Paulo Castellano 2026-05-19 14:27:14 -03:00
parent 99d1770c6c
commit 2902506f35
5 changed files with 53 additions and 21 deletions

View file

@ -5,6 +5,7 @@ import { computed } from 'vue';
import PickTimePopover from '@/components/posts/PickTimePopover.vue';
import { Button } from '@/components/ui/button';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
import { PostStatus } from '@/types/post';
interface Props {
post: { status: string };
@ -27,9 +28,12 @@ const emit = defineEmits<{
(e: 'submit', status: string): void;
}>();
const isReadOnly = computed(() => ['publishing', 'published', 'partially_published'].includes(props.post.status));
const isScheduled = computed(() => props.post.status === 'scheduled');
const isPublished = computed(() => ['published', 'partially_published'].includes(props.post.status));
const READONLY_STATUSES: readonly string[] = [PostStatus.Publishing, PostStatus.Published, PostStatus.PartiallyPublished];
const PUBLISHED_STATUSES: readonly string[] = [PostStatus.Published, PostStatus.PartiallyPublished];
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));
</script>
<template>

View file

@ -14,6 +14,7 @@ import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/comp
import { getPlatformLabel, getPlatformLogo } from '@/composables/usePlatformLogo';
import { Platform } from '@/enums/platform';
import type { PinterestBoard } from '@/types';
import { PostPlatformStatus } from '@/types/post';
interface SocialAccount {
id: string;
@ -199,10 +200,10 @@ const getPlatformAvatar = (pp: PostPlatform): string | null =>
>
<IconAlertCircle class="h-2.5 w-2.5" />
</Badge>
<Badge v-else-if="pp.status === 'published'" variant="success" class="absolute -top-1 -right-1 h-4 w-4 p-0">
<Badge v-else-if="pp.status === PostPlatformStatus.Published" variant="success" class="absolute -top-1 -right-1 h-4 w-4 p-0">
<IconCircleCheck class="h-2.5 w-2.5" />
</Badge>
<Badge v-else-if="pp.status === 'failed'" variant="destructive" class="absolute -top-1 -right-1 h-4 w-4 p-0 text-[9px]">!</Badge>
<Badge v-else-if="pp.status === PostPlatformStatus.Failed" variant="destructive" class="absolute -top-1 -right-1 h-4 w-4 p-0 text-[9px]">!</Badge>
</div>
<span
class="line-clamp-2 text-center text-xs leading-tight"
@ -226,7 +227,7 @@ const getPlatformAvatar = (pp: PostPlatform): string | null =>
</div>
</div>
<div v-if="postPlatforms.some(pp => pp.status !== 'pending')">
<div v-if="postPlatforms.some(pp => pp.status !== PostPlatformStatus.Pending)">
<p class="mb-2 text-[11px] font-black uppercase tracking-widest text-foreground/60">
{{ $t('posts.edit.platform_status') }}
</p>
@ -243,12 +244,12 @@ const getPlatformAvatar = (pp: PostPlatform): string | null =>
<span class="text-sm font-bold text-foreground">{{ getPlatformDisplayName(pp) }}</span>
</div>
<div class="flex items-center gap-2">
<Badge v-if="pp.status === 'published'" variant="success">{{ $t('posts.edit.status.published') }}</Badge>
<Badge v-else-if="pp.status === 'publishing'" variant="warning">
<Badge v-if="pp.status === PostPlatformStatus.Published" variant="success">{{ $t('posts.edit.status.published') }}</Badge>
<Badge v-else-if="pp.status === PostPlatformStatus.Publishing" variant="warning">
<IconLoader2 class="size-3 animate-spin" />
{{ $t('posts.edit.status.publishing') }}
</Badge>
<Badge v-else-if="pp.status === 'failed'" variant="destructive">{{ $t('posts.edit.status.failed') }}</Badge>
<Badge v-else-if="pp.status === PostPlatformStatus.Failed" variant="destructive">{{ $t('posts.edit.status.failed') }}</Badge>
<a
v-if="pp.platform_url"
:href="pp.platform_url"

View file

@ -18,6 +18,7 @@ import dayjs from '@/dayjs';
import debounce from '@/debounce';
import { ContentType } from '@/enums/content-type';
import { Platform } from '@/enums/platform';
import { PostStatus } from '@/types/post';
import AppLayout from '@/layouts/AppLayout.vue';
import { destroy as destroyPost, update as updatePost } from '@/routes/app/posts';
import type { PinterestBoard } from '@/types';
@ -98,10 +99,15 @@ const props = defineProps<{
}>();
const post = computed(() => props.post);
// Terminal states the user cannot recover from — delete, navigate, but never edit.
const isReadOnly = computed(() => ['publishing', 'published', 'partially_published', 'failed'].includes(post.value.status));
const isPublishing = computed(() => post.value.status === 'publishing');
const isScheduled = computed(() => post.value.status === 'scheduled');
const READONLY_STATUSES: readonly string[] = [
PostStatus.Publishing,
PostStatus.Published,
PostStatus.PartiallyPublished,
PostStatus.Failed,
];
const isReadOnly = computed(() => READONLY_STATUSES.includes(post.value.status));
const isPublishing = computed(() => post.value.status === PostStatus.Publishing);
const isScheduled = computed(() => post.value.status === PostStatus.Scheduled);
// Locked states — terminal + scheduled. Field edits and auto-save suppressed;
// user must unschedule to re-enter draft and edit.
const isLocked = computed(() => isReadOnly.value || isScheduled.value);
@ -345,7 +351,7 @@ const getLocalSchedule = () => {
return dayjs.utc(post.value.scheduled_at).local().format('YYYY-MM-DDTHH:mm:00');
};
const scheduledDateTime = ref(getLocalSchedule());
const hasPickedTime = ref(post.value.status === 'scheduled' && !! post.value.scheduled_at);
const hasPickedTime = ref(post.value.status === PostStatus.Scheduled && !! post.value.scheduled_at);
const pickTimeLabel = computed(() => {
if (! hasPickedTime.value || ! scheduledDateTime.value) {
@ -466,7 +472,7 @@ onUnmounted(() => {
debouncedSave.cancel();
});
const submit = (status: string = 'scheduled') => {
const submit = (status: string = PostStatus.Scheduled) => {
if (isSubmitting.value || isReadOnly.value) return;
debouncedSave.cancel();
@ -503,7 +509,7 @@ const unschedulePost = () => {
debouncedSave.cancel();
scheduledDateTime.value = '';
hasPickedTime.value = false;
submit('draft');
submit(PostStatus.Draft);
};
// Echo: listen for real-time platform status updates.

View file

@ -18,6 +18,7 @@ import { getPlatformStatusConfig, getPostStatusConfig } from '@/composables/useP
import dayjs from '@/dayjs';
import AppLayout from '@/layouts/AppLayout.vue';
import { index as postsIndex } from '@/routes/app/posts';
import { PostPlatformStatus, PostStatus } from '@/types/post';
interface MediaItem {
id: string;
@ -73,7 +74,7 @@ const props = defineProps<{
const enabledPlatforms = computed(() => props.post.platforms.filter((pp) => pp.enabled));
const isPublishing = computed(() => props.post.status === 'publishing');
const isPublishing = computed(() => props.post.status === PostStatus.Publishing);
const postStatus = computed(() => getPostStatusConfig(props.post.status));
@ -252,11 +253,11 @@ usePostEcho(props.post.id, '.post.platform.status.updated', () => {
<component
:is="getPlatformStatusConfig(pp.status).icon"
class="size-3"
:class="pp.status === 'publishing' ? 'animate-spin' : ''"
:class="pp.status === PostPlatformStatus.Publishing ? 'animate-spin' : ''"
/>
{{ getPlatformStatusConfig(pp.status).label }}
</Badge>
<TooltipProvider v-if="pp.status === 'published' && pp.platform_url" :delay-duration="200">
<TooltipProvider v-if="pp.status === PostPlatformStatus.Published && pp.platform_url" :delay-duration="200">
<Tooltip>
<TooltipTrigger as-child>
<a
@ -276,7 +277,7 @@ usePostEcho(props.post.id, '.post.platform.status.updated', () => {
<!-- Failed: error message -->
<div
v-if="pp.status === 'failed' && pp.error_message"
v-if="pp.status === PostPlatformStatus.Failed && pp.error_message"
class="border-t-2 border-foreground/10 bg-rose-50 px-4 py-3 text-xs font-semibold text-rose-700"
>
{{ pp.error_message }}
@ -284,7 +285,7 @@ usePostEcho(props.post.id, '.post.platform.status.updated', () => {
<!-- Published: metrics -->
<PostPlatformMetrics
v-if="pp.status === 'published'"
v-if="pp.status === PostPlatformStatus.Published"
:post-id="post.id"
:post-platform-id="pp.id"
/>

View file

@ -0,0 +1,20 @@
export const PostStatus = {
Draft: 'draft',
Scheduled: 'scheduled',
Publishing: 'publishing',
Published: 'published',
PartiallyPublished: 'partially_published',
Failed: 'failed',
} as const;
export type PostStatusValue = (typeof PostStatus)[keyof typeof PostStatus];
export const PostPlatformStatus = {
Pending: 'pending',
Publishing: 'publishing',
Published: 'published',
Failed: 'failed',
Retrying: 'retrying',
} as const;
export type PostPlatformStatusValue = (typeof PostPlatformStatus)[keyof typeof PostPlatformStatus];