Skip to content
Merged
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
19 changes: 10 additions & 9 deletions assets/css/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -313,17 +313,18 @@
}

.home-workflow-step {
background:
linear-gradient(180deg, color-mix(in oklab, var(--color-primary) 4%, transparent), transparent 100%),
color-mix(in oklab, var(--color-card) 92%, transparent);
box-shadow: 0 18px 60px -48px rgba(15, 23, 42, 0.42);
background: color-mix(in oklab, var(--color-card) 96%, var(--color-background) 4%);
box-shadow: 0 14px 44px -42px rgba(15, 23, 42, 0.28);
}

.home-workflow-output {
background:
linear-gradient(180deg, color-mix(in oklab, var(--color-primary) 5%, transparent), transparent 100%),
color-mix(in oklab, var(--color-card) 94%, transparent);
box-shadow: 0 20px 64px -52px rgba(15, 23, 42, 0.42);
background: color-mix(in oklab, var(--color-card) 97%, var(--color-background) 3%);
box-shadow: 0 14px 44px -42px rgba(15, 23, 42, 0.24);
}

.home-workflow-panel {
background: color-mix(in oklab, var(--color-card) 97%, var(--color-background) 3%);
box-shadow: 0 18px 54px -48px rgba(15, 23, 42, 0.28);
}

.home-media-stack {
Expand Down Expand Up @@ -496,7 +497,7 @@
}

.step-indicator {
@apply flex size-12 shrink-0 items-center justify-center rounded-2xl bg-primary/10 text-lg font-bold text-primary;
@apply flex size-9 shrink-0 items-center justify-center rounded-full border border-border/70 bg-background text-sm font-semibold text-foreground/68;
}

.product-3d-loader {
Expand Down
65 changes: 40 additions & 25 deletions assets/js/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ window.htmx = htmx;

const initializedResponsiveTables = new WeakSet<HTMLElement>();
let responsiveTableListenersBound = false;
let productSectionNavBound = false;

// Initialize animations after DOM ready
document.addEventListener("DOMContentLoaded", () => {
Expand Down Expand Up @@ -67,7 +68,7 @@ document.addEventListener("click", (event) => {
function initProductSectionNav() {
const sections = Array.from(document.querySelectorAll<HTMLElement>("[data-product-section]"));
const links = Array.from(document.querySelectorAll<HTMLElement>("[data-product-section-link]"));
if (sections.length === 0 || links.length === 0 || !("IntersectionObserver" in window)) {
if (sections.length === 0 || links.length === 0) {
return;
}

Expand All @@ -83,40 +84,54 @@ function initProductSectionNav() {
});
};

const visible = new Map<string, number>();
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
const section = entry.target as HTMLElement;
if (!section.id) {
return;
}
if (entry.isIntersecting) {
visible.set(section.id, entry.intersectionRatio);
} else {
visible.delete(section.id);
}
});
const stickyNav = document.querySelector<HTMLElement>(".product-detail-sticky-nav");
let ticking = false;

const getActivationOffset = () => {
const navBottom = stickyNav ? stickyNav.getBoundingClientRect().bottom : 0;
return navBottom + 24;
};

const [activeId] = Array.from(visible.entries()).sort((a, b) => b[1] - a[1])[0] || [];
if (activeId) {
setActive(activeId);
const syncActiveSection = () => {
ticking = false;

const activationOffset = getActivationOffset();
let activeSection = sections[0];

sections.forEach((section) => {
if (section.getBoundingClientRect().top <= activationOffset) {
activeSection = section;
}
},
{
rootMargin: "-30% 0px -55% 0px",
threshold: [0.08, 0.2, 0.4, 0.6],
},
);
});

sections.forEach((section) => observer.observe(section));
if (activeSection?.id) {
setActive(activeSection.id);
}
};

const scheduleSync = () => {
if (ticking) {
return;
}
ticking = true;
window.requestAnimationFrame(syncActiveSection);
};

if (!productSectionNavBound) {
productSectionNavBound = true;
window.addEventListener("scroll", scheduleSync, { passive: true });
window.addEventListener("resize", scheduleSync);
window.addEventListener("hashchange", scheduleSync);
}

const hash = window.location.hash.replace("#", "");
if (hash && sections.some((section) => section.id === hash)) {
setActive(hash);
} else {
setActive(sections[0].id);
}

scheduleSync();
}

function initResponsiveTables() {
Expand Down
25 changes: 23 additions & 2 deletions components/badge/badge.templ
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package badge

import "github.com/vestavision/btkcommerce/utils"
import (
"strings"

"github.com/vestavision/btkcommerce/utils"
)

type Variant string

Expand Down Expand Up @@ -34,6 +38,7 @@ templ Badge(props ...Props) {
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
"transition-[color,box-shadow] overflow-hidden",
p.variantClasses(),
p.darkBorderClasses(),
p.Class,
),
}
Expand All @@ -48,10 +53,26 @@ func (p Props) variantClasses() string {
case VariantDestructive:
return "border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60"
case VariantOutline:
return "text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground"
return "border-border text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground dark:border-border/70"
case VariantSecondary:
return "border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90"
default:
return "border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90"
}
}

func (p Props) darkBorderClasses() string {
if strings.Contains(p.Class, "dark:border-") {
return ""
}

if !strings.Contains(p.Class, "border-") {
return ""
}

if strings.Contains(p.Class, "border-transparent") {
return ""
}

return "dark:border-current/35"
}
27 changes: 24 additions & 3 deletions components/badge/badge_templ.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

51 changes: 26 additions & 25 deletions internal/domain/admin/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ type CredentialInput struct {
// Service is the admin domain contract.
type Service interface {
Dashboard(ctx context.Context, labels viewmodel.SiteLabels) (viewmodel.AdminDashboard, error)
Credentials(ctx context.Context) viewmodel.CredentialsPage
Credentials(ctx context.Context, labels viewmodel.SiteLabels) viewmodel.CredentialsPage
AddCredential(ctx context.Context, input CredentialInput) error
Disable(ctx context.Context, id string) error
ReactivateCredential(ctx context.Context, id string) error
Expand All @@ -63,32 +63,29 @@ func (MockAdminService) Dashboard(ctx context.Context, labels viewmodel.SiteLabe
points := []float64{8, 11, 9, 15, 12, 10, 7}
return viewmodel.AdminDashboard{
Metrics: []viewmodel.MetricCard{
{Label: "Pending sellers", Value: "12", Delta: "4 priority"},
{Label: "Review queue", Value: "38", Delta: "-6 today"},
{Label: "Search quality alert", Value: "7", Delta: "2 visual streams"},
{Label: labels.WorkspaceSellerStatus, Value: "12", Delta: labels.WorkspaceTeam},
{Label: labels.WorkspaceModerationQueue, Value: "38", Delta: labels.WorkspaceModeration},
{Label: labels.WorkspaceSearchSync, Value: "7", Delta: labels.WorkspaceSearchSyncJobs},
},
OverviewChart: adminOverviewChartFromPoints(labels.WorkspaceAdminOverviewTitle, labels.WorkspaceAdminOverviewSubtitle, points),
Moderation: []viewmodel.ModerationRow{
{ID: "MOD-201", Title: "Streetwear cap listing", Reason: "Color normalization needed", Status: "To review", UpdatedAt: "11:10"},
{ID: "MOD-199", Title: "Lamp cover image", Reason: "Preview mismatch risk", Status: "Queued", UpdatedAt: "10:42"},
{ID: "MOD-194", Title: "Text-only product summary", Reason: "Source description missing", Status: "Escalated", UpdatedAt: "09:58"},
{ID: "MOD-201", Title: "Streetwear şapka ilanı", Reason: "Renk normalizasyonu gerekli", Status: "pending", UpdatedAt: "11:10"},
{ID: "MOD-199", Title: "Lamba kapak görseli", Reason: "Önizleme uyuşmazlığı riski", Status: "processing", UpdatedAt: "10:42"},
{ID: "MOD-194", Title: "Metinsiz ürün özeti", Reason: "Kaynak açıklaması eksik", Status: "error", UpdatedAt: "09:58"},
},
Vendors: []viewmodel.VendorRow{
{Name: "North Loom", Category: "Fashion", Status: "Active", ListingRate: "92%"},
{Name: "Luma House", Category: "Home", Status: "Documents pending", ListingRate: "67%"},
{Name: "Threadform", Category: "Accessories", Status: "Active", ListingRate: "88%"},
{Name: "North Loom", Category: "Moda", Status: "active", ListingRate: "92%"},
{Name: "Luma House", Category: "Ev", Status: "pending", ListingRate: "67%"},
{Name: "Threadform", Category: "Aksesuar", Status: "active", ListingRate: "88%"},
},
}, nil
}

func (MockAdminService) Credentials(ctx context.Context) viewmodel.CredentialsPage {
func (MockAdminService) Credentials(ctx context.Context, labels viewmodel.SiteLabels) viewmodel.CredentialsPage {
_ = ctx
return viewmodel.CredentialsPage{
Records: []viewmodel.CredentialRow{},
Metrics: []viewmodel.MetricCard{
{Label: "Total records", Value: "0", Delta: ""},
{Label: "Active", Value: "0", Delta: ""},
{Label: "Disabled", Value: "0", Delta: ""},
},
Metrics: credentialMetrics(labels, 0, 0, 0, 0),
}
}

Expand Down Expand Up @@ -232,7 +229,7 @@ func sumPoints(points []float64) float64 {
return total
}

func (s *DBAdminService) Credentials(ctx context.Context) viewmodel.CredentialsPage {
func (s *DBAdminService) Credentials(ctx context.Context, labels viewmodel.SiteLabels) viewmodel.CredentialsPage {
records, err := s.store.List(ctx)
if err != nil {
records = []vision.CredentialRecord{}
Expand Down Expand Up @@ -289,12 +286,16 @@ func (s *DBAdminService) Credentials(ctx context.Context) viewmodel.CredentialsP
}
return viewmodel.CredentialsPage{
Records: rows,
Metrics: []viewmodel.MetricCard{
{Label: "Total records", Value: fmt.Sprintf("%d", len(rows)), Delta: ""},
{Label: "Active", Value: fmt.Sprintf("%d", active), Delta: ""},
{Label: "Disabled", Value: fmt.Sprintf("%d", disabled), Delta: ""},
{Label: "Degraded", Value: fmt.Sprintf("%d", degraded), Delta: ""},
},
Metrics: credentialMetrics(labels, len(rows), active, disabled, degraded),
}
}

func credentialMetrics(labels viewmodel.SiteLabels, total, active, disabled, degraded int) []viewmodel.MetricCard {
return []viewmodel.MetricCard{
{Label: labels.CredentialsMetricTotalRecords, Value: fmt.Sprintf("%d", total), Delta: ""},
{Label: labels.StatusActive, Value: fmt.Sprintf("%d", active), Delta: ""},
{Label: labels.StatusDisabled, Value: fmt.Sprintf("%d", disabled), Delta: ""},
{Label: labels.CredentialsMetricDegraded, Value: fmt.Sprintf("%d", degraded), Delta: ""},
}
}

Expand Down Expand Up @@ -364,9 +365,9 @@ func (s *DBAdminService) vendorRows(ctx context.Context) ([]viewmodel.VendorRow,
}
out := make([]viewmodel.VendorRow, 0, len(rows))
for _, row := range rows {
status := "No listings"
status := "pending"
if row.Total > 0 {
status = "Active"
status = "active"
}
rate := "0%"
if row.Total > 0 {
Expand Down
Loading
Loading