Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions .github/workflows/backport.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@ on:

jobs:
backport:
permissions:
contents: read
pull-requests: write
actions: write
runs-on: ubuntu-latest
if: |
github.event.pull_request.merged == true
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<fieldset class="<%= stimulus_id %>"
data-controller="<%= stimulus_id %>"
>
<div class="<%= stimulus_id %>--address-form flex flex-wrap gap-4 pb-4">
<%= render component("ui/forms/field").text_field(@name, :legal_name, object: @store) %>
<%= render component("ui/forms/field").text_field(@name, :address1, object: @store) %>
<%= render component("ui/forms/field").text_field(@name, :address2, object: @store) %>
<div class="flex gap-4 w-full">
<%= render component("ui/forms/field").text_field(@name, :city, object: @store) %>
<%= render component("ui/forms/field").text_field(@name, :zipcode, object: @store) %>
</div>

<%= render component("ui/forms/field").select(
@name,
:country_id,
Spree::Country.pluck(:name, :id),
object: @store,
value: @store.country_id,
"data-#{stimulus_id}-target": "country",
"data-action": "change->#{stimulus_id}#loadStates"
) %>
<%= content_tag :div,
class: "flex flex-col gap-2 w-full #{'hidden' if @store.country&.states_required}",
data: { "#{stimulus_id}-target": "stateNameWrapper" } do %>
<%= render component("ui/forms/field").text_field(
@name, :state_name,
object: @store,
value: @store.state_name,
data: { "#{stimulus_id}-target": "stateName" }
) %>
<% end %>

<input autocomplete="off" type="hidden" name=<%= "#{@name}[state_id]" %>>

<%= content_tag :div,
class: "flex flex-col gap-2 w-full #{'hidden' unless @store.country&.states_required}",
data: { "#{stimulus_id}-target": "stateWrapper" } do %>
<%= render component("ui/forms/field").select(
@name, :state_id,
state_options,
object: @store,
value: @store.state_id,
data: { "#{stimulus_id}-target": "state" }
) %>
<% end %>
<%= render component("ui/forms/field").text_field(@name, :tax_id, object: @store) %>
<%= render component("ui/forms/field").text_field(@name, :vat_id, object: @store, hint: t(".hint.vat_id").html_safe) %>
</div>
</fieldset>
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
static targets = ["country", "state", "stateName", "stateWrapper", "stateNameWrapper"]

loadStates() {
const countryId = this.countryTarget.value

fetch(`/admin/countries/${countryId}/states`)
.then((response) => response.json())
.then((data) => {
this.updateStateOptions(data)
})
}

updateStateOptions(states) {
if (states.length === 0) {
this.toggleStateFields(false)
} else {
this.toggleStateFields(true)
this.populateStateSelect(states)
}
}

toggleStateFields(showSelect) {
const stateWrapper = this.stateWrapperTarget
const stateNameWrapper = this.stateNameWrapperTarget
const stateSelect = this.stateTarget
const stateName = this.stateNameTarget

if (showSelect) {
// Show state select dropdown.
stateSelect.disabled = false
stateName.value = ""
stateWrapper.classList.remove("hidden")
stateNameWrapper.classList.add("hidden")
} else {
// Show state name text input if no states to choose from.
stateSelect.disabled = true
stateWrapper.classList.add("hidden")
stateNameWrapper.classList.remove("hidden")
}
}

populateStateSelect(states) {
const stateSelect = this.stateTarget
stateSelect.innerHTML = ""

states.forEach((state) => {
const option = document.createElement("option")
option.value = state.id
option.innerText = state.name
stateSelect.appendChild(option)
})
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# frozen_string_literal: true

class SolidusAdmin::Stores::AddressForm::Component < SolidusAdmin::BaseComponent
def initialize(store:)
@name = "store"
@store = store
end

def state_options
country = @store.country
return [] unless country && country.states_required

country.states.pluck(:name, :id)
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
en:
hint:
vat_id: Enter your VAT-ID without the Country Prefix (eg IT for Italy or DE for Germany) but solely the identification string.
87 changes: 87 additions & 0 deletions admin/app/components/solidus_admin/stores/edit/component.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<%= page do %>
<%= page_header do %>
<%= page_header_back(solidus_admin.stores_path) %>
<%= page_header_title(t(".title", store: @store&.name)) %>
<%= page_header_actions do %>
<div class="py-1.5 text-center">
<%= render component("ui/button").new(tag: :button, text: t(".update"), form: form_id) %>
<%= render component("ui/button").new(tag: :a, text: t(".cancel"), href: solidus_admin.edit_store_path(@store), scheme: :secondary) %>
</div>
<% end %>
<% end %>

<%= form_for @store, url: solidus_admin.store_path(@store), html: { id: form_id } do |f| %>
<%= page_with_sidebar do %>
<%= page_with_sidebar_main do %>
<%= render component("ui/panel").new(title: t(".store_settings")) do %>
<div class="flex flex-wrap gap-4 pb-4">
<%= render component("ui/forms/field").text_field(f, :name, required: true) %>
<%= render component("ui/forms/field").text_field(f, :url, required: true) %>
<%= render component("ui/forms/field").text_field(f, :code, required: true, hint: t(".hint.code").html_safe) %>
</div>
<% end %>

<%= render component("ui/panel").new(title: t(".regional_settings")) do %>
<div class="flex flex-wrap gap-4 pb-4">
<%= render component("ui/forms/field").select(
f,
:default_currency,
currency_options,
include_blank: true,
hint: t(".hint.default_currency").html_safe
) %>
<%= render component("ui/forms/field").select(
f,
:cart_tax_country_iso,
cart_tax_country_options,
include_blank: t(".no_cart_tax_country"),
hint: t(".hint.cart_tax_country_iso").html_safe
) %>
<%= render component("ui/forms/field").select(
f,
:available_locales,
localization_options,
multiple: true,
class: "select2",
name: "store[available_locales][]",
hint: t(".hint.available_locales").html_safe
) %>
</div>
<% end %>

<%= render component("ui/panel").new(title: t(".email_settings")) do %>
<div class="flex flex-wrap gap-4 pb-4">
<%= render component("ui/forms/field").text_field(f, :mail_from_address, required: true) %>
<%= render component("ui/forms/field").text_field(f, :bcc_email) %>
</div>
<% end %>

<%= render component("ui/panel").new(title: t(".store_legal_addres")) do %>
<div class="flex flex-wrap gap-4 pb-4">
<div class="js-addresses-form">
<%= render component("stores/address_form").new(
store: @store,
) %>
</div>
</div>
<% end %>

<%= render component("ui/panel").new(title: t(".contact_options")) do %>
<div class="flex flex-wrap gap-4 pb-4">
<%= render component("ui/forms/field").text_field(f, :contact_phone) %>
<%= render component("ui/forms/field").text_field(f, :contact_email) %>
</div>
<% end %>

<%= render component("ui/panel").new(title: t(".content_on_storefront")) do %>
<div class="flex flex-wrap gap-4 pb-4">
<%= render component("ui/forms/field").text_field(f, :seo_title) %>
<%= render component("ui/forms/field").text_area(f, :description) %>
<%= render component("ui/forms/field").text_field(f, :meta_keywords) %>
<%= render component("ui/forms/field").text_area(f, :meta_description) %>
</div>
<% end %>
<% end %>
<% end %>
<% end %>
<% end %>
62 changes: 62 additions & 0 deletions admin/app/components/solidus_admin/stores/edit/component.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# frozen_string_literal: true

class SolidusAdmin::Stores::Edit::Component < SolidusAdmin::BaseComponent
include SolidusAdmin::Layout::PageHelpers

# Define the necessary attributes for the component
attr_reader :store, :available_countries

# Initialize the component with required data
def initialize(store:)
@store = store
@available_countries = fetch_available_countries
end

def form_id
@form_id ||= "#{stimulus_id}--form-#{@store.id}"
end

def currency_options
Spree::Config.available_currencies.map(&:iso_code)
end

# Generates options for cart tax countries
def cart_tax_country_options
fetch_available_countries(restrict_to_zone: Spree::Config[:checkout_zone]).map do |country|
[country.name, country.iso]
end
end

# Generates available locales
def localization_options
Spree.i18n_available_locales.map do |locale|
[
I18n.t('spree.i18n.this_file_language', locale: locale, default: locale.to_s),
locale
]
end
end

# Fetch countries for the address form
def available_country_options
Spree::Country.order(:name).map { |country| [country.name, country.id] }
end

private

# Fetch the available countries for the localization section
def fetch_available_countries(restrict_to_zone: Spree::Config[:checkout_zone])
countries = Spree::Country.available(restrict_to_zone:)

country_names = Carmen::Country.all.map do |country|
[country.code, country.name]
end.to_h

country_names.update I18n.t('spree.country_names', default: {}).stringify_keys

countries.collect do |country|
country.name = country_names.fetch(country.iso, country.name)
country
end.sort_by { |country| country.name.parameterize }
end
end
17 changes: 17 additions & 0 deletions admin/app/components/solidus_admin/stores/edit/component.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
en:
address: Address
cancel: Cancel
contact_options: Contact Options
content_on_storefront: Content on Storefront
email_settings: Email Settings
hint:
available_locales: This determines which locales are available for your customers to choose from in the storefront.
cart_tax_country_iso: 'This determines which country is used for taxes on carts (orders which don''t yet have an address).<br> Default: None.'
code: An identifier for your store. Developers may need this value if you operate multiple storefronts.
default_currency: This determines which currency will be used for the storefront's product prices. Please, be aware that changing this configuration, only products that have prices in the selected currency will be listed on your storefront. <br>This setting won't change the default currency used when you create a product. For that, only the global `Spree::Config.currency` is taken into account.
regional_settings: Regional Settings
store_details: Store Details
store_legal_addres: Store Legal Address
store_settings: Store Name & URL / URI Settings
title: "%{store}"
update: Update
87 changes: 87 additions & 0 deletions admin/app/components/solidus_admin/stores/new/component.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<%= page do %>
<%= page_header do %>
<%= page_header_back(solidus_admin.stores_path) %>
<%= page_header_title(t(".title")) %>
<%= page_header_actions do %>
<div class="py-1.5 text-center">
<%= render component("ui/button").new(tag: :button, text: t(".save"), form: form_id) %>
<%= render component("ui/button").new(tag: :a, text: t(".cancel"), href: solidus_admin.new_store_path, scheme: :secondary) %>
</div>
<% end %>
<% end %>

<%= form_for @store, url: solidus_admin.stores_path, html: { id: form_id } do |f| %>
<%= page_with_sidebar do %>
<%= page_with_sidebar_main do %>
<%= render component("ui/panel").new(title: t(".store_settings")) do %>
<div class="flex flex-wrap gap-4 pb-4">
<%= render component("ui/forms/field").text_field(f, :name, required: true) %>
<%= render component("ui/forms/field").text_field(f, :url, required: true) %>
<%= render component("ui/forms/field").text_field(f, :code, required: true, hint: t(".hint.code").html_safe) %>
</div> <!-- Closing div was missing -->
<% end %>

<%= render component("ui/panel").new(title: t(".regional_settings")) do %>
<div class="flex flex-wrap gap-4 pb-4">
<%= render component("ui/forms/field").select(
f,
:default_currency,
currency_options,
include_blank: true,
hint: t(".hint.default_currency").html_safe
) %>
<%= render component("ui/forms/field").select(
f,
:cart_tax_country_iso,
cart_tax_country_options,
include_blank: t(".no_cart_tax_country"),
hint: t(".hint.cart_tax_country_iso").html_safe
) %>
<%= render component("ui/forms/field").select(
f,
:available_locales,
localization_options,
multiple: true,
class: "select2",
name: "store[available_locales][]",
hint: t(".hint.available_locales").html_safe
) %>
</div>
<% end %>

<%= render component("ui/panel").new(title: t(".email_settings")) do %>
<div class="flex flex-wrap gap-4 pb-4">
<%= render component("ui/forms/field").text_field(f, :mail_from_address, required: true) %>
<%= render component("ui/forms/field").text_field(f, :bcc_email) %>
</div>
<% end %>

<%= render component("ui/panel").new(title: t(".store_legal_addres")) do %>
<div class="flex flex-wrap gap-4 pb-4">
<div class="js-addresses-form">
<%= render component("stores/address_form").new(
store: @store,
) %>
</div>
</div>
<% end %>

<%= render component("ui/panel").new(title: t(".contact_options")) do %>
<div class="flex flex-wrap gap-4 pb-4">
<%= render component("ui/forms/field").text_field(f, :contact_phone) %>
<%= render component("ui/forms/field").text_field(f, :contact_email) %>
</div>
<% end %>

<%= render component("ui/panel").new(title: t(".content_on_storefront")) do %>
<div class="flex flex-wrap gap-4 pb-4">
<%= render component("ui/forms/field").text_field(f, :seo_title) %>
<%= render component("ui/forms/field").text_area(f, :description) %>
<%= render component("ui/forms/field").text_field(f, :meta_keywords) %>
<%= render component("ui/forms/field").text_area(f, :meta_description) %>
</div>
<% end %>
<% end %>
<% end %>
<% end %>
<% end %>
Loading