mirror of
https://github.com/hcengineering/platform.git
synced 2026-09-10 03:37:43 +02:00
UBER-853 Move documents to Uberflow (#3700)
Signed-off-by: Alexander Onnikov <alexander.onnikov@xored.com>
This commit is contained in:
@@ -1,150 +0,0 @@
|
||||
<!--
|
||||
//
|
||||
// Copyright © 2022 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.
|
||||
//
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Employee, PersonAccount } from '@hcengineering/contact'
|
||||
import { Data, generateId, getCurrentAccount, Ref } from '@hcengineering/core'
|
||||
import { CollaboratorDocument, Document, DocumentVersionState } from '@hcengineering/document'
|
||||
import { Card, getClient } from '@hcengineering/presentation'
|
||||
import { UserBoxList } from '@hcengineering/contact-resources'
|
||||
import { Button, createFocusManager, EditBox, FocusHandler } from '@hcengineering/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import document from '../plugin'
|
||||
|
||||
export function canClose (): boolean {
|
||||
return object.name === ''
|
||||
}
|
||||
|
||||
const id = generateId()
|
||||
const currentUser = getCurrentAccount() as PersonAccount
|
||||
|
||||
const object: Data<Document> = {
|
||||
name: '',
|
||||
versions: 0,
|
||||
attachments: 0,
|
||||
labels: 0,
|
||||
comments: 0,
|
||||
version: 0,
|
||||
latest: 0.1,
|
||||
approvers: [],
|
||||
authors: [currentUser.person as Ref<Employee>],
|
||||
reviewers: [],
|
||||
requests: 0
|
||||
}
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const client = getClient()
|
||||
|
||||
async function createDocument () {
|
||||
const docId = await client.createDoc(document.class.Document, document.space.Documents, object, id)
|
||||
const contentAttachmentId: Ref<CollaboratorDocument> = generateId()
|
||||
const versionId = await client.addCollection(
|
||||
document.class.DocumentVersion,
|
||||
document.space.Documents,
|
||||
docId,
|
||||
document.class.Document,
|
||||
'versions',
|
||||
{
|
||||
content: '',
|
||||
description: '',
|
||||
impact: '',
|
||||
reason: '',
|
||||
state: DocumentVersionState.Draft,
|
||||
version: 0.1,
|
||||
attachments: 0,
|
||||
comments: 0,
|
||||
contentAttachmentId,
|
||||
initialContentId: '' as Ref<CollaboratorDocument>
|
||||
}
|
||||
)
|
||||
|
||||
await client.addCollection(
|
||||
document.class.CollaboratorDocument,
|
||||
document.space.Documents,
|
||||
versionId,
|
||||
document.class.DocumentVersion,
|
||||
'attachments',
|
||||
{
|
||||
file: contentAttachmentId,
|
||||
name: 'content',
|
||||
size: 0,
|
||||
type: 'application/ydoc',
|
||||
description: '',
|
||||
pinned: false,
|
||||
lastModified: Date.now()
|
||||
},
|
||||
contentAttachmentId
|
||||
)
|
||||
|
||||
dispatch('close', id)
|
||||
}
|
||||
|
||||
const manager = createFocusManager()
|
||||
</script>
|
||||
|
||||
<FocusHandler {manager} />
|
||||
|
||||
<Card
|
||||
label={document.string.CreateDocument}
|
||||
okAction={createDocument}
|
||||
canSave={object.name.length > 0}
|
||||
on:close={() => {
|
||||
dispatch('close')
|
||||
}}
|
||||
on:changeContent
|
||||
>
|
||||
<div class="flex-row-center clear-mins">
|
||||
<div class="mr-3">
|
||||
<Button icon={document.icon.Document} size={'medium'} kind={'link-bordered'} noFocus />
|
||||
</div>
|
||||
<EditBox
|
||||
placeholder={document.string.DocumentNamePlaceholder}
|
||||
bind:value={object.name}
|
||||
kind={'large-style'}
|
||||
autoFocus
|
||||
focusIndex={1}
|
||||
/>
|
||||
</div>
|
||||
<svelte:fragment slot="pool">
|
||||
<UserBoxList
|
||||
items={object.authors}
|
||||
label={document.string.Authors}
|
||||
emptyLabel={document.string.Authors}
|
||||
kind={'regular'}
|
||||
size={'large'}
|
||||
width={'min-content'}
|
||||
on:update={({ detail }) => (object.authors = detail)}
|
||||
/>
|
||||
<UserBoxList
|
||||
items={object.approvers}
|
||||
label={document.string.Approvers}
|
||||
emptyLabel={document.string.Approvers}
|
||||
kind={'regular'}
|
||||
size={'large'}
|
||||
width={'min-content'}
|
||||
on:update={({ detail }) => (object.approvers = detail)}
|
||||
/>
|
||||
<UserBoxList
|
||||
items={object.reviewers}
|
||||
label={document.string.Reviewers}
|
||||
emptyLabel={document.string.Reviewers}
|
||||
kind={'regular'}
|
||||
size={'large'}
|
||||
width={'min-content'}
|
||||
on:update={({ detail }) => (object.reviewers = detail)}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
</Card>
|
||||
@@ -1,51 +0,0 @@
|
||||
<!--
|
||||
//
|
||||
// Copyright © 2022 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.
|
||||
//
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { DocumentVersion } from '@hcengineering/document'
|
||||
import { getMetadata } from '@hcengineering/platform'
|
||||
|
||||
import presentation from '@hcengineering/presentation'
|
||||
import document from '../plugin'
|
||||
|
||||
import { CollaboratorEditor } from '@hcengineering/text-editor'
|
||||
import { Markup } from '@hcengineering/core'
|
||||
|
||||
export let object: DocumentVersion
|
||||
export let readonly = false
|
||||
export let initialContentId: string | undefined = undefined
|
||||
export let comparedVersion: Markup | undefined = undefined
|
||||
|
||||
const token = getMetadata(presentation.metadata.Token) ?? ''
|
||||
const collaboratorURL = getMetadata(document.metadata.CollaboratorUrl) ?? ''
|
||||
let editor: CollaboratorEditor
|
||||
export function getHTML (): string | undefined {
|
||||
return editor.getHTML()
|
||||
}
|
||||
</script>
|
||||
|
||||
{#key comparedVersion}
|
||||
<CollaboratorEditor
|
||||
documentId={object.contentAttachmentId}
|
||||
{token}
|
||||
{collaboratorURL}
|
||||
{readonly}
|
||||
on:content
|
||||
{initialContentId}
|
||||
{comparedVersion}
|
||||
bind:this={editor}
|
||||
/>
|
||||
{/key}
|
||||
@@ -1,39 +0,0 @@
|
||||
<!--
|
||||
//
|
||||
// Copyright © 2022 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.
|
||||
//
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { WithLookup } from '@hcengineering/core'
|
||||
import { Document, DocumentVersion } from '@hcengineering/document'
|
||||
import { Icon } from '@hcengineering/ui'
|
||||
import document from '../plugin'
|
||||
|
||||
export let value: WithLookup<Document>
|
||||
|
||||
const versions = (value.$lookup?.versions ?? []) as DocumentVersion[]
|
||||
const lastVersion = versions.length > 0 ? versions[versions.length - 1] : undefined
|
||||
</script>
|
||||
|
||||
<div class="flex-row-center">
|
||||
<div class="flex-center p-1 content-dark-color flex-no-shrink mr-2-5">
|
||||
<Icon icon={document.icon.Document} size={'medium'} />
|
||||
</div>
|
||||
<span class="overflow-label">
|
||||
{value.name}
|
||||
{#if lastVersion}
|
||||
- {lastVersion?.version}
|
||||
{/if}
|
||||
</span>
|
||||
</div>
|
||||
@@ -1,49 +0,0 @@
|
||||
<!--
|
||||
//
|
||||
// Copyright © 2022 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.
|
||||
//
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { WithLookup } from '@hcengineering/core'
|
||||
import { Document } from '@hcengineering/document'
|
||||
import { tooltip, Icon } from '@hcengineering/ui'
|
||||
import { DocNavLink } from '@hcengineering/view-resources'
|
||||
import document from '../plugin'
|
||||
|
||||
export let value: WithLookup<Document>
|
||||
export let inline: boolean = false
|
||||
export let disabled: boolean = false
|
||||
export let accent: boolean = false
|
||||
export let noUnderline: boolean = false
|
||||
export let maxWidth = ''
|
||||
</script>
|
||||
|
||||
{#if value}
|
||||
<DocNavLink {disabled} object={value} {inline} {accent} {noUnderline} component={document.component.EditDoc}>
|
||||
{#if inline}
|
||||
<span class="antiMention" use:tooltip={{ label: document.string.Document }}>
|
||||
@{value.name}-{value.version}
|
||||
</span>
|
||||
{:else}
|
||||
<div class="flex-presenter" style:max-width={maxWidth} use:tooltip={{ label: document.string.Document }}>
|
||||
<div class="icon">
|
||||
<Icon icon={document.icon.Document} size={'small'} />
|
||||
</div>
|
||||
<span class="label nowrap" class:no-underline={noUnderline || disabled} class:fs-bold={accent}>
|
||||
{value.name}-{value.version}
|
||||
</span>
|
||||
</div>
|
||||
{/if}
|
||||
</DocNavLink>
|
||||
{/if}
|
||||
@@ -1,53 +0,0 @@
|
||||
<!--
|
||||
//
|
||||
// Copyright © 2022 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.
|
||||
//
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Ref, WithLookup } from '@hcengineering/core'
|
||||
import { Document, DocumentVersion } from '@hcengineering/document'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { getPanelURI, Icon } from '@hcengineering/ui'
|
||||
import document from '../plugin'
|
||||
|
||||
export let value: WithLookup<DocumentVersion>
|
||||
export let inline = false
|
||||
|
||||
$: doc = value.$lookup?.attachedTo as Document
|
||||
|
||||
$: if (doc === undefined) {
|
||||
getClient()
|
||||
.findOne(document.class.Document, { _id: value.attachedTo as Ref<Document> })
|
||||
.then((res) => {
|
||||
doc = res as Document
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if value}
|
||||
<a
|
||||
class="flex-presenter"
|
||||
href="#{getPanelURI(document.component.EditDoc, value.attachedTo, value.attachedToClass, 'content')}"
|
||||
class:inline-presenter={inline}
|
||||
>
|
||||
{#if !inline}
|
||||
<div class="icon">
|
||||
<Icon icon={document.icon.Document} size={'small'} />
|
||||
</div>
|
||||
{/if}
|
||||
<span class="label">
|
||||
{doc?.name} - {value.version}
|
||||
</span>
|
||||
</a>
|
||||
{/if}
|
||||
@@ -1,87 +0,0 @@
|
||||
<!--
|
||||
//
|
||||
// Copyright © 2022 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.
|
||||
//
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Doc, DocumentQuery } from '@hcengineering/core'
|
||||
import { Document } from '@hcengineering/document'
|
||||
import { ActionContext } from '@hcengineering/presentation'
|
||||
import { Label, Loading, SearchEdit } from '@hcengineering/ui'
|
||||
import view, { Viewlet, ViewletPreference } from '@hcengineering/view'
|
||||
import { FilterButton, TableBrowser, ViewletSelector, ViewletSettingButton } from '@hcengineering/view-resources'
|
||||
import document from '../plugin'
|
||||
|
||||
export let query: DocumentQuery<Document> = {}
|
||||
|
||||
let search = ''
|
||||
let resultQuery: DocumentQuery<Doc> = query
|
||||
|
||||
function updateResultQuery (search: string, query: DocumentQuery<Document>): void {
|
||||
resultQuery = search === '' ? { ...query } : { ...query, $search: search }
|
||||
}
|
||||
|
||||
let viewlet: Viewlet | undefined
|
||||
let loading = true
|
||||
|
||||
let preference: ViewletPreference | undefined
|
||||
</script>
|
||||
|
||||
<ActionContext
|
||||
context={{
|
||||
mode: 'browser'
|
||||
}}
|
||||
/>
|
||||
<div class="antiPanel-component">
|
||||
<div class="ac-header full divide caption-height">
|
||||
<div class="ac-header__wrap-title mr-3">
|
||||
<span class="ac-header__title"><Label label={document.string.Documents} /></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ac-header full divide search-start">
|
||||
<div class="ac-header-full small-gap">
|
||||
<SearchEdit bind:value={search} on:change={() => updateResultQuery(search, query)} />
|
||||
<!-- <ActionIcon icon={IconMoreH} size={'small'} /> -->
|
||||
<div class="buttons-divider" />
|
||||
<FilterButton _class={document.class.Document} />
|
||||
</div>
|
||||
<div class="ac-header-full medium-gap">
|
||||
<ViewletSelector
|
||||
hidden
|
||||
bind:viewlet
|
||||
bind:preference
|
||||
bind:loading
|
||||
viewletQuery={{
|
||||
attachTo: document.class.Document,
|
||||
descriptor: view.viewlet.Table
|
||||
}}
|
||||
/>
|
||||
<ViewletSettingButton bind:viewlet />
|
||||
<!-- <ActionIcon icon={IconMoreH} size={'small'} /> -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if loading}
|
||||
<Loading />
|
||||
{:else if viewlet}
|
||||
<TableBrowser
|
||||
_class={document.class.Document}
|
||||
config={preference?.config ?? viewlet.config}
|
||||
options={viewlet.options}
|
||||
query={resultQuery}
|
||||
totalQuery={query}
|
||||
showNotification
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -1,580 +0,0 @@
|
||||
<!--
|
||||
//
|
||||
// Copyright © 2022 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.
|
||||
//
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Attachments } from '@hcengineering/attachment-resources'
|
||||
import { Employee, PersonAccount } from '@hcengineering/contact'
|
||||
import core, { Class, Doc, generateId, getCurrentAccount, Ref, WithLookup } from '@hcengineering/core'
|
||||
import {
|
||||
CollaboratorDocument,
|
||||
Document,
|
||||
DocumentRequest,
|
||||
DocumentRequestKind,
|
||||
DocumentVersion,
|
||||
DocumentVersionState
|
||||
} from '@hcengineering/document'
|
||||
import notification from '@hcengineering/notification'
|
||||
import { Panel } from '@hcengineering/panel'
|
||||
import { getResource, translate } from '@hcengineering/platform'
|
||||
import { createQuery, getClient } from '@hcengineering/presentation'
|
||||
import tags from '@hcengineering/tags'
|
||||
import { CollaborationDiffViewer } from '@hcengineering/text-editor'
|
||||
import view from '@hcengineering/view'
|
||||
import { themeStore } from '@hcengineering/ui'
|
||||
|
||||
import {
|
||||
Button,
|
||||
Component,
|
||||
EditBox,
|
||||
eventToHTMLElement,
|
||||
IconCheck,
|
||||
IconClose,
|
||||
IconEdit,
|
||||
IconMoreH,
|
||||
IconShare,
|
||||
Label,
|
||||
SelectPopup,
|
||||
showPopup
|
||||
} from '@hcengineering/ui'
|
||||
import { ClassAttributeBar, ContextMenu, ParentsNavigator, UpDownNavigator } from '@hcengineering/view-resources'
|
||||
import { createEventDispatcher, onDestroy, onMount } from 'svelte'
|
||||
import document from '../plugin'
|
||||
import DocumentEditor from './DocumentEditor.svelte'
|
||||
|
||||
// import ControlPanel from './ControlPanel.svelte'
|
||||
// import CopyToClipboard from './CopyToClipboard.svelte'
|
||||
|
||||
export let _id: Ref<Document>
|
||||
export let _class: Ref<Class<Document>>
|
||||
export let embedded = false
|
||||
|
||||
let lastId: Ref<Doc> = _id
|
||||
|
||||
const query = createQuery()
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const client = getClient()
|
||||
|
||||
let documentObject: WithLookup<Document> | undefined
|
||||
|
||||
let name = ''
|
||||
|
||||
let innerWidth: number
|
||||
|
||||
const notificationClient = getResource(notification.function.GetNotificationClient).then((res) => res())
|
||||
|
||||
$: read(_id)
|
||||
function read (_id: Ref<Doc>) {
|
||||
if (lastId !== _id) {
|
||||
const prev = lastId
|
||||
lastId = _id
|
||||
notificationClient.then((client) => client.read(prev))
|
||||
}
|
||||
}
|
||||
|
||||
const currentUser = getCurrentAccount() as PersonAccount
|
||||
|
||||
onDestroy(async () => {
|
||||
notificationClient.then((client) => client.read(_id))
|
||||
})
|
||||
|
||||
let requests: DocumentRequest[] = []
|
||||
|
||||
$: myRequests = requests.filter((it) => it.assignee === currentUser.person)
|
||||
|
||||
$: approveRequest = myRequests.find((it) => it.kind === DocumentRequestKind.Approve)
|
||||
$: allApproveRequest = requests.find((it) => it.kind === DocumentRequestKind.Approve)
|
||||
// $: changesRequest = myRequests.find((it) => it.kind === DocumentRequestKind.Changes)
|
||||
// $: reviewRequest = myRequests.find((it) => it.kind === DocumentRequestKind.Review)
|
||||
|
||||
$: _id &&
|
||||
_class &&
|
||||
query.query(
|
||||
_class,
|
||||
{ _id },
|
||||
async (result) => {
|
||||
;[documentObject] = result
|
||||
name = documentObject?.name ?? ''
|
||||
|
||||
requests = (documentObject.$lookup?.requests as DocumentRequest[]) ?? []
|
||||
},
|
||||
{
|
||||
lookup: {
|
||||
_id: {
|
||||
requests: document.class.DocumentRequest
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
$: canSave = name.trim().length > 0
|
||||
|
||||
async function saveTitle (ev: Event) {
|
||||
ev.preventDefault()
|
||||
|
||||
if (!documentObject || !canSave) {
|
||||
return
|
||||
}
|
||||
|
||||
const nameTrimmed = name.trim()
|
||||
|
||||
if (nameTrimmed.length > 0 && nameTrimmed !== documentObject.name) {
|
||||
await client.update(documentObject, { name: nameTrimmed })
|
||||
}
|
||||
}
|
||||
|
||||
function showMenu (ev?: Event): void {
|
||||
if (documentObject) {
|
||||
showPopup(
|
||||
ContextMenu,
|
||||
{ object: documentObject, excludedActions: [view.action.Open] },
|
||||
(ev as MouseEvent).target as HTMLElement
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
dispatch('open', { ignoreKeys: ['comments', 'name', 'reviewers'] })
|
||||
})
|
||||
|
||||
const versionQuery = createQuery()
|
||||
let versions: DocumentVersion[] = []
|
||||
|
||||
$: versionQuery.query(
|
||||
document.class.DocumentVersion,
|
||||
{ attachedTo: _id },
|
||||
(res) => {
|
||||
versions = res
|
||||
if (autoSelect) {
|
||||
version = versions[versions.length - 1]
|
||||
}
|
||||
},
|
||||
{ sort: { version: 1 } }
|
||||
)
|
||||
let version: DocumentVersion | undefined
|
||||
let compareTo: DocumentVersion | undefined
|
||||
|
||||
let info: any
|
||||
|
||||
let labels: Record<DocumentVersionState, string> = {
|
||||
[DocumentVersionState.Draft]: '',
|
||||
[DocumentVersionState.Approved]: '',
|
||||
[DocumentVersionState.Rejected]: ''
|
||||
}
|
||||
|
||||
async function updateLabels (lang: string): Promise<void> {
|
||||
labels = {
|
||||
[DocumentVersionState.Draft]: await translate(document.string.Draft, {}, lang),
|
||||
[DocumentVersionState.Approved]: await translate(document.string.Approved, {}, lang),
|
||||
[DocumentVersionState.Rejected]: await translate(document.string.Rejected, {}, lang)
|
||||
}
|
||||
}
|
||||
updateLabels($themeStore.language)
|
||||
|
||||
$: {
|
||||
const ifo: any = [
|
||||
...versions.map((it) => ({
|
||||
id: it._id as string,
|
||||
text: `${it.version} - ${labels[it.state]} - ${new Date(it.modifiedOn).toDateString()} `
|
||||
}))
|
||||
]
|
||||
info = ifo
|
||||
}
|
||||
function selectVersion (event: MouseEvent): void {
|
||||
showPopup(
|
||||
SelectPopup,
|
||||
{
|
||||
value: info,
|
||||
placeholder: document.string.Version,
|
||||
searchable: true
|
||||
},
|
||||
eventToHTMLElement(event),
|
||||
(res) => {
|
||||
if (res != null) {
|
||||
version = versions.find((it) => it._id === res)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
function selectCompareToVersion (event: MouseEvent): void {
|
||||
showPopup(
|
||||
SelectPopup,
|
||||
{
|
||||
value: [{ id: null, text: '-' }, ...info.slice(0, info.length - 1)],
|
||||
placeholder: document.string.Version,
|
||||
searchable: true
|
||||
},
|
||||
eventToHTMLElement(event),
|
||||
(res) => {
|
||||
if (res != null) {
|
||||
compareTo = versions.find((it) => it._id === res)
|
||||
} else if (res === null) {
|
||||
compareTo = undefined
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
$: readonly = !documentObject?.authors.includes(currentUser.person as Ref<Employee>)
|
||||
|
||||
let autoSelect = true
|
||||
|
||||
async function doEdit (documentObject: Document): Promise<void> {
|
||||
processing = true
|
||||
// Looking for a draft version
|
||||
const draft = versions.find((it) => it.state === DocumentVersionState.Draft)
|
||||
const lastVersion = versions[versions.length - 1]
|
||||
if (draft === undefined) {
|
||||
// We need to create draft document.
|
||||
const newVersion = Math.round(documentObject.latest * 1000 + 100) / 1000
|
||||
|
||||
const versionId: Ref<DocumentVersion> = generateId()
|
||||
const contentAttachmentId: Ref<CollaboratorDocument> = generateId()
|
||||
|
||||
const ops = client
|
||||
.apply(documentObject._id)
|
||||
.match(document.class.Document, { _id: documentObject._id, latest: documentObject.latest })
|
||||
|
||||
ops.update(documentObject, { latest: newVersion })
|
||||
|
||||
ops.addCollection(
|
||||
document.class.DocumentVersion,
|
||||
documentObject.space,
|
||||
documentObject._id,
|
||||
documentObject._class,
|
||||
'versions',
|
||||
{
|
||||
content: lastVersion.content,
|
||||
description: '',
|
||||
impact: '',
|
||||
reason: '',
|
||||
state: DocumentVersionState.Draft,
|
||||
version: newVersion,
|
||||
attachments: 0,
|
||||
comments: 0,
|
||||
initialContentId: lastVersion.contentAttachmentId,
|
||||
contentAttachmentId
|
||||
},
|
||||
versionId
|
||||
)
|
||||
ops.addCollection(
|
||||
document.class.CollaboratorDocument,
|
||||
documentObject.space,
|
||||
versionId,
|
||||
document.class.DocumentVersion,
|
||||
'attachments',
|
||||
{
|
||||
file: contentAttachmentId,
|
||||
name: 'content',
|
||||
size: 0,
|
||||
type: 'application/ydoc',
|
||||
description: '',
|
||||
pinned: false,
|
||||
lastModified: Date.now()
|
||||
},
|
||||
contentAttachmentId
|
||||
)
|
||||
if (await ops.commit()) {
|
||||
// We should have a draft created by someone else.
|
||||
version = undefined
|
||||
autoSelect = true
|
||||
}
|
||||
}
|
||||
processing = false
|
||||
}
|
||||
|
||||
let processing = false
|
||||
|
||||
const updateRequests = async (kind: DocumentRequestKind): Promise<void> => {
|
||||
processing = true
|
||||
if (documentObject === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
const requests = await client.findAll(document.class.DocumentRequest, {
|
||||
attachedTo: documentObject?._id,
|
||||
kind
|
||||
})
|
||||
for (const a of documentObject?.approvers ?? []) {
|
||||
const ex = requests.find((it) => it.assignee === a)
|
||||
if (ex === undefined) {
|
||||
await client.addCollection(
|
||||
document.class.DocumentRequest,
|
||||
documentObject.space,
|
||||
documentObject._id,
|
||||
documentObject._class,
|
||||
'requests',
|
||||
{
|
||||
assignee: a,
|
||||
kind
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (version) {
|
||||
await client.update(version, {
|
||||
content: editor.getHTML()
|
||||
})
|
||||
}
|
||||
|
||||
processing = false
|
||||
}
|
||||
let editor: DocumentEditor
|
||||
const updateState = async (state: DocumentVersionState): Promise<void> => {
|
||||
processing = true
|
||||
if (documentObject === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
const draft = versions.find((it) => it.state === DocumentVersionState.Draft)
|
||||
if (draft !== undefined) {
|
||||
// We need to create draft document.
|
||||
const newVersion = documentObject.version + 1
|
||||
|
||||
const ops = client
|
||||
.apply(documentObject._id)
|
||||
.match(document.class.Document, { _id: documentObject._id, latest: documentObject.latest })
|
||||
|
||||
if (state === DocumentVersionState.Approved) {
|
||||
ops.update(documentObject, { latest: newVersion, version: newVersion })
|
||||
ops.update(draft, { version: newVersion, state })
|
||||
} else {
|
||||
ops.update(draft, { state })
|
||||
}
|
||||
// Remove all requests
|
||||
|
||||
if (await ops.commit()) {
|
||||
const docs = await client.findAll(document.class.DocumentRequest, { attachedTo: documentObject._id })
|
||||
for (const d of docs) {
|
||||
client.remove(d)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
processing = false
|
||||
}
|
||||
async function switchToDraft (): Promise<void> {
|
||||
const requests = await client.findAll(document.class.DocumentRequest, { attachedTo: documentObject?._id })
|
||||
for (const r of requests) {
|
||||
client.remove(r)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if documentObject !== undefined}
|
||||
<Panel
|
||||
object={documentObject}
|
||||
isHeader
|
||||
isAside={true}
|
||||
isSub={false}
|
||||
{embedded}
|
||||
bind:innerWidth
|
||||
floatAside={false}
|
||||
useMaxWidth={true}
|
||||
on:open
|
||||
on:close={() => dispatch('close')}
|
||||
>
|
||||
<svelte:fragment slot="navigator">
|
||||
<UpDownNavigator element={documentObject} />
|
||||
<ParentsNavigator element={documentObject} />
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="header">
|
||||
<span class="fs-title flex-row-center flex-shrink gap-1-5">
|
||||
<EditBox
|
||||
bind:value={name}
|
||||
placeholder={document.string.DocumentNamePlaceholder}
|
||||
kind="large-style"
|
||||
on:blur={(evt) => saveTitle(evt)}
|
||||
/>
|
||||
|
||||
<div class="p-1">-</div>
|
||||
<Button loading={processing} kind={'link-bordered'} on:click={selectVersion} disabled={info.length < 2}>
|
||||
<svelte:fragment slot="content">
|
||||
{#if version}
|
||||
{version.version} - {labels[version.state]}
|
||||
{:else}
|
||||
<Label label={document.string.Draft} />
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
loading={processing}
|
||||
kind={'link-bordered'}
|
||||
on:click={selectCompareToVersion}
|
||||
disabled={info.length < 2}
|
||||
>
|
||||
<svelte:fragment slot="content">
|
||||
{#if compareTo}
|
||||
{compareTo.version} - {labels[compareTo.state]}
|
||||
{:else}
|
||||
<Label label={document.string.CompareTo} />
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
</Button>
|
||||
</span>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="tools">
|
||||
{#if version && version?.state !== DocumentVersionState.Draft}
|
||||
<Button
|
||||
loading={processing}
|
||||
kind={'link-bordered'}
|
||||
label={document.string.CreateDraft}
|
||||
on:click={() => {
|
||||
if (documentObject) {
|
||||
doEdit(documentObject)
|
||||
}
|
||||
}}
|
||||
icon={IconEdit}
|
||||
size={'medium'}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
{#if !readonly && version && version?.state === DocumentVersionState.Draft && version.version === documentObject.latest && !allApproveRequest}
|
||||
<Button
|
||||
loading={processing}
|
||||
kind={'link-bordered'}
|
||||
label={document.string.SendForApproval}
|
||||
on:click={() => updateRequests(DocumentRequestKind.Approve)}
|
||||
icon={IconShare}
|
||||
size={'medium'}
|
||||
disabled={documentObject?.approvers?.length === 0}
|
||||
/>
|
||||
<!-- <Button
|
||||
loading={processing}
|
||||
kind={'link-bordered'}
|
||||
label={document.string.SendForReview}
|
||||
on:click={() => updateRequests(DocumentRequestKind.Review)}
|
||||
icon={IconShare}
|
||||
size={'medium'}
|
||||
disabled={documentObject?.reviewers?.length === 0}
|
||||
/> -->
|
||||
{/if}
|
||||
{#if version?.state === DocumentVersionState.Draft && approveRequest}
|
||||
<Button
|
||||
loading={processing}
|
||||
kind={'link-bordered'}
|
||||
label={document.string.Approve}
|
||||
on:click={() => updateState(DocumentVersionState.Approved)}
|
||||
icon={IconCheck}
|
||||
size={'medium'}
|
||||
/>
|
||||
<Button
|
||||
loading={processing}
|
||||
kind={'link-bordered'}
|
||||
label={document.string.Reject}
|
||||
on:click={() => updateState(DocumentVersionState.Rejected)}
|
||||
icon={IconClose}
|
||||
size={'medium'}
|
||||
/>
|
||||
{#if !readonly}
|
||||
<Button
|
||||
loading={processing}
|
||||
kind={'link-bordered'}
|
||||
label={document.string.Draft}
|
||||
on:click={() => switchToDraft()}
|
||||
icon={IconEdit}
|
||||
size={'medium'}
|
||||
/>
|
||||
{/if}
|
||||
{/if}
|
||||
<Button icon={IconMoreH} kind={'ghost'} size={'medium'} on:click={showMenu} />
|
||||
</svelte:fragment>
|
||||
|
||||
<div class="description-preview select-text mt-2 emphasized">
|
||||
{#if version && version.state === DocumentVersionState.Draft && approveRequest === undefined}
|
||||
{#key version?._id}
|
||||
<!-- suggestMode={mode === 'suggest'} -->
|
||||
<DocumentEditor
|
||||
object={version}
|
||||
initialContentId={version.initialContentId}
|
||||
comparedVersion={compareTo?.content ?? versions[versions.length - 2]?.content}
|
||||
readonly={false}
|
||||
bind:this={editor}
|
||||
/>
|
||||
{/key}
|
||||
{:else if version}
|
||||
{#key [compareTo?.content, version.content]}
|
||||
<CollaborationDiffViewer content={version.content} comparedVersion={compareTo?.content} />
|
||||
{/key}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="p-1 mt-6">
|
||||
<Attachments
|
||||
objectId={documentObject._id}
|
||||
space={documentObject.space}
|
||||
_class={documentObject._class}
|
||||
attachments={documentObject.attachments ?? 0}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<svelte:fragment slot="custom-attributes">
|
||||
<ClassAttributeBar
|
||||
object={documentObject}
|
||||
_class={documentObject._class}
|
||||
to={core.class.Doc}
|
||||
ignoreKeys={['name', 'reviewers']}
|
||||
{readonly}
|
||||
/>
|
||||
|
||||
<div class="doc-divider" />
|
||||
<div class="popupPanel-body__aside-grid">
|
||||
<span class="labelTop">
|
||||
<Label label={document.string.Labels} />
|
||||
</span>
|
||||
<div class="flex">
|
||||
<Component
|
||||
is={tags.component.TagsAttributeEditor}
|
||||
props={{ object: documentObject, label: document.string.AddLabel }}
|
||||
/>
|
||||
</div>
|
||||
<div class="divider" />
|
||||
<span class="labelOnPanel">
|
||||
<Label label={document.string.LastRevision} />
|
||||
</span>
|
||||
<span>{documentObject?.latest}</span>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
</Panel>
|
||||
{/if}
|
||||
|
||||
<style lang="scss">
|
||||
.description-preview {
|
||||
color: var(--content-color);
|
||||
line-height: 150%;
|
||||
// overflow: auto;
|
||||
}
|
||||
.doc-divider {
|
||||
flex-shrink: 0;
|
||||
margin: 0 0 0.5rem;
|
||||
height: 1px;
|
||||
background-color: var(--theme-divider-color);
|
||||
}
|
||||
.emphasized {
|
||||
padding: 1rem;
|
||||
background-color: var(--theme-bg-color);
|
||||
border: 1px solid var(--divider-color);
|
||||
border-radius: 0.5rem;
|
||||
transition: border-color 0.1s var(--timing-main), box-shadow 0.1s var(--timing-main);
|
||||
|
||||
&:focus-within {
|
||||
box-shadow: 0 0 0 3px var(--accented-button-outline);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,9 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { Employee, PersonAccount } from '@hcengineering/contact'
|
||||
import { Ref, getCurrentAccount } from '@hcengineering/core'
|
||||
import Documents from './Documents.svelte'
|
||||
|
||||
const currentUser = (getCurrentAccount() as PersonAccount).person as Ref<Employee>
|
||||
</script>
|
||||
|
||||
<Documents query={{ authors: currentUser }} />
|
||||
@@ -1,35 +0,0 @@
|
||||
<!--
|
||||
// Copyright © 2022 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.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Button, showPopup, IconAdd } from '@hcengineering/ui'
|
||||
import document from '../plugin'
|
||||
import CreateDocument from './CreateDocument.svelte'
|
||||
|
||||
async function newDocument (): Promise<void> {
|
||||
showPopup(CreateDocument, {}, 'top')
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="antiNav-subheader">
|
||||
<Button
|
||||
icon={IconAdd}
|
||||
label={document.string.CreateDocument}
|
||||
justify={'left'}
|
||||
width={'100%'}
|
||||
kind={'accented'}
|
||||
size={'large'}
|
||||
on:click={newDocument}
|
||||
/>
|
||||
</div>
|
||||
@@ -1,64 +0,0 @@
|
||||
<!--
|
||||
//
|
||||
// Copyright © 2022 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.
|
||||
//
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { WithLookup } from '@hcengineering/core'
|
||||
import { DocumentVersion, DocumentVersionState } from '@hcengineering/document'
|
||||
import { EastSideColor, FernColor, Label, SeaBuckthornColor } from '@hcengineering/ui'
|
||||
import document from '../plugin'
|
||||
|
||||
export let value: WithLookup<DocumentVersion>
|
||||
</script>
|
||||
|
||||
{#if value}
|
||||
<div class="status-container">
|
||||
{#if value.state === DocumentVersionState.Draft}
|
||||
<div class="status p-1" style:background-color={EastSideColor}>
|
||||
<Label label={document.string.Draft} />
|
||||
</div>
|
||||
{/if}
|
||||
{#if value.state === DocumentVersionState.Approved}
|
||||
<div class="status p-1" style:background-color={SeaBuckthornColor}>
|
||||
<Label label={document.string.Approved} />
|
||||
</div>
|
||||
{/if}
|
||||
{#if value.state === DocumentVersionState.Rejected}
|
||||
<div class="status p-1" style:background-color={FernColor}>
|
||||
<Label label={document.string.Rejected} />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style lang="scss">
|
||||
.status-container {
|
||||
display: flex;
|
||||
.status {
|
||||
border-radius: 8px;
|
||||
margin: 0.1rem;
|
||||
|
||||
font-weight: 500;
|
||||
font-size: 10px;
|
||||
|
||||
text-transform: uppercase;
|
||||
color: #ffffff;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,40 +0,0 @@
|
||||
<!--
|
||||
//
|
||||
// Copyright © 2022 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.
|
||||
//
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { WithLookup } from '@hcengineering/core'
|
||||
import { Document, DocumentVersion } from '@hcengineering/document'
|
||||
|
||||
export let value: WithLookup<Document>
|
||||
|
||||
let lastVersion: DocumentVersion | undefined
|
||||
|
||||
$: if (value.$lookup?.versions !== undefined) {
|
||||
let vv = -1
|
||||
let dv: DocumentVersion | undefined
|
||||
for (const v of value.$lookup.versions as DocumentVersion[]) {
|
||||
if (v.version > vv) {
|
||||
dv = v
|
||||
vv = v.version
|
||||
}
|
||||
}
|
||||
lastVersion = dv
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if lastVersion}
|
||||
{lastVersion.version}
|
||||
{/if}
|
||||
@@ -1,25 +0,0 @@
|
||||
<!--
|
||||
// Copyright © 2020, 2021 Anticrm Platform Contributors.
|
||||
// Copyright © 2021 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.
|
||||
-->
|
||||
<script lang="ts">
|
||||
export let size: 'small' | 'medium' | 'large'
|
||||
const fill: string = 'var(--caption-color)'
|
||||
</script>
|
||||
|
||||
<svg class="svg-{size}" {fill} viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M13.3,6.5l1.9-1.9c1.1-1.1,1.1-2.8,0-3.8c-1.1-1.1-2.8-1.1-3.8,0L9.5,2.7C10.4,4.3,11.7,5.6,13.3,6.5z M12.3,7.6c-1.6-1-2.9-2.3-3.8-3.8l-6.3,6.3l0,0C1.5,10.7,1.2,11,1,11.4c-0.2,0.4-0.3,0.8-0.5,1.6l-0.4,2c-0.1,0.5-0.1,0.7,0,0.9 c0.1,0.1,0.4,0.1,0.9,0l2-0.4c0.8-0.2,1.3-0.3,1.6-0.5c0.4-0.2,0.7-0.5,1.3-1.1L12.3,7.6z"
|
||||
/>
|
||||
</svg>
|
||||
Reference in New Issue
Block a user