kybernus 3.0.1 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/cli/commands/ecommerce.d.ts +3 -0
- package/dist/cli/commands/ecommerce.d.ts.map +1 -0
- package/dist/cli/commands/ecommerce.js +164 -0
- package/dist/cli/commands/ecommerce.js.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/templates/ecommerce/.env.example +10 -0
- package/templates/ecommerce/.github/workflows/ci.yml +102 -0
- package/templates/ecommerce/.github/workflows/deploy.yml +31 -0
- package/templates/ecommerce/.prettierrc +9 -0
- package/templates/ecommerce/Dockerfile +54 -0
- package/templates/ecommerce/README.md +295 -0
- package/templates/ecommerce/apps/api/.env.example +59 -0
- package/templates/ecommerce/apps/api/jest.config.ts +50 -0
- package/templates/ecommerce/apps/api/jest.integration.config.ts +45 -0
- package/templates/ecommerce/apps/api/package.json +59 -0
- package/templates/ecommerce/apps/api/prisma/migrations/20260306000137_init/migration.sql +184 -0
- package/templates/ecommerce/apps/api/prisma/migrations/migration_lock.toml +3 -0
- package/templates/ecommerce/apps/api/prisma/schema.prisma +181 -0
- package/templates/ecommerce/apps/api/prisma/seed.ts +159 -0
- package/templates/ecommerce/apps/api/src/__tests__/app.test.ts +39 -0
- package/templates/ecommerce/apps/api/src/__tests__/globalSetup.ts +34 -0
- package/templates/ecommerce/apps/api/src/__tests__/globalTeardown.ts +16 -0
- package/templates/ecommerce/apps/api/src/__tests__/setup.db.ts +18 -0
- package/templates/ecommerce/apps/api/src/__tests__/setup.env.ts +14 -0
- package/templates/ecommerce/apps/api/src/app.ts +133 -0
- package/templates/ecommerce/apps/api/src/application/admin/admin-user.service.ts +24 -0
- package/templates/ecommerce/apps/api/src/application/admin/dashboard.service.ts +102 -0
- package/templates/ecommerce/apps/api/src/application/auth/auth.service.ts +185 -0
- package/templates/ecommerce/apps/api/src/application/cart/cart.service.ts +151 -0
- package/templates/ecommerce/apps/api/src/application/cart/coupon.service.ts +51 -0
- package/templates/ecommerce/apps/api/src/application/catalog/catalog.service.ts +168 -0
- package/templates/ecommerce/apps/api/src/application/checkout/checkout.service.ts +114 -0
- package/templates/ecommerce/apps/api/src/application/orders/order.service.ts +93 -0
- package/templates/ecommerce/apps/api/src/application/ports/email.port.ts +3 -0
- package/templates/ecommerce/apps/api/src/application/ports/payment.port.ts +24 -0
- package/templates/ecommerce/apps/api/src/application/ports/shipping.port.ts +9 -0
- package/templates/ecommerce/apps/api/src/application/ports/storage.port.ts +3 -0
- package/templates/ecommerce/apps/api/src/application/ports/token-blacklist.port.ts +4 -0
- package/templates/ecommerce/apps/api/src/application/ports/token.port.ts +18 -0
- package/templates/ecommerce/apps/api/src/application/profile/profile.service.ts +76 -0
- package/templates/ecommerce/apps/api/src/domain/auth/user.entity.ts +109 -0
- package/templates/ecommerce/apps/api/src/domain/auth/user.repository.ts +11 -0
- package/templates/ecommerce/apps/api/src/domain/cart/cart.entity.ts +136 -0
- package/templates/ecommerce/apps/api/src/domain/cart/cart.repository.ts +8 -0
- package/templates/ecommerce/apps/api/src/domain/cart/coupon.entity.ts +58 -0
- package/templates/ecommerce/apps/api/src/domain/cart/coupon.repository.ts +10 -0
- package/templates/ecommerce/apps/api/src/domain/catalog/category.entity.ts +51 -0
- package/templates/ecommerce/apps/api/src/domain/catalog/category.repository.ts +10 -0
- package/templates/ecommerce/apps/api/src/domain/catalog/product.entity.ts +130 -0
- package/templates/ecommerce/apps/api/src/domain/catalog/product.repository.ts +28 -0
- package/templates/ecommerce/apps/api/src/domain/checkout/order.entity.ts +121 -0
- package/templates/ecommerce/apps/api/src/domain/checkout/order.repository.ts +11 -0
- package/templates/ecommerce/apps/api/src/domain/shared/AppError.ts +12 -0
- package/templates/ecommerce/apps/api/src/infrastructure/cache/redis.ts +16 -0
- package/templates/ecommerce/apps/api/src/infrastructure/config/registry/admin.registry.ts +13 -0
- package/templates/ecommerce/apps/api/src/infrastructure/config/registry/auth.registry.ts +34 -0
- package/templates/ecommerce/apps/api/src/infrastructure/config/registry/cart.registry.ts +49 -0
- package/templates/ecommerce/apps/api/src/infrastructure/config/registry/catalog.registry.ts +24 -0
- package/templates/ecommerce/apps/api/src/infrastructure/config/registry/checkout.registry.ts +47 -0
- package/templates/ecommerce/apps/api/src/infrastructure/config/registry/orders.registry.ts +6 -0
- package/templates/ecommerce/apps/api/src/infrastructure/config/registry/profile.registry.ts +4 -0
- package/templates/ecommerce/apps/api/src/infrastructure/persistence/in-memory/cart.memory.repository.ts +33 -0
- package/templates/ecommerce/apps/api/src/infrastructure/persistence/in-memory/category.memory.repository.ts +41 -0
- package/templates/ecommerce/apps/api/src/infrastructure/persistence/in-memory/coupon.memory.repository.ts +55 -0
- package/templates/ecommerce/apps/api/src/infrastructure/persistence/in-memory/order.memory.repository.ts +75 -0
- package/templates/ecommerce/apps/api/src/infrastructure/persistence/in-memory/product.memory.repository.ts +100 -0
- package/templates/ecommerce/apps/api/src/infrastructure/persistence/in-memory/user.memory.repository.ts +54 -0
- package/templates/ecommerce/apps/api/src/infrastructure/persistence/prisma/auth/user.prisma.repository.ts +83 -0
- package/templates/ecommerce/apps/api/src/infrastructure/persistence/prisma/catalog/category.prisma.repository.ts +69 -0
- package/templates/ecommerce/apps/api/src/infrastructure/persistence/prisma/catalog/product.prisma.repository.ts +185 -0
- package/templates/ecommerce/apps/api/src/infrastructure/persistence/prisma/checkout/order.prisma.repository.ts +149 -0
- package/templates/ecommerce/apps/api/src/infrastructure/persistence/prisma-client.ts +17 -0
- package/templates/ecommerce/apps/api/src/infrastructure/services/email/email.registry.ts +18 -0
- package/templates/ecommerce/apps/api/src/infrastructure/services/email/ethereal.email.service.ts +38 -0
- package/templates/ecommerce/apps/api/src/infrastructure/services/email/noop.email.service.ts +12 -0
- package/templates/ecommerce/apps/api/src/infrastructure/services/email/smtp.email.service.ts +36 -0
- package/templates/ecommerce/apps/api/src/infrastructure/services/payment/stripe-webhook.handler.ts +83 -0
- package/templates/ecommerce/apps/api/src/infrastructure/services/payment/stripe.adapter.ts +39 -0
- package/templates/ecommerce/apps/api/src/infrastructure/services/shipping/mock.shipping.service.ts +17 -0
- package/templates/ecommerce/apps/api/src/infrastructure/services/storage/in-memory.storage.service.ts +11 -0
- package/templates/ecommerce/apps/api/src/infrastructure/services/storage/local-disk.storage.service.ts +27 -0
- package/templates/ecommerce/apps/api/src/infrastructure/services/storage/s3.storage.service.ts +52 -0
- package/templates/ecommerce/apps/api/src/infrastructure/services/storage/storage.registry.ts +19 -0
- package/templates/ecommerce/apps/api/src/infrastructure/services/token/redis.token.blacklist.ts +23 -0
- package/templates/ecommerce/apps/api/src/infrastructure/services/token/token.blacklist.ts +30 -0
- package/templates/ecommerce/apps/api/src/infrastructure/services/token/token.service.ts +136 -0
- package/templates/ecommerce/apps/api/src/modules/admin/__tests__/admin.routes.integration.test.ts +250 -0
- package/templates/ecommerce/apps/api/src/modules/admin/admin.controller.ts +116 -0
- package/templates/ecommerce/apps/api/src/modules/admin/admin.registry.ts +1 -0
- package/templates/ecommerce/apps/api/src/modules/admin/admin.routes.ts +21 -0
- package/templates/ecommerce/apps/api/src/modules/admin/admin.service.ts +1 -0
- package/templates/ecommerce/apps/api/src/modules/admin/admin.user.service.ts +1 -0
- package/templates/ecommerce/apps/api/src/modules/auth/__tests__/auth.logout.redis.test.ts +104 -0
- package/templates/ecommerce/apps/api/src/modules/auth/__tests__/auth.routes.integration.test.ts +211 -0
- package/templates/ecommerce/apps/api/src/modules/auth/__tests__/auth.service.unit.test.ts +260 -0
- package/templates/ecommerce/apps/api/src/modules/auth/__tests__/email.service.unit.test.ts +94 -0
- package/templates/ecommerce/apps/api/src/modules/auth/__tests__/token.blacklist.redis.test.ts +65 -0
- package/templates/ecommerce/apps/api/src/modules/auth/__tests__/user.entity.unit.test.ts +79 -0
- package/templates/ecommerce/apps/api/src/modules/auth/__tests__/user.prisma.repository.test.ts +138 -0
- package/templates/ecommerce/apps/api/src/modules/auth/auth.controller.ts +148 -0
- package/templates/ecommerce/apps/api/src/modules/auth/auth.registry.ts +1 -0
- package/templates/ecommerce/apps/api/src/modules/auth/auth.routes.ts +17 -0
- package/templates/ecommerce/apps/api/src/modules/auth/auth.service.ts +1 -0
- package/templates/ecommerce/apps/api/src/modules/auth/redis.token.blacklist.ts +1 -0
- package/templates/ecommerce/apps/api/src/modules/auth/token.blacklist.ts +2 -0
- package/templates/ecommerce/apps/api/src/modules/auth/token.service.ts +2 -0
- package/templates/ecommerce/apps/api/src/modules/auth/user.entity.ts +1 -0
- package/templates/ecommerce/apps/api/src/modules/auth/user.prisma.repository.ts +1 -0
- package/templates/ecommerce/apps/api/src/modules/auth/user.repository.ts +2 -0
- package/templates/ecommerce/apps/api/src/modules/cart/__tests__/cart.entity.unit.test.ts +144 -0
- package/templates/ecommerce/apps/api/src/modules/cart/__tests__/cart.routes.integration.test.ts +242 -0
- package/templates/ecommerce/apps/api/src/modules/cart/__tests__/cart.service.unit.test.ts +151 -0
- package/templates/ecommerce/apps/api/src/modules/cart/__tests__/coupon.admin.integration.test.ts +136 -0
- package/templates/ecommerce/apps/api/src/modules/cart/cart.controller.ts +94 -0
- package/templates/ecommerce/apps/api/src/modules/cart/cart.entity.ts +1 -0
- package/templates/ecommerce/apps/api/src/modules/cart/cart.registry.ts +1 -0
- package/templates/ecommerce/apps/api/src/modules/cart/cart.repository.ts +2 -0
- package/templates/ecommerce/apps/api/src/modules/cart/cart.routes.ts +17 -0
- package/templates/ecommerce/apps/api/src/modules/cart/cart.service.ts +1 -0
- package/templates/ecommerce/apps/api/src/modules/cart/coupon.entity.ts +1 -0
- package/templates/ecommerce/apps/api/src/modules/cart/coupon.repository.ts +2 -0
- package/templates/ecommerce/apps/api/src/modules/cart/coupon.service.ts +1 -0
- package/templates/ecommerce/apps/api/src/modules/cart/shipping.service.ts +2 -0
- package/templates/ecommerce/apps/api/src/modules/catalog/__tests__/catalog.routes.integration.test.ts +275 -0
- package/templates/ecommerce/apps/api/src/modules/catalog/__tests__/catalog.service.unit.test.ts +223 -0
- package/templates/ecommerce/apps/api/src/modules/catalog/__tests__/product.image.integration.test.ts +130 -0
- package/templates/ecommerce/apps/api/src/modules/catalog/__tests__/product.prisma.repository.test.ts +174 -0
- package/templates/ecommerce/apps/api/src/modules/catalog/catalog.controller.ts +176 -0
- package/templates/ecommerce/apps/api/src/modules/catalog/catalog.registry.ts +1 -0
- package/templates/ecommerce/apps/api/src/modules/catalog/catalog.routes.ts +38 -0
- package/templates/ecommerce/apps/api/src/modules/catalog/catalog.service.ts +1 -0
- package/templates/ecommerce/apps/api/src/modules/catalog/category.entity.ts +1 -0
- package/templates/ecommerce/apps/api/src/modules/catalog/category.prisma.repository.ts +1 -0
- package/templates/ecommerce/apps/api/src/modules/catalog/category.repository.ts +2 -0
- package/templates/ecommerce/apps/api/src/modules/catalog/product.entity.ts +1 -0
- package/templates/ecommerce/apps/api/src/modules/catalog/product.prisma.repository.ts +1 -0
- package/templates/ecommerce/apps/api/src/modules/catalog/product.repository.ts +2 -0
- package/templates/ecommerce/apps/api/src/modules/checkout/__tests__/checkout.routes.integration.test.ts +163 -0
- package/templates/ecommerce/apps/api/src/modules/checkout/__tests__/checkout.service.unit.test.ts +191 -0
- package/templates/ecommerce/apps/api/src/modules/checkout/__tests__/order.prisma.repository.test.ts +150 -0
- package/templates/ecommerce/apps/api/src/modules/checkout/checkout.controller.ts +59 -0
- package/templates/ecommerce/apps/api/src/modules/checkout/checkout.registry.ts +1 -0
- package/templates/ecommerce/apps/api/src/modules/checkout/checkout.routes.ts +18 -0
- package/templates/ecommerce/apps/api/src/modules/checkout/checkout.service.ts +1 -0
- package/templates/ecommerce/apps/api/src/modules/checkout/order.entity.ts +1 -0
- package/templates/ecommerce/apps/api/src/modules/checkout/order.prisma.repository.ts +1 -0
- package/templates/ecommerce/apps/api/src/modules/checkout/order.repository.ts +2 -0
- package/templates/ecommerce/apps/api/src/modules/checkout/tax.service.ts +9 -0
- package/templates/ecommerce/apps/api/src/modules/orders/__tests__/order.entity.unit.test.ts +68 -0
- package/templates/ecommerce/apps/api/src/modules/orders/__tests__/order.routes.integration.test.ts +254 -0
- package/templates/ecommerce/apps/api/src/modules/orders/__tests__/order.service.email.unit.test.ts +142 -0
- package/templates/ecommerce/apps/api/src/modules/orders/order.controller.ts +96 -0
- package/templates/ecommerce/apps/api/src/modules/orders/order.registry.ts +1 -0
- package/templates/ecommerce/apps/api/src/modules/orders/order.routes.ts +17 -0
- package/templates/ecommerce/apps/api/src/modules/orders/order.service.ts +1 -0
- package/templates/ecommerce/apps/api/src/modules/payment/__tests__/stripe-webhook.unit.test.ts +330 -0
- package/templates/ecommerce/apps/api/src/modules/payment/__tests__/stripe.adapter.unit.test.ts +84 -0
- package/templates/ecommerce/apps/api/src/modules/payment/adapters/stripe.adapter.ts +1 -0
- package/templates/ecommerce/apps/api/src/modules/payment/payment.port.ts +1 -0
- package/templates/ecommerce/apps/api/src/modules/payment/stripe-webhook.handler.ts +1 -0
- package/templates/ecommerce/apps/api/src/modules/profile/__tests__/profile.routes.integration.test.ts +180 -0
- package/templates/ecommerce/apps/api/src/modules/profile/__tests__/profile.service.unit.test.ts +187 -0
- package/templates/ecommerce/apps/api/src/modules/profile/profile.controller.ts +92 -0
- package/templates/ecommerce/apps/api/src/modules/profile/profile.registry.ts +1 -0
- package/templates/ecommerce/apps/api/src/modules/profile/profile.routes.ts +14 -0
- package/templates/ecommerce/apps/api/src/modules/profile/profile.service.ts +1 -0
- package/templates/ecommerce/apps/api/src/presentation/middlewares/authenticate.ts +37 -0
- package/templates/ecommerce/apps/api/src/presentation/middlewares/authorize.ts +23 -0
- package/templates/ecommerce/apps/api/src/presentation/middlewares/errorHandler.ts +48 -0
- package/templates/ecommerce/apps/api/src/presentation/modules/admin/admin.controller.ts +116 -0
- package/templates/ecommerce/apps/api/src/presentation/modules/admin/admin.routes.ts +21 -0
- package/templates/ecommerce/apps/api/src/presentation/modules/auth/auth.controller.ts +147 -0
- package/templates/ecommerce/apps/api/src/presentation/modules/auth/auth.routes.ts +17 -0
- package/templates/ecommerce/apps/api/src/presentation/modules/cart/cart.controller.ts +94 -0
- package/templates/ecommerce/apps/api/src/presentation/modules/cart/cart.routes.ts +17 -0
- package/templates/ecommerce/apps/api/src/presentation/modules/catalog/catalog.controller.ts +176 -0
- package/templates/ecommerce/apps/api/src/presentation/modules/catalog/catalog.routes.ts +38 -0
- package/templates/ecommerce/apps/api/src/presentation/modules/checkout/checkout.controller.ts +59 -0
- package/templates/ecommerce/apps/api/src/presentation/modules/checkout/checkout.routes.ts +18 -0
- package/templates/ecommerce/apps/api/src/presentation/modules/orders/order.controller.ts +96 -0
- package/templates/ecommerce/apps/api/src/presentation/modules/orders/order.routes.ts +17 -0
- package/templates/ecommerce/apps/api/src/presentation/modules/profile/profile.controller.ts +92 -0
- package/templates/ecommerce/apps/api/src/presentation/modules/profile/profile.routes.ts +14 -0
- package/templates/ecommerce/apps/api/src/presentation/validators/uuidParam.ts +20 -0
- package/templates/ecommerce/apps/api/src/server.ts +47 -0
- package/templates/ecommerce/apps/api/src/shared/__tests__/uuid.validation.test.ts +111 -0
- package/templates/ecommerce/apps/api/src/shared/errors/AppError.ts +1 -0
- package/templates/ecommerce/apps/api/src/shared/infra/email/EtherealEmailService.ts +1 -0
- package/templates/ecommerce/apps/api/src/shared/infra/email/IEmailService.ts +1 -0
- package/templates/ecommerce/apps/api/src/shared/infra/email/NoopEmailService.ts +1 -0
- package/templates/ecommerce/apps/api/src/shared/infra/email/SmtpEmailService.ts +1 -0
- package/templates/ecommerce/apps/api/src/shared/infra/email/__tests__/ethereal.email.integration.test.ts +32 -0
- package/templates/ecommerce/apps/api/src/shared/infra/email/email.registry.ts +1 -0
- package/templates/ecommerce/apps/api/src/shared/infra/prisma.ts +1 -0
- package/templates/ecommerce/apps/api/src/shared/infra/redis.ts +1 -0
- package/templates/ecommerce/apps/api/src/shared/infra/storage/IStorageService.ts +1 -0
- package/templates/ecommerce/apps/api/src/shared/infra/storage/InMemoryStorageService.ts +1 -0
- package/templates/ecommerce/apps/api/src/shared/infra/storage/LocalDiskStorageService.ts +1 -0
- package/templates/ecommerce/apps/api/src/shared/infra/storage/S3StorageService.ts +1 -0
- package/templates/ecommerce/apps/api/src/shared/infra/storage/__tests__/s3.storage.unit.test.ts +73 -0
- package/templates/ecommerce/apps/api/src/shared/infra/storage/storage.registry.ts +1 -0
- package/templates/ecommerce/apps/api/src/shared/middlewares/authenticate.ts +1 -0
- package/templates/ecommerce/apps/api/src/shared/middlewares/authorize.ts +1 -0
- package/templates/ecommerce/apps/api/src/shared/middlewares/errorHandler.ts +1 -0
- package/templates/ecommerce/apps/api/src/shared/validators/uuidParam.ts +1 -0
- package/templates/ecommerce/apps/api/tsconfig.json +15 -0
- package/templates/ecommerce/apps/web/.env.example +8 -0
- package/templates/ecommerce/apps/web/index.html +19 -0
- package/templates/ecommerce/apps/web/jest.config.ts +45 -0
- package/templates/ecommerce/apps/web/package.json +38 -0
- package/templates/ecommerce/apps/web/src/App.tsx +133 -0
- package/templates/ecommerce/apps/web/src/__mocks__/fileMock.ts +1 -0
- package/templates/ecommerce/apps/web/src/__mocks__/styleMock.ts +1 -0
- package/templates/ecommerce/apps/web/src/index.css +159 -0
- package/templates/ecommerce/apps/web/src/main.tsx +13 -0
- package/templates/ecommerce/apps/web/src/modules/admin/__tests__/CouponsAdminPage.test.tsx +134 -0
- package/templates/ecommerce/apps/web/src/modules/admin/__tests__/DashboardPage.test.tsx +65 -0
- package/templates/ecommerce/apps/web/src/modules/admin/__tests__/OrdersAdminPage.test.tsx +79 -0
- package/templates/ecommerce/apps/web/src/modules/admin/__tests__/ProductsAdminPage.test.tsx +84 -0
- package/templates/ecommerce/apps/web/src/modules/admin/__tests__/UsersAdminPage.test.tsx +85 -0
- package/templates/ecommerce/apps/web/src/modules/admin/pages/CouponsAdminPage.tsx +179 -0
- package/templates/ecommerce/apps/web/src/modules/admin/pages/DashboardPage.tsx +58 -0
- package/templates/ecommerce/apps/web/src/modules/admin/pages/OrdersAdminPage.tsx +178 -0
- package/templates/ecommerce/apps/web/src/modules/admin/pages/ProductsAdminPage.tsx +444 -0
- package/templates/ecommerce/apps/web/src/modules/admin/pages/UsersAdminPage.tsx +87 -0
- package/templates/ecommerce/apps/web/src/modules/auth/LoginForm.tsx +91 -0
- package/templates/ecommerce/apps/web/src/modules/auth/RegisterForm.tsx +109 -0
- package/templates/ecommerce/apps/web/src/modules/auth/__tests__/ForgotPasswordPage.test.tsx +42 -0
- package/templates/ecommerce/apps/web/src/modules/auth/__tests__/LoginForm.test.tsx +76 -0
- package/templates/ecommerce/apps/web/src/modules/auth/__tests__/RegisterForm.test.tsx +62 -0
- package/templates/ecommerce/apps/web/src/modules/auth/__tests__/ResetPasswordPage.test.tsx +66 -0
- package/templates/ecommerce/apps/web/src/modules/auth/pages/ForgotPasswordPage.tsx +100 -0
- package/templates/ecommerce/apps/web/src/modules/auth/pages/LoginPage.tsx +39 -0
- package/templates/ecommerce/apps/web/src/modules/auth/pages/RegisterPage.tsx +39 -0
- package/templates/ecommerce/apps/web/src/modules/auth/pages/ResetPasswordPage.tsx +110 -0
- package/templates/ecommerce/apps/web/src/modules/auth/useAuthStore.ts +141 -0
- package/templates/ecommerce/apps/web/src/modules/cart/__tests__/CartPage.test.tsx +111 -0
- package/templates/ecommerce/apps/web/src/modules/cart/pages/CartPage.tsx +313 -0
- package/templates/ecommerce/apps/web/src/modules/catalog/__tests__/ProductCard.test.tsx +59 -0
- package/templates/ecommerce/apps/web/src/modules/catalog/__tests__/ProductFilters.test.tsx +56 -0
- package/templates/ecommerce/apps/web/src/modules/catalog/components/ProductCard.tsx +78 -0
- package/templates/ecommerce/apps/web/src/modules/catalog/components/ProductFilters.tsx +104 -0
- package/templates/ecommerce/apps/web/src/modules/catalog/pages/ProductDetailPage.tsx +179 -0
- package/templates/ecommerce/apps/web/src/modules/catalog/pages/ProductListPage.tsx +100 -0
- package/templates/ecommerce/apps/web/src/modules/checkout/__tests__/CheckoutPage.test.tsx +159 -0
- package/templates/ecommerce/apps/web/src/modules/checkout/__tests__/StripePaymentForm.test.tsx +79 -0
- package/templates/ecommerce/apps/web/src/modules/checkout/components/StripePaymentForm.tsx +55 -0
- package/templates/ecommerce/apps/web/src/modules/checkout/hooks/useCheckout.ts +56 -0
- package/templates/ecommerce/apps/web/src/modules/checkout/pages/CheckoutPage.tsx +344 -0
- package/templates/ecommerce/apps/web/src/modules/checkout/pages/CheckoutSuccessPage.tsx +12 -0
- package/templates/ecommerce/apps/web/src/modules/legal/pages/PrivacyPolicyPage.tsx +207 -0
- package/templates/ecommerce/apps/web/src/modules/legal/pages/TermsOfServicePage.tsx +175 -0
- package/templates/ecommerce/apps/web/src/modules/orders/__tests__/OrderDetailPage.test.tsx +75 -0
- package/templates/ecommerce/apps/web/src/modules/orders/__tests__/OrderHistoryPage.test.tsx +87 -0
- package/templates/ecommerce/apps/web/src/modules/orders/pages/OrderDetailPage.tsx +73 -0
- package/templates/ecommerce/apps/web/src/modules/orders/pages/OrderHistoryPage.tsx +97 -0
- package/templates/ecommerce/apps/web/src/modules/profile/__tests__/ProfilePage.test.tsx +150 -0
- package/templates/ecommerce/apps/web/src/modules/profile/pages/ProfilePage.tsx +275 -0
- package/templates/ecommerce/apps/web/src/setupTests.ts +10 -0
- package/templates/ecommerce/apps/web/src/shared/components/CookieConsent.tsx +108 -0
- package/templates/ecommerce/apps/web/src/shared/components/ErrorBoundary.tsx +112 -0
- package/templates/ecommerce/apps/web/src/shared/components/Layout.tsx +143 -0
- package/templates/ecommerce/apps/web/src/shared/components/ProtectedRoute.tsx +21 -0
- package/templates/ecommerce/apps/web/src/shared/config/siteConfig.ts +57 -0
- package/templates/ecommerce/apps/web/src/shared/hooks/usePageTitle.ts +16 -0
- package/templates/ecommerce/apps/web/src/shared/lib/apiFetch.ts +16 -0
- package/templates/ecommerce/apps/web/src/shared/pages/NotFoundPage.tsx +42 -0
- package/templates/ecommerce/apps/web/src/shared/theme/ThemeProvider.tsx +45 -0
- package/templates/ecommerce/apps/web/src/shared/theme/__tests__/ThemeProvider.test.tsx +78 -0
- package/templates/ecommerce/apps/web/src/shared/theme/createTheme.ts +58 -0
- package/templates/ecommerce/apps/web/src/shared/theme/tokens.ts +81 -0
- package/templates/ecommerce/apps/web/src/vite-env.d.ts +1 -0
- package/templates/ecommerce/apps/web/tsconfig.jest.json +12 -0
- package/templates/ecommerce/apps/web/tsconfig.json +25 -0
- package/templates/ecommerce/apps/web/tsconfig.node.json +11 -0
- package/templates/ecommerce/apps/web/vite.config.ts +30 -0
- package/templates/ecommerce/docker-compose.yml +85 -0
- package/templates/ecommerce/package-lock.json +11255 -0
- package/templates/ecommerce/package.json +27 -0
- package/templates/ecommerce/packages/shared-types/package.json +13 -0
- package/templates/ecommerce/packages/shared-types/src/index.ts +3 -0
- package/templates/ecommerce/packages/shared-types/src/theme.ts +44 -0
- package/templates/ecommerce/packages/shared-types/tsconfig.json +11 -0
- package/templates/ecommerce/scripts/customize.sh +201 -0
- package/templates/ecommerce/tsconfig.json +14 -0
- package/templates/java-spring/clean/.gitignore.hbs +72 -0
- package/templates/java-spring/clean/docker-compose.yml.hbs +6 -3
- package/templates/java-spring/clean/src/main/java/{{packagePath}}/application/usecase/PaymentUseCase.java.hbs +21 -17
- package/templates/java-spring/clean/src/main/java/{{packagePath}}/infrastructure/persistence/entity/UserEntity.java.hbs +52 -0
- package/templates/java-spring/clean/src/main/java/{{packagePath}}/infrastructure/persistence/repository/JpaUserRepository.java.hbs +12 -0
- package/templates/java-spring/clean/src/main/java/{{packagePath}}/infrastructure/security/JwtAuthenticationFilter.java.hbs +64 -0
- package/templates/java-spring/clean/src/main/java/{{packagePath}}/infrastructure/security/SecurityConfig.java.hbs +36 -0
- package/templates/java-spring/clean/src/main/java/{{packagePath}}/infrastructure/stripe/StripeGateway.java.hbs +63 -0
- package/templates/java-spring/clean/src/main/resources/application.properties.hbs +6 -7
- package/templates/java-spring/hexagonal/.gitignore.hbs +72 -0
- package/templates/java-spring/hexagonal/docker-compose.yml.hbs +6 -3
- package/templates/java-spring/hexagonal/src/main/java/{{packagePath}}/adapters/outbound/security/JwtFilter.java.hbs +71 -0
- package/templates/java-spring/hexagonal/src/main/java/{{packagePath}}/adapters/outbound/security/SecurityConfig.java.hbs +35 -0
- package/templates/java-spring/hexagonal/src/main/java/{{packagePath}}/core/service/PaymentService.java.hbs +3 -3
- package/templates/java-spring/hexagonal/src/main/resources/application.properties.hbs +4 -4
- package/templates/java-spring/mvc/.gitignore.hbs +72 -0
- package/templates/java-spring/mvc/docker-compose.yml.hbs +6 -3
- package/templates/java-spring/mvc/src/main/java/{{packagePath}}/config/SecurityConfig.java.hbs +13 -12
- package/templates/java-spring/mvc/src/main/java/{{packagePath}}/controller/AuthController.java.hbs +9 -8
- package/templates/java-spring/mvc/src/main/java/{{packagePath}}/controller/PaymentsController.java.hbs +5 -6
- package/templates/java-spring/mvc/src/main/java/{{packagePath}}/service/StripeService.java.hbs +3 -3
- package/templates/java-spring/mvc/src/main/resources/application.yml.hbs +29 -26
- package/templates/nestjs/clean/.gitignore.hbs +42 -0
- package/templates/nestjs/clean/Dockerfile.hbs +6 -3
- package/templates/nestjs/clean/docker-compose.yml.hbs +1 -11
- package/templates/nestjs/clean/src/app.module.ts.hbs +2 -1
- package/templates/nestjs/clean/src/application/payment.service.ts.hbs +72 -72
- package/templates/nestjs/clean/src/domain/entities/user.entity.ts.hbs +2 -2
- package/templates/nestjs/clean/src/domain/repositories/user.repository.ts.hbs +2 -2
- package/templates/nestjs/clean/src/infrastructure/database/repositories/prisma.user.repository.ts.hbs +18 -18
- package/templates/nestjs/clean/src/infrastructure/http/health.controller.ts.hbs +9 -0
- package/templates/nestjs/clean/src/main.ts.hbs +1 -4
- package/templates/nestjs/clean/src/payment.module.ts.hbs +12 -12
- package/templates/nestjs/hexagonal/.gitignore.hbs +42 -0
- package/templates/nestjs/hexagonal/Dockerfile.hbs +6 -3
- package/templates/nestjs/hexagonal/docker-compose.yml.hbs +1 -11
- package/templates/nestjs/hexagonal/src/adapters/inbound/health.controller.ts.hbs +9 -0
- package/templates/nestjs/hexagonal/src/app.module.ts.hbs +2 -1
- package/templates/nestjs/hexagonal/src/core/domain/user.entity.ts.hbs +6 -6
- package/templates/nestjs/hexagonal/src/core/ports/ports.ts.hbs +4 -4
- package/templates/nestjs/hexagonal/src/main.ts.hbs +1 -4
- package/templates/nestjs/mvc/.gitignore.hbs +42 -0
- package/templates/nestjs/mvc/Dockerfile.hbs +6 -3
- package/templates/nestjs/mvc/docker-compose.yml.hbs +1 -11
- package/templates/nestjs/mvc/src/auth/auth.controller.ts.hbs +11 -1
- package/templates/nestjs/mvc/src/auth/auth.service.ts.hbs +3 -1
- package/templates/nestjs/mvc/src/controllers/health.controller.ts.hbs +6 -6
- package/templates/nestjs/mvc/src/main.ts.hbs +1 -4
- package/templates/nestjs/mvc/src/models/create-item.dto.ts.hbs +5 -2
- package/templates/nestjs/mvc/src/prisma/prisma.service.ts.hbs +1 -0
- package/templates/nextjs/mvc/.gitignore.hbs +42 -0
- package/templates/nextjs/mvc/Dockerfile.hbs +23 -8
- package/templates/nextjs/mvc/docker-compose.yml.hbs +1 -1
- package/templates/nodejs-express/clean/.gitignore.hbs +42 -0
- package/templates/nodejs-express/clean/Dockerfile.hbs +6 -1
- package/templates/nodejs-express/clean/docker-compose.yml.hbs +2 -2
- package/templates/nodejs-express/clean/package.json.hbs +69 -69
- package/templates/nodejs-express/clean/src/config.ts.hbs +11 -0
- package/templates/nodejs-express/clean/src/domain/entities/User.ts.hbs +46 -8
- package/templates/nodejs-express/hexagonal/.gitignore.hbs +42 -0
- package/templates/nodejs-express/hexagonal/Dockerfile.hbs +1 -1
- package/templates/nodejs-express/hexagonal/docker-compose.yml.hbs +2 -2
- package/templates/nodejs-express/hexagonal/package.json.hbs +69 -69
- package/templates/nodejs-express/hexagonal/src/adapters/inbound/http/PaymentController.ts.hbs +21 -38
- package/templates/nodejs-express/hexagonal/src/adapters/outbound/persistence/prisma.ts.hbs +2 -0
- package/templates/nodejs-express/hexagonal/src/config.ts.hbs +9 -0
- package/templates/nodejs-express/hexagonal/src/core/AuthService.ts.hbs +5 -5
- package/templates/nodejs-express/hexagonal/src/core/PaymentService.ts.hbs +7 -22
- package/templates/nodejs-express/hexagonal/src/core/domain/entities/User.ts.hbs +24 -4
- package/templates/nodejs-express/mvc/.gitignore.hbs +42 -0
- package/templates/nodejs-express/mvc/package.json.hbs +67 -67
- package/templates/python-fastapi/clean/.gitignore.hbs +76 -0
- package/templates/python-fastapi/clean/app/application/services/payment_service.py.hbs +3 -3
- package/templates/python-fastapi/clean/app/config.py.hbs +6 -7
- package/templates/python-fastapi/clean/app/domain/usecases/login_user.py.hbs +15 -0
- package/templates/python-fastapi/clean/app/infrastructure/http/auth_controller.py.hbs +40 -6
- package/templates/python-fastapi/clean/app/infrastructure/http/payment_controller.py.hbs +5 -4
- package/templates/python-fastapi/clean/app/infrastructure/security/jwt.py.hbs +23 -0
- package/templates/python-fastapi/clean/app/main.py.hbs +3 -0
- package/templates/python-fastapi/clean/docker-compose.yml.hbs +5 -12
- package/templates/python-fastapi/clean/requirements.txt.hbs +3 -0
- package/templates/python-fastapi/hexagonal/.gitignore.hbs +76 -0
- package/templates/python-fastapi/hexagonal/app/adapters/inbound/http_adapter.py.hbs +6 -9
- package/templates/python-fastapi/hexagonal/app/adapters/inbound/payment_http_adapter.py.hbs +4 -3
- package/templates/python-fastapi/hexagonal/app/adapters/outbound/stripe_adapter.py.hbs +30 -19
- package/templates/python-fastapi/hexagonal/app/config.py.hbs +14 -4
- package/templates/python-fastapi/hexagonal/app/core/domain/user.py.hbs +3 -1
- package/templates/python-fastapi/hexagonal/app/core/payment_service.py.hbs +28 -18
- package/templates/python-fastapi/hexagonal/app/core/ports/__init__.py.hbs +3 -0
- package/templates/python-fastapi/hexagonal/app/core/ports/user_repository.py.hbs +15 -0
- package/templates/python-fastapi/hexagonal/app/infrastructure/database/session.py.hbs +7 -0
- package/templates/python-fastapi/hexagonal/app/infrastructure/database/user_repository.py.hbs +53 -0
- package/templates/python-fastapi/hexagonal/app/infrastructure/security/__init__.py.hbs +0 -0
- package/templates/python-fastapi/hexagonal/app/infrastructure/security/adapters.py.hbs +23 -0
- package/templates/python-fastapi/hexagonal/app/infrastructure/security/jwt.py.hbs +23 -0
- package/templates/python-fastapi/hexagonal/docker-compose.yml.hbs +5 -12
- package/templates/python-fastapi/hexagonal/requirements.txt.hbs +4 -0
- package/templates/python-fastapi/mvc/.gitignore.hbs +76 -0
- package/templates/python-fastapi/mvc/app/controllers/payments.py.hbs +3 -17
- package/templates/python-fastapi/mvc/app/middleware/security.py.hbs +24 -3
- package/templates/python-fastapi/mvc/app/schemas/item.py.hbs +3 -1
- package/templates/python-fastapi/mvc/docker-compose.yml.hbs +5 -12
- package/templates/python-fastapi/mvc/requirements.txt.hbs +3 -1
- package/templates/nodejs-express/hexagonal/src/adapters/outbound/persistence/prisma.ts +0 -5
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { Link } from 'react-router-dom';
|
|
2
|
+
import { siteConfig } from '../../../shared/config/siteConfig';
|
|
3
|
+
import { usePageTitle } from '../../../shared/hooks/usePageTitle';
|
|
4
|
+
|
|
5
|
+
const { name, legal, supportEmail, privacyEmail, url } = siteConfig;
|
|
6
|
+
|
|
7
|
+
export function PrivacyPolicyPage() {
|
|
8
|
+
usePageTitle('Política de Privacidade');
|
|
9
|
+
return (
|
|
10
|
+
<article style={{ maxWidth: 760, margin: '0 auto', lineHeight: 1.75, color: '#374151' }}>
|
|
11
|
+
<h1 style={{ fontSize: '1.75rem', marginBottom: '0.25rem' }}>Política de Privacidade</h1>
|
|
12
|
+
<p style={{ color: '#9ca3af', fontSize: '0.875rem', marginTop: 0, marginBottom: '2rem' }}>
|
|
13
|
+
Vigência a partir de: <strong>{legal.termsEffectiveDate}</strong>
|
|
14
|
+
</p>
|
|
15
|
+
|
|
16
|
+
<Section title="1. Quem Somos">
|
|
17
|
+
<p>
|
|
18
|
+
Esta Política de Privacidade aplica-se ao site <strong>{name}</strong> ({url}), operado por{' '}
|
|
19
|
+
<strong>{legal.companyName}</strong>, CNPJ {legal.cnpj}, com sede em {legal.address}.
|
|
20
|
+
</p>
|
|
21
|
+
<p>
|
|
22
|
+
Estamos comprometidos com a proteção dos seus dados pessoais em conformidade com a{' '}
|
|
23
|
+
<strong>Lei Geral de Proteção de Dados (LGPD — Lei 13.709/2018)</strong>.
|
|
24
|
+
</p>
|
|
25
|
+
</Section>
|
|
26
|
+
|
|
27
|
+
<Section title="2. Dados que Coletamos">
|
|
28
|
+
<p>Coletamos os seguintes dados pessoais:</p>
|
|
29
|
+
<ul>
|
|
30
|
+
<li>
|
|
31
|
+
<strong>Dados de cadastro:</strong> nome, endereço de e-mail, senha (armazenada de forma
|
|
32
|
+
criptografada).
|
|
33
|
+
</li>
|
|
34
|
+
<li>
|
|
35
|
+
<strong>Dados de entrega:</strong> endereço postal, CEP, nome do destinatário.
|
|
36
|
+
</li>
|
|
37
|
+
<li>
|
|
38
|
+
<strong>Dados de pagamento:</strong> os dados de cartão de crédito são processados diretamente
|
|
39
|
+
pelo <strong>Stripe</strong> e nunca armazenados em nossos servidores.
|
|
40
|
+
</li>
|
|
41
|
+
<li>
|
|
42
|
+
<strong>Dados de uso:</strong> páginas visitadas, cliques, tempo de sessão (via cookies
|
|
43
|
+
analíticos, com seu consentimento).
|
|
44
|
+
</li>
|
|
45
|
+
<li>
|
|
46
|
+
<strong>Dados técnicos:</strong> endereço IP, tipo de navegador, sistema operacional.
|
|
47
|
+
</li>
|
|
48
|
+
</ul>
|
|
49
|
+
</Section>
|
|
50
|
+
|
|
51
|
+
<Section title="3. Finalidade e Base Legal do Tratamento">
|
|
52
|
+
<table style={{ borderCollapse: 'collapse', width: '100%', fontSize: '0.875rem' }}>
|
|
53
|
+
<thead>
|
|
54
|
+
<tr style={{ background: '#f9fafb' }}>
|
|
55
|
+
<th style={thStyle}>Finalidade</th>
|
|
56
|
+
<th style={thStyle}>Base Legal (LGPD)</th>
|
|
57
|
+
</tr>
|
|
58
|
+
</thead>
|
|
59
|
+
<tbody>
|
|
60
|
+
{[
|
|
61
|
+
['Criação e gerenciamento de conta', 'Execução de contrato (art. 7, V)'],
|
|
62
|
+
['Processamento de pedidos e pagamentos', 'Execução de contrato (art. 7, V)'],
|
|
63
|
+
['Envio de e-mails transacionais (confirmação, nota fiscal)', 'Execução de contrato (art. 7, V)'],
|
|
64
|
+
['Prevenção de fraudes e segurança', 'Legítimo interesse (art. 7, IX)'],
|
|
65
|
+
['Análise de desempenho e melhoria do site', 'Consentimento (art. 7, I)'],
|
|
66
|
+
['Marketing e ofertas personalizadas', 'Consentimento (art. 7, I)'],
|
|
67
|
+
['Cumprimento de obrigações legais', 'Obrigação legal (art. 7, II)'],
|
|
68
|
+
].map(([finalidade, base]) => (
|
|
69
|
+
<tr key={finalidade} style={{ borderBottom: '1px solid #f3f4f6' }}>
|
|
70
|
+
<td style={{ padding: '0.5rem 0.75rem' }}>{finalidade}</td>
|
|
71
|
+
<td style={{ padding: '0.5rem 0.75rem', color: '#6b7280', fontSize: '0.8rem' }}>{base}</td>
|
|
72
|
+
</tr>
|
|
73
|
+
))}
|
|
74
|
+
</tbody>
|
|
75
|
+
</table>
|
|
76
|
+
</Section>
|
|
77
|
+
|
|
78
|
+
<Section title="4. Cookies">
|
|
79
|
+
<p>
|
|
80
|
+
Utilizamos cookies para o funcionamento do site (essenciais) e para análise de uso (com seu
|
|
81
|
+
consentimento). Você pode gerenciar suas preferências de cookies a qualquer momento pelo banner
|
|
82
|
+
exibido na primeira visita ou nas configurações do seu navegador.
|
|
83
|
+
</p>
|
|
84
|
+
<ul>
|
|
85
|
+
<li>
|
|
86
|
+
<strong>Cookies essenciais:</strong> necessários para o funcionamento do site (sessão,
|
|
87
|
+
autenticação, carrinho). Não podem ser desativados.
|
|
88
|
+
</li>
|
|
89
|
+
<li>
|
|
90
|
+
<strong>Cookies analíticos:</strong> coletam informações anônimas sobre como você usa o site.
|
|
91
|
+
Requerem seu consentimento.
|
|
92
|
+
</li>
|
|
93
|
+
</ul>
|
|
94
|
+
</Section>
|
|
95
|
+
|
|
96
|
+
<Section title="5. Compartilhamento de Dados">
|
|
97
|
+
<p>Não vendemos seus dados pessoais. Compartilhamos apenas com:</p>
|
|
98
|
+
<ul>
|
|
99
|
+
<li>
|
|
100
|
+
<strong>Stripe</strong> — processador de pagamentos (sujeito à própria política de privacidade
|
|
101
|
+
da Stripe).
|
|
102
|
+
</li>
|
|
103
|
+
<li>
|
|
104
|
+
<strong>Transportadoras</strong> — nome e endereço de entrega para envio dos pedidos.
|
|
105
|
+
</li>
|
|
106
|
+
<li>
|
|
107
|
+
<strong>Autoridades competentes</strong> — quando exigido por lei ou ordem judicial.
|
|
108
|
+
</li>
|
|
109
|
+
</ul>
|
|
110
|
+
</Section>
|
|
111
|
+
|
|
112
|
+
<Section title="6. Seus Direitos (LGPD art. 18)">
|
|
113
|
+
<p>Como titular de dados, você tem direito a:</p>
|
|
114
|
+
<ul>
|
|
115
|
+
<li>Confirmar a existência de tratamento dos seus dados.</li>
|
|
116
|
+
<li>Acessar os dados que temos sobre você.</li>
|
|
117
|
+
<li>Corrigir dados incompletos, inexatos ou desatualizados.</li>
|
|
118
|
+
<li>Solicitar a anonimização, bloqueio ou eliminação de dados desnecessários.</li>
|
|
119
|
+
<li>Portabilidade dos dados a outro fornecedor de serviço.</li>
|
|
120
|
+
<li>Revogar o consentimento a qualquer momento.</li>
|
|
121
|
+
<li>Opor-se ao tratamento realizado com base em legítimo interesse.</li>
|
|
122
|
+
</ul>
|
|
123
|
+
<p>
|
|
124
|
+
Para exercer seus direitos, entre em contato com nossa Encarregada de Dados (DPO) pelo e-mail:{' '}
|
|
125
|
+
<a href={`mailto:${privacyEmail}`}>{privacyEmail}</a>.
|
|
126
|
+
</p>
|
|
127
|
+
</Section>
|
|
128
|
+
|
|
129
|
+
<Section title="7. Retenção de Dados">
|
|
130
|
+
<p>
|
|
131
|
+
Mantemos seus dados pessoais pelo tempo necessário para cumprir as finalidades descritas nesta
|
|
132
|
+
política, salvo quando a lei exigir ou permitir período maior. Dados de pedidos são retidos por
|
|
133
|
+
até <strong>5 anos</strong> para cumprimento de obrigações fiscais e tributárias.
|
|
134
|
+
</p>
|
|
135
|
+
</Section>
|
|
136
|
+
|
|
137
|
+
<Section title="8. Segurança">
|
|
138
|
+
<p>
|
|
139
|
+
Adotamos medidas técnicas e organizacionais adequadas para proteger seus dados contra acesso não
|
|
140
|
+
autorizado, perda, destruição ou alteração. Utilizamos criptografia TLS em trânsito e
|
|
141
|
+
armazenamento seguro em repouso. Senhas são armazenadas com hashing bcrypt.
|
|
142
|
+
</p>
|
|
143
|
+
</Section>
|
|
144
|
+
|
|
145
|
+
<Section title="9. Encarregado de Dados (DPO)">
|
|
146
|
+
<p>
|
|
147
|
+
Nossa Encarregada de Dados está disponível pelo e-mail:{' '}
|
|
148
|
+
<a href={`mailto:${privacyEmail}`}>{privacyEmail}</a>. Você pode entrar em contato para exercer
|
|
149
|
+
seus direitos ou esclarecer dúvidas sobre o tratamento de seus dados.
|
|
150
|
+
</p>
|
|
151
|
+
</Section>
|
|
152
|
+
|
|
153
|
+
<Section title="10. Alterações a Esta Política">
|
|
154
|
+
<p>
|
|
155
|
+
Podemos atualizar esta Política periodicamente. Notificaremos sobre alterações significativas por
|
|
156
|
+
e-mail ou por aviso proeminente no site. A data de vigência no topo desta página reflete a versão
|
|
157
|
+
mais recente.
|
|
158
|
+
</p>
|
|
159
|
+
</Section>
|
|
160
|
+
|
|
161
|
+
<Section title="11. Contato">
|
|
162
|
+
<p>
|
|
163
|
+
Suporte geral: <a href={`mailto:${supportEmail}`}>{supportEmail}</a>
|
|
164
|
+
<br />
|
|
165
|
+
Privacidade / DPO: <a href={`mailto:${privacyEmail}`}>{privacyEmail}</a>
|
|
166
|
+
<br />
|
|
167
|
+
Também é possível registrar reclamações junto à{' '}
|
|
168
|
+
<strong>Autoridade Nacional de Proteção de Dados (ANPD)</strong> em{' '}
|
|
169
|
+
<a href="https://www.gov.br/anpd" target="_blank" rel="noopener noreferrer">
|
|
170
|
+
gov.br/anpd
|
|
171
|
+
</a>.
|
|
172
|
+
</p>
|
|
173
|
+
</Section>
|
|
174
|
+
|
|
175
|
+
<p style={{ marginTop: '2.5rem', fontSize: '0.8rem', color: '#9ca3af' }}>
|
|
176
|
+
{legal.companyName} — {legal.cnpj} — {legal.address}
|
|
177
|
+
</p>
|
|
178
|
+
|
|
179
|
+
<p style={{ fontSize: '0.8rem', color: '#9ca3af' }}>
|
|
180
|
+
Veja também: <Link to="/terms">Termos de Serviço</Link>
|
|
181
|
+
</p>
|
|
182
|
+
</article>
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
187
|
+
|
|
188
|
+
function Section({ title, children }: { title: string; children: React.ReactNode }) {
|
|
189
|
+
return (
|
|
190
|
+
<section style={{ marginBottom: '1.75rem' }}>
|
|
191
|
+
<h2 style={{ fontSize: '1.05rem', fontWeight: 700, color: '#111827', borderBottom: '1px solid #f3f4f6', paddingBottom: '0.375rem', marginBottom: '0.75rem' }}>
|
|
192
|
+
{title}
|
|
193
|
+
</h2>
|
|
194
|
+
{children}
|
|
195
|
+
</section>
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const thStyle: React.CSSProperties = {
|
|
200
|
+
textAlign: 'left',
|
|
201
|
+
padding: '0.5rem 0.75rem',
|
|
202
|
+
fontSize: '0.75rem',
|
|
203
|
+
fontWeight: 600,
|
|
204
|
+
color: '#6b7280',
|
|
205
|
+
textTransform: 'uppercase',
|
|
206
|
+
letterSpacing: '0.04em',
|
|
207
|
+
};
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { Link } from 'react-router-dom';
|
|
2
|
+
import { siteConfig } from '../../../shared/config/siteConfig';
|
|
3
|
+
import { usePageTitle } from '../../../shared/hooks/usePageTitle';
|
|
4
|
+
|
|
5
|
+
const { name, legal, supportEmail, url } = siteConfig;
|
|
6
|
+
|
|
7
|
+
export function TermsOfServicePage() {
|
|
8
|
+
usePageTitle('Termos de Serviço');
|
|
9
|
+
return (
|
|
10
|
+
<article style={{ maxWidth: 760, margin: '0 auto', lineHeight: 1.75, color: '#374151' }}>
|
|
11
|
+
<h1 style={{ fontSize: '1.75rem', marginBottom: '0.25rem' }}>Termos de Serviço</h1>
|
|
12
|
+
<p style={{ color: '#9ca3af', fontSize: '0.875rem', marginTop: 0, marginBottom: '2rem' }}>
|
|
13
|
+
Vigência a partir de: <strong>{legal.termsEffectiveDate}</strong>
|
|
14
|
+
</p>
|
|
15
|
+
|
|
16
|
+
<Section title="1. Aceitação dos Termos">
|
|
17
|
+
<p>
|
|
18
|
+
Ao acessar ou utilizar o site <strong>{name}</strong> (<a href={url}>{url}</a>), você concorda em
|
|
19
|
+
ficar vinculado a estes Termos de Serviço e à nossa{' '}
|
|
20
|
+
<Link to="/privacy">Política de Privacidade</Link>. Se você não concordar com qualquer parte dos
|
|
21
|
+
termos, não utilize nossos serviços.
|
|
22
|
+
</p>
|
|
23
|
+
</Section>
|
|
24
|
+
|
|
25
|
+
<Section title="2. Identificação da Empresa">
|
|
26
|
+
<Table rows={[
|
|
27
|
+
['Razão Social', legal.companyName],
|
|
28
|
+
['CNPJ', legal.cnpj],
|
|
29
|
+
['Endereço', legal.address],
|
|
30
|
+
['E-mail de suporte', supportEmail],
|
|
31
|
+
]} />
|
|
32
|
+
</Section>
|
|
33
|
+
|
|
34
|
+
<Section title="3. Produtos e Preços">
|
|
35
|
+
<p>
|
|
36
|
+
Os preços exibidos são em Reais (R$) e incluem todos os tributos devidos, salvo indicação em
|
|
37
|
+
contrário. Nos reservamos o direito de alterar preços a qualquer momento sem aviso prévio. O
|
|
38
|
+
preço válido é o exibido no momento da finalização do pedido.
|
|
39
|
+
</p>
|
|
40
|
+
<p>
|
|
41
|
+
Imagens dos produtos são meramente ilustrativas. Eventuais variações de cor, tamanho ou acabamento
|
|
42
|
+
serão descritas na página do produto.
|
|
43
|
+
</p>
|
|
44
|
+
</Section>
|
|
45
|
+
|
|
46
|
+
<Section title="4. Pedidos e Pagamento">
|
|
47
|
+
<p>
|
|
48
|
+
Ao finalizar um pedido, você realiza uma oferta de compra sujeita à nossa confirmação. O pedido
|
|
49
|
+
só é considerado confirmado após aprovação do pagamento e envio do e-mail de confirmação.
|
|
50
|
+
</p>
|
|
51
|
+
<p>
|
|
52
|
+
Aceitamos os métodos de pagamento listados na etapa de checkout, incluindo cartão de crédito/débito,
|
|
53
|
+
PIX e boleto bancário. Os pagamentos são processados de forma segura por meio do{' '}
|
|
54
|
+
<strong>Stripe</strong>.
|
|
55
|
+
</p>
|
|
56
|
+
</Section>
|
|
57
|
+
|
|
58
|
+
<Section title="5. Prazo de Entrega e Frete">
|
|
59
|
+
<p>
|
|
60
|
+
Os prazos de entrega são estimados e podem variar conforme a região. A {name} não se responsabiliza
|
|
61
|
+
por atrasos causados por transportadoras, greves, desastres naturais ou outras força maior.
|
|
62
|
+
</p>
|
|
63
|
+
<p>
|
|
64
|
+
O valor do frete será informado antes da finalização do pedido.
|
|
65
|
+
</p>
|
|
66
|
+
</Section>
|
|
67
|
+
|
|
68
|
+
<Section title="6. Política de Troca e Devolução (CDC)">
|
|
69
|
+
<p>
|
|
70
|
+
Em conformidade com o <strong>Código de Defesa do Consumidor (Lei 8.078/1990)</strong>:
|
|
71
|
+
</p>
|
|
72
|
+
<ul>
|
|
73
|
+
<li>
|
|
74
|
+
Você tem o direito de desistir da compra em até <strong>7 (sete) dias corridos</strong> a contar
|
|
75
|
+
da data de recebimento do produto, sem necessidade de justificativa (direito de arrependimento —
|
|
76
|
+
art. 49 do CDC).
|
|
77
|
+
</li>
|
|
78
|
+
<li>
|
|
79
|
+
Para produtos com defeito, o prazo para reclamação é de <strong>30 dias</strong> (bens não
|
|
80
|
+
duráveis) ou <strong>90 dias</strong> (bens duráveis) a partir da entrega.
|
|
81
|
+
</li>
|
|
82
|
+
<li>
|
|
83
|
+
O produto deve ser devolvido em sua embalagem original, sem sinais de uso, com todos os
|
|
84
|
+
acessórios e nota fiscal.
|
|
85
|
+
</li>
|
|
86
|
+
<li>
|
|
87
|
+
Para solicitar troca ou devolução, entre em contato com nosso suporte em{' '}
|
|
88
|
+
<a href={`mailto:${supportEmail}`}>{supportEmail}</a>.
|
|
89
|
+
</li>
|
|
90
|
+
</ul>
|
|
91
|
+
</Section>
|
|
92
|
+
|
|
93
|
+
<Section title="7. Uso Aceitável">
|
|
94
|
+
<p>Você concorda em não:</p>
|
|
95
|
+
<ul>
|
|
96
|
+
<li>Usar nossos serviços para fins ilegais ou fraudulentos.</li>
|
|
97
|
+
<li>Tentar acessar áreas restritas ou contas de terceiros sem autorização.</li>
|
|
98
|
+
<li>Publicar conteúdo ofensivo, falso ou que infrinja direitos de terceiros.</li>
|
|
99
|
+
<li>Realizar scraping ou coleta automatizada de dados sem consentimento expresso.</li>
|
|
100
|
+
</ul>
|
|
101
|
+
</Section>
|
|
102
|
+
|
|
103
|
+
<Section title="8. Propriedade Intelectual">
|
|
104
|
+
<p>
|
|
105
|
+
Todo o conteúdo disponível no site — textos, logotipo, imagens, ícones e software — é de
|
|
106
|
+
propriedade exclusiva de <strong>{legal.companyName}</strong> ou licenciado por terceiros. É
|
|
107
|
+
proibida a reprodução total ou parcial sem autorização prévia e por escrito.
|
|
108
|
+
</p>
|
|
109
|
+
</Section>
|
|
110
|
+
|
|
111
|
+
<Section title="9. Limitação de Responsabilidade">
|
|
112
|
+
<p>
|
|
113
|
+
Na máxima extensão permitida pela legislação brasileira, a {name} não será responsável por danos
|
|
114
|
+
indiretos, incidentais, especiais ou consequentes decorrentes do uso ou incapacidade de uso dos
|
|
115
|
+
nossos serviços.
|
|
116
|
+
</p>
|
|
117
|
+
</Section>
|
|
118
|
+
|
|
119
|
+
<Section title="10. Lei Aplicável e Foro">
|
|
120
|
+
<p>
|
|
121
|
+
Estes termos são regidos pelas leis brasileiras. Fica eleito o foro da comarca de{' '}
|
|
122
|
+
<strong>São Paulo – SP</strong> para dirimir eventuais controvérsias, com renúncia a qualquer
|
|
123
|
+
outro, por mais privilegiado que seja.
|
|
124
|
+
</p>
|
|
125
|
+
</Section>
|
|
126
|
+
|
|
127
|
+
<Section title="11. Alterações">
|
|
128
|
+
<p>
|
|
129
|
+
Reservamo-nos o direito de modificar estes termos a qualquer momento. As alterações entram em
|
|
130
|
+
vigor na data de publicação. O uso continuado dos serviços após a publicação das alterações
|
|
131
|
+
constitui aceitação dos novos termos.
|
|
132
|
+
</p>
|
|
133
|
+
</Section>
|
|
134
|
+
|
|
135
|
+
<Section title="12. Contato">
|
|
136
|
+
<p>
|
|
137
|
+
Dúvidas? Fale conosco em{' '}
|
|
138
|
+
<a href={`mailto:${supportEmail}`}>{supportEmail}</a>.
|
|
139
|
+
</p>
|
|
140
|
+
</Section>
|
|
141
|
+
|
|
142
|
+
<p style={{ marginTop: '2.5rem', fontSize: '0.8rem', color: '#9ca3af' }}>
|
|
143
|
+
{legal.companyName} — {legal.cnpj} — {legal.address}
|
|
144
|
+
</p>
|
|
145
|
+
</article>
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
150
|
+
|
|
151
|
+
function Section({ title, children }: { title: string; children: React.ReactNode }) {
|
|
152
|
+
return (
|
|
153
|
+
<section style={{ marginBottom: '1.75rem' }}>
|
|
154
|
+
<h2 style={{ fontSize: '1.05rem', fontWeight: 700, color: '#111827', borderBottom: '1px solid #f3f4f6', paddingBottom: '0.375rem', marginBottom: '0.75rem' }}>
|
|
155
|
+
{title}
|
|
156
|
+
</h2>
|
|
157
|
+
{children}
|
|
158
|
+
</section>
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function Table({ rows }: { rows: [string, string][] }) {
|
|
163
|
+
return (
|
|
164
|
+
<table style={{ borderCollapse: 'collapse', width: '100%', fontSize: '0.875rem' }}>
|
|
165
|
+
<tbody>
|
|
166
|
+
{rows.map(([label, value]) => (
|
|
167
|
+
<tr key={label} style={{ borderBottom: '1px solid #f3f4f6' }}>
|
|
168
|
+
<td style={{ padding: '0.5rem 0.75rem', fontWeight: 600, color: '#6b7280', width: '35%' }}>{label}</td>
|
|
169
|
+
<td style={{ padding: '0.5rem 0.75rem', color: '#111827' }}>{value}</td>
|
|
170
|
+
</tr>
|
|
171
|
+
))}
|
|
172
|
+
</tbody>
|
|
173
|
+
</table>
|
|
174
|
+
);
|
|
175
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { render, screen, waitFor } from '@testing-library/react';
|
|
2
|
+
import { MemoryRouter, Routes, Route } from 'react-router-dom';
|
|
3
|
+
import { OrderDetailPage } from '../pages/OrderDetailPage';
|
|
4
|
+
|
|
5
|
+
const mockFetch = jest.fn();
|
|
6
|
+
beforeAll(() => { global.fetch = mockFetch; });
|
|
7
|
+
beforeEach(() => mockFetch.mockClear());
|
|
8
|
+
|
|
9
|
+
function renderPage(orderId = 'test-order-id') {
|
|
10
|
+
return render(
|
|
11
|
+
<MemoryRouter initialEntries={[`/orders/${orderId}`]}>
|
|
12
|
+
<Routes>
|
|
13
|
+
<Route path="/orders/:id" element={<OrderDetailPage />} />
|
|
14
|
+
</Routes>
|
|
15
|
+
</MemoryRouter>,
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const baseOrder = {
|
|
20
|
+
id: 'test-order-id',
|
|
21
|
+
status: 'PAID',
|
|
22
|
+
total: 150,
|
|
23
|
+
subtotal: 150,
|
|
24
|
+
discount: 0,
|
|
25
|
+
shippingCost: 0,
|
|
26
|
+
trackingCode: null,
|
|
27
|
+
createdAt: '2026-01-01T00:00:00.000Z',
|
|
28
|
+
items: [
|
|
29
|
+
{ id: 'i1', name: 'Camiseta Azul', qty: 2, price: 75, sku: 'CAM-1', productId: 'p1', variantId: 'v1', image: null },
|
|
30
|
+
],
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
describe('OrderDetailPage', () => {
|
|
34
|
+
it('exibe estado de carregamento inicialmente', () => {
|
|
35
|
+
mockFetch.mockReturnValue(new Promise(() => {}));
|
|
36
|
+
renderPage();
|
|
37
|
+
expect(screen.getByText(/carregando/i)).toBeInTheDocument();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('renderiza detalhes do pedido (itens, total, status)', async () => {
|
|
41
|
+
mockFetch.mockResolvedValue({
|
|
42
|
+
ok: true,
|
|
43
|
+
json: async () => baseOrder,
|
|
44
|
+
});
|
|
45
|
+
renderPage();
|
|
46
|
+
await waitFor(() =>
|
|
47
|
+
expect(screen.getByText(/camiseta azul/i)).toBeInTheDocument(),
|
|
48
|
+
);
|
|
49
|
+
expect(screen.getByText('PAID')).toBeInTheDocument();
|
|
50
|
+
expect(screen.getByText(/150/)).toBeInTheDocument();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('exibe trackingCode quando status é SHIPPED', async () => {
|
|
54
|
+
mockFetch.mockResolvedValue({
|
|
55
|
+
ok: true,
|
|
56
|
+
json: async () => ({
|
|
57
|
+
...baseOrder,
|
|
58
|
+
status: 'SHIPPED',
|
|
59
|
+
trackingCode: 'BR123456789',
|
|
60
|
+
}),
|
|
61
|
+
});
|
|
62
|
+
renderPage();
|
|
63
|
+
await waitFor(() =>
|
|
64
|
+
expect(screen.getByText(/BR123456789/)).toBeInTheDocument(),
|
|
65
|
+
);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('exibe mensagem de erro quando pedido não é encontrado (404)', async () => {
|
|
69
|
+
mockFetch.mockResolvedValue({ ok: false, status: 404 });
|
|
70
|
+
renderPage();
|
|
71
|
+
await waitFor(() =>
|
|
72
|
+
expect(screen.getByRole('alert')).toBeInTheDocument(),
|
|
73
|
+
);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { render, screen, waitFor } from '@testing-library/react';
|
|
2
|
+
import { MemoryRouter } from 'react-router-dom';
|
|
3
|
+
import { OrderHistoryPage } from '../pages/OrderHistoryPage';
|
|
4
|
+
|
|
5
|
+
const mockFetch = jest.fn();
|
|
6
|
+
beforeAll(() => { global.fetch = mockFetch; });
|
|
7
|
+
beforeEach(() => mockFetch.mockClear());
|
|
8
|
+
|
|
9
|
+
function renderPage() {
|
|
10
|
+
return render(
|
|
11
|
+
<MemoryRouter>
|
|
12
|
+
<OrderHistoryPage />
|
|
13
|
+
</MemoryRouter>,
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
describe('OrderHistoryPage', () => {
|
|
18
|
+
it('exibe estado de carregamento inicialmente', () => {
|
|
19
|
+
mockFetch.mockReturnValue(new Promise(() => {}));
|
|
20
|
+
renderPage();
|
|
21
|
+
expect(screen.getByText(/carregando/i)).toBeInTheDocument();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('renderiza lista de pedidos com status badge', async () => {
|
|
25
|
+
mockFetch.mockResolvedValue({
|
|
26
|
+
ok: true,
|
|
27
|
+
json: async () => ({
|
|
28
|
+
items: [
|
|
29
|
+
{
|
|
30
|
+
id: 'order-abc-1',
|
|
31
|
+
status: 'PAID',
|
|
32
|
+
total: 100,
|
|
33
|
+
createdAt: '2026-01-01T00:00:00.000Z',
|
|
34
|
+
items: [],
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
nextCursor: null,
|
|
38
|
+
}),
|
|
39
|
+
});
|
|
40
|
+
renderPage();
|
|
41
|
+
await waitFor(() =>
|
|
42
|
+
expect(screen.getByText(/order-abc-1/i)).toBeInTheDocument(),
|
|
43
|
+
);
|
|
44
|
+
expect(screen.getByText('PAID')).toBeInTheDocument();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('exibe mensagem de vazio quando não há pedidos', async () => {
|
|
48
|
+
mockFetch.mockResolvedValue({
|
|
49
|
+
ok: true,
|
|
50
|
+
json: async () => ({ items: [], nextCursor: null }),
|
|
51
|
+
});
|
|
52
|
+
renderPage();
|
|
53
|
+
await waitFor(() =>
|
|
54
|
+
expect(screen.getByText(/nenhum pedido/i)).toBeInTheDocument(),
|
|
55
|
+
);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('cada pedido tem link para /orders/:id', async () => {
|
|
59
|
+
mockFetch.mockResolvedValue({
|
|
60
|
+
ok: true,
|
|
61
|
+
json: async () => ({
|
|
62
|
+
items: [
|
|
63
|
+
{
|
|
64
|
+
id: 'order-id-1',
|
|
65
|
+
status: 'PENDING',
|
|
66
|
+
total: 50,
|
|
67
|
+
createdAt: '2026-01-01T00:00:00.000Z',
|
|
68
|
+
items: [],
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
nextCursor: null,
|
|
72
|
+
}),
|
|
73
|
+
});
|
|
74
|
+
renderPage();
|
|
75
|
+
await waitFor(() =>
|
|
76
|
+
expect(screen.getByRole('link')).toHaveAttribute('href', '/orders/order-id-1'),
|
|
77
|
+
);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('exibe mensagem de erro em caso de falha na requisição', async () => {
|
|
81
|
+
mockFetch.mockResolvedValue({ ok: false, status: 401 });
|
|
82
|
+
renderPage();
|
|
83
|
+
await waitFor(() =>
|
|
84
|
+
expect(screen.getByRole('alert')).toBeInTheDocument(),
|
|
85
|
+
);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
import { useParams } from 'react-router-dom';
|
|
3
|
+
import { apiFetch } from '../../../shared/lib/apiFetch';
|
|
4
|
+
import { usePageTitle } from '../../../shared/hooks/usePageTitle';
|
|
5
|
+
|
|
6
|
+
interface OrderItem {
|
|
7
|
+
id: string;
|
|
8
|
+
name: string;
|
|
9
|
+
sku: string;
|
|
10
|
+
price: number;
|
|
11
|
+
qty: number;
|
|
12
|
+
image: string | null;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface OrderDetail {
|
|
16
|
+
id: string;
|
|
17
|
+
status: string;
|
|
18
|
+
subtotal: number;
|
|
19
|
+
discount: number;
|
|
20
|
+
shippingCost: number;
|
|
21
|
+
total: number;
|
|
22
|
+
trackingCode: string | null;
|
|
23
|
+
createdAt: string;
|
|
24
|
+
items: OrderItem[];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function formatBRL(value: number) {
|
|
28
|
+
return value.toLocaleString('pt-BR', { style: 'currency', currency: 'BRL' });
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function OrderDetailPage() {
|
|
32
|
+
usePageTitle('Detalhes do Pedido');
|
|
33
|
+
const { id } = useParams<{ id: string }>();
|
|
34
|
+
const [order, setOrder] = useState<OrderDetail | null>(null);
|
|
35
|
+
const [loading, setLoading] = useState(true);
|
|
36
|
+
const [error, setError] = useState<string | null>(null);
|
|
37
|
+
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
apiFetch<OrderDetail>(`/api/orders/${id}`)
|
|
40
|
+
.then((data) => {
|
|
41
|
+
setOrder(data);
|
|
42
|
+
setLoading(false);
|
|
43
|
+
})
|
|
44
|
+
.catch(() => {
|
|
45
|
+
setError('Pedido não encontrado');
|
|
46
|
+
setLoading(false);
|
|
47
|
+
});
|
|
48
|
+
}, [id]);
|
|
49
|
+
|
|
50
|
+
if (loading) return <p>Carregando...</p>;
|
|
51
|
+
if (error || !order) return <p role="alert">{error ?? 'Erro desconhecido'}</p>;
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<main>
|
|
55
|
+
<h1>Pedido #{order.id.slice(0, 8)}</h1>
|
|
56
|
+
<p>
|
|
57
|
+
Status: <strong>{order.status}</strong>
|
|
58
|
+
</p>
|
|
59
|
+
{order.trackingCode && (
|
|
60
|
+
<p>Código de rastreamento: <strong>{order.trackingCode}</strong></p>
|
|
61
|
+
)}
|
|
62
|
+
<h2>Itens</h2>
|
|
63
|
+
<ul>
|
|
64
|
+
{order.items.map((item) => (
|
|
65
|
+
<li key={item.id}>
|
|
66
|
+
{item.name} × {item.qty} — {formatBRL(item.price)}
|
|
67
|
+
</li>
|
|
68
|
+
))}
|
|
69
|
+
</ul>
|
|
70
|
+
<p>Total: {formatBRL(order.total)}</p>
|
|
71
|
+
</main>
|
|
72
|
+
);
|
|
73
|
+
}
|