diff --git a/Frontend Angular 4/src/app/layout/grupos/grupos.component.html b/Frontend Angular 4/src/app/layout/grupos/grupos.component.html index 43b6736bef71d8bddd036a17ccc38714e40e454d..3a3cf156d8e0d624df3497d84cee172dccf88b20 100755 --- a/Frontend Angular 4/src/app/layout/grupos/grupos.component.html +++ b/Frontend Angular 4/src/app/layout/grupos/grupos.component.html @@ -315,14 +315,20 @@ <ng-template [ngIf]="selectedSubgroup"> <div *ngFor="let user of selectedSubgroup.users" - class="col-sm-3 matefun-group-wrapper" + class="col-sm-3 matefun-group-wrapper group" > <i class="fa fa-user matefun-fa-user" aria-hidden="true" ></i> - <p> - {{ user.username }} + <p + class="overflow-hidden text-ellipsis group-hover:overflow-visible group-hover:z-10 group-hover:relative" + > + <span + class="group-hover:bg-main-blue group-hover:text-white group-hover:rounded-sm" + > + {{ user.username }} + </span> </p> </div> </ng-template> @@ -353,7 +359,7 @@ [selectFile]="selectSubgroupFile" [selectedEntity]="selectedSubgroup" [NoAssignmentsMessage]=" - 'No hay entregas del subgrupo: ' + selectedSubgroup?.name + 'No hay archivos del subgrupo: ' + selectedSubgroup?.name " ></app-assignments-requiring-feedback> </ng-template> @@ -396,13 +402,6 @@ > <i aria-hidden="true" class="fa fa-arrow-up"></i> </button> - <button - *ngIf="tipoArchivo == 'entrega'" - class="btn btn-sm btn-secondary pull-left mr-2" - (click)="mostrarModalCalificarEntrega()" - > - Calificar - </button> <button ngbPopover="{{ 'i18n.action.load' | translate | titleCase }}/{{ 'i18n.action.edit' | translate | titleCase diff --git a/Frontend Angular 4/src/app/layout/settings/settings.component.html b/Frontend Angular 4/src/app/layout/settings/settings.component.html index 850bd8df9e5e8a304a3d32d730cea4cd32b3b8d4..74605a1d875e62b47b58aefedc71442d1b4790f4 100755 --- a/Frontend Angular 4/src/app/layout/settings/settings.component.html +++ b/Frontend Angular 4/src/app/layout/settings/settings.component.html @@ -1,4 +1,32 @@ <div class="col-md-4"> + <h2 class="block text-3xl my-2 font-medium"> + {{ "i18n.settings.language.title" | translate | titleCase }} + </h2> + <div class="form-content mb-8"> + <div class="language-switcher"> + <div class="dropdown" ngbDropdown> + <button class="btn dropdown-toggle" ngbDropdownToggle> + <span + class="flag-icon flag-icon-{{ selectedLanguage.flagCode }}" + ></span> + {{ selectedLanguage.name }} + </button> + <div class="dropdown-menu" ngbDropdownMenu> + <div class="dropdown-menu-content"> + <button + class="dropdown-item" + *ngFor="let lang of languages" + (click)="onChangeLanguage(lang)" + > + <span class="flag-icon flag-icon-{{ lang.flagCode }}"></span> + {{ lang.name }} + </button> + </div> + </div> + </div> + </div> + </div> + <h2 class="block text-3xl my-2 font-medium"> {{ "i18n.settings.changePassword.title" | translate | titleCase }} </h2> diff --git a/Frontend Angular 4/src/app/layout/settings/settings.component.scss b/Frontend Angular 4/src/app/layout/settings/settings.component.scss index c829a56f1da08b16dbe0679fb649e052a9d7c610..e4335fcfb8d4dfd0425b7e2ce8d26070b62fae1b 100644 --- a/Frontend Angular 4/src/app/layout/settings/settings.component.scss +++ b/Frontend Angular 4/src/app/layout/settings/settings.component.scss @@ -17,3 +17,43 @@ outline: none; } } + +.language-switcher { + button { + color: #333; + border: 1px solid #ddd; + background-color: white; + padding: 8px 16px; + + &:hover, + &:active { + background-color: #f8f9fa; + } + + &:focus { + box-shadow: none; + } + } + + .flag-icon { + margin-right: 10px; + } + + .dropdown-menu { + padding: 0; + margin: 0.5rem 0; + border: 1px solid #ddd; + + &-content { + padding: 0.5rem 0; + } + } + + .dropdown-item { + padding: 8px 16px; + + &:hover { + background-color: #f8f9fa; + } + } +} diff --git a/Frontend Angular 4/src/app/layout/settings/settings.component.ts b/Frontend Angular 4/src/app/layout/settings/settings.component.ts index 1b4bfc5bcd57bfe53276eaab86d233df6aa52f8f..4a2f1706c6b79dc9f02a3cac3fd420b20acadd50 100755 --- a/Frontend Angular 4/src/app/layout/settings/settings.component.ts +++ b/Frontend Angular 4/src/app/layout/settings/settings.component.ts @@ -2,6 +2,7 @@ import { Component } from "@angular/core"; import { NotificacionService } from "../../shared/services/notificacion.service"; import { AuthenticationService } from "../../shared/services/authentication.service"; import { TranslateService } from "@ngx-translate/core"; +import { UserService } from "app/shared/services/user.service"; import { FormControl, Validators, @@ -10,7 +11,7 @@ import { AbstractControl, ValidationErrors, } from "@angular/forms"; - +import { Usuario, User } from "app/shared/objects/usuario"; @Component({ selector: "settings", templateUrl: "./settings.component.html", @@ -19,6 +20,13 @@ import { export class SettingsComponent { translateService: any; + languages = [ + { id: 0, name: "Español", code: "es", flagCode: "uy" }, + { id: 1, name: "English", code: "en", flagCode: "us" }, + ]; + + selectedLanguage: any; + passwordsNotEqual: ValidatorFn = ( control: AbstractControl ): ValidationErrors | null => { @@ -62,12 +70,38 @@ export class SettingsComponent { constructor( public translate: TranslateService, - private authenticationService: AuthenticationService + private authenticationService: AuthenticationService, + private userService: UserService ) { this.translateService = translate; } - ngOnInit() {} + ngOnInit() { + const currentUser = localStorage.getItem("currentUser"); + if (currentUser) { + const userData = JSON.parse(currentUser); + this.selectedLanguage = + this.languages.find((lang) => lang.code === userData.language) || + this.languages[0]; + } else { + this.selectedLanguage = this.languages[0]; + } + } + + onChangeLanguage(lang: any) { + this.selectedLanguage = lang; + this.translate.use(lang.code); + const currentUser = JSON.parse(localStorage.getItem("currentUser")); + + this.userService.updateUser(currentUser.id, lang.code).subscribe( + (data) => { + // Update language in localStorage + const usuario = this.userService.userToUsuario(data["user"]); + localStorage.setItem("currentUser", JSON.stringify(usuario)); + }, + (data) => {} + ); + } onSubmitPasswordChange = () => { console.log("onSubmitPasswordchange"); diff --git a/Frontend Angular 4/src/app/shared/components/checkbox-select/checkbox-select.component.html b/Frontend Angular 4/src/app/shared/components/checkbox-select/checkbox-select.component.html index 1e024582ad61dfd3c569fd7145b35be1b6407686..d453b338117a2b38d61aed94f65bfaaa04f96942 100644 --- a/Frontend Angular 4/src/app/shared/components/checkbox-select/checkbox-select.component.html +++ b/Frontend Angular 4/src/app/shared/components/checkbox-select/checkbox-select.component.html @@ -26,7 +26,15 @@ > </mat-checkbox> </th> - <td>{{ entity[entityNameProperty] }}</td> + <td> + <div class="overflow-hidden text-ellipsis hover:overflow-visible"> + <span + class="hover:bg-main-blue hover:text-white hover:z-10 hover:relative" + > + {{ entity[entityNameProperty] }} + </span> + </div> + </td> <ng-container *ngIf="{filterName}"> <td>{{ entity[filterName] }}</td> </ng-container> diff --git a/Frontend Angular 4/src/app/shared/config.ts b/Frontend Angular 4/src/app/shared/config.ts index 3523f702b46ba948985e8c4f813b55f91c5efb29..9f5a6dcc0aeb3bae6dbd625ad406f5c3bcccca17 100755 --- a/Frontend Angular 4/src/app/shared/config.ts +++ b/Frontend Angular 4/src/app/shared/config.ts @@ -29,6 +29,7 @@ export const LOGOUT_URL = SERVER + "/api/v2/users/sign_out"; export const ACCOUNT_CONFIRMATION_URL = SERVER + "/api/v2/users/confirmation"; export const GET_USER = SERVER + "/api/v2/users/:id"; +export const UPDATE_USER = SERVER + "/api/v2/users/:id"; export const UPDATE_EDITOR_CONFIGURATION = SERVER + "/api/v2/editor_configuration"; diff --git a/Frontend Angular 4/src/app/shared/objects/usuario.ts b/Frontend Angular 4/src/app/shared/objects/usuario.ts index 6fcc0db2792415487c422375d1aadf9bb59c6258..3a89f069e08f67e3a9bcd7d67ed51adf5d8d6f11 100755 --- a/Frontend Angular 4/src/app/shared/objects/usuario.ts +++ b/Frontend Angular 4/src/app/shared/objects/usuario.ts @@ -13,6 +13,7 @@ export class Usuario { apellido: string; username: string; tipo: string; + lenguaje?: string; configuracion: Configuracion; } @@ -21,7 +22,7 @@ export class User { email?: string; username: string; language?: string; - editor_configuration: EditorConfiguration + editor_configuration: EditorConfiguration; } export class EditorConfiguration { @@ -30,4 +31,4 @@ export class EditorConfiguration { font_size: number; infix_notation_warning_flag: boolean; function_use_warning_flag: boolean; -} \ No newline at end of file +} diff --git a/Frontend Angular 4/src/app/shared/services/user.service.ts b/Frontend Angular 4/src/app/shared/services/user.service.ts index c55668a9c5633e83332c4fc10d1ec7deccbed975..0f8433ab10f2839ab5cb4aa2be2a362dc88ca5c4 100644 --- a/Frontend Angular 4/src/app/shared/services/user.service.ts +++ b/Frontend Angular 4/src/app/shared/services/user.service.ts @@ -12,11 +12,13 @@ import { SERVER } from "../config"; import { TranslateService } from "@ngx-translate/core"; import { AuthenticationService } from "./authentication.service"; import { catchError } from "rxjs/operators"; -import { Usuario, User, EditorConfiguration, Configuracion } from "../objects/usuario" import { - GET_USER, - UPDATE_EDITOR_CONFIGURATION -} from "../config"; + Usuario, + User, + EditorConfiguration, + Configuracion, +} from "../objects/usuario"; +import { GET_USER, UPDATE_USER, UPDATE_EDITOR_CONFIGURATION } from "../config"; @Injectable() export class UserService { @@ -51,193 +53,84 @@ export class UserService { }); } -// TODO: Traer el usuario si se recarga la web y guardar sus datos y su configuración del editor + // TODO: Traer el usuario si se recarga la web y guardar sus datos y su configuración del editor -getUser(id: number): Observable<{ user: User}> { - return this.http - .get<{ user: User }>(GET_USER.replace(/:id/g, String(id)), { - headers: this.getHeaders(), - }) - .pipe(catchError(this.handleError)); -} - -userToUsuario(user: User) : Usuario { - return { - token: '', - cedula: '', - id: user.id, - nombre: '', - apellido: '', - username: user.username, - tipo: '', - configuracion: this.editorConfigurationToConfiguracion(user.editor_configuration) + getUser(id: number): Observable<{ user: User }> { + return this.http + .get<{ user: User }>(GET_USER.replace(/:id/g, String(id)), { + headers: this.getHeaders(), + }) + .pipe(catchError(this.handleError)); } -} -editorConfigurationToConfiguracion(editorConfiguration: EditorConfiguration) : Configuracion { - return { - themeEditor: editorConfiguration.theme, - fontSizeEditor: editorConfiguration.font_size, - infix_notation_warning_flag: editorConfiguration.infix_notation_warning_flag, - function_use_warning_flag: editorConfiguration.function_use_warning_flag + userToUsuario(user: User): Usuario { + return { + token: "", + cedula: "", + id: user.id, + nombre: "", + apellido: "", + username: user.username, + tipo: "", + lenguaje: user.language, + configuracion: this.editorConfigurationToConfiguracion( + user.editor_configuration + ), + }; } -} -ConfiguracionToEditorConfiguration(configuracion: Configuracion) : EditorConfiguration { - return { - theme: configuracion.themeEditor, - font_size: configuracion.fontSizeEditor, - infix_notation_warning_flag: configuracion.infix_notation_warning_flag, - function_use_warning_flag: configuracion.function_use_warning_flag + editorConfigurationToConfiguracion( + editorConfiguration: EditorConfiguration + ): Configuracion { + return { + themeEditor: editorConfiguration.theme, + fontSizeEditor: editorConfiguration.font_size, + infix_notation_warning_flag: + editorConfiguration.infix_notation_warning_flag, + function_use_warning_flag: editorConfiguration.function_use_warning_flag, + }; } -} - - -updateEditorConfiguration( - newConfiguration: EditorConfiguration -): Observable<HttpResponse<{ editor_configuration: EditorConfiguration }>> { - return this.http.patch<{ editor_configuration: EditorConfiguration }>( - UPDATE_EDITOR_CONFIGURATION, - { - editor_configuration: newConfiguration, - }, - { - observe: "response", - headers: this.postHeaders(), - } - ); -} - - - - - - + ConfiguracionToEditorConfiguration( + configuracion: Configuracion + ): EditorConfiguration { + return { + theme: configuracion.themeEditor, + font_size: configuracion.fontSizeEditor, + infix_notation_warning_flag: configuracion.infix_notation_warning_flag, + function_use_warning_flag: configuracion.function_use_warning_flag, + }; + } + updateEditorConfiguration( + newConfiguration: EditorConfiguration + ): Observable<HttpResponse<{ editor_configuration: EditorConfiguration }>> { + return this.http.patch<{ editor_configuration: EditorConfiguration }>( + UPDATE_EDITOR_CONFIGURATION, + { + editor_configuration: newConfiguration, + }, + { + observe: "response", + headers: this.postHeaders(), + } + ); + } - // getDocuments(ids: number[]): Observable<{ documents: Document[] }> { - // const params = new HttpParams({ - // fromObject: { 'document_ids[]': ids } - // }); - // return this.http - // .get<{ documents: Document[] }>(GET_DOCUMENTS, { - // headers: this.getHeaders(), - // params: params - // }) - // .pipe(catchError(this.handleError)); - // } - - // getDocument(id: number): Observable<{ document: Document }> { - // return this.http - // .get<{ document: Document }>(GET_DOCUMENT.replace(/:id/g, String(id)), { - // headers: this.getHeaders(), - // }) - // .pipe(catchError(this.handleError)); - // } - - // documentToArchivo(document: Document): Archivo { - // return { - // id: document.id, - // documentId: document.id, - // nombre: document.title, - // contenido: document.text_doc, - // fechaCreacion: document.createdAt, - // cedulaCreador: document.users[0]?.email, - // editable: true, - // padreId: document.parent_id ? document.parent_id : -1, - // fileId: document.file_id, - // archivoOrigenId: null, - // archivos: [], - // directorio: false, - // estado: "activo", - // eliminado: false, - // evaluacion: null, - // }; - // } - - // archivoToDocument(archivo: Archivo): Document { - // return { - // id: archivo.id, - // title: archivo.nombre, - // text_doc: archivo.contenido, - // createdAt: archivo.fechaCreacion, - // parent_id: archivo.padreId, - // users: [], - // }; - // } - - // createDocument(document: Document): Observable<HttpResponse<Object>> { - // return this.http.post<Document>( - // CREATE_DOCUMENT, - // { document }, - // { - // observe: "response", - // headers: this.postHeaders(), - // } - // ); - // } - - // // Legacy method, need to check if it's still used - // getArchivos(cedula: string): Observable<Archivo[]> { - // let headers = this.getHeaders(); - // let params: HttpParams = new HttpParams(); - // params = params.set("cedula", cedula); - // let httpOptions = { headers: headers, params: params }; - - // return this.http - // .get<Archivo[]>(SERVER + "/servicios/archivo", httpOptions) - // .pipe(catchError(this.handleError)); - // } - - // // Legacy method, need to check if it's still used - // getArchivosCompartidosAlumno(cedula: string): Observable<Archivo[]> { - // let headers = this.getHeaders(); - // let params: HttpParams = new HttpParams(); - // params = params.set("cedula", cedula); - // params = params.set("compartidos", "true"); - // let httpOptions = { headers: headers, params: params }; - - // return this.http - // .get<Archivo[]>(SERVER + "/servicios/archivo", httpOptions) - // .pipe(catchError(this.handleError)); - // } - - // createFile( - // file_title, - // file_binary_doc_text - // ): Observable<HttpResponse<Object>> { - // return this.http.post<Object>( - // CREATE_DOCUMENT, - // { - // document: { title: file_title, text_doc: file_binary_doc_text }, - // }, - // { - // observe: "response", - // headers: this.postHeaders(), - // } - // ); - // } - - // updateDocument( - // file_id, - // file_title, - // save_document_content = false - // ): Observable<HttpResponse<Object>> { - // return this.http.patch<Object>( - // `${UPDATE_DOCUMENT}/${file_id}`, - // { - // document: { - // title: file_title, - // save_document_content: save_document_content, - // }, - // }, - // { - // observe: "response", - // headers: this.postHeaders(), - // } - // ); - // } + updateUser(userId, userLanguage): Observable<HttpResponse<{ user: User }>> { + return this.http.patch<{ user: User }>( + UPDATE_USER.replace(":id", userId), + { + user: { + language: userLanguage, + }, + }, + { + observe: "response", + headers: this.postHeaders(), + } + ); + } // destroyAuxDocument(): Observable<HttpResponse<Object>> { // return this.http.delete<Object>(DESTROY_AUX_DOCUMENT, {