Skip to main content

max / goingson

13.5 KB · 394 lines History Blame Raw
1 /**
2 * GoingsOn - Demo Data Generator
3 * Creates sample projects, tasks, and events for testing
4 *
5 * Usage: Call GoingsOn.seedData.seed() from the browser console
6 */
7
8 (function() {
9 'use strict';
10
11 const DemoData = {
12 // Project names and types
13 projects: [
14 { name: 'Website Redesign', type: 'Job', description: 'Complete overhaul of company website with modern design' },
15 { name: 'Mobile App MVP', type: 'SideProject', description: 'Build minimum viable product for task management app' },
16 { name: 'Q1 Marketing Campaign', type: 'Job', description: 'Launch new marketing initiative across all channels' },
17 { name: 'Blog Writing', type: 'Article', description: 'Weekly blog posts on productivity and tech' },
18 { name: 'Learning Rust', type: 'SideProject', description: 'Self-study Rust programming language' },
19 { name: 'Home Office Setup', type: 'Other', description: 'Optimize home workspace for productivity' },
20 { name: 'Investment Research', type: 'Company', description: 'Research potential investment opportunities' },
21 { name: 'Photography Portfolio', type: 'SideProject', description: 'Curate and showcase best photography work' },
22 { name: 'Client Onboarding System', type: 'Job', description: 'Automate new client onboarding process' },
23 { name: 'Annual Review Essay', type: 'Essay', description: 'Reflections on the past year and goals for next' },
24 ],
25
26 // Task descriptions by category
27 taskTemplates: {
28 development: [
29 'Set up development environment',
30 'Create database schema',
31 'Implement user authentication',
32 'Build REST API endpoints',
33 'Write unit tests',
34 'Fix bug in login flow',
35 'Optimize database queries',
36 'Add caching layer',
37 'Implement rate limiting',
38 'Create admin dashboard',
39 'Add export to CSV feature',
40 'Implement search functionality',
41 'Fix mobile responsiveness',
42 'Add dark mode support',
43 'Implement WebSocket notifications',
44 ],
45 design: [
46 'Create wireframes',
47 'Design landing page mockup',
48 'Update color palette',
49 'Design email templates',
50 'Create icon set',
51 'Design onboarding flow',
52 'Update typography system',
53 'Create component library',
54 'Design error states',
55 'Update illustration style',
56 ],
57 marketing: [
58 'Write press release',
59 'Create social media content calendar',
60 'Design banner ads',
61 'Write email newsletter',
62 'Analyze competitor strategies',
63 'Update SEO keywords',
64 'Create video script',
65 'Plan influencer outreach',
66 'Design promotional materials',
67 'Write case study',
68 ],
69 admin: [
70 'Schedule team meeting',
71 'Review quarterly budget',
72 'Update documentation',
73 'Organize file structure',
74 'Backup important files',
75 'Review contracts',
76 'Update contact list',
77 'Plan team offsite',
78 'Review insurance policies',
79 'Prepare monthly report',
80 ],
81 personal: [
82 'Read industry articles',
83 'Complete online course module',
84 'Practice new skill',
85 'Write journal entry',
86 'Review personal goals',
87 'Organize workspace',
88 'Update resume',
89 'Network with peers',
90 'Research new tools',
91 'Plan next week priorities',
92 ],
93 },
94
95 // Event templates
96 eventTemplates: [
97 { title: 'Team Standup', location: 'Zoom', durationMins: 15 },
98 { title: 'Client Call', location: 'Google Meet', durationMins: 30 },
99 { title: 'Project Review', location: 'Conference Room A', durationMins: 60 },
100 { title: 'Design Review', location: 'Figma', durationMins: 45 },
101 { title: 'Sprint Planning', location: 'Zoom', durationMins: 90 },
102 { title: 'Code Review Session', location: 'VS Code Live Share', durationMins: 30 },
103 { title: 'Lunch with Mentor', location: 'Downtown Cafe', durationMins: 60 },
104 { title: 'Workshop: New Tools', location: 'Training Room', durationMins: 120 },
105 { title: 'Interview: Frontend Dev', location: 'Office', durationMins: 60 },
106 { title: 'Strategy Meeting', location: 'Board Room', durationMins: 90 },
107 { title: 'Coffee Chat', location: 'Local Coffee Shop', durationMins: 30 },
108 { title: 'Demo to Stakeholders', location: 'Presentation Room', durationMins: 60 },
109 ],
110
111 // Annotation templates
112 annotations: [
113 'Updated scope based on client feedback',
114 'Blocked by external dependency',
115 'Need to follow up with team',
116 'Good progress today',
117 'Waiting for approval',
118 'Resources identified',
119 'Dependencies resolved',
120 'On track for deadline',
121 'Complexity higher than expected',
122 'Found alternative approach',
123 ],
124
125 // Tag templates
126 tags: ['urgent', 'blocked', 'review', 'research', 'quick-win', 'backend', 'frontend', 'design', 'docs', 'meeting'],
127 };
128
129 /**
130 * Get a random item from an array
131 */
132 function randomItem(arr) {
133 return arr[Math.floor(Math.random() * arr.length)];
134 }
135
136 /**
137 * Get a random number between min and max (inclusive)
138 */
139 function randomInt(min, max) {
140 return Math.floor(Math.random() * (max - min + 1)) + min;
141 }
142
143 /**
144 * Get a random date within a range
145 */
146 function randomDate(startDays, endDays) {
147 const now = new Date();
148 const offsetDays = randomInt(startDays, endDays);
149 const date = new Date(now.getTime() + offsetDays * 24 * 60 * 60 * 1000);
150 date.setHours(randomInt(8, 18), randomInt(0, 3) * 15, 0, 0);
151 return date.toISOString();
152 }
153
154 /**
155 * Get random tags
156 */
157 function randomTags(maxCount = 3) {
158 const count = randomInt(0, maxCount);
159 const tags = [];
160 const availableTags = [...DemoData.tags];
161 for (let i = 0; i < count && availableTags.length > 0; i++) {
162 const idx = randomInt(0, availableTags.length - 1);
163 tags.push(availableTags.splice(idx, 1)[0]);
164 }
165 return tags;
166 }
167
168 /**
169 * Generate demo projects
170 */
171 async function generateProjects() {
172 const projects = [];
173 const statuses = ['Active', 'Active', 'Active', 'OnHold', 'Completed'];
174
175 for (const proj of DemoData.projects) {
176 try {
177 const project = await GoingsOn.api.projects.create({
178 name: proj.name,
179 description: proj.description,
180 projectType: proj.type,
181 status: randomItem(statuses),
182 });
183 projects.push(project);
184 console.log(`Created project: ${proj.name}`);
185 } catch (err) {
186 console.error(`Failed to create project ${proj.name}:`, err);
187 }
188 }
189
190 return projects;
191 }
192
193 /**
194 * Generate demo tasks
195 */
196 async function generateTasks(projects, count = 120) {
197 const tasks = [];
198 const priorities = ['High', 'Medium', 'Medium', 'Low'];
199 const recurrences = ['None', 'None', 'None', 'None', 'Daily', 'Weekly', 'Monthly'];
200 const categories = Object.keys(DemoData.taskTemplates);
201
202 for (let i = 0; i < count; i++) {
203 const category = randomItem(categories);
204 const description = randomItem(DemoData.taskTemplates[category]);
205 const project = Math.random() > 0.2 ? randomItem(projects) : null;
206
207 // Vary due dates: some past (overdue), some today, some future, some null
208 let due = null;
209 const dueChance = Math.random();
210 if (dueChance < 0.1) {
211 due = randomDate(-7, -1); // Overdue
212 } else if (dueChance < 0.2) {
213 due = randomDate(0, 0); // Today
214 } else if (dueChance < 0.7) {
215 due = randomDate(1, 30); // Future
216 }
217
218 try {
219 const task = await GoingsOn.api.tasks.create({
220 description: `${description} ${i + 1}`,
221 projectId: project?.id || null,
222 priority: randomItem(priorities),
223 due: due,
224 tags: randomTags(2),
225 recurrence: randomItem(recurrences),
226 });
227 tasks.push(task);
228
229 // Add subtasks to some tasks
230 if (Math.random() > 0.7) {
231 const subtaskCount = randomInt(2, 5);
232 for (let j = 0; j < subtaskCount; j++) {
233 await GoingsOn.api.subtasks.add(task.id, `Subtask ${j + 1} for this task`);
234 // Complete some subtasks
235 if (Math.random() > 0.5) {
236 const subtasks = await GoingsOn.api.subtasks.list(task.id);
237 if (subtasks.length > j) {
238 await GoingsOn.api.subtasks.toggle(task.id, subtasks[j].id);
239 }
240 }
241 }
242 }
243
244 // Add annotations to some tasks
245 if (Math.random() > 0.8) {
246 await GoingsOn.api.annotations.add(task.id, randomItem(DemoData.annotations));
247 }
248
249 // Snooze some tasks
250 if (Math.random() > 0.9) {
251 await GoingsOn.api.tasks.snooze(task.id, randomDate(1, 7));
252 }
253
254 if ((i + 1) % 20 === 0) {
255 console.log(`Created ${i + 1}/${count} tasks...`);
256 }
257 } catch (err) {
258 console.error(`Failed to create task:`, err);
259 }
260 }
261
262 console.log(`Created ${tasks.length} tasks`);
263 return tasks;
264 }
265
266 /**
267 * Generate demo events
268 */
269 async function generateEvents(projects, count = 50) {
270 const events = [];
271
272 for (let i = 0; i < count; i++) {
273 const template = randomItem(DemoData.eventTemplates);
274 const project = Math.random() > 0.3 ? randomItem(projects) : null;
275 const startTime = randomDate(-7, 30);
276 const endTime = new Date(new Date(startTime).getTime() + template.durationMins * 60000).toISOString();
277
278 try {
279 const event = await GoingsOn.api.events.create({
280 title: `${template.title} ${i + 1}`,
281 description: `Scheduled ${template.title.toLowerCase()} session`,
282 projectId: project?.id || null,
283 startTime: startTime,
284 endTime: endTime,
285 location: template.location,
286 });
287 events.push(event);
288
289 if ((i + 1) % 10 === 0) {
290 console.log(`Created ${i + 1}/${count} events...`);
291 }
292 } catch (err) {
293 console.error(`Failed to create event:`, err);
294 }
295 }
296
297 console.log(`Created ${events.length} events`);
298 return events;
299 }
300
301 /**
302 * Main function to seed all demo data
303 */
304 async function seedDemoData() {
305 console.log('=== Starting Demo Data Generation ===');
306 console.log('This will create sample projects, tasks, and events.');
307 console.log('');
308
309 console.log('Step 1/3: Creating projects...');
310 const projects = await generateProjects();
311 console.log(`Created ${projects.length} projects\n`);
312
313 console.log('Step 2/3: Creating tasks...');
314 const tasks = await generateTasks(projects, 120);
315 console.log(`Created ${tasks.length} tasks\n`);
316
317 console.log('Step 3/3: Creating events...');
318 const events = await generateEvents(projects, 50);
319 console.log(`Created ${events.length} events\n`);
320
321 console.log('=== Demo Data Generation Complete ===');
322 console.log(`Summary:`);
323 console.log(` - ${projects.length} projects`);
324 console.log(` - ${tasks.length} tasks`);
325 console.log(` - ${events.length} events`);
326 console.log('');
327 console.log('Refresh the page to see the data.');
328
329 // Refresh the current view
330 if (GoingsOn.navigation?.loadViewData) {
331 await GoingsOn.navigation.loadViewData(GoingsOn.navigation.getCurrentView());
332 }
333
334 return { projects, tasks, events };
335 }
336
337 /**
338 * Clear all demo data (use with caution!)
339 */
340 async function clearAllData() {
341 const ok = await GoingsOn.ui.showConfirmDialog(
342 'Clear all data',
343 'This will delete ALL projects, tasks, and events. Are you sure?',
344 { confirmText: 'Delete everything', danger: true }
345 );
346 if (!ok) {
347 console.log('Cancelled.');
348 return;
349 }
350
351 console.log('Clearing all data...');
352
353 try {
354 // Delete all tasks
355 const tasks = await GoingsOn.api.tasks.list();
356 for (const task of tasks) {
357 await GoingsOn.api.tasks.delete(task.id);
358 }
359 console.log(`Deleted ${tasks.length} tasks`);
360
361 // Delete all events
362 const events = await GoingsOn.api.events.list();
363 for (const event of events) {
364 await GoingsOn.api.events.delete(event.id);
365 }
366 console.log(`Deleted ${events.length} events`);
367
368 // Delete all projects
369 const projects = await GoingsOn.api.projects.list();
370 for (const project of projects) {
371 await GoingsOn.api.projects.delete(project.id);
372 }
373 console.log(`Deleted ${projects.length} projects`);
374
375 console.log('All data cleared.');
376
377 // Refresh the current view
378 if (GoingsOn.navigation?.loadViewData) {
379 await GoingsOn.navigation.loadViewData(GoingsOn.navigation.getCurrentView());
380 }
381 } catch (err) {
382 console.error('Error clearing data:', err);
383 }
384 }
385
386 // ============ Populate GoingsOn Namespace ============
387
388 GoingsOn.seedData = {
389 seed: seedDemoData,
390 clearAll: clearAllData,
391 };
392
393 })();
394