net.rrm.ehour.ui.report.panel.criteria.ReportCriteriaPanel.java Source code

Java tutorial

Introduction

Here is the source code for net.rrm.ehour.ui.report.panel.criteria.ReportCriteriaPanel.java

Source

/*
/*
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

package net.rrm.ehour.ui.report.panel.criteria;

import com.google.common.collect.Lists;
import com.googlecode.wicket.jquery.ui.form.datepicker.DatePicker;
import net.rrm.ehour.config.EhourConfig;
import net.rrm.ehour.domain.*;
import net.rrm.ehour.report.criteria.ReportCriteria;
import net.rrm.ehour.report.criteria.ReportCriteriaUpdateType;
import net.rrm.ehour.report.criteria.Sort;
import net.rrm.ehour.report.criteria.UserSelectedCriteria;
import net.rrm.ehour.report.service.ReportCriteriaService;
import net.rrm.ehour.ui.common.border.GreyBlueRoundedBorder;
import net.rrm.ehour.ui.common.border.GreySquaredRoundedBorder;
import net.rrm.ehour.ui.common.component.PlaceholderPanel;
import net.rrm.ehour.ui.common.decorator.LoadingSpinnerDecorator;
import net.rrm.ehour.ui.common.event.AjaxEvent;
import net.rrm.ehour.ui.common.event.EventPublisher;
import net.rrm.ehour.ui.common.panel.AbstractAjaxPanel;
import net.rrm.ehour.ui.common.panel.datepicker.LocalizedDatePicker;
import net.rrm.ehour.ui.common.renderers.DomainObjectChoiceRenderer;
import net.rrm.ehour.ui.common.session.EhourWebSession;
import net.rrm.ehour.ui.common.util.WebGeo;
import net.rrm.ehour.ui.common.wicket.Container;
import net.rrm.ehour.ui.report.panel.criteria.quick.*;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.attributes.AjaxRequestAttributes;
import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
import org.apache.wicket.ajax.markup.html.AjaxLink;
import org.apache.wicket.ajax.markup.html.form.AjaxButton;
import org.apache.wicket.ajax.markup.html.form.AjaxCheckBox;
import org.apache.wicket.event.Broadcast;
import org.apache.wicket.event.IEvent;
import org.apache.wicket.markup.head.IHeaderResponse;
import org.apache.wicket.markup.head.JavaScriptReferenceHeaderItem;
import org.apache.wicket.markup.head.OnDomReadyHeaderItem;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.DropDownChoice;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.IChoiceRenderer;
import org.apache.wicket.markup.html.form.ListMultipleChoice;
import org.apache.wicket.markup.html.panel.Fragment;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.PropertyModel;
import org.apache.wicket.model.ResourceModel;
import org.apache.wicket.request.resource.JavaScriptResourceReference;
import org.apache.wicket.spring.injection.annot.SpringBean;
import org.apache.wicket.util.string.AppendingStringBuffer;

import java.util.*;

import static net.rrm.ehour.report.criteria.UserSelectedCriteria.ReportType;
import static net.rrm.ehour.ui.report.panel.criteria.quick.QuickPeriod.QuickType.*;

/**
 * Base report criteria panel which adds the quick date selections
 */

public class ReportCriteriaPanel extends AbstractAjaxPanel<ReportCriteriaBackingBean> {
    private static final long serialVersionUID = 161160822264046559L;

    private static final int MAX_CRITERIA_ROW = 6;
    private static final JavaScriptResourceReference CRITERIA_JS = new JavaScriptResourceReference(
            ReportCriteriaPanel.class, "report_criteria.js");

    private static final String ID_USERDEPT_PLACEHOLDER = "userDepartmentPlaceholder";

    public static final int AMOUNT_OF_QUICKWEEKS = 8;
    public static final int AMOUNT_OF_QUICKMONTHS = 6;
    public static final int AMOUNT_OF_QUICKQUARTERS = 3;
    private static final String CUSTOMER_FILTER_INPUT_ID = "#customerFilterInput";
    private static final String PROJECT_FILTER_INPUT_ID = "#projectFilterInput";
    private static final String DEPARTMENT_FILTER_INPUT_ID = "#departmentFilterInput";
    private static final String USER_FILTER_INPUT_ID = "#userFilterInput";
    private static final int MONTHS_PER_QUARTER = 3;

    @SpringBean
    private ReportCriteriaService reportCriteriaService;

    private DatePicker startDatePicker;
    private DatePicker endDatePicker;
    private ListMultipleChoice<Project> projects;
    private ListMultipleChoice<Customer> customers;
    private ListMultipleChoice<User> users;
    private List<WebMarkupContainer> quickSelections;
    private ListMultipleChoice<UserDepartment> departments;
    private WebMarkupContainer lockedDateSelection;

    public ReportCriteriaPanel(String id, IModel<ReportCriteriaBackingBean> model) {
        super(id, model);

        GreySquaredRoundedBorder greyBorder = new GreySquaredRoundedBorder("border", WebGeo.W_FULL);
        add(greyBorder);

        setOutputMarkupId(true);

        Form<ReportCriteriaBackingBean> form = new Form<>("criteriaForm");
        form.setOutputMarkupId(true);
        greyBorder.add(form);

        addDates(form, model);

        GreyBlueRoundedBorder blueBorder = new GreyBlueRoundedBorder("customerProjectsBorder");
        form.add(blueBorder);

        ReportCriteriaBackingBean criteriaBackingBean = model.getObject();
        addCustomerSelection(criteriaBackingBean, blueBorder);
        addProjectSelection(criteriaBackingBean, blueBorder);

        UserSelectedCriteria userSelectedCriteria = criteriaBackingBean.getReportCriteria()
                .getUserSelectedCriteria();
        boolean showDepartmentAndOtherUsers = userSelectedCriteria.isForGlobalReport()
                || userSelectedCriteria.isForPm();
        form.add(showDepartmentAndOtherUsers ? addDepartmentsAndUsers(criteriaBackingBean, ID_USERDEPT_PLACEHOLDER)
                : new PlaceholderPanel(ID_USERDEPT_PLACEHOLDER));

        addSubmitButtons(form);

        form.add(addReportTypeSelection("reportRole", userSelectedCriteria));
    }

    private WebMarkupContainer addReportTypeSelection(String id, final UserSelectedCriteria criteria) {
        List<ReportType> types = determineReportRoleTypes();

        if (types.size() > 1) {
            final DropDownChoice<ReportType> choice = new DropDownChoice<>(id,
                    new PropertyModel<ReportType>(criteria, "selectedReportType"), types,
                    new IChoiceRenderer<ReportType>() {
                        @Override
                        public Object getDisplayValue(ReportType object) {

                            switch (object) {
                            case REPORT:
                                return new ResourceModel("report.useRole.global")
                                        .wrapOnAssignment(ReportCriteriaPanel.this).getObject();
                            case PM:
                                return new ResourceModel("report.useRole.pm")
                                        .wrapOnAssignment(ReportCriteriaPanel.this).getObject();
                            default:
                                return new ResourceModel("report.useRole.single")
                                        .wrapOnAssignment(ReportCriteriaPanel.this).getObject();
                            }
                        }

                        @Override
                        public String getIdValue(ReportType object, int index) {
                            return Integer.toString(index);
                        }
                    });

            choice.add(new AjaxFormComponentUpdatingBehavior("onchange") {
                @Override
                protected void onUpdate(AjaxRequestTarget target) {
                    ReportType type = choice.getModelObject();
                    User user = EhourWebSession.getUser();
                    switch (type) {
                    case REPORT:
                        criteria.setReportTypeToGlobal();
                        break;
                    case PM:
                        criteria.setReportTypeToPM(user);
                        break;
                    case INDIVIDUAL_USER:
                    default:
                        criteria.setReportTypeToIndividualUser(user);
                        break;
                    }

                    send(getPage(), Broadcast.EXACT, new ReportTypeChangeEvent(target, type));
                }
            });

            return choice;
        } else {
            WebMarkupContainer container = new WebMarkupContainer(id);
            container.setVisible(false);
            return container;
        }
    }

    public static class ReportTypeChangeEvent {
        public final AjaxRequestTarget target;
        public final ReportType reportType;

        public ReportTypeChangeEvent(AjaxRequestTarget target, ReportType reportType) {
            this.target = target;
            this.reportType = reportType;
        }
    }

    private List<ReportType> determineReportRoleTypes() {
        List<ReportType> types = Lists.newArrayList();

        EhourWebSession session = getEhourWebSession();

        if (session.isReporter()) {
            types.add(ReportType.REPORT);
        }

        if (session.isProjectManager()) {
            types.add(ReportType.PM);
        }

        types.add(ReportType.INDIVIDUAL_USER);
        return types;
    }

    private void addCustomerSelection(final ReportCriteriaBackingBean bean, WebMarkupContainer parent) {
        customers = new ListMultipleChoice<>("reportCriteria.userSelectedCriteria.customers",
                new PropertyModel<Collection<Customer>>(bean, "reportCriteria.userSelectedCriteria.customers"),
                new PropertyModel<List<Customer>>(bean, "reportCriteria.availableCriteria.customers"),
                new DomainObjectChoiceRenderer<Customer>());
        customers.setMarkupId("customerSelect");
        customers.setMaxRows(MAX_CRITERIA_ROW);

        customers.setOutputMarkupId(true);

        customers.add(new AjaxFormComponentUpdatingBehavior("change") {
            private static final long serialVersionUID = -5588313671121851508L;

            protected void onUpdate(AjaxRequestTarget target) {
                // show only projects for selected customers
                List<Customer> preCustomers = Lists
                        .newArrayList(bean.getReportCriteria().getAvailableCriteria().getCustomers());

                updateReportCriteria(ReportCriteriaUpdateType.UPDATE_CUSTOMERS_AND_PROJECTS);

                List<Customer> postCustomers = bean.getReportCriteria().getAvailableCriteria().getCustomers();

                if (!preCustomers.containsAll(postCustomers)) {
                    updateCustomers(target);
                }
                updateProjects(target);
            }
        });

        parent.add(customers);

        final AjaxCheckBox deactivateBox = new AjaxCheckBox(
                "reportCriteria.userSelectedCriteria.onlyActiveCustomers") {
            @Override
            protected void onUpdate(AjaxRequestTarget target) {
                updateCustomersAndProjects(target);
            }
        };
        deactivateBox.setOutputMarkupId(true);
        parent.add(deactivateBox);

        final DropDownChoice<Sort> customerSort = createCustomerSort();
        parent.add(customerSort);

        parent.add(new AjaxLink<Void>("clearCustomer") {
            @Override
            public void onClick(AjaxRequestTarget target) {
                bean.getReportCriteria().getUserSelectedCriteria().resetCustomerSelection();

                updateReportCriteria(ReportCriteriaUpdateType.UPDATE_CUSTOMERS_AND_PROJECTS);

                updateCustomers(target);
                updateProjects(target);

                target.appendJavaScript(getCustomerFilterClearScript());

                target.add(customerSort);
                target.add(deactivateBox);
            }
        });
    }

    private DropDownChoice<Sort> createCustomerSort() {
        DropDownChoice<Sort> customerSort = new DropDownChoice<>("customerSort",
                new PropertyModel<Sort>(getPanelModelObject(), "reportCriteria.userSelectedCriteria.customerSort"),
                Arrays.asList(Sort.NAME, Sort.CODE), new SortRenderer());
        customerSort.add(new AjaxFormComponentUpdatingBehavior("change") {
            @Override
            protected void onUpdate(AjaxRequestTarget target) {
                ReportCriteria reportCriteria = getPanelModelObject().getReportCriteria();
                reportCriteria.updateCustomerSort();

                target.add(customers);
                applyClientSideCustomerFilter(target);
            }
        });

        customerSort.setOutputMarkupId(true);

        return customerSort;
    }

    private void addProjectSelection(final ReportCriteriaBackingBean bean, WebMarkupContainer parent) {
        projects = new ListMultipleChoice<>("reportCriteria.userSelectedCriteria.projects",
                new PropertyModel<List<Project>>(bean, "reportCriteria.availableCriteria.projects"),
                new DomainObjectChoiceRenderer<Project>());
        projects.setMaxRows(MAX_CRITERIA_ROW);
        projects.setOutputMarkupId(true);
        projects.setMarkupId("projectSelect");

        projects.add(new AjaxFormComponentUpdatingBehavior("change") {
            @Override
            protected void onUpdate(AjaxRequestTarget target) {

            }
        });

        parent.add(projects);

        final AjaxCheckBox onlyActiveCheckbox = new AjaxCheckBox(
                "reportCriteria.userSelectedCriteria.onlyActiveProjects") {
            @Override
            protected void onUpdate(AjaxRequestTarget target) {
                updateReportCriteria(ReportCriteriaUpdateType.UPDATE_CUSTOMERS_AND_PROJECTS);
                updateCustomers(target);
                updateProjects(target);
            }
        };
        onlyActiveCheckbox.setOutputMarkupId(true);
        parent.add(onlyActiveCheckbox);

        final AjaxCheckBox billableCheckbox = new AjaxCheckBox(
                "reportCriteria.userSelectedCriteria.onlyBillableProjects",
                new PropertyModel<Boolean>(ReportCriteriaPanel.this.getDefaultModel(),
                        "reportCriteria.userSelectedCriteria.onlyBillableProjects")) {
            @Override
            protected void onUpdate(AjaxRequestTarget target) {
                updateReportCriteria(ReportCriteriaUpdateType.UPDATE_CUSTOMERS_AND_PROJECTS);
                updateProjects(target);
            }
        };

        billableCheckbox.setMarkupId("reportCriteria.userSelectedCriteria.onlyBillableProjects");
        billableCheckbox.setOutputMarkupId(true);
        parent.add(billableCheckbox);

        final DropDownChoice<Sort> projectSort = createProjectSort();
        parent.add(projectSort);

        parent.add(new AjaxLink<Void>("clearProject") {
            @Override
            public void onClick(AjaxRequestTarget target) {
                bean.getReportCriteria().getUserSelectedCriteria().resetProjectSelection();

                updateReportCriteria(ReportCriteriaUpdateType.UPDATE_CUSTOMERS_AND_PROJECTS);

                updateCustomers(target);
                updateProjects(target);

                target.appendJavaScript(getProjectFilterClearScript());

                target.add(projectSort);
                target.add(billableCheckbox);
                target.add(onlyActiveCheckbox);
            }
        });
    }

    private DropDownChoice<Sort> createProjectSort() {
        DropDownChoice<Sort> projectSort = new DropDownChoice<>("projectSort",
                new PropertyModel<Sort>(getPanelModelObject(), "reportCriteria.userSelectedCriteria.projectSort"),
                Arrays.asList(Sort.NAME, Sort.CODE), new SortRenderer());
        projectSort.add(new AjaxFormComponentUpdatingBehavior("change") {
            @Override
            protected void onUpdate(AjaxRequestTarget target) {
                ReportCriteria reportCriteria = getPanelModelObject().getReportCriteria();
                reportCriteria.updateProjectSort();

                target.add(projects);
                applyClientSideProjectFilter(target);
            }
        });

        projectSort.setOutputMarkupId(true);

        return projectSort;
    }

    private void updateCustomersAndProjects(AjaxRequestTarget target) {
        updateReportCriteria(ReportCriteriaUpdateType.UPDATE_CUSTOMERS_AND_PROJECTS);

        updateProjects(target);
        updateCustomers(target);
    }

    private void updateProjects(AjaxRequestTarget target) {
        target.add(projects);
        applyClientSideProjectFilter(target);
    }

    private void applyClientSideProjectFilter(AjaxRequestTarget target) {
        target.appendJavaScript(getProjectFilterRegistrationScript());
    }

    private void updateCustomers(AjaxRequestTarget target) {
        target.add(customers);
        applyClientSideCustomerFilter(target);
    }

    private void applyClientSideCustomerFilter(AjaxRequestTarget target) {
        target.appendJavaScript(getCustomerFilterRegistrationScript());
    }

    private void addUserSelection(final ReportCriteriaBackingBean bean, WebMarkupContainer parent) {
        users = new ListMultipleChoice<>("reportCriteria.userSelectedCriteria.users",
                new PropertyModel<List<User>>(getDefaultModel(), "reportCriteria.availableCriteria.users"),
                new DomainObjectChoiceRenderer<User>());
        users.setMarkupId("userSelect");
        users.setOutputMarkupId(true);
        users.setMaxRows(MAX_CRITERIA_ROW);
        parent.add(users);

        // hide active checkbox
        final AjaxCheckBox deactivateBox = new AjaxCheckBox("reportCriteria.userSelectedCriteria.onlyActiveUsers") {
            private static final long serialVersionUID = 2585047163449150793L;

            @Override
            protected void onUpdate(AjaxRequestTarget target) {
                updateReportCriteria(ReportCriteriaUpdateType.UPDATE_USERS_AND_DEPTS);
                target.add(users);

                // reapply the filter to the possible new contents of the dropdowns
                target.appendJavaScript(getUserFilterRegistrationScript());
            }
        };

        parent.add(deactivateBox);

        Label filterToggleText = new Label("onlyActiveUsersLabel", new ResourceModel("report.onlyActiveUsers"));
        parent.add(filterToggleText);

        parent.add(new AjaxLink<Void>("clearUser") {
            @Override
            public void onClick(AjaxRequestTarget target) {
                bean.getReportCriteria().getUserSelectedCriteria().resetUserSelection();

                updateReportCriteria(ReportCriteriaUpdateType.UPDATE_USERS_AND_DEPTS);

                updateUsers(target);
                updateDepartments(target);

                target.appendJavaScript(getUserFilterClearScript());
            }
        });
    }

    private void updateDepartments(AjaxRequestTarget target) {
        target.add(departments);
        applyClientSideDepartmentFilter(target);
    }

    private void applyClientSideDepartmentFilter(AjaxRequestTarget target) {
        target.appendJavaScript(getDepartmentFilterRegistrationScript());
    }

    private void updateUsers(AjaxRequestTarget target) {
        target.add(users);
        applyClientSideUserFilter(target);
    }

    private void applyClientSideUserFilter(AjaxRequestTarget target) {
        target.appendJavaScript(getUserFilterRegistrationScript());
    }

    private Fragment addDepartmentsAndUsers(ReportCriteriaBackingBean bean, String id) {
        Fragment fragment = new Fragment(id, "userDepartmentCriteria", this);

        GreyBlueRoundedBorder blueBorder = new GreyBlueRoundedBorder("deptUserBorder");
        fragment.add(blueBorder);

        addUserDepartmentSelection(bean, blueBorder);
        addUserSelection(bean, blueBorder);

        return fragment;
    }

    private void addUserDepartmentSelection(final ReportCriteriaBackingBean bean, WebMarkupContainer parent) {
        departments = new ListMultipleChoice<>("reportCriteria.userSelectedCriteria.userDepartments",
                new PropertyModel<List<UserDepartment>>(getDefaultModel(),
                        "reportCriteria.availableCriteria.userDepartments"),
                new DomainObjectChoiceRenderer<UserDepartment>());
        departments.setMarkupId("departmentSelect");
        departments.setMaxRows(MAX_CRITERIA_ROW);
        departments.setOutputMarkupId(true);

        // update projects when customer(s) selected
        departments.add(new AjaxFormComponentUpdatingBehavior("change") {
            private static final long serialVersionUID = 1L;

            protected void onUpdate(AjaxRequestTarget target) {
                // show only projects for selected customers
                updateReportCriteria(ReportCriteriaUpdateType.UPDATE_USERS_AND_DEPTS);
                target.add(users);
            }
        });

        parent.add(departments);

        parent.add(new AjaxLink<Void>("clearDepartment") {
            @Override
            public void onClick(AjaxRequestTarget target) {
                bean.getReportCriteria().getUserSelectedCriteria().resetUserDepartmentSelection();

                updateReportCriteria(ReportCriteriaUpdateType.UPDATE_USERS_AND_DEPTS);

                updateUsers(target);
                updateDepartments(target);

                target.appendJavaScript(getDepartmentFilterClearScript());
            }
        });
    }

    private void updateReportCriteria(ReportCriteriaUpdateType updateType) {
        ReportCriteriaBackingBean backingBean = getPanelModelObject();

        ReportCriteria reportCriteria = reportCriteriaService
                .syncUserReportCriteria(backingBean.getReportCriteria(), updateType);
        backingBean.getReportCriteria().setAvailableCriteria(reportCriteria.getAvailableCriteria());
    }

    @SuppressWarnings("serial")
    private void addSubmitButtons(Form<ReportCriteriaBackingBean> form) {
        // Submit
        AjaxButton submitButton = new AjaxButton("createReport", form) {
            @Override
            protected final void onSubmit(AjaxRequestTarget target, Form<?> form) {
                EventPublisher.publishAjaxEvent(this, new AjaxEvent(ReportCriteriaAjaxEventType.CRITERIA_UPDATED));
            }

            @Override
            protected void updateAjaxAttributes(AjaxRequestAttributes attributes) {
                super.updateAjaxAttributes(attributes);

                attributes.getAjaxCallListeners().add(new LoadingSpinnerDecorator());
            }

            @Override
            protected void onError(AjaxRequestTarget target, Form<?> form) {
                target.add(form);
            }
        };

        // default submit
        form.add(submitButton);

        // reset button
        AjaxButton resetButton = new AjaxButton("resetCriteria") {
            @Override
            protected final void onSubmit(AjaxRequestTarget target, Form<?> form) {
                EventPublisher.publishAjaxEvent(this, new AjaxEvent(ReportCriteriaAjaxEventType.CRITERIA_RESET));
            }

            @Override
            protected void onError(AjaxRequestTarget target, Form<?> form) {
                onSubmit(target, form);
            }
        };

        resetButton.setDefaultFormProcessing(false);

        form.add(resetButton);
    }

    private void addDates(Form<ReportCriteriaBackingBean> form, IModel<ReportCriteriaBackingBean> model) {
        startDatePicker = createDatePicker("reportCriteria.userSelectedCriteria.reportRange.dateStart",
                new PropertyModel<Date>(model, "reportCriteria.userSelectedCriteria.reportRange.dateStart"));
        form.add(startDatePicker);

        endDatePicker = createDatePicker("reportCriteria.userSelectedCriteria.reportRange.dateEnd",
                new PropertyModel<Date>(model, "reportCriteria.userSelectedCriteria.reportRange.dateEnd"));
        form.add(endDatePicker);

        quickSelections = new ArrayList<>();

        quickSelections.add(createQuickWeek());
        quickSelections.add(createQuickMonth());
        quickSelections.add(createQuickQuarter());

        for (WebMarkupContainer cont : quickSelections) {
            cont.setOutputMarkupId(true);
            form.add(cont);
        }

        lockedDateSelection = createLockedPeriodRangeDropdown("lockedDate");
        form.add(lockedDateSelection);
    }

    private WebMarkupContainer createLockedPeriodRangeDropdown(String id) {
        ReportCriteriaBackingBean panelModelObject = getPanelModelObject();

        List<TimesheetLock> locks = panelModelObject.getReportCriteria().getAvailableCriteria().getTimesheetLocks();

        if (locks == null || locks.isEmpty()) {
            Container placeholder = new Container(id);
            placeholder.setVisible(false);
            return placeholder;
        }

        DateDropDownChoice<TimesheetLock> lockedDateDropDown = new DateDropDownChoice<>(id,
                new PropertyModel<TimesheetLock>(getPanelModel(), "reportForLock"), locks,
                new IChoiceRenderer<TimesheetLock>() {
                    @Override
                    public Object getDisplayValue(TimesheetLock object) {
                        return object.getName();
                    }

                    @Override
                    public String getIdValue(TimesheetLock object, int index) {
                        return Integer.toString(index);
                    }
                });

        lockedDateDropDown.setOutputMarkupId(true);
        return lockedDateDropDown;

    }

    private DatePicker createDatePicker(String id, IModel<Date> model) {
        DatePicker datePicker = new LocalizedDatePicker(id, model);

        datePicker.setOutputMarkupId(true);
        datePicker.add(new AjaxFormComponentUpdatingBehavior("onchange") {
            private static final long serialVersionUID = -5588313671121851508L;

            @Override
            protected void onUpdate(AjaxRequestTarget target) {
                ReportCriteriaBackingBean backingBean = ReportCriteriaPanel.this.getPanelModelObject();
                backingBean.resetQuickSelections();

                for (WebMarkupContainer cont : quickSelections) {
                    target.add(cont);
                }
            }
        });

        return datePicker;
    }

    private WebMarkupContainer createQuickWeek() {
        List<QuickWeek> weeks = new ArrayList<>();
        Calendar currentDate = new GregorianCalendar();
        int currentWeek = -AMOUNT_OF_QUICKWEEKS;

        EhourConfig config = EhourWebSession.getEhourConfig();

        currentDate.setFirstDayOfWeek(config.getFirstDayOfWeek());
        currentDate.set(Calendar.DAY_OF_WEEK, config.getFirstDayOfWeek());

        currentDate.add(Calendar.WEEK_OF_YEAR, -1); //previous
        weeks.add(QuickWeek.shortcut(currentDate, config, QuickPeriod.QuickType.SHORTCUT_PREV));
        currentDate.add(Calendar.WEEK_OF_YEAR, 1); //current
        weeks.add(QuickWeek.shortcut(currentDate, config, QuickPeriod.QuickType.SHORTCUT_CURRENT));
        currentDate.add(Calendar.WEEK_OF_YEAR, 1); //next
        weeks.add(QuickWeek.shortcut(currentDate, config, QuickPeriod.QuickType.SHORTCUT_NEXT));
        currentDate.add(Calendar.WEEK_OF_YEAR, -1); //previous

        currentDate.add(Calendar.WEEK_OF_YEAR, currentWeek);
        weeks.add(QuickWeek.divider());

        for (; currentWeek < AMOUNT_OF_QUICKWEEKS; currentWeek++) {
            weeks.add(QuickWeek.instance(currentDate, config));

            currentDate.add(Calendar.WEEK_OF_YEAR, 1);
        }

        return createQuickDateDropDown("quickWeek", weeks, new QuickWeekRenderer());
    }

    private WebMarkupContainer createQuickMonth() {
        List<QuickMonth> months = new ArrayList<>();

        Calendar currentDate = new GregorianCalendar();
        currentDate.set(Calendar.DAY_OF_MONTH, 1);

        currentDate.add(Calendar.MONTH, -1); //previous
        months.add(QuickMonth.shortcut(currentDate, SHORTCUT_PREV));
        currentDate.add(Calendar.MONTH, 1); //current
        months.add(QuickMonth.shortcut(currentDate, SHORTCUT_CURRENT));
        currentDate.add(Calendar.MONTH, 1); //next
        months.add(QuickMonth.shortcut(currentDate, SHORTCUT_NEXT));

        months.add(QuickMonth.divider());

        currentDate.add(Calendar.MONTH, -1); //previous

        int currentMonth = -AMOUNT_OF_QUICKMONTHS;
        currentDate.add(Calendar.MONTH, currentMonth);

        for (; currentMonth < AMOUNT_OF_QUICKMONTHS; currentMonth++) {
            months.add(QuickMonth.instance(currentDate));
            currentDate.add(Calendar.MONTH, 1);
        }

        return createQuickDateDropDown("quickMonth", months, new QuickMonthRenderer());
    }

    private WebMarkupContainer createQuickQuarter() {
        List<QuickQuarter> quarters = new ArrayList<>();
        Calendar currentDate = new GregorianCalendar();
        int currentQuarter = -AMOUNT_OF_QUICKQUARTERS;

        int quarter = currentDate.get(Calendar.MONTH) / MONTHS_PER_QUARTER;
        currentDate.set(Calendar.MONTH, quarter * MONTHS_PER_QUARTER); // abuse rounding off

        currentDate.set(Calendar.DAY_OF_MONTH, 1);
        currentDate.add(Calendar.MONTH, -MONTHS_PER_QUARTER);
        quarters.add(QuickQuarter.shortcut(currentDate, SHORTCUT_PREV));
        currentDate.add(Calendar.MONTH, MONTHS_PER_QUARTER);
        quarters.add(QuickQuarter.shortcut(currentDate, SHORTCUT_CURRENT));
        currentDate.add(Calendar.MONTH, MONTHS_PER_QUARTER);
        quarters.add(QuickQuarter.shortcut(currentDate, SHORTCUT_NEXT));

        //reset
        currentDate.add(Calendar.MONTH, -MONTHS_PER_QUARTER);
        currentDate.add(Calendar.MONTH, currentQuarter * MONTHS_PER_QUARTER);

        quarters.add(QuickQuarter.divider());

        for (; currentQuarter < AMOUNT_OF_QUICKQUARTERS; currentQuarter++) {
            quarters.add(QuickQuarter.instance(currentDate));

            currentDate.add(Calendar.MONTH, MONTHS_PER_QUARTER);
        }

        return createQuickDateDropDown("quickQuarter", quarters, new QuickQuarterRenderer());
    }

    private <T extends QuickPeriod> DateDropDownChoice<T> createQuickDateDropDown(String id, List<T> options,
            IChoiceRenderer<T> renderer) {
        return new DateDropDownChoice<T>(id, options, renderer) {
            @Override
            protected void setOptionAttributes(AppendingStringBuffer buffer, T choice, int index, String selected) {
                super.setOptionAttributes(buffer, choice, index, selected);

                if (choice.isDivider()) {
                    buffer.append(" disabled");
                }
            }
        };
    }

    @Override
    public void onEvent(IEvent<?> event) {
        Object payload = event.getPayload();

        if (payload instanceof DateDropDownChoice.DateChangedPayload) {
            updateDates(((DateDropDownChoice.DateChangedPayload) payload).getTarget());
        }
    }

    private void updateDates(AjaxRequestTarget target) {
        target.add(startDatePicker);
        target.add(endDatePicker);

        if (lockedDateSelection.isVisibleInHierarchy()) {
            target.add(lockedDateSelection);
        }

        for (WebMarkupContainer quickSelection : quickSelections) {
            target.add(quickSelection);
        }
    }

    @Override
    public void renderHead(IHeaderResponse response) {
        response.render(JavaScriptReferenceHeaderItem.forReference(CRITERIA_JS));
        response.render(OnDomReadyHeaderItem.forScript(getCustomerFilterRegistrationScript()));
        response.render(OnDomReadyHeaderItem.forScript(getProjectFilterRegistrationScript()));
        response.render(OnDomReadyHeaderItem.forScript(getDepartmentFilterRegistrationScript()));
        response.render(OnDomReadyHeaderItem.forScript(getUserFilterRegistrationScript()));
    }

    private String getCustomerFilterRegistrationScript() {
        return String.format("initFilter('#customerSelect', '%s')", CUSTOMER_FILTER_INPUT_ID);
    }

    private String getProjectFilterRegistrationScript() {
        return String.format("initFilter('#projectSelect', '%s')", PROJECT_FILTER_INPUT_ID);
    }

    private CharSequence getDepartmentFilterRegistrationScript() {
        return String.format("initFilter('#departmentSelect', '%s')", DEPARTMENT_FILTER_INPUT_ID);
    }

    private CharSequence getUserFilterRegistrationScript() {
        return String.format("initFilter('#userSelect', '%s')", USER_FILTER_INPUT_ID);
    }

    private String getCustomerFilterClearScript() {
        return getFilterClearScript(CUSTOMER_FILTER_INPUT_ID);
    }

    private String getProjectFilterClearScript() {
        return getFilterClearScript(PROJECT_FILTER_INPUT_ID);
    }

    private String getDepartmentFilterClearScript() {
        return getFilterClearScript(DEPARTMENT_FILTER_INPUT_ID);
    }

    private String getUserFilterClearScript() {
        return getFilterClearScript(USER_FILTER_INPUT_ID);
    }

    private String getFilterClearScript(String inputId) {
        return String.format("clearFilter('%s');", inputId);
    }
}