- Per-app skeleton completion: property/client/setting now have services/,
tasks.py, views.py, urls.py, serializers.py, templates/<app>/, tests/
- admin.py added to all 10 apps (per spec §2.108 / §17.3)
- Top-level templates/: base.html, layouts/{app,auth}.html, components/
{topbar,sidebar,pagination,toast,modal,empty-state}.html, errors/
{403,404,500}.html
- static/: css/main.css (Tailwind entry), js/main.js (HTMX toast +
CSRF wiring per §7.4), vendor/.gitkeep
- tailwind.config.js: Primary teal + neutral slate + semantic colors,
Inter font stack, z-60/z-70, shadow-xs, slide-in-right animation per
UI_SYSTEM §2.7/§10.1
- package.json: tailwindcss-only build/watch
- Dockerfile + docker-compose.yml (6 services: web/db/redis/celery/
celery-beat/tailwind) + docker-compose.prod.yml + Makefile
- tests/ root: conftest.py with TenantClient fixture per §720,
integration/{property,client,release}/, e2e/, schemathesis skeleton
- Removed empty apps/tenant/models/ (tenant uses models.py per §17.1)
Validated: manage.py check passes; tree matches spec §2 exactly.
33 lines
1.0 KiB
JavaScript
33 lines
1.0 KiB
JavaScript
document.body.addEventListener("htmx:afterRequest", function (event) {
|
|
var trigger = event.detail.xhr.getResponseHeader("HX-Trigger");
|
|
if (!trigger) return;
|
|
|
|
try {
|
|
var payload = JSON.parse(trigger);
|
|
var toast = payload["fonrey:toast"];
|
|
if (!toast) return;
|
|
|
|
var container = document.getElementById("toast-container");
|
|
if (!container) return;
|
|
|
|
var node = document.createElement("div");
|
|
node.className =
|
|
"bg-white border border-neutral-200 rounded-lg shadow-xs px-4 py-3 min-w-[280px]";
|
|
node.setAttribute("data-toast-type", toast.type || "info");
|
|
node.textContent = toast.message || "";
|
|
container.appendChild(node);
|
|
|
|
setTimeout(function () {
|
|
node.remove();
|
|
}, 4000);
|
|
} catch (e) {
|
|
}
|
|
});
|
|
|
|
document.body.addEventListener("htmx:configRequest", function (event) {
|
|
var meta = document.querySelector('meta[name="csrf-token"]');
|
|
if (meta) {
|
|
event.detail.headers["X-CSRFToken"] = meta.getAttribute("content");
|
|
}
|
|
});
|