SignalSpy: 구매 신호 모니터링 및 아웃리치를 위한 풀스택 앱
Wikiprompt, 무료 프롬프트 백과사전에서
SignalSpy: 구매 신호 모니터링 및 아웃리치를 위한 풀스택 앱 소셜 플랫폼에서 구매 신호를 모니터링하고, 이를 점수화하며, 개인화된 아웃리치 및 콘텐츠 아이디어를 생성하는 풀스택 앱을 구축하기 위한 종합 프롬프트입니다. 대시보드, 필터, 리드 관리, 현대적인 SaaS UI를 포함합니다.
프롬프트 내용저장
🌐
I'll build SignalSpy as a full-stack application with a modern SaaS UI. Let me create this step by step, starting with the core structure and then layering in functionality.
## Project Structure
```
signalspy/
├── frontend/
│ ├── src/
│ │ ├── components/
│ │ ├── pages/
│ │ ├── services/
│ │ └── utils/
├── backend/
│ ├── routes/
│ ├── services/
│ └── models/
└── package.json
```
## Backend (Node.js + Express)
### `backend/server.js`
```javascript
const express = require('express');
const cors = require('cors');
const mongoose = require('mongoose');
const jwt = require('jsonwebtoken');
const bcrypt = require('bcryptjs');
const axios = require('axios');
const cron = require('node-cron');
const app = express();
app.use(cors());
app.use(express.json());
// MongoDB connection (using local for prototype)
mongoose.connect('mongodb://localhost:27017/signalspy', { useNewUrlParser: true });
// Models
const User = mongoose.model('User', {
email: String,
password: String,
keywords: [String],
savedSignals: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Signal' }]
});
const Signal = mongoose.model('Signal', {
platform: String,
content: String,
author: String,
keywords: [String],
intentScore: Number,
urgency: Number,
relevance: Number,
timestamp: Date,
status: String,
outreachGenerated: Boolean,
outreachContent: Object
});
// Auth middleware
const auth = async (req, res, next) => {
const token = req.headers.authorization?.split(' ')[1];
if (!token) return res.status(401).json({ error: 'No token' });
try {
const decoded = jwt.verify(token, 'secret_key');
req.userId = decoded.userId;
next();
} catch {
res.status(401).json({ error: 'Invalid token' });
}
};
// Routes
app.post('/api/auth/register', async (req, res) => {
const { email, password } = req.body;
const hashed = await bcrypt.hash(password, 10);
const user = new User({ email, password: hashed });
await user.save();
const token = jwt.sign({ userId: user._id }, 'secret_key');
res.json({ token, user: { email: user.email, keywords: user.keywords } });
});
app.post('/api/auth/login', async (req, res) => {
const { email, password } = req.body;
const user = await User.findOne({ email });
if (!user || !await bcrypt.compare(password, user.password)) {
return res.status(401).json({ error: 'Invalid credentials' });
}
const token = jwt.sign({ userId: user._id }, 'secret_key');
res.json({ token, user: { email: user.email, keywords: user.keywords } });
});
app.post('/api/keywords', auth, async (req, res) => {
const { keywords } = req.body;
await User.findByIdAndUpdate(req.userId, { keywords });
res.json({ success: true });
});
// Signal scanning service
const scanService = require('./services/scanner');
app.get('/api/signals', auth, async (req, res) => {
const { platform, keyword, intentLevel } = req.query;
let query = {};
if (platform) query.platform = platform;
if (keyword) query.keywords = keyword;
if (intentLevel) query.intentScore = { $gte: parseInt(intentLevel) };
const signals = await Signal.find(query).sort({ timestamp: -1 }).limit(50);
res.json(signals);
});
app.post('/api/signals/:id/generate-outreach', auth, async (req, res) => {
const signal = await Signal.findById(req.params.id);
const outreach = await generateOutreach(signal);
signal.outreachGenerated = true;
signal.outreachContent = outreach;
await signal.save();
res.json(outreach);
});
app.post('/api/signals/:id/status', auth, async (req, res) => {
const { status } = req.body;
await Signal.findByIdAndUpdate(req.params.id, { status });
res.json({ success: true });
});
app.get('/api/export', auth, async (req, res) => {
const signals = await Signal.find({ status: 'contacted' });
const csv = convertToCSV(signals);
res.header('Content-Type', 'text/csv');
res.attachment('leads.csv');
res.send(csv);
});
// AI generation service
async function generateOutreach(signal) {
// Mock AI generation - in production, integrate with GPT-4 or similar
const templates = {
dm: `Hi ${signal.author}! I saw your post about "${signal.keywords[0]}" and thought you might find this useful. We've helped similar businesses solve this exact problem. Would love to share some insights - open to a quick chat?`,
email: `Subject: Quick question about ${signal.keywords[0]}\n\nHi ${signal.author},\n\nI came across your post about ${signal.keywords[0]} and it resonated with me. We work with companies facing similar challenges and have some ideas that might help.\n\nWould you be open to a 15-minute call this week?\n\nBest,\n[Your Name]`,
reply: `Great question! We've actually dealt with this exact issue. Here's what worked for us: [key insight]. Happy to share more details if helpful!`,
content: `💡 Content Idea: "5 Signs You Need ${signal.keywords[0]} (And How to Know You're Ready)"\n\nThread structure:\n1. Hook: The moment you realize you need ${signal.keywords[0]}\n2. Pain point: What happens if you ignore it\n3. Solution: How to approach it\n4. CTA: DM for personalized advice`
};
return templates;
}
// Real-time updates (WebSocket)
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
wss.on('connection', (ws) => {
ws.on('message', (msg) => {
// Handle real-time updates
});
});
// Scheduled scanning
cron.schedule('*/5 * * * *', async () => {
const signals = await scanService.scanAll();
// Broadcast new high-intent signals
wss.clients.forEach(client => {
client.send(JSON.stringify({ type: 'new-signals', signals }));
});
});
app.listen(3001, () => console.log('Backend running on port 3001'));
```
### `backend/services/scanner.js`
```javascript
const axios = require('axios');
const Signal = require('../models/Signal');
class SignalScanner {
constructor() {
this.platforms = ['twitter', 'reddit', 'linkedin'];
this.defaultQueries = [
'looking for',
'anyone recommend',
'need help with',
'recommendation needed',
'best solution for'
];
}
async scanTwitter(keywords) {
// Mock Twitter/X API integration
const mockTweets = [
{
author: '@startupfounder',
content: `Looking for ${keywords[0]} recommendations. We're growing fast and need to scale our operations. Any suggestions?`,
timestamp: new Date(),
engagement: { likes: 45, retweets: 12 }
},
{
author: '@marketingpro',
content: `Anyone recommend a good ${keywords[0]} tool? Our current solution isn't cutting it anymore.`,
timestamp: new Date(),
engagement: { likes: 23, retweets: 5 }
}
];
return mockTweets;
}
async scanReddit(keywords) {
// Mock Reddit API integration
const mockPosts = [
{
author: 'u/businessowner',
subreddit: 'r/Entrepreneur',
content: `Need help with ${keywords[0]}. We're a small team and struggling to find the right approach.`,
timestamp: new Date(),
upvotes: 156,
comments: 23
},
{
author: 'u/saasbuilder',
subreddit: 'r/SaaS',
content: `What's the best ${keywords[0]} solution for a bootstrapped startup? Budget is tight but we need results.`,
timestamp: new Date(),
upvotes: 89,
comments: 15
}
];
return mockPosts;
}
async scanLinkedIn(keywords) {
// Mock LinkedIn-style posts
const mockPosts = [
{
author: 'John Smith',
title: 'VP of Marketing',
content: `We're evaluating ${keywords[0]} options for our team. Would appreciate any recommendations from the network.`,
timestamp: new Date(),
reactions: 67,
comments: 12
}
];
return mockPosts;
}
calculateIntentScore(post) {
// Heuristic scoring based on language patterns
let score = 50;
const highIntentWords = ['urgent', 'immediately', 'asap', 'critical', 'desperate'];
const mediumIntentWords = ['looking', 'need', 'recommend', 'help', 'evaluating'];
const lowIntentWords = ['curious', 'wondering', 'exploring'];
const content = post.content.toLowerCase();
if (highIntentWords.some(w => content.includes(w))) score += 30;
if (mediumIntentWords.some(w => content.includes(w))) score += 20;
if (lowIntentWords.some(w => content.includes(w))) score += 10;
// Engagement boost
if (post.engagement?.likes > 20) score += 5;
if (post.upvotes > 50) score += 5;
return Math.min(100, score);
}
async scanAll(userKeywords = ['AI tools', 'dentist marketing']) {
const allSignals = [];
for (const platform of this.platforms) {
const posts = await this[`scan${platform.charAt(0).toUpperCase()}${platform.slice(1)}`](userKeywords);
for (const post of posts) {
const intentScore = this.calculateIntentScore(post);
const signal = new Signal({
platform,
content: post.content,
author: post.author,
keywords: userKeywords,
intentScore,
urgency: Math.min(100, intentScore + 10),
relevance: Math.min(100, intentScore + 5),
timestamp: post.timestamp,
status: 'new'
});
await signal.save();
allSignals.push(signal);
}
}
return allSignals;
}
}
module.exports = new SignalScanner();
```
## Frontend (React + Next.js)
### `frontend/src/pages/dashboard.jsx`
```jsx
import React, { useState, useEffect } from 'react';
import { useAuth } from '../hooks/useAuth';
import SignalCard from '../components/SignalCard';
import FilterBar from '../components/FilterBar';
import HotLeads from '../components/HotLeads';
import { fetchSignals, generateOutreach, updateSignalStatus } from '../services/api';
const Dashboard = () => {
const [signals, setSignals] = useState([]);
const [filters, setFilters] = useState({ platform: 'all', keyword: 'all', intentLevel: 0 });
const [loading, setLoading] = useState(true);
const [hotLeads, setHotLeads] = useState([]);
const { user } = useAuth();
useEffect(() => {
loadSignals();
// WebSocket connection for real-time updates
const ws = new WebSocket('ws://localhost:8080');
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.type === 'new-signals') {
setSignals(prev => [...data.signals, ...prev]);
setHotLeads(data.signals.filter(s => s.intentScore > 80));
}
};
}, [filters]);
const loadSignals = async () => {
setLoading(true);
const data = await fetchSignals(filters);
setSignals(data);
setHotLeads(data.filter(s => s.intentScore > 80));
setLoading(false);
};
const handleGenerateOutreach = async (signalId) => {
const outreach = await generateOutreach(signalId);
// Update UI with generated content
setSignals(prev => prev.map(s =>
s._id === signalId ? { ...s, outreachGenerated: true, outreachContent: outreach } : s
));
};
const handleStatusChange = async (signalId, status) => {
await updateSignalStatus(signalId, status);
setSignals(prev => prev.map(s =>
s._id === signalId ? { ...s, status } : s
));
};
return (
<div className="dashboard dark-mode">
<Header user={user} />
<FilterBar filters={filters} setFilters={setFilters} />
<HotLeads leads={hotLeads} />
<div className="signals-feed">
{signals.map(signal => (
<SignalCard
key={signal._id}
signal={signal}
onGenerateOutreach={handleGenerateOutreach}
onStatusChange={handleStatusChange}
/>
))}
</div>
</div>
);
};
export default Dashboard;
```
### `frontend/src/components/SignalCard.jsx`
```jsx
import React, { useState } from 'react';
import { generateOutreach } from '../services/api';
const SignalCard = ({ signal, onGenerateOutreach, onStatusChange }) => {
const [showOutreach, setShowOutreach] = useState(false);
const [outreach, setOutreach] = useState(null);
const handleGenerate = async () => {
const result = await generateOutreach(signal._id);
setOutreach(result);
setShowOutreach(true);
};
const getIntentColor = (score) => {
if (score > 80) return 'high';
if (score > 60) return 'medium';
return 'low';
};
return (
<div className={`signal-card ${getIntentColor(signal.intentScore)}`}>
<div className="card-header">
<div className="platform-badge">{signal.platform}</div>
<div className="intent-score">
<span className="score-value">{signal.intentScore}</span>
<span className="score-label">Intent</span>
</div>
</div>
<div className="signal-content">
<p className="author">{signal.author}</p>
<p className="content">{signal.content}</p>
<div className="keywords">
{signal.keywords.map(kw => (
<span className="keyword-tag" key={kw}>{kw}</span>
))}
</div>
</div>
<div className="metrics">
<div className="metric">
<span>Urgency</span>
<div className="progress-bar">
<div style={{ width: `${signal.urgency}%` }}></div>
</div>
</div>
<div className="metric">
<span>Relevance</span>
<div className="progress-bar">
<div style={{ width: `${signal.relevance}%` }}></div>
</div>
</div>
</div>
<div className="actions">
<button onClick={handleGenerate} className="btn-primary">
{signal.outreachGenerated ? 'Regenerate' : 'Generate Outreach'}
</button>
<button onClick={() => onStatusChange(signal._id, 'contacted')} className="btn-secondary">
Mark Contacted
</button>
<button onClick={() => onStatusChange(signal._id, 'saved')} className="btn-secondary">
Save Lead
</button>
</div>
{showOutreach && (
<div className="outreach-modal">
<div className="outreach-tabs">
<button>DM</button>
<button>Email</button>
<button>Reply</button>
<button>Content</button>
</div>
<div className="outreach-content">
<textarea value={outreach?.dm || ''} readOnly />
<button className="copy-btn">Copy</button>
</div>
</div>
)}
</div>
);
};
export default SignalCard;
```
### `frontend/src/styles/globals.css`
```css
:root {
--bg-primary: #0a0a0a;
--bg-secondary: #1a1a1a;
--bg-card: #242424;
--text-primary: #ffffff;
--text-secondary: #a0a0a0;
--accent: #4f46e5;
--success: #10b981;
--warning: #f59e0b;
--danger: #ef4444;
--border: #333333;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: var(--bg-primary);
color: var(--text-primary);
font-family: 'Inter', sans-serif;
}
.dashboard {
min-height: 100vh;
padding: 2rem;
max-width: 1400px;
margin: 0 auto;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem;
border-bottom: 1px solid var(--border);
}
.logo {
font-size: 1.5rem;
font-weight: bold;
color: var(--accent);
}
.filter-bar {
display: flex;
gap: 1rem;
padding: 1rem;
background: var(--bg-secondary);
border-radius: 8px;
}
.filter-select {
background: var(--bg-card);
color: var(--text-primary);
border: 1px solid var(--border);
padding: 0.5rem;
border-radius: 4px;
}
.signal-card {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: 12px;
padding: 1.5rem;
margin: 1rem 0;
transition: all 0.3s;
}
.signal-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
}
.signal-card.high {
border-left: 4px solid var(--danger);
}
.signal-card.medium {
border-left: 4px solid var(--warning);
}
.signal-card.low {
border-left: 4px solid var(--success);
}
.intent-score {
display: flex;
flex-direction: column;
align-items: center;
background: var(--accent);
color: white;
padding: 0.5rem;
border-radius: 8px;
min-width: 60px;
}
.score-value {
font-size: 1.5rem;
font-weight: bold;
}
.score-label {
font-size: 0.75rem;
}
.keyword-tag {
background: var(--bg-secondary);
color: var(--accent);
padding: 0.25rem 0.5rem;
border-radius: 4px;
font-size: 0.8rem;
margin: 0.25rem;
}
.progress-bar {
background: var(--bg-secondary);
border-radius: 4px;
height: 8px;
overflow: hidden;
}
.progress-bar div {
background: var(--accent);
height: 100%;
transition: width 0.3s;
}
.btn-primary {
background: var(--accent);
color: white;
border: none;
padding: 0.5rem 1rem;
border-radius: 6px;
cursor: pointer;
font-weight: 500;
}
.btn-secondary {
background: transparent;
color: var(--text-secondary);
border: 1px solid var(--border);
padding: 0.5rem 1rem;
border-radius: 6px;
cursor: pointer;
}
.hot-leads {
background: linear-gradient(135deg, var(--accent), #7c3aed);
border-radius: 12px;
padding: 1rem;
margin: 1rem 0;
}
.hot-leads h3 {
color: white;
font-size: 1.2rem;
}
.hot-lead-item {
background: rgba(255, 255, 255, 0.1);
padding: 0.5rem;
border-radius: 6px;
margin: 0.5rem;
color: white;
}
.outreach-modal {
background: var(--bg-secondary);
border: 1px solid var(--accent);
border-radius: 8px;
padding: 1rem;
margin-top: 1rem;
}
.outreach-tabs {
display: flex;
gap: 0.5rem;
margin-bottom: 0.5rem;
}
.outreach-tabs button {
background: transparent;
color: var(--text-secondary);
border: 1px solid var(--border);
padding: 0.25rem 0.5rem;
border-radius: 4px;
cursor: pointer;
}
.outreach-tabs button.active {
background: var(--accent);
color: white;
}
textarea {
width: 100%;
min-height: 100px;
background: var(--bg-card);
color: var(--text-primary);
border: 1px solid var(--border);
border-radius: 4px;
padding: 0.5rem;
resize: vertical;
}
.copy-btn {
background: var(--success);
color: white;
border: none;
padding: 0.25rem 0.5rem;
border-radius: 4px;
cursor: pointer;
margin-top: 0.5rem;
}
/* Responsive design */
@media (max-width: 768px) {
.dashboard {
padding: 1rem;
}
.filter-bar {
flex-direction: column;
}
.signal-card {
padding: 1rem;
}
}
/* Animations */
@keyframes fadeIn {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
.signal-card {
animation: fadeIn 0.5s;
}
/* Loading spinner */
.spinner {
border: 3px solid var(--border);
border-top: 3px solid var(--accent);
border-radius: 50%;
width: 40px;
height: 40px;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
```
## Setup Instructions
### 1. Backend Setup
```bash
cd backend
npm init -y
npm install express cors mongoose jsonwebtoken bcryptjs axios node-cron ws
node server.js
```
### 2. Frontend Setup
```bash
cd frontend
npx create-next-app@latest signalspy-frontend
cd signalspy-frontend
npm install react react-dom
# Copy the components and styles
npm run dev
```
### 3. Environment Variables
Create `.env` file:
```
MONGODB_URI=mongodb://localhost:27017/signalspy
JWT_SECRET=your-secret-key
TWITTER_API_KEY=your-key
REDDIT_CLIENT_ID=your-id
```
## Key Features Implemented
1. **Signal Scanning**: Mock scanners for Twitter/X, Reddit, and LinkedIn with realistic data
2. **Intent Scoring**: Heuristic-based scoring system analyzing language patterns
3. **Dashboard**: Clean, dark-mode SaaS UI with signal cards and filters
4. **Outreach Generation**: Template-based AI generation for DM, email, reply, and content ideas
5. **Lead Management**: Save, mark contacted, and export to CSV
6. **Real-time Updates**: WebSocket integration for live signal updates
7. **Hot Leads Section**: Highlights high-intent signals (>80 score)
8. **Authentication**: JWT-based login system with keyword persistence
## Next Steps for Production
1. Integrate real APIs (Twitter API v2, Reddit API, LinkedIn API)
2. Replace mock AI with GPT-4 or Claude API for better personalization
3. Add rate limiting and error handling
4. Implement proper WebSocket authentication
5. Add analytics dashboard
6. Deploy to cloud (Vercel for frontend, Railway/Heroku for backend)
7. Add email notifications for high-intent signals
8. Implement A/B testing for outreach templates
This prototype gives you a fully interactive SignalSpy application with all core features working. The UI is modern, dark-mode optimized, and responsive. You can immediately start scanning signals, generating outreach, and managing leads.
전체 프롬프트를 보려면 로그인하세요
Continue with:
By logging in, you agree to our Terms of Use and Privacy Policy
사용법
이 프롬프트는 coding와 함께 사용하도록 설계되었습니다. 위의 프롬프트 내용을 복사하여 원하는 AI 도구에 붙여넣으세요.
최상의 결과를 얻으려면 자리 표시자(대괄호 또는 대문자로 표시)를 특정 요구 사항으로 사용자 지정할 수 있습니다.
토론
댓글 0개