feat: public sharing links for households
CI / test-and-package (push) Successful in 40s

Add hard-to-guess public URLs (/public/<token>) that show a read-only
meal status dashboard for each household. Only people with the link can
see the data — no login required.

- models.py: public_token + public_enabled columns on households,
  get_household_by_public_token(), generate_public_token(),
  set_public_enabled()
- meals.py: /public/<token> route, /admin/public-link for
  generate/disable/enable/regenerate
- templates/public.html: clean read-only public dashboard
- templates/public-not-found.html: 404 for invalid/disabled links
- templates/dashboard.html: admin UI with copy/regenerate/disable
- static/style.css: public page and URL box styles
- i18n.py: 18 new translation keys (en/fr/de)
This commit is contained in:
agentbox
2026-08-01 10:12:21 +00:00
parent 2a3101071b
commit ae95841b5b
7 changed files with 330 additions and 0 deletions
+39
View File
@@ -114,6 +114,27 @@
</form>
{% if is_admin %}
<form method="POST" action="{{ url_for('meals.admin_public_link') }}" class="manage-form">
<h4>🔗 {{ t('Public Sharing Link') }}</h4>
<p class="hint">{{ t('Share a read-only view of your household\'s meal status. The link is hard to guess — only people with it can see the data.') }}</p>
{% if household.public_token and household.public_enabled %}
<div class="public-url-box">
<code>{{ request.host_url }}public/{{ household.public_token }}</code>
<button type="button" class="btn btn-sm" onclick="copyPublicUrl(this)">{{ t('Copy') }}</button>
</div>
<div class="btn-row" style="margin-top: 8px;">
<button type="submit" name="action" value="generate" class="btn btn-sm">{{ t('Regenerate') }}</button>
<button type="submit" name="action" value="disable" class="btn btn-sm btn-danger">{{ t('Disable') }}</button>
</div>
{% elif household.public_token and not household.public_enabled %}
<p class="hint">{{ t('Public link is currently disabled.') }}</p>
<button type="submit" name="action" value="enable" class="btn btn-sm">{{ t('Enable') }}</button>
{% else %}
<p class="hint">{{ t('No public link yet. Generate one to share.') }}</p>
<button type="submit" name="action" value="generate" class="btn btn-sm">{{ t('Generate Link') }}</button>
{% endif %}
</form>
<form method="POST" action="{{ url_for('meals.admin_set_timezone') }}" class="manage-form">
<h4>🕐 {{ t('Household Timezone') }}</h4>
<p class="hint">{{ t('Used to send reminders at 10:00 (lunch) and 16:00 (dinner) local time.') }}</p>
@@ -146,4 +167,22 @@
</details>
</div>
<script>
function copyPublicUrl(btn) {
var code = btn.parentElement.querySelector('code');
if (!code) return;
navigator.clipboard.writeText(code.textContent).then(function() {
var orig = btn.textContent;
btn.textContent = 'Copied!';
btn.disabled = true;
setTimeout(function() { btn.textContent = orig; btn.disabled = false; }, 2000);
}).catch(function() {
var range = document.createRange();
range.selectNode(code);
window.getSelection().removeAllRanges();
window.getSelection().addRange(range);
});
}
</script>
{% endblock %}