mirror of
https://github.com/warmbly/warmbly.git
synced 2026-08-19 08:01:16 +00:00
177 lines
4.9 KiB
Go
177 lines
4.9 KiB
Go
package seed
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/jackc/pgx/v5/pgxpool"
|
|
)
|
|
|
|
type seededContact struct {
|
|
id uuid.UUID
|
|
userID uuid.UUID
|
|
orgID uuid.UUID
|
|
firstName string
|
|
lastName string
|
|
email string
|
|
company string
|
|
phone string
|
|
campaignID uuid.UUID
|
|
}
|
|
|
|
func acmeContacts() []seededContact {
|
|
rows := []struct {
|
|
first, last, company string
|
|
}{
|
|
{"Aiden", "Park", "Northwind"},
|
|
{"Beth", "Chen", "Initech"},
|
|
{"Carlos", "Diaz", "Pied Piper"},
|
|
{"Diana", "Patel", "Hooli"},
|
|
{"Eli", "Brown", "Wonka"},
|
|
{"Fiona", "Walsh", "Sterling Cooper"},
|
|
{"Greg", "Mori", "Vandelay"},
|
|
{"Hana", "Nakamura", "Massive Dynamic"},
|
|
}
|
|
out := make([]seededContact, 0, len(rows))
|
|
for i, c := range rows {
|
|
out = append(out, seededContact{
|
|
id: contactID(0x01, i+1),
|
|
userID: UserOwnerID,
|
|
orgID: OrgAcmeID,
|
|
firstName: c.first,
|
|
lastName: c.last,
|
|
email: fmt.Sprintf("%s.%s@%s.test", lower(c.first), lower(c.last), lower(c.company)),
|
|
company: c.company,
|
|
phone: "",
|
|
campaignID: CampaignAcmeActiveID,
|
|
})
|
|
}
|
|
return out
|
|
}
|
|
|
|
func globexContacts() []seededContact {
|
|
rows := []struct {
|
|
first, last, company string
|
|
}{
|
|
{"Ivan", "Petrov", "OldFriend Co"},
|
|
{"Jules", "Renoir", "Pilot Studio"},
|
|
{"Kim", "Tanaka", "AlphaLab"},
|
|
}
|
|
out := make([]seededContact, 0, len(rows))
|
|
for i, c := range rows {
|
|
out = append(out, seededContact{
|
|
id: contactID(0x02, i+1),
|
|
userID: UserFounderID,
|
|
orgID: OrgGlobexID,
|
|
firstName: c.first,
|
|
lastName: c.last,
|
|
email: fmt.Sprintf("%s.%s@%s.test", lower(c.first), lower(c.last), lower(c.company)),
|
|
company: c.company,
|
|
phone: "",
|
|
campaignID: CampaignGlobexID,
|
|
})
|
|
}
|
|
return out
|
|
}
|
|
|
|
func lower(s string) string {
|
|
b := []byte(s)
|
|
for i, c := range b {
|
|
if c >= 'A' && c <= 'Z' {
|
|
b[i] = c + ('a' - 'A')
|
|
}
|
|
if c == ' ' {
|
|
b[i] = '-'
|
|
}
|
|
}
|
|
return string(b)
|
|
}
|
|
|
|
func seedContacts(ctx context.Context, pool *pgxpool.Pool, _ *Result) error {
|
|
all := append([]seededContact{}, acmeContacts()...)
|
|
all = append(all, globexContacts()...)
|
|
|
|
for _, c := range all {
|
|
_, err := pool.Exec(ctx, `
|
|
INSERT INTO contacts (id, user_id, organization_id, first_name, last_name, email, company, phone, custom_fields, subscribed, updated_at, created_at)
|
|
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,'{}'::jsonb,TRUE,NOW(),NOW())
|
|
ON CONFLICT (id) DO UPDATE SET
|
|
first_name = EXCLUDED.first_name,
|
|
last_name = EXCLUDED.last_name,
|
|
email = EXCLUDED.email,
|
|
company = EXCLUDED.company,
|
|
updated_at = NOW()
|
|
`, c.id, c.userID, c.orgID, c.firstName, c.lastName, c.email, c.company, c.phone)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = pool.Exec(ctx, `
|
|
INSERT INTO campaign_leads (campaign_id, contact_id)
|
|
VALUES ($1, $2)
|
|
ON CONFLICT DO NOTHING
|
|
`, c.campaignID, c.id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// Tag one Acme contact as VIP and put another in the Lead category so the
|
|
// tag/category views aren't empty.
|
|
_, err := pool.Exec(ctx, `
|
|
INSERT INTO contact_categories (contact_id, category_id)
|
|
VALUES ($1, $2) ON CONFLICT DO NOTHING
|
|
`, contactID(0x01, 1), CategoryLeadID)
|
|
return err
|
|
}
|
|
|
|
func seedCampaignProgress(ctx context.Context, pool *pgxpool.Pool, _ *Result) error {
|
|
// Give the active Acme campaign a realistic-looking funnel: every contact
|
|
// has step 1 sent, most opened, a couple clicked, one replied.
|
|
contacts := acmeContacts()
|
|
for i, c := range contacts {
|
|
_, err := pool.Exec(ctx, `
|
|
INSERT INTO campaign_contact_progress (campaign_id, contact_id, sequence_id, sent_at, opened_at, clicked_at, replied_at)
|
|
VALUES ($1, $2, $3,
|
|
NOW() - INTERVAL '3 days',
|
|
CASE WHEN $4 < 6 THEN NOW() - INTERVAL '3 days' + INTERVAL '15 minutes' ELSE NULL END,
|
|
CASE WHEN $4 < 3 THEN NOW() - INTERVAL '3 days' + INTERVAL '20 minutes' ELSE NULL END,
|
|
CASE WHEN $4 = 0 THEN NOW() - INTERVAL '2 days' ELSE NULL END
|
|
)
|
|
ON CONFLICT (campaign_id, contact_id, sequence_id) DO UPDATE SET
|
|
sent_at = EXCLUDED.sent_at,
|
|
opened_at = EXCLUDED.opened_at,
|
|
clicked_at = EXCLUDED.clicked_at,
|
|
replied_at = EXCLUDED.replied_at
|
|
`, CampaignAcmeActiveID, c.id, SequenceAcmeStep1ID, i)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func seedCampaignLogs(ctx context.Context, pool *pgxpool.Pool, _ *Result) error {
|
|
type logRow struct {
|
|
campaign uuid.UUID
|
|
event string
|
|
message string
|
|
}
|
|
rows := []logRow{
|
|
{CampaignAcmeActiveID, "started", "Campaign moved to active"},
|
|
{CampaignAcmeActiveID, "sequence_added", "Step 3 - break-up added"},
|
|
{CampaignAcmeActiveID, "contact_replied", "aiden.park@northwind.test replied"},
|
|
{CampaignAcmeDraftID, "created", "Campaign created"},
|
|
}
|
|
for _, r := range rows {
|
|
_, err := pool.Exec(ctx, `
|
|
INSERT INTO campaign_logs (id, campaign_id, event_type, message, metadata, created_at)
|
|
VALUES (gen_random_uuid(), $1, $2, $3, '{}'::jsonb, NOW())
|
|
`, r.campaign, r.event, r.message)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|