Home
Wiki
Home
Wiki
  1. 2. Components
  • Back to home
  • 1. Themes
  • Vs Code
    • Getting Started
  • Kitchenware
    • Layout
      • New Layout
      • Legacy Layout
    • Components
      • Announcement
      • Banner Carousel
      • Banner With Products Carousel
      • Blog Category List
      • Blog List
      • Brand List
      • Brands Carousel
      • Breadcrumb
      • Call To Action
      • Cart
      • Categories List
      • Change Password
      • Checkout
      • Cookie Manager
      • Filter list
      • Footer
      • Forgot Password
      • Form
      • Hero Carousel
      • Icon Block
      • Invitation
      • Last Visited Products
      • Layout
      • Login
      • Map
      • Nav Bar
      • Offer
      • Product Attachments
      • Product Attributes
      • Product Documentation
      • Product Expected
      • Product Modal
      • Products Block
      • Products Carousel
      • Product Single
      • Profile
      • Quote
      • Register
      • Related Products
      • Search
      • Stores
      • Subscribe Newsletter
      • Text with Image
      • Top Bar
      • Video
    • Reusables
      • Getting Started
    • Assets
      • Getting Started
    • SDK
      • Products
        • _findProductsByCategory
        • _findProductsByIds
        • _findProductsByTitle
        • _findProductsByFilter
        • _findProductsByCriteria
        • _findProductsAndCalculate
        • _findProductsThenCalculate
        • _getProductAttributeSet
        • _setLastVisited
      • Categories
        • _findCategoryTreeById
        • _findCategoriesByIds
        • _findCategoryByAlias
        • _findCategoryTreeByAlias
        • _getCategoryContent
      • Collections
        • _getCollectionContent
        • _findCollectionsByIds
        • _findCollectionsByIdsThenCalculate
      • Brands
        • _getBrandContent
        • _findBrandsByIds
      • Cart
        • _addToCartMulti
        • _addToCart
        • _setCart
        • _clearCart
        • _setCartListener
        • _removeFromCart
        • _calculateCart
      • Checkout
        • _startCheckout
        • _updateCheckout
        • _completeCheckout
      • Shopping Lists
        • _getShoppingLists
        • _updateShoppingList
        • _createShoppingList
        • _deleteShoppingList
        • _getShoppingListByAlias
      • Navigation
        • _getFooterMenu
        • _getHeaderMenu
      • Users
        • _getUserById
      • Utils
        • _calculateCurrency
        • _getCurrencySymbol
        • _getCulture
        • _subscribeToNewsletter
        • _findUnitsByIds
  • Noir
    • 0. Introduction
    • 6. FAQ
    • 1. Structure
      • _Overview
      • LayoutA.liquid
      • ComponentsList.liquid
      • Metas.liquid
      • CssVariables.liquid
      • Json.liquid
      • GoogleTagManager.liquid
      • StagingButton.liquid
    • 2. Components
      • _Overview
      • Announcement
      • BannerCarousel
      • BlogCategoryList
      • BlogHighlights
      • BlogList
      • BrandList
      • Breadcrumb
      • Cart
      • CategoriesList
      • ChangePassword
      • Checkout
      • CookieManager
      • CustomTable
      • FilterList
      • Footer
      • ForgotPassword
      • Form
      • IconBlock
      • Invitation
      • LastVisitedProducts
      • Login
      • Map
      • NavBar
      • Offer
      • ProductAttachments
      • ProductAttributes
      • ProductComparison
      • ProductDocumentation
      • ProductMixList
      • ProductsBlock
      • ProductsCarousel
      • ProductSingle
      • Profile
      • Quote
      • Register
      • RelatedProducts
      • SingleBlog
      • Stores
      • TextWithImage
      • ThankYouPage
      • TopBar
      • Wishlist
    • 3. Reusables
      • _Overview
      • Addresses
      • AddressForm
      • AnnouncementModal
      • BackToTop
      • BillingRetail
      • BlogPostingStructuredData
      • BrandListStructuredData
      • CollectionStructuredData
      • Company
      • General
      • Login
      • LoginModal
      • MonthlyTransactions
      • Offers
      • Orders
      • Payment
      • ProductAttachments
      • ProductAttributes
      • ProductComparisonButton
      • ProductComparisonFloatingButton
      • ProductGridItem
      • ProductGridItemClassic
      • ProductListItem
      • ProductModal
      • ProductStructuredData
      • ProfileInfo
      • PromptModal
      • Quotes
      • Register
      • Services
      • Shipping
      • ShoppingLists
      • ShoppingListsButton
      • ShoppingListsNavbar
      • StoresStructuredData
      • Toast
      • Transactions
      • Users
      • VariantContent
      • VariantContentClassic
      • WishlistButton
    • 4. Assets
      • Fonts
      • Images
      • Templates
      • Javascript
        • _Overview
        • theme.js
      • Css - Scss
        • _Overview
        • ThemeClasses
    • 5. SDK
      • _Overview
      • LiquidGlobals
      • ServicesSDK
  1. 2. Components

Profile

Purpose#

The Profile component renders the “My account” page.
It:
builds a vertical tab menu (Orders / Info / Addresses / Company / Users / Shopping lists depending on settings)
renders only the current tab's content, delegating to a reusable (Orders, Addresses, Company, Users, etc.)
links each menu item to its own subpage (/profile/<tab>), so switching tabs is a full page load
keeps the sticky menu offset in sync with the header height
allows logout via navbardefault.handleLogout(...)
For B2B customers (GlobalData.Settings.IsB2bCustomer == true) it also supports finance-related tabs:
transactions → Reusables/Transactions/Default
monthlytransactions → Reusables/MonthlyTransactions/Default

Inputs (model contract)#

Model shape (storefront example)#

{
  "companyLogoUrl": null,
  "currentTab": "orders",
  "countriesList": [
    {
      "code": "GR",
      "name": "Sample text"
    },
    "..."
  ],
  "addresses": [
    {
      "address": {
        "id": "Address Id",
        "firstName": "Sample text",
        "lastName": "Sample text",
        "phoneNumber": "Sample text",
        "email": "sample@example.com",
        "address1": "Sample text",
        "additionalInfo": "",
        "label": "",
        "city": "Sample text",
        "postalCode": "Sample text",
        "state": "Sample text",
        "country": "Sample text",
        "countryCode": "GR"
      },
      "isValid": true
    },
    "..."
  ],
  "headQuarterAddress": null,
  "branches": null,
  "orderData": {
    "totalPages": 1,
    "page": 1,
    "list": [
      {
        "id": "Order Id",
        "status": "Placed",
        "code": "Sample text",
        "placedAt": "2026-01-01T00:00:00.0000000+00:00",
        "billingAddress": {
          "firstName": "Sample text",
          "lastName": "Sample text",
          "phoneNumber": "Sample text",
          "email": "sample@example.com",
          "address1": "Sample text",
          "city": "Sample text",
          "postalCode": "Sample text",
          "state": "Sample text",
          "country": "Sample text"
        },
        "shippingAddress": {
          "firstName": "Sample text",
          "lastName": "Sample text",
          "phoneNumber": "Sample text",
          "email": "sample@example.com",
          "address1": "Sample text",
          "city": "Sample text",
          "postalCode": "Sample text",
          "state": "Sample text",
          "country": "Sample text"
        },
        "shippingLine": {
          "totalAmount": 5,
          "title": "Sample text",
          "totalAmountText": "Sample price text",
          "carrierId": "Carrier Id",
          "carrierCode": "sample",
          "netPrice": 5,
          "vatLines": []
        },
        "payment": {
          "serviceAmount": 2,
          "serviceAmountText": "Sample price text",
          "name": "Sample text",
          "provider": "COD",
          "amount": 0
        },
        "documentType": "Receipt",
        "invoiceData": {
          "tin": "",
          "taxOffice": "",
          "companyName": "",
          "profession": ""
        },
        "totalQuantity": 2,
        "totalAmountText": "Sample price text",
        "subTotalPriceText": "Sample price text",
        "discountPriceText": "Sample price text",
        "netValuePriceText": "Sample price text",
        "finalPriceText": "Sample price text",
        "vatAmountText": "Sample price text",
        "companyId": "Company Id",
        "cartToken": "Cart Token",
        "customerId": "Customer Id",
        "customerName": "Sample text",
        "discountRate": 0,
        "discountValue": 0,
        "totalAmount": 87,
        "expenseAmount": 0,
        "netAmount": 64.52,
        "vatAmount": 15.48,
        "financialStatus": "Pending",
        "fulfillmentStatus": "Fulfilled",
        "requiresCalculation": true,
        "lines": [
          {
            "id": "Order Line Id",
            "productId": "Product Id",
            "productVariantId": "Variant Id",
            "productTitle": "Sample text",
            "sku": "Sample text",
            "quantity": 2,
            "mediaItem": {
              "id": "Media Item Id",
              "link": "https://example.com/media/sample.jpg",
              "position": 0,
              "alt": "sample.jpg",
              "mediaType": "Image"
            },
            "link": "sample-product",
            "priceText": "Sample price text",
            "totalPriceText": "Sample price text",
            "netValue": 64.52,
            "price": 40,
            "lineValue": 64.52,
            "discountValue": 0,
            "discountRate": 0,
            "vatValue": 15.48,
            "vatRate": 24,
            "vatCode": ""
          },
          "..."
        ],
        "vatAnalysis": [
          {
            "vatRate": 24,
            "vatAmount": 15.48,
            "vatCode": "",
            "netAmount": 64.52
          },
          "..."
        ],
        "tags": [],
        "sourceTags": [],
        "updateDate": "2026-01-01T00:00:00.0000000+00:00",
        "insertDate": "2026-01-01T00:00:00.0000000+00:00",
        "favorite": false,
        "userId": "User Id",
        "loyaltyPricing": {
          "usePoints": false,
          "useCoupon": false,
          "couponCodes": []
        },
        "pricingBeforeDiscount": {
          "totalAmount": 0,
          "totalAmountText": "Sample price text",
          "netAmount": 0,
          "netAmountText": "Sample price text",
          "vatAmount": 0,
          "vatAmountText": "Sample price text",
          "expenseAmountText": "Sample price text"
        }
      },
      "..."
    ],
    "availableStatuses": [
      "Placed",
      "..."
    ],
    "availableYears": [
      "2026",
      "..."
    ]
  },
  "shoppingLists": [],
  "userManagement": null,
  "transactionData": {
    "pagination": {
      "pageNumber": 1,
      "numberOfPages": 28
    },
    "dateFrom": "2025-01-01",
    "dateTo": "2025-01-31",
    "lines": [
      {
        "id": "Transaction Id",
        "code": "CRN-2025-000007",
        "credit": 80.0,
        "creditText": "80.00 €",
        "debit": 0.0,
        "debitText": "0.00 €",
        "turnover": -80.0,
        "turnoverText": "-80.00 €",
        "progressiveBalance": 980.5,
        "progressiveBalanceText": "980.50 €",
        "description": "Sample description",
        "notes": "Sample notes",
        "transactionDate": "2025-01-25T16:05:00Z",
        "invoiceUrl": "https://example.com/sample"
      }
    ],
    "forward": {
      "credit": 1500.0,
      "debit": 2200.0,
      "turnover": 700.0,
      "balance": 700.0
    },
    "total": {
      "credit": 2080.0,
      "debit": 3760.5,
      "turnover": 1680.5,
      "balance": 1680.5
    }
  },
  "balSheetData": {
    "years": [
      {
        "fiscalYear": "2026",
        "lines": [
          {
            "period": "January",
            "fiscalYear": "2026",
            "credit": 1500.0,
            "creditText": "1,500.00 €",
            "debit": 1200.5,
            "debitText": "1,200.50 €",
            "turnover": 299.5,
            "turnoverText": "299.50 €"
          },
          "..."
        ],
        "total": {
          "credit": 2300.0,
          "creditText": "2,300.00 €",
          "debit": 2150.5,
          "debitText": "2,150.50 €",
          "turnover": 149.5,
          "turnoverText": "149.50 €",
          "balance": null,
          "balanceText": null
        }
      },
      "..."
    ],
    "availableFiscalYears": [
      "2026",
      "2025"
    ],
    "total": {
      "credit": 45000.0,
      "creditText": "45,000.00 €",
      "debit": 42500.0,
      "debitText": "42,500.00 €",
      "turnover": 2500.0,
      "turnoverText": "2,500.00 €",
      "balance": 2500.0,
      "balanceText": "2,500.00 €"
    }
  },
  "name": "Profile",
  "view": "Default",
  "section": "SectionA",
  "settings": {
    "id": "Component Id",
    "section": "SectionA",
    "type": "NoirProfile",
    "name": "Profile",
    "configuredInContentApi": true,
    "view": "Default",
    "displayName": "",
    "cssClass": ""
  },
  "translations": {
    "myAccount": "Sample text",
    "hello": "Sample text",
    "orders": "Sample text",
    "...": "..."
  }
}

Required fields#

settings.id

Optional fields#

currentTab (defaults to orders)
tabQueryString (defaults to empty) — query string appended to every tab link so the Admin
preview context (env, templateCode) survives navigation
settings.cssClass / model.Settings.CssClass (string, optional)
Appended to the root <section> when it’s not empty and not (UNDEFINED).
orderData (consumed by Reusables\\Orders\\Default)
addresses, countriesList (consumed by Reusables\\Addresses\\Default)
headQuarterAddress, branches, companyLogoUrl (consumed by Reusables\\Company\\Default)
shoppingLists (consumed by Reusables\\ShoppingLists\\Default)
userManagement (consumed by Reusables\\Users\\Default)
offerData (consumed by Reusables\\Offers\\Default)
quoteData (consumed by Reusables\\Quotes\\Default)
transactionData (consumed by Reusables\\Transactions\\Default)
balSheetData (consumed by Reusables\\MonthlyTransactions\\Default)
translations.*
Only the property belonging to the requested tab is populated per request; the others keep their
default (empty) value — see "Tab routing" below.

Template behavior (Liquid + Alpine)#

Builds the tabs list differently for B2B vs B2C (see "Tabs list" below).
Computes initials in Liquid from GlobalData.User.* and shows them only on desktop.
Renders the menu as <a href="/profile/{tab}{{ tabQueryString }}"> links; the active one gets
active + aria-current="page".
Renders a single panel — the one matching currentTab — and delegates it to a reusable.
The menu is sticky and uses headerHeight (computed in JS) as its top offset.

JavaScript#

Global object#

Components/Profile/Default.js exposes:
Liquid binds it via Alpine:
<section x-data="profiledefault.initComponent()">

initComponent#

Creates the Alpine state for the profile component. Tab switching is not handled by Alpine
anymore: each tab is a normal link (<a href="/profile/{tab}">), so switching tabs is a full
page navigation, not a client-side state change (see "Tab routing" below).

init#

Tracks header height to compute the sticky menu offset and keeps it updated on resize/scroll.

updateHeaderHeight#

Measures the current <header> height and stores it in headerHeight.

Rendering / Behavior#

Tab routing (server-rendered, one subpage per request)#

Each profile subpage (/profile/orders, /profile/addresses, /profile/monthlytransactions, ...)
is a separate request. ProfileViewModelComponentConverter.GetCurrentTabFromUrl() resolves the
current tab from the request path, and ConvertAsync only fetches the data for that tab —
every other section's view-model property stays at its default (empty) value. This is a deliberate
cost/performance trade-off: previously all sections were fetched on every profile page load and
hidden client-side; now only the requested subpage's backend calls run.
Consequences:
Only the current tab's panel is rendered in Components/Profile/Default.liquid (the loop over tabs renders a
single <div class="tab__content"> matching currentTab, not one per tab with the rest hidden).
The side menu items are <a href="/profile/{tab}"> links, not buttons with @click handlers.
Clicking a menu item is a full page load.
Reusables that used to rely on Alpine.store("profile").tabChanged to reset state when the user
switched tabs client-side no longer need to: switching tabs now re-initializes the whole page, so
each reusable's Alpine state starts fresh already. That store/watch was removed.

URL segments#

Menu links always use a lowercase segment, so the shoppingLists tab links to
/profile/shoppinglists (and /profile/shoppinglists/{alias} for a single list). Client code that
reads the tab back out of window.location.pathname must compare case-insensitively — see
Reusables/ShoppingLists/Default.js.

Preserving the preview query string#

tabQueryString carries the Admin preview parameters (env, templateCode) of the current request
and is appended to every menu link, so navigating tabs inside the Admin preview does not fall back to
the published page. It is empty on a normal storefront request.

Unavailable tabs fall back to orders#

ResolveTabForContext mirrors the Liquid tab list: a tab the current customer cannot see (a B2C tab
on a B2B account, or a tab gated by EnableShoppingLists / ShowQuote / ShowOffer) resolves back
to orders. Without this the backend would fetch a section that Components/Profile/Default.liquid never renders,
producing a page with a menu and header but a blank content pane.

Tabs list (B2B vs B2C)#

Tabs are decided in Liquid based on global settings (see Components/Profile/Default.liquid):
If GlobalData.Settings.IsB2bCustomer:
base tabs: orders,transactions,monthlytransactions
optionally adds:
shoppingLists when GlobalData.Settings.EnableShoppingLists
offers when GlobalData.Settings.ShowOffer
quotes when GlobalData.Settings.ShowQuote
always appends: users,info,company
Else (B2C):
base tabs: orders
optionally adds: shoppingLists, offers, quotes (same flags as above)
always appends: info,addresses
The tab list still drives which menu links are rendered; it does not affect which data is fetched
per request — that is decided independently by the URL, in the backend.

Tab pane rendering#

Each tab delegates to a reusable:
orders → Reusables\\Orders\\Default (orderData)
shoppingLists → Reusables\\ShoppingLists\\Default (shoppingLists)
transactions → Reusables\\Transactions\\Default (transactionData)
monthlytransactions → Reusables\\MonthlyTransactions\\Default (balSheetData)
offers → Reusables\\Offers\\Default (offerData)
users → Reusables\\Users\\Default (userManagement)
info → Reusables\\ProfileInfo\\Default
addresses → Reusables\\Addresses\\Default (addresses, countriesList)
company → Reusables\\Company\\Default (headQuarterAddress, branches, companyLogoUrl)
quotes → Reusables\\Quotes\\Default (quoteData)

Global Alpine stores (used by Profile)#

Alpine.store("sharedAddresses") (addresses tab behavior)#

The Addresses reusable seeds runToast based on the current URL (window.location.pathname)
rather than a click handler, since navigating to /profile/addresses is now a full page load:
Alpine.store("sharedAddresses").runToast is driven by the Addresses reusable's own init().
This is used by the Addresses reusable to decide whether it should show validation feedback.
Alpine.store("profile") (tabChanged) and the toast clearing on tab click were removed along
with the client-side tab switching — see "Tab routing" above.

Dependencies#

Alpine.js
Liquid globals:
GlobalData.User.* (initials + greeting)
GlobalData.Settings.* (feature flags for tabs)
External JS objects:
navbardefault.handleLogout(...)
Alpine stores:
sharedAddresses
Reusables:
Reusables\\Orders\\Default
Reusables\\ShoppingLists\\Default
Reusables\\Transactions\\Default
Reusables\\MonthlyTransactions\\Default
Reusables\\Offers\\Default
Reusables\\Users\\Default
Reusables\\ProfileInfo\\Default
Reusables\\Addresses\\Default
Reusables\\Company\\Default
Reusables\\Quotes\\Default

Notes#

The set of tabs rendered depends on GlobalData.Settings.IsB2bCustomer,
GlobalData.Settings.EnableShoppingLists, GlobalData.Settings.ShowOffer and
GlobalData.Settings.ShowQuote.
The menu is plain navigation, not an ARIA tablist: inactive tabs have no panel in the DOM, so
role="tab" / aria-controls would point at elements that do not exist. The rendered panel uses
role="region" + aria-labelledby.
The template shows user initials based on GlobalData.User.FirstName/LastName (fallback to Email).
Modified at 2026-09-17 09:27:02
Previous
ProductSingle
Next
Quote
Built with