Skip to content

Commit 4120a03

Browse files
Create Deca
1 parent 6e474c0 commit 4120a03

File tree

1 file changed

+366
-0
lines changed

1 file changed

+366
-0
lines changed

Deca

Lines changed: 366 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,366 @@
1+
import React, { useState } from "react";
2+
3+
// SmartMarketingAssistant.jsx
4+
// Single-file React component (JavaScript + Tailwind classes) for DECA project
5+
// - Default export is the App component
6+
// - Uses Tailwind CSS for styling (assumes Tailwind is installed in host project)
7+
// - Includes mock AI logic for channel recommendation, posting-time prediction, and ad-copy generation
8+
// - Includes option to connect to a real AI API by replacing mockGenerate* functions with real fetch calls
9+
10+
// ---------- Utility helpers (mock AI) ----------
11+
function analyzeBusinessProfile({ industry, audienceAge, audienceLocation, averagePrice }) {
12+
// Basic heuristic scoring for channels
13+
const scores = {
14+
instagram: 0,
15+
tiktok: 0,
16+
facebook: 0,
17+
linkedin: 0,
18+
email: 0,
19+
google: 0,
20+
};
21+
22+
// Industry biases
23+
const youthIndustries = ["fashion", "beauty", "food", "entertainment", "gaming"];
24+
const b2bIndustries = ["consulting", "software", "hardware", "manufacturing"];
25+
26+
if (youthIndustries.includes(industry)) {
27+
scores.instagram += 3; scores.tiktok += 4; scores.facebook += 1; scores.email += 0;
28+
}
29+
if (b2bIndustries.includes(industry)) {
30+
scores.linkedin += 4; scores.email += 3; scores.google += 2;
31+
}
32+
33+
// Age influence
34+
if (audienceAge === "under25") { scores.tiktok += 3; scores.instagram += 2; }
35+
if (audienceAge === "25to44") { scores.instagram += 2; scores.facebook += 2; }
36+
if (audienceAge === "45plus") { scores.facebook += 3; scores.email += 2; }
37+
38+
// Price influence
39+
if (averagePrice > 200) { scores.linkedin += 1; scores.google += 2; }
40+
if (averagePrice < 50) { scores.tiktok += 1; scores.instagram += 1; }
41+
42+
// Location (global vs local)
43+
if (audienceLocation === "local") {
44+
scores.facebook += 2; scores.google += 2; scores.email += 1;
45+
} else {
46+
// worldwide
47+
scores.instagram += 1; scores.tiktok += 1; scores.google += 1;
48+
}
49+
50+
// Convert to sorted recommendations
51+
const recommendations = Object.entries(scores)
52+
.sort((a, b) => b[1] - a[1])
53+
.map(([k, v]) => ({ channel: k, score: v }));
54+
55+
return recommendations;
56+
}
57+
58+
function predictPostingTimes(recommendations) {
59+
// Mock posting time suggestions based on channel
60+
const times = {
61+
instagram: ["Weekdays 11:00–13:00", "Evenings 19:00–21:00"],
62+
tiktok: ["Evenings 18:00–23:00", "Weekends 10:00–14:00"],
63+
facebook: ["Weekdays 10:00–12:00", "Weekdays 18:00–20:00"],
64+
linkedin: ["Weekdays 07:30–09:00", "Weekdays 12:00–13:30"],
65+
email: ["Tuesday or Thursday 09:00–11:00"],
66+
google: ["Continuous (PPC peak bids at 09:00–17:00)"]
67+
};
68+
69+
return recommendations.slice(0, 3).map(r => ({ channel: r.channel, times: times[r.channel] || ["Anytime"] }));
70+
}
71+
72+
function generateAdCopy({ businessName, product, tone, channel, callToAction }) {
73+
// Small templates for each channel
74+
const templates = {
75+
instagram: [
76+
`${product} ✨ by ${businessName} — ${tone} & affordable. ${callToAction}`,
77+
`New drop: ${product}. Tap to shop and enjoy exclusive savings! ${callToAction}`
78+
],
79+
tiktok: [
80+
`You won't believe how ${product} transforms your day. ${callToAction}`,
81+
`Trend alert! ${product} from ${businessName} — watch till the end. ${callToAction}`
82+
],
83+
facebook: [
84+
`${businessName} presents ${product}. Trusted by customers worldwide. ${callToAction}`,
85+
`Discover ${product} — quality and value. Shop now at ${businessName}. ${callToAction}`
86+
],
87+
linkedin: [
88+
`${product} from ${businessName} increases efficiency and ROI. Contact us to learn more. ${callToAction}`
89+
],
90+
email: [
91+
`Subject: ${product} — Special offer from ${businessName}\n\nHi there,\nCheck out ${product} and enjoy an exclusive deal. ${callToAction}`
92+
],
93+
google: [
94+
`${product} — ${businessName}. Buy now. ${callToAction}`
95+
]
96+
};
97+
98+
const list = templates[channel] || [`Check out ${product} at ${businessName}. ${callToAction}`];
99+
// return first + a variant
100+
return { primary: list[0], variant: list[1] || list[0] };
101+
}
102+
103+
// ---------- Small UI components ----------
104+
function Badge({ children }) {
105+
return <span className="inline-block bg-indigo-100 text-indigo-700 px-2 py-1 rounded-full text-xs font-semibold">{children}</span>;
106+
}
107+
108+
function ChannelCard({ channel, score, times, adCopy }) {
109+
return (
110+
<div className="p-4 border rounded-lg shadow-sm hover:shadow-md transition">
111+
<div className="flex items-center justify-between">
112+
<div className="flex items-center space-x-3">
113+
<div className="w-12 h-12 rounded-full bg-gradient-to-br from-indigo-500 to-pink-500 flex items-center justify-center text-white font-bold uppercase">{channel[0]}</div>
114+
<div>
115+
<div className="font-semibold capitalize">{channel}</div>
116+
<div className="text-sm text-gray-500">Score: {score}</div>
117+
</div>
118+
</div>
119+
<div>
120+
<Badge>{Math.round(score)} pts</Badge>
121+
</div>
122+
</div>
123+
124+
<div className="mt-3 text-sm">
125+
<div className="font-medium">Suggested Times</div>
126+
<ul className="list-disc list-inside text-gray-600">
127+
{times.map((t, i) => <li key={i}>{t}</li>)}
128+
</ul>
129+
</div>
130+
131+
<div className="mt-3">
132+
<div className="font-medium">Ad Copy</div>
133+
<div className="mt-2 text-sm bg-gray-50 p-3 rounded">
134+
<div><strong>Primary:</strong> {adCopy.primary}</div>
135+
{adCopy.variant && <div className="mt-2 text-gray-700"><strong>Variant:</strong> {adCopy.variant}</div>}
136+
</div>
137+
</div>
138+
</div>
139+
);
140+
}
141+
142+
// ---------- Main App ----------
143+
export default function App() {
144+
const [form, setForm] = useState({
145+
businessName: "",
146+
industry: "fashion",
147+
product: "",
148+
audienceAge: "25to44",
149+
audienceLocation: "global",
150+
averagePrice: 50,
151+
tone: "friendly",
152+
callToAction: "Shop now",
153+
});
154+
const [recommendations, setRecommendations] = useState(null);
155+
const [postingTimes, setPostingTimes] = useState(null);
156+
const [adCopies, setAdCopies] = useState(null);
157+
const [lang, setLang] = useState("en");
158+
const [loading, setLoading] = useState(false);
159+
160+
async function handleAnalyze(e) {
161+
e.preventDefault();
162+
setLoading(true);
163+
164+
// 1) Channel recommendations (mock AI)
165+
const recs = analyzeBusinessProfile(form);
166+
167+
// 2) Predict posting times
168+
const times = predictPostingTimes(recs);
169+
170+
// 3) Generate ad copy for top 3 channels
171+
const copies = {};
172+
times.forEach(t => {
173+
copies[t.channel] = generateAdCopy({
174+
businessName: form.businessName || "Your Business",
175+
product: form.product || "your product",
176+
tone: form.tone,
177+
channel: t.channel,
178+
callToAction: form.callToAction
179+
});
180+
});
181+
182+
// simulate network delay
183+
await new Promise(r => setTimeout(r, 500));
184+
185+
setRecommendations(recs.slice(0, 5));
186+
setPostingTimes(times);
187+
setAdCopies(copies);
188+
setLoading(false);
189+
}
190+
191+
function handleChange(e) {
192+
const { name, value } = e.target;
193+
setForm(prev => ({ ...prev, [name]: name === "averagePrice" ? Number(value) : value }));
194+
}
195+
196+
function downloadReport() {
197+
// Create a minimal report and trigger download
198+
const report = {
199+
generatedAt: new Date().toISOString(),
200+
profile: form,
201+
recommendations,
202+
postingTimes,
203+
adCopies
204+
};
205+
const blob = new Blob([JSON.stringify(report, null, 2)], { type: "application/json" });
206+
const url = URL.createObjectURL(blob);
207+
const a = document.createElement("a");
208+
a.href = url; a.download = `${form.businessName || 'sma-report'}.json`;
209+
a.click();
210+
URL.revokeObjectURL(url);
211+
}
212+
213+
return (
214+
<div className="min-h-screen bg-gradient-to-br from-white via-indigo-50 to-pink-50 p-6">
215+
<div className="max-w-6xl mx-auto">
216+
<header className="flex items-center justify-between mb-6">
217+
<div className="flex items-center space-x-4">
218+
<div className="w-14 h-14 rounded-xl bg-gradient-to-br from-indigo-600 to-pink-500 flex items-center justify-center text-white font-extrabold text-lg">SMA</div>
219+
<div>
220+
<h1 className="text-2xl font-bold">Smart Marketing Assistant</h1>
221+
<p className="text-sm text-gray-600">AI-driven channel, timing, and ad copy suggestions for small businesses.</p>
222+
</div>
223+
</div>
224+
225+
<div className="flex items-center space-x-3">
226+
<select value={lang} onChange={e => setLang(e.target.value)} className="px-3 py-2 rounded border bg-white">
227+
<option value="en">English</option>
228+
<option value="es">Español</option>
229+
<option value="fr">Français</option>
230+
<option value="pt">Português</option>
231+
</select>
232+
233+
<button onClick={() => window.open('#', '_blank')} className="px-4 py-2 bg-white border rounded shadow-sm">Help</button>
234+
</div>
235+
</header>
236+
237+
<main className="grid grid-cols-1 lg:grid-cols-3 gap-6">
238+
<form onSubmit={handleAnalyze} className="col-span-1 lg:col-span-1 p-6 bg-white rounded-lg shadow">
239+
<h2 className="font-semibold text-lg mb-4">Business Profile</h2>
240+
241+
<label className="block text-sm font-medium text-gray-700">Business name</label>
242+
<input name="businessName" value={form.businessName} onChange={handleChange} className="mt-1 w-full px-3 py-2 border rounded" placeholder="e.g., Sunny Cafe" />
243+
244+
<label className="mt-3 block text-sm font-medium text-gray-700">Industry</label>
245+
<select name="industry" value={form.industry} onChange={handleChange} className="mt-1 w-full px-3 py-2 border rounded">
246+
<option value="fashion">Fashion</option>
247+
<option value="beauty">Beauty</option>
248+
<option value="food">Food & Beverage</option>
249+
<option value="software">Software</option>
250+
<option value="consulting">Consulting</option>
251+
<option value="fitness">Fitness</option>
252+
<option value="entertainment">Entertainment</option>
253+
<option value="other">Other</option>
254+
</select>
255+
256+
<label className="mt-3 block text-sm font-medium text-gray-700">Main product / service</label>
257+
<input name="product" value={form.product} onChange={handleChange} className="mt-1 w-full px-3 py-2 border rounded" placeholder="e.g., Handcrafted lattes" />
258+
259+
<div className="mt-3 grid grid-cols-2 gap-2">
260+
<div>
261+
<label className="block text-sm font-medium text-gray-700">Audience age</label>
262+
<select name="audienceAge" value={form.audienceAge} onChange={handleChange} className="mt-1 w-full px-3 py-2 border rounded">
263+
<option value="under25">Under 25</option>
264+
<option value="25to44">25–44</option>
265+
<option value="45plus">45+</option>
266+
</select>
267+
</div>
268+
269+
<div>
270+
<label className="block text-sm font-medium text-gray-700">Audience reach</label>
271+
<select name="audienceLocation" value={form.audienceLocation} onChange={handleChange} className="mt-1 w-full px-3 py-2 border rounded">
272+
<option value="local">Local</option>
273+
<option value="regional">Regional</option>
274+
<option value="global">Global</option>
275+
</select>
276+
</div>
277+
</div>
278+
279+
<label className="mt-3 block text-sm font-medium text-gray-700">Average price ($)</label>
280+
<input name="averagePrice" type="number" value={form.averagePrice} onChange={handleChange} className="mt-1 w-full px-3 py-2 border rounded" />
281+
282+
<label className="mt-3 block text-sm font-medium text-gray-700">Tone</label>
283+
<select name="tone" value={form.tone} onChange={handleChange} className="mt-1 w-full px-3 py-2 border rounded">
284+
<option value="friendly">Friendly</option>
285+
<option value="professional">Professional</option>
286+
<option value="fun">Fun</option>
287+
<option value="luxury">Luxury</option>
288+
</select>
289+
290+
<label className="mt-3 block text-sm font-medium text-gray-700">Call to action</label>
291+
<input name="callToAction" value={form.callToAction} onChange={handleChange} className="mt-1 w-full px-3 py-2 border rounded" />
292+
293+
<div className="mt-5 flex space-x-2">
294+
<button type="submit" className="px-4 py-2 bg-indigo-600 text-white rounded shadow">{loading ? 'Analyzing...' : 'Analyze & Generate'}</button>
295+
<button type="button" onClick={() => { setForm({ businessName: '', industry: 'fashion', product: '', audienceAge: '25to44', audienceLocation: 'global', averagePrice: 50, tone: 'friendly', callToAction: 'Shop now' }); setRecommendations(null); setPostingTimes(null); setAdCopies(null); }} className="px-4 py-2 border rounded">Reset</button>
296+
</div>
297+
298+
<div className="mt-6 text-xs text-gray-500">Local mock AI runs in-browser. To connect a real AI service, replace mock functions with API calls in the source code.</div>
299+
</form>
300+
301+
<section className="col-span-1 lg:col-span-2">
302+
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
303+
<div className="p-4 bg-white rounded shadow">
304+
<h3 className="font-semibold">Summary</h3>
305+
<p className="text-sm text-gray-600 mt-2">Overview of recommendations and quick links to export results or copy ad copy to clipboard.</p>
306+
307+
<div className="mt-3 flex space-x-2">
308+
<button onClick={downloadReport} disabled={!recommendations} className="px-3 py-2 bg-green-600 text-white rounded disabled:opacity-50">Download JSON Report</button>
309+
<button onClick={() => navigator.clipboard.writeText(JSON.stringify({ recommendations, postingTimes, adCopies }, null, 2))} disabled={!recommendations} className="px-3 py-2 bg-indigo-600 text-white rounded disabled:opacity-50">Copy Results</button>
310+
</div>
311+
</div>
312+
313+
<div className="p-4 bg-white rounded shadow">
314+
<h3 className="font-semibold">Impact Forecast (mock)</h3>
315+
<div className="mt-2 text-sm text-gray-600">Estimated improvements after 3 months of using SMA:</div>
316+
<ul className="mt-3 list-disc list-inside text-gray-700">
317+
<li>Engagement: +30%</li>
318+
<li>Ad spend waste reduction: -25%</li>
319+
<li>Average ROI improvement: +20%</li>
320+
</ul>
321+
</div>
322+
</div>
323+
324+
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
325+
{recommendations ? recommendations.map((r, i) => (
326+
<ChannelCard key={r.channel} channel={r.channel} score={r.score} times={(postingTimes.find(t => t.channel === r.channel) || { times: ['Anytime'] }).times} adCopy={(adCopies && adCopies[r.channel]) || generateAdCopy({ businessName: form.businessName, product: form.product, tone: form.tone, channel: r.channel, callToAction: form.callToAction })} />
327+
)) : (
328+
<div className="col-span-1 md:col-span-3 p-6 bg-white rounded shadow text-center text-gray-500">Run an analysis to see channel recommendations, optimal posting times, and tailored ad copy.</div>
329+
)}
330+
</div>
331+
332+
<div className="mt-6 p-6 bg-white rounded shadow">
333+
<h3 className="font-semibold">Implementation Roadmap</h3>
334+
<ol className="list-decimal list-inside mt-3 text-gray-700">
335+
<li>Connect social accounts + website analytics (Month 1)</li>
336+
<li>Train SMA with business data & set goals (Month 2)</li>
337+
<li>Launch first 4-week campaign with AI recommendations (Month 3)</li>
338+
<li>Optimize & scale based on KPIs (Months 4–6)</li>
339+
</ol>
340+
341+
<div className="mt-4 text-sm text-gray-600">Tip: Always A/B test AI-generated copy against human-written copy for best results.</div>
342+
</div>
343+
344+
<div className="mt-6 p-6 bg-white rounded shadow">
345+
<h3 className="font-semibold">About the Demo & Next Steps</h3>
346+
<p className="text-sm text-gray-600 mt-2">This demo runs mock AI logic in-browser for offline demonstration and judging. For production/global use, replace the mock generators with calls to a server-side AI (OpenAI, Anthropic, etc.), secure your API keys, and add internationalization of copy templates.</p>
347+
348+
<div className="mt-3 text-sm">
349+
<strong>Checklist to go worldwide:</strong>
350+
<ul className="list-disc list-inside text-gray-700">
351+
<li>Server side AI & rate-limiting</li>
352+
<li>Secure storage (GDPR / data residency)</li>
353+
<li>Translation & localization</li>
354+
<li>Payment & subscription for a SaaS model</li>
355+
</ul>
356+
</div>
357+
</div>
358+
359+
</section>
360+
</main>
361+
362+
<footer className="mt-8 text-center text-gray-600 text-sm">Built for DECA — Smart Marketing Assistant • Demo UI (in-browser mock AI) • Customize for your competition</footer>
363+
</div>
364+
</div>
365+
);
366+
}

0 commit comments

Comments
 (0)