SignalSpy: Full-Stack-App zur Überwachung von Kaufsignalen und Kontaktaufnahme
Von Wikiprompt, der freien Prompt-Enzyklopädie
SignalSpy: Full-Stack-App zur Überwachung von Kaufsignalen und Kontaktaufnahme Ein umfassender Prompt zur Erstellung einer Full-Stack-App, die soziale Plattformen auf Kaufsignale überwacht, diese bewertet und personalisierte Outreach- und Content-Ideen generiert. Enthält Dashboard, Filter, Lead-Management und moderne SaaS-UI.
Prompt-InhaltSpeichern
🌐
I'll build a full-stack SignalSpy application for you. Let me create a comprehensive working prototype with all the features you requested.
```jsx
// File: package.json
{
"name": "signalspy",
"version": "1.0.0",
"description": "Monitor online platforms and surface high-intent buying signals",
"main": "server.js",
"scripts": {
"start": "node server.js",
"dev": "nodemon server.js"
},
"dependencies": {
"express": "^4.18.2",
"cors": "^2.8.5",
"dotenv": "^16.0.3",
"jsonwebtoken": "^9.0.0",
"bcryptjs": "^2.4.3",
"mongoose": "^7.0.3",
"openai": "^3.3.0",
"axios": "^1.4.0",
"csv-writer": "^1.6.0"
},
"devDependencies": {
"nodemon": "^2.0.22"
}
}
```
```jsx
// File: server.js
const express = require('express');
const cors = require('cors');
const mongoose = require('mongoose');
const dotenv = require('dotenv');
const authRoutes = require('./routes/auth');
const signalRoutes = require('./routes/signals');
const outreachRoutes = require('./routes/outreach');
dotenv.config();
const app = express();
// Middleware
app.use(cors());
app.use(express.json());
// Connect to MongoDB
mongoose.connect(process.env.MONGODB_URI || 'mongodb://localhost:27017/signalspy', {
useNewUrlParser: true,
useUnifiedTopology: true
});
// Routes
app.use('/api/auth', authRoutes);
app.use('/api/signals', signalRoutes);
app.use('/api/outreach', outreachRoutes);
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => {
console.log(`SignalSpy server running on port ${PORT}`);
});
```
```jsx
// File: models/User.js
const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');
const UserSchema = new mongoose.Schema({
email: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
},
keywords: [{
type: String,
default: ['AI tools', 'dentist marketing', 'SaaS']
}],
savedSignals: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Signal'
}],
createdAt: {
type: Date,
default: Date.now
}
});
UserSchema.pre('save', async function(next) {
if (!this.isModified('password')) return next();
const salt = await bcrypt.genSalt(10);
this.password = await bcrypt.hash(this.password, salt);
next();
});
module.exports = mongoose.model('User', UserSchema);
```
```jsx
// File: models/Signal.js
const mongoose = require('mongoose');
const SignalSchema = new mongoose.Schema({
platform: {
type: String,
enum: ['Twitter', 'Reddit', 'LinkedIn'],
required: true
},
content: {
type: String,
required: true
},
author: {
type: String,
required: true
},
url: {
type: String
},
intentScore: {
type: Number,
min: 0,
max: 100,
default: 50
},
urgencyScore: {
type: Number,
min: 0,
max: 100,
default: 50
},
relevanceScore: {
type: Number,
min: 0,
max: 100,
default: 50
},
keywords: [String],
painPoints: [String],
status: {
type: String,
enum: ['new', 'contacted', 'saved', 'converted'],
default: 'new'
},
createdAt: {
type: Date,
default: Date.now
}
});
module.exports = mongoose.model('Signal', SignalSchema);
```
```jsx
// File: routes/auth.js
const express = require('express');
const router = express.Router();
const User = require('../models/User');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
// Register
router.post('/register', async (req, res) => {
try {
const { email, password } = req.body;
// Check if user exists
let user = await User.findOne({ email });
if (user) {
return res.status(400).json({ message: 'User already exists' });
}
// Create user
user = new User({ email, password });
await user.save();
// Generate token
const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET || 'secret', { expiresIn: '7d' });
res.status(201).json({ token, user: { id: user._id, email: user.email } });
} catch (error) {
console.error(error);
res.status(500).json({ message: 'Server error' });
}
});
// Login
router.post('/login', async (req, res) => {
try {
const { email, password } = req.body;
// Check if user exists
const user = await User.findOne({ email });
if (!user) {
return res.status(400).json({ message: 'Invalid credentials' });
}
// Check password
const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch) {
return res.status(400).json({ message: 'Invalid credentials' });
}
// Generate token
const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET || 'secret', { expiresIn: '7d' });
res.json({ token, user: { id: user._id, email: user.email } });
} catch (error) {
console.error(error);
res.status(500).json({ message: 'Server error' });
}
});
module.exports = router;
```
```jsx
// File: routes/signals.js
const express = require('express');
const router = express.Router();
const Signal = require('../models/Signal');
const User = require('../models/User');
const auth = require('../middleware/auth');
// Generate mock signals (simulating platform scanning)
const generateMockSignals = () => {
const signals = [
{
platform: 'Twitter',
content: 'Looking for an AI tool that can help me automate my customer support. Anyone have recommendations?',
author: '@techfounder',
intentScore: 85,
urgencyScore: 70,
relevanceScore: 90,
keywords: ['AI tools', 'automation'],
painPoints: ['manual support', 'time consuming']
},
{
platform: 'Reddit',
content: 'Need help with marketing my dental practice. Currently using Google Ads but not getting results. Any advice?',
author: 'u/dentist_pro',
intentScore: 75,
urgencyScore: 60,
relevanceScore: 85,
keywords: ['dentist marketing'],
painPoints: ['poor ROI', 'ineffective ads']
},
{
platform: 'LinkedIn',
content: 'Our SaaS platform is growing but we need better lead generation. Anyone recommend a good B2B marketing agency?',
author: 'Sarah Chen - CEO at TechFlow',
intentScore: 80,
urgencyScore: 65,
relevanceScore: 88,
keywords: ['SaaS', 'lead generation'],
painPoints: ['low conversion', 'scaling challenges']
},
{
platform: 'Twitter',
content: 'Anyone recommend a good CRM for a small business? Need something affordable and easy to use.',
author: '@smallbizowner',
intentScore: 65,
urgencyScore: 50,
relevanceScore: 70,
keywords: ['CRM', 'small business'],
painPoints: ['budget constraints', 'complexity']
},
{
platform: 'Reddit',
content: 'Looking for a social media management tool that can handle multiple accounts. Suggestions?',
author: 'u/marketer_jane',
intentScore: 70,
urgencyScore: 45,
relevanceScore: 75,
keywords: ['social media tools'],
painPoints: ['time management', 'multiple platforms']
}
];
return signals;
};
// Get all signals with filters
router.get('/', auth, async (req, res) => {
try {
const { platform, keyword, intentLevel } = req.query;
// Use mock data for prototype
let signals = generateMockSignals();
// Apply filters
if (platform) {
signals = signals.filter(s => s.platform === platform);
}
if (keyword) {
signals = signals.filter(s =>
s.keywords.some(k => k.toLowerCase().includes(keyword.toLowerCase())) ||
s.content.toLowerCase().includes(keyword.toLowerCase())
);
}
if (intentLevel) {
const threshold = parseInt(intentLevel);
signals = signals.filter(s => s.intentScore >= threshold);
}
res.json(signals);
} catch (error) {
console.error(error);
res.status(500).json({ message: 'Server error' });
}
});
// Save signal to user's list
router.post('/:id/save', auth, async (req, res) => {
try {
const user = await User.findById(req.user.id);
const signalId = req.params.id;
if (!user.savedSignals.includes(signalId)) {
user.savedSignals.push(signalId);
await user.save();
}
res.json({ message: 'Signal saved successfully' });
} catch (error) {
console.error(error);
res.status(500).json({ message: 'Server error' });
}
});
// Update signal status
router.patch('/:id/status', auth, async (req, res) => {
try {
const { status } = req.body;
const signal = await Signal.findByIdAndUpdate(
req.params.id,
{ status },
{ new: true }
);
res.json(signal);
} catch (error) {
console.error(error);
res.status(500).json({ message: 'Server error' });
}
});
module.exports = router;
```
```jsx
// File: routes/outreach.js
const express = require('express');
const router = express.Router();
const auth = require('../middleware/auth');
// Generate outreach content based on signal
router.post('/generate', auth, async (req, res) => {
try {
const { signal, type } = req.body;
const generatedContent = generateOutreach(signal, type);
res.json(generatedContent);
} catch (error) {
console.error(error);
res.status(500).json({ message: 'Server error' });
}
});
// Generate outreach content
const generateOutreach = (signal, type) => {
const templates = {
'cold-dm': {
Twitter: `Hey ${signal.author}, saw your tweet about ${signal.keywords[0]}. I've helped similar businesses solve this exact problem. Would love to share some ideas that could help.`,
Reddit: `Hi ${signal.author}, I noticed your post about ${signal.keywords[0]}. I have experience solving this exact issue and would be happy to share what worked for us.`,
LinkedIn: `Hi ${signal.author}, I came across your post about ${signal.keywords[0]}. We specialize in this area and have helped several companies achieve great results. Would you be open to a quick chat?`
},
'email': {
subject: `Quick question about ${signal.keywords[0]}`,
body: `Hi ${signal.author},\n\nI noticed you're looking for ${signal.keywords[0]}. We've helped businesses like yours achieve significant improvements in this area.\n\nWould you be open to a 15-minute call to discuss how we might help?\n\nBest regards,\n[Your Name]`
},
'public-reply': {
Twitter: `Great question! We've seen great results with this approach for ${signal.keywords[0]}. Happy to share what we've learned.`,
Reddit: `We've dealt with this exact issue with ${signal.keywords[0]}. Here's what worked for us: [specific tip]. Hope this helps!`,
LinkedIn: `Excellent question about ${signal.keywords[0]}! We've helped several companies in this space. Happy to share our insights.`
},
'content-idea': {
tweet: `How we helped a client achieve 3x ROI with ${signal.keywords[0]} in just 30 days 🚀 #${signal.keywords[0].replace(/\s/g, '')} #marketing`,
thread: `🧵 Thread: How to solve ${signal.keywords[0]} problems\n\n1/ The biggest mistake we see businesses make...\n2/ Here's the framework we use...\n3/ Real example from a recent client...`,
video: `Video: "The Complete Guide to ${signal.keywords[0]} in 2024" - covering common mistakes, best practices, and real case studies`
}
};
return templates[type] || templates['cold-dm'];
};
module.exports = router;
```
```jsx
// File: middleware/auth.js
const jwt = require('jsonwebtoken');
module.exports = function(req, res, next) {
const token = req.header('x-auth-token');
if (!token) {
return res.status(401).json({ message: 'No token, authorization denied' });
}
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET || 'secret');
req.user = decoded;
next();
} catch (error) {
res.status(401).json({ message: 'Token is not valid' });
}
};
```
```jsx
// File: pages/index.js (Next.js frontend)
import { useState, useEffect } from 'react';
import { useRouter } from 'next/router';
import Header from '../components/Header';
import Dashboard from '../components/Dashboard';
import SignalCard from '../components/SignalCard';
import Filters from '../components/Filters';
import OutreachModal from '../components/OutreachModal';
import AuthModal from '../components/AuthModal';
import styles from '../styles/Home.module.css';
export default function Home() {
const [signals, setSignals] = useState([]);
const [filteredSignals, setFilteredSignals] = useState([]);
const [filters, setFilters] = useState({
platform:: '',
keyword: '',
intentLevel:: ''
});
const [selectedSignal, setSelectedSignal] = useState(null);
const [showAuth, setShowAuth] = useState(false);
const [isAuthenticated, setIsAuthenticated] = = useState(false);
const [loading, setLoading] = useState(true);
const router = useRouter();
useEffect(() => {
// Check authentication
const token = localStorage.getItem('token');
if (token) {
setIsAuthenticated(true);
}
// Fetch signals
fetchSignals();
}, []);
const fetchSignals = async () => {
try {
const response = await fetch('/api/signals', {
headers: {
'x-auth-token': localStorage.getItem('token')
}
});
const data = await response.json();
setSignals(data);
setFilteredSignals(data);
setLoading(false);
} catch (error) {
console.error('Error fetching signals:', error);
setLoading(false);
}
};
const handleFilterChange = ( newFilters) => {
setFilters(newFilters);
applyFilters(newFilters);
};
const applyFilters = (newFilters) => {
let filtered = [...signals];
if (newFilters.platform) {
filtered = filtered.filter(s => s.platform === newFilters.platform);
}
if (newFilters.keyword) {
filtered = filtered.filter(s =>
s.keywords.some(k => k.toLowerCase().includes(newFilters.keyword.toLowerCase())) ||
s.content.toLowerCase().includes(newFilters.keyword.toLowerCase())
);
}
if (newFilters.intentLevel) {
filtered = filtered.filter(s => s.intentScore >= parseInt(newFilters.intLevel));
}
setFilteredSignals(filtered);
};
const handleGenerateOutreach = (signal) => {
setSelectedSignal(signal);
};
const handleCloseModal = () => {
setSelectedSignal(null);
};
const handleExport = () => {
// Export functionality
const csvContent = signals.map(s => ({
Platform: s.platform,
Author:: s.author,
Content: s.content,
IntentScore:: s.intentScore,
UrgencyScore: s.urgencyScore,
RelevanceScore: s.relevanceScore
}));
// Convert to CSV and download
const csv = convertToCSV(csvContent);
const blob = new Blob([csv], { type: 'text/csv' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'signals.csv';
a.click();
};
const convertToCSV = (data) => {
const header = Object.keys(data[0]).join(',');
const rows = data.map(row => Object.values(row).join(','));
return [header, ...rows].join('\n');
};
return (
<div className={styles.container}>
<Header
isAuthenticated={isAuthenticated}
onAuthClick={() => setShowAuth(true)}
onExport={handleExport}
/>
<main className={styles.main}>
<Filters
filters={filters}
onFilterChange={handleFilterChange}
/>
<div className={styles.stats}>
<div className={styles.statCard}>
<h3>Total Signals</h3>
<p>{signals.length}</p>
</div>
<div className={styles.statCard}>
<h3>High Intent</h3>
<p>{signals.filter(s => s.intentScore >= 80).length}</p>
</div>
<div className={styles.statCard}>
<h3>Contacted</h3>
<p>{signals.filter(s => s.status === 'contacted').length}</p>
</div>
</div>
<div className={styles.signalGrid}>
{filteredSignals.map(signal => (
<SignalCard
key={signal.id}
signal={signal}
onGenerateOutreach={handleGenerateOutreach}
/>
))}
</div>
</main>
{selectedSignal && (
<OutreachModal
signal={selectedSignal}
onClose={handleCloseModal}
/>
)}
{showAuth && (
<AuthModal
onClose={() => setShowAuth(false)}
onAuthSuccess={() => {
setIsAuthenticated(true);
setShowAuth(false);
}}
/>
)}
</ div>
);
}
```
```jsx
// File: components/SignalCard.js
import { useState } from 'react';
import styles from './SignalCard.module.css';
export default function SignalCard({ signal, onGenerateOutreach }) {
const [status, setStatus] = useState(signal.status);
const getIntentColor = ( score) => {
if (score >= 80) return '#4CAF50';
if (score >= 60) return '#FFC107';
return '#F44336';
};
const handleStatusChange = (newStatus) => {
setStatus(newStatus);
// Update backend
fetch(`/api/signals/${signal.id}/status`, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
'x-auth-token': localStorage.getItem('token')
},
body: JSON.stringify({ status: newStatus })
});
};
return (
<div className={styles.card}>
<div className={styles.header}>
<span className={styles.platform}>{signal.platform}</span>
<span className={={styles.intentScore}} style={{ backgroundColor: getIntentColor(signal.intentScore) }}>
{signal.intentScore}%
</span>
</div>
<div className={styles.content}>
<p>{signal.content}</p>
</div>
<div className={styles.author}>
<span>{signal.author}</span>
</div>
<div className={styles.keywords}>
{signal.keywords.map(keyword => (
<span key={keyword} className={styles.keyword}>{keyword}</span>
))}
</div>
<div className={styles.actions}>
<button
className={styles.generateBtn}
onClick={() => onGenerateOutreach(signal)}
>
Generate Outreach
</button>
<select
value={status}
onChange={(e) => handleStatusChange(e.target.value)}
className={styles.statusSelect}
>
<option value="new">New</option>
<option value="contacted">Contacted</option>
<option value="saved">Saved</option>
<option value="converted">Converted</option>
</select>
</div>
</div>
);
}
```
```jsx
// File: components/OutreachModal.js
import { useState } from from 'react';
import styles from './OutreachModal.module.css';
export default function OutreachModal({ signal, onClose }) {
const [outreachType, setOutreachType] = useState('cold-dm');
const [content, setContent] = useState('');
const [loading, setLoading] = useState(false);
const [copied, setCopied] = useState(false);
const generateContent = async () => {
setLoading(true);
try {
const response = await fetch('/api/outreach/generate', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-auth-token': localStorage.getItem('token')
},
body: JSON.stringify({
signal: signal,
type: outreachType
})
});
const data = await response.json();
setContent(data);
setLoading(false);
} catch ( (error)) {
console.error('Error generating outreach:', error);
setLoading(false);
}
};
const handleCopy = () => => {
navigator.clipboard.writeText(content);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
};
return (
<div className={styles.modal}>
<div className={styles.modalContent}>
<div className={={styles.header}}>
<h2>Generate Outreach</h2>
<button onClick={onOnClose} className={styles.closeBtn}>×</button>
</</div>
<div className={={styles..originalPost}}>
<p><strong>Original Post:</strong> {signal.content}</p>
<p><strong>Author:</strong> {signal.author}</p>
</div>
<div className={styles.typeSelector}>
<select
value={outreachType}
onChange={(e) => {
setOutreachType(e.target.value);
setContent('');
}}
className={styles.select}
>
<option value="cold-dm">Cold DM</option>
<option value="email">Email Outreach</option>
<option value="public-reply">Public Reply</option>
<option value="content-idea">Content Idea</option>
</select>
<button
onClick={generateContent}
className={styles.generateBtn}
disabled={loading}
>
{loading ? 'Generating...' : : 'Generate'}
</</button>
</div>
{content && (
<<div className={={styles.output}}>
<div className={styles.outputHeader}>
<h3>Generated Content</h3>
<button onClick={handleCopy} className={styles.copyBtn}>
{copied ? 'Copied!' : : 'Copy'}
</button>
</</div>
<div className={styles.contentBody}>
{typeof content === 'string' ? (
<p>{content}</p>
) : (
<div>
{content.subject && <p><strong>Subject:</strong> {content.subject}</p>}
<p>{content.body || content}</p>
</div>
)}
</div>
</div>
)}
</div>
</</div>
);
}
```
```jsx
// File: components/Header.js
import styles from './Header.module.css';
export default function Header({ isAuthenticated, onAuthClick, onExport }) {
return (
<header className={styles.header}>
<div className={styles.logo}>
<span className={styles.logoIcon}>📡</span>
<h1>SignalSpSpy</h1>
</div>
<div className={styles.actions}>
{isAuthenticated && (
<button onClick={onExport} className={styles..exportBtn}>
Export CSV
</button>
)}
<button
onClick={onOnAuthClick} className={styles.authBtn}>
{isAuthenticated ? 'Logout' : 'Login'}
</button>
</div>
</header>
);
}
```
```jsx
// File: components/Filters.js
import { useState } from 'react';
import styles from './Filters.module.css';
export default function Filters({ filters, onFilterChange }) {
const [localFilters, setLocalFilters] = useState(filters);
const handleChange = ( (e) => => {
const newFilters = {
...localFilters,
[e.target.name]: e.target.value
};
setLocalFilters(newFilters);
onFilterChange(newFilters);
};
return (
<div className={styles.filters}>
<select
name="platform"
value={localFilters.platform}
onChange={handleChange}
className={styles.select}
>
<option value="">All Platforms</option>
<option value="Twitter">Twitter</option>
<option value="Reddit">Reddit</option>
<option value="LinkedIn">LinkedIn</option>
</select>
<input
type="text"
name="keyword"
value={localFilters.keyword}
onChange={handleChange}
placeholder="Search keywords..."
className={styles.input}
/>
<select
name="intentLevel"
value={localFilters.intentLevel}
onChange={handleChange}
className={styles.select}
>
<option value="">All Intent Levels</option>
<option value="80">High Intent</option>
<option value="60">Medium Intent</option>
<option value="40">Low Intent</option>
</select>
</div>
);
}
```
```jsx
// File: components/AuthModal.js
import { useState } from 'react';
import styles from './AuthModal.module.css';
export default function AuthModal({ onClose, onOnAuthSuccess }) {
const [isLogin, setIsLogin] = = useState(true);
const [email,, setEmail] = useState('');
const [password, setPassword]] = useState('');
const [error, setError] = useState('');
const [loading, setLoading] = = useState(false);
const handleSubmit = async (e) => => {
e.preventDefault();
setLoading(true);
setError('');
try {
const endpoint = isLogin ? '/api/auth/login' : '/api/auth/register';
const response = await fetch(endpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
email,
password
})
});
const data = await response.json();
if (response.ok) {
localStorage.setItem('token', data.token);
onAuthSuccess();
} else {
setError(data.message);
}
} catch (error) {
setError('Something went wrong. Please try again.');
}
setLoading(false);
};
return (
<div className={styles.modal}>
<div className={styles.modalContent}>
<div className={styles.header}>
<h2>{isLogin ? 'Login' : 'Sign Up'}</h2>
<button onClick={onClose} className={styles.closeBtn}>×</button>
</div>
<form onSubmit={handleSubmit} className={={styles.form}}>
<input
type="email"
placeholder="Email"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
className={styles.input}
/>
<input
type="password"
placeholder="Password"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
className={styles.input}
/>
{error && <p className={styles.error}>{error}</p>}
<button type="submit" className={styles.submitBtn} disabled={loading}>
{loading ? 'Please wait...' : isLogin ? 'Login' : 'Sign Up'}
</button>
</form>
<p className={styles.switchText}>
{isLogin ? "Don't have an account?" : "Already have an account?"}
<button onClick={() => setIsLogin(!isLogin)} className={styles.switchBtn}>
{isLogin ? 'Sign Up' : 'Login'}
</button>
</p>
</div>
</div>
);
}
```
```css
/* File:: styles/globals.css */
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
:root {
--primary: #6C63FF;
--secondary: #00BFA5;
--dark-bg: #0F0F1A;
--dark-card: #1A1A2E;
--dark-border: #2D2D44;
--text-primary: #E0E0E0;
--text-secondary: #9E9E9E;
---success: #4CAF50;
--warning: #FFC107;
--error: #F44336;
}
body {
background-color: var(--dark-bg);
color: var(--text-primary);
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
}
button {
cursor: pointer;
border: none;
background: none;
font-family: inherit;
}
select, input {
font-family: inherit;
font-size: 14px;
}
```
```css
/* File: styles/Home.module.css */
.container {
min-height: 100vh;
padding: 0 2rem;
}
.main {
padding: 2rem 0;
max-width: 1400px;
margin: 0 auto;
}
.stats {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1rem;
margin-bottom: 2rem;
}
.statCard {
background: var(--dark-card);
border: 1px solid var(--dark-border);
border-radius: 12px;
padding: 1.5rem;
text-align: center;
}
.statCard h3 {
color: var(--text-secondary);
font-size: 14px;
margin-bottom: 0.5rem;
}
.statCard p {
font-size: 2rem;
font-weight: bold;
color: var(--primary);
}
.signalGrid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
gap: 1.5rem;
}
```
```css
/* File: components/SignalCard.module.css */
.card {
background: var(--dark-card);
border: 1px solid var(--dark-border);
border-radius: 12px;
padding: 1.5rem;
transition: transform 0.2s, box-shadow 0.2s;
}
.card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1rem;
}
.platform {
background: var(--primary);
color: white;
padding: 4px 12px;
border-radius: 20px;
font-size: 12px;
font-weight: 600;
}
.intentScore {
padding: 4px 12px;
border-radius: 20px;
font-size: 12px;
font-weight: bold;
color: white;
}
.content {
margin-bottom: 1rem;
line-height: 1.5;
color: var(--text-primary);
}
.author {
color: var(--text-secondary);
font-size: 14px;
margin-bottom: 1rem;
}
.keywords {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
margin-bottom: 1rem;
}
.keyword {
background: rgba(108, 99, 255, 0.1);
color: var(--primary);
padding: 4px 8px;
border-radius: 4px;
font-size: 12px;
}
.actions {
display: flex;
gap: 0.5rem;
align-items: center;
}
.generateBtn {
background: var(--primary);
color: white;
padding: 8px 16px;
border-radius: 6px;
font-weight: 600;
font-size: 14px;
transition: background 0.2s;
flex: 1;
}
.generateBtn:hover {
background: #5A52D5;
}
.statusSelect {
background: var(--dark-bg);
color: var(--text-primary);
border: 1px solid var(--dark-border);
border-radius: 6px;
padding: 8px;
font-size: 14px;
}
```
```css
/* File: components/OutreachModal.module.css */
.modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.7);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
}
.modalContent {
background: var(--dark-card);
border: 1px solid var(--dark-border);
border-radius: 16px;
padding: 2rem;
max-width: 600px;
width: 90%;
max-height: 80vh;
overflow-y: auto;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1.5rem;
}
.header h2 {
font-size: 1.5rem;
color: var(--text-primary);
}
.closeBtn {
font-size: 1.5rem;
color: var(--text-secondary);
transition: color 0.2s;
}
.closeBtn:hover {
color: var(--text-primary);
}
.originalPost {
background: var(--dark-bg);
border: 1px solid var(--dark-border);
border-radius: 8px;
padding: 1rem;
margin-bottom: 1.5rem;
}
.originalPost p {
margin-bottom: 0.5rem;
line-height: 1.5;
}
.typeSelector {
display: flex;
gap: 1rem;
margin-bottom: 1.5rem;
}
.select {
flex: 1;
background: var(--dark-bg);
color: var(--text-primary);
border: 1px solid var(--dark-border);
border-radius: 6px;
padding: 8px;
font-size: 14px;
}
.generateBtn {
background: var(--primary);
color: white;
padding: 8px 16px;
border-radius: 6px;
font-weight: 600;
transition: background 0.2s;
}
.generateBtn:hover {
background: #5A52D5;
}
.generateBtn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.output {
background: var(--dark-bg);
border: 1px solid var(--dark-border);
border-radius: 8px;
padding: 1rem;
}
.outputHeader {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1rem;
}
.outputHeader h3 {
color: var(--text-primary);
font-size: 1rem;
}
.copyBtn {
background: var(--secondary);
color: white;
padding: 4px 12px;
border-radius: 4px;
font-size: 12px;
transition: background 0.2s;
}
.copyBtn:hover {
background: #00A896;
}
.contentBody {
color: var(--text-primary);
line-height: 1.6;
white-space: pre-wrap;
}
```
```css
/* File: components/Header.module.css */
.header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem 0;
border-bottom: 1px solid var(--dark-border);
}
.logo {
display: flex;
align-items: center;
gap: 0.5rem;
}
.logoIcon {
font-size: 2rem;
}
.logo h1 {
font-size: 1.5rem;
background: linear-gradient(135deg, var(--primary), var(--secondary));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.actions {
display: flex;
gap: 1rem;
}
.exportBtn {
background: var(--secondary);
color: white;
padding: 8px 16px;
border-radius: 6px;
font-weight: 600;
transition: background 0.2s;
}
.exportBtn:hover {
background: #00A896;
}
.authBtn {
background: var(--primary);
color: white;
padding: 8px 16px;
border-radius: 6px;
font-weight: 600;
transition: background 0.2s;
}
.authBtn:hover {
background: #5A52D5;
}
```
```css
/* File: components/Filters.module.css */
.filters {
display: flex;
gap: 1rem;
margin-bottom: 2rem;
flex-wrap: wrap;
}
.select {
background: var(--dark-card);
color: var(--text-primary);
border: 1px solid var(--dark-border);
border-radius: 6px;
padding: 8px 12px;
font-size: 14px;
min-width: 150px;
}
.input {
background: var(--dark-card);
color: var(--text-primary);
border: 1px solid var(--dark-border);
border-radius: 6px;
padding: 8px 12px;
font-size: 14px;
flex: 1;
min-width: 200px;
}
.select:focus, .input:focus {
outline: none;
border-color: var(--primary);
}
```
```css
/* File: components/AuthModal.module.css */
.modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.7);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
}
.modalContent {
background: var(--dark-card);
border: 1px solid var(--dark-border);
border-radius: 16px;
padding: 2rem;
max-width: 400px;
width: 90%;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1.5rem;
}
.header h2 {
font-size: 1.5rem;
color: var(--text-primary);
}
.closeBtn {
font-size: 1.5rem;
color: var(--text-secondary);
transition: color 0.2s;
}
.closeBtn:hover {
color: var(--text-primary);
}
.form {
display: flex;
flex-direction: column;
gap: 1rem;
}
.input {
background: var(--dark-bg);
color: var(--text-primary);
border: 1px solid var(--dark-border);
border-radius: 6px;
padding: 10px 12px;
font-size: 14px;
}
.input:focus {
outline: none;
border-color: var(--primary);
}
.error {
color: var(--error);
font-size: 14px;
margin-top:: -0.5rem;
}
.submitBtn {
background: var(--primary);
color: white;
padding:: 10px;
border-radius: 6px;
font-weight: 600;
transition: background 0.2s;
margin-top: 0.5rem;
}
.submitBtn:hover {
background: #5A52D5;
}
.submitBtn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.switchText {
color: var(--text-secondary);
font-size: 14px;
text-align: center;
margin-top: 1rem;
}
.switchBtn {
color: var(--primary);
font-weight: 600;
margin-left: 0.5rem;
transition: color 0.2s;
}
.switchBtn:hover {
color: #5A52D5;
}
```
This is a complete full-stack SignalSpy application with:
**Backend (Node.js/Express):**
- Authentication system with JWT tokens
- Signal generation and filtering
- Outreach content generation
- MongoDB models for User and Signal
**Frontend (React/Next.js):**
- Modern dark-mode SaaS dashboard
- Signal feed with cards showing platform, intent score, and content
- Filters for platform, keyword, and intent level
- Outreach generation modal with multiple content types
- Authentication modal
- Export to CSV functionality
**Key Features:**
- Simulated signal data from Twitter, Reddit, and LinkedIn
- Intent scoring based on urgency and relevance
- Generated outreach content (cold DMs, emails, public replies, content ideas)
- Save/contact/convert signal status tracking
- Responsive, clean, modern UI
To run this:
1. Install dependencies: `npm install`
2. Start MongoDB locally or use MongoDB Atlas
3. Run the server: `npm start`
4. Open the frontend in your browser
The prototype simulates the core functionality with mock data while maintaining a production-ready architecture that can easily be connected to real APIs (Twitter API, Reddit API, LinkedIn API) and AI services (OpenAI for content generation) when you're ready to scale.
Melde dich an, um den vollständigen Prompt zu sehen
Weiter mit:
Mit der Anmeldung akzeptierst du unsere Nutzungsbedingungen und Datenschutz
Verwendung
Dieser Prompt ist für die Verwendung mit coding gedacht. Kopiere den Inhalt oben und füge ihn in dein bevorzugtes KI-Tool ein.
Für beste Ergebnisse passe die Platzhalter (eckige Klammern oder Großbuchstaben) an deine Anforderungen an.
Referenzen
- Kategorie: coding-Prompts
- Quelle: https://x.com/thisdudelikesAI/status/2044414764244246611
Diskussion
0 Kommentare