mirror of
https://github.com/hcengineering/platform.git
synced 2026-09-26 19:44:57 +02:00
Merge branch 'staging' into develop
Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
+50
-103
@@ -13,121 +13,68 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Ref, SortingOrder } from '@hcengineering/core'
|
||||
import { Label, Scroller } from '@hcengineering/ui'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import documents, { DocumentApprovalRequest, DocumentReviewRequest } from '@hcengineering/controlled-documents'
|
||||
import { employeeByIdStore } from '@hcengineering/contact-resources'
|
||||
import { Employee, Person, formatName } from '@hcengineering/contact'
|
||||
import { employeeByIdStore, personIdByAccountId } from '@hcengineering/contact-resources'
|
||||
import documents, {
|
||||
DocumentRequest,
|
||||
emptyBundle,
|
||||
extractValidationWorkflow
|
||||
} from '@hcengineering/controlled-documents'
|
||||
import { Ref } from '@hcengineering/core'
|
||||
import { IntlString } from '@hcengineering/platform'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { Label, Scroller } from '@hcengineering/ui'
|
||||
|
||||
import documentsRes from '../../plugin'
|
||||
import { $controlledDocument as controlledDocument } from '../../stores/editors/document/editor'
|
||||
import {
|
||||
$controlledDocument as controlledDocument,
|
||||
$documentSnapshots as documentSnapshots
|
||||
} from '../../stores/editors/document/editor'
|
||||
import { formatSignatureDate } from '../../utils'
|
||||
|
||||
interface Signer {
|
||||
id?: Ref<Person>
|
||||
role: 'author' | 'reviewer' | 'approver'
|
||||
name: string
|
||||
date: string
|
||||
let requests: DocumentRequest[] = []
|
||||
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
|
||||
$: doc = $controlledDocument
|
||||
|
||||
$: if (doc) {
|
||||
void client.findAll(documents.class.DocumentRequest, { attachedTo: doc._id }).then((r) => {
|
||||
requests = r
|
||||
})
|
||||
}
|
||||
|
||||
let signers: Signer[] = []
|
||||
$: workflow = extractValidationWorkflow(
|
||||
hierarchy,
|
||||
{
|
||||
...emptyBundle(),
|
||||
ControlledDocument: doc ? [doc] : [],
|
||||
DocumentRequest: requests,
|
||||
DocumentSnapshot: $documentSnapshots
|
||||
},
|
||||
(ref) => $personIdByAccountId.get(ref)
|
||||
)
|
||||
|
||||
let reviewRequest: DocumentReviewRequest
|
||||
let approvalRequest: DocumentApprovalRequest
|
||||
|
||||
const reviewQuery = createQuery()
|
||||
const approvalQuery = createQuery()
|
||||
|
||||
$: if ($controlledDocument !== undefined) {
|
||||
reviewQuery.query(
|
||||
documents.class.DocumentReviewRequest,
|
||||
{
|
||||
attachedTo: $controlledDocument?._id,
|
||||
attachedToClass: $controlledDocument?._class
|
||||
},
|
||||
(res) => {
|
||||
reviewRequest = res[0]
|
||||
},
|
||||
{
|
||||
sort: { createdOn: SortingOrder.Descending },
|
||||
limit: 1
|
||||
$: state = (doc ? workflow?.get(doc._id) ?? [] : [])[0]
|
||||
$: signers = (state?.approvals ?? [])
|
||||
.filter((a) => a.state === 'approved')
|
||||
.map((a) => {
|
||||
return {
|
||||
person: a.person,
|
||||
role: a.role,
|
||||
name: getNameByEmployeeId(a.person),
|
||||
date: a.timestamp ? formatSignatureDate(a.timestamp) : ''
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
approvalQuery.query(
|
||||
documents.class.DocumentApprovalRequest,
|
||||
{
|
||||
attachedTo: $controlledDocument?._id,
|
||||
attachedToClass: $controlledDocument?._class
|
||||
},
|
||||
(res) => {
|
||||
approvalRequest = res[0]
|
||||
},
|
||||
{
|
||||
sort: { createdOn: SortingOrder.Descending },
|
||||
limit: 1
|
||||
}
|
||||
)
|
||||
} else {
|
||||
reviewQuery.unsubscribe()
|
||||
approvalQuery.unsubscribe()
|
||||
}
|
||||
function getNameByEmployeeId (id: Ref<Person> | undefined): string {
|
||||
if (id === undefined) return ''
|
||||
|
||||
$: if ($controlledDocument !== null) {
|
||||
const getNameByEmployeeId = (id: Ref<Person> | undefined): string => {
|
||||
if (id === undefined) {
|
||||
return ''
|
||||
}
|
||||
const employee = $employeeByIdStore.get(id as Ref<Employee>)
|
||||
const rawName = employee?.name
|
||||
|
||||
const employee = $employeeByIdStore.get(id as Ref<Employee>)
|
||||
const rawName = employee?.name
|
||||
|
||||
return rawName !== undefined ? formatName(rawName) : ''
|
||||
}
|
||||
|
||||
const authorSignDate =
|
||||
reviewRequest !== undefined
|
||||
? reviewRequest.createdOn
|
||||
: approvalRequest !== undefined
|
||||
? approvalRequest.createdOn
|
||||
: $controlledDocument.createdOn
|
||||
|
||||
signers = [
|
||||
{
|
||||
id: $controlledDocument.author,
|
||||
role: 'author',
|
||||
name: getNameByEmployeeId($controlledDocument.author),
|
||||
date: authorSignDate !== undefined ? formatSignatureDate(authorSignDate) : ''
|
||||
}
|
||||
]
|
||||
|
||||
if (reviewRequest !== undefined) {
|
||||
reviewRequest.approved.forEach((reviewer, idx) => {
|
||||
const date = reviewRequest.approvedDates?.[idx]
|
||||
|
||||
signers.push({
|
||||
id: reviewer,
|
||||
role: 'reviewer',
|
||||
name: getNameByEmployeeId(reviewer),
|
||||
date: formatSignatureDate(date ?? reviewRequest.modifiedOn)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
if (approvalRequest !== undefined) {
|
||||
approvalRequest.approved.forEach((approver, idx) => {
|
||||
const date = approvalRequest.approvedDates?.[idx]
|
||||
|
||||
signers.push({
|
||||
id: approver,
|
||||
role: 'approver',
|
||||
name: getNameByEmployeeId(approver),
|
||||
date: formatSignatureDate(date ?? approvalRequest.modifiedOn)
|
||||
})
|
||||
})
|
||||
}
|
||||
return rawName !== undefined ? formatName(rawName) : ''
|
||||
}
|
||||
|
||||
function getSignerLabel (role: 'author' | 'reviewer' | 'approver'): IntlString {
|
||||
@@ -161,7 +108,7 @@
|
||||
{signer.name}
|
||||
</div>
|
||||
<div class="code">
|
||||
{signer.id}
|
||||
{signer.person}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+31
-90
@@ -1,93 +1,34 @@
|
||||
<script lang="ts">
|
||||
import { slide } from 'svelte/transition'
|
||||
import documents, { DocumentRequest } from '@hcengineering/controlled-documents'
|
||||
import chunter from '@hcengineering/chunter'
|
||||
import { type Person } from '@hcengineering/contact'
|
||||
import { PersonRefPresenter } from '@hcengineering/contact-resources'
|
||||
import { Ref } from '@hcengineering/core'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { DocumentValidationState } from '@hcengineering/controlled-documents'
|
||||
import { Chevron, Label, tooltip } from '@hcengineering/ui'
|
||||
import { slide } from 'svelte/transition'
|
||||
|
||||
import { $documentSnapshots as documentSnapshots } from '../../../stores/editors/document'
|
||||
import documentsRes from '../../../plugin'
|
||||
import ApprovedIcon from '../../icons/Approved.svelte'
|
||||
import RejectedIcon from '../../icons/Rejected.svelte'
|
||||
import CancelledIcon from '../../icons/Cancelled.svelte'
|
||||
import RejectedIcon from '../../icons/Rejected.svelte'
|
||||
import WaitingIcon from '../../icons/Waiting.svelte'
|
||||
import SignatureInfo from './SignatureInfo.svelte'
|
||||
|
||||
export let request: DocumentRequest
|
||||
export let state: DocumentValidationState
|
||||
export let initiallyExpanded: boolean = false
|
||||
|
||||
interface PersonalApproval {
|
||||
person?: Ref<Person>
|
||||
approved: 'approved' | 'rejected' | 'cancelled' | 'waiting'
|
||||
timestamp?: number
|
||||
}
|
||||
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
|
||||
let expanded: boolean = initiallyExpanded
|
||||
|
||||
let rejectingMessage: string | undefined
|
||||
let approvals: PersonalApproval[] = []
|
||||
|
||||
$: void getRequestData(request)
|
||||
|
||||
$: type = hierarchy.isDerived(request._class, documents.class.DocumentApprovalRequest)
|
||||
? documents.string.Approval
|
||||
: documents.string.Review
|
||||
|
||||
async function getRequestData (req: DocumentRequest): Promise<void> {
|
||||
if (req == null) {
|
||||
return
|
||||
}
|
||||
|
||||
approvals = await getApprovals(req)
|
||||
const rejectingComment = await client.findOne(chunter.class.ChatMessage, {
|
||||
attachedTo: req?._id,
|
||||
attachedToClass: req?._class
|
||||
})
|
||||
rejectingMessage = rejectingComment?.message
|
||||
}
|
||||
|
||||
async function getApprovals (req: DocumentRequest): Promise<PersonalApproval[]> {
|
||||
const rejectedBy: PersonalApproval[] =
|
||||
req.rejected !== undefined
|
||||
? [
|
||||
{
|
||||
person: req.rejected,
|
||||
approved: 'rejected',
|
||||
timestamp: req.modifiedOn
|
||||
}
|
||||
]
|
||||
: []
|
||||
const approvedBy: PersonalApproval[] = req.approved.map((id, idx) => ({
|
||||
person: id,
|
||||
approved: 'approved',
|
||||
timestamp: req.approvedDates?.[idx] ?? req.modifiedOn
|
||||
}))
|
||||
const ignoredBy = req.requested
|
||||
.filter((p) => p !== req?.rejected)
|
||||
.filter((p) => !(req?.approved as string[]).includes(p))
|
||||
.map(
|
||||
(id): PersonalApproval => ({
|
||||
person: id,
|
||||
approved: req?.rejected !== undefined ? 'cancelled' : 'waiting'
|
||||
})
|
||||
)
|
||||
return [...approvedBy, ...rejectedBy, ...ignoredBy]
|
||||
}
|
||||
|
||||
$: snapshot = $documentSnapshots
|
||||
.toReversed()
|
||||
.find((s) => s.createdOn !== undefined && request.createdOn !== undefined && s.createdOn > request.createdOn)
|
||||
|
||||
const dtf = new Intl.DateTimeFormat('default', {
|
||||
day: 'numeric',
|
||||
month: 'short'
|
||||
})
|
||||
|
||||
$: snapshot = state?.snapshot
|
||||
$: approvals = state?.approvals ?? []
|
||||
|
||||
const roleString = {
|
||||
author: documentsRes.string.Author,
|
||||
reviewer: documentsRes.string.Reviewer,
|
||||
approver: documentsRes.string.Approver
|
||||
}
|
||||
</script>
|
||||
|
||||
<button
|
||||
@@ -106,9 +47,7 @@
|
||||
{/if}
|
||||
</span>
|
||||
<span>•</span>
|
||||
<span><Label label={type} /></span>
|
||||
<span>•</span>
|
||||
<span class="date">{dtf.format(request?.modifiedOn)}</span>
|
||||
<span class="date">{dtf.format(state?.modifiedOn)}</span>
|
||||
<div class="chevron" class:visible={expanded}>
|
||||
<Chevron outline {expanded} size={'small'} />
|
||||
</div>
|
||||
@@ -116,37 +55,39 @@
|
||||
</button>
|
||||
{#if expanded}
|
||||
<div class="section" transition:slide|local>
|
||||
{#each approvals as approver}
|
||||
{#each approvals as approval}
|
||||
{@const messages = approval.messages ?? []}
|
||||
<div class="approver">
|
||||
<PersonRefPresenter value={approver.person} avatarSize="x-small" />
|
||||
{#key approver.timestamp}
|
||||
<!-- For some reason tooltip is not interactive w/o remount -->
|
||||
<PersonRefPresenter value={approval.person} avatarSize="x-small" />
|
||||
{#key approval.timestamp}
|
||||
<span
|
||||
use:tooltip={approver.timestamp !== undefined
|
||||
class="flex gap-1"
|
||||
use:tooltip={approval.timestamp !== undefined
|
||||
? {
|
||||
component: SignatureInfo,
|
||||
props: {
|
||||
id: approver.person,
|
||||
timestamp: approver.timestamp
|
||||
id: approval.person,
|
||||
timestamp: approval.timestamp
|
||||
}
|
||||
}
|
||||
: undefined}
|
||||
>
|
||||
{#if approver.approved === 'approved'}
|
||||
<span><Label label={roleString[approval.role]} /></span>
|
||||
{#if approval.state === 'approved'}
|
||||
<ApprovedIcon size="medium" fill={'var(--theme-docs-accepted-color)'} />
|
||||
{:else if approver.approved === 'rejected'}
|
||||
{:else if approval.state === 'rejected'}
|
||||
<RejectedIcon size="medium" fill={'var(--negative-button-default)'} />
|
||||
{:else if approver.approved === 'cancelled'}
|
||||
{:else if approval.state === 'cancelled'}
|
||||
<CancelledIcon size="medium" />
|
||||
{:else if approver.approved === 'waiting'}
|
||||
{:else if approval.state === 'waiting'}
|
||||
<WaitingIcon size="medium" />
|
||||
{/if}
|
||||
</span>
|
||||
{/key}
|
||||
</div>
|
||||
{#if rejectingMessage !== undefined && approver.approved === 'rejected'}
|
||||
<div class="reject-message">{rejectingMessage}</div>
|
||||
{/if}
|
||||
{#each messages as m}
|
||||
<div class="approval-status-message">{m.message}</div>
|
||||
{/each}
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
@@ -196,7 +137,7 @@
|
||||
flex-shrink: 0;
|
||||
border-bottom: 1px solid var(--theme-divider-color);
|
||||
|
||||
.reject-message {
|
||||
.approval-status-message {
|
||||
font-weight: 400;
|
||||
padding: 0.625rem 1rem 0 2rem;
|
||||
}
|
||||
|
||||
+49
-34
@@ -2,56 +2,71 @@
|
||||
import documents, {
|
||||
ControlledDocumentState,
|
||||
DocumentRequest,
|
||||
DocumentState
|
||||
DocumentState,
|
||||
emptyBundle,
|
||||
extractValidationWorkflow
|
||||
} from '@hcengineering/controlled-documents'
|
||||
import { SortingOrder } from '@hcengineering/core'
|
||||
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import { Label, Scroller } from '@hcengineering/ui'
|
||||
|
||||
import { $controlledDocument as controlledDocument } from '../../../stores/editors/document'
|
||||
import document from '../../../plugin'
|
||||
import RightPanelTabHeader from './RightPanelTabHeader.svelte'
|
||||
import DocumentApprovalItem from './DocumentApprovalItem.svelte'
|
||||
import { personIdByAccountId } from '@hcengineering/contact-resources'
|
||||
import documentsRes from '../../../plugin'
|
||||
import {
|
||||
$controlledDocument as controlledDocument,
|
||||
$documentSnapshots as documentSnapshots
|
||||
} from '../../../stores/editors/document'
|
||||
import DocumentApprovalGuideItem from './DocumentApprovalGuideItem.svelte'
|
||||
import DocumentApprovalItem from './DocumentApprovalItem.svelte'
|
||||
import RightPanelTabHeader from './RightPanelTabHeader.svelte'
|
||||
import chunter, { ChatMessage } from '@hcengineering/chunter'
|
||||
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
|
||||
let requests: DocumentRequest[] = []
|
||||
let approvals: DocumentRequest[] = []
|
||||
let messages: ChatMessage[] = []
|
||||
|
||||
$: approvals = requests.filter((p) => hierarchy.isDerived(p._class, documents.class.DocumentApprovalRequest))
|
||||
$: doc = $controlledDocument
|
||||
const requestQuery = createQuery()
|
||||
$: if (doc) {
|
||||
requestQuery.query(documents.class.DocumentRequest, { attachedTo: doc._id }, (r) => {
|
||||
requests = r
|
||||
})
|
||||
}
|
||||
|
||||
const query = createQuery()
|
||||
$: query.query(
|
||||
documents.class.DocumentRequest,
|
||||
const messageQuery = createQuery()
|
||||
$: if (doc) {
|
||||
messageQuery.query(chunter.class.ChatMessage, { attachedTo: { $in: requests.map((r) => r._id) } }, (r) => {
|
||||
messages = r
|
||||
})
|
||||
}
|
||||
|
||||
$: workflow = extractValidationWorkflow(
|
||||
hierarchy,
|
||||
{
|
||||
_class: {
|
||||
$in: [documents.class.DocumentApprovalRequest, documents.class.DocumentReviewRequest]
|
||||
},
|
||||
attachedTo: $controlledDocument?._id
|
||||
...emptyBundle(),
|
||||
ControlledDocument: doc ? [doc] : [],
|
||||
DocumentRequest: requests,
|
||||
DocumentSnapshot: $documentSnapshots,
|
||||
ChatMessage: messages
|
||||
},
|
||||
(result) => {
|
||||
requests = result
|
||||
},
|
||||
{
|
||||
sort: { createdOn: SortingOrder.Descending }
|
||||
}
|
||||
(ref) => $personIdByAccountId.get(ref)
|
||||
)
|
||||
|
||||
$: hasGuide =
|
||||
$controlledDocument?.state === DocumentState.Draft &&
|
||||
($controlledDocument?.controlledState == null ||
|
||||
![
|
||||
ControlledDocumentState.Approved,
|
||||
ControlledDocumentState.Rejected,
|
||||
ControlledDocumentState.InApproval
|
||||
].includes($controlledDocument?.controlledState))
|
||||
$: validationStates = ((doc ? workflow.get(doc._id) : []) ?? []).slice()
|
||||
|
||||
const noGuideStates: (ControlledDocumentState | undefined)[] = [
|
||||
ControlledDocumentState.Approved,
|
||||
ControlledDocumentState.Rejected,
|
||||
ControlledDocumentState.InApproval
|
||||
]
|
||||
$: hasGuide = doc && doc.state === DocumentState.Draft && !noGuideStates.includes(doc.controlledState)
|
||||
</script>
|
||||
|
||||
<RightPanelTabHeader>
|
||||
<Label label={document.string.DocumentApprovals} />
|
||||
<Label label={documentsRes.string.ValidationWorkflow} />
|
||||
</RightPanelTabHeader>
|
||||
|
||||
<Scroller>
|
||||
@@ -60,13 +75,13 @@
|
||||
<DocumentApprovalGuideItem />
|
||||
</div>
|
||||
{/if}
|
||||
{#if requests.length > 0}
|
||||
{#each requests as object, idx}
|
||||
<DocumentApprovalItem request={object} initiallyExpanded={!hasGuide && idx === 0} />
|
||||
{#if validationStates.length > 0}
|
||||
{#each validationStates as state, idx}
|
||||
<DocumentApprovalItem {state} initiallyExpanded={!hasGuide && idx === 0} />
|
||||
{/each}
|
||||
{/if}
|
||||
{#if !hasGuide && approvals.length === 0}
|
||||
<div class="no-approvals-message"><Label label={document.string.NoApprovalsDescription} /></div>
|
||||
{#if !hasGuide && requests.length === 0}
|
||||
<div class="no-approvals-message"><Label label={documentsRes.string.NoApprovalsDescription} /></div>
|
||||
{/if}
|
||||
</Scroller>
|
||||
|
||||
|
||||
+36
-146
@@ -14,183 +14,70 @@
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { type Ref, SortingOrder, notEmpty } from '@hcengineering/core'
|
||||
import { createQuery } from '@hcengineering/presentation'
|
||||
import { getCurrentEmployee } from '@hcengineering/contact'
|
||||
import documents, {
|
||||
type ControlledDocument,
|
||||
type DocumentMeta,
|
||||
import {
|
||||
DocumentBundle,
|
||||
ProjectDocumentTree,
|
||||
type HierarchyDocument,
|
||||
type Project,
|
||||
type ProjectMeta
|
||||
type Project
|
||||
} from '@hcengineering/controlled-documents'
|
||||
import { type Ref } from '@hcengineering/core'
|
||||
|
||||
import { compareDocs } from '../../../../utils'
|
||||
import { createDocumentHierarchyQuery } from '../../../../utils'
|
||||
import DocumentFlatTreeElement from './DocumentFlatTreeElement.svelte'
|
||||
|
||||
export let document: HierarchyDocument | undefined
|
||||
export let project: Ref<Project>
|
||||
|
||||
const currentPerson = getCurrentEmployee()
|
||||
let tree = new ProjectDocumentTree()
|
||||
|
||||
let meta: ProjectMeta | undefined
|
||||
const projectMetaQuery = createQuery()
|
||||
const query = createDocumentHierarchyQuery()
|
||||
$: if (document !== undefined && project !== undefined) {
|
||||
projectMetaQuery.query(
|
||||
documents.class.ProjectMeta,
|
||||
{
|
||||
project,
|
||||
meta: document.attachedTo
|
||||
},
|
||||
(result) => {
|
||||
;[meta] = result
|
||||
}
|
||||
)
|
||||
query.query(document.space, project, (data) => {
|
||||
tree = data
|
||||
})
|
||||
}
|
||||
|
||||
let parentMetas: ProjectMeta[] | undefined
|
||||
const parentMetasQuery = createQuery()
|
||||
$: if (meta !== undefined) {
|
||||
parentMetasQuery.query(
|
||||
documents.class.ProjectMeta,
|
||||
{
|
||||
meta: { $in: meta.path },
|
||||
project: meta.project
|
||||
},
|
||||
(result) => {
|
||||
parentMetas = result
|
||||
}
|
||||
)
|
||||
} else {
|
||||
parentMetasQuery.unsubscribe()
|
||||
}
|
||||
$: docmetaid = tree.metaOf(document?._id)
|
||||
|
||||
let directChildrenMetas: ProjectMeta[] | undefined
|
||||
const childrenMetasQuery = createQuery()
|
||||
$: if (meta !== undefined) {
|
||||
childrenMetasQuery.query(
|
||||
documents.class.ProjectMeta,
|
||||
{
|
||||
parent: meta?.meta,
|
||||
project: meta.project
|
||||
},
|
||||
(result) => {
|
||||
if (meta === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
directChildrenMetas = result
|
||||
}
|
||||
)
|
||||
} else {
|
||||
childrenMetasQuery.unsubscribe()
|
||||
}
|
||||
|
||||
let docs: Record<Ref<DocumentMeta>, ControlledDocument> = {}
|
||||
const docsQuery = createQuery()
|
||||
$: if (parentMetas !== undefined && directChildrenMetas !== undefined) {
|
||||
docsQuery.query(
|
||||
documents.class.ProjectDocument,
|
||||
{
|
||||
attachedTo: { $in: [...parentMetas.map((p) => p._id), ...directChildrenMetas.map((p) => p._id)] }
|
||||
},
|
||||
(result) => {
|
||||
docs = {}
|
||||
let lastTemplate: string | undefined = '###'
|
||||
let lastSeqNumber = -1
|
||||
|
||||
for (const prjdoc of result) {
|
||||
const doc = prjdoc.$lookup?.document as ControlledDocument | undefined
|
||||
if (doc === undefined) continue
|
||||
|
||||
// TODO add proper fix, when document with no template copied, saved value is null
|
||||
const template = doc.template ?? undefined
|
||||
|
||||
if (template === lastTemplate && doc.seqNumber === lastSeqNumber) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (
|
||||
doc.owner === currentPerson ||
|
||||
doc.coAuthors.findIndex((emp) => emp === currentPerson) >= 0 ||
|
||||
doc.approvers.findIndex((emp) => emp === currentPerson) >= 0 ||
|
||||
doc.reviewers.findIndex((emp) => emp === currentPerson) >= 0
|
||||
) {
|
||||
docs[doc.attachedTo] = doc
|
||||
|
||||
lastTemplate = template
|
||||
lastSeqNumber = doc.seqNumber
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
lookup: {
|
||||
document: documents.class.ControlledDocument
|
||||
},
|
||||
sort: {
|
||||
'$lookup.document.template': SortingOrder.Ascending,
|
||||
'$lookup.document.seqNumber': SortingOrder.Ascending,
|
||||
'$lookup.document.major': SortingOrder.Descending,
|
||||
'$lookup.document.minor': SortingOrder.Descending,
|
||||
'$lookup.document.patch': SortingOrder.Descending
|
||||
}
|
||||
}
|
||||
)
|
||||
} else {
|
||||
docsQuery.unsubscribe()
|
||||
}
|
||||
|
||||
let directChildrenDocs: ControlledDocument[] = []
|
||||
$: if (directChildrenMetas !== undefined) {
|
||||
directChildrenDocs = Object.values(docs).filter(
|
||||
(d) => directChildrenMetas !== undefined && directChildrenMetas.findIndex((m) => m.meta === d.attachedTo) >= 0
|
||||
)
|
||||
directChildrenDocs.sort(compareDocs)
|
||||
}
|
||||
|
||||
let parentDocs: ControlledDocument[] = []
|
||||
$: if (meta !== undefined) {
|
||||
parentDocs = [...meta.path]
|
||||
.reverse()
|
||||
.map((mId) => docs[mId])
|
||||
.filter(notEmpty)
|
||||
}
|
||||
|
||||
let levels: Array<[HierarchyDocument[], boolean]> = []
|
||||
let levels: Array<[DocumentBundle[], boolean]> = []
|
||||
$: {
|
||||
levels = []
|
||||
const parents = tree
|
||||
.parentChainOf(docmetaid)
|
||||
.reverse()
|
||||
.map((ref) => tree.bundleOf(ref))
|
||||
.filter((r) => r !== undefined)
|
||||
const me = tree.bundleOf(docmetaid)
|
||||
const children = tree
|
||||
.childrenOf(docmetaid)
|
||||
.map((ref) => tree.bundleOf(ref))
|
||||
.filter((r) => r !== undefined)
|
||||
|
||||
if (parentDocs?.length > 0) {
|
||||
levels.push([parentDocs, false])
|
||||
}
|
||||
|
||||
if (document !== undefined) {
|
||||
levels.push([[document], true])
|
||||
}
|
||||
|
||||
if (directChildrenDocs?.length > 0) {
|
||||
levels.push([directChildrenDocs, false])
|
||||
if (parents.length > 0) levels.push([parents as DocumentBundle[], false])
|
||||
if (me) {
|
||||
levels.push([[me], true])
|
||||
}
|
||||
if (children.length > 0) levels.push([children as DocumentBundle[], false])
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if levels.length > 0}
|
||||
{@const [firstDocs, firstHltd] = levels[0]}
|
||||
<div class="root">
|
||||
{#each firstDocs as doc}
|
||||
<DocumentFlatTreeElement {doc} {project} highlighted={firstHltd} />
|
||||
{#each firstDocs as bundle}
|
||||
<DocumentFlatTreeElement {bundle} {project} highlighted={firstHltd} />
|
||||
{/each}
|
||||
{#if levels.length > 1}
|
||||
{@const [secondDocs, secondHltd] = levels[1]}
|
||||
<div class="container">
|
||||
{#each secondDocs as doc}
|
||||
<DocumentFlatTreeElement {doc} {project} highlighted={secondHltd} />
|
||||
{#each secondDocs as bundle}
|
||||
<DocumentFlatTreeElement {bundle} {project} highlighted={secondHltd} />
|
||||
{/each}
|
||||
{#if levels.length > 2}
|
||||
{@const [thirdDocs, thirdHltd] = levels[2]}
|
||||
<div class="container">
|
||||
{#each thirdDocs as doc}
|
||||
<DocumentFlatTreeElement {doc} {project} highlighted={thirdHltd} />
|
||||
{#each thirdDocs as bundle}
|
||||
<DocumentFlatTreeElement {bundle} {project} highlighted={thirdHltd} />
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
@@ -214,5 +101,8 @@
|
||||
padding: 0 1rem;
|
||||
border-left: 2px solid var(--theme-navpanel-border);
|
||||
gap: 0.25rem;
|
||||
|
||||
padding-left: 0.25rem;
|
||||
margin-left: 0.75rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
+14
-5
@@ -14,17 +14,25 @@
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import documents, { getDocumentName, type Document, type Project } from '@hcengineering/controlled-documents'
|
||||
import documents, {
|
||||
DocumentBundle,
|
||||
getDocumentName,
|
||||
isFolder,
|
||||
type Project
|
||||
} from '@hcengineering/controlled-documents'
|
||||
import { type Ref } from '@hcengineering/core'
|
||||
import { Icon, navigate } from '@hcengineering/ui'
|
||||
|
||||
import { getProjectDocumentLink } from '../../../../navigation'
|
||||
|
||||
export let doc: Document
|
||||
export let bundle: DocumentBundle
|
||||
export let project: Ref<Project>
|
||||
export let highlighted: boolean = false
|
||||
|
||||
const icon = documents.icon.Document
|
||||
$: meta = bundle?.DocumentMeta[0]
|
||||
$: prjdoc = bundle?.ProjectDocument[0]
|
||||
$: document = bundle?.ControlledDocument[0]
|
||||
$: icon = isFolder(prjdoc) ? documents.icon.Folder : documents.icon.Document
|
||||
</script>
|
||||
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
@@ -32,7 +40,8 @@
|
||||
<div
|
||||
class="antiNav-element root"
|
||||
on:click={() => {
|
||||
const loc = getProjectDocumentLink(doc, project)
|
||||
if (!document) return
|
||||
const loc = getProjectDocumentLink(document, project)
|
||||
navigate(loc)
|
||||
}}
|
||||
>
|
||||
@@ -48,7 +57,7 @@
|
||||
</div>
|
||||
{/if}
|
||||
<span class="an-element__label" class:font-medium={highlighted}>
|
||||
{getDocumentName(doc)}
|
||||
{document ? getDocumentName(document) : meta.title}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user