working api create note, view note, view notes, create task with ui
This commit is contained in:
+25
-10
@@ -1,4 +1,4 @@
|
|||||||
import { type Component, createSignal, Show, onMount, onCleanup, lazy, Suspense } from 'solid-js';
|
import { type Component, createSignal, onMount, onCleanup, createMemo, lazy, Suspense, Show } from 'solid-js';
|
||||||
import { Layout } from './components/Layout';
|
import { Layout } from './components/Layout';
|
||||||
import { EmbedLayout } from './components/EmbedLayout';
|
import { EmbedLayout } from './components/EmbedLayout';
|
||||||
import { EmbedAuthWrapper } from './components/EmbedAuthWrapper';
|
import { EmbedAuthWrapper } from './components/EmbedAuthWrapper';
|
||||||
@@ -23,8 +23,13 @@ const App: Component = () => {
|
|||||||
// Basic routing state
|
// Basic routing state
|
||||||
const [currentView, setCurrentView] = createSignal("critical");
|
const [currentView, setCurrentView] = createSignal("critical");
|
||||||
const [isAuthenticated, setIsAuthenticated] = createSignal(pb.authStore.isValid);
|
const [isAuthenticated, setIsAuthenticated] = createSignal(pb.authStore.isValid);
|
||||||
|
const [location, setLocation] = createSignal(window.location.href);
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
|
const handleLocChange = () => setLocation(window.location.href);
|
||||||
|
window.addEventListener('popstate', handleLocChange);
|
||||||
|
window.addEventListener('hashchange', handleLocChange);
|
||||||
|
|
||||||
const removeListener = pb.authStore.onChange((token) => {
|
const removeListener = pb.authStore.onChange((token) => {
|
||||||
console.log('[DEBUG] pb.authStore.onChange fired, token present:', !!token);
|
console.log('[DEBUG] pb.authStore.onChange fired, token present:', !!token);
|
||||||
setIsAuthenticated(!!token);
|
setIsAuthenticated(!!token);
|
||||||
@@ -33,7 +38,11 @@ const App: Component = () => {
|
|||||||
}
|
}
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
onCleanup(() => removeListener());
|
onCleanup(() => {
|
||||||
|
removeListener();
|
||||||
|
window.removeEventListener('popstate', handleLocChange);
|
||||||
|
window.removeEventListener('hashchange', handleLocChange);
|
||||||
|
});
|
||||||
|
|
||||||
// Background preload of other views when idle
|
// Background preload of other views when idle
|
||||||
const idleCallback = (window as any).requestIdleCallback || ((cb: any) => setTimeout(cb, 1000));
|
const idleCallback = (window as any).requestIdleCallback || ((cb: any) => setTimeout(cb, 1000));
|
||||||
@@ -59,15 +68,19 @@ const App: Component = () => {
|
|||||||
|
|
||||||
// Determine if we are in an embed route (check both path and hash for compatibility)
|
// Determine if we are in an embed route (check both path and hash for compatibility)
|
||||||
const isEmbed = () => {
|
const isEmbed = () => {
|
||||||
const path = window.location.pathname;
|
const loc = location(); // Reactive dependency
|
||||||
const hash = window.location.hash;
|
const url = new URL(loc);
|
||||||
|
const path = url.pathname;
|
||||||
|
const hash = url.hash;
|
||||||
const res = path.startsWith('/embed/') || hash.startsWith('#/embed/');
|
const res = path.startsWith('/embed/') || hash.startsWith('#/embed/');
|
||||||
console.log('[DEBUG] isEmbed check:', { res, path, hash, href: window.location.href });
|
console.log('[DEBUG] isEmbed check:', { res, path, hash, href: loc });
|
||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getEmbedView = () => {
|
const getEmbedView = () => {
|
||||||
const fullPath = window.location.pathname + window.location.hash;
|
const loc = location(); // Reactive dependency
|
||||||
|
const url = new URL(loc);
|
||||||
|
const fullPath = url.pathname + url.hash;
|
||||||
let view = null;
|
let view = null;
|
||||||
if (fullPath.includes('/notes')) view = 'embed_notes';
|
if (fullPath.includes('/notes')) view = 'embed_notes';
|
||||||
else if (fullPath.includes('/quick-add')) view = 'embed_quick_add';
|
else if (fullPath.includes('/quick-add')) view = 'embed_quick_add';
|
||||||
@@ -101,10 +114,12 @@ const App: Component = () => {
|
|||||||
<Suspense fallback={<div class="flex items-center justify-center h-full"><div class="animate-spin rounded-full h-8 w-8 border-b-2 border-primary"></div></div>}>
|
<Suspense fallback={<div class="flex items-center justify-center h-full"><div class="animate-spin rounded-full h-8 w-8 border-b-2 border-primary"></div></div>}>
|
||||||
<Show when={getEmbedView() === 'embed_notes'}>
|
<Show when={getEmbedView() === 'embed_notes'}>
|
||||||
{(() => {
|
{(() => {
|
||||||
const searchParams = new URLSearchParams(window.location.search);
|
const noteIdFromUrl = createMemo(() => {
|
||||||
const noteIdFromUrl = searchParams.get('noteId');
|
const params = new URLSearchParams(new URL(location()).search);
|
||||||
const [selectedNoteId, setSelectedNoteId] = createSignal<string | null>(noteIdFromUrl);
|
return params.get('noteId');
|
||||||
return <NotepadView selectedNoteId={selectedNoteId()} setSelectedNoteId={setSelectedNoteId} hideNavigation={!!noteIdFromUrl} />;
|
});
|
||||||
|
console.log('[DEBUG App] Rendering embed/notes context. noteId trace:', noteIdFromUrl());
|
||||||
|
return <NotepadView selectedNoteId={noteIdFromUrl()} setSelectedNoteId={() => { }} hideNavigation={!!noteIdFromUrl()} />;
|
||||||
})()}
|
})()}
|
||||||
</Show>
|
</Show>
|
||||||
<Show when={getEmbedView() === 'embed_quick_add'}>
|
<Show when={getEmbedView() === 'embed_quick_add'}>
|
||||||
|
|||||||
@@ -23,9 +23,7 @@ export const NotepadView: Component<{
|
|||||||
// Derived states
|
// Derived states
|
||||||
const activeNote = createMemo(() => {
|
const activeNote = createMemo(() => {
|
||||||
const id = props.selectedNoteId?.trim();
|
const id = props.selectedNoteId?.trim();
|
||||||
const note = store.notes.find(n => n.id === id) || null;
|
return store.notes.find(n => n.id === id) || null;
|
||||||
console.log('[DEBUG NotepadView] store.notes length:', store.notes.length, 'selectedNoteId:', id, 'found Note:', !!note);
|
|
||||||
return note;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleUpdateNote = async (id: string, data: Partial<Note>) => {
|
const handleUpdateNote = async (id: string, data: Partial<Note>) => {
|
||||||
|
|||||||
+32
-12
@@ -160,11 +160,12 @@
|
|||||||
<p><strong>2. Creating Notes Programmatically (API):</strong></p>
|
<p><strong>2. Creating Notes Programmatically (API):</strong></p>
|
||||||
<p>Before embedding, a sister app can create a Note via PocketBase API to retrieve a new
|
<p>Before embedding, a sister app can create a Note via PocketBase API to retrieve a new
|
||||||
<code>noteId</code>. By default, notes should have an array of <code>tags</code> and a
|
<code>noteId</code>. By default, notes should have an array of <code>tags</code> and a
|
||||||
<code>title</code>.</p>
|
<code>title</code>.
|
||||||
|
</p>
|
||||||
<p><em>Using the PocketBase JS SDK:</em></p>
|
<p><em>Using the PocketBase JS SDK:</em></p>
|
||||||
<pre
|
<pre
|
||||||
style="background: #f8f9fa; padding: 15px; border-radius: 4px; border: 1px solid #e9ecef; overflow-x: auto; font-size: 12px;">
|
style="background: #f8f9fa; padding: 15px; border-radius: 4px; border: 1px solid #e9ecef; overflow-x: auto; font-size: 12px;">
|
||||||
const record = await pb.collection('notes').create({
|
const record = await pb.collection('TasGrid_Notes').create({
|
||||||
title: "Sister App Payload",
|
title: "Sister App Payload",
|
||||||
content: "<p>Initial content here...</p>",
|
content: "<p>Initial content here...</p>",
|
||||||
tags: ["sister_app_export"],
|
tags: ["sister_app_export"],
|
||||||
@@ -178,7 +179,7 @@ const newNoteId = record.id;
|
|||||||
<p><em>Using standard REST API (fetch):</em></p>
|
<p><em>Using standard REST API (fetch):</em></p>
|
||||||
<pre
|
<pre
|
||||||
style="background: #f8f9fa; padding: 15px; border-radius: 4px; border: 1px solid #e9ecef; overflow-x: auto; font-size: 12px;">
|
style="background: #f8f9fa; padding: 15px; border-radius: 4px; border: 1px solid #e9ecef; overflow-x: auto; font-size: 12px;">
|
||||||
const res = await fetch('https://pocketbase.ccllc.pro/api/collections/notes/records', {
|
const res = await fetch('https://pocketbase.ccllc.pro/api/collections/TasGrid_Notes/records', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
@@ -197,14 +198,24 @@ const newNoteId = data.id;</pre>
|
|||||||
|
|
||||||
<p><strong>3. Authentication Protocol:</strong></p>
|
<p><strong>3. Authentication Protocol:</strong></p>
|
||||||
<p>The parent application must send a <code>postMessage</code> to the iframe immediately after the
|
<p>The parent application must send a <code>postMessage</code> to the iframe immediately after the
|
||||||
iframe loads (or whenever auth state changes).</p>
|
iframe loads (or whenever auth state changes). Since iframes reset their internal state on
|
||||||
|
navigation,
|
||||||
|
you should always sync auth on the <code>onload</code> event.</p>
|
||||||
|
<p><em>Example using vanilla JS:</em></p>
|
||||||
<pre
|
<pre
|
||||||
style="background: #f8f9fa; padding: 15px; border-radius: 4px; border: 1px solid #e9ecef; overflow-x: auto; font-size: 12px;">
|
style="background: #f8f9fa; padding: 15px; border-radius: 4px; border: 1px solid #e9ecef; overflow-x: auto; font-size: 12px;">
|
||||||
iframe.contentWindow.postMessage({
|
// 1. Define sync function
|
||||||
type: "TASGRID_AUTH",
|
function syncAuth(iframe) {
|
||||||
token: "YOUR_POCKETBASE_TOKEN",
|
iframe.contentWindow.postMessage({
|
||||||
user: { /* optional PB user record */ }
|
type: "TASGRID_AUTH",
|
||||||
}, "*");</pre>
|
token: pb.authStore.token,
|
||||||
|
user: pb.authStore.model
|
||||||
|
}, "*");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Attach to iframe (either in HTML or via JS)
|
||||||
|
// <iframe onload="syncAuth(this)" src="..."></iframe>
|
||||||
|
</pre>
|
||||||
|
|
||||||
<p><strong>4. Responsiveness:</strong></p>
|
<p><strong>4. Responsiveness:</strong></p>
|
||||||
<p>Both routes are designed to be fluid. Ensure your iframe container has defined dimensions; the
|
<p>Both routes are designed to be fluid. Ensure your iframe container has defined dimensions; the
|
||||||
@@ -221,7 +232,7 @@ iframe.contentWindow.postMessage({
|
|||||||
<span>Notes Embed</span>
|
<span>Notes Embed</span>
|
||||||
<code style="font-size: 10px;">/embed/notes</code>
|
<code style="font-size: 10px;">/embed/notes</code>
|
||||||
</div>
|
</div>
|
||||||
<iframe id="notes-iframe" src="http://localhost:4000/embed/notes"></iframe>
|
<iframe id="notes-iframe" src="http://localhost:4000/embed/notes" onload="syncAuthOnLoad(this)"></iframe>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="iframe-container">
|
<div class="iframe-container">
|
||||||
@@ -230,7 +241,8 @@ iframe.contentWindow.postMessage({
|
|||||||
<span>Quick Add Embed</span>
|
<span>Quick Add Embed</span>
|
||||||
<code style="font-size: 10px;">/embed/quick-add</code>
|
<code style="font-size: 10px;">/embed/quick-add</code>
|
||||||
</div>
|
</div>
|
||||||
<iframe id="quick-add-iframe" src="http://localhost:4000/embed/quick-add"></iframe>
|
<iframe id="quick-add-iframe" src="http://localhost:4000/embed/quick-add"
|
||||||
|
onload="syncAuthOnLoad(this)"></iframe>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -238,6 +250,14 @@ iframe.contentWindow.postMessage({
|
|||||||
const pb = new PocketBase('https://pocketbase.ccllc.pro');
|
const pb = new PocketBase('https://pocketbase.ccllc.pro');
|
||||||
const statusEl = document.getElementById('status');
|
const statusEl = document.getElementById('status');
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function loadSpecificNote() {
|
function loadSpecificNote() {
|
||||||
const id = document.getElementById('note-id-input').value.trim();
|
const id = document.getElementById('note-id-input').value.trim();
|
||||||
const iframe = document.getElementById('notes-iframe');
|
const iframe = document.getElementById('notes-iframe');
|
||||||
@@ -261,7 +281,7 @@ iframe.contentWindow.postMessage({
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// Using PocketBase SDK to mimic a REST API call from sister app
|
// Using PocketBase SDK to mimic a REST API call from sister app
|
||||||
const record = await pb.collection('notes').create({
|
const record = await pb.collection('TasGrid_Notes').create({
|
||||||
title: title,
|
title: title,
|
||||||
content: "<p>This note was created automatically via the API script!</p>",
|
content: "<p>This note was created automatically via the API script!</p>",
|
||||||
tags: ["api_generated"],
|
tags: ["api_generated"],
|
||||||
|
|||||||
Reference in New Issue
Block a user