diff --git a/frontend/app.js b/frontend/app.js index 648877ddd..3e76c0033 100644 --- a/frontend/app.js +++ b/frontend/app.js @@ -81,7 +81,7 @@ } }) .state(STATES.EVENTS, { - url: "/events", + url: "/events?institutionKey", views: { user_content: { templateUrl: Utils.selectFieldBasedOnScreenSize("app/event/events.html", @@ -164,6 +164,18 @@ } } }) + .state(STATES.INST_DESCRIPTION, { + url: "/institution/:institutionKey/description", + views: { + institution_content: { + templateUrl: "app/institution/descriptionInst/description_inst.html", + controller: "DescriptionInstController as descriptionCtrl" + } + }, + params: { + institution: undefined + } + }) .state(STATES.INST_EVENTS, { url: "/institution/:institutionKey/institution_events", views: { @@ -515,7 +527,8 @@ */ app.run(function mobileInterceptor($transitions, $state, STATES, SCREEN_SIZES) { const permitted_routes = [ - STATES.CREATE_EVENT + STATES.CREATE_EVENT, + STATES.INST_DESCRIPTION ]; $transitions.onStart({ diff --git a/frontend/error/error.html b/frontend/error/error.html index 5981c35b0..137877e25 100644 --- a/frontend/error/error.html +++ b/frontend/error/error.html @@ -1,5 +1,5 @@ -
+
@@ -36,4 +36,9 @@
+
+ + + +
\ No newline at end of file diff --git a/frontend/error/errorController.js b/frontend/error/errorController.js index 379e5388c..b6498d7f9 100644 --- a/frontend/error/errorController.js +++ b/frontend/error/errorController.js @@ -16,5 +16,9 @@ errorCtrl.goToReport = function goToReport() { $window.open("http://support.plataformacis.org/report"); } + + errorCtrl.isMobileScreen = () => Utils.isMobileScreen(); + + errorCtrl.getMobileMsg = () => "Erro " + errorCtrl.status + ": " + errorCtrl.msg; }); })(); \ No newline at end of file diff --git a/frontend/event/create_event.html b/frontend/event/create_event.html index d45027df8..ee33d8fd1 100644 --- a/frontend/event/create_event.html +++ b/frontend/event/create_event.html @@ -1,9 +1,5 @@
-
- - keyboard_arrow_left - -
+
{ - return getEvents({ page: eventCtrl._actualPage, institutionKey: eventCtrl.institutionKey, - month: month, year: year}).then(function success(response) { + return getEvents({ page: eventCtrl._actualPage, month: month, year: year}) + .then(function success(response) { eventCtrl._actualPage += 1; eventCtrl._moreEvents = response.next; @@ -54,6 +54,7 @@ }); } + eventCtrl.events = $filter('filter')(eventCtrl.events, eventCtrl.institutionKey); eventCtrl.isLoadingEvents = false; eventCtrl._getEventsByDay(); }, function error() { @@ -269,6 +270,8 @@ eventCtrl.$onInit = () => { eventCtrl.institutionKey = $state.params.institutionKey; + getCurrentInstitution(); + if(Utils.isMobileScreen(SCREEN_SIZES.SMARTPHONE)) { eventCtrl._getMonths().then(() => { eventCtrl.setupToolbarFields(); @@ -277,5 +280,13 @@ eventCtrl.loadMoreEvents(); } }; + + function getCurrentInstitution() { + if (!_.isNil(eventCtrl.institutionKey)) { + InstitutionService.getInstitution(eventCtrl.institutionKey).then((institutionData) => { + eventCtrl.institution = new Institution(institutionData); + }); + } + } }); })(); \ No newline at end of file diff --git a/frontend/event/eventDetailsDirective.js b/frontend/event/eventDetailsDirective.js index 83f8ff04f..ad5fbfd44 100644 --- a/frontend/event/eventDetailsDirective.js +++ b/frontend/event/eventDetailsDirective.js @@ -3,7 +3,7 @@ var app = angular.module('app'); app.controller("EventDetailsController", function EventDetailsController(MessageService, EventService, - $state, $mdDialog, AuthService, STATES, SCREEN_SIZES) { + $state, $mdDialog, AuthService, STATES, SCREEN_SIZES, ngClipboard) { var eventCtrl = this; @@ -11,7 +11,8 @@ eventCtrl.isLoadingEvents = true; eventCtrl.showImage = true; - eventCtrl.share = function share(ev, event) { + + eventCtrl.share = function share(ev) { $mdDialog.show({ controller: "SharePostController", controllerAs: "sharePostCtrl", @@ -21,26 +22,27 @@ clickOutsideToClose: true, locals: { user: eventCtrl.user, - post: event, + post: eventCtrl.event, addPost: false } }); }; - eventCtrl.confirmDeleteEvent = function confirmDeleteEvent(ev, event) { + eventCtrl.confirmDeleteEvent = function confirmDeleteEvent(ev) { var dialog = MessageService.showConfirmationDialog(ev, 'Excluir Evento', 'Este evento será removido.'); dialog.then(function () { - deleteEvent(event); + deleteEvent(eventCtrl.event); }, function () { MessageService.showToast('Cancelado'); }); }; - function deleteEvent(event) { - let promise = EventService.deleteEvent(event); + function deleteEvent() { + let promise = EventService.deleteEvent(eventCtrl.event); promise.then(function success() { MessageService.showToast('Evento removido com sucesso!'); eventCtrl.event.state = "deleted"; + $state.go(STATES.EVENTS); }); return promise; } @@ -99,8 +101,8 @@ return !(emptyPhoto || nullPhoto); } - eventCtrl.isEventAuthor = function isEventAuthor(event) { - return event && (event.author_key === eventCtrl.user.key); + eventCtrl.isEventAuthor = function isEventAuthor() { + return eventCtrl.event && (eventCtrl.event.author_key === eventCtrl.user.key); }; eventCtrl.goToEvent = function goToEvent(event) { @@ -138,7 +140,7 @@ eventCtrl.isDeleted = () => { return eventCtrl.event ? eventCtrl.event.state === 'deleted' : true; - } + }; /** * This function receives a date in iso format, @@ -151,6 +153,30 @@ return new Date(isoTime).getHours(); }; + /** + * Copies the event's link to the clipboard. + * Checks if the user is following the event. + */ + eventCtrl.copyLink = function copyLink() { + var url = Utils.generateLink(`/event/${eventCtrl.event.key}/details`); + ngClipboard.toClipboard(url); + MessageService.showToast("O link foi copiado"); + }; + + /** + * Constructs a list with the menu options. + */ + eventCtrl.generateToolbarMenuOptions = function generateToolbarMenuOptions() { + eventCtrl.defaultToolbarOptions = [ + { title: 'Obter link', icon: 'link', action: () => { eventCtrl.copyLink() } }, + { title: 'Compartilhar', icon: 'share', action: () => { eventCtrl.share('$event') } }, + { title: 'Receber atualizações', icon: 'visibility', action: () => { eventCtrl.addFollower() }, hide: () => eventCtrl.isFollower() }, + { title: 'Não receber atualizações', icon: 'visibility_off', action: () => { eventCtrl.removeFollower() }, + hide: () => !eventCtrl.isFollower() || eventCtrl.isEventAuthor() }, + { title: 'Cancelar evento', icon: 'cancel', action: () => { eventCtrl.confirmDeleteEvent('$event') }, hide: () => !eventCtrl.canChange() } + ] + }; + /** * Checks if the user is following the event. */ @@ -193,10 +219,12 @@ $state.go(STATES.HOME); }); } - + eventCtrl.$onInit = function() { - if ($state.params.eventKey) + if ($state.params.eventKey) { + eventCtrl.generateToolbarMenuOptions(); return loadEvent($state.params.eventKey); + } }; }); diff --git a/frontend/event/event_details_small_page.html b/frontend/event/event_details_small_page.html index e934989ce..e299aad1d 100644 --- a/frontend/event/event_details_small_page.html +++ b/frontend/event/event_details_small_page.html @@ -1,39 +1,9 @@ + +
{{ eventDetailsCtrl.event.title | uppercase}} - - - - - - share - Compartilhar evento - - - - - cancel - Cancelar evento - - - - - visibility - Receber Atualizações - - - - - visibility_off - Deixar de receber atualizações - - - -
diff --git a/frontend/event/events_mobile.html b/frontend/event/events_mobile.html index ed91f5d50..349519ec8 100644 --- a/frontend/event/events_mobile.html +++ b/frontend/event/events_mobile.html @@ -1,14 +1,21 @@ -
+
- -

Nenhum evento a ser exibido.

-
+ + + +
- +
diff --git a/frontend/home/colorPickerController.js b/frontend/home/colorPickerController.js index 23f42e280..2561b1f71 100644 --- a/frontend/home/colorPickerController.js +++ b/frontend/home/colorPickerController.js @@ -3,9 +3,11 @@ (function () { const app = angular.module("app"); - app.controller("ColorPickerController", function ColorPickerController(user, ProfileService, MessageService, $mdDialog, AuthService, $http) { + app.controller("ColorPickerController", function ColorPickerController(user, institution, ProfileService, MessageService, $mdDialog, AuthService, $http) { var colorPickerCtrl = this; colorPickerCtrl.user = user; + colorPickerCtrl.institution = {}; + colorPickerCtrl.oldColorValue = institution.color; colorPickerCtrl.saveColor = function saveColor() { var diff = jsonpatch.compare(colorPickerCtrl.user, colorPickerCtrl.newUser); @@ -26,9 +28,7 @@ function loadProfile() { colorPickerCtrl.newUser = _.cloneDeep(colorPickerCtrl.user); - colorPickerCtrl.newProfile = _.find(colorPickerCtrl.newUser.institution_profiles, function (profile) { - return profile.institution_key === colorPickerCtrl.newUser.current_institution.key; - }); + colorPickerCtrl.institution = _.find(colorPickerCtrl.newUser.institution_profiles, ['institution_key', institution.institution_key]); } function loadColors() { diff --git a/frontend/home/color_picker.css b/frontend/home/color_picker.css new file mode 100644 index 000000000..e1f43aa96 --- /dev/null +++ b/frontend/home/color_picker.css @@ -0,0 +1,34 @@ +#color-dialog { + display: grid; + padding: 1em; + grid-template-rows: 10% 1fr auto; +} + +#color-dialog-buttons { + display: grid; + grid-auto-flow: column; + grid-auto-columns: min-content; + justify-content: end; +} + +.color-picker-area { + display: grid; + grid-template-columns: repeat(4, min-content); + justify-content: space-around; +} + +.color-picker-circle { + width: 3em !important; + height: 3em !important; +} + +.color-picker-title { + color: #009688; + font-size: 1em; + justify-self: center; + align-self: center; +} + +button.color-dialog-button { + font-size: 0.7em; +} diff --git a/frontend/home/color_picker.html b/frontend/home/color_picker.html index d1a42a5d7..04353edd0 100644 --- a/frontend/home/color_picker.html +++ b/frontend/home/color_picker.html @@ -5,9 +5,9 @@

Selecione uma cor:

- - check_circle + check_circle diff --git a/frontend/home/color_picker_mobile.html b/frontend/home/color_picker_mobile.html new file mode 100644 index 000000000..082f31eed --- /dev/null +++ b/frontend/home/color_picker_mobile.html @@ -0,0 +1,30 @@ + + +
+ Selecione uma cor +
+
+ + check_circle + +
+
+
+ + CANCELAR + + + ALTERAR + +
+
+
+
diff --git a/frontend/home/home.html b/frontend/home/home.html index 7118b3fbb..5c8504eb0 100644 --- a/frontend/home/home.html +++ b/frontend/home/home.html @@ -10,7 +10,7 @@
- +
diff --git a/frontend/home/post_dialog.html b/frontend/home/post_dialog.html index cfe7675b5..83821f8db 100644 --- a/frontend/home/post_dialog.html +++ b/frontend/home/post_dialog.html @@ -1,4 +1,4 @@ - + diff --git a/frontend/images/desenho-cis.png b/frontend/images/desenho-cis.png new file mode 100644 index 000000000..958e63f9a Binary files /dev/null and b/frontend/images/desenho-cis.png differ diff --git a/frontend/index.html b/frontend/index.html index 63bfdd6b8..4408ab934 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -55,6 +55,7 @@ + @@ -69,6 +70,13 @@ + + + + + + + @@ -229,6 +237,8 @@ + + @@ -285,7 +295,20 @@ + + + + + + + + + + + + + diff --git a/frontend/institution/base_institution.css b/frontend/institution/base_institution.css index 7049ce4f0..5781fe439 100644 --- a/frontend/institution/base_institution.css +++ b/frontend/institution/base_institution.css @@ -19,6 +19,12 @@ padding: 5px; } +.base-content-inst{ + height: 100%; + position: absolute; + width: 100%; +} + .timeline-z-index{ z-index: 1; } @@ -86,7 +92,11 @@ .icon-button-menu-inst{ color: #FFFFFF; font-size: 20px; - margin-left: 0.5em; + margin: 0; +} + +.vert-button-navbar-inst { + display: inherit !important; } .institution-button-menu{ @@ -99,10 +109,6 @@ height: 0px; } -.btn-follow-unfollow{ - margin-right: -20px; -} - #buttons-navbar-inst-container{ position: relative; display: grid; @@ -114,7 +120,7 @@ #buttons-navbar-inst-container-end{ padding-top: 5px; display: grid; - grid-template-columns: 107px auto auto; + grid-template-columns: min-content auto min-content; align-items: center; justify-content: end; overflow-x:hidden; @@ -134,7 +140,8 @@ } #btn-more{ - width: 48px; + margin: 0; + padding: 0; } #btn-edit{ @@ -165,9 +172,6 @@ .button-navbar-inst{ font-size: 12px; } - #btn-more{ - margin-left: -20px; - } } @media screen and (min-height: 620px) and (max-height:660px){ diff --git a/frontend/institution/base_institution_page_mobile.html b/frontend/institution/base_institution_page_mobile.html index d8c3059f4..298722027 100644 --- a/frontend/institution/base_institution_page_mobile.html +++ b/frontend/institution/base_institution_page_mobile.html @@ -1,4 +1,4 @@ - +
@@ -16,7 +16,8 @@
+ class="institution-button-menu" + ng-disabled="button.isDisabled"> {{button.icon}} {{button.label}} @@ -26,7 +27,8 @@
-
+
diff --git a/frontend/institution/descriptionInst/descriptionController.js b/frontend/institution/descriptionInst/descriptionController.js new file mode 100644 index 000000000..e158d6f46 --- /dev/null +++ b/frontend/institution/descriptionInst/descriptionController.js @@ -0,0 +1,26 @@ +(function(){ + const app = angular.module('app'); + + app.controller("DescriptionInstController", function DescriptionInstController($state, InstitutionService, $rootScope){ + const descriptionCtrl = this; + descriptionCtrl.isLoading = false; + + descriptionCtrl.$onInit = () => { + descriptionCtrl.isLoading = true; + InstitutionService.getInstitution($state.params.institutionKey).then(function(institution){ + descriptionCtrl.institution = institution; + descriptionCtrl.isLoading = false; + }) + }; + + /** Listenner edit description inst event, and should refresh institution. + */ + $rootScope.$on('EDIT_DESCRIPTION_INST', () => { + descriptionCtrl.isLoading = true; + InstitutionService.getInstitution($state.params.institutionKey).then(function(institution){ + descriptionCtrl.institution = institution; + descriptionCtrl.isLoading = false; + }) + }); + }); +})(); \ No newline at end of file diff --git a/frontend/institution/descriptionInst/description_inst.css b/frontend/institution/descriptionInst/description_inst.css new file mode 100644 index 000000000..da916d95d --- /dev/null +++ b/frontend/institution/descriptionInst/description_inst.css @@ -0,0 +1,65 @@ +#textarea-edit-description{ + height: 100% !important; + border-style: solid; + border-width: 1px; + border-color: black; + border-radius: 5px; + padding: 0.5em; + overflow-y: scroll; +} + +#label-edit-description{ + font-weight: bold; + font-size: 25px; + color:black; +} + +.height-dialog{ + max-height: 90%; +} + +.description-inst{ + padding: 0px 1.5em; + margin-right: auto; + word-break: break-all; +} + +.button-confirm-describe{ + justify-self: end; + margin: 0em; +} + +.dialog-edit-description{ + padding: 2em 1em 1em 1em; + display: grid; + grid-template-rows: 2.5em 1fr 3.5em; + width: 16em; + height: 100%; +} + +.margin-zero{ + margin: 0px; +} + +.input-edit-desciption .md-resize-wrapper{ + height: 100%; +} + +.input-edit-desciption{ + height: 22em; +} + +@media screen and (max-height:570px){ + .dialog-edit-description{ + width: 14em; + } + .input-edit-desciption{ + height: 18em; + } +} + +@media screen and (min-height:700px){ + .input-edit-desciption{ + height: 26em; + } +} \ No newline at end of file diff --git a/frontend/institution/descriptionInst/description_inst.html b/frontend/institution/descriptionInst/description_inst.html new file mode 100644 index 000000000..3fae1d257 --- /dev/null +++ b/frontend/institution/descriptionInst/description_inst.html @@ -0,0 +1,6 @@ +
+ +
+

{{descriptionCtrl.institution.description}}

+
+
\ No newline at end of file diff --git a/frontend/institution/descriptionInst/editDescriptionController.js b/frontend/institution/descriptionInst/editDescriptionController.js new file mode 100644 index 000000000..db6ec45b2 --- /dev/null +++ b/frontend/institution/descriptionInst/editDescriptionController.js @@ -0,0 +1,29 @@ +(function(){ + const app = angular.module('app'); + + app.controller("EditDescriptionController",['institution', 'InstitutionService', + '$rootScope', '$mdDialog', function EditDescriptionController(institution, InstitutionService, + $rootScope, $mdDialog){ + const descriptionCtrl = this; + + descriptionCtrl.$onInit = () => { + descriptionCtrl.institution = institution; + descriptionCtrl.institutionClone = _.cloneDeep(institution); + }; + + /** Save changes of institution and emit event. + */ + descriptionCtrl.save = () => { + const clone = JSON.parse(angular.toJson(descriptionCtrl.institutionClone)); + const modified = JSON.parse(angular.toJson(descriptionCtrl.institution)); + const patch = jsonpatch.compare(clone, modified); + + InstitutionService.update(descriptionCtrl.institution.key, patch).then( + function success() { + $rootScope.$emit('EDIT_DESCRIPTION_INST'); + $mdDialog.hide(); + } + ); + } + }]); +})(); \ No newline at end of file diff --git a/frontend/institution/descriptionInst/edit_description.html b/frontend/institution/descriptionInst/edit_description.html new file mode 100644 index 000000000..714dc6b24 --- /dev/null +++ b/frontend/institution/descriptionInst/edit_description.html @@ -0,0 +1,12 @@ + +
+ + + + + + CONFIRMAR + +
+
\ No newline at end of file diff --git a/frontend/institution/edit_registration_data.html b/frontend/institution/edit_registration_data.html index d00a9c2c1..8c47749ac 100644 --- a/frontend/institution/edit_registration_data.html +++ b/frontend/institution/edit_registration_data.html @@ -1,9 +1,6 @@ -
- - keyboard_arrow_left - -

EDITAR DADOS CADASTRAIS

-
+ +
Utils.isMobileScreen() ? + "app/requests/request_invitation_dialog_mobile.html" : "app/requests/request_invitation_dialog.html"; }); })(); \ No newline at end of file diff --git a/frontend/institution/institution_header.html b/frontend/institution/institution_header.html index 84e13d349..1bfe4cba9 100644 --- a/frontend/institution/institution_header.html +++ b/frontend/institution/institution_header.html @@ -7,7 +7,7 @@
+ id="btn-description" ng-click="instHeaderCtrl.actionsButtons.goToDescription()"> DESCRIÇÃO
@@ -23,7 +23,7 @@ - more_vert + more_vert @@ -42,8 +42,8 @@ - + EDITAR
diff --git a/frontend/institution/management_institution.css b/frontend/institution/management_institution.css index e261fbc37..d3021594c 100644 --- a/frontend/institution/management_institution.css +++ b/frontend/institution/management_institution.css @@ -42,6 +42,10 @@ width: 16%; } +.margin-input-info{ + margin-top: 9px; +} + .manage-member-content { overflow: auto; max-height: 220px; @@ -57,6 +61,10 @@ margin-top: 5px; } +.padding-div-info-admin{ + padding-left: 15px; +} + @media screen and (min-width: 601px) { .edit-inst-name, .edit-inst-street { width: 59%; diff --git a/frontend/institution/management_institution_page.html b/frontend/institution/management_institution_page.html index e9d4c512f..30faec327 100644 --- a/frontend/institution/management_institution_page.html +++ b/frontend/institution/management_institution_page.html @@ -7,7 +7,7 @@
- +
diff --git a/frontend/institution/management_members.html b/frontend/institution/management_members.html index ca79f524f..dbeec8c55 100644 --- a/frontend/institution/management_members.html +++ b/frontend/institution/management_members.html @@ -91,19 +91,19 @@

{{ manageMemberCtrl.institution.admin.name }}

-
-
+

{{ manageMemberCtrl.getMemberName(invite.invitee_key) }}

{{ invite.invitee }}

-
+

PENDENTE

-
+
TODAS diff --git a/frontend/institution/timeline_inst.html b/frontend/institution/timeline_inst.html index 3ec2a84fc..96d9c23bc 100644 --- a/frontend/institution/timeline_inst.html +++ b/frontend/institution/timeline_inst.html @@ -4,7 +4,7 @@
-
+
diff --git a/frontend/main/main.html b/frontend/main/main.html index 256196fb7..804b917fe 100644 --- a/frontend/main/main.html +++ b/frontend/main/main.html @@ -36,7 +36,7 @@
-
+
refresh diff --git a/frontend/main/mainController.js b/frontend/main/mainController.js index c3b442444..9d643d3ac 100644 --- a/frontend/main/mainController.js +++ b/frontend/main/mainController.js @@ -143,6 +143,13 @@ AuthService.reload(); }; + /** Should update version, refresh user and reload the page. + */ + mainCtrl.updateVersion = function updateVersion() { + mainCtrl.refreshUser(); + $window.location.reload(); + }; + /** Return correct class according currently state. */ mainCtrl.getSelectedClass = function (stateName){ diff --git a/frontend/notification/notification_list_mobile.css b/frontend/notification/notification_list_mobile.css index 5c5da45c1..3f661ce83 100644 --- a/frontend/notification/notification_list_mobile.css +++ b/frontend/notification/notification_list_mobile.css @@ -13,12 +13,6 @@ margin-top: 1%; } -#mobile-notification-empty { - display: grid; - grid-template-columns: auto; - text-align: center; -} - #mobile-notification-content { display: grid; grid-template-columns: auto; diff --git a/frontend/notification/notifications_list_mobile.html b/frontend/notification/notifications_list_mobile.html index 4a868a929..0cf64b0a8 100644 --- a/frontend/notification/notifications_list_mobile.html +++ b/frontend/notification/notifications_list_mobile.html @@ -1,7 +1,7 @@ - -

- Nenhuma notificação no momento -

+
+ +
@@ -26,4 +26,4 @@

+
+
\ No newline at end of file diff --git a/frontend/post/pdfDialog.html b/frontend/pdfUpload/pdfDialog.html similarity index 100% rename from frontend/post/pdfDialog.html rename to frontend/pdfUpload/pdfDialog.html diff --git a/frontend/pdfUpload/pdfDialogMobile.html b/frontend/pdfUpload/pdfDialogMobile.html new file mode 100644 index 000000000..dd2df476e --- /dev/null +++ b/frontend/pdfUpload/pdfDialogMobile.html @@ -0,0 +1,17 @@ + + +
+
+ + picture_as_pdf + +
+
+

{{ctrl.pdf.name}}

+
+
+
+
\ No newline at end of file diff --git a/frontend/pdfUpload/pdfService.js b/frontend/pdfUpload/pdfService.js index d648b0f34..d0c067d84 100644 --- a/frontend/pdfUpload/pdfService.js +++ b/frontend/pdfUpload/pdfService.js @@ -3,7 +3,7 @@ (function() { var app = angular.module('app'); - app.service("PdfService", function PdfService($q, $firebaseStorage, $http) { + app.service("PdfService", function PdfService($q, $firebaseStorage, $http, $mdDialog, $window) { var service = this; var fileFolder = "files/"; var INDEX_FILE_NAME = 0; @@ -79,6 +79,10 @@ return deferred.promise; }; + service.download = function download (url) { + $window.open(url); + }; + function isValidPdf(file) { if(file) { var correctType = file.type === PDF_TYPE; @@ -87,5 +91,53 @@ } return false; } + + service.showPdfDialog = function showPdfDialog (ev, pdf) { + $mdDialog.show({ + templateUrl: Utils.selectFieldBasedOnScreenSize( + 'app/pdfUpload/pdfDialog.html', + 'app/pdfUpload/pdfDialogMobile.html' + ), + targetEvent: ev, + clickOutsideToClose:true, + locals: { + pdf: pdf + }, + controller: [ + "PdfService", + "$sce", + "pdf", + PdfDialogController, + ], + controllerAs: 'ctrl' + }); + }; + + function PdfDialogController(PdfService, $sce, pdf) { + var ctrl = this; + ctrl.pdfUrl = ""; + ctrl.isLoadingPdf = true; + ctrl.pdf = pdf; + + function readPdf() { + var readablePdf = {}; + PdfService.getReadableURL(pdf.url, setPdfURL, readablePdf).then(function success() { + var trustedUrl = $sce.trustAsResourceUrl(readablePdf.url); + ctrl.pdfUrl = trustedUrl; + ctrl.isLoadingPdf = false; + }); + } + + ctrl.downloadPdf = () => PdfService.download(ctrl.pdf.url); + + (function main() { + if (!Utils.isMobileScreen()) readPdf(); + })(); + } + + function setPdfURL(url, pdf) { + pdf.url = url; + } + }); })(); \ No newline at end of file diff --git a/frontend/post/create_post.css b/frontend/post/create_post.css index b3693be20..79df71166 100644 --- a/frontend/post/create_post.css +++ b/frontend/post/create_post.css @@ -1,6 +1,5 @@ .save-post-dialog { display: grid; - height: 100%; padding: 8px; overflow: scroll; } @@ -162,8 +161,6 @@ } .create-post-actions { - position: absolute; - bottom: 50px; width: 85%; } @@ -241,6 +238,19 @@ } } +.dialog-transparent-without-shadow { + background-color: rgba(0, 0, 0, 0); + box-shadow: 0 0 0; +} + +@media screen and (max-width: 960px) { + .dialog-transparent-without-shadow { + max-width: 100%; + max-height: 100%; + border-radius: 0; + } +} + @media screen and (max-width: 600px) { .create-post-title-placeholder { font-size: 12px !important; @@ -249,6 +259,24 @@ .create-post-text { font-size: 12px !important; } + + .save-post-dialog { + height: 100%; + } + + .dialog-transparent-without-shadow { + height: 100%; + } +} + +@media screen and (min-width: 601px) { + .save-post-dialog { + overflow: auto; + } + + .dialog-transparent-without-shadow { + width: 60%; + } } .no-padding { diff --git a/frontend/post/pdfViewDirective.js b/frontend/post/pdfViewDirective.js index 54e31169a..d27833846 100644 --- a/frontend/post/pdfViewDirective.js +++ b/frontend/post/pdfViewDirective.js @@ -3,7 +3,7 @@ var app = angular.module('app'); - app.controller('PdfController', function PdfController($mdDialog) { + app.controller('PdfController', function PdfController(PdfService) { var pdfCtrl = this; pdfCtrl.showFiles = function() { @@ -13,16 +13,7 @@ pdfCtrl.pdfDialog = function(ev, pdf) { if(!pdfCtrl.isEditing) { - $mdDialog.show({ - templateUrl: 'app/post/pdfDialog.html', - targetEvent: ev, - clickOutsideToClose:true, - locals: { - pdf: pdf - }, - controller: DialogController, - controllerAs: 'ctrl' - }); + PdfService.showPdfDialog(ev, pdf); } }; @@ -46,25 +37,6 @@ function setPdfURL(url, pdf) { pdf.url = url; } - - function DialogController($mdDialog, PdfService, $sce, pdf) { - var ctrl = this; - ctrl.pdfUrl = ""; - ctrl.isLoadingPdf = true; - - function readPdf() { - var readablePdf = {}; - PdfService.getReadableURL(pdf.url, setPdfURL, readablePdf).then(function success() { - var trustedUrl = $sce.trustAsResourceUrl(readablePdf.url); - ctrl.pdfUrl = trustedUrl; - ctrl.isLoadingPdf = false; - }); - } - - (function main() { - readPdf(); - })(); - } }); /** diff --git a/frontend/post/postDetailsDirective.js b/frontend/post/postDetailsDirective.js index 387427c50..aae0f96bd 100644 --- a/frontend/post/postDetailsDirective.js +++ b/frontend/post/postDetailsDirective.js @@ -4,7 +4,8 @@ var app = angular.module('app'); app.controller('PostDetailsController', function(PostService, AuthService, CommentService, $state, - $mdDialog, MessageService, ngClipboard, ProfileService, $rootScope, POST_EVENTS, STATES, EventService) { + $mdDialog, MessageService, ngClipboard, ProfileService, $rootScope, + POST_EVENTS, STATES, EventService, SCREEN_SIZES) { var postDetailsCtrl = this; @@ -550,6 +551,13 @@ return {background: color}; }; + /** + * Checks if the application is being used by a mobile device. + */ + postDetailsCtrl.isMobileScreen = () => { + return Utils.isMobileScreen(SCREEN_SIZES.SMARTPHONE); + }; + function adjustText(text){ return (!postDetailsCtrl.isPostPage && text) ? Utils.limitString(text, LIMIT_POST_CHARACTERS) : text; diff --git a/frontend/post/postPageController.js b/frontend/post/postPageController.js index a02a3f5fd..49400fa43 100644 --- a/frontend/post/postPageController.js +++ b/frontend/post/postPageController.js @@ -3,11 +3,16 @@ var app = angular.module('app'); - app.controller("PostPageController", function PostPageController(PostService, $state, STATES) { + app.controller("PostPageController", function PostPageController(PostService, $state, + STATES, MessageService, ngClipboard, AuthService, $mdDialog) { var postCtrl = this; postCtrl.post = null; + postCtrl.user = AuthService.getCurrentUser(); + + const EDIT_POST_PERMISSION = 'edit_post'; + postCtrl.isHiden = function isHiden() { var isDeleted = postCtrl.post.state == 'deleted'; var hasNoComments = postCtrl.post.number_of_comments === 0; @@ -17,6 +22,176 @@ return postCtrl.post.state === 'deleted' && hasNoActivity; }; + /** + * It copies the post's url to the clipboard + */ + postCtrl.copyLink = function copyLink() { + var url = Utils.generateLink('/post/' + postCtrl.post.key); + ngClipboard.toClipboard(url); + MessageService.showToast("O link foi copiado"); + }; + + /** + * Refreshes the post by retrieving it from + * the server once again. + */ + postCtrl.reloadPost = function reloadPost() { + var type_survey = postCtrl.post.type_survey; + postCtrl.post.type_survey = ''; + return PostService.getPost(postCtrl.post.key) + .then(function success(response) { + response.data_comments = Object.values(response.data_comments); + postCtrl.post = response; + }, function error(response) { + postCtrl.post.type_survey = type_survey; + }); + }; + + /** + * Open up a dialog that allows the user to share + * the post. + */ + postCtrl.share = function share(event) { + const post = getOriginalPost(); + $mdDialog.show({ + controller: "SharePostController", + controllerAs: "sharePostCtrl", + templateUrl: 'app/post/share_post_dialog.html', + parent: angular.element(document.body), + targetEvent: event, + clickOutsideToClose: true, + locals: { + user: postCtrl.user, + post: post, + addPost: true + } + }); + }; + + /** + * Add the user to the post's subscribers list + * what makes him to receive the notifications realated + * to the post. + */ + postCtrl.addSubscriber = function addSubscriber() { + PostService.addSubscriber(postCtrl.post.key).then(function success() { + MessageService.showToast('Esse post foi marcado como de seu interesse.'); + postCtrl.post.subscribers.push(postCtrl.user.key); + }); + }; + + /** + * Removes the user from the post's subscribers list + * peventing him of receive notifications related to the post. + */ + postCtrl.removeSubscriber = function removeSubscriber() { + PostService.removeSubscriber(postCtrl.post.key).then(function success() { + MessageService.showToast('Esse post foi removido dos posts de seu interesse.'); + _.remove(postCtrl.post.subscribers, function (userKey) { + return userKey === postCtrl.user.key; + }); + }, function error(response) { + $state.go($state.current); + }); + }; + + /** + * Checks if the user is in the post's subscribers list + */ + postCtrl.isSubscriber = function isSubscriber() { + return postCtrl.post && postCtrl.post.subscribers.includes(postCtrl.user.key); + }; + + /** + * If the post comes from another + * it returns the shared one, otherwise + * the post is returned. + */ + function getOriginalPost() { + if (postCtrl.post.shared_post) { + return postCtrl.post.shared_post; + } else if (postCtrl.post.shared_event) { + return postCtrl.post.shared_event; + } + return postCtrl.post; + } + + /** + * Open up a dialog that allows the user to edit the post. + */ + postCtrl.edit = function edit(event) { + $mdDialog.show({ + controller: function DialogController() { }, + controllerAs: "controller", + templateUrl: 'app/home/post_dialog.html', + parent: angular.element(document.body), + targetEvent: event, + clickOutsideToClose: true, + locals: { + originalPost: postCtrl.post, + isEditing: true + }, + bindToController: true + }).then(function success(editedPost) { + postCtrl.post.title = editedPost.title; + postCtrl.post.text = editedPost.text; + postCtrl.post.photo_url = editedPost.photo_url; + postCtrl.post.pdf_files = editedPost.pdf_files; + postCtrl.post.video_url = editedPost.video_url; + }, function error() { }); + }; + + /** + * Checks if the user can edit the post. For that, the user needs to have the + * permissions. Besides, the post and the institution can not be unavailable + */ + postCtrl.canEdit = function canEdit() { + const hasPermission = postCtrl.post && postCtrl.user.hasPermission(EDIT_POST_PERMISSION, postCtrl.post.key); + var isActiveInst = postCtrl.post && postCtrl.post.institution_state == "active"; + return hasPermission && postCtrl.post.state !== 'deleted' && isActiveInst && + !postCtrl.postHasActivity() && !postCtrl.isShared() && !postCtrl.post.type_survey; + }; + + /** + * Checks if the post has any activity. It can be comments or likes. + */ + postCtrl.postHasActivity = function postHasActivity() { + var hasNoComments = postCtrl.post.number_of_comments === 0; + var hasNoLikes = postCtrl.post.number_of_likes === 0; + + return !hasNoComments || !hasNoLikes; + }; + + /** + * Checks if the post came from another post or event by sharing. + */ + postCtrl.isShared = function isShared() { + return postCtrl.post.shared_post || + postCtrl.post.shared_event; + }; + + /** + * Constructs a list with the menu options. + */ + postCtrl.generateToolbarOptions = function generateToolbarOptions() { + postCtrl.defaultToolbarOptions = [ + { title: 'Obter link', icon: 'link', action: () => { postCtrl.copyLink() } }, + { title: 'Atualizar post', icon: 'refresh', action: () => { postCtrl.reloadPost() } }, + { title: 'Compartilhar', icon: 'share', action: () => { postCtrl.share('$event') } }, + { title: 'Receber atualizações', icon: 'bookmark', action: () => { postCtrl.addSubscriber() }, hide: () => postCtrl.isSubscriber() }, + { title: 'Não receber atualizações', icon: 'bookmark', + action: () => { postCtrl.removeSubscriber() }, hide: () => !postCtrl.isSubscriber() || postCtrl.isPostAuthor() }, + { title: 'Editar postagem', icon: 'edit', action: () => { postCtrl.edit() }, hide: () => !postCtrl.canEdit() } + ]; + }; + + /** + * Checks if the current user is the post's author. + */ + postCtrl.isPostAuthor = function isPostAuthor() { + return postCtrl.post.author_key === postCtrl.user.key; + }; + function loadPost(postKey) { var promise = PostService.getPost(postKey); promise.then(function success(response) { @@ -28,6 +203,9 @@ return promise; } - loadPost($state.params.key); + postCtrl.$onInit = () => { + loadPost($state.params.key); + postCtrl.generateToolbarOptions(); + }; }); })(); \ No newline at end of file diff --git a/frontend/post/post_details.html b/frontend/post/post_details.html index 344715525..4a05f04bb 100644 --- a/frontend/post/post_details.html +++ b/frontend/post/post_details.html @@ -6,7 +6,7 @@

+ ng-hide="postDetailsCtrl.isHidden() || postDetailsCtrl.isPostEmpty()" id="post-container">
ENQUETE FINALIZADA | {{postDetailsCtrl.post.deadline | amUtc | amLocal | amCalendar:referenceTime:formats}} @@ -41,7 +41,7 @@ - + more_vert diff --git a/frontend/post/post_page.html b/frontend/post/post_page.html index 9a0d55a1e..8e86b89d4 100644 --- a/frontend/post/post_page.html +++ b/frontend/post/post_page.html @@ -1,14 +1,16 @@ -
-
+ + +
+
-
- +
+
- +
- +

Esta publicação foi removida.

diff --git a/frontend/post/timeline.html b/frontend/post/timeline.html index e579e55bc..7c5ec51fc 100644 --- a/frontend/post/timeline.html +++ b/frontend/post/timeline.html @@ -1,9 +1,11 @@
- - -

Esta instituição ainda não possui publicações.

-
+ + + + +

Esta instituição ainda não possui publicações.

+
@@ -11,7 +13,7 @@

Esta instituição ainda não possui publicações.

refresh
-
+
diff --git a/frontend/post/timelineDirective.js b/frontend/post/timelineDirective.js index 0704e28a8..c619381eb 100644 --- a/frontend/post/timelineDirective.js +++ b/frontend/post/timelineDirective.js @@ -56,6 +56,8 @@ return timelineCtrl.posts.loadMorePosts(); }; + timelineCtrl.isMobileScreen = () => Utils.isMobileScreen(); + /** * Set the properties necessary to make the default Timeline work * with the expected values to this context. diff --git a/frontend/requests/request_invitation_dialog.css b/frontend/requests/request_invitation_dialog.css new file mode 100644 index 000000000..35031078f --- /dev/null +++ b/frontend/requests/request_invitation_dialog.css @@ -0,0 +1,14 @@ +.request-invitation__dialog { + max-width: 90%; + border-radius: 0; +} + +.request-invitation__content { + padding: 2.5em 2em; +} + +.request-invitation__title { + display: flex; + text-align: center; + margin: 0 0 1.5em; +} \ No newline at end of file diff --git a/frontend/requests/request_invitation_dialog_mobile.html b/frontend/requests/request_invitation_dialog_mobile.html new file mode 100644 index 000000000..c7eceff01 --- /dev/null +++ b/frontend/requests/request_invitation_dialog_mobile.html @@ -0,0 +1,40 @@ + + + + Solicitar vínculo institucional + {{requestInvCtrl.request.institution_name}} +
+ + + +
+
Este campo é obrigatório!
+
+
+ + + + +
+
Este campo é obrigatório!
+
+
+ + + + +
+
Este campo é obrigatório!
+
+
+
+ + Cancelar + + + Confirmar + +
+
+
+
diff --git a/frontend/search/search.css b/frontend/search/search.css index 7b4a7ebb4..34dbc0182 100644 --- a/frontend/search/search.css +++ b/frontend/search/search.css @@ -6,10 +6,11 @@ width: 100%; font-size: 20px; z-index: 2; + overflow: hidden; } .search-mobile-content { - margin-top: 85px; + overflow: hidden; } .search-mobile-content form { diff --git a/frontend/search/search_mobile.html b/frontend/search/search_mobile.html index 132197f0b..28a79f090 100644 --- a/frontend/search/search_mobile.html +++ b/frontend/search/search_mobile.html @@ -1,10 +1,6 @@ - -
- - clear - - PESQUISAR -
+ + +
@@ -18,7 +14,7 @@
Pesquisa avançada
-
+
- \ No newline at end of file +
\ No newline at end of file diff --git a/frontend/sideMenu/sideMenu.component.js b/frontend/sideMenu/sideMenu.component.js index 5fd094a6a..9f5111397 100644 --- a/frontend/sideMenu/sideMenu.component.js +++ b/frontend/sideMenu/sideMenu.component.js @@ -16,6 +16,14 @@ HomeItemsFactory, ManageInstItemsFactory, InstitutionService, SIDE_MENU_TYPES) { const sideMenuCtrl = this; + const colorPickerButton = { + text: 'Gerenciar cores', + icon: 'color_lens', + }; + const backButton = { + text: 'Voltar', + icon: 'keyboard_arrow_left', + }; sideMenuCtrl.user = AuthService.getCurrentUser(); @@ -92,17 +100,94 @@ return type === sideMenuCtrl.type; }; - sideMenuCtrl.openColorPicker = () => { + /** + * Store (internally) colorPicker's active state, + * allowing a single dynamic view for both mobile and desktop. + * When it's on: avatar is replaced with that institution's color; + * clicking on a institution changes that to current; + * last button on menu shows "Voltar" and toggles this variable; + * When it's off: institution's avatar is shown; + * clicking on a institution shows a dialog to pick a new color for that; + * last button on menu shows "Gerenciar cores". + */ + sideMenuCtrl._isColorPickerActive = false; + + /** + * Toggle colorPicker's active state. + */ + sideMenuCtrl.toggleColorPicker = () => { + sideMenuCtrl._isColorPickerActive = !sideMenuCtrl._isColorPickerActive; + } + + /** + * Gets user's current_institution profile. + * Needed to provide a default profile to #openColorPicker (desktop behavior). + */ + sideMenuCtrl.getCurrentInstitutionProfile = () => { + return _.find(sideMenuCtrl.user.institution_profiles, ['institution_key', sideMenuCtrl.user.current_institution.key]); + } + + /** + * Calls the correct function based on if it's a mobile screen, + * and current colorPicker's active state. + * The color picker dialog should only open here when on mobile + * AND the color picker is active. + * Otherwise, that should be made the current institution on the user. + * + * @param {Object} profile - institution profile to be made current or have its color changed + */ + sideMenuCtrl.institutionButtonAction = (profile) => { + sideMenuCtrl.isMobileScreen && sideMenuCtrl.isColorPickerActive ? + sideMenuCtrl.openColorPicker(profile) : + sideMenuCtrl.changeInstitution(profile); + } + + /** + * Defines a property .currentMenuOption, + * returning the current button based on colorPicker's active state. + */ + Object.defineProperty(sideMenuCtrl, 'currentMenuOption', { + get: function() { + return sideMenuCtrl.isColorPickerActive ? backButton : colorPickerButton; + } + }) + + /** + * Defines a property .isColorPickerActive, + * that always return true on desktop. + * On mobile, maps to internal _isColorPickerActive variable. + */ + Object.defineProperty(sideMenuCtrl, 'isColorPickerActive', { + get: function() { + return sideMenuCtrl.isMobileScreen ? sideMenuCtrl._isColorPickerActive : true; + } + }); + + /** + * Defines a property .isMobileScreen, + * shorthand for Utils.isMobileScreen(). + * Needed by the view to provide adequate buttons on desktop and mobile. + */ + Object.defineProperty(sideMenuCtrl, 'isMobileScreen', { + get: function() { + return Utils.isMobileScreen(); + }, + }); + + sideMenuCtrl.openColorPicker = (institution = sideMenuCtrl.getCurrentInstitutionProfile()) => { $mdDialog.show({ controller: "ColorPickerController", controllerAs: "colorPickerCtrl", - templateUrl: 'app/home/color_picker.html', + templateUrl: Utils.selectFieldBasedOnScreenSize('app/home/color_picker.html', + 'app/home/color_picker_mobile.html'), parent: angular.element(document.body), clickOutsideToClose: true, locals: { - user : sideMenuCtrl.user + user : sideMenuCtrl.user, + institution, }, - bindToController: true + bindToController: true, + onComplete: function() { sideMenuCtrl._isColorPickerActive = false }, }); }; } diff --git a/frontend/sideMenu/side_menu.css b/frontend/sideMenu/side_menu.css index 7e4520798..890f2c776 100644 --- a/frontend/sideMenu/side_menu.css +++ b/frontend/sideMenu/side_menu.css @@ -7,4 +7,25 @@ font-size: 12px; font-weight: 700; color: #019587; -} \ No newline at end of file +} + +img.avatar-icon { + padding: 0; + display: inline-block; + background-repeat: no-repeat no-repeat; + pointer-events: none; + width: 2em; + height: 2em; + border-radius: 50%; + margin-right: 0.6em; +} + +button.color-picker-button { + text-overflow: ellipsis; +} + +md-icon.color-picker-icon { + font-size: 2em; + margin-bottom: 0.3em !important; + margin-right: 0.5em !important; +} diff --git a/frontend/sideMenu/side_menu.html b/frontend/sideMenu/side_menu.html index 35213b44f..d144a4955 100644 --- a/frontend/sideMenu/side_menu.html +++ b/frontend/sideMenu/side_menu.html @@ -18,16 +18,21 @@ - - brightness_1 + + + brightness_1 {{ profile.institution.name }} - + - - color_lens + + color_lens GERENCIAR CORES + + {{ sideMenuCtrl.currentMenuOption.icon }} + {{ sideMenuCtrl.currentMenuOption.text }} + diff --git a/frontend/styles/custom.css b/frontend/styles/custom.css index 3551eab00..5d750a7d2 100644 --- a/frontend/styles/custom.css +++ b/frontend/styles/custom.css @@ -5,6 +5,11 @@ height: 100vh; } +.fill-width { + width: 100%; + min-width: 100%; +} + @media screen and (max-width: 1366px) { .fill-screen { min-height: 0; @@ -696,12 +701,6 @@ a:active { border-radius: 50%; } -.survey-card{ - margin-top: -1px; - margin-left: 40px; - margin-right: 40px; -} - .sm-label { font-size: 0.9em; opacity: 0.75; @@ -722,10 +721,10 @@ a:active { margin-right: -16px; text-align: center; width: 78px; - height: 20px; + height: 17px; color: #616161; padding-top: 16px; - padding-bottom: 16px; + padding-bottom: 15px; } .percentage-selected{ @@ -862,10 +861,44 @@ md-radio-button.md-default-theme.md-checked .md-off, md-radio-button.md-checked border: 2px solid; } +.expired-survey { + background-color: rgba(190, 186, 186, 0.966); + text-align: center; + font-size: 15px; +} + @media only screen and (max-width: 600px) { .small-text { font-size: 15px; } + + .hyperlink { + color: #009688; + } + + .option { + margin: 0 12px; + } + + .post-details-container { + display: block; + width: 100%; + overflow: hidden; + } + + .expired-survey { + font-size: 13px; + padding: 4px 0; + background-color: rgb(0, 150, 136) + } + + #post-container { + margin: 8px 0; + } + + .hide-scrollbar-mobile::-webkit-scrollbar { + display: none; + } } @media only screen and (max-width: 450px) { @@ -894,19 +927,6 @@ md-radio-button.md-default-theme.md-checked .md-off, md-radio-button.md-checked box-shadow:0 0 12px #9E9E9E; } -.dialog-transparent-without-shadow { - background-color: rgba(0, 0, 0, 0); - box-shadow: 0 0 0; -} - -@media screen and (max-width: 960px) { - .dialog-transparent-without-shadow { - max-width: 100%; - max-height: 100%; - border-radius: 0; - } -} - .share-post{ max-height: 250px; overflow: auto; @@ -942,12 +962,6 @@ md-input-container:not(.md-input-invalid).md-input-has-value .inputEmail { border-width: 0 0 2px; } -.expired-survey { - background-color: rgba(190, 186, 186, 0.966); - text-align: center; - font-size: 15px; -} - .shared-survey{ padding-top :0px; margin-top : -25px; @@ -1115,6 +1129,11 @@ md-input-container:not(.md-input-invalid).md-input-has-value .inputEmail { border-bottom-color: #009688 !important; } +.custom-title { + font-size: 20px; + font-weight: 500; +} + /* Smartphones */ @media screen and (max-width: 420px) { .custom-card { @@ -1156,4 +1175,22 @@ md-input-container:not(.md-input-invalid).md-input-has-value .inputEmail { white-space: nowrap; text-overflow: ellipsis; overflow: hidden; +} + +#post { + width: 100%; +} + +#post-page-container { + height: 100%; + overflow: hidden; +} + +#post-content { + max-height: 100%; +} + +#post-content > div { + max-height: 100%; + overflow-y: auto; } \ No newline at end of file diff --git a/frontend/survey/survey.css b/frontend/survey/survey.css index d39c0133b..fe4f801eb 100644 --- a/frontend/survey/survey.css +++ b/frontend/survey/survey.css @@ -38,6 +38,8 @@ .survey-card{ margin-top: -1px; + margin-left: 40px; + margin-right: 40px; } .survey-title-placeholder { @@ -65,6 +67,10 @@ grid-template-columns: auto 65px; } +.vote-btn { + display: grid; +} + @media screen and (max-width: 959px) { .survey-title-placeholder, { font-size: 0.84375em !important; @@ -193,6 +199,10 @@ .survey-title-placeholder, .survey-options { font-size: 0.75em !important; } + + .survey-card { + margin: 0; + } } @media screen and (max-width: 450px) { diff --git a/frontend/survey/survey_details.html b/frontend/survey/survey_details.html index c64e19e2d..71f60e6d7 100644 --- a/frontend/survey/survey_details.html +++ b/frontend/survey/survey_details.html @@ -13,7 +13,7 @@ + ng-class="surveyCtrl.votedOption(option) || option.selected ?'option-selected':''" class="option"> @@ -30,10 +30,15 @@ -
+
VOTAR
+
+ + VOTAR + +
diff --git a/frontend/test/specs/event/eventControllerSpec.js b/frontend/test/specs/event/eventControllerSpec.js index fb5b9070a..ec8f6c0f2 100644 --- a/frontend/test/specs/event/eventControllerSpec.js +++ b/frontend/test/specs/event/eventControllerSpec.js @@ -314,7 +314,7 @@ expect(eventCtrl._loadEvents).toHaveBeenCalled(); }); - it('Should call _loadEvents with eventService.getEvents', () => { + it('Should call _loadEvents with eventService.getEvents when key is null', () => { spyOn(eventCtrl, '_loadEvents'); eventCtrl.institutionKey = null; eventCtrl.loadMoreEvents(); @@ -322,12 +322,12 @@ .toHaveBeenCalledWith(eventService.getEvents, december, testYear); }); - it('Should call _loadEvents with eventService.getInstEvents', () => { + it("Should call _loadEvents with eventService.getEvents when key isn't null", () => { spyOn(eventCtrl, '_loadEvents'); eventCtrl.institutionKey = institution.key; eventCtrl.loadMoreEvents(); expect(eventCtrl._loadEvents) - .toHaveBeenCalledWith(eventService.getInstEvents, december, testYear); + .toHaveBeenCalledWith(eventService.getEvents, december, testYear); }); }); diff --git a/frontend/test/specs/event/eventDetailsControllerSpec.js b/frontend/test/specs/event/eventDetailsControllerSpec.js index af01a20ff..722022e6a 100644 --- a/frontend/test/specs/event/eventDetailsControllerSpec.js +++ b/frontend/test/specs/event/eventDetailsControllerSpec.js @@ -2,7 +2,8 @@ (describe('Test EventDetailsController', function () { - let eventCtrl, scope, q, httpBackend, rootScope, deffered, eventService, messageService, mdDialog, state; + let eventCtrl, scope, httpBackend, rootScope, deffered, eventService, + messageService, mdDialog, state, clipboard, q, states; const splab = { name: 'Splab', key: '098745' }, @@ -38,7 +39,7 @@ beforeEach(module('app')); beforeEach(inject(function ($controller, $httpBackend, $http, $q, AuthService, - $rootScope, EventService, MessageService, $mdDialog, $state) { + $rootScope, EventService, MessageService, $mdDialog, $state, ngClipboard, STATES) { scope = $rootScope.$new(); httpBackend = $httpBackend; rootScope = $rootScope; @@ -47,7 +48,9 @@ messageService = MessageService; mdDialog = $mdDialog; state = $state; + clipboard = ngClipboard; q = $q; + states = STATES; AuthService.login(user); eventCtrl = $controller('EventDetailsController', { @@ -73,6 +76,7 @@ describe('confirmDeleteEvent()', function () { beforeEach(function () { spyOn(mdDialog, 'confirm').and.callThrough(); + spyOn(state, 'go').and.callThrough(); spyOn(mdDialog, 'show').and.callFake(function () { return { then: function (callback) { @@ -92,6 +96,7 @@ expect(eventService.deleteEvent).toHaveBeenCalledWith(other_event); expect(mdDialog.confirm).toHaveBeenCalled(); expect(mdDialog.show).toHaveBeenCalled(); + expect(state.go).toHaveBeenCalledWith(states.EVENTS); }); }); @@ -253,6 +258,30 @@ }); }); + describe('copyLink()', () => { + it('should call toClipboard', () => { + spyOn(clipboard, 'toClipboard'); + spyOn(messageService, 'showToast'); + + eventCtrl.event = new Event({key: 'aposdkspoakdposa'}); + eventCtrl.copyLink(); + + expect(clipboard.toClipboard).toHaveBeenCalled(); + expect(messageService.showToast).toHaveBeenCalled(); + }); + }); + + describe('generateToolbarMenuOptions()', () => { + it('should set defaultToolbarOptions', () => { + expect(eventCtrl.defaultToolbarOptions).toBeFalsy(); + + eventCtrl.generateToolbarMenuOptions(); + + expect(eventCtrl.defaultToolbarOptions).toBeTruthy(); + expect(eventCtrl.defaultToolbarOptions.length).toEqual(5); + }); + }); + describe('isFollower()', () => { beforeEach(() => { eventCtrl.event = new Event({ @@ -278,7 +307,7 @@ spyOn(messageService, 'showToast'); eventCtrl.event = new Event({ key: 'aopskdopas-OKAPODKAOP', followers: [] }); spyOn(eventCtrl.event, 'addFollower').and.callThrough(); - + eventCtrl.addFollower(); scope.$apply(); @@ -297,7 +326,7 @@ spyOn(eventCtrl.event, 'addFollower').and.callThrough(); const promise = eventCtrl.addFollower(); - + promise.catch(() => { expect(eventService.addFollower).toHaveBeenCalledWith(eventCtrl.event.key); expect(eventCtrl.event.addFollower).not.toHaveBeenCalled(); diff --git a/frontend/test/specs/home/colorPickerControllerSpec.js b/frontend/test/specs/home/colorPickerControllerSpec.js index a25027f38..a9789295a 100644 --- a/frontend/test/specs/home/colorPickerControllerSpec.js +++ b/frontend/test/specs/home/colorPickerControllerSpec.js @@ -8,7 +8,15 @@ var institution = { 'email' : 'institution@gmail.com', 'key': '123456', - 'institution_key' : '123456' + 'institution_key' : '123456', + 'color' : 'teal', + }; + + const secondInstitution = { + 'email' : 'institution2@gmail.com', + 'key': '1234567', + 'institution_key' : '1234567', + 'color' : 'teal', }; var colors = [{'value' : 'red'}, {'value':'purple'}, {'value':'blue'}]; @@ -17,10 +25,10 @@ 'name' : 'user', 'key': '12345', 'state' : 'active', - 'institution_profiles': [institution], - 'current_institution' : institution + 'institution_profiles': [institution, secondInstitution], + 'current_institution' : institution, }; - + beforeEach(inject(function ($controller, $httpBackend, HttpService, $mdDialog, AuthService, $rootScope, ProfileService) { scope = $rootScope.$new(); @@ -31,7 +39,8 @@ profileService = ProfileService; colorPickerCtrl = $controller('ColorPickerController', { - user: user + user, + institution, }); AuthService.login(user); @@ -54,25 +63,49 @@ describe('saveColor()', function() { beforeEach(function() { spyOn(profileService, 'editProfile').and.callThrough(); - }); - it('Should return true', function(done) { - var change = { + it('should change first institutions color', function(done) { + const change = { 'color' : 'blue', 'email' : 'institution@gmail.com', 'key': '123456', 'institution_key' : '123456' }; - colorPickerCtrl.newProfile = change; - colorPickerCtrl.newUser.institution_profiles = [change]; + colorPickerCtrl.newUser.institution_profiles = [change, secondInstitution]; + const diff = jsonpatch.compare(colorPickerCtrl.user, colorPickerCtrl.newUser); httpBackend.expect('PATCH', '/api/user').respond(200); - var promise = colorPickerCtrl.saveColor(); + const promise = colorPickerCtrl.saveColor(); promise.should.be.fulfilled.then(function() { - expect(colorPickerCtrl.user.getProfileColor()).toBe('blue'); - expect(colorPickerCtrl.user.institution_profiles).toHaveBeenCalled(change); - }).should.notify(done); + expect(colorPickerCtrl.user).toEqual(colorPickerCtrl.newUser); + expect(colorPickerCtrl.user.institution_profiles[0]).toEqual(change); + expect(profileService.editProfile).toHaveBeenCalledWith(diff); + done(); + }); + httpBackend.flush(); + scope.$apply(); + }); + + it('should change second institutions color', (done) => { + const change = { + 'color': 'red', + 'email' : 'institution2@gmail.com', + 'key': '1234567', + 'institution_key' : '1234567' + } + + colorPickerCtrl.newUser.institution_profiles = [institution, change]; + const diff = jsonpatch.compare(colorPickerCtrl.user, colorPickerCtrl.newUser); + + httpBackend.expect('PATCH', '/api/user').respond(200); + const promise = colorPickerCtrl.saveColor(); + promise.should.be.fulfilled.then(function() { + expect(colorPickerCtrl.user).toEqual(colorPickerCtrl.newUser); + expect(colorPickerCtrl.user.institution_profiles[1]).toEqual(change); + expect(profileService.editProfile).toHaveBeenCalledWith(diff); + done(); + }); httpBackend.flush(); scope.$apply(); }); diff --git a/frontend/test/specs/institution/editDescriptionControllerSpec.js b/frontend/test/specs/institution/editDescriptionControllerSpec.js new file mode 100644 index 000000000..ccf631000 --- /dev/null +++ b/frontend/test/specs/institution/editDescriptionControllerSpec.js @@ -0,0 +1,79 @@ +'use strict'; + +describe('Test Edit Description Institution', function() { + beforeEach(module('app')); + + var data, institution, editDescriptionCtrl, httpBackend, + observerRecorderService, scope, state, mdDialog, institutionService; + + var user = new User({ + institutions: [], + follows: [] + }); + + const fakeCallback = function(){ + const fakeResponse = callback => callback(); + return { + then: fakeResponse, + catch: fakeResponse, + finally: fakeResponse + }; + }; + + beforeEach(inject(function($controller, $httpBackend, $rootScope, $state, AuthService, + ObserverRecorderService, $mdDialog, InstitutionService) { + httpBackend = $httpBackend; + scope = $rootScope.$new(); + state = $state; + mdDialog = $mdDialog; + institutionService = InstitutionService; + observerRecorderService = ObserverRecorderService; + + AuthService.login(user); + + institution = { + name: 'InstName', + address: { + street: 'StreetName', + number: 1, + neighbourhood: 'NeighbourhoddName', + city: 'CityName', + federal_state: 'FederalStateName', + country: 'Brasil' + }, + state: 'active', + leader: 'LeaderName', + legal_nature: 'Public', + actuation_area: 'Laboratory', + description: 'institutionDescription', + sent_invitations: [], + parent_institution: null, + children_institutions: [], + key: 'instKey' + }; + + institution = new Institution(data); + + state.params.institution = institution; + editDescriptionCtrl = $controller('EditDescriptionController',{ + scope: scope, + institutionService: institutionService, + institution: institution + }); + editDescriptionCtrl.$onInit(); + })); + + describe('save()', function () { + beforeEach(function() { + spyOn(institutionService, 'update').and.callFake(fakeCallback); + spyOn(mdDialog, 'hide').and.callThrough(); + }); + + it('should update and call functions', function () { + editDescriptionCtrl.institution.description = "edit description"; + editDescriptionCtrl.save(); + expect(institutionService.update).toHaveBeenCalled(); + expect(mdDialog.hide).toHaveBeenCalled(); + }); + }); +}); \ No newline at end of file diff --git a/frontend/test/specs/institution/institutionControllerSpec.js b/frontend/test/specs/institution/institutionControllerSpec.js index a84eb2c95..e2117ddb0 100644 --- a/frontend/test/specs/institution/institutionControllerSpec.js +++ b/frontend/test/specs/institution/institutionControllerSpec.js @@ -258,13 +258,11 @@ describe('goToEvents', function() { it('should call state.go with the right params', function(){ spyOn(utilsService, 'selectNavOption'); - institutionCtrl.posts = posts; institutionCtrl.goToEvents(first_institution.key); expect(utilsService.selectNavOption).toHaveBeenCalledWith( - states.INST_EVENTS, + states.EVENTS, { institutionKey: first_institution.key, - posts: posts } ); }); diff --git a/frontend/test/specs/main/mainControllerSpec.js b/frontend/test/specs/main/mainControllerSpec.js index ee67d80f5..4c2f5e2c4 100644 --- a/frontend/test/specs/main/mainControllerSpec.js +++ b/frontend/test/specs/main/mainControllerSpec.js @@ -4,6 +4,7 @@ let mainCtrl, httpBackend, scope, createCtrl, state, states, mainToolbar; let authService, requestInvitationService, notificationListenerService, utilsService, pushNotificationService; + const window = {'location': {'reload': function(){}}}; const user = { name: 'user', key: 'user-key', @@ -81,7 +82,6 @@ spyOn(NotificationListenerService, 'multipleEventsListener').and.callFake(eventsListenerFake); spyOn(pushNotificationService, 'setupPushNotificationPermission').and.callFake(callFake); - authService.login(user); httpBackend.when('GET', "main/main.html").respond(200); @@ -89,10 +89,12 @@ httpBackend.when('GET', "error/user_inactive.html").respond(200); httpBackend.when('GET', "home/home.html").respond(200); httpBackend.when('GET', "auth/login.html").respond(200); + createCtrl = function() { return $controller('MainController', { scope: scope, - AuthService: authService + AuthService: authService, + $window: window }); }; mainCtrl = createCtrl(); @@ -219,4 +221,12 @@ expect(authService.reload).toHaveBeenCalled(); }); }); + + describe('updateVersion', () => { + it('should call reload()', () => { + spyOn(authService, 'reload'); + mainCtrl.updateVersion(); + expect(authService.reload).toHaveBeenCalled(); + }); + }); })); \ No newline at end of file diff --git a/frontend/test/specs/post/postDetailsControllerSpec.js b/frontend/test/specs/post/postDetailsControllerSpec.js index 058ac398a..f4255a8c3 100644 --- a/frontend/test/specs/post/postDetailsControllerSpec.js +++ b/frontend/test/specs/post/postDetailsControllerSpec.js @@ -684,4 +684,14 @@ expect(postDetailsCtrl.isFollowingEvent()).toEqual(false); }); }); + + describe('isMobileScreen()', () => { + it('should call Utils.isMobileScreen', () => { + spyOn(Utils, 'isMobileScreen'); + + postDetailsCtrl.isMobileScreen(); + + expect(Utils.isMobileScreen).toHaveBeenCalled(); + }); + }); })); \ No newline at end of file diff --git a/frontend/test/specs/post/postPageControllerSpec.js b/frontend/test/specs/post/postPageControllerSpec.js index 0672ffc09..51544ed6a 100644 --- a/frontend/test/specs/post/postPageControllerSpec.js +++ b/frontend/test/specs/post/postPageControllerSpec.js @@ -2,7 +2,7 @@ (describe('Test PostPageController', function () { - var postCtrl, scope, httpBackend, postService, state; + let postCtrl, scope, httpBackend, postService, state, clipboard, messageService, mdDialog, q; var institutions = [ { name: 'Splab', key: '098745' }, @@ -13,18 +13,29 @@ 'title': 'Shared Post', 'text': 'This post will be shared', 'photo_url': null, - 'key': '12300' + 'key': '12300', + subscribers: [] }, institutions[1].institution_key); beforeEach(module('app')); - beforeEach(inject(function ($controller, $httpBackend, PostService, $state) { + beforeEach(inject(function ($controller, $httpBackend, PostService, $state, ngClipboard, + MessageService, $mdDialog, $q, AuthService, $rootScope) { httpBackend = $httpBackend; state = $state; postService = PostService; + clipboard = ngClipboard; + messageService = MessageService; + mdDialog = $mdDialog; + q = $q; + scope = $rootScope.$new(); httpBackend.when('GET', "main/main.html").respond(200); httpBackend.when('GET', "home/home.html").respond(200); + AuthService.getCurrentUser = function () { + return new User({key: 'aopsdkopdaskospdpokdskop'}); + }; + spyOn(postService, 'getPost').and.callFake(function () { return { then: function (callback) { @@ -36,10 +47,11 @@ state.params.key = post.key postCtrl = $controller('PostPageController', { PostService: postService, - state: state + state: state, + scope: scope }); - + postCtrl.$onInit(); })); afterEach(function () { @@ -78,4 +90,95 @@ expect(result).toEqual(true); }); }); + + describe('copyLink()', () => { + it('should call toClipboard', () => { + spyOn(Utils, 'generateLink'); + spyOn(clipboard, 'toClipboard'); + spyOn(messageService, 'showToast'); + + postCtrl.copyLink(); + + expect(Utils.generateLink).toHaveBeenCalled(); + expect(clipboard.toClipboard).toHaveBeenCalled(); + expect(messageService.showToast).toHaveBeenCalled(); + }); + }); + + describe('reloadPost()', () => { + + }); + + describe('share()', () => { + it('should call show', () => { + spyOn(mdDialog, 'show'); + + postCtrl.share('$event'); + + expect(mdDialog.show).toHaveBeenCalled(); + }); + }); + + describe('addSubscriber()', () => { + it('should call addSubscriber', () => { + spyOn(postService, 'addSubscriber').and.callFake(() => { + return q.when(); + }); + spyOn(messageService, 'showToast'); + + postCtrl.addSubscriber(); + scope.$apply(); + + expect(postService.addSubscriber).toHaveBeenCalled(); + expect(messageService.showToast).toHaveBeenCalled(); + expect(postCtrl.post.subscribers).toEqual([postCtrl.user.key]); + }); + }); + + describe('removeSubscriber()', () => { + it('should call removeSubscriber', () => { + spyOn(postService, 'removeSubscriber').and.callFake(() => { + return q.when(); + }); + spyOn(messageService, 'showToast'); + + postCtrl.removeSubscriber(); + scope.$apply(); + + expect(postService.removeSubscriber).toHaveBeenCalled(); + expect(messageService.showToast).toHaveBeenCalled(); + }); + }); + + describe('isSubscriber()', () => { + it('should be truthy', () => { + postCtrl.post.subscribers.push(postCtrl.user.key); + expect(postCtrl.isSubscriber()).toBeTruthy(); + }); + + it('should be falsy', () => { + postCtrl.post.subscribers = []; + expect(postCtrl.isSubscriber()).toBeFalsy(); + }); + }); + + describe('generateToolbarOptions()', () => { + it('should set defaultToolbrOptions', () => { + postCtrl.defaultToolbarOptions = []; + + postCtrl.generateToolbarOptions(); + + expect(postCtrl.defaultToolbarOptions.length).toEqual(6); + }); + }); + + describe('reloadPost()', () => { + it('should call getPost', () => { + spyOn(Object, 'values'); + postCtrl.reloadPost(); + + expect(postService.getPost).toHaveBeenCalled(); + expect(Object.values).toHaveBeenCalled(); + }); + }); })); \ No newline at end of file diff --git a/frontend/test/specs/toolbar/defaultToolbarControllerSpec.js b/frontend/test/specs/toolbar/defaultToolbarControllerSpec.js new file mode 100644 index 000000000..292e3a0a1 --- /dev/null +++ b/frontend/test/specs/toolbar/defaultToolbarControllerSpec.js @@ -0,0 +1,33 @@ +'use strict'; + +describe('DefaultToolbarController test', () => { + beforeEach(module('app')); + + let scope, defaultToolbarCtrl, window; + + beforeEach(inject(function ($componentController, $rootScope, SCREEN_SIZES, $window) { + scope = $rootScope.$new(); + window = $window; + defaultToolbarCtrl = $componentController('defaultToolbar', null, { + scope: scope, + SCREEN_SIZES: SCREEN_SIZES, + $window: window + }); + })); + + describe('isMobileScreen()', () => { + it('should call isMobileScreen', () => { + spyOn(Utils, 'isMobileScreen'); + defaultToolbarCtrl.isMobileScreen(); + expect(Utils.isMobileScreen).toHaveBeenCalled(); + }); + }); + + describe('goBack()', () => { + it('should call back()', () => { + spyOn(window.history, 'back'); + defaultToolbarCtrl.goBack(); + expect(window.history.back).toHaveBeenCalled(); + }); + }); +}); \ No newline at end of file diff --git a/frontend/test/specs/toolbar/whiteToolbarControllerSpec.js b/frontend/test/specs/toolbar/whiteToolbarControllerSpec.js new file mode 100644 index 000000000..0f44bae74 --- /dev/null +++ b/frontend/test/specs/toolbar/whiteToolbarControllerSpec.js @@ -0,0 +1,33 @@ +'use strict'; + +describe('DefaultToolbarController test', () => { + beforeEach(module('app')); + + let scope, whiteToolbarCtrl, window; + + beforeEach(inject(function ($componentController, $rootScope, SCREEN_SIZES, $window) { + scope = $rootScope.$new(); + window = $window; + whiteToolbarCtrl = $componentController('whiteToolbar', null, { + scope: scope, + SCREEN_SIZES: SCREEN_SIZES, + $window: window + }); + })); + + describe('isMobileScreen()', () => { + it('should call isMobileScreen', () => { + spyOn(Utils, 'isMobileScreen'); + whiteToolbarCtrl.isMobileScreen(); + expect(Utils.isMobileScreen).toHaveBeenCalled(); + }); + }); + + describe('goBack()', () => { + it('should call back()', () => { + spyOn(window.history, 'back'); + whiteToolbarCtrl.goBack(); + expect(window.history.back).toHaveBeenCalled(); + }); + }); +}); \ No newline at end of file diff --git a/frontend/toolbar/defaultToolbar.component.js b/frontend/toolbar/defaultToolbar.component.js new file mode 100644 index 000000000..00ff39bd7 --- /dev/null +++ b/frontend/toolbar/defaultToolbar.component.js @@ -0,0 +1,38 @@ +'use strict'; + +(function () { + const app = angular.module("app"); + + /** + * Generic component that lives in some pages that doesn't need + * the main toolbar. Only for mobile. + * {object} menuOptions -- Some options the user can choose when the menu is clicked + */ + app.component("defaultToolbar", { + templateUrl: 'app/toolbar/default_toolbar_mobile.html', + controller: ['SCREEN_SIZES', '$window', DefaultToolbarController], + controllerAs: 'defaultToolbarCtrl', + bindings: { + menuOptions: '=', + noOptions: '@' + } + }); + + function DefaultToolbarController(SCREEN_SIZES, $window) { + const defaultToolbarCtrl = this; + + /** + * Returns true if the application is being used by a mobile + */ + defaultToolbarCtrl.isMobileScreen = () => { + return Utils.isMobileScreen(SCREEN_SIZES.SMARTPHONE); + }; + + /** + * Redirect the user to the previous page. + */ + defaultToolbarCtrl.goBack = () => { + $window.history.back(); + }; + } +})(); \ No newline at end of file diff --git a/frontend/toolbar/default_toolbar_mobile.css b/frontend/toolbar/default_toolbar_mobile.css new file mode 100644 index 000000000..21f7a0c3c --- /dev/null +++ b/frontend/toolbar/default_toolbar_mobile.css @@ -0,0 +1,22 @@ +.default-toolbar-mobile { + display: grid; + grid-template-columns: 50% 50%; + grid-template-rows: 100%; + width: 100%; +} + +.toolbar-back-button { + justify-self: start; + grid-column-start: 1; + grid-column-end: 2; +} + +.toolbar-back-button md-icon { + font-size: 2.5em; +} + +.default-toolbar-menu-container { + justify-self: end; + grid-column-start: 2; + grid-column-end: 3; +} \ No newline at end of file diff --git a/frontend/toolbar/default_toolbar_mobile.html b/frontend/toolbar/default_toolbar_mobile.html new file mode 100644 index 000000000..21729e553 --- /dev/null +++ b/frontend/toolbar/default_toolbar_mobile.html @@ -0,0 +1,25 @@ + +
+ + keyboard_arrow_left + +
+ + + more_vert + + + +
+ + {{option.icon}} + + {{option.title}} +
+
+
+
+
+
+
\ No newline at end of file diff --git a/frontend/toolbar/main_toolbar_mobile.html b/frontend/toolbar/main_toolbar_mobile.html index 71744e11a..73b05debe 100644 --- a/frontend/toolbar/main_toolbar_mobile.html +++ b/frontend/toolbar/main_toolbar_mobile.html @@ -1,4 +1,4 @@ -
diff --git a/frontend/toolbar/whiteToolbar.component.js b/frontend/toolbar/whiteToolbar.component.js new file mode 100644 index 000000000..6f503fd51 --- /dev/null +++ b/frontend/toolbar/whiteToolbar.component.js @@ -0,0 +1,36 @@ +'use strict'; + +(function () { + const app = angular.module("app"); + + app.component('whiteToolbar', { + templateUrl: 'app/toolbar/white_toolbar_mobile.html', + controller: ['$window', 'SCREEN_SIZES', WhiteToolbarController], + controllerAs: 'whiteToolbarCtrl', + bindings: { + title: '@', + rightButton: '=', + primaryButtonIcon: '@', + titleClass: '@', + menuOptions: '=' + } + }); + + function WhiteToolbarController($window, SCREEN_SIZES) { + const whiteToolbarCtrl = this; + + /** + * Redirect the user to the previous page. + */ + whiteToolbarCtrl.goBack = () => { + return $window.history.back(); + }; + + /** + * Returns true if the application is being used by a mobile + */ + whiteToolbarCtrl.isMobileScreen = () => { + return Utils.isMobileScreen(SCREEN_SIZES.SMARTPHONE); + }; + } +})(); \ No newline at end of file diff --git a/frontend/toolbar/white_toolbar_mobile.css b/frontend/toolbar/white_toolbar_mobile.css new file mode 100644 index 000000000..58c627209 --- /dev/null +++ b/frontend/toolbar/white_toolbar_mobile.css @@ -0,0 +1,44 @@ +.white-toolbar-mobile { + background-color: white; + box-shadow: 0px 3px 6px grey; + height: 60px; + width: 100%; + font-size: 20px; + display: grid; + grid-template-rows: auto; + grid-template-columns: 20% 50% auto; + align-items: center; +} + +.toolbar-icon-alignment { + font-size: 2.5em; + line-height: inherit !important; +} + +.edit-inst-toolbar-title { + color: grey; + font-size: 0.8em; + word-break: normal; + grid-column-end: 4 !important; + text-transform: uppercase; +} + +.white-toolbar-menu-button { + grid-column-start: 3; + grid-column-end: 4; + justify-self: end; + align-self: center; +} + +.white-toolbar-title-container { + grid-column-start: 2; + grid-column-end: 3; +} + +.white-toolbar-menu-option-title { + margin-left: 0.5em; +} + +.white-toolbar-menu-content { + width: 100%; +} \ No newline at end of file diff --git a/frontend/toolbar/white_toolbar_mobile.html b/frontend/toolbar/white_toolbar_mobile.html new file mode 100644 index 000000000..b2c428448 --- /dev/null +++ b/frontend/toolbar/white_toolbar_mobile.html @@ -0,0 +1,34 @@ +
+ + {{whiteToolbarCtrl.primaryButtonIcon}} + +
+ {{whiteToolbarCtrl.title}} +
+ + {{whiteToolbarCtrl.rightButton.name}} + + {{whiteToolbarCtrl.rightButton.icon}} + + +
+ + + more_vert + + + +
+ + {{option.icon}} + + {{option.title}} +
+
+
+
+
+
\ No newline at end of file diff --git a/frontend/user/configProfile/configProfileController.js b/frontend/user/configProfile/configProfileController.js index d590cf1c3..e7a863bb6 100644 --- a/frontend/user/configProfile/configProfileController.js +++ b/frontend/user/configProfile/configProfileController.js @@ -25,8 +25,9 @@ configProfileCtrl.$onInit = () => { configProfileCtrl._setupUser(); + configProfileCtrl.setSaveButton(); setPushNotificationModel(); - } + }; configProfileCtrl._setupUser = () => { if(configProfileCtrl.canEdit()) { @@ -183,6 +184,18 @@ window.history.back(); }; + /** + * Sets save button's properties. + */ + configProfileCtrl.setSaveButton = () => { + configProfileCtrl.saveButton = { + class: 'config-profile__toolbar--save', + action: configProfileCtrl.finish, + name: 'SALVAR', + isAvailable: () => !configProfileCtrl.loadingSubmission + }; + }; + function deleteUser() { const promise = UserService.deleteAccount(); promise.then(function success() { diff --git a/frontend/user/configProfile/config_profile_mobile.html b/frontend/user/configProfile/config_profile_mobile.html index 257d2cf6d..b410a2ab0 100644 --- a/frontend/user/configProfile/config_profile_mobile.html +++ b/frontend/user/configProfile/config_profile_mobile.html @@ -1,17 +1,7 @@ - -
- - close - - MEU PERFIL - - SALVAR - -
-
+ - +
diff --git a/frontend/utils/entityShowcase/entityShowcase.component.js b/frontend/utils/entityShowcase/entityShowcase.component.js new file mode 100644 index 000000000..af59f7ae1 --- /dev/null +++ b/frontend/utils/entityShowcase/entityShowcase.component.js @@ -0,0 +1,50 @@ +(function () { + 'use strict'; + + /** + * Component that shows an entity (user or institution). It receives a title (entity name), + * a subtitle (an entity information) and an avatar (the image to be shown). Also, it can + * receive an icon in case the entity doesn't have an avatar url. It can receive a left + * action that will be triggered when the user clicks on the avatar or the icon and an array + * of rightIconBtns objects that has an icon, an icon-color and an action. You shouldn't use + * both icon and avatar at the same time. + * rightIconBtns example: + * [ + * { + * icon: aIcon, + * iconColor: aIconColor, + * action: aAction, + * }, + * ] + * @class entityShowcase + * @example + * + * + */ + angular.module("app").component("entityShowcase", { + templateUrl: "app/utils/entityShowcase/entityShowcase.html", + controller: entityShowcaseController, + controllerAs: "entityShowcaseCtrl", + bindings: { + avatar: "<", + icon: "@", + title: "<", + subtitle: "<", + leftAction: "<", + rightIconBtns: "<", + }, + }); + + function entityShowcaseController() { + const entityShowcaseCtrl = this; + + entityShowcaseCtrl.showIcon = () => !_.isNil(entityShowcaseCtrl.icon); + } + +})(); \ No newline at end of file diff --git a/frontend/utils/entityShowcase/entityShowcase.css b/frontend/utils/entityShowcase/entityShowcase.css new file mode 100644 index 000000000..0c124c6cc --- /dev/null +++ b/frontend/utils/entityShowcase/entityShowcase.css @@ -0,0 +1,46 @@ +.entity-showcase { + display: grid; + grid-template-columns: max-content auto auto; + grid-template-rows: max-content max-content; + grid-template-areas: + 'avatar title right-icon-buttons' + 'avatar subtitle right-icon-buttons'; + grid-gap: 0 0.6rem; + padding: 0.6rem; + align-items: center; + background-color: #E0E0E0; +} + +.entity-showcase__avatar { + grid-area: avatar; + height: 3em; + width: 3em; + border-radius: 50%; +} + +.entity-showcase__icon { + grid-area: avatar; + background-color: #8BC34A; + color: white; + height: 2em; + width: 2em; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; +} + +.entity-showcase__title { + grid-area: title; + line-height: 1rem; +} + +.entity-showcase__subtitle { + grid-area: subtitle; + font-size: 0.8em; + line-height: 1rem; +} + +.entity-showcase__right-icon-buttons { + grid-area: right-icon-buttons; +} \ No newline at end of file diff --git a/frontend/utils/entityShowcase/entityShowcase.html b/frontend/utils/entityShowcase/entityShowcase.html new file mode 100644 index 000000000..64a9ad883 --- /dev/null +++ b/frontend/utils/entityShowcase/entityShowcase.html @@ -0,0 +1,16 @@ +
+ + + {{ entityShowcaseCtrl.icon }} + + {{entityShowcaseCtrl.title }} + {{entityShowcaseCtrl.subtitle }} +
+ + +
+
\ No newline at end of file diff --git a/frontend/utils/generic-dialog/gen-dialog.component.js b/frontend/utils/generic-dialog/gen-dialog.component.js new file mode 100644 index 000000000..9196ad771 --- /dev/null +++ b/frontend/utils/generic-dialog/gen-dialog.component.js @@ -0,0 +1,53 @@ +(function () { + 'use strict'; + + /** + * An generical dialog component that has a title, a subtitle, a cancel button + * and a confirm button. It receives as a binding the title, the subtitle and + * the confirm action. + * @class genDialog + * @example + * + * + *

The dialog content

+ *

It could be any HTML

+ * Including a component + *
+ *
+ */ + angular.module("app").component("genDialog", { + templateUrl: "app/utils/generic-dialog/gen-dialog.html", + controller: [ + '$mdDialog', + dialogController, + ], + controllerAs: "dialogCtrl", + bindings: { + title: "@", + subtitle: "@", + confirmAction: "<", + cancelText: "@", + confirmText: "@", + }, + transclude: true, + }); + + function dialogController($mdDialog) { + const dialogCtrl = this; + + dialogCtrl.$onInit = () => { + _.defaults(dialogCtrl, { + confirmAction: () => {}, + cancelText: "Cancelar", + confirmText: "Confirmar", + }); + }; + + dialogCtrl.cancelDialog = $mdDialog.cancel; + dialogCtrl.confirmDialog = () => { + dialogCtrl.confirmAction(); + $mdDialog.hide(); + }; + } + +})(); \ No newline at end of file diff --git a/frontend/utils/generic-dialog/gen-dialog.css b/frontend/utils/generic-dialog/gen-dialog.css new file mode 100644 index 000000000..491362a50 --- /dev/null +++ b/frontend/utils/generic-dialog/gen-dialog.css @@ -0,0 +1,34 @@ +.dialog__container { + display: grid; + grid-template-areas: + "title" + "subtitle" + "content" + "buttons"; + padding: 1.5em 2em 0; +} + +.dialog__title { + grid-area: title; + margin: 0; +} + +.dialog__subtitle { + grid-area: subtitle; + padding: 0.5em 0; +} + +.dialog__content { + grid-area: content; +} + +.dialog__buttons { + grid-area: buttons; + display: flex; + flex-direction: row; + justify-content: flex-end; +} + +.dialog__button { + margin: 1em 0; +} \ No newline at end of file diff --git a/frontend/utils/generic-dialog/gen-dialog.html b/frontend/utils/generic-dialog/gen-dialog.html new file mode 100644 index 000000000..87ce510d7 --- /dev/null +++ b/frontend/utils/generic-dialog/gen-dialog.html @@ -0,0 +1,16 @@ + + + {{ dialogCtrl.title }} + {{ dialogCtrl.subtitle }} + + + +
+ + {{ dialogCtrl.cancelText }} + + + {{ dialogCtrl.confirmText }} + +
+
\ No newline at end of file diff --git a/frontend/utils/hideNavbar.js b/frontend/utils/hideNavbar.js index 83ecbba79..a02bf9625 100644 --- a/frontend/utils/hideNavbar.js +++ b/frontend/utils/hideNavbar.js @@ -3,7 +3,7 @@ (function () { const app = angular.module('app'); - app.directive('hideNavbar', ['STATES','$state', function(STATES, $state) { + app.directive('hideNavbar', ['$transitions', 'STATES','$state', 'SCREEN_SIZES', function($transitions, STATES, $state, SCREEN_SIZES) { return { restrict: 'A', link: function(scope, element, attrs) { @@ -12,33 +12,47 @@ scope.INSTITUTION_STATES = [ STATES.INST_TIMELINE, STATES.INST_FOLLOWERS, STATES.INST_EVENTS, STATES.INST_MEMBERS, STATES.INST_REGISTRATION_DATA, STATES.INST_LINKS, - STATES.CONFIG_PROFILE + STATES.INST_DESCRIPTION, STATES.CONFIG_PROFILE ]; + + scope.STATES_WITHOUT_TOP_TOOLBAR = scope.INSTITUTION_STATES; + scope.STATES_WITHOUT_BOTTOM_TOOLBAR = [ STATES.CREATE_EVENT ]; + + scope.STATES_DINAMICALLY_BOTTOM = [ + STATES.INST_TIMELINE + ]; + + scope.STATES_DINAMICALLY_TOP = [ + STATES.HOME + ]; /** Definy initial style of toolbars according the current state. */ scope.initialToolbarDisplayState = function initialToolbarDisplayState(){ - const shouldHideBottomToolbar = !scope.isBottomToolbarAllowed() || - scope.INSTITUTION_STATES.includes($state.current.name); - if (!scope.isStateAllowedTopMobile) scope.hideElement(scope.topTollbar); - if (shouldHideBottomToolbar) scope.hideElement(scope.bottomToolbar); + if(Utils.isMobileScreen(450)){ + const shouldHideBottomToolbar = !scope.isBottomToolbarAllowed() || + STATES.INST_TIMELINE === $state.current.name; + if (!scope.isStateAllowedTopMobile) scope.hideElement(scope.topTollbar); + if (shouldHideBottomToolbar) scope.hideElement(scope.bottomToolbar) + else{ + if(scope.bottomToolbar)scope.bottomToolbar.style.display = 'flex'; + } + } } /** Verify if current states is allowed to show top toolbar. */ scope.isTopToolbarAllowed = function isTopToolbarAllowed() { - let statesNotAllowed = [STATES.CREATE_EVENT]; - statesNotAllowed = statesNotAllowed.concat(scope.INSTITUTION_STATES); - return !statesNotAllowed.includes($state.current.name); + return !inStateArray(scope.STATES_WITHOUT_TOP_TOOLBAR); } /** Verify if current states is allowed to show bottom toolbar. */ scope.isBottomToolbarAllowed = function isBottomToolbarAllowed() { - return !scope.STATES_WITHOUT_BOTTOM_TOOLBAR.includes($state.current.name); + return !inStateArray(scope.STATES_WITHOUT_BOTTOM_TOOLBAR); } /** Set property CSS to hide element. @@ -56,22 +70,24 @@ const hideTop = attrs.hideNavbar === "top" || hideBoth; const hideBottom = attrs.hideNavbar === "bottom" || hideBoth; - if(Utils.isMobileScreen(450)){ + if(Utils.isMobileScreen(SCREEN_SIZES.SMARTPHONE)){ const content = element[0]; const limitScrol = 30; - const hideTopDynamically = hideTop && scope.isStateAllowedTopMobile; - const hideBottomDynamically = hideBottom && scope.isStateAllowedBottom; + const hideTopDynamically = hideTop && inStateArray(scope.STATES_DINAMICALLY_TOP); + const hideBottomDynamically = hideBottom && inStateArray(scope.STATES_DINAMICALLY_BOTTOM); content.addEventListener('scroll', function() { const screenPosition = content.scrollTop; if (screenPosition <= limitScrol) { - scope.topTollbar.style.animation='1.0s fadeNav ease'; - scope.topTollbar.style.animationDelay='0s'; - if(hideTopDynamically) scope.topTollbar.style.display = 'block'; + if(scope.topTollbar){ + scope.topTollbar.style.animation='1.0s fadeNav ease'; + scope.topTollbar.style.animationDelay='0s'; + if(hideTopDynamically) scope.topTollbar.style.display = 'block'; + } if(hideBottomDynamically) scope.hideElement(scope.bottomToolbar); } else { - if(hideTopDynamically) scope.hideElement(scope.topTollbar); + if(hideTopDynamically) scope.topTollbar && scope.hideElement(scope.topTollbar); if(hideBottomDynamically) scope.bottomToolbar.style.display = 'flex'; } }); @@ -79,6 +95,18 @@ } + /** Verify array includes current state. + */ + function inStateArray(array){ + return array.includes($state.current.name) + } + + /** Observer to state change and definy how initial state of toolbar. + */ + $transitions.onSuccess({ + to: () => {return true;} + }, () => {scope.initialToolbarDisplayState()}); + scope.isStateAllowedTopMobile = scope.isTopToolbarAllowed(); scope.isStateAllowedBottom = scope.isBottomToolbarAllowed(); diff --git a/frontend/utils/iconButton/iconButton.component.js b/frontend/utils/iconButton/iconButton.component.js new file mode 100644 index 000000000..244adc05a --- /dev/null +++ b/frontend/utils/iconButton/iconButton.component.js @@ -0,0 +1,32 @@ +(function () { + 'use strict'; + + /** + * Icon button component that receives an icon and an icon color as a binding. + * If no icon color has been passed, it will be defined to be equals to the default + * color (#EEE). The on click function should be added by ng-click on the component + * tag. + * @class iconButton + * @example + * + */ + angular.module("app").component("iconButton", { + templateUrl: "app/utils/iconButton/iconButton.html", + controller: iconButtonController, + controllerAs: "iconButtonCtrl", + bindings: { + icon: "@", + iconColor: "@", + action: '<', + }, + }); + + function iconButtonController() { + const iconButtonCtrl = this; + + iconButtonCtrl.$onInit = () => { + iconButtonCtrl.iconColor = iconButtonCtrl.iconColor || "#EEE"; + }; + } + +})(); \ No newline at end of file diff --git a/frontend/utils/iconButton/iconButton.html b/frontend/utils/iconButton/iconButton.html new file mode 100644 index 000000000..1d1b79311 --- /dev/null +++ b/frontend/utils/iconButton/iconButton.html @@ -0,0 +1,5 @@ + + + {{ iconButtonCtrl.icon }} + + \ No newline at end of file diff --git a/frontend/utils/is-empty-card/is-empty-card.component.js b/frontend/utils/is-empty-card/is-empty-card.component.js new file mode 100644 index 000000000..f34f6b275 --- /dev/null +++ b/frontend/utils/is-empty-card/is-empty-card.component.js @@ -0,0 +1,24 @@ +(function () { + 'use strict'; + + /** + * Component that is shown when something is empty and in the error page. + * It's shows a default image and a text that it receives as a binding. + * @class isEmptyCard + * @example + * + */ + angular.module("app").component("isEmptyCard", { + templateUrl: "app/utils/is-empty-card/is-empty-card.html", + controller: isEmptyCardController, + controllerAs: "isEmptyCardCtrl", + bindings: { + text: "@" + }, + }); + + function isEmptyCardController() { + + } + +})(); \ No newline at end of file diff --git a/frontend/utils/is-empty-card/is-empty-card.css b/frontend/utils/is-empty-card/is-empty-card.css new file mode 100644 index 000000000..6dcbd8dfe --- /dev/null +++ b/frontend/utils/is-empty-card/is-empty-card.css @@ -0,0 +1,26 @@ +.is-empty-card__container { + display: grid; + grid-template-rows: auto auto; + grid-template-areas: + 'image' + 'text'; + width: 100%; + height: 100%; +} + +.is-empty-card__image { + grid-area: image; + width: 100%; +} + +.is-empty-card__text { + grid-area: text; + display: flex; + font-size: 1.5em; + margin: 1em 0; + padding: 0 0.5em; + font-weight: bold; + color: #009688; + text-align: center; + justify-content: center; +} \ No newline at end of file diff --git a/frontend/utils/is-empty-card/is-empty-card.html b/frontend/utils/is-empty-card/is-empty-card.html new file mode 100644 index 000000000..0e62794a9 --- /dev/null +++ b/frontend/utils/is-empty-card/is-empty-card.html @@ -0,0 +1,4 @@ +
+ + {{ isEmptyCardCtrl.text }} +
diff --git a/frontend/utils/statesConstants.js b/frontend/utils/statesConstants.js index 46a0d8d44..5c74d2543 100644 --- a/frontend/utils/statesConstants.js +++ b/frontend/utils/statesConstants.js @@ -16,6 +16,7 @@ NOTIFICATION: "app.user.notifications", INSTITUTION: "app.institution", INST_TIMELINE: "app.institution.timeline", + INST_DESCRIPTION: "app.institution.description", INST_FOLLOWERS: "app.institution.followers", INST_EVENTS: "app.institution.events", INST_MEMBERS: "app.institution.members",