rename project from SimpleshopTheme to Berrypod

All modules, configs, paths, and references updated.
836 tests pass, zero warnings.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
jamey
2026-02-18 21:23:15 +00:00
parent c65e777832
commit 9528700862
300 changed files with 23932 additions and 1349 deletions

View File

@@ -14,7 +14,7 @@ Build a Products context that syncs products from external POD providers (Printi
## Current Domain Analysis
SimpleShop has **6 well-defined domains** with clear boundaries:
Berrypod has **6 well-defined domains** with clear boundaries:
| Domain | Purpose | Schemas | Public Functions |
|--------|---------|---------|------------------|
@@ -36,12 +36,12 @@ The new **Products** context will be a new top-level domain that:
```
┌─────────────────────────────────────────────────────────────────────────────┐
SimpleshopTheme
Berrypod
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────────────────────────────────────────────────────┐ │
│ │ WEB LAYER │ │
│ │ SimpleshopThemeWeb │ │
│ │ BerrypodWeb │ │
│ │ ┌────────────────┐ ┌────────────────┐ ┌─────────────────────────┐ │ │
│ │ │ Shop LiveViews │ │ Admin LiveViews│ │ Theme Editor LiveView │ │ │
│ │ │ - ProductShow │ │ - UserLogin │ │ - ThemeLive.Index │ │ │
@@ -299,22 +299,22 @@ Extract reusable helpers into new `Printify.Catalog` module:
**Module structure:**
```
lib/simpleshop_theme/clients/
lib/berrypod/clients/
├── printify.ex # Printify HTTP client (moved from printify/client.ex)
├── gelato.ex # Gelato HTTP client
└── prodigi.ex # Prodigi HTTP client
lib/simpleshop_theme/providers/
lib/berrypod/providers/
├── provider.ex # Behaviour definition
├── printify.ex # Printify implementation (uses Clients.Printify)
├── gelato.ex # Gelato implementation (uses Clients.Gelato)
└── prodigi.ex # Prodigi implementation (uses Clients.Prodigi)
lib/simpleshop_theme/mockups/
lib/berrypod/mockups/
└── generator.ex # Mockup generation (currently uses Clients.Printify)
# Provider-agnostic location for future flexibility
lib/simpleshop_theme/printify/
lib/berrypod/printify/
└── catalog.ex # Blueprint/variant discovery helpers (Printify-specific)
```
@@ -343,7 +343,7 @@ Each provider module uses its corresponding client. The mockup generator is in a
- Unique constraint: `[:provider_connection_id, :provider_product_id]`
2. **Credentials encrypted in database**
- Use `SimpleshopTheme.Vault` for at-rest encryption
- Use `Berrypod.Vault` for at-rest encryption
- `api_key_encrypted`, `oauth_access_token_encrypted` fields
3. **Cost tracking for profit calculation**
@@ -832,46 +832,46 @@ Add to `mix.exs`:
- `*_create_admin_notifications.exs`
### Schemas
- `lib/simpleshop_theme/products/provider_connection.ex`
- `lib/simpleshop_theme/products/product.ex`
- `lib/simpleshop_theme/products/product_image.ex`
- `lib/simpleshop_theme/products/product_variant.ex`
- `lib/simpleshop_theme/orders/order.ex`
- `lib/simpleshop_theme/orders/order_fulfillment.ex`
- `lib/simpleshop_theme/orders/order_line_item.ex`
- `lib/simpleshop_theme/orders/order_event.ex`
- `lib/simpleshop_theme/admin_notifications/notification.ex`
- `lib/berrypod/products/provider_connection.ex`
- `lib/berrypod/products/product.ex`
- `lib/berrypod/products/product_image.ex`
- `lib/berrypod/products/product_variant.ex`
- `lib/berrypod/orders/order.ex`
- `lib/berrypod/orders/order_fulfillment.ex`
- `lib/berrypod/orders/order_line_item.ex`
- `lib/berrypod/orders/order_event.ex`
- `lib/berrypod/admin_notifications/notification.ex`
### Contexts
- `lib/simpleshop_theme/products.ex` - Product queries, sync logic
- `lib/simpleshop_theme/orders.ex` - Order creation, submission
- `lib/simpleshop_theme/admin_notifications.ex` - Admin notification management
- `lib/berrypod/products.ex` - Product queries, sync logic
- `lib/berrypod/orders.ex` - Order creation, submission
- `lib/berrypod/admin_notifications.ex` - Admin notification management
### Providers
- `lib/simpleshop_theme/providers/provider.ex` - Behaviour definition
- `lib/simpleshop_theme/providers/printify.ex` - Printify implementation
- `lib/berrypod/providers/provider.ex` - Behaviour definition
- `lib/berrypod/providers/printify.ex` - Printify implementation
### Workers
- `lib/simpleshop_theme/sync/product_sync_worker.ex` - Oban worker
- `lib/berrypod/sync/product_sync_worker.ex` - Oban worker
### Webhooks
- `lib/simpleshop_theme_web/controllers/webhook_controller.ex`
- `lib/simpleshop_theme/webhooks/printify_handler.ex`
- `lib/berrypod_web/controllers/webhook_controller.ex`
- `lib/berrypod/webhooks/printify_handler.ex`
### Notifiers
- `lib/simpleshop_theme_web/notifiers/customer_notifier.ex` - Customer emails
- `lib/berrypod_web/notifiers/customer_notifier.ex` - Customer emails
### Support
- `lib/simpleshop_theme/vault.ex` - Credential encryption
- `lib/berrypod/vault.ex` - Credential encryption
---
## Files to Modify
- `lib/simpleshop_theme/printify/client.ex` → Move to `lib/simpleshop_theme/clients/printify.ex`
- `lib/simpleshop_theme/printify/mockup_generator.ex` → Move to `lib/simpleshop_theme/mockups/generator.ex`
- `lib/simpleshop_theme/theme/preview_data.ex` - Query real products when available
- `lib/simpleshop_theme_web/live/shop_live/*.ex` - Use Products context instead of PreviewData
- `lib/berrypod/printify/client.ex` → Move to `lib/berrypod/clients/printify.ex`
- `lib/berrypod/printify/mockup_generator.ex` → Move to `lib/berrypod/mockups/generator.ex`
- `lib/berrypod/theme/preview_data.ex` - Query real products when available
- `lib/berrypod_web/live/shop_live/*.ex` - Use Products context instead of PreviewData
---
@@ -943,10 +943,10 @@ end
```
### Files to Create
- `lib/simpleshop_theme/admin_notifications.ex` - Admin notification context
- `lib/simpleshop_theme/admin_notifications/notification.ex` - Schema
- `lib/simpleshop_theme/orders/order_event.ex` - Customer-facing event schema
- `lib/simpleshop_theme_web/notifiers/customer_notifier.ex` - Emails
- `lib/berrypod/admin_notifications.ex` - Admin notification context
- `lib/berrypod/admin_notifications/notification.ex` - Schema
- `lib/berrypod/orders/order_event.ex` - Customer-facing event schema
- `lib/berrypod_web/notifiers/customer_notifier.ex` - Emails
---
@@ -1012,15 +1012,15 @@ end
**Mocking External APIs (Mox pattern):**
```elixir
# test/support/mocks.ex
Mox.defmock(SimpleshopTheme.Clients.MockPrintify, for: SimpleshopTheme.Clients.PrintifyBehaviour)
Mox.defmock(Berrypod.Clients.MockPrintify, for: Berrypod.Clients.PrintifyBehaviour)
# config/test.exs
config :simpleshop_theme, :printify_client, SimpleshopTheme.Clients.MockPrintify
config :berrypod, :printify_client, Berrypod.Clients.MockPrintify
```
**Oban testing:**
```elixir
use Oban.Testing, repo: SimpleshopTheme.Repo
use Oban.Testing, repo: Berrypod.Repo
# Jobs run synchronously in tests via perform_job/2
```
@@ -1049,7 +1049,7 @@ use Oban.Testing, repo: SimpleshopTheme.Repo
### Example Test Cases
```elixir
# test/simpleshop_theme/products_test.exs
# test/berrypod/products_test.exs
describe "sync_products/1" do
test "syncs products from provider" do
conn = provider_connection_fixture()
@@ -1082,7 +1082,7 @@ describe "sync_products/1" do
end
end
# test/simpleshop_theme/orders_test.exs
# test/berrypod/orders_test.exs
describe "create_order_from_cart/1" do
test "splits cart into fulfillments by provider" do
printify_variant = variant_fixture(provider: :printify)
@@ -1098,7 +1098,7 @@ describe "create_order_from_cart/1" do
end
end
# test/simpleshop_theme/sync/product_sync_worker_test.exs
# test/berrypod/sync/product_sync_worker_test.exs
describe "perform/1" do
test "retries on API failure" do
expect(MockPrintify, :list_products, fn _ -> {:error, :timeout} end)
@@ -1197,12 +1197,12 @@ live "/admin/providers/:id/edit", ProviderLive.Index, :edit
| File | Purpose |
|------|---------|
| `lib/simpleshop_theme_web/live/provider_live/index.ex` | LiveView for provider list + modal forms |
| `lib/simpleshop_theme_web/live/provider_live/index.html.heex` | Template |
| `lib/simpleshop_theme_web/live/provider_live/form_component.ex` | Form component for new/edit |
| `lib/simpleshop_theme/providers/printify.ex` | Add `register_webhooks/1`, `unregister_webhooks/1` |
| `lib/simpleshop_theme/workers/product_sync_worker.ex` | Stub for "Sync Now" (full impl in next task) |
| `test/simpleshop_theme_web/live/provider_live_test.exs` | LiveView tests |
| `lib/berrypod_web/live/provider_live/index.ex` | LiveView for provider list + modal forms |
| `lib/berrypod_web/live/provider_live/index.html.heex` | Template |
| `lib/berrypod_web/live/provider_live/form_component.ex` | Form component for new/edit |
| `lib/berrypod/providers/printify.ex` | Add `register_webhooks/1`, `unregister_webhooks/1` |
| `lib/berrypod/workers/product_sync_worker.ex` | Stub for "Sync Now" (full impl in next task) |
| `test/berrypod_web/live/provider_live_test.exs` | LiveView tests |
### UI Design
@@ -1264,11 +1264,11 @@ Single-page admin with modal for add/edit (follows Phoenix generator pattern):
**Index LiveView (`provider_live/index.ex`):**
```elixir
defmodule SimpleshopThemeWeb.ProviderLive.Index do
use SimpleshopThemeWeb, :live_view
defmodule BerrypodWeb.ProviderLive.Index do
use BerrypodWeb, :live_view
alias SimpleshopTheme.Products
alias SimpleshopTheme.Products.ProviderConnection
alias Berrypod.Products
alias Berrypod.Products.ProviderConnection
@impl true
def mount(_params, _session, socket) do
@@ -1300,7 +1300,7 @@ defmodule SimpleshopThemeWeb.ProviderLive.Index do
end
@impl true
def handle_info({SimpleshopThemeWeb.ProviderLive.FormComponent, {:saved, connection}}, socket) do
def handle_info({BerrypodWeb.ProviderLive.FormComponent, {:saved, connection}}, socket) do
{:noreply, stream_insert(socket, :connections, connection)}
end
@@ -1351,7 +1351,7 @@ end
defp test_provider_connection("printify", api_key) do
# Build temporary connection struct for testing
conn = %ProviderConnection{provider_type: "printify", api_key: api_key}
SimpleshopTheme.Providers.Printify.test_connection(conn)
Berrypod.Providers.Printify.test_connection(conn)
end
```
@@ -1404,7 +1404,7 @@ def handle_event("save", %{"provider" => params}, socket) do
end
defp register_webhooks(%{provider_type: "printify"} = conn) do
SimpleshopTheme.Providers.Printify.register_webhooks(conn)
Berrypod.Providers.Printify.register_webhooks(conn)
end
```
@@ -1422,7 +1422,7 @@ def handle_event("delete", %{"id" => id}, socket) do
end
defp unregister_webhooks(%{provider_type: "printify"} = conn) do
SimpleshopTheme.Providers.Printify.unregister_webhooks(conn)
Berrypod.Providers.Printify.unregister_webhooks(conn)
end
```
@@ -1432,7 +1432,7 @@ end
@webhook_topics ~w(product:publish:started product:deleted shop:disconnected)
def register_webhooks(conn) do
webhook_url = SimpleshopThemeWeb.Endpoint.url() <> "/webhooks/printify"
webhook_url = BerrypodWeb.Endpoint.url() <> "/webhooks/printify"
shop_id = get_shop_id(conn)
results = Enum.map(@webhook_topics, fn topic ->
@@ -1455,7 +1455,7 @@ def unregister_webhooks(conn) do
# List existing webhooks and delete ours
case Client.get(conn, "/shops/#{shop_id}/webhooks.json") do
{:ok, %{"webhooks" => webhooks}} ->
our_url = SimpleshopThemeWeb.Endpoint.url() <> "/webhooks/printify"
our_url = BerrypodWeb.Endpoint.url() <> "/webhooks/printify"
webhooks
|> Enum.filter(&(&1["url"] == our_url))
@@ -1504,7 +1504,7 @@ Template snippet:
### Context Additions
Add to `lib/simpleshop_theme/products.ex`:
Add to `lib/berrypod/products.ex`:
```elixir
@doc """
@@ -1513,7 +1513,7 @@ Returns {:ok, job} or {:error, changeset}.
"""
def enqueue_sync(%ProviderConnection{} = conn) do
%{connection_id: conn.id}
|> SimpleshopTheme.Workers.ProductSyncWorker.new()
|> Berrypod.Workers.ProductSyncWorker.new()
|> Oban.insert()
end
@@ -1543,7 +1543,7 @@ end
### Testing
```elixir
# test/simpleshop_theme_web/live/provider_live_test.exs
# test/berrypod_web/live/provider_live_test.exs
describe "Index" do
setup :register_and_log_in_user
@@ -1619,25 +1619,25 @@ Implement a robust product sync strategy with three mechanisms:
| File | Purpose |
|------|---------|
| `lib/simpleshop_theme/workers/product_sync_worker.ex` | Oban worker for full/single product sync |
| `lib/simpleshop_theme_web/controllers/webhook_controller.ex` | Receives webhooks from providers |
| `lib/simpleshop_theme/webhooks/printify_handler.ex` | Printify-specific webhook processing |
| `test/simpleshop_theme/workers/product_sync_worker_test.exs` | Worker tests |
| `test/simpleshop_theme_web/controllers/webhook_controller_test.exs` | Webhook endpoint tests |
| `lib/berrypod/workers/product_sync_worker.ex` | Oban worker for full/single product sync |
| `lib/berrypod_web/controllers/webhook_controller.ex` | Receives webhooks from providers |
| `lib/berrypod/webhooks/printify_handler.ex` | Printify-specific webhook processing |
| `test/berrypod/workers/product_sync_worker_test.exs` | Worker tests |
| `test/berrypod_web/controllers/webhook_controller_test.exs` | Webhook endpoint tests |
### Part 1: ProductSyncWorker (~1hr)
Oban worker that syncs products from a provider connection.
```elixir
defmodule SimpleshopTheme.Workers.ProductSyncWorker do
defmodule Berrypod.Workers.ProductSyncWorker do
use Oban.Worker,
queue: :sync,
max_attempts: 3,
unique: [period: 60, fields: [:args, :queue]]
alias SimpleshopTheme.Products
alias SimpleshopTheme.Providers
alias Berrypod.Products
alias Berrypod.Providers
@impl Oban.Worker
def perform(%Oban.Job{args: %{"connection_id" => conn_id} = args}) do
@@ -1714,10 +1714,10 @@ post "/webhooks/printify", WebhookController, :printify
**Controller:**
```elixir
defmodule SimpleshopThemeWeb.WebhookController do
use SimpleshopThemeWeb, :controller
defmodule BerrypodWeb.WebhookController do
use BerrypodWeb, :controller
alias SimpleshopTheme.Webhooks.PrintifyHandler
alias Berrypod.Webhooks.PrintifyHandler
def printify(conn, params) do
with :ok <- verify_printify_signature(conn),
@@ -1737,7 +1737,7 @@ defmodule SimpleshopThemeWeb.WebhookController do
# Header: X-Printify-Signature
signature = get_req_header(conn, "x-printify-signature") |> List.first()
body = conn.assigns[:raw_body]
secret = Application.get_env(:simpleshop_theme, :printify_webhook_secret)
secret = Application.get_env(:berrypod, :printify_webhook_secret)
expected = :crypto.mac(:hmac, :sha256, secret, body) |> Base.encode16(case: :lower)
@@ -1752,9 +1752,9 @@ end
**Handler:**
```elixir
defmodule SimpleshopTheme.Webhooks.PrintifyHandler do
alias SimpleshopTheme.Products
alias SimpleshopTheme.Workers.ProductSyncWorker
defmodule Berrypod.Webhooks.PrintifyHandler do
alias Berrypod.Products
alias Berrypod.Workers.ProductSyncWorker
def handle(%{"type" => "product:publish:started", "resource" => resource}) do
%{"shop_id" => shop_id, "id" => product_id} = resource
@@ -1800,16 +1800,16 @@ end
Add to Oban config for daily fallback:
```elixir
# In config/config.exs
config :simpleshop_theme, Oban,
config :berrypod, Oban,
plugins: [
{Oban.Plugins.Cron, crontab: [
{"0 3 * * *", SimpleshopTheme.Workers.ScheduledSyncWorker} # 3 AM daily
{"0 3 * * *", Berrypod.Workers.ScheduledSyncWorker} # 3 AM daily
]}
]
```
```elixir
defmodule SimpleshopTheme.Workers.ScheduledSyncWorker do
defmodule Berrypod.Workers.ScheduledSyncWorker do
use Oban.Worker, queue: :sync
def perform(_job) do
@@ -1827,7 +1827,7 @@ end
### Context Additions
Add to `lib/simpleshop_theme/products.ex`:
Add to `lib/berrypod/products.ex`:
```elixir
def archive_product_by_provider(connection_id, provider_product_id) do