mirror of
https://github.com/hcengineering/platform.git
synced 2026-09-09 19:27:44 +02:00
Display original data for tables (#10413)
Signed-off-by: Artem Savchenko <armisav@gmail.com>
This commit is contained in:
+93
@@ -0,0 +1,93 @@
|
||||
<!--
|
||||
//
|
||||
// Copyright © 2025 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 type { Class, Doc, DocumentQuery, Ref } from '@hcengineering/core'
|
||||
import presentation, { Card } from '@hcengineering/presentation'
|
||||
import textEditor from '@hcengineering/text-editor'
|
||||
import { Component, Loading } from '@hcengineering/ui'
|
||||
import type { BuildModelKey, Viewlet } from '@hcengineering/view'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
|
||||
export let _class: Ref<Class<Doc>>
|
||||
export let config: Array<string | BuildModelKey> | undefined = undefined
|
||||
export let documentIds: string[]
|
||||
export let query: DocumentQuery<Doc> | undefined = undefined
|
||||
export let viewlet: Viewlet | undefined = undefined
|
||||
export let viewletWithLookup: any | undefined = undefined
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
// Build query from documentIds if available (preserves order), otherwise use provided query
|
||||
let tableQuery: DocumentQuery<Doc>
|
||||
$: tableQuery = documentIds.length > 0 ? { _id: { $in: documentIds as any[] } } : (query ?? {})
|
||||
|
||||
// Use config from viewlet if available, otherwise use metadata config
|
||||
// RelationshipTable doesn't use viewlets, so config comes directly from metadata
|
||||
// Ensure config is never empty - use default empty string key for default presenter
|
||||
$: viewletConfig = viewlet?.config
|
||||
$: tableConfig =
|
||||
viewletConfig !== undefined && viewletConfig.length > 0
|
||||
? viewletConfig
|
||||
: config !== undefined && config.length > 0
|
||||
? config
|
||||
: ['']
|
||||
|
||||
// Get component from viewlet descriptor lookup
|
||||
$: viewletComponent = viewletWithLookup?.$lookup?.descriptor?.component
|
||||
|
||||
function handleClose (): void {
|
||||
dispatch('close')
|
||||
}
|
||||
</script>
|
||||
|
||||
<Card
|
||||
label={textEditor.string.SeeOriginalData}
|
||||
fullSize={true}
|
||||
canSave={true}
|
||||
okLabel={presentation.string.Ok}
|
||||
okAction={handleClose}
|
||||
onCancel={handleClose}
|
||||
on:close={handleClose}
|
||||
>
|
||||
<div class="original-table-container">
|
||||
{#if viewletComponent}
|
||||
<Component
|
||||
is={viewletComponent}
|
||||
props={{
|
||||
_class,
|
||||
config: tableConfig,
|
||||
query: tableQuery,
|
||||
totalQuery: tableQuery,
|
||||
options: viewlet?.options,
|
||||
readonly: true,
|
||||
viewlet,
|
||||
viewOptions: viewlet?.viewOptions,
|
||||
viewOptionsConfig: viewlet?.viewOptions?.other
|
||||
}}
|
||||
/>
|
||||
{:else}
|
||||
<Loading />
|
||||
{/if}
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<style lang="scss">
|
||||
.original-table-container {
|
||||
padding: 1rem;
|
||||
overflow: auto;
|
||||
max-height: 80vh;
|
||||
}
|
||||
</style>
|
||||
+80
-4
@@ -13,17 +13,93 @@
|
||||
//
|
||||
|
||||
import { type Editor } from '@tiptap/core'
|
||||
import type { Class, Doc, Ref } from '@hcengineering/core'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { showPopup } from '@hcengineering/ui'
|
||||
import view, { type Viewlet } from '@hcengineering/view'
|
||||
import { findTable } from '../utils'
|
||||
import { getTableMetadata } from '../tableMetadata'
|
||||
import OriginalTableDataViewer from './OriginalTableDataViewer.svelte'
|
||||
|
||||
/**
|
||||
* Show original table data from when it was first created
|
||||
*/
|
||||
export async function seeOriginalTableData (editor: Editor): Promise<void> {
|
||||
const table = findTable(editor.state.selection)
|
||||
if (table === undefined) return
|
||||
if (table === undefined) {
|
||||
console.warn('No table found to show original data')
|
||||
return
|
||||
}
|
||||
|
||||
const metadata = getTableMetadata(table.node)
|
||||
if (metadata === null || metadata === undefined) return
|
||||
// Empty handler for now
|
||||
console.log('See original data:', metadata)
|
||||
if (metadata === null || metadata === undefined) {
|
||||
console.warn('Table has no metadata to show original data')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
|
||||
// Convert string cardClass to Ref<Class<Doc>> and validate
|
||||
const cardClassRef = metadata.cardClass as any
|
||||
const cardClass = hierarchy.getClass(cardClassRef)
|
||||
if (cardClass == null) {
|
||||
console.warn('Invalid cardClass in table metadata:', metadata.cardClass)
|
||||
return
|
||||
}
|
||||
|
||||
// Validate that we have documentIds or query to fetch original data
|
||||
if (
|
||||
(metadata.documentIds === undefined || metadata.documentIds.length === 0) &&
|
||||
(metadata.query === null || metadata.query === undefined)
|
||||
) {
|
||||
console.warn('Table metadata has no documentIds or query to show original data')
|
||||
return
|
||||
}
|
||||
|
||||
// Load viewlet with descriptor lookup if viewletId is provided (to get config and component)
|
||||
// RelationshipTable has viewletId: undefined (as per buildRelationshipTableMetadata)
|
||||
let viewlet: Viewlet | undefined
|
||||
let viewletWithLookup: any | undefined
|
||||
if (metadata.viewletId !== undefined) {
|
||||
const viewlets = await client.findAll(
|
||||
view.class.Viewlet,
|
||||
{ _id: metadata.viewletId as Ref<Viewlet> },
|
||||
{ lookup: { descriptor: view.class.ViewletDescriptor } }
|
||||
)
|
||||
viewlet = viewlets[0]
|
||||
viewletWithLookup = viewlets[0]
|
||||
} else {
|
||||
// For RelationshipTable (no viewletId), load Table descriptor as fallback
|
||||
// Table component can display both regular and relationship tables
|
||||
const descriptors = await client.findAll(view.class.ViewletDescriptor, {
|
||||
_id: view.viewlet.Table
|
||||
})
|
||||
if (descriptors.length > 0) {
|
||||
viewletWithLookup = { $lookup: { descriptor: descriptors[0] } }
|
||||
}
|
||||
}
|
||||
|
||||
// Use documentIds if available (preserves original order), otherwise use query
|
||||
const documentIds = metadata.documentIds ?? []
|
||||
|
||||
// Show original data in popup
|
||||
// Note: Component is resolved from viewlet descriptor or Table descriptor as fallback
|
||||
// RelationshipTable metadata has viewletId: undefined, so config comes from metadata.config
|
||||
showPopup(
|
||||
OriginalTableDataViewer,
|
||||
{
|
||||
_class: cardClassRef as Ref<Class<Doc>>,
|
||||
config: metadata.config,
|
||||
documentIds: documentIds.length > 0 ? documentIds : [],
|
||||
query: documentIds.length === 0 ? metadata.query : undefined,
|
||||
viewlet,
|
||||
viewletWithLookup
|
||||
},
|
||||
'center'
|
||||
)
|
||||
} catch (error) {
|
||||
console.error('Failed to show original table data:', error)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -496,12 +496,24 @@ export interface TableMetadata {
|
||||
|
||||
/**
|
||||
* Build metadata object from props and documents
|
||||
* If viewlet is not provided, tries to find a default viewlet for the class
|
||||
*/
|
||||
function buildTableMetadata (props: CopyAsMarkdownTableProps, docs: Doc[]): TableMetadata {
|
||||
async function buildTableMetadata (
|
||||
props: CopyAsMarkdownTableProps,
|
||||
docs: Doc[],
|
||||
client?: Client
|
||||
): Promise<TableMetadata> {
|
||||
// If viewlet is not provided, try to find a default viewlet for the class
|
||||
let viewletId: Ref<Viewlet> | undefined = props.viewlet?._id
|
||||
if (viewletId === undefined && client !== undefined) {
|
||||
const { viewlet } = await loadViewletConfig(client, client.getHierarchy(), props.cardClass, undefined, props.config)
|
||||
viewletId = viewlet?._id
|
||||
}
|
||||
|
||||
return {
|
||||
version: '1.0',
|
||||
cardClass: props.cardClass,
|
||||
viewletId: props.viewlet?._id,
|
||||
viewletId,
|
||||
config: props.config,
|
||||
query: props.query,
|
||||
documentIds: docs.map((d) => d._id),
|
||||
@@ -689,7 +701,7 @@ export async function CopyAsMarkdownTable (
|
||||
}
|
||||
|
||||
// Build metadata for table refresh/diff functionality
|
||||
const metadata = buildTableMetadata(props, docs)
|
||||
const metadata = await buildTableMetadata(props, docs, client)
|
||||
await copyMarkdown(markdown, metadata)
|
||||
|
||||
const language = getCurrentLanguage()
|
||||
|
||||
Reference in New Issue
Block a user