"""Slot panels, forms, edit/add, slot picker JS.""" from __future__ import annotations from shared.sx.helpers import sx_call # =========================================================================== # SLOT PICKER JS — shared by entry edit + entry add forms # =========================================================================== _SLOT_PICKER_JS = """\ """ # --------------------------------------------------------------------------- # Slot options (shared by entry edit + add forms) # --------------------------------------------------------------------------- def _slot_options_data(day_slots, selected_slot_id=None) -> list: """Extract slot option data for sx composition.""" result = [] for slot in day_slots: start = slot.time_start.strftime("%H:%M") if slot.time_start else "" end = slot.time_end.strftime("%H:%M") if slot.time_end else "" flexible = getattr(slot, "flexible", False) cost = getattr(slot, "cost", None) cost_str = str(cost) if cost is not None else "0" label_parts = [slot.name, f"({start}"] if end: label_parts.append(f"\u2013{end})") else: label_parts.append("\u2013open-ended)") if flexible: label_parts.append("[flexible]") result.append({ "value": str(slot.id), "data-start": start, "data-end": end, "data-flexible": "1" if flexible else "0", "data-cost": cost_str, "selected": "selected" if selected_slot_id == slot.id else None, "label": " ".join(label_parts), }) return result # --------------------------------------------------------------------------- # Slot header row # --------------------------------------------------------------------------- def _slot_header_html(ctx: dict, *, oob: bool = False) -> str: """Build the slot detail header row.""" from quart import url_for calendar = ctx.get("calendar") if not calendar: return "" cal_slug = getattr(calendar, "slug", "") slot = ctx.get("slot") if not slot: return "" desc = getattr(slot, "description", "") or "" label_sx = sx_call("events-slot-label", name=slot.name, description=desc) return sx_call("menu-row-sx", id="slot-row", level=5, link_label_content=label_sx, child_id="slot-header-child", oob=oob) # --------------------------------------------------------------------------- # Slot main panel # --------------------------------------------------------------------------- def render_slot_main_panel(slot, calendar, *, oob: bool = False) -> str: """Render slot detail view via data extraction + sx defcomp.""" from quart import url_for, g styles = getattr(g, "styles", None) or {} list_container = getattr(styles, "list_container", "") if hasattr(styles, "list_container") else styles.get("list_container", "") pre_action = getattr(styles, "pre_action_button", "") if hasattr(styles, "pre_action_button") else styles.get("pre_action_button", "") cal_slug = getattr(calendar, "slug", "") days_display = getattr(slot, "days_display", "\u2014") days = days_display.split(", ") if days and days[0] == "\u2014": days = [] time_start = slot.time_start.strftime("%H:%M") if slot.time_start else "" time_end = slot.time_end.strftime("%H:%M") if slot.time_end else "" cost = getattr(slot, "cost", None) return sx_call("events-slot-panel-from-data", slot_id=str(slot.id), list_container=list_container, days=days or None, flexible="yes" if getattr(slot, "flexible", False) else "no", time_str=f"{time_start} \u2014 {time_end}", cost_str=f"{cost:.2f}" if cost is not None else "", pre_action=pre_action, edit_url=url_for("calendar.slots.slot.get_edit", slot_id=slot.id, calendar_slug=cal_slug), description=getattr(slot, "description", "") or "", oob=oob or None) # --------------------------------------------------------------------------- # Slots table # --------------------------------------------------------------------------- def render_slots_table(slots, calendar) -> str: """Render slots table via data extraction + sx defcomp.""" from quart import url_for, g from shared.browser.app.csrf import generate_csrf_token csrf = generate_csrf_token() styles = getattr(g, "styles", None) or {} list_container = getattr(styles, "list_container", "") if hasattr(styles, "list_container") else styles.get("list_container", "") tr_cls = getattr(styles, "tr", "") if hasattr(styles, "tr") else styles.get("tr", "") pill_cls = getattr(styles, "pill", "") if hasattr(styles, "pill") else styles.get("pill", "") action_btn = getattr(styles, "action_button", "") if hasattr(styles, "action_button") else styles.get("action_button", "") pre_action = getattr(styles, "pre_action_button", "") if hasattr(styles, "pre_action_button") else styles.get("pre_action_button", "") hx_select = getattr(g, "hx_select_search", "#main-panel") cal_slug = getattr(calendar, "slug", "") slots_data = [] if slots: for s in slots: days_display = getattr(s, "days_display", "\u2014") day_list = days_display.split(", ") if day_list and day_list[0] == "\u2014": day_list = [] time_start = s.time_start.strftime("%H:%M") if s.time_start else "" time_end = s.time_end.strftime("%H:%M") if s.time_end else "" cost = getattr(s, "cost", None) slots_data.append({ "slot-href": url_for("defpage_slot_detail", calendar_slug=cal_slug, slot_id=s.id), "slot-name": s.name, "description": getattr(s, "description", "") or "", "flexible": "yes" if s.flexible else "no", "days": day_list or None, "time-str": f"{time_start} - {time_end}", "cost-str": f"{cost:.2f}" if cost is not None else "", "del-url": url_for("calendar.slots.slot.slot_delete", calendar_slug=cal_slug, slot_id=s.id), }) return sx_call("events-slots-table-from-data", list_container=list_container, slots=slots_data or None, pre_action=pre_action, add_url=url_for("calendar.slots.add_form", calendar_slug=cal_slug), tr_cls=tr_cls, pill_cls=pill_cls, action_btn=action_btn, hx_select=hx_select, csrf_hdr=f'{{"X-CSRFToken": "{csrf}"}}') # --------------------------------------------------------------------------- # Slot edit form # --------------------------------------------------------------------------- def render_slot_edit_form(slot, calendar) -> str: """Render slot edit form via data extraction + sx defcomp.""" from quart import url_for, g from shared.browser.app.csrf import generate_csrf_token csrf = generate_csrf_token() styles = getattr(g, "styles", None) or {} list_container = styles.get("list_container", "") if isinstance(styles, dict) else getattr(styles, "list_container", "") action_btn = styles.get("action_button", "") if isinstance(styles, dict) else getattr(styles, "action_button", "") cancel_btn = styles.get("cancel_button", "") if isinstance(styles, dict) else getattr(styles, "cancel_button", "") cal_slug = getattr(calendar, "slug", "") sid = slot.id cost = getattr(slot, "cost", None) day_keys = [("mon", "Mon"), ("tue", "Tue"), ("wed", "Wed"), ("thu", "Thu"), ("fri", "Fri"), ("sat", "Sat"), ("sun", "Sun")] days_data = [{"name": k, "label": lbl, "checked": getattr(slot, k, False) or None} for k, lbl in day_keys] all_checked = all(getattr(slot, k, False) for k, _ in day_keys) return sx_call("events-slot-edit-form-from-data", slot_id=str(sid), list_container=list_container, put_url=url_for("calendar.slots.slot.put", calendar_slug=cal_slug, slot_id=sid), cancel_url=url_for("calendar.slots.slot.get_view", calendar_slug=cal_slug, slot_id=sid), csrf=csrf, name_val=slot.name or "", cost_val=f"{cost:.2f}" if cost is not None else "", start_val=slot.time_start.strftime("%H:%M") if slot.time_start else "", end_val=slot.time_end.strftime("%H:%M") if slot.time_end else "", desc_val=getattr(slot, "description", "") or "", days_data=days_data, all_checked=all_checked or None, flexible_checked="checked" if getattr(slot, "flexible", False) else None, action_btn=action_btn, cancel_btn=cancel_btn) # --------------------------------------------------------------------------- # Slot add form / button # --------------------------------------------------------------------------- def render_slot_add_form(calendar) -> str: """Render slot add form via data extraction + sx defcomp.""" from quart import url_for, g from shared.browser.app.csrf import generate_csrf_token csrf = generate_csrf_token() styles = getattr(g, "styles", None) or {} action_btn = styles.get("action_button", "") if isinstance(styles, dict) else getattr(styles, "action_button", "") cancel_btn = styles.get("cancel_button", "") if isinstance(styles, dict) else getattr(styles, "cancel_button", "") cal_slug = getattr(calendar, "slug", "") day_keys = [("mon", "Mon"), ("tue", "Tue"), ("wed", "Wed"), ("thu", "Thu"), ("fri", "Fri"), ("sat", "Sat"), ("sun", "Sun")] days_data = [{"name": k, "label": lbl} for k, lbl in day_keys] return sx_call("events-slot-add-form-from-data", post_url=url_for("calendar.slots.post", calendar_slug=cal_slug), csrf=f'{{"X-CSRFToken": "{csrf}"}}', days_data=days_data, action_btn=action_btn, cancel_btn=cancel_btn, cancel_url=url_for("calendar.slots.add_button", calendar_slug=cal_slug)) def render_slot_add_button(calendar) -> str: """Render slot add button (replaces _types/slots/_add_button.html).""" from quart import url_for, g styles = getattr(g, "styles", None) or {} pre_action = styles.get("pre_action_button", "") if isinstance(styles, dict) else getattr(styles, "pre_action_button", "") cal_slug = getattr(calendar, "slug", "") add_url = url_for("calendar.slots.add_form", calendar_slug=cal_slug) return sx_call("events-slot-add-button", pre_action=pre_action, add_url=add_url)