Redesign FeaturedProgramCard and reorder Home sections
- Replace external pill badges (level + FREE) with immersive overlay card: level tag pinned top-right with ultraThinMaterial blur, FREE shown inline with checkmark.seal.fill icon, dark scrim for text legibility - Remove card drop shadow - Move Browse by Zone section above Featured in Home tab scroll order
This commit is contained in:
@@ -53,6 +53,22 @@ struct HomeTab: View {
|
||||
}
|
||||
.padding(.horizontal)
|
||||
|
||||
// ── Body Zone Grid ──
|
||||
VStack(alignment: .leading, spacing: 12) {
|
||||
SectionHeader(title: "Browse by Zone", subtitle: "Target specific muscle groups")
|
||||
.padding(.horizontal)
|
||||
|
||||
VStack(spacing: 12) {
|
||||
ForEach(vm.availableZones, id: \.self) { zone in
|
||||
NavigationLink(destination: BodyZoneView(zone: zone)) {
|
||||
ZoneCard(zone: zone)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
}
|
||||
}
|
||||
.padding(.horizontal)
|
||||
}
|
||||
|
||||
// ── Featured Workouts ──
|
||||
if !vm.featuredPrograms.isEmpty {
|
||||
VStack(alignment: .leading, spacing: 12) {
|
||||
@@ -71,22 +87,6 @@ struct HomeTab: View {
|
||||
}
|
||||
}
|
||||
|
||||
// ── Body Zone Grid ──
|
||||
VStack(alignment: .leading, spacing: 12) {
|
||||
SectionHeader(title: "Browse by Zone", subtitle: "Target specific muscle groups")
|
||||
.padding(.horizontal)
|
||||
|
||||
VStack(spacing: 12) {
|
||||
ForEach(vm.availableZones, id: \.self) { zone in
|
||||
NavigationLink(destination: BodyZoneView(zone: zone)) {
|
||||
ZoneCard(zone: zone)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
}
|
||||
}
|
||||
.padding(.horizontal)
|
||||
}
|
||||
|
||||
// ── Loading / Error State ──
|
||||
if vm.isLoading {
|
||||
ProgressView()
|
||||
@@ -148,42 +148,55 @@ struct FeaturedProgramCard: View {
|
||||
let program: WorkoutProgram
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
// Header gradient area
|
||||
RoundedRectangle(cornerRadius: 16, style: .continuous)
|
||||
.fill(Theme.zoneGradient(program.bodyZone))
|
||||
.frame(width: 220, height: 110)
|
||||
.overlay(alignment: .bottomLeading) {
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
RoundedRectangle(cornerRadius: 20, style: .continuous)
|
||||
.fill(Theme.zoneGradient(program.bodyZone))
|
||||
.frame(width: 220, height: 170)
|
||||
.overlay {
|
||||
ZStack(alignment: .topTrailing) {
|
||||
// ── Bottom scrim for text legibility ──
|
||||
LinearGradient(
|
||||
colors: [.clear, .black.opacity(0.55)],
|
||||
startPoint: .center,
|
||||
endPoint: .bottom
|
||||
)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 20, style: .continuous))
|
||||
|
||||
// ── Level tag — top right ──
|
||||
Text(program.level.capitalized)
|
||||
.font(.caption2.weight(.bold))
|
||||
.foregroundStyle(.white)
|
||||
.padding(.horizontal, 9)
|
||||
.padding(.vertical, 5)
|
||||
.background(.ultraThinMaterial.opacity(0.85))
|
||||
.clipShape(RoundedRectangle(cornerRadius: 8, style: .continuous))
|
||||
.padding(12)
|
||||
|
||||
// ── Bottom content ──
|
||||
VStack(alignment: .leading, spacing: 6) {
|
||||
Spacer()
|
||||
|
||||
Text(program.titleEn)
|
||||
.font(.headline.weight(.bold))
|
||||
.foregroundStyle(.white)
|
||||
.lineLimit(2)
|
||||
HStack(spacing: 6) {
|
||||
Label("\(program.estimatedDuration)m", systemImage: "clock")
|
||||
Label("\(program.estimatedCalories) kcal", systemImage: "flame")
|
||||
}
|
||||
.font(.caption.weight(.medium))
|
||||
.foregroundStyle(.white.opacity(0.85))
|
||||
}
|
||||
.padding(12)
|
||||
}
|
||||
.shadow(color: .black.opacity(0.3), radius: 4, x: 0, y: 2)
|
||||
|
||||
HStack {
|
||||
LevelBadge(level: program.level)
|
||||
Spacer()
|
||||
if program.isFree {
|
||||
Text("FREE")
|
||||
.font(.caption2.weight(.bold))
|
||||
.foregroundStyle(Theme.success)
|
||||
.padding(.horizontal, 8)
|
||||
.padding(.vertical, 3)
|
||||
.background(Theme.success.opacity(0.15))
|
||||
.clipShape(Capsule())
|
||||
HStack(spacing: 10) {
|
||||
Label("\(program.estimatedDuration)m", systemImage: "clock.fill")
|
||||
Label("\(program.estimatedCalories) kcal", systemImage: "flame.fill")
|
||||
if program.isFree {
|
||||
Label("Free", systemImage: "checkmark.seal.fill")
|
||||
.foregroundStyle(Theme.success)
|
||||
}
|
||||
}
|
||||
.font(.caption.weight(.semibold))
|
||||
.foregroundStyle(.white.opacity(0.9))
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.padding(14)
|
||||
}
|
||||
}
|
||||
}
|
||||
.frame(width: 220)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user