chore: complete M1.1 runtime QA and release packaging

This commit is contained in:
XShop
2026-09-13 19:04:41 +03:30
commit f11e1d650d
66 changed files with 3094 additions and 0 deletions
+105
View File
@@ -0,0 +1,105 @@
<?php
/**
* Front Page — extensible section architecture.
* Static front page: hero + latest posts + promo foundations.
* Blog front page: delegates to index behavior.
*
* @package XShop
*/
declare(strict_types=1);
defined('ABSPATH') || exit;
if (is_home()) {
// When front page shows blog posts, behave like index.
get_template_part('index');
return;
}
get_header();
?>
<div class="xshop-container xshop-stack">
<?php
// Hero — filterable so child theme / builder can replace.
$hero = apply_filters('xshop_front_hero_args', [
'title' => get_bloginfo('name') ? get_bloginfo('name') : esc_html__('XShop — Foundation', 'xshop'),
'text' => get_bloginfo('description') ? get_bloginfo('description') : esc_html__('A polished, RTL-ready WooCommerce foundation. Your shop content will appear here.', 'xshop'),
'cta_label' => esc_html__('Browse Shop', 'xshop'),
'cta_url' => function_exists('wc_get_page_permalink') ? wc_get_page_permalink('shop') : home_url('/'),
]);
get_template_part('template-parts/components/hero', null, $hero);
?>
<?php
// Category / content grid foundation — shows top categories when available, else latest content placeholder.
$cats = get_terms(['taxonomy' => 'category', 'hide_empty' => true, 'number' => 6]);
if (!is_wp_error($cats) && !empty($cats)):
?>
<section class="xshop-section" aria-labelledby="xshop-cat-heading">
<?php get_template_part('template-parts/components/section-heading', null, ['title' => esc_html__('Categories', 'xshop'), 'tag' => 'h2']); ?>
<div class="xshop-grid" style="grid-template-columns:repeat(auto-fill,minmax(160px,1fr))">
<?php foreach ($cats as $cat): ?>
<a class="xshop-card" href="<?php echo esc_url(get_term_link($cat)); ?>" style="padding:var(--xshop-space-4);text-align:center">
<strong><?php echo esc_html($cat->name); ?></strong>
<span class="xshop-text-muted" style="font-size:var(--xshop-text-sm)"><?php echo esc_html(sprintf(esc_html__('%s posts', 'xshop'), number_format_i18n((int) $cat->count))); ?></span>
</a>
<?php endforeach; ?>
</div>
</section>
<?php endif; ?>
<?php
// Product/content grid foundation — latest products if WC active, else latest posts.
if (xshop_has_woocommerce() && function_exists('wc_get_products')):
$products = wc_get_products(['limit' => 6, 'status' => 'publish', 'orderby' => 'date', 'order' => 'DESC', 'return' => 'ids']);
if (!empty($products)):
?>
<section class="xshop-section" aria-labelledby="xshop-products-heading">
<?php get_template_part('template-parts/components/section-heading', null, ['title' => esc_html__('Latest Products', 'xshop'), 'link_label' => esc_html__('View shop', 'xshop'), 'link_url' => function_exists('wc_get_page_permalink') ? wc_get_page_permalink('shop') : home_url('/')]); ?>
<div class="xshop-grid xshop-grid--products">
<?php foreach ($products as $pid):
$p = wc_get_product((int) $pid);
if (!$p) { continue; }
?>
<article class="xshop-card">
<a class="xshop-card__media" href="<?php echo esc_url(get_permalink((int) $pid)); ?>" tabindex="-1">
<?php if (has_post_thumbnail((int) $pid)): echo get_the_post_thumbnail((int) $pid, 'xshop-card', ['loading'=>'lazy','decoding'=>'async']); else: ?><span class="xshop-card__media--placeholder xshop-card__media" style="aspect-ratio:16/10;display:flex;align-items:center;justify-content:center"><?php esc_html_e('No image', 'xshop'); ?></span><?php endif; ?>
</a>
<div class="xshop-card__body">
<h3 class="xshop-card__title"><a href="<?php echo esc_url(get_permalink((int) $pid)); ?>"><?php echo esc_html($p->get_name()); ?></a></h3>
<div class="xshop-card__meta"><?php echo wp_kses_post($p->get_price_html()); ?></div>
</div>
</article>
<?php endforeach; ?>
</div>
</section>
<?php endif; endif; ?>
<?php
get_template_part('template-parts/components/promo-banner', null, [
'title' => esc_html__('Promotional Banner Foundation', 'xshop'),
'text' => esc_html__('Replace with xshop-core Banners (position: home_hero) in later milestones.', 'xshop'),
'cta_label' => esc_html__('Learn more', 'xshop'),
'cta_url' => home_url('/'),
]);
?>
<?php
// Latest posts foundation — always rendered as content grid fallback.
$latest = new WP_Query(['post_type'=>'post','posts_per_page'=>6,'no_found_rows'=>true,'ignore_sticky_posts'=>true]);
if ($latest->have_posts()):
?>
<section class="xshop-section" aria-labelledby="xshop-latest-heading">
<?php get_template_part('template-parts/components/section-heading', null, ['title' => esc_html__('Latest Posts', 'xshop'), 'link_label' => esc_html__('View blog', 'xshop'), 'link_url' => get_permalink(get_option('page_for_posts')) ?: home_url('/')]); ?>
<div class="xshop-grid xshop-grid--posts">
<?php while ($latest->have_posts()): $latest->the_post(); get_template_part('template-parts/components/post-card'); endwhile; wp_reset_postdata(); ?>
</div>
</section>
<?php else: ?>
<?php get_template_part('template-parts/components/empty-state', null, ['title'=>esc_html__('No posts yet','xshop'),'text'=>esc_html__('Publish posts to see them here.','xshop')]); ?>
<?php endif; ?>
</div>
<?php get_footer(); ?>