📸 Add screenshots, live demo section, and seed data

- 6 app screenshots (login, dashboard, kanban, card detail, users, email)
- Live demo URL with 4 demo accounts (admin + 3 members)
- Demo data: 3 boards, 21 cards, labels, comments, activity
- Seed script for reproducible demo environment
- Updated README with screenshots section and demo credentials
This commit is contained in:
admin
2026-04-03 15:24:08 +00:00
Unverified
parent 460f83aef8
commit 576c41b3ca
10 changed files with 1180 additions and 20 deletions

1027
server/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -16,6 +16,7 @@
"jsonwebtoken": "^9.0.2",
"mailparser": "^3.7.2",
"nodemailer": "^6.10.0",
"puppeteer-core": "^24.40.0",
"uuid": "^11.0.5"
}
}

101
server/seed-demo.cjs Normal file
View File

@@ -0,0 +1,101 @@
const bcrypt = require('bcryptjs');
const db = require('better-sqlite3')('./data/teamflow.db');
db.exec(`DELETE FROM card_comments; DELETE FROM card_activity; DELETE FROM card_labels;
DELETE FROM checklist_items; DELETE FROM checklists; DELETE FROM email_tokens;
DELETE FROM cards; DELETE FROM lists; DELETE FROM board_members; DELETE FROM labels;
DELETE FROM boards; DELETE FROM notifications; DELETE FROM email_log; DELETE FROM users;`);
const users = [
{ email: 'admin@teamflow.local', name: 'Admin', role: 'admin', color: '#6366f1' },
{ email: 'sarah@demo.teamflow', name: 'Sarah Chen', role: 'member', color: '#ec4899' },
{ email: 'alex@demo.teamflow', name: 'Alex Rivera', role: 'member', color: '#14b8a6' },
{ email: 'jordan@demo.teamflow', name: 'Jordan Lee', role: 'member', color: '#f97316' },
];
const ins = db.prepare('INSERT INTO users (email, name, password, role, avatar_color) VALUES (?, ?, ?, ?, ?)');
users.forEach(u => ins.run(u.email, u.name, bcrypt.hashSync('demo1234', 12), u.role, u.color));
const listIns = db.prepare('INSERT INTO lists (board_id, title, position) VALUES (?, ?, ?)');
const labIns = db.prepare('INSERT INTO labels (board_id, name, color) VALUES (?, ?, ?)');
const cardIns = db.prepare(`INSERT INTO cards (list_id, title, description, position, priority, assigned_to, created_by, due_date, estimated_hours, time_spent) VALUES (?,?,?,?,?,?,?,?,?,?)`);
const clIns = db.prepare('INSERT OR IGNORE INTO card_labels (card_id, label_id) VALUES (?, ?)');
const actIns = db.prepare('INSERT INTO card_activity (card_id, user_id, action, details) VALUES (?, ?, ?, ?)');
const commIns = db.prepare('INSERT INTO card_comments (card_id, user_id, content) VALUES (?, ?, ?)');
const bmIns = db.prepare('INSERT INTO board_members (board_id, user_id, role) VALUES (?, ?, ?)');
const bIns = db.prepare('INSERT INTO boards (title, description, background, created_by) VALUES (?, ?, ?, ?)');
// Board 1: Sprint
const b1 = bIns.run('Sprint 24 — Product Launch', 'Current sprint deliverables for Q2 product launch', 'gradient-indigo', 1);
const b1id = b1.lastInsertRowid;
[1,2,3,4].forEach((uid,i) => bmIns.run(b1id, uid, i===0?'admin':'member'));
const l1ids = ['📋 Backlog','🔄 In Progress','👀 In Review','✅ Done'].map((t,i) => listIns.run(b1id,t,i*65536).lastInsertRowid);
const lab1ids = [
['Bug','#ef4444'],['Feature','#3b82f6'],['Design','#a855f7'],['Urgent','#f97316'],['Documentation','#22c55e']
].map(([n,c]) => labIns.run(b1id,n,c).lastInsertRowid);
const cards1 = [
[0,'Fix payment gateway timeout','Users report intermittent 504 errors during checkout on mobile Safari.','urgent',3,[0,3],'2026-04-05',4,2],
[0,'Design onboarding flow v2','New 5-step onboarding with progress indicator and skip option.','high',2,[2],'2026-04-10',8,3],
[0,'Add SSO integration for enterprise','Support SAML and OIDC providers. Okta, Azure AD, Google Workspace.','medium',1,[1],'2026-04-15',16,0],
[0,'Write API documentation','OpenAPI 3.0 spec for all public endpoints.','low',4,[4],'2026-04-20',6,0],
[1,'Implement dark mode toggle','System preference detection + manual override.','medium',2,[1,2],'2026-04-08',3,2],
[1,'Optimize image loading pipeline','Lazy loading, WebP conversion, responsive srcsets.','high',3,[1],'2026-04-07',5,4],
[1,'Refactor auth middleware','JWT validation, refresh token rotation, rate limiting.','medium',1,[1],'2026-04-09',6,1],
[2,'Dashboard analytics widgets','Revenue chart, user growth, conversion funnel.','medium',4,[1,2],'2026-04-06',5,5],
[3,'Set up CI/CD pipeline','GitHub Actions with staging and production deployments.','high',1,[1],'2026-04-03',4,4],
[3,'Fix email notification formatting','HTML templates breaking on Outlook. Switched to table-based layout.','medium',3,[0],'2026-04-02',2,1.5],
[3,'Add rate limiting to API','Express-rate-limit with Redis backend.','low',1,[1],'2026-04-01',3,2],
];
const cids1 = [];
cards1.forEach((c,i) => {
const [list,title,desc,pri,assign,labs,due,est,spent] = c;
const r = cardIns.run(l1ids[list],title,desc,i*65536,pri,assign,assign,due,est,spent);
const cid = r.lastInsertRowid;
cids1.push(cid);
labs.forEach(l => clIns.run(cid, lab1ids[l]));
actIns.run(cid, assign, 'created', '');
});
commIns.run(cids1[0], 3, 'I can reproduce this on iPhone 15 Pro with Safari 17.4. Request hangs ~30s then 504.');
commIns.run(cids1[0], 1, 'Stripe webhook is timing out. Check the timeout config in our payment service.');
commIns.run(cids1[0], 2, 'Related to the issue we saw in staging last week. Gateway config was set to 10s timeout.');
commIns.run(cids1[4], 2, 'Using CSS custom properties with prefers-color-scheme. Manual toggle in localStorage.');
commIns.run(cids1[4], 1, 'Great approach. Make sure email templates also respect the preference.');
// Board 2: Marketing
const b2 = bIns.run('Marketing Campaigns', 'Q2 marketing initiatives and content calendar', 'gradient-pink', 2);
const b2id = b2.lastInsertRowid;
[1,2,4].forEach((uid,i) => bmIns.run(b2id, uid, i===0?'admin':'member'));
const l2ids = ['📝 Ideas','🎨 Creating','📅 Scheduled','📊 Analytics'].map((t,i) => listIns.run(b2id,t,i*65536).lastInsertRowid);
[
[0,'Product demo video series','medium',2],
[0,'Blog: 10 Tips for Remote Teams','low',4],
[1,'Social media launch graphics','high',2],
[1,'Email drip campaign for onboarding','medium',4],
[2,'Launch announcement blog post','high',2],
[3,'March analytics review','low',4],
].forEach(([list,title,pri,assign],i) => cardIns.run(l2ids[list],title,'',i*65536,pri,assign,assign,null,0,0));
// Board 3: Bug Tracker
const b3 = bIns.run('Bug Tracker', 'Open bugs and issue triage', 'gradient-red', 3);
const b3id = b3.lastInsertRowid;
[1,3].forEach((uid,i) => bmIns.run(b3id, uid, i===0?'admin':'member'));
const l3ids = ['🐛 New','🔧 Fixing','✅ Resolved'].map((t,i) => listIns.run(b3id,t,i*65536).lastInsertRowid);
[
[0,'Memory leak in dashboard widgets','high',3],
[0,'CSV export truncates at 10k rows','medium',1],
[1,'Login redirect loop on Safari','urgent',3],
[2,'Tooltip z-index overlap on modals','low',1],
].forEach(([list,title,pri,assign],i) => cardIns.run(l3ids[list],title,'',i*65536,pri,assign,assign,null,0,0));
db.prepare('INSERT OR REPLACE INTO email_config (id, smtp_host, smtp_port, email, app_password) VALUES (1,?,?,?,?)')
.run('smtp.gmail.com', 587, 'demo@teamflow.app', 'demo-app-password');
const jwt = require('jsonwebtoken');
const token = jwt.sign({id:1,email:'admin@teamflow.local',role:'admin'},'teamflow-secret-change-in-production',{expiresIn:'365d'});
console.log('OK');
console.log('TOKEN:' + token);