diff --git a/package-lock.json b/package-lock.json index 3ec8ba3..607364f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2664,9 +2664,9 @@ } }, "node_modules/@eslint/eslintrc/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, "license": "MIT", "dependencies": { @@ -8700,9 +8700,9 @@ } }, "node_modules/eslint/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, "license": "MIT", "dependencies": { @@ -12610,9 +12610,9 @@ "dev": true }, "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", "dev": true, "license": "MIT", "dependencies": { diff --git a/src/components/IconLoader.jsx b/src/components/IconLoader.jsx index caa6577..b068a2b 100644 --- a/src/components/IconLoader.jsx +++ b/src/components/IconLoader.jsx @@ -64,6 +64,10 @@ import ITOps from "./icons/itops"; import Tenant from "./icons/Tenant"; import Role from "./icons/Role"; import User from "./icons/User"; +import DirectoryIcon from "./icons/Directory"; +import Employee from "./icons/Employee"; +import Customer from "./icons/Customer"; +import HumanResources from "./icons/HumanResources"; const icon_components = { @@ -79,22 +83,26 @@ const icon_components = { config_group: ConfigManagementIcon, config_management: ConfigManagementIcon, copy: CopyIcon, + customer: Customer, device_status_bad: InventoryStatusBadIcon, device_status_ok: InventoryStatusOkIcon, device_status_unk: InventoryStatusUknIcon, device_status_warn: InventoryStatusWarnIcon, devops: CodeIcon, + directory: DirectoryIcon, documentation: Documentation, reply: ReplyIcon, delete: DeleteIcon, device: DeviceIcon, edit: EditIcon, + employee: Employee, feature_flag: FeatureFlag, git: Git, github: GitHub, gitlab: GitLab, help: HelpIcon, history: HistoryIcon, + human_resources: HumanResources, information: InformationIcon, kb: InformationIcon, itam: ItamIcon, diff --git a/src/components/InlineFields.jsx b/src/components/InlineFields.jsx index c45eb21..065cc2f 100644 --- a/src/components/InlineFields.jsx +++ b/src/components/InlineFields.jsx @@ -360,10 +360,11 @@ export const InlineFieldAction = async ({ request, params }) => { const update = await apiFetch( document.location.pathname, - null, request.method, - form_data + form_data, + false, + false ) .then(data => { diff --git a/src/components/Table.jsx b/src/components/Table.jsx index 7a28094..b242616 100644 --- a/src/components/Table.jsx +++ b/src/components/Table.jsx @@ -29,7 +29,7 @@ const Table = ({ const API_SPLIT = String('api/v2') - const [loaded, setPageLoaded] = useState(false) + const [loaded, setPageLoaded] = useState(loader_data ? true : false) const [metadata, setMetaData] = useState(null); @@ -54,7 +54,6 @@ const Table = ({ useEffect(() => { - setPageLoaded(false) setMetaData(loader_metadata) setTableData(loader_data) setPageNumberValue(1) @@ -84,7 +83,6 @@ const Table = ({ } } - setPageLoaded(true) }, [ // loader_metadata, @@ -94,8 +92,6 @@ const Table = ({ useEffect(() =>{ - setPageLoaded(false) - let url = null if( page !== 0 ) { @@ -108,59 +104,58 @@ const Table = ({ } - apiFetch( url ) - .then((result) => { + if( loaded === false || page !== 0 ) { + apiFetch( url ) + .then((result) => { + if( result.status == 200 ) { - if( result.status == 200 ) { + if( result.api_metadata !== null ) { - if( result.api_metadata !== null ) { + setMetaData(result.api_metadata) + + } + + setTableData(result.api_page_data) - setMetaData(result.api_metadata) - - } - - setTableData(result.api_page_data) + if( Array(result.api_metadata.table_fields).length < 2 ) { - if( Array(result.api_metadata.table_fields).length < 2 ) { + console.error("Missing Table Fields") - console.error("Missing Table Fields") + } - } + if( SetContentHeaderIcon ) { - if( SetContentHeaderIcon ) { + SetContentHeaderIcon( + <> + {result.api_metadata['documentation'] && + + + + } + + ) + } - SetContentHeaderIcon( - <> - {result.api_metadata['documentation'] && - - - - } - - ) - } + if( callback ) { - if( callback ) { + callback(result.api_metadata.name) - callback(result.api_metadata.name) + } - } + if( page !== 0 ) { + + setPageNumberValue(page) - if( page !== 0 ) { - - setPageNumberValue(page) + } + setPageLoaded(true) } - - setPageLoaded(true) - } - - }) - + ) + } }, [ page, ]); @@ -199,9 +194,7 @@ const Table = ({ return ( <> - { loaded && - <> - { metadata && + { loaded && (metadata && table_data) &&
{ metadata.allowed_methods.includes('POST') && ()} @@ -502,8 +495,6 @@ const Table = ({ } - } - ); } diff --git a/src/components/form/Menu.jsx b/src/components/form/Menu.jsx index 2661eb7..97e46f1 100644 --- a/src/components/form/Menu.jsx +++ b/src/components/form/Menu.jsx @@ -30,14 +30,13 @@ const Menu = ({ const dropDownMenuId = useId(); const dropDownMenuItemsId = useId(); - const dropDownMenu = document.getElementById(dropDownMenuId) - document.onclick = (e) => { handleMenuClose(e) } const handleMenuClose = (e) => { + const dropDownMenu = document.getElementById(dropDownMenuId) e.stopPropagation() @@ -46,6 +45,7 @@ const Menu = ({ const handleMenuToggle = (e) => { + const dropDownMenu = document.getElementById(dropDownMenuId) e.stopPropagation() diff --git a/src/components/form/Textarea.jsx b/src/components/form/Textarea.jsx index 70da349..a09367e 100644 --- a/src/components/form/Textarea.jsx +++ b/src/components/form/Textarea.jsx @@ -104,8 +104,9 @@ const TextArea = ({ } }} - - >{value} + + value={value} + > ) diff --git a/src/components/icons/Customer.jsx b/src/components/icons/Customer.jsx new file mode 100644 index 0000000..ee93520 --- /dev/null +++ b/src/components/icons/Customer.jsx @@ -0,0 +1,14 @@ +const Customer = ({ + width = '20px', + height = '20px', + fill = '#FFF' +}) => { + + return ( + + + + ); +} + +export default Customer; diff --git a/src/components/icons/Directory.jsx b/src/components/icons/Directory.jsx new file mode 100644 index 0000000..576cd1a --- /dev/null +++ b/src/components/icons/Directory.jsx @@ -0,0 +1,14 @@ +const DirectoryIcon = ({ + width = '20px', + height = '20px', + fill = '#FFF' +}) => { + + return ( + + + + ); +} + +export default DirectoryIcon \ No newline at end of file diff --git a/src/components/icons/Employee.jsx b/src/components/icons/Employee.jsx new file mode 100644 index 0000000..c42dcf9 --- /dev/null +++ b/src/components/icons/Employee.jsx @@ -0,0 +1,14 @@ +const Employee = ({ + width = '20px', + height = '20px', + fill = '#FFF' +}) => { + + return ( + + + + ); +} + +export default Employee; diff --git a/src/components/icons/HumanResources.jsx b/src/components/icons/HumanResources.jsx new file mode 100644 index 0000000..a1339e0 --- /dev/null +++ b/src/components/icons/HumanResources.jsx @@ -0,0 +1,14 @@ +const HumanResources = ({ + width = '20px', + height = '20px', + fill = '#FFF' +}) => { + + return ( + + + + ); +} + +export default HumanResources; diff --git a/src/components/page/ticket/LinkedItems.jsx b/src/components/page/ticket/LinkedItems.jsx index 47e5baf..3879640 100644 --- a/src/components/page/ticket/LinkedItems.jsx +++ b/src/components/page/ticket/LinkedItems.jsx @@ -5,6 +5,7 @@ import FieldData from "../../../functions/FieldData"; import { apiFetch } from "../../../hooks/apiFetch"; import Section from "../../Section"; +import IconLoader from "../../IconLoader"; @@ -14,6 +15,7 @@ const LinkedItems = ({ const [ page_data, setPageData ] = useState(null) const [ metadata, setMetaData ] = useState(null) + const [ refresh, setRefresh ] = useState(false) useEffect(() => { @@ -29,9 +31,27 @@ const LinkedItems = ({ undefined, undefined, ) - }, [ data_url ]) + }, [ data_url, refresh ]) + const handleDeleteLinkedItem = (e) => { + + let url = `${data_url}/${e.currentTarget.id}` + + console.log(`Removing Linked Item ${url}`) + + apiFetch( + url, + null, + 'DELETE', + null, + false + ) + + setRefresh( refresh ? false : true ) + + } + return (
@@ -75,6 +98,23 @@ const LinkedItems = ({ field_name='display_name' data={linked_item} /> + + + + + ) diff --git a/src/components/page/ticket/RelatedTickets.jsx b/src/components/page/ticket/RelatedTickets.jsx index 5cc1813..6b6e162 100644 --- a/src/components/page/ticket/RelatedTickets.jsx +++ b/src/components/page/ticket/RelatedTickets.jsx @@ -16,6 +16,7 @@ const RelatedTickets = ({ const [ page_data, setPageData ] = useState(null) const [ metadata, setMetaData ] = useState(null) + const [ refresh, setRefresh ] = useState(false) useEffect(() => { @@ -29,8 +30,23 @@ const RelatedTickets = ({ } ) - },[ data_url ]) + },[ data_url, refresh ]) + const handleDeleteRelatedTicket = (e) => { + + let url = `${data_url}/${e.currentTarget.id}` + + console.log(`Removing Dependent ticket ${url}`) + + apiFetch( + url, + null, + 'DELETE' + ) + + setRefresh( refresh ? false : true ) + + } return (
{related_name} + + - + + + diff --git a/src/components/page/ticket/TicketComment.jsx b/src/components/page/ticket/TicketComment.jsx index 990b81f..ad282b6 100644 --- a/src/components/page/ticket/TicketComment.jsx +++ b/src/components/page/ticket/TicketComment.jsx @@ -8,17 +8,19 @@ import IconLoader from "../../IconLoader" import { secondsToTime } from "../../../layout/Ticket" import Section from "../../Section" import TicketCommentForm from "./TicketCommentForm" +import TicketComments from "./TicketComments" const TicketComment = ({ - discussion = false, comment_data = {}, metadata = null, ticket_id = null, post_url, edit_callback = null, - callback_value = null + callback_value = null, + parent_comment = null, + new_comment_url = null, }) => { if( String(post_url).includes('?') ) { @@ -26,6 +28,12 @@ const TicketComment = ({ post_url = String(post_url).split('?')[0] } + const [ comment_metadata, setCommentMetadata ] = useState( metadata ) + + const [ comment_page_data, setCommentPageData ] = useState( comment_data ) + + const [ start_thread, setStartThread ] = useState( false ) + let comment_header = ' wrote' let comment_class = 'comment comment-type-default' @@ -36,19 +44,19 @@ const TicketComment = ({ try { - if( isNaN(comment_data.comment_type) ) { + if( isNaN(comment_page_data.comment_type) ) { - comment_type = comment_data.comment_type + comment_type = comment_page_data.comment_type }else { - comment_type = String(metadata.fields.comment_type.choices[Number(comment_data.comment_type)-1].display_name).toLowerCase() + comment_type = String(comment_metadata.fields.comment_type.choices[Number(comment_page_data.comment_type)-1].display_name).toLowerCase() } }catch( e ) { - console.log('error: ' + comment_data.id ) + console.error('error: ' + comment_page_data.id ) } @@ -80,76 +88,87 @@ const TicketComment = ({ const comment_header_text_updated = (Updated ) const comment_header_text = ( - metadata && comment_data && + comment_metadata && comment_page_data &&
{comment_header} on {comment_updated && {comment_header_text_updated} }
) - const [threads_url, setThreadsURL] = useState(comment_data._urls.threads ? String(comment_data._urls.threads).split('api/v2')[1] : null) - - const [ threads, setThreads ] = useState(null) - const [ reload, setRelaod ] = useState(false) useEffect(() => { - if( threads_url ) { - apiFetch( - threads_url + '?page[size]=500', - (data) => { - setThreads(data) - }, - undefined, - undefined, - false - ) - - setRelaod(false) - } - },[ reload, threads_url ]) + if( ! comment_metadata ) { + + async function do_fetch() { + + await apiFetch( + comment_page_data._urls._self, + null, + 'OPTIONS' + ) + .then((result) => { + + if( result.status === 200 ) { + + if( result.api_metadata !== null ) { + + setCommentMetadata(result.api_metadata) + + } + } + }) + } + + do_fetch() + + }; + + }, []) + if( comment_type === 'action' ) { return( -
+ comment_metadata && +
   
@@ -157,14 +176,15 @@ const TicketComment = ({ } const header_icons = ( -
- {comment_data['parent'] == null && + comment_page_data && +
+ {comment_page_data['parent'] == null && { - setThreads( { - results: [] - } ) + // if(threads.results.length === 0) { + setStartThread( (start_thread ? false : true) ) + // } }}> { editing && { setIsEditing(false) }} - commentCallback={() => { + commentCallback={(response) => { + + setCommentPageData(response.api_page_data); + setIsEditing(false) edit_callback( callback_value ? false : true ) }} @@ -231,67 +255,52 @@ const TicketComment = ({ )} > -
- { comment_data.source && + { comment_page_data.source &&
} - {comment_data.status && + {comment_page_data.status &&
} - {comment_data.responsible_user && + {comment_page_data.assignee &&
- +
} - {comment_data.responsible_team && -
- - - - -
} - - { comment_data.category && + { comment_page_data.category &&
} @@ -302,9 +311,9 @@ const TicketComment = ({
@@ -312,50 +321,50 @@ const TicketComment = ({
- { comment_data.planned_start_date && + { comment_page_data.planned_start_date &&
} - { comment_data.planned_finish_date && + { comment_page_data.planned_finish_date &&
} - { comment_data.real_start_date && + { comment_page_data.real_start_date &&
} - { comment_data.real_finish_date && + { comment_page_data.real_finish_date &&
} @@ -363,7 +372,7 @@ const TicketComment = ({
- {secondsToTime(comment_data['duration'])} + {secondsToTime(comment_page_data['duration'])}
@@ -371,7 +380,8 @@ const TicketComment = ({
} - { threads && + + { (comment_page_data._urls?.threads || start_thread ) &&
-
    - { threads.results && - threads.results.map((comment, index) => ( -
  • - { - setRelaod(true) - }} - /> -
  • - ))} -
  • - { - setRelaod(true) - - setThreadsURL(post_url + '/' + comment_data['id'] + '/threads') - }} - /> -
  • -
+ { console.log(`Thread Start`) } +
} ); diff --git a/src/components/page/ticket/TicketCommentForm.jsx b/src/components/page/ticket/TicketCommentForm.jsx index 2954886..9f404dc 100644 --- a/src/components/page/ticket/TicketCommentForm.jsx +++ b/src/components/page/ticket/TicketCommentForm.jsx @@ -33,105 +33,56 @@ const TicketCommentForm = ({ const formId = useId(); - const[ comment_type, setCommentType ] = useState('') + const[ comment_type, setCommentType ] = useState( is_edit ? comment_data['comment_type'] : 'comment' ) - let edit_form_data = { - // 'comment_type': 'comment' - } - - let is_task_comment = false - - let is_solution_comment = false + const [ comment_metadata, setCommentMetadata ] = useState(metadata) - let is_notification_comment = false - - const [form_data, setFormData] = useState(edit_form_data) - - const [task_comment, setTaskComment] = useState( is_task_comment ) + /* + ToDo:Uupdate Centurion Serializer so that comment type does not + have to be sent as the viewset can determin as the URL is set by + the view serializer. + */ + const [form_data, setFormData] = useState({ + ['comment_type']: comment_type + }) const user = useContext(UserContext) - if( String(post_url).includes('?') ) { - console.log('url has qs') - post_url = String(post_url).split('?')[0].replace('/comment', '') - } - - let HTTP_METHOD = 'POST' - - if( is_edit ) { - - post_url = comment_data['_urls']['_self'] - HTTP_METHOD = 'PATCH' - } else { - - post_url = post_url.replace('/comment', '/' + form_data['comment_type']) - } - let comment_class = 'comment-type-default comment-form' - if( comment_data && is_edit ) { + + useEffect(() => { + if( ! is_edit && ! comment_metadata ) { - } + apiFetch( + comment_data?.id ? comment_data._urls._self : post_url.replace('/comment',`/${comment_type}`) + ) + .then((result) => { + if( result.status == 200 ) { - useEffect(() => { + if( result.api_metadata !== null ) { + setCommentMetadata(result.api_metadata) - console.log(`menu entry click value ${comment_type}`) - - for( let meta_comment_type of metadata.fields['comment_type'].choices) { - - if( - Number(comment_data['comment_type']) === Number(meta_comment_type.value) - || Number(form_data['comment_type']) === Number(meta_comment_type.value) - ) { - - setCommentType(String(meta_comment_type.display_name).toLowerCase()) - - } else if( - comment_data['comment_type'] === meta_comment_type.value - || form_data['comment_type'] === meta_comment_type.value - ) { - - setCommentType(String(meta_comment_type.display_name).toLowerCase()) - - }else { - - setCommentType(String('comment')) - } - - } - - console.log(`menu entry is ${comment_type}`) - - if( comment_type === 'task' ) { - - is_task_comment = true - - }else if( comment_type === 'solution' ) { - - is_solution_comment = true - - }else if( comment_type === 'notification' ) { - - is_notification_comment = true - - } - - }, [ - metadata - ]) + } + } + } + ) + } else { + } + }, [ + comment_type, + ]) const handleChange = (e) => { - console.log(`pos url is ${post_url}`) - let field_value = e.target.value if( e.target.type === 'checkbox' ) { @@ -140,18 +91,6 @@ const TicketCommentForm = ({ } - if( ! form_data.comment_type && ! is_edit ) { - - for( let comment_type_choice of metadata.fields['comment_type'].choices) { - - if( comment_type === String(comment_type_choice.value).toLowerCase() ) { - - setFormData((prevState) => ({ ...prevState, ['comment_type']: comment_type_choice.value })) - - } - } - } - if( ! form_data.ticket && ticket_id ) { setFormData((prevState) => ({ ...prevState, ['ticket']: ticket_id })) @@ -173,7 +112,7 @@ const TicketCommentForm = ({ return ( - metadata && + comment_metadata &&
@@ -189,7 +128,7 @@ const TicketCommentForm = ({ for (let [key, value] of Object.entries(form_data)) { - if( String(metadata.fields[key].type).toLowerCase() === 'datetime' ) { + if( String(comment_metadata.fields[key].type).toLowerCase() === 'datetime' ) { value = FormatTime({ time: String(value), @@ -207,10 +146,12 @@ const TicketCommentForm = ({ } const response = await apiFetch( - post_url, + is_edit ? comment_data._urls._self : post_url.replace('/comment',`/${comment_type}`), setFormError, - HTTP_METHOD, - processed_form_data + is_edit ? 'PATCH' : 'POST', + processed_form_data, + false, + true ) if( @@ -221,13 +162,16 @@ const TicketCommentForm = ({ ) ) { - commentCallback(); - is_task_comment = false - is_solution_comment = false - is_notification_comment = false + commentCallback(response); + e.target.reset(); - setFormData(edit_form_data); - setTaskComment(false) + + // Reset the form + setCommentMetadata(metadata) + setCommentType('comment') + setFormData({ + ['comment_type']: 'comment' + }); } }} @@ -237,32 +181,24 @@ const TicketCommentForm = ({ } - {task_comment && - } @@ -270,7 +206,7 @@ const TicketCommentForm = ({