24<meta name="viewport" content="width=device-width, initial-scale=1.0">
25<title>Deconstruction Engine</title>
26<link rel="preconnect" href="https://fonts.googleapis.com">
27<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
28<link href="https://fonts.googleapis.com/css2?family=AudioWide&display=swap" rel="stylesheet">
29<style>
30* {
181<script>
182(function() {
183const API_URL = '${sourceUrl}';
184const form = document.getElementById('prompt-form');
185const input = document.getElementById('prompt-input');
276277try {
278const response = await fetch(API_URL, {
279method: 'POST',
280headers: { 'Content-Type': 'application/json' },
315if (req.method === "POST") {
316try {
317const openai = new OpenAI({ apiKey: Deno.env.get("openai") });
318const body = await req.json();
319const prompt = body.prompt;
1import { PollyClient, SynthesizeSpeechCommand } from "npm:@aws-sdk/client-polly";
23const BEARER_TOKEN = Deno.env.get("API_TOKEN");
45// Domain model: Speech synthesis request
134}
135136// Handle ReadableStream (Node.js style or Web API)
137if (response.AudioStream && typeof response.AudioStream === "object") {
138// Try to convert to Uint8Array
14const auth = new GoogleAuth({
15credentials,
16scopes: ["https://www.googleapis.com/auth/documents"],
17});
1848}
4950// Insert at the beginning of the document using direct API call
51const requests = [{
52insertText: {
58}];
5960const response = await fetch(`https://docs.googleapis.com/v1/documents/${documentId}:batchUpdate`, {
61method: 'POST',
62headers: {
69if (!response.ok) {
70const errorText = await response.text();
71throw new Error(`Google Docs API error: ${response.status} ${response.statusText} - ${errorText}`);
72}
73
Createusersmain.ts1 match
2829try {
30const res = await fetch(`${ASTRA_BASE_URL}/api/rest/v2/namespaces/${KEYSPACE}/collections`, {
31method: "POST",
32headers,
40};
4142const res = await fetch(`${ASTRA_BASE_URL}/api/rest/v2/namespaces/${KEYSPACE}/collections/${COLLECTION}`, {
43method: "POST",
44headers,
59// ✅ GET /users
60if (pathname === "/users" && method === "GET") {
61const res = await fetch(`${ASTRA_BASE_URL}/api/rest/v2/namespaces/${KEYSPACE}/collections/${COLLECTION}`, {
62method: "GET",
63headers,
80log.requestBody = body;
8182const res = await fetch(`${ASTRA_BASE_URL}/api/rest/v2/namespaces/${KEYSPACE}/collections/${COLLECTION}`, {
83method: "POST",
84headers,
34// /users GET
35if (pathname === "/users" && method === "GET") {
36const res = await fetch(`${ASTRA_BASE_URL}/api/rest/v2/namespaces/${KEYSPACE}/collections/${COLLECTION}`, {
37method: "GET",
38headers,
54// /users POST
55if (pathname === "/users" && method === "POST" && body) {
56const res = await fetch(`${ASTRA_BASE_URL}/api/rest/v2/namespaces/${KEYSPACE}/collections/${COLLECTION}`, {
57method: "POST",
58headers,
78};
7980const res = await fetch(`${ASTRA_BASE_URL}/api/rest/v2/namespaces/${KEYSPACE}/collections/${COLLECTION}`, {
81method: "POST",
82headers,
169<head>
170<meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Franchise CRM Dashboard</title>
171<link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600;700&family=Source+Sans+Pro:wght@700;900&display=swap" rel="stylesheet">
172<style>
173:root { --bg-main: #f8f9fa; --bg-panel: #ffffff; --border-color: #dee2e6; --text-primary: #212529; --text-secondary: #6c757d; --brand-yellow: #ffc72c; --brand-blue: #005eb8; --shadow-sm: 0 1px 2px 0 rgba(0,0,0,0.05); }
224(function() {
225const G = (id) => document.getElementById(id); let customers = []; let selectedCustomerId = null; let currentFranchiseId = 1;
226async function api(action, method = 'POST', body = null) {
227const url = \`${baseUrl}?action=\${action}\`;
228const options = { method, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ ...body, franchiseId: currentFranchiseId }) };
229if (method === 'GET') { url += \`&franchiseId=\${currentFranchiseId}\`; delete options.body; }
230try { const r = await fetch(url, options); if (!r.ok) { const e = await r.json().catch(() => ({})); throw new Error(e.error || 'HTTP ' + r.status); } return r.json(); }
231catch (e) { console.error('API Error:', e); throw e; }
232}
233async function api(action, method = 'POST', body = null) {
234// CORRECTED: Changed 'const' to 'let' to allow modification.
235let url = \`${"\${baseUrl}"}?action=\${action}\`;
252return response.json();
253} catch (error) {
254console.error('API call ' + action + ' failed:', error);
255throw error;
256}
306const btn = G('submit-new-customer'); btn.disabled = true; btn.textContent = "Ingesting...";
307const payload = { customerData: { posId: G('posId').value, name: G('name').value, email: G('email').value, phone: G('phone').value }, transactionData: { date: new Date().toISOString(), total: G('total').value, items: G('items').value.split(',').map(s=>s.trim()), channel: G('channel').value } };
308try { await api('ingestInteraction', 'POST', payload); location.reload(); }
309catch (err) { alert("Failed to save: " + err.message); btn.disabled = false; btn.textContent = "Save & Ingest"; }
310};
312function renderSeedUI() {
313G('content-pane').innerHTML = \`<div class="placeholder"><h2>Welcome to the AI CRM</h2><p>The database is empty. Click to populate it with sample data and AI-generated profiles.</p><button id="seed-btn" class="action-btn" style="background:var(--brand-blue); color:white; padding:12px 24px;">Seed Sample Data</button></div>\`;
314G('seed-btn').addEventListener('click', async (e) => { e.target.disabled = true; e.target.textContent = 'Seeding...'; try { await api('seedDatabase', 'POST'); e.target.textContent = 'Success! Reloading...'; location.reload(); } catch (err) { e.target.disabled = false; e.target.textContent = 'Seed Sample Data'; alert('Failed to seed: ' + err.message); } });
315}
316async function loadInitialData() {
317try {
318const data = await api('getFranchiseData', 'GET');
319G('franchise-name').textContent = data.franchise.name; G('franchise-location').textContent = data.franchise.location;
320customers = data.customers; renderCustomerList();
321G('analytics-btn').onclick = async (e) => {
322e.target.disabled = true; e.target.textContent = "Analyzing..."; G('segment-results').innerHTML = 'AI is analyzing...';
323try { const result = await api('runSegmentation', 'POST'); G('segment-results').innerHTML = result.segments.map(s => \`<div class="detail-card" style="margin:0 0 1rem 0; padding:1rem;"><h4>\${s.name}</h4><p style="font-size:0.85rem">\${s.description}</p></div>\`).join(''); await loadInitialData(); }
324catch (err) { G('segment-results').innerHTML = 'Error running analysis.'; } finally { e.target.disabled = false; e.target.textContent = "Run AI Segmentation"; }
325};
334}
335336// --- API ENDPOINTS & MAIN HANDLER ---
337export default async function(req: Request) {
338const url = new URL(req.url);
339const action = url.searchParams.get("action");
340const openai = new OpenAI({ apiKey: Deno.env.get("OPENAI_API_KEY") });
341await initializeDatabase();
342try {
1const CACHE_NAME = 'kur-saulyte-v1';
2const API_CACHE_NAME = 'kur-saulyte-api-v1';
34// Static assets to cache
13];
1415// API endpoints that can be cached
16const CACHEABLE_APIS = [
17'/api/weather',
18'/api/location',
19'/api/geocode'
20];
2141return Promise.all(
42cacheNames.map((cacheName) => {
43if (cacheName !== CACHE_NAME && cacheName !== API_CACHE_NAME) {
44console.log('PWA: Deleting old cache:', cacheName);
45return caches.delete(cacheName);
58const requestUrl = new URL(event.request.url);
59
60// Handle API requests
61if (CACHEABLE_APIS.some(api => requestUrl.pathname.startsWith(api))) {
62event.respondWith(handleApiRequest(event.request));
63return;
64}
80});
8182// API request handling - cache with timestamp for freshness
83async function handleApiRequest(request) {
84const url = new URL(request.url);
85const cacheKey = `${url.pathname}${url.search}`;
91if (networkResponse.ok) {
92// Cache successful responses with timestamp
93const cache = await caches.open(API_CACHE_NAME);
94const responseToCache = networkResponse.clone();
95
105
106await cache.put(cacheKey, responseWithTimestamp);
107console.log('PWA: API response cached:', cacheKey);
108return networkResponse;
109}
113// Network failed, try cache
114console.log('PWA: Network failed, trying cache for:', cacheKey);
115const cache = await caches.open(API_CACHE_NAME);
116const cachedResponse = await cache.match(cacheKey);
117
122
123if (cachedAt && parseInt(cachedAt) > thirtyMinutesAgo) {
124console.log('PWA: Serving fresh cached API response');
125return cachedResponse;
126} else {
127console.log('PWA: Serving stale cached API response');
128// Return stale data but with a header indicating it's stale
129return new Response(cachedResponse.body, {
187// Create offline response for different endpoints
188function createOfflineResponse(pathname) {
189if (pathname.startsWith('/api/weather')) {
190return new Response(JSON.stringify({
191error: 'Offline - weather data not available',
197}
198
199if (pathname.startsWith('/api/location')) {
200return new Response(JSON.stringify({
201lat: 52.3676,
210}
211
212if (pathname.startsWith('/api/geocode')) {
213return new Response(JSON.stringify({
214results: [],
Townie2system_prompt.txt9 matches
13- Generate code in TypeScript or TSX
14- Add appropriate TypeScript types and interfaces for all data structures
15- Prefer official SDKs or libraries than writing API calls directly
16- Ask the user to supply API or library documentation if you are at all unsure about it
17- **Never bake in secrets into the code** - always use environment variables
18- Include comments explaining complex logic (avoid commenting obvious operations)
23### 1. HTTP Trigger
2425- Create web APIs and endpoints
26- Handle HTTP requests and responses
27- Example structure:
158- **AI Image:** To inline generate an AI image use: `<img src="https://maxm-imggenurl.web.val.run/the-description-of-your-image" />`
159- **Storage:** DO NOT use the Deno KV module for storage
160- **Browser APIs:** DO NOT use the `alert()`, `prompt()`, or `confirm()` methods
161- **Weather Data:** Use open-meteo for weather data (doesn't require API keys) unless otherwise specified
162- **Error Debugging:** Add `<script src="https://esm.town/v/std/catch"></script>` to HTML to capture client-side errors
163- **Error Handling:** Only use try...catch when there's a clear local resolution; Avoid catches that merely log or return 500s. Let errors bubble up with full context
164- **Environment Variables:** Use `Deno.env.get('keyname')` when you need to, but generally prefer APIs that don't require keys
165- **Imports:** Use `https://esm.sh` for npm and Deno dependencies to ensure compatibility on server and browser
166- **Storage Strategy:** Only use backend storage if explicitly required; prefer simple static client-side sites
200### Backend (Hono) Best Practices
201202- Hono is the recommended API framework
203- Main entry point should be `backend/index.ts`
204- Do NOT use Hono serveStatic middleware
225});
226```
227- Create RESTful API routes for CRUD operations
228- Always include this snippet at the top-level Hono app to re-throwing errors to see full stack traces:
229```ts
262- For files in the project, use `readFile` helpers
2632645. **API Design:**
265- `fetch` handler is the entry point for HTTP vals
266- Run the Hono app with `export default app.fetch // This is the entry point for HTTP vals`