← back to Marketing Command Center

public/panels/composer.html

129 lines

<div class="cmp-head">
  <div class="muted" style="font-size:12.5px">Combine an image or banner + copy, pick your socials, and publish (or stage) to all of them at once.</div>
  <button class="btn ghost" id="cmp-refresh" style="font-size:12.5px;padding:6px 12px">↻ Refresh</button>
</div>

<div class="cmp-grid">
  <!-- LEFT: source rail — assets + banners -->
  <section class="cmp-col cmp-left">
    <div class="cmp-tabs">
      <button class="cmp-tab on" data-src="assets">🖼️ Assets</button>
      <button class="cmp-tab" data-src="reels">🎬 Reels</button>
      <button class="cmp-tab" data-src="banners">🎏 Banners</button>
    </div>
    <input id="cmp-search" class="cmp-search" placeholder="Filter…">
    <div id="cmp-srcgrid" class="cmp-srcgrid"><div class="loading">Loading…</div></div>
  </section>

  <!-- MIDDLE: the post being assembled — THIS is where it all combines -->
  <section class="cmp-col cmp-mid">
    <div class="cmp-preview" id="cmp-preview">
      <div class="cmp-ph">Pick an asset, reel, or banner on the left ←</div>
      <img id="cmp-img" style="display:none" alt="selected">
      <video id="cmp-video" style="display:none" controls muted playsinline preload="metadata"></video>
    </div>
    <div id="cmp-bannernote" class="muted" style="font-size:11.5px;color:#8a6a2a;min-height:14px"></div>
    <div class="cmp-copywrap">
      <div class="cmp-copybar">
        <label class="muted" style="font-size:11.5px">Tone
          <select id="cmp-tone" style="width:auto;font-size:12px">
            <option value="luxury">Luxury</option>
            <option value="playful">Playful</option>
            <option value="editorial">Editorial</option>
            <option value="trade">Trade / B2B</option>
          </select>
        </label>
        <button class="btn ghost" id="cmp-gen" style="font-size:12px;padding:5px 10px">✨ Generate copy</button>
        <span id="cmp-count" class="muted" style="font-size:11px;margin-left:auto">0</span>
      </div>
      <textarea id="cmp-caption" class="cmp-caption" placeholder="Write your caption — or hit ✨ Generate copy. #designerwallcoverings"></textarea>
    </div>
  </section>

  <!-- RIGHT: publish targets + fire -->
  <section class="cmp-col cmp-right">
    <div class="cmp-rhead">
      <span style="font-weight:700;font-size:13px">Post to</span>
      <button class="btn ghost" id="cmp-all" style="font-size:11px;padding:3px 9px">Select connected</button>
    </div>
    <div id="cmp-targets" class="cmp-targets"><div class="loading">Loading…</div></div>
    <!-- Facebook Page picker — revealed only when Facebook is a selected target -->
    <div id="cmp-fbpages" class="cmp-fbpages" style="display:none">
      <div class="cmp-fbpages-head">
        <span>📘 Post to Page(s)</span>
        <span class="cmp-fbpages-tools">
          <button id="cmp-fball" class="cmp-mini">all</button>
          <button id="cmp-fbrefresh" class="cmp-mini" title="Refresh Pages from Meta">↻</button>
        </span>
      </div>
      <input id="cmp-fbsearch" class="cmp-search" style="margin-bottom:6px" placeholder="Filter pages…">
      <div id="cmp-fbpages-list" class="cmp-fbpages-list"><div class="loading">Loading…</div></div>
    </div>
    <label class="cmp-dry"><input type="checkbox" id="cmp-dry" checked> Dry-run (stage, don't post live)</label>
    <button class="btn gold" id="cmp-publish" style="width:100%;font-size:14px;padding:11px">Publish</button>
    <div id="cmp-result" class="cmp-result"></div>
  </section>
</div>

<style>
  .cmp-head{display:flex;justify-content:space-between;align-items:center;gap:14px;margin-bottom:14px;flex-wrap:wrap}
  .cmp-grid{display:grid;grid-template-columns:280px 1fr 260px;gap:16px;align-items:start}
  @media(max-width:1100px){.cmp-grid{grid-template-columns:1fr}}
  .cmp-col{background:var(--card,#fff);border:1px solid var(--line,#e7e1d6);border-radius:14px;padding:12px;
    box-shadow:0 1px 3px rgba(0,0,0,.04);max-height:calc(100vh - 190px);display:flex;flex-direction:column}
  .cmp-tabs{display:flex;gap:6px;margin-bottom:9px}
  .cmp-tab{flex:1;font-size:12px;padding:6px 8px;border:1px solid var(--line,#e7e1d6);background:#fbf9f5;
    border-radius:9px;cursor:pointer;color:var(--mut,#8a8170);font-weight:600}
  .cmp-tab.on{background:var(--ink,#2a2620);color:#fff;border-color:var(--ink,#2a2620)}
  .cmp-search{width:100%;font-size:12px;padding:7px 9px;border:1px solid var(--line,#e7e1d6);border-radius:9px;margin-bottom:9px}
  .cmp-srcgrid{overflow-y:auto;display:grid;grid-template-columns:1fr 1fr;gap:8px;flex:1}
  .cmp-thumb{border:1px solid var(--line,#ece6da);border-radius:9px;overflow:hidden;cursor:pointer;background:#f0ece3;
    aspect-ratio:1/1;position:relative}
  .cmp-thumb img{width:100%;height:100%;object-fit:cover;display:block}
  .cmp-thumb.sel{outline:3px solid var(--gold,#b8945a);outline-offset:-1px}
  .cmp-thumb .lbl{position:absolute;left:0;right:0;bottom:0;font-size:9.5px;background:rgba(0,0,0,.55);color:#fff;
    padding:2px 5px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .cmp-mid{gap:12px}
  .cmp-preview{border:1px solid var(--line,#ece6da);border-radius:12px;background:#f7f4ee;min-height:260px;
    display:flex;align-items:center;justify-content:center;overflow:hidden}
  .cmp-preview img{width:100%;max-height:420px;object-fit:contain;display:block}
  .cmp-preview video{width:100%;max-height:460px;object-fit:contain;display:block;background:#000}
  .cmp-ph{color:var(--mut,#8a8170);font-size:13px;font-style:italic;padding:30px}
  /* reel thumbnails — portrait video tiles with a play badge */
  .cmp-srcgrid.reels{grid-template-columns:1fr 1fr}
  .cmp-reel{border:1px solid var(--line,#ece6da);border-radius:9px;overflow:hidden;cursor:pointer;background:#000;
    position:relative;aspect-ratio:9/16}
  .cmp-reel video{width:100%;height:100%;object-fit:cover;display:block;pointer-events:none}
  .cmp-reel.sel{outline:3px solid var(--gold,#b8945a);outline-offset:-1px}
  .cmp-reel .play{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
    font-size:26px;color:#fff;text-shadow:0 2px 8px rgba(0,0,0,.6);pointer-events:none}
  .cmp-reel .lbl{position:absolute;left:0;right:0;bottom:0;font-size:9.5px;background:rgba(0,0,0,.6);color:#fff;
    padding:3px 5px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  /* facebook page picker */
  .cmp-fbpages{border:1px solid var(--line,#ece6da);border-radius:10px;padding:9px;margin-bottom:10px;background:#fbfaf6}
  .cmp-fbpages-head{display:flex;justify-content:space-between;align-items:center;font-size:12px;font-weight:700;margin-bottom:7px}
  .cmp-fbpages-tools{display:flex;gap:5px}
  .cmp-mini{font-size:10.5px;padding:2px 7px;border:1px solid var(--line,#e7e1d6);background:#fff;border-radius:7px;cursor:pointer;color:var(--mut,#8a8170)}
  .cmp-fbpages-list{max-height:150px;overflow-y:auto;display:flex;flex-direction:column;gap:3px}
  .cmp-fbrow{display:flex;align-items:center;gap:7px;font-size:12px;padding:4px 6px;border-radius:7px;cursor:pointer}
  .cmp-fbrow:hover{background:#f2ede3}
  .cmp-fbrow .nm{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .cmp-fbrow .ig{font-size:9.5px;color:#8a8170}
  .cmp-copybar{display:flex;align-items:center;gap:10px;margin-bottom:7px}
  .cmp-caption{width:100%;min-height:120px;font-size:13.5px;line-height:1.5;padding:10px;resize:vertical;
    border:1px solid var(--line,#e7e1d6);border-radius:10px;font-family:inherit}
  .cmp-rhead{display:flex;justify-content:space-between;align-items:center;margin-bottom:10px}
  .cmp-targets{display:flex;flex-direction:column;gap:7px;overflow-y:auto;flex:1;margin-bottom:10px}
  .cmp-tgt{display:flex;align-items:center;gap:9px;font-size:13px;padding:8px 10px;border:1px solid var(--line,#ece6da);
    border-radius:10px;cursor:pointer;background:#fdfcf9}
  .cmp-tgt.off{opacity:.5;cursor:not-allowed}
  .cmp-tgt .ico{font-size:16px}
  .cmp-tgt .nm{flex:1;font-weight:600}
  .cmp-tgt .st{font-size:9.5px;font-weight:700;padding:1px 7px;border-radius:999px}
  .cmp-tgt .st.on{background:#e3efe0;color:#3a6b3a}
  .cmp-tgt .st.off{background:#f3e9e9;color:#9a5a5a}
  .cmp-dry{display:flex;align-items:center;gap:7px;font-size:12px;color:var(--mut,#8a8170);margin-bottom:10px}
  .cmp-result{margin-top:11px;font-size:12px;line-height:1.5}
  .cmp-result .ok{color:#3a6b3a}.cmp-result .warn{color:#8a6a2a}.cmp-result .err{color:#9a3a3a}
</style>