diff --git a/app/assets/javascripts/components/modal_mixin.ts b/app/assets/javascripts/components/modal_mixin.ts new file mode 100644 index 0000000000..d385b0dc01 --- /dev/null +++ b/app/assets/javascripts/components/modal_mixin.ts @@ -0,0 +1,69 @@ +import { html, TemplateResult, render } from "lit"; +import { ref } from "lit/directives/ref.js"; +import { Modal as Modal } from "bootstrap"; +import { ShadowlessLitElement } from "components/shadowless_lit_element"; + +export declare abstract class ModalMixinInterface { + modalTemplate(title: TemplateResult, body: TemplateResult, footer: TemplateResult): TemplateResult; + abstract get filledModalTemplate() :TemplateResult; + showModal(): void; + hideModal(): void; +} + +type Constructor = abstract new (...args: any[]) => T; + +export function modalMixin>(superClass: T): Constructor & T { + abstract class ModalMixinClass extends superClass implements ModalMixinInterface { + modal: Modal; + + private initModal(el: Element): void { + if (!this.modal) { + this.modal = new Modal(el); + } else { + this.modal.handleUpdate(); + } + } + + modalTemplate(title: TemplateResult, body: TemplateResult, footer: TemplateResult): TemplateResult { + return html` + `; + } + + abstract get filledModalTemplate() :TemplateResult; + + update(changedProperties: Map): void { + super.update(changedProperties); + this.renderModal(); + } + + private renderModal(): void { + render(this.filledModalTemplate, document.getElementById("modal-container"), { host: this }); + } + + showModal(): void { + this.renderModal(); + this.modal?.show(); + } + + hideModal(): void { + this.modal?.hide(); + } + } + + return ModalMixinClass as Constructor & T; +} diff --git a/app/assets/javascripts/components/saved_annotations/edit_saved_annotation.ts b/app/assets/javascripts/components/saved_annotations/edit_saved_annotation.ts new file mode 100644 index 0000000000..d481018a4c --- /dev/null +++ b/app/assets/javascripts/components/saved_annotations/edit_saved_annotation.ts @@ -0,0 +1,61 @@ +import { customElement, property } from "lit/decorators.js"; +import { html, TemplateResult } from "lit"; +import { ShadowlessLitElement } from "components/shadowless_lit_element"; +import { SavedAnnotation, updateSavedAnnotation } from "state/SavedAnnotations"; +import "./saved_annotation_form"; +import { modalMixin } from "components/modal_mixin"; + +@customElement("d-edit-saved-annotation") +export class EditSavedAnnotation extends modalMixin(ShadowlessLitElement) { + @property({ type: Object }) + savedAnnotation: SavedAnnotation; + + @property({ state: true }) + errors: string[]; + + async updateSavedAnnotation(): Promise { + try { + await updateSavedAnnotation(this.savedAnnotation.id, { + saved_annotation: this.savedAnnotation + }); + this.errors = undefined; + this.hideModal(); + } catch (errors) { + this.errors = errors; + } + } + + get filledModalTemplate(): TemplateResult { + return this.modalTemplate(html` + ${I18n.t("js.saved_annotation.edit.title")} + `, html` + ${this.errors !== undefined ? html` +
+

${I18n.t("js.saved_annotation.edit.errors", {count: this.errors.length})}

+
    + ${this.errors.map(error => html` +
  • ${error}
  • `)} +
+
+ ` : ""} + this.savedAnnotation = e.detail} + > + `, html` + + `); + } + + render(): TemplateResult { + return html` + this.showModal()} + > + + `; + } +} 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 f88b5e87e1..c93a878a99 100644 --- a/app/assets/javascripts/components/saved_annotations/new_saved_annotation.ts +++ b/app/assets/javascripts/components/saved_annotations/new_saved_annotation.ts @@ -2,12 +2,11 @@ import { customElement, property } from "lit/decorators.js"; import { html, TemplateResult } from "lit"; import { ShadowlessLitElement } from "components/shadowless_lit_element"; import { createSavedAnnotation, SavedAnnotation } from "state/SavedAnnotations"; -import { ref } from "lit/directives/ref.js"; -import { Modal } from "bootstrap"; import "./saved_annotation_form"; +import { modalMixin } from "components/modal_mixin"; @customElement("d-new-saved-annotation") -export class NewSavedAnnotation extends ShadowlessLitElement { +export class NewSavedAnnotation extends modalMixin(ShadowlessLitElement) { @property({ type: Number, attribute: "from-annotation-id" }) fromAnnotationId: number; @property({ type: String, attribute: "annotation-text" }) @@ -17,7 +16,6 @@ export class NewSavedAnnotation extends ShadowlessLitElement { errors: string[]; savedAnnotation: SavedAnnotation; - modal: Modal; get newSavedAnnotation(): SavedAnnotation { return { @@ -34,54 +32,43 @@ export class NewSavedAnnotation extends ShadowlessLitElement { saved_annotation: this.savedAnnotation }); this.errors = undefined; - this.modal?.hide(); + this.hideModal(); } catch (errors) { this.errors = errors; } } - initModal(el: Element): void { - if (!this.modal) { - this.modal = new Modal(el); - } + get filledModalTemplate(): TemplateResult { + return this.modalTemplate(html` + ${I18n.t("js.saved_annotation.new.title")} + `, html` + ${this.errors !== undefined ? html` +
+

${I18n.t("js.saved_annotation.new.errors", {count: this.errors.length})}

+
    + ${this.errors.map(error => html` +
  • ${error}
  • `)} +
+
+ ` : ""} + this.savedAnnotation = e.detail} + > + `, html` + + `); } render(): TemplateResult { return html` this.modal.show()} + @click=${() => this.showModal()} > - - `; + `; } } diff --git a/app/assets/javascripts/components/saved_annotations/saved_annotation_list.ts b/app/assets/javascripts/components/saved_annotations/saved_annotation_list.ts index a9c4122804..9d60acd6e5 100644 --- a/app/assets/javascripts/components/saved_annotations/saved_annotation_list.ts +++ b/app/assets/javascripts/components/saved_annotations/saved_annotation_list.ts @@ -3,6 +3,7 @@ import { html, TemplateResult } from "lit"; import { ShadowlessLitElement } from "components/shadowless_lit_element"; import { getSavedAnnotations, SavedAnnotation } from "state/SavedAnnotations"; import { stateMixin } from "state/StateMixin"; +import "./edit_saved_annotation"; @customElement("d-saved-annotation-list") export class SavedAnnotationList extends stateMixin(ShadowlessLitElement) { @@ -24,7 +25,7 @@ export class SavedAnnotationList extends stateMixin(ShadowlessLitElement) { } render(): TemplateResult { - return html` + return this.savedAnnotations.length > 0 ? html`

${I18n.t("js.saved_annotation.list.title")} @@ -35,9 +36,7 @@ export class SavedAnnotationList extends stateMixin(ShadowlessLitElement) { ${sa.title} - - - + @@ -47,6 +46,6 @@ export class SavedAnnotationList extends stateMixin(ShadowlessLitElement) {

- `; + ` : html``; } } diff --git a/app/assets/javascripts/i18n/translations.js b/app/assets/javascripts/i18n/translations.js index 1ead94d0e8..24e99cadeb 100644 --- a/app/assets/javascripts/i18n/translations.js +++ b/app/assets/javascripts/i18n/translations.js @@ -1,3 +1,3 @@ I18n.translations || (I18n.translations = {}); -I18n.translations["en"] = I18n.extend((I18n.translations["en"] || {}), JSON.parse('{"date":{"abbr_day_names":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"abbr_month_names":[null,"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"day_names":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"formats":{"default":"%Y-%m-%d","long":"%B %d, %Y","monthday_long":"%B %d","short":"%b %d","weekday_long":"%A %B %d","weekday_short":"%a %b %d"},"month_names":[null,"January","February","March","April","May","June","July","August","September","October","November","December"],"order":["year","month","day"]},"js":{"actions":"Actions","activity-added-failed":"Adding activity failed","activity-added-success":"Activity added","activity-removed-failed":"Removing activity failed","activity-removed-success":"Activity removed","activity_types":"Activity types","annotation":{"hidden":{"plural":"%{count} hidden annotations","single":"Hidden annotation"},"type":{"error":"Error","info":"Notice","question":"Question","user":"Comment","warning":"Warning"}},"attempts":"attempts","code":{"copy-to-clipboard":"Copy code to clipboard"},"coding_scratchpad":{"copy_code":"Copy to Dodona","overwrite_code":"Do you want to overwrite the code in the scratchpad with your latest changes?"},"configured-institution":"the configured institution","copy-fail":"Press Ctrl-C to copy","copy-success":"Copied!","copy-to-clipboard":"Click to copy","course_labels":"Member labels","courses":"Courses","ctimeseries_desc":"This graph shows the evolution of the percentage of students that correctly solved each exercise.","ctimeseries_title":"Users with at least one correct submission","date_before":"before","date_on":"on","description_languages":"Language of the description","dropdown":{"multi":{"activity_types":"Activity types","course_labels":"Member labels","courses":"Courses","description_languages":"Languages","event_types":"Event types","institutions":"Institutions","judges":"Judges","labels":"Labels","programming_languages":"Programming Languages","question_states":"Statuses","repositories":"Repositories","statuses":"Statuses"},"single":{"activity_types":"Activity type","course_labels":"Member label","courses":"Course","description_languages":"Language","event_types":"Event type","institutions":"Institution","judges":"Judge","labels":"Label","programming_languages":"Programming Language","question_states":"Status","repositories":"Repository","statuses":"Status"}},"en":"English","event_types":"Event types","favorite-course-do":"Favorite","favorite-course-failed":"Favoriting course failed","favorite-course-succeeded":"Favorited course","institutions":"Institutions","judges":"Judges","label-undeletable":"This label can\'t be deleted because it was set in the dirconfig file of a parent directory of the learning activity.","labels":"Labels","loading":"Loading...","machine_annotation":{"external_url":"more information"},"mark_as_read":"Mark as read","mark_as_unread":"Mark as unread","mean":"average","months":{"long":{"apr":"April","aug":"August","dec":"December","feb":"February","jan":"January","jul":"July","jun":"June","mar":"March","may":"May","nov":"November","oct":"October","sep":"September"},"short":{"apr":"Apr","aug":"Aug","dec":"Dec","feb":"Feb","jan":"Jan","jul":"Jul","jun":"Jun","mar":"Mar","may":"May","nov":"Nov","oct":"Oct","sep":"Sep"}},"n_submissions":"Number of submissions","nl":"Dutch","no_data":"There is not enough data to create a graph.","no_selection":"You must select at least one item before being able to download solutions.","options":"Options","programming_languages":"Programming Languages","question":{"state":{"answered":"Answered","in_progress":"In progress","unanswered":"Unanswered"}},"question_states":"Status","repositories":"Repositories","saved_annotation":{"annotation_text":"Text","form":{"markdown_html":"\\u003ca href=\\"https://docs.dodona.be/en/references/exercise-description/#markdown\\" target=\\"_blank\\"\\u003eMarkdown\\u003c/a\\u003e is supported."},"list":{"title":"Saved comments"},"new":{"button_title":"Save comment","errors":"%{count} errors prevented saving","save":"Save","title":"Save comment for re-use"},"title":"Title"},"score":{"confirm":"Are you sure?","conflict":"This score was already changed by another user","unknown":"An unexpected error occured when saving the score"},"score_item":{"error":"Error while updating"},"stacked_desc":"This graph shows the distribution of submissions statuses.","stacked_title":"Distribution of submission statuses","status":{"compilation_error":"compilation error","correct":"correct","memory_limit_exceeded":"memory limit exceeded","output_limit_exceeded":"output limit exceeded","runtime_error":"runtime error","time_limit_exceeded":"time limit exceeded","wrong":"wrong"},"statuses":"Statuses","submission":"submission","submission-emoji":"Oops, something went wrong while saving your solution. Your solution contains special symbols such as emoji or math characters that were probably copied from the problem description. Please remove them and try again. If you can\'t find the problem, contact the teaching assistant.","submission-failed":"Oops, something went wrong while saving your solution. Reload this page, try again, or contact the teaching assistant.","submission-network":"Oops, we\'re unable to connect to the server. Please check your internet connection.","submission-not-allowed":"Oops, you are not allowed to submit a solution to this exercise.","submission-processed":"Evaluation finished","submission-rate-limit":"Oops, you\'re submitting too fast! Please wait a moment between each submission.","submission-saved":"Submission saved","submission-too-long":"Oops, something went wrong while saving your solution. Your solution is too long. Try to make it shorter and try again.","submission_motivational_message":{"1":"Congratulations!","2":"You did it!","3":"Keep up the good work!","4":"Nice!","5":"Good job!","6":"Well done!"},"submissions":"submissions","submissions_on":"submissions on","timeseries_desc":"This graph shows on which moments the students submitted the most solutions.","timeseries_title":"Submissions over time","total":"total","tutor-failed":"Something went wrong while loading the online python tutor","unfavorite-course-do":"Unfavorite","unfavorite-course-failed":"Unfavoriting course failed","unfavorite-course-succeeded":"Unfavorited course","unknown-error-loading-feedback":"An unknown error occurred while loading the feedback for your submission","user":"user","user_annotation":{"cancel":"Cancel","delete":"Delete","delete_confirm":"Are you sure you want to delete this comment?","edit":"Edit comment","fields":{"annotation_text":"Comment"},"help":"Press Shift + Enter to send. \\u003ca href=\\"https://docs.dodona.be/en/references/exercise-description/#markdown\\" target=\\"_blank\\"\\u003eMarkdown\\u003c/a\\u003e is supported.","meta":"%{user} · %{time}","not_released":"This comment is not yet visible for students","save":"Save comment","send":"Comment","update":"Update","use_saved":"Saved comment"},"user_question":{"conflict":"The status of this question was already changed by another user","delete_confirm":"Are you sure you want to delete this question?","deleted":"This question was deleted by another user","edit":"Edit question","has_newer_submission":"There was a newer submission since this question. Click here to go to the latest submission.","in_progress":"Mark question as in progress","meta_else":"%{user} · %{time} · %{state} by %{last}","meta_unanswered":"%{user} · %{time} · %{state}","resolve":"Mark as answered","send":"Ask question","unresolve":"Re-open question","update":"Update question"},"users":"users","violin_desc":"This graph shows the distribution of the number of submissions. The larger the zone, the more students submitted that number of solutions. The dot indicates the average. When the average is larger than 20, the dot turns hollow.","violin_title":"Number of submissions per user","weekdays":{"long":{"fri":"Friday","mon":"Monday","sat":"Saturday","sun":"Sunday","thu":"Thursday","tue":"Tuesday","wed":"Wednesday"},"short":{"fri":"Fr","mon":"Mo","sat":"Sa","sun":"Su","thu":"Th","tue":"Tu","wed":"We"}},"with":"with"},"time":{"am":"am","formats":{"annotation":"%B %d, %Y %H:%M","announcement":"%B %d, %Y %H:%M","default":"%a, %d %b %Y %H:%M:%S %z","flatpickr_long":"F j Y H:i","flatpickr_short":"m/d/Y H:i","long":"%B %d, %Y %H:%M","plain_time":"%H:%M","question":"%B %d, %Y %H:%M:%S","read_state":"%B %d, %Y %H:%M:%S","short":"%d %b %H:%M","submission":"%B %d, %Y %H:%M:%S","us":"%m/%d/%Y %I:%M %p"},"pm":"pm","units":{"day":["day","days"],"hour":["hour","hours"],"min":["minute","minutes"],"sec":["second","seconds"],"week":["week","weeks"]}}}')); -I18n.translations["nl"] = I18n.extend((I18n.translations["nl"] || {}), JSON.parse('{"date":{"abbr_day_names":["Zo","Ma","Di","Wo","Do","Vr","Za"],"abbr_month_names":[null,"Jan","Feb","Mrt","Apr","Mei","Jun","Jul","Aug","Sep","Okt","Nov","Dec"],"day_names":["zondag","maandag","dinsdag","woensdag","donderdag","vrijdag","zaterdag"],"formats":{"default":"%d-%m-%Y","long":"%e %B %Y","monthday_long":"%B %d","short":"%e %b","weekday_long":"%A %d %B","weekday_short":"%a %d %b"},"month_names":[null,"januari","februari","maart","april","mei","juni","juli","augustus","september","oktober","november","december"],"order":["day","month","year"]},"js":{"actions":"Acties","activity-added-failed":"Leeractiviteit toevoegen mislukt","activity-added-success":"Leeractiviteit toegevoegd","activity-removed-failed":"Leeractiviteit verwijderen mislukt","activity-removed-success":"Leeractiviteit verwijderd","activity_types":"Activiteitstypes","annotation":{"hidden":{"plural":"%{count} verborgen annotaties","single":"Verborgen annotatie"},"type":{"error":"Fout","info":"Info","question":"Vraag","user":"Opmerking","warning":"Waarschuwing"}},"attempts":"pogingen","code":{"copy-to-clipboard":"Kopieer code naar klembord"},"coding_scratchpad":{"copy_code":"Kopieer naar Dodona","overwrite_code":"Wil je de code in het kladblok overschrijven met je laatste aanpassingen?"},"configured-institution":"de ingestelde onderwijsinstelling","copy-fail":"Druk Ctrl-C om te kopiëren","copy-success":"Gekopieerd!","copy-to-clipboard":"Klik om te kopiëren","course_labels":"Gebruikerlabels","courses":"Cursussen","ctimeseries_desc":"Deze grafiek geeft de evolutie weer van het percentage studenten dat een oefening correct had.","ctimeseries_title":"Gebruikers met minstens één correcte oplossing","date_before":"voor","date_on":"op","description_languages":"Taal van de beschrijving","dropdown":{"multi":{"activity_types":"Activiteitstypes","course_labels":"Gebruikerlabels","courses":"Cursussen","description_languages":"Talen","event_types":"Event types","institutions":"Onderwijsinstellingen","judges":"Judges","labels":"Labels","programming_languages":"Programmeertalen","question_states":"Toestanden","repositories":"Repository\'s","statuses":"Statussen"},"single":{"activity_types":"Activiteitstype","course_labels":"Gebruikerlabel","courses":"Cursus","description_languages":"Taal","event_types":"Event type","institutions":"Onderwijsinstelling","judges":"Judge","labels":"Label","programming_languages":"Programmeertaal","question_states":"Toestand","repositories":"Repository","statuses":"Status"}},"en":"Engels","event_types":"Event types","favorite-course-do":"Voeg toe aan favorieten","favorite-course-failed":"Cursus aan favorieten toevoegen mislukt","favorite-course-succeeded":"Cursus toegevoegd aan favorieten","institutions":"Onderwijsinstellingen","judges":"Judges","label-undeletable":"Dit label kan niet verwijderd worden omdat het werd ingesteld in het dirconfig bestand van een bovenliggende map.","labels":"Labels","loading":"Aan het laden...","machine_annotation":{"external_url":"meer informatie"},"mark_as_read":"Als gelezen markeren","mark_as_unread":"Als ongelezen markeren","mean":"gemiddeld aantal","months":{"long":{"apr":"April","aug":"Augustus","dec":"December","feb":"Februari","jan":"Januari","jul":"Juli","jun":"Juni","mar":"Maart","may":"Mei","nov":"November","oct":"Oktober","sep":"September"},"short":{"apr":"Apr","aug":"Aug","dec":"Dec","feb":"Feb","jan":"Jan","jul":"Jul","jun":"Jun","mar":"Maa","may":"Mei","nov":"Nov","oct":"Okt","sep":"Sep"}},"n_submissions":"Aantal ingediende oplossingen","nl":"Nederlands","no_data":"Er is niet genoeg data om een grafiek te maken.","no_selection":"Er moet ten minste één iets gekozen worden om oplossingen te kunnen downloaden.","options":"Opties","programming_languages":"Programmeertalen","question":{"state":{"answered":"Beantwoord","in_progress":"In behandeling","unanswered":"Onbeantwoord"}},"question_states":"Toestand","repositories":"Repository\'s","saved_annotation":{"annotation_text":"Tekst","form":{"markdown_html":"\\u003ca href=\\"https://docs.dodona.be/nl/references/exercise-description/#markdown\\" target=\\"_blank\\"\\u003eMarkdown\\u003c/a\\u003e wordt ondersteund."},"list":{"title":"Opgeslagen opmerkingen"},"new":{"button_title":"Opmerking opslaan","errors":"%{count} errors verhinderen het opslaan","save":"Opslaan","title":"Opmerking opslaan om later te hergebruiken"},"title":"Titel"},"score":{"confirm":"Bent u zeker?","conflict":"Deze score is al aangepast door een andere gebruiker","unknown":"Een onverwachte fout trad op bij het opslaan van de score"},"score_item":{"error":"Fout bij bijwerken"},"stacked_desc":"Deze grafiek geeft de verdeling van de oplossingsstatussen per oefening weer.","stacked_title":"Verdeling van de oplossingsstatus","status":{"compilation_error":"compilatiefout","correct":"correct","memory_limit_exceeded":"geheugenlimiet overschreden","output_limit_exceeded":"uitvoerlimiet overschreden","runtime_error":"uitvoeringsfout","time_limit_exceeded":"tijdslimiet overschreden","wrong":"fout"},"statuses":"Statussen","submission":"oplossing","submission-emoji":"Oeps, er ging iets fout bij het opslaan van je oplossing. Je oplossing bevat speciale tekens zoals emoji of wiskundige symbolen die je uit de opgave gekopieerd hebt. Verwijder deze en probeer opnieuw. Contacteer de assistent als je het probleem niet vindt.","submission-failed":"Oeps, er ging iets fout bij het opslaan van je oplossing. Herlaad de pagina, probeer opnieuw, of contacteer de assistent.","submission-network":"Oeps, we kunnen niet verbinden met de server. Controleer je internetverbinding.","submission-not-allowed":"Oeps, je bent niet toegelaten om een oplossing in te dienen voor deze oefening.","submission-processed":"Evaluatie afgerond","submission-rate-limit":"Oeps, je bent te snel! Wacht eventjes tussen het indienen van een nieuwe oplossing.","submission-saved":"Oplossing opgeslagen","submission-too-long":"Oeps, er ging iets fout bij het opslaan van je oplossing. Je oplossing is te lang. Maak ze korter en probeer opnieuw.","submission_motivational_message":{"1":"Proficiat!","2":"Goed bezig!","3":"Doe zo verder!","4":"Mooi!","5":"Puik werk!","6":"Goed gedaan!"},"submissions":"oplossingen","submissions_on":"ingediende oplossingen op","timeseries_desc":"Deze grafiek toont op welke momenten het meest aan een bepaalde oefening werd gewerkt.","timeseries_title":"Ingediende oplossingen over tijd","total":"totaal aantal","tutor-failed":"Er ging iets fout bij het laden van de online python tutor","unfavorite-course-do":"Verwijder uit favorieten","unfavorite-course-failed":"Cursus uit favorieten verwijderen mislukt","unfavorite-course-succeeded":"Cursus verwijderd uit favorieten","unknown-error-loading-feedback":"Er is een onbekende fout opgetreden bij het laden van de feedback voor je oplossing","user":"gebruiker","user_annotation":{"cancel":"Annuleren","delete":"Verwijderen","delete_confirm":"Ben je zeker dat je deze opmerking wil verwijderen?","edit":"Opmerking bewerken","fields":{"annotation_text":"Opmerking"},"help":"Druk Shift + Enter om te verzenden. \\u003ca href=\\"https://docs.dodona.be/nl/references/exercise-description/#markdown\\" target=\\"_blank\\"\\u003eMarkdown\\u003c/a\\u003e wordt ondersteund.","meta":"%{user} · %{time}","not_released":"Deze opmerking is nog niet zichtbaar voor studenten","save":"Opmerking opslaan","send":"Toevoegen","update":"Updaten","use_saved":"Opgeslagen opmerking"},"user_question":{"conflict":"De status van deze vraag is al aangepast door een andere gebruiker","delete_confirm":"Ben je zeker dat je deze vraag wilt verwijderen?","deleted":"Deze vraag is verwijderd door een andere gebruiker","edit":"Vraag aanpassen","has_newer_submission":"Er is een nieuwe oplosssing ingediend sinds deze vraag. Klik hier om naar de nieuwste oplossing te gaan.","in_progress":"In behandeling markeren","meta_else":"%{user} · %{time} · %{state} door %{last}","meta_unanswered":"%{user} · %{time} · %{state}","resolve":"Als beantwoord markeren","send":"Vraag stellen","unresolve":"Vraag terug openen","update":"Vraag aanpassen"},"users":"gebruikers","violin_desc":"Deze grafiek geeft de verdeling weer van het aantal ingediende oplossingen. Hoe dikker de zone op de grafiek, hoe meer studenten dat aantal pogingen nodig had. Het bolletje geeft het gemiddelde aan. Wanneer het gemiddelde groter dan 20 is, wordt het bolletje hol.","violin_title":"Aantal oplossingen per gebruiker","weekdays":{"long":{"fri":"Vrijdag","mon":"Maandag","sat":"Zaterdag","sun":"Zondag","thu":"Donderdag","tue":"Dinsdag","wed":"Woensdag"},"short":{"fri":"Vr","mon":"Ma","sat":"Za","sun":"Zo","thu":"Do","tue":"Di","wed":"Wo"}},"with":"met"},"time":{"am":"\'s ochtends","formats":{"annotation":"%e %B %Y, %H:%M","announcement":"%e %B %Y, %H:%M","default":"%a %d %b %Y %H:%M:%S %Z","flatpickr_long":"j F Y H:i","flatpickr_short":"d/m/Y H:i","long":"%d %B %Y %H:%M","plain_time":"%H:%M","question":"%d %B %Y %H:%M:%S","read_state":"%d %B %Y %H:%M:%S","short":"%d %b %H:%M","submission":"%d %B %Y %H:%M:%S"},"pm":"\'s middags","units":{"day":["dag","dagen"],"hour":["uur","uren"],"min":["minuut","minuten"],"sec":["seconde","seconden"],"week":["week","weken"]}}}')); +I18n.translations["en"] = I18n.extend((I18n.translations["en"] || {}), JSON.parse('{"date":{"abbr_day_names":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"abbr_month_names":[null,"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"day_names":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"formats":{"default":"%Y-%m-%d","long":"%B %d, %Y","monthday_long":"%B %d","short":"%b %d","weekday_long":"%A %B %d","weekday_short":"%a %b %d"},"month_names":[null,"January","February","March","April","May","June","July","August","September","October","November","December"],"order":["year","month","day"]},"js":{"actions":"Actions","activity-added-failed":"Adding activity failed","activity-added-success":"Activity added","activity-removed-failed":"Removing activity failed","activity-removed-success":"Activity removed","activity_types":"Activity types","annotation":{"hidden":{"plural":"%{count} hidden annotations","single":"Hidden annotation"},"type":{"error":"Error","info":"Notice","question":"Question","user":"Comment","warning":"Warning"}},"attempts":"attempts","code":{"copy-to-clipboard":"Copy code to clipboard"},"coding_scratchpad":{"copy_code":"Copy to Dodona","overwrite_code":"Do you want to overwrite the code in the scratchpad with your latest changes?"},"configured-institution":"the configured institution","copy-fail":"Press Ctrl-C to copy","copy-success":"Copied!","copy-to-clipboard":"Click to copy","course_labels":"Member labels","courses":"Courses","ctimeseries_desc":"This graph shows the evolution of the percentage of students that correctly solved each exercise.","ctimeseries_title":"Users with at least one correct submission","date_before":"before","date_on":"on","description_languages":"Language of the description","dropdown":{"multi":{"activity_types":"Activity types","course_labels":"Member labels","courses":"Courses","description_languages":"Languages","event_types":"Event types","institutions":"Institutions","judges":"Judges","labels":"Labels","programming_languages":"Programming Languages","question_states":"Statuses","repositories":"Repositories","statuses":"Statuses"},"single":{"activity_types":"Activity type","course_labels":"Member label","courses":"Course","description_languages":"Language","event_types":"Event type","institutions":"Institution","judges":"Judge","labels":"Label","programming_languages":"Programming Language","question_states":"Status","repositories":"Repository","statuses":"Status"}},"en":"English","event_types":"Event types","favorite-course-do":"Favorite","favorite-course-failed":"Favoriting course failed","favorite-course-succeeded":"Favorited course","institutions":"Institutions","judges":"Judges","label-undeletable":"This label can\'t be deleted because it was set in the dirconfig file of a parent directory of the learning activity.","labels":"Labels","loading":"Loading...","machine_annotation":{"external_url":"more information"},"mark_as_read":"Mark as read","mark_as_unread":"Mark as unread","mean":"average","months":{"long":{"apr":"April","aug":"August","dec":"December","feb":"February","jan":"January","jul":"July","jun":"June","mar":"March","may":"May","nov":"November","oct":"October","sep":"September"},"short":{"apr":"Apr","aug":"Aug","dec":"Dec","feb":"Feb","jan":"Jan","jul":"Jul","jun":"Jun","mar":"Mar","may":"May","nov":"Nov","oct":"Oct","sep":"Sep"}},"n_submissions":"Number of submissions","nl":"Dutch","no_data":"There is not enough data to create a graph.","no_selection":"You must select at least one item before being able to download solutions.","options":"Options","programming_languages":"Programming Languages","question":{"state":{"answered":"Answered","in_progress":"In progress","unanswered":"Unanswered"}},"question_states":"Status","repositories":"Repositories","saved_annotation":{"annotation_text":"Text","edit":{"button_title":"Edit saved comment","errors":"%{count} errors prevented saving","save":"Save","title":"Edit saved comment"},"form":{"markdown_html":"\\u003ca href=\\"https://docs.dodona.be/en/references/exercise-description/#markdown\\" target=\\"_blank\\"\\u003eMarkdown\\u003c/a\\u003e is supported."},"list":{"title":"Saved comments"},"new":{"button_title":"Save comment","errors":"%{count} errors prevented saving","save":"Save","title":"Save comment for re-use"},"title":"Title"},"score":{"confirm":"Are you sure?","conflict":"This score was already changed by another user","unknown":"An unexpected error occured when saving the score"},"score_item":{"error":"Error while updating"},"stacked_desc":"This graph shows the distribution of submissions statuses.","stacked_title":"Distribution of submission statuses","status":{"compilation_error":"compilation error","correct":"correct","memory_limit_exceeded":"memory limit exceeded","output_limit_exceeded":"output limit exceeded","runtime_error":"runtime error","time_limit_exceeded":"time limit exceeded","wrong":"wrong"},"statuses":"Statuses","submission":"submission","submission-emoji":"Oops, something went wrong while saving your solution. Your solution contains special symbols such as emoji or math characters that were probably copied from the problem description. Please remove them and try again. If you can\'t find the problem, contact the teaching assistant.","submission-failed":"Oops, something went wrong while saving your solution. Reload this page, try again, or contact the teaching assistant.","submission-network":"Oops, we\'re unable to connect to the server. Please check your internet connection.","submission-not-allowed":"Oops, you are not allowed to submit a solution to this exercise.","submission-processed":"Evaluation finished","submission-rate-limit":"Oops, you\'re submitting too fast! Please wait a moment between each submission.","submission-saved":"Submission saved","submission-too-long":"Oops, something went wrong while saving your solution. Your solution is too long. Try to make it shorter and try again.","submission_motivational_message":{"1":"Congratulations!","2":"You did it!","3":"Keep up the good work!","4":"Nice!","5":"Good job!","6":"Well done!"},"submissions":"submissions","submissions_on":"submissions on","timeseries_desc":"This graph shows on which moments the students submitted the most solutions.","timeseries_title":"Submissions over time","total":"total","tutor-failed":"Something went wrong while loading the online python tutor","unfavorite-course-do":"Unfavorite","unfavorite-course-failed":"Unfavoriting course failed","unfavorite-course-succeeded":"Unfavorited course","unknown-error-loading-feedback":"An unknown error occurred while loading the feedback for your submission","user":"user","user_annotation":{"cancel":"Cancel","delete":"Delete","delete_confirm":"Are you sure you want to delete this comment?","edit":"Edit comment","fields":{"annotation_text":"Comment"},"help":"Press Shift + Enter to send. \\u003ca href=\\"https://docs.dodona.be/en/references/exercise-description/#markdown\\" target=\\"_blank\\"\\u003eMarkdown\\u003c/a\\u003e is supported.","meta":"%{user} · %{time}","not_released":"This comment is not yet visible for students","save":"Save comment","send":"Comment","update":"Update","use_saved":"Saved comment"},"user_question":{"conflict":"The status of this question was already changed by another user","delete_confirm":"Are you sure you want to delete this question?","deleted":"This question was deleted by another user","edit":"Edit question","has_newer_submission":"There was a newer submission since this question. Click here to go to the latest submission.","in_progress":"Mark question as in progress","meta_else":"%{user} · %{time} · %{state} by %{last}","meta_unanswered":"%{user} · %{time} · %{state}","resolve":"Mark as answered","send":"Ask question","unresolve":"Re-open question","update":"Update question"},"users":"users","violin_desc":"This graph shows the distribution of the number of submissions. The larger the zone, the more students submitted that number of solutions. The dot indicates the average. When the average is larger than 20, the dot turns hollow.","violin_title":"Number of submissions per user","weekdays":{"long":{"fri":"Friday","mon":"Monday","sat":"Saturday","sun":"Sunday","thu":"Thursday","tue":"Tuesday","wed":"Wednesday"},"short":{"fri":"Fr","mon":"Mo","sat":"Sa","sun":"Su","thu":"Th","tue":"Tu","wed":"We"}},"with":"with"},"time":{"am":"am","formats":{"annotation":"%B %d, %Y %H:%M","announcement":"%B %d, %Y %H:%M","default":"%a, %d %b %Y %H:%M:%S %z","flatpickr_long":"F j Y H:i","flatpickr_short":"m/d/Y H:i","long":"%B %d, %Y %H:%M","plain_time":"%H:%M","question":"%B %d, %Y %H:%M:%S","read_state":"%B %d, %Y %H:%M:%S","short":"%d %b %H:%M","submission":"%B %d, %Y %H:%M:%S","us":"%m/%d/%Y %I:%M %p"},"pm":"pm","units":{"day":["day","days"],"hour":["hour","hours"],"min":["minute","minutes"],"sec":["second","seconds"],"week":["week","weeks"]}}}')); +I18n.translations["nl"] = I18n.extend((I18n.translations["nl"] || {}), JSON.parse('{"date":{"abbr_day_names":["Zo","Ma","Di","Wo","Do","Vr","Za"],"abbr_month_names":[null,"Jan","Feb","Mrt","Apr","Mei","Jun","Jul","Aug","Sep","Okt","Nov","Dec"],"day_names":["zondag","maandag","dinsdag","woensdag","donderdag","vrijdag","zaterdag"],"formats":{"default":"%d-%m-%Y","long":"%e %B %Y","monthday_long":"%B %d","short":"%e %b","weekday_long":"%A %d %B","weekday_short":"%a %d %b"},"month_names":[null,"januari","februari","maart","april","mei","juni","juli","augustus","september","oktober","november","december"],"order":["day","month","year"]},"js":{"actions":"Acties","activity-added-failed":"Leeractiviteit toevoegen mislukt","activity-added-success":"Leeractiviteit toegevoegd","activity-removed-failed":"Leeractiviteit verwijderen mislukt","activity-removed-success":"Leeractiviteit verwijderd","activity_types":"Activiteitstypes","annotation":{"hidden":{"plural":"%{count} verborgen annotaties","single":"Verborgen annotatie"},"type":{"error":"Fout","info":"Info","question":"Vraag","user":"Opmerking","warning":"Waarschuwing"}},"attempts":"pogingen","code":{"copy-to-clipboard":"Kopieer code naar klembord"},"coding_scratchpad":{"copy_code":"Kopieer naar Dodona","overwrite_code":"Wil je de code in het kladblok overschrijven met je laatste aanpassingen?"},"configured-institution":"de ingestelde onderwijsinstelling","copy-fail":"Druk Ctrl-C om te kopiëren","copy-success":"Gekopieerd!","copy-to-clipboard":"Klik om te kopiëren","course_labels":"Gebruikerlabels","courses":"Cursussen","ctimeseries_desc":"Deze grafiek geeft de evolutie weer van het percentage studenten dat een oefening correct had.","ctimeseries_title":"Gebruikers met minstens één correcte oplossing","date_before":"voor","date_on":"op","description_languages":"Taal van de beschrijving","dropdown":{"multi":{"activity_types":"Activiteitstypes","course_labels":"Gebruikerlabels","courses":"Cursussen","description_languages":"Talen","event_types":"Event types","institutions":"Onderwijsinstellingen","judges":"Judges","labels":"Labels","programming_languages":"Programmeertalen","question_states":"Toestanden","repositories":"Repository\'s","statuses":"Statussen"},"single":{"activity_types":"Activiteitstype","course_labels":"Gebruikerlabel","courses":"Cursus","description_languages":"Taal","event_types":"Event type","institutions":"Onderwijsinstelling","judges":"Judge","labels":"Label","programming_languages":"Programmeertaal","question_states":"Toestand","repositories":"Repository","statuses":"Status"}},"en":"Engels","event_types":"Event types","favorite-course-do":"Voeg toe aan favorieten","favorite-course-failed":"Cursus aan favorieten toevoegen mislukt","favorite-course-succeeded":"Cursus toegevoegd aan favorieten","institutions":"Onderwijsinstellingen","judges":"Judges","label-undeletable":"Dit label kan niet verwijderd worden omdat het werd ingesteld in het dirconfig bestand van een bovenliggende map.","labels":"Labels","loading":"Aan het laden...","machine_annotation":{"external_url":"meer informatie"},"mark_as_read":"Als gelezen markeren","mark_as_unread":"Als ongelezen markeren","mean":"gemiddeld aantal","months":{"long":{"apr":"April","aug":"Augustus","dec":"December","feb":"Februari","jan":"Januari","jul":"Juli","jun":"Juni","mar":"Maart","may":"Mei","nov":"November","oct":"Oktober","sep":"September"},"short":{"apr":"Apr","aug":"Aug","dec":"Dec","feb":"Feb","jan":"Jan","jul":"Jul","jun":"Jun","mar":"Maa","may":"Mei","nov":"Nov","oct":"Okt","sep":"Sep"}},"n_submissions":"Aantal ingediende oplossingen","nl":"Nederlands","no_data":"Er is niet genoeg data om een grafiek te maken.","no_selection":"Er moet ten minste één iets gekozen worden om oplossingen te kunnen downloaden.","options":"Opties","programming_languages":"Programmeertalen","question":{"state":{"answered":"Beantwoord","in_progress":"In behandeling","unanswered":"Onbeantwoord"}},"question_states":"Toestand","repositories":"Repository\'s","saved_annotation":{"annotation_text":"Tekst","edit":{"button_title":"Bewerk opgeslagen opmerking","errors":"%{count} errors verhinderen het opslaan","save":"Opslaan","title":"Bewerk opgeslagen opmerking"},"form":{"markdown_html":"\\u003ca href=\\"https://docs.dodona.be/nl/references/exercise-description/#markdown\\" target=\\"_blank\\"\\u003eMarkdown\\u003c/a\\u003e wordt ondersteund."},"list":{"title":"Opgeslagen opmerkingen"},"new":{"button_title":"Opmerking opslaan","errors":"%{count} errors verhinderen het opslaan","save":"Opslaan","title":"Opmerking opslaan om later te hergebruiken"},"title":"Titel"},"score":{"confirm":"Bent u zeker?","conflict":"Deze score is al aangepast door een andere gebruiker","unknown":"Een onverwachte fout trad op bij het opslaan van de score"},"score_item":{"error":"Fout bij bijwerken"},"stacked_desc":"Deze grafiek geeft de verdeling van de oplossingsstatussen per oefening weer.","stacked_title":"Verdeling van de oplossingsstatus","status":{"compilation_error":"compilatiefout","correct":"correct","memory_limit_exceeded":"geheugenlimiet overschreden","output_limit_exceeded":"uitvoerlimiet overschreden","runtime_error":"uitvoeringsfout","time_limit_exceeded":"tijdslimiet overschreden","wrong":"fout"},"statuses":"Statussen","submission":"oplossing","submission-emoji":"Oeps, er ging iets fout bij het opslaan van je oplossing. Je oplossing bevat speciale tekens zoals emoji of wiskundige symbolen die je uit de opgave gekopieerd hebt. Verwijder deze en probeer opnieuw. Contacteer de assistent als je het probleem niet vindt.","submission-failed":"Oeps, er ging iets fout bij het opslaan van je oplossing. Herlaad de pagina, probeer opnieuw, of contacteer de assistent.","submission-network":"Oeps, we kunnen niet verbinden met de server. Controleer je internetverbinding.","submission-not-allowed":"Oeps, je bent niet toegelaten om een oplossing in te dienen voor deze oefening.","submission-processed":"Evaluatie afgerond","submission-rate-limit":"Oeps, je bent te snel! Wacht eventjes tussen het indienen van een nieuwe oplossing.","submission-saved":"Oplossing opgeslagen","submission-too-long":"Oeps, er ging iets fout bij het opslaan van je oplossing. Je oplossing is te lang. Maak ze korter en probeer opnieuw.","submission_motivational_message":{"1":"Proficiat!","2":"Goed bezig!","3":"Doe zo verder!","4":"Mooi!","5":"Puik werk!","6":"Goed gedaan!"},"submissions":"oplossingen","submissions_on":"ingediende oplossingen op","timeseries_desc":"Deze grafiek toont op welke momenten het meest aan een bepaalde oefening werd gewerkt.","timeseries_title":"Ingediende oplossingen over tijd","total":"totaal aantal","tutor-failed":"Er ging iets fout bij het laden van de online python tutor","unfavorite-course-do":"Verwijder uit favorieten","unfavorite-course-failed":"Cursus uit favorieten verwijderen mislukt","unfavorite-course-succeeded":"Cursus verwijderd uit favorieten","unknown-error-loading-feedback":"Er is een onbekende fout opgetreden bij het laden van de feedback voor je oplossing","user":"gebruiker","user_annotation":{"cancel":"Annuleren","delete":"Verwijderen","delete_confirm":"Ben je zeker dat je deze opmerking wil verwijderen?","edit":"Opmerking bewerken","fields":{"annotation_text":"Opmerking"},"help":"Druk Shift + Enter om te verzenden. \\u003ca href=\\"https://docs.dodona.be/nl/references/exercise-description/#markdown\\" target=\\"_blank\\"\\u003eMarkdown\\u003c/a\\u003e wordt ondersteund.","meta":"%{user} · %{time}","not_released":"Deze opmerking is nog niet zichtbaar voor studenten","save":"Opmerking opslaan","send":"Toevoegen","update":"Updaten","use_saved":"Opgeslagen opmerking"},"user_question":{"conflict":"De status van deze vraag is al aangepast door een andere gebruiker","delete_confirm":"Ben je zeker dat je deze vraag wilt verwijderen?","deleted":"Deze vraag is verwijderd door een andere gebruiker","edit":"Vraag aanpassen","has_newer_submission":"Er is een nieuwe oplosssing ingediend sinds deze vraag. Klik hier om naar de nieuwste oplossing te gaan.","in_progress":"In behandeling markeren","meta_else":"%{user} · %{time} · %{state} door %{last}","meta_unanswered":"%{user} · %{time} · %{state}","resolve":"Als beantwoord markeren","send":"Vraag stellen","unresolve":"Vraag terug openen","update":"Vraag aanpassen"},"users":"gebruikers","violin_desc":"Deze grafiek geeft de verdeling weer van het aantal ingediende oplossingen. Hoe dikker de zone op de grafiek, hoe meer studenten dat aantal pogingen nodig had. Het bolletje geeft het gemiddelde aan. Wanneer het gemiddelde groter dan 20 is, wordt het bolletje hol.","violin_title":"Aantal oplossingen per gebruiker","weekdays":{"long":{"fri":"Vrijdag","mon":"Maandag","sat":"Zaterdag","sun":"Zondag","thu":"Donderdag","tue":"Dinsdag","wed":"Woensdag"},"short":{"fri":"Vr","mon":"Ma","sat":"Za","sun":"Zo","thu":"Do","tue":"Di","wed":"Wo"}},"with":"met"},"time":{"am":"\'s ochtends","formats":{"annotation":"%e %B %Y, %H:%M","announcement":"%e %B %Y, %H:%M","default":"%a %d %b %Y %H:%M:%S %Z","flatpickr_long":"j F Y H:i","flatpickr_short":"d/m/Y H:i","long":"%d %B %Y %H:%M","plain_time":"%H:%M","question":"%d %B %Y %H:%M:%S","read_state":"%d %B %Y %H:%M:%S","short":"%d %b %H:%M","submission":"%d %B %Y %H:%M:%S"},"pm":"\'s middags","units":{"day":["dag","dagen"],"hour":["uur","uren"],"min":["minuut","minuten"],"sec":["seconde","seconden"],"week":["week","weken"]}}}')); diff --git a/app/assets/javascripts/state/SavedAnnotations.ts b/app/assets/javascripts/state/SavedAnnotations.ts index e8882a65e9..d7fb0b8160 100644 --- a/app/assets/javascripts/state/SavedAnnotations.ts +++ b/app/assets/javascripts/state/SavedAnnotations.ts @@ -52,12 +52,20 @@ export async function createSavedAnnotation(data: { from: number, saved_annotati return savedAnnotation.id; } -export async function updateSavedAnnotation(number, id: number, data: {saved_annotation: SavedAnnotation}): Promise { +export async function updateSavedAnnotation(id: number, data: {saved_annotation: SavedAnnotation}): Promise { const url = `${URL}/${id}`; - await fetch(url, { + const response = await fetch(url, { method: "put", body: JSON.stringify(data), + headers: { + "X-CSRF-Token": $("meta[name='csrf-token']").attr("content"), + "Content-type": "application/json" + }, }); + if (response.status === 422) { + const errors = await response.json(); + throw errors; + } events.publish("fetchSavedAnnotations"); events.publish(`fetchSavedAnnotation${id}`, id); } diff --git a/app/controllers/saved_annotations_controller.rb b/app/controllers/saved_annotations_controller.rb index 21cfefc772..57257edc5b 100644 --- a/app/controllers/saved_annotations_controller.rb +++ b/app/controllers/saved_annotations_controller.rb @@ -33,7 +33,7 @@ def create def update respond_to do |format| - if @saved_annotation.update(args) + if @saved_annotation.update(permitted_attributes(SavedAnnotation)) format.json { render :show, status: :ok, location: @saved_annotation } else format.json { render json: @saved_annotation.errors, status: :unprocessable_entity } diff --git a/app/views/layouts/_main_container.html.erb b/app/views/layouts/_main_container.html.erb index 41c34f21fe..36aa9ebb3c 100644 --- a/app/views/layouts/_main_container.html.erb +++ b/app/views/layouts/_main_container.html.erb @@ -36,5 +36,6 @@ <% end %> + <%= yield %> diff --git a/config/locales/js/en.yml b/config/locales/js/en.yml index f10773bbdb..9ea8fe2a13 100644 --- a/config/locales/js/en.yml +++ b/config/locales/js/en.yml @@ -217,5 +217,10 @@ en: save: "Save" title: "Save comment for re-use" errors: "%{count} errors prevented saving" + edit: + button_title: 'Edit saved comment' + save: "Save" + title: "Edit saved comment" + errors: "%{count} errors prevented saving" list: title: Saved comments diff --git a/config/locales/js/nl.yml b/config/locales/js/nl.yml index 7ebc4b3df4..af1d2d21db 100644 --- a/config/locales/js/nl.yml +++ b/config/locales/js/nl.yml @@ -217,6 +217,11 @@ nl: save: "Opslaan" title: "Opmerking opslaan om later te hergebruiken" errors: "%{count} errors verhinderen het opslaan" + edit: + button_title: 'Bewerk opgeslagen opmerking' + save: "Opslaan" + title: "Bewerk opgeslagen opmerking" + errors: "%{count} errors verhinderen het opslaan" list: title: Opgeslagen opmerkingen