Add HTML5 project deletion flow
This commit is contained in:
@@ -452,6 +452,17 @@ def _project_row(project: object) -> str:
|
|||||||
<td>
|
<td>
|
||||||
<a class="button" href="/html5/projects/{quote(project_id)}/editor">IDE</a>
|
<a class="button" href="/html5/projects/{quote(project_id)}/editor">IDE</a>
|
||||||
<a class="button" href="/html5/projects/{quote(project_id)}/setup">Setup</a>
|
<a class="button" href="/html5/projects/{quote(project_id)}/setup">Setup</a>
|
||||||
|
<form
|
||||||
|
class="delete-project"
|
||||||
|
method="post"
|
||||||
|
action="/html5/projects/{quote(project_id)}/delete"
|
||||||
|
hx-post="/html5/projects/{quote(project_id)}/delete"
|
||||||
|
hx-target="[data-html5-projects-body]"
|
||||||
|
hx-swap="innerHTML"
|
||||||
|
>
|
||||||
|
<input name="confirmation" value="{escape(project_id)}" aria-label="confirmation" />
|
||||||
|
<button type="submit">Удалить</button>
|
||||||
|
</form>
|
||||||
</td>
|
</td>
|
||||||
</tr>"""
|
</tr>"""
|
||||||
|
|
||||||
@@ -605,7 +616,7 @@ def _css() -> str:
|
|||||||
:root{color-scheme:light;--bg:#f7f8fb;--card:#fff;--text:#17202a;--muted:#687385;--line:#dce2ea;--brand:#2457d6;--ok:#168457;--warn:#a16207}
|
:root{color-scheme:light;--bg:#f7f8fb;--card:#fff;--text:#17202a;--muted:#687385;--line:#dce2ea;--brand:#2457d6;--ok:#168457;--warn:#a16207}
|
||||||
*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--text);font:14px/1.45 system-ui,-apple-system,Segoe UI,sans-serif}a{color:inherit}
|
*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--text);font:14px/1.45 system-ui,-apple-system,Segoe UI,sans-serif}a{color:inherit}
|
||||||
.shell{min-height:100vh;padding:32px}.hero{display:flex;justify-content:space-between;gap:24px;align-items:end;margin:0 auto 24px;max-width:1180px}.hero h1{margin:0;font-size:36px;letter-spacing:0}.lead{max-width:640px;color:var(--muted);font-size:16px}.eyebrow{margin:0 0 8px;color:var(--brand);font-size:12px;font-weight:800;text-transform:uppercase}.hero-metrics{min-width:160px;border:1px solid var(--line);background:var(--card);padding:18px}.hero-metrics strong{display:block;font-size:34px}.hero-metrics span,.muted{color:var(--muted)}
|
.shell{min-height:100vh;padding:32px}.hero{display:flex;justify-content:space-between;gap:24px;align-items:end;margin:0 auto 24px;max-width:1180px}.hero h1{margin:0;font-size:36px;letter-spacing:0}.lead{max-width:640px;color:var(--muted);font-size:16px}.eyebrow{margin:0 0 8px;color:var(--brand);font-size:12px;font-weight:800;text-transform:uppercase}.hero-metrics{min-width:160px;border:1px solid var(--line);background:var(--card);padding:18px}.hero-metrics strong{display:block;font-size:34px}.hero-metrics span,.muted{color:var(--muted)}
|
||||||
.band,.panel,.editor{border:1px solid var(--line);background:var(--card)}.band{max-width:1180px;margin:auto;padding:18px}.section-title,.topbar,.editor-head{display:flex;align-items:center;justify-content:space-between;gap:12px}.button,button{display:inline-flex;align-items:center;justify-content:center;height:32px;border:1px solid var(--line);background:#fff;padding:0 12px;text-decoration:none;font-weight:700;cursor:pointer}table{width:100%;border-collapse:collapse}th,td{border-top:1px solid var(--line);padding:12px;text-align:left}td small{display:block;color:var(--muted)}
|
.band,.panel,.editor{border:1px solid var(--line);background:var(--card)}.band{max-width:1180px;margin:auto;padding:18px}.section-title,.topbar,.editor-head{display:flex;align-items:center;justify-content:space-between;gap:12px}.button,button{display:inline-flex;align-items:center;justify-content:center;height:32px;border:1px solid var(--line);background:#fff;padding:0 12px;text-decoration:none;font-weight:700;cursor:pointer}table{width:100%;border-collapse:collapse}th,td{border-top:1px solid var(--line);padding:12px;text-align:left}td small{display:block;color:var(--muted)}td .button,td form{margin-right:6px}.delete-project{display:inline-flex;gap:4px;vertical-align:middle}.delete-project input{height:32px;width:120px;border:1px solid var(--line);padding:0 6px}
|
||||||
.workspace{min-height:100vh;padding-bottom:34px}.topbar{position:sticky;top:0;z-index:2;height:54px;border-bottom:1px solid var(--line);background:rgba(255,255,255,.94);padding:0 14px}.brand{font-weight:900;text-decoration:none;color:var(--brand)}.project-nav{display:flex;gap:6px;overflow:auto;flex:1}.project-link{white-space:nowrap;text-decoration:none;border:1px solid var(--line);padding:6px 10px;background:#fff}.project-link.active{background:var(--brand);border-color:var(--brand);color:#fff}
|
.workspace{min-height:100vh;padding-bottom:34px}.topbar{position:sticky;top:0;z-index:2;height:54px;border-bottom:1px solid var(--line);background:rgba(255,255,255,.94);padding:0 14px}.brand{font-weight:900;text-decoration:none;color:var(--brand)}.project-nav{display:flex;gap:6px;overflow:auto;flex:1}.project-link{white-space:nowrap;text-decoration:none;border:1px solid var(--line);padding:6px 10px;background:#fff}.project-link.active{background:var(--brand);border-color:var(--brand);color:#fff}
|
||||||
.layout{display:grid;grid-template-columns:280px minmax(0,1fr)320px;height:calc(100vh - 88px)}.panel{overflow:auto}.panel-title{padding:12px;border-bottom:1px solid var(--line);font-size:12px;font-weight:900;text-transform:uppercase;color:var(--muted)}.tree-item{display:block;padding:10px 12px;border-bottom:1px solid var(--line);text-decoration:none}.tree-item span{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.tree-item small{color:var(--muted)}
|
.layout{display:grid;grid-template-columns:280px minmax(0,1fr)320px;height:calc(100vh - 88px)}.panel{overflow:auto}.panel-title{padding:12px;border-bottom:1px solid var(--line);font-size:12px;font-weight:900;text-transform:uppercase;color:var(--muted)}.tree-item{display:block;padding:10px 12px;border-bottom:1px solid var(--line);text-decoration:none}.tree-item span{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.tree-item small{color:var(--muted)}
|
||||||
.editor{min-width:0;overflow:hidden;border-top:0;border-bottom:0}.editor-head{height:72px;border-bottom:1px solid var(--line);padding:0 14px}.editor-head h1{margin:0;font-size:18px}.search{display:flex;gap:6px}.search input{height:32px;width:260px;border:1px solid var(--line);padding:0 10px}.code{height:calc(100% - 72px);margin:0;overflow:auto;padding:16px;background:#fbfaf7;font:13px/1.55 ui-monospace,SFMono-Regular,Consolas,monospace;white-space:pre-wrap}
|
.editor{min-width:0;overflow:hidden;border-top:0;border-bottom:0}.editor-head{height:72px;border-bottom:1px solid var(--line);padding:0 14px}.editor-head h1{margin:0;font-size:18px}.search{display:flex;gap:6px}.search input{height:32px;width:260px;border:1px solid var(--line);padding:0 10px}.code{height:calc(100% - 72px);margin:0;overflow:auto;padding:16px;background:#fbfaf7;font:13px/1.55 ui-monospace,SFMono-Regular,Consolas,monospace;white-space:pre-wrap}
|
||||||
|
|||||||
@@ -1601,6 +1601,17 @@ async def html5_create_project(request: Request) -> Response:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/html5/projects/{project_id}/delete")
|
||||||
|
async def html5_delete_project(project_id: str, request: Request) -> Response:
|
||||||
|
form = await _html5_form_data(request)
|
||||||
|
confirmation = _form_value(form, "confirmation") or ""
|
||||||
|
await delete_project(project_id, ProjectDeleteRequest(confirmation=confirmation))
|
||||||
|
return Response(
|
||||||
|
render_html5_project_rows(_project_summaries()),
|
||||||
|
media_type="text/html; charset=utf-8",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@app.get("/html5/projects/{project_id}/editor")
|
@app.get("/html5/projects/{project_id}/editor")
|
||||||
async def html5_project_editor(project_id: str, q: str = "") -> Response:
|
async def html5_project_editor(project_id: str, q: str = "") -> Response:
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -158,12 +158,23 @@ def test_html5_project_index_creates_project_with_fragment():
|
|||||||
assert f'data-html5-project="{project_id}"' in created.text
|
assert f'data-html5-project="{project_id}"' in created.text
|
||||||
assert "HTML5 Created" in created.text
|
assert "HTML5 Created" in created.text
|
||||||
assert f"/html5/projects/{project_id}/setup" in created.text
|
assert f"/html5/projects/{project_id}/setup" in created.text
|
||||||
|
assert f'hx-post="/html5/projects/{project_id}/delete"' in created.text
|
||||||
assert "<html" not in created.text
|
assert "<html" not in created.text
|
||||||
|
|
||||||
setup = client.get(f"/html5/projects/{project_id}/setup")
|
setup = client.get(f"/html5/projects/{project_id}/setup")
|
||||||
assert setup.status_code == 200
|
assert setup.status_code == 200
|
||||||
assert "HTML5 Created" in setup.text
|
assert "HTML5 Created" in setup.text
|
||||||
|
|
||||||
|
deleted = client.post(f"/html5/projects/{project_id}/delete", data={"confirmation": project_id})
|
||||||
|
assert deleted.status_code == 200
|
||||||
|
assert "text/html" in deleted.headers["content-type"]
|
||||||
|
assert f'data-html5-project="{project_id}"' not in deleted.text
|
||||||
|
assert "<html" not in deleted.text
|
||||||
|
|
||||||
|
deleted_setup = client.get(f"/projects/{project_id}/setup")
|
||||||
|
assert deleted_setup.status_code == 200
|
||||||
|
assert deleted_setup.json()["status"] == "NOT_CONFIGURED"
|
||||||
|
|
||||||
|
|
||||||
def test_html5_project_setup_renders_server_fragments():
|
def test_html5_project_setup_renders_server_fragments():
|
||||||
client = TestClient(app)
|
client = TestClient(app)
|
||||||
|
|||||||
Reference in New Issue
Block a user