Skip to content

Commit

Permalink
Simplify agenda view (#144)
Browse files Browse the repository at this point in the history
* Simplify agenda view

* Add upgrade step

* Add back edit and delete overlays

* Simplify edit / delete overlay

* Fix test

* Fix changelog

* Code review
rodfersou authored and hvelarde committed Sep 25, 2018
1 parent dfbfbb9 commit 5b769e9
Showing 19 changed files with 193 additions and 177 deletions.
3 changes: 3 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
@@ -8,6 +8,9 @@ Changelog
Este release atualiza as dependências do processamento de recursos estáticos.
Em ambiente de desenvolvimento pode ser necessário remover as pastas ``parts`` e ``webpack/node_modules`` para efetivar a atualização de ambiente.

- Simplifica visão de agenda.
[rodfersou]

- Corrige título da agenda diária caso o nome da pessoa no cargo mudar (closes `#142 <https://github.com/plonegovbr/brasil.gov.agenda/issues/142>`_).
[hvelarde]

1 change: 1 addition & 0 deletions buildout.cfg
Original file line number Diff line number Diff line change
@@ -15,6 +15,7 @@ parts +=
omelette
precompile
pylint
rebuild_i18n-sh
node
staticresources

1 change: 1 addition & 0 deletions src/brasil/gov/agenda/browser/agenda.py
Original file line number Diff line number Diff line change
@@ -30,6 +30,7 @@ def setup(self):
self._ts = api.portal.get_tool('translation_service')
self.agenda = self.context
self.editable = context_state.is_editable()
self.date = datetime.now()

def results(self, b_size=16):
"""Retorna as ultimas agendas diárias"""
21 changes: 11 additions & 10 deletions src/brasil/gov/agenda/browser/mixin.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# -*- coding: utf-8 -*-
from six.moves import range # noqa: I001
from datetime import datetime
from datetime import timedelta
from plone import api
from zope.component import getMultiAdapter
@@ -27,33 +26,35 @@ def _translate(self, msgid, locale='plonelocales', mapping=None):

def month(self):
tool = api.portal.get_tool('translation_service')
today = datetime.now()
strmonth = self._translate(tool.month_msgid(today.strftime('%m')))
strmonth = self._translate(tool.month_msgid(self.date.strftime('%m')))
return {
'strmonth': strmonth[:3].upper(),
'strmonthcomplete': strmonth.upper(),
'month': today.month,
'year': today.year,
'month': self.date.month,
'year': self.date.year,
}

def days(self):
tool = api.portal.get_tool('translation_service')
today = datetime.now()
# get a list with 3 days before and 3 days after today
days = [(today + timedelta(i)) for i in range(-3, 4)]
# get a list with 3 days before and 3 days after current day
days = [(self.date + timedelta(i)) for i in range(-3, 4)]
weekdays = []
for day in days:
cssclass = ['day']
if day == today:
has_appointment = False
if day == self.date:
cssclass.append('is-selected')
if self.agenda.get(day.strftime('%Y-%m-%d'), False):
has_appointment = True
cssclass.append('has-appointment')
strweek = self._translate(tool.day_msgid(day.weekday()))
# Weekday difference between datetime and DateTime objects
strweek = self._translate(tool.day_msgid((day.weekday() + 1) % 7))
weekdays.append({
'day': day.day,
'weekday': strweek[:3],
'iso': day.isoformat(),
'cssclass': ' '.join(cssclass),
'hasappointment': has_appointment,
})
return weekdays

13 changes: 8 additions & 5 deletions src/brasil/gov/agenda/browser/templates/agenda_macros.pt
Original file line number Diff line number Diff line change
@@ -30,10 +30,13 @@
<tal:event repeat="day view/days">
<li tal:attributes="data-day day/iso;
class day/cssclass;">
<div class="daypicker-day"
tal:content="day/day" />
<div class="daypicker-weekday"
tal:content="day/weekday" />
<a tal:attributes="href string:${view/agenda/absolute_url}/${day/iso}"
tal:omit-tag="not:day/hasappointment">
<div class="daypicker-day"
tal:content="day/day" />
<div class="daypicker-weekday"
tal:content="day/weekday" />
</a>
</li>
</tal:event>
</ul>
@@ -98,7 +101,7 @@
</li>
<li class="sem-compromisso item-compromisso"
tal:condition="view/exibe_sem_compromissos">
<span i18n:translate="label_agendadiaria_sem_compromissos">Sem compromissos oficiais.</span>
<span i18n:translate="">Atualmente não existem compromissos agendados.</span>
</li>
</ul>
</metal:form>
3 changes: 1 addition & 2 deletions src/brasil/gov/agenda/browser/templates/agendadiariaview.pt
Original file line number Diff line number Diff line change
@@ -13,8 +13,7 @@
</metal:slot>
<metal:block fill-slot="content-core">
<div class="dados-agenda"
tal:attributes="data-url view/agenda/absolute_url;
data-editable view/editable">
tal:attributes="data-url view/agenda/absolute_url">
<div class="calendar"></div>
<span metal:use-macro="context/@@agenda-macros/daypicker" />
<span metal:use-macro="context/@@agenda-macros/search" />
2 changes: 1 addition & 1 deletion src/brasil/gov/agenda/profiles/default/metadata.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0"?>
<metadata>
<version>4102</version>
<version>4103</version>
<dependencies>
<dependency>profile-collective.cover:default</dependency>
<dependency>profile-plone.app.dexterity:default</dependency>
2 changes: 1 addition & 1 deletion src/brasil/gov/agenda/tests/test_setup.py
Original file line number Diff line number Diff line change
@@ -40,7 +40,7 @@ def test_installed(self):

def test_version(self):
self.assertEqual(
self.st.getLastVersionForProfile(self.profile), (u'4102',))
self.st.getLastVersionForProfile(self.profile), (u'4103',))

def test_css_registered(self):
cssreg = getattr(self.portal, 'portal_css')
11 changes: 11 additions & 0 deletions src/brasil/gov/agenda/tests/test_upgrades.py
Original file line number Diff line number Diff line change
@@ -129,3 +129,14 @@ def test_uninstall_dependency(self):
self.assertIsNotNone(step)

self.execute_upgrade_step(step)


class to4103TestCase(UpgradeTestCaseBase):

def setUp(self):
UpgradeTestCaseBase.setUp(self, u'4102', u'4103')

def test_registrations(self):
version = self.setup.getLastVersionForProfile(self.profile_id)[0]
self.assertGreaterEqual(int(version), int(self.to_version))
self.assertEqual(self.total_steps, 2)
2 changes: 1 addition & 1 deletion src/brasil/gov/agenda/tiles/agenda.pt
Original file line number Diff line number Diff line change
@@ -101,7 +101,7 @@
</div>
</tal:page>
<div class="swiper-slide no-events"
i18n:translate="label_agendadiaria_sem_compromissos"
i18n:translate=""
tal:condition="not:view/agenda_diaria">
Sem compromissos oficiais.
</div>
7 changes: 7 additions & 0 deletions src/brasil/gov/agenda/tiles/agenda.py
Original file line number Diff line number Diff line change
@@ -82,6 +82,13 @@ class AgendaTile(PersistentCoverTile, AgendaMixin):
limit = 1
page_size = 3 # items by swiper slide

def __init__(self, context, request):
super(AgendaTile, self).__init__(context, request)
self.setup()

def setup(self):
self.date = datetime.now()

def populate_with_object(self, obj):
super(AgendaTile, self).populate_with_object(obj) # check permissions

1 change: 1 addition & 0 deletions src/brasil/gov/agenda/upgrades/configure.zcml
Original file line number Diff line number Diff line change
@@ -2,4 +2,5 @@
<include package=".v4100" />
<include package=".v4101" />
<include package=".v4102" />
<include package=".v4103" />
</configure>
1 change: 1 addition & 0 deletions src/brasil/gov/agenda/upgrades/v4103/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# -*- coding: utf-8 -*-
23 changes: 23 additions & 0 deletions src/brasil/gov/agenda/upgrades/v4103/configure.zcml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<configure
xmlns="http://namespaces.zope.org/zope"
xmlns:genericsetup="http://namespaces.zope.org/genericsetup">

<genericsetup:upgradeSteps
source="4102"
destination="4103"
profile="brasil.gov.agenda:default">

<genericsetup:upgradeStep
title="Cook CSS resources"
description="There were changes in the CSS files, so we need to cook the resources."
handler="..cook_css_resources"
/>
<genericsetup:upgradeStep
title="Cook JS resources"
description="There were changes in the JS files, so we need to cook the resources."
handler="..cook_javascript_resources"
/>

</genericsetup:upgradeSteps>

</configure>
107 changes: 29 additions & 78 deletions webpack/app/js/agenda.js
Original file line number Diff line number Diff line change
@@ -7,88 +7,39 @@ let zfill = (number, size=2) => (Array(size).fill('0').join('') + number).slice(
export default class AgendaView {
constructor(container) {
this.container = container;
this.datepicker = new DatePicker(container, this.onDateChange.bind(this), true);
this.datepicker = new DatePicker(container);
this.$appointments = this.$('.list-compromissos');
this.editable = container.dataset.editable === 'True';
this.bindEvents();
}
$(selector) {
return $(selector, this.container.parentElement);
}
extractTime(dateTime) {
let [, time] = dateTime.split('T');
let [hours, minutes, ] = time.split(':');
return `${zfill(hours)}h${zfill(minutes)}`;
}
onDateChange(agendaDiaria) {
let $item, $edit;
this.$appointments.html('');
this.$('.portalMessage.info').html(agendaDiaria.update);
if (agendaDiaria.hasAppointment === false) {
this.$appointments.append(`
<li class="sem-compromisso item-compromisso">
<span>Atualmente não existem compromissos agendados.</span>
</li>
`);
return;
}
for (let compromisso of agendaDiaria.items) {
$item = $(`
<li class="item-compromisso-wrapper">
<div class="item-compromisso">
<div class="compromisso-horarios">
<time class="horario compromisso-inicio"
datetime="${compromisso.datetime}">
${compromisso.start}
</time>
</div>
<div class="compromisso-dados">
<h4 class="compromisso-titulo">${compromisso.title}</h4>` +
(compromisso.location == null? '': `<p class="compromisso-local">${compromisso.location}</p>`) +
`<span class="download-compromisso">
<a class="add-agenda vcal" href="${compromisso.href}/vcal_view">VCAL</a>
<span>Adicionar ao meu calendário</span>
</span>
</div>
</div>
</li>
`);
if (this.editable) {
$edit = $(`
<ul class="compromisso-acoes">
<li class="compromisso-acao">
<a class="compromisso editar_compromisso acao" href="${compromisso.href}/edit">Editar</a>
</li>
<li class="compromisso-acao">
<a class="compromisso remover_compromisso acao" href="${compromisso.href}/delete_confirmation">Remover</a>
</li>
</ul>
`);
$('.editar_compromisso', $edit).prepOverlay({
subtype: 'ajax',
filter: common_content_filter,
formselector: '#form',
cssclass: 'overlay-compromisso',
noform: function(el) {return $.plonepopups.noformerrorshow(el, 'redirect');},
redirect: location.href,
closeselector: '[name="form.buttons.cancel"]',
width:'50%'
});
$('.remover_compromisso', $edit).prepOverlay({
subtype: 'ajax',
filter: common_content_filter,
formselector: '#delete_confirmation',
cssclass: 'overlay-compromisso',
noform: function(el) {return $.plonepopups.noformerrorshow(el, 'redirect');},
redirect: location.href,
closeselector: '[name="form.button.Cancel"]',
width:'50%'
});
$('.item-compromisso', $item).append($edit);
}
if (compromisso.isNow) {
$('.compromisso-horarios', $item).append('<div class="now">Agora</div>');
}
this.$appointments.append($item);
}
/**
* Create overlays
* Create overlays when edit and remove appointments
**/
bindEvents(){
// overlay when edit appointment
this.$('.editar_compromisso').prepOverlay({
subtype: 'ajax',
filter: common_content_filter,
formselector: '#form',
cssclass: 'overlay-compromisso',
noform: function(el) {return $.plonepopups.noformerrorshow(el, 'redirect');},
redirect: location.href,
closeselector: '[name="form.buttons.cancel"]',
width:'50%'
});
// overlay when delete appointment
this.$('.remover_compromisso').prepOverlay({
subtype: 'ajax',
filter: common_content_filter,
formselector: '#delete_confirmation',
cssclass: 'overlay-compromisso',
noform: function(el) {return $.plonepopups.noformerrorshow(el, 'redirect');},
redirect: location.href,
closeselector: '[name="form.button.Cancel"]',
width:'50%'
});
}
}
82 changes: 16 additions & 66 deletions webpack/app/js/datepicker.js
Original file line number Diff line number Diff line change
@@ -3,11 +3,9 @@ let zfill = (number, size=2) => (Array(size).fill('0').join('') + number).slice(


export default class DatePicker {
constructor(container, callback, updateTitle=false) {
constructor(container) {
this.container = container;
this.agendaURL = container.getAttribute('data-url');
this.callback = callback;
this.updateTitle = updateTitle;
this.$month = this.$('.monthpicker .month');
this.$year = this.$('.monthpicker .year');
this.$day = this.$('.daypicker')
@@ -18,11 +16,9 @@ export default class DatePicker {
this.daysWithAppointments = []
this.initMonthPicker();
let today = new Date();
this.pageLoad = true;
this.year = today.getFullYear();
this.month = today.getMonth();
this.day = today.getDate();
this.$('.daypicker').on('click', '.day', this.onDayClick.bind(this));
$(window).resize(this.resize.bind(this));
if ($('.portaltype-agendadiaria').length > 0) {
let pathDate = location.pathname.split('/').pop();
@@ -38,30 +34,14 @@ export default class DatePicker {
}
update() {
let agendaDiariaURL = `${this.year}-${zfill(this.month + 1)}-${zfill(this.day)}`;
if (typeof this.callback === 'function') {
$.ajax({
url: `${this.agendaURL}/json/${agendaDiariaURL}`,
context: this,
global: false,
}).always(function(data) {
this.callback(data[3]);
this.updateDayPicker(data);
this.daysWithAppointments = data[3].daysWithAppointments;
this.updateMonthPicker();
});
}
// we don't want to change URL when page load
if (this.updateTitle && ! this.pageLoad) {
let agendaDiaria = `${zfill(this.day)}/${zfill(this.month + 1)}/${this.year}`;
let title = `Agenda de ${$('.documentFirstHeading').text().trim()} para ${agendaDiaria}`;
window.history.pushState(
{day: this.day, month: this.month, year: this.year},
title,
`${this.agendaURL}/${agendaDiariaURL}`
);
document.title = title;
}
this.pageLoad = false;
$.ajax({
url: `${this.agendaURL}/json/${agendaDiariaURL}`,
context: this,
global: false,
}).always(function(data) {
this.daysWithAppointments = data[3].daysWithAppointments;
this.updateMonthPicker();
});
}
updateMonthPicker() {
this.$currentPicker.datepicker('setDate', new Date(this.year, this.month, this.day));
@@ -77,25 +57,6 @@ export default class DatePicker {
this.resize();
}
}
updateDayPicker(data) {
this.$day.html('');
for (let day of data) {
let cssclass = ['day'];
if (day.isSelected) {
cssclass.push('is-selected');
}
let $day = $(`
<li data-day="${day.datetime}" class="${cssclass.join(' ')}">
<div class="daypicker-day">${day.day}</div>
<div class="daypicker-weekday">${day.weekday}</div>
</li>
`);
if (day.hasAppointment) {
$day.addClass('has-appointment');
}
this.$day.append($day);
}
}
resize(e) {
let isMobile = ($(window).width() <= 767);
// if it is called by resize event and responsive don't change
@@ -129,17 +90,19 @@ export default class DatePicker {
// this event is needed to get right translation
$(window).on('load', function() {
let onSelect = function(dateText, inst) {
this.year = inst.selectedYear;
this.month = inst.selectedMonth;
this.day = parseInt(inst.selectedDay);
this.update();
let year = inst.selectedYear;
let month = inst.selectedMonth + 1;
let day = parseInt(inst.selectedDay);
// https://stackoverflow.com/a/19374679
window.location = `${this.agendaURL}/${year}-${zfill(month)}-${zfill(day)}`;
}.bind(this);
let beforeShowDay = function(date) {
let day = date.toISOString().slice(0, 10);
if (this.daysWithAppointments.indexOf(day) >= 0) {
return [true, 'ui-has-appointments', ''];
}
return [true, '', ''];
// disable click for days without appointments
return [false, '', ''];
}.bind(this);
this.$datePicker.datepicker( {
onSelect: onSelect,
@@ -156,17 +119,4 @@ export default class DatePicker {
this.update();
}.bind(this));
}
onDayClick(e) {
e.preventDefault();
let day = e.target;
if (day.tagName !== 'LI') {
day = day.parentElement;
}
let date = new Date(day.getAttribute('data-day'))
this.year = date.getFullYear();
this.month = date.getMonth();
this.day = date.getDate();
this.$currentPicker.datepicker('setDate', date);
this.update();
}
}
57 changes: 52 additions & 5 deletions webpack/app/js/tile.js
Original file line number Diff line number Diff line change
@@ -11,15 +11,11 @@ export default class AgendaTile {
this.datepicker = new DatePicker(this.tile, this.onDateChange.bind(this));
this.initSwiper();
this.$('.is-now').append('<div class="now">Agora</div>')
this.$('.daypicker').on('click', '.day', this.onDayClick.bind(this));
}
$(selector) {
return $(selector, this.tile);
}
extractTime(dateTime) {
let [, time] = dateTime.split('T');
let [hours, minutes, ] = time.split(':');
return `${zfill(hours)}h${zfill(minutes)}`;
}
onDateChange(agendaDiaria) {
this.swiper.removeAllSlides();
let $slide = $('<div class="swiper-slide"></div>');
@@ -69,4 +65,55 @@ export default class AgendaTile {
},
});
}
/**
* Update day picker
* Recreate list of days when ajax finish
**/
updateDayPicker(data) {
this.datepicker.$day.html('');
for (let day of data) {
let cssclass = ['day'];
if (day.isSelected) {
cssclass.push('is-selected');
}
let $day = $(`
<li data-day="${day.datetime}" class="${cssclass.join(' ')}">
<div class="daypicker-day">${day.day}</div>
<div class="daypicker-weekday">${day.weekday}</div>
</li>
`);
if (day.hasAppointment) {
$day.addClass('has-appointment');
}
this.datepicker.$day.append($day);
}
}
/**
* Day Click Event
* When click a day need to show the appointments of selected day, and upgate the list of days
**/
onDayClick(e) {
e.preventDefault();
let day = e.target;
if (day.tagName !== 'LI') {
day = day.parentElement;
}
let date = new Date(day.getAttribute('data-day'))
this.datepicker.year = date.getFullYear();
this.datepicker.month = date.getMonth();
this.datepicker.day = date.getDate();
this.datepicker.$currentPicker.datepicker('setDate', date);

let agendaDiariaURL = `${this.datepicker.year}-${zfill(this.datepicker.month + 1)}-${zfill(this.datepicker.day)}`;
$.ajax({
url: `${this.datepicker.agendaURL}/json/${agendaDiariaURL}`,
context: this,
global: false,
}).always(function(data) {
this.updateDayPicker(data);
this.onDateChange(data[3]);
this.datepicker.daysWithAppointments = data[3].daysWithAppointments;
this.datepicker.updateMonthPicker();
});
}
}
30 changes: 22 additions & 8 deletions webpack/app/scss/_datepicker.scss
Original file line number Diff line number Diff line change
@@ -129,7 +129,12 @@ body .ui-datepicker,
color: #222222;
text-transform: uppercase;
}
a.ui-state-default {
.ui-state-disabled,
.ui-widget-content .ui-state-disabled,
.ui-widget-header .ui-state-disabled {
opacity: 1;
}
.ui-state-default {
width: 30px;
height: 30px;
background: none;
@@ -180,25 +185,34 @@ body .ui-datepicker,

list-style-type: none;
}
.day {
.day,
.day > a:link,
.day > a:visited,
.day > a:hover {
display: flex;
flex-direction: column;

border: none;

width: 95px;
height: 95px;

cursor: pointer;

color: #DBDBDB;

align-items: center;
justify-content: center;
}
.day.has-appointment {
color: #222;
.day.has-appointment,
.day.has-appointment > a:link,
.day.has-appointment > a:visited,
.day.has-appointment > a:hover {
color: #222 !important;
}
.day.is-selected {
color: #FFF;
.day.is-selected,
.day.is-selected > a:link,
.day.is-selected > a:visited,
.day.is-selected > a:hover {
color: #FFF !important;
background-color: #56C75F;
}
.day:last-child {
3 changes: 3 additions & 0 deletions webpack/app/scss/_tile.scss
Original file line number Diff line number Diff line change
@@ -160,6 +160,9 @@
height: 15rem;
align-items: center;
}
.day {
cursor: pointer;
}
}
#content .agenda-tile.has-image {
.title {

0 comments on commit 5b769e9

Please sign in to comment.