Store
Describes the current store and its store-wide settings: name, logo, address, contacts, social accounts, apps and feature switches.
import { Store, StoreSettings, StoreContacts, StoreSocial, StoreApps, StoreScope, OpeningHoursSettings } from '@salla.sa/twilight-theme-engine/types';In plain words
Store is the shape of the shop a page is rendered for: its name, logo, web address, contact details and social accounts. It also holds settings, a large object of switches the merchant controls in the Salla dashboard: is the coupon box on, does the store sell in several languages, should numbers show in Arabic digits.
A component gets the store from useStore(). Import the type when you write a helper that takes the store or part of it, so your editor can autocomplete settings.cart.apply_coupon_enabled.
Signature
interface Store {
id: number; name: string; username: string;
url: string; // includes the language, and the username on a shared host
description: string; // HTML
logo: string; icon: string; slogan?: string; about?: string;
meta?: { title?: string; description?: string; keywords?: string };
settings: StoreSettings;
contacts: StoreContacts;
social: StoreSocial;
country: string; store_country?: string; // uppercase ISO codes, e.g. 'SA'
apps?: StoreApps;
scope?: StoreScope | null;
features?: string[];
api?: string; favicon?: string; ray?: number; template?: string | null;
created_at?: string; updated_at?: string; is_merchant?: boolean;
}
interface StoreSettings {
auth: { email_allowed: boolean; mobile_allowed: boolean; is_email_required: boolean;
countries?: string[]; force_login?: boolean };
cart: { apply_coupon_enabled: boolean };
product: { total_sold_enabled: number; show_price_as_dash?: boolean;
fit_type?: 'cover' | 'contain' | null; filters?: boolean | null; /* … */ };
category: { testimonial_enabled: boolean };
arabic_numbers_enabled: boolean; is_multilingual: boolean;
currencies_enabled: boolean; rating_enabled: boolean;
rating?: { /* 16 review switches */ };
blog?: { is_enabled: boolean; allow_likes_and_comments: boolean };
keys?: { maps?: string; gtm?: string[]; sift?: string };
tax?: { number?: string; certificate?: string | null; taxable_prices_enabled: boolean };
buy_now?: string | null; installments?: string | null; quick_order?: string | null;
opening_hours?: OpeningHoursSettings;
features?: { price_quote?: boolean; 'pre-order-campaigns'?: boolean };
// …payments, use_sar_symbol, is_loyalty_enabled, upload_size and more
}
interface StoreContacts { mobile?: string; phone?: string; email?: string; whatsapp?: string; telegram?: string }
interface StoreSocial {
instagram?: string; snapchat?: string; twitter?: string; youtube?: string; facebook?: string;
pinterest?: string; maroof?: string; whatsapp?: string; tiktok?: string;
}
interface StoreApps { appstore?: string; googleplay?: string }
interface StoreScope {
name: string; display_as: 'popup' | 'inline' | 'default';
id?: number; type?: string; selected?: boolean; is_open?: boolean; always_ask?: boolean;
languages?: string[]; currencies?: string[]; countries?: string[]; allocation?: unknown | null;
}
interface OpeningHoursSettings { enabled?: boolean; hours?: Record<string, string[]> }Try it live
Store and StoreSettings. ✗ marks a value the type does not allow.Try this: pick store.settings and turn on Only mismatches: buy_now is an object and installments an empty array.| store.settings. | declared | sent by the demo store |
|---|---|---|
auth | object | object {"email_allowed":true,"mobile_allowed":t |
cart | object | object {"apply_coupon_enabled":true,"basket_gap |
product | object | object {"total_sold_enabled":1,"manual_quantity |
category | object | object {"testimonial_enabled":true} |
payments | array | undefined | array ["mada","credit_card","bank","apple_pay" |
arabic_numbers_enabled | boolean | boolean true |
content_copyright | boolean | undefined | boolean false |
use_sar_symbol | boolean | undefined | boolean true |
is_multilingual | boolean | boolean true |
currencies_enabled | boolean | boolean true |
rating_enabled | boolean | boolean true |
rating | object | undefined | object {"is_enabled":true,"show_on_category":tr |
blog | object | undefined | object {"is_enabled":true,"allow_likes_and_comm |
is_loyalty_enabled | boolean | undefined | boolean false |
keys | object | undefined | object {"maps":"AIzaSyAZpoNuLbXR-VvGYEP4ZbMD9Fq |
is_salla_gateway | boolean | undefined | boolean false |
upload_size | number | undefined | number 2000 |
tax | object | undefined | object {"number":"123456789","certificate":"htt |
certificate | object | null | undefined | object {"id":"1234567890"} |
made_in_ksa | boolean | undefined | boolean false |
commercial_number | string | null | undefined | null null |
freelance_number | string | null | undefined | null null |
buy_now | string | null | undefined | ✗ object {"label":"Salla","countryCode":"SA","sup |
installments | string | null | undefined | ✗ array [] |
quick_order | string | null | undefined | null null |
bullet_delivery | string | null | undefined | null null |
ticketing_system_enabled | boolean | undefined | undefined |
opening_hours | object | undefined | undefined |
features | object | undefined | undefined |
Sent but not in the type: nothing
import { useStore } from '@salla.sa/twilight-theme-engine/hooks/useStore';
export function InstallmentsNote() {
const { settings } = useStore();
// Typed string | null, but the API can send an array, and [] is truthy.
const installments: unknown = settings.installments;
const offered = Array.isArray(installments) ? installments.length > 0 : Boolean(installments);
return offered ? <p className="installments-note">Split your payment</p> : null;
}
Example
import type { StoreSocial } from '@salla.sa/twilight-theme-engine/types';
import { useStore } from '@salla.sa/twilight-theme-engine/hooks/useStore';
const NETWORKS: (keyof StoreSocial)[] = ['instagram', 'twitter', 'tiktok', 'youtube', 'snapchat'];
export function SocialLinks() {
const { social } = useStore();
const links = NETWORKS.filter((network) => social[network]);
return (
<ul className="social-links">
{links.map((network) => (
<li key={network}>
<a href={social[network]} target="_blank" rel="noopener noreferrer">
{network}
</a>
</li>
))}
</ul>
);
}
How it behaves
Read it with
useStore()(the fields are spread at the top level),useTwilight().store, orgetTwilightContext().settings.storein a loader orhead. It is thestoreof the store settings response as sent: the engine does not reshape it.urlincludes the language and, on a shared Salla host, the store username:https://demostore.salla.sa/ar/dev-vgckq3fssfhjewwi/on the demo store. Link to pages with the engineLinkand a path, rather than joining strings onto it.On the demo store
socialvalues are full URLs,descriptionis HTML, andsettings.product.total_sold_enabledis the number1, nottrue.scopedescribes the markets or branches a shopper picks from. When it is set,MasterLayoutrenders Salla's scopes component (its selectionmandatorywhendisplay_asispopup, otherwiseoptional) and the engineHeadera button labelled withscope.namethat opens it. The demo store sendsscope: null.appsfeedsresolveAppStoreUrl(@salla.sa/twilight-theme-engine/utils), andsettings.opening_hoursfeedsuseOpeningHours.
Gotchas
settings.buy_nowandsettings.installmentsare typedstring | null, but the demo store sends an object and an empty array. An empty array is truthy, soif (settings.installments)runs when there are no installments. TestArray.isArray(value) ? value.length > 0 : Boolean(value).settings.product.filtersis typedboolean | null; the demo store sends an object of switches, one per filter ({ category_id: true, price: true, … }).meta.title,meta.descriptionandmeta.keywordsarrive as empty strings when the merchant left them blank (demo store), sostore.meta?.title ?? store.namegives"". Use||, as the enginebuildBaseHeaddoes.settings.opening_hours,settings.featuresandsettings.keys.siftare marked "TODO: add to API response" in the source and are absent on the demo store, so code that reads them seesundefined.The response also carries fields the type does not declare, such as
shipping,ratingsandsupport_pickupon the store, andsettings.cart.multi_coupon(demo store). Reading them needs a cast, and the engine promises nothing about them.
Related
Reads the current store (name, logo, settings, contacts…) and can refresh it from the Salla SDK.
ThemeDescribes the merchant's theme record: colors, font, live or preview mode, and the settings object your twilight.json declares.
BootData and the settings response typesDescribes the raw store settings response: store, theme, currencies, languages, login and analytics configuration, and echoed headers.
getMobileOS, resolveAppStoreUrlDetect whether the visitor uses an iPhone or Android device, and pick the store's matching App Store or Google Play link.
useOpeningHoursWorks out from a weekly schedule whether the store is open right now and, when closed, when it opens next.