admin_mandanten ergänzt
This commit is contained in:
parent
5a322aa403
commit
1d0256d81a
@ -5,6 +5,7 @@ from functools import wraps
|
||||
from logging.handlers import RotatingFileHandler
|
||||
|
||||
import psycopg2
|
||||
|
||||
from flask import (
|
||||
Flask,
|
||||
redirect,
|
||||
@ -13,6 +14,7 @@ from flask import (
|
||||
send_from_directory,
|
||||
session,
|
||||
url_for,
|
||||
abort,
|
||||
)
|
||||
from werkzeug.security import check_password_hash, generate_password_hash
|
||||
|
||||
@ -145,15 +147,46 @@ def ensure_default_admin():
|
||||
""", ("KOLB", "Kolb Compliance", "info@kolb.cc", 0))
|
||||
mandant_id = cur.fetchone()[0]
|
||||
|
||||
cur.execute("""
|
||||
SELECT id FROM app_group
|
||||
WHERE mandant_id = %s AND group_name = %s
|
||||
""", (1, "Administratoren"))
|
||||
group_row = cur.fetchone()
|
||||
|
||||
if group_row:
|
||||
admin_group_id = group_row[0]
|
||||
else:
|
||||
cur.execute("""
|
||||
INSERT INTO app_group (mandant_id, group_name)
|
||||
VALUES (%s, %s)
|
||||
RETURNING id
|
||||
""", (1, "Administratoren"))
|
||||
admin_group_id = cur.fetchone()[0]
|
||||
|
||||
cur.execute("SELECT id FROM app_user WHERE email = %s", ("admin@kolb.cc",))
|
||||
user_row = cur.fetchone()
|
||||
|
||||
if not user_row:
|
||||
if user_row:
|
||||
admin_user_id = user_row[0]
|
||||
else:
|
||||
password_hash = generate_password_hash("topsecret")
|
||||
cur.execute("""
|
||||
INSERT INTO app_user (email, name, mandant_id, password_hash, status)
|
||||
VALUES (%s, %s, %s, %s, %s)
|
||||
""", ("admin@kolb.cc", "Admin", mandant_id, password_hash, 1))
|
||||
RETURNING id
|
||||
""", ("admin@kolb.cc", "Admin", 1, password_hash, 1))
|
||||
admin_user_id = cur.fetchone()[0]
|
||||
|
||||
cur.execute("""
|
||||
SELECT 1 FROM user_group
|
||||
WHERE user_id = %s AND group_id = %s AND mandant_id = %s
|
||||
""", (admin_user_id, admin_group_id, 1))
|
||||
|
||||
if cur.fetchone() is None:
|
||||
cur.execute("""
|
||||
INSERT INTO user_group (user_id, group_id, mandant_id)
|
||||
VALUES (%s, %s, %s)
|
||||
""", (admin_user_id, admin_group_id, 1))
|
||||
|
||||
conn.commit()
|
||||
cur.close()
|
||||
@ -186,6 +219,68 @@ def get_current_user():
|
||||
"is_logged_in": bool(session.get("user_id")),
|
||||
}
|
||||
|
||||
def fetchone_dict(cur):
|
||||
row = cur.fetchone()
|
||||
if row is None:
|
||||
return None
|
||||
columns = [desc[0] for desc in cur.description]
|
||||
return dict(zip(columns, row))
|
||||
|
||||
|
||||
def fetchall_dict(cur):
|
||||
rows = cur.fetchall()
|
||||
columns = [desc[0] for desc in cur.description]
|
||||
return [dict(zip(columns, row)) for row in rows]
|
||||
|
||||
|
||||
def user_is_admin():
|
||||
user_id = session.get("user_id")
|
||||
if not user_id:
|
||||
return False
|
||||
|
||||
conn = get_connection()
|
||||
cur = conn.cursor()
|
||||
|
||||
cur.execute("""
|
||||
SELECT 1
|
||||
FROM app_user u
|
||||
JOIN user_group ug ON ug.user_id = u.id
|
||||
JOIN app_group g ON g.id = ug.group_id
|
||||
WHERE u.id = %s
|
||||
AND ug.mandant_id = 1
|
||||
AND g.mandant_id = 1
|
||||
AND g.group_name = 'Administratoren'
|
||||
LIMIT 1
|
||||
""", (user_id,))
|
||||
|
||||
result = cur.fetchone()
|
||||
|
||||
cur.close()
|
||||
conn.close()
|
||||
|
||||
return result is not None
|
||||
|
||||
|
||||
def get_current_user():
|
||||
return {
|
||||
"user_id": session.get("user_id"),
|
||||
"user_name": session.get("user_name"),
|
||||
"user_email": session.get("user_email"),
|
||||
"is_logged_in": bool(session.get("user_id")),
|
||||
"is_admin": user_is_admin() if session.get("user_id") else False,
|
||||
}
|
||||
|
||||
|
||||
def admin_required(view_func):
|
||||
@wraps(view_func)
|
||||
def wrapper(*args, **kwargs):
|
||||
if not session.get("user_id"):
|
||||
return redirect(url_for("login", next=request.path))
|
||||
if not user_is_admin():
|
||||
abort(403)
|
||||
return view_func(*args, **kwargs)
|
||||
return wrapper
|
||||
|
||||
|
||||
def login_required(view_func):
|
||||
@wraps(view_func)
|
||||
@ -325,3 +420,112 @@ def serve_style(filename):
|
||||
@app.route("/files/<path:filename>")
|
||||
def serve_file(filename):
|
||||
return send_from_directory("/app/files", filename)
|
||||
|
||||
#temporär
|
||||
@app.route("/pwd/<password>/<key>")
|
||||
def generate_pwd_hash(password, key):
|
||||
if key != "geheim":
|
||||
return "Forbidden", 403
|
||||
|
||||
return f"<pre>{generate_password_hash(password)}</pre>"
|
||||
|
||||
@app.route("/profil")
|
||||
@login_required
|
||||
def profil():
|
||||
conn = get_connection()
|
||||
cur = conn.cursor()
|
||||
|
||||
cur.execute("""
|
||||
SELECT u.id, u.email, u.name, u.mandant_id, u.last_login, u.status,
|
||||
m.name AS mandant_name, m.kuerzel AS mandant_kuerzel
|
||||
FROM app_user u
|
||||
JOIN mandant m ON m.id = u.mandant_id
|
||||
WHERE u.id = %s
|
||||
""", (session["user_id"],))
|
||||
|
||||
user_data = fetchone_dict(cur)
|
||||
|
||||
cur.close()
|
||||
conn.close()
|
||||
|
||||
return render_template(
|
||||
"profil.html",
|
||||
page_title="Profil",
|
||||
active_page="profil",
|
||||
profile=user_data,
|
||||
**get_current_user()
|
||||
)
|
||||
|
||||
@app.route("/admin/mandanten", methods=["GET", "POST"])
|
||||
@admin_required
|
||||
def admin_mandanten():
|
||||
conn = get_connection()
|
||||
cur = conn.cursor()
|
||||
|
||||
if request.method == "POST":
|
||||
action = request.form.get("action")
|
||||
|
||||
if action == "create":
|
||||
kuerzel = request.form.get("kuerzel", "").strip()
|
||||
name = request.form.get("name", "").strip()
|
||||
kontakt_email = request.form.get("kontakt_email", "").strip()
|
||||
level = request.form.get("level", "0").strip()
|
||||
|
||||
cur.execute("""
|
||||
INSERT INTO mandant (kuerzel, name, kontakt_email, level)
|
||||
VALUES (%s, %s, %s, %s)
|
||||
""", (kuerzel, name, kontakt_email or None, int(level or 0)))
|
||||
conn.commit()
|
||||
|
||||
elif action == "update":
|
||||
mandant_id = request.form.get("id")
|
||||
kuerzel = request.form.get("kuerzel", "").strip()
|
||||
name = request.form.get("name", "").strip()
|
||||
kontakt_email = request.form.get("kontakt_email", "").strip()
|
||||
level = request.form.get("level", "0").strip()
|
||||
|
||||
cur.execute("""
|
||||
UPDATE mandant
|
||||
SET kuerzel = %s,
|
||||
name = %s,
|
||||
kontakt_email = %s,
|
||||
level = %s
|
||||
WHERE id = %s
|
||||
""", (kuerzel, name, kontakt_email or None, int(level or 0), int(mandant_id)))
|
||||
conn.commit()
|
||||
|
||||
elif action == "delete":
|
||||
mandant_id = request.form.get("id")
|
||||
cur.execute("DELETE FROM mandant WHERE id = %s", (int(mandant_id),))
|
||||
conn.commit()
|
||||
|
||||
cur.close()
|
||||
conn.close()
|
||||
return redirect(url_for("admin_mandanten"))
|
||||
|
||||
cur.execute("""
|
||||
SELECT id, kuerzel, name, kontakt_email, level
|
||||
FROM mandant
|
||||
ORDER BY id
|
||||
""")
|
||||
mandanten = fetchall_dict(cur)
|
||||
|
||||
cur.close()
|
||||
conn.close()
|
||||
|
||||
return render_template(
|
||||
"admin_mandanten.html",
|
||||
page_title="Admin - Mandanten",
|
||||
active_page="admin",
|
||||
mandanten=mandanten,
|
||||
**get_current_user()
|
||||
)
|
||||
|
||||
@app.errorhandler(403)
|
||||
def forbidden(_error):
|
||||
return render_template(
|
||||
"403.html",
|
||||
page_title="Kein Zugriff",
|
||||
active_page="",
|
||||
**get_current_user()
|
||||
), 403
|
||||
18
app/flask-postgres/app/templates/403.html
Normal file
18
app/flask-postgres/app/templates/403.html
Normal file
@ -0,0 +1,18 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{ page_title }}</title>
|
||||
<link rel="stylesheet" href="/styles/site.css">
|
||||
</head>
|
||||
<body>
|
||||
<main class="content-area">
|
||||
<section class="content-box">
|
||||
<h1>Kein Zugriff</h1>
|
||||
<p>Sie haben keine Berechtigung für diese Seite.</p>
|
||||
<p><a href="/home">Zurück zur Startseite</a></p>
|
||||
</section>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
116
app/flask-postgres/app/templates/admin_mandanten.html
Normal file
116
app/flask-postgres/app/templates/admin_mandanten.html
Normal file
@ -0,0 +1,116 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{ page_title }}</title>
|
||||
<link rel="stylesheet" href="/styles/site.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="site-header">
|
||||
<div class="header-inner">
|
||||
<div class="logo-area">
|
||||
<a href="/home">
|
||||
<img src="/images/Logo-Compliance-Verification-bg-1.png" alt="Logo" class="site-logo">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<nav class="top-nav">
|
||||
<a href="/home">Home</a>
|
||||
<a href="/preise">Preise</a>
|
||||
<a href="/allgemein">Allgemein</a>
|
||||
|
||||
{% if is_logged_in %}
|
||||
<div class="user-menu">
|
||||
<button class="user-menu-toggle" type="button">{{ user_name }} ▾</button>
|
||||
<div class="user-menu-dropdown">
|
||||
<a href="/profil">Profil</a>
|
||||
{% if is_admin %}
|
||||
<a href="/admin/mandanten">Admin</a>
|
||||
{% endif %}
|
||||
<a href="/logout">Logout</a>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<a href="/login">Login</a>
|
||||
{% endif %}
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="content-area">
|
||||
<section class="content-box">
|
||||
<h1>Mandantenverwaltung</h1>
|
||||
|
||||
<h2>Neuen Mandanten anlegen</h2>
|
||||
<form method="post" class="admin-form">
|
||||
<input type="hidden" name="action" value="create">
|
||||
|
||||
<div class="form-row">
|
||||
<label>Kürzel</label>
|
||||
<input type="text" name="kuerzel" required>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<label>Name</label>
|
||||
<input type="text" name="name" required>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<label>Kontakt E-Mail</label>
|
||||
<input type="email" name="kontakt_email">
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<label>Level</label>
|
||||
<input type="number" name="level" value="0">
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<button type="submit" class="btn-primary">Mandant anlegen</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<h2>Mandanten</h2>
|
||||
|
||||
{% for mandant in mandanten %}
|
||||
<form method="post" class="admin-card-form">
|
||||
<input type="hidden" name="id" value="{{ mandant.id }}">
|
||||
|
||||
<div class="admin-card">
|
||||
<div class="form-row">
|
||||
<label>ID</label>
|
||||
<input type="text" value="{{ mandant.id }}" readonly>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<label>Kürzel</label>
|
||||
<input type="text" name="kuerzel" value="{{ mandant.kuerzel }}" required>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<label>Name</label>
|
||||
<input type="text" name="name" value="{{ mandant.name }}" required>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<label>Kontakt E-Mail</label>
|
||||
<input type="email" name="kontakt_email" value="{{ mandant.kontakt_email or '' }}">
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<label>Level</label>
|
||||
<input type="number" name="level" value="{{ mandant.level }}">
|
||||
</div>
|
||||
|
||||
<div class="admin-actions">
|
||||
<button type="submit" name="action" value="update" class="btn-primary">Speichern</button>
|
||||
<button type="submit" name="action" value="delete" class="btn-danger" onclick="return confirm('Mandant wirklich löschen?')">Löschen</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
{% endfor %}
|
||||
</section>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
@ -21,8 +21,16 @@
|
||||
<a href="/allgemein" class="{% if active_page == 'allgemein' %}active{% endif %}">Allgemein</a>
|
||||
|
||||
{% if is_logged_in %}
|
||||
<span class="user-box">{{ user_name }}</span>
|
||||
<div class="user-menu">
|
||||
<button class="user-menu-toggle" type="button">{{ user_name }} ▾</button>
|
||||
<div class="user-menu-dropdown">
|
||||
<a href="/profil">Profil</a>
|
||||
{% if is_admin %}
|
||||
<a href="/admin/mandanten">Admin</a>
|
||||
{% endif %}
|
||||
<a href="/logout">Logout</a>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<a href="/login" class="{% if active_page == 'login' %}active{% endif %}">Login</a>
|
||||
{% endif %}
|
||||
|
||||
@ -16,15 +16,23 @@
|
||||
</div>
|
||||
|
||||
<nav class="top-nav">
|
||||
<a href="/home">Home</a>
|
||||
<a href="/preise">Preise</a>
|
||||
<a href="/allgemein">Allgemein</a>
|
||||
<a href="/home" class="{% if active_page == 'home' %}active{% endif %}">Home</a>
|
||||
<a href="/preise" class="{% if active_page == 'preise' %}active{% endif %}">Preise</a>
|
||||
<a href="/allgemein" class="{% if active_page == 'allgemein' %}active{% endif %}">Allgemein</a>
|
||||
|
||||
{% if is_logged_in %}
|
||||
<span class="user-box">{{ user_name }}</span>
|
||||
<div class="user-menu">
|
||||
<button class="user-menu-toggle" type="button">{{ user_name }} ▾</button>
|
||||
<div class="user-menu-dropdown">
|
||||
<a href="/profil">Profil</a>
|
||||
{% if is_admin %}
|
||||
<a href="/admin/mandanten">Admin</a>
|
||||
{% endif %}
|
||||
<a href="/logout">Logout</a>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<a href="/login" class="active">Login</a>
|
||||
<a href="/login" class="{% if active_page == 'login' %}active{% endif %}">Login</a>
|
||||
{% endif %}
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
56
app/flask-postgres/app/templates/profil.html
Normal file
56
app/flask-postgres/app/templates/profil.html
Normal file
@ -0,0 +1,56 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{ page_title }}</title>
|
||||
<link rel="stylesheet" href="/styles/site.css">
|
||||
</head>
|
||||
<body>
|
||||
<header class="site-header">
|
||||
<div class="header-inner">
|
||||
<div class="logo-area">
|
||||
<a href="/home">
|
||||
<img src="/images/Logo-Compliance-Verification-bg-1.png" alt="Logo" class="site-logo">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<nav class="top-nav">
|
||||
<a href="/home">Home</a>
|
||||
<a href="/preise">Preise</a>
|
||||
<a href="/allgemein">Allgemein</a>
|
||||
|
||||
{% if is_logged_in %}
|
||||
<div class="user-menu">
|
||||
<button class="user-menu-toggle" type="button">{{ user_name }} ▾</button>
|
||||
<div class="user-menu-dropdown">
|
||||
<a href="/profil">Profil</a>
|
||||
{% if is_admin %}
|
||||
<a href="/admin/mandanten">Admin</a>
|
||||
{% endif %}
|
||||
<a href="/logout">Logout</a>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<a href="/login">Login</a>
|
||||
{% endif %}
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="content-area">
|
||||
<section class="content-box">
|
||||
<h1>Profil</h1>
|
||||
|
||||
<table class="admin-table">
|
||||
<tr><th>ID</th><td>{{ profile.id }}</td></tr>
|
||||
<tr><th>Name</th><td>{{ profile.name }}</td></tr>
|
||||
<tr><th>E-Mail</th><td>{{ profile.email }}</td></tr>
|
||||
<tr><th>Mandant</th><td>{{ profile.mandant_name }} ({{ profile.mandant_kuerzel }})</td></tr>
|
||||
<tr><th>Status</th><td>{{ profile.status }}</td></tr>
|
||||
<tr><th>Letzter Login</th><td>{{ profile.last_login }}</td></tr>
|
||||
</table>
|
||||
</section>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
51
app/flask-postgres/deploy_flask.sh
Normal file
51
app/flask-postgres/deploy_flask.sh
Normal file
@ -0,0 +1,51 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
SRC_ROOT="/Volumes/MacBook SD/Projekte/compliance-verification/app/flask-postgres"
|
||||
DST_ROOT="/Volumes/docker/flask-postgres"
|
||||
|
||||
NAS_USER="BKolb"
|
||||
NAS_HOST="192.168.0.10"
|
||||
CONTAINER_NAME="flask_web"
|
||||
|
||||
echo "Starte Deployment..."
|
||||
|
||||
[ -d "$SRC_ROOT/app" ] || { echo "Quelle app fehlt: $SRC_ROOT/app"; exit 1; }
|
||||
[ -d "$SRC_ROOT/images" ] || { echo "Quelle images fehlt: $SRC_ROOT/images"; exit 1; }
|
||||
[ -d "$SRC_ROOT/styles" ] || { echo "Quelle styles fehlt: $SRC_ROOT/styles"; exit 1; }
|
||||
|
||||
[ -d "$DST_ROOT/app" ] || { echo "Ziel app fehlt: $DST_ROOT/app"; exit 1; }
|
||||
[ -d "$DST_ROOT/images" ] || { echo "Ziel images fehlt: $DST_ROOT/images"; exit 1; }
|
||||
[ -d "$DST_ROOT/styles" ] || { echo "Ziel styles fehlt: $DST_ROOT/styles"; exit 1; }
|
||||
|
||||
echo "Synchronisiere app/ ..."
|
||||
rsync -av --delete \
|
||||
--exclude '.DS_Store' \
|
||||
--exclude '._*' \
|
||||
--exclude '__pycache__/' \
|
||||
--exclude '*.pyc' \
|
||||
--exclude 'images/' \
|
||||
--exclude 'styles/' \
|
||||
--exclude 'files/' \
|
||||
--exclude 'Dockerfile.txt' \
|
||||
"$SRC_ROOT/app/" "$DST_ROOT/app/"
|
||||
|
||||
echo "Synchronisiere images/ ..."
|
||||
rsync -av --delete \
|
||||
--exclude '.DS_Store' \
|
||||
--exclude '._*' \
|
||||
--exclude 'videos/' \
|
||||
"$SRC_ROOT/images/" "$DST_ROOT/images/"
|
||||
|
||||
echo "Synchronisiere styles/ ..."
|
||||
rsync -av --delete \
|
||||
--exclude '.DS_Store' \
|
||||
--exclude '._*' \
|
||||
"$SRC_ROOT/styles/" "$DST_ROOT/styles/"
|
||||
|
||||
echo "files/ wird bewusst nicht angefasst."
|
||||
|
||||
echo "Starte Container manuell neu ..."
|
||||
#ssh "${NAS_USER}@${NAS_HOST}" "/usr/bin/docker restart ${CONTAINER_NAME}"
|
||||
|
||||
echo "Deployment abgeschlossen."
|
||||
@ -324,3 +324,98 @@ p {
|
||||
.check-list li {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.user-menu {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.user-menu-toggle {
|
||||
background: #376da6;
|
||||
color: #fff;
|
||||
border: 0;
|
||||
border-radius: 999px;
|
||||
padding: 10px 18px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
min-width: 140px;
|
||||
}
|
||||
|
||||
.user-menu-dropdown {
|
||||
display: none;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 44px;
|
||||
min-width: 180px;
|
||||
background: #ffffff;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 12px 30px rgba(0, 0, 0, 0.18);
|
||||
overflow: hidden;
|
||||
z-index: 200;
|
||||
}
|
||||
|
||||
.user-menu-dropdown a {
|
||||
display: block;
|
||||
padding: 12px 16px;
|
||||
color: #1b2430;
|
||||
text-decoration: none;
|
||||
min-width: unset;
|
||||
text-align: left;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.user-menu-dropdown a:hover {
|
||||
background: #eef4fb;
|
||||
color: #0d2f57;
|
||||
}
|
||||
|
||||
.user-menu:hover .user-menu-dropdown {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.admin-form,
|
||||
.admin-card-form {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.admin-card {
|
||||
background: #f8fbff;
|
||||
border: 1px solid #dce3ea;
|
||||
border-radius: 16px;
|
||||
padding: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.admin-actions {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
display: inline-block;
|
||||
padding: 12px 18px;
|
||||
border: 0;
|
||||
border-radius: 10px;
|
||||
background: #b62323;
|
||||
color: #ffffff;
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.admin-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.admin-table th,
|
||||
.admin-table td {
|
||||
padding: 12px 14px;
|
||||
border-bottom: 1px solid #dce3ea;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.admin-table th {
|
||||
width: 220px;
|
||||
color: #0d2f57;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user