Чтобы могли сильно менять внешний вид, не сильно влезая в наш код, мы прикрутили bootstrap.
В сложных случаях нужно будет менять layout.xhtml.
По поводу меню - будет возможность менять, пока не решили как.
Контент каждой странички - отдельный xhtml, например, WEB-INF/content/kernel/status.xhtml.
Классы-"поставщики данных" могут быть использованы в любом xhtml (т.е. нет привязки одна страница - один поставщик).
Но конкретно сейчас для каждой страницы есть свой класс, плюс она может использовать другие.
Можете посмотреть тот же status.xhtml из последней сборки - убрали там всё лишнее, стало близко к обычному html.
Вот StatusBean.class, с которым эта страница работает через #{statusBean. ...}
Код:
package ru.bitel.mybgbilling.kernel.contract;
import java.io.Serializable;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import javax.enterprise.context.ConversationScoped;
import javax.inject.Named;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ru.bitel.bgbilling.common.BGException;
import ru.bitel.bgbilling.kernel.contract.api.common.bean.ContractStatus;
import ru.bitel.bgbilling.kernel.contract.api.common.service.ContractStatusService;
import ru.bitel.bgbilling.kernel.contract.api.common.service.ContractStatusService.ContractStatusChangeParameters;
import ru.bitel.bgbilling.kernel.contract.status.common.bean.Status;
import ru.bitel.bgbilling.kernel.directory.api.common.Directory;
import ru.bitel.common.function.Lazy;
import ru.bitel.mybgbilling.kernel.common.AbstractConversationBean;
import ru.bitel.mybgbilling.kernel.common.inject.BGInject;
import ru.bitel.mybgbilling.kernel.common.inject.BGInjection;
import bitel.billing.common.TimeUtils;
@Named
@ConversationScoped
@BGInjection
public class StatusBean
   extends AbstractConversationBean
   implements Serializable
{
   private static final Logger logger = LoggerFactory.getLogger( StatusBean.class );
   @BGInject
   private ContractStatusService contractStatusService;
   @BGInject(type = Status.class)
   private Directory<Status> statusDirectory;
   private Lazy<List<ContractStatus>> contractStatusList;
   private Lazy<ContractStatusChangeParameters> contractStatusChangeParameters;
   private Date changeDate;
   @Override
   protected void init()
      throws BGException
   {
      super.init();
      populate();
   }
   public String getStatusTitle( int id )
      throws BGException
   {
      Status status = statusDirectory.get( id );
      if( status != null )
      {
         return status.getTitle();
      }
      return null;
   }
   public Date getChangeDate()
   {
      return changeDate;
   }
   public void setChangeDate( Date changeDate )
      throws BGException
   {
      if( changeDate != null )
      {
         final ContractStatusChangeParameters params = getContractStatusChangeParameters();
         if( params.getDate() != null )
         {
            if( TimeUtils.dateBefore( changeDate, params.getDate() ) )
            {
               changeDate = params.getDate();
            }
         }
         else if( params.getDateList() != null && params.getDateList().size() > 0 )
         {
            changeDate = params.getDateList().get( 0 );
         }
         else if( changeDate.toInstant().atZone( ZoneId.systemDefault() ).toLocalDate().compareTo( LocalDate.now() ) > 0 )
         {
            changeDate = null;
         }
      }
      this.changeDate = changeDate;
   }
   public List<ContractStatus> getContractStatusList()
      throws BGException
   {
      return contractStatusList.get();
   }
   public ContractStatusChangeParameters getContractStatusChangeParameters()
      throws BGException
   {
      return contractStatusChangeParameters.get();
   }
   public void populate()
      throws BGException
   {
      customerBean.populate();
      contractStatusList = Lazy.of( ( ) -> reverse( contractStatusService.contractStatusList( getContractId(), null ) ) );
      contractStatusChangeParameters = Lazy.of( ( ) -> contractStatusService.contractStatusChangeParametersGet( getContractId() ) );
   }
   public void changeStatus( int status, Date date, String comment )
      throws BGException
   {
      logger.debug( "changeStatus" );
      ContractStatus contractStatus = new ContractStatus();
      contractStatus.setContractId( getContractId() );
      contractStatus.setStatus( status );
      contractStatus.setDateFrom( date );
      contractStatus.setDateTo( null );
      contractStatus.setComment( comment != null ? comment : "Изменено абонентом" );
      contractStatusService.contractStatusChange( contractStatus, true );
      changeDate = null;
      populate();
   }
   public void cancelStatus( String comment )
      throws BGException
   {
      logger.debug( "cancelStatus" );
      ContractStatus contractStatus = new ContractStatus();
      contractStatus.setContractId( getContractId() );
      contractStatus.setStatus( customerBean.getContract().getStatus() );
      contractStatus.setDateFrom( new Date() );
      contractStatus.setDateTo( null );
      contractStatus.setComment( comment != null ? comment : "Изменено абонентом (отмена смены статуса)" );
      contractStatusService.contractStatusChange( contractStatus, true );
      changeDate = null;
      populate();
   }
}
Как видите, почти ничего лишнего - поля формы (с getter'ами и setter'ами) и методы, которые можно вызывать.
Чем мне нравится JSF, что там нельзя вызвать метод (ни через post, ни через javascript), если вызов не прописан в .jsf/.xhtml.
http://stackoverflow.com/questions/3710 ... st-or-somehttp://stackoverflow.com/questions/9619 ... javascripthttp://stackoverflow.com/questions/1105 ... t-rendered