@@ -13,6 +13,12 @@ import loggedInTemplate from "./templates/header/loggedIn.html";
1313
1414// Module exports go here
1515import "./modules/dragDrop" ;
16+ import { t } from "./modules/i18n" ;
17+ import "./modules/i18n-ui" ;
18+ // You could also do this, to ensure that i18n is loaded before the rest of the app:
19+ // import initI18n from "./modules/i18n";
20+ // await initI18n();
21+ // But it can be bad.
1622
1723import Swal from "sweetalert2" ;
1824import { User } from "./models/User" ;
@@ -71,13 +77,13 @@ function queryElements() {
7177 finishedTasksDiv = contentDiv . querySelector ( "[data-group=finished]" ) ;
7278 finishedTasksUl = finishedTasksDiv . querySelector ( ".task-list" ) ;
7379
74- backlogAddButton = backlogTasksDiv . querySelector ( ". task-add-button" ) ;
80+ backlogAddButton = backlogTasksDiv . querySelector ( "#backlog- task-add-button" ) ;
7581 backlogSubmitButton = backlogTasksDiv . querySelector ( ".task-submit-button" ) ;
7682 addInputWrapper = contentDiv . querySelector ( ".add-input-wrapper" ) ;
7783 addInput = addInputWrapper . querySelector ( ".add-input" ) ;
7884}
7985
80- document . addEventListener ( "submit" , function ( event ) {
86+ document . addEventListener ( "submit" , async function ( event ) {
8187 if ( event . target . matches ( "#app-login-form" ) ) {
8288 event . preventDefault ( ) ;
8389 const formData = new FormData ( event . target ) ;
@@ -101,6 +107,7 @@ document.addEventListener("submit", function (event) {
101107 const userContextmenu = headerRight . querySelector ( ".user-contextmenu" ) ;
102108 const listItem = document . createElement ( "li" ) ;
103109 listItem . classList . add ( "contextmenu-item" ) ;
110+ listItem . dataset . i18n = "manage-users" ;
104111 listItem . textContent = "Manage users" ;
105112 listItem . dataset . action = "manage-users" ;
106113 userContextmenu . prepend ( listItem ) ;
@@ -125,7 +132,9 @@ document.addEventListener("submit", function (event) {
125132 const [ login , currentPassword , newPassword , newPasswordConfirmation ] =
126133 formData . values ( ) ;
127134 if ( login === appState . currentUser . login && ! newPassword ) {
128- swal . fire ( "You have not changed anything; aborting." ) ;
135+ swal . fire (
136+ t ( "not-changed-anything" , "You have not changed anything; aborting." ) ,
137+ ) ;
129138 return ;
130139 }
131140
@@ -135,26 +144,35 @@ document.addEventListener("submit", function (event) {
135144 }
136145
137146 if ( ! login ) {
138- invalidate ( loginInput , " The login must not be empty.") ;
147+ invalidate ( loginInput , t ( "empty-login" , " The login must not be empty.") ) ;
139148 }
140149 if ( newPassword ) {
141150 if ( ! currentPassword ) {
142151 invalidate (
143152 currentPasswordInput ,
144- "You must confirm your current password to change it." ,
153+ t (
154+ "must-confirm-current-password" ,
155+ "You must confirm your current password to change it." ,
156+ ) ,
145157 ) ;
146158 } else if ( currentPassword !== appState . currentUser . password ) {
147159 invalidate (
148160 currentPasswordInput ,
149- "The provided password does not match your password." ,
161+ t (
162+ "wrong-password" ,
163+ "The provided password does not match your password." ,
164+ ) ,
150165 ) ;
151166 } else if ( ! newPasswordConfirmation ) {
152167 invalidate (
153168 confirmNewPasswordInput ,
154- " You must confirm your new password.",
169+ t ( "must-confirm-new-password" , " You must confirm your new password.") ,
155170 ) ;
156171 } else if ( newPassword !== newPasswordConfirmation ) {
157- invalidate ( confirmNewPasswordInput , "Both passwords should match." ) ;
172+ invalidate (
173+ confirmNewPasswordInput ,
174+ t ( "passwords-do-not-match" , "Both passwords should match." ) ,
175+ ) ;
158176 }
159177 }
160178
@@ -207,14 +225,17 @@ document.body.addEventListener("keyup", (event) => {
207225} ) ;
208226
209227document . body . addEventListener ( "click" , ( event ) => {
210- if ( event . target === backlogAddButton ) {
211- if ( addInputWrapper . classList . contains ( "d-none" ) ) {
228+ if (
229+ backlogAddButton &&
230+ event . target . closest ( "#backlog-task-add-button" ) === backlogAddButton
231+ ) {
232+ if ( addInputWrapper . classList ?. contains ( "d-none" ) ) {
212233 addInputWrapper . classList . remove ( "d-none" ) ;
213234 }
214235 addInput . focus ( ) ;
215236 backlogAddButton . classList . add ( "d-none" ) ;
216237 backlogSubmitButton . classList . remove ( "d-none" ) ;
217- } else if ( event . target . matches ( ".task-add-button" ) ) {
238+ } else if ( event . target . closest ( ".task-add-button" ) ) {
218239 // Populate the select with valid options
219240 const sourceCategory = taskCategoryRelationships . get (
220241 event . target . closest ( ".task-group" ) . dataset . group ,
@@ -224,7 +245,9 @@ document.body.addEventListener("click", (event) => {
224245 appState . currentUser . canReadTask ( task ) &&
225246 task . category === sourceCategory ,
226247 ) ;
227- const select = event . target . previousElementSibling ;
248+ const select = event . target
249+ . closest ( ".task-group-controls" )
250+ . querySelector ( ".card-select" ) ;
228251 function createOption ( value , content , disabled = false , selected = false ) {
229252 const option = document . createElement ( "option" ) ;
230253 option . value = value ;
@@ -233,15 +256,17 @@ document.body.addEventListener("click", (event) => {
233256 if ( selected ) option . selected = "selected" ;
234257 return option ;
235258 }
236- const options = [ createOption ( "" , "Select a task..." , true , true ) ] ;
259+ const options = [
260+ createOption ( "" , t ( "select-a-task" , "Select a task..." ) , true , true ) ,
261+ ] ;
237262 for ( const task of sourceTasks ) {
238263 options . push ( createOption ( task . id , task . title ) ) ;
239264 }
240265
241266 select . innerHTML = "" ;
242267 select . append ( ...options ) ;
243268 select . classList . remove ( "d-none" ) ;
244- event . target . classList . add ( "d-none" ) ;
269+ event . target . closest ( ".task-add-button" ) . classList . add ( "d-none" ) ;
245270 } else if (
246271 event . target !== addInput &&
247272 ! addInputWrapper ?. classList . contains ( "d-none" ) &&
@@ -289,7 +314,9 @@ document.body.addEventListener("click", (event) => {
289314 break ;
290315 }
291316 default : {
292- alert ( `Unknown action ${ action } ` ) ;
317+ alert (
318+ `${ t ( "something-went-wrong" , "Something went wrong" ) } : unknown action ${ action } ` ,
319+ ) ;
293320 break ;
294321 }
295322 }
@@ -306,7 +333,7 @@ document.body.addEventListener("click", (event) => {
306333 let taskElement ;
307334 if ( ( taskElement = event . target . closest ( ".task" ) ) ) {
308335 showEditPopup (
309- taskElement . textContent ,
336+ taskElement . querySelector ( ".task-title" ) . textContent ,
310337 taskElement . dataset . description ,
311338 taskElement . dataset . id ,
312339 ) ;
@@ -329,24 +356,37 @@ document.body.addEventListener("click", (event) => {
329356 const sharedConfig = {
330357 showCancelButton : true ,
331358 inputValidator : ( value ) => {
332- if ( ! value ) return `The new ${ role } 's login cannot be empty.` ;
359+ if ( ! value )
360+ return t (
361+ "new-login-required" ,
362+ `The new ${ role } 's login cannot be empty.` ,
363+ { role } ,
364+ ) ;
333365 } ,
334366 } ;
335367
336368 const loginPromptResult = await swal . fire ( {
337- titleText : `Enter the new ${ role } 's login` ,
369+ titleText : t ( "enter-new-login" , `Enter the new ${ role } 's login` , {
370+ role,
371+ } ) ,
338372 input : "text" ,
339- inputLabel : ` Enter login:` ,
373+ inputLabel : t ( "enter-login" , " Enter login:" ) ,
340374 ...sharedConfig ,
341375 } ) ;
342376 if ( loginPromptResult . isDismissed ) {
343377 throw new Error ( ) ;
344378 }
345379
346380 const passwordPromptResult = await swal . fire ( {
347- titleText : `Enter ${ loginPromptResult . value } 's password` ,
381+ titleText : t (
382+ "enter-user-password" ,
383+ `Enter ${ loginPromptResult . value } 's password` ,
384+ {
385+ login : loginPromptResult . value ,
386+ } ,
387+ ) ,
348388 input : "password" ,
349- inputLabel : ` Enter password:` ,
389+ inputLabel : t ( "enter-password" , " Enter password:" ) ,
350390 ...sharedConfig ,
351391 } ) ;
352392 if ( passwordPromptResult . isDismissed ) {
@@ -370,22 +410,22 @@ document.body.addEventListener("click", (event) => {
370410 if ( userId === appState . currentUser . id ) userDeletingThemselves = true ;
371411
372412 const listItem = event . target . closest ( ".user-list-item" ) ;
373- const userRole = listItem . dataset . role ;
374413 const userLogin =
375414 listItem . querySelector ( ".user-list-login" ) . textContent ;
415+ const userRole = listItem . dataset . role ;
376416
377417 const userPresentation = userDeletingThemselves
378- ? " <b>yourself</b>"
379- : `the ${ userRole } ' ${ userLogin } ' ` ;
418+ ? ` <b>${ t ( " yourself" , "yourself" ) } </b>`
419+ : `${ t ( " the" , "the" ) } ${ t ( ` ${ userRole } -ya` , userRole ) } " ${ userLogin } " ` ;
380420 swal
381421 . fire ( {
382- title : " Confirm deletion",
422+ title : t ( "confirm-deletion" , " Confirm deletion") ,
383423 [ userDeletingThemselves ? "html" : "text" ] :
384- `Are you really sure you want to delete ${ userPresentation } ?` ,
424+ `${ t ( "sure-you-want-to-delete" , " Are you really sure you want to delete" ) } ${ userPresentation } ?` ,
385425 icon : "question" ,
386426 showDenyButton : true ,
387- confirmButtonText : " Delete",
388- denyButtonText : " No, keep",
427+ confirmButtonText : t ( "delete-confirmation" , " Delete") ,
428+ denyButtonText : t ( "delete-cancel" , " No, keep") ,
389429 reverseButtons : true ,
390430 } )
391431 . then ( ( result ) => {
@@ -407,7 +447,9 @@ document.body.addEventListener("click", (event) => {
407447 break ;
408448 }
409449 default : {
410- alert ( `Unknown action ${ manageUsersAction } !` ) ;
450+ alert (
451+ `${ t ( "something-went-wrong" , "Something went wrong" ) } : unknown action ${ manageUsersAction } !` ,
452+ ) ;
411453 break ;
412454 }
413455 }
@@ -416,12 +458,15 @@ document.body.addEventListener("click", (event) => {
416458
417459document . body . addEventListener ( "change" , ( event ) => {
418460 let select ;
419- if ( ( select = event . target . closest ( "select" ) ) ) {
461+ if ( ( select = event . target . closest ( ".card- select" ) ) ) {
420462 const taskId = select . value ;
421463 if ( ! appState . currentUser . canReadTask ( Task . get ( taskId ) ) ) {
422464 swal . fire ( {
423- title : "Error" ,
424- titleText : "You can't move a task you can't see." ,
465+ title : t ( "erorr" , "Error" ) ,
466+ titleText : t (
467+ "can't-see-can't-move" ,
468+ "You can't move a task you can't see." ,
469+ ) ,
425470 icon : "error" ,
426471 } ) ;
427472 return ;
@@ -524,8 +569,8 @@ function handlePopupAction(popup, action) {
524569 case "save" :
525570 if ( ! title ) {
526571 swal . fire ( {
527- title : " Error",
528- text : " Task title cannot be empty!",
572+ title : t ( "error" , " Error") ,
573+ text : t ( "title-can't-be-empty" , " Task title cannot be empty!") ,
529574 icon : "error" ,
530575 } ) ;
531576 } else {
@@ -537,10 +582,10 @@ function handlePopupAction(popup, action) {
537582 case "delete" :
538583 swal
539584 . fire ( {
540- titleText : `Are you sure you want to delete "${ title } "?` ,
585+ titleText : `${ t ( "sure-you-want-to-delete" , " Are you sure you want to delete" ) } "${ title } "?` ,
541586 showDenyButton : true ,
542- confirmButtonText : " Delete",
543- denyButtonText : " No, keep it",
587+ confirmButtonText : t ( "delete-confirmation" , " Delete") ,
588+ denyButtonText : t ( "delete-cancel" , " No, keep it") ,
544589 reverseButtons : true ,
545590 customClass : {
546591 confirmButton : "btn btn-danger" ,
@@ -556,7 +601,9 @@ function handlePopupAction(popup, action) {
556601 } ) ;
557602 break ;
558603 default :
559- alert ( `Unknown popup action ${ action } !` ) ;
604+ alert (
605+ `${ t ( "something-went-wrong" , "Something went wrong" ) } : unknown popup action ${ action } !` ,
606+ ) ;
560607 }
561608}
562609
@@ -602,7 +649,10 @@ export function renderTasks(tasks = appState.tasks) {
602649 for ( const task of accessibleTasks ) {
603650 const listItem = document . createElement ( "li" ) ;
604651 listItem . classList . add ( "task" ) ;
605- listItem . textContent = task . title ;
652+ const taskTitleSpan = document . createElement ( "span" ) ;
653+ taskTitleSpan . classList . add ( "task-title" ) ;
654+ taskTitleSpan . textContent = task . title ;
655+ listItem . append ( taskTitleSpan ) ;
606656 listItem . dataset . description = task . description ;
607657 listItem . dataset . id = task . id ;
608658 listItem . setAttribute ( "draggable" , "true" ) ;
0 commit comments