Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: ✨ product shipping countdown timer #3324

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@
},
"[liquid]": {
"editor.formatOnSave": true
}
},
"editor.defaultFormatter": "Shopify.theme-check-vscode"
}
14 changes: 14 additions & 0 deletions assets/component-product-shipping-countdown.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
product-shipping-countdown {
display: flex;
padding: 1rem 1.65rem;

background: rgba(var(--color-button), 1);
color: rgba(var(--color-button-text), 1);
border-radius: var(--buttons-radius);
font-weight: 600;
justify-content: center;

&:empty {
display: none;
}
}
95 changes: 95 additions & 0 deletions assets/product-shipping-countdown.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
class ProductShippingCountdown extends HTMLElement {
constructor() {
super();
this.intervalId = null;
}
connectedCallback() {
this.data = {
message: this.dataset.message,
timeThreshold: this.dataset.timeThreshold,
timeZone: this.dataset.timeZone,
showOnWeekends: this.dataset.showOnWeekends.toLowerCase() === 'true',
excludedDates: this.dataset.excludedDates.split(',').map(date => date.trim()),
}

this.updateMessage();
this.intervalId = setInterval(() => this.updateMessage(), 60000);
}
disconnectedCallback() {
if (this.intervalId) {
clearInterval(this.intervalId);
}
}
get timeThreshold() {
const [hours, minutes] = this.data.timeThreshold.split(':').map(Number)
const timeZone = this.data.timeZone

const dateOptions = {
timeZone,
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false
}
const formatter = new Intl.DateTimeFormat('en-US', dateOptions)
const formattedDate = formatter.format(new Date())
const dateParts = formattedDate.match(/(\d{2})\/(\d{2})\/(\d{4}), (\d{2}):(\d{2}):(\d{2})/);

const threshold = new Date(`${dateParts[3]}-${dateParts[1]}-${dateParts[2]}T${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:00Z`);
threshold.setTime(threshold.getTime() + threshold.getTimezoneOffset() * 60 * 1000)
return threshold
}
get isVisible() {
// Returns true if current time is within the threshold
const now = new Date()

// Weekend check
const isWeekend = now.getDay() === 0 || now.getDay() === 6
if (isWeekend && !this.data.showOnWeekends) return false

// Excluded dates check
if (this.data.excludedDates.length) {
const todayFormatted = now.toLocaleDateString('en-US', { month: '2-digit', day: '2-digit' })
if (this.data.excludedDates.includes(todayFormatted)) return false
}

return now < this.timeThreshold
}
get messageCountdownVariable() {
// Returns replacement for {countdown} in the message
// {countdown} -> '3 hours, 2 minutes'
const now = new Date()
const diff = this.timeThreshold - now
const hours = Math.floor(diff / (1000 * 60 * 60))
const minutes = Math.floor((diff / (1000 * 60)) % 60)

const hoursString = hours > 0 ? `${hours} hour${hours > 1 ? 's' : ''}` : ''
const minutesString = minutes > 0 ? `${minutes} minute${minutes > 1 ? 's' : ''}` : ''
const separator = hoursString && minutesString ? ', ' : ''
return `${hoursString}${separator}${minutesString}`
}
get messageTimeVariable() {
const options = { hour: '2-digit', minute: '2-digit', hour12: true, timeZone: this.data.timeZone };
const formatter = new Intl.DateTimeFormat('en-US', options);
return formatter.format(this.timeThreshold);
}
get formattedMessage() {
// Returns message with countdown and time variables replaced
const message = this.data.message
.replace('{countdown}', this.messageCountdownVariable)
.replace('{time}', this.messageTimeVariable)

return message
}
updateMessage() {
console.log('updating message')
if (this.isVisible) {
this.innerText = this.formattedMessage;
}
}
}

customElements.define('product-shipping-countdown', ProductShippingCountdown)
185 changes: 184 additions & 1 deletion config/settings_data.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,188 @@
{
"current": "Default",
"current": {
"logo_width": 90,
"type_header_font": "assistant_n4",
"heading_scale": 100,
"type_body_font": "assistant_n4",
"body_scale": 100,
"page_width": 1200,
"spacing_sections": 0,
"spacing_grid_horizontal": 8,
"spacing_grid_vertical": 8,
"buttons_border_thickness": 1,
"buttons_border_opacity": 100,
"buttons_radius": 0,
"buttons_shadow_opacity": 0,
"buttons_shadow_horizontal_offset": 0,
"buttons_shadow_vertical_offset": 4,
"buttons_shadow_blur": 5,
"variant_pills_border_thickness": 1,
"variant_pills_border_opacity": 55,
"variant_pills_radius": 40,
"variant_pills_shadow_opacity": 0,
"variant_pills_shadow_horizontal_offset": 0,
"variant_pills_shadow_vertical_offset": 4,
"variant_pills_shadow_blur": 5,
"inputs_border_thickness": 1,
"inputs_border_opacity": 55,
"inputs_radius": 0,
"inputs_shadow_opacity": 0,
"inputs_shadow_horizontal_offset": 0,
"inputs_shadow_vertical_offset": 4,
"inputs_shadow_blur": 5,
"card_style": "standard",
"card_image_padding": 0,
"card_text_alignment": "left",
"card_color_scheme": "scheme-2",
"card_border_thickness": 0,
"card_border_opacity": 10,
"card_corner_radius": 0,
"card_shadow_opacity": 0,
"card_shadow_horizontal_offset": 0,
"card_shadow_vertical_offset": 4,
"card_shadow_blur": 5,
"collection_card_style": "standard",
"collection_card_image_padding": 0,
"collection_card_text_alignment": "left",
"collection_card_color_scheme": "scheme-2",
"collection_card_border_thickness": 0,
"collection_card_border_opacity": 10,
"collection_card_corner_radius": 0,
"collection_card_shadow_opacity": 0,
"collection_card_shadow_horizontal_offset": 0,
"collection_card_shadow_vertical_offset": 4,
"collection_card_shadow_blur": 5,
"blog_card_style": "standard",
"blog_card_image_padding": 0,
"blog_card_text_alignment": "left",
"blog_card_color_scheme": "scheme-2",
"blog_card_border_thickness": 0,
"blog_card_border_opacity": 10,
"blog_card_corner_radius": 0,
"blog_card_shadow_opacity": 0,
"blog_card_shadow_horizontal_offset": 0,
"blog_card_shadow_vertical_offset": 4,
"blog_card_shadow_blur": 5,
"text_boxes_border_thickness": 0,
"text_boxes_border_opacity": 10,
"text_boxes_radius": 0,
"text_boxes_shadow_opacity": 0,
"text_boxes_shadow_horizontal_offset": 0,
"text_boxes_shadow_vertical_offset": 4,
"text_boxes_shadow_blur": 5,
"media_border_thickness": 1,
"media_border_opacity": 5,
"media_radius": 0,
"media_shadow_opacity": 0,
"media_shadow_horizontal_offset": 0,
"media_shadow_vertical_offset": 4,
"media_shadow_blur": 5,
"popup_border_thickness": 1,
"popup_border_opacity": 10,
"popup_corner_radius": 0,
"popup_shadow_opacity": 5,
"popup_shadow_horizontal_offset": 0,
"popup_shadow_vertical_offset": 4,
"popup_shadow_blur": 5,
"drawer_border_thickness": 1,
"drawer_border_opacity": 10,
"drawer_shadow_opacity": 0,
"drawer_shadow_horizontal_offset": 0,
"drawer_shadow_vertical_offset": 4,
"drawer_shadow_blur": 5,
"badge_position": "bottom left",
"badge_corner_radius": 40,
"sale_badge_color_scheme": "scheme-5",
"sold_out_badge_color_scheme": "scheme-3",
"social_facebook_link": "",
"social_instagram_link": "",
"social_youtube_link": "",
"social_tiktok_link": "",
"social_twitter_link": "",
"social_snapchat_link": "",
"social_pinterest_link": "",
"social_tumblr_link": "",
"social_vimeo_link": "",
"predictive_search_enabled": true,
"predictive_search_show_vendor": false,
"predictive_search_show_price": false,
"currency_code_enabled": true,
"cart_type": "notification",
"show_vendor": false,
"show_cart_note": false,
"cart_drawer_collection": "",
"sections": {
"main-password-header": {
"type": "main-password-header",
"settings": {
"color_scheme": "scheme-1"
}
},
"main-password-footer": {
"type": "main-password-footer",
"settings": {
"color_scheme": "scheme-1"
}
}
},
"color_schemes": {
"scheme-1": {
"settings": {
"background": "#FFFFFF",
"background_gradient": "",
"text": "#121212",
"button": "#121212",
"button_label": "#FFFFFF",
"secondary_button_label": "#121212",
"shadow": "#121212"
}
},
"scheme-2": {
"settings": {
"background": "#F3F3F3",
"background_gradient": "",
"text": "#121212",
"button": "#121212",
"button_label": "#F3F3F3",
"secondary_button_label": "#121212",
"shadow": "#121212"
}
},
"scheme-3": {
"settings": {
"background": "#242833",
"background_gradient": "",
"text": "#FFFFFF",
"button": "#FFFFFF",
"button_label": "#000000",
"secondary_button_label": "#FFFFFF",
"shadow": "#121212"
}
},
"scheme-4": {
"settings": {
"background": "#121212",
"background_gradient": "",
"text": "#FFFFFF",
"button": "#FFFFFF",
"button_label": "#121212",
"secondary_button_label": "#FFFFFF",
"shadow": "#121212"
}
},
"scheme-5": {
"settings": {
"background": "#334FB4",
"background_gradient": "",
"text": "#FFFFFF",
"button": "#FFFFFF",
"button_label": "#334FB4",
"secondary_button_label": "#FFFFFF",
"shadow": "#121212"
}
}
}
},
"presets": {
"Default": {
"logo_width": 90,
Expand Down
24 changes: 24 additions & 0 deletions locales/en.default.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -1955,6 +1955,30 @@
"price": {
"name": "Price"
},
"shipping_countdown": {
"name": "Shipping countdown",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Localization quality issue found

The following issues may affect the quality of localized translations if they are not addressed:

  • The value Shipping countdown for key sections.main-product.blocks.shipping_countdown.name is very short. Short strings are more likely to be misunderstood by translators without context. Please provide additional context for the translators if possible.

Please look out for other instances of this issue in your PR and fix them as well if possible.

Questions about these messages? Hop in the #help-localization Slack channel.

"settings": {
"message": {
"label": "Message",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Localization quality issue found

The following issues may affect the quality of localized translations if they are not addressed:

  • The value Message for key sections.main-product.blocks.shipping_countdown.settings.message.label is very short. Short strings are more likely to be misunderstood by translators without context. Please provide additional context for the translators if possible.

Please look out for other instances of this issue in your PR and fix them as well if possible.

Questions about these messages? Hop in the #help-localization Slack channel.

"info": "{countdown}: Active countdown timer until time threshold.\n{time}: Show time threshold."
},
"time_threshold": {
"label": "Time threshold",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Localization quality issue found

The following issues may affect the quality of localized translations if they are not addressed:

  • The value Time threshold for key sections.main-product.blocks.shipping_countdown.settings.time_threshold.label is very short. Short strings are more likely to be misunderstood by translators without context. Please provide additional context for the translators if possible.

Please look out for other instances of this issue in your PR and fix them as well if possible.

Questions about these messages? Hop in the #help-localization Slack channel.

"info": "Time threshold in 24-hour format. Example: 14:00 for 2:00 PM."
},
"time_zone": {
"label": "Time zone",
"info": "Time zone in [IANA format](https:\/\/en.wikipedia.org\/wiki\/List_of_tz_database_time_zones). Example: America\/Los_Angeles."
},
"show_on_weekends": {
"label": "Show widget on weekends"
},
"excluded_dates": {
"label": "Excluded dates",
"info": "Include a list of dates to hide the widget in format DD\/MM separated by commas. E.g. 12\/25, 01\/01."
}
}
},
"inventory": {
"name": "Inventory status",
"settings": {
Expand Down