Skip to content

Commit

Permalink
Add "context" field in feedbacks (#729)
Browse files Browse the repository at this point in the history
* client: update packages

* client: add "firebase" CLI package

* client: use `patchValue` (instead of `setValue`) to handle old drafts (with no `context` field)
  • Loading branch information
avine authored Dec 12, 2024
1 parent e70e960 commit bcfa660
Show file tree
Hide file tree
Showing 20 changed files with 9,858 additions and 3,733 deletions.
13,401 changes: 9,721 additions & 3,680 deletions client/package-lock.json

Large diffs are not rendered by default.

31 changes: 16 additions & 15 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,23 +36,23 @@
},
"private": true,
"dependencies": {
"@angular/animations": "^19.0.3",
"@angular/cdk": "^19.0.2",
"@angular/common": "^19.0.3",
"@angular/compiler": "^19.0.3",
"@angular/core": "^19.0.3",
"@angular/forms": "^19.0.3",
"@angular/material": "^19.0.2",
"@angular/material-date-fns-adapter": "^19.0.2",
"@angular/platform-browser": "^19.0.3",
"@angular/platform-browser-dynamic": "^19.0.3",
"@angular/router": "^19.0.3",
"@angular/animations": "^19.0.4",
"@angular/cdk": "^19.0.3",
"@angular/common": "^19.0.4",
"@angular/compiler": "^19.0.4",
"@angular/core": "^19.0.4",
"@angular/forms": "^19.0.4",
"@angular/material": "^19.0.3",
"@angular/material-date-fns-adapter": "^19.0.3",
"@angular/platform-browser": "^19.0.4",
"@angular/platform-browser-dynamic": "^19.0.4",
"@angular/router": "^19.0.4",
"@fontsource/nunito": "^5.1.0",
"@material-symbols/font-600": "^0.27.2",
"canvas-confetti": "^1.9.3",
"date-fns": "^4.1.0",
"echarts": "^5.5.1",
"firebase": "^11.0.2",
"firebase": "^11.1.0",
"js-cookie": "^3.0.5",
"ngx-echarts": "^19.0.0",
"rxjs": "~7.8.1",
Expand All @@ -62,19 +62,20 @@
"devDependencies": {
"@angular/build": "^19.0.4",
"@angular/cli": "^19.0.4",
"@angular/compiler-cli": "^19.0.3",
"@angular/localize": "^19.0.3",
"@angular/compiler-cli": "^19.0.4",
"@angular/localize": "^19.0.4",
"@playwright/test": "^1.49.1",
"@types/canvas-confetti": "^1.6.4",
"@types/jest": "^29.5.14",
"@types/js-cookie": "^3.0.6",
"@types/node": "^22.10.1",
"@types/node": "^22.10.2",
"angular-eslint": "^19.0.2",
"autoprefixer": "^10.4.20",
"concurrently": "^9.1.0",
"eslint": "^9.16.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.2.1",
"firebase-tools": "^13.28.0",
"jest": "^29.7.0",
"jest-preset-angular": "^14.4.2",
"postcss": "^8.4.49",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ <h1 class="gbl-page-title">
<app-autocomplete-email [email]="form.controls.receiverEmail" class="gbl-form-field" />

<app-give-feedback-details
[context]="form.controls.context"
[positive]="form.controls.positive"
[negative]="form.controls.negative"
[comment]="form.controls.comment"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ export class GiveFeedbackComponent implements LeaveForm, OnDestroy {
this.getQueryParam('receiverEmail'),
[Validators.required, Validators.email, this.allowedEmailDomainsValidator, this.forbiddenValuesValidator],
],
context: [''], // Note: validators are defined in `GiveFeedbackDetailsComponent`
positive: [''], // Note: validators are defined in `GiveFeedbackDetailsComponent`
negative: [''], // Note: validators are defined in `GiveFeedbackDetailsComponent`
comment: [''], // Note: validators are defined in `GiveFeedbackDetailsComponent`
Expand Down Expand Up @@ -111,7 +112,10 @@ export class GiveFeedbackComponent implements LeaveForm, OnDestroy {
}),
)
.subscribe((draft) => {
this.form.setValue(draft);
// WARNING:
// The `context` field may not be present in the `draft` object, because it was added to the feedback type later.
// Therefore DO NOT use `.setValue()` instead of `.patchValue()`, otherwise it will fail for old draft objects.
this.form.patchValue(draft);
this.form.updateValueAndValidity();
this.leaveFormService.takeSnapshot();
});
Expand Down Expand Up @@ -142,9 +146,11 @@ export class GiveFeedbackComponent implements LeaveForm, OnDestroy {
}
this.disableForm(true);

const { receiverEmail, positive, negative, comment, shared } = this.form.value as Required<typeof this.form.value>;
const { receiverEmail, context, positive, negative, comment, shared } = this.form.value as Required<
typeof this.form.value
>;

this.feedbackService.give({ receiverEmail, positive, negative, comment, shared }).subscribe((result) => {
this.feedbackService.give({ receiverEmail, context, positive, negative, comment, shared }).subscribe((result) => {
if (result.id === undefined) {
this.disableForm(false);
if (result.message === 'invalid_email') {
Expand All @@ -164,9 +170,11 @@ export class GiveFeedbackComponent implements LeaveForm, OnDestroy {
protected onDraft() {
this.disableForm(true);

const { receiverEmail, positive, negative, comment, shared } = this.form.value as Required<typeof this.form.value>;
const { receiverEmail, context, positive, negative, comment, shared } = this.form.value as Required<
typeof this.form.value
>;

this.giveFeedbackDraftService.give({ receiverEmail, positive, negative, comment, shared }).subscribe({
this.giveFeedbackDraftService.give({ receiverEmail, context, positive, negative, comment, shared }).subscribe({
error: () => {
this.disableForm(false);
this.notificationService.showError();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ <h3 class="app-give-requested-feedback__anonymous-heading">
</mat-form-field>

<app-give-feedback-details
[context]="form.controls.context"
[positive]="form.controls.positive"
[negative]="form.controls.negative"
[comment]="form.controls.comment"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export class GiveRequestedFeedbackComponent implements GiveRequestedFeedbackData

request = input.required<FeedbackRequest>();

draft = input<Pick<FeedbackRequestDraft, 'positive' | 'negative' | 'comment'>>();
draft = input<Pick<FeedbackRequestDraft, 'context' | 'positive' | 'negative' | 'comment'>>();

private router = inject(Router);

Expand All @@ -63,6 +63,7 @@ export class GiveRequestedFeedbackComponent implements GiveRequestedFeedbackData
leaveFormService = inject(LeaveFormService);

form = this.formBuilder.group({
context: [''], // Note: validators are defined in `GiveFeedbackDetailsComponent`
positive: [''], // Note: validators are defined in `GiveFeedbackDetailsComponent`
negative: [''], // Note: validators are defined in `GiveFeedbackDetailsComponent`
comment: [''], // Note: validators are defined in `GiveFeedbackDetailsComponent`
Expand All @@ -77,8 +78,12 @@ export class GiveRequestedFeedbackComponent implements GiveRequestedFeedbackData
}

ngOnInit(): void {
if (this.draft()) {
this.form.setValue(this.draft()!);
const draft = this.draft();
if (draft) {
// WARNING:
// The `context` field may not be present in the `draft` object, because it was added to the feedback type later.
// Therefore DO NOT use `.setValue()` instead of `.patchValue()`, otherwise it will fail for old draft objects.
this.form.patchValue(draft);
this.form.updateValueAndValidity();
this.leaveFormService.takeSnapshot();
}
Expand All @@ -90,27 +95,29 @@ export class GiveRequestedFeedbackComponent implements GiveRequestedFeedbackData
}
this.disableForm(true);

const { positive, negative, comment } = this.form.value as Required<typeof this.form.value>;

this.feedbackService.giveRequested({ token: this.token(), positive, negative, comment }).subscribe((success) => {
if (!success) {
this.disableForm(false);
this.notificationService.showError();
} else {
this.giveRequestedFeedbackListService.refresh();
this.leaveFormService.unregisterForm();
this.feedbackId = this.anonymous ? undefined : this.request()?.id;
this.navigateToSuccess();
}
});
const { context, positive, negative, comment } = this.form.value as Required<typeof this.form.value>;

this.feedbackService
.giveRequested({ token: this.token(), context, positive, negative, comment })
.subscribe((success) => {
if (!success) {
this.disableForm(false);
this.notificationService.showError();
} else {
this.giveRequestedFeedbackListService.refresh();
this.leaveFormService.unregisterForm();
this.feedbackId = this.anonymous ? undefined : this.request()?.id;
this.navigateToSuccess();
}
});
}

protected onDraft() {
this.disableForm(true);

const { positive, negative, comment } = this.form.value as Required<typeof this.form.value>;
const { context, positive, negative, comment } = this.form.value as Required<typeof this.form.value>;

this.feedbackService.giveRequestedDraft({ token: this.token(), positive, negative, comment }).subscribe({
this.feedbackService.giveRequestedDraft({ token: this.token(), context, positive, negative, comment }).subscribe({
error: () => this.notificationService.showError(),
complete: () => {
this.disableForm(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ export const giveRequestedFeedbackGuard = (route: ActivatedRouteSnapshot): Obser
tap(({ request, draft }) => {
let _draft: GiveRequestedFeedbackData['draft'] = undefined;
if (draft) {
const { positive, negative, comment } = draft;
_draft = { positive, negative, comment };
const { context, positive, negative, comment } = draft;
_draft = { context, positive, negative, comment };
}
// Note: this guard has more than one responsibility (it also provides data to the routed component)
route.data = { token, request, draft: _draft } satisfies GiveRequestedFeedbackData;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export type GiveRequestedFeedbackData = {
token: string | Signal<string>;
request: FeedbackRequest | Signal<FeedbackRequest>;
draft:
| Pick<FeedbackRequestDraft, 'positive' | 'negative' | 'comment'>
| Pick<FeedbackRequestDraft, 'context' | 'positive' | 'negative' | 'comment'>
| undefined
| Signal<Pick<FeedbackRequestDraft, 'positive' | 'negative' | 'comment'> | undefined>;
| Signal<Pick<FeedbackRequestDraft, 'context' | 'positive' | 'negative' | 'comment'> | undefined>;
};
Original file line number Diff line number Diff line change
@@ -1,3 +1,20 @@
<mat-form-field appearance="outline" class="gbl-form-field">
<mat-label i18n="@@Feedback.Context">Contexte</mat-label>

<textarea
[formControl]="context()"
[maxlength]="contextMaxLength"
matInput
rows="3"
cdkTextareaAutosize
cdkAutosizeMaxRows="10"
placeholder="Dans quel contexte avez-vous travaillé avec votre collègue ?"
i18n-placeholder="@@Component.GiveFeedbackDetails.ContextPlaceholder"
></textarea>

<mat-hint align="end">{{ context().value.length }} / {{ contextMaxLength }}</mat-hint>
</mat-form-field>

<mat-form-field appearance="outline" class="gbl-form-field">
<mat-label i18n="@@Feedback.Positive">Points forts</mat-label>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,24 @@ import { ValidationErrorMessagePipe } from '../../../shared/validation/validatio
export class GiveFeedbackDetailsComponent implements OnInit {
protected matDialog = inject(MatDialog);

protected contextMaxLength = MEDIUM_MAX_LENGTH;

protected feedbackMaxLength = LARGE_MAX_LENGTH;

protected commentMaxLength = MEDIUM_MAX_LENGTH;

context = input.required<FormControl<string>>();

positive = input.required<FormControl<string>>();

negative = input.required<FormControl<string>>();

comment = input.required<FormControl<string>>();

ngOnInit(): void {
this.context().addValidators([Validators.maxLength(this.contextMaxLength)]);
this.context().updateValueAndValidity();

this.positive().addValidators([
Validators.required,
isNotBlankValidator,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ <h2 class="app-feedback-body__title" i18n="@@Feedback.Message">Message</h2>
}

@if (feedback.status === 'done') {
@if (feedback.context) {
<h2 class="app-feedback-body__title" i18n="@@Feedback.Context">Contexte</h2>
<app-multi-line class="app-feedback-body__content" [text]="feedback.context" />
}

<h2 class="app-feedback-body__title" i18n="@@Feedback.Positive">Points forts</h2>
<app-multi-line class="app-feedback-body__content" [text]="feedback.positive" />

Expand Down
2 changes: 2 additions & 0 deletions client/src/app/shared/feedback/feedback.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@ export type FeedbackArchiveRequestDto = {

export type GiveRequestedFeedbackDto = {
token: string;
context: string;
positive: string;
negative: string;
comment: string;
};

export type GiveFeedbackDto = {
receiverEmail: string;
context: string;
positive: string;
negative: string;
comment: string;
Expand Down
3 changes: 3 additions & 0 deletions client/src/app/shared/feedback/feedback.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export type Feedback = {
id: string;
giverEmail: string;
receiverEmail: string;
context: string;
positive: string;
negative: string;
comment: string;
Expand Down Expand Up @@ -69,6 +70,7 @@ export type FeedbackRequestDraftType = typeof FeedbackRequestDraftType;

export type FeedbackDraft = {
receiverEmail: string;
context: string;
positive: string;
negative: string;
comment: string;
Expand All @@ -78,6 +80,7 @@ export type FeedbackDraft = {
export type FeedbackRequestDraft = {
token: string;
receiverEmail: string;
context: string;
positive: string;
negative: string;
comment: string;
Expand Down
2 changes: 2 additions & 0 deletions client/src/locales/messages.en.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
"Component.GiveFeedback.Share": "Share feedback with your colleague's manager",
"Component.GiveFeedback.ShareFeedbackMessageContent": "By sharing your feedZback, your colleague and his or her manager will be able to discuss it together and make the most of its content.",
"Component.GiveFeedback.ShareFeedbackMessageTitle": "Why is sharing feedZback recommended?",
"Component.GiveFeedbackDetails.ContextPlaceholder": "In what context did you work with your colleague?",
"Component.GiveFeedbackDetails.Guide": "Guide",
"Component.GiveFeedbackSuccess.GiveAnother": "Give another feedZback",
"Component.GiveFeedbackSuccess.Title": "FeedZback sent to:",
Expand Down Expand Up @@ -118,6 +119,7 @@
"Component.SkipLinks.Link": "Skip to main content",
"Demo.LoremIpsum": "{$INTERPOLATION} dolor sit amet",
"Feedback.Comment": "Comment",
"Feedback.Context": "Context",
"Feedback.Give": "Give",
"Feedback.Message": "Message",
"Feedback.Negative": "Areas for improvement",
Expand Down
2 changes: 2 additions & 0 deletions client/src/locales/messages.fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
"Component.GiveFeedback.Share": " Partager le feedZback avec le manager de votre collègue ",
"Component.GiveFeedback.ShareFeedbackMessageContent": "En partageant votre feedZback, votre collègue et son manager pourront échanger ensemble et ainsi bénéficier au mieux de son contenu.",
"Component.GiveFeedback.ShareFeedbackMessageTitle": "Pourquoi le partage du feedZback est recommandé ?",
"Component.GiveFeedbackDetails.ContextPlaceholder": "Dans quel contexte avez-vous travaillé avec votre collègue ?",
"Component.GiveFeedbackDetails.Guide": "Guide",
"Component.GiveFeedbackSuccess.GiveAnother": "Donner un autre feedZback",
"Component.GiveFeedbackSuccess.Title": "FeedZback envoyé à :",
Expand Down Expand Up @@ -118,6 +119,7 @@
"Component.SkipLinks.Link": "Accéder au contenu principal",
"Demo.LoremIpsum": "{$INTERPOLATION} dolor sit amet",
"Feedback.Comment": "Commentaire",
"Feedback.Context": "Contexte",
"Feedback.Give": " Donner ",
"Feedback.Message": "Message",
"Feedback.Negative": "Axes d'améliorations",
Expand Down
4 changes: 2 additions & 2 deletions server/src/feedback/feedback-db/feedback-db.params.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { Feedback } from './feedback-db.types';

export type FeedbackRequestParams = Pick<Feedback, 'giverEmail' | 'receiverEmail' | 'message' | 'shared'>;

export type GiveRequestedFeedbackParams = Pick<Feedback, 'positive' | 'negative' | 'comment'>;
export type GiveRequestedFeedbackParams = Pick<Feedback, 'context' | 'positive' | 'negative' | 'comment'>;

export type GiveFeedbackParams = Pick<
Feedback,
'giverEmail' | 'receiverEmail' | 'positive' | 'negative' | 'comment' | 'shared'
'giverEmail' | 'receiverEmail' | 'context' | 'positive' | 'negative' | 'comment' | 'shared'
>;
Loading

0 comments on commit bcfa660

Please sign in to comment.