poprawki graficzne, zdjęcia, panel administarcyjny oraz układ stron

This commit is contained in:
2026-07-24 14:52:19 +02:00
parent 93e67bd27e
commit 966dd3f209
19 changed files with 1997 additions and 584 deletions
+12 -7
View File
@@ -5,6 +5,7 @@
*/
import { useCallback, useEffect, useState } from 'react';
import { apiFetch } from '../auth';
import { RichTextEditor } from '../RichText';
type IconComponent = (props: { name: string }) => JSX.Element | null;
@@ -289,14 +290,14 @@ function LeadsSection({ Icon }: { Icon: IconComponent }) {
<tbody>
{leads.map((lead) => (
<tr key={lead.id}>
<td>{lead.name || '—'}</td>
<td>{lead.name || '-'}</td>
<td>
<div className="mkt-contact">
{lead.email && <span>{lead.email}</span>}
{lead.phone && <span className="mkt-muted">{lead.phone}</span>}
</div>
</td>
<td>{lead.city || '—'}</td>
<td>{lead.city || '-'}</td>
<td><span className="mkt-chip">{SOURCE_LABEL[lead.source]}</span></td>
<td>{STATUS_LABEL[lead.status]}</td>
<td>
@@ -324,7 +325,7 @@ function LeadsSection({ Icon }: { Icon: IconComponent }) {
<div className="mkt-import">
<h3><Icon name="document" /> Import kontaktów (CSV)</h3>
<p className="mkt-muted">Kolumny: <code>nazwa;email;telefon;miasto</code> — jeden kontakt w wierszu, nagłówek opcjonalny.</p>
<p className="mkt-muted">Kolumny: <code>nazwa;email;telefon;miasto</code> - jeden kontakt w wierszu, nagłówek opcjonalny.</p>
<textarea
rows={4}
value={csv}
@@ -430,7 +431,7 @@ function TargetGroupsSection({ Icon }: { Icon: IconComponent }) {
<div className="mkt-grid-2">
<div className="admin-card mkt-card">
<div className="admin-card-head"><h2>Nowa grupa docelowa</h2></div>
<p className="mkt-muted">Grupa to dynamiczny segment — odbiorcy wyliczają się na bieżąco z kryteriów.</p>
<p className="mkt-muted">Grupa to dynamiczny segment - odbiorcy wyliczają się na bieżąco z kryteriów.</p>
<div className="mkt-form">
<label>Nazwa
<input value={form.name} onChange={(e) => setForm({ ...form, name: e.target.value })} placeholder="np. Deweloperzy Warszawa" />
@@ -485,7 +486,7 @@ function TargetGroupsSection({ Icon }: { Icon: IconComponent }) {
<li key={g.id}>
<div>
<strong>{g.name}</strong>
{g.description && <span className="mkt-muted"> — {g.description}</span>}
{g.description && <span className="mkt-muted"> - {g.description}</span>}
<div className="mkt-group-meta">
<span className="mkt-badge">{g.memberCount} odbiorców</span>
{g.sources.length > 0 && <span className="mkt-muted">{g.sources.map((s) => SOURCE_LABEL[s]).join(', ')}</span>}
@@ -624,7 +625,7 @@ export function AdminCampaignsView({ Icon }: { Icon: IconComponent }) {
</label>
<label>Grupa docelowa
<select value={form.targetGroupId} onChange={(e) => setForm({ ...form, targetGroupId: e.target.value })}>
<option value="">— wybierz —</option>
<option value="">- wybierz -</option>
{groups.map((g) => (
<option key={g.id} value={g.id}>{g.name} ({g.memberCount})</option>
))}
@@ -637,7 +638,11 @@ export function AdminCampaignsView({ Icon }: { Icon: IconComponent }) {
</label>
)}
<label>Treść <span className="mkt-muted">(użyj {'{{name}}'} dla personalizacji; stopka z rezygnacją dodawana automatycznie)</span>
<textarea rows={5} value={form.body} onChange={(e) => setForm({ ...form, body: e.target.value })} placeholder="Cześć {{name}}, ..." />
{form.channel === 'EMAIL' ? (
<RichTextEditor value={form.body} onChange={(html) => setForm({ ...form, body: html })} ariaLabel="Treść kampanii e-mail" placeholder="Cześć {{name}}, ..." />
) : (
<textarea rows={5} value={form.body} onChange={(e) => setForm({ ...form, body: e.target.value })} placeholder="Cześć {{name}}, ..." />
)}
</label>
<div className="mkt-form-row">
<label>Dzienny limit