<h:outputScript>
var KanbanDemo = KanbanDemo || {};
KanbanDemo.widget = function() {
return PF('kanbanWidget');
};
KanbanDemo.boardId = function() {
return $('#boardSelect_input').val();
};
KanbanDemo.itemId = function() {
return $('#itemIdInput').val();
};
KanbanDemo.notify = function(summary, detail, severity) {
severity = severity || 'info';
PF('growl').show([{
summary: summary,
detail: detail,
severity: severity
}]);
};
KanbanDemo.notifyError = function(summary, detail) {
KanbanDemo.notify(summary, detail, 'error');
};
KanbanDemo.notifyWarn = function(summary, detail) {
KanbanDemo.notify(summary, detail, 'warn');
};
KanbanDemo.flashBoard = function(boardEl) {
boardEl.classList.add('board-highlight');
setTimeout(function() {
boardEl.classList.remove('board-highlight');
}, 2000);
};
KanbanDemo.flashItem = function(itemEl) {
itemEl.classList.add('item-highlight');
setTimeout(function() {
itemEl.classList.remove('item-highlight');
}, 2000);
};
KanbanDemo.showPill = function(pillId, text) {
var $pill = $('#' + pillId);
$pill.text(text).addClass('visible');
setTimeout(function() {
$pill.removeClass('visible');
}, 3000);
};
KanbanDemo.hidePill = function(pillId) {
$('#' + pillId).removeClass('visible').text('');
};
KanbanDemo.assertItemId = function() {
var id = KanbanDemo.itemId();
if (!id) {
KanbanDemo.notifyError('No ID', 'Click a task or type an ID');
return null;
}
return id;
};
KanbanDemo.onAddItem = function() {
var widget = KanbanDemo.widget();
var board = KanbanDemo.boardId();
if (!widget.findBoard(board)) {
KanbanDemo.notifyError('Board not found', 'Board "' + board + '" no longer exists');
return;
}
widget.addElement(board, {
id: 't-' + Date.now(),
title: 'New Task',
description: 'Added via client API'
});
KanbanDemo.notify('Item Added', 'New task added to ' + board);
};
KanbanDemo.onRemoveBoard = function() {
var widget = KanbanDemo.widget();
var board = KanbanDemo.boardId();
if (!widget.findBoard(board)) {
KanbanDemo.notifyWarn('Already removed', 'Board "' + board + '" does not exist');
return;
}
widget.removeBoard(board);
KanbanDemo.notifyWarn('Board Removed', 'Board removed: ' + board);
};
KanbanDemo.onCountItems = function() {
var widget = KanbanDemo.widget();
var board = KanbanDemo.boardId();
var boardEl = widget.findBoard(board);
var pillId = 'countResult';
KanbanDemo.hidePill(pillId);
if (!boardEl) {
KanbanDemo.notifyError('Board not found', 'Board "' + board + '" no longer exists');
return;
}
var count = boardEl.querySelectorAll('.kanban-item').length;
KanbanDemo.flashBoard(boardEl);
KanbanDemo.showPill(pillId, count + ' items');
KanbanDemo.notify(count + ' items', 'Board "' + board + '" has ' + count + ' items');
};
KanbanDemo.onFindItem = function() {
var widget = KanbanDemo.widget();
var id = KanbanDemo.assertItemId();
if (!id) return;
var itemEl = widget.findElement(id);
if (itemEl) {
KanbanDemo.flashItem(itemEl);
KanbanDemo.notify('Found', 'Item "' + id + '" highlighted');
} else {
KanbanDemo.notifyError('Not found', 'No item with ID "' + id + '"');
}
};
KanbanDemo.onParentBoard = function() {
var widget = KanbanDemo.widget();
var id = KanbanDemo.assertItemId();
var pillId = 'parentResult';
KanbanDemo.hidePill(pillId);
if (!id) return;
var board = widget.getParentBoardID(id);
if (board) {
var boardEl = widget.findBoard(board);
if (boardEl) {
KanbanDemo.flashBoard(boardEl);
KanbanDemo.showPill(pillId, 'In: ' + board);
}
}
if (board) {
KanbanDemo.notify('Parent Board', 'Item "' + id + '" is in board "' + board + '"');
} else {
KanbanDemo.notifyError('Not found', 'Item "' + id + '" not found');
}
};
KanbanDemo.onReplaceItem = function() {
var widget = KanbanDemo.widget();
var id = KanbanDemo.assertItemId();
if (!id) return;
var itemEl = widget.findElement(id);
if (!itemEl) {
KanbanDemo.notifyError('Not found', 'No item with ID "' + id + '"');
return;
}
widget.replaceElement(id, {
title: 'Updated at ' + new Date().toLocaleTimeString()
});
KanbanDemo.notify('Replaced', 'Content replaced for "' + id + '"');
};
KanbanDemo.onHighlightBoard = function() {
var widget = KanbanDemo.widget();
var board = KanbanDemo.boardId();
var boardEl = widget.findBoard(board);
if (!boardEl) {
KanbanDemo.notifyError('Board not found', 'Board "' + board + '" no longer exists');
return;
}
KanbanDemo.flashBoard(boardEl);
KanbanDemo.notify('Board Found', 'Board "' + board + '" highlighted');
};
KanbanDemo.onItemClick = function(args) {
if (args.itemId) {
$('#itemIdInput').val(args.itemId);
}
};
</h:outputScript>
<p:remoteCommand name="rcAddItem" actionListener="#{kanbanClientApiController.onClientApiCommand}" update="@none" process="@this" oncomplete="KanbanDemo.onAddItem()" />
<p:remoteCommand name="rcRemoveBoard" actionListener="#{kanbanClientApiController.onClientApiCommand}" update="@none" process="@this" oncomplete="KanbanDemo.onRemoveBoard()" />
<p:remoteCommand name="rcCountItems" actionListener="#{kanbanClientApiController.onClientApiCommand}" update="@none" process="@this" oncomplete="KanbanDemo.onCountItems()" />
<p:remoteCommand name="rcFindItem" actionListener="#{kanbanClientApiController.onClientApiCommand}" update="@none" process="@this" oncomplete="KanbanDemo.onFindItem()" />
<p:remoteCommand name="rcParentBoard" actionListener="#{kanbanClientApiController.onClientApiCommand}" update="@none" process="@this" oncomplete="KanbanDemo.onParentBoard()" />
<p:remoteCommand name="rcReplaceItem" actionListener="#{kanbanClientApiController.onClientApiCommand}" update="@none" process="@this" oncomplete="KanbanDemo.onReplaceItem()" />
<p:remoteCommand name="rcHighlightBoard" actionListener="#{kanbanClientApiController.onClientApiCommand}" update="@none" process="@this" oncomplete="KanbanDemo.onHighlightBoard()" />
<div class="api-toolbar">
<div class="row">
<span style="font-weight:bold;min-width:50px">Board</span>
<p:selectOneMenu id="boardSelect" style="width:150px">
<f:selectItem itemLabel="Backlog" itemValue="backlog" />
<f:selectItem itemLabel="In Progress" itemValue="inprogress" />
<f:selectItem itemLabel="Done" itemValue="done" />
</p:selectOneMenu>
<p:commandButton value="Add Item" icon="pi pi-plus" type="button"
styleClass="ui-button-sm"
onclick="rcAddItem()" />
<p:commandButton value="Count Items" icon="pi pi-list" type="button"
styleClass="ui-button-sm"
onclick="rcCountItems()" />
<span class="result-pill" id="countResult"></span>
<p:commandButton value="Remove Board" icon="pi pi-trash" type="button"
styleClass="ui-button-sm ui-button-danger"
onclick="rcRemoveBoard()" />
<p:commandButton value="Highlight Board" icon="pi pi-eye" type="button"
styleClass="ui-button-sm"
onclick="rcHighlightBoard()" />
</div>
<div class="row">
<span style="font-weight:bold;min-width:50px">Item</span>
<p:inputText id="itemIdInput" placeholder="e.g. t-1" style="width:130px" />
<h:panelGroup id="selectedItemBadge" layout="block" style="display:inline">
<span class="selected-badge">
Selected: #{kanbanClientApiController.lastClickedItemId != null ? kanbanClientApiController.lastClickedItemId : 'click a task'}
</span>
</h:panelGroup>
<span class="sep"></span>
<p:commandButton value="Find" icon="pi pi-search" type="button"
styleClass="ui-button-sm"
onclick="rcFindItem()" />
<p:commandButton value="Parent Board" icon="pi pi-arrow-right" type="button"
styleClass="ui-button-sm"
onclick="rcParentBoard()" />
<span class="result-pill" id="parentResult"></span>
<p:commandButton value="Replace Title" icon="pi pi-pencil" type="button"
styleClass="ui-button-sm"
onclick="rcReplaceItem()" />
</div>
</div>
<pe:kanban id="kanban"
widgetVar="kanbanWidget"
value="#{kanbanClientApiController.columns}"
draggable="true"
addItemButton="true"
style="height:400px">
<p:ajax event="itemClick" listener="#{kanbanClientApiController.onItemClick}" update="growl selectedItemBadge"
oncomplete="KanbanDemo.onItemClick(args)" />
<p:ajax event="itemAdd" update="kanban growl" />
</pe:kanban>
@Named
@ViewScoped
public class KanbanClientApiController implements Serializable {
private static final long serialVersionUID = 1L;
private List<KanbanColumn> columns;
private String lastClickedItemId;
private String lastClickedItem;
@PostConstruct
public void init() {
columns = new ArrayList<>();
KanbanColumn backlog = new KanbanColumn("backlog", "Backlog");
backlog.setCssClass("kanban-backlog");
backlog.getItems().addAll(List.of(
new KanbanItem("t-1", "Research authentication", "Evaluate OAuth"),
new KanbanItem("t-2", "Design database schema", "Create ERD")));
columns.add(backlog);
KanbanColumn inprogress = new KanbanColumn("inprogress", "In Progress");
inprogress.setCssClass("kanban-inprogress");
inprogress.getItems().addAll(List.of(
new KanbanItem("t-3", "Implement REST API", "Build CRUD endpoints"),
new KanbanItem("t-4", "Add validation", "Server-side validation")));
columns.add(inprogress);
KanbanColumn done = new KanbanColumn("done", "Done");
done.setCssClass("kanban-done");
done.getItems().addAll(List.of(
new KanbanItem("t-5", "Setup CI/CD", "GitHub Actions"),
new KanbanItem("t-6", "Write tests", "80% coverage")));
columns.add(done);
}
public List<KanbanColumn> getColumns() {
return columns;
}
public String getLastClickedItemId() {
return lastClickedItemId;
}
public String getLastClickedItem() {
return lastClickedItem;
}
public void onItemClick(final KanbanItemClickEvent event) {
this.lastClickedItemId = event.getItemId();
String title = resolveItemTitle(lastClickedItemId, event.getColumnId());
this.lastClickedItem = title != null ? title : lastClickedItemId;
FacesContext.getCurrentInstance().addMessage(null,
new FacesMessage(FacesMessage.SEVERITY_INFO,
"Item Clicked", "Task: " + lastClickedItem));
org.primefaces.PrimeFaces.current().ajax().addCallbackParam("itemId", lastClickedItemId);
}
public void onClientApiCommand() {
// no-op: used by p:remoteCommand to complete the AJAX cycle
}
private String resolveItemTitle(final String itemId, final String columnId) {
for (KanbanColumn col : columns) {
if (col.getId().equals(columnId)) {
for (KanbanItem item : col.getItems()) {
if (item.getId().equals(itemId)) {
return item.getTitle();
}
}
}
}
return null;
}
}