Added task and note crteation SDKs
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
/**
|
||||
* TasGrid SDK
|
||||
*
|
||||
* A headless utility for creating tasks in the TasGrid PocketBase database
|
||||
* from sister applications, ensuring all TasGrid defaults and date calculations
|
||||
* are applied consistently.
|
||||
*/
|
||||
|
||||
const URGENCY_HOURS = {
|
||||
10: 12,
|
||||
9: 24,
|
||||
8: 36,
|
||||
7: 72,
|
||||
6: 120,
|
||||
5: 240,
|
||||
4: 504,
|
||||
3: 1080,
|
||||
2: 2160,
|
||||
1: 4320
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculates a future ISO date string based on the TasGrid urgency decay algorithm.
|
||||
* @param {number} level Urgency level (1-10)
|
||||
* @returns {string} ISO Date string
|
||||
*/
|
||||
export const calculateDateFromUrgency = (level) => {
|
||||
const roundedLevel = Math.max(1, Math.min(10, Math.round(level)));
|
||||
const hours = URGENCY_HOURS[roundedLevel] || 24;
|
||||
return new Date(Date.now() + hours * 60 * 60 * 1000).toISOString();
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a new task in the TasGrid database using the provided PocketBase instance.
|
||||
*
|
||||
* @param {object} pb - An authenticated PocketBase JS SDK instance.
|
||||
* @param {object} payload - The task payload.
|
||||
* @param {string} [payload.title="New Task"] - The task title.
|
||||
* @param {string} [payload.owner] - PocketBase User ID. Defaults to pb.authStore.model.id.
|
||||
* @param {number} [payload.priority=5] - Priority (1-10).
|
||||
* @param {number} [payload.urgency=5] - Urgency (1-10). Used to calculate dueDate if omitted.
|
||||
* @param {number} [payload.size=3] - Size (0-10).
|
||||
* @param {number} [payload.status=0] - Status (0-10).
|
||||
* @param {boolean} [payload.completed=false] - Completion status.
|
||||
* @param {string[]} [payload.tags=[]] - Array of tag strings.
|
||||
* @param {string} [payload.content=""] - HTML string for task description/notes.
|
||||
* @param {string} [payload.dueDate] - Strict ISO Date string. Overrides urgency calculation.
|
||||
* @param {string} [payload.startDate=""] - ISO Date string.
|
||||
* @returns {Promise<object>} The created PocketBase task record.
|
||||
*/
|
||||
export const createTasgridTask = async (pb, payload = {}) => {
|
||||
if (!pb || !pb.authStore || !pb.authStore.isValid || !pb.authStore.model) {
|
||||
throw new Error("Invalid or unauthenticated PocketBase instance provided.");
|
||||
}
|
||||
|
||||
const urgencyVal = payload.urgency ?? 5;
|
||||
|
||||
const taskData = {
|
||||
title: payload.title || "New Task",
|
||||
priority: payload.priority ?? 5,
|
||||
size: payload.size ?? 3,
|
||||
status: payload.status ?? 0,
|
||||
completed: payload.completed ?? false,
|
||||
tags: payload.tags || [],
|
||||
content: payload.content || "",
|
||||
user: payload.owner || pb.authStore.model.id,
|
||||
dueDate: payload.dueDate || calculateDateFromUrgency(urgencyVal),
|
||||
startDate: payload.startDate || "",
|
||||
};
|
||||
|
||||
try {
|
||||
const record = await pb.collection('TasGrid').create(taskData);
|
||||
return record;
|
||||
} catch (err) {
|
||||
console.error("TasGrid SDK: Failed to create task", err);
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a new note in the TasGrid database using the provided PocketBase instance.
|
||||
*
|
||||
* @param {object} pb - An authenticated PocketBase JS SDK instance.
|
||||
* @param {object} payload - The note payload.
|
||||
* @param {string} [payload.title="New Note"] - The note title.
|
||||
* @param {string} [payload.content=""] - HTML string for note content.
|
||||
* @param {string[]} [payload.tags=[]] - Array of tag strings.
|
||||
* @param {boolean} [payload.isPrivate=false] - Whether the note is private.
|
||||
* @param {string[]} [payload.tasks=[]] - Array of task IDs to link.
|
||||
* @param {string} [payload.owner] - PocketBase User ID. Defaults to pb.authStore.model.id.
|
||||
* @returns {Promise<object>} The created PocketBase note record.
|
||||
*/
|
||||
export const createTasgridNote = async (pb, payload = {}) => {
|
||||
if (!pb || !pb.authStore || !pb.authStore.isValid || !pb.authStore.model) {
|
||||
throw new Error("Invalid or unauthenticated PocketBase instance provided.");
|
||||
}
|
||||
|
||||
const noteData = {
|
||||
title: payload.title || "New Note",
|
||||
content: payload.content || "",
|
||||
tags: payload.tags || [],
|
||||
isPrivate: payload.isPrivate || false,
|
||||
tasks: payload.tasks || [],
|
||||
user: payload.owner || pb.authStore.model.id,
|
||||
};
|
||||
|
||||
try {
|
||||
const record = await pb.collection('TasGrid_Notes').create(noteData);
|
||||
return record;
|
||||
} catch (err) {
|
||||
console.error("TasGrid SDK: Failed to create note", err);
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
+138
-65
@@ -113,6 +113,22 @@
|
||||
<h1>TasGrid Parent App Simulator</h1>
|
||||
|
||||
<div class="controls">
|
||||
<div id="env-settings" style="margin-bottom: 20px; padding-bottom: 20px; border-bottom: 1px solid #eee;">
|
||||
<p style="font-size: 13px; color: #666; margin-top: 0;"><strong>Target Environment:</strong></p>
|
||||
<label style="margin-right: 15px; cursor: pointer;">
|
||||
<input type="radio" name="envToggle" value="https://tasgrid.ccllc.pro" checked onchange="updateEnv()">
|
||||
Production (tasgrid.ccllc.pro)
|
||||
</label>
|
||||
<label style="margin-right: 15px; cursor: pointer;">
|
||||
<input type="radio" name="envToggle" value="http://localhost:4000" onchange="updateEnv()">
|
||||
Localhost:4000
|
||||
</label>
|
||||
<label style="cursor: pointer;">
|
||||
<input type="radio" name="envToggle" value="http://localhost:4001" onchange="updateEnv()">
|
||||
Localhost:4001
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div id="login-section">
|
||||
<h3>Login to Parent Application</h3>
|
||||
<p>This simulates the parent application authenticating with the shared backend.</p>
|
||||
@@ -132,12 +148,23 @@
|
||||
<button onclick="loadSpecificNote()" style="padding: 8px 16px; cursor: pointer;">Load Note</button>
|
||||
</div>
|
||||
|
||||
<p style="font-size: 13px; color: #666; margin-top: 20px;"><strong>Test API Creation:</strong></p>
|
||||
<div style="display: flex; gap: 10px; margin-bottom: 10px;">
|
||||
<input type="text" id="new-note-title" style="flex: 1; padding: 8px;" placeholder="New Note Title...">
|
||||
<button onclick="createNoteViaAPI()"
|
||||
style="padding: 8px 16px; cursor: pointer; background: #28a745; color: white; border: none; border-radius: 4px; font-weight: bold;">Create
|
||||
& Open Note</button>
|
||||
<p style="font-size: 13px; color: #666; margin-top: 20px;"><strong>Test Headless SDK (Centralized
|
||||
Logic):</strong></p>
|
||||
<div style="display: flex; flex-direction: column; gap: 10px; margin-bottom: 10px;">
|
||||
<div style="display: flex; gap: 10px;">
|
||||
<input type="text" id="sdk-task-title" style="flex: 1; padding: 8px;"
|
||||
placeholder="New Task Title...">
|
||||
<button onclick="createTaskViaSDK()"
|
||||
style="padding: 8px 16px; cursor: pointer; background: #6f42c1; color: white; border: none; border-radius: 4px; font-weight: bold;">Create
|
||||
Task via SDK</button>
|
||||
</div>
|
||||
<div style="display: flex; gap: 10px;">
|
||||
<input type="text" id="sdk-note-title" style="flex: 1; padding: 8px;"
|
||||
placeholder="New Note Title...">
|
||||
<button onclick="createNoteViaSDK()"
|
||||
style="padding: 8px 16px; cursor: pointer; background: #fd7e14; color: white; border: none; border-radius: 4px; font-weight: bold;">Create
|
||||
Note via SDK</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p style="font-size: 13px; color: #666; margin-top: 20px;"><strong>Manual Auth:</strong></p>
|
||||
@@ -157,44 +184,46 @@
|
||||
<li><code>/embed/quick-add</code>: Renders a standalone task creation form.</li>
|
||||
</ul>
|
||||
|
||||
<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
|
||||
<code>noteId</code>. By default, notes should have an array of <code>tags</code> and a
|
||||
<code>title</code>.
|
||||
<p><strong>2. Programmatic Creation (TasGrid SDK):</strong></p>
|
||||
<p>The recommended way to create entries is using the <code>tasgrid-sdk.js</code> module. This
|
||||
ensures all TasGrid-specific defaults and date logic (like urgency decay) are handled correctly.
|
||||
</p>
|
||||
<p><em>Using the PocketBase JS SDK:</em></p>
|
||||
|
||||
<p><em>Setup:</em></p>
|
||||
<pre
|
||||
style="background: #f8f9fa; padding: 15px; border-radius: 4px; border: 1px solid #e9ecef; overflow-x: auto; font-size: 12px;">
|
||||
const record = await pb.collection('TasGrid_Notes').create({
|
||||
title: "Sister App Payload",
|
||||
content: "<p>Initial content here...</p>",
|
||||
tags: ["sister_app_export"],
|
||||
import { createTasgridTask, createTasgridNote } from 'https://tasgrid.ccllc.pro/tasgrid-sdk.js';</pre>
|
||||
|
||||
<div style="border-left: 4px solid #6f42c1; padding-left: 15px; margin: 20px 0;">
|
||||
<p><strong>A. Creating a TASK:</strong></p>
|
||||
<p style="font-size: 12px; color: #666; margin-top: -10px;">Use this for items that need status,
|
||||
priority, and automated due date calculation.</p>
|
||||
<pre
|
||||
style="background: #f8f9fa; padding: 15px; border-radius: 4px; border: 1px solid #e9ecef; overflow-x: auto; font-size: 12px;">
|
||||
const task = await createTasgridTask(pb, {
|
||||
title: "Urgent Task",
|
||||
urgency: 10, // Automated: 10=Today, 5=Next Week, 1=Six Months
|
||||
priority: 8, // 1-10
|
||||
size: 3, // Default: 3 (1 hour). Range 0-10.
|
||||
tags: ["external"],
|
||||
owner: "USER_ID" // Optional
|
||||
});</pre>
|
||||
</div>
|
||||
|
||||
<div style="border-left: 4px solid #fd7e14; padding-left: 15px; margin: 20px 0;">
|
||||
<p><strong>B. Creating a NOTE:</strong></p>
|
||||
<p style="font-size: 12px; color: #666; margin-top: -10px;">Use this for long-form content or
|
||||
documentation without a deadline.</p>
|
||||
<pre
|
||||
style="background: #f8f9fa; padding: 15px; border-radius: 4px; border: 1px solid #e9ecef; overflow-x: auto; font-size: 12px;">
|
||||
const note = await createTasgridNote(pb, {
|
||||
title: "Project Brief",
|
||||
content: "<p>HTML content here...</p>",
|
||||
tags: ["documentation"],
|
||||
isPrivate: false,
|
||||
user: pb.authStore.model.id, // Must be authenticated user ID
|
||||
tasks: [] // Optional array of linked task IDs
|
||||
});
|
||||
const newNoteId = record.id;
|
||||
// Then iframe to /embed/notes?noteId=${newNoteId}
|
||||
</pre>
|
||||
<p><em>Using standard REST API (fetch):</em></p>
|
||||
<pre
|
||||
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/TasGrid_Notes/records', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'YOUR_PB_AUTH_TOKEN'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
title: "REST Custom Note",
|
||||
user: "YOUR_USER_ID",
|
||||
tags: [],
|
||||
tasks: [],
|
||||
isPrivate: false
|
||||
})
|
||||
});
|
||||
const data = await res.json();
|
||||
const newNoteId = data.id;</pre>
|
||||
tasks: [] // Optional: Array of Task IDs to link
|
||||
});</pre>
|
||||
</div>
|
||||
|
||||
<p><strong>3. Authentication Protocol:</strong></p>
|
||||
<p>The parent application must send a <code>postMessage</code> to the iframe immediately after the
|
||||
@@ -232,7 +261,7 @@ function syncAuth(iframe) {
|
||||
<span>Notes Embed</span>
|
||||
<code style="font-size: 10px;">/embed/notes</code>
|
||||
</div>
|
||||
<iframe id="notes-iframe" src="http://localhost:4000/embed/notes" onload="syncAuthOnLoad(this)"></iframe>
|
||||
<iframe id="notes-iframe" onload="syncAuthOnLoad(this)"></iframe>
|
||||
</div>
|
||||
|
||||
<div class="iframe-container">
|
||||
@@ -241,14 +270,36 @@ function syncAuth(iframe) {
|
||||
<span>Quick Add Embed</span>
|
||||
<code style="font-size: 10px;">/embed/quick-add</code>
|
||||
</div>
|
||||
<iframe id="quick-add-iframe" src="http://localhost:4000/embed/quick-add"
|
||||
onload="syncAuthOnLoad(this)"></iframe>
|
||||
<iframe id="quick-add-iframe" onload="syncAuthOnLoad(this)"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const pb = new PocketBase('https://pocketbase.ccllc.pro');
|
||||
const statusEl = document.getElementById('status');
|
||||
let ENV_URL = 'https://tasgrid.ccllc.pro';
|
||||
|
||||
function updateEnv() {
|
||||
const selected = document.querySelector('input[name="envToggle"]:checked').value;
|
||||
ENV_URL = selected;
|
||||
|
||||
// Reload iframes with new base URL
|
||||
const notesIframe = document.getElementById('notes-iframe');
|
||||
const noteId = document.getElementById('note-id-input')?.value.trim();
|
||||
if (noteId) {
|
||||
notesIframe.src = `${ENV_URL}/embed/notes?noteId=${noteId}`;
|
||||
} else {
|
||||
notesIframe.src = `${ENV_URL}/embed/notes`;
|
||||
}
|
||||
|
||||
document.getElementById('quick-add-iframe').src = `${ENV_URL}/embed/quick-add`;
|
||||
console.log(`Switched target environment to: ${ENV_URL}`);
|
||||
}
|
||||
|
||||
// Initialize iframes on load
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
updateEnv();
|
||||
});
|
||||
|
||||
function syncAuthOnLoad(iframe) {
|
||||
console.log(`Iframe ${iframe.id} loaded, checking auth sync...`);
|
||||
@@ -262,46 +313,68 @@ 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 = `${ENV_URL}/embed/notes?noteId=${id}`;
|
||||
} else {
|
||||
iframe.src = `http://localhost:4000/embed/notes`;
|
||||
iframe.src = `${ENV_URL}/embed/notes`;
|
||||
}
|
||||
}
|
||||
|
||||
async function createNoteViaAPI() {
|
||||
const title = document.getElementById('new-note-title').value.trim() || 'API Generated Note';
|
||||
async function createTaskViaSDK() {
|
||||
const title = document.getElementById('sdk-task-title').value.trim() || 'SDK Generated Task';
|
||||
if (!pb.authStore.isValid) {
|
||||
alert("Please log in first before creating a note via API.");
|
||||
alert("Please log in first before creating a task via SDK.");
|
||||
return;
|
||||
}
|
||||
|
||||
statusEl.className = 'status';
|
||||
statusEl.textContent = 'Creating note via API...';
|
||||
statusEl.textContent = 'Importing SDK and creating task...';
|
||||
statusEl.style.display = 'block';
|
||||
|
||||
try {
|
||||
// Using PocketBase SDK to mimic a REST API call from sister app
|
||||
const record = await pb.collection('TasGrid_Notes').create({
|
||||
const { createTasgridTask } = await import(`${ENV_URL}/tasgrid-sdk.js`);
|
||||
|
||||
const record = await createTasgridTask(pb, {
|
||||
title: title,
|
||||
content: "<p>This note was created automatically via the API script!</p>",
|
||||
tags: ["api_generated"],
|
||||
isPrivate: false,
|
||||
user: pb.authStore.model.id,
|
||||
tasks: []
|
||||
priority: 8,
|
||||
urgency: 4,
|
||||
tags: ["sdk_test"],
|
||||
content: "This task was created by a sister app using the imported Headless SDK!"
|
||||
});
|
||||
|
||||
statusEl.className = 'status success';
|
||||
statusEl.textContent = `Note created successfully (ID: ${record.id})! Loading iframe...`;
|
||||
|
||||
// Automatically set the new ID in the deep link input
|
||||
document.getElementById('note-id-input').value = record.id;
|
||||
|
||||
// Automatically load it
|
||||
loadSpecificNote();
|
||||
|
||||
statusEl.textContent = `Task created successfully via SDK (ID: ${record.id})!`;
|
||||
} catch (err) {
|
||||
statusEl.className = 'status error';
|
||||
statusEl.textContent = 'Failed to create note: ' + err.message;
|
||||
statusEl.textContent = 'Failed to create task via SDK: ' + err.message;
|
||||
}
|
||||
}
|
||||
|
||||
async function createNoteViaSDK() {
|
||||
const title = document.getElementById('sdk-note-title').value.trim() || 'SDK Generated Note';
|
||||
if (!pb.authStore.isValid) {
|
||||
alert("Please log in first before creating a note via SDK.");
|
||||
return;
|
||||
}
|
||||
|
||||
statusEl.className = 'status';
|
||||
statusEl.textContent = 'Importing SDK and creating note...';
|
||||
statusEl.style.display = 'block';
|
||||
|
||||
try {
|
||||
const { createTasgridNote } = await import(`${ENV_URL}/tasgrid-sdk.js`);
|
||||
|
||||
const record = await createTasgridNote(pb, {
|
||||
title: title,
|
||||
content: "<p>This note was created headlessly via the SDK!</p>",
|
||||
tags: ["sdk_test"],
|
||||
isPrivate: false
|
||||
});
|
||||
|
||||
statusEl.className = 'status success';
|
||||
statusEl.textContent = `Note created successfully via SDK (ID: ${record.id})!`;
|
||||
} catch (err) {
|
||||
statusEl.className = 'status error';
|
||||
statusEl.textContent = 'Failed to create note via SDK: ' + err.message;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -63,6 +63,10 @@ export default defineConfig({
|
||||
server: {
|
||||
host: '0.0.0.0',
|
||||
port: 4000,
|
||||
cors: { origin: '*' },
|
||||
headers: {
|
||||
'Access-Control-Allow-Origin': '*'
|
||||
},
|
||||
allowedHosts: [
|
||||
'tasgrid.ccllc.pro'
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user