From e96f4951f51a53ea1f8f28661f919bc341bfffa1 Mon Sep 17 00:00:00 2001 From: anaisberg Date: Mon, 2 Jun 2025 11:25:42 +0200 Subject: [PATCH 01/85] =?UTF-8?q?=F0=9F=9A=A7=20create=20block?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 1 + .../doc-editor/components/BlockNoteEditor.tsx | 3 +- .../components/BlockNoteSuggestionMenu.tsx | 2 + .../custom-blocks/DatabaseBlock.tsx | 166 ++++++++++++++++++ .../components/custom-blocks/index.ts | 1 + .../blocks-mapping/databaseDocx.tsx | 24 +++ .../doc-export/blocks-mapping/databasePDF.tsx | 35 ++++ .../docs/doc-export/blocks-mapping/index.ts | 2 + .../features/docs/doc-export/mappingDocx.tsx | 2 + .../features/docs/doc-export/mappingPDF.tsx | 2 + .../apps/impress/src/i18n/translations.json | 2 + 11 files changed, 239 insertions(+), 1 deletion(-) create mode 100644 src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock.tsx create mode 100644 src/frontend/apps/impress/src/features/docs/doc-export/blocks-mapping/databaseDocx.tsx create mode 100644 src/frontend/apps/impress/src/features/docs/doc-export/blocks-mapping/databasePDF.tsx diff --git a/CHANGELOG.md b/CHANGELOG.md index c831e4eec..a91aef717 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ and this project adheres to - ✨(backend) allow setting session cookie age via env var #977 - ✨(backend) allow theme customnization using a configuration file #948 - ✨(frontend) Add a custom callout block to the editor #892 +- ✨(frontend) Add a custom database block to the editor #892 - 🚩(frontend) version MIT only #911 - ✨(backend) integrate maleware_detection from django-lasuite #936 - 🏗️(frontend) Footer configurable #959 diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteEditor.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteEditor.tsx index 7ed06af1d..9d30d958f 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteEditor.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteEditor.tsx @@ -27,13 +27,14 @@ import { randomColor } from '../utils'; import { BlockNoteSuggestionMenu } from './BlockNoteSuggestionMenu'; import { BlockNoteToolbar } from './BlockNoteToolBar/BlockNoteToolbar'; -import { CalloutBlock, DividerBlock } from './custom-blocks'; +import { CalloutBlock, DatabaseBlock, DividerBlock } from './custom-blocks'; export const blockNoteSchema = withPageBreak( BlockNoteSchema.create({ blockSpecs: { ...defaultBlockSpecs, callout: CalloutBlock, + database: DatabaseBlock, divider: DividerBlock, }, }), diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteSuggestionMenu.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteSuggestionMenu.tsx index 3122b1c17..3a9879073 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteSuggestionMenu.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteSuggestionMenu.tsx @@ -13,6 +13,7 @@ import { DocsBlockSchema } from '../types'; import { getCalloutReactSlashMenuItems, + getDatabaseReactSlashMenuItems, getDividerReactSlashMenuItems, } from './custom-blocks'; @@ -29,6 +30,7 @@ export const BlockNoteSuggestionMenu = () => { getDefaultReactSlashMenuItems(editor), getPageBreakReactSlashMenuItems(editor), getCalloutReactSlashMenuItems(editor, t, basicBlocksName), + getDatabaseReactSlashMenuItems(editor, t, basicBlocksName), getDividerReactSlashMenuItems(editor, t, basicBlocksName), ), query, diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock.tsx new file mode 100644 index 000000000..db06c7caa --- /dev/null +++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock.tsx @@ -0,0 +1,166 @@ +/* eslint-disable react-hooks/rules-of-hooks */ +import { defaultProps, insertOrUpdateBlock } from '@blocknote/core'; +import { BlockTypeSelectItem, createReactBlockSpec } from '@blocknote/react'; +import { TFunction } from 'i18next'; +import React, { useEffect, useState } from 'react'; +import { css } from 'styled-components'; + +import { Box, BoxButton, Icon } from '@/components'; + +import { DocsBlockNoteEditor } from '../../types'; +import { EmojiPicker } from '../EmojiPicker'; + +const databaseCustom = [ + { + name: 'Databas', + id: 'database', + emojis: [ + 'bulb', + 'point_right', + 'point_up', + 'ok_hand', + 'key', + 'construction', + 'warning', + 'fire', + 'pushpin', + 'scissors', + 'question', + 'no_entry', + 'no_entry_sign', + 'alarm_clock', + 'phone', + 'rotating_light', + 'recycle', + 'white_check_mark', + 'lock', + 'paperclip', + 'book', + 'speaking_head_in_silhouette', + 'arrow_right', + 'loudspeaker', + 'hammer_and_wrench', + 'gear', + ], + }, +]; + +const databaseCategories = [ + 'database', + 'people', + 'nature', + 'foods', + 'activity', + 'places', + 'flags', + 'objects', + 'symbols', +]; + +export const DatabaseBlock = createReactBlockSpec( + { + type: 'database', + propSchema: { + textAlignment: defaultProps.textAlignment, + backgroundColor: defaultProps.backgroundColor, + emoji: { default: '💡' }, + }, + content: 'inline', + }, + { + render: ({ block, editor, contentRef }) => { + const [openEmojiPicker, setOpenEmojiPicker] = useState(false); + + const toggleEmojiPicker = (e: React.MouseEvent) => { + e.preventDefault(); + e.stopPropagation(); + setOpenEmojiPicker(!openEmojiPicker); + }; + + const onClickOutside = () => setOpenEmojiPicker(false); + + const onEmojiSelect = ({ native }: { native: string }) => { + editor.updateBlock(block, { props: { emoji: native } }); + setOpenEmojiPicker(false); + }; + + // Temporary: sets a yellow background color to a database block when added by + // the user, while keeping the colors menu on the drag handler usable for + // this custom block. + useEffect(() => { + if ( + !block.content.length && + block.props.backgroundColor === 'default' + ) { + editor.updateBlock(block, { props: { backgroundColor: 'yellow' } }); + } + }, [block, editor]); + + return ( + + + {block.props.emoji} + + + {openEmojiPicker && ( + + )} + + + ); + }, + }, +); + +export const getDatabaseReactSlashMenuItems = ( + editor: DocsBlockNoteEditor, + t: TFunction<'translation', undefined>, + group: string, +) => [ + { + title: t('Database'), + onItemClick: () => { + insertOrUpdateBlock(editor, { + type: 'database', + }); + }, + aliases: ['database', 'encadré', 'hervorhebung', 'benadrukken'], + group, + icon: , + subtext: t('Add a database block'), + }, +]; + +export const getDatabaseFormattingToolbarItems = ( + t: TFunction<'translation', undefined>, +): BlockTypeSelectItem => ({ + name: t('Database'), + type: 'database', + icon: () => , + isSelected: (block) => block.type === 'database', +}); diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/index.ts b/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/index.ts index 34a8c459c..400fb28e3 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/index.ts +++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/index.ts @@ -1,2 +1,3 @@ export * from './CalloutBlock'; export * from './DividerBlock'; +export * from './DatabaseBlock'; diff --git a/src/frontend/apps/impress/src/features/docs/doc-export/blocks-mapping/databaseDocx.tsx b/src/frontend/apps/impress/src/features/docs/doc-export/blocks-mapping/databaseDocx.tsx new file mode 100644 index 000000000..5e71ce244 --- /dev/null +++ b/src/frontend/apps/impress/src/features/docs/doc-export/blocks-mapping/databaseDocx.tsx @@ -0,0 +1,24 @@ +import { Paragraph, TextRun } from 'docx'; + +import { DocsExporterDocx } from '../types'; +import { docxBlockPropsToStyles } from '../utils'; + +export const blockMappingDatabaseDocx: DocsExporterDocx['mappings']['blockMapping']['database'] = + (block, exporter) => { + return new Paragraph({ + ...docxBlockPropsToStyles(block.props, exporter.options.colors), + spacing: { before: 10, after: 10 }, + children: [ + new TextRun({ + text: ' ', + break: 1, + }), + new TextRun(' ' + block.props.emoji + ' '), + ...exporter.transformInlineContent(block.content), + new TextRun({ + text: ' ', + break: 1, + }), + ], + }); + }; diff --git a/src/frontend/apps/impress/src/features/docs/doc-export/blocks-mapping/databasePDF.tsx b/src/frontend/apps/impress/src/features/docs/doc-export/blocks-mapping/databasePDF.tsx new file mode 100644 index 000000000..0afbadef3 --- /dev/null +++ b/src/frontend/apps/impress/src/features/docs/doc-export/blocks-mapping/databasePDF.tsx @@ -0,0 +1,35 @@ +import { StyleSheet, Text, View } from '@react-pdf/renderer'; + +import { DocsExporterPDF } from '../types'; + +const styles = StyleSheet.create({ + wrapper: { + width: '100%', + display: 'flex', + flexDirection: 'row', + padding: 8, + gap: 4, + }, + emoji: { + fontSize: 16, + }, + text: { + maxWidth: '94%', + paddingTop: 2, + }, +}); + +export const blockMappingDatabasePDF: DocsExporterPDF['mappings']['blockMapping']['database'] = + (block, exporter) => { + return ( + + + {block.props.emoji} + + + {' '} + {exporter.transformInlineContent(block.content)}{' '} + + + ); + }; diff --git a/src/frontend/apps/impress/src/features/docs/doc-export/blocks-mapping/index.ts b/src/frontend/apps/impress/src/features/docs/doc-export/blocks-mapping/index.ts index e3e766dbc..a81fccf3c 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-export/blocks-mapping/index.ts +++ b/src/frontend/apps/impress/src/features/docs/doc-export/blocks-mapping/index.ts @@ -1,5 +1,7 @@ export * from './calloutDocx'; export * from './calloutPDF'; +export * from './databaseDocx'; +export * from './databasePDF'; export * from './dividerDocx'; export * from './dividerPDF'; export * from './headingPDF'; diff --git a/src/frontend/apps/impress/src/features/docs/doc-export/mappingDocx.tsx b/src/frontend/apps/impress/src/features/docs/doc-export/mappingDocx.tsx index 434daa995..2f31e3021 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-export/mappingDocx.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-export/mappingDocx.tsx @@ -2,6 +2,7 @@ import { docxDefaultSchemaMappings } from '@blocknote/xl-docx-exporter'; import { blockMappingCalloutDocx, + blockMappingDatabaseDocx, blockMappingDividerDocx, blockMappingImageDocx, blockMappingQuoteDocx, @@ -14,6 +15,7 @@ export const docxDocsSchemaMappings: DocsExporterDocx['mappings'] = { ...docxDefaultSchemaMappings.blockMapping, callout: blockMappingCalloutDocx, divider: blockMappingDividerDocx, + database: blockMappingDatabaseDocx, quote: blockMappingQuoteDocx, image: blockMappingImageDocx, }, diff --git a/src/frontend/apps/impress/src/features/docs/doc-export/mappingPDF.tsx b/src/frontend/apps/impress/src/features/docs/doc-export/mappingPDF.tsx index 4224045d1..27574874c 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-export/mappingPDF.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-export/mappingPDF.tsx @@ -2,6 +2,7 @@ import { pdfDefaultSchemaMappings } from '@blocknote/xl-pdf-exporter'; import { blockMappingCalloutPDF, + blockMappingDatabasePDF, blockMappingDividerPDF, blockMappingHeadingPDF, blockMappingImagePDF, @@ -16,6 +17,7 @@ export const pdfDocsSchemaMappings: DocsExporterPDF['mappings'] = { blockMapping: { ...pdfDefaultSchemaMappings.blockMapping, callout: blockMappingCalloutPDF, + database: blockMappingDatabasePDF, // TODO: create Db block heading: blockMappingHeadingPDF, image: blockMappingImagePDF, paragraph: blockMappingParagraphPDF, diff --git a/src/frontend/apps/impress/src/i18n/translations.json b/src/frontend/apps/impress/src/i18n/translations.json index 24985d1b2..abca3f075 100644 --- a/src/frontend/apps/impress/src/i18n/translations.json +++ b/src/frontend/apps/impress/src/i18n/translations.json @@ -251,6 +251,7 @@ "Banner image": "Imagen de portada", "Beautify": "Embellecer", "Callout": "Destacado", + "Database": "Database", "Can't load this page, please check your internet connection.": "No se puede cargar esta página, por favor compruebe su conexión a Internet.", "Cancel": "Cancelar", "Close the modal": "Cerrar modal", @@ -420,6 +421,7 @@ "Accessible to authenticated users": "Accessible aux utilisateurs authentifiés", "Add": "Ajouter", "Add a callout block": "Ajouter un bloc d'alerte", + "Add a database block": "Ajouter un bloc base de données", "Add a horizontal line": "Ajouter une ligne horizontale", "Administrator": "Administrateur", "All docs": "Tous les documents", From d349a514d0c8e9b610410570ffa179d3381e9ecb Mon Sep 17 00:00:00 2001 From: anaisberg Date: Mon, 2 Jun 2025 12:16:32 +0200 Subject: [PATCH 02/85] =?UTF-8?q?=F0=9F=92=84=20text=20and=20icons?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../doc-editor/components/custom-blocks/DatabaseBlock.tsx | 8 ++++---- src/frontend/apps/impress/src/i18n/translations.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock.tsx index db06c7caa..84aef50fb 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock.tsx @@ -149,10 +149,10 @@ export const getDatabaseReactSlashMenuItems = ( type: 'database', }); }, - aliases: ['database', 'encadré', 'hervorhebung', 'benadrukken'], + aliases: ['database', 'db', 'base de donnée'], group, - icon: , - subtext: t('Add a database block'), + icon: , + subtext: t('Create database view synced with Grist'), }, ]; @@ -161,6 +161,6 @@ export const getDatabaseFormattingToolbarItems = ( ): BlockTypeSelectItem => ({ name: t('Database'), type: 'database', - icon: () => , + icon: () => , isSelected: (block) => block.type === 'database', }); diff --git a/src/frontend/apps/impress/src/i18n/translations.json b/src/frontend/apps/impress/src/i18n/translations.json index abca3f075..8461c54ff 100644 --- a/src/frontend/apps/impress/src/i18n/translations.json +++ b/src/frontend/apps/impress/src/i18n/translations.json @@ -421,7 +421,7 @@ "Accessible to authenticated users": "Accessible aux utilisateurs authentifiés", "Add": "Ajouter", "Add a callout block": "Ajouter un bloc d'alerte", - "Add a database block": "Ajouter un bloc base de données", + "Create database view synced with Grist": "Créer une vue table synchronisée avec Grist", "Add a horizontal line": "Ajouter une ligne horizontale", "Administrator": "Administrateur", "All docs": "Tous les documents", From ff1d0e62749a16c04bb18353a7a298bb8be41393 Mon Sep 17 00:00:00 2001 From: anaisberg Date: Mon, 2 Jun 2025 12:24:18 +0200 Subject: [PATCH 03/85] =?UTF-8?q?=F0=9F=92=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../custom-blocks/DatabaseBlock.tsx | 93 +------------------ 1 file changed, 3 insertions(+), 90 deletions(-) diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock.tsx index 84aef50fb..735d6f8f6 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock.tsx @@ -2,60 +2,12 @@ import { defaultProps, insertOrUpdateBlock } from '@blocknote/core'; import { BlockTypeSelectItem, createReactBlockSpec } from '@blocknote/react'; import { TFunction } from 'i18next'; -import React, { useEffect, useState } from 'react'; +import React, { useEffect } from 'react'; import { css } from 'styled-components'; -import { Box, BoxButton, Icon } from '@/components'; +import { Box, Icon } from '@/components'; import { DocsBlockNoteEditor } from '../../types'; -import { EmojiPicker } from '../EmojiPicker'; - -const databaseCustom = [ - { - name: 'Databas', - id: 'database', - emojis: [ - 'bulb', - 'point_right', - 'point_up', - 'ok_hand', - 'key', - 'construction', - 'warning', - 'fire', - 'pushpin', - 'scissors', - 'question', - 'no_entry', - 'no_entry_sign', - 'alarm_clock', - 'phone', - 'rotating_light', - 'recycle', - 'white_check_mark', - 'lock', - 'paperclip', - 'book', - 'speaking_head_in_silhouette', - 'arrow_right', - 'loudspeaker', - 'hammer_and_wrench', - 'gear', - ], - }, -]; - -const databaseCategories = [ - 'database', - 'people', - 'nature', - 'foods', - 'activity', - 'places', - 'flags', - 'objects', - 'symbols', -]; export const DatabaseBlock = createReactBlockSpec( { @@ -69,21 +21,6 @@ export const DatabaseBlock = createReactBlockSpec( }, { render: ({ block, editor, contentRef }) => { - const [openEmojiPicker, setOpenEmojiPicker] = useState(false); - - const toggleEmojiPicker = (e: React.MouseEvent) => { - e.preventDefault(); - e.stopPropagation(); - setOpenEmojiPicker(!openEmojiPicker); - }; - - const onClickOutside = () => setOpenEmojiPicker(false); - - const onEmojiSelect = ({ native }: { native: string }) => { - editor.updateBlock(block, { props: { emoji: native } }); - setOpenEmojiPicker(false); - }; - // Temporary: sets a yellow background color to a database block when added by // the user, while keeping the colors menu on the drag handler usable for // this custom block. @@ -105,31 +42,6 @@ export const DatabaseBlock = createReactBlockSpec( flexDirection: 'row', }} > - - {block.props.emoji} - - - {openEmojiPicker && ( - - )} ); @@ -156,6 +68,7 @@ export const getDatabaseReactSlashMenuItems = ( }, ]; +// TODO: remove if unused export const getDatabaseFormattingToolbarItems = ( t: TFunction<'translation', undefined>, ): BlockTypeSelectItem => ({ From 2c2e34f83b1f1c917c8a44eaab4fff1f09d14027 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Di=20Domenico?= Date: Mon, 2 Jun 2025 12:33:57 +0200 Subject: [PATCH 04/85] =?UTF-8?q?=E2=9C=A8=20base=20style=20option=20for?= =?UTF-8?q?=20database=20in=20WIP?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../doc-editor/components/custom-blocks/DatabaseBlock.tsx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock.tsx index 735d6f8f6..db536a248 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock.tsx @@ -3,7 +3,6 @@ import { defaultProps, insertOrUpdateBlock } from '@blocknote/core'; import { BlockTypeSelectItem, createReactBlockSpec } from '@blocknote/react'; import { TFunction } from 'i18next'; import React, { useEffect } from 'react'; -import { css } from 'styled-components'; import { Box, Icon } from '@/components'; @@ -15,12 +14,11 @@ export const DatabaseBlock = createReactBlockSpec( propSchema: { textAlignment: defaultProps.textAlignment, backgroundColor: defaultProps.backgroundColor, - emoji: { default: '💡' }, }, content: 'inline', }, { - render: ({ block, editor, contentRef }) => { + render: ({ block, editor }) => { // Temporary: sets a yellow background color to a database block when added by // the user, while keeping the colors menu on the drag handler usable for // this custom block. @@ -29,7 +27,7 @@ export const DatabaseBlock = createReactBlockSpec( !block.content.length && block.props.backgroundColor === 'default' ) { - editor.updateBlock(block, { props: { backgroundColor: 'yellow' } }); + editor.updateBlock(block, { props: { backgroundColor: 'orange' } }); } }, [block, editor]); @@ -42,7 +40,7 @@ export const DatabaseBlock = createReactBlockSpec( flexDirection: 'row', }} > - + 🏗️ Database block is in development ); }, From ae0502ee38efa9f96735c74f9bf025de2ba6839d Mon Sep 17 00:00:00 2001 From: anaisberg Date: Mon, 2 Jun 2025 12:42:39 +0200 Subject: [PATCH 05/85] =?UTF-8?q?=F0=9F=8C=90=20translate=20database?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/frontend/apps/impress/src/i18n/translations.json | 1 + 1 file changed, 1 insertion(+) diff --git a/src/frontend/apps/impress/src/i18n/translations.json b/src/frontend/apps/impress/src/i18n/translations.json index 8461c54ff..6de839425 100644 --- a/src/frontend/apps/impress/src/i18n/translations.json +++ b/src/frontend/apps/impress/src/i18n/translations.json @@ -421,6 +421,7 @@ "Accessible to authenticated users": "Accessible aux utilisateurs authentifiés", "Add": "Ajouter", "Add a callout block": "Ajouter un bloc d'alerte", + "Database": "Base de données", "Create database view synced with Grist": "Créer une vue table synchronisée avec Grist", "Add a horizontal line": "Ajouter une ligne horizontale", "Administrator": "Administrateur", From 4b42fc0ea894bd96d28ab085c0760dbc06c4609a Mon Sep 17 00:00:00 2001 From: anaisberg Date: Mon, 2 Jun 2025 13:35:01 +0200 Subject: [PATCH 06/85] =?UTF-8?q?=F0=9F=8C=90=20subtitle?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/frontend/apps/impress/src/i18n/translations.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/apps/impress/src/i18n/translations.json b/src/frontend/apps/impress/src/i18n/translations.json index 6de839425..13439463e 100644 --- a/src/frontend/apps/impress/src/i18n/translations.json +++ b/src/frontend/apps/impress/src/i18n/translations.json @@ -422,7 +422,7 @@ "Add": "Ajouter", "Add a callout block": "Ajouter un bloc d'alerte", "Database": "Base de données", - "Create database view synced with Grist": "Créer une vue table synchronisée avec Grist", + "Create database view synced with Grist": "Créer une base de données synchronisée avec Grist", "Add a horizontal line": "Ajouter une ligne horizontale", "Administrator": "Administrateur", "All docs": "Tous les documents", From 3abdb870f96aa26551da4ca6c2cf81224a4b547a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Di=20Domenico?= Date: Mon, 2 Jun 2025 13:35:49 +0200 Subject: [PATCH 07/85] =?UTF-8?q?=E2=9C=A8=20add=20database=20suggestion?= =?UTF-8?q?=20in=20advanced=20group?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../docs/doc-editor/components/BlockNoteSuggestionMenu.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteSuggestionMenu.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteSuggestionMenu.tsx index 3a9879073..26a65b9e0 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteSuggestionMenu.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteSuggestionMenu.tsx @@ -21,6 +21,7 @@ export const BlockNoteSuggestionMenu = () => { const editor = useBlockNoteEditor(); const { t } = useTranslation(); const basicBlocksName = useDictionary().slash_menu.page_break.group; + const advancedBlocksName = useDictionary().slash_menu.table.group; const getSlashMenuItems = useMemo(() => { return async (query: string) => @@ -30,13 +31,13 @@ export const BlockNoteSuggestionMenu = () => { getDefaultReactSlashMenuItems(editor), getPageBreakReactSlashMenuItems(editor), getCalloutReactSlashMenuItems(editor, t, basicBlocksName), - getDatabaseReactSlashMenuItems(editor, t, basicBlocksName), + getDatabaseReactSlashMenuItems(editor, t, advancedBlocksName), getDividerReactSlashMenuItems(editor, t, basicBlocksName), ), query, ), ); - }, [basicBlocksName, editor, t]); + }, [basicBlocksName, advancedBlocksName, editor, t]); return ( Date: Mon, 2 Jun 2025 16:19:24 +0200 Subject: [PATCH 08/85] =?UTF-8?q?=E2=9C=A8=20useGristTable=20hook?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/frontend/apps/impress/src/api/config.ts | 2 + src/frontend/apps/impress/src/api/gristApi.ts | 15 +++ src/frontend/apps/impress/src/api/index.ts | 1 + .../custom-blocks/DatabaseBlock.tsx | 25 +++-- .../apps/impress/src/features/grist/index.ts | 1 + .../src/features/grist/useGristTable.ts | 98 +++++++++++++++++++ 6 files changed, 136 insertions(+), 6 deletions(-) create mode 100644 src/frontend/apps/impress/src/api/gristApi.ts create mode 100644 src/frontend/apps/impress/src/features/grist/index.ts create mode 100644 src/frontend/apps/impress/src/features/grist/useGristTable.ts diff --git a/src/frontend/apps/impress/src/api/config.ts b/src/frontend/apps/impress/src/api/config.ts index 916585e60..f4edb5d4d 100644 --- a/src/frontend/apps/impress/src/api/config.ts +++ b/src/frontend/apps/impress/src/api/config.ts @@ -20,3 +20,5 @@ export const backendUrl = () => */ export const baseApiUrl = (apiVersion: string = '1.0') => `${backendUrl()}/api/v${apiVersion}/`; + +export const gristApiUrl = () => 'http://localhost:8484/api/'; diff --git a/src/frontend/apps/impress/src/api/gristApi.ts b/src/frontend/apps/impress/src/api/gristApi.ts new file mode 100644 index 000000000..d17527305 --- /dev/null +++ b/src/frontend/apps/impress/src/api/gristApi.ts @@ -0,0 +1,15 @@ +import { gristApiUrl } from './config'; + +export const gristFetchApi = async (input: string, init?: RequestInit) => { + const apiUrl = `${gristApiUrl()}${input}`; + + const headers = { + 'Content-Type': 'application/json', + Authorization: `Bearer 26c3446a0165be92f25cacbda4cff1a18ef42d59`, + }; + + return await fetch(apiUrl, { + ...init, + headers, + }); +}; diff --git a/src/frontend/apps/impress/src/api/index.ts b/src/frontend/apps/impress/src/api/index.ts index 1d742adb8..2354dda3c 100644 --- a/src/frontend/apps/impress/src/api/index.ts +++ b/src/frontend/apps/impress/src/api/index.ts @@ -1,6 +1,7 @@ export * from './APIError'; export * from './config'; export * from './fetchApi'; +export * from './gristApi'; export * from './helpers'; export * from './types'; export * from './utils'; diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock.tsx index db536a248..114900bcf 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock.tsx @@ -5,6 +5,7 @@ import { TFunction } from 'i18next'; import React, { useEffect } from 'react'; import { Box, Icon } from '@/components'; +import { useGristTable } from '@/features/grist'; import { DocsBlockNoteEditor } from '../../types'; @@ -14,22 +15,34 @@ export const DatabaseBlock = createReactBlockSpec( propSchema: { textAlignment: defaultProps.textAlignment, backgroundColor: defaultProps.backgroundColor, + documentId: { + type: 'string', + default: '', + }, + tableId: { + type: 'string', + default: '', + }, }, content: 'inline', }, { - render: ({ block, editor }) => { - // Temporary: sets a yellow background color to a database block when added by - // the user, while keeping the colors menu on the drag handler usable for - // this custom block. + render: ({ block, editor, contentRef }) => { + const { tableData } = useGristTable({ + tableId: block.props.tableId, + documentId: block.props.documentId, + }); + useEffect(() => { if ( !block.content.length && block.props.backgroundColor === 'default' ) { editor.updateBlock(block, { props: { backgroundColor: 'orange' } }); + } else { + editor.updateBlock(block, { content: JSON.stringify(tableData) }); } - }, [block, editor]); + }, [block, editor, tableData]); return ( - 🏗️ Database block is in development + ); }, diff --git a/src/frontend/apps/impress/src/features/grist/index.ts b/src/frontend/apps/impress/src/features/grist/index.ts new file mode 100644 index 000000000..2e6ec6305 --- /dev/null +++ b/src/frontend/apps/impress/src/features/grist/index.ts @@ -0,0 +1 @@ +export * from './useGristTable'; diff --git a/src/frontend/apps/impress/src/features/grist/useGristTable.ts b/src/frontend/apps/impress/src/features/grist/useGristTable.ts new file mode 100644 index 000000000..eae43614c --- /dev/null +++ b/src/frontend/apps/impress/src/features/grist/useGristTable.ts @@ -0,0 +1,98 @@ +import { useEffect, useState } from 'react'; + +import { APIError, errorCauses, gristFetchApi } from '@/api'; + +export type UseGristTableArguments = { documentId?: string; tableId?: string }; + +export const useGristTable = ({ + documentId, + tableId, +}: UseGristTableArguments) => { + const [tableData, setTableData] = useState([]); + + useEffect(() => { + const fetchData = async () => { + const url = `docs/${documentId}/tables/${tableId}/table`; + const response = await gristFetchApi(url); + if (!response.ok) { + throw new APIError( + 'Failed to request ai transform', + await errorCauses(response), + ); + } + return (await response.json()) as Promise; + }; + + const createDocument = async (workspaceId: string) => { + const url = `workspaces/${workspaceId}/docs`; + const response = await gristFetchApi(url, { + method: 'POST', + body: JSON.stringify({ name: 'New Doc' }), + }); + if (!response.ok) { + throw new APIError( + 'Failed to create Grist document', + await errorCauses(response), + ); + } + return (await response.json()) as Promise; + }; + + const createTable = async (documentId: string) => { + const url = `docs/${documentId}/tables`; + const response = await gristFetchApi(url, { + method: 'POST', + body: JSON.stringify({ + tables: [{ id: 'New Table', columns: [] }], + }), + }); + if (!response.ok) { + throw new APIError( + 'Failed to create Grist table', + await errorCauses(response), + ); + } + return (await response.json()) as Promise; + }; + + if (!documentId) { + const DEFAULT_WORKSPACE_ID = '2'; + createDocument(DEFAULT_WORKSPACE_ID) + .then((newDocumentId) => { + createTable(newDocumentId) + .then((newTableId) => { + console.log( + `Created new Grist document with ID: ${newDocumentId} and table ID: ${newTableId}`, + ); + }) + .catch((error) => { + console.error('Error creating Grist table:', error); + }); + }) + .catch((error) => { + console.error('Error creating Grist document:', error); + }); + } + + if (!tableId) { + createTable(documentId ?? '') + .then((newTableId) => { + console.log(`Created new Grist table with ID: ${newTableId}`); + }) + .catch((error) => { + console.error('Error creating Grist table:', error); + }); + } + + fetchData() + .then((res) => { + setTableData(res); + }) + .catch((error) => { + console.error('Error fetching Grist table data:', error); + }); + }, [documentId, tableId]); + return { + tableData, + }; +}; From 822e9ad22d63b887c8aa545d89b89ef91c242d0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Di=20Domenico?= Date: Mon, 2 Jun 2025 18:35:38 +0200 Subject: [PATCH 09/85] =?UTF-8?q?=E2=9C=A8=20connection=20to=20existing=20?= =?UTF-8?q?table?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/DatabaseSourceSelector.tsx | 45 +++++++++++++ .../components/DatabaseTableDisplay.tsx | 16 +++++ .../custom-blocks/DatabaseBlock.tsx | 62 ++++++++++------- .../apps/impress/src/features/grist/index.ts | 2 + .../src/features/grist/useGristTableData.ts | 40 +++++++++++ .../src/features/grist/useListGristDocs.ts | 66 +++++++++++++++++++ .../src/features/grist/useListGristTables.ts | 54 +++++++++++++++ 7 files changed, 263 insertions(+), 22 deletions(-) create mode 100644 src/frontend/apps/impress/src/features/docs/doc-editor/components/DatabaseSourceSelector.tsx create mode 100644 src/frontend/apps/impress/src/features/docs/doc-editor/components/DatabaseTableDisplay.tsx create mode 100644 src/frontend/apps/impress/src/features/grist/useGristTableData.ts create mode 100644 src/frontend/apps/impress/src/features/grist/useListGristDocs.ts create mode 100644 src/frontend/apps/impress/src/features/grist/useListGristTables.ts diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/DatabaseSourceSelector.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/DatabaseSourceSelector.tsx new file mode 100644 index 000000000..c6cbc80ca --- /dev/null +++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/DatabaseSourceSelector.tsx @@ -0,0 +1,45 @@ +import { useState } from 'react'; + +import { Box, DropdownMenu, Text } from '@/components'; +import { useListGristTables } from '@/features/grist'; +import { Doc, useListGristDocs } from '@/features/grist/useListGristDocs'; + +type DatabaseSourceSelectorProps = { + onSourceSelected: (args: { documentId: number; tableId: string }) => void; +}; + +export const DatabaseSourceSelector = ({ + onSourceSelected, +}: DatabaseSourceSelectorProps) => { + const [selectedDoc, setSelectedDoc] = useState(); + const { docs } = useListGristDocs(); + const { tables } = useListGristTables(selectedDoc?.id); + + return ( + + ({ + label: doc.name, + value: doc.id, + callback: () => setSelectedDoc(doc), + }))} + showArrow + > + {selectedDoc?.name ?? 'Sélectionner un document Grist'} + + {selectedDoc && tables && ( + ({ + label: id, + value: id, + callback: () => + onSourceSelected({ documentId: selectedDoc.id, tableId: id }), + }))} + showArrow + > + Sélectionner une table Grist existante + + )} + + ); +}; diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/DatabaseTableDisplay.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/DatabaseTableDisplay.tsx new file mode 100644 index 000000000..931ca0346 --- /dev/null +++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/DatabaseTableDisplay.tsx @@ -0,0 +1,16 @@ +import { useGristTableData } from '@/features/grist/useGristTableData'; + +export const DatabaseTableDisplay = ({ + documentId, + tableId, +}: { + documentId: string; + tableId: string; +}) => { + const { tableData } = useGristTableData({ + documentId, + tableId, + }); + + return JSON.stringify(tableData, null, 2); +}; diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock.tsx index 114900bcf..db723110c 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock.tsx @@ -1,19 +1,19 @@ /* eslint-disable react-hooks/rules-of-hooks */ import { defaultProps, insertOrUpdateBlock } from '@blocknote/core'; import { BlockTypeSelectItem, createReactBlockSpec } from '@blocknote/react'; +import { Button } from '@openfun/cunningham-react'; import { TFunction } from 'i18next'; -import React, { useEffect } from 'react'; -import { Box, Icon } from '@/components'; -import { useGristTable } from '@/features/grist'; +import { Box, Icon, Text } from '@/components'; import { DocsBlockNoteEditor } from '../../types'; +import { DatabaseSourceSelector } from '../DatabaseSourceSelector'; +import { DatabaseTableDisplay } from '../DatabaseTableDisplay'; export const DatabaseBlock = createReactBlockSpec( { type: 'database', propSchema: { - textAlignment: defaultProps.textAlignment, backgroundColor: defaultProps.backgroundColor, documentId: { type: 'string', @@ -27,23 +27,7 @@ export const DatabaseBlock = createReactBlockSpec( content: 'inline', }, { - render: ({ block, editor, contentRef }) => { - const { tableData } = useGristTable({ - tableId: block.props.tableId, - documentId: block.props.documentId, - }); - - useEffect(() => { - if ( - !block.content.length && - block.props.backgroundColor === 'default' - ) { - editor.updateBlock(block, { props: { backgroundColor: 'orange' } }); - } else { - editor.updateBlock(block, { content: JSON.stringify(tableData) }); - } - }, [block, editor, tableData]); - + render: ({ block, editor }) => { return ( - + + {block.props.documentId && block.props.tableId ? ( + + + + ) : ( + + + ou + { + editor.updateBlock(block, { + props: { documentId: documentId.toString(), tableId }, + }); + }} + /> + + )} ); }, diff --git a/src/frontend/apps/impress/src/features/grist/index.ts b/src/frontend/apps/impress/src/features/grist/index.ts index 2e6ec6305..6640f5190 100644 --- a/src/frontend/apps/impress/src/features/grist/index.ts +++ b/src/frontend/apps/impress/src/features/grist/index.ts @@ -1 +1,3 @@ export * from './useGristTable'; +export * from './useListGristDocs'; +export * from './useListGristTables'; diff --git a/src/frontend/apps/impress/src/features/grist/useGristTableData.ts b/src/frontend/apps/impress/src/features/grist/useGristTableData.ts new file mode 100644 index 000000000..255b9e2c2 --- /dev/null +++ b/src/frontend/apps/impress/src/features/grist/useGristTableData.ts @@ -0,0 +1,40 @@ +import { useEffect, useState } from 'react'; + +import { APIError, errorCauses, gristFetchApi } from '@/api'; + +export type UseGristTableDataArguments = { + documentId: string; + tableId: string; +}; + +export const useGristTableData = ({ + documentId, + tableId, +}: UseGristTableDataArguments) => { + const [tableData, setTableData] = useState([]); + + useEffect(() => { + const fetchData = async () => { + const url = `docs/${documentId}/tables/${tableId}/data`; + const response = await gristFetchApi(url); + if (!response.ok) { + throw new APIError( + 'Failed to request ai transform', + await errorCauses(response), + ); + } + return (await response.json()) as Promise; + }; + + fetchData() + .then((res) => { + setTableData(res); + }) + .catch((error) => { + console.error('Error fetching Grist table data:', error); + }); + }, [documentId, tableId]); + return { + tableData, + }; +}; diff --git a/src/frontend/apps/impress/src/features/grist/useListGristDocs.ts b/src/frontend/apps/impress/src/features/grist/useListGristDocs.ts new file mode 100644 index 000000000..1df7d7c57 --- /dev/null +++ b/src/frontend/apps/impress/src/features/grist/useListGristDocs.ts @@ -0,0 +1,66 @@ +import { useEffect, useState } from 'react'; + +import { APIError, errorCauses, gristFetchApi } from '@/api'; + +export interface Workspace { + id: number; + name: string; + access: string; + docs: Doc[]; + org: Org; +} + +export interface Doc { + id: number; + name: string; + access: string; + isPinned: boolean; + urlId: null; +} + +export interface Org { + id: number; + name: string; + domain: string; + owner: Owner; + access: string; + createdAt: Date; + updatedAt: Date; +} + +export interface Owner { + id: number; + name: string; + picture: null; +} + +export const useListGristDocs = (): { docs: Doc[] } => { + const [docs, setDocs] = useState([]); + + const fetchDocs = async () => { + const DEFAULT_WORKSPACE_ID = 2; + const url = `workspaces/${DEFAULT_WORKSPACE_ID}`; + const response = await gristFetchApi(url); + if (!response.ok) { + throw new APIError( + 'Failed to request ai transform', + await errorCauses(response), + ); + } + return (await response.json()) as Promise; + }; + + useEffect(() => { + fetchDocs() + .then((workspace) => { + setDocs(workspace.docs); + }) + .catch((error) => { + console.error('Error fetching Grist documents:', error); + }); + }, []); + + return { + docs, + }; +}; diff --git a/src/frontend/apps/impress/src/features/grist/useListGristTables.ts b/src/frontend/apps/impress/src/features/grist/useListGristTables.ts new file mode 100644 index 000000000..553b6e7ca --- /dev/null +++ b/src/frontend/apps/impress/src/features/grist/useListGristTables.ts @@ -0,0 +1,54 @@ +import { useEffect, useState } from 'react'; + +import { APIError, errorCauses, gristFetchApi } from '@/api'; + +export interface TableDescription { + tables: Table[]; +} + +export interface Table { + id: string; + fields: Fields; +} + +export interface Fields { + tableRef: number; + onDemand: boolean; +} + +export const useListGristTables = ( + documentId?: number, +): { tables: Table[] | null } => { + const [tables, setTables] = useState(null); + + useEffect(() => { + const fetchTables = async () => { + if (!documentId) { + console.warn('Document ID is required to fetch Grist tables'); + return; + } + const url = `docs/${documentId}/tables`; + const response = await gristFetchApi(url); + if (!response.ok) { + throw new APIError( + 'Failed to fetch Grist tables', + await errorCauses(response), + ); + } + return (await response.json()) as Promise; // Adjusted to return an array of Table objects + }; + fetchTables() + .then((response) => { + if (response) { + setTables(response.tables); + } + }) + .catch((error) => { + console.error('Error fetching Grist documents:', error); + }); + }, [documentId]); + + return { + tables, + }; +}; From a2399da2b78086ee81367422624008e3cb7d9f49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Di=20Domenico?= Date: Mon, 2 Jun 2025 18:49:14 +0200 Subject: [PATCH 10/85] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20null=20args=20handli?= =?UTF-8?q?ng=20in=20grist=20hooks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/DatabaseSourceSelector.tsx | 41 +++++--- .../apps/impress/src/features/grist/index.ts | 2 +- .../src/features/grist/useGristTable.ts | 98 ------------------- .../src/features/grist/useGristTableData.ts | 2 +- .../src/features/grist/useListGristDocs.ts | 2 +- .../src/features/grist/useListGristTables.ts | 11 +-- 6 files changed, 35 insertions(+), 121 deletions(-) delete mode 100644 src/frontend/apps/impress/src/features/grist/useGristTable.ts diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/DatabaseSourceSelector.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/DatabaseSourceSelector.tsx index c6cbc80ca..f7c29cfb9 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-editor/components/DatabaseSourceSelector.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/DatabaseSourceSelector.tsx @@ -8,12 +8,34 @@ type DatabaseSourceSelectorProps = { onSourceSelected: (args: { documentId: number; tableId: string }) => void; }; +const TableSelector = ({ + documentId, + onSourceSelected, +}: { documentId: number } & DatabaseSourceSelectorProps) => { + const { tables } = useListGristTables(documentId); + return tables ? ( + ({ + label: id, + value: id, + callback: () => onSourceSelected({ documentId, tableId: id }), + }))} + showArrow + > + Sélectionner une table Grist existante + + ) : ( + + No tables available + + ); +}; + export const DatabaseSourceSelector = ({ onSourceSelected, }: DatabaseSourceSelectorProps) => { const [selectedDoc, setSelectedDoc] = useState(); const { docs } = useListGristDocs(); - const { tables } = useListGristTables(selectedDoc?.id); return ( @@ -27,18 +49,11 @@ export const DatabaseSourceSelector = ({ > {selectedDoc?.name ?? 'Sélectionner un document Grist'} - {selectedDoc && tables && ( - ({ - label: id, - value: id, - callback: () => - onSourceSelected({ documentId: selectedDoc.id, tableId: id }), - }))} - showArrow - > - Sélectionner une table Grist existante - + {selectedDoc && ( + )} ); diff --git a/src/frontend/apps/impress/src/features/grist/index.ts b/src/frontend/apps/impress/src/features/grist/index.ts index 6640f5190..83f941593 100644 --- a/src/frontend/apps/impress/src/features/grist/index.ts +++ b/src/frontend/apps/impress/src/features/grist/index.ts @@ -1,3 +1,3 @@ -export * from './useGristTable'; +export * from './useGristTableData'; export * from './useListGristDocs'; export * from './useListGristTables'; diff --git a/src/frontend/apps/impress/src/features/grist/useGristTable.ts b/src/frontend/apps/impress/src/features/grist/useGristTable.ts deleted file mode 100644 index eae43614c..000000000 --- a/src/frontend/apps/impress/src/features/grist/useGristTable.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { useEffect, useState } from 'react'; - -import { APIError, errorCauses, gristFetchApi } from '@/api'; - -export type UseGristTableArguments = { documentId?: string; tableId?: string }; - -export const useGristTable = ({ - documentId, - tableId, -}: UseGristTableArguments) => { - const [tableData, setTableData] = useState([]); - - useEffect(() => { - const fetchData = async () => { - const url = `docs/${documentId}/tables/${tableId}/table`; - const response = await gristFetchApi(url); - if (!response.ok) { - throw new APIError( - 'Failed to request ai transform', - await errorCauses(response), - ); - } - return (await response.json()) as Promise; - }; - - const createDocument = async (workspaceId: string) => { - const url = `workspaces/${workspaceId}/docs`; - const response = await gristFetchApi(url, { - method: 'POST', - body: JSON.stringify({ name: 'New Doc' }), - }); - if (!response.ok) { - throw new APIError( - 'Failed to create Grist document', - await errorCauses(response), - ); - } - return (await response.json()) as Promise; - }; - - const createTable = async (documentId: string) => { - const url = `docs/${documentId}/tables`; - const response = await gristFetchApi(url, { - method: 'POST', - body: JSON.stringify({ - tables: [{ id: 'New Table', columns: [] }], - }), - }); - if (!response.ok) { - throw new APIError( - 'Failed to create Grist table', - await errorCauses(response), - ); - } - return (await response.json()) as Promise; - }; - - if (!documentId) { - const DEFAULT_WORKSPACE_ID = '2'; - createDocument(DEFAULT_WORKSPACE_ID) - .then((newDocumentId) => { - createTable(newDocumentId) - .then((newTableId) => { - console.log( - `Created new Grist document with ID: ${newDocumentId} and table ID: ${newTableId}`, - ); - }) - .catch((error) => { - console.error('Error creating Grist table:', error); - }); - }) - .catch((error) => { - console.error('Error creating Grist document:', error); - }); - } - - if (!tableId) { - createTable(documentId ?? '') - .then((newTableId) => { - console.log(`Created new Grist table with ID: ${newTableId}`); - }) - .catch((error) => { - console.error('Error creating Grist table:', error); - }); - } - - fetchData() - .then((res) => { - setTableData(res); - }) - .catch((error) => { - console.error('Error fetching Grist table data:', error); - }); - }, [documentId, tableId]); - return { - tableData, - }; -}; diff --git a/src/frontend/apps/impress/src/features/grist/useGristTableData.ts b/src/frontend/apps/impress/src/features/grist/useGristTableData.ts index 255b9e2c2..a075b2790 100644 --- a/src/frontend/apps/impress/src/features/grist/useGristTableData.ts +++ b/src/frontend/apps/impress/src/features/grist/useGristTableData.ts @@ -19,7 +19,7 @@ export const useGristTableData = ({ const response = await gristFetchApi(url); if (!response.ok) { throw new APIError( - 'Failed to request ai transform', + 'Failed to fetch Grist table data', await errorCauses(response), ); } diff --git a/src/frontend/apps/impress/src/features/grist/useListGristDocs.ts b/src/frontend/apps/impress/src/features/grist/useListGristDocs.ts index 1df7d7c57..9c97eba6b 100644 --- a/src/frontend/apps/impress/src/features/grist/useListGristDocs.ts +++ b/src/frontend/apps/impress/src/features/grist/useListGristDocs.ts @@ -43,7 +43,7 @@ export const useListGristDocs = (): { docs: Doc[] } => { const response = await gristFetchApi(url); if (!response.ok) { throw new APIError( - 'Failed to request ai transform', + 'Failed to fetch Grist documents', await errorCauses(response), ); } diff --git a/src/frontend/apps/impress/src/features/grist/useListGristTables.ts b/src/frontend/apps/impress/src/features/grist/useListGristTables.ts index 553b6e7ca..b45a6833c 100644 --- a/src/frontend/apps/impress/src/features/grist/useListGristTables.ts +++ b/src/frontend/apps/impress/src/features/grist/useListGristTables.ts @@ -17,16 +17,12 @@ export interface Fields { } export const useListGristTables = ( - documentId?: number, -): { tables: Table[] | null } => { - const [tables, setTables] = useState(null); + documentId: number, +): { tables: Table[] | undefined } => { + const [tables, setTables] = useState(); useEffect(() => { const fetchTables = async () => { - if (!documentId) { - console.warn('Document ID is required to fetch Grist tables'); - return; - } const url = `docs/${documentId}/tables`; const response = await gristFetchApi(url); if (!response.ok) { @@ -37,6 +33,7 @@ export const useListGristTables = ( } return (await response.json()) as Promise; // Adjusted to return an array of Table objects }; + fetchTables() .then((response) => { if (response) { From 2accc71bda747c5ce059ae1070e7bce3806706e1 Mon Sep 17 00:00:00 2001 From: anaisberg Date: Mon, 2 Jun 2025 16:24:00 +0200 Subject: [PATCH 11/85] =?UTF-8?q?=F0=9F=93=A6=20install=20ag-grid?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/frontend/apps/impress/package.json | 1 + src/frontend/yarn.lock | 69 ++++++++++++++++++++++---- 2 files changed, 60 insertions(+), 10 deletions(-) diff --git a/src/frontend/apps/impress/package.json b/src/frontend/apps/impress/package.json index c111d1a40..3ef4e7c4c 100644 --- a/src/frontend/apps/impress/package.json +++ b/src/frontend/apps/impress/package.json @@ -32,6 +32,7 @@ "@react-pdf/renderer": "4.3.0", "@sentry/nextjs": "9.22.0", "@tanstack/react-query": "5.77.1", + "ag-grid-react": "^33.3.1", "canvg": "4.0.3", "clsx": "2.1.1", "cmdk": "1.1.1", diff --git a/src/frontend/yarn.lock b/src/frontend/yarn.lock index 59ec4487c..ef93ac396 100644 --- a/src/frontend/yarn.lock +++ b/src/frontend/yarn.lock @@ -6514,13 +6514,20 @@ dependencies: "@types/node" "*" -"@types/node@*", "@types/node@22.10.7", "@types/node@22.15.21", "@types/node@^22.7.5": +"@types/node@*", "@types/node@^22.7.5": version "22.15.21" resolved "https://registry.yarnpkg.com/@types/node/-/node-22.15.21.tgz#196ef14fe20d87f7caf1e7b39832767f9a995b77" integrity sha512-EV/37Td6c+MgKAbkcLG6vqZ2zEYHD7bvSrzqqs2RIhbA6w3x+Dqz8MZM3sP6kGTeLrdoOgKZe+Xja7tUB2DNkQ== dependencies: undici-types "~6.21.0" +"@types/node@22.10.7": + version "22.10.7" + resolved "https://artifactory.laforge.cloud.bpifrance.fr/artifactory/api/npm/npm-all/@types/node/-/node-22.10.7.tgz#14a1ca33fd0ebdd9d63593ed8d3fbc882a6d28d7" + integrity sha512-V09KvXxFiutGp6B7XkpaDXlNadZxrzajcY50EuoLIpQ6WWYCSvf19lVIazzfIzQvhUN2HjX12spLojTnhuKlGg== + dependencies: + undici-types "~6.20.0" + "@types/parse-json@^4.0.0": version "4.0.2" resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.2.tgz#5950e50960793055845e956c427fc2b0d70c5239" @@ -6573,7 +6580,7 @@ resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.7.tgz#50ae4353eaaddc04044279812f52c8c65857dbcb" integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ== -"@types/react-dom@*", "@types/react-dom@19.1.5": +"@types/react-dom@*": version "19.1.5" resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-19.1.5.tgz#cdfe2c663742887372f54804b16e8dbc26bd794a" integrity sha512-CMCjrWucUBZvohgZxkjd6S9h0nZxXjzus6yDfUb+xLxYM7VvjKNH1tQrE9GWLql1XoOP4/Ds3bwFqShHUYraGg== @@ -6590,7 +6597,7 @@ resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.12.tgz#b5d76568485b02a307238270bfe96cb51ee2a044" integrity sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w== -"@types/react@*", "@types/react@19.1.5": +"@types/react@*": version "19.1.5" resolved "https://registry.yarnpkg.com/@types/react/-/react-19.1.5.tgz#9feb3bdeb506d0c79d8533b6ebdcacdbcb4756db" integrity sha512-piErsCVVbpMMT2r7wbawdZsq4xMvIAhQuac2gedQHysu1TZYEigE6pnFfgZT+/jQnrRuF5r+SHzuehFjfRjr4g== @@ -6703,7 +6710,7 @@ dependencies: "@types/yargs-parser" "*" -"@typescript-eslint/eslint-plugin@*", "@typescript-eslint/eslint-plugin@8.32.1", "@typescript-eslint/eslint-plugin@^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0": +"@typescript-eslint/eslint-plugin@*", "@typescript-eslint/eslint-plugin@^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0": version "8.32.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.32.1.tgz#9185b3eaa3b083d8318910e12d56c68b3c4f45b4" integrity sha512-6u6Plg9nP/J1GRpe/vcjjabo6Uc5YQPAMxsgQyGC/I0RuukiG1wIe3+Vtg3IrSCVJDmqK3j8adrtzXSENRtFgg== @@ -6718,7 +6725,7 @@ natural-compare "^1.4.0" ts-api-utils "^2.1.0" -"@typescript-eslint/parser@*", "@typescript-eslint/parser@8.32.1", "@typescript-eslint/parser@^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0": +"@typescript-eslint/parser@*", "@typescript-eslint/parser@^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0": version "8.32.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.32.1.tgz#18b0e53315e0bc22b2619d398ae49a968370935e" integrity sha512-LKMrmwCPoLhM45Z00O1ulb6jwyVr2kr3XJp+G+tSEZcbauNnScewcQwtJqXDhXeYPDEjZ8C1SjXm015CirEmGg== @@ -7055,6 +7062,26 @@ acorn@^8.1.0, acorn@^8.11.0, acorn@^8.14.0, acorn@^8.4.1, acorn@^8.8.1, acorn@^8 resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.1.tgz#721d5dc10f7d5b5609a891773d47731796935dfb" integrity sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg== +ag-charts-types@11.3.1: + version "11.3.1" + resolved "https://artifactory.laforge.cloud.bpifrance.fr/artifactory/api/npm/npm-all/ag-charts-types/-/ag-charts-types-11.3.1.tgz#5664cb61ecb3c8318e22bd1eb60cc4c4dfbb261e" + integrity sha512-kUSSypwsmkQDT5Oqgcr3zYQShzQuhvYeHaT1l2ocUrz3hBzkIiYwzp9CVG3Vs//M9TUCJLTcR0CBIr9ADhPITw== + +ag-grid-community@33.3.1: + version "33.3.1" + resolved "https://artifactory.laforge.cloud.bpifrance.fr/artifactory/api/npm/npm-all/ag-grid-community/-/ag-grid-community-33.3.1.tgz#598a3322dda944455d0ca69ce3a8f32403b0a0cb" + integrity sha512-c3JXKY5K5V5KVZ6rhFc13hK/8UzaZNwTqL70YKkqUbqwOcogDVVeG4I25W6o5YOlFO6LFN/5xFnFPASWn4LwBw== + dependencies: + ag-charts-types "11.3.1" + +ag-grid-react@^33.3.1: + version "33.3.1" + resolved "https://artifactory.laforge.cloud.bpifrance.fr/artifactory/api/npm/npm-all/ag-grid-react/-/ag-grid-react-33.3.1.tgz#b6c10e922b493c79408e09335bb30c91135a1aea" + integrity sha512-CHuuHxzuz2rsKuyxjuGnBadwm0/I2DCG4j1WgIa2wJkdlTUrcKjF6RD9qwfRLYKA4P40shG640emVuU6/qiZLA== + dependencies: + ag-grid-community "33.3.1" + prop-types "^15.8.1" + agent-base@6: version "6.0.2" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" @@ -9013,7 +9040,7 @@ eslint-visitor-keys@^4.2.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz#687bacb2af884fcdda8a6e7d65c606f46a14cd45" integrity sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw== -eslint@*, eslint@8.57.0: +eslint@*: version "8.57.0" resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.57.0.tgz#c786a6fd0e0b68941aaf624596fb987089195668" integrity sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ== @@ -13359,13 +13386,20 @@ react-dnd@^14.0.3: fast-deep-equal "^3.1.3" hoist-non-react-statics "^3.3.2" -react-dom@*, react-dom@19.0.0, react-dom@19.1.0: +react-dom@*: version "19.1.0" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-19.1.0.tgz#133558deca37fa1d682708df8904b25186793623" integrity sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g== dependencies: scheduler "^0.26.0" +react-dom@19.0.0: + version "19.0.0" + resolved "https://artifactory.laforge.cloud.bpifrance.fr/artifactory/api/npm/npm-all/react-dom/-/react-dom-19.0.0.tgz#43446f1f01c65a4cd7f7588083e686a6726cfb57" + integrity sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ== + dependencies: + scheduler "^0.25.0" + react-i18next@15.5.2: version "15.5.2" resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-15.5.2.tgz#2cfbd8e055efea077a7cbd7fbd9528c76d31925e" @@ -13593,11 +13627,16 @@ react-window@^1.8.11: "@babel/runtime" "^7.0.0" memoize-one ">=3.1.1 <6" -react@*, react@19.0.0, react@19.1.0: +react@*: version "19.1.0" resolved "https://registry.yarnpkg.com/react/-/react-19.1.0.tgz#926864b6c48da7627f004795d6cce50e90793b75" integrity sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg== +react@19.0.0: + version "19.0.0" + resolved "https://artifactory.laforge.cloud.bpifrance.fr/artifactory/api/npm/npm-all/react/-/react-19.0.0.tgz#6e1969251b9f108870aa4bff37a0ce9ddfaaabdd" + integrity sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ== + readable-stream@^3.4.0: version "3.6.2" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" @@ -14077,6 +14116,11 @@ scheduler@0.25.0-rc-603e6108-20241029: resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.25.0-rc-603e6108-20241029.tgz#684dd96647e104d23e0d29a37f18937daf82df19" integrity sha512-pFwF6H1XrSdYYNLfOcGlM28/j8CGLu8IvdrxqhjWULe2bPcKiKW4CV+OWqR/9fT52mywx65l7ysNkjLKBda7eA== +scheduler@^0.25.0: + version "0.25.0" + resolved "https://artifactory.laforge.cloud.bpifrance.fr/artifactory/api/npm/npm-all/scheduler/-/scheduler-0.25.0.tgz#336cd9768e8cceebf52d3c80e3dcf5de23e7e015" + integrity sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA== + scheduler@^0.26.0: version "0.26.0" resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.26.0.tgz#4ce8a8c2a2095f13ea11bf9a445be50c555d6337" @@ -15180,7 +15224,7 @@ typed-array-length@^1.0.7: possible-typed-array-names "^1.0.0" reflect.getprototypeof "^1.0.6" -typescript@*, typescript@5.8.3, typescript@^5.0.4: +typescript@*, typescript@^5.0.4: version "5.8.3" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.8.3.tgz#92f8a3e5e3cf497356f4178c34cd65a7f5e8440e" integrity sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ== @@ -15213,6 +15257,11 @@ underscore.string@~3.3.4: sprintf-js "^1.1.1" util-deprecate "^1.0.2" +undici-types@~6.20.0: + version "6.20.0" + resolved "https://artifactory.laforge.cloud.bpifrance.fr/artifactory/api/npm/npm-all/undici-types/-/undici-types-6.20.0.tgz#8171bf22c1f588d1554d55bf204bc624af388433" + integrity sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg== + undici-types@~6.21.0: version "6.21.0" resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.21.0.tgz#691d00af3909be93a7faa13be61b3a5b50ef12cb" @@ -16136,7 +16185,7 @@ yargs@17.7.2, yargs@^17.3.1: y18n "^5.0.5" yargs-parser "^21.1.1" -yjs@*, yjs@13.6.27, yjs@^13.6.15: +yjs@*, yjs@^13.6.15: version "13.6.27" resolved "https://registry.yarnpkg.com/yjs/-/yjs-13.6.27.tgz#8899be929d57da05a0aa112d044a5c204393ab7b" integrity sha512-OIDwaflOaq4wC6YlPBy2L6ceKeKuF7DeTxx+jPzv1FHn9tCZ0ZwSRnUBxD05E3yed46fv/FWJbvR+Ud7x0L7zw== From b5324fdda99af6d8f9e28ff35f6ae5db88bea34c Mon Sep 17 00:00:00 2001 From: anaisberg Date: Mon, 2 Jun 2025 16:25:18 +0200 Subject: [PATCH 12/85] =?UTF-8?q?=E2=9C=A8=20display=20hardcoded=20grid?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../doc-editor/components/BlockNoteEditor.tsx | 3 ++ .../custom-blocks/DatabaseBlock.tsx | 32 +++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteEditor.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteEditor.tsx index 9d30d958f..f45c9b6c2 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteEditor.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteEditor.tsx @@ -10,6 +10,7 @@ import { BlockNoteView } from '@blocknote/mantine'; import '@blocknote/mantine/style.css'; import { useCreateBlockNote } from '@blocknote/react'; import { HocuspocusProvider } from '@hocuspocus/provider'; +import { AllCommunityModule, ModuleRegistry } from 'ag-grid-community'; import { useEffect } from 'react'; import { useTranslation } from 'react-i18next'; import * as Y from 'yjs'; @@ -29,6 +30,8 @@ import { BlockNoteSuggestionMenu } from './BlockNoteSuggestionMenu'; import { BlockNoteToolbar } from './BlockNoteToolBar/BlockNoteToolbar'; import { CalloutBlock, DatabaseBlock, DividerBlock } from './custom-blocks'; +ModuleRegistry.registerModules([AllCommunityModule]); + export const blockNoteSchema = withPageBreak( BlockNoteSchema.create({ blockSpecs: { diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock.tsx index db723110c..5846bd243 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock.tsx @@ -2,7 +2,10 @@ import { defaultProps, insertOrUpdateBlock } from '@blocknote/core'; import { BlockTypeSelectItem, createReactBlockSpec } from '@blocknote/react'; import { Button } from '@openfun/cunningham-react'; +import { ColDef } from 'ag-grid-community'; +import { AgGridReact } from 'ag-grid-react'; import { TFunction } from 'i18next'; +import React, { useRef, useState } from 'react'; import { Box, Icon, Text } from '@/components'; @@ -28,6 +31,25 @@ export const DatabaseBlock = createReactBlockSpec( }, { render: ({ block, editor }) => { + const gridRef = useRef(null); + + const [rowData, setRowData] = useState([ + { make: 'Tesla', model: 'Model Y', price: 64950, electric: true }, + { make: 'Ford', model: 'F-Series', price: 33850, electric: false }, + { make: 'Toyota', model: 'Corolla', price: 29600, electric: false }, + ]); + + // Column Definitions: Defines the columns to be displayed. + const [colDefs, setColDefs] = useState([ + { field: 'make' } as const, + { field: 'model' } as const, + { field: 'price' } as const, + { field: 'electric' } as const, + ]); + + const defaultColDef = { + flex: 1, + }; return ( @@ -44,6 +68,14 @@ export const DatabaseBlock = createReactBlockSpec( documentId={block.props.documentId} tableId={block.props.tableId} /> + + + ) : ( Date: Mon, 2 Jun 2025 16:32:48 +0200 Subject: [PATCH 13/85] =?UTF-8?q?=E2=9C=A8=20sort=20columns?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/custom-blocks/DatabaseBlock.tsx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock.tsx index 5846bd243..bc69dae1a 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock.tsx @@ -41,10 +41,13 @@ export const DatabaseBlock = createReactBlockSpec( // Column Definitions: Defines the columns to be displayed. const [colDefs, setColDefs] = useState([ - { field: 'make' } as const, - { field: 'model' } as const, - { field: 'price' } as const, - { field: 'electric' } as const, + { field: 'make', unSortIcon: true, sort: 'desc' }, + { + field: 'model', + unSortIcon: true, + }, + { field: 'price', unSortIcon: true }, + { field: 'electric' }, ]); const defaultColDef = { From 376dad247af5eaf1fda0cd0d9954d7c209c97ff6 Mon Sep 17 00:00:00 2001 From: anaisberg Date: Mon, 2 Jun 2025 16:37:50 +0200 Subject: [PATCH 14/85] =?UTF-8?q?=E2=9C=A8=20filter=20columns?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../docs/doc-editor/components/custom-blocks/DatabaseBlock.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock.tsx index bc69dae1a..e34669d14 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock.tsx @@ -45,8 +45,9 @@ export const DatabaseBlock = createReactBlockSpec( { field: 'model', unSortIcon: true, + filter: true, }, - { field: 'price', unSortIcon: true }, + { field: 'price', unSortIcon: true, filter: true }, { field: 'electric' }, ]); From 64482417e49309bb3ea3170734113b21c8d2d511 Mon Sep 17 00:00:00 2001 From: anaisberg Date: Mon, 2 Jun 2025 16:41:41 +0200 Subject: [PATCH 15/85] =?UTF-8?q?=E2=9C=A8=20edit=20cells?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../docs/doc-editor/components/custom-blocks/DatabaseBlock.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock.tsx index e34669d14..673f7e696 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock.tsx @@ -53,6 +53,7 @@ export const DatabaseBlock = createReactBlockSpec( const defaultColDef = { flex: 1, + editable: true, }; return ( Date: Mon, 2 Jun 2025 16:59:00 +0200 Subject: [PATCH 16/85] =?UTF-8?q?=F0=9F=92=84=20automatic=20height?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../docs/doc-editor/components/custom-blocks/DatabaseBlock.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock.tsx index 673f7e696..e9a3871e8 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock.tsx @@ -62,7 +62,6 @@ export const DatabaseBlock = createReactBlockSpec( style={{ flexGrow: 1, flexDirection: 'row', - height: '500px', width: '100%', }} > @@ -79,6 +78,7 @@ export const DatabaseBlock = createReactBlockSpec( rowData={rowData} columnDefs={colDefs} defaultColDef={defaultColDef} + domLayout="autoHeight" /> From 53899821cae5b29b1b803b22b2ced3fa709ea3de Mon Sep 17 00:00:00 2001 From: anaisberg Date: Mon, 2 Jun 2025 17:11:16 +0200 Subject: [PATCH 17/85] =?UTF-8?q?=E2=9C=A8=20always=20show=20unsort=20icon?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../doc-editor/components/custom-blocks/DatabaseBlock.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock.tsx index e9a3871e8..6ad866a3b 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock.tsx @@ -41,19 +41,19 @@ export const DatabaseBlock = createReactBlockSpec( // Column Definitions: Defines the columns to be displayed. const [colDefs, setColDefs] = useState([ - { field: 'make', unSortIcon: true, sort: 'desc' }, + { field: 'make', sort: 'desc' }, { field: 'model', - unSortIcon: true, filter: true, }, - { field: 'price', unSortIcon: true, filter: true }, + { field: 'price', filter: true }, { field: 'electric' }, ]); const defaultColDef = { flex: 1, editable: true, + unSortIcon: true, }; return ( Date: Mon, 2 Jun 2025 18:31:22 +0200 Subject: [PATCH 18/85] =?UTF-8?q?=E2=9C=A8=20add=20column=20button?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../custom-blocks/DatabaseBlock.tsx | 67 ++++++++++++++++++- 1 file changed, 65 insertions(+), 2 deletions(-) diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock.tsx index 6ad866a3b..656adc491 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock.tsx @@ -1,13 +1,13 @@ /* eslint-disable react-hooks/rules-of-hooks */ import { defaultProps, insertOrUpdateBlock } from '@blocknote/core'; import { BlockTypeSelectItem, createReactBlockSpec } from '@blocknote/react'; -import { Button } from '@openfun/cunningham-react'; +import { Button, Input } from '@openfun/cunningham-react'; import { ColDef } from 'ag-grid-community'; import { AgGridReact } from 'ag-grid-react'; import { TFunction } from 'i18next'; import React, { useRef, useState } from 'react'; -import { Box, Icon, Text } from '@/components'; +import { Box, DropButton, Icon, Text } from '@/components'; import { DocsBlockNoteEditor } from '../../types'; import { DatabaseSourceSelector } from '../DatabaseSourceSelector'; @@ -31,6 +31,8 @@ export const DatabaseBlock = createReactBlockSpec( }, { render: ({ block, editor }) => { + const [addColumnToggleOpen, setAddColumnToggleOpen] = useState(false); + const gridRef = useRef(null); const [rowData, setRowData] = useState([ @@ -39,6 +41,53 @@ export const DatabaseBlock = createReactBlockSpec( { make: 'Toyota', model: 'Corolla', price: 29600, electric: false }, ]); + const AddButtonComponent = ({ + isOpen, + setIsOpen, + }: { + isOpen: boolean; + setIsOpen: (open: boolean) => void; + }) => { + const onOpenChange = (isOpen: boolean) => { + setIsOpen(isOpen); + }; + + return ( + + add + + } + > + + + Ajouter une colonne + + + + + + ); + }; + // Column Definitions: Defines the columns to be displayed. const [colDefs, setColDefs] = useState([ { field: 'make', sort: 'desc' }, @@ -48,6 +97,19 @@ export const DatabaseBlock = createReactBlockSpec( }, { field: 'price', filter: true }, { field: 'electric' }, + { + headerComponentParams: { + innerHeaderComponent: () => + AddButtonComponent({ + isOpen: addColumnToggleOpen, + setIsOpen: setAddColumnToggleOpen, + }), + }, + unSortIcon: false, + editable: false, + sortable: false, + spanRows: true, + }, ]); const defaultColDef = { @@ -79,6 +141,7 @@ export const DatabaseBlock = createReactBlockSpec( columnDefs={colDefs} defaultColDef={defaultColDef} domLayout="autoHeight" + enableCellSpan={true} /> From 09132a49016dd4b8aa4dbc67e04afbc1ff426c11 Mon Sep 17 00:00:00 2001 From: anaisberg Date: Mon, 2 Jun 2025 18:36:49 +0200 Subject: [PATCH 19/85] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20extract=20add=20colu?= =?UTF-8?q?mn=20button=20component?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../custom-blocks/DatabaseBlock.tsx | 52 ++----------------- .../DatabaseBlock/AddColumnButton.tsx | 45 ++++++++++++++++ 2 files changed, 48 insertions(+), 49 deletions(-) create mode 100644 src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock/AddColumnButton.tsx diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock.tsx index 656adc491..683ec2b1e 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock.tsx @@ -1,18 +1,19 @@ /* eslint-disable react-hooks/rules-of-hooks */ import { defaultProps, insertOrUpdateBlock } from '@blocknote/core'; import { BlockTypeSelectItem, createReactBlockSpec } from '@blocknote/react'; -import { Button, Input } from '@openfun/cunningham-react'; import { ColDef } from 'ag-grid-community'; import { AgGridReact } from 'ag-grid-react'; import { TFunction } from 'i18next'; import React, { useRef, useState } from 'react'; -import { Box, DropButton, Icon, Text } from '@/components'; +import { Box, Icon } from '@/components'; import { DocsBlockNoteEditor } from '../../types'; import { DatabaseSourceSelector } from '../DatabaseSourceSelector'; import { DatabaseTableDisplay } from '../DatabaseTableDisplay'; +import { AddButtonComponent } from './DatabaseBlock/AddColumnButton'; + export const DatabaseBlock = createReactBlockSpec( { type: 'database', @@ -41,53 +42,6 @@ export const DatabaseBlock = createReactBlockSpec( { make: 'Toyota', model: 'Corolla', price: 29600, electric: false }, ]); - const AddButtonComponent = ({ - isOpen, - setIsOpen, - }: { - isOpen: boolean; - setIsOpen: (open: boolean) => void; - }) => { - const onOpenChange = (isOpen: boolean) => { - setIsOpen(isOpen); - }; - - return ( - - add - - } - > - - - Ajouter une colonne - - - - - - ); - }; - // Column Definitions: Defines the columns to be displayed. const [colDefs, setColDefs] = useState([ { field: 'make', sort: 'desc' }, diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock/AddColumnButton.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock/AddColumnButton.tsx new file mode 100644 index 000000000..b7bfa97d0 --- /dev/null +++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock/AddColumnButton.tsx @@ -0,0 +1,45 @@ +import { Button, Input } from '@openfun/cunningham-react'; + +import { Box, DropButton, Text } from '@/components'; + +export const AddButtonComponent = ({ + isOpen, + setIsOpen, +}: { + isOpen: boolean; + setIsOpen: (open: boolean) => void; +}) => { + const onOpenChange = (isOpen: boolean) => { + setIsOpen(isOpen); + }; + + return ( + + add + + } + > + + + Ajouter une colonne + + + + + + ); +}; From 33379c40ef571daa8be5dc8b9d9d811c2b9585a4 Mon Sep 17 00:00:00 2001 From: anaisberg Date: Mon, 2 Jun 2025 18:50:17 +0200 Subject: [PATCH 20/85] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20extract=20grid=20com?= =?UTF-8?q?ponent?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../custom-blocks/DatabaseBlock.tsx | 57 ++------------ .../DatabaseBlock/DatabaseGrid.tsx | 75 +++++++++++++++++++ 2 files changed, 80 insertions(+), 52 deletions(-) create mode 100644 src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock/DatabaseGrid.tsx diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock.tsx index 683ec2b1e..023f56e8b 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock.tsx @@ -1,18 +1,17 @@ /* eslint-disable react-hooks/rules-of-hooks */ import { defaultProps, insertOrUpdateBlock } from '@blocknote/core'; import { BlockTypeSelectItem, createReactBlockSpec } from '@blocknote/react'; -import { ColDef } from 'ag-grid-community'; -import { AgGridReact } from 'ag-grid-react'; +import { Button } from '@openfun/cunningham-react'; import { TFunction } from 'i18next'; -import React, { useRef, useState } from 'react'; +import React from 'react'; -import { Box, Icon } from '@/components'; +import { Box, Icon, Text } from '@/components'; import { DocsBlockNoteEditor } from '../../types'; import { DatabaseSourceSelector } from '../DatabaseSourceSelector'; import { DatabaseTableDisplay } from '../DatabaseTableDisplay'; -import { AddButtonComponent } from './DatabaseBlock/AddColumnButton'; +import { DatabaseGrid } from './DatabaseBlock/DatabaseGrid'; export const DatabaseBlock = createReactBlockSpec( { @@ -32,45 +31,6 @@ export const DatabaseBlock = createReactBlockSpec( }, { render: ({ block, editor }) => { - const [addColumnToggleOpen, setAddColumnToggleOpen] = useState(false); - - const gridRef = useRef(null); - - const [rowData, setRowData] = useState([ - { make: 'Tesla', model: 'Model Y', price: 64950, electric: true }, - { make: 'Ford', model: 'F-Series', price: 33850, electric: false }, - { make: 'Toyota', model: 'Corolla', price: 29600, electric: false }, - ]); - - // Column Definitions: Defines the columns to be displayed. - const [colDefs, setColDefs] = useState([ - { field: 'make', sort: 'desc' }, - { - field: 'model', - filter: true, - }, - { field: 'price', filter: true }, - { field: 'electric' }, - { - headerComponentParams: { - innerHeaderComponent: () => - AddButtonComponent({ - isOpen: addColumnToggleOpen, - setIsOpen: setAddColumnToggleOpen, - }), - }, - unSortIcon: false, - editable: false, - sortable: false, - spanRows: true, - }, - ]); - - const defaultColDef = { - flex: 1, - editable: true, - unSortIcon: true, - }; return ( - + ) : ( diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock/DatabaseGrid.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock/DatabaseGrid.tsx new file mode 100644 index 000000000..f3f83973d --- /dev/null +++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock/DatabaseGrid.tsx @@ -0,0 +1,75 @@ +import { ColDef } from 'ag-grid-community'; +import { AgGridReact } from 'ag-grid-react'; +import { useRef, useState } from 'react'; + +import { Box } from '@/components'; + +import { AddButtonComponent } from './AddColumnButton'; + +export const DatabaseGrid = () => { + const gridRef = useRef(null); + const [addColumnToggleOpen, setAddColumnToggleOpen] = useState(false); + + const [rowData, setRowData] = useState([ + { make: 'Tesla', model: 'Model Y', price: 64950, electric: true }, + { make: 'Ford', model: 'F-Series', price: 33850, electric: false }, + { make: 'Toyota', model: 'Corolla', price: 29600, electric: false }, + ]); + + // Column Definitions: Defines the columns to be displayed. + const [colDefs, setColDefs] = useState([ + { field: 'make', sort: 'desc' }, + { + field: 'model', + }, + { field: 'price' }, + { field: 'electric' }, + { + headerComponentParams: { + innerHeaderComponent: () => + AddButtonComponent({ + addColumn, + isOpen: addColumnToggleOpen, + setIsOpen: setAddColumnToggleOpen, + }), + }, + unSortIcon: false, + editable: false, + sortable: false, + spanRows: true, + filter: false, + }, + ]); + + const defaultColDef = { + flex: 1, + filter: true, + editable: true, + unSortIcon: true, + }; + + const addColumn = (columnName: string) => { + const newColDef: ColDef = { + field: columnName, + }; + const addColumn = colDefs.pop(); + setColDefs((prev) => [...prev, newColDef]); + if (addColumn !== undefined) { + setColDefs((prev) => [...prev, addColumn]); + } + setAddColumnToggleOpen(false); + }; + + return ( + + + + ); +}; From bd2d1b745ebbd164317146a36b6f3f86718ef208 Mon Sep 17 00:00:00 2001 From: anaisberg Date: Mon, 2 Jun 2025 18:50:31 +0200 Subject: [PATCH 21/85] =?UTF-8?q?=E2=9C=A8=20add=20column=20to=20table?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DatabaseBlock/AddColumnButton.tsx | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock/AddColumnButton.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock/AddColumnButton.tsx index b7bfa97d0..2ac5bc5f5 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock/AddColumnButton.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/DatabaseBlock/AddColumnButton.tsx @@ -1,17 +1,21 @@ import { Button, Input } from '@openfun/cunningham-react'; +import { useState } from 'react'; import { Box, DropButton, Text } from '@/components'; export const AddButtonComponent = ({ + addColumn, isOpen, setIsOpen, }: { + addColumn: (columnName: string) => void; isOpen: boolean; setIsOpen: (open: boolean) => void; }) => { const onOpenChange = (isOpen: boolean) => { setIsOpen(isOpen); }; + const [columnName, setColumnName] = useState(''); return ( Ajouter une colonne - + { + setColumnName(event.target.value); + }} + >