Hotfix Rule: Force Fresh Data After CRUD Mutation

Task-h not implements
This commit is contained in:
phaichayon
2026-06-16 11:59:41 +07:00
parent 357414c247
commit 56683ee7b9
16 changed files with 763 additions and 186 deletions

View File

@@ -32,43 +32,104 @@ import type {
QuotationTopicMutationPayload
} from './types';
function invalidateQuotationDocumentQueries(quotationId: string) {
getQueryClient().invalidateQueries({ queryKey: quotationDocumentKeys.data(quotationId) });
getQueryClient().invalidateQueries({ queryKey: quotationDocumentKeys.preview(quotationId) });
async function invalidateQuotationLists() {
await getQueryClient().invalidateQueries({ queryKey: quotationKeys.lists() });
}
async function invalidateQuotationDetail(quotationId: string) {
await getQueryClient().invalidateQueries({ queryKey: quotationKeys.detail(quotationId) });
}
async function invalidateQuotationDocumentQueries(quotationId: string) {
await Promise.all([
getQueryClient().invalidateQueries({ queryKey: quotationDocumentKeys.data(quotationId) }),
getQueryClient().invalidateQueries({ queryKey: quotationDocumentKeys.preview(quotationId) })
]);
}
async function invalidateQuotationHeaderQueries(quotationId: string) {
await Promise.all([
invalidateQuotationLists(),
invalidateQuotationDetail(quotationId),
invalidateQuotationDocumentQueries(quotationId)
]);
}
async function invalidateQuotationItems(quotationId: string) {
await getQueryClient().invalidateQueries({ queryKey: quotationKeys.items(quotationId) });
}
async function invalidateQuotationCustomers(quotationId: string) {
await getQueryClient().invalidateQueries({ queryKey: quotationKeys.customers(quotationId) });
}
async function invalidateQuotationTopics(quotationId: string) {
await getQueryClient().invalidateQueries({ queryKey: quotationKeys.topics(quotationId) });
}
async function invalidateQuotationFollowups(quotationId: string) {
await getQueryClient().invalidateQueries({ queryKey: quotationKeys.followups(quotationId) });
}
async function invalidateQuotationAttachments(quotationId: string) {
await getQueryClient().invalidateQueries({ queryKey: quotationKeys.attachments(quotationId) });
}
async function invalidateQuotationRevisions(quotationId: string) {
await getQueryClient().invalidateQueries({ queryKey: quotationKeys.revisions(quotationId) });
}
async function removeQuotationDetailQueries(quotationId: string) {
getQueryClient().removeQueries({ queryKey: quotationKeys.detail(quotationId) });
getQueryClient().removeQueries({ queryKey: quotationKeys.items(quotationId) });
getQueryClient().removeQueries({ queryKey: quotationKeys.customers(quotationId) });
getQueryClient().removeQueries({ queryKey: quotationKeys.topics(quotationId) });
getQueryClient().removeQueries({ queryKey: quotationKeys.followups(quotationId) });
getQueryClient().removeQueries({ queryKey: quotationKeys.attachments(quotationId) });
getQueryClient().removeQueries({ queryKey: quotationKeys.revisions(quotationId) });
getQueryClient().removeQueries({ queryKey: quotationDocumentKeys.data(quotationId) });
getQueryClient().removeQueries({ queryKey: quotationDocumentKeys.preview(quotationId) });
}
export const createQuotationMutation = mutationOptions({
mutationFn: (data: QuotationMutationPayload) => createQuotation(data),
onSuccess: () => {
getQueryClient().invalidateQueries({ queryKey: quotationKeys.all });
onSettled: async (_data, error) => {
if (!error) {
await invalidateQuotationLists();
}
}
});
export const updateQuotationMutation = mutationOptions({
mutationFn: ({ id, values }: { id: string; values: QuotationMutationPayload }) =>
updateQuotation(id, values),
onSuccess: (_data, variables) => {
getQueryClient().invalidateQueries({ queryKey: quotationKeys.all });
getQueryClient().invalidateQueries({ queryKey: quotationKeys.detail(variables.id) });
invalidateQuotationDocumentQueries(variables.id);
onSettled: async (_data, error, variables) => {
if (!error) {
await invalidateQuotationHeaderQueries(variables.id);
}
}
});
export const deleteQuotationMutation = mutationOptions({
mutationFn: (id: string) => deleteQuotation(id),
onSuccess: () => {
getQueryClient().invalidateQueries({ queryKey: quotationKeys.all });
onSettled: async (_data, error, id) => {
if (!error) {
await removeQuotationDetailQueries(id);
await invalidateQuotationLists();
}
}
});
export const createQuotationItemMutation = mutationOptions({
mutationFn: ({ quotationId, values }: { quotationId: string; values: QuotationItemMutationPayload }) =>
createQuotationItem(quotationId, values),
onSuccess: (_data, variables) => {
getQueryClient().invalidateQueries({ queryKey: quotationKeys.items(variables.quotationId) });
getQueryClient().invalidateQueries({ queryKey: quotationKeys.detail(variables.quotationId) });
getQueryClient().invalidateQueries({ queryKey: quotationKeys.all });
invalidateQuotationDocumentQueries(variables.quotationId);
onSettled: async (_data, error, variables) => {
if (!error) {
await Promise.all([
invalidateQuotationItems(variables.quotationId),
invalidateQuotationHeaderQueries(variables.quotationId)
]);
}
}
});
@@ -82,22 +143,26 @@ export const updateQuotationItemMutation = mutationOptions({
itemId: string;
values: QuotationItemMutationPayload;
}) => updateQuotationItem(quotationId, itemId, values),
onSuccess: (_data, variables) => {
getQueryClient().invalidateQueries({ queryKey: quotationKeys.items(variables.quotationId) });
getQueryClient().invalidateQueries({ queryKey: quotationKeys.detail(variables.quotationId) });
getQueryClient().invalidateQueries({ queryKey: quotationKeys.all });
invalidateQuotationDocumentQueries(variables.quotationId);
onSettled: async (_data, error, variables) => {
if (!error) {
await Promise.all([
invalidateQuotationItems(variables.quotationId),
invalidateQuotationHeaderQueries(variables.quotationId)
]);
}
}
});
export const deleteQuotationItemMutation = mutationOptions({
mutationFn: ({ quotationId, itemId }: { quotationId: string; itemId: string }) =>
deleteQuotationItem(quotationId, itemId),
onSuccess: (_data, variables) => {
getQueryClient().invalidateQueries({ queryKey: quotationKeys.items(variables.quotationId) });
getQueryClient().invalidateQueries({ queryKey: quotationKeys.detail(variables.quotationId) });
getQueryClient().invalidateQueries({ queryKey: quotationKeys.all });
invalidateQuotationDocumentQueries(variables.quotationId);
onSettled: async (_data, error, variables) => {
if (!error) {
await Promise.all([
invalidateQuotationItems(variables.quotationId),
invalidateQuotationHeaderQueries(variables.quotationId)
]);
}
}
});
@@ -109,12 +174,13 @@ export const createQuotationCustomerMutation = mutationOptions({
quotationId: string;
values: QuotationCustomerMutationPayload;
}) => createQuotationCustomer(quotationId, values),
onSuccess: (_data, variables) => {
getQueryClient().invalidateQueries({
queryKey: quotationKeys.customers(variables.quotationId)
});
getQueryClient().invalidateQueries({ queryKey: quotationKeys.detail(variables.quotationId) });
invalidateQuotationDocumentQueries(variables.quotationId);
onSettled: async (_data, error, variables) => {
if (!error) {
await Promise.all([
invalidateQuotationCustomers(variables.quotationId),
invalidateQuotationHeaderQueries(variables.quotationId)
]);
}
}
});
@@ -128,51 +194,68 @@ export const updateQuotationCustomerMutation = mutationOptions({
relationId: string;
values: QuotationCustomerMutationPayload;
}) => updateQuotationCustomer(quotationId, relationId, values),
onSuccess: (_data, variables) => {
getQueryClient().invalidateQueries({
queryKey: quotationKeys.customers(variables.quotationId)
});
getQueryClient().invalidateQueries({ queryKey: quotationKeys.detail(variables.quotationId) });
invalidateQuotationDocumentQueries(variables.quotationId);
onSettled: async (_data, error, variables) => {
if (!error) {
await Promise.all([
invalidateQuotationCustomers(variables.quotationId),
invalidateQuotationHeaderQueries(variables.quotationId)
]);
}
}
});
export const deleteQuotationCustomerMutation = mutationOptions({
mutationFn: ({ quotationId, relationId }: { quotationId: string; relationId: string }) =>
deleteQuotationCustomer(quotationId, relationId),
onSuccess: (_data, variables) => {
getQueryClient().invalidateQueries({
queryKey: quotationKeys.customers(variables.quotationId)
});
getQueryClient().invalidateQueries({ queryKey: quotationKeys.detail(variables.quotationId) });
invalidateQuotationDocumentQueries(variables.quotationId);
onSettled: async (_data, error, variables) => {
if (!error) {
await Promise.all([
invalidateQuotationCustomers(variables.quotationId),
invalidateQuotationHeaderQueries(variables.quotationId)
]);
}
}
});
export const createQuotationTopicMutation = mutationOptions({
mutationFn: ({ quotationId, values }: { quotationId: string; values: QuotationTopicMutationPayload }) =>
createQuotationTopic(quotationId, values),
onSuccess: (_data, variables) => {
getQueryClient().invalidateQueries({ queryKey: quotationKeys.topics(variables.quotationId) });
invalidateQuotationDocumentQueries(variables.quotationId);
onSettled: async (_data, error, variables) => {
if (!error) {
await Promise.all([
invalidateQuotationTopics(variables.quotationId),
invalidateQuotationDetail(variables.quotationId),
invalidateQuotationDocumentQueries(variables.quotationId)
]);
}
}
});
export const updateQuotationTopicMutation = mutationOptions({
mutationFn: ({ quotationId, values }: { quotationId: string; values: QuotationTopicMutationPayload }) =>
updateQuotationTopic(quotationId, values),
onSuccess: (_data, variables) => {
getQueryClient().invalidateQueries({ queryKey: quotationKeys.topics(variables.quotationId) });
invalidateQuotationDocumentQueries(variables.quotationId);
onSettled: async (_data, error, variables) => {
if (!error) {
await Promise.all([
invalidateQuotationTopics(variables.quotationId),
invalidateQuotationDetail(variables.quotationId),
invalidateQuotationDocumentQueries(variables.quotationId)
]);
}
}
});
export const deleteQuotationTopicMutation = mutationOptions({
mutationFn: ({ quotationId, topicId }: { quotationId: string; topicId: string }) =>
deleteQuotationTopic(quotationId, topicId),
onSuccess: (_data, variables) => {
getQueryClient().invalidateQueries({ queryKey: quotationKeys.topics(variables.quotationId) });
invalidateQuotationDocumentQueries(variables.quotationId);
onSettled: async (_data, error, variables) => {
if (!error) {
await Promise.all([
invalidateQuotationTopics(variables.quotationId),
invalidateQuotationDetail(variables.quotationId),
invalidateQuotationDocumentQueries(variables.quotationId)
]);
}
}
});
@@ -184,11 +267,13 @@ export const createQuotationFollowupMutation = mutationOptions({
quotationId: string;
values: QuotationFollowupMutationPayload;
}) => createQuotationFollowup(quotationId, values),
onSuccess: (_data, variables) => {
getQueryClient().invalidateQueries({
queryKey: quotationKeys.followups(variables.quotationId)
});
getQueryClient().invalidateQueries({ queryKey: quotationKeys.detail(variables.quotationId) });
onSettled: async (_data, error, variables) => {
if (!error) {
await Promise.all([
invalidateQuotationFollowups(variables.quotationId),
invalidateQuotationDetail(variables.quotationId)
]);
}
}
});
@@ -200,22 +285,26 @@ export const updateQuotationFollowupMutation = mutationOptions({
quotationId: string;
values: QuotationFollowupMutationPayload;
}) => updateQuotationFollowup(quotationId, values),
onSuccess: (_data, variables) => {
getQueryClient().invalidateQueries({
queryKey: quotationKeys.followups(variables.quotationId)
});
getQueryClient().invalidateQueries({ queryKey: quotationKeys.detail(variables.quotationId) });
onSettled: async (_data, error, variables) => {
if (!error) {
await Promise.all([
invalidateQuotationFollowups(variables.quotationId),
invalidateQuotationDetail(variables.quotationId)
]);
}
}
});
export const deleteQuotationFollowupMutation = mutationOptions({
mutationFn: ({ quotationId, followupId }: { quotationId: string; followupId: string }) =>
deleteQuotationFollowup(quotationId, followupId),
onSuccess: (_data, variables) => {
getQueryClient().invalidateQueries({
queryKey: quotationKeys.followups(variables.quotationId)
});
getQueryClient().invalidateQueries({ queryKey: quotationKeys.detail(variables.quotationId) });
onSettled: async (_data, error, variables) => {
if (!error) {
await Promise.all([
invalidateQuotationFollowups(variables.quotationId),
invalidateQuotationDetail(variables.quotationId)
]);
}
}
});
@@ -227,10 +316,13 @@ export const createQuotationAttachmentMutation = mutationOptions({
quotationId: string;
values: QuotationAttachmentMutationPayload;
}) => createQuotationAttachment(quotationId, values),
onSuccess: (_data, variables) => {
getQueryClient().invalidateQueries({
queryKey: quotationKeys.attachments(variables.quotationId)
});
onSettled: async (_data, error, variables) => {
if (!error) {
await Promise.all([
invalidateQuotationAttachments(variables.quotationId),
invalidateQuotationDetail(variables.quotationId)
]);
}
}
});
@@ -242,20 +334,26 @@ export const updateQuotationAttachmentMutation = mutationOptions({
quotationId: string;
values: QuotationAttachmentMutationPayload;
}) => updateQuotationAttachment(quotationId, values),
onSuccess: (_data, variables) => {
getQueryClient().invalidateQueries({
queryKey: quotationKeys.attachments(variables.quotationId)
});
onSettled: async (_data, error, variables) => {
if (!error) {
await Promise.all([
invalidateQuotationAttachments(variables.quotationId),
invalidateQuotationDetail(variables.quotationId)
]);
}
}
});
export const deleteQuotationAttachmentMutation = mutationOptions({
mutationFn: ({ quotationId, attachmentId }: { quotationId: string; attachmentId: string }) =>
deleteQuotationAttachment(quotationId, attachmentId),
onSuccess: (_data, variables) => {
getQueryClient().invalidateQueries({
queryKey: quotationKeys.attachments(variables.quotationId)
});
onSettled: async (_data, error, variables) => {
if (!error) {
await Promise.all([
invalidateQuotationAttachments(variables.quotationId),
invalidateQuotationDetail(variables.quotationId)
]);
}
}
});
@@ -267,8 +365,12 @@ export const createQuotationRevisionMutation = mutationOptions({
quotationId: string;
revisionRemark?: string;
}) => createQuotationRevision(quotationId, revisionRemark),
onSuccess: (_data, variables) => {
getQueryClient().invalidateQueries({ queryKey: quotationKeys.revisions(variables.quotationId) });
getQueryClient().invalidateQueries({ queryKey: quotationKeys.all });
onSettled: async (_data, error, variables) => {
if (!error) {
await Promise.all([
invalidateQuotationRevisions(variables.quotationId),
invalidateQuotationHeaderQueries(variables.quotationId)
]);
}
}
});

View File

@@ -13,14 +13,22 @@ import type { QuotationFilters } from './types';
export const quotationKeys = {
all: ['crm-quotations'] as const,
list: (filters: QuotationFilters) => [...quotationKeys.all, 'list', filters] as const,
detail: (id: string) => [...quotationKeys.all, 'detail', id] as const,
items: (id: string) => [...quotationKeys.all, 'items', id] as const,
customers: (id: string) => [...quotationKeys.all, 'customers', id] as const,
topics: (id: string) => [...quotationKeys.all, 'topics', id] as const,
followups: (id: string) => [...quotationKeys.all, 'followups', id] as const,
attachments: (id: string) => [...quotationKeys.all, 'attachments', id] as const,
revisions: (id: string) => [...quotationKeys.all, 'revisions', id] as const
lists: () => [...quotationKeys.all, 'list'] as const,
list: (filters: QuotationFilters) => [...quotationKeys.lists(), filters] as const,
details: () => [...quotationKeys.all, 'detail'] as const,
detail: (id: string) => [...quotationKeys.details(), id] as const,
itemsRoot: () => [...quotationKeys.all, 'items'] as const,
items: (id: string) => [...quotationKeys.itemsRoot(), id] as const,
customersRoot: () => [...quotationKeys.all, 'customers'] as const,
customers: (id: string) => [...quotationKeys.customersRoot(), id] as const,
topicsRoot: () => [...quotationKeys.all, 'topics'] as const,
topics: (id: string) => [...quotationKeys.topicsRoot(), id] as const,
followupsRoot: () => [...quotationKeys.all, 'followups'] as const,
followups: (id: string) => [...quotationKeys.followupsRoot(), id] as const,
attachmentsRoot: () => [...quotationKeys.all, 'attachments'] as const,
attachments: (id: string) => [...quotationKeys.attachmentsRoot(), id] as const,
revisionsRoot: () => [...quotationKeys.all, 'revisions'] as const,
revisions: (id: string) => [...quotationKeys.revisionsRoot(), id] as const
};
export const quotationsQueryOptions = (filters: QuotationFilters) =>