UBER-853 Move documents to Uberflow (#3700)

Signed-off-by: Alexander Onnikov <alexander.onnikov@xored.com>
This commit is contained in:
Alexander Onnikov
2023-09-15 15:31:24 +07:00
committed by GitHub
parent b61b47a4c3
commit 2d16709ccb
78 changed files with 10 additions and 3356 deletions
@@ -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>