File size: 5,470 Bytes
3d2098f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
import { useState } from 'react'
import { api } from '../api'
import { ErrorBox } from '../hooks'

const csv = (s) => s.split(',').map((x) => x.trim()).filter(Boolean)

// mode="create": standalone form on the home screen.
// mode="edit": step 1 of the wizard, edits the existing project.
export default function SetupStep({ mode, data, refreshProject, setStep, onCreated, onCancel }) {
  const refresh = refreshProject
  const project = data?.project
  const [form, setForm] = useState(() => ({
    name: project?.name || '',
    topic: project?.niche.topic || '',
    keywords: project?.niche.keywords.join(', ') || '',
    subreddits: project?.niche.subreddits.join(', ') || '',
    audience: project?.niche.audience || '',
    tone: project?.niche.tone || '',
    format: project?.format || 'youtube_short',
  }))
  const [busy, setBusy] = useState(false)
  const [filling, setFilling] = useState(false)
  const [error, setError] = useState('')

  const set = (k) => (e) => setForm({ ...form, [k]: e.target.value })

  // Ask the LLM to derive the remaining fields from the niche topic.
  // Only fills columns the user left blank, so it never overwrites their input.
  const autofill = async () => {
    if (!form.topic.trim()) { setError('Enter a niche topic first.'); return }
    setFilling(true)
    setError('')
    try {
      const s = await api.post('/api/niche/suggest', { topic: form.topic.trim() })
      setForm((f) => ({
        ...f,
        name: f.name.trim() || s.name || '',
        keywords: f.keywords.trim() || (s.keywords || []).join(', '),
        subreddits: f.subreddits.trim() || (s.subreddits || []).join(', '),
        audience: f.audience.trim() || s.audience || '',
        tone: f.tone.trim() || s.tone || '',
      }))
    } catch (e) {
      setError(e.message)
    } finally {
      setFilling(false)
    }
  }

  const body = () => ({
    name: form.name || form.topic,
    niche: {
      topic: form.topic,
      keywords: csv(form.keywords),
      subreddits: csv(form.subreddits),
      audience: form.audience,
      tone: form.tone,
    },
    format: form.format,
    references: project ? project.references : [],
  })

  const submit = async () => {
    if (!form.topic.trim()) { setError('A niche topic is required.'); return }
    setBusy(true)
    setError('')
    try {
      if (mode === 'create') {
        const created = await api.post('/api/projects', body())
        onCreated(created)
      } else {
        await api.patch(`/api/projects/${project.id}`, body())
        await refresh()
        setStep(1)
      }
    } catch (e) {
      setError(e.message)
    } finally {
      setBusy(false)
    }
  }

  return (
    <div>
      <h2>{mode === 'create' ? 'New project' : 'Project setup'}</h2>
      <p className="subtitle">Define the niche. Nothing here is hardcoded — every later stage builds on these fields.</p>
      <ErrorBox error={error} />
      <div className="card">
        <label className="field"><span className="lbl">Project name</span>
          <input type="text" value={form.name} onChange={set('name')} placeholder="e.g. Deep Sea Shorts" />
        </label>
        <label className="field"><span className="lbl">Niche topic *</span>
          <div className="row">
            <input className="grow" type="text" value={form.topic} onChange={set('topic')}
              placeholder="e.g. deep sea creatures"
              onKeyDown={(e) => { if (e.key === 'Enter') { e.preventDefault(); autofill() } }} />
            <button className="btn secondary" onClick={autofill} disabled={filling || !form.topic.trim()}
              title="Suggest keywords, subreddits, audience and tone from this niche (only fills blank fields)">
              {filling ? 'Filling…' : '✨ Autofill'}
            </button>
          </div>
        </label>
        <div className="row">
          <label className="field grow"><span className="lbl">Keywords (comma separated)</span>
            <input type="text" value={form.keywords} onChange={set('keywords')} placeholder="ocean, marine biology" />
          </label>
          <label className="field grow"><span className="lbl">Subreddits (comma separated)</span>
            <input type="text" value={form.subreddits} onChange={set('subreddits')} placeholder="thalassophobia, ocean" />
          </label>
        </div>
        <div className="row">
          <label className="field grow"><span className="lbl">Audience</span>
            <input type="text" value={form.audience} onChange={set('audience')} placeholder="curious adults" />
          </label>
          <label className="field grow"><span className="lbl">Tone</span>
            <input type="text" value={form.tone} onChange={set('tone')} placeholder="playful, awe-inspiring" />
          </label>
        </div>
        <label className="field"><span className="lbl">Output format</span>
          <select value={form.format} onChange={set('format')}>
            <option value="youtube_short">YouTube Short (9:16)</option>
            <option value="instagram_reel">Instagram Reel (9:16)</option>
          </select>
        </label>
        <div className="row">
          <button className="btn" onClick={submit} disabled={busy}>
            {busy ? 'Saving…' : mode === 'create' ? 'Create project' : 'Save & continue'}
          </button>
          {onCancel && <button className="btn secondary" onClick={onCancel}>Cancel</button>}
        </div>
      </div>
    </div>
  )
}