Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions frontend_multi_user/src/plan_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -949,6 +949,27 @@ def plan():
)


@plan_routes_bp.route("/plan/import", methods=["GET", "POST"])
@login_required
def plan_import():
message = None
message_type = None
if request.method == "POST":
zip_file = request.files.get("zip_file")
if zip_file is None or zip_file.filename == "":
message = "No file selected."
message_type = "error"
elif not zip_file.filename.endswith(".zip"):
message = "Please upload a .zip file."
message_type = "error"
else:
# TODO: process the zip file
logger.info("Plan import: received zip file %r (%s bytes)", zip_file.filename, zip_file.content_length)
message = f"Uploaded {zip_file.filename} (not yet processed)."
message_type = "success"
return render_template("plan_import.html", message=message, message_type=message_type)


@plan_routes_bp.route("/plan/stop", methods=["POST"])
@login_required
def plan_stop():
Expand Down
92 changes: 92 additions & 0 deletions frontend_multi_user/templates/plan_import.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
{% extends "base.html" %}
{% block title %}Import Plan - PlanExe{% endblock %}
{% block head %}
<style>
.import-title {
margin: 0 0 8px;
font-size: 1.2rem;
font-weight: 700;
}
.import-subtitle {
margin: 0 0 20px;
font-size: 0.82rem;
color: var(--color-text-secondary);
}
.import-wrap {
border: 1px solid var(--color-border);
border-radius: var(--radius-lg);
background: var(--color-card-bg);
padding: 24px;
max-width: 500px;
}
.import-label {
display: block;
font-size: 0.82rem;
font-weight: 600;
margin-bottom: 8px;
color: var(--color-text);
}
.import-file-input {
display: block;
margin-bottom: 16px;
font-size: 0.82rem;
}
.import-submit {
padding: 8px 20px;
font-size: 0.82rem;
font-weight: 600;
color: #fff;
background: var(--color-primary, #0066cc);
border: none;
border-radius: var(--radius-lg);
cursor: pointer;
}
.import-submit:hover {
opacity: 0.9;
}
.import-back {
display: inline-block;
margin-bottom: 16px;
font-size: 0.82rem;
color: var(--color-primary, #0066cc);
text-decoration: none;
}
.import-message {
margin-top: 16px;
padding: 10px 14px;
border-radius: var(--radius-lg);
font-size: 0.82rem;
}
.import-message-success {
background: #e6f4ea;
color: #1a7f37;
border: 1px solid #a3d9a5;
}
.import-message-error {
background: #fdecea;
color: #c0392b;
border: 1px solid #e6b0aa;
}
</style>
{% endblock %}

{% block content %}
<a href="/plan" class="import-back">&larr; Back to Plans</a>
<h1 class="import-title">Import Plan</h1>
<p class="import-subtitle">Upload a plan zip file to import it.</p>

<div class="import-wrap">
<form method="POST" enctype="multipart/form-data">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<label class="import-label" for="zip-file">Select zip file</label>
<input class="import-file-input" type="file" id="zip-file" name="zip_file" accept=".zip" required>
<button class="import-submit" type="submit">Upload</button>
</form>

{% if message %}
<div class="import-message {% if message_type == 'error' %}import-message-error{% else %}import-message-success{% endif %}">
{{ message }}
</div>
{% endif %}
</div>
{% endblock %}
5 changes: 4 additions & 1 deletion frontend_multi_user/templates/plan_list.html
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,10 @@
{% endblock %}

{% block content %}
<h1 class="plan-list-title">Plans</h1>
<div style="display:flex; justify-content:space-between; align-items:center;">
<h1 class="plan-list-title">Plans</h1>
<a href="/plan/import" style="padding:6px 14px; font-size:0.8rem; font-weight:600; color:var(--color-text); background:var(--color-bg-soft); border:1px solid var(--color-border); border-radius:var(--radius-lg); text-decoration:none; white-space:nowrap;">Import Plan</a>
</div>
<p class="plan-list-subtitle">Technical queue view · newest first · click row to inspect</p>
<div class="plan-list-wrap">
{% if plan_rows %}
Expand Down
Loading