@imj_media/tareas 0.0.7
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/.env.template +8 -0
- package/.storybook/main.ts +26 -0
- package/.storybook/preview.ts +15 -0
- package/LICENSE.md +21 -0
- package/README.md +50 -0
- package/core/actions/get_all_users.action.ts +16 -0
- package/core/actions/get_salesman_response.action.ts +16 -0
- package/core/actions/get_tasks_project.action.ts +62 -0
- package/core/actions/get_tasks_response.action.ts +56 -0
- package/eslint.config.js +28 -0
- package/global.d.ts +3 -0
- package/infraestructure/interfaces/salesmans-obp-response.ts +43 -0
- package/infraestructure/interfaces/salesmans-obp.ts +4 -0
- package/infraestructure/interfaces/tasks-campania-response.ts +118 -0
- package/infraestructure/interfaces/tasks-campania.ts +26 -0
- package/infraestructure/interfaces/tasks-kanban-general.ts +25 -0
- package/infraestructure/interfaces/tasks-reponse.ts +111 -0
- package/infraestructure/interfaces/teams-response.ts +7 -0
- package/infraestructure/interfaces/teams.ts +5 -0
- package/infraestructure/interfaces/users-obp-response.ts +52 -0
- package/infraestructure/interfaces/users.ts +5 -0
- package/infraestructure/mappers/all-users-obp.ts +12 -0
- package/infraestructure/mappers/campaign-tasks.ts +35 -0
- package/infraestructure/mappers/kanban-tasks.ts +35 -0
- package/infraestructure/mappers/salesmans-obp.ts +11 -0
- package/infraestructure/mappers/teams.ts +12 -0
- package/package.json +61 -0
- package/postcss.config.js +6 -0
- package/src/components/atoms/Avatar.tsx +14 -0
- package/src/components/atoms/Comment.tsx +33 -0
- package/src/components/atoms/InputSearch.tsx +40 -0
- package/src/components/atoms/SkeletonCard.tsx +17 -0
- package/src/components/atoms/TabButton.tsx +16 -0
- package/src/components/atoms/TooltipUser.tsx +26 -0
- package/src/components/atoms/index.ts +2 -0
- package/src/components/index.ts +3 -0
- package/src/components/kanban-campania/DoneBoard.tsx +12 -0
- package/src/components/kanban-campania/KanbanCampania.tsx +39 -0
- package/src/components/kanban-campania/ToDoBoard.tsx +12 -0
- package/src/components/kanban-campania/WorkingBoard.tsx +13 -0
- package/src/components/kanban-campania/filters.ts +46 -0
- package/src/components/kanban-campania/index.ts +3 -0
- package/src/components/kanban-general/DoneBoard.tsx +12 -0
- package/src/components/kanban-general/KanbanGeneral.tsx +40 -0
- package/src/components/kanban-general/ToDoBoard.tsx +12 -0
- package/src/components/kanban-general/WorkingBoard.tsx +13 -0
- package/src/components/kanban-general/filters.ts +58 -0
- package/src/components/kanban-general/index.ts +3 -0
- package/src/components/layout/FilterButton.tsx +50 -0
- package/src/components/layout/FilterContent.tsx +70 -0
- package/src/components/layout/IndexComponents.tsx +32 -0
- package/src/components/lista-campania/ChildTask.tsx +22 -0
- package/src/components/lista-campania/Date.tsx +30 -0
- package/src/components/lista-campania/ListaCampania.tsx +21 -0
- package/src/components/lista-campania/ParentTask.tsx +57 -0
- package/src/components/molecules/AllComments.tsx +78 -0
- package/src/components/molecules/ButtonAssignUsers.tsx +175 -0
- package/src/components/molecules/DependentTasks.tsx +64 -0
- package/src/components/molecules/Tabs.tsx +39 -0
- package/src/components/molecules/index.ts +1 -0
- package/src/components/organisms/Board.tsx +87 -0
- package/src/components/organisms/Checkbox.tsx +45 -0
- package/src/components/organisms/DetailsTask.tsx +286 -0
- package/src/components/organisms/TabDetailsTask.tsx +39 -0
- package/src/components/organisms/Task.tsx +176 -0
- package/src/components/organisms/index.ts +2 -0
- package/src/components/tasks/PriorityButton.tsx +79 -0
- package/src/components/templates/Layout.tsx +84 -0
- package/src/components/templates/TableList/components/TableList.scss +270 -0
- package/src/components/templates/TableList/components/TableList.tsx +239 -0
- package/src/components/templates/TableList/components/index.tsx +1 -0
- package/src/constants/colors.ts +64 -0
- package/src/constants/gaps.ts +8 -0
- package/src/constants/paddings.ts +8 -0
- package/src/constants/shadows.ts +5 -0
- package/src/context/filtersLayout.context.tsx +118 -0
- package/src/context/kanbanCampania.context.tsx +50 -0
- package/src/context/useApis.context.tsx +47 -0
- package/src/context/userLog.context.tsx +50 -0
- package/src/env.d.ts +10 -0
- package/src/functions/taskCalculations.tsx +818 -0
- package/src/hooks/useAllUsers.ts +18 -0
- package/src/hooks/useCheckTask.tsx +15 -0
- package/src/hooks/useComerciales.ts +58 -0
- package/src/hooks/useDoneTasks.ts +90 -0
- package/src/hooks/useElementPosition.ts +34 -0
- package/src/hooks/useFunctionsTasks.ts +57 -0
- package/src/hooks/useNormalizedData.ts +36 -0
- package/src/hooks/useTeams.ts +19 -0
- package/src/hooks/useToDoTasks.ts +89 -0
- package/src/hooks/useWorkingTasks.ts +85 -0
- package/src/index.css +55 -0
- package/src/index.ts +2 -0
- package/src/index.tsx +1 -0
- package/src/pages/App.tsx +42 -0
- package/src/pages/NoAccessToken.tsx +20 -0
- package/src/pages/NoUser.tsx +20 -0
- package/src/stories/AppTasks.stories.tsx +36 -0
- package/src/stories/Table.stories.tsx +160 -0
- package/src/types/index.ts +107 -0
- package/src/types/interfaces.ts +67 -0
- package/src/types/layout.types.ts +30 -0
- package/src/utils/filters.functions.ts +17 -0
- package/src/utils/formats.ts +33 -0
- package/src/utils/functionsStorybook.tsx +0 -0
- package/src/utils/inputs.functions.ts +25 -0
- package/src/utils/tanstack.functions.ts +19 -0
- package/src/utils/utils.ts +12 -0
- package/src/vite-env.d.ts +1 -0
- package/tailwind.config.js +31 -0
- package/tsconfig.app.json +26 -0
- package/tsconfig.json +7 -0
- package/tsconfig.node.json +24 -0
- package/vite.config.ts +16 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
|
|
2
|
+
export interface IAllUsersOBPResponse {
|
|
3
|
+
id: number;
|
|
4
|
+
nombre: string;
|
|
5
|
+
username: string;
|
|
6
|
+
email: string;
|
|
7
|
+
blocked: boolean;
|
|
8
|
+
siglas: null | string;
|
|
9
|
+
role: Role;
|
|
10
|
+
imagen: Imagen | null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface Imagen {
|
|
14
|
+
id: number;
|
|
15
|
+
name: string;
|
|
16
|
+
formats: Formats;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface Formats {
|
|
20
|
+
large?: Large;
|
|
21
|
+
small: Large;
|
|
22
|
+
medium?: Large;
|
|
23
|
+
thumbnail: Large;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface Large {
|
|
27
|
+
ext: EXT;
|
|
28
|
+
url: string;
|
|
29
|
+
hash: string;
|
|
30
|
+
mime: MIME;
|
|
31
|
+
name: string;
|
|
32
|
+
path: null;
|
|
33
|
+
size: number;
|
|
34
|
+
width: number;
|
|
35
|
+
height: number;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export enum EXT {
|
|
39
|
+
JPEG = ".jpeg",
|
|
40
|
+
Jpg = ".jpg",
|
|
41
|
+
PNG = ".png",
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export enum MIME {
|
|
45
|
+
ImageJPEG = "image/jpeg",
|
|
46
|
+
ImagePNG = "image/png",
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface Role {
|
|
50
|
+
id: number;
|
|
51
|
+
name: string;
|
|
52
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { IAllUsersOBPResponse } from "../interfaces/users-obp-response";
|
|
2
|
+
import { IUsers } from "../interfaces/users";
|
|
3
|
+
|
|
4
|
+
export class AllUsersOBPMapper{
|
|
5
|
+
static toUsers(user:IAllUsersOBPResponse):IUsers{
|
|
6
|
+
return {
|
|
7
|
+
id: user.id,
|
|
8
|
+
name: user.nombre,
|
|
9
|
+
image: user.imagen?.formats?.thumbnail?.url
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { ITasksCampaignResponse } from "../interfaces/tasks-campania-response";
|
|
2
|
+
import { ITasksCampaign } from "../interfaces/tasks-campania";
|
|
3
|
+
import { IUsers } from "../interfaces/users";
|
|
4
|
+
|
|
5
|
+
export class CampaignTasksMapper {
|
|
6
|
+
static toCampaignTasks(task: ITasksCampaignResponse): ITasksCampaign {
|
|
7
|
+
let users: IUsers[] =[];
|
|
8
|
+
if(task?.responsable) users.push({id: task.responsable.id, name: task.responsable.nombre, image: task.responsable?.userOBP?.imagen?.formats?.thumbnail?.url});
|
|
9
|
+
|
|
10
|
+
console.log("task", task)
|
|
11
|
+
//TODO: add map of userTeams
|
|
12
|
+
if(task?.equipo?.miembros) task.equipo.miembros.forEach(miembro => {
|
|
13
|
+
if(users?.find(user => user.id === miembro.idUser.id)) return;
|
|
14
|
+
if(miembro.idUser) users.push({id: miembro.idUser.id, name: miembro.idUser.nombre, image: miembro?.userOBP?.imagen?.formats?.thumbnail?.url});
|
|
15
|
+
});
|
|
16
|
+
return {
|
|
17
|
+
id: task?.id ?? null,
|
|
18
|
+
task: task?.texto_corto ?? null,
|
|
19
|
+
status: task?.estatus ?? null,
|
|
20
|
+
difficulty: task?.nivel_dificultad ?? null,
|
|
21
|
+
priority: task?.prioridad ?? 0,
|
|
22
|
+
endDate: new Date(task?.ffin ?? null),
|
|
23
|
+
startDate: new Date(task?.finicio ?? null),
|
|
24
|
+
mediumName: task?.nombre_medio?.nombre ?? null,
|
|
25
|
+
users: users,
|
|
26
|
+
repeatsToDo: task?.repeticiones ?? null,
|
|
27
|
+
repeats: task?.repeticiones_realizadas ?? null,
|
|
28
|
+
comments: task?.comentarios?.length ?? null,
|
|
29
|
+
willBePaused: task?.es_pausable,
|
|
30
|
+
statusRequiredTask: task?.id_t_requerida?.estatus ?? null,
|
|
31
|
+
nameRequiredTask: task?.id_t_requerida?.texto_corto ?? null,
|
|
32
|
+
tasks: task?.tareas?.map(childTask => ({id: childTask.id, name: childTask.texto_corto, status: childTask.estatus})) ?? null,
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { TasksKanbanGeneral } from "../interfaces/tasks-kanban-general";
|
|
2
|
+
import { ITasksResponse } from "../interfaces/tasks-reponse";
|
|
3
|
+
import { IUsers } from "../interfaces/users";
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
export class KanbanTasksMapper {
|
|
7
|
+
static toKanbanTasks(task: ITasksResponse): TasksKanbanGeneral {
|
|
8
|
+
let users: IUsers[] =[];
|
|
9
|
+
|
|
10
|
+
if(task?.responsable) users.push({id: task.responsable.id, name: task.responsable.nombre, image: task.responsable?.userOBP?.imagen?.formats?.thumbnail?.url});
|
|
11
|
+
|
|
12
|
+
//TODO: add map of userTeams
|
|
13
|
+
if(task?.equipo?.miembros) task.equipo.miembros.forEach(miembro => {
|
|
14
|
+
if(users?.find(user => user.id === miembro.idUser.id)) return;
|
|
15
|
+
if(miembro.idUser) users.push({id: miembro.idUser.id, name: miembro.idUser.nombre, image: miembro?.userOBP?.imagen?.formats?.thumbnail?.url});
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
return {
|
|
19
|
+
comments:0,
|
|
20
|
+
nameProject: task?.proyecto?.nombre,
|
|
21
|
+
idProject: task?.proyecto?.id,
|
|
22
|
+
willBePaused: task.es_pausable,
|
|
23
|
+
difficulty: task.nivel_dificultad,
|
|
24
|
+
endDate: new Date(task.ffin),
|
|
25
|
+
id: task.id,
|
|
26
|
+
priority: task.prioridad ?? 0,
|
|
27
|
+
status: task.estatus,
|
|
28
|
+
task: task.texto_corto,
|
|
29
|
+
users: users,
|
|
30
|
+
responsible: task?.responsable?.id ?? null,
|
|
31
|
+
statusRequiredTask: task.id_t_requerida?.estatus ?? null,
|
|
32
|
+
nameRequiredTask: task.id_t_requerida?.texto_corto ?? null,
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { ISalesmanUsers } from "../interfaces/salesmans-obp";
|
|
2
|
+
import { SalesmansOBPResponse } from "../interfaces/salesmans-obp-response";
|
|
3
|
+
|
|
4
|
+
export class SalesmanUsersMapper {
|
|
5
|
+
static toSalesmanUsers(salesman: SalesmansOBPResponse): ISalesmanUsers {
|
|
6
|
+
return {
|
|
7
|
+
id: salesman.id,
|
|
8
|
+
name: salesman.nombre,
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ITeam } from "../interfaces/teams";
|
|
2
|
+
import { ITeamsResponse } from "../interfaces/teams-response";
|
|
3
|
+
|
|
4
|
+
export class TeamsMapper {
|
|
5
|
+
static toDomain(data: ITeamsResponse): ITeam {
|
|
6
|
+
return {
|
|
7
|
+
id: data.id,
|
|
8
|
+
name: data.nombre,
|
|
9
|
+
color: data.color
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@imj_media/tareas",
|
|
3
|
+
"private": false,
|
|
4
|
+
"version": "0.0.7",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "vite",
|
|
8
|
+
"build-old": "rollup -c",
|
|
9
|
+
"build": "tsc && vite build",
|
|
10
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
11
|
+
"storybook": "storybook dev -p 6006",
|
|
12
|
+
"build-storybook": "build-storybook"
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"@imj_media/imj-ui": "^1.0.18",
|
|
16
|
+
"@imj_media/tasks-modules": "^2.0.0",
|
|
17
|
+
"@tailwindcss/line-clamp": "^0.4.4",
|
|
18
|
+
"@tanstack/react-query": "^5.62.3",
|
|
19
|
+
"axios": "^1.7.9",
|
|
20
|
+
"date-fns": "^4.1.0",
|
|
21
|
+
"qs": "^6.13.1",
|
|
22
|
+
"react": "^18.3.1",
|
|
23
|
+
"react-dom": "^18.3.1",
|
|
24
|
+
"react-hook-form": "^7.54.1"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@chromatic-com/storybook": "^3.2.2",
|
|
28
|
+
"@eslint/js": "^9.15.0",
|
|
29
|
+
"@storybook/addon-essentials": "^8.4.6",
|
|
30
|
+
"@storybook/addon-interactions": "^8.4.6",
|
|
31
|
+
"@storybook/addon-onboarding": "^8.4.6",
|
|
32
|
+
"@storybook/addon-styling-webpack": "^1.0.1",
|
|
33
|
+
"@storybook/blocks": "^8.4.6",
|
|
34
|
+
"@storybook/react": "^8.4.6",
|
|
35
|
+
"@storybook/react-vite": "^8.4.6",
|
|
36
|
+
"@storybook/test": "^8.4.6",
|
|
37
|
+
"@types/node": "^22.10.1",
|
|
38
|
+
"@types/react": "^18.3.12",
|
|
39
|
+
"@types/react-dom": "^18.3.1",
|
|
40
|
+
"@vitejs/plugin-react": "^4.3.4",
|
|
41
|
+
"@vitejs/plugin-react-swc": "^3.5.0",
|
|
42
|
+
"autoprefixer": "^10.4.20",
|
|
43
|
+
"eslint": "^9.15.0",
|
|
44
|
+
"eslint-plugin-react-hooks": "^5.0.0",
|
|
45
|
+
"eslint-plugin-react-refresh": "^0.4.14",
|
|
46
|
+
"eslint-plugin-storybook": "^0.11.1",
|
|
47
|
+
"globals": "^15.12.0",
|
|
48
|
+
"postcss": "^8.4.49",
|
|
49
|
+
"storybook": "^8.4.6",
|
|
50
|
+
"tailwindcss": "^3.4.16",
|
|
51
|
+
"typescript": "~5.6.2",
|
|
52
|
+
"typescript-eslint": "^8.15.0",
|
|
53
|
+
"vite": "^6.0.1",
|
|
54
|
+
"vite-plugin-css-injected-by-js": "^3.5.2"
|
|
55
|
+
},
|
|
56
|
+
"publishConfig": {
|
|
57
|
+
"access": "public",
|
|
58
|
+
"registry": "https://registry.npmjs.org/@imjmedia"
|
|
59
|
+
},
|
|
60
|
+
"gitHead": "72741a44222d9aaa34fdab7511035ceb4c989c6a"
|
|
61
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { IAvatar } from "../../types/interfaces";
|
|
2
|
+
|
|
3
|
+
const Avatar = ({imageUrl = "https://www.uv.unach.mx/assets/img/prueba/directorio/userM.png", alt = "avatar"}: IAvatar) => {
|
|
4
|
+
|
|
5
|
+
return (
|
|
6
|
+
<img
|
|
7
|
+
src={imageUrl === "" ? "https://www.uv.unach.mx/assets/img/prueba/directorio/userM.png" : imageUrl}
|
|
8
|
+
alt={alt ?? "avatar"}
|
|
9
|
+
className="w-[28px] h-[28px] rounded-full bg-containers border-2 border-white"
|
|
10
|
+
/>
|
|
11
|
+
)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export default Avatar
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
import { Avatar, AvatarFallback, AvatarImage } from '@imj_media/imj-ui';
|
|
3
|
+
import { formatDistanceToNow, parseISO } from 'date-fns';
|
|
4
|
+
import { es } from 'date-fns/locale';
|
|
5
|
+
|
|
6
|
+
import { CommentProps } from '../../types';
|
|
7
|
+
|
|
8
|
+
const URL_IMAGES = 'https://devobp.imjmedia.com.mx';
|
|
9
|
+
|
|
10
|
+
export default function Comment({ comment }: CommentProps) {
|
|
11
|
+
function formatDate(dateString: string) {
|
|
12
|
+
const date = parseISO(dateString); // Convertir la cadena en un objeto Date
|
|
13
|
+
return formatDistanceToNow(date, { locale: es, addSuffix: true });
|
|
14
|
+
}
|
|
15
|
+
return (
|
|
16
|
+
<div>
|
|
17
|
+
<div className="flex flex-row gap-2 items-start">
|
|
18
|
+
<Avatar className="w-8 h-8">
|
|
19
|
+
<AvatarImage
|
|
20
|
+
src={`${URL_IMAGES}${comment?.autor?.imagen?.formats?.thumbnail?.url}`}
|
|
21
|
+
alt="@shadcn"
|
|
22
|
+
/>
|
|
23
|
+
<AvatarFallback>CN</AvatarFallback>
|
|
24
|
+
</Avatar>
|
|
25
|
+
<p className="text-sm text-neutral-900 font-medium">{comment.autor.nombre}</p>
|
|
26
|
+
<span className="text-xs text-neutral-600 font-normal mt-[2px]">
|
|
27
|
+
{formatDate(comment.createdAt)}
|
|
28
|
+
</span>
|
|
29
|
+
</div>
|
|
30
|
+
<p className="text-sm text-neutral-600 font-normal pl-10">{comment.comentario}</p>
|
|
31
|
+
</div>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Icons } from "@imj_media/tasks-modules"
|
|
2
|
+
import { useState } from "react"
|
|
3
|
+
import Avatar from "./Avatar"
|
|
4
|
+
|
|
5
|
+
const InputSearch = ({options, onselect}: {options: any[], onselect: (option: any)=>void}) => {
|
|
6
|
+
const [search, setSearch] = useState<string | null>(null)
|
|
7
|
+
const idSearch = Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15)
|
|
8
|
+
const filteredOptions = [...options].filter((option: any)=> option.name.toLowerCase().includes(search?.toLowerCase() ?? ""))
|
|
9
|
+
return (
|
|
10
|
+
<div className="h-fit w-full min-w-max">
|
|
11
|
+
<div className="relative flex items-center">
|
|
12
|
+
<input
|
|
13
|
+
id={idSearch}
|
|
14
|
+
value={search ?? ""}
|
|
15
|
+
onChange={(e)=>setSearch(e.target.value)}
|
|
16
|
+
type="text"
|
|
17
|
+
className="w-full h-[32px] bg-bg-card border-cards rounded-lg p-2"
|
|
18
|
+
placeholder="Buscar"
|
|
19
|
+
/>
|
|
20
|
+
<div className="absolute right-[8px]">
|
|
21
|
+
<Icons icon="search" size="xs" strokeWidth={3}/>
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
|
24
|
+
|
|
25
|
+
<div className="bg-bg-card border-cards rounded-lg p-2 flex flex-col gap-s overflow-y-auto max-h-[120px] min-w-max">
|
|
26
|
+
{filteredOptions.map((option: any)=>(
|
|
27
|
+
<button onClick={()=>{
|
|
28
|
+
onselect(option)
|
|
29
|
+
}} className="flex gap-s items-center max-w-[200px] min-w-full hover:bg-primary-light cursor-pointer">
|
|
30
|
+
<Avatar imageUrl={option.image} alt={option.name}/>
|
|
31
|
+
<p className="text-xs font-bold content-text">{option.name}</p>
|
|
32
|
+
</button>
|
|
33
|
+
))}
|
|
34
|
+
</div>
|
|
35
|
+
|
|
36
|
+
</div>
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export default InputSearch
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const SkeletonCard = () => {
|
|
2
|
+
return (
|
|
3
|
+
<div className='bg-gray-200 h-[150px] w-full animate-pulse rounded-md flex flex-col gap-[50px] p-4'>
|
|
4
|
+
<div className="flex h-[30px] gap-3">
|
|
5
|
+
<div className="w-[30px] min-w-[30px] h-full bg-gray-300 rounded-full"/>
|
|
6
|
+
<div className="w-full h-full bg-gray-300"/>
|
|
7
|
+
<div className="w-[30px] min-w-[30px] h-full bg-gray-300 rounded-full"/>
|
|
8
|
+
</div>
|
|
9
|
+
<div className='flex justify-between w-full h-[30px] gap-[50px]'>
|
|
10
|
+
<div className='w-full h-[30px] bg-gray-300'/>
|
|
11
|
+
<div className='w-full h-[30px] bg-gray-300'/>
|
|
12
|
+
</div>
|
|
13
|
+
</div>
|
|
14
|
+
)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export default SkeletonCard
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
interface ITabButtonProps {
|
|
4
|
+
label: string;
|
|
5
|
+
active: boolean;
|
|
6
|
+
onClick: () => void;
|
|
7
|
+
}
|
|
8
|
+
const TabButton = ({label, active, onClick}: ITabButtonProps) => {
|
|
9
|
+
return (
|
|
10
|
+
<button onClick={onClick} className={`${active ? "text-texts-enfasis border-b-2 border-texts-enfasis" : "text-texts-subtext"} text-xs font-semibold `}>
|
|
11
|
+
{label}
|
|
12
|
+
</button>
|
|
13
|
+
)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export default TabButton
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Tooltip } from "@imj_media/tasks-modules"
|
|
2
|
+
import { IUsers } from "../../../infraestructure/interfaces/users";
|
|
3
|
+
import Avatar from "./Avatar"
|
|
4
|
+
|
|
5
|
+
interface TooltipUserProps {
|
|
6
|
+
trigger: React.ReactNode;
|
|
7
|
+
user: Partial<IUsers>;
|
|
8
|
+
showUser?: boolean;
|
|
9
|
+
}
|
|
10
|
+
const TooltipUser = ({
|
|
11
|
+
trigger,
|
|
12
|
+
user,
|
|
13
|
+
showUser = true
|
|
14
|
+
}: TooltipUserProps) => {
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<Tooltip dispatch={trigger}>
|
|
18
|
+
{showUser && <div className="flex items-center gap-2 bg-containers w-fit h-fit p-2 rounded-lg border-containers">
|
|
19
|
+
<Avatar imageUrl={user.image ?? ""} alt={user.name} />
|
|
20
|
+
<p className="text-xs font-bold content-text">{user.name}</p>
|
|
21
|
+
</div>}
|
|
22
|
+
</Tooltip>
|
|
23
|
+
)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export default TooltipUser
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import useDoneTasks from '../../hooks/useDoneTasks';
|
|
2
|
+
import { fetchNextPage } from '../../utils/tanstack.functions';
|
|
3
|
+
import { Board } from '../organisms';
|
|
4
|
+
|
|
5
|
+
const DoneBoard = () => {
|
|
6
|
+
const {doneTasks, total} = useDoneTasks();
|
|
7
|
+
|
|
8
|
+
const tasks = [...(doneTasks?.data?.pages?.flat() ?? [])]
|
|
9
|
+
return <Board title='Completadas' tasks={tasks} loadNextPage={()=>fetchNextPage(doneTasks)} isFetching={doneTasks?.isFetching} total={total}/>
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export default DoneBoard
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { useEffect } from "react";
|
|
2
|
+
import { useFiltersLayoutContext } from "../../context/filtersLayout.context";
|
|
3
|
+
import { filtersToShow } from "./filters";
|
|
4
|
+
import { useInfoCampania } from "../../context/kanbanCampania.context";
|
|
5
|
+
import { Board } from "../organisms";
|
|
6
|
+
|
|
7
|
+
const KanbanCampania = () => {
|
|
8
|
+
const { defineFields } = useFiltersLayoutContext();
|
|
9
|
+
const {tasksProject} = useInfoCampania();
|
|
10
|
+
|
|
11
|
+
//* fetch fields to show in the filters
|
|
12
|
+
useEffect(()=>{
|
|
13
|
+
const fetchFields = async () => {
|
|
14
|
+
let fields = [...filtersToShow];
|
|
15
|
+
defineFields(fields)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
fetchFields()
|
|
19
|
+
},[])
|
|
20
|
+
|
|
21
|
+
let tasks = tasksProject?.data?.reduce((acc: any, task: any) => {
|
|
22
|
+
acc[task.status] = acc[task.status] || []
|
|
23
|
+
acc[task.status].push(task)
|
|
24
|
+
return acc
|
|
25
|
+
}, {})
|
|
26
|
+
|
|
27
|
+
const Boards = ["Por hacer", "Trabajando", "Completadas"]
|
|
28
|
+
return (
|
|
29
|
+
<div className="flex gap-4 w-full h-full min-h-[100%] relative " >
|
|
30
|
+
{Boards.map((board, index)=>{
|
|
31
|
+
return (
|
|
32
|
+
<Board total={tasks?.[index]?.length ?? 0} title={board} tasks={tasks?.[index] ?? []} isFetching={tasksProject?.isFetching} isLoadingData={tasksProject?.isLoading} />
|
|
33
|
+
)
|
|
34
|
+
})}
|
|
35
|
+
</div>
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export default KanbanCampania
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import useToDoTasks from '../../hooks/useToDoTasks';
|
|
2
|
+
import { fetchNextPage } from '../../utils/tanstack.functions';
|
|
3
|
+
import { Board } from '../organisms';
|
|
4
|
+
|
|
5
|
+
const ToDoBoard = () => {
|
|
6
|
+
const {toDoTasks, total} = useToDoTasks();
|
|
7
|
+
|
|
8
|
+
const tasks = [...(toDoTasks?.data?.pages?.flat() ?? [])]
|
|
9
|
+
return <Board total={total} title='Por hacer' tasks={tasks} loadNextPage={()=>fetchNextPage(toDoTasks)} isFetching={toDoTasks?.isFetching} isLoadingData={toDoTasks?.isLoading}/>
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export default ToDoBoard
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import useWorkingTasks from "../../hooks/useWorkingTasks";
|
|
2
|
+
import { fetchNextPage } from "../../utils/tanstack.functions";
|
|
3
|
+
import { Board } from "../organisms";
|
|
4
|
+
|
|
5
|
+
const WorkingBoard = () => {
|
|
6
|
+
|
|
7
|
+
const {workingTasks, total} = useWorkingTasks();
|
|
8
|
+
|
|
9
|
+
const tasks = [...(workingTasks?.data?.pages?.flat() ?? [])]
|
|
10
|
+
return <Board total={total} title='Trabajando' tasks={tasks} loadNextPage={()=>fetchNextPage(workingTasks)} isFetching={workingTasks?.isFetching}/>
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export default WorkingBoard
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export const filtersToShow = [
|
|
2
|
+
{
|
|
3
|
+
label: "Tipo de dependencia",
|
|
4
|
+
type: "select",
|
|
5
|
+
id: "dependency",
|
|
6
|
+
options: [{
|
|
7
|
+
id:"con",
|
|
8
|
+
name: "Con dependencia"
|
|
9
|
+
},{
|
|
10
|
+
id:"sin",
|
|
11
|
+
name: "Sin dependencia"
|
|
12
|
+
}
|
|
13
|
+
]
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
label: "Por responsable",
|
|
17
|
+
type: "select",
|
|
18
|
+
id: "owner.config",
|
|
19
|
+
options: [
|
|
20
|
+
{id:"yo", name:"Mis tareas"},
|
|
21
|
+
{id:"yo_y_mis_equipos", name:"Tareas mías y de mis equipos"},
|
|
22
|
+
{id:"mis_equipos", name:"Tareas de mis equipos"}
|
|
23
|
+
]
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
label: "Por dificultad",
|
|
27
|
+
type: "select",
|
|
28
|
+
id: "difficulty",
|
|
29
|
+
options: [{
|
|
30
|
+
id:0,
|
|
31
|
+
name: "Muy facil"
|
|
32
|
+
},{
|
|
33
|
+
id:1,
|
|
34
|
+
name: "Facil"
|
|
35
|
+
},{
|
|
36
|
+
id:2,
|
|
37
|
+
name: "Media"
|
|
38
|
+
},{
|
|
39
|
+
id:3,
|
|
40
|
+
name: "Dificil"
|
|
41
|
+
},{
|
|
42
|
+
id:4,
|
|
43
|
+
name: "Muy dificil"
|
|
44
|
+
}]
|
|
45
|
+
}
|
|
46
|
+
]
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import useDoneTasks from '../../hooks/useDoneTasks';
|
|
2
|
+
import { fetchNextPage } from '../../utils/tanstack.functions';
|
|
3
|
+
import { Board } from '../organisms';
|
|
4
|
+
|
|
5
|
+
const DoneBoard = () => {
|
|
6
|
+
const {doneTasks, total} = useDoneTasks({});
|
|
7
|
+
|
|
8
|
+
const tasks = [...(doneTasks?.data?.pages?.flat() ?? [])]
|
|
9
|
+
return <Board title='Completadas' tasks={tasks} loadNextPage={()=>fetchNextPage(doneTasks)} isFetching={doneTasks?.isFetching} total={total}/>
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export default DoneBoard
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { useEffect } from "react";
|
|
2
|
+
//* Components
|
|
3
|
+
import { DoneBoard, ToDoBoard, WorkingBoard } from "../kanban-general";
|
|
4
|
+
//* Context
|
|
5
|
+
import { useFiltersLayoutContext } from "../../context/filtersLayout.context";
|
|
6
|
+
//* Hooks
|
|
7
|
+
import useSalesmanUsers from "../../hooks/useComerciales";
|
|
8
|
+
//* Filters
|
|
9
|
+
import { filtersToShow } from "./filters";
|
|
10
|
+
|
|
11
|
+
const KanbanGeneral = () => {
|
|
12
|
+
const { defineFields } = useFiltersLayoutContext();
|
|
13
|
+
const {getSalesmanAndProjects} = useSalesmanUsers()
|
|
14
|
+
|
|
15
|
+
//* fetch fields to show in the filters
|
|
16
|
+
useEffect(()=>{
|
|
17
|
+
const fetchFields = async () => {
|
|
18
|
+
let fields = [...filtersToShow];
|
|
19
|
+
let {salesmanUsers, projects} = await getSalesmanAndProjects();
|
|
20
|
+
|
|
21
|
+
defineFields(fields.map(field=>{
|
|
22
|
+
if(field.id === "project.id"){ field.options = projects }
|
|
23
|
+
if(field.id === "salesman.name"){ field.options = salesmanUsers }
|
|
24
|
+
return field
|
|
25
|
+
}))
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
fetchFields()
|
|
29
|
+
},[])
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<div className="flex gap-4 w-full h-full min-h-[100%] relative " >
|
|
33
|
+
<ToDoBoard/>
|
|
34
|
+
<WorkingBoard/>
|
|
35
|
+
<DoneBoard/>
|
|
36
|
+
</div>
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export default KanbanGeneral
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import useToDoTasks from '../../hooks/useToDoTasks';
|
|
2
|
+
import { fetchNextPage } from '../../utils/tanstack.functions';
|
|
3
|
+
import { Board } from '../organisms';
|
|
4
|
+
|
|
5
|
+
const ToDoBoard = () => {
|
|
6
|
+
const {toDoTasks, total} = useToDoTasks();
|
|
7
|
+
|
|
8
|
+
const tasks = [...(toDoTasks?.data?.pages?.flat() ?? [])]
|
|
9
|
+
return <Board total={total} title='Por hacer' tasks={tasks} loadNextPage={()=>fetchNextPage(toDoTasks)} isFetching={toDoTasks?.isFetching} isLoadingData={toDoTasks?.isLoading}/>
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export default ToDoBoard
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import useWorkingTasks from "../../hooks/useWorkingTasks";
|
|
2
|
+
import { fetchNextPage } from "../../utils/tanstack.functions";
|
|
3
|
+
import { Board } from "../organisms";
|
|
4
|
+
|
|
5
|
+
const WorkingBoard = () => {
|
|
6
|
+
|
|
7
|
+
const {workingTasks, total} = useWorkingTasks({});
|
|
8
|
+
|
|
9
|
+
const tasks = [...(workingTasks?.data?.pages?.flat() ?? [])]
|
|
10
|
+
return <Board total={total} title='Trabajando' tasks={tasks} loadNextPage={()=>fetchNextPage(workingTasks)} isFetching={workingTasks?.isFetching}/>
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export default WorkingBoard
|