feat(postcard): split create into sent/received, add pending status and mark-sent flow
This commit is contained in:
@@ -73,17 +73,23 @@
|
|||||||
"profiles.confirm_delete": "Delete this profile?",
|
"profiles.confirm_delete": "Delete this profile?",
|
||||||
"pc_list.title": "'s postcards",
|
"pc_list.title": "'s postcards",
|
||||||
"pc_list.new": "+ New postcard",
|
"pc_list.new": "+ New postcard",
|
||||||
|
"pc_list.new_sent": "✉️ Sent",
|
||||||
|
"pc_list.new_received": "📬 Received",
|
||||||
"pc_list.duplicate": "Card number already exists",
|
"pc_list.duplicate": "Card number already exists",
|
||||||
"pc_list.all_countries": "All countries",
|
"pc_list.all_countries": "All countries",
|
||||||
"pc_list.filter": "Filter",
|
"pc_list.filter": "Filter",
|
||||||
"pc_list.tab_sent": "Sent",
|
"pc_list.tab_sent": "Sent",
|
||||||
"pc_list.tab_received": "Received",
|
"pc_list.tab_received": "Received",
|
||||||
|
"pc_list.mark_sent": "Send",
|
||||||
|
"pc_list.confirm_sent": "Mark as sent?",
|
||||||
"pc_list.mark_delivered": "Delivered",
|
"pc_list.mark_delivered": "Delivered",
|
||||||
"pc_list.confirm_delivered": "Mark as delivered?",
|
"pc_list.confirm_delivered": "Mark as delivered?",
|
||||||
"pc_list.empty_sent": "No sent postcards.",
|
"pc_list.empty_sent": "No sent postcards.",
|
||||||
"pc_list.empty_received": "No received postcards.",
|
"pc_list.empty_received": "No received postcards.",
|
||||||
"pc_detail.title": "Postcard detail",
|
"pc_detail.title": "Postcard detail",
|
||||||
"pc_detail.edit": "Edit",
|
"pc_detail.edit": "Edit",
|
||||||
|
"pc_detail.mark_sent": "Send",
|
||||||
|
"pc_detail.confirm_sent": "Mark as sent?",
|
||||||
"pc_detail.mark_delivered": "Mark delivered",
|
"pc_detail.mark_delivered": "Mark delivered",
|
||||||
"pc_detail.confirm_delivered": "Mark as delivered?",
|
"pc_detail.confirm_delivered": "Mark as delivered?",
|
||||||
"pc_detail.delete": "Delete",
|
"pc_detail.delete": "Delete",
|
||||||
@@ -106,10 +112,13 @@
|
|||||||
"pc_detail.profile": "Profile",
|
"pc_detail.profile": "Profile",
|
||||||
"pc_detail.notes": "Notes",
|
"pc_detail.notes": "Notes",
|
||||||
"status.sent": "Sent",
|
"status.sent": "Sent",
|
||||||
|
"status.pending": "Pending",
|
||||||
"status.delivered": "Delivered",
|
"status.delivered": "Delivered",
|
||||||
"status.received": "Received",
|
"status.received": "Received",
|
||||||
"pc_form.edit": "Edit postcard",
|
"pc_form.edit": "Edit postcard",
|
||||||
"pc_form.new": "New postcard",
|
"pc_form.new": "New postcard",
|
||||||
|
"pc_form.new_sent": "New sent",
|
||||||
|
"pc_form.new_received": "New received",
|
||||||
"pc_form.number": "Number",
|
"pc_form.number": "Number",
|
||||||
"pc_form.number_hint": "Format: AB-1234567",
|
"pc_form.number_hint": "Format: AB-1234567",
|
||||||
"pc_form.status": "Status",
|
"pc_form.status": "Status",
|
||||||
|
|||||||
@@ -73,17 +73,23 @@
|
|||||||
"profiles.confirm_delete": "确定删除此 Profile?",
|
"profiles.confirm_delete": "确定删除此 Profile?",
|
||||||
"pc_list.title": " 的明信片",
|
"pc_list.title": " 的明信片",
|
||||||
"pc_list.new": "+ 新明信片",
|
"pc_list.new": "+ 新明信片",
|
||||||
|
"pc_list.new_sent": "✉️ 寄出",
|
||||||
|
"pc_list.new_received": "📬 收到",
|
||||||
"pc_list.duplicate": "编号已存在",
|
"pc_list.duplicate": "编号已存在",
|
||||||
"pc_list.all_countries": "全部国家",
|
"pc_list.all_countries": "全部国家",
|
||||||
"pc_list.filter": "筛选",
|
"pc_list.filter": "筛选",
|
||||||
"pc_list.tab_sent": "寄出",
|
"pc_list.tab_sent": "寄出",
|
||||||
"pc_list.tab_received": "收到",
|
"pc_list.tab_received": "收到",
|
||||||
|
"pc_list.mark_sent": "寄出",
|
||||||
|
"pc_list.confirm_sent": "确认寄出?",
|
||||||
"pc_list.mark_delivered": "已送达",
|
"pc_list.mark_delivered": "已送达",
|
||||||
"pc_list.confirm_delivered": "确认已送达?",
|
"pc_list.confirm_delivered": "确认已送达?",
|
||||||
"pc_list.empty_sent": "没有寄出的明信片。",
|
"pc_list.empty_sent": "没有寄出的明信片。",
|
||||||
"pc_list.empty_received": "没有收到的明信片。",
|
"pc_list.empty_received": "没有收到的明信片。",
|
||||||
"pc_detail.title": "明信片详情",
|
"pc_detail.title": "明信片详情",
|
||||||
"pc_detail.edit": "编辑",
|
"pc_detail.edit": "编辑",
|
||||||
|
"pc_detail.mark_sent": "寄出",
|
||||||
|
"pc_detail.confirm_sent": "确认寄出?",
|
||||||
"pc_detail.mark_delivered": "已送达",
|
"pc_detail.mark_delivered": "已送达",
|
||||||
"pc_detail.confirm_delivered": "确认已送达?",
|
"pc_detail.confirm_delivered": "确认已送达?",
|
||||||
"pc_detail.delete": "删除",
|
"pc_detail.delete": "删除",
|
||||||
@@ -106,10 +112,13 @@
|
|||||||
"pc_detail.profile": "所属 Profile",
|
"pc_detail.profile": "所属 Profile",
|
||||||
"pc_detail.notes": "备注",
|
"pc_detail.notes": "备注",
|
||||||
"status.sent": "已寄出",
|
"status.sent": "已寄出",
|
||||||
|
"status.pending": "待寄出",
|
||||||
"status.delivered": "已送达",
|
"status.delivered": "已送达",
|
||||||
"status.received": "已收到",
|
"status.received": "已收到",
|
||||||
"pc_form.edit": "编辑明信片",
|
"pc_form.edit": "编辑明信片",
|
||||||
"pc_form.new": "新建明信片",
|
"pc_form.new": "新建明信片",
|
||||||
|
"pc_form.new_sent": "新建寄出",
|
||||||
|
"pc_form.new_received": "新建收到",
|
||||||
"pc_form.number": "编号",
|
"pc_form.number": "编号",
|
||||||
"pc_form.number_hint": "格式:AB-1234567",
|
"pc_form.number_hint": "格式:AB-1234567",
|
||||||
"pc_form.status": "状态",
|
"pc_form.status": "状态",
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ def index_page(request: Request, db: Session = Depends(get_db)):
|
|||||||
).all()
|
).all()
|
||||||
for c in cards:
|
for c in cards:
|
||||||
visible = True
|
visible = True
|
||||||
if c.status in ("sent", "delivered") and p.showcase_visible not in ("sent", "both"):
|
if c.status in ("pending", "sent", "delivered") and p.showcase_visible not in ("sent", "both"):
|
||||||
visible = False
|
visible = False
|
||||||
if c.status == "received" and p.showcase_visible not in ("received", "both"):
|
if c.status == "received" and p.showcase_visible not in ("received", "both"):
|
||||||
visible = False
|
visible = False
|
||||||
@@ -173,7 +173,7 @@ def api_public_cards(
|
|||||||
).all()
|
).all()
|
||||||
for c in cards:
|
for c in cards:
|
||||||
vis = True
|
vis = True
|
||||||
if c.status in ("sent", "delivered") and p.showcase_visible not in ("sent", "both"):
|
if c.status in ("pending", "sent", "delivered") and p.showcase_visible not in ("sent", "both"):
|
||||||
vis = False
|
vis = False
|
||||||
if c.status == "received" and p.showcase_visible not in ("received", "both"):
|
if c.status == "received" and p.showcase_visible not in ("received", "both"):
|
||||||
vis = False
|
vis = False
|
||||||
@@ -218,7 +218,7 @@ def api_public_cards_user(
|
|||||||
Postcard.image_front.isnot(None), Postcard.image_front != ""
|
Postcard.image_front.isnot(None), Postcard.image_front != ""
|
||||||
).all()
|
).all()
|
||||||
if tab == "sent" and p.showcase_visible in ("sent", "both"):
|
if tab == "sent" and p.showcase_visible in ("sent", "both"):
|
||||||
all_cards.extend([c for c in cards if c.status in ("sent", "delivered")])
|
all_cards.extend([c for c in cards if c.status in ("pending", "sent", "delivered")])
|
||||||
elif tab == "received" and p.showcase_visible in ("received", "both"):
|
elif tab == "received" and p.showcase_visible in ("received", "both"):
|
||||||
all_cards.extend([c for c in cards if c.status == "received"])
|
all_cards.extend([c for c in cards if c.status == "received"])
|
||||||
all_cards.sort(key=lambda c: c.send_time or c.created_at, reverse=True)
|
all_cards.sort(key=lambda c: c.send_time or c.created_at, reverse=True)
|
||||||
@@ -613,7 +613,7 @@ def dashboard(request: Request, user: User = Depends(get_current_user_web), db:
|
|||||||
for c in all_cards:
|
for c in all_cards:
|
||||||
if not c.country_to:
|
if not c.country_to:
|
||||||
continue
|
continue
|
||||||
if c.status in ("sent", "delivered"):
|
if c.status in ("pending", "sent", "delivered"):
|
||||||
sent_country_counts[c.country_to] = sent_country_counts.get(c.country_to, 0) + 1
|
sent_country_counts[c.country_to] = sent_country_counts.get(c.country_to, 0) + 1
|
||||||
elif c.status == "received":
|
elif c.status == "received":
|
||||||
received_country_counts[c.country_to] = received_country_counts.get(c.country_to, 0) + 1
|
received_country_counts[c.country_to] = received_country_counts.get(c.country_to, 0) + 1
|
||||||
@@ -712,7 +712,7 @@ def postcard_list(
|
|||||||
postcards.sort(key=lambda pc: (_status_order.get(pc.status, 9), -(pc.send_time or pc.arrival_time or pc.receive_time or pc.created_at).timestamp()))
|
postcards.sort(key=lambda pc: (_status_order.get(pc.status, 9), -(pc.send_time or pc.arrival_time or pc.receive_time or pc.created_at).timestamp()))
|
||||||
|
|
||||||
# split into sent and received
|
# split into sent and received
|
||||||
sent = [pc for pc in postcards if pc.status in ("sent", "delivered")]
|
sent = [pc for pc in postcards if pc.status in ("pending", "sent", "delivered")]
|
||||||
received = [pc for pc in postcards if pc.status == "received"]
|
received = [pc for pc in postcards if pc.status == "received"]
|
||||||
|
|
||||||
return templates.TemplateResponse(
|
return templates.TemplateResponse(
|
||||||
@@ -722,11 +722,12 @@ def postcard_list(
|
|||||||
|
|
||||||
|
|
||||||
@router.get("/profiles/{profile_id}/postcards/new", response_class=HTMLResponse)
|
@router.get("/profiles/{profile_id}/postcards/new", response_class=HTMLResponse)
|
||||||
def postcard_new(request: Request, profile_id: int, user: User = Depends(get_current_user_web), db: Session = Depends(get_db)):
|
def postcard_new(request: Request, profile_id: int, create_type: str = Query("sent"), user: User = Depends(get_current_user_web), db: Session = Depends(get_db)):
|
||||||
profile = db.query(Profile).filter(Profile.id == profile_id, Profile.user_id == user.id).first()
|
profile = db.query(Profile).filter(Profile.id == profile_id, Profile.user_id == user.id).first()
|
||||||
if not profile:
|
if not profile:
|
||||||
return _redirect("/profiles")
|
return _redirect("/profiles")
|
||||||
return templates.TemplateResponse("postcard_form.html", {"request": request, "user": user, "profile": profile, "postcard": None})
|
today = datetime.now().strftime("%Y-%m-%d")
|
||||||
|
return templates.TemplateResponse("postcard_form.html", {"request": request, "user": user, "profile": profile, "postcard": None, "create_type": create_type, "today": today})
|
||||||
|
|
||||||
|
|
||||||
@router.post("/profiles/{profile_id}/postcards")
|
@router.post("/profiles/{profile_id}/postcards")
|
||||||
@@ -916,6 +917,28 @@ def mark_delivered(
|
|||||||
return _redirect(f"/postcards/{postcard_id}")
|
return _redirect(f"/postcards/{postcard_id}")
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/postcards/{postcard_id}/mark-sent")
|
||||||
|
def mark_sent(
|
||||||
|
postcard_id: int,
|
||||||
|
user: User = Depends(get_current_user_web),
|
||||||
|
db: Session = Depends(get_db),
|
||||||
|
):
|
||||||
|
"""Transition pending → sent, auto-fill send_time if empty."""
|
||||||
|
pc = (
|
||||||
|
db.query(Postcard)
|
||||||
|
.join(Profile)
|
||||||
|
.filter(Postcard.id == postcard_id, Profile.user_id == user.id)
|
||||||
|
.first()
|
||||||
|
)
|
||||||
|
if not pc:
|
||||||
|
return _redirect("/profiles")
|
||||||
|
pc.status = "sent"
|
||||||
|
if not pc.send_time:
|
||||||
|
pc.send_time = datetime.now()
|
||||||
|
db.commit()
|
||||||
|
return _redirect(f"/postcards/{postcard_id}")
|
||||||
|
|
||||||
|
|
||||||
# ---------- Image Upload ----------
|
# ---------- Image Upload ----------
|
||||||
|
|
||||||
@router.post("/postcards/{postcard_id}/image/{side}")
|
@router.post("/postcards/{postcard_id}/image/{side}")
|
||||||
@@ -1028,11 +1051,11 @@ def public_showcase(request: Request, username: str, db: Session = Depends(get_d
|
|||||||
Postcard.image_front.isnot(None), Postcard.image_front != ""
|
Postcard.image_front.isnot(None), Postcard.image_front != ""
|
||||||
).all()
|
).all()
|
||||||
if p.showcase_visible in ("sent", "both"):
|
if p.showcase_visible in ("sent", "both"):
|
||||||
sent_all.extend([c for c in cards if c.status in ("sent", "delivered")])
|
sent_all.extend([c for c in cards if c.status in ("pending", "sent", "delivered")])
|
||||||
if p.showcase_visible in ("received", "both"):
|
if p.showcase_visible in ("received", "both"):
|
||||||
received_all.extend([c for c in cards if c.status == "received"])
|
received_all.extend([c for c in cards if c.status == "received"])
|
||||||
if p.showcase_visible in ("sent", "both"):
|
if p.showcase_visible in ("sent", "both"):
|
||||||
p_sent = [c for c in cards if c.status in ("sent", "delivered")]
|
p_sent = [c for c in cards if c.status in ("pending", "sent", "delivered")]
|
||||||
else:
|
else:
|
||||||
p_sent = []
|
p_sent = []
|
||||||
if p.showcase_visible in ("received", "both"):
|
if p.showcase_visible in ("received", "both"):
|
||||||
|
|||||||
@@ -156,6 +156,7 @@ code { background: var(--bg); padding: .15rem .4rem; border-radius: 4px; font-si
|
|||||||
/* Badge */
|
/* Badge */
|
||||||
.badge { display: inline-block; padding: .1rem .4rem; border-radius: 999px; font-size: .65rem; font-weight: 600; color: #fff; background: var(--text-muted); white-space: nowrap; line-height: 1.4; align-self: center; }
|
.badge { display: inline-block; padding: .1rem .4rem; border-radius: 999px; font-size: .65rem; font-weight: 600; color: #fff; background: var(--text-muted); white-space: nowrap; line-height: 1.4; align-self: center; }
|
||||||
.badge-sent { background: var(--sent); }
|
.badge-sent { background: var(--sent); }
|
||||||
|
.badge-pending { background: var(--primary); }
|
||||||
.badge-delivered { background: var(--delivered); }
|
.badge-delivered { background: var(--delivered); }
|
||||||
.badge-received { background: var(--received); }
|
.badge-received { background: var(--received); }
|
||||||
|
|
||||||
|
|||||||
@@ -5,11 +5,15 @@
|
|||||||
<div class="detail-title-row">
|
<div class="detail-title-row">
|
||||||
<a href="/profiles/{{ postcard.profile_id }}/postcards" class="btn btn-back">←</a>
|
<a href="/profiles/{{ postcard.profile_id }}/postcards" class="btn btn-back">←</a>
|
||||||
<h1>{{ postcard.card_number }}</h1>
|
<h1>{{ postcard.card_number }}</h1>
|
||||||
<span class="badge badge-{{ postcard.status }}">{{ {'sent':'已寄出','delivered':'已送达','received':'已收到'}.get(postcard.status, postcard.status) if user.language=='zh' else {'sent':'Sent','delivered':'Delivered','received':'Received'}.get(postcard.status, postcard.status) }}</span>
|
<span class="badge badge-{{ postcard.status }}">{{ {'pending':'待寄出','sent':'已寄出','delivered':'已送达','received':'已收到'}.get(postcard.status, postcard.status) if user.language=='zh' else {'pending':'Pending','sent':'Sent','delivered':'Delivered','received':'Received'}.get(postcard.status, postcard.status) }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="detail-actions">
|
<div class="detail-actions">
|
||||||
<a href="/postcards/{{ postcard.id }}/edit" class="btn">{{ 'pc_detail.edit'|t(user.language) }}</a>
|
<a href="/postcards/{{ postcard.id }}/edit" class="btn">{{ 'pc_detail.edit'|t(user.language) }}</a>
|
||||||
{% if postcard.status == 'sent' %}
|
{% if postcard.status == 'pending' %}
|
||||||
|
<form method="post" action="/postcards/{{ postcard.id }}/mark-sent" class="inline">
|
||||||
|
<button type="submit" class="btn btn-primary" onclick="return confirm('{{ 'pc_detail.confirm_sent'|t(user.language) }}')">{{ 'pc_detail.mark_sent'|t(user.language) }}</button>
|
||||||
|
</form>
|
||||||
|
{% elif postcard.status == 'sent' %}
|
||||||
<form method="post" action="/postcards/{{ postcard.id }}/mark-delivered" class="inline">
|
<form method="post" action="/postcards/{{ postcard.id }}/mark-delivered" class="inline">
|
||||||
<button type="submit" class="btn btn-primary" onclick="return confirm('{{ 'pc_detail.confirm_delivered'|t(user.language) }}')">{{ 'pc_detail.mark_delivered'|t(user.language) }}</button>
|
<button type="submit" class="btn btn-primary" onclick="return confirm('{{ 'pc_detail.confirm_delivered'|t(user.language) }}')">{{ 'pc_detail.mark_delivered'|t(user.language) }}</button>
|
||||||
</form>
|
</form>
|
||||||
@@ -49,12 +53,14 @@
|
|||||||
<div class="detail-meta">
|
<div class="detail-meta">
|
||||||
<dl>
|
<dl>
|
||||||
<dt>{{ 'pc_detail.card_number'|t(user.language) }}</dt><dd>{{ postcard.card_number }}</dd>
|
<dt>{{ 'pc_detail.card_number'|t(user.language) }}</dt><dd>{{ postcard.card_number }}</dd>
|
||||||
<dt>{{ 'pc_detail.status'|t(user.language) }}</dt><dd>{{ {'sent':'已寄出','delivered':'已送达','received':'已收到'}.get(postcard.status, postcard.status) if user.language=='zh' else {'sent':'Sent','delivered':'Delivered','received':'Received'}.get(postcard.status, postcard.status) }}</dd>
|
<dt>{{ 'pc_detail.status'|t(user.language) }}</dt><dd>{{ {'pending':'待寄出','sent':'已寄出','delivered':'已送达','received':'已收到'}.get(postcard.status, postcard.status) if user.language=='zh' else {'pending':'Pending','sent':'Sent','delivered':'Delivered','received':'Received'}.get(postcard.status, postcard.status) }}</dd>
|
||||||
{% if postcard.status in ('sent','delivered') %}
|
{% if postcard.status in ('pending','sent','delivered') %}
|
||||||
<dt>{{ 'pc_detail.recipient'|t(user.language) }}</dt><dd>{{ postcard.recipient_name or '-' }}</dd>
|
<dt>{{ 'pc_detail.recipient'|t(user.language) }}</dt><dd>{{ postcard.recipient_name or '-' }}</dd>
|
||||||
<dt>{{ 'pc_detail.route'|t(user.language) }}</dt><dd>{% if postcard.country_from %}{{ postcard.country_from|flag }} {{ postcard.country_from }} → {% endif %}{{ postcard.country_to|flag }} {{ postcard.country_to or '-' }}</dd>
|
<dt>{{ 'pc_detail.route'|t(user.language) }}</dt><dd>{% if postcard.country_from %}{{ postcard.country_from|flag }} {{ postcard.country_from }} → {% endif %}{{ postcard.country_to|flag }} {{ postcard.country_to or '-' }}</dd>
|
||||||
<dt>{{ 'pc_detail.send_time'|t(user.language) }}</dt><dd>{{ postcard.send_time.strftime('%Y-%m-%d') if postcard.send_time else '-' }}</dd>
|
<dt>{{ 'pc_detail.send_time'|t(user.language) }}</dt><dd>{{ postcard.send_time.strftime('%Y-%m-%d') if postcard.send_time else '-' }}</dd>
|
||||||
|
{% if postcard.status == 'delivered' %}
|
||||||
<dt>{{ 'pc_detail.arrival_time'|t(user.language) }}</dt><dd>{{ postcard.arrival_time.strftime('%Y-%m-%d') if postcard.arrival_time else '-' }}</dd>
|
<dt>{{ 'pc_detail.arrival_time'|t(user.language) }}</dt><dd>{{ postcard.arrival_time.strftime('%Y-%m-%d') if postcard.arrival_time else '-' }}</dd>
|
||||||
|
{% endif %}
|
||||||
{% else %}
|
{% else %}
|
||||||
<dt>{{ 'pc_detail.sender'|t(user.language) }}</dt><dd>{{ postcard.sender_name or '-' }}</dd>
|
<dt>{{ 'pc_detail.sender'|t(user.language) }}</dt><dd>{{ postcard.sender_name or '-' }}</dd>
|
||||||
<dt>{{ 'pc_detail.route'|t(user.language) }}</dt><dd>{% if postcard.country_from %}{{ postcard.country_from|flag }} {{ postcard.country_from }} → {% endif %}{{ postcard.country_to|flag }} {{ postcard.country_to or '-' }}</dd>
|
<dt>{{ 'pc_detail.route'|t(user.language) }}</dt><dd>{% if postcard.country_from %}{{ postcard.country_from|flag }} {{ postcard.country_from }} → {% endif %}{{ postcard.country_to|flag }} {{ postcard.country_to or '-' }}</dd>
|
||||||
|
|||||||
@@ -1,44 +1,73 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% block title %}{{ ('pc_form.edit'|t(user.language)) if postcard else ('pc_form.new'|t(user.language)) }} - {{ 'brand'|t(user.language) }}{% endblock %}
|
{% block title %}{% if postcard %}{{ 'pc_form.edit'|t(user.language) }}{% else %}{% if create_type == 'received' %}{{ 'pc_form.new_received'|t(user.language) }}{% else %}{{ 'pc_form.new_sent'|t(user.language) }}{% endif %}{% endif %} - {{ 'brand'|t(user.language) }}{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="section-header">
|
<div class="section-header">
|
||||||
<h1>{{ ('pc_form.edit'|t(user.language)) if postcard else ('pc_form.new'|t(user.language)) }}</h1>
|
<h1>{% if postcard %}{{ 'pc_form.edit'|t(user.language) }}{% else %}{% if create_type == 'received' %}{{ 'pc_form.new_received'|t(user.language) }}{% else %}{{ 'pc_form.new_sent'|t(user.language) }}{% endif %}{% endif %}</h1>
|
||||||
</div>
|
</div>
|
||||||
<form method="post" action="{{ '/postcards/%d/edit' % postcard.id if postcard else '/profiles/%d/postcards' % profile.id }}" class="form-card">
|
|
||||||
<label>{{ 'pc_form.number'|t(user.language) }} *</label>
|
|
||||||
<input type="text" name="card_number" value="{{ postcard.card_number if postcard else '' }}" required>
|
|
||||||
|
|
||||||
|
{% if error %}<div class="form-card" style="border-left:3px solid var(--danger);margin-bottom:1rem;padding:.75rem 1rem;color:var(--danger)">{{ error }}</div>{% endif %}
|
||||||
|
|
||||||
|
{% set is_new = (postcard is none) %}
|
||||||
|
{% set is_edit = (postcard is not none) %}
|
||||||
|
{% set edit_status = postcard.status if is_edit else '' %}
|
||||||
|
{% set is_received_form = (create_type == 'received' and is_new) or (is_edit and edit_status == 'received') %}
|
||||||
|
|
||||||
|
<form method="post" action="{{ '/postcards/%d/edit' % postcard.id if is_edit else '/profiles/%d/postcards' % profile.id }}" class="form-card">
|
||||||
|
<label>{{ 'pc_form.number'|t(user.language) }} *</label>
|
||||||
|
<input type="text" name="card_number" value="{{ postcard.card_number if is_edit else '' }}" required>
|
||||||
|
|
||||||
|
{% if is_received_form %}
|
||||||
|
{# ── 收到表单:无状态选择 ── #}
|
||||||
|
<input type="hidden" name="status" value="received">
|
||||||
|
|
||||||
|
<label>{{ 'pc_form.sender'|t(user.language) }} *</label>
|
||||||
|
<input type="text" name="sender_name" value="{{ postcard.sender_name if is_edit else '' }}">
|
||||||
|
|
||||||
|
<label>{{ 'pc_form.receive_time'|t(user.language) }} *</label>
|
||||||
|
<input type="date" name="receive_time" value="{{ postcard.receive_time.strftime('%Y-%m-%d') if is_edit and postcard.receive_time else today }}">
|
||||||
|
|
||||||
|
<label>{{ 'pc_form.country'|t(user.language) }}</label>
|
||||||
|
<input type="text" name="country_to" value="{{ postcard.country_to if is_edit and postcard.country_to else (profile.country if profile else '') }}" maxlength="2" placeholder="JP" style="width:80px;text-transform:uppercase" oninput="this.value=this.value.toUpperCase()">
|
||||||
|
|
||||||
|
<label>{{ 'pc_form.country_from'|t(user.language) }}</label>
|
||||||
|
<input type="text" name="country_from" value="{{ postcard.country_from if is_edit and postcard.country_from else '' }}" maxlength="2" style="width:80px;text-transform:uppercase" oninput="this.value=this.value.toUpperCase()">
|
||||||
|
|
||||||
|
{% else %}
|
||||||
|
{# ── 寄出表单 ── #}
|
||||||
|
{% if is_new %}
|
||||||
|
{# 新建寄出:状态由日期自动决定,隐藏选择 #}
|
||||||
|
<input type="hidden" name="status" id="hidden-status" value="sent">
|
||||||
|
<label>{{ 'pc_form.status'|t(user.language) }}</label>
|
||||||
|
<div id="auto-status-display" class="form-card" style="padding:.4rem .75rem;margin-bottom:.25rem;font-size:.85rem;display:inline-block"></div>
|
||||||
|
{% else %}
|
||||||
|
{# 编辑时:显示状态选择 #}
|
||||||
<label>{{ 'pc_form.status'|t(user.language) }} *</label>
|
<label>{{ 'pc_form.status'|t(user.language) }} *</label>
|
||||||
<select name="status" id="status-select" onchange="toggleFields()" required>
|
<select name="status" id="status-select" onchange="toggleFields()" required>
|
||||||
<option value="sent" {% if postcard and postcard.status=='sent' %}selected{% endif %}>{{ 'status.sent'|t(user.language) }}</option>
|
<option value="pending" {% if edit_status=='pending' %}selected{% endif %}>{{ 'status.pending'|t(user.language) }}</option>
|
||||||
<option value="delivered" {% if postcard and postcard.status=='delivered' %}selected{% endif %}>{{ 'status.delivered'|t(user.language) }}</option>
|
<option value="sent" {% if edit_status=='sent' %}selected{% endif %}>{{ 'status.sent'|t(user.language) }}</option>
|
||||||
<option value="received" {% if postcard and postcard.status=='received' %}selected{% endif %}>{{ 'status.received'|t(user.language) }}</option>
|
<option value="delivered" {% if edit_status=='delivered' %}selected{% endif %}>{{ 'status.delivered'|t(user.language) }}</option>
|
||||||
</select>
|
</select>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<div id="fields-sent-delivered">
|
<div id="fields-sent-delivered">
|
||||||
<label>{{ 'pc_form.recipient'|t(user.language) }} *</label>
|
<label>{{ 'pc_form.recipient'|t(user.language) }} *</label>
|
||||||
<input type="text" name="recipient_name" id="input-recipient" value="{{ postcard.recipient_name if postcard else '' }}">
|
<input type="text" name="recipient_name" value="{{ postcard.recipient_name if is_edit else '' }}">
|
||||||
<label>{{ 'pc_form.send_time'|t(user.language) }} *</label>
|
<label>{{ 'pc_form.send_time'|t(user.language) }} *</label>
|
||||||
<input type="date" name="send_time" id="input-send-time" value="{{ postcard.send_time.strftime('%Y-%m-%d') if postcard and postcard.send_time else '' }}">
|
<input type="date" name="send_time" id="input-send-time" value="{{ postcard.send_time.strftime('%Y-%m-%d') if is_edit and postcard.send_time else (today if is_new else '') }}">
|
||||||
<label id="label-arrival">{{ 'pc_form.arrival_time'|t(user.language) }}</label>
|
<label id="label-arrival">{{ 'pc_form.arrival_time'|t(user.language) }}</label>
|
||||||
<input type="date" name="arrival_time" id="input-arrival" value="{{ postcard.arrival_time.strftime('%Y-%m-%d') if postcard and postcard.arrival_time else '' }}">
|
<input type="date" name="arrival_time" id="input-arrival" value="{{ postcard.arrival_time.strftime('%Y-%m-%d') if is_edit and postcard.arrival_time else '' }}" {% if is_new or (is_edit and edit_status=='sent') %}disabled{% endif %}>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="fields-received" style="display:none">
|
|
||||||
<label>{{ 'pc_form.sender'|t(user.language) }} *</label>
|
|
||||||
<input type="text" name="sender_name" id="input-sender" value="{{ postcard.sender_name if postcard else '' }}">
|
|
||||||
<label>{{ 'pc_form.receive_time'|t(user.language) }} *</label>
|
|
||||||
<input type="date" name="receive_time" id="input-receive" value="{{ postcard.receive_time.strftime('%Y-%m-%d') if postcard and postcard.receive_time else '' }}">
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<label>{{ 'pc_form.country_from'|t(user.language) }}</label>
|
<label>{{ 'pc_form.country_from'|t(user.language) }}</label>
|
||||||
<input type="text" name="country_from" id="input-country-from" value="{{ postcard.country_from if postcard and postcard.country_from else (profile.country if profile else '') }}" maxlength="2" style="width:80px;text-transform:uppercase" oninput="this.value=this.value.toUpperCase()">
|
<input type="text" name="country_from" value="{{ postcard.country_from if is_edit and postcard.country_from else (profile.country if profile else '') }}" maxlength="2" style="width:80px;text-transform:uppercase" oninput="this.value=this.value.toUpperCase()">
|
||||||
|
|
||||||
<label>{{ 'pc_form.country'|t(user.language) }} *</label>
|
<label>{{ 'pc_form.country'|t(user.language) }}</label>
|
||||||
<input type="text" name="country_to" id="input-country-to" value="{{ postcard.country_to if postcard else '' }}" maxlength="2" placeholder="JP" style="width:80px;text-transform:uppercase" oninput="this.value=this.value.toUpperCase()">
|
<input type="text" name="country_to" value="{{ postcard.country_to if is_edit and postcard.country_to else '' }}" maxlength="2" placeholder="JP" style="width:80px;text-transform:uppercase" oninput="this.value=this.value.toUpperCase()">
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<label>{{ 'pc_form.notes'|t(user.language) }}</label>
|
<label>{{ 'pc_form.notes'|t(user.language) }}</label>
|
||||||
<textarea name="notes" rows="3" placeholder="{{ 'pc_form.notes_ph'|t(user.language) }}">{{ postcard.notes if postcard else '' }}</textarea>
|
<textarea name="notes" rows="3" placeholder="{{ 'pc_form.notes_ph'|t(user.language) }}">{{ postcard.notes if is_edit else '' }}</textarea>
|
||||||
|
|
||||||
<div class="form-actions">
|
<div class="form-actions">
|
||||||
<button type="submit" class="btn btn-primary">{{ 'pc_form.save'|t(user.language) }}</button>
|
<button type="submit" class="btn btn-primary">{{ 'pc_form.save'|t(user.language) }}</button>
|
||||||
@@ -46,68 +75,62 @@
|
|||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<script>
|
<script>
|
||||||
function toggleFields() {
|
{% if is_new and not is_received_form %}
|
||||||
var s = document.getElementById('status-select').value;
|
// ── 新建寄出:根据日期自动判定状态 ──
|
||||||
var sd = document.getElementById('fields-sent-delivered');
|
(function() {
|
||||||
var rc = document.getElementById('fields-received');
|
|
||||||
var recipient = document.getElementById('input-recipient');
|
|
||||||
var country = document.getElementById('input-country');
|
|
||||||
var sendTime = document.getElementById('input-send-time');
|
var sendTime = document.getElementById('input-send-time');
|
||||||
|
var hiddenStatus = document.getElementById('hidden-status');
|
||||||
|
var display = document.getElementById('auto-status-display');
|
||||||
var arrival = document.getElementById('input-arrival');
|
var arrival = document.getElementById('input-arrival');
|
||||||
var labelArrival = document.getElementById('label-arrival');
|
var labelArrival = document.getElementById('label-arrival');
|
||||||
var sender = document.getElementById('input-sender');
|
var today = new Date().toISOString().slice(0, 10);
|
||||||
var receive = document.getElementById('input-receive');
|
// 如果没填日期,自动填今天
|
||||||
if (s === 'received') {
|
if (!sendTime.value) sendTime.value = today;
|
||||||
sd.style.display = 'none';
|
function updateAutoStatus() {
|
||||||
rc.style.display = '';
|
var v = sendTime.value;
|
||||||
recipient.required = false;
|
if (!v) { display.textContent = ''; return; }
|
||||||
country.required = false;
|
if (v > today) {
|
||||||
sendTime.required = false;
|
hiddenStatus.value = 'pending';
|
||||||
arrival.required = false;
|
display.textContent = '📋 {{ "status.pending"|t(user.language) }}';
|
||||||
arrival.disabled = false;
|
display.style.borderLeft = '3px solid var(--primary)';
|
||||||
sender.required = true;
|
} else {
|
||||||
receive.required = true;
|
hiddenStatus.value = 'sent';
|
||||||
} else if (s === 'sent') {
|
display.textContent = '📮 {{ "status.sent"|t(user.language) }}';
|
||||||
sd.style.display = '';
|
display.style.borderLeft = '3px solid var(--sent)';
|
||||||
rc.style.display = 'none';
|
}
|
||||||
recipient.required = true;
|
// arrived 仅在 delivered 时启用,sent/pending 时禁用
|
||||||
country.required = true;
|
if (hiddenStatus.value !== 'delivered') {
|
||||||
sendTime.required = true;
|
|
||||||
arrival.required = false;
|
|
||||||
arrival.disabled = true;
|
arrival.disabled = true;
|
||||||
arrival.value = '';
|
arrival.value = '';
|
||||||
labelArrival.textContent = '到达时间';
|
}
|
||||||
sender.required = false;
|
}
|
||||||
receive.required = false;
|
sendTime.addEventListener('change', updateAutoStatus);
|
||||||
} else {
|
sendTime.addEventListener('input', updateAutoStatus);
|
||||||
sd.style.display = '';
|
updateAutoStatus();
|
||||||
rc.style.display = 'none';
|
})();
|
||||||
recipient.required = true;
|
{% elif is_edit and not is_received_form %}
|
||||||
country.required = true;
|
// ── 编辑寄出:根据状态切换 arrival 可用性 ──
|
||||||
sendTime.required = true;
|
function toggleFields() {
|
||||||
|
var s = document.getElementById('status-select').value;
|
||||||
|
var arrival = document.getElementById('input-arrival');
|
||||||
|
var labelArrival = document.getElementById('label-arrival');
|
||||||
|
if (s === 'delivered') {
|
||||||
arrival.disabled = false;
|
arrival.disabled = false;
|
||||||
arrival.required = true;
|
labelArrival.textContent = '{{ "pc_form.arrival_time"|t(user.language) }} *';
|
||||||
labelArrival.textContent = '到达时间 *';
|
} else {
|
||||||
sender.required = false;
|
arrival.disabled = true;
|
||||||
receive.required = false;
|
arrival.value = '';
|
||||||
|
labelArrival.textContent = '{{ "pc_form.arrival_time"|t(user.language) }}';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
toggleFields();
|
toggleFields();
|
||||||
{% if not postcard %}
|
{% endif %}
|
||||||
// Auto-fill date fields with today on new postcards
|
{% if is_new and is_received_form %}
|
||||||
|
// ── 新建收到:自动填日期 ──
|
||||||
(function() {
|
(function() {
|
||||||
var today = new Date().toISOString().slice(0, 10);
|
var today = new Date().toISOString().slice(0, 10);
|
||||||
var sendTime = document.getElementById('input-send-time');
|
var rt = document.querySelector('input[name="receive_time"]');
|
||||||
var receiveTime = document.getElementById('input-receive');
|
if (rt && !rt.value) rt.value = today;
|
||||||
var statusSel = document.getElementById('status-select');
|
|
||||||
// Fill on page load
|
|
||||||
if (statusSel.value === 'sent' && !sendTime.value) sendTime.value = today;
|
|
||||||
if (statusSel.value === 'received' && !receiveTime.value) receiveTime.value = today;
|
|
||||||
// Fill on status change
|
|
||||||
statusSel.addEventListener('change', function() {
|
|
||||||
if (this.value === 'sent' && !sendTime.value) sendTime.value = today;
|
|
||||||
if (this.value === 'received' && !receiveTime.value) receiveTime.value = today;
|
|
||||||
});
|
|
||||||
})();
|
})();
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -3,7 +3,10 @@
|
|||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="section-header">
|
<div class="section-header">
|
||||||
<h1>{{ profile.nickname }} {{ 'pc_list.title'|t(user.language) }}</h1>
|
<h1>{{ profile.nickname }} {{ 'pc_list.title'|t(user.language) }}</h1>
|
||||||
<a href="/profiles/{{ profile.id }}/postcards/new" class="btn btn-primary">{{ 'pc_list.new'|t(user.language) }}</a>
|
<div style="display:flex;gap:.5rem;flex-wrap:wrap">
|
||||||
|
<a href="/profiles/{{ profile.id }}/postcards/new?create_type=sent" class="btn btn-primary">{{ 'pc_list.new_sent'|t(user.language) }}</a>
|
||||||
|
<a href="/profiles/{{ profile.id }}/postcards/new?create_type=received" class="btn">{{ 'pc_list.new_received'|t(user.language) }}</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div style="margin-bottom:1rem">
|
<div style="margin-bottom:1rem">
|
||||||
{% if profile.notes %}
|
{% if profile.notes %}
|
||||||
@@ -41,10 +44,10 @@
|
|||||||
<div class="postcard-row-info">
|
<div class="postcard-row-info">
|
||||||
<strong>{{ pc.card_number }}</strong>
|
<strong>{{ pc.card_number }}</strong>
|
||||||
<div class="postcard-row-detail">
|
<div class="postcard-row-detail">
|
||||||
<span>{% if pc.country_from %}{{ pc.country_from|flag }}→{% endif %}{{ pc.country_to|flag }} {{ pc.country_to or '-' }}</span>
|
<span>{% if pc.country_from %}{{ pc.country_from|flag }} {{ pc.country_from }} → {% endif %}{{ pc.country_to|flag }} {{ pc.country_to or '-' }}</span>
|
||||||
<span>→ {{ pc.recipient_name or '-' }}</span>
|
<span>→ {{ pc.recipient_name or '-' }}</span>
|
||||||
</div>
|
</div>
|
||||||
<span class="badge badge-{{ pc.status }}">{{ {'sent':'已寄出','delivered':'已送达'}.get(pc.status) if user.language=='zh' else {'sent':'Sent','delivered':'Delivered'}.get(pc.status) }}</span>
|
<span class="badge badge-{{ pc.status }}">{{ {'pending':'待寄出','sent':'已寄出','delivered':'已送达'}.get(pc.status) if user.language=='zh' else {'pending':'Pending','sent':'Sent','delivered':'Delivered'}.get(pc.status) }}</span>
|
||||||
<span class="postcard-row-date">
|
<span class="postcard-row-date">
|
||||||
{% if pc.send_time %}{{ pc.send_time.strftime('%Y-%m-%d') }}{% if user.language=='zh' %}寄出{% endif %}{% endif %}
|
{% if pc.send_time %}{{ pc.send_time.strftime('%Y-%m-%d') }}{% if user.language=='zh' %}寄出{% endif %}{% endif %}
|
||||||
{% if pc.arrival_time %}<br>{{ pc.arrival_time.strftime('%Y-%m-%d') }}{% if user.language=='zh' %}送达{% endif %}{% endif %}
|
{% if pc.arrival_time %}<br>{{ pc.arrival_time.strftime('%Y-%m-%d') }}{% if user.language=='zh' %}送达{% endif %}{% endif %}
|
||||||
@@ -52,7 +55,11 @@
|
|||||||
</div>
|
</div>
|
||||||
{% if pc.notes %}<span class="postcard-row-notes">{{ pc.notes[:50] }}{% if pc.notes|length > 50 %}…{% endif %}</span>{% endif %}
|
{% if pc.notes %}<span class="postcard-row-notes">{{ pc.notes[:50] }}{% if pc.notes|length > 50 %}…{% endif %}</span>{% endif %}
|
||||||
<div class="postcard-row-action">
|
<div class="postcard-row-action">
|
||||||
{% if pc.status == 'sent' %}
|
{% if pc.status == 'pending' %}
|
||||||
|
<form method="post" action="/postcards/{{ pc.id }}/mark-sent" onclick="event.preventDefault();event.stopPropagation();if(confirm('{{ 'pc_list.confirm_sent'|t(user.language) }}'))this.submit();">
|
||||||
|
<button type="submit" class="btn btn-sm btn-primary">{{ 'pc_list.mark_sent'|t(user.language) }}</button>
|
||||||
|
</form>
|
||||||
|
{% elif pc.status == 'sent' %}
|
||||||
<form method="post" action="/postcards/{{ pc.id }}/mark-delivered" onclick="event.preventDefault();event.stopPropagation();if(confirm('{{ 'pc_list.confirm_delivered'|t(user.language) }}'))this.submit();">
|
<form method="post" action="/postcards/{{ pc.id }}/mark-delivered" onclick="event.preventDefault();event.stopPropagation();if(confirm('{{ 'pc_list.confirm_delivered'|t(user.language) }}'))this.submit();">
|
||||||
<button type="submit" class="btn btn-sm btn-primary">{{ 'pc_list.mark_delivered'|t(user.language) }}</button>
|
<button type="submit" class="btn btn-sm btn-primary">{{ 'pc_list.mark_delivered'|t(user.language) }}</button>
|
||||||
</form>
|
</form>
|
||||||
@@ -81,7 +88,10 @@
|
|||||||
<div class="postcard-row-detail">
|
<div class="postcard-row-detail">
|
||||||
<span>← {{ pc.sender_name or '-' }}</span>
|
<span>← {{ pc.sender_name or '-' }}</span>
|
||||||
</div>
|
</div>
|
||||||
{% if pc.receive_time %}<span class="postcard-row-date">{{ pc.receive_time.strftime('%Y-%m-%d') }}{% if user.language=='zh' %}收到{% endif %}</span>{% endif %}
|
<span class="badge badge-received">{{ 'status.received'|t(user.language) }}</span>
|
||||||
|
<span class="postcard-row-date">
|
||||||
|
{% if pc.receive_time %}{{ pc.receive_time.strftime('%Y-%m-%d') }}{% if user.language=='zh' %}收到{% endif %}{% endif %}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{% if pc.notes %}<span class="postcard-row-notes">{{ pc.notes[:50] }}{% if pc.notes|length > 50 %}…{% endif %}</span>{% endif %}
|
{% if pc.notes %}<span class="postcard-row-notes">{{ pc.notes[:50] }}{% if pc.notes|length > 50 %}…{% endif %}</span>{% endif %}
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
Reference in New Issue
Block a user