Pouring a Beverage
In the final step of this tutorial, we add functionality to pour a selected beverage. We’ll create a pour button that appears at the bottom of the screen, allowing the user to initiate the pouring process.
Generate the Pour Button Component
Use the kosui CLI to generate a new pour button component within the model-components project:
kosui component pour-button
? Which project should the component be created in? model-components
⠧
> NX Generating @kosdev-code/kos-nx-plugin:kos-component
CREATE libs/model-components/src/components/pour-button/pour-button.tsx
CREATE libs/model-components/src/components/pour-button/index.tsDefine Component Properties
The PourButton component must handle several properties to reflect the pouring state:
- isPouring: Indicates if the nozzle is currently pouring a beverage.
- canPour: Indicates whether a valid beverage has been selected and if the device is ready to pour.
- pourable: The selected beverage, used to display its name.
- pour handler: A function that calls the backend pour endpoints.
The NozzlePourResponse type from the CUI Kit provides the current pour status (including isPouring and canPour) along with handlers that are optimized for touch devices. The currently selected pourable is also provided as part of the CUI Kit state.
Define the component props as follows:
// libs/model-components/src/components/pour-button/pour-button.tsx
import type { NozzlePourResponse } from '@kosdev-code/kos-dispense-sdk';
interface Props {
pour: NozzlePourResponse;
pourable?: BeverageModel | null;
}Implement the Pour Button Logic
The logic of the PourButton is straightforward. It renders its text and styling based on the current pour status:
- If a beverage is selected and ready to pour, the button displays: Pour {Beverage Name}.
- If pouring is in progress, it displays: Pouring….
- Otherwise, it displays: No Beverage Selected.
The implementation is as follows:\
// libs/model-components/src/components/pour-button/pour-button.tsx
export const PourButton: React.FunctionComponent<Props> = kosComponent(
({ pour, pourable }) => {
const { status, handlers } = pour;
const displayStatus =
status?.canPour && !!pourable
? `Pour ${pourable.data.name ?? pourable.note}`
: status?.isPouring
? 'Pouring...'
: 'No Beverage Selected';
return (
<PourButtonContainer
canPour={!!status?.canPour}
isPouring={!!status?.isPouring}
{...handlers()}
>
{displayStatus}
</PourButtonContainer>
);
}
);
export default PourButton;Note: Wrapping the component in kosComponent ensures that it automatically subscribes to changes in the underlying kOS models, so any updates to the pour status trigger a re-render.
Style the Pour Button
The PourButtonContainer styled component adjusts its appearance based on the pouring state. It modifies text color, background, and border to provide visual feedback:
// libs/model-components/src/components/pour-button/pour-button.tsx
const PourButtonContainer = styled(BaseContainer)<{
canPour: boolean;
isPouring: boolean;
}>`
height: 120px;
width: 300px;
font-size: 24px;
border-radius: 30px;
font-family: sans-serif;
color: ${({ isPouring }) => (isPouring ? 'black' : 'white')};
display: flex;
justify-content: center;
user-select: none;
border: 10px solid ${({ color }) => color ?? 'black'};
background: ${({ canPour, isPouring }) => {
if (isPouring) {
return `linear-gradient(135deg, blue, white)`;
}
return canPour ? 'black' : 'grey';
}};
`;Integrate the Pour Button into the Main Application
Finally, update your main application view to include the PourButton component. Pass the pour state and selected beverage (pourable) from the CUI Kit to the component:
// apps/training-zero4-ui/src/app/components/main-view/main-view.tsx
<PourButton
pour={pour}
pourable={selections[DISPENSE_GROUP_BEVERAGE]}
/>Summary
Congratulations! You have now implemented a complete “select and pour” workflow. The PourButton component reflects live pour status from the CUI Kit and Dispense SDK models, enabling dynamic updates when the backend state changes. This reactive behavior simplifies UI development by ensuring that any modifications in model state are automatically and efficiently rendered in the UI.
