This commit is contained in:
Daniel Arantes Loverde
2026-04-28 09:26:08 -03:00
parent e54c0a029f
commit a12cfb6bf3
70 changed files with 335 additions and 230 deletions

View File

@@ -928,3 +928,5 @@ Para dashboard da loja e análise de produto, usar:
**Compatibilidade Store:** **Compatibilidade Store:**
- O painel `/store/reviews` segue funcional com os campos legados (`rate`, `message`, `itemFeedback`, `improvementFeedback`, `deliveryFeedback`). - O painel `/store/reviews` segue funcional com os campos legados (`rate`, `message`, `itemFeedback`, `improvementFeedback`, `deliveryFeedback`).
- Campos novos coexistem para evolução gradual sem quebrar o fluxo atual. - Campos novos coexistem para evolução gradual sem quebrar o fluxo atual.
Autor: Daniel Arantes Loverde

View File

@@ -638,3 +638,5 @@ Assina o contrato draft atual da loja.
### 14. Visualizar PDF do Contrato ### 14. Visualizar PDF do Contrato
**GET** `/api/store/contract/:contractId/pdf` **GET** `/api/store/contract/:contractId/pdf`
Retorna o stream do arquivo PDF do contrato (draft ou assinado). Retorna o stream do arquivo PDF do contrato (draft ou assinado).
Autor: Daniel Arantes Loverde

View File

@@ -98,3 +98,5 @@ FEATURE_CONTROL_MODULE_TOKEN="550e8400-e29b-41d4-a716-44665544000b" npm start
cd feature-control-bff cd feature-control-bff
npm test npm test
``` ```
Autor: Daniel Arantes Loverde

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 82 KiB

View File

@@ -1,116 +1,44 @@
{ {
"images": [ "images" : [
{ { "filename" : "icon_iphone_20pt@2x.png", "idiom" : "iphone", "scale" : "2x", "size" : "20x20" },
"idiom": "iphone", { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "filename" : "icon_iphone_20pt@2x_dark.png", "idiom" : "iphone", "scale" : "2x", "size" : "20x20" },
"size": "20x20", { "filename" : "icon_iphone_20pt@3x.png", "idiom" : "iphone", "scale" : "3x", "size" : "20x20" },
"scale": "2x", { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "filename" : "icon_iphone_20pt@3x_dark.png", "idiom" : "iphone", "scale" : "3x", "size" : "20x20" },
"filename": "icon_iphone_20pt@2x.png" { "filename" : "icon_iphone_29pt@2x.png", "idiom" : "iphone", "scale" : "2x", "size" : "29x29" },
}, { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "filename" : "icon_iphone_29pt@2x_dark.png", "idiom" : "iphone", "scale" : "2x", "size" : "29x29" },
{ { "filename" : "icon_iphone_29pt@3x.png", "idiom" : "iphone", "scale" : "3x", "size" : "29x29" },
"idiom": "iphone", { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "filename" : "icon_iphone_29pt@3x_dark.png", "idiom" : "iphone", "scale" : "3x", "size" : "29x29" },
"size": "20x20", { "filename" : "icon_iphone_40pt@2x.png", "idiom" : "iphone", "scale" : "2x", "size" : "40x40" },
"scale": "3x", { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "filename" : "icon_iphone_40pt@2x_dark.png", "idiom" : "iphone", "scale" : "2x", "size" : "40x40" },
"filename": "icon_iphone_20pt@3x.png" { "filename" : "icon_iphone_40pt@3x.png", "idiom" : "iphone", "scale" : "3x", "size" : "40x40" },
}, { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "filename" : "icon_iphone_40pt@3x_dark.png", "idiom" : "iphone", "scale" : "3x", "size" : "40x40" },
{ { "filename" : "icon_iphone_60pt@2x.png", "idiom" : "iphone", "scale" : "2x", "size" : "60x60" },
"idiom": "iphone", { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "filename" : "icon_iphone_60pt@2x_dark.png", "idiom" : "iphone", "scale" : "2x", "size" : "60x60" },
"size": "29x29", { "filename" : "icon_iphone_60pt@3x.png", "idiom" : "iphone", "scale" : "3x", "size" : "60x60" },
"scale": "2x", { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "filename" : "icon_iphone_60pt@3x_dark.png", "idiom" : "iphone", "scale" : "3x", "size" : "60x60" },
"filename": "icon_iphone_29pt@2x.png" { "filename" : "icon_ipad_20pt@1x.png", "idiom" : "ipad", "scale" : "1x", "size" : "20x20" },
}, { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "filename" : "icon_ipad_20pt@1x_dark.png", "idiom" : "ipad", "scale" : "1x", "size" : "20x20" },
{ { "filename" : "icon_ipad_20pt@2x.png", "idiom" : "ipad", "scale" : "2x", "size" : "20x20" },
"idiom": "iphone", { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "filename" : "icon_ipad_20pt@2x_dark.png", "idiom" : "ipad", "scale" : "2x", "size" : "20x20" },
"size": "29x29", { "filename" : "icon_ipad_29pt@1x.png", "idiom" : "ipad", "scale" : "1x", "size" : "29x29" },
"scale": "3x", { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "filename" : "icon_ipad_29pt@1x_dark.png", "idiom" : "ipad", "scale" : "1x", "size" : "29x29" },
"filename": "icon_iphone_29pt@3x.png" { "filename" : "icon_ipad_29pt@2x.png", "idiom" : "ipad", "scale" : "2x", "size" : "29x29" },
}, { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "filename" : "icon_ipad_29pt@2x_dark.png", "idiom" : "ipad", "scale" : "2x", "size" : "29x29" },
{ { "filename" : "icon_ipad_40pt@1x.png", "idiom" : "ipad", "scale" : "1x", "size" : "40x40" },
"idiom": "iphone", { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "filename" : "icon_ipad_40pt@1x_dark.png", "idiom" : "ipad", "scale" : "1x", "size" : "40x40" },
"size": "40x40", { "filename" : "icon_ipad_40pt@2x.png", "idiom" : "ipad", "scale" : "2x", "size" : "40x40" },
"scale": "2x", { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "filename" : "icon_ipad_40pt@2x_dark.png", "idiom" : "ipad", "scale" : "2x", "size" : "40x40" },
"filename": "icon_iphone_40pt@2x.png" { "filename" : "icon_ipad_76pt@1x.png", "idiom" : "ipad", "scale" : "1x", "size" : "76x76" },
}, { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "filename" : "icon_ipad_76pt@1x_dark.png", "idiom" : "ipad", "scale" : "1x", "size" : "76x76" },
{ { "filename" : "icon_ipad_76pt@2x.png", "idiom" : "ipad", "scale" : "2x", "size" : "76x76" },
"idiom": "iphone", { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "filename" : "icon_ipad_76pt@2x_dark.png", "idiom" : "ipad", "scale" : "2x", "size" : "76x76" },
"size": "40x40", { "filename" : "icon_ipad_83.5pt@2x.png", "idiom" : "ipad", "scale" : "2x", "size" : "83.5x83.5" },
"scale": "3x", { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "filename" : "icon_ipad_83.5pt@2x_dark.png", "idiom" : "ipad", "scale" : "2x", "size" : "83.5x83.5" },
"filename": "icon_iphone_40pt@3x.png" { "filename" : "icon_ios-marketing_1024pt@1x.png", "idiom" : "ios-marketing", "scale" : "1x", "size" : "1024x1024" },
}, { "appearances" : [ { "appearance" : "luminosity", "value" : "dark" } ], "filename" : "icon_ios-marketing_1024pt@1x_dark.png", "idiom" : "ios-marketing", "scale" : "1x", "size" : "1024x1024" }
{
"idiom": "iphone",
"size": "60x60",
"scale": "2x",
"filename": "icon_iphone_60pt@2x.png"
},
{
"idiom": "iphone",
"size": "60x60",
"scale": "3x",
"filename": "icon_iphone_60pt@3x.png"
},
{
"idiom": "ipad",
"size": "20x20",
"scale": "1x",
"filename": "icon_ipad_20pt@1x.png"
},
{
"idiom": "ipad",
"size": "20x20",
"scale": "2x",
"filename": "icon_ipad_20pt@2x.png"
},
{
"idiom": "ipad",
"size": "29x29",
"scale": "1x",
"filename": "icon_ipad_29pt@1x.png"
},
{
"idiom": "ipad",
"size": "29x29",
"scale": "2x",
"filename": "icon_ipad_29pt@2x.png"
},
{
"idiom": "ipad",
"size": "40x40",
"scale": "1x",
"filename": "icon_ipad_40pt@1x.png"
},
{
"idiom": "ipad",
"size": "40x40",
"scale": "2x",
"filename": "icon_ipad_40pt@2x.png"
},
{
"idiom": "ipad",
"size": "76x76",
"scale": "1x",
"filename": "icon_ipad_76pt@1x.png"
},
{
"idiom": "ipad",
"size": "76x76",
"scale": "2x",
"filename": "icon_ipad_76pt@2x.png"
},
{
"idiom": "ipad",
"size": "83.5x83.5",
"scale": "2x",
"filename": "icon_ipad_83.5pt@2x.png"
},
{
"idiom": "ios-marketing",
"size": "1024x1024",
"scale": "1x",
"filename": "icon_ios-marketing_1024pt@1x.png"
}
], ],
"info": { "info" : {
"version": 1, "author" : "xcode",
"author": "xcode" "version" : 1
} }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 KiB

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.8 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 82 KiB

View File

@@ -1,4 +1,4 @@
# Plano - Tela de Perfil e# Plano - Tela de Perfil
Legenda: Legenda:
- 🟢 Concluído - 🟢 Concluído
@@ -28,3 +28,4 @@ Legenda:
- Configurações/Ajuda (confirmar nomenclatura final) - Configurações/Ajuda (confirmar nomenclatura final)
- 🔴 Corrigir build global do projeto (dependência `LCEssentials` ausente em `StoreDetailView.swift`) - 🔴 Corrigir build global do projeto (dependência `LCEssentials` ausente em `StoreDetailView.swift`)
Autor: Daniel Arantes Loverde

View File

@@ -49,3 +49,4 @@ Implicação:
1. Mudança de splash para iOS pode ser feita sem tocar Android. 1. Mudança de splash para iOS pode ser feita sem tocar Android.
2. Quando Android for tratado, deve entrar em camada própria (tema/recursos Android), sem impactar o fluxo iOS. 2. Quando Android for tratado, deve entrar em camada própria (tema/recursos Android), sem impactar o fluxo iOS.
Autor: Daniel Arantes Loverde

View File

@@ -58,3 +58,5 @@ Kotlin JUnit tests in the Robolectric Android simulation environment.
Parity testing can be performed with `skip test`, Parity testing can be performed with `skip test`,
which will output a table of the test results for both platforms. which will output a table of the test results for both platforms.
Autor: Daniel Arantes Loverde

View File

@@ -58,7 +58,7 @@ struct SecondaryButton: View {
image image
} }
} }
.foregroundStyle(AppColors.textInverse) .foregroundStyle(AppColors.textPrimary)
.frame(maxWidth: fullWidth ? .infinity : nil) .frame(maxWidth: fullWidth ? .infinity : nil)
.frame(height: 56) .frame(height: 56)
.background(AppColors.secondary) .background(AppColors.secondary)

View File

@@ -7,7 +7,7 @@ struct SearchField: View {
var body: some View { var body: some View {
HStack(spacing: 12) { HStack(spacing: 12) {
Image(systemName: "magnifyingglass") Image(systemName: "magnifyingglass")
.foregroundStyle(AppColors.secondary) .foregroundStyle(AppColors.primary)
TextField(placeholder, text: $text) TextField(placeholder, text: $text)
.appNoAutoCap() .appNoAutoCap()
} }
@@ -17,7 +17,7 @@ struct SearchField: View {
.clipShape(RoundedRectangle(cornerRadius: AppLayout.radiusLG, style: .continuous)) .clipShape(RoundedRectangle(cornerRadius: AppLayout.radiusLG, style: .continuous))
.overlay( .overlay(
RoundedRectangle(cornerRadius: AppLayout.radiusLG, style: .continuous) RoundedRectangle(cornerRadius: AppLayout.radiusLG, style: .continuous)
.stroke(AppColors.secondary.opacity(0.15), lineWidth: 1) .stroke(AppColors.primary.opacity(0.15), lineWidth: 1)
) )
} }
} }
@@ -30,14 +30,14 @@ struct PillButton: View {
Text(title) Text(title)
.font(AppTypography.caption) .font(AppTypography.caption)
.tracking(AppTypography.captionLetterSpacing) .tracking(AppTypography.captionLetterSpacing)
.foregroundStyle(isActive ? AppColors.textInverse : AppColors.secondary) .foregroundStyle(isActive ? AppColors.textInverse : AppColors.primary)
.padding(.horizontal, 14) .padding(.horizontal, 14)
.padding(.vertical, 8) .padding(.vertical, 8)
.background(isActive ? AppColors.primary : AppColors.backgroundLight) .background(isActive ? AppColors.primary : AppColors.backgroundLight)
.clipShape(Capsule()) .clipShape(Capsule())
.overlay( .overlay(
Capsule() Capsule()
.stroke(AppColors.secondary.opacity(0.15), lineWidth: isActive ? 0 : 1) .stroke(AppColors.primary.opacity(0.15), lineWidth: isActive ? 0 : 1)
) )
} }
} }

View File

@@ -1,28 +1,36 @@
import SwiftUI import SwiftUI
#if canImport(UIKit)
import UIKit
#elseif canImport(AppKit)
import AppKit
#endif
// MARK: - Colors // MARK: - Colors
enum AppColors { enum AppColors {
static let primary = Color(hex: "#1E6B43") // Brandbook 2026 (Pedi Foods)
static let secondary = Color(hex: "#2F7C4B") // Primary now follows the "FOODS" dark green tone from the logo.
static let tertiary = Color(hex: "#8CFF2E") static let primary = Color(hex: "#345D54")
// Secondary keeps the vivid lime from the symbol/logo body.
static let secondary = Color(hex: "#D5D841")
static let tertiary = Color(hex: "#A7BF42")
static let brandDark = Color(hex: "#1B5C3A") static let brandDark = Color(hex: "#1F2D40")
static let brandSoft = Color(hex: "#E8F2EC") static let brandSoft = Color(hex: "#F2F5E3")
static let backgroundLight = Color(hex: "#F3F5F7") static let backgroundLight = Color(hex: "#F3F5F7")
static let backgroundDark = Color(hex: "#18230F") static let backgroundDark = Color(hex: "#1F2D40")
static let surface = Color(hex: "#FFFFFF") static let surface = Color(hex: "#FFFFFF")
static let textPrimary = Color(hex: "#1C1F23") static let textPrimary = Color(hex: "#000000")
static let textInverse = Color(hex: "#FFFFFF") static let textInverse = Color(hex: "#FFFFFF")
static let textMuted = Color(hex: "#7B8794") static let textMuted = Color(hex: "#667085")
} }
enum AppDarkColors { enum AppDarkColors {
static let background = Color(hex: "#18230F") static let background = Color(hex: "#1F2D40")
static let surface = Color(hex: "#FFFFFF", alpha: 0.05) static let surface = Color(hex: "#FFFFFF", alpha: 0.08)
static let textPrimary = Color(hex: "#FFFFFF") static let textPrimary = Color(hex: "#FFFFFF")
static let textSecondary = Color(hex: "#A3A3A3") static let textSecondary = Color(hex: "#D0D5DD")
static let primary = AppColors.primary static let primary = AppColors.primary
static let secondary = AppColors.secondary static let secondary = AppColors.secondary
static let tertiary = AppColors.tertiary static let tertiary = AppColors.tertiary
@@ -31,22 +39,38 @@ enum AppDarkColors {
// MARK: - Typography // MARK: - Typography
enum AppTypography { enum AppTypography {
static let fontFamily = "Plus Jakarta Sans" // Brandbook typography: Nexa (fallback: system font)
static let fontFamily = "Nexa-Regular"
// Avoid applying dynamic weight on custom font descriptors to prevent static let heading1 = resolvedFont(size: 28, fallbackWeight: .bold)
// SwiftUI runtime warnings on some platforms/toolchains. static let heading25 = resolvedFont(size: 25, fallbackWeight: .bold)
static let heading1 = Font.custom(fontFamily, size: 28) static let heading2 = resolvedFont(size: 20, fallbackWeight: .semibold)
static let heading25 = Font.custom(fontFamily, size: 25) static let heading3 = resolvedFont(size: 16, fallbackWeight: .semibold)
static let heading2 = Font.custom(fontFamily, size: 20) static let body = resolvedFont(size: 16, fallbackWeight: .regular)
static let heading3 = Font.custom(fontFamily, size: 16) static let button = resolvedFont(size: 14, fallbackWeight: .semibold)
static let body = Font.custom(fontFamily, size: 16) static let caption = resolvedFont(size: 10, fallbackWeight: .regular)
static let button = Font.custom(fontFamily, size: 14) static let overline = resolvedFont(size: 11, fallbackWeight: .regular)
static let caption = Font.custom(fontFamily, size: 10)
static let overline = Font.custom(fontFamily, size: 11)
static let bodyLineHeight: CGFloat = 1.6 static let bodyLineHeight: CGFloat = 1.6
static let buttonLetterSpacing: CGFloat = 0.08 static let buttonLetterSpacing: CGFloat = 0.08
static let captionLetterSpacing: CGFloat = 0.12 static let captionLetterSpacing: CGFloat = 0.12
private static func resolvedFont(size: CGFloat, fallbackWeight: Font.Weight) -> Font {
if isBrandFontAvailable {
return Font.custom(fontFamily, size: size)
}
return .system(size: size, weight: fallbackWeight, design: .default)
}
private static var isBrandFontAvailable: Bool {
#if canImport(UIKit)
return UIFont(name: fontFamily, size: 16) != nil
#elseif canImport(AppKit)
return NSFont(name: fontFamily, size: 16) != nil
#else
return false
#endif
}
} }
// MARK: - Layout // MARK: - Layout

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 120 KiB

View File

@@ -14,8 +14,83 @@
], ],
"groups" : [ "groups" : [
{ {
"hidden" : false,
"layers" : [ "layers" : [
{
"glass-specializations" : [
{
"appearance" : "dark",
"value" : true
}
],
"hidden" : false,
"image-name" : "Screenshot 2026-04-25 at 16.22.36.png",
"name" : "Screenshot 2026-04-25 at 16.22.36",
"opacity-specializations" : [
{
"value" : 0
},
{
"appearance" : "dark",
"value" : 1
}
],
"position-specializations" : [
{
"idiom" : "iOS",
"value" : {
"scale" : 1.24,
"translation-in-points" : [
0,
0
]
}
},
{
"idiom" : "watchOS",
"value" : {
"scale" : 1.54,
"translation-in-points" : [
0,
0
]
}
}
]
},
{
"hidden" : false,
"image-name" : "Screenshot 2026-04-25 at 16.22.59.png",
"name" : "Screenshot 2026-04-25 at 16.22.59",
"position-specializations" : [
{
"idiom" : "iOS",
"value" : {
"scale" : 1.24,
"translation-in-points" : [
0,
0
]
}
},
{
"idiom" : "watchOS",
"value" : {
"scale" : 1.54,
"translation-in-points" : [
0,
0
]
}
}
]
}
],
"opacity-specializations" : [
{
"appearance" : "dark",
"value" : 1
}
], ],
"shadow" : { "shadow" : {
"kind" : "neutral", "kind" : "neutral",
@@ -27,54 +102,18 @@
} }
}, },
{ {
"layers" : [ "blend-mode" : "normal",
"hidden-specializations" : [
{ {
"blend-mode-specializations" : [ "value" : false
{ },
"value" : "normal" {
}, "idiom" : "iOS",
{ "value" : false
"appearance" : "dark",
"value" : "normal"
}
],
"fill-specializations" : [
{
"value" : "none"
},
{
"appearance" : "dark",
"value" : {
"solid" : "srgb:0.00000,0.50000,0.04260,1.00000"
}
}
],
"glass" : false,
"hidden" : false,
"image-name" : "pedifoods_big.png",
"name" : "pedifoods_big",
"position-specializations" : [
{
"value" : {
"scale" : 1,
"translation-in-points" : [
137.390625,
11.984375
]
}
},
{
"idiom" : "square",
"value" : {
"scale" : 0.35,
"translation-in-points" : [
8,
0
]
}
}
]
} }
],
"layers" : [
], ],
"shadow" : { "shadow" : {
"kind" : "neutral", "kind" : "neutral",
@@ -87,6 +126,9 @@
} }
], ],
"supported-platforms" : { "supported-platforms" : {
"circles" : [
"watchOS"
],
"squares" : [ "squares" : [
"iOS" "iOS"
] ]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 82 KiB

View File

@@ -258,12 +258,12 @@ final class FeatureControlService {
} }
private func featureKeys() -> [String] { private func featureKeys() -> [String] {
let raw = ProcessInfo.processInfo.environment["FEATURE_CONTROL_KEYS"] ?? "fc.promo,fc.promo-codes,fc.android,fc.city-aguai,fc.ios" let raw = ProcessInfo.processInfo.environment["FEATURE_CONTROL_KEYS"] ?? "at.city.aguai,at.promo,at.ios.only,at.android.only"
let items = raw let items = raw
.split(separator: ",") .split(separator: ",")
.map { $0.trimmingCharacters(in: .whitespacesAndNewlines) } .map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }
.filter { $0.isEmpty == false } .filter { $0.isEmpty == false }
return items.isEmpty ? ["fc.ios"] : items return items.isEmpty ? ["at.ios.only"] : items
} }
private func platformName() -> String { private func platformName() -> String {

View File

@@ -62,6 +62,16 @@ struct FeatureFlagsState: Codable, Equatable {
var source: String = "default" var source: String = "default"
var values: [String: FeatureFlagValue] = [:] var values: [String: FeatureFlagValue] = [:]
var raw: [String: FeatureControlRawFlag] = [:] var raw: [String: FeatureControlRawFlag] = [:]
func isEnabled(_ key: String, default defaultValue: Bool = false) -> Bool {
if let rawValue = raw[key] {
return rawValue.enabled || rawValue.variant.lowercased() == "on"
}
if let mapped = values[key] {
return mapped.boolValue
}
return defaultValue
}
} }
enum AppModal: String, Identifiable { enum AppModal: String, Identifiable {

View File

@@ -67,7 +67,9 @@ extension View {
#if os(macOS) #if os(macOS)
self self
#else #else
self.navigationBarTitleDisplayMode(.inline) self
.navigationBarTitleDisplayMode(.inline)
.modifier(AppRoundedBackButtonModifier())
#endif #endif
} }
@@ -139,3 +141,28 @@ extension View {
#endif #endif
} }
} }
#if !os(macOS)
private struct AppRoundedBackButtonModifier: ViewModifier {
@Environment(\.dismiss) private var dismiss
func body(content: Content) -> some View {
content
.navigationBarBackButtonHidden(true)
.toolbar {
ToolbarItem(placement: .topBarLeading) {
Button(action: { dismiss() }) {
Image(systemName: "chevron.left")
.font(.system(size: 24, weight: .semibold))
.foregroundStyle(AppColors.textPrimary)
.frame(width: 52, height: 52)
.background(AppColors.surface)
.clipShape(Circle())
.shadow(color: .black.opacity(0.06), radius: 8, y: 2)
}
.buttonStyle(.plain)
}
}
}
}
#endif

View File

@@ -47,6 +47,13 @@ struct AddAddressFormView: View {
ScrollView { ScrollView {
VStack(spacing: 0) { VStack(spacing: 0) {
screenHeader(
title: existingAddress == nil ? "Novo endereço" : "Editar endereço",
onBack: { dismiss() }
)
.padding(.horizontal, 24)
.padding(.bottom, 12)
logoImage logoImage
.scaledToFit() .scaledToFit()
.frame(width: 120, height: 120) .frame(width: 120, height: 120)
@@ -134,6 +141,28 @@ struct AddAddressFormView: View {
} }
} }
private func screenHeader(title: String, onBack: @escaping () -> Void) -> some View {
ZStack {
Text(title)
.font(AppTypography.heading2)
.foregroundStyle(colorScheme == .dark ? Color.white : AppColors.textPrimary)
HStack {
Button(action: onBack) {
Image(systemName: "chevron.left")
.font(.system(size: 24, weight: .semibold))
.foregroundStyle(AppColors.textPrimary)
.frame(width: 52, height: 52)
.background(AppColors.surface)
.clipShape(Circle())
.shadow(color: .black.opacity(0.06), radius: 8, y: 2)
}
.buttonStyle(.plain)
Spacer()
}
}
}
private func saveAddress() { private func saveAddress() {
guard !isLoading else { return } guard !isLoading else { return }
let latLong: [Double]? = { let latLong: [Double]? = {

View File

@@ -22,7 +22,7 @@ struct AddressesView: View {
ScrollView(showsIndicators: false) { ScrollView(showsIndicators: false) {
VStack(spacing: 20) { VStack(spacing: 20) {
header screenHeader(title: "Meus Endereços", onBack: { dismiss() })
if let message { if let message {
Text(message) Text(message)
@@ -131,28 +131,6 @@ struct AddressesView: View {
} }
} }
var header: some View {
ZStack {
Text("Meus Endereços")
.font(AppTypography.heading2)
.foregroundStyle(AppColors.textPrimary)
HStack {
Button(action: { dismiss() }) {
Image(systemName: "chevron.left")
.font(.system(size: 24, weight: .semibold))
.foregroundStyle(AppColors.textPrimary)
.frame(width: 52, height: 52)
.background(AppColors.surface)
.clipShape(Circle())
.shadow(color: .black.opacity(0.06), radius: 8, y: 2)
}
Spacer()
}
}
}
var bottomOverlay: some View { var bottomOverlay: some View {
ZStack(alignment: .bottom) { ZStack(alignment: .bottom) {
Rectangle() Rectangle()
@@ -181,6 +159,28 @@ struct AddressesView: View {
} }
} }
private func screenHeader(title: String, onBack: @escaping () -> Void) -> some View {
ZStack {
Text(title)
.font(AppTypography.heading2)
.foregroundStyle(AppColors.textPrimary)
HStack {
Button(action: onBack) {
Image(systemName: "chevron.left")
.font(.system(size: 24, weight: .semibold))
.foregroundStyle(AppColors.textPrimary)
.frame(width: 52, height: 52)
.background(AppColors.surface)
.clipShape(Circle())
.shadow(color: .black.opacity(0.06), radius: 8, y: 2)
}
.buttonStyle(.plain)
Spacer()
}
}
}
private func selectAddress(_ address: CustomerAddress) { private func selectAddress(_ address: CustomerAddress) {
appState.address.selectedId = address.id appState.address.selectedId = address.id
let label = (address.label ?? "").trimmingCharacters(in: .whitespacesAndNewlines) let label = (address.label ?? "").trimmingCharacters(in: .whitespacesAndNewlines)

View File

@@ -121,15 +121,17 @@ struct HomeView: View {
} }
} }
section(title: "#PediPromo") { if appState.featureFlags.isEnabled("at.promo") {
ScrollView(.horizontal, showsIndicators: false) { section(title: "#PediPromo") {
HStack(spacing: 16) { ScrollView(.horizontal, showsIndicators: false) {
ForEach(specials) { item in HStack(spacing: 16) {
SpecialOfferCard(model: item) ForEach(specials) { item in
.frame(width: 260, height: 120) SpecialOfferCard(model: item)
.frame(width: 260, height: 120)
}
} }
.padding(.horizontal, 20)
} }
.padding(.horizontal, 20)
} }
} }
@@ -204,7 +206,7 @@ struct HomeView: View {
return ZStack(alignment: .top) { return ZStack(alignment: .top) {
RoundedRectangle(cornerRadius: 32, style: .continuous) RoundedRectangle(cornerRadius: 32, style: .continuous)
.fill(AppColors.brandDark) .fill(AppColors.primary)
.frame(height: height) .frame(height: height)
.overlay(headerRings.opacity(1 - collapseProgress), alignment: .topTrailing) .overlay(headerRings.opacity(1 - collapseProgress), alignment: .topTrailing)

View File

@@ -28,7 +28,7 @@ struct CategoryChip: View {
.foregroundStyle(isActive ? AppColors.textInverse : AppColors.textPrimary) .foregroundStyle(isActive ? AppColors.textInverse : AppColors.textPrimary)
.padding(.horizontal, 16) .padding(.horizontal, 16)
.padding(.vertical, 10) .padding(.vertical, 10)
.background(isActive ? AppColors.brandDark : AppColors.surface) .background(isActive ? AppColors.primary : AppColors.surface)
.clipShape(Capsule()) .clipShape(Capsule())
} }
} }

View File

@@ -72,12 +72,12 @@ struct MainTabView: View {
.font(AppTypography.heading3) .font(AppTypography.heading3)
} }
} }
.foregroundStyle(isActive ? AppColors.primary : AppColors.textPrimary) .foregroundStyle(isActive ? AppColors.textInverse : AppColors.textPrimary)
.padding(.horizontal, 18) .padding(.horizontal, 18)
.padding(.vertical, 10) .padding(.vertical, 10)
.background( .background(
Capsule() Capsule()
.fill(isActive ? AppColors.brandSoft : Color.clear) .fill(isActive ? AppColors.primary : Color.clear)
) )
} }
.buttonStyle(.plain) .buttonStyle(.plain)

View File

@@ -1,6 +1,7 @@
import SwiftUI import SwiftUI
struct OrdersView: View { struct OrdersView: View {
@Environment(\.dismiss) var dismiss
@State var isLoading = false @State var isLoading = false
@State var errorMessage: String? = nil @State var errorMessage: String? = nil
@State var orders: [AppOrderSummary] = [] @State var orders: [AppOrderSummary] = []
@@ -12,6 +13,8 @@ struct OrdersView: View {
var body: some View { var body: some View {
ScrollView(showsIndicators: false) { ScrollView(showsIndicators: false) {
VStack(alignment: .leading, spacing: 14) { VStack(alignment: .leading, spacing: 14) {
screenHeader(title: "Meus Pedidos", onBack: { dismiss() })
if isLoading { if isLoading {
ProgressView() ProgressView()
.frame(maxWidth: .infinity) .frame(maxWidth: .infinity)
@@ -35,11 +38,12 @@ struct OrdersView: View {
} }
} }
.padding(.horizontal, 20) .padding(.horizontal, 20)
.padding(.top, 18)
.padding(.bottom, UIDevice.bottomNotch + 18) .padding(.bottom, UIDevice.bottomNotch + 18)
} }
.background(AppColors.backgroundLight) .background(AppColors.backgroundLight)
.navigationTitle("Meus Pedidos") .navigationBarBackButtonHidden(true)
.appInlineNavigationTitle() .appHiddenNavigationBar()
.task { .task {
await loadOrdersIfNeeded() await loadOrdersIfNeeded()
await refreshStoreRatings() await refreshStoreRatings()
@@ -59,6 +63,28 @@ struct OrdersView: View {
} }
} }
private func screenHeader(title: String, onBack: @escaping () -> Void) -> some View {
ZStack {
Text(title)
.font(AppTypography.heading2)
.foregroundStyle(AppColors.textPrimary)
HStack {
Button(action: onBack) {
Image(systemName: "chevron.left")
.font(.system(size: 24, weight: .semibold))
.foregroundStyle(AppColors.textPrimary)
.frame(width: 52, height: 52)
.background(AppColors.surface)
.clipShape(Circle())
.shadow(color: .black.opacity(0.06), radius: 8, y: 2)
}
.buttonStyle(.plain)
Spacer()
}
}
}
private func orderCard(_ order: AppOrderSummary) -> some View { private func orderCard(_ order: AppOrderSummary) -> some View {
let status = orderVisualStatus(for: order) let status = orderVisualStatus(for: order)
let detailsRoute = OrderRouteContext( let detailsRoute = OrderRouteContext(
@@ -81,7 +107,6 @@ struct OrdersView: View {
AsyncStoreImage(imageURL: resolvedMediaURL(order.storeLogoURL)) AsyncStoreImage(imageURL: resolvedMediaURL(order.storeLogoURL))
.frame(width: 80, height: 80) .frame(width: 80, height: 80)
.clipShape(RoundedRectangle(cornerRadius: 20, style: .continuous)) .clipShape(RoundedRectangle(cornerRadius: 20, style: .continuous))
.background(AppColors.brandSoft, in: RoundedRectangle(cornerRadius: 20, style: .continuous))
VStack(alignment: .leading, spacing: 5) { VStack(alignment: .leading, spacing: 5) {

View File

@@ -74,7 +74,7 @@ struct StoreDetailView: View {
.refreshable { .refreshable {
await loadStoreData(forceRefresh: true) await loadStoreData(forceRefresh: true)
} }
.appNamedCoordinateSpace(StoreDetailScrollCoordinateSpace.name) .coordinateSpace(name: StoreDetailScrollCoordinateSpace.name)
.ignoresSafeArea(edges: .top) .ignoresSafeArea(edges: .top)
} }
.ignoresSafeArea(edges: .top) .ignoresSafeArea(edges: .top)
@@ -286,7 +286,7 @@ struct StoreDetailView: View {
HStack(spacing: 0) { HStack(spacing: 0) {
statItem(title: "DISTÂNCIA", value: distanceValueLabel) statItem(title: "DISTÂNCIA", value: distanceValueLabel)
Divider().frame(height: 34) Divider().frame(height: 34)
statItem(title: "TEMPO", value: info?.deliveryTime ?? "25-35 min") statItem(title: "TEMPO", value: info?.deliveryTime ?? "--")
Divider().frame(height: 34) Divider().frame(height: 34)
statItem(title: "ENTREGA", value: deliveryValueLabel) statItem(title: "ENTREGA", value: deliveryValueLabel)
} }

View File

@@ -131,3 +131,5 @@
3. `[🟥]` Implementar carrinho real (itens, subtotal, edicao) para destravar checkout. 3. `[🟥]` Implementar carrinho real (itens, subtotal, edicao) para destravar checkout.
4. `[🟥]` Implementar mapa e tracking de pedidos. 4. `[🟥]` Implementar mapa e tracking de pedidos.
5. `[🟥]` Corrigir bloqueio de build por dependencia (`LCEssentials`) para fechar validacao iOS/Android ponta a ponta. 5. `[🟥]` Corrigir bloqueio de build por dependencia (`LCEssentials`) para fechar validacao iOS/Android ponta a ponta.
Autor: Daniel Arantes Loverde

View File

@@ -179,3 +179,5 @@ Icon {
active?: boolean active?: boolean
} }
``` ```
Autor: Daniel Arantes Loverde

View File

@@ -359,3 +359,5 @@ Legenda:
- Build Swift/Kotlin finalizado. - Build Swift/Kotlin finalizado.
- Etapa de conexão Android falhou com `adb devices` vazio no ambiente atual. - Etapa de conexão Android falhou com `adb devices` vazio no ambiente atual.
- Próximo passo para concluir Task 6: conectar device físico (USB) ou iniciar AVD válido. - Próximo passo para concluir Task 6: conectar device físico (USB) ou iniciar AVD válido.
Autor: Daniel Arantes Loverde

View File

@@ -742,3 +742,5 @@ Implementar assim:
- nova alteração somente após 90 dias - nova alteração somente após 90 dias
- aviso visual claro antes de salvar - aviso visual claro antes de salvar
- registro do slug antigo para redirect temporário - registro do slug antigo para redirect temporário
Autor: Daniel Arantes Loverde