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 31 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
0da9c0e
Add Tiptap and a first integration in NotesModal
yaaax Aug 8, 2024
af23880
Fix markdown content recovery
yaaax Aug 9, 2024
ffb6046
Set default code block language to html
yaaax Aug 16, 2024
4becae3
Fix eslint imports
yaaax Aug 16, 2024
8bfbe35
Fix eslint finally
yaaax Aug 16, 2024
ba218d0
Fix tiptap code blocks style
yaaax Aug 17, 2024
df48b96
Fix eslint warning (type)
yaaax Aug 26, 2024
c8d1262
Factorize code in AuditService
yaaax Sep 8, 2024
beeef1b
Add display field for uploads
yaaax Sep 9, 2024
7a165f8
Tiptap: handle images drag and drop
yaaax Sep 13, 2024
bf48bc2
refactor(tiptap): simplify code
yaaax Oct 9, 2024
0b86107
Titap: prevent layout shift on image load
yaaax Oct 14, 2024
10d792d
Titap: fix and improve image style
yaaax Oct 14, 2024
4e653bb
Ajout de la config de debug globale
yaaax Oct 21, 2024
1091882
Titap: fix drop cursor and image position when dropped
yaaax Oct 21, 2024
0506218
Titap: handle multiple image drop
yaaax Oct 21, 2024
963e492
Attachment: fix error 500
yaaax Oct 23, 2024
0866f14
Tiptap: improve accessibility
yaaax Oct 23, 2024
8a946b1
Image upload: add error message
yaaax Oct 23, 2024
0e6190c
Minor typo: Tiptap instead of TipTap
yaaax Oct 24, 2024
b571d24
Tiptap: improve drag and drop + upload
yaaax Oct 24, 2024
d98b78e
Add upload/delete timeout error messages
yaaax Oct 28, 2024
c92eddf
Improve error messages (avoid displaying null)
yaaax Oct 28, 2024
5197c70
Tiptap: improve drop cursor style
yaaax Oct 28, 2024
43588d4
Tiptap: handle and fix drop + paste
yaaax Oct 28, 2024
331b86a
Tiptap style: improve focus outline
yaaax Nov 15, 2024
cd147ed
Tiptap style: do not fix height
yaaax Nov 15, 2024
627f805
Tiptap: refactor to allow image upload from button
yaaax Nov 15, 2024
a28acde
Fix CSS errors
yaaax Nov 15, 2024
46f21eb
Fix type issues (error messages can be strings)
yaaax Nov 15, 2024
b3b41d2
Tiptap: add buttons
yaaax Dec 4, 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';
13 changes: 12 additions & 1 deletion confiture-rest-api/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,11 @@ model AuditTrace {
Audit Audit?
}

enum FileDisplay {
EDITOR
ATTACHMENT
}

model StoredFile {
id Int @id @default(autoincrement())
originalFilename String
Expand All @@ -184,6 +189,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 @@ -203,7 +211,10 @@ model AuditFile {
key String
thumbnailKey String?

audit Audit? @relation(fields: [auditUniqueId], references: [editUniqueId], onDelete: Cascade)
audit Audit? @relation(fields: [auditUniqueId], references: [editUniqueId], onDelete: Cascade)
// Inside TipTap editor (EDITOR) or added as an attachment (ATTACHMENT)?
display FileDisplay @default(ATTACHMENT)

auditUniqueId String?
}

Expand Down
109 changes: 58 additions & 51 deletions confiture-rest-api/src/audits/audit.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
CriterionResult,
CriterionResultStatus,
CriterionResultUserImpact,
FileDisplay,
Prisma,
StoredFile
} from "@prisma/client";
Expand Down Expand Up @@ -418,30 +419,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 @@ -459,7 +444,8 @@ export class AuditService {
originalFilename: file.originalname,
mimetype: file.mimetype,
size: file.size,
thumbnailKey
thumbnailKey,
display
}
});

Expand Down Expand Up @@ -500,22 +486,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 @@ -529,25 +552,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 @@ -832,7 +837,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 @@ -999,7 +1005,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 @@ -174,7 +175,8 @@ export class AuditsController {
body.pageId,
body.topic,
body.criterium,
file
file,
body.display
);
}

Expand All @@ -191,15 +193,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 @@ -190,18 +191,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
1 change: 1 addition & 0 deletions confiture-web-app/src/assets/images/code-block.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading