Skip to content

Commit

Permalink
Add organization structured data fields to Spree stores for SEO
Browse files Browse the repository at this point in the history
- Added new fields to spree_stores to support organization structured
data for SEO.
- Fields include legal_name, contact_email, contact_phone, description,
vat_id, tax_id, address1, address2, city, zipcode, state_name,
country_id, and state_id.
- This update enhances SEO by allowing structured data markup for better
search engine visibility.
  • Loading branch information
rahulsingh321 committed Feb 17, 2025
1 parent 7205ff0 commit 4d128b4
Show file tree
Hide file tree
Showing 8 changed files with 264 additions and 10 deletions.
7 changes: 4 additions & 3 deletions api/lib/spree/api_configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,10 @@ def promotion_attributes=(value)
deprecate "promotion_attributes=" => promotion_attributes_deprecation_message, deprecator: Spree.deprecator

preference :store_attributes, :array, default: [
:id, :name, :url, :meta_description, :meta_keywords, :seo_title,
:mail_from_address, :default_currency, :code, :default, :available_locales,
:bcc_email
:id, :name, :legal_name, :url, :meta_description, :meta_keywords, :seo_title,
:mail_from_address, :default_currency, :code, :default,
:bcc_email, :contact_phone, :contact_email, :tax_id, :vat_id, :description,
:address1, :address2, :city, :zipcode, :country_id, :state_id, :state_name, :available_locales
]

preference :store_credit_history_attributes, :array, default: [
Expand Down
134 changes: 129 additions & 5 deletions api/spec/requests/spree/api/stores_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@

module Spree::Api
describe 'Stores', type: :request do
let(:country) { create :country, states_required: true }
let(:country_without_states) { create :country, states_required: false }
let(:state) { create :state, name: 'maryland', abbr: 'md', country: }
let!(:base_attributes) { Spree::Api::Config.store_attributes }

let!(:store) do
create(:store, name: "My Spree Store", url: "spreestore.example.com")
end
Expand All @@ -22,6 +27,58 @@ module Spree::Api
default: false)
end

describe "store state validation" do
context "when store country has states_required" do
it "is invalid without a state" do
store = Spree::Store.new(name: "Test Store", country: country, state: nil, url: "spreestore.example.com",
mail_from_address: "[email protected]", code: "test-store",)
expect(store).not_to be_valid
expect(store.errors[:state]).to include("can't be blank")
end

it "is valid with a state" do
store = Spree::Store.new(name: "Test Store", country: country, state: state, url: "spreestore.example.com",
mail_from_address: "[email protected]", code: "test-store",)
expect(store).to be_valid
end
end

context "when store country has no states" do
it "is valid without a state" do
store = Spree::Store.new(name: "Test Store", country: country_without_states, state: nil, url: "spreestore.example.com",
mail_from_address: "[email protected]", code: "test-store",)
expect(store).to be_valid
end
end

it "is valid without an address and without country/state" do
expect(store).to be_valid
end

it "is valid with only correct country and state" do
store = Spree::Store.create!(
name: "Test Store",
url: "spreestore.example.com",
mail_from_address: "spreestore.example.com",
code: "test-store",
address1: "123 Main St",
city: "New York",
zipcode: "10001",
state: state,
country: country,
)
expect(store).to be_valid
end
end

describe "#index" do
it "ensures the API store attributes match the expected attributes" do
get spree.api_stores_path
first_store = json_response["stores"].first
expect(first_store.keys).to include(*base_attributes.map(&:to_s))
end
end

it "can list the available stores" do
get spree.api_stores_path
expect(json_response["stores"]).to match_array([
Expand All @@ -37,7 +94,20 @@ module Spree::Api
"default_currency" => nil,
"code" => store.code,
"default" => true,
"available_locales" => ["en"]
"available_locales" => ["en"],
"legal_name" => nil,
"contact_email" => nil,
"contact_phone" => nil,
"description" => nil,
"tax_id" => nil,
"vat_id" => nil,
"address1" => nil,
"address2" => nil,
"city" => nil,
"zipcode" => nil,
"country_id" => nil,
"state_id" => nil,
"state_name" => nil
},
{
"id" => non_default_store.id,
Expand All @@ -51,7 +121,20 @@ module Spree::Api
"default_currency" => nil,
"code" => non_default_store.code,
"default" => false,
"available_locales" => ["en"]
"available_locales" => ["en"],
"legal_name" => nil,
"contact_email" => nil,
"contact_phone" => nil,
"description" => nil,
"tax_id" => nil,
"vat_id" => nil,
"address1" => nil,
"address2" => nil,
"city" => nil,
"zipcode" => nil,
"country_id" => nil,
"state_id" => nil,
"state_name" => nil
}
])
end
Expand All @@ -70,7 +153,20 @@ module Spree::Api
"default_currency" => nil,
"code" => store.code,
"default" => true,
"available_locales" => ["en"]
"available_locales" => ["en"],
"legal_name" => nil,
"contact_email" => nil,
"contact_phone" => nil,
"description" => nil,
"tax_id" => nil,
"vat_id" => nil,
"address1" => nil,
"address2" => nil,
"city" => nil,
"zipcode" => nil,
"country_id" => nil,
"state_id" => nil,
"state_name" => nil
)
end

Expand All @@ -79,7 +175,14 @@ module Spree::Api
code: "spree123",
name: "Hack0rz",
url: "spree123.example.com",
mail_from_address: "[email protected]"
mail_from_address: "[email protected]",
legal_name: 'ABC Corp',
address1: "123 Main St",
city: 'San Francisco',
country_id: country.id,
state_id: state.id,
phone: "123-456-7890",
zipcode: "12345"
}
post spree.api_stores_path, params: { store: store_hash }
expect(response.status).to eq(201)
Expand All @@ -89,13 +192,34 @@ module Spree::Api
store_hash = {
url: "spree123.example.com",
mail_from_address: "[email protected]",
bcc_email: "[email protected]"
bcc_email: "[email protected]",
legal_name: 'XYZ Corp',
description: "Leading provider of high-quality tech accessories, offering the latest gadgets, peripherals, and electronics to enhance your digital lifestyle.",
tax_id: "TX-987654321",
vat_id: "VAT-123456789",
address1: "123 Innovation Drive",
address2: "Suite 456",
city: "New York",
country_id: country.id,
state_id: state.id,
contact_phone: "123-456-7888",
zipcode: "10001"
}
put spree.api_store_path(store), params: { store: store_hash }
expect(response.status).to eq(200)
expect(store.reload.url).to eql "spree123.example.com"
expect(store.reload.mail_from_address).to eql "[email protected]"
expect(store.reload.bcc_email).to eql "[email protected]"
expect(store.reload.legal_name).to eql "XYZ Corp"
expect(store.reload.tax_id).to eql "TX-987654321"
expect(store.reload.vat_id).to eql "VAT-123456789"
expect(store.reload.address1).to eql "123 Innovation Drive"
expect(store.reload.address2).to eql "Suite 456"
expect(store.reload.city).to eql "New York"
expect(store.reload.country_id).to eql country.id
expect(store.reload.state_id).to eql state.id
expect(store.reload.contact_phone).to eql "123-456-7888"
expect(store.reload.zipcode).to eql "10001"
end

context "deleting a store" do
Expand Down
65 changes: 65 additions & 0 deletions backend/app/views/spree/admin/stores/_address_form.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@

<% s_or_b = type.chars.first %>
<hr>

<div class="row" id="<%= type %>" data-hook="address_fields">
<div class="col-12 col-md-6">
<div class="field <%= "#{type}-row" %>">
<%= f.label :legal_name %>
<%= f.text_field :legal_name, class: 'fullwidth' %>
</div>

<div class="field <%= "#{type}-row" %>">
<%= f.label :address1 %>
<%= f.text_field :address1, class: 'fullwidth' %>
</div>

<div class="field <%= "#{type}-row" %>">
<%= f.label :address2 %>
<%= f.text_field :address2, class: 'fullwidth' %>
</div>

<div class="field <%= "#{type}-row" %>">
<%= f.label :contact_phone %>
<%= f.phone_field :contact_phone, class: 'fullwidth' %>
</div>
</div>

<div class="col-12 col-md-6">
<div class="field <%= "#{type}-row" %>">
<%= f.label :city %>
<%= f.text_field :city, class: 'fullwidth' %>
</div>

<div class="field <%= "#{type}-row" %>">
<%= f.label :zipcode %>
<%= f.text_field :zipcode, class: 'fullwidth' %>
</div>

<div class="field <%= "#{type}-row" %>">
<%= f.label :country_id, Spree::Country.model_name.human %>
<span id="<%= s_or_b %>country">
<%= f.collection_select :country_id, available_countries, :id, :name, { include_blank: true }, {class: 'custom-select fullwidth js-country_id'} %>
</span>
</div>

<div class="field <%= "#{type}-row" %>">
<%= f.label :state_id, Spree::State.model_name.human %>
<span id="<%= s_or_b %>state">
<%= f.hidden_field :state_name, value: nil %>
<% states = f.object.country.try(:states).nil? ? [] : f.object.country.states %>
<%= f.text_field :state_name,
style: "display: #{states.empty? ? 'block' : 'none' };",
disabled: !states.empty?, class: 'fullwidth state_name js-state_name' %>
<%= f.hidden_field :state_id, value: nil %>
<%= f.collection_select :state_id,
states.sort,
:id, :name,
{ include_blank: true },
{ class: 'custom-select fullwidth js-state_id',
style: "display: #{states.empty? ? 'none' : 'block' };",
disabled: states.empty? } %>
</span>
</div>
</div>
</div>
26 changes: 26 additions & 0 deletions backend/app/views/spree/admin/stores/_form.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,21 @@
<%= f.text_area :meta_description, class: 'fullwidth' %>
<%= f.error_message_on :meta_description %>
<% end %>

<%= f.field_container :tax_id do %>
<%= f.label :tax_id %>
<%= f.text_field :tax_id, class: 'fullwidth' %>
<%= f.error_message_on :tax_id %>
<% end %>

<%= f.field_container :vat_id do %>
<%= f.label :vat_id %>
<%= f.text_field :vat_id, class: 'fullwidth' %>
<%= f.error_message_on :vat_id %>
<% end %>

</div>

<div class="col-12 col-md-6">
<%= f.field_container :url do %>
<%= f.label :url, class: 'required' %>
Expand Down Expand Up @@ -81,5 +95,17 @@
{ class: 'select2 fullwidth', multiple: true } %>
<%= f.error_message_on :default_currency %>
<% end %>

<%= f.field_container :description do %>
<%= f.label :description %>
<%= f.text_area :description, class: 'fullwidth' %>
<%= f.error_message_on :description %>
<% end %>
</div>

<div class="col-12">
<div class="js-addresses-form">
<%= render partial: 'address_form', locals: { f: f, type: 'store' } %>
</div>
</div>
</div>
4 changes: 4 additions & 0 deletions core/app/models/spree/store.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,16 @@ class Store < Spree::Base
has_many :store_shipping_methods, inverse_of: :store
has_many :shipping_methods, through: :store_shipping_methods

belongs_to :state, class_name: 'Spree::State', optional: true
belongs_to :country, class_name: 'Spree::Country', optional: true

has_many :orders, class_name: "Spree::Order"

validates :code, presence: true, uniqueness: { allow_blank: true, case_sensitive: true }
validates :name, presence: true
validates :url, presence: true
validates :mail_from_address, presence: true
validates :state, presence: true, if: -> { country&.states_required }

self.allowed_ransackable_attributes = %w[name url code]

Expand Down
13 changes: 13 additions & 0 deletions core/config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -352,18 +352,31 @@ en:
quantity: Quantity
variant: Variant
spree/store:
address: Address
address1: Street Address
address2: Street Address (cont'd)
available_locales: Locales Available in the Storefront
bcc_email: BCC Email
cart_tax_country_iso: Tax Country for Empty Carts
city: City
code: Slug
contact_email: Contact Email
contact_phone: Contact Phone
country_id: Country
default: Default
default_currency: Default Currency
description: Store Description
legal_name: Legal Name
mail_from_address: Mail From Address
meta_description: Meta Description
meta_keywords: Meta Keywords
name: Site Name
postal_code: Postal Code
seo_title: Seo Title
state_id: State
tax_id: Tax ID
url: Site URL
vat_id: VAT ID
spree/store_credit:
amount: Amount
amount_authorized: Amount Authorized
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
class AddStoreAttributesToSpreeStores < ActiveRecord::Migration[7.2]
def change
add_column :spree_stores, :legal_name, :string
add_column :spree_stores, :contact_email, :string
add_column :spree_stores, :description, :text
add_column :spree_stores, :vat_id, :string
add_column :spree_stores, :tax_id, :string
add_column :spree_stores, :address1, :string
add_column :spree_stores, :address2, :string
add_column :spree_stores, :city, :string
add_column :spree_stores, :zipcode, :string
add_column :spree_stores, :state_name, :string
add_column :spree_stores, :contact_phone, :string
add_column :spree_stores, :country_id, :integer
add_column :spree_stores, :state_id, :integer
add_index :spree_stores, :country_id
add_index :spree_stores, :state_id
end
end
6 changes: 4 additions & 2 deletions core/lib/spree/permitted_attributes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,12 @@ module PermittedAttributes
:quantity, :stock_item, :stock_item_id, :originator, :action
]

@@store_attributes = [:name, :url, :seo_title, :meta_keywords,
@@store_attributes = [:name, :legal_name, :url, :seo_title, :meta_keywords,
:meta_description, :default_currency,
:mail_from_address, :cart_tax_country_iso,
:bcc_email]
:bcc_email, :contact_email, :contact_phone, :code,
:tax_id, :vat_id, :description, :address1, :address2,
:city, :zipcode, :country_id, :state_id, :state_name]

@@taxonomy_attributes = [:name]

Expand Down

0 comments on commit 4d128b4

Please sign in to comment.