Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions plugins/text-editor-assets/lang/cs.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
"SeeOriginalData": "Zobrazit původní data",
"CategoryVersioning": "Verzování",
"TableDiffLabel": "Změny v tabulce",
"TableRefreshConfirmationLabel": "Potvrdit obnovení tabulky",
"ConvertToLinkPreview": "Zobrazit jako odkaz",
"ConvertToEmbedPreview": "Zobrazit jako náhled obsahu",
"UnableToLoadEmbeddedContent": "Náhled odkazu nelze načíst kvůli nastavení oprávnění nebo nepodporovanému obsahu",
Expand Down
1 change: 1 addition & 0 deletions plugins/text-editor-assets/lang/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
"SeeOriginalData": "Originaldaten anzeigen",
"CategoryVersioning": "Versionierung",
"TableDiffLabel": "Tabellenänderungen",
"TableRefreshConfirmationLabel": "Tabellenaktualisierung bestätigen",
"ConvertToLinkPreview": "Als Link anzeigen",
"ConvertToEmbedPreview": "Als Inhaltsvorschau anzeigen",
"UnableToLoadEmbeddedContent": "Die Linkvorschau konnte aufgrund von Berechtigungseinstellungen oder nicht unterstütztem Inhalt nicht geladen werden",
Expand Down
1 change: 1 addition & 0 deletions plugins/text-editor-assets/lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
"SeeOriginalData": "See Original Data",
"CategoryVersioning": "Versioning",
"TableDiffLabel": "Table Changes",
"TableRefreshConfirmationLabel": "Confirm Table Refresh",

"ConvertToLinkPreview": "Show as a link",
"ConvertToEmbedPreview": "Show as a content preview",
Expand Down
1 change: 1 addition & 0 deletions plugins/text-editor-assets/lang/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
"SeeOriginalData": "Ver datos originales",
"CategoryVersioning": "Control de versiones",
"TableDiffLabel": "Cambios en la tabla",
"TableRefreshConfirmationLabel": "Confirmar actualización de tabla",
"ConvertToLinkPreview": "Mostrar como enlace",
"ConvertToEmbedPreview": "Mostrar como vista previa de contenido",
"UnableToLoadEmbeddedContent": "No se pudo cargar la vista previa del enlace debido a la configuración de permisos o contenido no compatible",
Expand Down
1 change: 1 addition & 0 deletions plugins/text-editor-assets/lang/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
"SeeOriginalData": "Voir les données originales",
"CategoryVersioning": "Gestion de versions",
"TableDiffLabel": "Modifications du tableau",
"TableRefreshConfirmationLabel": "Confirmer l'actualisation du tableau",
"ConvertToLinkPreview": "Afficher comme lien",
"ConvertToEmbedPreview": "Afficher comme aperçu de contenu",
"UnableToLoadEmbeddedContent": "L’aperçu du lien n’a pas pu être chargé en raison des paramètres d’autorisation ou d’un contenu non pris en charge",
Expand Down
1 change: 1 addition & 0 deletions plugins/text-editor-assets/lang/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
"SeeOriginalData": "Vedi dati originali",
"CategoryVersioning": "Versionamento",
"TableDiffLabel": "Modifiche alla tabella",
"TableRefreshConfirmationLabel": "Conferma aggiornamento tabella",
"ConvertToLinkPreview": "Mostra come link",
"ConvertToEmbedPreview": "Mostra come anteprima del contenuto",
"UnableToLoadEmbeddedContent": "Impossibile caricare l'anteprima del link a causa delle impostazioni dei permessi o di contenuto non supportato",
Expand Down
1 change: 1 addition & 0 deletions plugins/text-editor-assets/lang/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
"SeeOriginalData": "元のデータを表示",
"CategoryVersioning": "バージョン管理",
"TableDiffLabel": "テーブルの変更",
"TableRefreshConfirmationLabel": "テーブルの更新を確認",
"ConvertToLinkPreview": "リンクとして表示",
"ConvertToEmbedPreview": "コンテンツプレビューとして表示",
"UnableToLoadEmbeddedContent": "リンクのプレビューを読み込めません。権限設定または非対応のコンテンツが原因です",
Expand Down
1 change: 1 addition & 0 deletions plugins/text-editor-assets/lang/pt.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
"SeeOriginalData": "Ver dados originais",
"CategoryVersioning": "Versionamento",
"TableDiffLabel": "Alterações na tabela",
"TableRefreshConfirmationLabel": "Confirmar atualização da tabela",
"ConvertToLinkPreview": "Mostrar como link",
"ConvertToEmbedPreview": "Mostrar como pré-visualização de conteúdo",
"UnableToLoadEmbeddedContent": "Não foi possível carregar a pré-visualização do link devido às permissões ou a conteúdo não suportado",
Expand Down
1 change: 1 addition & 0 deletions plugins/text-editor-assets/lang/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
"SeeOriginalData": "Показать исходные данные",
"CategoryVersioning": "Версионирование",
"TableDiffLabel": "Изменения в таблице",
"TableRefreshConfirmationLabel": "Подтвердить обновление таблицы",
"ConvertToLinkPreview": "Показать как ссылку",
"ConvertToEmbedPreview": "Показать как превью контента",
"UnableToLoadEmbeddedContent": "Не удалось загрузить превью ссылки из-за настроек доступа или неподдерживаемого содержимого",
Expand Down
1 change: 1 addition & 0 deletions plugins/text-editor-assets/lang/tr.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
"SeeOriginalData": "Orijinal verileri görüntüle",
"CategoryVersioning": "Sürümleme",
"TableDiffLabel": "Tablo değişiklikleri",
"TableRefreshConfirmationLabel": "Tablo yenilemeyi onayla",
"ConvertToLinkPreview": "Bağlantı olarak göster",
"ConvertToEmbedPreview": "İçerik önizlemesi olarak göster",
"UnableToLoadEmbeddedContent": "Bağlantı önizlemesi izin ayarları veya desteklenmeyen içerik nedeniyle yüklenemedi",
Expand Down
1 change: 1 addition & 0 deletions plugins/text-editor-assets/lang/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
"SeeOriginalData": "查看原始数据",
"CategoryVersioning": "版本控制",
"TableDiffLabel": "表格变更",
"TableRefreshConfirmationLabel": "确认表格刷新",
"ConvertToLinkPreview": "显示为链接",
"ConvertToEmbedPreview": "显示为内容预览",
"UnableToLoadEmbeddedContent": "由于权限设置或不支持的内容,无法加载链接预览",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<!--
//
// 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 { markdownToMarkup } from '@hcengineering/text-markdown'
import presentation, { Card } from '@hcengineering/presentation'
import textEditor from '@hcengineering/text-editor'
import { Button } from '@hcengineering/ui'
import { createEventDispatcher } from 'svelte'

import MarkupDiffViewer from '../../../MarkupDiffViewer.svelte'

export let oldMarkdown: string
export let newMarkdown: string
export let onApply: () => void = () => {}

const dispatch = createEventDispatcher()

// Convert markdown strings to MarkupNode for structured diffing
$: oldContent = markdownToMarkup(oldMarkdown)
$: newContent = markdownToMarkup(newMarkdown)

function handleApply (): void {
onApply()
dispatch('close')
}

function handleCancel (): void {
dispatch('close')
}
</script>

<Card
label={textEditor.string.TableRefreshConfirmationLabel}
fullSize={true}
canSave={true}
okAction={handleApply}
onCancel={handleCancel}
on:close={handleCancel}
>
<div class="table-diff-container">
<MarkupDiffViewer content={newContent} comparedVersion={oldContent} />
</div>
<svelte:fragment slot="buttons">
<Button label={presentation.string.Cancel} kind="secondary" size="large" on:click={handleCancel} />
</svelte:fragment>
</Card>

<style>
.table-diff-container {
padding: 1rem;
overflow: auto;
max-height: 80vh;
}
</style>
Original file line number Diff line number Diff line change
Expand Up @@ -15,82 +15,24 @@
import { type Editor } from '@tiptap/core'
import { Node } from '@tiptap/pm/model'
import { markdownToMarkup } from '@hcengineering/text-markdown'
import { getClient } from '@hcengineering/presentation'
import { buildMarkdownTableFromDocs } from '../refreshTable'
import { showPopup } from '@hcengineering/ui'
import { findTable } from '../utils'
import { getTableMetadata } from '../tableMetadata'
import TableRefreshConfirmation from './TableRefreshConfirmation.svelte'
import { extractTableMarkdown, fetchFreshTableData } from './tableUtils'

/**
* Refresh a table by re-executing its query and rebuilding the table content
* Apply the refreshed table to the editor
*/
export async function refreshTable (editor: Editor): Promise<void> {
const table = findTable(editor.state.selection)
if (table === undefined) {
console.warn('No table found to refresh')
return
}

const metadata = getTableMetadata(table.node)
if (metadata === null || metadata === undefined) {
console.warn('Table has no metadata to refresh')
return
}

function applyTableRefresh (
editor: Editor,
table: { node: Node, pos: number },
freshTableMarkdown: string,
metadata: any
): void {
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
}

// Build query: use documentIds as filter if available (preserves selected docs), otherwise use query
let query: any
let useDocumentIdsOrder = false
if (metadata.documentIds !== undefined && metadata.documentIds.length > 0) {
// Build query from document IDs (preserves selected docs filter)
query = { ...(metadata?.query ?? {}), _id: { $in: metadata.documentIds } }
useDocumentIdsOrder = true
} else if (metadata.query !== null && metadata.query !== undefined) {
query = metadata.query
} else {
console.warn('Table metadata has no query or documentIds to execute')
return
}

// Execute query to fetch fresh documents
let docs = await client.findAll(cardClassRef, query)

// Sort by documentIds order if query was built from documentIds
if (useDocumentIdsOrder && metadata.documentIds !== undefined) {
const idIndexMap = new Map(metadata.documentIds.map((id, index) => [id, index]))
docs = docs.sort((a, b) => {
const indexA = idIndexMap.get(a._id) ?? Infinity
const indexB = idIndexMap.get(b._id) ?? Infinity
return indexA - indexB
})
}

if (docs.length === 0) {
console.warn('Query returned no documents')
// Optionally show empty table or keep existing
return
}

// Build markdown table from fresh documents
const markdown = await buildMarkdownTableFromDocs(docs, metadata, client)

if (markdown.length === 0) {
console.warn('Failed to build markdown table')
return
}

// Convert markdown to ProseMirror nodes
const markupNode = markdownToMarkup(markdown)
const markupNode = markdownToMarkup(freshTableMarkdown)
const content = Node.fromJSON(editor.state.schema, markupNode)

// Find the table node in the parsed content
Expand All @@ -108,7 +50,6 @@ export async function refreshTable (editor: Editor): Promise<void> {
}

// Preserve metadata in the new table node
// TypeScript needs explicit type assertion here
const tableNode: Node = newTableNode
const metadataAttr = JSON.stringify(metadata)
const newAttrs = { ...tableNode.attrs, tableMetadata: metadataAttr }
Expand All @@ -120,8 +61,53 @@ export async function refreshTable (editor: Editor): Promise<void> {

// Dispatch the transaction
editor.view.dispatch(tr)
} catch (error) {
console.error('Failed to apply table refresh:', error)
}
}

/**
* Refresh a table by re-executing its query and rebuilding the table content
* Shows a confirmation modal with diff before applying changes
*/
export async function refreshTable (editor: Editor): Promise<void> {
const table = findTable(editor.state.selection)
if (table === undefined) {
console.warn('No table found to refresh')
return
}

const metadata = getTableMetadata(table.node)
if (metadata === null || metadata === undefined) {
console.warn('Table has no metadata to refresh')
return
}

try {
const currentTableMarkdown = extractTableMarkdown(table.node)

const freshTableMarkdown = await fetchFreshTableData(metadata)
if (freshTableMarkdown === null) {
return
}

await new Promise<void>((resolve) => {
showPopup(
TableRefreshConfirmation,
{
oldMarkdown: currentTableMarkdown,
newMarkdown: freshTableMarkdown,
onApply: () => {
applyTableRefresh(editor, table, freshTableMarkdown, metadata)
}
},
'center',
() => {
resolve()
}
)
})
} catch (error) {
console.error('Failed to refresh table:', error)
// Error is logged, user can retry if needed
}
}
Loading
Loading