@spree/docs 0.1.177 → 0.1.178

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/dist/developer/cli/quickstart.md +1 -1
  2. package/dist/developer/contributing/creating-an-extension.md +2 -2
  3. package/dist/developer/core-concepts/addresses.md +3 -3
  4. package/dist/developer/core-concepts/architecture.md +4 -4
  5. package/dist/developer/core-concepts/calculators.md +2 -2
  6. package/dist/developer/core-concepts/carts.md +207 -0
  7. package/dist/developer/core-concepts/channels.md +3 -3
  8. package/dist/developer/core-concepts/customers.md +1 -1
  9. package/dist/developer/core-concepts/events.md +1 -1
  10. package/dist/developer/core-concepts/fulfillments.md +278 -0
  11. package/dist/developer/core-concepts/imports-exports.md +6 -18
  12. package/dist/developer/core-concepts/inventory.md +3 -3
  13. package/dist/developer/core-concepts/media.md +1 -1
  14. package/dist/developer/core-concepts/metafields.md +1 -1
  15. package/dist/developer/core-concepts/orders.md +92 -339
  16. package/dist/developer/core-concepts/payments.md +1 -1
  17. package/dist/developer/core-concepts/promotions.md +68 -195
  18. package/dist/developer/core-concepts/returns-exchanges-claims.md +173 -0
  19. package/dist/developer/core-concepts/store-credits-gift-cards.md +16 -18
  20. package/dist/developer/core-concepts/stores.md +1 -1
  21. package/dist/developer/core-concepts/taxes-discounts-fees.md +199 -0
  22. package/dist/developer/core-concepts/taxes.md +3 -3
  23. package/dist/developer/create-spree-app/quickstart.md +1 -1
  24. package/dist/developer/customization/api.md +36 -11
  25. package/dist/developer/customization/configuration.md +207 -37
  26. package/dist/developer/customization/decorators.md +8 -8
  27. package/dist/developer/customization/permissions.md +34 -229
  28. package/dist/developer/customization/quickstart.md +159 -108
  29. package/dist/developer/customization/validations.md +1 -1
  30. package/dist/developer/dashboard/overview.md +1 -1
  31. package/dist/developer/how-to/build-a-b2b-store.md +21 -0
  32. package/dist/developer/how-to/build-a-marketplace.md +25 -0
  33. package/dist/developer/how-to/custom-api-authentication.md +1 -1
  34. package/dist/developer/how-to/custom-delivery-rate-provider.md +242 -0
  35. package/dist/developer/how-to/custom-document-numbers.md +154 -0
  36. package/dist/developer/how-to/custom-order-routing.md +1 -1
  37. package/dist/developer/how-to/custom-payment-method.md +1 -1
  38. package/dist/developer/how-to/custom-promotion.md +76 -115
  39. package/dist/developer/how-to/custom-report.md +2 -2
  40. package/dist/developer/how-to/custom-stock-splitter.md +5 -5
  41. package/dist/developer/how-to/sell-digital-products.md +20 -0
  42. package/dist/developer/multi-tenant/quickstart.md +1 -1
  43. package/dist/developer/providers/dam.md +14 -0
  44. package/dist/developer/providers/erp.md +31 -0
  45. package/dist/developer/providers/fulfillment.md +25 -0
  46. package/dist/developer/{deployment/telemetry.md → providers/observability.md} +1 -1
  47. package/dist/developer/providers/overview.md +44 -0
  48. package/dist/developer/providers/pim.md +25 -0
  49. package/dist/developer/providers/sso.md +20 -0
  50. package/dist/developer/sdk/admin/extending.md +1 -1
  51. package/dist/developer/sdk/admin/quickstart.md +1 -1
  52. package/dist/developer/sdk/admin/resources.md +1 -1
  53. package/dist/developer/sdk/extending.md +1 -1
  54. package/dist/developer/tutorial/admin-api.md +13 -0
  55. package/dist/developer/tutorial/dashboard-plugin.md +14 -0
  56. package/dist/developer/tutorial/events.md +7 -161
  57. package/dist/developer/tutorial/introduction.md +15 -35
  58. package/dist/developer/tutorial/model.md +7 -98
  59. package/dist/developer/tutorial/store-api.md +13 -0
  60. package/dist/developer/tutorial/storefront.md +12 -0
  61. package/dist/developer/tutorial/testing.md +7 -711
  62. package/dist/developer/upgrades/5.6-to-6.0.md +2 -2
  63. package/package.json +1 -1
  64. package/dist/developer/admin/admin.md +0 -214
  65. package/dist/developer/admin/authentication.md +0 -59
  66. package/dist/developer/admin/components.md +0 -711
  67. package/dist/developer/admin/custom-css.md +0 -256
  68. package/dist/developer/admin/custom-javascript.md +0 -116
  69. package/dist/developer/admin/extending-ui.md +0 -1839
  70. package/dist/developer/admin/form-builder.md +0 -444
  71. package/dist/developer/admin/helper-methods.md +0 -531
  72. package/dist/developer/admin/navigation.md +0 -805
  73. package/dist/developer/admin/tables.md +0 -490
  74. package/dist/developer/advanced/adding_spree_to_rails_app.md +0 -92
  75. package/dist/developer/core-concepts/adjustments.md +0 -113
  76. package/dist/developer/core-concepts/reports.md +0 -208
  77. package/dist/developer/core-concepts/shipments.md +0 -307
  78. package/dist/developer/core-concepts/users.md +0 -303
  79. package/dist/developer/customization/authentication.md +0 -100
  80. package/dist/developer/customization/checkout.md +0 -202
  81. package/dist/developer/customization/emails.md +0 -18
  82. package/dist/developer/customization/routes.md +0 -24
  83. package/dist/developer/multi-vendor/installation.md +0 -61
  84. package/dist/developer/multi-vendor/quickstart.md +0 -17
  85. package/dist/developer/tutorial/admin.md +0 -206
  86. package/dist/developer/tutorial/api.md +0 -606
  87. package/dist/developer/tutorial/extending-models.md +0 -393
  88. package/dist/developer/tutorial/sdk.md +0 -170
  89. package/dist/developer/upgrades/2.0-to-2.1.md +0 -46
  90. package/dist/developer/upgrades/2.1-to-2.2.md +0 -59
  91. package/dist/developer/upgrades/2.2-to-2.3.md +0 -44
  92. package/dist/developer/upgrades/2.3-to-2.4.md +0 -42
  93. package/dist/developer/upgrades/3.0-to-3.1.md +0 -47
  94. package/dist/developer/upgrades/3.1-to-3.2.md +0 -34
  95. package/dist/developer/upgrades/3.2-to-3.3.md +0 -70
  96. package/dist/developer/upgrades/3.3-to-3.4.md +0 -36
  97. package/dist/developer/upgrades/3.4-to-3.5.md +0 -44
  98. package/dist/developer/upgrades/3.5-to-3.6.md +0 -40
  99. package/dist/developer/upgrades/3.6-to-3.7.md +0 -62
  100. package/dist/developer/upgrades/3.7-to-4.0.md +0 -152
  101. package/dist/developer/upgrades/4.0-to-4.1.md +0 -92
  102. package/dist/developer/upgrades/4.1-to-4.2.md +0 -109
  103. package/dist/developer/upgrades/4.10-to-5.0.md +0 -131
  104. package/dist/developer/upgrades/4.2-to-4.3.md +0 -100
  105. package/dist/developer/upgrades/4.3-to-4.4.md +0 -125
  106. package/dist/developer/upgrades/4.4-to-4.5.md +0 -94
  107. package/dist/developer/upgrades/4.5-to-4.6.md +0 -119
  108. package/dist/developer/upgrades/4.6-to-4.7.md +0 -39
  109. package/dist/developer/upgrades/4.8-to-4.9.md +0 -24
  110. package/dist/developer/upgrades/4.9-to-4.10.md +0 -24
  111. package/dist/developer/upgrades/4.x-to-4.8.md +0 -52
  112. package/dist/developer/upgrades/5.0-to-5.1.md +0 -28
  113. package/dist/developer/upgrades/5.1-to-5.2.md +0 -131
  114. package/dist/developer/upgrades/5.2-to-5.3.md +0 -338
  115. package/dist/developer/upgrades/5.3-to-5.4.md +0 -277
  116. package/dist/developer/upgrades/5.4-to-5.5.md +0 -301
  117. package/dist/developer/upgrades/5.5-to-5.6.md +0 -207
@@ -0,0 +1,14 @@
1
+ ---
2
+ title: Dashboard Plugin
3
+ description: Scaffold a dashboard plugin for ProductFaqs — a table, a form, and navigation, generated by the CLI and wired to the Admin API endpoint.
4
+ ---
5
+
6
+ With the Admin API in place, the back-office UI is a dashboard plugin: a table listing FAQs, a form to create and edit them, and a navigation entry — scaffolded by the CLI and customized from there.
7
+
8
+ ## What this chapter covers
9
+
10
+ - Scaffolding the plugin with the CLI
11
+ - The resource table: columns, search and pagination against the Admin API
12
+ - The form: fields, validation errors mapped from API responses
13
+ - Registering navigation and permissions-aware visibility
14
+ - Running it against your development store
@@ -1,166 +1,12 @@
1
1
  ---
2
- title: Events & Webhooks
3
- description: React to Spree store activity and connect to external systems like an OMS, warehouse, or ERP using event subscribers, the subscriber generator, and webhooks.
2
+ title: Events
3
+ description: Publish lifecycle events for FAQs and react to them subscribers inside the app, webhooks for systems outside it.
4
4
  ---
5
5
 
6
- > **INFO:** This guide assumes you've completed the [Model](model.md) and [API](api.md) tutorials we'll reuse the Brand serializer created there.
6
+ Features rarely live alone something usually needs to know when data changes. This chapter publishes lifecycle events for FAQs and consumes them both ways.
7
7
 
8
- Almost every real store talks to other systems: an order management system (OMS), a warehouse (WMS), an ERP, a CRM, a marketing platform. In Spree, the integration surface is the **events system** — models publish events as things happen, and you react to them without touching core code.
8
+ ## What this chapter covers
9
9
 
10
- There are two ways to consume events, and they serve different audiences:
11
-
12
- | Mechanism | Code lives | Best for |
13
- |---|---|---|
14
- | **Subscriber** | In your Spree app | Calling external APIs with your own client code, internal side effects, anything needing app context |
15
- | **Webhook** | In the external system | Letting a third party receive HTTP callbacks — no Ruby in your app, endpoints managed from the admin |
16
-
17
- We'll do both: push completed orders to an OMS with a subscriber, give the Brand model its own lifecycle events, and set up an outbound webhook.
18
-
19
- ## Step 1: Subscribe to a Core Event
20
-
21
- When an order completes, send it to the OMS. Generate a subscriber:
22
-
23
-
24
- ```bash Spree CLI (Docker)
25
- spree generate subscriber OmsOrderSync order.completed
26
- ```
27
-
28
- ```bash Without Spree CLI
29
- bin/rails g spree:subscriber OmsOrderSync order.completed
30
- ```
31
-
32
-
33
- This creates the subscriber, a spec stub, and — crucially — **registers it** in `config/initializers/spree.rb` (injected into the existing `after_initialize` block every Spree app ships with). Subscribers are not auto-discovered; a subscriber that never gets appended to `Spree.subscribers` is a silent no-op, which is why the generator owns that step (re-runs are idempotent, and each new subscriber appends to the same initializer).
34
-
35
- Fill in the handler:
36
-
37
- ```ruby app/subscribers/oms_order_sync_subscriber.rb
38
- class OmsOrderSyncSubscriber < Spree::Subscriber
39
- subscribes_to 'order.completed'
40
-
41
- def handle(event)
42
- order = Spree::Order.find_by_prefix_id(event.payload['id'])
43
- return unless order
44
-
45
- OmsClient.create_order(
46
- number: order.number,
47
- email: order.email,
48
- line_items: order.line_items.map { |li| { sku: li.sku, quantity: li.quantity } }
49
- )
50
- end
51
- end
52
- ```
53
-
54
- Two things worth understanding:
55
-
56
- - **Payloads carry prefixed IDs**, not raw database IDs — always look records up with `find_by_prefix_id`. The payload itself is the resource serialized with its v3 API serializer, so `event.payload['number']`, `['email']`, etc. are available directly when you don't need the full record.
57
- - **Subscribers run async by default** — each `handle` call is an ActiveJob on the events queue, so a slow OMS API never blocks checkout. Pass `subscribes_to 'order.completed', async: false` only when you genuinely need synchronous execution.
58
-
59
- Restart the server, complete a test order, and watch the job fire (`spree logs worker` — or your job backend's UI).
60
-
61
- ## Step 2: Give Brand Its Own Lifecycle Events
62
-
63
- Core models like Payment and Shipment publish `*.created` / `*.updated` / `*.deleted` events automatically. Your models can too — add one line to the Brand model:
64
-
65
- ```ruby app/models/spree/brand.rb {3}
66
- module Spree
67
- class Brand < Spree.base_class
68
- publishes_lifecycle_events
69
-
70
- # ... existing code ...
71
- end
72
- end
73
- ```
74
-
75
- Now `brand.created`, `brand.updated`, and `brand.deleted` fire after the matching transactions commit — and because you created `Spree::Api::V3::BrandSerializer` in the [API step](api.md), the payloads automatically use it (the events system resolves the serializer by naming convention). Anyone — subscriber or webhook — can react to brand changes with the same JSON shape your API serves.
76
-
77
-
78
- ```bash Spree CLI (Docker)
79
- spree generate subscriber BrandSync brand.created brand.updated
80
- ```
81
-
82
- ```bash Without Spree CLI
83
- bin/rails g spree:subscriber BrandSync brand.created brand.updated
84
- ```
85
-
86
-
87
- ```ruby app/subscribers/brand_sync_subscriber.rb
88
- class BrandSyncSubscriber < Spree::Subscriber
89
- subscribes_to 'brand.created', 'brand.updated'
90
-
91
- def handle(event)
92
- SearchIndexer.upsert_brand(event.payload)
93
- end
94
- end
95
- ```
96
-
97
- ## Step 3: Publish a Custom Event
98
-
99
- Lifecycle events cover persistence; custom events express *domain* moments. Say featuring a brand should notify the marketing platform:
100
-
101
- ```ruby
102
- brand.publish_event('brand.featured')
103
- ```
104
-
105
- The payload defaults to the serializer output; pass your own hash as the second argument when the event needs different data. Subscribers consume it like any other event name.
106
-
107
- ## Step 4: Outbound Webhooks — No Code Required
108
-
109
- When the consumer is an external system you don't deploy code into, use webhooks. In the admin, go to **Settings → Webhooks**, add an endpoint with the destination URL, and pick the events to deliver — `order.completed`, `brand.created`, anything publishing in your store. The endpoint's **signing secret is shown once on creation** — store it in the receiving system.
110
-
111
- Each delivery is an HTTP POST with this envelope:
112
-
113
- ```json
114
- {
115
- "id": "550e8400-e29b-41d4-a716-446655440000",
116
- "name": "order.completed",
117
- "created_at": "2026-06-11T12:00:00Z",
118
- "data": { "id": "or_m3Rp9wXz", "number": "R123456789", "...": "..." },
119
- "metadata": {}
120
- }
121
- ```
122
-
123
- And three headers the receiver should use:
124
-
125
- | Header | Contents |
126
- |---|---|
127
- | `X-Spree-Webhook-Event` | The event name |
128
- | `X-Spree-Webhook-Timestamp` | Unix timestamp of the delivery |
129
- | `X-Spree-Webhook-Signature` | `HMAC-SHA256(secret, "{timestamp}.{body}")` |
130
-
131
- Verify the signature before trusting a payload:
132
-
133
- ```ruby
134
- def verified?(request, secret)
135
- timestamp = request.headers['X-Spree-Webhook-Timestamp']
136
- expected = OpenSSL::HMAC.hexdigest('SHA256', secret, "#{timestamp}.#{request.raw_post}")
137
- ActiveSupport::SecurityUtils.secure_compare(expected, request.headers['X-Spree-Webhook-Signature'])
138
- end
139
- ```
140
-
141
- Delivery semantics to design around: failed deliveries (timeouts, connection errors, non-2xx responses) are **recorded, not retried automatically** — redeliver from the endpoint's delivery history in the admin, or via `POST /api/v3/admin/webhook_endpoints/:webhook_endpoint_id/deliveries/:id/redeliver`. After **15 consecutive failures** the endpoint auto-disables and store staff get an email; a successful delivery resets the counter.
142
-
143
- ## Testing a Subscriber
144
-
145
- Subscribers are plain Ruby — test `handle` directly with a constructed event:
146
-
147
- ```ruby spec/subscribers/oms_order_sync_subscriber_spec.rb
148
- require 'rails_helper'
149
-
150
- RSpec.describe OmsOrderSyncSubscriber do
151
- it 'pushes completed orders to the OMS' do
152
- order = create(:completed_order_with_totals)
153
- event = Spree::Event.new(name: 'order.completed', payload: { 'id' => order.prefixed_id })
154
-
155
- expect(OmsClient).to receive(:create_order).with(hash_including(number: order.number))
156
-
157
- described_class.new.handle(event)
158
- end
159
- end
160
- ```
161
-
162
- ## Related Documentation
163
-
164
- - [Events](../core-concepts/events.md) — the full event catalog, subscriber DSL, and delivery guarantees
165
- - [Webhooks](../core-concepts/webhooks.md) — endpoint management, security, and the per-event payload schemas
166
- - [Extending Core Models](extending-models.md) — when a decorator is the right tool instead
10
+ - Publishing lifecycle events from the model
11
+ - A subscriber reacting inside the application
12
+ - Delivering the event to an external system as a webhook
@@ -1,44 +1,24 @@
1
1
  ---
2
- title: Tutorial
3
- sidebarTitle: Introduction
4
- description: Build a complete custom Brands feature in Spree — model, admin UI, Store and Admin APIs, events, and TypeScript storefront integration — end to end.
2
+ title: "Tutorial: Build a Custom Feature"
3
+ description: Build Product FAQs end to end — a custom model, an Admin API endpoint, a dashboard plugin, a Store API endpoint, and storefront rendering through the TypeScript SDK.
5
4
  ---
6
5
 
7
- This tutorial walks you through creating a complete Spree feature from scratch: a "Brands" feature that lets admins manage [Product](../core-concepts/products.md) brands, exposes them through the Store API, and consumes them from TypeScript.
6
+ This tutorial builds one complete feature — **Product FAQs**, questions and answers attached to products — through every layer of a Spree 6 application. By the end you will have a custom model, back-office management in the dashboard, and customer-facing display on the storefront.
8
7
 
9
- It's written for **both backend and frontend developers**. The Ruby you'll write is minimal and mostly generated — Spree's generators produce convention-correct models, admin UIs, and API endpoints, and the TypeScript SDK gives you typed access from your storefront. You don't need to be a Rails expert to follow along.
8
+ ## The two tracks
10
9
 
11
- ## Before you start
12
-
13
- You need a running Spree app. The fastest path is [create-spree-app](../create-spree-app/quickstart.md) — a Docker-based project with the [Spree CLI](../cli/quickstart.md), no local Ruby required:
14
-
15
- ```bash
16
- npx create-spree-app@latest my-store
17
- ```
18
-
19
- An existing app with Spree gems works just as well. **Every command in this tutorial is shown in two forms** — **Spree CLI** (runs inside Docker) and **Without Spree CLI** (runs directly on your machine) — pick the tab that matches your setup and ignore the other.
10
+ The tutorial is organized so you only build what your feature needs:
20
11
 
21
- > **NOTE:** **create-spree-app projects:** run `spree eject` once before starting. Fresh projects use a prebuilt Docker image; ejecting switches to the dev setup that mounts `backend/` into the container, so the files the generators create land in your project (and reload live as you edit them).
12
+ | Track | Chapters | Build this when |
13
+ |---|---|---|
14
+ | Foundation | [Model](model.md) | Always |
15
+ | **A — manage it in the dashboard** | [Admin API](admin-api.md) → [Dashboard plugin](dashboard-plugin.md) | The feature is operated by staff — internal tooling, back-office data |
16
+ | **B — expose it on the storefront** | [Store API](store-api.md) → [Storefront](storefront.md) | Customers see the feature |
17
+ | Finishing | [Events](events.md) → [Testing](testing.md) | Always |
22
18
 
23
- > **TIP:** Using an AI coding agent? Install the [Spree agent skills](../agentic/agent-skills.md) (`npx skills add spree/agent-skills`) and connect the [docs MCP server](../agentic/mcp.md) before starting your agent will know the conventions this tutorial teaches and can follow along with you. See [Agentic Development](../agentic/overview.md).
24
-
25
- ## What you'll build
26
-
27
- * A `Brand` model with a rich text description and an uploadable logo — one generator command
28
- * An admin UI for managing brands — one generator command, then customized
29
- * A `brand` association on Products, surfaced in the product admin form
30
- * Store API endpoints (`GET /api/v3/store/brands`) with serializers and prefixed IDs
31
- * Typed TypeScript access to brands from your storefront via `@spree/sdk`
32
- * Event subscribers and webhooks connecting the store to external systems
33
- * Automated tests for all of it
34
-
35
- ## Tutorial Sections
19
+ An internal tool is Track A alone. A storefront feature over existing data is Track B alone. Product FAQs need both: staff write the answers, customers read them.
36
20
 
21
+ ## Before you start
37
22
 
38
- - [1. Model](model.md) Generate the Brand model columns, rich text description, and logo upload in one command
39
- - [2. Admin Dashboard](admin.md)Scaffold the admin UI, then add the description editor, logo upload, and table columns
40
- - [3. Extending Core Models](extending-models.md) — Connect Brands to Products using a decorator and an association
41
- - [4. API](api.md) — Expose Brands through the Store and Admin APIs — generated in one command, or hand-built to learn the pieces
42
- - [5. Events & Webhooks](events.md) — React to store activity and connect external systems — OMS, warehouse, ERP
43
- - [6. SDK](sdk.md) — Consume Brand endpoints from TypeScript using the SDK
44
- - [7. Testing](testing.md) — Write automated tests for your feature
23
+ - A running Spree applicationsee the [quickstart](../getting-started/quickstart.md)
24
+ - The AI tooling set up — the [agent skills and MCP server](../agentic/overview.md) make every step of this tutorial faster
@@ -1,104 +1,13 @@
1
1
  ---
2
2
  title: Model
3
- description: Create a custom Brand model in Spree with database columns, a rich text description, and an Active Storage logo upload using a single generator command.
3
+ description: Generate the ProductFaq model migration, associations, validations and store scoping the foundation both tracks build on.
4
4
  ---
5
5
 
6
- > **NOTE:** This is a backend customization. You need to perform it in the `backend` directory if you used `create-spree-app` to set up your Spree application.
6
+ Every feature starts with a model. This chapter generates `ProductFaq` question, answer, position, attached to a product and scoped to the store.
7
7
 
8
- In this step we'll create the `Spree::Brand` model with everything it needs: a `name` column, a rich text `description`, and an uploadable `logo`.
8
+ ## What this chapter covers
9
9
 
10
- ## Step 1: Generate the Model
11
-
12
- Spree ships a `spree:model` generator that produces a model and migration following all Spree conventions — `Spree::` namespacing, prefixed IDs, presence validations, and API filtering allowlists. It understands Rails attribute types including the virtual ones, so rich text and file attachments are part of the same command:
13
-
14
-
15
- ```bash Spree CLI (Docker)
16
- spree generate model Brand name:string:index description:rich_text logo:attachment
17
- ```
18
-
19
- ```bash Without Spree CLI
20
- bin/rails g spree:model Brand name:string:index description:rich_text logo:attachment
21
- ```
22
-
23
-
24
- > **INFO:** Throughout this tutorial every command is shown in two forms. **Spree CLI** is for projects created with [create-spree-app](../create-spree-app/quickstart.md), where the app runs in Docker and `spree <command>` routes into the container. **Without Spree CLI** is for apps running Spree backend directly on your machine.
25
-
26
- This creates two files:
27
-
28
- - `app/models/spree/brand.rb` — the model
29
- - `db/migrate/XXXXXXXXXXXXXX_create_spree_brands.rb` — the migration
30
-
31
- Now apply the migration:
32
-
33
-
34
- ```bash Spree CLI (Docker)
35
- spree migrate
36
- ```
37
-
38
- ```bash Without Spree CLI
39
- bin/rails db:migrate
40
- ```
41
-
42
-
43
- This creates the `spree_brands` table with an indexed `name` column. The `description` and `logo` attributes don't add columns — rich text lives in Action Text's `action_text_rich_texts` table, and uploads live in Active Storage's tables.
44
-
45
- ## Step 2: Understand the Generated Model
46
-
47
- Open `app/models/spree/brand.rb`:
48
-
49
- ```ruby app/models/spree/brand.rb
50
- module Spree
51
- class Brand < Spree.base_class
52
- has_prefix_id :brand
53
-
54
- has_rich_text :description
55
- has_one_attached :logo
56
-
57
- validates :name, presence: true
58
-
59
- self.whitelisted_ransackable_attributes = %w[name]
60
- self.whitelisted_ransackable_associations = %w[]
61
- self.whitelisted_ransackable_scopes = %w[]
62
- end
63
- end
64
- ```
65
-
66
- Line by line:
67
-
68
- - **`Spree.base_class`** — inherits all Spree model functionality (multi-store scoping helpers, preferences, and more). The class is namespaced under `Spree::`, so it's available as `Spree::Brand`.
69
- - **`has_prefix_id :brand`** — records get Stripe-style public IDs like `brand_k5nR8xLq`. APIs never expose raw database IDs.
70
- - **`has_rich_text :description`** — formatted content via [Action Text](https://guides.rubyonrails.org/action_text_overview.html).
71
- - **`has_one_attached :logo`** — file uploads via [Active Storage](https://guides.rubyonrails.org/active_storage_overview.html), with image processing and direct-to-storage uploads.
72
- - **`validates :name, presence: true`** — generated automatically for required columns.
73
- - **`whitelisted_ransackable_attributes`** — controls which attributes API clients may filter and sort by. Only allowlisted attributes are queryable, so adding `name` here is what later makes `?q[name_cont]=nike` work.
74
-
75
- ## Step 3: Try It in the Console
76
-
77
-
78
- ```bash Spree CLI (Docker)
79
- spree console
80
- ```
81
-
82
- ```bash Without Spree CLI
83
- bin/rails console
84
- ```
85
-
86
-
87
- ```ruby
88
- brand = Spree::Brand.create!(name: "Wilson")
89
- brand.prefixed_id # => "brand_k5nR8xLq"
90
-
91
- brand.update!(description: "<h1>Hello</h1><p>World</p>")
92
- brand.description.to_s # => rendered rich text HTML
93
- brand.description.to_plain_text # => "Hello World"
94
-
95
- Spree::Brand.find_by_prefix_id!("brand_k5nR8xLq") # lookup by public ID
96
- ```
97
-
98
- We'll upload the logo through the admin UI in the next step — the model side is already done.
99
-
100
- > **TIP:** The generator accepts more attribute types and options — references with auto-resolved class names (`user:belongs_to`), unique indexes (`slug:string:uniq`), soft delete (`--paranoid`), and custom fields support (`--metafields`). Run it with `--help` to see everything.
101
-
102
- ## Next Step
103
-
104
- The model is complete. Now let's give admins a UI to manage brands: [Admin Dashboard](admin.md).
10
+ - Generating the model and migration from the command line
11
+ - Store scoping — why every store-owned record belongs to a store, and what that buys you
12
+ - The association to products, validations, and ordering
13
+ - Running the migration and creating a record in the console
@@ -0,0 +1,13 @@
1
+ ---
2
+ title: Store API
3
+ description: Expose FAQs to customers — a read-only Store API endpoint with a serializer that shows only what shoppers should see.
4
+ ---
5
+
6
+ Customers read FAQs through the Store API. This chapter adds the customer-facing endpoint — read-only, scoped to the current store, with a serializer deliberately smaller than the admin one.
7
+
8
+ ## What this chapter covers
9
+
10
+ - The store resource controller: read-only by default, and why
11
+ - The store serializer: public fields only — no timestamps, no internal data
12
+ - Scoping: only FAQs for purchasable products of the current store
13
+ - Nesting under the product endpoint
@@ -0,0 +1,12 @@
1
+ ---
2
+ title: Storefront
3
+ description: Render FAQs on the product page — regenerate the SDK types, extend the TypeScript client, and build the component in the Next.js storefront.
4
+ ---
5
+
6
+ The Store API endpoint becomes typed TypeScript and a component on the product page. This chapter closes the loop from Rails model to customer screen.
7
+
8
+ ## What this chapter covers
9
+
10
+ - The type generation pipeline: serializer to TypeScript types to runtime schemas
11
+ - Extending the SDK client with the new resource
12
+ - Fetching and rendering FAQs on the product detail page in the Next.js storefront