├── .eslintrc.js ├── .flake8 ├── .git-blame-ignore-revs ├── .github ├── AgentListView.png ├── Hero2.png ├── ISSUE_TEMPLATE │ ├── bug_report.yaml │ └── feature_request.yaml ├── KB.png ├── Search2.png ├── hd-logo.svg ├── helpers │ ├── .flake8_strict │ ├── install.sh │ └── site_config_mariadb.json ├── release.yml ├── try-on-f-cloud-button.svg └── workflows │ ├── build.yml │ ├── lint.yml │ └── server-tests.yml ├── .gitignore ├── .gitmodules ├── .lintstagedrc ├── .pre-commit-config.yaml ├── .prettierrc.json ├── LICENSE ├── MANIFEST.in ├── README.md ├── commitlint.config.js ├── desk ├── .gitignore ├── index.html ├── lucide.ts ├── package.json ├── postcss.config.js ├── public │ ├── article-2.png │ ├── article.png │ ├── desk.png │ ├── favicon.svg │ ├── manifest │ │ ├── apple-icon-180.png │ │ ├── apple-splash-1125-2436.jpg │ │ ├── apple-splash-1136-640.jpg │ │ ├── apple-splash-1170-2532.jpg │ │ ├── apple-splash-1179-2556.jpg │ │ ├── apple-splash-1242-2208.jpg │ │ ├── apple-splash-1242-2688.jpg │ │ ├── apple-splash-1284-2778.jpg │ │ ├── apple-splash-1290-2796.jpg │ │ ├── apple-splash-1334-750.jpg │ │ ├── apple-splash-1488-2266.jpg │ │ ├── apple-splash-1536-2048.jpg │ │ ├── apple-splash-1620-2160.jpg │ │ ├── apple-splash-1640-2360.jpg │ │ ├── apple-splash-1668-2224.jpg │ │ ├── apple-splash-1668-2388.jpg │ │ ├── apple-splash-1792-828.jpg │ │ ├── apple-splash-2048-1536.jpg │ │ ├── apple-splash-2048-2732.jpg │ │ ├── apple-splash-2160-1620.jpg │ │ ├── apple-splash-2208-1242.jpg │ │ ├── apple-splash-2224-1668.jpg │ │ ├── apple-splash-2266-1488.jpg │ │ ├── apple-splash-2360-1640.jpg │ │ ├── apple-splash-2388-1668.jpg │ │ ├── apple-splash-2436-1125.jpg │ │ ├── apple-splash-2532-1170.jpg │ │ ├── apple-splash-2556-1179.jpg │ │ ├── apple-splash-2688-1242.jpg │ │ ├── apple-splash-2732-2048.jpg │ │ ├── apple-splash-2778-1284.jpg │ │ ├── apple-splash-2796-1290.jpg │ │ ├── apple-splash-640-1136.jpg │ │ ├── apple-splash-750-1334.jpg │ │ ├── apple-splash-828-1792.jpg │ │ ├── manifest-icon-192.maskable.png │ │ └── manifest-icon-512.maskable.png │ └── videos │ │ └── createInviteContact.mp4 ├── src │ ├── App.vue │ ├── assets │ │ ├── custom_icons │ │ │ ├── bullet.svg │ │ │ ├── check.svg │ │ │ ├── chevron-down.svg │ │ │ ├── chevron-up.svg │ │ │ ├── circle_check.svg │ │ │ ├── comment.svg │ │ │ ├── corner_up_left.svg │ │ │ ├── customers.svg │ │ │ ├── dislike.svg │ │ │ ├── document.svg │ │ │ ├── external_link.svg │ │ │ ├── f-desk.svg │ │ │ ├── filter.svg │ │ │ ├── folder.svg │ │ │ ├── knowledge-based.svg │ │ │ ├── like.svg │ │ │ ├── lock.svg │ │ │ ├── log_out.svg │ │ │ ├── priority_high.svg │ │ │ ├── priority_low.svg │ │ │ ├── priority_medium.svg │ │ │ ├── priority_urgent.svg │ │ │ ├── reports.svg │ │ │ ├── select.svg │ │ │ ├── settings.svg │ │ │ ├── sla-fail.svg │ │ │ ├── sort-ascending.svg │ │ │ ├── text_editor_icons │ │ │ │ ├── Bold.svg │ │ │ │ ├── Bullet list.svg │ │ │ │ ├── Clear Formatting.svg │ │ │ │ ├── Code.svg │ │ │ │ ├── Decrease Indent.svg │ │ │ │ ├── Image-add.svg │ │ │ │ ├── Increase Indent.svg │ │ │ │ ├── Italic.svg │ │ │ │ ├── Left Align.svg │ │ │ │ ├── Link-url.svg │ │ │ │ ├── Numbered List.svg │ │ │ │ ├── Quote.svg │ │ │ │ ├── Text Color.svg │ │ │ │ └── Underline.svg │ │ │ ├── ticket.svg │ │ │ ├── time.svg │ │ │ └── user-plus.svg │ │ ├── icons │ │ │ ├── activity.svg │ │ │ ├── add.svg │ │ │ ├── alert-circle.svg │ │ │ ├── at-sign.svg │ │ │ ├── attachment.svg │ │ │ ├── call.svg │ │ │ ├── chat.svg │ │ │ ├── comment.svg │ │ │ ├── contact-solid.svg │ │ │ ├── contact.svg │ │ │ ├── customer-solid.svg │ │ │ ├── customer.svg │ │ │ ├── dashboard-solid.svg │ │ │ ├── dashboard.svg │ │ │ ├── delete.svg │ │ │ ├── details.svg │ │ │ ├── dot-horizontal.svg │ │ │ ├── email.svg │ │ │ ├── file.svg │ │ │ ├── filter.svg │ │ │ ├── hash.svg │ │ │ ├── home.svg │ │ │ ├── knowledge-base-solid.svg │ │ │ ├── knowledge-base.svg │ │ │ ├── location.svg │ │ │ ├── mail.svg │ │ │ ├── pdf.svg │ │ │ ├── right-chevron.svg │ │ │ ├── settings-solid.svg │ │ │ ├── settings.svg │ │ │ ├── sort-arrow.svg │ │ │ ├── teams.svg │ │ │ ├── ticket-solid.svg │ │ │ ├── ticket.svg │ │ │ ├── web-link.svg │ │ │ └── web.svg │ │ ├── images │ │ │ ├── frappe-mail.svg │ │ │ ├── gmail.png │ │ │ ├── outlook.png │ │ │ ├── sendgrid.png │ │ │ ├── sparkpost.webp │ │ │ ├── yahoo.png │ │ │ └── yandex.png │ │ └── logos │ │ │ └── HDLogo.vue │ ├── components │ │ ├── Apps.vue │ │ ├── AssignmentModal.vue │ │ ├── AttachmentItem.vue │ │ ├── Autocomplete.vue │ │ ├── BrandLogo.vue │ │ ├── CannedResponseSelectorModal.vue │ │ ├── CommentBox.vue │ │ ├── CommentTextEditor.vue │ │ ├── CommunicationArea.vue │ │ ├── CustomActions.vue │ │ ├── DiscardButton.vue │ │ ├── EmailArea.vue │ │ ├── EmailContent.vue │ │ ├── EmailEditor.vue │ │ ├── EmptyState.vue │ │ ├── FadedScrollableDiv.vue │ │ ├── HistoryBox.vue │ │ ├── Icon.vue │ │ ├── IconPicker.vue │ │ ├── LayoutHeader.vue │ │ ├── ListRows.vue │ │ ├── ListViewBuilder.vue │ │ ├── MultiSelect.vue │ │ ├── MultiSelectInput.vue │ │ ├── MultipleAvatar.vue │ │ ├── NestedPopover.vue │ │ ├── PageTitle.vue │ │ ├── Pill.vue │ │ ├── SearchArticles.vue │ │ ├── SearchComplete.vue │ │ ├── SearchPopover.vue │ │ ├── Section.vue │ │ ├── Settings │ │ │ ├── AgentCard.vue │ │ │ ├── Agents.vue │ │ │ ├── Branding.vue │ │ │ ├── EmailAccountCard.vue │ │ │ ├── EmailAccountList.vue │ │ │ ├── EmailAdd.vue │ │ │ ├── EmailConfig.vue │ │ │ ├── EmailEdit.vue │ │ │ ├── EmailProviderIcon.vue │ │ │ ├── NewTeamModal.vue │ │ │ ├── SettingsModal.vue │ │ │ ├── Teams │ │ │ │ ├── TeamEdit.vue │ │ │ │ ├── TeamsConfig.vue │ │ │ │ └── TeamsList.vue │ │ │ ├── agents.ts │ │ │ └── emailConfig.ts │ │ ├── SidebarLink.vue │ │ ├── StarRating.vue │ │ ├── TextEditor.vue │ │ ├── UniInput.vue │ │ ├── UniInput2.vue │ │ ├── UserAvatar.vue │ │ ├── UserMenu.vue │ │ ├── ViewBreadcrumbs.vue │ │ ├── ViewModal.vue │ │ ├── canned-response │ │ │ ├── CannedResponseModal.vue │ │ │ └── index.js │ │ ├── command-palette │ │ │ ├── CP.vue │ │ │ ├── CPGroup.vue │ │ │ └── CPGroupResult.vue │ │ ├── desk │ │ │ └── global │ │ │ │ ├── AddNewAgentsDialog.vue │ │ │ │ ├── CustomIcons.vue │ │ │ │ ├── NewContactDialog.vue │ │ │ │ └── NewCustomerDialog.vue │ │ ├── dialogs.jsx │ │ ├── frappe-ui │ │ │ ├── Autocomplete.vue │ │ │ ├── Dropdown.vue │ │ │ └── Link.vue │ │ ├── icons │ │ │ ├── ActivityIcon.vue │ │ │ ├── AppsIcon.vue │ │ │ ├── AscendingIcon.vue │ │ │ ├── AttachmentIcon.vue │ │ │ ├── ColumnsIcon.vue │ │ │ ├── CommentIcon.vue │ │ │ ├── DescendingIcon.vue │ │ │ ├── DetailsIcon.vue │ │ │ ├── DotIcon.vue │ │ │ ├── DragIcon.vue │ │ │ ├── EditIcon.vue │ │ │ ├── EmailAtIcon.vue │ │ │ ├── EmailIcon.vue │ │ │ ├── FilterIcon.vue │ │ │ ├── FrappeCloudIcon.vue │ │ │ ├── IndicatorIcon.vue │ │ │ ├── InviteCustomer.vue │ │ │ ├── LinkIcon.vue │ │ │ ├── OrganizationsIcon.vue │ │ │ ├── PhoneIcon.vue │ │ │ ├── PinIcon.vue │ │ │ ├── RefreshIcon.vue │ │ │ ├── ReloadIcon.vue │ │ │ ├── ReplyAllIcon.vue │ │ │ ├── ReplyIcon.vue │ │ │ ├── SelectIcon.vue │ │ │ ├── SortIcon.vue │ │ │ ├── ThumbsDownFilledIcon.vue │ │ │ ├── ThumbsDownIcon.vue │ │ │ ├── ThumbsUpFilledIcon.vue │ │ │ ├── ThumbsUpIcon.vue │ │ │ ├── TicketIcon.vue │ │ │ ├── UnpinIcon.vue │ │ │ └── index.ts │ │ ├── index.ts │ │ ├── knowledge-base │ │ │ ├── ArticleCard.vue │ │ │ ├── ArticleFeedback.vue │ │ │ ├── CategoryFolder.vue │ │ │ ├── CategoryFolderContainer.vue │ │ │ ├── CategoryModal.vue │ │ │ ├── MergeCategoryModal.vue │ │ │ └── MoveToCategoryModal.vue │ │ ├── layouts │ │ │ ├── AppHeader.vue │ │ │ ├── DesktopLayout.vue │ │ │ ├── MobileAppHeader.vue │ │ │ ├── MobileLayout.vue │ │ │ ├── MobileSidebar.vue │ │ │ ├── Sidebar.vue │ │ │ └── layoutSettings.ts │ │ ├── notifications │ │ │ └── Notifications.vue │ │ ├── ticket │ │ │ ├── ActivityHeader.vue │ │ │ ├── ExportModal.vue │ │ │ ├── TicketAgentActivities.vue │ │ │ ├── TicketAgentContact.vue │ │ │ ├── TicketAgentDetails.vue │ │ │ ├── TicketAgentFields.vue │ │ │ ├── TicketAgentSidebar.vue │ │ │ ├── TicketCustomerSidebar.vue │ │ │ ├── TicketFeedback.vue │ │ │ ├── TicketMergeModal.vue │ │ │ ├── TicketSplitModal.vue │ │ │ └── index.ts │ │ └── view-controls │ │ │ ├── ColumnSettings.vue │ │ │ ├── Filter.vue │ │ │ ├── QuickFilterField.vue │ │ │ ├── QuickFilters.vue │ │ │ ├── Reload.vue │ │ │ ├── SortBy.vue │ │ │ └── index.ts │ ├── composables │ │ ├── device.ts │ │ ├── error.ts │ │ ├── fc.ts │ │ ├── formCustomisation.ts │ │ ├── index.ts │ │ ├── mobile.ts │ │ ├── screen.ts │ │ └── useView.ts │ ├── dayjs.ts │ ├── index.css │ ├── main.js │ ├── pages │ │ ├── CannedResponses.vue │ │ ├── CustomerPortalRoot.vue │ │ ├── HRoot.vue │ │ ├── InvalidPage.vue │ │ ├── KeymapDialog.vue │ │ ├── MobileNotifications.vue │ │ ├── desk │ │ │ ├── AgentRoot.vue │ │ │ ├── contact │ │ │ │ ├── ContactDialog.vue │ │ │ │ ├── Contacts.vue │ │ │ │ └── dialogState.ts │ │ │ └── customer │ │ │ │ ├── CustomerDialog.vue │ │ │ │ └── Customers.vue │ │ ├── knowledge-base │ │ │ ├── Article.vue │ │ │ ├── Articles.vue │ │ │ ├── KnowledgeBaseAgent.vue │ │ │ ├── KnowledgeBaseCustomer.vue │ │ │ └── NewArticle.vue │ │ └── ticket │ │ │ ├── MobileTicketAgent.vue │ │ │ ├── TicketAgent.vue │ │ │ ├── TicketBreadcrumbs.vue │ │ │ ├── TicketCommunication.vue │ │ │ ├── TicketConversation.vue │ │ │ ├── TicketCustomer.vue │ │ │ ├── TicketCustomerTemplateFields.vue │ │ │ ├── TicketFeedback.vue │ │ │ ├── TicketNew.vue │ │ │ ├── TicketTextEditor.vue │ │ │ ├── Tickets.vue │ │ │ ├── data.ts │ │ │ ├── modalStates.ts │ │ │ └── symbols.ts │ ├── router │ │ └── index.ts │ ├── socket.ts │ ├── stores │ │ ├── agent.ts │ │ ├── auth.ts │ │ ├── config.ts │ │ ├── contact.ts │ │ ├── globalStore.ts │ │ ├── keymap.ts │ │ ├── knowledgeBase.ts │ │ ├── notification.ts │ │ ├── sidebar.ts │ │ ├── ticketStatus.ts │ │ └── user.ts │ ├── telemetry.ts │ ├── tiptap-extensions.ts │ ├── types.ts │ └── utils.ts ├── tailwind.config.js ├── tsconfig.json ├── vite.config.js └── yarn.lock ├── docker ├── docker-compose.yml └── init.sh ├── helpdesk ├── __init__.py ├── api │ ├── agent.py │ ├── article.py │ ├── auth.py │ ├── config.py │ ├── doc.py │ ├── general.py │ ├── knowledge_base.py │ ├── onboarding.py │ ├── permission.py │ ├── session.py │ ├── settings.py │ ├── telemetry.py │ └── ticket.py ├── config │ ├── __init__.py │ ├── desktop.py │ └── docs.py ├── consts.py ├── extends │ ├── assignment_rule.py │ └── data_import.py ├── helpdesk │ ├── __init__.py │ ├── doctype │ │ ├── __init__.py │ │ ├── hd_action │ │ │ ├── __init__.py │ │ │ ├── hd_action.js │ │ │ ├── hd_action.json │ │ │ ├── hd_action.py │ │ │ └── test_hd_action.py │ │ ├── hd_agent │ │ │ ├── __init__.py │ │ │ ├── hd_agent.js │ │ │ ├── hd_agent.json │ │ │ ├── hd_agent.py │ │ │ └── test_hd_agent.py │ │ ├── hd_article │ │ │ ├── __init__.py │ │ │ ├── hd_article.js │ │ │ ├── hd_article.json │ │ │ ├── hd_article.py │ │ │ └── test_hd_article.py │ │ ├── hd_article_category │ │ │ ├── __init__.py │ │ │ ├── hd_article_category.js │ │ │ ├── hd_article_category.json │ │ │ ├── hd_article_category.py │ │ │ └── test_hd_article_category.py │ │ ├── hd_article_feedback │ │ │ ├── __init__.py │ │ │ ├── hd_article_feedback.js │ │ │ ├── hd_article_feedback.json │ │ │ ├── hd_article_feedback.py │ │ │ └── test_hd_article_feedback.py │ │ ├── hd_canned_response │ │ │ ├── __init__.py │ │ │ ├── hd_canned_response.js │ │ │ ├── hd_canned_response.json │ │ │ ├── hd_canned_response.py │ │ │ └── test_hd_canned_response.py │ │ ├── hd_customer │ │ │ ├── __init__.py │ │ │ ├── hd_customer.js │ │ │ ├── hd_customer.json │ │ │ ├── hd_customer.py │ │ │ └── test_hd_customer.py │ │ ├── hd_desk_account_request │ │ │ ├── __init__.py │ │ │ ├── hd_desk_account_request.js │ │ │ ├── hd_desk_account_request.json │ │ │ ├── hd_desk_account_request.py │ │ │ └── test_hd_desk_account_request.py │ │ ├── hd_escalation_rule │ │ │ ├── __init__.py │ │ │ ├── hd_escalation_rule.js │ │ │ ├── hd_escalation_rule.json │ │ │ ├── hd_escalation_rule.py │ │ │ └── test_hd_escalation_rule.py │ │ ├── hd_form_script │ │ │ ├── __init__.py │ │ │ ├── hd_form_script.js │ │ │ ├── hd_form_script.json │ │ │ ├── hd_form_script.py │ │ │ └── test_hd_form_script.py │ │ ├── hd_holiday │ │ │ ├── README.md │ │ │ ├── __init__.py │ │ │ ├── hd_holiday.json │ │ │ └── hd_holiday.py │ │ ├── hd_notification │ │ │ ├── __init__.py │ │ │ ├── hd_notification.js │ │ │ ├── hd_notification.json │ │ │ ├── hd_notification.py │ │ │ ├── test_hd_notification.py │ │ │ └── utils.py │ │ ├── hd_organization │ │ │ ├── __init__.py │ │ │ ├── hd_organization.js │ │ │ ├── hd_organization.json │ │ │ ├── hd_organization.py │ │ │ └── test_hd_organization.py │ │ ├── hd_organization_contact_item │ │ │ ├── __init__.py │ │ │ ├── hd_organization_contact_item.json │ │ │ └── hd_organization_contact_item.py │ │ ├── hd_pause_service_level_agreement_on_status │ │ │ ├── __init__.py │ │ │ ├── hd_pause_service_level_agreement_on_status.json │ │ │ └── hd_pause_service_level_agreement_on_status.py │ │ ├── hd_portal_signup_request │ │ │ ├── __init__.py │ │ │ ├── hd_portal_signup_request.js │ │ │ ├── hd_portal_signup_request.json │ │ │ ├── hd_portal_signup_request.py │ │ │ └── test_hd_portal_signup_request.py │ │ ├── hd_preset_filter │ │ │ ├── __init__.py │ │ │ ├── hd_preset_filter.js │ │ │ ├── hd_preset_filter.json │ │ │ ├── hd_preset_filter.py │ │ │ └── test_hd_preset_filter.py │ │ ├── hd_preset_filter_item │ │ │ ├── __init__.py │ │ │ ├── hd_preset_filter_item.json │ │ │ └── hd_preset_filter_item.py │ │ ├── hd_service_day │ │ │ ├── __init__.py │ │ │ ├── hd_service_day.json │ │ │ └── hd_service_day.py │ │ ├── hd_service_holiday_list │ │ │ ├── README.md │ │ │ ├── __init__.py │ │ │ ├── hd_service_holiday_list.js │ │ │ ├── hd_service_holiday_list.json │ │ │ ├── hd_service_holiday_list.py │ │ │ ├── hd_service_holiday_list_calendar.js │ │ │ ├── hd_service_holiday_list_dashboard.py │ │ │ ├── test_hd_service_holiday_list.py │ │ │ └── test_records.json │ │ ├── hd_service_level_agreement │ │ │ ├── __init__.py │ │ │ ├── hd_service_level_agreement.js │ │ │ ├── hd_service_level_agreement.json │ │ │ ├── hd_service_level_agreement.py │ │ │ ├── hd_service_level_agreement_dashboard.py │ │ │ ├── patches │ │ │ │ └── missing_sla_creation.py │ │ │ ├── test_hd_service_level_agreement.py │ │ │ └── utils.py │ │ ├── hd_service_level_agreement_fulfilled_on_status │ │ │ ├── __init__.py │ │ │ ├── hd_service_level_agreement_fulfilled_on_status.json │ │ │ └── hd_service_level_agreement_fulfilled_on_status.py │ │ ├── hd_service_level_priority │ │ │ ├── __init__.py │ │ │ ├── hd_service_level_priority.json │ │ │ └── hd_service_level_priority.py │ │ ├── hd_settings │ │ │ ├── __init__.py │ │ │ ├── hd_settings.js │ │ │ ├── hd_settings.json │ │ │ ├── hd_settings.py │ │ │ └── test_hd_settings.py │ │ ├── hd_stopword │ │ │ ├── __init__.py │ │ │ ├── hd_stopword.js │ │ │ ├── hd_stopword.json │ │ │ ├── hd_stopword.py │ │ │ └── test_hd_stopword.py │ │ ├── hd_support_search_source │ │ │ ├── __init__.py │ │ │ ├── hd_support_search_source.json │ │ │ └── hd_support_search_source.py │ │ ├── hd_synonym │ │ │ ├── __init__.py │ │ │ ├── hd_synonym.json │ │ │ └── hd_synonym.py │ │ ├── hd_synonyms │ │ │ ├── __init__.py │ │ │ ├── hd_synonyms.js │ │ │ ├── hd_synonyms.json │ │ │ ├── hd_synonyms.py │ │ │ └── test_hd_synonyms.py │ │ ├── hd_team │ │ │ ├── __init__.py │ │ │ ├── hd_team.js │ │ │ ├── hd_team.json │ │ │ ├── hd_team.py │ │ │ └── test_hd_team.py │ │ ├── hd_team_member │ │ │ ├── __init__.py │ │ │ ├── hd_team_member.json │ │ │ └── hd_team_member.py │ │ ├── hd_ticket │ │ │ ├── __init__.py │ │ │ ├── api.py │ │ │ ├── hd_ticket.js │ │ │ ├── hd_ticket.json │ │ │ ├── hd_ticket.py │ │ │ ├── patches │ │ │ │ ├── fallback_ticket_type.py │ │ │ │ ├── feedback_in_master.py │ │ │ │ ├── first_responded_on.py │ │ │ │ └── replace_overdue_failed.py │ │ │ └── test_hd_ticket.py │ │ ├── hd_ticket_activity │ │ │ ├── __init__.py │ │ │ ├── hd_ticket_activity.js │ │ │ ├── hd_ticket_activity.json │ │ │ ├── hd_ticket_activity.py │ │ │ └── test_hd_ticket_activity.py │ │ ├── hd_ticket_comment │ │ │ ├── __init__.py │ │ │ ├── hd_ticket_comment.js │ │ │ ├── hd_ticket_comment.json │ │ │ ├── hd_ticket_comment.py │ │ │ └── test_hd_ticket_comment.py │ │ ├── hd_ticket_feedback_option │ │ │ ├── __init__.py │ │ │ ├── hd_ticket_feedback_option.js │ │ │ ├── hd_ticket_feedback_option.json │ │ │ ├── hd_ticket_feedback_option.py │ │ │ ├── patches │ │ │ │ ├── label_as_name.py │ │ │ │ └── ootb.py │ │ │ └── test_hd_ticket_feedback_option.py │ │ ├── hd_ticket_priority │ │ │ ├── __init__.py │ │ │ ├── hd_ticket_priority.js │ │ │ ├── hd_ticket_priority.json │ │ │ ├── hd_ticket_priority.py │ │ │ └── test_hd_ticket_priority.py │ │ ├── hd_ticket_template │ │ │ ├── __init__.py │ │ │ ├── api.py │ │ │ ├── hd_ticket_template.js │ │ │ ├── hd_ticket_template.json │ │ │ ├── hd_ticket_template.py │ │ │ └── test_hd_ticket_template.py │ │ ├── hd_ticket_template_field │ │ │ ├── __init__.py │ │ │ ├── hd_ticket_template_field.js │ │ │ ├── hd_ticket_template_field.json │ │ │ ├── hd_ticket_template_field.py │ │ │ └── test_hd_ticket_template_field.py │ │ ├── hd_ticket_type │ │ │ ├── __init__.py │ │ │ ├── hd_ticket_type.js │ │ │ ├── hd_ticket_type.json │ │ │ ├── hd_ticket_type.py │ │ │ └── test_hd_ticket_type.py │ │ └── hd_view │ │ │ ├── __init__.py │ │ │ ├── hd_view.js │ │ │ ├── hd_view.json │ │ │ ├── hd_view.py │ │ │ └── test_hd_view.py │ ├── hooks │ │ └── contact.py │ ├── report │ │ ├── __init__.py │ │ ├── first_response_time_for_tickets │ │ │ ├── __init__.py │ │ │ ├── first_response_time_for_tickets.js │ │ │ ├── first_response_time_for_tickets.json │ │ │ └── first_response_time_for_tickets.py │ │ ├── support_hour_distribution │ │ │ ├── __init__.py │ │ │ ├── support_hour_distribution.js │ │ │ ├── support_hour_distribution.json │ │ │ └── support_hour_distribution.py │ │ ├── ticket_analytics │ │ │ ├── __init__.py │ │ │ ├── test_ticket_analytics.py │ │ │ ├── ticket_analytics.js │ │ │ ├── ticket_analytics.json │ │ │ └── ticket_analytics.py │ │ ├── ticket_search_analysis │ │ │ ├── __init__.py │ │ │ ├── ticket_search_analysis.js │ │ │ ├── ticket_search_analysis.json │ │ │ └── ticket_search_analysis.py │ │ └── ticket_summary │ │ │ ├── __init__.py │ │ │ ├── ticket_summary.js │ │ │ ├── ticket_summary.json │ │ │ └── ticket_summary.py │ ├── utils │ │ └── email.py │ ├── web_form │ │ ├── __init__.py │ │ └── tickets │ │ │ ├── __init__.py │ │ │ ├── tickets.js │ │ │ ├── tickets.json │ │ │ └── tickets.py │ └── workspace │ │ └── helpdesk │ │ └── helpdesk.json ├── hooks.py ├── mixins │ └── mentions.py ├── modules.txt ├── overrides │ └── contact.py ├── patches.txt ├── patches │ ├── add_priority_integer.py │ ├── agent_manager_perms.py │ ├── change_app_name_to_helpdesk.py │ ├── communication_read_perm_agent.py │ ├── create_helpdesk_folder.py │ ├── default_article_category.py │ ├── naming_autoincrement.py │ ├── remove_agents_teams_default_views.py │ ├── rename_doctypes_prefix_with_hd.py │ ├── rename_frappedesk_module_references.py │ ├── template_remove_default_fields.py │ └── update_hd_team_users.py ├── search.py ├── setup │ ├── default_template.py │ ├── file.py │ ├── install.py │ ├── setup_wizard.py │ ├── ticket_feedback.py │ ├── ticket_type.py │ └── welcome_ticket.py ├── templates │ ├── components │ │ ├── contact_with_us.html │ │ ├── knowledge_base_title_with_search_bar.html │ │ └── search │ │ │ ├── search.html │ │ │ └── search.py │ ├── emails │ │ ├── email_verification.html │ │ ├── macros.html │ │ ├── new_reply_on_customer_portal_notification.html │ │ ├── new_reply_to_agent.html │ │ └── notification.html │ └── kb_base_template.html ├── utils.py └── www │ ├── __init__.py │ └── helpdesk │ ├── __init__.py │ └── index.py ├── package.json ├── pyproject.toml ├── screenshot.webp └── yarn.lock /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | es2021: true, 5 | node: true, 6 | }, 7 | extends: [ 8 | "eslint:recommended", 9 | "plugin:@typescript-eslint/recommended", 10 | "plugin:json/recommended", 11 | "plugin:vue/vue3-recommended", 12 | "plugin:tailwindcss/recommended", 13 | "plugin:prettier/recommended", 14 | ], 15 | parserOptions: { 16 | ecmaVersion: "latest", 17 | sourceType: "module", 18 | parser: "@typescript-eslint/parser", 19 | }, 20 | rules: { 21 | "tailwindcss/no-custom-classname": "off", 22 | "vue/multi-word-component-names": "off", 23 | }, 24 | }; 25 | -------------------------------------------------------------------------------- /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | ignore = 3 | E121, 4 | E126, 5 | E127, 6 | E128, 7 | E203, 8 | E225, 9 | E226, 10 | E231, 11 | E241, 12 | E251, 13 | E261, 14 | E265, 15 | E302, 16 | E303, 17 | E305, 18 | E402, 19 | E501, 20 | E741, 21 | W291, 22 | W292, 23 | W293, 24 | W391, 25 | W503, 26 | W504, 27 | F403, 28 | B007, 29 | B950, 30 | W191, 31 | E124, # closing bracket, irritating while writing QB code 32 | E131, # continuation line unaligned for hanging indent 33 | E123, # closing bracket does not match indentation of opening bracket's line 34 | E101, # ensured by use of black 35 | 36 | max-line-length = 200 37 | exclude=.github/helper/semgrep_rules -------------------------------------------------------------------------------- /.git-blame-ignore-revs: -------------------------------------------------------------------------------- 1 | # Since version 2.23 (released in August 2019), git-blame has a feature 2 | # to ignore or bypass certain commits. 3 | # 4 | # This file contains a list of commits that are not likely what you 5 | # are looking for in a blame, such as mass reformatting or renaming. 6 | # You can set this file as a default ignore file for blame by running 7 | # the following command. 8 | # 9 | # $ git config blame.ignoreRevsFile .git-blame-ignore-revs 10 | 11 | # bulk formatting 12 | b7fb25fe7c784c4dcf639cf954445ac8452a85d9 13 | 353aaae07a5f56c5686caa79ce5d1e2e1544494a 14 | -------------------------------------------------------------------------------- /.github/AgentListView.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/.github/AgentListView.png -------------------------------------------------------------------------------- /.github/Hero2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/.github/Hero2.png -------------------------------------------------------------------------------- /.github/KB.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/.github/KB.png -------------------------------------------------------------------------------- /.github/Search2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/.github/Search2.png -------------------------------------------------------------------------------- /.github/hd-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /.github/helpers/.flake8_strict: -------------------------------------------------------------------------------- 1 | [flake8] 2 | ignore = 3 | B007, 4 | B009, 5 | B010, 6 | B950, 7 | E101, 8 | E111, 9 | E114, 10 | E116, 11 | E117, 12 | E121, 13 | E122, 14 | E123, 15 | E124, 16 | E125, 17 | E126, 18 | E127, 19 | E128, 20 | E131, 21 | E201, 22 | E202, 23 | E203, 24 | E211, 25 | E221, 26 | E222, 27 | E223, 28 | E224, 29 | E225, 30 | E226, 31 | E228, 32 | E231, 33 | E241, 34 | E242, 35 | E251, 36 | E261, 37 | E262, 38 | E265, 39 | E266, 40 | E271, 41 | E272, 42 | E273, 43 | E274, 44 | E301, 45 | E302, 46 | E303, 47 | E305, 48 | E306, 49 | E402, 50 | E501, 51 | E502, 52 | E701, 53 | E702, 54 | E703, 55 | E741, 56 | F403, 57 | W191, 58 | W291, 59 | W292, 60 | W293, 61 | W391, 62 | W503, 63 | W504, 64 | E711, 65 | E129, 66 | F841, 67 | E713, 68 | E712, 69 | B023 70 | 71 | 72 | max-line-length = 200 73 | exclude=.github/helper/semgrep_rules,test_*.py -------------------------------------------------------------------------------- /.github/helpers/site_config_mariadb.json: -------------------------------------------------------------------------------- 1 | { 2 | "db_host": "127.0.0.1", 3 | "db_port": 3306, 4 | "db_name": "test_frappe", 5 | "db_password": "test_frappe", 6 | "auto_email_id": "test@example.com", 7 | "mail_server": "smtp.example.com", 8 | "mail_login": "test@example.com", 9 | "mail_password": "test", 10 | "admin_password": "admin", 11 | "root_login": "root", 12 | "root_password": "root", 13 | "host_name": "http://test_site:8000", 14 | "install_apps": ["helpdesk"], 15 | "throttle_user_limit": 100 16 | } 17 | -------------------------------------------------------------------------------- /.github/release.yml: -------------------------------------------------------------------------------- 1 | changelog: 2 | exclude: 3 | authors: 4 | - octocat 5 | categories: 6 | - title: Breaking Changes 🛠 7 | labels: 8 | - breaking-change 9 | - title: Bug Fixes 🐛 10 | labels: 11 | - bug-fix 12 | - title: Exciting New Features 🎉 13 | labels: 14 | - enhancement 15 | - feature 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | 4 | .DS_Store 5 | *.pyc 6 | *.egg-info 7 | *.swp 8 | *.log 9 | tags 10 | node_modules 11 | .vscode 12 | 13 | # Distribution / packaging 14 | .Python 15 | build/ 16 | develop-eggs/ 17 | dist/ 18 | downloads/ 19 | eggs/ 20 | .eggs/ 21 | lib/ 22 | lib64/ 23 | parts/ 24 | sdist/ 25 | var/ 26 | wheels/ 27 | pip-wheel-metadata/ 28 | share/python-wheels/ 29 | *.egg-info/ 30 | .installed.cfg 31 | *.egg 32 | 33 | helpdesk/docs/current 34 | helpdesk/public/desk 35 | helpdesk/public/frontend 36 | helpdesk/public/node_modules 37 | helpdesk/www/helpdesk/index.html 38 | desk/components.d.ts 39 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "frappe-ui"] 2 | path = frappe-ui 3 | url = https://github.com/frappe/frappe-ui.git 4 | -------------------------------------------------------------------------------- /.lintstagedrc: -------------------------------------------------------------------------------- 1 | { 2 | "**/*.{vue,js,ts,json}": "eslint --fix" 3 | } 4 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | exclude: "node_modules|.git" 2 | default_stages: [commit] 3 | fail_fast: false 4 | 5 | repos: 6 | - repo: https://github.com/psf/black 7 | rev: 22.6.0 8 | hooks: 9 | - id: black 10 | types_or: [python, pyi] 11 | 12 | - repo: https://github.com/PyCQA/flake8 13 | rev: 5.0.4 14 | hooks: 15 | - id: flake8 16 | additional_dependencies: 17 | - flake8-bugbear 18 | - flake8-comprehensions 19 | - flake8-simplify 20 | args: ['--config', '.github/helpers/.flake8_strict'] 21 | 22 | - repo: https://github.com/pre-commit/mirrors-prettier 23 | rev: v2.6.2 24 | hooks: 25 | - id: prettier 26 | additional_dependencies: 27 | - prettier@2.6.2 28 | types: [file] 29 | types_or: [javascript, vue, html] 30 | 31 | - repo: https://github.com/timothycrosley/isort 32 | rev: 5.12.0 33 | hooks: 34 | - id: isort 35 | args: [--profile, black] 36 | 37 | ci: 38 | autoupdate_schedule: weekly 39 | skip: [] 40 | submodules: false 41 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "tabWidth": 2, 4 | "useTabs": false 5 | } 6 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include MANIFEST.in 2 | include requirements.txt 3 | include *.json 4 | include *.md 5 | include *.py 6 | include *.txt 7 | recursive-include helpdesk *.css 8 | recursive-include helpdesk *.csv 9 | recursive-include helpdesk *.html 10 | recursive-include helpdesk *.ico 11 | recursive-include helpdesk *.js 12 | recursive-include helpdesk *.json 13 | recursive-include helpdesk *.md 14 | recursive-include helpdesk *.png 15 | recursive-include helpdesk *.py 16 | recursive-include helpdesk *.svg 17 | recursive-include helpdesk *.txt 18 | recursive-exclude helpdesk *.pyc 19 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parserPreset: "conventional-changelog-conventionalcommits", 3 | rules: { 4 | "subject-empty": [2, "never"], 5 | "type-case": [2, "always", "lower-case"], 6 | "type-empty": [2, "never"], 7 | "type-enum": [ 8 | 2, 9 | "always", 10 | [ 11 | "build", 12 | "chore", 13 | "ci", 14 | "docs", 15 | "feat", 16 | "fix", 17 | "perf", 18 | "refactor", 19 | "revert", 20 | "style", 21 | "test", 22 | ], 23 | ], 24 | }, 25 | }; 26 | -------------------------------------------------------------------------------- /desk/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | *.local 6 | dev-dist 7 | 8 | # local env files 9 | .env.local 10 | .env.*.local 11 | 12 | # Log files 13 | npm-debug.log* 14 | yarn-debug.log* 15 | yarn-error.log* 16 | 17 | # Editor directories and files 18 | .idea 19 | .vscode 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? -------------------------------------------------------------------------------- /desk/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /desk/public/article-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/desk/public/article-2.png -------------------------------------------------------------------------------- /desk/public/article.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/desk/public/article.png -------------------------------------------------------------------------------- /desk/public/desk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/desk/public/desk.png -------------------------------------------------------------------------------- /desk/public/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /desk/public/manifest/apple-icon-180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/desk/public/manifest/apple-icon-180.png -------------------------------------------------------------------------------- /desk/public/manifest/apple-splash-1125-2436.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/desk/public/manifest/apple-splash-1125-2436.jpg -------------------------------------------------------------------------------- /desk/public/manifest/apple-splash-1136-640.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/desk/public/manifest/apple-splash-1136-640.jpg -------------------------------------------------------------------------------- /desk/public/manifest/apple-splash-1170-2532.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/desk/public/manifest/apple-splash-1170-2532.jpg -------------------------------------------------------------------------------- /desk/public/manifest/apple-splash-1179-2556.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/desk/public/manifest/apple-splash-1179-2556.jpg -------------------------------------------------------------------------------- /desk/public/manifest/apple-splash-1242-2208.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/desk/public/manifest/apple-splash-1242-2208.jpg -------------------------------------------------------------------------------- /desk/public/manifest/apple-splash-1242-2688.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/desk/public/manifest/apple-splash-1242-2688.jpg -------------------------------------------------------------------------------- /desk/public/manifest/apple-splash-1284-2778.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/desk/public/manifest/apple-splash-1284-2778.jpg -------------------------------------------------------------------------------- /desk/public/manifest/apple-splash-1290-2796.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/desk/public/manifest/apple-splash-1290-2796.jpg -------------------------------------------------------------------------------- /desk/public/manifest/apple-splash-1334-750.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/desk/public/manifest/apple-splash-1334-750.jpg -------------------------------------------------------------------------------- /desk/public/manifest/apple-splash-1488-2266.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/desk/public/manifest/apple-splash-1488-2266.jpg -------------------------------------------------------------------------------- /desk/public/manifest/apple-splash-1536-2048.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/desk/public/manifest/apple-splash-1536-2048.jpg -------------------------------------------------------------------------------- /desk/public/manifest/apple-splash-1620-2160.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/desk/public/manifest/apple-splash-1620-2160.jpg -------------------------------------------------------------------------------- /desk/public/manifest/apple-splash-1640-2360.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/desk/public/manifest/apple-splash-1640-2360.jpg -------------------------------------------------------------------------------- /desk/public/manifest/apple-splash-1668-2224.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/desk/public/manifest/apple-splash-1668-2224.jpg -------------------------------------------------------------------------------- /desk/public/manifest/apple-splash-1668-2388.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/desk/public/manifest/apple-splash-1668-2388.jpg -------------------------------------------------------------------------------- /desk/public/manifest/apple-splash-1792-828.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/desk/public/manifest/apple-splash-1792-828.jpg -------------------------------------------------------------------------------- /desk/public/manifest/apple-splash-2048-1536.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/desk/public/manifest/apple-splash-2048-1536.jpg -------------------------------------------------------------------------------- /desk/public/manifest/apple-splash-2048-2732.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/desk/public/manifest/apple-splash-2048-2732.jpg -------------------------------------------------------------------------------- /desk/public/manifest/apple-splash-2160-1620.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/desk/public/manifest/apple-splash-2160-1620.jpg -------------------------------------------------------------------------------- /desk/public/manifest/apple-splash-2208-1242.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/desk/public/manifest/apple-splash-2208-1242.jpg -------------------------------------------------------------------------------- /desk/public/manifest/apple-splash-2224-1668.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/desk/public/manifest/apple-splash-2224-1668.jpg -------------------------------------------------------------------------------- /desk/public/manifest/apple-splash-2266-1488.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/desk/public/manifest/apple-splash-2266-1488.jpg -------------------------------------------------------------------------------- /desk/public/manifest/apple-splash-2360-1640.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/desk/public/manifest/apple-splash-2360-1640.jpg -------------------------------------------------------------------------------- /desk/public/manifest/apple-splash-2388-1668.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/desk/public/manifest/apple-splash-2388-1668.jpg -------------------------------------------------------------------------------- /desk/public/manifest/apple-splash-2436-1125.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/desk/public/manifest/apple-splash-2436-1125.jpg -------------------------------------------------------------------------------- /desk/public/manifest/apple-splash-2532-1170.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/desk/public/manifest/apple-splash-2532-1170.jpg -------------------------------------------------------------------------------- /desk/public/manifest/apple-splash-2556-1179.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/desk/public/manifest/apple-splash-2556-1179.jpg -------------------------------------------------------------------------------- /desk/public/manifest/apple-splash-2688-1242.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/desk/public/manifest/apple-splash-2688-1242.jpg -------------------------------------------------------------------------------- /desk/public/manifest/apple-splash-2732-2048.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/desk/public/manifest/apple-splash-2732-2048.jpg -------------------------------------------------------------------------------- /desk/public/manifest/apple-splash-2778-1284.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/desk/public/manifest/apple-splash-2778-1284.jpg -------------------------------------------------------------------------------- /desk/public/manifest/apple-splash-2796-1290.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/desk/public/manifest/apple-splash-2796-1290.jpg -------------------------------------------------------------------------------- /desk/public/manifest/apple-splash-640-1136.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/desk/public/manifest/apple-splash-640-1136.jpg -------------------------------------------------------------------------------- /desk/public/manifest/apple-splash-750-1334.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/desk/public/manifest/apple-splash-750-1334.jpg -------------------------------------------------------------------------------- /desk/public/manifest/apple-splash-828-1792.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/desk/public/manifest/apple-splash-828-1792.jpg -------------------------------------------------------------------------------- /desk/public/manifest/manifest-icon-192.maskable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/desk/public/manifest/manifest-icon-192.maskable.png -------------------------------------------------------------------------------- /desk/public/manifest/manifest-icon-512.maskable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/desk/public/manifest/manifest-icon-512.maskable.png -------------------------------------------------------------------------------- /desk/public/videos/createInviteContact.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/desk/public/videos/createInviteContact.mp4 -------------------------------------------------------------------------------- /desk/src/assets/custom_icons/bullet.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /desk/src/assets/custom_icons/check.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /desk/src/assets/custom_icons/chevron-down.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /desk/src/assets/custom_icons/chevron-up.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /desk/src/assets/custom_icons/circle_check.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /desk/src/assets/custom_icons/comment.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /desk/src/assets/custom_icons/corner_up_left.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /desk/src/assets/custom_icons/dislike.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /desk/src/assets/custom_icons/document.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /desk/src/assets/custom_icons/external_link.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /desk/src/assets/custom_icons/filter.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /desk/src/assets/custom_icons/folder.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /desk/src/assets/custom_icons/knowledge-based.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /desk/src/assets/custom_icons/like.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /desk/src/assets/custom_icons/log_out.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /desk/src/assets/custom_icons/priority_high.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /desk/src/assets/custom_icons/priority_low.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /desk/src/assets/custom_icons/priority_medium.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /desk/src/assets/custom_icons/priority_urgent.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /desk/src/assets/custom_icons/reports.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /desk/src/assets/custom_icons/select.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /desk/src/assets/custom_icons/sla-fail.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /desk/src/assets/custom_icons/sort-ascending.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /desk/src/assets/custom_icons/text_editor_icons/Bullet list.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /desk/src/assets/custom_icons/text_editor_icons/Clear Formatting.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /desk/src/assets/custom_icons/text_editor_icons/Code.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /desk/src/assets/custom_icons/text_editor_icons/Decrease Indent.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /desk/src/assets/custom_icons/text_editor_icons/Image-add.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /desk/src/assets/custom_icons/text_editor_icons/Increase Indent.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /desk/src/assets/custom_icons/text_editor_icons/Italic.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /desk/src/assets/custom_icons/text_editor_icons/Left Align.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /desk/src/assets/custom_icons/text_editor_icons/Link-url.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /desk/src/assets/custom_icons/text_editor_icons/Quote.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /desk/src/assets/custom_icons/text_editor_icons/Text Color.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /desk/src/assets/custom_icons/text_editor_icons/Underline.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /desk/src/assets/custom_icons/ticket.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /desk/src/assets/custom_icons/time.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /desk/src/assets/custom_icons/user-plus.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /desk/src/assets/icons/add.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /desk/src/assets/icons/alert-circle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /desk/src/assets/icons/customer-solid.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /desk/src/assets/icons/customer.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /desk/src/assets/icons/dashboard-solid.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /desk/src/assets/icons/dot-horizontal.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /desk/src/assets/icons/email.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /desk/src/assets/icons/filter.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /desk/src/assets/icons/mail.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /desk/src/assets/icons/right-chevron.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /desk/src/assets/icons/web-link.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /desk/src/assets/images/frappe-mail.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /desk/src/assets/images/gmail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/desk/src/assets/images/gmail.png -------------------------------------------------------------------------------- /desk/src/assets/images/outlook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/desk/src/assets/images/outlook.png -------------------------------------------------------------------------------- /desk/src/assets/images/sendgrid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/desk/src/assets/images/sendgrid.png -------------------------------------------------------------------------------- /desk/src/assets/images/sparkpost.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/desk/src/assets/images/sparkpost.webp -------------------------------------------------------------------------------- /desk/src/assets/images/yahoo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/desk/src/assets/images/yahoo.png -------------------------------------------------------------------------------- /desk/src/assets/images/yandex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/desk/src/assets/images/yandex.png -------------------------------------------------------------------------------- /desk/src/assets/logos/HDLogo.vue: -------------------------------------------------------------------------------- 1 | 19 | -------------------------------------------------------------------------------- /desk/src/components/BrandLogo.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /desk/src/components/EmptyState.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /desk/src/components/Icon.vue: -------------------------------------------------------------------------------- 1 | 12 | 22 | -------------------------------------------------------------------------------- /desk/src/components/LayoutHeader.vue: -------------------------------------------------------------------------------- 1 | 15 | 23 | -------------------------------------------------------------------------------- /desk/src/components/PageTitle.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 23 | -------------------------------------------------------------------------------- /desk/src/components/Pill.vue: -------------------------------------------------------------------------------- 1 | 14 | 35 | -------------------------------------------------------------------------------- /desk/src/components/Settings/EmailConfig.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 29 | -------------------------------------------------------------------------------- /desk/src/components/Settings/EmailProviderIcon.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /desk/src/components/Settings/Teams/TeamsConfig.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /desk/src/components/UserAvatar.vue: -------------------------------------------------------------------------------- 1 | 20 | 45 | -------------------------------------------------------------------------------- /desk/src/components/canned-response/index.js: -------------------------------------------------------------------------------- 1 | export { default as CannedResponseModal } from "./CannedResponseModal.vue"; 2 | -------------------------------------------------------------------------------- /desk/src/components/command-palette/CPGroup.vue: -------------------------------------------------------------------------------- 1 | 22 | 36 | -------------------------------------------------------------------------------- /desk/src/components/command-palette/CPGroupResult.vue: -------------------------------------------------------------------------------- 1 | 23 | 37 | -------------------------------------------------------------------------------- /desk/src/components/icons/ActivityIcon.vue: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /desk/src/components/icons/AscendingIcon.vue: -------------------------------------------------------------------------------- 1 | 21 | -------------------------------------------------------------------------------- /desk/src/components/icons/ColumnsIcon.vue: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /desk/src/components/icons/DescendingIcon.vue: -------------------------------------------------------------------------------- 1 | 21 | -------------------------------------------------------------------------------- /desk/src/components/icons/DetailsIcon.vue: -------------------------------------------------------------------------------- 1 | 21 | -------------------------------------------------------------------------------- /desk/src/components/icons/DotIcon.vue: -------------------------------------------------------------------------------- 1 | 12 | 20 | -------------------------------------------------------------------------------- /desk/src/components/icons/EditIcon.vue: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /desk/src/components/icons/EmailIcon.vue: -------------------------------------------------------------------------------- 1 | 16 | -------------------------------------------------------------------------------- /desk/src/components/icons/FilterIcon.vue: -------------------------------------------------------------------------------- 1 | 32 | -------------------------------------------------------------------------------- /desk/src/components/icons/IndicatorIcon.vue: -------------------------------------------------------------------------------- 1 | 19 | -------------------------------------------------------------------------------- /desk/src/components/icons/PhoneIcon.vue: -------------------------------------------------------------------------------- 1 | 16 | -------------------------------------------------------------------------------- /desk/src/components/icons/PinIcon.vue: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /desk/src/components/icons/RefreshIcon.vue: -------------------------------------------------------------------------------- 1 | 19 | -------------------------------------------------------------------------------- /desk/src/components/icons/ReloadIcon.vue: -------------------------------------------------------------------------------- 1 | 23 | -------------------------------------------------------------------------------- /desk/src/components/icons/ReplyAllIcon.vue: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /desk/src/components/icons/ReplyIcon.vue: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /desk/src/components/icons/SelectIcon.vue: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /desk/src/components/icons/ThumbsDownFilledIcon.vue: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /desk/src/components/icons/ThumbsUpFilledIcon.vue: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /desk/src/components/knowledge-base/CategoryFolder.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 34 | -------------------------------------------------------------------------------- /desk/src/components/knowledge-base/CategoryFolderContainer.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 19 | -------------------------------------------------------------------------------- /desk/src/components/knowledge-base/MoveToCategoryModal.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 39 | -------------------------------------------------------------------------------- /desk/src/components/layouts/AppHeader.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /desk/src/components/layouts/DesktopLayout.vue: -------------------------------------------------------------------------------- 1 | 12 | 17 | -------------------------------------------------------------------------------- /desk/src/components/layouts/MobileAppHeader.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 15 | -------------------------------------------------------------------------------- /desk/src/components/layouts/MobileLayout.vue: -------------------------------------------------------------------------------- 1 | 10 | 14 | -------------------------------------------------------------------------------- /desk/src/components/ticket/TicketFeedback.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /desk/src/components/ticket/index.ts: -------------------------------------------------------------------------------- 1 | export { default as TicketAgentActivities } from "./TicketAgentActivities.vue"; 2 | export { default as TicketAgentSidebar } from "./TicketAgentSidebar.vue"; 3 | -------------------------------------------------------------------------------- /desk/src/components/view-controls/Reload.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 17 | -------------------------------------------------------------------------------- /desk/src/components/view-controls/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Filter } from "./Filter.vue"; 2 | export { default as SortBy } from "./SortBy.vue"; 3 | export { default as QuickFilters } from "./QuickFilters.vue"; 4 | export { default as Reload } from "./Reload.vue"; 5 | export { default as ColumnSettings } from "./ColumnSettings.vue"; 6 | -------------------------------------------------------------------------------- /desk/src/composables/device.ts: -------------------------------------------------------------------------------- 1 | import LucideChevronUp from "~icons/lucide/chevron-up"; 2 | import LucideCommand from "~icons/lucide/command"; 3 | 4 | /** 5 | * Helper values to determine the device type/make etc. 6 | * @returns An object containing device information 7 | */ 8 | export function useDevice() { 9 | const isMac = navigator.userAgent.indexOf("Mac OS X") != -1; 10 | const controlKey = isMac ? "⌃" : "Ctrl"; 11 | const altKey = isMac ? "⌥" : "Alt"; 12 | const metaKey = isMac ? "⌘" : "Meta"; 13 | const modifier = isMac ? "meta" : "control"; 14 | const modifierIcon = isMac ? LucideCommand : LucideChevronUp; 15 | return { 16 | altKey, 17 | controlKey, 18 | isMac, 19 | metaKey, 20 | modifier, 21 | modifierIcon, 22 | }; 23 | } 24 | -------------------------------------------------------------------------------- /desk/src/composables/error.ts: -------------------------------------------------------------------------------- 1 | import { createToast } from "@/utils"; 2 | 3 | interface Options { 4 | title?: string; 5 | icon?: string; 6 | iconClass?: string; 7 | } 8 | 9 | interface Error { 10 | message?: string; 11 | messages?: string[]; 12 | } 13 | 14 | export function useError(o?: Options) { 15 | function getMessage(e: Error) { 16 | if (e.messages) return e.messages.join(", "); 17 | else if (e.message) return e.message; 18 | else return ""; 19 | } 20 | 21 | function getTitle(e: Error) { 22 | if (o?.title) return o?.title; 23 | return getMessage(e); 24 | } 25 | 26 | return function (e: Error) { 27 | createToast({ 28 | title: getTitle(e), 29 | message: o?.title ? undefined : getMessage(e), 30 | icon: o?.icon ?? "x", 31 | iconClasses: o?.iconClass ?? "text-red-500", 32 | }); 33 | }; 34 | } 35 | -------------------------------------------------------------------------------- /desk/src/composables/index.ts: -------------------------------------------------------------------------------- 1 | export { useDevice } from "./device"; 2 | -------------------------------------------------------------------------------- /desk/src/composables/mobile.ts: -------------------------------------------------------------------------------- 1 | import { ref } from "vue"; 2 | 3 | export const mobileSidebarOpened = ref(false); 4 | -------------------------------------------------------------------------------- /desk/src/composables/screen.ts: -------------------------------------------------------------------------------- 1 | import { computed, onMounted, onUnmounted, reactive } from "vue"; 2 | 3 | export function useScreenSize() { 4 | const size = reactive({ 5 | width: window.innerWidth, 6 | height: window.innerHeight, 7 | }); 8 | 9 | const onResize = () => { 10 | size.width = window.innerWidth; 11 | size.height = window.innerHeight; 12 | }; 13 | 14 | onMounted(() => { 15 | window.addEventListener("resize", onResize); 16 | }); 17 | 18 | onUnmounted(() => { 19 | window.removeEventListener("resize", onResize); 20 | }); 21 | 22 | const mobileThreshold = 640; 23 | const isMobileView = computed(() => size.width < mobileThreshold); 24 | 25 | return { 26 | size, 27 | isMobileView, 28 | }; 29 | } 30 | -------------------------------------------------------------------------------- /desk/src/dayjs.ts: -------------------------------------------------------------------------------- 1 | import d from "dayjs"; 2 | import localizedFormat from "dayjs/plugin/localizedFormat"; 3 | import relativeTime from "dayjs/plugin/relativeTime"; 4 | import utc from "dayjs/plugin/utc"; 5 | import timezone from "dayjs/plugin/timezone"; 6 | import { useAuthStore } from "./stores/auth"; 7 | 8 | const authStore = useAuthStore(); 9 | declare module "dayjs" { 10 | interface Dayjs { 11 | /** Example: `Aug 15, 2:29 AM` */ 12 | short(): string; 13 | /** Example: `Tuesday, August 15, 2023 2:29 AM` */ 14 | long(): string; 15 | } 16 | } 17 | 18 | d.extend(localizedFormat); 19 | d.extend(relativeTime); 20 | d.extend(function (_, cls) { 21 | cls.prototype.short = function () { 22 | return this.format("MMM D, h:mm A"); 23 | }; 24 | cls.prototype.long = function () { 25 | return this.format("LLLL"); 26 | }; 27 | }); 28 | d.extend(utc); 29 | d.extend(timezone); 30 | d.tz.setDefault(authStore.timezone); 31 | 32 | export const dayjs = d; 33 | -------------------------------------------------------------------------------- /desk/src/index.css: -------------------------------------------------------------------------------- 1 | @import 'frappe-ui/src/fonts/Inter/inter.css'; 2 | 3 | @tailwind base; 4 | @tailwind components; 5 | @tailwind utilities; 6 | 7 | @layer components { 8 | .prose-f { 9 | @apply 10 | break-normal 11 | max-w-none 12 | prose 13 | prose-code:break-all 14 | prose-code:whitespace-pre-wrap 15 | prose-img:border 16 | prose-img:rounded-lg 17 | prose-sm 18 | prose-table:table-fixed 19 | prose-td:border 20 | prose-td:border-gray-300 21 | prose-td:p-2 22 | prose-td:relative 23 | prose-th:bg-gray-100 24 | prose-th:border 25 | prose-th:border-gray-300 26 | prose-th:p-2 27 | prose-th:relative 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /desk/src/pages/CustomerPortalRoot.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 29 | -------------------------------------------------------------------------------- /desk/src/pages/HRoot.vue: -------------------------------------------------------------------------------- 1 | 18 | -------------------------------------------------------------------------------- /desk/src/pages/InvalidPage.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 18 | -------------------------------------------------------------------------------- /desk/src/pages/desk/AgentRoot.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 39 | -------------------------------------------------------------------------------- /desk/src/pages/desk/contact/dialogState.ts: -------------------------------------------------------------------------------- 1 | import { ref } from "vue"; 2 | 3 | export const showNewContactModal = ref(false); 4 | -------------------------------------------------------------------------------- /desk/src/pages/ticket/TicketBreadcrumbs.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 44 | -------------------------------------------------------------------------------- /desk/src/pages/ticket/data.ts: -------------------------------------------------------------------------------- 1 | import { createResource } from "frappe-ui"; 2 | import { Resource, Ticket } from "@/types"; 3 | 4 | export function useTicket( 5 | id?: number | string, 6 | isCustomerPortal: boolean = false, 7 | transformCB?: (data: any) => any, 8 | successCB?: (data: any) => any, 9 | errorCB?: (err: any) => any 10 | ): Resource { 11 | const t = createResource({ 12 | url: "helpdesk.helpdesk.doctype.hd_ticket.api.get_one", 13 | cache: ["Ticket", id], 14 | params: { 15 | name: id, 16 | is_customer_portal: isCustomerPortal, 17 | }, 18 | auto: true, 19 | transform: (data) => { 20 | transformCB && transformCB(data); 21 | }, 22 | onSuccess: (data) => { 23 | successCB && successCB(data); 24 | }, 25 | onError: (err) => { 26 | console.log("here"); 27 | errorCB && errorCB(err); 28 | }, 29 | }); 30 | return t; 31 | } 32 | -------------------------------------------------------------------------------- /desk/src/pages/ticket/modalStates.ts: -------------------------------------------------------------------------------- 1 | import { ref } from "vue"; 2 | 3 | export const showAssignmentModal = ref(false); 4 | export const showEmailBox = ref(false); 5 | export const showCommentBox = ref(false); 6 | -------------------------------------------------------------------------------- /desk/src/pages/ticket/symbols.ts: -------------------------------------------------------------------------------- 1 | import { InjectionKey } from "vue"; 2 | import { Resource, Ticket } from "@/types"; 3 | 4 | export const ITicket: InjectionKey> = Symbol("Ticket"); 5 | -------------------------------------------------------------------------------- /desk/src/socket.ts: -------------------------------------------------------------------------------- 1 | import { getCachedListResource, getCachedResource } from "frappe-ui"; 2 | import { io } from "socket.io-client"; 3 | import { socketio_port } from "../../../../sites/common_site_config.json"; 4 | 5 | function init() { 6 | let host = window.location.hostname; 7 | let siteName = window.site_name || host; 8 | let port = window.location.port ? `:${socketio_port}` : ""; 9 | let protocol = port ? "http" : "https"; 10 | let url = `${protocol}://${host}${port}/${siteName}`; 11 | const socket = io(url, { 12 | withCredentials: true, 13 | reconnectionAttempts: 5, 14 | }); 15 | 16 | socket.on("refetch_resource", (data) => { 17 | if (data.cache_key) { 18 | const resource = 19 | getCachedResource(data.cache_key) || 20 | getCachedListResource(data.cache_key); 21 | if (resource) { 22 | resource.reload(); 23 | } 24 | } 25 | }); 26 | 27 | return socket; 28 | } 29 | 30 | export const socket = init(); 31 | -------------------------------------------------------------------------------- /desk/src/stores/agent.ts: -------------------------------------------------------------------------------- 1 | import { computed } from "vue"; 2 | import { defineStore } from "pinia"; 3 | import { createListResource } from "frappe-ui"; 4 | 5 | export const useAgentStore = defineStore("agent", () => { 6 | const agents = createListResource({ 7 | doctype: "HD Agent", 8 | fields: ["name", "agent_name", "user", "user.user_image"], 9 | filters: { is_active: 1 }, 10 | pageLength: 99999, 11 | }); 12 | 13 | const dropdown = computed(() => 14 | agents.data?.map((o) => ({ 15 | label: o.agent_name, 16 | value: o.name, 17 | })) 18 | ); 19 | 20 | function searchAgents(query: string) { 21 | return agents.data.filter((a) => 22 | a.user?.toLowerCase().includes(query.toLowerCase()) 23 | ); 24 | } 25 | 26 | return { 27 | dropdown, 28 | agents, 29 | searchAgents, 30 | }; 31 | }); 32 | -------------------------------------------------------------------------------- /desk/src/stores/contact.ts: -------------------------------------------------------------------------------- 1 | import { computed, ComputedRef } from "vue"; 2 | import { defineStore } from "pinia"; 3 | import { createListResource } from "frappe-ui"; 4 | 5 | type Contact = { 6 | name: string; 7 | first_name: string; 8 | last_name: string; 9 | full_name: string; 10 | email_id: string; 11 | }; 12 | 13 | export const useContactStore = defineStore("contact", () => { 14 | const d__ = createListResource({ 15 | doctype: "Contact", 16 | fields: ["*"], 17 | auto: true, 18 | pageLength: 99999, 19 | }); 20 | 21 | const options: ComputedRef> = computed( 22 | () => d__.list?.data || [] 23 | ); 24 | 25 | return { 26 | options, 27 | }; 28 | }); 29 | -------------------------------------------------------------------------------- /desk/src/stores/globalStore.ts: -------------------------------------------------------------------------------- 1 | import { getCurrentInstance } from "vue"; 2 | 3 | export const globalStore = () => { 4 | const app = getCurrentInstance(); 5 | const { $dialog, $socket } = app.appContext.config.globalProperties; 6 | 7 | return { 8 | $dialog, 9 | $socket, 10 | }; 11 | }; 12 | -------------------------------------------------------------------------------- /desk/src/stores/sidebar.ts: -------------------------------------------------------------------------------- 1 | import { computed, ref } from "vue"; 2 | import { defineStore } from "pinia"; 3 | import { useStorage } from "@vueuse/core"; 4 | 5 | export const useSidebarStore = defineStore("sidebar", () => { 6 | const isOpen = ref(true); 7 | const isExpanded = useStorage("sidebar_is_expanded", true); 8 | const width = computed(() => { 9 | return isExpanded.value ? "224px" : "50px"; 10 | }); 11 | 12 | function toggle(state?: boolean) { 13 | isOpen.value = state ?? !isOpen.value; 14 | } 15 | 16 | function toggleExpanded(state?: boolean) { 17 | isExpanded.value = state ?? !isExpanded.value; 18 | } 19 | 20 | return { 21 | isExpanded, 22 | isOpen, 23 | toggle, 24 | toggleExpanded, 25 | width, 26 | }; 27 | }); 28 | -------------------------------------------------------------------------------- /desk/src/stores/ticketStatus.ts: -------------------------------------------------------------------------------- 1 | import { computed, ref } from "vue"; 2 | import { defineStore } from "pinia"; 3 | 4 | export const useTicketStatusStore = defineStore("ticketStatus", () => { 5 | const options = ref(["Open", "Replied", "Resolved", "Closed"]); 6 | const dropdown = computed(() => 7 | options.value.map((o) => ({ 8 | label: o, 9 | value: o, 10 | })) 11 | ); 12 | const colorMap = { 13 | Open: "red", 14 | Replied: "blue", 15 | Resolved: "green", 16 | Closed: "gray", 17 | }; 18 | const textColorMap = { 19 | Open: "!text-red-600", 20 | Replied: "!text-blue-600", 21 | "Awaiting Response": "!text-blue-600", 22 | Resolved: "!text-green-700", 23 | Closed: "!text-gray-700", 24 | }; 25 | const stateActive = ["Open", "Replied"]; 26 | const stateInactive = ["Resolved", "Closed"]; 27 | 28 | return { 29 | colorMap, 30 | dropdown, 31 | options, 32 | stateActive, 33 | stateInactive, 34 | textColorMap, 35 | }; 36 | }); 37 | -------------------------------------------------------------------------------- /desk/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | mode: "jit", 3 | presets: [require("frappe-ui/src/tailwind/preset")], 4 | content: [ 5 | "./index.html", 6 | "./src/**/*.{vue,js,ts,jsx,tsx}", 7 | "./node_modules/frappe-ui/src/**/*.{vue,js,ts,jsx,tsx}", 8 | "../node_modules/frappe-ui/src/**/*.{vue,js,ts,jsx,tsx}", 9 | "./node_modules/frappe-ui/frappe/**/*.{vue,js,ts,jsx,tsx}", 10 | "../node_modules/frappe-ui/frappe/**/*.{vue,js,ts,jsx,tsx}", 11 | ], 12 | theme: { 13 | extend: { 14 | height: { 15 | 18: "68px", 16 | }, 17 | margin: { 18 | 3.5: "14px", 19 | }, 20 | padding: { 21 | 2.5: "10px", 22 | 3.5: "14px", 23 | }, 24 | }, 25 | }, 26 | plugins: [ 27 | require("@tailwindcss/typography"), 28 | function ({ addUtilities }) { 29 | addUtilities({ 30 | ".hide-scrollbar": { 31 | "scrollbar-width": "none", 32 | "-ms-overflow-style": "none", 33 | "&::-webkit-scrollbar": { 34 | display: "none", 35 | }, 36 | }, 37 | }); 38 | }, 39 | ], 40 | }; 41 | -------------------------------------------------------------------------------- /desk/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "esModuleInterop": true, 4 | "allowSyntheticDefaultImports": true, 5 | "target": "es2021", 6 | "module": "esnext", 7 | "moduleResolution":"node", 8 | "jsx": "preserve", 9 | "paths": { 10 | "@/*": ["./src/*"] 11 | }, 12 | "types": [ 13 | "unplugin-icons/types/vue", 14 | "vite/client" 15 | ], 16 | }, 17 | "include": ["src","src/*","src/**/*.d.ts", "src/**/*.ts", "src/**/*.tsx", "src/**/*.js", "src/**/*.jsx","src/**/*.vue"], 18 | "exclude": ["node_modules"] 19 | } 20 | -------------------------------------------------------------------------------- /docker/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.7" 2 | services: 3 | mariadb: 4 | image: mariadb:10.8 5 | command: 6 | - --character-set-server=utf8mb4 7 | - --collation-server=utf8mb4_unicode_ci 8 | - --skip-character-set-client-handshake 9 | - --skip-innodb-read-only-compressed # Temporary fix for MariaDB 10.6 10 | environment: 11 | MYSQL_ROOT_PASSWORD: 123 12 | volumes: 13 | - mariadb-data:/var/lib/mysql 14 | 15 | redis: 16 | image: redis:alpine 17 | 18 | frappe: 19 | image: frappe/bench:latest 20 | command: bash /workspace/init.sh 21 | environment: 22 | - SHELL=/bin/bash 23 | working_dir: /home/frappe 24 | volumes: 25 | - .:/workspace 26 | ports: 27 | - 8000:8000 28 | - 9000:9000 29 | 30 | volumes: 31 | mariadb-data: -------------------------------------------------------------------------------- /helpdesk/__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = "1.9.1" 2 | -------------------------------------------------------------------------------- /helpdesk/api/agent.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | from helpdesk.utils import agent_only 4 | 5 | 6 | @frappe.whitelist() 7 | @agent_only 8 | def sent_invites(emails, send_welcome_mail_to_user=True): 9 | for email in emails: 10 | if frappe.db.exists("User", email): 11 | user = frappe.get_doc("User", email) 12 | else: 13 | user = frappe.get_doc( 14 | {"doctype": "User", "email": email, "first_name": email.split("@")[0]} 15 | ).insert() 16 | 17 | if send_welcome_mail_to_user: 18 | user.send_welcome_mail_to_user() 19 | 20 | frappe.get_doc({"doctype": "HD Agent", "user": user.name}).insert() 21 | return 22 | -------------------------------------------------------------------------------- /helpdesk/api/config.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | @frappe.whitelist(allow_guest=True) 5 | def get_config(): 6 | fields = [ 7 | "brand_logo", 8 | "prefer_knowledge_base", 9 | "setup_complete", 10 | "skip_email_workflow", 11 | "is_feedback_mandatory", 12 | "restrict_tickets_by_agent_group", 13 | ] 14 | res = frappe.get_value(doctype="HD Settings", fieldname=fields, as_dict=True) 15 | return res 16 | -------------------------------------------------------------------------------- /helpdesk/api/permission.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def has_app_permission(): 5 | """Check if the user has permission to access the app.""" 6 | if frappe.session.user == "Administrator": 7 | return True 8 | 9 | roles = frappe.get_roles() 10 | helpdesk_roles = ["Agent"] 11 | if any(role in roles for role in helpdesk_roles): 12 | return True 13 | 14 | return False 15 | -------------------------------------------------------------------------------- /helpdesk/api/session.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | from helpdesk.utils import agent_only 4 | 5 | 6 | @frappe.whitelist() 7 | @agent_only 8 | def get_users(): 9 | users = frappe.qb.get_query( 10 | "User", 11 | fields=["name", "email", "enabled", "user_image", "full_name", "user_type"], 12 | order_by="full_name asc", 13 | distinct=True, 14 | ).run(as_dict=1) 15 | 16 | for user in users: 17 | roles = frappe.get_roles(user.name) 18 | if "Agent Manager" in roles: 19 | user.role = "Manager" 20 | elif "Agent" in roles: 21 | user.role = "Agent" 22 | elif "Guest" in roles: 23 | user.role = "Guest" 24 | if frappe.session.user == user.name: 25 | user.session_user = True 26 | 27 | return users 28 | -------------------------------------------------------------------------------- /helpdesk/api/telemetry.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | from frappe.utils.telemetry import POSTHOG_HOST_FIELD, POSTHOG_PROJECT_FIELD 3 | 4 | 5 | @frappe.whitelist() 6 | def is_enabled(): 7 | return bool( 8 | frappe.get_system_settings("enable_telemetry") 9 | and frappe.conf.get("posthog_host") 10 | and frappe.conf.get("posthog_project_id") 11 | ) 12 | 13 | 14 | @frappe.whitelist() 15 | def get_credentials(): 16 | return { 17 | "project_id": frappe.conf.get("posthog_project_id"), 18 | "telemetry_host": frappe.conf.get("posthog_host"), 19 | } 20 | 21 | 22 | @frappe.whitelist() 23 | def get_posthog_settings(): 24 | return { 25 | "posthog_project_id": frappe.conf.get(POSTHOG_PROJECT_FIELD), 26 | "posthog_host": frappe.conf.get(POSTHOG_HOST_FIELD), 27 | "enable_telemetry": frappe.get_system_settings("enable_telemetry"), 28 | "telemetry_site_age": frappe.utils.telemetry.site_age(), 29 | } 30 | -------------------------------------------------------------------------------- /helpdesk/api/ticket.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | from frappe import _ 3 | 4 | from helpdesk.utils import agent_only 5 | 6 | 7 | def assign_ticket_to_agent(ticket_id, agent_id=None): 8 | if not ticket_id: 9 | return 10 | 11 | ticket_doc = frappe.get_doc("HD Ticket", ticket_id) 12 | 13 | if not agent_id: 14 | # assign to self 15 | agent_id = frappe.session.user 16 | 17 | if not frappe.db.exists("HD Agent", agent_id): 18 | frappe.throw(_("Tickets can only be assigned to agents")) 19 | 20 | ticket_doc.assign_agent(agent_id) 21 | return ticket_doc 22 | 23 | 24 | @frappe.whitelist() 25 | @agent_only 26 | def bulk_assign_ticket_to_agent(ticket_ids, agent_id=None): 27 | if ticket_ids: 28 | ticket_docs = [] 29 | for ticket_id in ticket_ids: 30 | ticket_doc = assign_ticket_to_agent(ticket_id, agent_id) 31 | ticket_docs.append(ticket_doc) 32 | return ticket_docs 33 | -------------------------------------------------------------------------------- /helpdesk/config/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/helpdesk/config/__init__.py -------------------------------------------------------------------------------- /helpdesk/config/desktop.py: -------------------------------------------------------------------------------- 1 | from frappe import _ 2 | 3 | 4 | def get_data(): 5 | return [ 6 | { 7 | "module_name": "HelpDesk", 8 | "color": "grey", 9 | "icon": "octicon octicon-file-directory", 10 | "type": "module", 11 | "label": _("HelpDesk"), 12 | } 13 | ] 14 | -------------------------------------------------------------------------------- /helpdesk/config/docs.py: -------------------------------------------------------------------------------- 1 | """ 2 | Configuration for docs 3 | """ 4 | 5 | # source_link = "https://github.com/frappe/desk" 6 | # headline = "App that does everything" 7 | # sub_heading = "Yes, you got that right the first time, everything" 8 | 9 | 10 | def get_context(context): 11 | context.brand_html = "Frappe Helpdesk" 12 | -------------------------------------------------------------------------------- /helpdesk/consts.py: -------------------------------------------------------------------------------- 1 | DEFAULT_TICKET_TYPE = "Unspecified" 2 | DEFAULT_TICKET_PRIORITY = "Medium" 3 | DEFAULT_TICKET_TEMPLATE = "Default" 4 | DEFAULT_ARTICLE_CATEGORY = "General" 5 | -------------------------------------------------------------------------------- /helpdesk/extends/assignment_rule.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def on_assignment_rule_trash(doc, event): 5 | if not frappe.get_all( 6 | "Assignment Rule", 7 | filters={"document_type": "HD Ticket", "name": ["!=", doc.name]}, 8 | ): 9 | frappe.throw("There should atleast be 1 assignment rule for ticket") 10 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/helpdesk/helpdesk/__init__.py -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/helpdesk/helpdesk/doctype/__init__.py -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_action/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/helpdesk/helpdesk/doctype/hd_action/__init__.py -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_action/hd_action.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023, Frappe Technologies and contributors 2 | // For license information, please see license.txt 3 | 4 | // frappe.ui.form.on("HD Action", { 5 | // refresh(frm) { 6 | 7 | // }, 8 | // }); 9 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_action/hd_action.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023, Frappe Technologies and contributors 2 | # For license information, please see license.txt 3 | 4 | # import frappe 5 | from frappe.model.document import Document 6 | 7 | 8 | class HDAction(Document): 9 | pass 10 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_action/test_hd_action.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023, Frappe Technologies and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | from frappe.tests.utils import FrappeTestCase 6 | 7 | 8 | class TestHDAction(FrappeTestCase): 9 | pass 10 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_agent/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/helpdesk/helpdesk/doctype/hd_agent/__init__.py -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_agent/hd_agent.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Frappe Technologies and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.ui.form.on("HD Agent", { 5 | // refresh: function(frm) { 6 | // } 7 | }); 8 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_agent/test_hd_agent.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Frappe Technologies and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | import unittest 6 | 7 | 8 | class TestHDAgent(unittest.TestCase): 9 | pass 10 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_article/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/helpdesk/helpdesk/doctype/hd_article/__init__.py -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_article/hd_article.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023, Frappe Technologies and contributors 2 | // For license information, please see license.txt 3 | 4 | // frappe.ui.form.on("HD Article", { 5 | // refresh(frm) { 6 | 7 | // }, 8 | // }); 9 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_article/test_hd_article.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2021, Frappe Technologies and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | import unittest 6 | 7 | 8 | class TestHDArticle(unittest.TestCase): 9 | pass 10 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_article_category/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/helpdesk/helpdesk/doctype/hd_article_category/__init__.py -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_article_category/hd_article_category.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021, Frappe Technologies and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.ui.form.on("HD Article Category", { 5 | // refresh: function(frm) { 6 | // } 7 | }); 8 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_article_category/test_hd_article_category.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2021, Frappe Technologies and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | import unittest 6 | 7 | 8 | class TestHDArticleCategory(unittest.TestCase): 9 | pass 10 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_article_feedback/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/helpdesk/helpdesk/doctype/hd_article_feedback/__init__.py -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_article_feedback/hd_article_feedback.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024, Frappe Technologies and contributors 2 | // For license information, please see license.txt 3 | 4 | // frappe.ui.form.on("HD Article Feedback", { 5 | // refresh(frm) { 6 | 7 | // }, 8 | // }); 9 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_article_feedback/hd_article_feedback.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Frappe Technologies and contributors 2 | # For license information, please see license.txt 3 | 4 | # import frappe 5 | from frappe.model.document import Document 6 | 7 | 8 | class HDArticleFeedback(Document): 9 | pass 10 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_article_feedback/test_hd_article_feedback.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024, Frappe Technologies and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | from frappe.tests import IntegrationTestCase, UnitTestCase 6 | 7 | # On IntegrationTestCase, the doctype test records and all 8 | # link-field test record depdendencies are recursively loaded 9 | # Use these module variables to add/remove to/from that list 10 | EXTRA_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"] 11 | IGNORE_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"] 12 | 13 | 14 | class UnitTestHDArticleFeedback(UnitTestCase): 15 | """ 16 | Unit tests for HDArticleFeedback. 17 | Use this class for testing individual functions and methods. 18 | """ 19 | 20 | pass 21 | 22 | 23 | class IntegrationTestHDArticleFeedback(IntegrationTestCase): 24 | """ 25 | Integration tests for HDArticleFeedback. 26 | Use this class for testing interactions between multiple components. 27 | """ 28 | 29 | pass 30 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_canned_response/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/helpdesk/helpdesk/doctype/hd_canned_response/__init__.py -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_canned_response/hd_canned_response.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Frappe Technologies and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.ui.form.on("HD Canned Response", { 5 | // refresh: function(frm) { 6 | // } 7 | }); 8 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_canned_response/test_hd_canned_response.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Frappe Technologies and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | from frappe.tests.utils import FrappeTestCase 6 | 7 | 8 | class TestHDCannedResponse(FrappeTestCase): 9 | pass 10 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_customer/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/helpdesk/helpdesk/doctype/hd_customer/__init__.py -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_customer/hd_customer.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Frappe Technologies and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.ui.form.on("HD Customer", { 5 | // refresh: function(frm) { 6 | // } 7 | }); 8 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_customer/hd_customer.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Frappe Technologies and contributors 2 | # For license information, please see license.txt 3 | 4 | # import frappe 5 | from frappe.model.document import Document 6 | 7 | 8 | class HDCustomer(Document): 9 | @staticmethod 10 | def default_list_data(): 11 | columns = [ 12 | { 13 | "label": "Name", 14 | "key": "name", 15 | "width": "17rem", 16 | "type": "Data", 17 | }, 18 | { 19 | "label": "Domain", 20 | "key": "domain", 21 | "width": "24rem", 22 | "type": "Data", 23 | }, 24 | { 25 | "label": "Created On", 26 | "key": "creation", 27 | "width": "8rem", 28 | "type": "Datetime", 29 | }, 30 | ] 31 | return {"columns": columns} 32 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_customer/test_hd_customer.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Frappe Technologies and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | from frappe.tests.utils import FrappeTestCase 6 | 7 | 8 | class TestHDCustomer(FrappeTestCase): 9 | pass 10 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_desk_account_request/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/helpdesk/helpdesk/doctype/hd_desk_account_request/__init__.py -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_desk_account_request/hd_desk_account_request.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Frappe Technologies and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.ui.form.on("HD Desk Account Request", { 5 | // refresh: function(frm) { 6 | // } 7 | }); 8 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_desk_account_request/test_hd_desk_account_request.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Frappe Technologies and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | from frappe.tests.utils import FrappeTestCase 6 | 7 | 8 | class TestHDDeskAccountRequest(FrappeTestCase): 9 | pass 10 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_escalation_rule/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/helpdesk/helpdesk/doctype/hd_escalation_rule/__init__.py -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_escalation_rule/hd_escalation_rule.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023, Frappe Technologies and contributors 2 | // For license information, please see license.txt 3 | 4 | // frappe.ui.form.on("HD Escalation Rule", { 5 | // refresh(frm) { 6 | 7 | // }, 8 | // }); 9 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_escalation_rule/test_hd_escalation_rule.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023, Frappe Technologies and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | from frappe.tests.utils import FrappeTestCase 6 | 7 | 8 | class TestHDEscalationRule(FrappeTestCase): 9 | pass 10 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_form_script/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/helpdesk/helpdesk/doctype/hd_form_script/__init__.py -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_form_script/hd_form_script.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024, Frappe Technologies and contributors 2 | // For license information, please see license.txt 3 | 4 | // frappe.ui.form.on("HD Form Script", { 5 | // refresh(frm) { 6 | 7 | // }, 8 | // }); 9 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_form_script/test_hd_form_script.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024, Frappe Technologies and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | from frappe.tests.utils import FrappeTestCase 6 | 7 | 8 | class TestHDFormScript(FrappeTestCase): 9 | pass 10 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_holiday/README.md: -------------------------------------------------------------------------------- 1 | Holiday date in Holiday List. 2 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_holiday/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/helpdesk/helpdesk/doctype/hd_holiday/__init__.py -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_holiday/hd_holiday.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors 2 | # License: GNU General Public License v3. See license.txt 3 | 4 | 5 | from frappe.model.document import Document 6 | 7 | 8 | class HDHoliday(Document): 9 | pass 10 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_notification/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/helpdesk/helpdesk/doctype/hd_notification/__init__.py -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_notification/hd_notification.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Frappe Technologies and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.ui.form.on("HD Notification", { 5 | // refresh: function(frm) { 6 | // } 7 | }); 8 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_notification/test_hd_notification.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Frappe Technologies and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | from frappe.tests.utils import FrappeTestCase 6 | 7 | 8 | class TestHDNotification(FrappeTestCase): 9 | pass 10 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_notification/utils.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | @frappe.whitelist() 5 | def clear(ticket: str | int = None, comment: str = None): 6 | """ 7 | Mark notifications as read. No arguments will clear all notifications for `user`. 8 | 9 | :param ticket: Ticket to clear notifications for 10 | :param comment: Comment to clear notifications for 11 | """ 12 | filters = {"user_to": frappe.session.user, "read": False} 13 | if ticket: 14 | filters["reference_ticket"] = ticket 15 | if comment: 16 | filters["reference_comment"] = comment 17 | for notification in frappe.get_all( 18 | "HD Notification", filters=filters, pluck="name" 19 | ): 20 | frappe.db.set_value("HD Notification", notification, "read", 1) 21 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_organization/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/helpdesk/helpdesk/doctype/hd_organization/__init__.py -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_organization/hd_organization.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Frappe Technologies and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.ui.form.on("HD Organization", { 5 | // refresh: function(frm) { 6 | // } 7 | }); 8 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_organization/hd_organization.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Frappe Technologies and contributors 2 | # For license information, please see license.txt 3 | 4 | # import frappe 5 | from frappe.model.document import Document 6 | 7 | 8 | class HDOrganization(Document): 9 | pass 10 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_organization/test_hd_organization.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Frappe Technologies and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | from frappe.tests.utils import FrappeTestCase 6 | 7 | 8 | class TestHDOrganization(FrappeTestCase): 9 | pass 10 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_organization_contact_item/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/helpdesk/helpdesk/doctype/hd_organization_contact_item/__init__.py -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_organization_contact_item/hd_organization_contact_item.json: -------------------------------------------------------------------------------- 1 | { 2 | "actions": [], 3 | "allow_rename": 1, 4 | "creation": "2022-03-23 10:28:55.637815", 5 | "doctype": "DocType", 6 | "editable_grid": 1, 7 | "engine": "InnoDB", 8 | "field_order": [ 9 | "contact" 10 | ], 11 | "fields": [ 12 | { 13 | "fieldname": "contact", 14 | "fieldtype": "Link", 15 | "in_list_view": 1, 16 | "label": "Contact", 17 | "options": "Contact" 18 | } 19 | ], 20 | "index_web_pages_for_search": 1, 21 | "istable": 1, 22 | "links": [], 23 | "modified": "2022-03-23 10:33:48.137526", 24 | "modified_by": "Administrator", 25 | "module": "Helpdesk", 26 | "name": "HD Organization Contact Item", 27 | "owner": "Administrator", 28 | "permissions": [], 29 | "sort_field": "modified", 30 | "sort_order": "DESC", 31 | "states": [] 32 | } 33 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_organization_contact_item/hd_organization_contact_item.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Frappe Technologies and contributors 2 | # For license information, please see license.txt 3 | 4 | # import frappe 5 | from frappe.model.document import Document 6 | 7 | 8 | class HDOrganizationContactItem(Document): 9 | pass 10 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_pause_service_level_agreement_on_status/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/helpdesk/helpdesk/doctype/hd_pause_service_level_agreement_on_status/__init__.py -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_pause_service_level_agreement_on_status/hd_pause_service_level_agreement_on_status.json: -------------------------------------------------------------------------------- 1 | { 2 | "actions": [], 3 | "creation": "2020-06-05 13:59:43.265588", 4 | "doctype": "DocType", 5 | "editable_grid": 1, 6 | "engine": "InnoDB", 7 | "field_order": [ 8 | "status" 9 | ], 10 | "fields": [ 11 | { 12 | "fieldname": "status", 13 | "fieldtype": "Select", 14 | "in_list_view": 1, 15 | "label": "Status", 16 | "options": "Open\nReplied\nResolved\nClosed", 17 | "reqd": 1 18 | } 19 | ], 20 | "istable": 1, 21 | "links": [], 22 | "modified": "2023-09-18 12:23:59.039194", 23 | "modified_by": "Administrator", 24 | "module": "Helpdesk", 25 | "name": "HD Pause Service Level Agreement On Status", 26 | "owner": "Administrator", 27 | "permissions": [], 28 | "quick_entry": 1, 29 | "sort_field": "modified", 30 | "sort_order": "DESC", 31 | "states": [], 32 | "track_changes": 1 33 | } -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_pause_service_level_agreement_on_status/hd_pause_service_level_agreement_on_status.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors 3 | # For license information, please see license.txt 4 | 5 | from __future__ import unicode_literals 6 | 7 | # import frappe 8 | from frappe.model.document import Document 9 | 10 | 11 | class HDPauseServiceLevelAgreementOnStatus(Document): 12 | pass 13 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_portal_signup_request/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/helpdesk/helpdesk/doctype/hd_portal_signup_request/__init__.py -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_portal_signup_request/hd_portal_signup_request.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Frappe Technologies and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.ui.form.on("HD Portal Signup Request", { 5 | // refresh: function(frm) { 6 | // } 7 | }); 8 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_portal_signup_request/hd_portal_signup_request.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Frappe Technologies and contributors 2 | # For license information, please see license.txt 3 | 4 | # import frappe 5 | from frappe.model.document import Document 6 | 7 | 8 | class HDPortalSignupRequest(Document): 9 | pass 10 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_portal_signup_request/test_hd_portal_signup_request.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Frappe Technologies and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | from frappe.tests.utils import FrappeTestCase 6 | 7 | 8 | class TestHDPortalSignupRequest(FrappeTestCase): 9 | pass 10 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_preset_filter/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/helpdesk/helpdesk/doctype/hd_preset_filter/__init__.py -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_preset_filter/hd_preset_filter.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Frappe Technologies and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.ui.form.on("HD Preset Filter", { 5 | // refresh: function(frm) { 6 | // } 7 | }); 8 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_preset_filter/hd_preset_filter.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Frappe Technologies and contributors 2 | # For license information, please see license.txt 3 | 4 | import frappe 5 | from frappe.model.document import Document 6 | 7 | 8 | class HDPresetFilter(Document): 9 | def before_save(self): 10 | if self.type == "User": 11 | self.user = frappe.session.user 12 | 13 | def on_trash(self): 14 | if self.type == "System": 15 | frappe.throw("System filters cannot be deleted") 16 | 17 | def after_insert(self): 18 | frappe.publish_realtime("helpdesk:new-preset-filter", self) 19 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_preset_filter/test_hd_preset_filter.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Frappe Technologies and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | from frappe.tests.utils import FrappeTestCase 6 | 7 | 8 | class TestHDPresetFilter(FrappeTestCase): 9 | pass 10 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_preset_filter_item/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/helpdesk/helpdesk/doctype/hd_preset_filter_item/__init__.py -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_preset_filter_item/hd_preset_filter_item.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Frappe Technologies and contributors 2 | # For license information, please see license.txt 3 | 4 | # import frappe 5 | from frappe.model.document import Document 6 | 7 | 8 | class HDPresetFilterItem(Document): 9 | pass 10 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_service_day/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/helpdesk/helpdesk/doctype/hd_service_day/__init__.py -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_service_day/hd_service_day.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors 3 | # For license information, please see license.txt 4 | 5 | from __future__ import unicode_literals 6 | 7 | # import frappe 8 | from frappe.model.document import Document 9 | 10 | 11 | class HDServiceDay(Document): 12 | pass 13 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_service_holiday_list/README.md: -------------------------------------------------------------------------------- 1 | List of Holidays. 2 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_service_holiday_list/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/helpdesk/helpdesk/doctype/hd_service_holiday_list/__init__.py -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_service_holiday_list/hd_service_holiday_list_calendar.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors 2 | // License: GNU General Public License v3. See license.txt 3 | 4 | frappe.views.calendar["HD Service Holiday List"] = { 5 | field_map: { 6 | start: "holiday_date", 7 | end: "holiday_date", 8 | id: "name", 9 | title: "description", 10 | allDay: "allDay", 11 | }, 12 | order_by: `from_date`, 13 | get_events_method: 14 | "helpdesk.helpdesk.doctype.hd_service_holiday_list.hd_service_holiday_list.get_events", 15 | filters: [ 16 | { 17 | fieldtype: "Link", 18 | fieldname: "holiday_list", 19 | options: "HD Service Holiday List", 20 | label: __("HD Service Holiday List"), 21 | }, 22 | ], 23 | }; 24 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_service_holiday_list/hd_service_holiday_list_dashboard.py: -------------------------------------------------------------------------------- 1 | def get_data(): 2 | return { 3 | "fieldname": "holiday_list", 4 | "non_standard_fieldnames": { 5 | "Company": "default_holiday_list", 6 | "Leave Period": "optional_holiday_list", 7 | }, 8 | "transactions": [{"items": ["Service Level", "SLA"]}], 9 | } 10 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_service_holiday_list/test_records.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "doctype": "HD Service Holiday List", 4 | "from_date": "2013-01-01", 5 | "to_date": "2013-12-31", 6 | "holidays": [ 7 | { 8 | "description": "New Year", 9 | "holiday_date": "2013-01-01" 10 | }, 11 | { 12 | "description": "Republic Day", 13 | "holiday_date": "2013-01-26" 14 | }, 15 | { 16 | "description": "Test Holiday", 17 | "holiday_date": "2013-02-01" 18 | } 19 | ], 20 | "holiday_list_name": "_Test Holiday List" 21 | } 22 | ] 23 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_service_level_agreement/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/helpdesk/helpdesk/doctype/hd_service_level_agreement/__init__.py -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_service_level_agreement/hd_service_level_agreement_dashboard.py: -------------------------------------------------------------------------------- 1 | from frappe import _ 2 | 3 | 4 | def get_data(): 5 | return { 6 | "fieldname": "sla", 7 | "transactions": [{"label": _("HD Ticket"), "items": ["HD Ticket"]}], 8 | } 9 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_service_level_agreement/patches/missing_sla_creation.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def execute(): 5 | QBTicket = frappe.qb.DocType("HD Ticket") 6 | frappe.qb.update(QBTicket).set( 7 | QBTicket.service_level_agreement_creation, QBTicket.creation 8 | ).where(QBTicket.service_level_agreement_creation.isnull()).run() 9 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_service_level_agreement/test_hd_service_level_agreement.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors 3 | # See license.txt 4 | from __future__ import unicode_literals 5 | 6 | import unittest 7 | 8 | 9 | class TestHDServiceLevelAgreement(unittest.TestCase): 10 | pass 11 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_service_level_agreement_fulfilled_on_status/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/helpdesk/helpdesk/doctype/hd_service_level_agreement_fulfilled_on_status/__init__.py -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_service_level_agreement_fulfilled_on_status/hd_service_level_agreement_fulfilled_on_status.json: -------------------------------------------------------------------------------- 1 | { 2 | "actions": [], 3 | "creation": "2021-05-26 21:11:29.176369", 4 | "doctype": "DocType", 5 | "editable_grid": 1, 6 | "engine": "InnoDB", 7 | "field_order": [ 8 | "status" 9 | ], 10 | "fields": [ 11 | { 12 | "fieldname": "status", 13 | "fieldtype": "Select", 14 | "in_list_view": 1, 15 | "label": "Status", 16 | "options": "Open\nReplied\nResolved\nClosed", 17 | "reqd": 1 18 | } 19 | ], 20 | "istable": 1, 21 | "links": [], 22 | "modified": "2023-09-18 12:23:34.266895", 23 | "modified_by": "Administrator", 24 | "module": "Helpdesk", 25 | "name": "HD Service Level Agreement Fulfilled On Status", 26 | "owner": "Administrator", 27 | "permissions": [], 28 | "quick_entry": 1, 29 | "sort_field": "modified", 30 | "sort_order": "DESC", 31 | "states": [], 32 | "track_changes": 1 33 | } -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_service_level_agreement_fulfilled_on_status/hd_service_level_agreement_fulfilled_on_status.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors 2 | # For license information, please see license.txt 3 | 4 | # import frappe 5 | from frappe.model.document import Document 6 | 7 | 8 | class HDServiceLevelAgreementFulfilledOnStatus(Document): 9 | pass 10 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_service_level_priority/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/helpdesk/helpdesk/doctype/hd_service_level_priority/__init__.py -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_service_level_priority/hd_service_level_priority.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors 3 | # For license information, please see license.txt 4 | 5 | from __future__ import unicode_literals 6 | 7 | # import frappe 8 | from frappe.model.document import Document 9 | 10 | 11 | class HDServiceLevelPriority(Document): 12 | pass 13 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_settings/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/helpdesk/helpdesk/doctype/hd_settings/__init__.py -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_settings/hd_settings.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.ui.form.on("HD Settings", { 5 | refresh: function (frm) { 6 | frm.add_custom_button(__("Regenerate Search Index"), () => { 7 | frappe.call({ 8 | method: "helpdesk.search.build_index", 9 | callback: function (r) { 10 | frappe.msgprint(__("Search Index Regenerated")); 11 | }, 12 | }); 13 | }); 14 | }, 15 | }); 16 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_settings/test_hd_settings.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors 3 | # See license.txt 4 | from __future__ import unicode_literals 5 | 6 | import unittest 7 | 8 | 9 | class TestHDSettings(unittest.TestCase): 10 | pass 11 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_stopword/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/helpdesk/helpdesk/doctype/hd_stopword/__init__.py -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_stopword/hd_stopword.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024, Frappe Technologies and contributors 2 | // For license information, please see license.txt 3 | 4 | // frappe.ui.form.on("HD Stopword", { 5 | // refresh(frm) { 6 | 7 | // }, 8 | // }); 9 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_stopword/hd_stopword.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024, Frappe Technologies and contributors 2 | # For license information, please see license.txt 3 | 4 | # import frappe 5 | from frappe.model.document import Document 6 | 7 | 8 | class HDStopword(Document): 9 | pass 10 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_stopword/test_hd_stopword.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024, Frappe Technologies and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | from frappe.tests.utils import FrappeTestCase 6 | 7 | 8 | class TestHDStopword(FrappeTestCase): 9 | pass 10 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_support_search_source/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/helpdesk/helpdesk/doctype/hd_support_search_source/__init__.py -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_support_search_source/hd_support_search_source.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors 3 | # For license information, please see license.txt 4 | 5 | from __future__ import unicode_literals 6 | 7 | from frappe.model.document import Document 8 | 9 | 10 | class HDSupportSearchSource(Document): 11 | pass 12 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_synonym/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/helpdesk/helpdesk/doctype/hd_synonym/__init__.py -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_synonym/hd_synonym.json: -------------------------------------------------------------------------------- 1 | { 2 | "actions": [], 3 | "allow_rename": 1, 4 | "autoname": "field:synonym", 5 | "creation": "2024-09-16 23:26:46.588705", 6 | "doctype": "DocType", 7 | "editable_grid": 1, 8 | "engine": "InnoDB", 9 | "field_order": [ 10 | "synonym" 11 | ], 12 | "fields": [ 13 | { 14 | "allow_in_quick_entry": 1, 15 | "fieldname": "synonym", 16 | "fieldtype": "Data", 17 | "in_list_view": 1, 18 | "label": "synonym", 19 | "unique": 1 20 | } 21 | ], 22 | "index_web_pages_for_search": 1, 23 | "istable": 1, 24 | "links": [], 25 | "modified": "2024-09-16 23:28:52.647865", 26 | "modified_by": "Administrator", 27 | "module": "Helpdesk", 28 | "name": "HD Synonym", 29 | "naming_rule": "Expression (old style)", 30 | "owner": "Administrator", 31 | "permissions": [], 32 | "sort_field": "creation", 33 | "sort_order": "DESC", 34 | "states": [] 35 | } -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_synonym/hd_synonym.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024, Frappe Technologies and contributors 2 | # For license information, please see license.txt 3 | 4 | # import frappe 5 | from frappe.model.document import Document 6 | 7 | 8 | class HDSynonym(Document): 9 | pass 10 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_synonyms/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/helpdesk/helpdesk/doctype/hd_synonyms/__init__.py -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_synonyms/hd_synonyms.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024, Frappe Technologies and contributors 2 | // For license information, please see license.txt 3 | 4 | // frappe.ui.form.on("HD Synonyms", { 5 | // refresh(frm) { 6 | 7 | // }, 8 | // }); 9 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_synonyms/hd_synonyms.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024, Frappe Technologies and contributors 2 | # For license information, please see license.txt 3 | 4 | # import frappe 5 | from frappe.model.document import Document 6 | 7 | 8 | class HDSynonyms(Document): 9 | pass 10 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_synonyms/test_hd_synonyms.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024, Frappe Technologies and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | from frappe.tests.utils import FrappeTestCase 6 | 7 | 8 | class TestHDSynonyms(FrappeTestCase): 9 | pass 10 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_team/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/helpdesk/helpdesk/doctype/hd_team/__init__.py -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_team/hd_team.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Frappe Technologies and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.ui.form.on("HD Team", { 5 | // refresh: function(frm) { 6 | // } 7 | }); 8 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_team/test_hd_team.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Frappe Technologies and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | import unittest 6 | 7 | 8 | class TestHDTeam(unittest.TestCase): 9 | pass 10 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_team_member/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/helpdesk/helpdesk/doctype/hd_team_member/__init__.py -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_team_member/hd_team_member.json: -------------------------------------------------------------------------------- 1 | { 2 | "actions": [], 3 | "autoname": "autoincrement", 4 | "creation": "2022-12-22 16:29:33.679680", 5 | "doctype": "DocType", 6 | "editable_grid": 1, 7 | "engine": "InnoDB", 8 | "field_order": [ 9 | "user" 10 | ], 11 | "fields": [ 12 | { 13 | "fieldname": "user", 14 | "fieldtype": "Link", 15 | "in_list_view": 1, 16 | "in_standard_filter": 1, 17 | "label": "User", 18 | "options": "User" 19 | } 20 | ], 21 | "index_web_pages_for_search": 1, 22 | "istable": 1, 23 | "links": [], 24 | "modified": "2022-12-22 18:52:50.658355", 25 | "modified_by": "Administrator", 26 | "module": "Helpdesk", 27 | "name": "HD Team Member", 28 | "naming_rule": "Autoincrement", 29 | "owner": "Administrator", 30 | "permissions": [], 31 | "sort_field": "modified", 32 | "sort_order": "DESC", 33 | "states": [] 34 | } 35 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_team_member/hd_team_member.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Frappe Technologies and contributors 2 | # For license information, please see license.txt 3 | 4 | # import frappe 5 | from frappe.model.document import Document 6 | 7 | 8 | class HDTeamMember(Document): 9 | pass 10 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_ticket/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/helpdesk/helpdesk/doctype/hd_ticket/__init__.py -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_ticket/hd_ticket.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023, Frappe Technologies and contributors 2 | // For license information, please see license.txt 3 | 4 | // frappe.ui.form.on("HD Ticket", { 5 | // refresh(frm) { 6 | 7 | // }, 8 | // }); 9 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_ticket/patches/fallback_ticket_type.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | from helpdesk.consts import DEFAULT_TICKET_TYPE 4 | 5 | 6 | def execute(): 7 | add_fallback() 8 | set_ticket_type() 9 | 10 | 11 | def add_fallback(): 12 | if frappe.db.exists("HD Ticket Type", DEFAULT_TICKET_TYPE): 13 | return 14 | d = frappe.new_doc("HD Ticket Type") 15 | d.is_system = True 16 | d.name = DEFAULT_TICKET_TYPE 17 | d.save() 18 | 19 | 20 | def set_ticket_type(): 21 | QBTicket = frappe.qb.DocType("HD Ticket") 22 | ( 23 | frappe.qb.update(QBTicket) 24 | .set(QBTicket.ticket_type, DEFAULT_TICKET_TYPE) 25 | .where(QBTicket.ticket_type.isnull()) 26 | .run() 27 | ) 28 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_ticket/patches/feedback_in_master.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def execute(): 5 | """ 6 | Move feedback rating and text from `HD Ticket Feedback Option` to `HD Ticket`. 7 | This is sometimes better because it avoids an extra API call when fetching. 8 | """ 9 | for t in frappe.get_all("HD Ticket"): 10 | t = frappe.get_doc("HD Ticket", t.name) 11 | if not t.feedback: 12 | continue 13 | if not frappe.db.exists("HD Ticket Feedback Option", t.feedback): 14 | continue 15 | f = frappe.get_doc("HD Ticket Feedback Option", t.feedback) 16 | t.db_set("feedback_rating", f.rating) 17 | t.db_set("feedback_text", f.label) 18 | frappe.db.commit() 19 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_ticket/patches/replace_overdue_failed.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def execute(): 5 | QBTicket = frappe.qb.DocType("HD Ticket") 6 | frappe.qb.update(QBTicket).set(QBTicket.agreement_status, "Failed").where( 7 | QBTicket.agreement_status == "Overdue" 8 | ).run() 9 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_ticket_activity/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/helpdesk/helpdesk/doctype/hd_ticket_activity/__init__.py -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_ticket_activity/hd_ticket_activity.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Frappe Technologies and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.ui.form.on("HD Ticket Activity", { 5 | // refresh: function(frm) { 6 | // } 7 | }); 8 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_ticket_activity/hd_ticket_activity.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Frappe Technologies and contributors 2 | # For license information, please see license.txt 3 | 4 | import frappe 5 | from frappe.model.document import Document 6 | 7 | 8 | class HDTicketActivity(Document): 9 | pass 10 | 11 | 12 | def log_ticket_activity(ticket, action): 13 | return frappe.get_doc( 14 | {"doctype": "HD Ticket Activity", "ticket": ticket, "action": action} 15 | ).insert(ignore_permissions=True) 16 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_ticket_activity/test_hd_ticket_activity.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Frappe Technologies and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | from frappe.tests.utils import FrappeTestCase 6 | 7 | 8 | class TestHDTicketActivity(FrappeTestCase): 9 | pass 10 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_ticket_comment/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/helpdesk/helpdesk/doctype/hd_ticket_comment/__init__.py -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_ticket_comment/hd_ticket_comment.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Frappe Technologies and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.ui.form.on("HD Ticket Comment", { 5 | // refresh: function(frm) { 6 | // } 7 | }); 8 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_ticket_comment/hd_ticket_comment.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Frappe Technologies and contributors 2 | # For license information, please see license.txt 3 | 4 | from frappe.model.document import Document 5 | 6 | from helpdesk.mixins.mentions import HasMentions 7 | from helpdesk.utils import capture_event, publish_event 8 | 9 | 10 | class HDTicketComment(HasMentions, Document): 11 | mentions_field = "content" 12 | 13 | def on_update(self): 14 | self.notify_mentions() 15 | 16 | def after_insert(self): 17 | event = "helpdesk:new-ticket-comment" 18 | data = {"ticket_id": self.reference_ticket} 19 | telemetry_event = "ticket_comment_added" 20 | 21 | publish_event(event, data) 22 | capture_event(telemetry_event) 23 | 24 | def after_delete(self): 25 | event = "helpdesk:delete-ticket-comment" 26 | data = {"ticket_id": self.reference_ticket} 27 | telemetry_event = "ticket_comment_deleted" 28 | 29 | publish_event(event, data) 30 | capture_event(telemetry_event) 31 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_ticket_comment/test_hd_ticket_comment.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Frappe Technologies and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | from frappe.tests.utils import FrappeTestCase 6 | 7 | 8 | class TestHDTicketComment(FrappeTestCase): 9 | pass 10 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_ticket_feedback_option/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/helpdesk/helpdesk/doctype/hd_ticket_feedback_option/__init__.py -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_ticket_feedback_option/hd_ticket_feedback_option.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023, Frappe Technologies and contributors 2 | // For license information, please see license.txt 3 | 4 | // frappe.ui.form.on("HD Ticket Feedback Option", { 5 | // refresh(frm) { 6 | 7 | // }, 8 | // }); 9 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_ticket_feedback_option/hd_ticket_feedback_option.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023, Frappe Technologies and contributors 2 | # For license information, please see license.txt 3 | 4 | import frappe 5 | from frappe import _ 6 | from frappe.model.document import Document 7 | 8 | 9 | class HDTicketFeedbackOption(Document): 10 | allowed_ratings = [0.2, 0.4, 0.6, 0.8, 1.0] 11 | 12 | def validate(self): 13 | self.validate_allowed_ratings() 14 | self.validate_bounds() 15 | 16 | def validate_allowed_ratings(self): 17 | if self.rating not in self.allowed_ratings: 18 | frappe.throw(_("Rating {0} is not allowed").format(self.rating)) 19 | 20 | def validate_bounds(self): 21 | if not (0.2 <= self.rating <= 1.0): 22 | frappe.throw(_("Rating must be between 0.2 and 1.0")) 23 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_ticket_feedback_option/patches/label_as_name.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def execute(): 5 | doctype = "HD Ticket Feedback Option" 6 | options = frappe.get_list( 7 | doctype, fields=["name", "label"], limit_page_length=99999 8 | ) 9 | for opt in options: 10 | if opt.name != opt.label: 11 | frappe.rename_doc(doctype, opt.name, opt.label, ignore_if_exists=True) 12 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_ticket_feedback_option/patches/ootb.py: -------------------------------------------------------------------------------- 1 | from helpdesk.setup.ticket_feedback import create_ticket_feedback_options 2 | 3 | 4 | def execute(): 5 | create_ticket_feedback_options() 6 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_ticket_feedback_option/test_hd_ticket_feedback_option.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023, Frappe Technologies and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | from frappe.tests.utils import FrappeTestCase 6 | 7 | 8 | class TestHDTicketFeedbackOption(FrappeTestCase): 9 | pass 10 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_ticket_priority/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/helpdesk/helpdesk/doctype/hd_ticket_priority/__init__.py -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_ticket_priority/hd_ticket_priority.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.ui.form.on("HD Ticket Priority", { 5 | // refresh: function(frm) { 6 | // } 7 | }); 8 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_ticket_priority/hd_ticket_priority.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors 3 | # For license information, please see license.txt 4 | 5 | from __future__ import unicode_literals 6 | 7 | from frappe.model.document import Document 8 | 9 | 10 | class HDTicketPriority(Document): 11 | pass 12 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_ticket_priority/test_hd_ticket_priority.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors 3 | # See license.txt 4 | from __future__ import unicode_literals 5 | 6 | import unittest 7 | 8 | 9 | class TestHDTicketPriority(unittest.TestCase): 10 | pass 11 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_ticket_template/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/helpdesk/helpdesk/doctype/hd_ticket_template/__init__.py -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_ticket_template/hd_ticket_template.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Frappe Technologies and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.ui.form.on("HD Ticket Template", { 5 | setup: function (frm) {}, 6 | }); 7 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_ticket_template/test_hd_ticket_template.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Frappe Technologies and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | import unittest 6 | 7 | 8 | class TestHDTicketTemplate(unittest.TestCase): 9 | pass 10 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_ticket_template_field/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/helpdesk/helpdesk/doctype/hd_ticket_template_field/__init__.py -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_ticket_template_field/hd_ticket_template_field.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023, Frappe Technologies and contributors 2 | // For license information, please see license.txt 3 | 4 | // frappe.ui.form.on("HD Ticket Template Field", { 5 | // refresh(frm) { 6 | 7 | // }, 8 | // }); 9 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_ticket_template_field/hd_ticket_template_field.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023, Frappe Technologies and contributors 2 | # For license information, please see license.txt 3 | 4 | # import frappe 5 | from frappe.model.document import Document 6 | 7 | 8 | class HDTicketTemplateField(Document): 9 | pass 10 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_ticket_template_field/test_hd_ticket_template_field.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023, Frappe Technologies and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | from frappe.tests.utils import FrappeTestCase 6 | 7 | 8 | class TestHDTicketTemplateField(FrappeTestCase): 9 | pass 10 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_ticket_type/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/helpdesk/helpdesk/doctype/hd_ticket_type/__init__.py -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_ticket_type/hd_ticket_type.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.ui.form.on("HD Ticket Type", { 5 | refresh: function (frm) {}, 6 | }); 7 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_ticket_type/hd_ticket_type.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | from frappe import _ 3 | from frappe.model.document import Document 4 | 5 | 6 | class HDTicketType(Document): 7 | def on_trash(self): 8 | self.prevent_system_delete() 9 | 10 | def prevent_system_delete(self): 11 | if self.is_system: 12 | frappe.throw(_("System types can not be deleted")) 13 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_ticket_type/test_hd_ticket_type.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors 3 | # See license.txt 4 | from __future__ import unicode_literals 5 | 6 | import unittest 7 | 8 | 9 | class TestHDTicketType(unittest.TestCase): 10 | pass 11 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_view/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/helpdesk/helpdesk/doctype/hd_view/__init__.py -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_view/hd_view.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025, Frappe Technologies and contributors 2 | // For license information, please see license.txt 3 | 4 | // frappe.ui.form.on("HD View", { 5 | // refresh(frm) { 6 | 7 | // }, 8 | // }); 9 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/doctype/hd_view/test_hd_view.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2025, Frappe Technologies and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | from frappe.tests import IntegrationTestCase, UnitTestCase 6 | 7 | # On IntegrationTestCase, the doctype test records and all 8 | # link-field test record dependencies are recursively loaded 9 | # Use these module variables to add/remove to/from that list 10 | EXTRA_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"] 11 | IGNORE_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"] 12 | 13 | 14 | class UnitTestHDView(UnitTestCase): 15 | """ 16 | Unit tests for HDView. 17 | Use this class for testing individual functions and methods. 18 | """ 19 | 20 | pass 21 | 22 | 23 | class IntegrationTestHDView(IntegrationTestCase): 24 | """ 25 | Integration tests for HDView. 26 | Use this class for testing interactions between multiple components. 27 | """ 28 | 29 | pass 30 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/report/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/helpdesk/helpdesk/report/__init__.py -------------------------------------------------------------------------------- /helpdesk/helpdesk/report/first_response_time_for_tickets/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/helpdesk/helpdesk/report/first_response_time_for_tickets/__init__.py -------------------------------------------------------------------------------- /helpdesk/helpdesk/report/first_response_time_for_tickets/first_response_time_for_tickets.json: -------------------------------------------------------------------------------- 1 | { 2 | "add_total_row": 0, 3 | "columns": [], 4 | "creation": "2020-08-10 18:12:42.391224", 5 | "disable_prepared_report": 0, 6 | "disabled": 0, 7 | "docstatus": 0, 8 | "doctype": "Report", 9 | "filters": [], 10 | "idx": 0, 11 | "is_standard": "Yes", 12 | "letter_head": "", 13 | "modified": "2021-10-21 14:31:29.437910", 14 | "modified_by": "Administrator", 15 | "module": "Helpdesk", 16 | "name": "First Response Time for Tickets", 17 | "owner": "Administrator", 18 | "prepared_report": 0, 19 | "query": "select date(creation) as creation_date, avg(mins_to_first_response) from tabTicket where creation > '2016-05-01' group by date(creation) order by creation_date;", 20 | "ref_doctype": "HD Ticket", 21 | "report_name": "First Response Time for Tickets", 22 | "report_type": "Script Report", 23 | "roles": [ 24 | { 25 | "role": "Support Team" 26 | } 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/report/support_hour_distribution/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/helpdesk/helpdesk/report/support_hour_distribution/__init__.py -------------------------------------------------------------------------------- /helpdesk/helpdesk/report/support_hour_distribution/support_hour_distribution.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors 2 | // For license information, please see license.txt 3 | /* eslint-disable */ 4 | 5 | frappe.query_reports["Support Hour Distribution"] = { 6 | filters: [ 7 | { 8 | lable: __("From Date"), 9 | fieldname: "from_date", 10 | fieldtype: "Date", 11 | default: frappe.datetime.nowdate(), 12 | reqd: 1, 13 | }, 14 | { 15 | lable: __("To Date"), 16 | fieldname: "to_date", 17 | fieldtype: "Date", 18 | default: frappe.datetime.nowdate(), 19 | reqd: 1, 20 | }, 21 | ], 22 | }; 23 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/report/support_hour_distribution/support_hour_distribution.json: -------------------------------------------------------------------------------- 1 | { 2 | "add_total_row": 0, 3 | "columns": [], 4 | "creation": "2017-07-13 17:14:40.408706", 5 | "disable_prepared_report": 0, 6 | "disabled": 0, 7 | "docstatus": 0, 8 | "doctype": "Report", 9 | "filters": [], 10 | "idx": 0, 11 | "is_standard": "Yes", 12 | "letter_head": "", 13 | "modified": "2021-10-21 14:31:29.524026", 14 | "modified_by": "Administrator", 15 | "module": "Helpdesk", 16 | "name": "Support Hour Distribution", 17 | "owner": "Administrator", 18 | "prepared_report": 0, 19 | "ref_doctype": "Issue", 20 | "report_name": "Support Hour Distribution", 21 | "report_type": "Script Report", 22 | "roles": [ 23 | { 24 | "role": "Support Team" 25 | }, 26 | { 27 | "role": "System Manager" 28 | } 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/report/ticket_analytics/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/helpdesk/helpdesk/report/ticket_analytics/__init__.py -------------------------------------------------------------------------------- /helpdesk/helpdesk/report/ticket_analytics/test_ticket_analytics.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | import unittest 4 | 5 | 6 | class TestTicketAnalytics(unittest.TestCase): 7 | pass 8 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/report/ticket_analytics/ticket_analytics.json: -------------------------------------------------------------------------------- 1 | { 2 | "add_total_row": 1, 3 | "columns": [], 4 | "creation": "2020-10-09 19:52:10.227317", 5 | "disable_prepared_report": 0, 6 | "disabled": 0, 7 | "docstatus": 0, 8 | "doctype": "Report", 9 | "filters": [], 10 | "idx": 0, 11 | "is_standard": "Yes", 12 | "modified": "2021-10-21 14:31:29.591583", 13 | "modified_by": "Administrator", 14 | "module": "Helpdesk", 15 | "name": "Ticket Analytics", 16 | "owner": "Administrator", 17 | "prepared_report": 0, 18 | "ref_doctype": "HD Ticket", 19 | "report_name": "Ticket Analytics", 20 | "report_type": "Script Report", 21 | "roles": [ 22 | { 23 | "role": "Support Team" 24 | } 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/report/ticket_search_analysis/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/helpdesk/helpdesk/report/ticket_search_analysis/__init__.py -------------------------------------------------------------------------------- /helpdesk/helpdesk/report/ticket_search_analysis/ticket_search_analysis.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2024, Frappe Technologies and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.query_reports["Ticket-Search Analysis"] = { 5 | filters: [ 6 | // { 7 | // "fieldname": "my_filter", 8 | // "label": __("My Filter"), 9 | // "fieldtype": "Data", 10 | // "reqd": 1, 11 | // }, 12 | ], 13 | }; 14 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/report/ticket_search_analysis/ticket_search_analysis.json: -------------------------------------------------------------------------------- 1 | { 2 | "add_total_row": 0, 3 | "columns": [], 4 | "creation": "2024-09-11 22:31:05.837918", 5 | "disabled": 0, 6 | "docstatus": 0, 7 | "doctype": "Report", 8 | "filters": [], 9 | "idx": 0, 10 | "is_standard": "Yes", 11 | "letterhead": null, 12 | "modified": "2024-09-11 22:31:05.837918", 13 | "modified_by": "Administrator", 14 | "module": "Helpdesk", 15 | "name": "Ticket-Search Analysis", 16 | "owner": "Administrator", 17 | "prepared_report": 0, 18 | "ref_doctype": "HD Ticket", 19 | "report_name": "Ticket-Search Analysis", 20 | "report_type": "Script Report", 21 | "roles": [ 22 | { 23 | "role": "System Manager" 24 | }, 25 | { 26 | "role": "Agent" 27 | }, 28 | { 29 | "role": "Support Team" 30 | }, 31 | { 32 | "role": "Supported Site User" 33 | }, 34 | { 35 | "role": "External Agent" 36 | } 37 | ] 38 | } -------------------------------------------------------------------------------- /helpdesk/helpdesk/report/ticket_summary/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/helpdesk/helpdesk/report/ticket_summary/__init__.py -------------------------------------------------------------------------------- /helpdesk/helpdesk/report/ticket_summary/ticket_summary.json: -------------------------------------------------------------------------------- 1 | { 2 | "add_total_row": 0, 3 | "columns": [], 4 | "creation": "2022-11-21 13:02:56.691256", 5 | "disable_prepared_report": 0, 6 | "disabled": 0, 7 | "docstatus": 0, 8 | "doctype": "Report", 9 | "filters": [], 10 | "idx": 0, 11 | "is_standard": "Yes", 12 | "modified": "2022-11-21 13:02:56.691256", 13 | "modified_by": "Administrator", 14 | "module": "Helpdesk", 15 | "name": "Ticket Summary", 16 | "owner": "Administrator", 17 | "prepared_report": 0, 18 | "ref_doctype": "HD Ticket", 19 | "report_name": "Ticket Summary", 20 | "report_type": "Script Report", 21 | "roles": [ 22 | { 23 | "role": "Support Team" 24 | } 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/web_form/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/helpdesk/helpdesk/web_form/__init__.py -------------------------------------------------------------------------------- /helpdesk/helpdesk/web_form/tickets/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/helpdesk/helpdesk/web_form/tickets/__init__.py -------------------------------------------------------------------------------- /helpdesk/helpdesk/web_form/tickets/tickets.js: -------------------------------------------------------------------------------- 1 | frappe.ready(function () { 2 | // bind events here 3 | }); 4 | -------------------------------------------------------------------------------- /helpdesk/helpdesk/web_form/tickets/tickets.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | 4 | def get_context(context): 5 | # do your magic here 6 | pass 7 | -------------------------------------------------------------------------------- /helpdesk/modules.txt: -------------------------------------------------------------------------------- 1 | Helpdesk 2 | -------------------------------------------------------------------------------- /helpdesk/overrides/contact.py: -------------------------------------------------------------------------------- 1 | from frappe.contacts.doctype.contact.contact import Contact 2 | 3 | 4 | class CustomContact(Contact): 5 | @staticmethod 6 | def default_list_data(): 7 | columns = [ 8 | { 9 | "label": "Name", 10 | "type": "Data", 11 | "key": "full_name", 12 | "width": "17rem", 13 | }, 14 | { 15 | "label": "Email", 16 | "type": "Data", 17 | "key": "email_id", 18 | "width": "24rem", 19 | }, 20 | # { 21 | # "label": "Phone", 22 | # "type": "Data", 23 | # "key": "mobile_no", 24 | # "width": "12rem", 25 | # }, 26 | { 27 | "label": "Created On", 28 | "type": "Datetime", 29 | "key": "creation", 30 | "width": "8rem", 31 | }, 32 | ] 33 | return {"columns": columns} 34 | -------------------------------------------------------------------------------- /helpdesk/patches/add_priority_integer.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | BUILTIN_PRIORITIES = { 4 | "Urgent": 100, 5 | "High": 200, 6 | "Medium": 300, 7 | "Low": 400, 8 | } 9 | 10 | 11 | def execute(): 12 | for p in BUILTIN_PRIORITIES: 13 | d = frappe.get_doc("HD Ticket Priority", p) 14 | 15 | if d.integer_value: 16 | continue 17 | 18 | d.integer_value = BUILTIN_PRIORITIES[p] 19 | d.save() 20 | -------------------------------------------------------------------------------- /helpdesk/patches/agent_manager_perms.py: -------------------------------------------------------------------------------- 1 | from helpdesk.setup.install import add_agent_manager_permissions 2 | 3 | 4 | def execute(): 5 | add_agent_manager_permissions() 6 | -------------------------------------------------------------------------------- /helpdesk/patches/change_app_name_to_helpdesk.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | from frappe.installer import add_to_installed_apps, remove_from_installed_apps 3 | 4 | 5 | def execute(): 6 | old_app_name = "frappedesk" 7 | new_app_name = "helpdesk" 8 | installed_apps = frappe.db.get_global("installed_apps") 9 | 10 | if old_app_name in installed_apps: 11 | remove_from_installed_apps(old_app_name) 12 | add_to_installed_apps(new_app_name) 13 | -------------------------------------------------------------------------------- /helpdesk/patches/communication_read_perm_agent.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | from frappe.permissions import add_permission 3 | 4 | 5 | def execute(): 6 | if frappe.db.exists("Role", "Agent"): 7 | add_permission("Communication", "Agent", 0) 8 | -------------------------------------------------------------------------------- /helpdesk/patches/create_helpdesk_folder.py: -------------------------------------------------------------------------------- 1 | from ..setup.file import create_helpdesk_folder 2 | 3 | 4 | def execute(): 5 | create_helpdesk_folder() 6 | -------------------------------------------------------------------------------- /helpdesk/patches/default_article_category.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | from helpdesk.consts import DEFAULT_ARTICLE_CATEGORY 4 | 5 | 6 | def execute(): 7 | default_category = frappe.db.exists( 8 | "HD Article Category", {"category_name": DEFAULT_ARTICLE_CATEGORY} 9 | ) 10 | if not default_category: 11 | default_category = frappe.get_doc( 12 | { 13 | "doctype": "HD Article Category", 14 | "category_name": DEFAULT_ARTICLE_CATEGORY, 15 | } 16 | ).insert() 17 | default_category = default_category.get("name") 18 | 19 | articles = frappe.get_all("HD Article", filters={"category": ""}, pluck="name") 20 | # create one default article for general category 21 | if len(articles) == 0: 22 | frappe.new_doc( 23 | "HD Article", title="New Article", category=default_category 24 | ).insert() 25 | else: 26 | for article in articles: 27 | frappe.db.set_value("HD Article", article, "category", default_category) 28 | -------------------------------------------------------------------------------- /helpdesk/patches/remove_agents_teams_default_views.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def execute(): 5 | print("Removing default views of agents") 6 | frappe.db.delete("HD View", {"dt": "HD Agent"}) 7 | print("Removed default views of agents") 8 | print("--------------------") 9 | print("Removing default views of teams") 10 | frappe.db.delete("HD View", {"dt": "HD Team"}) 11 | print("Removed default views of teams") 12 | -------------------------------------------------------------------------------- /helpdesk/patches/rename_frappedesk_module_references.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | old_module = "FrappeDesk" 4 | new_module = "Helpdesk" 5 | doctypes = [ 6 | "Report", 7 | "Server Script", 8 | "Web Form", 9 | ] 10 | 11 | 12 | def execute(): 13 | for doctype in doctypes: 14 | QBTable = frappe.qb.DocType(doctype) 15 | 16 | frappe.qb.update(QBTable).set(QBTable.module, new_module).where( 17 | QBTable.module == old_module 18 | ).run() 19 | -------------------------------------------------------------------------------- /helpdesk/patches/template_remove_default_fields.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | from frappe.query_builder import Case 3 | 4 | 5 | def execute(): 6 | QBDocField = frappe.qb.DocType("HD Ticket Template DocField") 7 | case = Case.any( 8 | [QBDocField.fieldname == "Subject", QBDocField.fieldname == "Description"] 9 | ) 10 | frappe.qb.from_(QBDocField).delete().where(case).run() 11 | frappe.db.commit() 12 | -------------------------------------------------------------------------------- /helpdesk/patches/update_hd_team_users.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def execute(): 5 | teams = frappe.get_all("HD Team", pluck="name") 6 | for team in teams: 7 | existing_agents = frappe.get_all( 8 | "HD Team Item", filters={"team": team}, pluck="parent" 9 | ) # agents in HD Agent doctype 10 | team_users = frappe.get_all( 11 | "HD Team Member", filters={"parent": team}, pluck="user" 12 | ) # agents in HD Team doctype 13 | 14 | for agent in existing_agents: 15 | is_agent_active = frappe.get_value("HD Agent", agent, "is_active") 16 | if is_agent_active and agent not in team_users: 17 | team_doc = ( 18 | frappe.get_doc("HD Team", team) 19 | .append("users", {"user": agent}) 20 | .save() 21 | ) 22 | print("Agent Added") 23 | -------------------------------------------------------------------------------- /helpdesk/setup/default_template.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | from helpdesk.consts import DEFAULT_TICKET_TEMPLATE 4 | 5 | 6 | def create_default_template(): 7 | if frappe.db.exists("HD Ticket Template", DEFAULT_TICKET_TEMPLATE): 8 | return 9 | doc = { 10 | "doctype": "HD Ticket Template", 11 | "template_name": DEFAULT_TICKET_TEMPLATE, 12 | } 13 | frappe.get_doc(doc).save() 14 | -------------------------------------------------------------------------------- /helpdesk/setup/file.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def create_helpdesk_folder(): 5 | f = frappe.new_doc("File") 6 | f.file_name = "Helpdesk" 7 | f.is_folder = 1 8 | f.folder = "Home" 9 | f.insert(ignore_if_duplicate=True) 10 | -------------------------------------------------------------------------------- /helpdesk/setup/setup_wizard.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors 2 | # License: GNU General Public License v3. See license.txt 3 | import frappe 4 | 5 | # from frappe import _ 6 | 7 | 8 | # nosemgrep 9 | def setup_complete(args=None): 10 | 11 | email = args.get("email") or frappe.session.user 12 | if not email: 13 | return 14 | # Create first Agent for the user 15 | new_user = frappe.db.get_list( 16 | "User", filters={"email": email}, limit=1, pluck="name" 17 | ) 18 | if not new_user: 19 | return 20 | new_user = new_user[0] 21 | new_agent = frappe.new_doc("HD Agent") 22 | new_agent.user = new_user 23 | new_agent.insert(ignore_if_duplicate=True) 24 | -------------------------------------------------------------------------------- /helpdesk/setup/ticket_feedback.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | OPTIONS = { 4 | 0.2: ["Response did not help", "No resolution provided"], 5 | 0.4: ["Delayed response time", "Adequate help, bit slow"], 6 | 0.6: ["Clear guidance given", "Helpful answers, reasonable wait"], 7 | 0.8: ["Quick and precise solutions", "Prompt, informative support"], 8 | 1.0: ["Exceptional support experience", "Instant, top-notch help"], 9 | } 10 | 11 | 12 | def create_ticket_feedback_options(): 13 | for rating in OPTIONS: 14 | for label in OPTIONS[rating]: 15 | doc = { 16 | "doctype": "HD Ticket Feedback Option", 17 | "rating": rating, 18 | "label": label, 19 | } 20 | if not frappe.db.exists(doc): 21 | frappe.get_doc(doc).insert() 22 | -------------------------------------------------------------------------------- /helpdesk/setup/ticket_type.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | from helpdesk.consts import DEFAULT_TICKET_TYPE 4 | 5 | DT = "HD Ticket Type" 6 | TICKET_TYPES = ["Question", "Bug", "Incident"] 7 | 8 | 9 | def create_fallback_ticket_type(): 10 | if frappe.db.exists(DT, DEFAULT_TICKET_TYPE): 11 | return 12 | 13 | d = frappe.new_doc(DT) 14 | d.name = DEFAULT_TICKET_TYPE 15 | d.is_system = True 16 | d.save() 17 | 18 | 19 | def create_ootb_ticket_types(): 20 | for ticket_type in TICKET_TYPES: 21 | if frappe.db.exists(DT, ticket_type): 22 | return 23 | 24 | d = frappe.new_doc(DT) 25 | d.name = ticket_type 26 | d.is_system = False 27 | d.save() 28 | -------------------------------------------------------------------------------- /helpdesk/templates/components/contact_with_us.html: -------------------------------------------------------------------------------- 1 |
2 | {% if hr_tag %} 3 |
4 | {% endif %} 5 |
6 |
7 |

{{ _("Still got questions? We're here to assist you.") }}

8 |
9 |
10 | 13 |
14 |
15 | 16 | 23 |
24 | -------------------------------------------------------------------------------- /helpdesk/templates/components/knowledge_base_title_with_search_bar.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |

Knowledge Base

5 |
6 | 15 |
16 |
17 |
18 |
19 | -------------------------------------------------------------------------------- /helpdesk/templates/components/search/search.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | @frappe.whitelist(allow_guest=True) 5 | def search_text(text): 6 | text = frappe.db.escape(text) 7 | 8 | categories = frappe.db.sql( 9 | f""" 10 | SELECT 11 | category.category_name as title, 12 | category.route 13 | FROM `tabCategory` category 14 | WHERE (category.category_name LIKE '%{text}%') OR (category.description LIKE '%{text}%') 15 | """, 16 | as_dict=True, 17 | ) 18 | 19 | articles = frappe.db.sql( 20 | f""" 21 | SELECT 22 | article.title, 23 | article.route 24 | FROM `tabArticle` article 25 | WHERE (article.title LIKE '%{text}%') OR (article.content LIKE '%{text}%') 26 | """, 27 | as_dict=True, 28 | ) 29 | 30 | results = articles + categories 31 | 32 | return results 33 | -------------------------------------------------------------------------------- /helpdesk/templates/emails/email_verification.html: -------------------------------------------------------------------------------- 1 | {% import "templates/emails/macros.html" as utils %} {% block content %} 2 | 3 | 4 |
5 |

Hello,

6 |

Please confirm your email address by clicking the button below:

7 | {{ utils.button('Verify Account', link, true) }} {{ utils.separator() }} {{ 8 | utils.signature() }} 9 |
10 | 11 | 12 | {% endblock %} 13 | -------------------------------------------------------------------------------- /helpdesk/templates/emails/macros.html: -------------------------------------------------------------------------------- 1 | {% macro button(label, link, primary = true) %} 2 | 3 | 4 | 5 | 8 | 9 | 10 |
6 | {{ label }} 7 |
11 | {% endmacro %} {% macro link(label, link) %} 12 | {{ label }} 13 | {% endmacro %} {% macro separator() %} 14 | 15 | 16 | 17 | 20 | 21 | 22 | 23 | {% endmacro %} {% macro signature() %} 24 |

25 | Thanks,
26 | Team Frappe 27 |

28 | {% endmacro %} 29 | -------------------------------------------------------------------------------- /helpdesk/templates/emails/new_reply_on_customer_portal_notification.html: -------------------------------------------------------------------------------- 1 |
2 |

Ticket #{{ ticket_id }}

3 |

You have a new reply on this ticket

4 |
5 |
6 |

Message

7 |
15 | {{ message }} 16 |
17 |
18 |

Please visit the customer portal to reply to this message

19 | View in Portal
26 |
27 | -------------------------------------------------------------------------------- /helpdesk/templates/emails/new_reply_to_agent.html: -------------------------------------------------------------------------------- 1 |
2 |

Hello,

3 |

You have a new reply on the ticket #{{ ticket_id }}.

4 |

Subject: {{ticket_subject}}

5 |

Raised By: {{raised_by}}

6 |

Priority: {{priority}}

7 | 8 |
9 |

10 | You can view and respond to this ticket by 11 | clicking here. 12 |

13 |

Regards,
Support Team

14 |
15 | -------------------------------------------------------------------------------- /helpdesk/templates/emails/notification.html: -------------------------------------------------------------------------------- 1 |

{{ title }}

2 |
3 |
{{ comment }}
4 | {{ button_label }} 5 |
6 | -------------------------------------------------------------------------------- /helpdesk/www/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/helpdesk/www/__init__.py -------------------------------------------------------------------------------- /helpdesk/www/helpdesk/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/helpdesk/www/helpdesk/__init__.py -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "postinstall": "cd desk && yarn install", 5 | "dev": "cd desk && yarn dev", 6 | "build": "cd desk && yarn build" 7 | }, 8 | "workspaces": ["desk", "frappe-ui"], 9 | "resolutions": { 10 | "cheerio": "1.0.0-rc.12" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "helpdesk" 3 | authors = [ 4 | { name = "Frappe Technologies Pvt Ltd", email = "hello@frappe.io"} 5 | ] 6 | description = "Open Source Customer Service Software" 7 | requires-python = ">=3.10" 8 | readme = "README.md" 9 | dynamic = ["version"] 10 | dependencies = [ 11 | # Core dependencies 12 | "textblob==0.18.0.post0", 13 | ] 14 | [build-system] 15 | requires = ["flit_core >=3.4,<4"] 16 | build-backend = "flit_core.buildapi" -------------------------------------------------------------------------------- /screenshot.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/helpdesk/f266dcba739dd7724a1344dda8b3327f9ce383be/screenshot.webp --------------------------------------------------------------------------------