Add display deletion endpoint and admin UI tweaks
This commit is contained in:
@@ -498,3 +498,32 @@ def update_display_name(display_id: int):
|
||||
db.session.commit()
|
||||
flash("Display name updated", "success")
|
||||
return redirect(url_for("admin.company_detail", company_id=display.company_id))
|
||||
|
||||
|
||||
@bp.post("/displays/<int:display_id>/delete")
|
||||
@login_required
|
||||
def delete_display(display_id: int):
|
||||
"""Admin: delete a display."""
|
||||
|
||||
admin_required()
|
||||
|
||||
display = db.session.get(Display, display_id)
|
||||
if not display:
|
||||
abort(404)
|
||||
|
||||
company_id = display.company_id
|
||||
display_name = display.name
|
||||
|
||||
# If FK constraints are enabled, delete in a safe order.
|
||||
# 1) Unassign playlist
|
||||
display.assigned_playlist_id = None
|
||||
|
||||
# 2) Delete active sessions for this display
|
||||
DisplaySession.query.filter_by(display_id=display.id).delete(synchronize_session=False)
|
||||
|
||||
# 3) Delete display
|
||||
db.session.delete(display)
|
||||
db.session.commit()
|
||||
|
||||
flash(f"Display '{display_name}' deleted.", "success")
|
||||
return redirect(url_for("admin.company_detail", company_id=company_id))
|
||||
|
||||
BIN
app/static/favicon.png
Normal file
BIN
app/static/favicon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.9 KiB |
139
app/static/logo.svg
Normal file
139
app/static/logo.svg
Normal file
@@ -0,0 +1,139 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="150.74998mm"
|
||||
height="46.903393mm"
|
||||
viewBox="0 0 150.74998 46.903393"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
inkscape:version="1.4.3 (0d15f75, 2025-12-25)"
|
||||
sodipodi:docname="opencast logo.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:zoom="0.78840488"
|
||||
inkscape:cx="397.00414"
|
||||
inkscape:cy="561.25984"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1129"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g35">
|
||||
<inkscape:page
|
||||
x="-3.5699601e-22"
|
||||
y="0"
|
||||
width="150.74998"
|
||||
height="46.903393"
|
||||
id="page2"
|
||||
margin="0"
|
||||
bleed="0" />
|
||||
</sodipodi:namedview>
|
||||
<defs
|
||||
id="defs1">
|
||||
<rect
|
||||
x="319.63272"
|
||||
y="230.84586"
|
||||
width="465.49686"
|
||||
height="96.397171"
|
||||
id="rect4" />
|
||||
<filter
|
||||
style="color-interpolation-filters:sRGB"
|
||||
inkscape:label="Drop Shadow"
|
||||
id="filter36"
|
||||
x="-0.16131238"
|
||||
y="-0.23838385"
|
||||
width="1.358472"
|
||||
height="1.5529181">
|
||||
<feFlood
|
||||
result="flood"
|
||||
in="SourceGraphic"
|
||||
flood-opacity="0.498039"
|
||||
flood-color="rgb(0,0,0)"
|
||||
id="feFlood35" />
|
||||
<feGaussianBlur
|
||||
result="blur"
|
||||
in="SourceGraphic"
|
||||
stdDeviation="3.000000"
|
||||
id="feGaussianBlur35" />
|
||||
<feOffset
|
||||
result="offset"
|
||||
in="blur"
|
||||
dx="1.600000"
|
||||
dy="2.300000"
|
||||
id="feOffset35" />
|
||||
<feComposite
|
||||
result="comp1"
|
||||
operator="in"
|
||||
in="flood"
|
||||
in2="offset"
|
||||
id="feComposite35" />
|
||||
<feComposite
|
||||
result="comp2"
|
||||
operator="over"
|
||||
in="SourceGraphic"
|
||||
in2="comp1"
|
||||
id="feComposite36" />
|
||||
</filter>
|
||||
</defs>
|
||||
<g
|
||||
inkscape:label="Laag 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-19.647457,-50.186441)">
|
||||
<g
|
||||
id="g35"
|
||||
style="filter:url(#filter36)"
|
||||
inkscape:export-filename="rssfeed\app\static\logo.svg"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96"
|
||||
transform="translate(-1.6779659,-0.67118643)">
|
||||
<ellipse
|
||||
style="fill:#ffe511;fill-opacity:1;stroke-width:0.212298"
|
||||
id="path1"
|
||||
cx="43.582642"
|
||||
cy="76.934746"
|
||||
rx="15.057219"
|
||||
ry="11.056598"
|
||||
inkscape:export-filename="rssfeed\app\static\logo.svg"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96" />
|
||||
<ellipse
|
||||
style="fill:#ffef6e;fill-opacity:1;stroke-width:0.212298"
|
||||
id="path2"
|
||||
cx="51.380131"
|
||||
cy="68.574883"
|
||||
rx="13.175065"
|
||||
ry="10.517252" />
|
||||
<ellipse
|
||||
style="fill:#fff5a3;fill-opacity:1;stroke-width:0.212298"
|
||||
id="path3"
|
||||
cx="57.833221"
|
||||
cy="78.148277"
|
||||
rx="15.326097"
|
||||
ry="10.112741" />
|
||||
</g>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
transform="matrix(0.26458333,0,0,0.26458333,-4.5436437,1.7684327)"
|
||||
id="text3"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:74.6667px;font-family:Georgia;-inkscape-font-specification:'Georgia, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;writing-mode:lr-tb;direction:ltr;white-space:pre;shape-inside:url(#rect4);display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.13386;stroke-dasharray:none;stroke-opacity:1"><tspan
|
||||
x="319.63281"
|
||||
y="298.21152"
|
||||
id="tspan2"><tspan
|
||||
style="stroke:#000000"
|
||||
id="tspan1">OpenSlide</tspan></tspan></text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.2 KiB |
@@ -56,6 +56,12 @@ body {
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
|
||||
.brand-logo {
|
||||
width: 160px;
|
||||
height: 45px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.navbar-brand {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
@@ -45,7 +45,17 @@
|
||||
<td class="monospace small">{{ d.token }}</td>
|
||||
<td class="text-muted">{{ d.assigned_playlist.name if d.assigned_playlist else "(none)" }}</td>
|
||||
<td class="text-end">
|
||||
<a class="btn btn-outline-ink btn-sm" href="{{ url_for('display.display_player', token=d.token) }}" target="_blank">Open</a>
|
||||
<div class="d-inline-flex gap-2">
|
||||
<a class="btn btn-outline-ink btn-sm" href="{{ url_for('display.display_player', token=d.token) }}" target="_blank">Open</a>
|
||||
<form
|
||||
method="post"
|
||||
action="{{ url_for('admin.delete_display', display_id=d.id) }}"
|
||||
data-confirm="Delete display {{ d.name }}? This cannot be undone."
|
||||
onsubmit="return confirm(this.dataset.confirm);"
|
||||
>
|
||||
<button class="btn btn-outline-danger btn-sm" type="submit">Delete</button>
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>{{ title or "Signage" }}</title>
|
||||
<link rel="icon" href="{{ url_for('static', filename='favicon.png') }}" type="image/png" />
|
||||
<link rel="apple-touch-icon" href="{{ url_for('static', filename='favicon.png') }}" />
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet" />
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}" />
|
||||
</head>
|
||||
@@ -11,8 +13,14 @@
|
||||
<nav class="navbar navbar-expand-lg navbar-light fixed-top app-navbar">
|
||||
<div class="container">
|
||||
<a class="navbar-brand d-flex align-items-center gap-2" href="/">
|
||||
<span class="brand-mark" aria-hidden="true">S</span>
|
||||
<span>Signage</span>
|
||||
<img
|
||||
class="brand-logo"
|
||||
src="{{ url_for('static', filename='logo.svg') }}"
|
||||
alt="Signage"
|
||||
width="34"
|
||||
height="34"
|
||||
/>
|
||||
|
||||
</a>
|
||||
|
||||
<button
|
||||
@@ -31,16 +39,9 @@
|
||||
<ul class="navbar-nav me-auto">
|
||||
{% if current_user.is_authenticated %}
|
||||
{% if current_user.is_admin %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{{ url_for('admin.dashboard') }}">Admin</a>
|
||||
</li>
|
||||
{# Dashboard link removed: users can click the logo to go to the dashboard. #}
|
||||
{% else %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{{ url_for('company.dashboard') }}">Dashboard</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{{ url_for('company.my_company') }}">My Company</a>
|
||||
</li>
|
||||
{# Dashboard link removed: users can click the logo to go to the dashboard. #}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</ul>
|
||||
@@ -49,6 +50,9 @@
|
||||
{% if current_user.is_authenticated %}
|
||||
<div class="small text-muted">{{ current_user.email }}</div>
|
||||
<a class="btn btn-outline-ink btn-sm" href="{{ url_for('auth.change_password') }}">Change password</a>
|
||||
{% if not current_user.is_admin %}
|
||||
<a class="btn btn-outline-ink btn-sm" href="{{ url_for('company.my_company') }}">My company</a>
|
||||
{% endif %}
|
||||
{% if session.get('impersonator_admin_id') %}
|
||||
<a class="btn btn-brand btn-sm" href="{{ url_for('auth.stop_impersonation') }}">Stop impersonation</a>
|
||||
{% endif %}
|
||||
|
||||
Reference in New Issue
Block a user