Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Améliorer la saisie avec un éditeur WYSIWYG #796

Open
wants to merge 25 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
6483124
Add Tiptap and a first integration in NotesModal
yaaax Aug 8, 2024
20632e8
Fix markdown content recovery
yaaax Aug 9, 2024
31ce9e5
Set default code block language to html
yaaax Aug 16, 2024
fee1c1a
Fix eslint imports
yaaax Aug 16, 2024
5dee6e3
Fix eslint finally
yaaax Aug 16, 2024
1250bda
Fix tiptap code blocks style
yaaax Aug 17, 2024
ecd65d7
Fix eslint warning (type)
yaaax Aug 26, 2024
729ff78
Factorize code in AuditService
yaaax Sep 8, 2024
d9932b1
Add display field for uploads
yaaax Sep 9, 2024
9f3a245
Tiptap: handle images drag and drop
yaaax Sep 13, 2024
ab88c6a
refactor(tiptap): simplify code
yaaax Oct 9, 2024
c74754a
Titap: prevent layout shift on image load
yaaax Oct 14, 2024
81b2798
Titap: fix and improve image style
yaaax Oct 14, 2024
7f5468e
Ajout de la config de debug globale
yaaax Oct 21, 2024
a2d6592
Titap: fix drop cursor and image position when dropped
yaaax Oct 21, 2024
52e1c6c
Titap: handle multiple image drop
yaaax Oct 21, 2024
8189818
Attachment: fix error 500
yaaax Oct 23, 2024
0934488
Tiptap: improve accessibility
yaaax Oct 23, 2024
c869c50
Image upload: add error message
yaaax Oct 23, 2024
9d32502
Minor typo: Tiptap instead of TipTap
yaaax Oct 24, 2024
7981839
Tiptap: improve drag and drop + upload
yaaax Oct 24, 2024
8926b1b
Add upload/delete timeout error messages
yaaax Oct 28, 2024
b7cbb38
Improve error messages (avoid displaying null)
yaaax Oct 28, 2024
c990515
Tiptap: improve drop cursor style
yaaax Oct 28, 2024
9b59f25
Tiptap: handle and fix drop + paste
yaaax Oct 28, 2024
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
40 changes: 40 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"version": "0.2.0",
"configurations": [
// Frontend (Chrome)
{
"type": "chrome",
"request": "launch",
"name": "FRONT (Vue.js) – Chrome",
"url": "http://localhost:3000",
"webRoot": "${workspaceFolder}/confiture-web-app/src",
"sourceMapPathOverrides": {
"webpack:///src/*": "${webRoot}/*"
}
},
// Frontend (Chrome)
{
"type": "firefox",
"request": "launch",
"name": "FRONT (Vue.js) – Firefox",
"url": "http://localhost:3000",
"webRoot": "${workspaceFolder}/confiture-web-app/src",
"pathMappings": [
{ "url": "webpack:///confiture-web-app/src/", "path": "${webRoot}/" }
]
},
// Backend (REST API)
{
"type": "node",
"request": "launch",
"name": "BACKEND (nest)",
"runtimeExecutable": "npm",
"runtimeArgs": ["run", "start:debug", "--", "--inspect-brk"],
"autoAttachChildProcesses": true,
"restart": true,
"sourceMaps": true,
"stopOnEntry": false,
"console": "integratedTerminal"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
-- CreateEnum
CREATE TYPE "FileDisplay" AS ENUM ('EDITOR', 'ATTACHMENT');

-- AlterTable
ALTER TABLE "AuditFile" ADD COLUMN "display" "FileDisplay" NOT NULL DEFAULT 'ATTACHMENT';

-- AlterTable
ALTER TABLE "StoredFile" ADD COLUMN "display" "FileDisplay" NOT NULL DEFAULT 'ATTACHMENT';
10 changes: 10 additions & 0 deletions confiture-rest-api/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,10 @@ model AuditTrace {
Audit Audit?
}

enum FileDisplay {
EDITOR
ATTACHMENT
}
model StoredFile {
id Int @id @default(autoincrement())
originalFilename String
Expand All @@ -178,6 +182,9 @@ model StoredFile {
key String
thumbnailKey String

// Inside Tiptap editor (EDITOR) or added as an attachment (ATTACHMENT)?
display FileDisplay @default(ATTACHMENT)

criterionResult CriterionResult? @relation(fields: [criterionResultId], references: [id], onDelete: Cascade, onUpdate: Cascade)
criterionResultId Int?
}
Expand All @@ -197,6 +204,9 @@ model AuditFile {
key String
thumbnailKey String?

// Inside TipTap editor (EDITOR) or added as an attachment (ATTACHMENT)?
display FileDisplay @default(ATTACHMENT)

audit Audit? @relation(fields: [auditUniqueId], references: [editUniqueId], onDelete: Cascade)
auditUniqueId String?
}
Expand Down
111 changes: 59 additions & 52 deletions confiture-rest-api/src/audits/audit.service.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { Injectable } from "@nestjs/common";
import {
Audit,
AuditFile,
AuditedPage,
CriterionResult,
CriterionResultStatus,
CriterionResultUserImpact,
AuditFile,
FileDisplay,
Prisma,
StoredFile,
TestEnvironment
Expand Down Expand Up @@ -445,30 +446,14 @@ export class AuditService {
pageId: number,
topic: number,
criterium: number,
file: Express.Multer.File
file: Express.Multer.File,
display: FileDisplay = FileDisplay.ATTACHMENT
) {
const randomPrefix = nanoid();

const key = `audits/${editUniqueId}/${randomPrefix}/${file.originalname}`;

const thumbnailKey = `audits/${editUniqueId}/${randomPrefix}/thumbnail_${file.originalname}`;

const thumbnailBuffer = await sharp(file.buffer)
.jpeg({
mozjpeg: true
})
.flatten({ background: { r: 255, g: 255, b: 255, alpha: 0 } })
.resize(200, 200, { fit: "inside" })
.toBuffer();

await Promise.all([
this.fileStorageService.uploadFile(file.buffer, file.mimetype, key),
this.fileStorageService.uploadFile(
thumbnailBuffer,
"image/jpeg",
thumbnailKey
)
]);
const { key, thumbnailKey } = await this.uploadFileToStorage(
editUniqueId,
file,
{ createThumbnail: display === FileDisplay.ATTACHMENT }
);

const storedFile = await this.prisma.storedFile.create({
data: {
Expand All @@ -486,7 +471,8 @@ export class AuditService {
originalFilename: file.originalname,
mimetype: file.mimetype,
size: file.size,
thumbnailKey
thumbnailKey,
display
}
});

Expand Down Expand Up @@ -527,22 +513,59 @@ export class AuditService {
return true;
}

async saveNotesFile(editUniqueId: string, file: Express.Multer.File) {
async saveNotesFile(
editUniqueId: string,
file: Express.Multer.File,
display: FileDisplay = FileDisplay.ATTACHMENT
) {
const { key, thumbnailKey } = await this.uploadFileToStorage(
editUniqueId,
file,
{ createThumbnail: display === FileDisplay.ATTACHMENT }
);

const storedFile = await this.prisma.auditFile.create({
data: {
audit: {
connect: {
editUniqueId
}
},

key,
originalFilename: file.originalname,
mimetype: file.mimetype,
size: file.size,

thumbnailKey,
display
}
});

return storedFile;
}

async uploadFileToStorage(
uniqueId: string,
file: Express.Multer.File,
options?: { createThumbnail: boolean }
): Promise<{ key: string; thumbnailKey?: string }> {
const randomPrefix = nanoid();

const key = `audits/${editUniqueId}/${randomPrefix}/${file.originalname}`;
const key: string = `audits/${uniqueId}/${randomPrefix}/${file.originalname}`;

let thumbnailKey;
let thumbnailKey: string;

if (file.mimetype.startsWith("image")) {
if (file.mimetype.startsWith("image") && options.createThumbnail) {
// If it's an image, create a thumbnail and upload it
thumbnailKey = `audits/${editUniqueId}/${randomPrefix}/thumbnail_${file.originalname}`;
thumbnailKey = `audits/${uniqueId}/${randomPrefix}/thumbnail_${file.originalname}`;

const thumbnailBuffer = await sharp(file.buffer)
.resize(200, 200, { fit: "inside" })
.jpeg({
mozjpeg: true
})
.flatten({ background: { r: 255, g: 255, b: 255, alpha: 0 } })
.resize(200, 200, { fit: "inside" })
.toBuffer();

await Promise.all([
Expand All @@ -556,25 +579,7 @@ export class AuditService {
} else {
await this.fileStorageService.uploadFile(file.buffer, file.mimetype, key);
}

const storedFile = await this.prisma.auditFile.create({
data: {
audit: {
connect: {
editUniqueId
}
},

key,
originalFilename: file.originalname,
mimetype: file.mimetype,
size: file.size,

thumbnailKey
}
});

return storedFile;
return { key, thumbnailKey };
}

/**
Expand Down Expand Up @@ -845,7 +850,8 @@ export class AuditService {
key: file.key,
thumbnailKey: file.thumbnailKey,
size: file.size,
mimetype: file.mimetype
mimetype: file.mimetype,
display: file.display
})),

criteriaCount: {
Expand Down Expand Up @@ -1009,7 +1015,8 @@ export class AuditService {
exampleImages: r.exampleImages.map((img) => ({
filename: img.originalFilename,
key: img.key,
thumbnailKey: img.thumbnailKey
thumbnailKey: img.thumbnailKey,
display: img.display
}))
}))
};
Expand Down
9 changes: 6 additions & 3 deletions confiture-rest-api/src/audits/audits.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
import { Audit } from "src/generated/nestjs-dto/audit.entity";
import { CriterionResult } from "src/generated/nestjs-dto/criterionResult.entity";
import { MailService } from "../mail/mail.service";
import { NotesFileDto } from "./dto/notes-file.dto";
import { AuditExportService } from "./audit-export.service";
import { AuditService } from "./audit.service";
import { CreateAuditDto } from "./dto/create-audit.dto";
Expand Down Expand Up @@ -173,7 +174,8 @@ export class AuditsController {
body.pageId,
body.topic,
body.criterium,
file
file,
body.display
);
}

Expand All @@ -190,15 +192,16 @@ export class AuditsController {
errorHttpStatusCode: HttpStatus.UNPROCESSABLE_ENTITY
})
)
file: Express.Multer.File
file: Express.Multer.File,
@Body() body: NotesFileDto
) {
const audit = await this.auditService.getAuditWithEditUniqueId(uniqueId);

if (!audit) {
return this.sendAuditNotFoundStatus(uniqueId);
}

return await this.auditService.saveNotesFile(uniqueId, file);
return await this.auditService.saveNotesFile(uniqueId, file, body.display);
}

@Delete("/:uniqueId/results/examples/:exampleId")
Expand Down
18 changes: 14 additions & 4 deletions confiture-rest-api/src/audits/dto/audit-report.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import { ApiProperty } from "@nestjs/swagger";
import {
AuditType,
CriterionResultStatus,
CriterionResultUserImpact
CriterionResultUserImpact,
FileDisplay
} from "@prisma/client";

export class AuditReportDto {
Expand Down Expand Up @@ -192,18 +193,27 @@ class ReportCriterionResult {
}

class ExampleImage {
/** @example "mon-image.jpg" */
/** @example "my-image.jpg" */
filename: string;
/** @example "audit/xxxx/my-image.jpg" */
/** @example "audit/EWIsM6sYI2cC0lI7Ok2PE/3gnCTQ5ztOdEnKRraIMYG/my-image.jpg" */
key: string;
/** @example "audit/xxxx/my-image_thumbnail.jpg" */
/** @example "audit/EWIsM6sYI2cC0lI7Ok2PE/3gnCTQ5ztOdEnKRraIMYG/my-image_thumbnail.jpg" */
thumbnailKey: string;
/** @example "ATTACHMENT" */
display: FileDisplay;
}

class NotesFile {
/** @example "screenshot_001.png" */
originalFilename: string;
/** @example "audits/EWIsM6sYI2cC0lI7Ok2PE/uqoOes4QqhFyKV8v0s2AQ/screenshot_001.png" */
key: string;
/** @example "audits/EWIsM6sYI2cC0lI7Ok2PE/uqoOes4QqhFyKV8v0s2AQ/thumbnail_screenshot_001.png" */
thumbnailKey: string;
/** @example 4631 */
size: number;
/** @example "image/png" */
mimetype: string;
/** @example "ATTACHMENT" */
display: FileDisplay;
}
9 changes: 9 additions & 0 deletions confiture-rest-api/src/audits/dto/notes-file.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { FileDisplay } from "@prisma/client";
import { IsIn, IsOptional, IsString } from "class-validator";

export class NotesFileDto {
@IsOptional()
@IsString()
@IsIn(Object.values(FileDisplay))
display?: FileDisplay;
}
3 changes: 3 additions & 0 deletions confiture-rest-api/src/audits/dto/upload-image.dto.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Type } from "class-transformer";
import { IsInt, IsNumber, IsPositive, Max, Min } from "class-validator";
import { IsRgaaCriterium } from "./update-results.dto";
import { FileDisplay } from "@prisma/client";

/*
The `@Type(() => Number)` decorator is required to correctly parse strings into numbers
Expand Down Expand Up @@ -34,4 +35,6 @@ export class UploadImageDto {
"topic and criterium numbers must be a valid RGAA criterium combination"
})
criterium: number;

display: FileDisplay;
}
21 changes: 17 additions & 4 deletions confiture-web-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,34 @@
"dependencies": {
"@gouvfr/dsfr": "1.12.1",
"@sentry/tracing": "^7.37.2",
"@sentry/vite-plugin": "^0.3.0",
"@sentry/vue": "^7.37.2",
"@tiptap/extension-code-block-lowlight": "^2.5.9",
"@tiptap/extension-highlight": "^2.5.9",
"@tiptap/extension-image": "^2.6.6",
"@tiptap/extension-link": "^2.5.9",
"@tiptap/extension-task-item": "^2.5.9",
"@tiptap/extension-task-list": "^2.5.9",
"@tiptap/extension-typography": "^2.5.9",
"@tiptap/pm": "^2.5.9",
"@tiptap/starter-kit": "^2.5.9",
"@tiptap/vue-3": "^2.5.9",
"@unhead/vue": "^1.5.3",
"@vitejs/plugin-vue": "^4.4.1",
"dompurify": "^2.4.1",
"highlight.js": "^11.10.0",
"jwt-decode": "^3.1.2",
"ky": "^0.33.0",
"lodash-es": "^4.17.21",
"lowlight": "^3.1.0",
"marked": "^4.2.4",
"pinia": "^2.0.28",
"slugify": "^1.6.5",
"tiptap-markdown": "^0.8.10",
"vite": "^4.5.0",
"vue": "^3.3.8",
"vue-matomo": "^4.2.0",
"vue-router": "^4.2.5",
"vite": "^4.5.0",
"@vitejs/plugin-vue": "^4.4.1",
"@sentry/vite-plugin": "^0.3.0"
"vue-router": "^4.2.5"
},
"devDependencies": {
"@types/dompurify": "^2.4.0",
Expand Down
Loading