Skip to content

Commit

Permalink
Permet de réordonner les pages de l'échantillon (#613)
Browse files Browse the repository at this point in the history
* add select to change pages order

* update order or pages when updating select

* focus endIndex select after order change

* fix select label index

* delete old css class

* sort report pages in the correct order

* add order property to audit pages

* order pages in report statement and report errors

* Update confiture-rest-api/src/audits/audit.service.ts

Co-authored-by: Adrien Boutigny <[email protected]>

* Update confiture-rest-api/src/audits/audit.service.ts

Co-authored-by: Adrien Boutigny <[email protected]>

* Update confiture-rest-api/src/audits/audit.service.ts

Co-authored-by: Adrien Boutigny <[email protected]>

* remove order from dto

* merge migrations

* vocally announce page position changes

* order tabs by pages order

* fix page type

* fix page type

* simplfiy swapping pages

* trigger heroku deployment

* update changelog

---------

Co-authored-by: Adrien Boutigny <[email protected]>
  • Loading branch information
bellangerq and hissalht authored Jan 25, 2024
1 parent 2e69e23 commit 049c8b0
Show file tree
Hide file tree
Showing 11 changed files with 161 additions and 61 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ Tous les changements notables de Ara sont documentés ici avec leur date, leur c

## 25/01/2024

### Nouvelles fonctionnalités 🚀

- Ajoute la possibilité de changer l’ordre des pages de l’échantilloon depuis les paramètres de l’audit ([#613](https://github.com/DISIC/Ara/pull/613))

### Corrections 🐛

- Corrige la navigation au clavier des onglets des pages de l’audit ([#625](https://github.com/DISIC/Ara/pull/625))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "AuditedPage" ADD COLUMN "order" INTEGER NOT NULL DEFAULT 0;
11 changes: 6 additions & 5 deletions confiture-rest-api/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,10 @@ model TestEnvironment {
}

model AuditedPage {
id Int @id @default(autoincrement())
name String
url String
id Int @id @default(autoincrement())
order Int @default(0)
name String
url String
audit Audit? @relation(fields: [auditUniqueId], references: [editUniqueId], onDelete: Cascade)
auditUniqueId String?
Expand Down Expand Up @@ -220,11 +221,11 @@ model User {
orgName String?
/// @DtoEntityHidden
newEmail String?
newEmail String?
/// @DtoEntityHidden
newEmailVerificationJti String?
}

model ActiveFeedbackToken {
uid String @id
}
}
26 changes: 17 additions & 9 deletions confiture-rest-api/src/audits/audit.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,9 @@ export class AuditService {

pages: {
createMany: {
data: data.pages
data: data.pages.map((p, i) => {
return { ...p, order: i };
})
}
},

Expand Down Expand Up @@ -183,8 +185,9 @@ export class AuditService {
data: UpdateAuditDto
): Promise<Audit | undefined> {
try {
const updatedPages = data.pages.filter((p) => p.id);
const newPages = data.pages.filter((p) => !p.id);
const orderedPages = data.pages.map((p, i) => ({ ...p, order: i }));
const updatedPages = orderedPages.filter((p) => p.id);
const newPages = orderedPages.filter((p) => !p.id);

const [audit] = await this.prisma.$transaction([
this.prisma.audit.update({
Expand Down Expand Up @@ -302,12 +305,14 @@ export class AuditService {
update: updatedPages.map((p) => ({
where: { id: p.id },
data: {
order: p.order,
name: p.name,
url: p.url
}
})),
createMany: {
data: newPages.map((p) => ({
order: p.order,
name: p.name,
url: p.url
}))
Expand Down Expand Up @@ -784,12 +789,15 @@ export class AuditService {
browserVersion: e.browserVersion
})),
referencial: "RGAA Version 4.1",
samples: audit.pages.map((p, i) => ({
name: p.name,
number: i + 1,
url: p.url,
id: p.id
})),
samples: audit.pages
.map((p, i) => ({
name: p.name,
order: p.order,
number: i + 1,
url: p.url,
id: p.id
}))
.sort((p) => p.order),
tools: audit.tools,
technologies: audit.technologies
},
Expand Down
1 change: 1 addition & 0 deletions confiture-rest-api/src/audits/dto/audit-report.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ class ReportContext {
class PageSample {
id: number;
number: number;
order: number;
name: string;
url: string;
}
Expand Down
1 change: 1 addition & 0 deletions confiture-rest-api/src/audits/dto/create-audit.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export class CreateAuditPage {
@IsNumber()
@IsOptional()
id?: number;

/**
* @example "Page de contact"
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,56 @@ async function deletePage(i: number) {
}
}
const pageOrderSelectRefs = ref<HTMLSelectElement[]>();
const positionSuccessMessage = ref("");
/**
* Change the order of pages. Swap pages if it is adjacent.
* Otherwise, insert `startIndex` page at `endIndex` position.
* @param {number} startIndex
* @param {number} endIndex
* @example
* Given [1, 2, 3, 4] and if updatePageOrder(1, 3), new order will be [1, 4, 2, 3].
*/
function updatePageOrder(startIndex: number, endIndex: number) {
positionSuccessMessage.value = "";
pagesArePristine.value = false;
const defaultState = [...pages.value];
const startEl = defaultState[startIndex];
if (startIndex === endIndex + 1 || startIndex === endIndex - 1) {
// Swap 2 adjacent pages
const temp = pages.value[startIndex];
pages.value[startIndex] = pages.value[endIndex];
pages.value[endIndex] = temp;
} else {
// Insert startIndex and endIndex
pages.value =
startIndex < endIndex
? [
...defaultState.slice(0, startIndex),
...defaultState.slice(startIndex + 1, endIndex + 1),
startEl,
...defaultState.slice(endIndex + 1)
]
: [
...defaultState.slice(0, endIndex),
startEl,
...defaultState.slice(endIndex, startIndex),
...defaultState.slice(startIndex + 1)
];
}
// Focus `endIndex` select
pageOrderSelectRefs.value?.at(endIndex)?.focus();
positionSuccessMessage.value = `Page déplacée en position ${
endIndex + 1
} sur ${pages.value.length}`;
}
/**
* Dev function to avoid filling all fields manually
*/
Expand All @@ -159,7 +209,7 @@ function onSubmit() {
emit("submit", {
auditType: auditType.value!,
procedureName: procedureName.value,
// remove leading/trailing whitespaces from urls, the browser valifation might accept those our backend won't !
// remove leading/trailing whitespaces from urls, the browser validation might accept those our backend won't!
pages: pages.value.map((p) => ({ ...p, url: p.url.trim() })),
auditorName: procedureAuditorName.value,
auditorEmail: formatEmail(procedureAuditorEmail.value)
Expand Down Expand Up @@ -255,16 +305,44 @@ const previousRoute = usePreviousRoute();
<h3 class="fr-h6 fr-mb-0">Page {{ i + 1 }}</h3>
</legend>

<button
class="fr-btn fr-btn--tertiary-no-outline page-delete-button"
type="button"
:disabled="pages.length === 1"
data-cy="delete"
@click="deletePage(i)"
>
Supprimer
<span class="sr-only">la page {{ i + 1 }}</span>
</button>
<div class="page-right-actions">
<button
class="fr-btn fr-btn--icon-left fr-icon-delete-line fr-btn--tertiary-no-outline"
type="button"
:disabled="pages.length === 1"
data-cy="delete"
@click="deletePage(i)"
>
Supprimer
<span class="sr-only">la page {{ i + 1 }}</span>
</button>

<div class="fr-select-group fr-mb-0">
<label class="fr-label sr-only" :for="`page-order-${i}`">
Position de la page {{ i + 1 }}
</label>
<select
:id="`page-order-${i}`"
ref="pageOrderSelectRefs"
class="fr-select fr-mt-0"
:value="i"
@change="
updatePageOrder(
i,
Number(($event.target as HTMLSelectElement).value)
)
"
>
<option v-for="(_, j) in pages" :key="j" :value="j">
Position {{ j + 1 }} sur {{ pages.length }}
</option>
</select>

<div class="sr-only" aria-live="polite" role="alert">
<p v-if="positionSuccessMessage">{{ positionSuccessMessage }}</p>
</div>
</div>
</div>

<DsfrField
:id="`page-name-${i + 1}`"
Expand All @@ -289,7 +367,7 @@ const previousRoute = usePreviousRoute();
</DsfrField>
</fieldset>
<button
class="fr-btn fr-btn--tertiary-no-outline fr-mt-2w fr-mb-6w"
class="fr-btn fr-btn--icon-left fr-icon-add-line fr-btn--secondary fr-mt-4w fr-mb-6w"
type="button"
@click="addPage"
>
Expand Down Expand Up @@ -356,12 +434,6 @@ const previousRoute = usePreviousRoute();
color: var(--text-mention-grey);
}
.audit-types {
display: flex;
gap: 1rem;
flex-wrap: wrap;
}
.partial-audit-radios {
display: flex;
flex-wrap: wrap;
Expand All @@ -382,7 +454,10 @@ const previousRoute = usePreviousRoute();
margin-top: 0.375rem;
}
.page-delete-button {
.page-right-actions {
display: flex;
align-items: center;
gap: 1rem;
float: right;
}
Expand Down
50 changes: 27 additions & 23 deletions confiture-web-app/src/components/report/ReportErrors.vue
Original file line number Diff line number Diff line change
Expand Up @@ -61,29 +61,33 @@ const errors = computed(() => {
} as Record<number, AuditReport["results"]>;
// TODO: make more legible
const data = Object.values(
mapValues(resultsGroupedByPage, (results, pageId) => {
return {
pageId: Number(pageId),
pageName: getPage(Number(pageId)).name,
pageUrl: getPage(Number(pageId)).url,
topics: sortBy(
Object.values(
mapValues(groupBy(results, "topic"), (results, topicNumber) => {
return {
topic: Number(topicNumber),
name: getTopicName(Number(topicNumber)),
errors: sortBy(
results.filter((r) => !r.transverse),
"criterium"
)
};
})
),
"topic"
)
};
})
const data = sortBy(
Object.values(
mapValues(resultsGroupedByPage, (results, pageId) => {
return {
pageId: Number(pageId),
pageOrder: getPage(Number(pageId)).order,
pageName: getPage(Number(pageId)).name,
pageUrl: getPage(Number(pageId)).url,
topics: sortBy(
Object.values(
mapValues(groupBy(results, "topic"), (results, topicNumber) => {
return {
topic: Number(topicNumber),
name: getTopicName(Number(topicNumber)),
errors: sortBy(
results.filter((r) => !r.transverse),
"criterium"
)
};
})
),
"topic"
)
};
})
),
(el) => el.pageOrder
);
return data;
Expand Down
6 changes: 4 additions & 2 deletions confiture-web-app/src/pages/audit/AuditGenerationPage.vue
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<script setup lang="ts">
import { sortBy } from "lodash-es";
import { computed, ref, watch } from "vue";
import { onBeforeRouteLeave, useRoute } from "vue-router";
Expand Down Expand Up @@ -212,11 +213,12 @@ const pageTitle = computed(() => {
type TabData = { label: string; data: AuditPage };
const tabsData = computed((): TabData[] => {
return (
return sortBy(
auditStore.currentAudit?.pages.map((p) => ({
label: p.name,
data: p
})) ?? []
})) ?? [],
(p) => p.data.order
);
});
Expand Down
1 change: 1 addition & 0 deletions confiture-web-app/src/types/report.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ interface AuditReportContext {
interface PageSample {
// number: number;
id: number;
order: number;
name: string;
url: string;
}
Expand Down
7 changes: 4 additions & 3 deletions confiture-web-app/src/types/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export interface AuditEnvironment {

export interface AuditPage {
id: number;
order: number;
name: string;
url: string;
}
Expand Down Expand Up @@ -66,16 +67,16 @@ export interface Audit {
notes: string | null;
}

/** Audit type but without the generated IDs and step 2 fields */
/** Audit type but without the generated ìd` and `order` and step 2 fields */
export type CreateAuditRequestData = Pick<
Audit,
"auditType" | "procedureName" | "auditorEmail" | "auditorName"
> & { pages: Omit<AuditPage, "id">[] };
> & { pages: Omit<AuditPage, "id" | "order">[] };

/** Creation data type plus step 2 fields. */
export type UpdateAuditRequestData = Omit<Audit, "environments" | "pages"> & {
environments: Omit<AuditEnvironment, "id">[];
pages: Omit<AuditPage, "id">[];
pages: Omit<AuditPage, "id" | "order">[];
};

export interface CreateFeedbackRequestData {
Expand Down

0 comments on commit 049c8b0

Please sign in to comment.