Pre-spec'd in [[sprint-my-sea-sign-gate-may19]] as the unblocker for tomorrow's visual verification of 4b's no-sig branch — admin user (@disco) had a saved sig from Sprint 4a testing & there was no in-UI affordance to undo it short of DB surgery. Lands ahead of the deferred 4b visual verify so dev users can toggle between sig/no-sig states on Claudezilla.
- Endpoint: `path("my-sign/clear", views.clear_sign, name="clear_sign")` — POST sets `User.significator = None` + `significator_reversed = False`, redirects to picker; GET is a no-mutation redirect to picker (mirrors save_sign's GET handling). `login_required(login_url="/")`. No trailing slash per [[feedback_url_convention_actions_no_trailing_slash]] (action endpoint, not page).
- Template (my_sign.html): `<form id="id_clear_sign_form" class="my-sign-clear-form">` w. `<button id="id_clear_sign_btn" class="btn btn-danger">DEL</button>`, rendered ONLY when `current_significator` is set; sits inside `.my-sign-landing` as a sibling of `.room-shell` so it's bound to the landing-phase UI alone (picker phase already has its own NVM unlock affordance on focused thumbnails).
- SCSS: anchored bottom-right of `.my-sign-landing` via `position: absolute; bottom: .75rem; right: 1rem` — `.my-sign-landing` gains `position: relative` to scope the absolute. `.btn-danger` carries the destructive treatment; "DEL" mirrors post.html gear menu's DEL convention from [[sprint-post-polish-may13]].
- 3 FTs in new `MySignClearTest` class — covers: btn renders on landing when sig saved (T1, asserts text "DEL" + `.btn-danger` class); btn absent when no sig (T2); click POSTs, reloads, & wipes `User.significator` + `significator_reversed` in DB (T3).
- 6 ITs in new `ClearSignViewTest` + `MySignClearAffordanceTemplateTest` — covers: login_required gate, POST wipes both fields w. redirect-back, GET redirects w.o mutation, POST-w/o-existing-sig is idempotent no-op, template renders btn only when sig set, template's form action targets `clear_sign` reverse.
- 1029 IT/UT green in 47s (+6 from baseline); 20/20 FT green across test_bill_my_sign + test_game_my_sea in 165s.
Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Sonnet 4.6 <noreply@anthropic.com>
30 lines
1.5 KiB
Python
30 lines
1.5 KiB
Python
from django.urls import path
|
|
|
|
from apps.billboard import views
|
|
|
|
app_name = "billboard"
|
|
|
|
urlpatterns = [
|
|
path("", views.billboard, name="billboard"),
|
|
path("toggle-applets", views.toggle_billboard_applets, name="toggle_applets"),
|
|
path("my-notes/", views.my_notes, name="my_notes"),
|
|
path("note/<slug:slug>/set-palette", views.note_set_palette, name="note_set_palette"),
|
|
path("note/<slug:slug>/don", views.don_title, name="don_title"),
|
|
path("note/<slug:slug>/doff", views.doff_title, name="doff_title"),
|
|
path("room/<uuid:room_id>/scroll/", views.scroll, name="scroll"),
|
|
path("room/<uuid:room_id>/scroll-position/", views.save_scroll_position, name="save_scroll_position"),
|
|
# Post/Line CRUD (relocated from apps.dashboard.urls)
|
|
path("new-post", views.new_post, name="new_post"),
|
|
path("post/<uuid:post_id>/", views.view_post, name="view_post"),
|
|
path("post/<uuid:post_id>/share-post", views.share_post, name="share_post"),
|
|
path("post/<uuid:post_id>/delete", views.delete_post, name="delete_post"),
|
|
path("post/<uuid:post_id>/abandon", views.abandon_post, name="abandon_post"),
|
|
path("users/<uuid:user_id>/", views.my_posts, name="my_posts"),
|
|
path("my-buds/", views.my_buds, name="my_buds"),
|
|
path("buds/add", views.add_bud, name="add_bud"),
|
|
path("buds/search", views.search_buds, name="search_buds"),
|
|
path("my-sign/", views.my_sign, name="my_sign"),
|
|
path("my-sign/save", views.save_sign, name="save_sign"),
|
|
path("my-sign/clear", views.clear_sign, name="clear_sign"),
|
|
]
|