true-perfect-code
Version: 1.2.75
The P11SlotPlanner is a high-performance, logic-driven time management engine designed for Blazor WASM and Capacitor. Unlike traditional date-pickers, it uses a zero-latency boolean-map architecture to handle 24h schedules with micro-second precision. Key features include an interactive 'Hollow-Donut' SVG visualization, WCAG-compliant keyboard navigation, and a hybrid data model that allows for both standalone confirmation and real-time parent-state synchronization. Optimized for mobile-first UX, it ensures fluid slot toggling even on low-powered devices.

Single Day Planning

Classic standalone mode with integrated range selection and instant binding.

Processing hours: 8:00 - 18:00 Range Selection

Binding hours: 8 / 18
Binding _myBookings:
33
35

Classic standalone mode with integrated range selection and internal accept button.

Processing hours: 8:00 - 18:00 Range Selection

Weekend Planner

Select slots for Saturday and Sunday. Changes are synced instantly.

Saturday
Sunday

Implementation

<h2 class="fw-bold"><i class="bi bi-clock-history me-2"></i> Single Day Planning</h2>

<div class="border-bottom mb-4 pb-2">
    <p class="text-muted">Classic standalone mode with integrated range selection and instant binding.</p>
</div>

<P11SlotPlanner SlotSize="15"
                @bind-ViewRangeStartHour="@ViewRangeStartHour"
                @bind-ViewRangeEndHour="@ViewRangeEndHour"
                ColorActive="#b02a37"
                ColorInactive="#e9ecef"
                ColorAccent="#212529"
                ColorButton="#FFFFFF"
                IsVisibleRangeSelection="true"
                UseRangeSelector="false"
                IsVisibleAcceptButton="false"
                EnableInstantBinding="true"
                OccupiedSlotIndices="_myBookings"
                OccupiedSlotIndicesChanged="(List<int> items) => OccupiedSlotIndicesChanged(items)" />
<hr />
<div>
    Binding hours: <stroong>@ViewRangeStartHour</stroong> / <stroong>@ViewRangeEndHour</stroong>
    <br />
    Binding _myBookings:
    @foreach (var item in _myBookings)
    {
        <br />
        <stroong>@item</stroong>
    }
</div>

<div class="container mt-5">
    <div class="border-bottom mb-4 pb-2">
        <p class="text-muted">Classic standalone mode with integrated range selection and internal accept button.</p>
    </div>

    <P11SlotPlanner SlotSize="15"
                    ViewRangeStartHour="8"
                    ViewRangeEndHour="18"
                    ColorActive="#b02a37"
                    ColorInactive="#e9ecef"
                    ColorAccent="#212529"
                    ColorButton="#FFFFFF"
                    IsVisibleRangeSelection="true"
                    UseRangeSelector="true"
                    IsVisibleAcceptButton="true"
                    EnableInstantBinding="false"
                    @bind-OccupiedSlotIndices="_myBookings"
                    OnAccept="HandleSave" />
</div>

<hr />

<div class="container mt-5">
    <div class="card shadow-lg border-0">
        <div class="card-header bg-dark text-white p-4">
            <h3 class="mb-0"><i class="bi bi-calendar3-range me-2"></i> Weekend Planner</h3>
            <p class="mb-0 opacity-75">Select slots for Saturday and Sunday. Changes are synced instantly.</p>
        </div>

        <div class="card-body p-4 bg-light">
            <div class="row">
                @* SATURDAY COLUMN *@
                <div class="col-md-6 border-end">
                    <h5 class="fw-bold text-primary mb-3"><i class="bi bi-calendar-event me-2"></i> Saturday</h5>
                    <P11SlotPlanner SlotSize="30"
                                    ViewRangeStartHour="10"
                                    ViewRangeEndHour="22"
                                    ColorActive="#0d6efd"
                                    IsVisibleRangeSelection="false"
                                    IsVisibleAcceptButton="false"
                                    EnableInstantBinding="true"
                                    @bind-OccupiedSlotIndices="_saturdayBookings" />
                </div>

                @* SUNDAY COLUMN *@
                <div class="col-md-6">
                    <h5 class="fw-bold text-danger mb-3"><i class="bi bi-calendar-check me-2"></i> Sunday</h5>
                    <P11SlotPlanner SlotSize="30"
                                    ViewRangeStartHour="10"
                                    ViewRangeEndHour="18"
                                    ColorActive="#dc3545"
                                    IsVisibleRangeSelection="false"
                                    IsVisibleAcceptButton="false"
                                    EnableInstantBinding="true"
                                    @bind-OccupiedSlotIndices="_sundayBookings" />
                </div>
            </div>
        </div>

        @* MASTER ACTION FOOTER *@
        <div class="card-footer bg-white p-4 d-flex justify-content-between align-items-center">
            <div class="text-muted small">
                Total Slots: <strong>@(_saturdayBookings.Count + _sundayBookings.Count)</strong>
            </div>
            <button type="button" class="btn btn-success btn-lg px-5 fw-bold shadow" @onclick="HandleMasterSave">
                SAVE WEEKEND PLAN <i class="bi bi-cloud-upload-fill ms-2"></i>
            </button>
        </div>
    </div>
</div>                
@code {
    private int ViewRangeStartHour = 8;
    private int ViewRangeEndHour = 18;

    private void OccupiedSlotIndicesChanged(List<int> indices)
    {
        _myBookings = indices;
    }

    // Initial data: 08:00 (Slot 33 for 15m), 08:30 (Slot 35)
    private List<int> _myBookings = new() { 33, 35 };

    private void HandleSave()
    {
        Console.WriteLine($"--- SAVING SINGLE DAY ---");
        foreach (var slotId in _myBookings.OrderBy(x => x))
        {
            int totalMinutes = (slotId - 1) * 15;
            int h = totalMinutes / 60;
            int m = totalMinutes % 60;
            Console.WriteLine($"Slot: {slotId} ({h:D2}:{m:D2})");
        }
    }


    private List<int> _saturdayBookings = new() { 21, 22 }; // Beispiel-Daten
    private List<int> _sundayBookings = new();

    private void HandleMasterSave()
    {
        // Da EnableInstantBinding="true" ist, sind _saturdayBookings
        // und _sundayBookings bereits auf dem neuesten Stand!

        Console.WriteLine("--- SAVING WEEKEND PLAN ---");

        ProcessDay("Saturday", _saturdayBookings, 30);
        ProcessDay("Sunday", _sundayBookings, 30);
    }

    private void ProcessDay(string dayName, List<int> indices, int slotSize)
    {
        Console.WriteLine($"{dayName} selection:");
        foreach (var slotId in indices.OrderBy(x => x))
        {
            int totalMinutes = (slotId - 1) * slotSize;
            int h = totalMinutes / 60;
            int m = totalMinutes % 60;
            Console.WriteLine($" - Slot: {slotId} ({h:D2}:{m:D2})");
        }
    }
}             


Component API: P11SlotPlanner

Parameter / Event Type Default Description
Core Logic & Data
ComponentId string Guid (8 chars) Unique identifier for the component instance. Auto-generated if not set.
OccupiedSlotIndices List<int> new() The 1-based indices of selected slots. Primary data source for binding.
SlotSize int 5 Size of a single slot in minutes. Supports 5, 10, 15, 30, 60.
EnableInstantBinding bool false If true, updates the parent immediately on every click without needing 'Accept'.
OccupiedSlotIndicesChanged EventCallback<List<int>> - Fired when the selection changes (manually or via instant binding).
OnAccept EventCallback - Callback for custom save logic in the parent component. Fired on Accept button click.
View & UI Control
ViewRangeStartHour int 8 First visible hour in the UI grid (0-23).
ViewRangeStartHourChanged EventCallback<int> - Two-way binding callback. Fired when the start hour changes via the range selector.
ViewRangeEndHour int 18 Last visible hour in the UI grid (1-24).
ViewRangeEndHourChanged EventCallback<int> - Two-way binding callback. Fired when the end hour changes via the range selector.
IsVisibleRangeSelection bool true Toggles the visibility of the time-range adjustment header.
UseRangeSelector bool false Switch between slider-track and dropdown-menus for range selection.
IsVisibleAcceptButton bool true Shows/Hides the footer 'Accept' button.
AriaLabel string "Time Slot Planner" Accessible label for screen readers.
Styling & Branding
ColorActive string #0d6efd Hex/CSS color for selected slots and active donut segments.
ColorActiveCheck string #ffffff Text/icon color on active/selected slot checkboxes.
ColorInactive string #e9ecef Color for inactive/empty slots and UI backgrounds.
ColorInactiveCheck string #333 Text/icon color on inactive/empty slot checkboxes.
ColorButton string #9df79c Background color for button slots in the visual donut and highlights.
ColorButtonText string #333 Text/icon color for button slots.
ColorPanel string #9df79c Background color for the info/summary panel.
ColorAccent string #0d6efd Accent color for UI highlights like the range track bridge.
CssClass string? null Optional CSS classes for the root container.
ButtonCssClass string? null Optional CSS classes specifically for the hour selection buttons.
AcceptButtonCssClass string? null Optional CSS classes for the main 'Accept' action button.
Localization (Texts)
TextAcceptButton string? "ACCEPT SELECTION" Custom label for the confirmation button.
IconAcceptButton string? "bi bi-check-circle-fill" Bootstrap icon class for the confirmation button.
TextProcessingHours string? "Processing hours" Label for the active time range display.
TextRangeSelection string? "Range Selection" Label for the range selection section header.
TextStartHour string? "Start Hour" Label for the start hour input in the range selector.
TextEndHour string? "End Hour" Label for the end hour input in the range selector.
An unhandled error has occurred. Reload 🗙