refactor: organize frontend by feature
Some checks failed
Backend CI/CD / build_and_deploy (push) Has been cancelled
Frontend CI/CD / build_and_deploy (push) Has been cancelled

This commit is contained in:
2026-04-25 01:05:50 -04:00
parent b6eb692c27
commit 121757546a
60 changed files with 107 additions and 183 deletions

View File

@@ -0,0 +1,255 @@
import { reactive, ref } from 'vue';
import { defineStore } from 'pinia';
import { useWorkspaceStore } from '@/features/workspaces/stores/workspaceStore.js';
import { useClient } from '@/plugins/api.js';
export const useContentItemDetailStore = defineStore('content-item-detail', () => {
const workspaceStore = useWorkspaceStore();
const client = useClient();
const item = ref(null);
const revisions = ref([]);
const assets = ref([]);
const comments = ref([]);
const approvals = ref([]);
const notifications = ref([]);
const isLoading = ref(false);
const error = ref(null);
const actions = reactive({
revision: false,
asset: false,
assetRevision: false,
comment: false,
approval: false,
decision: false,
status: false,
});
function reset() {
item.value = null;
revisions.value = [];
assets.value = [];
comments.value = [];
approvals.value = [];
notifications.value = [];
error.value = null;
}
async function fetchContentItemDetail(contentItemId) {
isLoading.value = true;
error.value = null;
try {
const [
itemResponse,
revisionsResponse,
assetsResponse,
commentsResponse,
approvalsResponse,
notificationsResponse,
] = await Promise.all([
client.get(`/api/content-items/${contentItemId}`),
client.get(`/api/content-items/${contentItemId}/revisions`),
client.get('/api/assets', { params: { contentItemId } }),
client.get('/api/comments', { params: { contentItemId } }),
client.get('/api/approvals', { params: { contentItemId } }),
client.get('/api/notifications', {
params: {
workspaceId: workspaceStore.activeWorkspaceId,
contentItemId,
},
}),
]);
item.value = itemResponse.data;
revisions.value = revisionsResponse.data ?? [];
assets.value = assetsResponse.data ?? [];
comments.value = commentsResponse.data ?? [];
approvals.value = approvalsResponse.data ?? [];
notifications.value = notificationsResponse.data ?? [];
} catch (fetchError) {
console.error('Failed to load content item detail:', fetchError);
reset();
error.value = 'Failed to load the content item detail.';
} finally {
isLoading.value = false;
}
}
async function createRevision(contentItemId, payload) {
actions.revision = true;
try {
const response = await client.post(`/api/content-items/${contentItemId}/revisions`, payload);
if (response.data) {
revisions.value = [response.data, ...revisions.value];
await fetchContentItemDetail(contentItemId);
}
return response.data;
} finally {
actions.revision = false;
}
}
async function addGoogleDriveAsset(contentItemId, payload) {
actions.asset = true;
try {
const response = await client.post('/api/assets/google-drive', {
...payload,
contentItemId,
workspaceId: workspaceStore.activeWorkspaceId,
});
if (response.data) {
assets.value = [...assets.value, response.data];
await fetchNotifications(contentItemId);
}
return response.data;
} finally {
actions.asset = false;
}
}
async function addAssetRevision(contentItemId, assetId, payload) {
actions.assetRevision = true;
try {
const response = await client.post(`/api/assets/${assetId}/revisions`, payload);
if (response.data) {
await fetchAssets(contentItemId);
await fetchNotifications(contentItemId);
}
return response.data;
} finally {
actions.assetRevision = false;
}
}
async function addComment(contentItemId, payload) {
actions.comment = true;
try {
const response = await client.post('/api/comments', {
...payload,
contentItemId,
workspaceId: workspaceStore.activeWorkspaceId,
});
if (response.data) {
comments.value = [...comments.value, response.data];
await fetchNotifications(contentItemId);
}
return response.data;
} finally {
actions.comment = false;
}
}
async function resolveComment(contentItemId, commentId) {
actions.comment = true;
try {
const response = await client.post(`/api/comments/${commentId}/resolve`);
if (response.data) {
comments.value = comments.value.map(comment => comment.id === commentId ? response.data : comment);
await fetchNotifications(contentItemId);
}
return response.data;
} finally {
actions.comment = false;
}
}
async function createApproval(contentItemId, payload) {
actions.approval = true;
try {
const response = await client.post('/api/approvals', {
...payload,
contentItemId,
workspaceId: workspaceStore.activeWorkspaceId,
});
if (response.data) {
approvals.value = [response.data, ...approvals.value];
await fetchContentItem(contentItemId);
await fetchNotifications(contentItemId);
}
return response.data;
} finally {
actions.approval = false;
}
}
async function submitDecision(contentItemId, approvalId, payload) {
actions.decision = true;
try {
const response = await client.post(`/api/approvals/${approvalId}/decisions`, payload);
if (response.data) {
approvals.value = approvals.value.map(approval => approval.id === approvalId ? response.data : approval);
await fetchContentItem(contentItemId);
await fetchNotifications(contentItemId);
}
return response.data;
} finally {
actions.decision = false;
}
}
async function updateStatus(contentItemId, status) {
actions.status = true;
try {
const response = await client.post(`/api/content-items/${contentItemId}/status`, { status });
item.value = response.data;
await fetchNotifications(contentItemId);
return response.data;
} finally {
actions.status = false;
}
}
async function fetchContentItem(contentItemId) {
const response = await client.get(`/api/content-items/${contentItemId}`);
item.value = response.data;
return response.data;
}
async function fetchAssets(contentItemId) {
const response = await client.get('/api/assets', { params: { contentItemId } });
assets.value = response.data ?? [];
return assets.value;
}
async function fetchNotifications(contentItemId) {
const response = await client.get('/api/notifications', {
params: {
workspaceId: workspaceStore.activeWorkspaceId,
contentItemId,
},
});
notifications.value = response.data ?? [];
return notifications.value;
}
return {
item,
revisions,
assets,
comments,
approvals,
notifications,
isLoading,
error,
actions,
reset,
fetchContentItemDetail,
createRevision,
addGoogleDriveAsset,
addAssetRevision,
addComment,
resolveComment,
createApproval,
submitDecision,
updateStatus,
};
});

View File

@@ -0,0 +1,112 @@
import { computed, ref, watch } from 'vue';
import { defineStore } from 'pinia';
import { useAuthStore } from '@/features/auth/stores/authStore.js';
import { useWorkspaceStore } from '@/features/workspaces/stores/workspaceStore.js';
import { useClient } from '@/plugins/api.js';
export const useContentItemsStore = defineStore('content-items', () => {
const authStore = useAuthStore();
const workspaceStore = useWorkspaceStore();
const client = useClient();
const items = ref([]);
const isLoading = ref(false);
const isCreating = ref(false);
const error = ref(null);
const activeCount = computed(() =>
items.value.filter(item => item.status !== 'Approved' && item.status !== 'Published' && item.status !== 'Archived')
.length
);
async function fetchContentItems(filters = {}) {
if (!authStore.isAuthenticated || !workspaceStore.activeWorkspaceId) {
items.value = [];
error.value = null;
return;
}
isLoading.value = true;
error.value = null;
try {
const response = await client.get('/api/content-items', {
params: {
workspaceId: workspaceStore.activeWorkspaceId,
clientId: filters.clientId,
projectId: filters.projectId,
},
});
items.value = response.data ?? [];
} catch (fetchError) {
console.error('Failed to fetch content items:', fetchError);
items.value = [];
error.value = 'Failed to load content items.';
} finally {
isLoading.value = false;
}
}
async function createContentItem(payload) {
if (!authStore.isAuthenticated || !workspaceStore.activeWorkspaceId) {
throw new Error('You must be authenticated to create a content item.');
}
if (isCreating.value) {
throw new Error('A content item creation request is already in progress.');
}
isCreating.value = true;
error.value = null;
try {
const response = await client.post('/api/content-items', {
...payload,
workspaceId: workspaceStore.activeWorkspaceId,
});
if (response.data) {
items.value = [response.data, ...items.value];
}
return response.data;
} catch (createError) {
console.error('Failed to create content item:', createError);
error.value = 'Failed to create content item.';
throw createError;
} finally {
isCreating.value = false;
}
}
async function fetchContentItem(id) {
const response = await client.get(`/api/content-items/${id}`);
return response.data;
}
watch(
() => [authStore.isAuthenticated, workspaceStore.activeWorkspaceId],
async ([isAuthenticated, workspaceId]) => {
if (!isAuthenticated || !workspaceId) {
items.value = [];
error.value = null;
return;
}
await fetchContentItems();
},
{ immediate: true }
);
return {
items,
isLoading,
isCreating,
error,
activeCount,
fetchContentItems,
fetchContentItem,
createContentItem,
};
});