feat: fall back to demo data when rPhotos/rInbox APIs return empty
Show sample data with an info banner instead of blank empty states when Immich isn't configured or no mailboxes exist. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
023a88352f
commit
161f7a10de
|
|
@ -17,6 +17,7 @@ class FolkInboxClient extends HTMLElement {
|
||||||
private filter: "all" | "open" | "snoozed" | "closed" = "all";
|
private filter: "all" | "open" | "snoozed" | "closed" = "all";
|
||||||
private helpOpen = false;
|
private helpOpen = false;
|
||||||
private composeOpen = false;
|
private composeOpen = false;
|
||||||
|
private showingSampleData = false;
|
||||||
private demoApprovals: any[] = [
|
private demoApprovals: any[] = [
|
||||||
{
|
{
|
||||||
id: "a1", subject: "Re: Q1 Budget approval for rSpace infrastructure", status: "PENDING",
|
id: "a1", subject: "Re: Q1 Budget approval for rSpace infrastructure", status: "PENDING",
|
||||||
|
|
@ -92,11 +93,17 @@ class FolkInboxClient extends HTMLElement {
|
||||||
this.mailboxes = data.mailboxes || [];
|
this.mailboxes = data.mailboxes || [];
|
||||||
}
|
}
|
||||||
} catch { /* ignore */ }
|
} catch { /* ignore */ }
|
||||||
|
|
||||||
|
if (this.mailboxes.length === 0) {
|
||||||
|
this.showingSampleData = true;
|
||||||
|
this.loadDemoData();
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async loadThreads(slug: string) {
|
private async loadThreads(slug: string) {
|
||||||
if (this.space === "demo") {
|
if (this.space === "demo" || this.showingSampleData) {
|
||||||
this.threads = this.demoThreads[slug] || [];
|
this.threads = this.demoThreads[slug] || [];
|
||||||
if (this.filter !== "all") this.threads = this.threads.filter(t => t.status === this.filter);
|
if (this.filter !== "all") this.threads = this.threads.filter(t => t.status === this.filter);
|
||||||
this.render();
|
this.render();
|
||||||
|
|
@ -115,7 +122,7 @@ class FolkInboxClient extends HTMLElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
private async loadThread(id: string) {
|
private async loadThread(id: string) {
|
||||||
if (this.space === "demo") {
|
if (this.space === "demo" || this.showingSampleData) {
|
||||||
const all = [...(this.demoThreads.team || []), ...(this.demoThreads.support || [])];
|
const all = [...(this.demoThreads.team || []), ...(this.demoThreads.support || [])];
|
||||||
this.currentThread = all.find(t => t.id === id) || null;
|
this.currentThread = all.find(t => t.id === id) || null;
|
||||||
if (this.currentThread) {
|
if (this.currentThread) {
|
||||||
|
|
@ -135,7 +142,7 @@ class FolkInboxClient extends HTMLElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
private async loadApprovals() {
|
private async loadApprovals() {
|
||||||
if (this.space === "demo") { this.approvals = this.demoApprovals; this.render(); return; }
|
if (this.space === "demo" || this.showingSampleData) { this.approvals = this.demoApprovals; this.render(); return; }
|
||||||
try {
|
try {
|
||||||
const base = window.location.pathname.replace(/\/$/, "");
|
const base = window.location.pathname.replace(/\/$/, "");
|
||||||
const q = this.currentMailbox ? `?mailbox=${this.currentMailbox.slug}` : "";
|
const q = this.currentMailbox ? `?mailbox=${this.currentMailbox.slug}` : "";
|
||||||
|
|
@ -306,6 +313,8 @@ class FolkInboxClient extends HTMLElement {
|
||||||
.help-step-text { font-size: 0.8rem; color: #cbd5e1; line-height: 1.5; }
|
.help-step-text { font-size: 0.8rem; color: #cbd5e1; line-height: 1.5; }
|
||||||
.help-cta { display: inline-block; padding: 0.6rem 1.5rem; background: linear-gradient(135deg, #6366f1, #0891b2); color: white; border-radius: 8px; text-decoration: none; font-size: 0.85rem; font-weight: 600; }
|
.help-cta { display: inline-block; padding: 0.6rem 1.5rem; background: linear-gradient(135deg, #6366f1, #0891b2); color: white; border-radius: 8px; text-decoration: none; font-size: 0.85rem; font-weight: 600; }
|
||||||
|
|
||||||
|
.sample-banner { padding: 8px 16px; background: rgba(99,102,241,0.12); border: 1px solid rgba(99,102,241,0.25); border-radius: 8px; color: #a5b4fc; font-size: 13px; text-align: center; margin-bottom: 12px; }
|
||||||
|
|
||||||
@media (max-width: 600px) {
|
@media (max-width: 600px) {
|
||||||
.mailbox-grid { grid-template-columns: 1fr; }
|
.mailbox-grid { grid-template-columns: 1fr; }
|
||||||
.thread-from { max-width: 100px; }
|
.thread-from { max-width: 100px; }
|
||||||
|
|
@ -315,6 +324,7 @@ class FolkInboxClient extends HTMLElement {
|
||||||
</style>
|
</style>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
${this.renderNav()}
|
${this.renderNav()}
|
||||||
|
${this.showingSampleData ? '<div class="sample-banner">Showing sample data — create a mailbox to get started</div>' : ''}
|
||||||
${this.renderView()}
|
${this.renderView()}
|
||||||
${this.helpOpen ? this.renderHelp() : ""}
|
${this.helpOpen ? this.renderHelp() : ""}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -699,7 +709,7 @@ class FolkInboxClient extends HTMLElement {
|
||||||
const submitCompose = this.shadow.querySelector("[data-action='submit-compose']");
|
const submitCompose = this.shadow.querySelector("[data-action='submit-compose']");
|
||||||
if (submitCompose) {
|
if (submitCompose) {
|
||||||
submitCompose.addEventListener("click", async () => {
|
submitCompose.addEventListener("click", async () => {
|
||||||
if (this.space === "demo") {
|
if (this.space === "demo" || this.showingSampleData) {
|
||||||
alert("Compose is disabled in demo mode. In a live space, this would submit the email for team approval.");
|
alert("Compose is disabled in demo mode. In a live space, this would submit the email for team approval.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -724,7 +734,7 @@ class FolkInboxClient extends HTMLElement {
|
||||||
// Approval actions
|
// Approval actions
|
||||||
this.shadow.querySelectorAll("[data-approve]").forEach((btn) => {
|
this.shadow.querySelectorAll("[data-approve]").forEach((btn) => {
|
||||||
btn.addEventListener("click", async () => {
|
btn.addEventListener("click", async () => {
|
||||||
if (this.space === "demo") { alert("Approvals are disabled in demo mode."); return; }
|
if (this.space === "demo" || this.showingSampleData) { alert("Approvals are disabled in demo mode."); return; }
|
||||||
const id = (btn as HTMLElement).dataset.approve!;
|
const id = (btn as HTMLElement).dataset.approve!;
|
||||||
const base = window.location.pathname.replace(/\/$/, "");
|
const base = window.location.pathname.replace(/\/$/, "");
|
||||||
await fetch(`${base}/api/approvals/${id}/sign`, {
|
await fetch(`${base}/api/approvals/${id}/sign`, {
|
||||||
|
|
@ -737,7 +747,7 @@ class FolkInboxClient extends HTMLElement {
|
||||||
});
|
});
|
||||||
this.shadow.querySelectorAll("[data-reject]").forEach((btn) => {
|
this.shadow.querySelectorAll("[data-reject]").forEach((btn) => {
|
||||||
btn.addEventListener("click", async () => {
|
btn.addEventListener("click", async () => {
|
||||||
if (this.space === "demo") { alert("Approvals are disabled in demo mode."); return; }
|
if (this.space === "demo" || this.showingSampleData) { alert("Approvals are disabled in demo mode."); return; }
|
||||||
const id = (btn as HTMLElement).dataset.reject!;
|
const id = (btn as HTMLElement).dataset.reject!;
|
||||||
const base = window.location.pathname.replace(/\/$/, "");
|
const base = window.location.pathname.replace(/\/$/, "");
|
||||||
await fetch(`${base}/api/approvals/${id}/sign`, {
|
await fetch(`${base}/api/approvals/${id}/sign`, {
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ class FolkPhotoGallery extends HTMLElement {
|
||||||
private lightboxAsset: Asset | null = null;
|
private lightboxAsset: Asset | null = null;
|
||||||
private loading = false;
|
private loading = false;
|
||||||
private error = "";
|
private error = "";
|
||||||
|
private showingSampleData = false;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
@ -108,7 +109,7 @@ class FolkPhotoGallery extends HTMLElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
private isDemo(): boolean {
|
private isDemo(): boolean {
|
||||||
return this.space === "demo";
|
return this.space === "demo" || this.showingSampleData;
|
||||||
}
|
}
|
||||||
|
|
||||||
private getApiBase(): string {
|
private getApiBase(): string {
|
||||||
|
|
@ -136,7 +137,14 @@ class FolkPhotoGallery extends HTMLElement {
|
||||||
this.albums = albumsData.albums || [];
|
this.albums = albumsData.albums || [];
|
||||||
this.assets = assetsData.assets || [];
|
this.assets = assetsData.assets || [];
|
||||||
} catch {
|
} catch {
|
||||||
this.error = "Could not connect to photo service. Make sure Immich is running.";
|
// Fall through to empty check below
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.albums.length === 0 && this.assets.length === 0) {
|
||||||
|
this.showingSampleData = true;
|
||||||
|
this.loading = false;
|
||||||
|
this.loadDemoData();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
|
|
@ -330,6 +338,8 @@ class FolkPhotoGallery extends HTMLElement {
|
||||||
text-shadow: 0 2px 6px rgba(0,0,0,0.5);
|
text-shadow: 0 2px 6px rgba(0,0,0,0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sample-banner { padding: 8px 16px; background: rgba(99,102,241,0.12); border: 1px solid rgba(99,102,241,0.25); border-radius: 8px; color: #a5b4fc; font-size: 13px; text-align: center; margin-bottom: 12px; }
|
||||||
|
|
||||||
@media (max-width: 480px) {
|
@media (max-width: 480px) {
|
||||||
.albums-grid { grid-template-columns: 1fr; }
|
.albums-grid { grid-template-columns: 1fr; }
|
||||||
.photo-grid { grid-template-columns: repeat(2, 1fr); }
|
.photo-grid { grid-template-columns: repeat(2, 1fr); }
|
||||||
|
|
@ -363,6 +373,8 @@ class FolkPhotoGallery extends HTMLElement {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
${this.showingSampleData ? '<div class="sample-banner">Showing sample data — connect Immich to see your photos</div>' : ''}
|
||||||
|
|
||||||
${!hasContent ? `
|
${!hasContent ? `
|
||||||
<div class="empty">
|
<div class="empty">
|
||||||
<div class="empty-icon">📸</div>
|
<div class="empty-icon">📸</div>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue