feat: refine content calendar experience

This commit is contained in:
2026-05-05 23:25:58 -04:00
parent b66c10b681
commit a7535d460d
72 changed files with 3233 additions and 1310 deletions

View File

@@ -4,10 +4,11 @@
import { useRoute, useRouter } from 'vue-router';
import { useSessionStorage } from '@vueuse/core';
import { mdiArrowLeft } from '@mdi/js';
import AppAvatar from '@/components/AppAvatar.vue';
import { useChannelsStore } from '@/features/channels/stores/channelsStore.js';
import { useClientsStore } from '@/features/clients/stores/clientsStore.js';
import ContentApprovalPanel from '@/features/content/components/ContentApprovalPanel.vue';
import ContentCommentComposer from '@/features/content/components/ContentCommentComposer.vue';
import ContentCommentFeed from '@/features/content/components/ContentCommentFeed.vue';
import { useCalendarIntegrationsStore } from '@/features/content/stores/calendarIntegrationsStore.js';
import { useContentItemDetailStore } from '@/features/content/stores/contentItemDetailStore.js';
import { useContentItemsStore } from '@/features/content/stores/contentItemsStore.js';
@@ -39,10 +40,6 @@
placements: [],
});
const commentForm = reactive({
body: '',
});
const assetForm = reactive({
assetType: 'Image',
displayName: '',
@@ -102,6 +99,11 @@
{ key: 'assets', label: 'Assets', count: detailStore.assets.length },
{ key: 'activity', label: 'Activity', count: detailStore.activity.length },
]);
const workspaceMembers = computed(() =>
contentWorkspaceId.value
? workspaceStore.membersByWorkspace[contentWorkspaceId.value] ?? []
: []
);
const selectedDateKey = computed(() => /^\d{4}-\d{2}-\d{2}$/.test(form.dueDate) ? form.dueDate : '');
const contextAnchorDate = computed(() => selectedDateKey.value ? parseDateKey(selectedDateKey.value) : startOfDay(new Date()));
const calendarFetchRange = computed(() => {
@@ -450,13 +452,12 @@
await detailStore.submitDecision(contentItemId.value, approvalId, payload);
}
async function submitComment() {
if (!contentItemId.value || !commentForm.body.trim()) {
async function submitComment(payload) {
if (!contentItemId.value || !payload?.body?.trim()) {
return;
}
await detailStore.addComment(contentItemId.value, { body: commentForm.body.trim() });
commentForm.body = '';
await detailStore.addComment(contentItemId.value, payload);
}
function inferGoogleDriveFileId(value) {
@@ -674,6 +675,7 @@
await Promise.all([
calendarStore.fetchSources(workspaceId),
calendarStore.fetchEvents({ workspaceId, startDate, endDate }),
workspaceStore.fetchMembers(workspaceId),
]);
},
{ immediate: true }
@@ -1127,50 +1129,18 @@
</div>
<template v-if="activeProductionTab === 'comments'">
<div class="panel-stack">
<label class="field field-wide">
<span>New comment</span>
<textarea v-model="commentForm.body"></textarea>
</label>
<button
class="primary-button"
:disabled="detailStore.actions.comment"
@click="submitComment"
>
{{ detailStore.actions.comment ? 'Posting...' : 'Post comment' }}
</button>
</div>
<ContentCommentComposer
:members="workspaceMembers"
:is-posting="detailStore.actions.comment"
@submit-comment="submitComment"
/>
<div class="timeline-list">
<article
v-for="comment in detailStore.comments"
:key="comment.id"
class="timeline-row"
>
<div class="identity-row align-start">
<AppAvatar
:name="comment.authorDisplayName"
:email="comment.authorEmail"
:src="comment.authorPortraitUrl"
size="sm"
/>
<div>
<strong>{{ comment.authorDisplayName }}</strong>
<span>{{ comment.body }}</span>
</div>
</div>
<div class="timeline-actions">
<small>{{ formatDateTime(comment.createdAt) }}</small>
</div>
</article>
<div
v-if="!detailStore.comments.length"
class="empty-note"
>
No comments yet.
</div>
</div>
<ContentCommentFeed
:comments="detailStore.comments"
:members="workspaceMembers"
:is-posting="detailStore.actions.comment"
@submit-comment="submitComment"
/>
</template>
<template v-else-if="activeProductionTab === 'revisions'">

File diff suppressed because it is too large Load Diff