diff --git a/__pycache__/main.cpython-313.pyc b/__pycache__/main.cpython-313.pyc index 1585d14..44e028f 100644 Binary files a/__pycache__/main.cpython-313.pyc and b/__pycache__/main.cpython-313.pyc differ diff --git a/main.py b/main.py index f4d1383..450c178 100644 --- a/main.py +++ b/main.py @@ -184,9 +184,19 @@ def index() -> Response: @app.route("/dashboard") # type: ignore[untyped-decorator] def dashboard() -> str: + # render projects overview return str(render_template("dashboard.html")) +@app.route("/dashboard/project/") # type: ignore[untyped-decorator] +def project_dashboard(slug: str) -> str: + # render single project management dashboard + proj_dir = _project_dir(slug) + if not proj_dir.exists(): + abort(404) + return str(render_template("project_dashboard.html", slug=slug)) + + @app.route("/editor/") # type: ignore[untyped-decorator] def editor(slug: str) -> str: proj_dir = _project_dir(slug) @@ -345,6 +355,21 @@ def api_pages_tree(slug: str) -> tuple[Response, int] | Response: return jsonify(tree) +@app.route("/api/projects//pages-tree", methods=["PUT"]) # type: ignore[untyped-decorator] +def api_put_pages_tree(slug: str) -> tuple[Response, int] | Response: + """儲存自訂的頁面樹狀結構到 project.json 的 page_tree 欄位。""" + if not _project_dir(slug).exists(): + return jsonify({"error": "專案不存在"}), 404 + body: dict[str, Any] = request.get_json(force=True) or {} + if not isinstance(body, dict): + return jsonify({"error": "不正確的資料格式"}), 400 + data = _load_project(slug) + data["page_tree"] = body + data["updated_at"] = _now_iso() + _save_project(slug, data) + return jsonify({"ok": True, "page_tree": body}) + + @app.route("/api/projects//media/upload", methods=["POST"]) # type: ignore[untyped-decorator] def api_media_upload(slug: str) -> tuple[Response, int] | Response: proj_dir = _project_dir(slug) diff --git a/scripts/test_phase1.py b/scripts/test_phase1.py new file mode 100644 index 0000000..a1dde66 --- /dev/null +++ b/scripts/test_phase1.py @@ -0,0 +1,63 @@ +"""Automated basic end-to-end test for Phase 1 features. +Creates a project, updates settings, uploads an image, lists media, deletes image. +""" +import io +import os +import sys +import time +import json +import requests +from PIL import Image + +BASE = 'http://127.0.0.1:5000' + +def create_project(name='test-project'): + r = requests.post(f'{BASE}/api/projects', json={'name': name, 'description': 'automated test'}) + r.raise_for_status() + return r.json() + +def update_settings(slug): + r = requests.put(f'{BASE}/api/projects/{slug}/settings', json={'title':'Auto Test','description':'desc'}) + r.raise_for_status() + return r.json() + +def upload_image(slug): + # generate small PNG + im = Image.new('RGB', (100,100), color=(123,222,100)) + buf = io.BytesIO() + im.save(buf, format='PNG') + buf.seek(0) + files = {'file': ('test.png', buf, 'image/png')} + r = requests.post(f'{BASE}/api/projects/{slug}/media/upload', files=files) + r.raise_for_status() + return r.json() + +def list_media(slug): + r = requests.get(f'{BASE}/api/projects/{slug}/media/list') + r.raise_for_status() + return r.json() + +def delete_media(slug, rel): + r = requests.delete(f'{BASE}/api/projects/{slug}/media/{rel}') + return r + +if __name__ == '__main__': + print('Creating project...') + proj = create_project('phase1-e2e') + slug = proj['slug'] + print('Project created:', slug) + print('Updating settings...') + print(update_settings(slug)) + print('Uploading image...') + up = upload_image(slug) + print('Upload result:', up) + time.sleep(0.5) + items = list_media(slug) + print('Media list count:', len(items)) + if items: + it = items[0] + rel = f"media/images/{it['date']}/{it['filename']}" + print('Deleting', rel) + r = delete_media(slug, rel) + print('Delete status', r.status_code) + print('Done') diff --git a/templates/dashboard.html b/templates/dashboard.html index 8bdc7df..acc3d10 100644 --- a/templates/dashboard.html +++ b/templates/dashboard.html @@ -1,136 +1,3 @@ - - - - - - 管理器 - Dashboard - - - - -
-

網站管理器

-
- -
-
概覽
-
網站設定
-
頁面管理
-
媒體庫
-
- -
-
選擇一個專案在右側編輯。
- - - - - - -
- - - - diff --git a/templates/editor.html b/templates/editor.html index 742f65a..2db982f 100644 --- a/templates/editor.html +++ b/templates/editor.html @@ -2319,7 +2319,7 @@ window.VVVEB_PROJECT_SLUG = "{% endraw %}{{ slug | safe }}{% raw %}"; if(!btn) return; btn.addEventListener('click', function(e){ var slug = window.VVVEB_PROJECT_SLUG || ''; - var url = '/dashboard' + (slug ? ('?slug='+encodeURIComponent(slug)) : ''); + var url = '/dashboard/project/' + encodeURIComponent(slug || ''); window.open(url, '_blank'); }); }); diff --git a/templates/project_dashboard.html b/templates/project_dashboard.html new file mode 100644 index 0000000..c4ec8d9 --- /dev/null +++ b/templates/project_dashboard.html @@ -0,0 +1,203 @@ + + + + + + 專案管理 + + + +
+

專案管理

+
+ +
+
概覽
+
網站設定
+
頁面管理
+
媒體庫
+
+ +
+
專案:
+ + + + + + +
+ + + + diff --git a/websites/phase1-e2e/index.html b/websites/phase1-e2e/index.html new file mode 100644 index 0000000..8821b1c --- /dev/null +++ b/websites/phase1-e2e/index.html @@ -0,0 +1,33 @@ + + + + + + + + My page + + + + + + + + + +
+
+
+

Bootstrap 5 start page

+

Start by dragging components to page or double click to edit text

+
+
+
+ + diff --git a/websites/phase1-e2e/media/images/2026-05/3179739a9b94477881912423503cd09c_thumb.png b/websites/phase1-e2e/media/images/2026-05/3179739a9b94477881912423503cd09c_thumb.png new file mode 100644 index 0000000..e4b37ef Binary files /dev/null and b/websites/phase1-e2e/media/images/2026-05/3179739a9b94477881912423503cd09c_thumb.png differ diff --git a/websites/phase1-e2e/media/images/uploads.json b/websites/phase1-e2e/media/images/uploads.json new file mode 100644 index 0000000..b0c86f2 --- /dev/null +++ b/websites/phase1-e2e/media/images/uploads.json @@ -0,0 +1,10 @@ +{ + "2026-05": [ + { + "filename": "5717be82fe7246a6b7b437182cc5748f.png", + "original_name": "test.png", + "size": 289, + "uploaded_at": "2026-05-26T03:57:55" + } + ] +} \ No newline at end of file diff --git a/websites/phase1-e2e/project.json b/websites/phase1-e2e/project.json new file mode 100644 index 0000000..63f4b1c --- /dev/null +++ b/websites/phase1-e2e/project.json @@ -0,0 +1,11 @@ +{ + "name": "phase1-e2e", + "slug": "phase1-e2e", + "description": "automated test", + "created_at": "2026-05-26T03:57:54", + "settings": { + "title": "Auto Test", + "description": "desc" + }, + "updated_at": "2026-05-26T03:57:55" +} \ No newline at end of file