@confidencesystemsinc/sdk 1.2.0 → 1.2.1
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/components/playbook/confidence-playbook.d.ts +12 -5
- package/dist/components/playbook/playbook-header.d.ts +3 -1
- package/dist/components/playbook-button/ConfidencePlaybookButton.d.ts +2 -1
- package/dist/components/task/confidence-task.d.ts +10 -3
- package/dist/components/task/task-buttons.d.ts +2 -1
- package/dist/components/task/task-expanded-content.d.ts +3 -0
- package/dist/components/ui/button.d.ts +1 -0
- package/dist/confidence_logo.png +0 -0
- package/dist/constants/settings.constants.d.ts +2 -2
- package/dist/context/confidence-context.d.ts +10 -0
- package/dist/hooks/task/useTaskDetails.d.ts +173 -0
- package/dist/hooks/usePlaybookExpandedTasks.d.ts +4 -0
- package/dist/hooks/useTaskButtons.d.ts +2 -1
- package/dist/index.cjs +15 -15
- package/dist/index.js +3596 -3336
- package/dist/services/task-details.service.d.ts +4 -0
- package/dist/stories/confidence-task.stories.d.ts +50 -0
- package/dist/theme.css +1 -1
- package/package.json +3 -2
- package/src/components/badge.tsx +116 -0
- package/src/components/initiate-playbook-modal/InitiatePlaybookModal.tsx +53 -0
- package/src/components/playbook/confidence-playbook.tsx +309 -0
- package/src/components/playbook/playbook-header.tsx +34 -0
- package/src/components/playbook-button/ConfidencePlaybookButton.tsx +79 -0
- package/src/components/task/confidence-task.tsx +297 -0
- package/src/components/task/task-buttons.tsx +35 -0
- package/src/components/task/task-dropdown-badge.tsx +121 -0
- package/src/components/task/task-expanded-content.tsx +46 -0
- package/src/components/task/task-left-panel.tsx +60 -0
- package/src/components/task/task-status-badge.tsx +23 -0
- package/src/components/ui/button.tsx +272 -0
- package/src/components/ui/header.tsx +12 -0
- package/src/components/ui/input.tsx +39 -0
- package/src/components/ui/modal.tsx +88 -0
- package/src/components/ui/ui-wrapper.tsx +7 -0
- package/src/constants/settings.constants.ts +4 -0
- package/src/context/confidence-context.tsx +25 -0
- package/src/hooks/task/useCompleteTask.ts +32 -0
- package/src/hooks/task/useStartTask.ts +35 -0
- package/src/hooks/task/useTaskDetails.ts +42 -0
- package/src/hooks/usePlaybook.ts +54 -0
- package/src/hooks/usePlaybookActions.ts +69 -0
- package/src/hooks/usePlaybookExpandedTasks.ts +35 -0
- package/src/hooks/useTaskButtons.ts +47 -0
- package/src/index.ts +7 -0
- package/src/services/complete-task.service.ts +25 -0
- package/src/services/initiate-playbook.service.ts +26 -0
- package/src/services/start-task.services.ts +27 -0
- package/src/services/task-details.service.ts +17 -0
- package/src/stories/confidence-playbook.stories.tsx +124 -0
- package/src/stories/confidence-task.stories.tsx +63 -0
- package/src/stories/initiate-playbook-modal.stories.tsx +31 -0
- package/src/stories/modal.stories.tsx +50 -0
- package/src/task-description.css +629 -0
- package/src/theme.css +11 -0
- package/src/types/playbook.types.ts +22 -0
- package/src/types/task.types.ts +20 -0
- package/src/utils/cn.ts +6 -0
- package/src/vite-env.d.ts +1 -0
- /package/dist/hooks/{task-events → task}/useCompleteTask.d.ts +0 -0
- /package/dist/hooks/{task-events → task}/useStartTask.d.ts +0 -0
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ReactNode,
|
|
3
|
+
ButtonHTMLAttributes,
|
|
4
|
+
CSSProperties,
|
|
5
|
+
forwardRef,
|
|
6
|
+
} from "react";
|
|
7
|
+
import { twMerge } from "tailwind-merge";
|
|
8
|
+
/*
|
|
9
|
+
DOCS: https://tailwindcss.com/docs/content-configuration#class-detection-in-depth
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const ButtonBase =
|
|
13
|
+
"whitespace-nowrap font-normal flex items-center justify-center";
|
|
14
|
+
const ButtonFocus =
|
|
15
|
+
"focus:outline focus:outline-3 focus:outline-primary-500/[.3]";
|
|
16
|
+
const ButtonDisabled = "disabled:opacity-15 disabled:pointer-events-none";
|
|
17
|
+
|
|
18
|
+
const isLight = (color: string) => {
|
|
19
|
+
return Boolean(color === "grayLight" || color === "warning");
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const handleNoneColor = (fn: () => string, color?: string) => {
|
|
23
|
+
if (!color || color === "none") {
|
|
24
|
+
return "";
|
|
25
|
+
}
|
|
26
|
+
return fn();
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const buttonColorBase = (color: string, active: boolean) =>
|
|
30
|
+
handleNoneColor(() => {
|
|
31
|
+
const useColor = color === "gray400" ? "gray" : color;
|
|
32
|
+
const textColor = isLight(useColor)
|
|
33
|
+
? "active:text-black"
|
|
34
|
+
: "active:text-white";
|
|
35
|
+
if (color === "primary") {
|
|
36
|
+
return active
|
|
37
|
+
? `bg-${useColor}-800 text-white`
|
|
38
|
+
: `hover:bg-${useColor}-800 active:bg-${useColor}-800 hover:text-white ${textColor}`;
|
|
39
|
+
} else if (useColor === "grayDark") {
|
|
40
|
+
return active
|
|
41
|
+
? "bg-gray-800 text-white"
|
|
42
|
+
: `hover:bg-gray-800 active:bg-gray-800 hover:text-white ${textColor}`;
|
|
43
|
+
} else if (useColor === "grayLight") {
|
|
44
|
+
return active
|
|
45
|
+
? "bg-gray-50 text-black"
|
|
46
|
+
: `hover:bg-gray-50 active:bg-gray-50 hover:text-black ${textColor}`;
|
|
47
|
+
} else if (color === "gray400") {
|
|
48
|
+
return active
|
|
49
|
+
? `bg-${useColor}-500 text-white`
|
|
50
|
+
: `hover:bg-${useColor}-500 active:bg-${useColor}-500 hover:text-white ${textColor}`;
|
|
51
|
+
}
|
|
52
|
+
return active
|
|
53
|
+
? `bg-${useColor}-700 text-white`
|
|
54
|
+
: `hover:bg-${useColor}-600 active:bg-${useColor}-700 hover:text-white ${textColor}`;
|
|
55
|
+
}, color);
|
|
56
|
+
|
|
57
|
+
const buttonColorSolid = (color: string) =>
|
|
58
|
+
handleNoneColor(() => {
|
|
59
|
+
const textColor = isLight(color) ? "text-black" : "text-white";
|
|
60
|
+
if (color === "primary") {
|
|
61
|
+
return `bg-primary-600 ${textColor}`;
|
|
62
|
+
} else if (color === "gray400") {
|
|
63
|
+
return `bg-gray-400 ${textColor}`;
|
|
64
|
+
}
|
|
65
|
+
return `bg-${color}-500 ${textColor}`;
|
|
66
|
+
}, color);
|
|
67
|
+
|
|
68
|
+
const buttonColorLink = (color: string) => {
|
|
69
|
+
const classes: Record<string, string> = {
|
|
70
|
+
dark: "text-dark hover:text-dark",
|
|
71
|
+
yellow: "text-yellow hover:text-yellow",
|
|
72
|
+
gray: "text-gray-500 hover:text-gray-800",
|
|
73
|
+
primary: "text-primary-600 hover:text-primary-800",
|
|
74
|
+
secondary: "text-secondary-500 hover:text-secondary-800",
|
|
75
|
+
success: "text-success-500 hover:text-success-800",
|
|
76
|
+
info: "text-info-500 hover:text-info-800",
|
|
77
|
+
warning: "text-warning-500 hover:text-warning-800",
|
|
78
|
+
danger: "text-danger-500 hover:text-danger-800",
|
|
79
|
+
steel: "text-steel-500 hover:text-steel-800",
|
|
80
|
+
orange: "text-orange-500 hover:text-orange-800",
|
|
81
|
+
purple: "text-purple-500 hover:text-purple-800",
|
|
82
|
+
"fiserv-orange": "text-fiserv-orange-500 hover:text-fiserv-orange-800",
|
|
83
|
+
grayDark: "text-gray-800 hover:text-gray-900",
|
|
84
|
+
grayLight: "text-gray-50 hover:text-gray-400",
|
|
85
|
+
gray400: "text-gray-400 hover:text-gray-700",
|
|
86
|
+
};
|
|
87
|
+
return (color && classes[color]) || "";
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const buttonColorLinkHoverSolid = (color: string, active: boolean) => {
|
|
91
|
+
const classes: Record<string, string> = {
|
|
92
|
+
dark: active
|
|
93
|
+
? "bg-dark text-white"
|
|
94
|
+
: "text-dark hover:bg-dark hover:text-white",
|
|
95
|
+
yellow: active
|
|
96
|
+
? "bg-yellow text-white"
|
|
97
|
+
: "text-yellow hover:bg-yellow hover:text-white",
|
|
98
|
+
gray: active
|
|
99
|
+
? "bg-gray-500 text-white"
|
|
100
|
+
: "text-gray-500 hover:bg-gray-500 hover:text-white",
|
|
101
|
+
primary: active
|
|
102
|
+
? "bg-primary-800 text-white"
|
|
103
|
+
: "text-primary-600 hover:bg-primary-800 hover:text-white",
|
|
104
|
+
secondary: active
|
|
105
|
+
? "bg-secondary-500 text-white"
|
|
106
|
+
: "text-secondary-500 hover:bg-secondary-500 hover:text-white",
|
|
107
|
+
success: active
|
|
108
|
+
? "bg-success-500 text-white"
|
|
109
|
+
: "text-success-500 hover:bg-success-500 hover:text-white",
|
|
110
|
+
info: active
|
|
111
|
+
? "bg-info-500 text-white"
|
|
112
|
+
: "text-info-500 hover:bg-info-500 hover:text-white",
|
|
113
|
+
warning: active
|
|
114
|
+
? "bg-warning-500 text-white"
|
|
115
|
+
: "text-warning-500 hover:bg-warning-500 hover:text-white",
|
|
116
|
+
danger: active
|
|
117
|
+
? "bg-danger-500 text-white"
|
|
118
|
+
: "text-danger-500 hover:bg-danger-500 hover:text-white",
|
|
119
|
+
steel: active
|
|
120
|
+
? "bg-steel-500 text-white"
|
|
121
|
+
: "text-steel-500 hover:bg-steel-500 hover:text-white",
|
|
122
|
+
orange: active
|
|
123
|
+
? "bg-orange-500 text-white"
|
|
124
|
+
: "text-orange-500 hover:bg-orange-500 hover:text-white",
|
|
125
|
+
purple: active
|
|
126
|
+
? "bg-purple-500 text-white"
|
|
127
|
+
: "text-purple-500 hover:bg-purple-500 hover:text-white",
|
|
128
|
+
"fiserv-orange": active
|
|
129
|
+
? "bg-fiserv-orange-500 text-white"
|
|
130
|
+
: "text-fiserv-orange-500 hover:bg-fiserv-orange-500 hover:text-white",
|
|
131
|
+
grayDark: active
|
|
132
|
+
? "bg-gray-900 text-white"
|
|
133
|
+
: "text-gray-800 hover:bg-gray-900 hover:text-white",
|
|
134
|
+
grayLight: active
|
|
135
|
+
? "bg-gray-400 text-white"
|
|
136
|
+
: "text-gray-50 hover:bg-gray-400 hover:text-white",
|
|
137
|
+
gray400: active
|
|
138
|
+
? "bg-gray-500 text-white"
|
|
139
|
+
: "text-gray-400 hover:bg-gray-500 hover:text-white",
|
|
140
|
+
};
|
|
141
|
+
return (color && classes[color]) || "";
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const buttonColorOutline = (color: string, active: boolean) => {
|
|
145
|
+
if (!color || color === "none") {
|
|
146
|
+
return "";
|
|
147
|
+
} else if (color === "primary") {
|
|
148
|
+
return `border border-primary-600 ${active ? "" : "text-primary-600"}`;
|
|
149
|
+
} else if (color === "grayDark") {
|
|
150
|
+
return `border border-gray-800 ${active ? "" : "text-gray-800"}`;
|
|
151
|
+
} else if (color === "grayLight") {
|
|
152
|
+
return `border border-gray-50 ${active ? "" : "text-gray-400"}`;
|
|
153
|
+
} else if (color === "gray400") {
|
|
154
|
+
return `border border-gray-400 ${active ? "" : "text-gray-400"}`;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return `border border-${color}-500 ${active ? "" : `text-${color}-500`}`;
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
export type ButtonCategoryType =
|
|
161
|
+
| "solid"
|
|
162
|
+
| "link"
|
|
163
|
+
| "linkHoverSolid"
|
|
164
|
+
| "outline";
|
|
165
|
+
|
|
166
|
+
const ButtonCategoryFn = (
|
|
167
|
+
color: string,
|
|
168
|
+
active: boolean,
|
|
169
|
+
): { [key in ButtonCategoryType]: string } => {
|
|
170
|
+
return {
|
|
171
|
+
solid: buttonColorSolid(color),
|
|
172
|
+
link: buttonColorLink(color),
|
|
173
|
+
linkHoverSolid: buttonColorLinkHoverSolid(color, active),
|
|
174
|
+
outline: buttonColorOutline(color, active),
|
|
175
|
+
};
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
export const ButtonSize = {
|
|
179
|
+
none: "",
|
|
180
|
+
default: "min-w-fit h-[40px] text-base px-3",
|
|
181
|
+
base: "w-full md:min-w-fit h-[40px] text-base px-3",
|
|
182
|
+
small: "min-w-[106px] h-[30px] text-sm px-2",
|
|
183
|
+
normal: "min-w-[140px] h-[38px] text-base px-3",
|
|
184
|
+
large: "min-w-[160px] h-[40px] text-lg px-3",
|
|
185
|
+
NormalBlock: "w-full h-[40px] text-lg px-3",
|
|
186
|
+
box: "w-[38px] h-[38px]",
|
|
187
|
+
smallCollapse: "h-[30px] text-sm px-2",
|
|
188
|
+
normalCollapse: "h-[38px] text-base px-3",
|
|
189
|
+
} as const;
|
|
190
|
+
|
|
191
|
+
export type ButtonSizeType = keyof typeof ButtonSize;
|
|
192
|
+
|
|
193
|
+
export const ButtonRounded = {
|
|
194
|
+
none: "",
|
|
195
|
+
default: "rounded",
|
|
196
|
+
pill: "rounded-pill",
|
|
197
|
+
} as const;
|
|
198
|
+
|
|
199
|
+
export type ButtonRoundedType = keyof typeof ButtonRounded;
|
|
200
|
+
|
|
201
|
+
export interface ButtonProps
|
|
202
|
+
extends ButtonHTMLAttributes<Omit<HTMLButtonElement, "type">> {
|
|
203
|
+
active?: boolean;
|
|
204
|
+
children: ReactNode;
|
|
205
|
+
color?: string;
|
|
206
|
+
size?: ButtonSizeType;
|
|
207
|
+
category?: "solid" | "link" | "outline" | "linkHoverSolid";
|
|
208
|
+
type?: "button" | "submit";
|
|
209
|
+
rounded?: ButtonRoundedType;
|
|
210
|
+
className?: string;
|
|
211
|
+
useFilterStyle?: boolean;
|
|
212
|
+
usePaginationStyle?: boolean;
|
|
213
|
+
dataCy?: string;
|
|
214
|
+
testingIdKey?: string;
|
|
215
|
+
style?: CSSProperties;
|
|
216
|
+
primaryColor?: string; // for custom primary color
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const button = (
|
|
220
|
+
{
|
|
221
|
+
active = false,
|
|
222
|
+
children,
|
|
223
|
+
color = "primary",
|
|
224
|
+
size = "normal",
|
|
225
|
+
category = "solid",
|
|
226
|
+
className,
|
|
227
|
+
type = "button",
|
|
228
|
+
rounded = "default",
|
|
229
|
+
style = {},
|
|
230
|
+
primaryColor,
|
|
231
|
+
...props
|
|
232
|
+
}: ButtonProps,
|
|
233
|
+
ref: React.Ref<HTMLButtonElement>,
|
|
234
|
+
) => {
|
|
235
|
+
const disabledClasses = ButtonDisabled;
|
|
236
|
+
const colorBaseClasses = !/link/.test(category)
|
|
237
|
+
? buttonColorBase(color, active)
|
|
238
|
+
: null;
|
|
239
|
+
const focusClasses = ButtonFocus; // for accessibility we always need focus outlines
|
|
240
|
+
const sizeClasses = ButtonSize[size];
|
|
241
|
+
const categoryClasses = ButtonCategoryFn(color, active)?.[category];
|
|
242
|
+
const colorClasses = twMerge(colorBaseClasses, categoryClasses);
|
|
243
|
+
const roundedClasses = ButtonRounded[rounded];
|
|
244
|
+
return (
|
|
245
|
+
<button
|
|
246
|
+
{...props}
|
|
247
|
+
style={{
|
|
248
|
+
...style,
|
|
249
|
+
transition:
|
|
250
|
+
"color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out",
|
|
251
|
+
backgroundColor: primaryColor ? primaryColor + "!important" : undefined,
|
|
252
|
+
}}
|
|
253
|
+
type={type}
|
|
254
|
+
ref={ref}
|
|
255
|
+
className={twMerge(
|
|
256
|
+
ButtonBase,
|
|
257
|
+
focusClasses,
|
|
258
|
+
disabledClasses,
|
|
259
|
+
sizeClasses,
|
|
260
|
+
colorClasses,
|
|
261
|
+
roundedClasses,
|
|
262
|
+
className,
|
|
263
|
+
"cursor-pointer",
|
|
264
|
+
)}
|
|
265
|
+
>
|
|
266
|
+
{children}
|
|
267
|
+
</button>
|
|
268
|
+
);
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
const Button = forwardRef(button);
|
|
272
|
+
export default Button;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Description, Field, Input, Label } from "@headlessui/react";
|
|
2
|
+
import { cn } from "../../utils/cn";
|
|
3
|
+
|
|
4
|
+
interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
|
|
5
|
+
type?: string; // Optional type prop, default is "text"
|
|
6
|
+
label: string; // Required label prop for the input field
|
|
7
|
+
description?: string; // Optional description prop for additional information
|
|
8
|
+
}
|
|
9
|
+
export const InputField = ({
|
|
10
|
+
type = "text",
|
|
11
|
+
className,
|
|
12
|
+
label,
|
|
13
|
+
description,
|
|
14
|
+
...props
|
|
15
|
+
}: InputProps & {
|
|
16
|
+
className?: string;
|
|
17
|
+
}) => {
|
|
18
|
+
return (
|
|
19
|
+
<div className="w-full max-w-md px-4">
|
|
20
|
+
<Field>
|
|
21
|
+
<Label className="font-medium text-gray-900">{label}</Label>
|
|
22
|
+
{description && (
|
|
23
|
+
<Description className="text-sm text-secondary">
|
|
24
|
+
{description}
|
|
25
|
+
</Description>
|
|
26
|
+
)}
|
|
27
|
+
<Input
|
|
28
|
+
type={type}
|
|
29
|
+
className={cn(
|
|
30
|
+
"mt-3 block w-full rounded-lg border border-gray-200 px-3 py-1.5 text-sm/6 text-gray-800",
|
|
31
|
+
"focus:not-data-focus:outline-none data-focus:outline-2 data-focus:-outline-offset-2 data-focus:outline-primary-500",
|
|
32
|
+
className,
|
|
33
|
+
)}
|
|
34
|
+
{...props}
|
|
35
|
+
/>
|
|
36
|
+
</Field>
|
|
37
|
+
</div>
|
|
38
|
+
);
|
|
39
|
+
};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Dialog,
|
|
3
|
+
DialogBackdrop,
|
|
4
|
+
DialogPanel,
|
|
5
|
+
DialogTitle,
|
|
6
|
+
} from "@headlessui/react";
|
|
7
|
+
import React from "react";
|
|
8
|
+
import Button from "./button";
|
|
9
|
+
|
|
10
|
+
export const Modal = ({
|
|
11
|
+
isOpen,
|
|
12
|
+
close,
|
|
13
|
+
title,
|
|
14
|
+
dismissOptions,
|
|
15
|
+
confirmOptions,
|
|
16
|
+
children,
|
|
17
|
+
}: {
|
|
18
|
+
isOpen: boolean;
|
|
19
|
+
close: () => void;
|
|
20
|
+
title: string;
|
|
21
|
+
children: React.ReactNode;
|
|
22
|
+
dismissOptions?: {
|
|
23
|
+
label: string;
|
|
24
|
+
onClick: () => void;
|
|
25
|
+
};
|
|
26
|
+
confirmOptions?: {
|
|
27
|
+
label: string;
|
|
28
|
+
onClick: () => void;
|
|
29
|
+
disabled?: boolean;
|
|
30
|
+
};
|
|
31
|
+
}) => {
|
|
32
|
+
return (
|
|
33
|
+
<Dialog
|
|
34
|
+
open={isOpen}
|
|
35
|
+
onClose={close}
|
|
36
|
+
className="relative z-50 transition duration-300 ease-out data-closed:opacity-0 confidence-ui"
|
|
37
|
+
transition
|
|
38
|
+
>
|
|
39
|
+
{/* The backdrop, rendered as a fixed sibling to the panel container */}
|
|
40
|
+
<DialogBackdrop className="fixed inset-0 bg-black/30" />
|
|
41
|
+
|
|
42
|
+
{/* Full-screen container to center the panel */}
|
|
43
|
+
<div className="fixed inset-0 flex w-screen items-center justify-center p-4">
|
|
44
|
+
{/* The actual dialog panel */}
|
|
45
|
+
<DialogPanel className="max-w-lg divide-y divide-gray-100 bg-white rounded-sm shadow-xl">
|
|
46
|
+
<DialogTitle className=" text-gray-900 p-4">{title}</DialogTitle>
|
|
47
|
+
<div className="p-4">{children}</div>
|
|
48
|
+
{dismissOptions || confirmOptions ? (
|
|
49
|
+
<div className="flex gap-4 justify-end p-4">
|
|
50
|
+
{dismissOptions ? (
|
|
51
|
+
<Button
|
|
52
|
+
onClick={() => {
|
|
53
|
+
dismissOptions.onClick();
|
|
54
|
+
close();
|
|
55
|
+
}}
|
|
56
|
+
size="small"
|
|
57
|
+
color="secondary"
|
|
58
|
+
>
|
|
59
|
+
{dismissOptions.label}
|
|
60
|
+
</Button>
|
|
61
|
+
) : (
|
|
62
|
+
<></>
|
|
63
|
+
)}
|
|
64
|
+
|
|
65
|
+
{confirmOptions ? (
|
|
66
|
+
<Button
|
|
67
|
+
disabled={confirmOptions.disabled}
|
|
68
|
+
onClick={() => {
|
|
69
|
+
confirmOptions.onClick();
|
|
70
|
+
close();
|
|
71
|
+
}}
|
|
72
|
+
size="small"
|
|
73
|
+
color="primary"
|
|
74
|
+
>
|
|
75
|
+
{confirmOptions.label}
|
|
76
|
+
</Button>
|
|
77
|
+
) : (
|
|
78
|
+
<></>
|
|
79
|
+
)}
|
|
80
|
+
</div>
|
|
81
|
+
) : (
|
|
82
|
+
<></>
|
|
83
|
+
)}
|
|
84
|
+
</DialogPanel>
|
|
85
|
+
</div>
|
|
86
|
+
</Dialog>
|
|
87
|
+
);
|
|
88
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
2
|
+
import { createContext, useContext, useState } from "react";
|
|
3
|
+
|
|
4
|
+
const Context = createContext<{
|
|
5
|
+
queryClient: QueryClient;
|
|
6
|
+
} | null>(null);
|
|
7
|
+
|
|
8
|
+
export const ConfidenceProvider = Context.Provider;
|
|
9
|
+
|
|
10
|
+
export const useConfidenceContext = () => useContext(Context)!;
|
|
11
|
+
|
|
12
|
+
export const ConfidenceContext = ({
|
|
13
|
+
children,
|
|
14
|
+
}: {
|
|
15
|
+
children: React.ReactNode;
|
|
16
|
+
}) => {
|
|
17
|
+
const [queryClient] = useState(new QueryClient());
|
|
18
|
+
console.log("Confidence Playbook Context Initialized");
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<Context.Provider value={{ queryClient }}>
|
|
22
|
+
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
|
|
23
|
+
</Context.Provider>
|
|
24
|
+
);
|
|
25
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { useMutation } from "@tanstack/react-query";
|
|
2
|
+
import { completeTask } from "../../services/complete-task.service";
|
|
3
|
+
import { useConfidenceContext } from "../../context/confidence-context";
|
|
4
|
+
|
|
5
|
+
export const useCompleteTask = () => {
|
|
6
|
+
const { queryClient } = useConfidenceContext();
|
|
7
|
+
|
|
8
|
+
const completeTaskMutation = useMutation(
|
|
9
|
+
{
|
|
10
|
+
mutationFn: async (taskInstanceId: string) => {
|
|
11
|
+
return completeTask(taskInstanceId);
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
queryClient,
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
const completeTaskHandler = async (taskInstanceId: string) => {
|
|
18
|
+
return completeTaskMutation.mutateAsync(taskInstanceId, {
|
|
19
|
+
onSuccess: () => {
|
|
20
|
+
console.log("Task completed successfully");
|
|
21
|
+
},
|
|
22
|
+
onError: (error) => {
|
|
23
|
+
console.error("Error completing task:", error);
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
completeTask: completeTaskHandler,
|
|
30
|
+
...completeTaskMutation,
|
|
31
|
+
};
|
|
32
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { useMutation } from "@tanstack/react-query";
|
|
2
|
+
import { startTask } from "../../services/start-task.services";
|
|
3
|
+
import { useConfidenceContext } from "../../context/confidence-context";
|
|
4
|
+
|
|
5
|
+
export const useStartTask = () => {
|
|
6
|
+
const { queryClient } = useConfidenceContext();
|
|
7
|
+
|
|
8
|
+
const startTaskMutation = useMutation(
|
|
9
|
+
{
|
|
10
|
+
mutationFn: async (taskInstanceId: string) => {
|
|
11
|
+
const res = await startTask(taskInstanceId);
|
|
12
|
+
console.log(res);
|
|
13
|
+
|
|
14
|
+
return res;
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
queryClient,
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
const startTaskHandler = (taskInstanceId: string) => {
|
|
21
|
+
return startTaskMutation.mutateAsync(taskInstanceId, {
|
|
22
|
+
onSuccess: () => {
|
|
23
|
+
console.log("Task started successfully");
|
|
24
|
+
},
|
|
25
|
+
onError: (error) => {
|
|
26
|
+
console.error("Error starting task:", error);
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
startTask: startTaskHandler,
|
|
33
|
+
...startTaskMutation,
|
|
34
|
+
};
|
|
35
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { useQuery } from "@tanstack/react-query";
|
|
2
|
+
import { getTaskDetails } from "../../services/task-details.service";
|
|
3
|
+
import { useConfidenceContext } from "../../context/confidence-context";
|
|
4
|
+
|
|
5
|
+
export const useTaskDetails = ({
|
|
6
|
+
taskId,
|
|
7
|
+
playbookId,
|
|
8
|
+
enabled,
|
|
9
|
+
}: {
|
|
10
|
+
taskId: number | string;
|
|
11
|
+
playbookId: string | number;
|
|
12
|
+
enabled: boolean;
|
|
13
|
+
}) => {
|
|
14
|
+
const { queryClient } = useConfidenceContext();
|
|
15
|
+
const queryFn = async () =>
|
|
16
|
+
getTaskDetails({
|
|
17
|
+
taskId: +taskId,
|
|
18
|
+
templateId: playbookId.toString(),
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const queryKey = ["taskDetails", +taskId, playbookId];
|
|
22
|
+
const query = useQuery(
|
|
23
|
+
{
|
|
24
|
+
queryKey,
|
|
25
|
+
queryFn,
|
|
26
|
+
enabled,
|
|
27
|
+
},
|
|
28
|
+
queryClient,
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
const prefetchQuery = () => {
|
|
32
|
+
queryClient.prefetchQuery({
|
|
33
|
+
queryFn,
|
|
34
|
+
queryKey,
|
|
35
|
+
});
|
|
36
|
+
};
|
|
37
|
+
return {
|
|
38
|
+
taskDetails: query.data,
|
|
39
|
+
prefetchQuery,
|
|
40
|
+
...query,
|
|
41
|
+
};
|
|
42
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { useQuery } from "@tanstack/react-query";
|
|
2
|
+
import { useConfidenceContext } from "../context/confidence-context";
|
|
3
|
+
import { Playbook } from "../types/playbook.types";
|
|
4
|
+
import { usePlaybookActions } from "./usePlaybookActions";
|
|
5
|
+
import { CONFIDENCE_API_ENDPOINT } from "../constants/settings.constants";
|
|
6
|
+
|
|
7
|
+
export const usePlaybook = (playbookInstanceId: number | string) => {
|
|
8
|
+
const { queryClient } = useConfidenceContext();
|
|
9
|
+
const query = useQuery(
|
|
10
|
+
{
|
|
11
|
+
queryFn: async () => {
|
|
12
|
+
const headers = new Headers();
|
|
13
|
+
|
|
14
|
+
const response = await fetch(
|
|
15
|
+
`${CONFIDENCE_API_ENDPOINT}/playbook-details`,
|
|
16
|
+
{
|
|
17
|
+
method: "POST",
|
|
18
|
+
cache: "no-store",
|
|
19
|
+
headers,
|
|
20
|
+
body: JSON.stringify({
|
|
21
|
+
playbookInstanceId: +playbookInstanceId,
|
|
22
|
+
start: 0,
|
|
23
|
+
limit: 50,
|
|
24
|
+
}),
|
|
25
|
+
},
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
if (!response.ok) {
|
|
29
|
+
throw new Error("Network response was not ok");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const data = await response.json();
|
|
33
|
+
if (data.error) {
|
|
34
|
+
throw new Error(data.error);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return data;
|
|
38
|
+
},
|
|
39
|
+
queryKey: ["playbook", +playbookInstanceId],
|
|
40
|
+
},
|
|
41
|
+
queryClient,
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
const actions = usePlaybookActions(playbookInstanceId, {
|
|
45
|
+
quickComplete: query.data?.quickComplete,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
playbook: query.data as Playbook,
|
|
50
|
+
isLoading: query.isLoading,
|
|
51
|
+
error: query.error,
|
|
52
|
+
actions,
|
|
53
|
+
};
|
|
54
|
+
};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { useConfidenceContext } from "../context/confidence-context";
|
|
2
|
+
import { Playbook } from "../types/playbook.types";
|
|
3
|
+
import { Task, TASK_STATUS } from "../types/task.types";
|
|
4
|
+
import { useCompleteTask } from "./task/useCompleteTask";
|
|
5
|
+
import { useStartTask } from "./task/useStartTask";
|
|
6
|
+
|
|
7
|
+
export const usePlaybookActions = (
|
|
8
|
+
playbookInstanceId: number | string,
|
|
9
|
+
options: {
|
|
10
|
+
quickComplete?: boolean;
|
|
11
|
+
},
|
|
12
|
+
) => {
|
|
13
|
+
const { queryClient } = useConfidenceContext();
|
|
14
|
+
|
|
15
|
+
const { completeTask } = useCompleteTask();
|
|
16
|
+
const { startTask } = useStartTask();
|
|
17
|
+
|
|
18
|
+
const completeTaskHandler = async (
|
|
19
|
+
taskInstanceId: number,
|
|
20
|
+
sequenceOrder: number,
|
|
21
|
+
) => {
|
|
22
|
+
const { status } = await completeTask(taskInstanceId.toString());
|
|
23
|
+
if (!status) return;
|
|
24
|
+
|
|
25
|
+
queryClient.setQueryData(
|
|
26
|
+
["playbook", +playbookInstanceId],
|
|
27
|
+
(oldData: Playbook) => {
|
|
28
|
+
if (!oldData) return oldData;
|
|
29
|
+
const updatedTasks = oldData.tasks.map((task: Task) => {
|
|
30
|
+
if (
|
|
31
|
+
task.sequenceOrder === sequenceOrder + 1 &&
|
|
32
|
+
options.quickComplete
|
|
33
|
+
) {
|
|
34
|
+
return { ...task, workflowStatus: TASK_STATUS.IN_PROGRESS };
|
|
35
|
+
}
|
|
36
|
+
if (task.taskInstanceId === taskInstanceId) {
|
|
37
|
+
return { ...task, workflowStatus: status };
|
|
38
|
+
}
|
|
39
|
+
return task;
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
return { ...oldData, tasks: updatedTasks };
|
|
43
|
+
},
|
|
44
|
+
);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const startTaskHandler = async (taskInstanceId: number) => {
|
|
48
|
+
const { status } = await startTask(taskInstanceId.toString());
|
|
49
|
+
if (!status) return;
|
|
50
|
+
queryClient.setQueryData(
|
|
51
|
+
["playbook", +playbookInstanceId],
|
|
52
|
+
(oldData: Playbook) => {
|
|
53
|
+
if (!oldData) return oldData;
|
|
54
|
+
const updatedTasks = oldData.tasks.map((task: Task) => {
|
|
55
|
+
if (task.taskInstanceId === taskInstanceId) {
|
|
56
|
+
return { ...task, workflowStatus: status };
|
|
57
|
+
}
|
|
58
|
+
return task;
|
|
59
|
+
});
|
|
60
|
+
return { ...oldData, tasks: updatedTasks };
|
|
61
|
+
},
|
|
62
|
+
);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
completeTask: completeTaskHandler,
|
|
67
|
+
startTask: startTaskHandler,
|
|
68
|
+
};
|
|
69
|
+
};
|