From 9a66bb9d8858884f911c6524e6ebbce147eff0c8 Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Wed, 13 Sep 2023 15:38:14 +0200 Subject: [PATCH 01/19] Create index page for annotations --- app/controllers/annotations_controller.rb | 22 +++++++- .../annotations/_annotations_table.html.erb | 50 +++++++++++++++++++ app/views/annotations/index.html.erb | 19 +++++++ app/views/annotations/index.js.erb | 1 + config/locales/views/annotations/en.yml | 7 +++ config/locales/views/annotations/nl.yml | 7 +++ 6 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 app/views/annotations/_annotations_table.html.erb create mode 100644 app/views/annotations/index.html.erb create mode 100644 app/views/annotations/index.js.erb diff --git a/app/controllers/annotations_controller.rb b/app/controllers/annotations_controller.rb index c6230825b5..0633e34a16 100644 --- a/app/controllers/annotations_controller.rb +++ b/app/controllers/annotations_controller.rb @@ -12,7 +12,27 @@ class AnnotationsController < ApplicationController def index authorize Annotation - @annotations = apply_scopes(policy_scope(Annotation.all)).where(thread_root_id: nil) + @annotations = policy_scope(Annotation.all) + + respond_to do |format| + format.html do + @title = I18n.t('annotations.index.title') + @crumbs = [[I18n.t('annotations.index.title'), saved_annotations_path]] + @courses = Course.where(id: @annotations.joins(:submission).pluck("submissions.course_id").uniq) + @exercises = Activity.where(id: @annotations.joins(:submission).pluck("submissions.exercise_id").uniq) + @annotations = apply_scopes(@annotations) + .includes(:course).includes(:user).includes(:submission) + .paginate(page: parse_pagination_param(params[:page]), per_page: parse_pagination_param(params[:per_page])) + end + format.json do + @annotations = apply_scopes(@annotations).where(thread_root_id: nil) + end + format.js do + @annotations = apply_scopes(@annotations) + .includes(:course).includes(:user).includes(:submission) + .paginate(page: parse_pagination_param(params[:page]), per_page: parse_pagination_param(params[:per_page])) + end + end end def question_index diff --git a/app/views/annotations/_annotations_table.html.erb b/app/views/annotations/_annotations_table.html.erb new file mode 100644 index 0000000000..4c539b6c55 --- /dev/null +++ b/app/views/annotations/_annotations_table.html.erb @@ -0,0 +1,50 @@ +
+ + + + + + + + + + <% local_assigns[:annotations].each do |annotation| %> + + + + + + <% end %> + +
<%= t ".submission" %><%= t ".annotation_text" %><%= t ".time" %>
+ + <%= link_to "##{annotation.submission.number}" ,submission_path(annotation.submission, anchor: 'code')%> + <%= t ".by" %> + <%= annotation.submission.user.full_name %> + +
+ + + <%= annotation.submission.course.name %> + : + + <%= annotation.submission.exercise.name %> + + +
+
+ + <%= markdown annotation.annotation_text %> + + + + <%= t "time.ago", time: time_ago_in_words(annotation.created_at) %> + +
+
+<% if local_assigns[:annotations].empty? %> +

+ <%= t ".empty" %> +

+<% end %> +
<%= page_navigation_links annotations, true, "annotations" %>
diff --git a/app/views/annotations/index.html.erb b/app/views/annotations/index.html.erb new file mode 100644 index 0000000000..b9317d4bea --- /dev/null +++ b/app/views/annotations/index.html.erb @@ -0,0 +1,19 @@ +
+
+
+
+

+ + <%= t ".title" %> + +

+
+
+ <%= render partial: 'layouts/searchbar', locals: { refresh_element: "#annotation-container", courses: @courses, exercises: @exercises } %> +
+ <%= render partial: 'annotations_table', locals: { annotations: @annotations } %> +
+
+
+
+
diff --git a/app/views/annotations/index.js.erb b/app/views/annotations/index.js.erb new file mode 100644 index 0000000000..cf51556e43 --- /dev/null +++ b/app/views/annotations/index.js.erb @@ -0,0 +1 @@ +dodona.setHTMLExecuteScripts(document.querySelector("#questions-table-wrapper"), "<%= escape_javascript(render partial: 'annotations_table', locals: { annotations: @annotations }) %>"); diff --git a/config/locales/views/annotations/en.yml b/config/locales/views/annotations/en.yml index 80e58d9f20..3fce75e694 100644 --- a/config/locales/views/annotations/en.yml +++ b/config/locales/views/annotations/en.yml @@ -2,6 +2,13 @@ en: annotations: index: new_annotation: "There is a new comment on your code" + title: Comments + annotations_table: + submission: Submission + annotation_text: Comment + time: Time + by: "by" + empty: "No comments found" questions: index: title: All questions diff --git a/config/locales/views/annotations/nl.yml b/config/locales/views/annotations/nl.yml index 5130f0fba3..29ace6c93d 100644 --- a/config/locales/views/annotations/nl.yml +++ b/config/locales/views/annotations/nl.yml @@ -2,6 +2,13 @@ nl: annotations: index: new_annotation: "Er is een nieuwe opmerking op je code" + title: Opmerkingen + annotations_table: + submission: Oplossing + annotation_text: Opmerking + time: Tijd + by: "door" + empty: "Geen opmerkingen gevonden" questions: index: title: Alle vragen From 10dd3c6f731b9326c65af3303c49541d5675f1af Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Fri, 15 Sep 2023 13:35:30 +0200 Subject: [PATCH 02/19] Use annotations table on saved_annotation detail page --- app/controllers/saved_annotations_controller.rb | 2 +- app/views/saved_annotations/show.html.erb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/saved_annotations_controller.rb b/app/controllers/saved_annotations_controller.rb index 377f91dab6..5205a9e578 100644 --- a/app/controllers/saved_annotations_controller.rb +++ b/app/controllers/saved_annotations_controller.rb @@ -27,7 +27,7 @@ def show format.html do @title = @saved_annotation.title @crumbs = [[I18n.t('saved_annotations.index.title'), saved_annotations_path], [@saved_annotation.title, saved_annotation_path(@saved_annotation)]] - @submissions = @saved_annotation.submissions.paginate(page: parse_pagination_param(params[:page])) + @annotations = @saved_annotation.annotations.paginate(page: parse_pagination_param(params[:page])) end format.json end diff --git a/app/views/saved_annotations/show.html.erb b/app/views/saved_annotations/show.html.erb index 1e4302c044..223a48a827 100644 --- a/app/views/saved_annotations/show.html.erb +++ b/app/views/saved_annotations/show.html.erb @@ -22,7 +22,7 @@

<%= t ".linked_submissions" %>

- <%= render partial: 'submissions/submissions_table', locals: {submissions: @submissions, exercise: @saved_annotation.exercise, course: @saved_annotation.course, user: nil} %> + <%= render partial: 'annotations/annotations_table', locals: { annotations: @annotations } %>
From b44efb6f0b6e57b289e1651627abfcadbb70594f Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Fri, 15 Sep 2023 15:03:40 +0200 Subject: [PATCH 03/19] Create new page --- app/controllers/annotations_controller.rb | 2 +- app/controllers/saved_annotations_controller.rb | 16 ++++++++++++++++ app/models/annotation.rb | 1 + app/policies/saved_annotation_policy.rb | 4 ++++ app/views/saved_annotations/index.html.erb | 11 ++++++----- app/views/saved_annotations/new.html.erb | 15 +++++++++++++++ app/views/saved_annotations/new.js.erb | 1 + config/locales/views/saved_annotations/en.yml | 2 ++ config/locales/views/saved_annotations/nl.yml | 2 ++ config/routes.rb | 2 +- 10 files changed, 49 insertions(+), 7 deletions(-) create mode 100644 app/views/saved_annotations/new.html.erb create mode 100644 app/views/saved_annotations/new.js.erb diff --git a/app/controllers/annotations_controller.rb b/app/controllers/annotations_controller.rb index 0633e34a16..8bc36edf3a 100644 --- a/app/controllers/annotations_controller.rb +++ b/app/controllers/annotations_controller.rb @@ -17,7 +17,7 @@ def index respond_to do |format| format.html do @title = I18n.t('annotations.index.title') - @crumbs = [[I18n.t('annotations.index.title'), saved_annotations_path]] + @crumbs = [[I18n.t('annotations.index.title'), annotations_path]] @courses = Course.where(id: @annotations.joins(:submission).pluck("submissions.course_id").uniq) @exercises = Activity.where(id: @annotations.joins(:submission).pluck("submissions.exercise_id").uniq) @annotations = apply_scopes(@annotations) diff --git a/app/controllers/saved_annotations_controller.rb b/app/controllers/saved_annotations_controller.rb index 5205a9e578..36209c1596 100644 --- a/app/controllers/saved_annotations_controller.rb +++ b/app/controllers/saved_annotations_controller.rb @@ -8,6 +8,9 @@ class SavedAnnotationsController < ApplicationController has_scope :by_exercise, as: 'exercise_id' has_scope :by_filter, as: 'filter' + has_scope :by_exercise, as: :exercise_id, only: :new + has_scope :by_course, as: :course_id, only: :new + order_by :annotations_count, :title, :annotation_text def index @@ -33,6 +36,19 @@ def show end end + def new + authorize SavedAnnotation + @annotations = AnnotationPolicy::Scope.new(current_user, Annotation.all).resolve + @courses = Course.where(id: @annotations.joins(:submission).pluck("submissions.course_id").uniq) + @exercises = Activity.where(id: @annotations.joins(:submission).pluck("submissions.exercise_id").uniq) + @annotations = apply_scopes(@annotations) + .includes(:course).includes(:user).includes(:submission) + .paginate(page: parse_pagination_param(params[:page]), per_page: parse_pagination_param(params[:per_page])) + + @title = I18n.t('saved_annotations.new.title') + @crumbs = [[I18n.t('saved_annotations.index.title'), saved_annotations_path], [I18n.t('saved_annotations.new.title'), '#']] + end + def edit @title = I18n.t('saved_annotations.edit.title') @crumbs = [[I18n.t('saved_annotations.index.title'), saved_annotations_path], [@saved_annotation.title, saved_annotation_path(@saved_annotation)], [I18n.t('saved_annotations.edit.title'), '#']] diff --git a/app/models/annotation.rb b/app/models/annotation.rb index f5ffbc8de7..17f63bcc59 100644 --- a/app/models/annotation.rb +++ b/app/models/annotation.rb @@ -57,6 +57,7 @@ class Annotation < ApplicationRecord scope :by_course, ->(course_id) { where(submission: Submission.in_course(Course.find(course_id))) } scope :by_username, ->(name) { where(user: User.by_filter(name)) } scope :by_exercise_name, ->(name) { where(submission: Submission.by_exercise_name(name)) } + scope :by_exercise, ->(exercise_id) { where(submission: { exercise_id: exercise_id }) } before_validation :set_last_updated_by, on: :create before_validation :set_course_id, on: :create diff --git a/app/policies/saved_annotation_policy.rb b/app/policies/saved_annotation_policy.rb index 973245b271..68c9db913f 100644 --- a/app/policies/saved_annotation_policy.rb +++ b/app/policies/saved_annotation_policy.rb @@ -45,6 +45,10 @@ def show? user_admin_of_beta_course? && record_in_beta_course? && record&.user_id == user.id end + def new? + user&.a_course_admin? + end + def edit? update? end diff --git a/app/views/saved_annotations/index.html.erb b/app/views/saved_annotations/index.html.erb index 62138ae27d..962a50f1ce 100644 --- a/app/views/saved_annotations/index.html.erb +++ b/app/views/saved_annotations/index.html.erb @@ -2,11 +2,12 @@
-

- - <%= t ".title" %> - -

+

<%= t ".title" %>

+ <% if policy(SavedAnnotation).new? %> +
+ <%= render 'fab_link', url: new_saved_annotation_path, icon: 'plus' %> +
+ <% end %>
<%= render partial: 'layouts/searchbar', locals: { courses: @courses, exercises: @exercises, autoSearch: false } %> diff --git a/app/views/saved_annotations/new.html.erb b/app/views/saved_annotations/new.html.erb new file mode 100644 index 0000000000..49f1220a54 --- /dev/null +++ b/app/views/saved_annotations/new.html.erb @@ -0,0 +1,15 @@ +
+
+
+
+

<%= t ".title" %>

+
+
+ <%= render partial: 'layouts/searchbar', locals: { refresh_element: "#annotation-container", courses: @courses, exercises: @exercises } %> +
+ <%= render partial: 'annotations/annotations_table', locals: { annotations: @annotations } %> +
+
+
+
+
diff --git a/app/views/saved_annotations/new.js.erb b/app/views/saved_annotations/new.js.erb new file mode 100644 index 0000000000..9b951ee249 --- /dev/null +++ b/app/views/saved_annotations/new.js.erb @@ -0,0 +1 @@ +dodona.setHTMLExecuteScripts(document.querySelector("#questions-table-wrapper"), "<%= escape_javascript(render partial: 'annotations/annotations_table', locals: { annotations: @annotations }) %>"); diff --git a/config/locales/views/saved_annotations/en.yml b/config/locales/views/saved_annotations/en.yml index bbb1270882..19f801d602 100644 --- a/config/locales/views/saved_annotations/en.yml +++ b/config/locales/views/saved_annotations/en.yml @@ -12,3 +12,5 @@ en: warning_no_annotations_changed: Existing comments will not be updated. These changes will only be applied to new comments. destroy: Delete save: Save + new: + title: Save comment diff --git a/config/locales/views/saved_annotations/nl.yml b/config/locales/views/saved_annotations/nl.yml index a8f821e55a..df8185d8a1 100644 --- a/config/locales/views/saved_annotations/nl.yml +++ b/config/locales/views/saved_annotations/nl.yml @@ -12,3 +12,5 @@ nl: warning_no_annotations_changed: Bestaande opmerkingen zullen niet worden aangepast. Deze wijzigingen zullen enkel worden toegepast op nieuwe opmerkingen. destroy: Verwijderen save: Opslaan + new: + title: Opmerking opslaan diff --git a/config/routes.rb b/config/routes.rb index acdfd00869..ff275ce9da 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -174,7 +174,7 @@ resources :annotations, only: %i[index show create update destroy] - resources :saved_annotations, only: %i[index show create update destroy edit] + resources :saved_annotations, only: %i[index show create update destroy edit new] get 'questions', to: 'annotations#question_index' From 372051b0fbd28e9a560f18de1542c9f36211bde0 Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Fri, 15 Sep 2023 15:56:37 +0200 Subject: [PATCH 04/19] Add save comment button on new page --- .../components/annotations/annotation_form.ts | 6 ++--- .../components/annotations/user_annotation.ts | 16 ++++++----- .../saved_annotations/new_saved_annotation.ts | 27 ++++++++++--------- .../saved_annotation_form.ts | 8 +++++- .../saved_annotation_input.ts | 2 -- .../saved_annotation_title_input.ts | 5 +--- .../javascripts/state/SavedAnnotations.ts | 3 +-- app/javascript/packs/saved_annotation.js | 1 + .../annotations/_annotations_table.html.erb | 11 ++++++++ 9 files changed, 48 insertions(+), 31 deletions(-) diff --git a/app/assets/javascripts/components/annotations/annotation_form.ts b/app/assets/javascripts/components/annotations/annotation_form.ts index dd5ae67d8c..82bc9b11c9 100644 --- a/app/assets/javascripts/components/annotations/annotation_form.ts +++ b/app/assets/javascripts/components/annotations/annotation_form.ts @@ -11,7 +11,6 @@ import { userAnnotationState } from "state/UserAnnotations"; import { savedAnnotationState } from "state/SavedAnnotations"; import { courseState } from "state/Courses"; import { exerciseState } from "state/Exercises"; -import { userState } from "state/Users"; // Min and max of the annotation text is defined in the annotation model. const maxLength = 10_000; @@ -230,14 +229,13 @@ export class AnnotationForm extends watchMixin(ShadowlessLitElement) { get potentialSavedAnnotationsExist(): boolean { return (savedAnnotationState.getList(new Map([ ["course_id", courseState.id.toString()], - ["exercise_id", exerciseState.id.toString()], - ["user_id", userState.id.toString()] + ["exercise_id", exerciseState.id.toString()] ])) || []).length > 0; } get isTitleTaken(): boolean { return savedAnnotationState.isTitleTaken( - this.savedAnnotationTitle, exerciseState.id, courseState.id, userState.id); + this.savedAnnotationTitle, exerciseState.id, courseState.id); } render(): TemplateResult { diff --git a/app/assets/javascripts/components/annotations/user_annotation.ts b/app/assets/javascripts/components/annotations/user_annotation.ts index 7f7370e45c..8e73a7b9f5 100644 --- a/app/assets/javascripts/components/annotations/user_annotation.ts +++ b/app/assets/javascripts/components/annotations/user_annotation.ts @@ -11,6 +11,7 @@ import { initTooltips } from "utilities"; import "components/saved_annotations/saved_annotation_icon"; import { annotationState } from "state/Annotations"; import { savedAnnotationState } from "state/SavedAnnotations"; +import { isBetaCourse } from "saved_annotation_beta"; /** * This component represents a single user annotation. @@ -152,13 +153,16 @@ export class UserAnnotationComponent extends i18nMixin(ShadowlessLitElement) { `); } - if (this.data.permission.save) { + if (this.data.permission.save && isBetaCourse()) { options.push(html` - - +
  • + + +
  • `); } if (this.data.permission.destroy) { diff --git a/app/assets/javascripts/components/saved_annotations/new_saved_annotation.ts b/app/assets/javascripts/components/saved_annotations/new_saved_annotation.ts index 63164d0d64..805955f9c4 100644 --- a/app/assets/javascripts/components/saved_annotations/new_saved_annotation.ts +++ b/app/assets/javascripts/components/saved_annotations/new_saved_annotation.ts @@ -7,7 +7,6 @@ import { modalMixin } from "components/modal_mixin"; import { isBetaCourse } from "saved_annotation_beta"; import { exerciseState } from "state/Exercises"; import { courseState } from "state/Courses"; -import { userState } from "state/Users"; /** * This component represents an creation button for a saved annotation @@ -27,6 +26,10 @@ export class NewSavedAnnotation extends modalMixin(ShadowlessLitElement) { annotationText: string; @property({ type: Number, attribute: "saved-annotation-id" }) savedAnnotationId: number; + @property({ type: Number, attribute: "exercise-id" }) + exerciseId: number = exerciseState.id; + @property({ type: Number, attribute: "course-id" }) + courseId: number = courseState.id; @property({ state: true }) errors: string[]; @@ -46,17 +49,17 @@ export class NewSavedAnnotation extends modalMixin(ShadowlessLitElement) { id: undefined, // Take the first five words, with a max of 40 chars as default title title: this.annotationText.split(/\s+/).slice(0, 5).join(" ").slice(0, 40), - annotation_text: this.annotationText + annotation_text: this.annotationText, }; } - get isTitleTaken(): boolean { + isTitleTaken(): boolean { return savedAnnotationState.isTitleTaken( - this.newSavedAnnotation.title, exerciseState.id, courseState.id, userState.id); + this.newSavedAnnotation.title, this.exerciseId, this.courseId); } async createSavedAnnotation(): Promise { - if (this.isTitleTaken && !confirm(I18n.t("js.saved_annotation.confirm_title_taken"))) { + if (this.isTitleTaken() && !confirm(I18n.t("js.saved_annotation.confirm_title_taken"))) { return; } @@ -88,6 +91,8 @@ export class NewSavedAnnotation extends modalMixin(ShadowlessLitElement) { this.savedAnnotation = e.detail} + .courseId=${this.courseId} + .exerciseId=${this.exerciseId} > `, html`
    diff --git a/app/assets/javascripts/components/saved_annotations/saved_annotation_input.ts b/app/assets/javascripts/components/saved_annotations/saved_annotation_input.ts index ec09cc02d4..7ca105c82c 100644 --- a/app/assets/javascripts/components/saved_annotations/saved_annotation_input.ts +++ b/app/assets/javascripts/components/saved_annotations/saved_annotation_input.ts @@ -4,7 +4,6 @@ import { ShadowlessLitElement } from "components/meta/shadowless_lit_element"; import "components/datalist_input"; import { SavedAnnotation, savedAnnotationState } from "state/SavedAnnotations"; import { unsafeHTML } from "lit/directives/unsafe-html.js"; -import { userState } from "state/Users"; import { courseState } from "state/Courses"; import { exerciseState } from "state/Exercises"; @@ -44,7 +43,6 @@ export class SavedAnnotationInput extends ShadowlessLitElement { const savedAnnotations = savedAnnotationState.getList(new Map([ ["course_id", courseState.id.toString()], ["exercise_id", exerciseState.id.toString()], - ["user_id", userState.id.toString()], ["filter", this.__label] ])); if (savedAnnotations === undefined) { diff --git a/app/assets/javascripts/components/saved_annotations/saved_annotation_title_input.ts b/app/assets/javascripts/components/saved_annotations/saved_annotation_title_input.ts index 7debbc2624..3adda5249d 100644 --- a/app/assets/javascripts/components/saved_annotations/saved_annotation_title_input.ts +++ b/app/assets/javascripts/components/saved_annotations/saved_annotation_title_input.ts @@ -4,7 +4,6 @@ import { ShadowlessLitElement } from "components/meta/shadowless_lit_element"; import { savedAnnotationState } from "state/SavedAnnotations"; import { exerciseState } from "state/Exercises"; import { courseState } from "state/Courses"; -import { userState } from "state/Users"; @customElement("d-saved-annotation-title-input") export class SavedAnnotationTitleInput extends ShadowlessLitElement { @@ -16,15 +15,13 @@ export class SavedAnnotationTitleInput extends ShadowlessLitElement { exerciseId: number = exerciseState.id; @property({ type: Number, attribute: "course-id" }) courseId: number = courseState.id; - @property({ type: Number, attribute: "user-id" }) - userId: number = userState.id; @property({ state: true }) _value: string; get isTitleTaken(): boolean { return savedAnnotationState.isTitleTaken( - this._value ?? this.value, this.exerciseId, this.courseId, this.userId, this.savedAnnotationId); + this._value ?? this.value, this.exerciseId, this.courseId, this.savedAnnotationId); } render(): TemplateResult { diff --git a/app/assets/javascripts/state/SavedAnnotations.ts b/app/assets/javascripts/state/SavedAnnotations.ts index c407b4abcb..c850e45078 100644 --- a/app/assets/javascripts/state/SavedAnnotations.ts +++ b/app/assets/javascripts/state/SavedAnnotations.ts @@ -108,12 +108,11 @@ class SavedAnnotationState extends State { } } - isTitleTaken(title: string, exerciseId: number, courseId: number, userId: number, savedAnnotationId: number = undefined): boolean { + isTitleTaken(title: string, exerciseId: number, courseId: number, savedAnnotationId: number = undefined): boolean { const params = new Map([ ["filter", title], ["exercise_id", exerciseId.toString()], ["course_id", courseId.toString()], - ["user_id", userId.toString()], ]); const list = this.getList(params); return list?.find(annotation => annotation.title === title && annotation.id != savedAnnotationId) !== undefined; diff --git a/app/javascript/packs/saved_annotation.js b/app/javascript/packs/saved_annotation.js index a8a41425c6..e9b3e226c2 100644 --- a/app/javascript/packs/saved_annotation.js +++ b/app/javascript/packs/saved_annotation.js @@ -1 +1,2 @@ import "components/saved_annotations/saved_annotation_title_input"; +import "components/saved_annotations/new_saved_annotation"; diff --git a/app/views/annotations/_annotations_table.html.erb b/app/views/annotations/_annotations_table.html.erb index 4c539b6c55..45447b1fc0 100644 --- a/app/views/annotations/_annotations_table.html.erb +++ b/app/views/annotations/_annotations_table.html.erb @@ -1,3 +1,4 @@ +<%= javascript_include_tag "saved_annotation" %>
    @@ -5,6 +6,7 @@ + @@ -37,6 +39,15 @@ <%= t "time.ago", time: time_ago_in_words(annotation.created_at) %> + <% end %> From 5b89a173db85067d0c283a122c18bbd2dd7ec7ab Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Mon, 18 Sep 2023 10:28:43 +0200 Subject: [PATCH 05/19] Redirect to list after annotation save --- .../saved_annotations/new_saved_annotation.ts | 13 +++++++++++++ app/controllers/saved_annotations_controller.rb | 1 + app/javascript/packs/saved_annotation.js | 3 +++ app/views/annotations/_annotations_table.html.erb | 3 +++ 4 files changed, 20 insertions(+) diff --git a/app/assets/javascripts/components/saved_annotations/new_saved_annotation.ts b/app/assets/javascripts/components/saved_annotations/new_saved_annotation.ts index 805955f9c4..4ff5820363 100644 --- a/app/assets/javascripts/components/saved_annotations/new_saved_annotation.ts +++ b/app/assets/javascripts/components/saved_annotations/new_saved_annotation.ts @@ -7,6 +7,7 @@ import { modalMixin } from "components/modal_mixin"; import { isBetaCourse } from "saved_annotation_beta"; import { exerciseState } from "state/Exercises"; import { courseState } from "state/Courses"; +import { search } from "search"; /** * This component represents an creation button for a saved annotation @@ -70,6 +71,8 @@ export class NewSavedAnnotation extends modalMixin(ShadowlessLitElement) { }); this.errors = undefined; this.hideModal(); + const event = new CustomEvent("annotation-saved", { bubbles: true, composed: true }); + this.dispatchEvent(event); } catch (errors) { this.errors = errors; } @@ -110,3 +113,13 @@ export class NewSavedAnnotation extends modalMixin(ShadowlessLitElement) { ` : html``; } } + +export function initNewSavedAnnotationButtons(path: string): void { + const newSavedAnnotationElements = document.querySelectorAll("d-new-saved-annotation"); + newSavedAnnotationElements.forEach((element: NewSavedAnnotation) => { + element.addEventListener("annotation-saved", () => { + // redirect to the saved annotation list + window.location.href = path; + }); + }); +} diff --git a/app/controllers/saved_annotations_controller.rb b/app/controllers/saved_annotations_controller.rb index 36209c1596..4102501cd2 100644 --- a/app/controllers/saved_annotations_controller.rb +++ b/app/controllers/saved_annotations_controller.rb @@ -39,6 +39,7 @@ def show def new authorize SavedAnnotation @annotations = AnnotationPolicy::Scope.new(current_user, Annotation.all).resolve + @annotations = @annotations.where(saved_annotation_id: nil) @courses = Course.where(id: @annotations.joins(:submission).pluck("submissions.course_id").uniq) @exercises = Activity.where(id: @annotations.joins(:submission).pluck("submissions.exercise_id").uniq) @annotations = apply_scopes(@annotations) diff --git a/app/javascript/packs/saved_annotation.js b/app/javascript/packs/saved_annotation.js index e9b3e226c2..33bc316188 100644 --- a/app/javascript/packs/saved_annotation.js +++ b/app/javascript/packs/saved_annotation.js @@ -1,2 +1,5 @@ import "components/saved_annotations/saved_annotation_title_input"; import "components/saved_annotations/new_saved_annotation"; +import { initNewSavedAnnotationButtons } from "components/saved_annotations/new_saved_annotation"; + +dodona.initNewSavedAnnotationButtons = initNewSavedAnnotationButtons; diff --git a/app/views/annotations/_annotations_table.html.erb b/app/views/annotations/_annotations_table.html.erb index 45447b1fc0..4a43fa0b3a 100644 --- a/app/views/annotations/_annotations_table.html.erb +++ b/app/views/annotations/_annotations_table.html.erb @@ -59,3 +59,6 @@

    <% end %>
    <%= page_navigation_links annotations, true, "annotations" %>
    + From 62b215320f1407f1c861641237f3e3794b72aaae Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Mon, 18 Sep 2023 10:38:07 +0200 Subject: [PATCH 06/19] Only show save buuton when relevant --- .../saved_annotations_controller.rb | 2 +- .../annotations/_annotations_table.html.erb | 33 +++++++++++-------- app/views/saved_annotations/new.html.erb | 2 +- app/views/saved_annotations/new.js.erb | 2 +- 4 files changed, 23 insertions(+), 16 deletions(-) diff --git a/app/controllers/saved_annotations_controller.rb b/app/controllers/saved_annotations_controller.rb index 4102501cd2..98daebd81f 100644 --- a/app/controllers/saved_annotations_controller.rb +++ b/app/controllers/saved_annotations_controller.rb @@ -39,7 +39,7 @@ def show def new authorize SavedAnnotation @annotations = AnnotationPolicy::Scope.new(current_user, Annotation.all).resolve - @annotations = @annotations.where(saved_annotation_id: nil) + @annotations = @annotations.where(saved_annotation_id: nil).where(user_id: current_user.id) @courses = Course.where(id: @annotations.joins(:submission).pluck("submissions.course_id").uniq) @exercises = Activity.where(id: @annotations.joins(:submission).pluck("submissions.exercise_id").uniq) @annotations = apply_scopes(@annotations) diff --git a/app/views/annotations/_annotations_table.html.erb b/app/views/annotations/_annotations_table.html.erb index 4a43fa0b3a..575a56d227 100644 --- a/app/views/annotations/_annotations_table.html.erb +++ b/app/views/annotations/_annotations_table.html.erb @@ -1,4 +1,5 @@ <%= javascript_include_tag "saved_annotation" %> +<% allow_save = local_assigns.fetch :allow_save, false %>
    <%= t ".submission" %> <%= t ".annotation_text" %> <%= t ".time" %>
    + + +
    @@ -6,7 +7,9 @@ - + <% if allow_save %> + + <% end %> @@ -39,15 +42,17 @@ <%= t "time.ago", time: time_ago_in_words(annotation.created_at) %> - + <% if allow_save %> + + <% end %> <% end %> @@ -59,6 +64,8 @@

    <% end %>
    <%= page_navigation_links annotations, true, "annotations" %>
    - +<% if allow_save %> + +<% end %> diff --git a/app/views/saved_annotations/new.html.erb b/app/views/saved_annotations/new.html.erb index 49f1220a54..7919e9a573 100644 --- a/app/views/saved_annotations/new.html.erb +++ b/app/views/saved_annotations/new.html.erb @@ -7,7 +7,7 @@
    <%= render partial: 'layouts/searchbar', locals: { refresh_element: "#annotation-container", courses: @courses, exercises: @exercises } %>
    - <%= render partial: 'annotations/annotations_table', locals: { annotations: @annotations } %> + <%= render partial: 'annotations/annotations_table', locals: { annotations: @annotations, allow_save: true } %>
    diff --git a/app/views/saved_annotations/new.js.erb b/app/views/saved_annotations/new.js.erb index 9b951ee249..437a9c43ef 100644 --- a/app/views/saved_annotations/new.js.erb +++ b/app/views/saved_annotations/new.js.erb @@ -1 +1 @@ -dodona.setHTMLExecuteScripts(document.querySelector("#questions-table-wrapper"), "<%= escape_javascript(render partial: 'annotations/annotations_table', locals: { annotations: @annotations }) %>"); +dodona.setHTMLExecuteScripts(document.querySelector("#questions-table-wrapper"), "<%= escape_javascript(render partial: 'annotations/annotations_table', locals: { annotations: @annotations, allow_save: true }) %>"); From 373218868dfb8041614df3d379c36053519ef4d8 Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Mon, 18 Sep 2023 15:30:54 +0200 Subject: [PATCH 07/19] Don's show empty list item --- .../components/annotations/user_annotation.ts | 5 ++--- .../saved_annotations/new_saved_annotation.ts | 17 ++--------------- 2 files changed, 4 insertions(+), 18 deletions(-) diff --git a/app/assets/javascripts/components/annotations/user_annotation.ts b/app/assets/javascripts/components/annotations/user_annotation.ts index 8e73a7b9f5..e981ee5ff0 100644 --- a/app/assets/javascripts/components/annotations/user_annotation.ts +++ b/app/assets/javascripts/components/annotations/user_annotation.ts @@ -153,14 +153,13 @@ export class UserAnnotationComponent extends i18nMixin(ShadowlessLitElement) { `); } - if (this.data.permission.save && isBetaCourse()) { + if (this.data.permission.save && isBetaCourse() && !this.data.saved_annotation_id) { options.push(html`
  • + annotation-text="${this.data.annotation_text}">
  • `); diff --git a/app/assets/javascripts/components/saved_annotations/new_saved_annotation.ts b/app/assets/javascripts/components/saved_annotations/new_saved_annotation.ts index 4ff5820363..536d8e701b 100644 --- a/app/assets/javascripts/components/saved_annotations/new_saved_annotation.ts +++ b/app/assets/javascripts/components/saved_annotations/new_saved_annotation.ts @@ -4,10 +4,8 @@ import { ShadowlessLitElement } from "components/meta/shadowless_lit_element"; import { SavedAnnotation, savedAnnotationState } from "state/SavedAnnotations"; import "./saved_annotation_form"; import { modalMixin } from "components/modal_mixin"; -import { isBetaCourse } from "saved_annotation_beta"; import { exerciseState } from "state/Exercises"; import { courseState } from "state/Courses"; -import { search } from "search"; /** * This component represents an creation button for a saved annotation @@ -17,7 +15,6 @@ import { search } from "search"; * * @prop {Number} fromAnnotationId - the id of the annotation which will be saved * @prop {String} annotationText - the original text of the annotation which wil be saved - * @prop {Number} savedAnnotationId - the id of the saved annotation */ @customElement("d-new-saved-annotation") export class NewSavedAnnotation extends modalMixin(ShadowlessLitElement) { @@ -25,8 +22,6 @@ export class NewSavedAnnotation extends modalMixin(ShadowlessLitElement) { fromAnnotationId: number; @property({ type: String, attribute: "annotation-text" }) annotationText: string; - @property({ type: Number, attribute: "saved-annotation-id" }) - savedAnnotationId: number; @property({ type: Number, attribute: "exercise-id" }) exerciseId: number = exerciseState.id; @property({ type: Number, attribute: "course-id" }) @@ -37,14 +32,6 @@ export class NewSavedAnnotation extends modalMixin(ShadowlessLitElement) { savedAnnotation: SavedAnnotation; - get isAlreadyLinked(): boolean { - return this.savedAnnotationId != undefined; - } - - get linkedSavedAnnotation(): SavedAnnotation { - return savedAnnotationState.get(this.savedAnnotationId); - } - get newSavedAnnotation(): SavedAnnotation { return { id: undefined, @@ -105,12 +92,12 @@ export class NewSavedAnnotation extends modalMixin(ShadowlessLitElement) { } render(): TemplateResult { - return !(this.isAlreadyLinked && this.linkedSavedAnnotation) ? html` + return html` ${I18n.t("js.saved_annotation.new.button_title")} - ` : html``; + `; } } From f5af19d86f35d6effade58ad7d35292d1f4f4d2c Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Mon, 18 Sep 2023 16:03:57 +0200 Subject: [PATCH 08/19] Sort annotations --- app/controllers/saved_annotations_controller.rb | 11 ++++++++--- app/javascript/packs/saved_annotation.js | 3 +++ app/models/annotation.rb | 3 +++ app/views/annotations/_annotations_table.html.erb | 7 +++++-- app/views/saved_annotations/show.html.erb | 4 +++- app/views/saved_annotations/show.js.erb | 1 + 6 files changed, 23 insertions(+), 6 deletions(-) create mode 100644 app/views/saved_annotations/show.js.erb diff --git a/app/controllers/saved_annotations_controller.rb b/app/controllers/saved_annotations_controller.rb index 98daebd81f..e152fe268f 100644 --- a/app/controllers/saved_annotations_controller.rb +++ b/app/controllers/saved_annotations_controller.rb @@ -11,7 +11,7 @@ class SavedAnnotationsController < ApplicationController has_scope :by_exercise, as: :exercise_id, only: :new has_scope :by_course, as: :course_id, only: :new - order_by :annotations_count, :title, :annotation_text + order_by :annotations_count, :title, :annotation_text, :created_at def index authorize SavedAnnotation @@ -30,9 +30,14 @@ def show format.html do @title = @saved_annotation.title @crumbs = [[I18n.t('saved_annotations.index.title'), saved_annotations_path], [@saved_annotation.title, saved_annotation_path(@saved_annotation)]] - @annotations = @saved_annotation.annotations.paginate(page: parse_pagination_param(params[:page])) + @annotations = apply_scopes(@saved_annotation.annotations.order_by_created_at(:DESC)) + .paginate(page: parse_pagination_param(params[:page])) end format.json + format.js do + @annotations = apply_scopes(@saved_annotation.annotations.order_by_created_at(:DESC)) + .paginate(page: parse_pagination_param(params[:page])) + end end end @@ -42,7 +47,7 @@ def new @annotations = @annotations.where(saved_annotation_id: nil).where(user_id: current_user.id) @courses = Course.where(id: @annotations.joins(:submission).pluck("submissions.course_id").uniq) @exercises = Activity.where(id: @annotations.joins(:submission).pluck("submissions.exercise_id").uniq) - @annotations = apply_scopes(@annotations) + @annotations = apply_scopes(@annotations.order_by_created_at(:DESC)) .includes(:course).includes(:user).includes(:submission) .paginate(page: parse_pagination_param(params[:page]), per_page: parse_pagination_param(params[:per_page])) diff --git a/app/javascript/packs/saved_annotation.js b/app/javascript/packs/saved_annotation.js index 33bc316188..713285d4b9 100644 --- a/app/javascript/packs/saved_annotation.js +++ b/app/javascript/packs/saved_annotation.js @@ -1,5 +1,8 @@ import "components/saved_annotations/saved_annotation_title_input"; import "components/saved_annotations/new_saved_annotation"; +import "components/search/sort_button"; import { initNewSavedAnnotationButtons } from "components/saved_annotations/new_saved_annotation"; +import { search } from "search"; dodona.initNewSavedAnnotationButtons = initNewSavedAnnotationButtons; +dodona.initSortButtons = () => search.autoSearch = true; diff --git a/app/models/annotation.rb b/app/models/annotation.rb index 17f63bcc59..cdb0b33107 100644 --- a/app/models/annotation.rb +++ b/app/models/annotation.rb @@ -59,6 +59,9 @@ class Annotation < ApplicationRecord scope :by_exercise_name, ->(name) { where(submission: Submission.by_exercise_name(name)) } scope :by_exercise, ->(exercise_id) { where(submission: { exercise_id: exercise_id }) } + scope :order_by_annotation_text, ->(direction) { reorder(annotation_text: direction) } + scope :order_by_created_at, ->(direction) { reorder(created_at: direction) } + before_validation :set_last_updated_by, on: :create before_validation :set_course_id, on: :create after_create :annotate_submission diff --git a/app/views/annotations/_annotations_table.html.erb b/app/views/annotations/_annotations_table.html.erb index 575a56d227..a7350e40f5 100644 --- a/app/views/annotations/_annotations_table.html.erb +++ b/app/views/annotations/_annotations_table.html.erb @@ -6,7 +6,7 @@
    - + <% if allow_save %> <% end %> @@ -34,7 +34,7 @@
    <%= t ".submission" %> <%= t ".annotation_text" %> <%= t ".time" %>
    - - - + + +
    <%= t ".submission" %> <%= t ".annotation_text" %><%= t ".time" %><%= t ".time" %> - <%= markdown annotation.annotation_text %> + <%= annotation.annotation_text %> @@ -69,3 +69,6 @@ dodona.ready.then(() => dodona.initNewSavedAnnotationButtons("<%= saved_annotations_path %>")); <% end %> + diff --git a/app/views/saved_annotations/show.html.erb b/app/views/saved_annotations/show.html.erb index 223a48a827..56b056bcc9 100644 --- a/app/views/saved_annotations/show.html.erb +++ b/app/views/saved_annotations/show.html.erb @@ -22,7 +22,9 @@

    <%= t ".linked_submissions" %>

    - <%= render partial: 'annotations/annotations_table', locals: { annotations: @annotations } %> +
    + <%= render partial: 'annotations/annotations_table', locals: { annotations: @annotations } %> +
    diff --git a/app/views/saved_annotations/show.js.erb b/app/views/saved_annotations/show.js.erb new file mode 100644 index 0000000000..9b951ee249 --- /dev/null +++ b/app/views/saved_annotations/show.js.erb @@ -0,0 +1 @@ +dodona.setHTMLExecuteScripts(document.querySelector("#questions-table-wrapper"), "<%= escape_javascript(render partial: 'annotations/annotations_table', locals: { annotations: @annotations }) %>"); From a3c795a8ea2023e448cf31a0db1cf0881c2a609a Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Tue, 19 Sep 2023 09:23:45 +0200 Subject: [PATCH 09/19] Remove index page --- app/controllers/annotations_controller.rb | 22 +--------------------- app/views/annotations/index.html.erb | 19 ------------------- app/views/annotations/index.js.erb | 1 - 3 files changed, 1 insertion(+), 41 deletions(-) delete mode 100644 app/views/annotations/index.html.erb delete mode 100644 app/views/annotations/index.js.erb diff --git a/app/controllers/annotations_controller.rb b/app/controllers/annotations_controller.rb index 8bc36edf3a..c6230825b5 100644 --- a/app/controllers/annotations_controller.rb +++ b/app/controllers/annotations_controller.rb @@ -12,27 +12,7 @@ class AnnotationsController < ApplicationController def index authorize Annotation - @annotations = policy_scope(Annotation.all) - - respond_to do |format| - format.html do - @title = I18n.t('annotations.index.title') - @crumbs = [[I18n.t('annotations.index.title'), annotations_path]] - @courses = Course.where(id: @annotations.joins(:submission).pluck("submissions.course_id").uniq) - @exercises = Activity.where(id: @annotations.joins(:submission).pluck("submissions.exercise_id").uniq) - @annotations = apply_scopes(@annotations) - .includes(:course).includes(:user).includes(:submission) - .paginate(page: parse_pagination_param(params[:page]), per_page: parse_pagination_param(params[:per_page])) - end - format.json do - @annotations = apply_scopes(@annotations).where(thread_root_id: nil) - end - format.js do - @annotations = apply_scopes(@annotations) - .includes(:course).includes(:user).includes(:submission) - .paginate(page: parse_pagination_param(params[:page]), per_page: parse_pagination_param(params[:per_page])) - end - end + @annotations = apply_scopes(policy_scope(Annotation.all)).where(thread_root_id: nil) end def question_index diff --git a/app/views/annotations/index.html.erb b/app/views/annotations/index.html.erb deleted file mode 100644 index b9317d4bea..0000000000 --- a/app/views/annotations/index.html.erb +++ /dev/null @@ -1,19 +0,0 @@ -
    -
    -
    -
    -

    - - <%= t ".title" %> - -

    -
    -
    - <%= render partial: 'layouts/searchbar', locals: { refresh_element: "#annotation-container", courses: @courses, exercises: @exercises } %> -
    - <%= render partial: 'annotations_table', locals: { annotations: @annotations } %> -
    -
    -
    -
    -
    diff --git a/app/views/annotations/index.js.erb b/app/views/annotations/index.js.erb deleted file mode 100644 index cf51556e43..0000000000 --- a/app/views/annotations/index.js.erb +++ /dev/null @@ -1 +0,0 @@ -dodona.setHTMLExecuteScripts(document.querySelector("#questions-table-wrapper"), "<%= escape_javascript(render partial: 'annotations_table', locals: { annotations: @annotations }) %>"); From c5dc4886fb4e6992cf0f7ce251d254ae7d0b03ef Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Tue, 19 Sep 2023 09:33:03 +0200 Subject: [PATCH 10/19] Improve comments --- .../components/saved_annotations/new_saved_annotation.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/assets/javascripts/components/saved_annotations/new_saved_annotation.ts b/app/assets/javascripts/components/saved_annotations/new_saved_annotation.ts index 536d8e701b..f36810c005 100644 --- a/app/assets/javascripts/components/saved_annotations/new_saved_annotation.ts +++ b/app/assets/javascripts/components/saved_annotations/new_saved_annotation.ts @@ -15,6 +15,8 @@ import { courseState } from "state/Courses"; * * @prop {Number} fromAnnotationId - the id of the annotation which will be saved * @prop {String} annotationText - the original text of the annotation which wil be saved + * @prop {Number} exerciseId - the id of the exercise to which the annotation belongs + * @prop {Number} courseId - the id of the course to which the annotation belongs */ @customElement("d-new-saved-annotation") export class NewSavedAnnotation extends modalMixin(ShadowlessLitElement) { From 62a195dc9a9d905a52c831d1f3a8413aff25763b Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Tue, 19 Sep 2023 09:34:13 +0200 Subject: [PATCH 11/19] Undo unnneeded change --- .../components/saved_annotations/new_saved_annotation.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/components/saved_annotations/new_saved_annotation.ts b/app/assets/javascripts/components/saved_annotations/new_saved_annotation.ts index f36810c005..5f729b776b 100644 --- a/app/assets/javascripts/components/saved_annotations/new_saved_annotation.ts +++ b/app/assets/javascripts/components/saved_annotations/new_saved_annotation.ts @@ -43,13 +43,13 @@ export class NewSavedAnnotation extends modalMixin(ShadowlessLitElement) { }; } - isTitleTaken(): boolean { + get isTitleTaken(): boolean { return savedAnnotationState.isTitleTaken( this.newSavedAnnotation.title, this.exerciseId, this.courseId); } async createSavedAnnotation(): Promise { - if (this.isTitleTaken() && !confirm(I18n.t("js.saved_annotation.confirm_title_taken"))) { + if (this.isTitleTaken && !confirm(I18n.t("js.saved_annotation.confirm_title_taken"))) { return; } From 4230b4e96fe36ce283fda4e5d0f593985949a9e9 Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Tue, 19 Sep 2023 09:37:04 +0200 Subject: [PATCH 12/19] Add comments --- .../components/saved_annotations/saved_annotation_form.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/assets/javascripts/components/saved_annotations/saved_annotation_form.ts b/app/assets/javascripts/components/saved_annotations/saved_annotation_form.ts index c4d0599279..1fac73683c 100644 --- a/app/assets/javascripts/components/saved_annotations/saved_annotation_form.ts +++ b/app/assets/javascripts/components/saved_annotations/saved_annotation_form.ts @@ -11,6 +11,8 @@ import "components/saved_annotations/saved_annotation_title_input"; * @element d-saved-annotation-form * * @prop {SavedAnnotation} savedAnnotation - the saved annotation to be edited in this form + * @prop {Number} exerciseId - the id of the exercise to which the annotation belongs + * @prop {Number} courseId - the id of the course to which the annotation belongs * * @fires change - on user changes in the form, event.detail has the new state of the SavedAnnotation */ From c08a5c737b1da83d8e4d545f329986622fe0f895 Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Tue, 19 Sep 2023 09:54:46 +0200 Subject: [PATCH 13/19] PRefilter saved annotations on the link --- .../components/saved_annotations/saved_annotation_input.ts | 6 +++++- app/assets/javascripts/i18n/translations.json | 4 ++-- config/locales/js/en.yml | 2 +- config/locales/js/nl.yml | 2 +- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/app/assets/javascripts/components/saved_annotations/saved_annotation_input.ts b/app/assets/javascripts/components/saved_annotations/saved_annotation_input.ts index 7ca105c82c..b31161e522 100644 --- a/app/assets/javascripts/components/saved_annotations/saved_annotation_input.ts +++ b/app/assets/javascripts/components/saved_annotations/saved_annotation_input.ts @@ -61,6 +61,10 @@ export class SavedAnnotationInput extends ShadowlessLitElement { return this.savedAnnotations.map(sa => ({ label: sa.title, value: sa.id.toString(), extra: sa.annotation_text })); } + get savedAnnotationsPath(): string { + return `/saved_annotations?course_id=${courseState.id}&exercise_id=${exerciseState.id}`; + } + get icon(): string { if (this.selectedAnnotation == undefined) { return ""; @@ -103,7 +107,7 @@ export class SavedAnnotationInput extends ShadowlessLitElement { ` : ""}
    - ${unsafeHTML(I18n.t("js.saved_annotation.input.help_html"))} + ${unsafeHTML(I18n.t("js.saved_annotation.input.help_html", { path: this.savedAnnotationsPath }))}
    `; diff --git a/app/assets/javascripts/i18n/translations.json b/app/assets/javascripts/i18n/translations.json index a0562324fa..62a2532254 100644 --- a/app/assets/javascripts/i18n/translations.json +++ b/app/assets/javascripts/i18n/translations.json @@ -317,7 +317,7 @@ }, "input": { "edited": "The current comment differs from the saved comment", - "help_html": "Click here to manage all saved comments.", + "help_html": "Click here to manage all saved comments.", "placeholder": "Search saved comment", "title": "Reuse a saved comment" }, @@ -837,7 +837,7 @@ }, "input": { "edited": "De huidige opmerking verschilt van de opgeslagen opmerking", - "help_html": "Klik hier om alle opgeslagen opmerkingen te beheren.", + "help_html": "Klik hier om alle opgeslagen opmerkingen te beheren.", "placeholder": "Zoek opgeslagen opmerking", "title": "Hergebruik een opgeslagen opmerking" }, diff --git a/config/locales/js/en.yml b/config/locales/js/en.yml index dd11bf813f..309f49c326 100644 --- a/config/locales/js/en.yml +++ b/config/locales/js/en.yml @@ -292,7 +292,7 @@ en: input: placeholder: Search saved comment title: 'Reuse a saved comment' - help_html: 'Click here to manage all saved comments.' + help_html: 'Click here to manage all saved comments.' edited: The current comment differs from the saved comment sign_in_search_bar: institution_search: "Type to search for your institution" diff --git a/config/locales/js/nl.yml b/config/locales/js/nl.yml index de2c50a1f2..9aad3a64df 100644 --- a/config/locales/js/nl.yml +++ b/config/locales/js/nl.yml @@ -292,7 +292,7 @@ nl: input: placeholder: Zoek opgeslagen opmerking title: 'Hergebruik een opgeslagen opmerking' - help_html: 'Klik hier om alle opgeslagen opmerkingen te beheren.' + help_html: 'Klik hier om alle opgeslagen opmerkingen te beheren.' edited: De huidige opmerking verschilt van de opgeslagen opmerking sign_in_search_bar: institution_search: "Typ om jouw school te vinden" From 13637be26d2f0561e6ca8858e60a498e58efcf7d Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Tue, 19 Sep 2023 09:59:29 +0200 Subject: [PATCH 14/19] Fix filtering on saved annotation index page --- app/controllers/saved_annotations_controller.rb | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/controllers/saved_annotations_controller.rb b/app/controllers/saved_annotations_controller.rb index e152fe268f..a133680cf4 100644 --- a/app/controllers/saved_annotations_controller.rb +++ b/app/controllers/saved_annotations_controller.rb @@ -8,9 +8,6 @@ class SavedAnnotationsController < ApplicationController has_scope :by_exercise, as: 'exercise_id' has_scope :by_filter, as: 'filter' - has_scope :by_exercise, as: :exercise_id, only: :new - has_scope :by_course, as: :course_id, only: :new - order_by :annotations_count, :title, :annotation_text, :created_at def index From d6aa587ee40cfbe3d7edda923760a7b11345a14a Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Tue, 19 Sep 2023 10:31:42 +0200 Subject: [PATCH 15/19] Keep scope when creating new saved annotation --- .../saved_annotations/new_saved_annotation.ts | 3 +- .../new_saved_annotation_link.ts | 39 +++++++++++++++++++ app/javascript/packs/saved_annotation.js | 1 + app/views/saved_annotations/index.html.erb | 3 +- 4 files changed, 44 insertions(+), 2 deletions(-) create mode 100644 app/assets/javascripts/components/saved_annotations/new_saved_annotation_link.ts diff --git a/app/assets/javascripts/components/saved_annotations/new_saved_annotation.ts b/app/assets/javascripts/components/saved_annotations/new_saved_annotation.ts index 5f729b776b..16a5e80b84 100644 --- a/app/assets/javascripts/components/saved_annotations/new_saved_annotation.ts +++ b/app/assets/javascripts/components/saved_annotations/new_saved_annotation.ts @@ -44,8 +44,9 @@ export class NewSavedAnnotation extends modalMixin(ShadowlessLitElement) { } get isTitleTaken(): boolean { + const annotation = this.savedAnnotation || this.newSavedAnnotation; return savedAnnotationState.isTitleTaken( - this.newSavedAnnotation.title, this.exerciseId, this.courseId); + annotation.title, this.exerciseId, this.courseId); } async createSavedAnnotation(): Promise { diff --git a/app/assets/javascripts/components/saved_annotations/new_saved_annotation_link.ts b/app/assets/javascripts/components/saved_annotations/new_saved_annotation_link.ts new file mode 100644 index 0000000000..e9ac47a07f --- /dev/null +++ b/app/assets/javascripts/components/saved_annotations/new_saved_annotation_link.ts @@ -0,0 +1,39 @@ +import { searchQueryState } from "state/SearchQuery"; +import { updateURLParameter } from "utilities"; +import { html, TemplateResult } from "lit"; +import { customElement } from "lit/decorators.js"; +import { ShadowlessLitElement } from "components/meta/shadowless_lit_element"; + +/** + * This component represents a link to the new saved annotations page + * It is responsible for adding the course_id and exercise_id query parameters to the link + */ +@customElement("d-new-saved-annotation-link") +export class NewSavedAnnotationLink extends ShadowlessLitElement { + get courseId(): string { + return searchQueryState.queryParams.get("course_id"); + } + + get exerciseId(): string { + return searchQueryState.queryParams.get("exercise_id"); + } + + get path(): string { + let path = "/saved_annotations/new"; + if (this.courseId) { + path = updateURLParameter(path, "course_id", this.courseId); + } + if (this.exerciseId) { + path = updateURLParameter(path, "exercise_id", this.exerciseId); + } + return path; + } + + render(): TemplateResult { + return html` + + + + `; + } +} diff --git a/app/javascript/packs/saved_annotation.js b/app/javascript/packs/saved_annotation.js index 713285d4b9..b7a58c03f1 100644 --- a/app/javascript/packs/saved_annotation.js +++ b/app/javascript/packs/saved_annotation.js @@ -1,6 +1,7 @@ import "components/saved_annotations/saved_annotation_title_input"; import "components/saved_annotations/new_saved_annotation"; import "components/search/sort_button"; +import "components/saved_annotations/new_saved_annotation_link"; import { initNewSavedAnnotationButtons } from "components/saved_annotations/new_saved_annotation"; import { search } from "search"; diff --git a/app/views/saved_annotations/index.html.erb b/app/views/saved_annotations/index.html.erb index 962a50f1ce..a02435d104 100644 --- a/app/views/saved_annotations/index.html.erb +++ b/app/views/saved_annotations/index.html.erb @@ -1,3 +1,4 @@ +<%= javascript_include_tag "saved_annotation" %>
    @@ -5,7 +6,7 @@

    <%= t ".title" %>

    <% if policy(SavedAnnotation).new? %>
    - <%= render 'fab_link', url: new_saved_annotation_path, icon: 'plus' %> +
    <% end %>
    From 50dc49cf0e3651c0937219e95f0d591e52a6e728 Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Tue, 19 Sep 2023 10:35:46 +0200 Subject: [PATCH 16/19] Fix linting --- .../components/saved_annotations/new_saved_annotation.ts | 2 ++ app/controllers/saved_annotations_controller.rb | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/components/saved_annotations/new_saved_annotation.ts b/app/assets/javascripts/components/saved_annotations/new_saved_annotation.ts index 16a5e80b84..4ea5d42eb4 100644 --- a/app/assets/javascripts/components/saved_annotations/new_saved_annotation.ts +++ b/app/assets/javascripts/components/saved_annotations/new_saved_annotation.ts @@ -6,6 +6,8 @@ import "./saved_annotation_form"; import { modalMixin } from "components/modal_mixin"; import { exerciseState } from "state/Exercises"; import { courseState } from "state/Courses"; +import { searchQueryState } from "state/SearchQuery"; +import { updateURLParameter } from "utilities"; /** * This component represents an creation button for a saved annotation diff --git a/app/controllers/saved_annotations_controller.rb b/app/controllers/saved_annotations_controller.rb index a133680cf4..4051969233 100644 --- a/app/controllers/saved_annotations_controller.rb +++ b/app/controllers/saved_annotations_controller.rb @@ -42,8 +42,8 @@ def new authorize SavedAnnotation @annotations = AnnotationPolicy::Scope.new(current_user, Annotation.all).resolve @annotations = @annotations.where(saved_annotation_id: nil).where(user_id: current_user.id) - @courses = Course.where(id: @annotations.joins(:submission).pluck("submissions.course_id").uniq) - @exercises = Activity.where(id: @annotations.joins(:submission).pluck("submissions.exercise_id").uniq) + @courses = Course.where(id: @annotations.joins(:submission).pluck('submissions.course_id').uniq) + @exercises = Activity.where(id: @annotations.joins(:submission).pluck('submissions.exercise_id').uniq) @annotations = apply_scopes(@annotations.order_by_created_at(:DESC)) .includes(:course).includes(:user).includes(:submission) .paginate(page: parse_pagination_param(params[:page]), per_page: parse_pagination_param(params[:per_page])) From 2002434f9ce9a3ba3e9d2aa0a8e30cbeed0db60a Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Wed, 20 Sep 2023 11:46:39 +0200 Subject: [PATCH 17/19] Use outline button --- app/views/annotations/_annotations_table.html.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/annotations/_annotations_table.html.erb b/app/views/annotations/_annotations_table.html.erb index a7350e40f5..580126bd77 100644 --- a/app/views/annotations/_annotations_table.html.erb +++ b/app/views/annotations/_annotations_table.html.erb @@ -45,7 +45,7 @@ <% if allow_save %>
    Date: Thu, 21 Sep 2023 10:52:30 +0200 Subject: [PATCH 18/19] Remove unused imports --- .../components/saved_annotations/new_saved_annotation.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/assets/javascripts/components/saved_annotations/new_saved_annotation.ts b/app/assets/javascripts/components/saved_annotations/new_saved_annotation.ts index 4ea5d42eb4..16a5e80b84 100644 --- a/app/assets/javascripts/components/saved_annotations/new_saved_annotation.ts +++ b/app/assets/javascripts/components/saved_annotations/new_saved_annotation.ts @@ -6,8 +6,6 @@ import "./saved_annotation_form"; import { modalMixin } from "components/modal_mixin"; import { exerciseState } from "state/Exercises"; import { courseState } from "state/Courses"; -import { searchQueryState } from "state/SearchQuery"; -import { updateURLParameter } from "utilities"; /** * This component represents an creation button for a saved annotation From 61f86f9fb8af9550d382bc97fb91fe46264c37f6 Mon Sep 17 00:00:00 2001 From: jorg-vr Date: Thu, 21 Sep 2023 10:54:19 +0200 Subject: [PATCH 19/19] fix translations --- .../components/saved_annotations/new_saved_annotation.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/components/saved_annotations/new_saved_annotation.ts b/app/assets/javascripts/components/saved_annotations/new_saved_annotation.ts index 16a5e80b84..cf5a6acc16 100644 --- a/app/assets/javascripts/components/saved_annotations/new_saved_annotation.ts +++ b/app/assets/javascripts/components/saved_annotations/new_saved_annotation.ts @@ -6,6 +6,7 @@ import "./saved_annotation_form"; import { modalMixin } from "components/modal_mixin"; import { exerciseState } from "state/Exercises"; import { courseState } from "state/Courses"; +import { i18nMixin } from "components/meta/i18n_mixin"; /** * This component represents an creation button for a saved annotation @@ -19,7 +20,7 @@ import { courseState } from "state/Courses"; * @prop {Number} courseId - the id of the course to which the annotation belongs */ @customElement("d-new-saved-annotation") -export class NewSavedAnnotation extends modalMixin(ShadowlessLitElement) { +export class NewSavedAnnotation extends i18nMixin(modalMixin(ShadowlessLitElement)) { @property({ type: Number, attribute: "from-annotation-id" }) fromAnnotationId: number; @property({ type: String, attribute: "annotation-text" })