Task lists used to be a week-view-only sub-feature bolted onto calendar widgets (CalendarWidgetConfig.tasks_*), so a task list could only exist tied to a calendar's view and only inside its footprint. Tasks are now a standalone widget type (TaskWidgetConfig, app/widgets/tasks.py) that can be placed and sized independently, same as photos/calendar/ whiteboard -- no separate "enabled" flag either, since being on the grid at all is the on/off switch, matching every other widget type. Migration 17 creates task_widget_configs, extracts any existing calendar widget's configured task source into a new sibling tasks widget (auto-placed in open grid space, source dropped+logged if truly none is left), then drops calendar_widget_configs' now-dead tasks_* columns in the same migration -- this project's usual same-migration- drop convention. Also handles the rarer case of a database jumping straight from before the widget system existed to after this split in one boot, via the legacy Frame.calendar_tasks_* columns. Verified live in the browser at desktop and mobile widths: adding a Tasks widget, its own dialog (task-list source picker + preview), and confirming the calendar widget's dialog no longer mentions tasks at all. Full test suite (180 tests, including new coverage for the widget render/actions, the migration's data-extraction path, and the permission-boundary shape for tasks-source) passes.
64 lines
2.6 KiB
HTML
64 lines
2.6 KiB
HTML
{% extends "app_base.html" %}
|
|
|
|
{% block title %}{{ frame.name or "Frame" }} · Layout{% endblock %}
|
|
{% block page_title %}{% include "_frame_name_edit.html" %}{% endblock %}
|
|
|
|
{% block device_status %}{% include "_device_status_bar.html" %}{% endblock %}
|
|
{% block tabs %}{% include "_frame_tabs.html" %}{% endblock %}
|
|
|
|
{% block content %}
|
|
<div id="control-banner" class="control-banner" style="display: none;">
|
|
<span id="control-holder"></span>
|
|
<button type="button" id="take-control" class="btn-inline">Take control</button>
|
|
</div>
|
|
|
|
<div class="layout">
|
|
<div class="main-col">
|
|
<section class="card">
|
|
<div class="card-title-row">
|
|
<h2 class="card-title">Widgets</h2>
|
|
<button type="button" id="clear-all-widgets" class="secondary btn-inline">Clear all</button>
|
|
</div>
|
|
<p class="sub">Drag a widget to move it, drag its bottom-right corner
|
|
to resize it -- like arranging widgets on a phone's home screen.
|
|
Widgets can't overlap. Click a widget's gear icon for its own
|
|
settings (which album, which calendars, etc.).</p>
|
|
<div id="widget-canvas-wrap">
|
|
<div id="widget-canvas"></div>
|
|
</div>
|
|
<p class="sub" id="widget-canvas-empty-hint" style="display: none; margin-top: 10px;">
|
|
Nothing placed yet -- add a widget below.</p>
|
|
</section>
|
|
|
|
<section class="card" style="margin-top: 20px;">
|
|
<h2 class="card-title">Add a widget</h2>
|
|
<div id="add-widget-buttons" class="checkbox-row" style="gap: 10px; flex-wrap: wrap;"></div>
|
|
<p class="sub" id="add-widget-hint" style="margin-top: 8px;"></p>
|
|
</section>
|
|
</div>
|
|
</div>
|
|
|
|
<dialog id="widget-dialog">
|
|
<button type="button" id="widget-dialog-close" class="icon-btn" aria-label="Close" title="Close">×</button>
|
|
<div id="widget-dialog-body"><p class="sub">Loading...</p></div>
|
|
<div class="dialog-result"></div>
|
|
</dialog>
|
|
|
|
<div id="result"></div>
|
|
{% endblock %}
|
|
|
|
{% block scripts %}
|
|
<script>
|
|
window.FRAME_BASE_API = window.FRAME_API = {{ ("/api/frames/" ~ frame.id) | tojson }};
|
|
window.FRAME_ID = {{ frame.id | tojson }};
|
|
</script>
|
|
<script src="/static/device_status_bar.js"></script>
|
|
<script src="/static/frame_header.js"></script>
|
|
<script src="/static/widget_dialog_photos.js"></script>
|
|
<script src="/static/queue.js"></script>
|
|
<script src="/static/widget_dialog_calendar.js"></script>
|
|
<script src="/static/widget_dialog_whiteboard.js"></script>
|
|
<script src="/static/widget_dialog_tasks.js"></script>
|
|
<script src="/static/frame_layout.js"></script>
|
|
{% endblock %}
|