@cccsaurora/howler-ui 2.17.0-dev.516 → 2.17.0-dev.518

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.
@@ -109,6 +109,6 @@ const HitBanner = ({ hit, layout = HitLayout.NORMAL, showAssigned = true }) => {
109
109
  width: theme.spacing(3)
110
110
  }
111
111
  }
112
- ], children: [_jsx(HitTimestamp, { hit: hit, layout: layout }), showAssigned && _jsx(Assigned, { hit: hit, layout: layout }), _jsxs(Stack, { direction: "row", spacing: layout !== HitLayout.COMFY ? 0.5 : 1, children: [_jsx(EscalationChip, { hit: hit, layout: layout }), ['in-progress', 'on-hold'].includes(hit.howler.status) && (_jsx(Chip, { sx: { width: 'fit-content', display: 'inline-flex' }, label: hit.howler.status, size: layout !== HitLayout.COMFY ? 'small' : 'medium', color: "primary" })), hit.howler.related && (_jsx(Chip, { size: layout !== HitLayout.COMFY ? 'small' : 'medium', label: t('hit.header.related', { count: hit.howler.related.length }) }))] }), howlerPluginStore.plugins.flatMap(plugin => pluginStore.executeFunction(`${plugin}.status`, { hit, layout }))] })] }));
112
+ ], children: [_jsx(HitTimestamp, { hit: hit, layout: layout }), showAssigned && _jsx(Assigned, { hit: hit, layout: layout }), _jsxs(Stack, { direction: "row", spacing: layout !== HitLayout.COMFY ? 0.5 : 1, children: [_jsx(EscalationChip, { hit: hit, layout: layout }), ['in-progress', 'on-hold'].includes(hit.howler.status) && (_jsx(Chip, { sx: { width: 'fit-content', display: 'inline-flex' }, label: hit.howler.status, size: layout !== HitLayout.COMFY ? 'small' : 'medium', color: "primary" }))] }), hit.howler.related && (_jsx(Chip, { size: layout !== HitLayout.COMFY ? 'small' : 'medium', label: t('hit.header.related', { count: hit.howler.related.length }) })), howlerPluginStore.plugins.flatMap(plugin => pluginStore.executeFunction(`${plugin}.status`, { hit, layout }))] })] }));
113
113
  };
114
114
  export default HitBanner;
@@ -1,10 +1,11 @@
1
1
  import type { Task } from '@cccsaurora/howler-ui/models/entities/generated/Task';
2
2
  import { type FC } from 'react';
3
3
  declare const CaseTask: FC<{
4
- task: Task;
4
+ task?: Task;
5
5
  paths: string[];
6
- onDelete: () => void;
7
- onEdit: (task: Partial<Task>) => Promise<void>;
6
+ onDelete?: () => Promise<void>;
7
+ onEdit: (task?: Partial<Task>) => Promise<void>;
8
8
  loading?: boolean;
9
+ newTask?: boolean;
9
10
  }>;
10
11
  export default CaseTask;
@@ -2,45 +2,49 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Check, Close, Delete, Edit } from '@mui/icons-material';
3
3
  import { Autocomplete, Card, Checkbox, Chip, IconButton, LinearProgress, Stack, TextField, Tooltip, Typography } from '@mui/material';
4
4
  import UserList from '@cccsaurora/howler-ui/components/elements/UserList';
5
- import { useState } from 'react';
5
+ import { useEffect, useState } from 'react';
6
6
  import { useTranslation } from 'react-i18next';
7
7
  import { Link } from 'react-router-dom';
8
- const CaseTask = ({ task, onEdit, onDelete, paths }) => {
8
+ const CaseTask = ({ task, onEdit, onDelete, paths, newTask = false }) => {
9
9
  const { t } = useTranslation();
10
- const [editing, setEditing] = useState(false);
11
10
  const [loading, setLoading] = useState(false);
12
- const [summary, setSummary] = useState(task.summary);
13
- const [path, setPath] = useState(task.path);
14
- const dirty = summary !== task.summary || path !== task.path;
15
- const onOwnerChange = async ([assignment]) => {
16
- setLoading(true);
17
- await onEdit({
18
- assignment
19
- });
20
- setLoading(false);
21
- };
11
+ const [editing, setEditing] = useState(newTask);
12
+ const [summary, setSummary] = useState(task?.summary || '');
13
+ const [path, setPath] = useState(task?.path ?? null);
14
+ const [assignment, setAssignment] = useState(task?.assignment);
15
+ const [complete, setComplete] = useState(task?.complete ?? false);
16
+ const dirty = summary !== task?.summary || path !== task?.path || complete !== task?.complete || assignment !== task?.assignment;
22
17
  const onSubmit = async () => {
23
- if (dirty) {
18
+ if (dirty && editing) {
24
19
  setLoading(true);
25
- await onEdit({ summary, path: !path ? null : path });
20
+ await onEdit({ summary, path: !path ? null : path, assignment, complete });
26
21
  setLoading(false);
27
22
  }
28
23
  };
29
- return (_jsxs(Card, { sx: { pl: 0.5, pr: 1, py: 0.5, position: 'relative' }, children: [_jsxs(Stack, { direction: "row", alignItems: "center", spacing: 1, children: [_jsx(Checkbox, { disabled: loading, color: "success", checked: task.complete, size: "small", onChange: async (_ev, complete) => {
30
- try {
24
+ useEffect(() => {
25
+ if (!editing && task?.assignment !== assignment) {
26
+ setLoading(true);
27
+ onEdit({ assignment }).finally(() => setLoading(false));
28
+ }
29
+ // eslint-disable-next-line react-hooks/exhaustive-deps
30
+ }, [assignment]);
31
+ useEffect(() => {
32
+ if (!editing && task?.complete !== complete) {
33
+ setLoading(true);
34
+ onEdit({ complete }).finally(() => setLoading(false));
35
+ }
36
+ // eslint-disable-next-line react-hooks/exhaustive-deps
37
+ }, [complete]);
38
+ return (_jsxs(Card, { sx: { pl: 0.5, pr: 1, py: 0.5, position: 'relative' }, children: [_jsxs(Stack, { direction: "row", alignItems: "center", spacing: 1, children: [_jsx(Checkbox, { disabled: loading, color: "success", checked: complete, size: "small", onChange: (_ev, _complete) => setComplete(_complete) }), editing ? (_jsx(TextField, { disabled: loading, value: summary, onChange: e => setSummary(e.target.value), size: "small", fullWidth: true, sx: { minWidth: '40%' } })) : (_jsx(Typography, { sx: [complete && { textDecoration: 'line-through' }], children: task?.summary || summary })), !editing && path && _jsx(Chip, { clickable: true, component: Link, to: path, label: path }), editing && (_jsx(Autocomplete, { disabled: loading, value: path, options: paths, onChange: (_ev, value) => setPath(value), fullWidth: true, renderInput: params => _jsx(TextField, { ...params, size: "small" }) })), _jsx(UserList, { disabled: loading, userIds: [assignment], onChange: ([_assigment]) => setAssignment(_assigment), i18nLabel: "route.cases.task.set.assignment", avatarHeight: 24 }), _jsx("div", { style: { flex: 1 } }), editing && (_jsx(Tooltip, { title: t('route.cases.task.delete'), children: _jsx(IconButton, { size: "small", color: "error", onClick: () => {
31
39
  setLoading(true);
32
- await onEdit({ complete });
33
- }
34
- finally {
35
- setLoading(false);
36
- }
37
- } }), editing ? (_jsx(TextField, { disabled: loading, value: summary, onChange: e => setSummary(e.target.value), size: "small", fullWidth: true, sx: { minWidth: '40%' } })) : (_jsx(Typography, { sx: [task.complete && { textDecoration: 'line-through' }], children: task.summary })), task.path && !editing && _jsx(Chip, { clickable: true, component: Link, to: task.path, label: task.path }), editing && (_jsx(Autocomplete, { disabled: loading, value: path, options: paths, onChange: (_ev, value) => setPath(value), fullWidth: true, renderInput: params => _jsx(TextField, { ...params, size: "small" }) })), task.assignment && (_jsx(UserList, { disabled: loading, userIds: [task.assignment], onChange: onOwnerChange, i18nLabel: "route.cases.task.set.assignment", avatarHeight: 24 })), _jsx("div", { style: { flex: 1 } }), editing && (_jsx(Tooltip, { title: t('route.cases.task.delete'), children: _jsx(IconButton, { size: "small", color: "error", onClick: onDelete, children: _jsx(Delete, { fontSize: "small" }) }) })), _jsx(Tooltip, { title: t(editing ? 'route.cases.task.edit.save' : 'route.cases.task.edit'), children: _jsx(IconButton, { size: "small", color: editing ? 'success' : 'default', onClick: () => {
38
- if (!editing) {
39
- setEditing(true);
40
- return;
41
- }
42
- setEditing(false);
43
- onSubmit();
44
- }, disabled: (!dirty && editing) || loading, children: editing ? _jsx(Check, { fontSize: "small" }) : _jsx(Edit, { fontSize: "small" }) }) }), editing && (_jsx(Tooltip, { title: t('route.cases.task.edit.cancel'), children: _jsx(IconButton, { size: "small", onClick: () => setEditing(false), disabled: loading, children: _jsx(Close, { fontSize: "small" }) }) }))] }), loading && _jsx(LinearProgress, { sx: { left: 0, bottom: 0, right: 0, position: 'absolute' } })] }, task.id));
40
+ onDelete().then(() => setLoading(false));
41
+ }, disabled: loading, children: _jsx(Delete, { fontSize: "small" }) }) })), _jsx(Tooltip, { title: t(editing ? 'route.cases.task.edit.save' : 'route.cases.task.edit'), children: _jsx("span", { children: _jsx(IconButton, { size: "small", color: editing ? 'success' : 'default', onClick: async () => {
42
+ if (!editing) {
43
+ setEditing(true);
44
+ return;
45
+ }
46
+ await onSubmit();
47
+ setEditing(false);
48
+ }, disabled: (!dirty && editing) || loading || !summary, children: editing ? _jsx(Check, { fontSize: "small" }) : _jsx(Edit, { fontSize: "small" }) }) }) }), editing && (_jsx(Tooltip, { title: t('route.cases.task.edit.cancel'), children: _jsx(IconButton, { size: "small", onClick: () => setEditing(false), disabled: loading, children: _jsx(Close, { fontSize: "small" }) }) }))] }), loading && _jsx(LinearProgress, { sx: { left: 0, bottom: 0, right: 0, position: 'absolute' } })] }));
45
49
  };
46
50
  export default CaseTask;
@@ -13,7 +13,7 @@ const RelatedCasePanel = ({ case: _case }) => {
13
13
  if (!_case) {
14
14
  return _jsx(Skeleton, { height: 240 });
15
15
  }
16
- return (_jsxs(Stack, { spacing: 1, children: [_jsxs(Stack, { direction: "row", children: [_jsx(Typography, { flex: 1, variant: "h4", children: t('page.cases.dashboard.alerts') }), _jsx(Pagination, { count: casePages.length, page: casePage, onChange: (_, page) => setCasePage(page) })] }), _jsx(Divider, {}), casePages[casePage - 1]?.map(item => (_jsxs(Box, { position: "relative", children: [_jsx(CaseCard, { caseId: item.id }), _jsx(Box, { component: Link, to: item.path, sx: {
16
+ return (_jsxs(Stack, { spacing: 1, children: [_jsxs(Stack, { direction: "row", children: [_jsx(Typography, { flex: 1, variant: "h4", children: t('page.cases.dashboard.cases') }), _jsx(Pagination, { count: casePages.length, page: casePage, onChange: (_, page) => setCasePage(page) })] }), _jsx(Divider, {}), casePages[casePage - 1]?.map(item => (_jsxs(Box, { position: "relative", children: [_jsx(CaseCard, { caseId: item.id }), _jsx(Box, { component: Link, to: item.path, sx: {
17
17
  position: 'absolute',
18
18
  top: 0,
19
19
  left: 0,
@@ -1,23 +1,52 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Add } from '@mui/icons-material';
2
3
  import { Divider, Skeleton, Stack, Typography } from '@mui/material';
3
- import {} from 'react';
4
+ import { useState } from 'react';
4
5
  import { useTranslation } from 'react-i18next';
5
6
  import CaseTask from './CaseTask';
6
7
  const TaskPanel = ({ case: _case, updateCase }) => {
7
8
  const { t } = useTranslation();
9
+ const [addingTask, setAddingTask] = useState(false);
10
+ const onEdit = (task) => async (newTask) => {
11
+ if (task) {
12
+ await updateCase({
13
+ tasks: _case.tasks.map(_task => {
14
+ if (_task.id !== task.id) {
15
+ return _task;
16
+ }
17
+ return {
18
+ ..._task,
19
+ ...newTask
20
+ };
21
+ })
22
+ });
23
+ }
24
+ else {
25
+ await updateCase({
26
+ tasks: [..._case.tasks, newTask]
27
+ });
28
+ }
29
+ };
8
30
  if (!_case) {
9
31
  return _jsx(Skeleton, { height: 240 });
10
32
  }
11
- return (_jsxs(Stack, { spacing: 1, children: [_jsx(Typography, { flex: 1, variant: "h4", children: t('page.cases.dashboard.tasks') }), _jsx(Divider, {}), _case.tasks.map(task => (_jsx(CaseTask, { task: task, paths: _case.items.map(item => item.path), onEdit: newTask => updateCase({
12
- tasks: _case.tasks.map(_task => {
13
- if (_task.id !== task.id) {
14
- return _task;
15
- }
16
- return {
17
- ..._task,
18
- ...newTask
19
- };
20
- })
21
- }), onDelete: () => updateCase({ tasks: _case.tasks.filter(_task => _task.id !== task.id) }) }, task.id)))] }));
33
+ return (_jsxs(Stack, { spacing: 1, children: [_jsx(Typography, { flex: 1, variant: "h4", children: t('page.cases.dashboard.tasks') }), _jsx(Divider, {}), _case.tasks.map(task => (_jsx(CaseTask, { task: task, paths: _case.items.map(item => item.path), onEdit: onEdit(task), onDelete: () => updateCase({ tasks: _case.tasks.filter(_task => _task.id !== task.id) }) }, task.id))), addingTask && (_jsx(CaseTask, { newTask: true, paths: _case.items.map(item => item.path), onEdit: async (task) => {
34
+ await onEdit()(task);
35
+ setAddingTask(false);
36
+ }, onDelete: async () => setAddingTask(false) })), _jsxs(Stack, { onClick: () => setAddingTask(true), direction: "row", spacing: 2, sx: theme => ({
37
+ borderStyle: 'dashed',
38
+ borderColor: theme.palette.text.secondary,
39
+ borderWidth: '0.15rem',
40
+ borderRadius: '0.15rem',
41
+ opacity: 0.3,
42
+ justifyContent: 'center',
43
+ alignItems: 'center',
44
+ padding: 1,
45
+ transition: theme.transitions.create('opacity'),
46
+ '&:hover': {
47
+ opacity: 1,
48
+ cursor: 'pointer'
49
+ }
50
+ }), children: [_jsx(Add, {}), _jsx(Typography, { children: t('page.cases.dashboard.tasks.add') })] })] }));
22
51
  };
23
52
  export default TaskPanel;
@@ -114,7 +114,6 @@
114
114
  "help.hit.banner.description": "See the below hit banner example for the hit keys necessary to properly populate it. If you have any additional questions, ask in the HOWLER support channel.",
115
115
  "help.hit.banner.json": "Here is the hit data used to populate this banner:",
116
116
  "help.hit.banner.title": "Populating the Hit Banner",
117
- "help.hit.bundle.title": "Hit Bundles",
118
117
  "help.hit.labels.title": "Hit Labels",
119
118
  "help.hit.links.title": "Hit Links",
120
119
  "help.hit.schema.description.missing": "No description provided.",
@@ -168,6 +167,7 @@
168
167
  "hit.header.escalation": "Escalation Level: ",
169
168
  "hit.header.indicators": "Indicators",
170
169
  "hit.header.rationale": "Rationale",
170
+ "hit.header.related": "{{count}} related records",
171
171
  "hit.header.scrutiny": "Scrutiny: ",
172
172
  "hit.header.status": "Status: ",
173
173
  "hit.header.summary": "Summary",
@@ -336,6 +336,7 @@
336
336
  "page.cases.created": "Created",
337
337
  "page.cases.dashboard": "Dashboard",
338
338
  "page.cases.dashboard.alerts": "Alerts",
339
+ "page.cases.dashboard.cases": "Related Cases",
339
340
  "page.cases.dashboard.duration": "Duration",
340
341
  "page.cases.dashboard.indicators": "Indicators",
341
342
  "page.cases.dashboard.target": "Targets",
@@ -618,7 +619,6 @@
618
619
  "route.help.views": "Views",
619
620
  "route.history": "History mode: See all previous queries",
620
621
  "route.hits": "Alerts",
621
- "route.hits.bundle": "View Bundle",
622
622
  "route.hits.view": "View Hit",
623
623
  "route.home": "User Dashboard",
624
624
  "route.home.add": "Add New Panel",
@@ -340,6 +340,7 @@
340
340
  "page.cases.created": "Créé",
341
341
  "page.cases.dashboard": "Tableau de bord",
342
342
  "page.cases.dashboard.alerts": "Alertes",
343
+ "page.cases.dashboard.cases": "Cas connexes",
343
344
  "page.cases.dashboard.duration": "Durée",
344
345
  "page.cases.dashboard.indicators": "Indicateurs",
345
346
  "page.cases.dashboard.target": "Cibles",
package/package.json CHANGED
@@ -101,7 +101,7 @@
101
101
  "internal-slot": "1.0.7"
102
102
  },
103
103
  "type": "module",
104
- "version": "2.17.0-dev.516",
104
+ "version": "2.17.0-dev.518",
105
105
  "exports": {
106
106
  "./i18n": "./i18n.js",
107
107
  "./index.css": "./index.css",