@greatapps/common 1.1.499 → 1.1.500
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/infra/utils/array.mjs +34 -0
- package/dist/infra/utils/array.mjs.map +1 -0
- package/dist/modules/projects/hooks/create-project.hook.mjs +53 -22
- package/dist/modules/projects/hooks/create-project.hook.mjs.map +1 -1
- package/dist/modules/projects/hooks/delete-project.hook.mjs +5 -6
- package/dist/modules/projects/hooks/delete-project.hook.mjs.map +1 -1
- package/package.json +1 -1
- package/src/infra/utils/array.ts +54 -0
- package/src/modules/projects/hooks/create-project.hook.ts +70 -27
- package/src/modules/projects/hooks/delete-project.hook.ts +6 -6
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
function insertSorted(data, newItem, compareFn) {
|
|
2
|
+
const index = data.findIndex((item) => compareFn(newItem, item) < 0);
|
|
3
|
+
if (index === -1) return [...data, newItem];
|
|
4
|
+
return [...data.slice(0, index), newItem, ...data.slice(index)];
|
|
5
|
+
}
|
|
6
|
+
function buildCompareFn(sort) {
|
|
7
|
+
const fields = sort.split(",").map((f) => f.trim()).filter(Boolean);
|
|
8
|
+
const parsedFields = fields.map((field) => {
|
|
9
|
+
const desc = field.startsWith("-");
|
|
10
|
+
const key = desc ? field.slice(1) : field;
|
|
11
|
+
return { key, desc };
|
|
12
|
+
});
|
|
13
|
+
return (a, b) => {
|
|
14
|
+
for (const { key, desc } of parsedFields) {
|
|
15
|
+
const aVal = a[key];
|
|
16
|
+
const bVal = b[key];
|
|
17
|
+
let result;
|
|
18
|
+
if (typeof aVal === "string" && typeof bVal === "string") {
|
|
19
|
+
result = aVal.localeCompare(bVal);
|
|
20
|
+
} else if (typeof aVal === "number" && typeof bVal === "number") {
|
|
21
|
+
result = aVal - bVal;
|
|
22
|
+
} else {
|
|
23
|
+
result = String(aVal ?? "").localeCompare(String(bVal ?? ""));
|
|
24
|
+
}
|
|
25
|
+
if (result !== 0) return desc ? -result : result;
|
|
26
|
+
}
|
|
27
|
+
return 0;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export {
|
|
31
|
+
buildCompareFn,
|
|
32
|
+
insertSorted
|
|
33
|
+
};
|
|
34
|
+
//# sourceMappingURL=array.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/infra/utils/array.ts"],"sourcesContent":["/**\n * Insere um item em um array já ordenado, mantendo a ordenação\n * com base na função de comparação fornecida.\n */\nexport function insertSorted<T>(\n data: T[],\n newItem: T,\n compareFn: (a: T, b: T) => number\n): T[] {\n const index = data.findIndex((item) => compareFn(newItem, item) < 0);\n if (index === -1) return [...data, newItem];\n return [...data.slice(0, index), newItem, ...data.slice(index)];\n}\n\n/**\n * Cria uma função de comparação a partir de uma string de sort da API.\n * Suporta múltiplos campos separados por vírgula e prefixo `-` para desc.\n *\n * Exemplos:\n * - \"title\" → ascendente por title\n * - \"-title\" → descendente por title\n * - \"title,-datetime_add\" → ascendente por title, depois descendente por datetime_add\n */\nexport function buildCompareFn<T extends Record<string, unknown>>(\n sort: string\n): (a: T, b: T) => number {\n const fields = sort.split(',').map((f) => f.trim()).filter(Boolean);\n\n const parsedFields = fields.map((field) => {\n const desc = field.startsWith('-');\n const key = desc ? field.slice(1) : field;\n return { key, desc };\n });\n\n return (a: T, b: T) => {\n for (const { key, desc } of parsedFields) {\n const aVal = a[key];\n const bVal = b[key];\n\n let result: number;\n\n if (typeof aVal === 'string' && typeof bVal === 'string') {\n result = aVal.localeCompare(bVal);\n } else if (typeof aVal === 'number' && typeof bVal === 'number') {\n result = aVal - bVal;\n } else {\n result = String(aVal ?? '').localeCompare(String(bVal ?? ''));\n }\n\n if (result !== 0) return desc ? -result : result;\n }\n return 0;\n };\n}\n"],"mappings":"AAIO,SAAS,aACd,MACA,SACA,WACK;AACL,QAAM,QAAQ,KAAK,UAAU,CAAC,SAAS,UAAU,SAAS,IAAI,IAAI,CAAC;AACnE,MAAI,UAAU,GAAI,QAAO,CAAC,GAAG,MAAM,OAAO;AAC1C,SAAO,CAAC,GAAG,KAAK,MAAM,GAAG,KAAK,GAAG,SAAS,GAAG,KAAK,MAAM,KAAK,CAAC;AAChE;AAWO,SAAS,eACd,MACwB;AACxB,QAAM,SAAS,KAAK,MAAM,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,OAAO;AAElE,QAAM,eAAe,OAAO,IAAI,CAAC,UAAU;AACzC,UAAM,OAAO,MAAM,WAAW,GAAG;AACjC,UAAM,MAAM,OAAO,MAAM,MAAM,CAAC,IAAI;AACpC,WAAO,EAAE,KAAK,KAAK;AAAA,EACrB,CAAC;AAED,SAAO,CAAC,GAAM,MAAS;AACrB,eAAW,EAAE,KAAK,KAAK,KAAK,cAAc;AACxC,YAAM,OAAO,EAAE,GAAG;AAClB,YAAM,OAAO,EAAE,GAAG;AAElB,UAAI;AAEJ,UAAI,OAAO,SAAS,YAAY,OAAO,SAAS,UAAU;AACxD,iBAAS,KAAK,cAAc,IAAI;AAAA,MAClC,WAAW,OAAO,SAAS,YAAY,OAAO,SAAS,UAAU;AAC/D,iBAAS,OAAO;AAAA,MAClB,OAAO;AACL,iBAAS,OAAO,QAAQ,EAAE,EAAE,cAAc,OAAO,QAAQ,EAAE,CAAC;AAAA,MAC9D;AAEA,UAAI,WAAW,EAAG,QAAO,OAAO,CAAC,SAAS;AAAA,IAC5C;AACA,WAAO;AAAA,EACT;AACF;","names":[]}
|
|
@@ -3,34 +3,65 @@ import { useMutation, useQueryClient } from "@tanstack/react-query";
|
|
|
3
3
|
import { createProjectAction } from "../actions/create-project.action";
|
|
4
4
|
import { withAction } from "../../../utils/withAction";
|
|
5
5
|
import { useAuthQueryKey } from "../../../hooks/useAuthQueryKey";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
6
|
+
import { PROJECTS_BASE_KEY } from "./list-projects.hook";
|
|
7
|
+
import { insertSorted, buildCompareFn } from "../../../infra/utils/array";
|
|
8
|
+
function extractSort(query) {
|
|
9
|
+
const key = query.queryKey;
|
|
10
|
+
for (let i = key.length - 1; i >= 0; i--) {
|
|
11
|
+
const segment = key[i];
|
|
12
|
+
if (segment && typeof segment === "object" && "sort" in segment) {
|
|
13
|
+
return segment.sort;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return void 0;
|
|
17
|
+
}
|
|
8
18
|
function useCreateProject() {
|
|
9
19
|
const queryClient = useQueryClient();
|
|
10
|
-
const
|
|
11
|
-
const listKey = useAuthQueryKey([...PROJECTS_QUERY_KEY()]);
|
|
20
|
+
const projectsKey = useAuthQueryKey([...PROJECTS_BASE_KEY]);
|
|
12
21
|
return useMutation({
|
|
13
22
|
mutationFn: (data) => withAction(createProjectAction)(data),
|
|
14
23
|
onSuccess: (newProject) => {
|
|
15
|
-
queryClient.
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
)
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
24
|
+
const cache = queryClient.getQueryCache();
|
|
25
|
+
const queries = cache.findAll({ queryKey: projectsKey });
|
|
26
|
+
for (const query of queries) {
|
|
27
|
+
const sort = extractSort(query);
|
|
28
|
+
const isInfinite = query.queryKey.includes("infinite");
|
|
29
|
+
if (isInfinite) {
|
|
30
|
+
queryClient.setQueryData(
|
|
31
|
+
query.queryKey,
|
|
32
|
+
(old) => {
|
|
33
|
+
if (!old?.pages.length) return old;
|
|
34
|
+
const pageSize = old.pages[0].data.length || 20;
|
|
35
|
+
const allItems = old.pages.flatMap((page) => page.data);
|
|
36
|
+
const withNew = sort ? insertSorted(allItems, newProject, buildCompareFn(sort)) : [newProject, ...allItems];
|
|
37
|
+
const newTotal = (old.pages[0].total ?? 0) + 1;
|
|
38
|
+
return {
|
|
39
|
+
...old,
|
|
40
|
+
pages: old.pages.map((page, i) => ({
|
|
41
|
+
...page,
|
|
42
|
+
data: withNew.slice(i * pageSize, (i + 1) * pageSize),
|
|
43
|
+
total: newTotal
|
|
44
|
+
}))
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
);
|
|
48
|
+
} else {
|
|
49
|
+
queryClient.setQueryData(
|
|
50
|
+
query.queryKey,
|
|
51
|
+
(old) => {
|
|
52
|
+
if (!old) return old;
|
|
53
|
+
const pageSize = old.data.length || 20;
|
|
54
|
+
const withNew = sort ? insertSorted(old.data, newProject, buildCompareFn(sort)) : [newProject, ...old.data];
|
|
55
|
+
return {
|
|
56
|
+
...old,
|
|
57
|
+
data: withNew.slice(0, pageSize),
|
|
58
|
+
total: old.total + 1
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
);
|
|
32
62
|
}
|
|
33
|
-
|
|
63
|
+
}
|
|
64
|
+
queryClient.invalidateQueries({ queryKey: projectsKey });
|
|
34
65
|
}
|
|
35
66
|
});
|
|
36
67
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/modules/projects/hooks/create-project.hook.ts"],"sourcesContent":["'use client';\n\nimport { useMutation, useQueryClient, InfiniteData } from '@tanstack/react-query';\nimport { createProjectAction } from '../actions/create-project.action';\nimport { withAction } from '../../../utils/withAction';\nimport { useAuthQueryKey } from '../../../hooks/useAuthQueryKey';\nimport {
|
|
1
|
+
{"version":3,"sources":["../../../../src/modules/projects/hooks/create-project.hook.ts"],"sourcesContent":["'use client';\n\nimport { useMutation, useQueryClient, InfiniteData, Query } from '@tanstack/react-query';\nimport { createProjectAction } from '../actions/create-project.action';\nimport { withAction } from '../../../utils/withAction';\nimport { useAuthQueryKey } from '../../../hooks/useAuthQueryKey';\nimport { PROJECTS_BASE_KEY } from './list-projects.hook';\nimport { insertSorted, buildCompareFn } from '../../../infra/utils/array';\nimport type { CreateProjectRequest, Project, ProjectsPage } from '../types';\n\nfunction extractSort(query: Query): string | undefined {\n const key = query.queryKey;\n for (let i = key.length - 1; i >= 0; i--) {\n const segment = key[i];\n if (segment && typeof segment === 'object' && 'sort' in segment) {\n return (segment as Record<string, unknown>).sort as string | undefined;\n }\n }\n return undefined;\n}\n\nexport function useCreateProject() {\n const queryClient = useQueryClient();\n const projectsKey = useAuthQueryKey([...PROJECTS_BASE_KEY]);\n\n return useMutation({\n mutationFn: (data: CreateProjectRequest) =>\n withAction(createProjectAction)(data),\n\n onSuccess: (newProject: Project) => {\n const cache = queryClient.getQueryCache();\n const queries = cache.findAll({ queryKey: projectsKey });\n\n for (const query of queries) {\n const sort = extractSort(query);\n const isInfinite = query.queryKey.includes('infinite');\n\n if (isInfinite) {\n queryClient.setQueryData<InfiniteData<ProjectsPage>>(\n query.queryKey,\n (old) => {\n if (!old?.pages.length) return old;\n\n const pageSize = old.pages[0].data.length || 20;\n const allItems = old.pages.flatMap((page) => page.data);\n\n const withNew = sort\n ? insertSorted(allItems, newProject, buildCompareFn<Project>(sort))\n : [newProject, ...allItems];\n\n const newTotal = (old.pages[0].total ?? 0) + 1;\n\n return {\n ...old,\n pages: old.pages.map((page, i) => ({\n ...page,\n data: withNew.slice(i * pageSize, (i + 1) * pageSize),\n total: newTotal,\n })),\n };\n },\n );\n } else {\n queryClient.setQueryData<ProjectsPage>(\n query.queryKey,\n (old) => {\n if (!old) return old;\n\n const pageSize = old.data.length || 20;\n\n const withNew = sort\n ? insertSorted(old.data, newProject, buildCompareFn<Project>(sort))\n : [newProject, ...old.data];\n\n return {\n ...old,\n data: withNew.slice(0, pageSize),\n total: old.total + 1,\n };\n },\n );\n }\n }\n\n queryClient.invalidateQueries({ queryKey: projectsKey });\n },\n });\n}\n"],"mappings":";AAEA,SAAS,aAAa,sBAA2C;AACjE,SAAS,2BAA2B;AACpC,SAAS,kBAAkB;AAC3B,SAAS,uBAAuB;AAChC,SAAS,yBAAyB;AAClC,SAAS,cAAc,sBAAsB;AAG7C,SAAS,YAAY,OAAkC;AACrD,QAAM,MAAM,MAAM;AAClB,WAAS,IAAI,IAAI,SAAS,GAAG,KAAK,GAAG,KAAK;AACxC,UAAM,UAAU,IAAI,CAAC;AACrB,QAAI,WAAW,OAAO,YAAY,YAAY,UAAU,SAAS;AAC/D,aAAQ,QAAoC;AAAA,IAC9C;AAAA,EACF;AACA,SAAO;AACT;AAEO,SAAS,mBAAmB;AACjC,QAAM,cAAc,eAAe;AACnC,QAAM,cAAc,gBAAgB,CAAC,GAAG,iBAAiB,CAAC;AAE1D,SAAO,YAAY;AAAA,IACjB,YAAY,CAAC,SACX,WAAW,mBAAmB,EAAE,IAAI;AAAA,IAEtC,WAAW,CAAC,eAAwB;AAClC,YAAM,QAAQ,YAAY,cAAc;AACxC,YAAM,UAAU,MAAM,QAAQ,EAAE,UAAU,YAAY,CAAC;AAEvD,iBAAW,SAAS,SAAS;AAC3B,cAAM,OAAO,YAAY,KAAK;AAC9B,cAAM,aAAa,MAAM,SAAS,SAAS,UAAU;AAErD,YAAI,YAAY;AACd,sBAAY;AAAA,YACV,MAAM;AAAA,YACN,CAAC,QAAQ;AACP,kBAAI,CAAC,KAAK,MAAM,OAAQ,QAAO;AAE/B,oBAAM,WAAW,IAAI,MAAM,CAAC,EAAE,KAAK,UAAU;AAC7C,oBAAM,WAAW,IAAI,MAAM,QAAQ,CAAC,SAAS,KAAK,IAAI;AAEtD,oBAAM,UAAU,OACZ,aAAa,UAAU,YAAY,eAAwB,IAAI,CAAC,IAChE,CAAC,YAAY,GAAG,QAAQ;AAE5B,oBAAM,YAAY,IAAI,MAAM,CAAC,EAAE,SAAS,KAAK;AAE7C,qBAAO;AAAA,gBACL,GAAG;AAAA,gBACH,OAAO,IAAI,MAAM,IAAI,CAAC,MAAM,OAAO;AAAA,kBACjC,GAAG;AAAA,kBACH,MAAM,QAAQ,MAAM,IAAI,WAAW,IAAI,KAAK,QAAQ;AAAA,kBACpD,OAAO;AAAA,gBACT,EAAE;AAAA,cACJ;AAAA,YACF;AAAA,UACF;AAAA,QACF,OAAO;AACL,sBAAY;AAAA,YACV,MAAM;AAAA,YACN,CAAC,QAAQ;AACP,kBAAI,CAAC,IAAK,QAAO;AAEjB,oBAAM,WAAW,IAAI,KAAK,UAAU;AAEpC,oBAAM,UAAU,OACZ,aAAa,IAAI,MAAM,YAAY,eAAwB,IAAI,CAAC,IAChE,CAAC,YAAY,GAAG,IAAI,IAAI;AAE5B,qBAAO;AAAA,gBACL,GAAG;AAAA,gBACH,MAAM,QAAQ,MAAM,GAAG,QAAQ;AAAA,gBAC/B,OAAO,IAAI,QAAQ;AAAA,cACrB;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAEA,kBAAY,kBAAkB,EAAE,UAAU,YAAY,CAAC;AAAA,IACzD;AAAA,EACF,CAAC;AACH;","names":[]}
|
|
@@ -3,17 +3,15 @@ import { useMutation, useQueryClient } from "@tanstack/react-query";
|
|
|
3
3
|
import { deleteProjectAction } from "../actions/delete-project.action";
|
|
4
4
|
import { withAction } from "../../../utils/withAction";
|
|
5
5
|
import { useAuthQueryKey } from "../../../hooks/useAuthQueryKey";
|
|
6
|
-
import {
|
|
7
|
-
import { INFINITE_PROJECTS_QUERY_KEY } from "./list-infinite-projects.hook";
|
|
6
|
+
import { PROJECTS_BASE_KEY } from "./list-projects.hook";
|
|
8
7
|
function useDeleteProject() {
|
|
9
8
|
const queryClient = useQueryClient();
|
|
10
|
-
const
|
|
11
|
-
const listKey = useAuthQueryKey([...PROJECTS_QUERY_KEY()]);
|
|
9
|
+
const projectsKey = useAuthQueryKey([...PROJECTS_BASE_KEY]);
|
|
12
10
|
return useMutation({
|
|
13
11
|
mutationFn: ({ projectId, moveToProjectId }) => withAction(deleteProjectAction)(projectId, moveToProjectId),
|
|
14
12
|
onSuccess: (_, { projectId }) => {
|
|
15
13
|
queryClient.setQueriesData(
|
|
16
|
-
{ queryKey:
|
|
14
|
+
{ queryKey: projectsKey, exact: false },
|
|
17
15
|
(old) => {
|
|
18
16
|
if (!old?.pages.length) return old;
|
|
19
17
|
return {
|
|
@@ -27,7 +25,7 @@ function useDeleteProject() {
|
|
|
27
25
|
}
|
|
28
26
|
);
|
|
29
27
|
queryClient.setQueriesData(
|
|
30
|
-
{ queryKey:
|
|
28
|
+
{ queryKey: projectsKey, exact: false },
|
|
31
29
|
(old) => {
|
|
32
30
|
if (!old) return old;
|
|
33
31
|
return {
|
|
@@ -37,6 +35,7 @@ function useDeleteProject() {
|
|
|
37
35
|
};
|
|
38
36
|
}
|
|
39
37
|
);
|
|
38
|
+
queryClient.invalidateQueries({ queryKey: projectsKey });
|
|
40
39
|
}
|
|
41
40
|
});
|
|
42
41
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/modules/projects/hooks/delete-project.hook.ts"],"sourcesContent":["'use client';\n\nimport { useMutation, useQueryClient, InfiniteData } from '@tanstack/react-query';\nimport { deleteProjectAction } from '../actions/delete-project.action';\nimport { withAction } from '../../../utils/withAction';\nimport { useAuthQueryKey } from '../../../hooks/useAuthQueryKey';\nimport {
|
|
1
|
+
{"version":3,"sources":["../../../../src/modules/projects/hooks/delete-project.hook.ts"],"sourcesContent":["'use client';\n\nimport { useMutation, useQueryClient, InfiniteData } from '@tanstack/react-query';\nimport { deleteProjectAction } from '../actions/delete-project.action';\nimport { withAction } from '../../../utils/withAction';\nimport { useAuthQueryKey } from '../../../hooks/useAuthQueryKey';\nimport { PROJECTS_BASE_KEY } from './list-projects.hook';\nimport type { ProjectsPage } from '../types';\n\nexport function useDeleteProject() {\n const queryClient = useQueryClient();\n const projectsKey = useAuthQueryKey([...PROJECTS_BASE_KEY]);\n\n return useMutation({\n mutationFn: ({ projectId, moveToProjectId }: { projectId: number; moveToProjectId: number }) =>\n withAction(deleteProjectAction)(projectId, moveToProjectId),\n\n onSuccess: (_, { projectId }) => {\n queryClient.setQueriesData<InfiniteData<ProjectsPage>>(\n { queryKey: projectsKey, exact: false },\n (old) => {\n if (!old?.pages.length) return old;\n return {\n ...old,\n pages: old.pages.map((page) => ({\n ...page,\n data: page.data.filter((p) => p.id !== projectId),\n total: page.total - 1,\n })),\n };\n },\n );\n\n queryClient.setQueriesData<ProjectsPage>(\n { queryKey: projectsKey, exact: false },\n (old) => {\n if (!old) return old;\n return {\n ...old,\n data: old.data.filter((p) => p.id !== projectId),\n total: old.total - 1,\n };\n },\n );\n\n queryClient.invalidateQueries({ queryKey: projectsKey });\n },\n });\n}\n"],"mappings":";AAEA,SAAS,aAAa,sBAAoC;AAC1D,SAAS,2BAA2B;AACpC,SAAS,kBAAkB;AAC3B,SAAS,uBAAuB;AAChC,SAAS,yBAAyB;AAG3B,SAAS,mBAAmB;AACjC,QAAM,cAAc,eAAe;AACnC,QAAM,cAAc,gBAAgB,CAAC,GAAG,iBAAiB,CAAC;AAE1D,SAAO,YAAY;AAAA,IACjB,YAAY,CAAC,EAAE,WAAW,gBAAgB,MACxC,WAAW,mBAAmB,EAAE,WAAW,eAAe;AAAA,IAE5D,WAAW,CAAC,GAAG,EAAE,UAAU,MAAM;AAC/B,kBAAY;AAAA,QACV,EAAE,UAAU,aAAa,OAAO,MAAM;AAAA,QACtC,CAAC,QAAQ;AACP,cAAI,CAAC,KAAK,MAAM,OAAQ,QAAO;AAC/B,iBAAO;AAAA,YACL,GAAG;AAAA,YACH,OAAO,IAAI,MAAM,IAAI,CAAC,UAAU;AAAA,cAC9B,GAAG;AAAA,cACH,MAAM,KAAK,KAAK,OAAO,CAAC,MAAM,EAAE,OAAO,SAAS;AAAA,cAChD,OAAO,KAAK,QAAQ;AAAA,YACtB,EAAE;AAAA,UACJ;AAAA,QACF;AAAA,MACF;AAEA,kBAAY;AAAA,QACV,EAAE,UAAU,aAAa,OAAO,MAAM;AAAA,QACtC,CAAC,QAAQ;AACP,cAAI,CAAC,IAAK,QAAO;AACjB,iBAAO;AAAA,YACL,GAAG;AAAA,YACH,MAAM,IAAI,KAAK,OAAO,CAAC,MAAM,EAAE,OAAO,SAAS;AAAA,YAC/C,OAAO,IAAI,QAAQ;AAAA,UACrB;AAAA,QACF;AAAA,MACF;AAEA,kBAAY,kBAAkB,EAAE,UAAU,YAAY,CAAC;AAAA,IACzD;AAAA,EACF,CAAC;AACH;","names":[]}
|
package/package.json
CHANGED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Insere um item em um array já ordenado, mantendo a ordenação
|
|
3
|
+
* com base na função de comparação fornecida.
|
|
4
|
+
*/
|
|
5
|
+
export function insertSorted<T>(
|
|
6
|
+
data: T[],
|
|
7
|
+
newItem: T,
|
|
8
|
+
compareFn: (a: T, b: T) => number
|
|
9
|
+
): T[] {
|
|
10
|
+
const index = data.findIndex((item) => compareFn(newItem, item) < 0);
|
|
11
|
+
if (index === -1) return [...data, newItem];
|
|
12
|
+
return [...data.slice(0, index), newItem, ...data.slice(index)];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Cria uma função de comparação a partir de uma string de sort da API.
|
|
17
|
+
* Suporta múltiplos campos separados por vírgula e prefixo `-` para desc.
|
|
18
|
+
*
|
|
19
|
+
* Exemplos:
|
|
20
|
+
* - "title" → ascendente por title
|
|
21
|
+
* - "-title" → descendente por title
|
|
22
|
+
* - "title,-datetime_add" → ascendente por title, depois descendente por datetime_add
|
|
23
|
+
*/
|
|
24
|
+
export function buildCompareFn<T extends Record<string, unknown>>(
|
|
25
|
+
sort: string
|
|
26
|
+
): (a: T, b: T) => number {
|
|
27
|
+
const fields = sort.split(',').map((f) => f.trim()).filter(Boolean);
|
|
28
|
+
|
|
29
|
+
const parsedFields = fields.map((field) => {
|
|
30
|
+
const desc = field.startsWith('-');
|
|
31
|
+
const key = desc ? field.slice(1) : field;
|
|
32
|
+
return { key, desc };
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
return (a: T, b: T) => {
|
|
36
|
+
for (const { key, desc } of parsedFields) {
|
|
37
|
+
const aVal = a[key];
|
|
38
|
+
const bVal = b[key];
|
|
39
|
+
|
|
40
|
+
let result: number;
|
|
41
|
+
|
|
42
|
+
if (typeof aVal === 'string' && typeof bVal === 'string') {
|
|
43
|
+
result = aVal.localeCompare(bVal);
|
|
44
|
+
} else if (typeof aVal === 'number' && typeof bVal === 'number') {
|
|
45
|
+
result = aVal - bVal;
|
|
46
|
+
} else {
|
|
47
|
+
result = String(aVal ?? '').localeCompare(String(bVal ?? ''));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (result !== 0) return desc ? -result : result;
|
|
51
|
+
}
|
|
52
|
+
return 0;
|
|
53
|
+
};
|
|
54
|
+
}
|
|
@@ -1,45 +1,88 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { useMutation, useQueryClient, InfiniteData } from '@tanstack/react-query';
|
|
3
|
+
import { useMutation, useQueryClient, InfiniteData, Query } from '@tanstack/react-query';
|
|
4
4
|
import { createProjectAction } from '../actions/create-project.action';
|
|
5
5
|
import { withAction } from '../../../utils/withAction';
|
|
6
6
|
import { useAuthQueryKey } from '../../../hooks/useAuthQueryKey';
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
7
|
+
import { PROJECTS_BASE_KEY } from './list-projects.hook';
|
|
8
|
+
import { insertSorted, buildCompareFn } from '../../../infra/utils/array';
|
|
9
9
|
import type { CreateProjectRequest, Project, ProjectsPage } from '../types';
|
|
10
10
|
|
|
11
|
+
function extractSort(query: Query): string | undefined {
|
|
12
|
+
const key = query.queryKey;
|
|
13
|
+
for (let i = key.length - 1; i >= 0; i--) {
|
|
14
|
+
const segment = key[i];
|
|
15
|
+
if (segment && typeof segment === 'object' && 'sort' in segment) {
|
|
16
|
+
return (segment as Record<string, unknown>).sort as string | undefined;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
|
|
11
22
|
export function useCreateProject() {
|
|
12
23
|
const queryClient = useQueryClient();
|
|
13
|
-
const
|
|
14
|
-
const listKey = useAuthQueryKey([...PROJECTS_QUERY_KEY()]);
|
|
24
|
+
const projectsKey = useAuthQueryKey([...PROJECTS_BASE_KEY]);
|
|
15
25
|
|
|
16
26
|
return useMutation({
|
|
17
27
|
mutationFn: (data: CreateProjectRequest) =>
|
|
18
28
|
withAction(createProjectAction)(data),
|
|
19
29
|
|
|
20
30
|
onSuccess: (newProject: Project) => {
|
|
21
|
-
queryClient.
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
)
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
31
|
+
const cache = queryClient.getQueryCache();
|
|
32
|
+
const queries = cache.findAll({ queryKey: projectsKey });
|
|
33
|
+
|
|
34
|
+
for (const query of queries) {
|
|
35
|
+
const sort = extractSort(query);
|
|
36
|
+
const isInfinite = query.queryKey.includes('infinite');
|
|
37
|
+
|
|
38
|
+
if (isInfinite) {
|
|
39
|
+
queryClient.setQueryData<InfiniteData<ProjectsPage>>(
|
|
40
|
+
query.queryKey,
|
|
41
|
+
(old) => {
|
|
42
|
+
if (!old?.pages.length) return old;
|
|
43
|
+
|
|
44
|
+
const pageSize = old.pages[0].data.length || 20;
|
|
45
|
+
const allItems = old.pages.flatMap((page) => page.data);
|
|
46
|
+
|
|
47
|
+
const withNew = sort
|
|
48
|
+
? insertSorted(allItems, newProject, buildCompareFn<Project>(sort))
|
|
49
|
+
: [newProject, ...allItems];
|
|
50
|
+
|
|
51
|
+
const newTotal = (old.pages[0].total ?? 0) + 1;
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
...old,
|
|
55
|
+
pages: old.pages.map((page, i) => ({
|
|
56
|
+
...page,
|
|
57
|
+
data: withNew.slice(i * pageSize, (i + 1) * pageSize),
|
|
58
|
+
total: newTotal,
|
|
59
|
+
})),
|
|
60
|
+
};
|
|
61
|
+
},
|
|
62
|
+
);
|
|
63
|
+
} else {
|
|
64
|
+
queryClient.setQueryData<ProjectsPage>(
|
|
65
|
+
query.queryKey,
|
|
66
|
+
(old) => {
|
|
67
|
+
if (!old) return old;
|
|
68
|
+
|
|
69
|
+
const pageSize = old.data.length || 20;
|
|
70
|
+
|
|
71
|
+
const withNew = sort
|
|
72
|
+
? insertSorted(old.data, newProject, buildCompareFn<Project>(sort))
|
|
73
|
+
: [newProject, ...old.data];
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
...old,
|
|
77
|
+
data: withNew.slice(0, pageSize),
|
|
78
|
+
total: old.total + 1,
|
|
79
|
+
};
|
|
80
|
+
},
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
queryClient.invalidateQueries({ queryKey: projectsKey });
|
|
43
86
|
},
|
|
44
87
|
});
|
|
45
88
|
}
|
|
@@ -4,14 +4,12 @@ import { useMutation, useQueryClient, InfiniteData } from '@tanstack/react-query
|
|
|
4
4
|
import { deleteProjectAction } from '../actions/delete-project.action';
|
|
5
5
|
import { withAction } from '../../../utils/withAction';
|
|
6
6
|
import { useAuthQueryKey } from '../../../hooks/useAuthQueryKey';
|
|
7
|
-
import {
|
|
8
|
-
import { INFINITE_PROJECTS_QUERY_KEY } from './list-infinite-projects.hook';
|
|
7
|
+
import { PROJECTS_BASE_KEY } from './list-projects.hook';
|
|
9
8
|
import type { ProjectsPage } from '../types';
|
|
10
9
|
|
|
11
10
|
export function useDeleteProject() {
|
|
12
11
|
const queryClient = useQueryClient();
|
|
13
|
-
const
|
|
14
|
-
const listKey = useAuthQueryKey([...PROJECTS_QUERY_KEY()]);
|
|
12
|
+
const projectsKey = useAuthQueryKey([...PROJECTS_BASE_KEY]);
|
|
15
13
|
|
|
16
14
|
return useMutation({
|
|
17
15
|
mutationFn: ({ projectId, moveToProjectId }: { projectId: number; moveToProjectId: number }) =>
|
|
@@ -19,7 +17,7 @@ export function useDeleteProject() {
|
|
|
19
17
|
|
|
20
18
|
onSuccess: (_, { projectId }) => {
|
|
21
19
|
queryClient.setQueriesData<InfiniteData<ProjectsPage>>(
|
|
22
|
-
{ queryKey:
|
|
20
|
+
{ queryKey: projectsKey, exact: false },
|
|
23
21
|
(old) => {
|
|
24
22
|
if (!old?.pages.length) return old;
|
|
25
23
|
return {
|
|
@@ -34,7 +32,7 @@ export function useDeleteProject() {
|
|
|
34
32
|
);
|
|
35
33
|
|
|
36
34
|
queryClient.setQueriesData<ProjectsPage>(
|
|
37
|
-
{ queryKey:
|
|
35
|
+
{ queryKey: projectsKey, exact: false },
|
|
38
36
|
(old) => {
|
|
39
37
|
if (!old) return old;
|
|
40
38
|
return {
|
|
@@ -44,6 +42,8 @@ export function useDeleteProject() {
|
|
|
44
42
|
};
|
|
45
43
|
},
|
|
46
44
|
);
|
|
45
|
+
|
|
46
|
+
queryClient.invalidateQueries({ queryKey: projectsKey });
|
|
47
47
|
},
|
|
48
48
|
});
|
|
49
49
|
}
|