diff --git a/src/App.tsx b/src/App.tsx index c15edc4..d7ae6f4 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -115,8 +115,16 @@ const App: Component = () => { {(() => { const noteIdFromUrl = createMemo(() => { - const params = new URLSearchParams(new URL(location()).search); - return params.get('noteId'); + const loc = location(); + const url = new URL(loc); + // Try standard search params + let id = url.searchParams.get('noteId'); + if (!id && url.hash.includes('?')) { + // Try parsing from hash (for hash-based routing) + const hashSearch = url.hash.split('?')[1]; + id = new URLSearchParams(hashSearch).get('noteId'); + } + return id; }); console.log('[DEBUG App] Rendering embed/notes context. noteId trace:', noteIdFromUrl()); return { }} hideNavigation={!!noteIdFromUrl()} />; diff --git a/src/lib/pocketbase.ts b/src/lib/pocketbase.ts index 1134f6d..f4532a2 100644 --- a/src/lib/pocketbase.ts +++ b/src/lib/pocketbase.ts @@ -1,6 +1,7 @@ import PocketBase from 'pocketbase'; export const pb = new PocketBase('https://pocketbase.ccllc.pro'); +pb.autoCancellation(false); export const TASGRID_COLLECTION = 'TasGrid'; export const TAGS_COLLECTION = 'TasGrid_Tags'; diff --git a/src/store/index.ts b/src/store/index.ts index bab5ad0..15e5213 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -756,340 +756,347 @@ export const subscribeToRealtime = async () => { }); }; +let isInitializing = false; export const initStore = async () => { + if (isInitializing) return; if (!pb.authStore.isValid) return; - // 0. Synchronous hydration from localStorage for "instant" first paint - const key = getStorageKey(); - if (key) { - const saved = localStorage.getItem(key); - if (saved) { - try { - setStore(JSON.parse(saved)); - } catch (e) { - console.error("Failed to parse cached data", e); - } - } - } - - // Heartbeat - Ensure only one interval is running - if (heartbeatInterval) clearInterval(heartbeatInterval); - heartbeatInterval = setInterval(() => { - setNow(Date.now()); - checkRecurringTasks(); // Check for recurrence triggers (minutely, etc.) - }, 60000); - + isInitializing = true; try { - // 1. Fetch Preferences from User Profile (Weights, Scale, Tags) - try { - const userId = pb.authStore.model?.id; - if (userId) { - const user = await pb.collection('users').getOne(userId, { requestKey: null }); - const prefs = user.Taskgrid_pref || {}; - - setStore({ - pWeight: prefs.pWeight || 1.0, - uWeight: prefs.uWeight || 1.0, - matrixScaleDays: prefs.matrixScaleDays || 30, - prefId: userId, - subscribedBuckets: user.subscribedBuckets || [] - }); + // 0. Synchronous hydration from localStorage for "instant" first paint + const key = getStorageKey(); + if (key) { + const saved = localStorage.getItem(key); + if (saved) { + try { + setStore(JSON.parse(saved)); + } catch (e) { + console.error("Failed to parse cached data", e); + } } - } catch (prefErr) { - console.warn("Failed to load preferences:", prefErr); } - // 1.2 Fetch Buckets - try { - const buckets = await pb.collection(BUCKETS_COLLECTION).getFullList({ sort: 'name', requestKey: null }); - setStore("buckets", buckets.map((b: any) => ({ - id: b.id, - name: b.name, - color: b.color, - description: b.description - }))); - } catch (bucketErr) { - console.warn("Failed to load buckets (collection might not exist yet):", bucketErr); - } + // Heartbeat - Ensure only one interval is running + if (heartbeatInterval) clearInterval(heartbeatInterval); + heartbeatInterval = setInterval(() => { + setNow(Date.now()); + checkRecurringTasks(); // Check for recurrence triggers (minutely, etc.) + }, 60000); - // 1.3 Fetch Notes try { + // 1. Fetch Preferences from User Profile (Weights, Scale, Tags) + try { + const userId = pb.authStore.model?.id; + if (userId) { + const user = await pb.collection('users').getOne(userId, { requestKey: null }); + const prefs = user.Taskgrid_pref || {}; + + setStore({ + pWeight: prefs.pWeight || 1.0, + uWeight: prefs.uWeight || 1.0, + matrixScaleDays: prefs.matrixScaleDays || 30, + prefId: userId, + subscribedBuckets: user.subscribedBuckets || [] + }); + } + } catch (prefErr) { + console.warn("Failed to load preferences:", prefErr); + } + + // 1.2 Fetch Buckets + try { + const buckets = await pb.collection(BUCKETS_COLLECTION).getFullList({ sort: 'name', requestKey: null }); + setStore("buckets", buckets.map((b: any) => ({ + id: b.id, + name: b.name, + color: b.color, + description: b.description + }))); + } catch (bucketErr) { + console.warn("Failed to load buckets (collection might not exist yet):", bucketErr); + } + + // 1.3 Fetch Notes + try { + const currentUserId = pb.authStore.model?.id; + const notesRecords = await pb.collection(NOTES_COLLECTION).getFullList({ + filter: `isPrivate = false || user = "${currentUserId}"`, + sort: '-created', + requestKey: null + }); + setStore("notes", notesRecords.map(mapRecordToNote)); + } catch (notesErr) { + console.warn("Failed to load notes (collection might not exist yet):", notesErr); + } + + // 1.5. Fetch Tag Definitions + try { + const tagRecords = await pb.collection(TAGS_COLLECTION).getFullList({ + filter: `user = "${pb.authStore.model?.id}"`, + requestKey: null + }); + const loadedTags: TagDefinition[] = tagRecords.map(r => ({ + id: r.id, + name: r.name, + value: r.value, + color: r.color, + theme: r.theme, + isUser: r.isUser || false, + isBucket: r.isBucket || false + })); + setStore("tagDefinitions", reconcile(loadedTags)); + } catch (tagErr) { + console.warn("Failed to load tags:", tagErr); + } + + // 1.8 Fetch Share Rules (CRITICAL for filter construction) + // We need these loaded BEFORE we construct the focus filter + await loadShareRules(); + const currentUserId = pb.authStore.model?.id; - const notesRecords = await pb.collection(NOTES_COLLECTION).getFullList({ - filter: `isPrivate = false || user = "${currentUserId}"`, - sort: '-created', - requestKey: null - }); - setStore("notes", notesRecords.map(mapRecordToNote)); - } catch (notesErr) { - console.warn("Failed to load notes (collection might not exist yet):", notesErr); - } + if (!currentUserId) return; - // 1.5. Fetch Tag Definitions - try { - const tagRecords = await pb.collection(TAGS_COLLECTION).getFullList({ - filter: `user = "${pb.authStore.model?.id}"`, - requestKey: null - }); - const loadedTags: TagDefinition[] = tagRecords.map(r => ({ - id: r.id, - name: r.name, - value: r.value, - color: r.color, - theme: r.theme, - isUser: r.isUser || false, - isBucket: r.isBucket || false - })); - setStore("tagDefinitions", reconcile(loadedTags)); - } catch (tagErr) { - console.warn("Failed to load tags:", tagErr); - } + // --- PHASE 2: Multi-Stage Loading --- - // 1.8 Fetch Share Rules (CRITICAL for filter construction) - // We need these loaded BEFORE we construct the focus filter - await loadShareRules(); + // --- WINDOWED SYNC STRATEGY --- - const currentUserId = pb.authStore.model?.id; - if (!currentUserId) return; - - // --- PHASE 2: Multi-Stage Loading --- - - // --- WINDOWED SYNC STRATEGY --- - - // STAGE 1: Instant Focus (Top 30 Incomplete - User Only) - // Goal: < 100ms First Paint. Uses 'idx_user_focused'. - try { - const focusRecords = await pb.collection(TASGRID_COLLECTION).getList(1, 30, { - filter: `user = "${currentUserId}" && completed = false && deletedAt = ""`, - sort: '-priority,dueDate', - requestKey: 'initial_focus' - }); - - // Filter out templates client-side to ensure tasks with empty/null tags aren't excluded by DB - const focusTasks = focusRecords.items - .map(mapRecordToTask) - .filter(t => !t.tags?.includes("__template__")); - - setStore("tasks", focusTasks); - } catch (focusErr) { - console.warn("Stage 1 load failed:", focusErr); - } - - // STAGE 2 & 3: Background Sync - const backgroundSync = async () => { - - const bucketNames = store.subscribedBuckets - .map(id => store.buckets.find(b => b.id === id)?.name) - .filter(Boolean); - - // Sync System Tags & Share Rules FIRST to ensure Self-Rules exist for query construction - await syncSystemTagsAndRules(); - - // Add rules from shareRules store (which was just synced) - const incomingRules = store.shareRules.filter(r => r.targetUserId === currentUserId); - - // 2a. Fetch ALL Incomplete Tasks (Metadata Only) to ensure availability - // We must replicate the complex sharing logic here - const incompletePromises = [ - // Own Incomplete - pb.collection(TASGRID_COLLECTION).getFullList({ - filter: `user = "${currentUserId}" && completed = false`, - sort: '-created', - fields: LIST_FIELDS, - requestKey: null - }), - // Shared With Individual Incomplete - pb.collection(TASGRID_COLLECTION).getFullList({ - filter: `sharedWith ~ "${currentUserId}" && completed = false`, - sort: '-created', - fields: LIST_FIELDS, - requestKey: null - }).catch(() => []), - // Bucket Incomplete - (bucketNames.length > 0) ? pb.collection(TASGRID_COLLECTION).getFullList({ - filter: `(${bucketNames.map(name => `tags ~ "${name}"`).join(' || ')}) && completed = false`, - sort: '-created', - fields: LIST_FIELDS, - requestKey: null - }).catch(() => []) : Promise.resolve([]) - ]; - - // Share Rules Incomplete - if (incomingRules.length > 0) { - // Separate "Global Rules" (Self-Rules) from "Specific User Rules" - const globalTagRules: string[] = []; - const specificUserRules = new Map(); - - incomingRules.forEach((rule: any) => { - if (rule.ownerId === rule.targetUserId && rule.type === 'tag' && rule.tagName) { - globalTagRules.push(rule.tagName); - } else { - const existing = specificUserRules.get(rule.ownerId) || []; - existing.push({ type: rule.type, tagName: rule.tagName }); - specificUserRules.set(rule.ownerId, existing); - } + // STAGE 1: Instant Focus (Top 30 Incomplete - User Only) + // Goal: < 100ms First Paint. Uses 'idx_user_focused'. + try { + const focusRecords = await pb.collection(TASGRID_COLLECTION).getList(1, 30, { + filter: `user = "${currentUserId}" && completed = false && deletedAt = ""`, + sort: '-priority,dueDate', + requestKey: 'initial_focus' }); - // 1. Fetch Global Rule Tasks (from ANY user) - // 1. Fetch Global Rule Tasks (from ANY user) - if (globalTagRules.length > 0) { - // We want tasks that have ANY of these tags, AND are not completed - // filter: (tags ~ "A" || tags ~ "B") && completed = false - const tagFilter = globalTagRules.map(t => `tags ~ "${t}"`).join(' || '); - incompletePromises.push(pb.collection(TASGRID_COLLECTION).getFullList({ - filter: `(${tagFilter}) && completed = false`, + // Filter out templates client-side to ensure tasks with empty/null tags aren't excluded by DB + const focusTasks = focusRecords.items + .map(mapRecordToTask) + .filter(t => !t.tags?.includes("__template__")); + + setStore("tasks", focusTasks); + } catch (focusErr) { + console.warn("Stage 1 load failed:", focusErr); + } + + // STAGE 2 & 3: Background Sync + const backgroundSync = async () => { + + const bucketNames = store.subscribedBuckets + .map(id => store.buckets.find(b => b.id === id)?.name) + .filter(Boolean); + + // Sync System Tags & Share Rules FIRST to ensure Self-Rules exist for query construction + await syncSystemTagsAndRules(); + + // Add rules from shareRules store (which was just synced) + const incomingRules = store.shareRules.filter(r => r.targetUserId === currentUserId); + + // 2a. Fetch ALL Incomplete Tasks (Metadata Only) to ensure availability + // We must replicate the complex sharing logic here + const incompletePromises = [ + // Own Incomplete + pb.collection(TASGRID_COLLECTION).getFullList({ + filter: `user = "${currentUserId}" && completed = false`, sort: '-created', fields: LIST_FIELDS, requestKey: null - }).catch(() => [])); + }), + // Shared With Individual Incomplete + pb.collection(TASGRID_COLLECTION).getFullList({ + filter: `sharedWith ~ "${currentUserId}" && completed = false`, + sort: '-created', + fields: LIST_FIELDS, + requestKey: null + }).catch(() => []), + // Bucket Incomplete + (bucketNames.length > 0) ? pb.collection(TASGRID_COLLECTION).getFullList({ + filter: `(${bucketNames.map(name => `tags ~ "${name}"`).join(' || ')}) && completed = false`, + sort: '-created', + fields: LIST_FIELDS, + requestKey: null + }).catch(() => []) : Promise.resolve([]) + ]; + + // Share Rules Incomplete + if (incomingRules.length > 0) { + // Separate "Global Rules" (Self-Rules) from "Specific User Rules" + const globalTagRules: string[] = []; + const specificUserRules = new Map(); + + incomingRules.forEach((rule: any) => { + if (rule.ownerId === rule.targetUserId && rule.type === 'tag' && rule.tagName) { + globalTagRules.push(rule.tagName); + } else { + const existing = specificUserRules.get(rule.ownerId) || []; + existing.push({ type: rule.type, tagName: rule.tagName }); + specificUserRules.set(rule.ownerId, existing); + } + }); + + // 1. Fetch Global Rule Tasks (from ANY user) + // 1. Fetch Global Rule Tasks (from ANY user) + if (globalTagRules.length > 0) { + // We want tasks that have ANY of these tags, AND are not completed + // filter: (tags ~ "A" || tags ~ "B") && completed = false + const tagFilter = globalTagRules.map(t => `tags ~ "${t}"`).join(' || '); + incompletePromises.push(pb.collection(TASGRID_COLLECTION).getFullList({ + filter: `(${tagFilter}) && completed = false`, + sort: '-created', + fields: LIST_FIELDS, + requestKey: null + }).catch(() => [])); + } + + // 2. Fetch Specific User Rule Tasks (from specific owner) + Array.from(specificUserRules.entries()).forEach(([ownerId, rules]) => { + const hasAllRule = rules.some(r => r.type === 'all'); + const tagRules = rules.filter(r => r.type === 'tag').map(r => r.tagName).filter(Boolean); + + let filter = `user = "${ownerId}" && completed = false`; + + if (hasAllRule) { + // Get everything from this user + } else if (tagRules.length > 0) { + // Get specific tags from this user + filter += ` && (${tagRules.map(t => `tags ~ "${t}"`).join(' || ')})`; + } else { + return; // No valid rules for this user + } + + incompletePromises.push(pb.collection(TASGRID_COLLECTION).getFullList({ + filter, + sort: '-created', + fields: LIST_FIELDS, + requestKey: null + }).catch(() => [])); + }); } - // 2. Fetch Specific User Rule Tasks (from specific owner) - Array.from(specificUserRules.entries()).forEach(([ownerId, rules]) => { - const hasAllRule = rules.some(r => r.type === 'all'); - const tagRules = rules.filter(r => r.type === 'tag').map(r => r.tagName).filter(Boolean); + const stage2Results = await Promise.all(incompletePromises); + const allIncomplete = stage2Results.flat(); - let filter = `user = "${ownerId}" && completed = false`; - - if (hasAllRule) { - // Get everything from this user - } else if (tagRules.length > 0) { - // Get specific tags from this user - filter += ` && (${tagRules.map(t => `tags ~ "${t}"`).join(' || ')})`; - } else { - return; // No valid rules for this user - } - - incompletePromises.push(pb.collection(TASGRID_COLLECTION).getFullList({ - filter, - sort: '-created', - fields: LIST_FIELDS, - requestKey: null - }).catch(() => [])); + setStore("tasks", (currentTasks) => { + const taskMap = new Map(currentTasks.map(t => [t.id, t])); + allIncomplete.forEach((r: any) => { + if (r.tags?.includes("__template__")) return; + const existing = taskMap.get(r.id); + // If existing has content, keep it. Otherwise use new record (which likely has no content yet) + const newTask = mapRecordToTask(r); + if (existing && existing.content) { + newTask.content = existing.content; + } + taskMap.set(r.id, newTask); + }); + return Array.from(taskMap.values()); }); - } - const stage2Results = await Promise.all(incompletePromises); - const allIncomplete = stage2Results.flat(); + // 2b. Content Backfill for Incomplete Tasks + // Fetch content for tasks that don't have it yet, in chunks + const tasksNeedingContent = store.tasks.filter(t => !t.completed && t.content === undefined).map(t => t.id); + if (tasksNeedingContent.length > 0) { + // Fetch in chunks of 20 to avoid URL length limits + const chunkSize = 20; + for (let i = 0; i < tasksNeedingContent.length; i += chunkSize) { + const ids = tasksNeedingContent.slice(i, i + chunkSize); + // We fetch just ID and Content + try { + const contentRecords = await pb.collection(TASGRID_COLLECTION).getFullList({ + filter: ids.map(id => `id="${id}"`).join(' || '), + fields: 'id,content', + requestKey: null + }); - setStore("tasks", (currentTasks) => { - const taskMap = new Map(currentTasks.map(t => [t.id, t])); - allIncomplete.forEach((r: any) => { - if (r.tags?.includes("__template__")) return; - const existing = taskMap.get(r.id); - // If existing has content, keep it. Otherwise use new record (which likely has no content yet) - const newTask = mapRecordToTask(r); - if (existing && existing.content) { - newTask.content = existing.content; + setStore("tasks", (tasks) => tasks.map(t => { + const match = contentRecords.find((r: any) => r.id === t.id); + if (match) return { ...t, content: match.content }; + return t; + })); + } catch (e) { console.warn("Backfill chunk failed", e); } } - taskMap.set(r.id, newTask); - }); - return Array.from(taskMap.values()); - }); + } - // 2b. Content Backfill for Incomplete Tasks - // Fetch content for tasks that don't have it yet, in chunks - const tasksNeedingContent = store.tasks.filter(t => !t.completed && t.content === undefined).map(t => t.id); - if (tasksNeedingContent.length > 0) { - // Fetch in chunks of 20 to avoid URL length limits - const chunkSize = 20; - for (let i = 0; i < tasksNeedingContent.length; i += chunkSize) { - const ids = tasksNeedingContent.slice(i, i + chunkSize); - // We fetch just ID and Content + // STAGE 3: Recent History (Cap 150) + const currentCount = store.tasks.length; + const remainingSlots = 150 - currentCount; + + if (remainingSlots > 0) { try { - const contentRecords = await pb.collection(TASGRID_COLLECTION).getFullList({ - filter: ids.map(id => `id="${id}"`).join(' || '), - fields: 'id,content', + const recentCompleted = await pb.collection(TASGRID_COLLECTION).getList(1, remainingSlots, { + filter: `user = "${currentUserId}" && completed = true`, + sort: '-updated', + fields: LIST_FIELDS, requestKey: null }); - setStore("tasks", (tasks) => tasks.map(t => { - const match = contentRecords.find((r: any) => r.id === t.id); - if (match) return { ...t, content: match.content }; - return t; - })); - } catch (e) { console.warn("Backfill chunk failed", e); } - } - } - - // STAGE 3: Recent History (Cap 150) - const currentCount = store.tasks.length; - const remainingSlots = 150 - currentCount; - - if (remainingSlots > 0) { - try { - const recentCompleted = await pb.collection(TASGRID_COLLECTION).getList(1, remainingSlots, { - filter: `user = "${currentUserId}" && completed = true`, - sort: '-updated', - fields: LIST_FIELDS, - requestKey: null - }); - - setStore("tasks", (prev) => { - const newTasks = [...prev]; - recentCompleted.items.forEach((r: any) => { - if (!newTasks.some(t => t.id === r.id)) { - newTasks.push(mapRecordToTask(r)); - } + setStore("tasks", (prev) => { + const newTasks = [...prev]; + recentCompleted.items.forEach((r: any) => { + if (!newTasks.some(t => t.id === r.id)) { + newTasks.push(mapRecordToTask(r)); + } + }); + return newTasks; }); - return newTasks; - }); - } catch (e) { - console.warn("Stage 3 load failed", e); + } catch (e) { + console.warn("Stage 3 load failed", e); + } } - } - checkRecurringTasks(); + checkRecurringTasks(); - // Reconstructive migration - const foundTagNames = new Set(); - store.tasks.forEach(t => t.tags?.forEach(tag => foundTagNames.add(tag))); - for (const tagName of foundTagNames) { - if (tagName === "__template__") continue; - if (!store.tagDefinitions.find(d => d.name === tagName)) { - await upsertTagDefinition(tagName, 5, undefined, document.documentElement.classList.contains("dark") ? "dark" : "light"); + // Reconstructive migration + const foundTagNames = new Set(); + store.tasks.forEach(t => t.tags?.forEach(tag => foundTagNames.add(tag))); + for (const tagName of foundTagNames) { + if (tagName === "__template__") continue; + if (!store.tagDefinitions.find(d => d.name === tagName)) { + await upsertTagDefinition(tagName, 5, undefined, document.documentElement.classList.contains("dark") ? "dark" : "light"); + } } + + // Sync System Tags & Share Rules (Moved to top of backgroundSync to ensure rules exist before use) + // await syncSystemTagsAndRules(); + }; + + // Fire off background sync after a short delay + setTimeout(backgroundSync, 100); + + // Separate Templates load (less critical) + try { + const templates = await pb.collection(TASGRID_COLLECTION).getFullList({ + filter: `user = "${currentUserId}" && tags ~ "__template__"`, + requestKey: 'templates_load' + }); + const loadedTemplates = templates.map(r => { + let meta: any = {}; + try { meta = JSON.parse(r.content || "{}"); } catch (e) { } + return { + id: r.id, + name: r.title, + title: meta.title || "", + priority: r.priority, + urgency: meta.urgency || 5, + tags: meta.tags || [], + content: meta.content || "" + }; + }); + setStore("templates", loadedTemplates); + } catch (err) { } + + // Start real-time subscription + await subscribeToRealtime(); + + // 2.8 Fetch Filter Templates + await loadFilterTemplates(); + } catch (err) { + if (store.tasks.length === 0) { + console.error("Failed to load data:", err); + toast.error("Failed to sync with server."); } - - // Sync System Tags & Share Rules (Moved to top of backgroundSync to ensure rules exist before use) - // await syncSystemTagsAndRules(); - }; - - // Fire off background sync after a short delay - setTimeout(backgroundSync, 100); - - // Separate Templates load (less critical) - try { - const templates = await pb.collection(TASGRID_COLLECTION).getFullList({ - filter: `user = "${currentUserId}" && tags ~ "__template__"`, - requestKey: 'templates_load' - }); - const loadedTemplates = templates.map(r => { - let meta: any = {}; - try { meta = JSON.parse(r.content || "{}"); } catch (e) { } - return { - id: r.id, - name: r.title, - title: meta.title || "", - priority: r.priority, - urgency: meta.urgency || 5, - tags: meta.tags || [], - content: meta.content || "" - }; - }); - setStore("templates", loadedTemplates); - } catch (err) { } - - // Start real-time subscription - await subscribeToRealtime(); - - // 2.8 Fetch Filter Templates - await loadFilterTemplates(); - } catch (err) { - if (store.tasks.length === 0) { - console.error("Failed to load data:", err); - toast.error("Failed to sync with server."); } + } finally { + isInitializing = false; } }; diff --git a/test-embed.html b/test-embed.html index 5644486..5fe3a41 100644 --- a/test-embed.html +++ b/test-embed.html @@ -232,7 +232,8 @@ function syncAuth(iframe) { Notes Embed /embed/notes - +
@@ -241,7 +242,7 @@ function syncAuth(iframe) { Quick Add Embed /embed/quick-add
- @@ -253,8 +254,13 @@ function syncAuth(iframe) { function syncAuthOnLoad(iframe) { console.log(`Iframe ${iframe.id} loaded, checking auth sync...`); if (pb.authStore.isValid) { - console.log(`Syncing existing auth to ${iframe.id}`); - sendAuthToIframes(); + console.log(`Syncing existing auth to ${iframe.id} ONLY`); + const payload = { + type: "TASGRID_AUTH", + token: pb.authStore.token, + user: pb.authStore.model + }; + iframe.contentWindow.postMessage(payload, "*"); } } @@ -262,9 +268,9 @@ function syncAuth(iframe) { const id = document.getElementById('note-id-input').value.trim(); const iframe = document.getElementById('notes-iframe'); if (id) { - iframe.src = `http://localhost:4000/embed/notes?noteId=${id}`; + iframe.src = `https://tasgrid.ccllc.pro/embed/notes?noteId=${id}`; } else { - iframe.src = `http://localhost:4000/embed/notes`; + iframe.src = `https://tasgrid.ccllc.pro/embed/notes`; } }