@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.
@@ -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 { PROJECTS_QUERY_KEY } from "./list-projects.hook";
7
- import { INFINITE_PROJECTS_QUERY_KEY } from "./list-infinite-projects.hook";
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 infiniteKey = useAuthQueryKey([...INFINITE_PROJECTS_QUERY_KEY()]);
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.setQueriesData(
16
- { queryKey: infiniteKey, exact: false },
17
- (old) => {
18
- if (!old?.pages.length) return old;
19
- return {
20
- ...old,
21
- pages: old.pages.map(
22
- (page, i) => i === 0 ? { ...page, data: [newProject, ...page.data], total: page.total + 1 } : page
23
- )
24
- };
25
- }
26
- );
27
- queryClient.setQueriesData(
28
- { queryKey: listKey, exact: false },
29
- (old) => {
30
- if (!old) return old;
31
- return { ...old, data: [newProject, ...old.data], total: old.total + 1 };
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 { PROJECTS_QUERY_KEY } from './list-projects.hook';\nimport { INFINITE_PROJECTS_QUERY_KEY } from './list-infinite-projects.hook';\nimport type { CreateProjectRequest, Project, ProjectsPage } from '../types';\n\nexport function useCreateProject() {\n const queryClient = useQueryClient();\n const infiniteKey = useAuthQueryKey([...INFINITE_PROJECTS_QUERY_KEY()]);\n const listKey = useAuthQueryKey([...PROJECTS_QUERY_KEY()]);\n\n return useMutation({\n mutationFn: (data: CreateProjectRequest) =>\n withAction(createProjectAction)(data),\n\n onSuccess: (newProject: Project) => {\n queryClient.setQueriesData<InfiniteData<ProjectsPage>>(\n { queryKey: infiniteKey, exact: false },\n (old) => {\n if (!old?.pages.length) return old;\n return {\n ...old,\n pages: old.pages.map((page, i) =>\n i === 0\n ? { ...page, data: [newProject, ...page.data], total: page.total + 1 }\n : page\n ),\n };\n },\n );\n\n queryClient.setQueriesData<ProjectsPage>(\n { queryKey: listKey, exact: false },\n (old) => {\n if (!old) return old;\n return { ...old, data: [newProject, ...old.data], total: old.total + 1 };\n },\n );\n },\n });\n}\n"],"mappings":";AAEA,SAAS,aAAa,sBAAoC;AAC1D,SAAS,2BAA2B;AACpC,SAAS,kBAAkB;AAC3B,SAAS,uBAAuB;AAChC,SAAS,0BAA0B;AACnC,SAAS,mCAAmC;AAGrC,SAAS,mBAAmB;AACjC,QAAM,cAAc,eAAe;AACnC,QAAM,cAAc,gBAAgB,CAAC,GAAG,4BAA4B,CAAC,CAAC;AACtE,QAAM,UAAU,gBAAgB,CAAC,GAAG,mBAAmB,CAAC,CAAC;AAEzD,SAAO,YAAY;AAAA,IACjB,YAAY,CAAC,SACX,WAAW,mBAAmB,EAAE,IAAI;AAAA,IAEtC,WAAW,CAAC,eAAwB;AAClC,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;AAAA,cAAI,CAAC,MAAM,MAC1B,MAAM,IACF,EAAE,GAAG,MAAM,MAAM,CAAC,YAAY,GAAG,KAAK,IAAI,GAAG,OAAO,KAAK,QAAQ,EAAE,IACnE;AAAA,YACN;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAEA,kBAAY;AAAA,QACV,EAAE,UAAU,SAAS,OAAO,MAAM;AAAA,QAClC,CAAC,QAAQ;AACP,cAAI,CAAC,IAAK,QAAO;AACjB,iBAAO,EAAE,GAAG,KAAK,MAAM,CAAC,YAAY,GAAG,IAAI,IAAI,GAAG,OAAO,IAAI,QAAQ,EAAE;AAAA,QACzE;AAAA,MACF;AAAA,IACF;AAAA,EACF,CAAC;AACH;","names":[]}
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 { PROJECTS_QUERY_KEY } from "./list-projects.hook";
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 infiniteKey = useAuthQueryKey([...INFINITE_PROJECTS_QUERY_KEY()]);
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: infiniteKey, exact: false },
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: listKey, exact: false },
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 { PROJECTS_QUERY_KEY } from './list-projects.hook';\nimport { INFINITE_PROJECTS_QUERY_KEY } from './list-infinite-projects.hook';\nimport type { ProjectsPage } from '../types';\n\nexport function useDeleteProject() {\n const queryClient = useQueryClient();\n const infiniteKey = useAuthQueryKey([...INFINITE_PROJECTS_QUERY_KEY()]);\n const listKey = useAuthQueryKey([...PROJECTS_QUERY_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: infiniteKey, 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: listKey, 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 });\n}\n"],"mappings":";AAEA,SAAS,aAAa,sBAAoC;AAC1D,SAAS,2BAA2B;AACpC,SAAS,kBAAkB;AAC3B,SAAS,uBAAuB;AAChC,SAAS,0BAA0B;AACnC,SAAS,mCAAmC;AAGrC,SAAS,mBAAmB;AACjC,QAAM,cAAc,eAAe;AACnC,QAAM,cAAc,gBAAgB,CAAC,GAAG,4BAA4B,CAAC,CAAC;AACtE,QAAM,UAAU,gBAAgB,CAAC,GAAG,mBAAmB,CAAC,CAAC;AAEzD,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,SAAS,OAAO,MAAM;AAAA,QAClC,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;AAAA,IACF;AAAA,EACF,CAAC;AACH;","names":[]}
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@greatapps/common",
3
- "version": "1.1.499",
3
+ "version": "1.1.500",
4
4
  "description": "Shared library for GreatApps frontend applications",
5
5
  "main": "./dist/index.mjs",
6
6
  "types": "./src/index.ts",
@@ -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 { PROJECTS_QUERY_KEY } from './list-projects.hook';
8
- import { INFINITE_PROJECTS_QUERY_KEY } from './list-infinite-projects.hook';
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 infiniteKey = useAuthQueryKey([...INFINITE_PROJECTS_QUERY_KEY()]);
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.setQueriesData<InfiniteData<ProjectsPage>>(
22
- { queryKey: infiniteKey, exact: false },
23
- (old) => {
24
- if (!old?.pages.length) return old;
25
- return {
26
- ...old,
27
- pages: old.pages.map((page, i) =>
28
- i === 0
29
- ? { ...page, data: [newProject, ...page.data], total: page.total + 1 }
30
- : page
31
- ),
32
- };
33
- },
34
- );
35
-
36
- queryClient.setQueriesData<ProjectsPage>(
37
- { queryKey: listKey, exact: false },
38
- (old) => {
39
- if (!old) return old;
40
- return { ...old, data: [newProject, ...old.data], total: old.total + 1 };
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 { PROJECTS_QUERY_KEY } from './list-projects.hook';
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 infiniteKey = useAuthQueryKey([...INFINITE_PROJECTS_QUERY_KEY()]);
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: infiniteKey, exact: false },
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: listKey, exact: false },
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
  }