// // Copyright © 2024 Hardcore Engineering Inc. // // Licensed under the Eclipse Public License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. You may // obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // // See the License for the specific language governing permissions and // limitations under the License. // import contact from '@hcengineering/contact' import { type Class, type Client, type DocumentQuery, type Ref, type RelatedDocument, SortingOrder, type WithLookup, type Doc } from '@hcengineering/core' import { type CollaborativeDocumentSection, type Document, type DocumentSection, type DocumentSpace, DocumentState, type DocumentMeta } from '@hcengineering/controlled-documents' import { type Resources } from '@hcengineering/platform' import { type ObjectSearchResult, getClient, MessageBox, getCollaboratorClient } from '@hcengineering/presentation' import { EmptyMarkup } from '@hcengineering/text' import { showPopup } from '@hcengineering/ui' import { deleteObjects } from '@hcengineering/view-resources' import CreateDocument from './components/CreateDocument.svelte' import QmsDocumentWizard from './components/create-doc/QmsDocumentWizard.svelte' import QmsTemplateWizard from './components/create-doc/QmsTemplateWizard.svelte' import DocumentStatusTag from './components/document/common/DocumentStatusTag.svelte' import DocumentSpacePresenter from './components/hierarchy/DocumentSpacePresenter.svelte' import DocumentPresenter from './components/document/presenters/DocumentPresenter.svelte' import StatePresenter from './components/document/presenters/StatePresenter.svelte' import StateFilterValuePresenter from './components/document/presenters/StateFilterValuePresenter.svelte' import TitlePresenter from './components/document/presenters/TitlePresenter.svelte' import OwnerPresenter from './components/document/presenters/OwnerPresenter.svelte' import AddCommentPopup from './components/document/popups/AddCommentPopup.svelte' import DocumentCommentsPopup from './components/document/popups/DocumentCommentsPopup.svelte' import ChangeOwnerPopup from './components/document/popups/ChangeOwnerPopup.svelte' import DeleteCategoryPopup from './components/category/popups/DeleteCategoryPopup.svelte' import CategoryPresenter from './components/category/presenters/CategoryPresenter.svelte' import Documents from './components/Documents.svelte' import DocumentsContainer from './components/DocumentsContainer.svelte' import MyDocuments from './components/MyDocuments.svelte' import EditDoc from './components/EditDoc.svelte' import EditProjectDoc from './components/EditProjectDoc.svelte' import DocumentItem from './components/DocumentItem.svelte' import NewDocumentHeader from './components/NewDocumentHeader.svelte' import DocumentIcon from './components/icons/DocumentIcon.svelte' import DocumentTemplates from './components/DocumentTemplates.svelte' import EditTemplateSections from './components/template/EditTemplateSections.svelte' import Categories from './components/Categories.svelte' import EditDocumentCategory from './components/EditDocumentCategory.svelte' import DocumentTitle from './components/document/DocumentTitle.svelte' import EditDocContent from './components/document/EditDocContent.svelte' import CollaborativeSectionEditor from './components/document/editors/CollaborativeSectionEditor.svelte' import AttachmentsSectionEditor from './components/document/editors/AttachmentsSectionEditor.svelte' import CollaborativeSectionPresenter from './components/document/presenters/CollaborativeSectionPresenter.svelte' import AttachmentsSectionPresenter from './components/document/presenters/AttachmentsSectionPresenter.svelte' import DocumentVersionPresenter from './components/document/presenters/DocumentVersionPresenter.svelte' import DocumentSectionDeletePopup from './components/DocumentSectionDeletePopup.svelte' import DocumentReviewRequest from './components/requests/DocumentReviewRequest.svelte' import DocumentReviewRequestPresenter from './components/requests/DocumentReviewRequestPresenter.svelte' import DocumentApprovalRequest from './components/requests/DocumentApprovalRequest.svelte' import DocumentApprovalRequestPresenter from './components/requests/DocumentApprovalRequestPresenter.svelte' import ControlledStateFilterValuePresenter from './components/document/presenters/ControlledStateFilterValuePresenter.svelte' import DocumentMetaPresenter from './components/DocumentMetaPresenter.svelte' import CreateDocumentsSpace from './components/docspace/CreateDocumentsSpace.svelte' import CreateDocumentSpaceType from './components/docspace/CreateDocumentSpaceType.svelte' import Projects from './components/project/Projects.svelte' import ProjectPresenter from './components/project/ProjectPresenter.svelte' import ProjectRefPresenter from './components/project/ProjectRefPresenter.svelte' import documents from './plugin' import { documentSectionDescriptionEditingRequested } from './stores/editors/document' import './styles/_colors.scss' import { resolveLocation } from './navigation' import { addSectionBetween, createAttachmentsSection, createCollaborativeSection, getVisibleFilters, sortDocumentStates, getAllDocumentStates, openGuidanceEditor, canChangeDocumentOwner, canDeleteDocumentCategory, canCreateChildTemplate, canCreateChildDocument, documentIdentifierProvider, getControlledDocumentTitle, getDocumentMetaLinkFragment, createChildDocument, createChildTemplate, createDocument, createTemplate } from './utils' import { comment, isCommentVisible } from './text' export { DocumentStatusTag, DocumentTitle, DocumentVersionPresenter, StatePresenter } const toObjectSearchResult = (e: WithLookup): ObjectSearchResult => ({ doc: e, title: e.title, icon: documents.icon.Document, component: DocumentItem }) async function queryDocumentMeta ( _class: Ref>, client: Client, search: string, filter?: { in?: RelatedDocument[], nin?: RelatedDocument[] } ): Promise { const q: DocumentQuery = { name: { $like: `%${search}%` } } if (filter?.in !== undefined || filter?.nin !== undefined) { q._id = {} if (filter.in !== undefined) { q._id.$in = filter.in?.map((it) => it._id as Ref) } if (filter.nin !== undefined) { q._id.$nin = filter.nin?.map((it) => it._id as Ref) } } return ( await client.findAll(_class, q, { limit: 200 }) ).map(toObjectSearchResult) } async function addSection ( section: CollaborativeDocumentSection, addAfter: boolean = true, copyFrom?: DocumentSection ): Promise { const client = getClient() const doc = await client.findOne(section.attachedToClass, { _id: section.attachedTo }) if (doc === undefined) { return } const sections = await client.findAll( documents.class.DocumentSection, { attachedTo: section.attachedTo }, { sort: { rank: !addAfter ? SortingOrder.Ascending : SortingOrder.Descending } } ) if (sections === undefined) { return } let prevSection: DocumentSection | undefined for (const s of sections) { if (s._id === section._id) { break } prevSection = s } const [first, second] = !addAfter ? [prevSection, section] : [section, prevSection] const sectionClass = client.getHierarchy().getClass(section._class) await addSectionBetween(client, doc as Document, sectionClass, first, second, copyFrom) } async function addCollaborativeSectionAbove (section: CollaborativeDocumentSection): Promise { await addSection(section, false) } async function addCollaborativeSectionBelow (section: CollaborativeDocumentSection): Promise { await addSection(section) } async function deleteCollaborativeSection (section: CollaborativeDocumentSection): Promise { showPopup( contact.component.DeleteConfirmationPopup, { object: section, deleteAction: async () => { const client = getClient() const document = await client.findOne(documents.class.Document, { _id: section.attachedTo as Ref }) await deleteObjects(client, [section], false) if (document !== undefined) { await getCollaboratorClient().updateContent(document.content, section.collaboratorSectionId, EmptyMarkup) } } }, undefined ) } async function duplicateSection (section: CollaborativeDocumentSection): Promise { await addSection(section, true, section) } async function editSectionDescription (section: CollaborativeDocumentSection): Promise { documentSectionDescriptionEditingRequested(section._id) } async function editSectionGuidance (section: CollaborativeDocumentSection): Promise { const client = getClient() const sections = await client.findAll( documents.class.DocumentSection, { attachedTo: section.attachedTo }, { sort: { rank: SortingOrder.Ascending } } ) const sectionIndex = sections.findIndex((s) => s._id === section._id) openGuidanceEditor(client, section, sectionIndex + 1, 'editing') } async function deleteDocuments (obj: Document | Document[]): Promise { const docs = Array.isArray(obj) ? obj : [obj] const docNames = docs.map((d) => `${d.title} (${d.prefix}-${d.seqNumber})`).join(', ') showPopup(MessageBox, { label: docs.length === 1 ? documents.string.MarkDocAsDeleted : documents.string.MarkDocsAsDeleted, message: documents.string.MarkDocAsDeletedConfirm, params: { titles: docNames }, action: async () => { const client = getClient() for (const doc of docs) { await client.update(doc, { state: DocumentState.Deleted }) } } }) } async function isLatestDraftDoc (obj?: Doc | Doc[]): Promise { if (obj == null) { return false } const objs = Array.isArray(obj) ? obj : [obj] const client = getClient() const h = client.getHierarchy() for (const obj of objs) { if (!h.isDerived(obj._class, documents.class.Document)) { return false } const doc = obj as Document if (doc.state !== DocumentState.Draft) { return false } const { template, seqNumber } = doc const latestDoc = await client.findOne( documents.class.Document, { template, seqNumber }, { sort: { major: SortingOrder.Descending, minor: SortingOrder.Descending, patch: SortingOrder.Descending } } ) if (latestDoc == null || latestDoc.state !== DocumentState.Draft) { return false } if (latestDoc._id !== doc._id) { return false } } return objs.length > 0 } async function editDocSpace (docSpace: DocumentSpace | undefined): Promise { if (docSpace !== undefined) { showPopup(CreateDocumentsSpace, { docSpace, clazz: docSpace?._class }) } } export default async (): Promise => ({ component: { CreateDocument, QmsDocumentWizard, QmsTemplateWizard, CreateDocumentsSpace, CreateDocumentSpaceType, DocumentSpacePresenter, DocumentPresenter, Documents, DocumentsContainer, DocumentMetaPresenter, EditDoc, EditProjectDoc, TitlePresenter, DocumentVersionPresenter, StatePresenter, OwnerPresenter, NewDocumentHeader, MyDocuments, DocumentTemplates, EditTemplateSections, CategoryPresenter, Categories, EditDocumentCategory, EditDocContent, CollaborativeSectionEditor, AttachmentsSectionEditor, DocumentSectionDeletePopup, CollaborativeSectionPresenter, AttachmentsSectionPresenter, DocumentReviewRequest, DocumentReviewRequestPresenter, DocumentApprovalRequest, DocumentApprovalRequestPresenter, StateFilterValuePresenter, ControlledStateFilterValuePresenter, AddCommentPopup, DocumentCommentsPopup, ChangeOwnerPopup, DeleteCategoryPopup, Projects, ProjectPresenter, ProjectRefPresenter, DocumentIcon }, completion: { DocumentMetaQuery: async ( client: Client, query: string, filter?: { in?: RelatedDocument[], nin?: RelatedDocument[] } ) => await queryDocumentMeta(documents.class.DocumentMeta, client, query, filter) }, function: { // CheckIsDocumentCreationDisabled: getDisablingChecker(document.functionalityItem.CreateDraftQmsDocuments), // CheckAreTemplatesDisabled: getDisablingChecker(document.functionalityItem.Templates), // CheckAreDomainsDisabled: getDisablingChecker(document.functionalityItem.Domains), // CheckIsLibraryDisabled: getDisablingChecker(document.functionalityItem.EffectiveLibrary) CanChangeDocumentOwner: canChangeDocumentOwner, CanCreateTemplate: canCreateChildTemplate, CanCreateDocument: canCreateChildDocument, CanCreateChildTemplate: canCreateChildTemplate, CanCreateChildDocument: canCreateChildDocument, CanDeleteDocumentCategory: canDeleteDocumentCategory, CollaborativeSectionCreator: createCollaborativeSection, AttachmentsSectionCreator: createAttachmentsSection, GetVisibleFilters: getVisibleFilters, DocumentStateSort: sortDocumentStates, GetAllDocumentStates: getAllDocumentStates, GetDocumentMetaLinkFragment: getDocumentMetaLinkFragment, IsLatestDraftDoc: isLatestDraftDoc, DocumentIdentifierProvider: documentIdentifierProvider, ControlledDocumentTitleProvider: getControlledDocumentTitle, Comment: comment, IsCommentVisible: isCommentVisible }, actionImpl: { AddCollaborativeSectionAbove: addCollaborativeSectionAbove, AddCollaborativeSectionBelow: addCollaborativeSectionBelow, DeleteCollaborativeSection: deleteCollaborativeSection, Duplicate: duplicateSection, EditDescription: editSectionDescription, EditGuidance: editSectionGuidance, CreateChildDocument: createChildDocument, CreateChildTemplate: createChildTemplate, CreateDocument: createDocument, CreateTemplate: createTemplate, DeleteDocument: deleteDocuments, EditDocSpace: editDocSpace }, resolver: { Location: resolveLocation } })