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
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"@refinedev/supabase": "^5.9.8",
"@supabase/supabase-js": "^2.50.3",
"@tanstack/react-query": "^4.10.1",
"@uiw/react-md-editor": "^4.0.8",
"antd": "^5.26.3",
"dayjs": "^1.10.7",
"i18next": "^25.3.1",
Expand All @@ -33,7 +34,9 @@
"react": "^18.0.0",
"react-dom": "^18.0.0",
"react-i18next": "^15.6.0",
"react-router": "^7.6.3"
"react-markdown": "^10.1.0",
Copy link

Copilot AI Nov 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The explicit dependency on react-markdown@^10.1.0 may conflict with @uiw/react-md-editor which internally uses react-markdown@~9.0.1 (as seen in yarn.lock line 6787). This could lead to multiple versions of react-markdown being installed.

Consider removing the explicit react-markdown dependency from package.json and rely on the version that comes with @uiw/react-md-editor, or ensure version compatibility. If you need react-markdown@^10.1.0 for the MarkdownRender component separately, document why both versions are needed.

Suggested change
"react-markdown": "^10.1.0",

Copilot uses AI. Check for mistakes.
"react-router": "^7.6.3",
"rehype-sanitize": "^6.0.0"
},
"devDependencies": {
"@biomejs/biome": "2.1.1",
Expand Down
3 changes: 2 additions & 1 deletion src/equipments/components/equipment-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import dayjs from 'dayjs';
import type { FC } from 'react';

import { BoatSystemSelect } from '@/boats/components/boat-system-select';
import { MarkdownEditor } from '@/shared/components/markdown-editor';

interface EquipmentFormProps {
formProps: any;
Expand Down Expand Up @@ -58,7 +59,7 @@ const EquipmentForm: FC<EquipmentFormProps> = ({
label={translate('equipments.form.labels.description')}
name="description"
>
<Input.TextArea rows={4} />
<MarkdownEditor />
</Form.Item>
<Form.Item
label={translate('equipments.form.labels.warranty_end_date')}
Expand Down
13 changes: 9 additions & 4 deletions src/equipments/pages/show.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { useParams } from 'react-router';

import { useCurrentBoat } from '@/boats/hooks/use-current-boat';
import { AttachmentList } from '@/shared/components/attachment-list';
import { MarkdownRender } from '@/shared/components/markdown-render';
import { PageHeader } from '@/shared/components/page-header';

const ShowEquipment = () => {
Expand Down Expand Up @@ -36,10 +37,6 @@ const ShowEquipment = () => {
: null}
{equipment?.data.name}
</Typography.Paragraph>
<Typography.Paragraph>
<strong>{translate('equipments.form.labels.description')}: </strong>
{equipment?.data.description}
</Typography.Paragraph>
<Typography.Paragraph>
<strong>{translate('equipments.form.labels.system')}: </strong>
{translate(`boats.systems.list.${equipment?.data.system_key}.name`)}
Expand Down Expand Up @@ -104,6 +101,14 @@ const ShowEquipment = () => {
{dayjs(equipment.data.warranty_end_date).format('DD/MM/YYYY')}
</Typography.Paragraph>
) : null}
{equipment?.data.description ? (
<>
<Typography.Title level={5}>
{translate('equipments.form.labels.description')}:
</Typography.Title>
<MarkdownRender content={equipment?.data.description} />
</>
) : null}
</Card>
<AttachmentList
resource="equipment"
Expand Down
4 changes: 3 additions & 1 deletion src/interventions/components/intervention-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import dayjs from 'dayjs';
import type { FC } from 'react';
import { useState } from 'react';

import { MarkdownEditor } from '@/shared/components/markdown-editor';

interface InterventionFormProps {
formProps: any;
handleOnFinish: (values: any) => void;
Expand Down Expand Up @@ -48,7 +50,7 @@ const InterventionForm: FC<InterventionFormProps> = ({
label={translate('interventions.form.labels.description')}
name="description"
>
<Input.TextArea />
<MarkdownEditor />
</Form.Item>

<Form.Item
Expand Down
17 changes: 9 additions & 8 deletions src/interventions/pages/show.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { useParams } from 'react-router';

import { useCurrentBoat } from '@/boats/hooks/use-current-boat';
import { AttachmentList } from '@/shared/components/attachment-list';
import { MarkdownRender } from '@/shared/components/markdown-render';
import { PageHeader } from '@/shared/components/page-header';
import { getCostCalculationString } from '../utils/cost';

Expand Down Expand Up @@ -37,14 +38,6 @@ const ShowIntervention = () => {
<strong>{translate('interventions.form.labels.title')}: </strong>
{intervention?.data.title}
</Typography.Paragraph>
{intervention?.data.description ? (
<Typography.Paragraph>
<strong>
{translate('interventions.form.labels.description')}:
</strong>
{intervention.data.description}
</Typography.Paragraph>
) : null}
{intervention?.data.total_cost ? (
<>
<Typography.Paragraph style={{ marginBottom: 0 }}>
Expand All @@ -61,6 +54,14 @@ const ShowIntervention = () => {
</Typography.Paragraph>
</>
) : null}
{intervention?.data.description ? (
<>
<Typography.Title level={5}>
{translate('interventions.form.labels.description')}:
</Typography.Title>
<MarkdownRender content={intervention.data.description} />
</>
) : null}
</Card>
<AttachmentList
resource="intervention"
Expand Down
55 changes: 55 additions & 0 deletions src/shared/components/markdown-editor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import MDEditor, { commands } from '@uiw/react-md-editor';
import type React from 'react';
import rehypeSanitize from 'rehype-sanitize';

interface MarkdownEditorProps {
value?: string;
onChange?: (value: string | undefined) => void;
id?: string;
}

Copy link

Copilot AI Nov 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Consider adding JSDoc comments to document:

  1. What markdown features are supported (bold, italic, strikethrough, links)
  2. The sanitization being applied via rehypeSanitize
  3. Any usage examples or limitations

Example:

/**
 * A markdown editor component with sanitization support.
 * 
 * Supports the following markdown features:
 * - Bold text
 * - Italic text
 * - Strikethrough
 * - Links
 * 
 * Content is sanitized using rehype-sanitize to prevent XSS attacks.
 */
Suggested change
/**
* A markdown editor component with sanitization support.
*
* Supports the following markdown features:
* - Bold text
* - Italic text
* - Strikethrough
* - Links
*
* Content is sanitized using rehype-sanitize to prevent XSS attacks.
*
* Usage example:
* ```tsx
* <MarkdownEditor value={markdown} onChange={setMarkdown} />
* ```
*
* Limitations:
* - Only the listed markdown features are supported.
* - Other markdown features (e.g., tables, code blocks) may not be available.
*/

Copilot uses AI. Check for mistakes.
/**
* A markdown editor component with sanitization support.
*
* Supports the following markdown features:
* - Bold text
* - Italic text
* - Strikethrough
* - Links
*
* Content is sanitized using rehype-sanitize to prevent XSS attacks.
*
* Usage example:
* ```tsx
* <MarkdownEditor value={markdown} onChange={setMarkdown} />
* ```
*
* Limitations:
* - Only the listed markdown features are supported.
* - Other markdown features (e.g., tables, code blocks) may not be available.
*/
const MarkdownEditor: React.FC<MarkdownEditorProps> = ({
id,
value,
onChange,
}) => {
return (
<MDEditor
id={id}
value={value}
onChange={onChange}
data-color-mode="light"
commands={[
commands.bold,
commands.italic,
commands.strikethrough,
commands.link,
]}
previewOptions={{
rehypePlugins: [[rehypeSanitize]],
}}
/>
);
};

export { MarkdownEditor };
26 changes: 26 additions & 0 deletions src/shared/components/markdown-render.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import type React from 'react';
import ReactMarkdown from 'react-markdown';
import rehypeSanitize from 'rehype-sanitize';

interface MarkdownRenderProps {
content?: string;
}

Copy link

Copilot AI Nov 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Consider adding JSDoc comments to document the component's purpose and any limitations:

/**
 * Renders markdown content as HTML.
 * 
 * @param content - The markdown string to render. Can be undefined.
 * 
 * @remarks
 * This component uses react-markdown to safely parse and render markdown content.
 * It's companion to the MarkdownEditor component.
 */
Suggested change
/**
* Renders markdown content as HTML.
*
* @param content - The markdown string to render. Can be undefined.
*
* @remarks
* This component uses react-markdown to safely parse and render markdown content.
* It's companion to the MarkdownEditor component.
*/

Copilot uses AI. Check for mistakes.
/**
* Renders markdown content as HTML.
*
* @param content - The markdown string to render. Can be undefined.
*
* @remarks
* This component uses react-markdown to safely parse and render markdown content.
* It's companion to the MarkdownEditor component.
*/
const MarkdownRender: React.FC<MarkdownRenderProps> = ({ content }) => {
return (
<ReactMarkdown rehypePlugins={[rehypeSanitize]}>
{content || ''}
</ReactMarkdown>
);
};

export { MarkdownRender };
Loading