shadcn.io is not affiliated with official shadcn/ui
Prospect Manager Kanban Block for React Aria
A four-lane prospecting board of ten leads whose last contact is a word worked out from a date, a Follow up after select that flags idle leads, a Log contact button on each card and a rule that keeps idle leads out of Hot.
Installation
Overview
Keep warm leads from going cold with this prospect board for React with React Aria. A hairline frame with a plus mark on each corner holds four lanes that share their lines: Cold, Warm, Hot and Customer. Each card shows a business, its contact and when they last spoke as a word worked out from a date against one fixed "today" (Monday 12 October in this demo, with no clock): "Last contact 11 days ago", "Contacted today". A Native Select in the header, "Follow up after" 7, 14 or 30 days, sets how long a lead may sit: any lead past it in the three lanes before Customer gets a "Follow up" mark, a bell glyph and the words in an outline Badge, the lane heads count them and the sentence under the heading gives the total. "Log contact" on a card sets its last contact to today and clears the mark. One rule guards the Hot lane: a lead past the mark can't move there until a contact is logged, and the refusal says how long it has been. A card moves three ways: drag it by its grip, focus the grip and press Space to pick it up, the arrow keys to move it, Space to drop it and Escape to cancel, or open its Dropdown Menu and choose a lane under "Move to", which is the route for a single pointer and for touch. A polite status line in the foot reports each change and Reset puts the list back. The frame is ruled by Guides. On a phone the lanes scroll sideways in one named region with the next lane showing. For sales prospecting, outreach lists and any relationship that goes stale without a touch.
FAQ
How do I fill it with my own leads?
Edit COLUMNS, LIMITS and START. A lead has an id, a title (the business), a contact, a last contact date as YYYY-MM-DD and the column it starts in; a limit is an id that is a number of days and a label. Every count, word and sentence is worked out from them. Change AS_OF to move the demo's "today".
When is a lead marked Follow up?
When it is not in the Customer lane and its last contact is more than the chosen number of days ago: staleOf compares the days since last with the select's value, so a lead exactly at the limit is not yet flagged. The mark is a bell glyph and the words, not a colour, and the space for it is always reserved on the card, so logging a contact never changes a card's height. Customers are not chased.
Why was I refused when I moved a lead to Hot?
Because it is past the follow-up mark. refusalOf stops a lead that hasn't been spoken to inside the limit from being called hot, and the status line says when it was last contacted. Press Log contact on the card, which makes the last contact today, and the move goes through. The drop, the keyboard steps and the menu all ask the same function; moving a lead to any other lane is always allowed.
How do I move a card without a mouse?
Tab to the grip at the start of a card, named "Move" with the card's title. Press Space or Enter to pick it up, then ArrowUp and ArrowDown to change its place, ArrowLeft and ArrowRight to go to the neighbouring lane (the arrows swap in right-to-left languages), Home and End for the first and last place, Space or Enter to drop it and Escape to put it back. Every step is announced in the status line. Log contact is a separate stop on each card.
Why is there a menu as well as the grip?
Dragging needs a held pointer and a path, which not everyone has, so each card also has a "More actions" menu with a "Move to" choice for every lane and the current one ticked. Choosing one moves the card by the same route as a drop, so the same rule applies, puts focus on the card in its new place and says so in the status line. React Aria's menu is a group of selectable items, so the lane is chosen by selection rather than by a radio item.
What stays in the demo, and how do I save the moves?
Everything: the cards live in the component's state, nothing is sent anywhere, and a reload or Reset brings back the starting board. To save for real, call your API from put for moves and act for Log contact, the functions every change goes through, and send the new last date. The names, jobs and dates are made up.