From 88479e785097bf1969210bb791cd2e464230b785 Mon Sep 17 00:00:00 2001 From: Pablo Date: Fri, 8 May 2026 16:53:43 -0300 Subject: [PATCH 1/5] chore(infra): add springdoc and configure cors --- api/pom.xml | 6 ++ .../ecommerce/config/SecurityConfig.java | 75 ++++++++++++------- 2 files changed, 53 insertions(+), 28 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index 9ba2aa3..e6bb2a7 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -21,6 +21,12 @@ + + org.springdoc + springdoc-openapi-starter-webmvc-ui + 2.5.0 + + org.springframework.boot spring-boot-starter-data-jpa diff --git a/api/src/main/java/com/orderflow/ecommerce/config/SecurityConfig.java b/api/src/main/java/com/orderflow/ecommerce/config/SecurityConfig.java index 0e9ff97..d1a172d 100644 --- a/api/src/main/java/com/orderflow/ecommerce/config/SecurityConfig.java +++ b/api/src/main/java/com/orderflow/ecommerce/config/SecurityConfig.java @@ -1,32 +1,51 @@ -package com.orderflow.ecommerce.config; + package com.orderflow.ecommerce.config; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; -import org.springframework.security.config.http.SessionCreationPolicy; -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.security.web.SecurityFilterChain; + import org.springframework.context.annotation.Bean; + import org.springframework.context.annotation.Configuration; + import org.springframework.security.config.annotation.web.builders.HttpSecurity; + import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; + import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; + import org.springframework.security.config.http.SessionCreationPolicy; + import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; + import org.springframework.security.crypto.password.PasswordEncoder; + import org.springframework.security.web.SecurityFilterChain; + import org.springframework.web.cors.CorsConfiguration; + import org.springframework.web.cors.CorsConfigurationSource; + import org.springframework.web.cors.UrlBasedCorsConfigurationSource; -@Configuration -@EnableWebSecurity -public class SecurityConfig { + import java.util.Arrays; - @Bean - public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { - http - .csrf(AbstractHttpConfigurer::disable) // Desabilita CSRF (comum em APIs REST) - .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) // API sem estado - .authorizeHttpRequests(auth -> auth - .anyRequest().permitAll() // Por enquanto, libera tudo para você não se travar - ); + @Configuration + @EnableWebSecurity + public class SecurityConfig { - return http.build(); - } - @Bean - public PasswordEncoder passwordEncoder() { - return new BCryptPasswordEncoder(); - } -} \ No newline at end of file + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http + .csrf(AbstractHttpConfigurer::disable) // Desabilita CSRF (comum em APIs REST) + .cors(cors -> cors.configurationSource(corsConfigurationSource())) // Adiciona CORS + .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) // API sem estado + .authorizeHttpRequests(auth -> auth + .anyRequest().permitAll() // Por enquanto, libera tudo para você não se travar + ); + + return http.build(); + } + + @Bean + public CorsConfigurationSource corsConfigurationSource() { + CorsConfiguration configuration = new CorsConfiguration(); + configuration.setAllowedOriginPatterns(Arrays.asList("*")); // Permite todas as origens (para desenvolvimento) + configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS")); + configuration.setAllowedHeaders(Arrays.asList("*")); + configuration.setAllowCredentials(true); + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", configuration); + return source; + } + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + } \ No newline at end of file From 1fc72633954eecc3e284fae8fcb416661b4e34ec Mon Sep 17 00:00:00 2001 From: Pablo Date: Fri, 8 May 2026 16:54:30 -0300 Subject: [PATCH 2/5] chore(infra): configure tailwind and routing dependencies --- web/package-lock.json | 130 ++++++++++++++++++++++++++++++++++++------ web/package.json | 7 ++- web/postcss.config.js | 5 ++ 3 files changed, 124 insertions(+), 18 deletions(-) create mode 100644 web/postcss.config.js diff --git a/web/package-lock.json b/web/package-lock.json index bf516aa..e4bc71d 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -9,10 +9,12 @@ "version": "0.0.0", "dependencies": { "react": "^19.2.5", - "react-dom": "^19.2.5" + "react-dom": "^19.2.5", + "react-router-dom": "^7.14.2" }, "devDependencies": { "@eslint/js": "^10.0.1", + "@tailwindcss/postcss": "^4.2.4", "@tailwindcss/vite": "^4.2.4", "@types/node": "^24.12.2", "@types/react": "^19.2.14", @@ -22,12 +24,26 @@ "eslint-plugin-react-hooks": "^7.1.1", "eslint-plugin-react-refresh": "^0.5.2", "globals": "^17.5.0", + "postcss": "^8.5.14", "tailwindcss": "^4.2.4", "typescript": "~6.0.2", "typescript-eslint": "^8.58.2", "vite": "^8.0.10" } }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@babel/code-frame": { "version": "7.29.0", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", @@ -59,7 +75,6 @@ "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -269,6 +284,29 @@ "node": ">=6.9.0" } }, + "node_modules/@emnapi/core": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", + "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", + "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@emnapi/wasi-threads": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", @@ -1074,6 +1112,20 @@ "node": ">= 20" } }, + "node_modules/@tailwindcss/postcss": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.2.4.tgz", + "integrity": "sha512-wgAVj6nUWAolAu8YFvzT2cTBIElWHkjZwFYovF+xsqKsW2ADxM/X2opxj5NsF/qVccAOjRNe8X2IdPzMsWyHTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "@tailwindcss/node": "4.2.4", + "@tailwindcss/oxide": "4.2.4", + "postcss": "^8.5.6", + "tailwindcss": "4.2.4" + } + }, "node_modules/@tailwindcss/vite": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.2.4.tgz", @@ -1127,7 +1179,6 @@ "integrity": "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~7.16.0" } @@ -1138,7 +1189,6 @@ "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -1198,7 +1248,6 @@ "integrity": "sha512-HDQH9O/47Dxi1ceDhBXdaldtf/WV9yRYMjbjCuNk3qnaTD564qwv61Y7+gTxwxRKzSrgO5uhtw584igXVuuZkA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.59.1", "@typescript-eslint/types": "8.59.1", @@ -1429,7 +1478,6 @@ "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -1520,7 +1568,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", @@ -1563,6 +1610,19 @@ "dev": true, "license": "MIT" }, + "node_modules/cookie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -1670,7 +1730,6 @@ "integrity": "sha512-wiyGaKsDgqXvF40P8mDwiUp/KQjE1FdrIEJsM8PZ3XCiniTMXS3OHWWUe5FI5agoCnr8x4xPrTDZuxsBlNHl+Q==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.2", @@ -2586,7 +2645,6 @@ "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -2595,9 +2653,9 @@ } }, "node_modules/postcss": { - "version": "8.5.12", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.12.tgz", - "integrity": "sha512-W62t/Se6rA0Az3DfCL0AqJwXuKwBeYg6nOaIgzP+xZ7N5BFCI7DYi1qs6ygUYT6rvfi6t9k65UMLJC+PHZpDAA==", + "version": "8.5.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.14.tgz", + "integrity": "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==", "dev": true, "funding": [ { @@ -2648,7 +2706,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.5.tgz", "integrity": "sha512-llUJLzz1zTUBrskt2pwZgLq59AemifIftw4aB7JxOqf1HY2FDaGDxgwpAPVzHU1kdWabH7FauP4i1oEeer2WCA==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -2665,6 +2722,44 @@ "react": "^19.2.5" } }, + "node_modules/react-router": { + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.14.2.tgz", + "integrity": "sha512-yCqNne6I8IB6rVCH7XUvlBK7/QKyqypBFGv+8dj4QBFJiiRX+FG7/nkdAvGElyvVZ/HQP5N19wzteuTARXi5Gw==", + "license": "MIT", + "dependencies": { + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/react-router-dom": { + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.14.2.tgz", + "integrity": "sha512-YZcM5ES8jJSM+KrJ9BdvHHqlnGTg5tH3sC5ChFRj4inosKctdyzBDhOyyHdGk597q2OT6NTrCA1OvB/YDwfekQ==", + "license": "MIT", + "dependencies": { + "react-router": "7.14.2" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, "node_modules/rolldown": { "version": "1.0.0-rc.17", "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.17.tgz", @@ -2722,6 +2817,12 @@ "semver": "bin/semver.js" } }, + "node_modules/set-cookie-parser": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", + "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", + "license": "MIT" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -2833,7 +2934,6 @@ "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -2920,7 +3020,6 @@ "integrity": "sha512-rZuUu9j6J5uotLDs+cAA4O5H4K1SfPliUlQwqa6YEwSrWDZzP4rhm00oJR5snMewjxF5V/K3D4kctsUTsIU9Mw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", @@ -3045,7 +3144,6 @@ "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", "dev": true, "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/web/package.json b/web/package.json index 4ff02fc..a6055bd 100644 --- a/web/package.json +++ b/web/package.json @@ -10,11 +10,13 @@ "preview": "vite preview" }, "dependencies": { - "react": "^19.2.5", - "react-dom": "^19.2.5" + "react": "^19.2.5", + "react-dom": "^19.2.5", + "react-router-dom": "^7.14.2" }, "devDependencies": { "@eslint/js": "^10.0.1", + "@tailwindcss/postcss": "^4.2.4", "@tailwindcss/vite": "^4.2.4", "@types/node": "^24.12.2", "@types/react": "^19.2.14", @@ -24,6 +26,7 @@ "eslint-plugin-react-hooks": "^7.1.1", "eslint-plugin-react-refresh": "^0.5.2", "globals": "^17.5.0", + "postcss": "^8.5.14", "tailwindcss": "^4.2.4", "typescript": "~6.0.2", "typescript-eslint": "^8.58.2", diff --git a/web/postcss.config.js b/web/postcss.config.js new file mode 100644 index 0000000..db3c3d1 --- /dev/null +++ b/web/postcss.config.js @@ -0,0 +1,5 @@ +export default { + plugins: { + "@tailwindcss/postcss": {}, + }, +} \ No newline at end of file From 1270f38e53d11401502747698cefe6924b3e5d0e Mon Sep 17 00:00:00 2001 From: Pablo Date: Fri, 8 May 2026 16:54:56 -0300 Subject: [PATCH 3/5] style: add design tokens and theme variables --- web/src/index.css | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/web/src/index.css b/web/src/index.css index 03d224a..066c6ab 100644 --- a/web/src/index.css +++ b/web/src/index.css @@ -1,5 +1,38 @@ @import "tailwindcss"; +@theme { + --color-text: #141414; + --color-textSecondary: #737373; + --color-textHint: #a6a6a6; + --color-surface: #ffffff; + --color-bg: #f7f7f7; + --color-border: #e0e0e0; + --color-primary: #378add; + --color-primaryLight: #e6f1fb; + --color-primaryDark: #185fa5; + --color-successText: #3b6d11; + --color-errorLight: #fcebeb; + --color-errorText: #a32d2d; + --color-skeleton: #e8e8e8; +} + +:root { + --color-text: #141414; + --color-textSecondary: #737373; + --color-textHint: #a6a6a6; + --color-surface: #ffffff; + --color-bg: #f7f7f7; + --color-border: #e0e0e0; + --color-primary: #378add; + --color-primaryLight: #e6f1fb; + --color-primaryDark: #185fa5; + --color-successText: #3b6d11; + --color-errorLight: #fcebeb; + --color-errorText: #a32d2d; + --color-skeleton: #e8e8e8; +} + body { margin: 0; + background-color: var(--color-bg); } From 8a85759f835ddf9a934968d68fe760b2451585fb Mon Sep 17 00:00:00 2001 From: Pablo Date: Fri, 8 May 2026 16:55:19 -0300 Subject: [PATCH 4/5] chore(docker): add web dev service with hot reload --- docker-compose.yml | 17 +++++++++++++++++ web/Dockerfile.dev | 6 ++++++ 2 files changed, 23 insertions(+) create mode 100644 web/Dockerfile.dev diff --git a/docker-compose.yml b/docker-compose.yml index d065089..c0bfa8d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -92,6 +92,23 @@ services: - orderflow-network restart: unless-stopped + web-dev: + build: + context: ./web + dockerfile: Dockerfile.dev + container_name: orderflow-web-dev + ports: + - "5173:5173" + volumes: + - ./web:/app + - /app/node_modules + depends_on: + app: + condition: service_started + networks: + - orderflow-network + restart: unless-stopped + volumes: postgres_data: rabbitmq_data: diff --git a/web/Dockerfile.dev b/web/Dockerfile.dev new file mode 100644 index 0000000..32535d4 --- /dev/null +++ b/web/Dockerfile.dev @@ -0,0 +1,6 @@ +FROM node:22-alpine +WORKDIR /app +COPY package*.json ./ +RUN npm install +EXPOSE 5173 +CMD ["npm", "run", "dev", "--", "--host"] \ No newline at end of file From b08f74805a575abd9352c88f630cac44529b58df Mon Sep 17 00:00:00 2001 From: Pablo Date: Fri, 8 May 2026 17:02:46 -0300 Subject: [PATCH 5/5] feat(produto): add homepage with product listing and shared components --- web/public/products/1.webp | Bin 0 -> 16602 bytes web/public/products/2.webp | Bin 0 -> 4304 bytes web/public/products/3.webp | Bin 0 -> 12252 bytes web/public/products/4.webp | Bin 0 -> 14984 bytes web/src/App.tsx | 71 +++-------- web/src/app/HomePage/page.tsx | 118 ++++++++++++++++++ web/src/components/Navbar/index.tsx | 53 ++++++++ web/src/components/ProductCard/index.tsx | 46 +++++++ .../components/ProductCardSkeleton/index.tsx | 20 +++ 9 files changed, 254 insertions(+), 54 deletions(-) create mode 100644 web/public/products/1.webp create mode 100644 web/public/products/2.webp create mode 100644 web/public/products/3.webp create mode 100644 web/public/products/4.webp create mode 100644 web/src/app/HomePage/page.tsx create mode 100644 web/src/components/Navbar/index.tsx create mode 100644 web/src/components/ProductCard/index.tsx create mode 100644 web/src/components/ProductCardSkeleton/index.tsx diff --git a/web/public/products/1.webp b/web/public/products/1.webp new file mode 100644 index 0000000000000000000000000000000000000000..280055187a8f84bd130208480526e0da27742678 GIT binary patch literal 16602 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 literal 0 HcmV?d00001 diff --git a/web/public/products/2.webp b/web/public/products/2.webp new file mode 100644 index 0000000000000000000000000000000000000000..22dcd5b458d6903fb043be2a2118b229de8bf948 GIT binary patch literal 4304 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 literal 0 HcmV?d00001 diff --git a/web/public/products/3.webp b/web/public/products/3.webp new file mode 100644 index 0000000000000000000000000000000000000000..e52d20ba905ef4995ccc4e325b3e46d50683fb8e GIT binary patch literal 12252 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 literal 0 HcmV?d00001 diff --git a/web/public/products/4.webp b/web/public/products/4.webp new file mode 100644 index 0000000000000000000000000000000000000000..20884d2403613bd4cca72049065276049780aeb4 GIT binary patch literal 14984 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�HWp-YdErDJ&R01!Sd z?w2YzNZ4Xzn6}rt3_`r_rE5yvi0WB*mLcm*#+8619^dHR-%f8tHhDG(@k92AeyjU&~H(^QZs?t=aV?dcy+$6 z*WM$#)WKaLAlHUL(j>i9Z7bi_3B93J6NA!V4dmC$TCl#Wl=Q%r|wBWP^B0^DO^_oQ?oneZPwW}RAKih&3x_TJ*$UeN_# zamALP>Ri9tal&j;Z>H-uUm(^JI(6Y z$=&YA*LG2V-5L!(Y+J15vZnNSeHk<*PQZ)d*=H-6pWA!gnD!})4p7l0CRSQHI15Ao z&Yo7=3f}%MzeKothIivehO2`qKO86eB7XMY3Ovoc`0lv^Y5BY>{Em4M>TT^CS#k8& z_HPo?kP>hICwI3n1puU^X#QQp2?;<20Rw>i)&H}91A+KASNkjD{*SEpSEl+O8_>Tp z8tDJCw+IFQ*Zwcyze)1%@gJQ3%^?ARI4A)4UkwurGZRSse=^8l1VHZp=$V> X{QCMkT+00qk?C(yVE>=|5B&cDr|AR- literal 0 HcmV?d00001 diff --git a/web/src/App.tsx b/web/src/App.tsx index ac122e0..f8eb619 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -1,56 +1,19 @@ -import { useCallback, useState } from 'react' - -import { apiGet, getApiBaseUrl } from './lib/api' - -function App() { - const [result, setResult] = useState('') - const [loading, setLoading] = useState(false) - - const tryPing = useCallback(async () => { - setLoading(true) - setResult('') - try { - const data = await apiGet<{ status?: string }>('/test/ping') - setResult( - `OK: ${JSON.stringify(data)}`, - ) - } catch (e) { - setResult(e instanceof Error ? e.message : 'Erro desconhecido') - } finally { - setLoading(false) - } - }, []) - - return ( -
-
-
-

- OrderFlow Web -

-

- Boilerplate React + TypeScript + Tailwind. Base da API:{' '} - - {getApiBaseUrl()} - -

-
- - {result ? ( -

- {result} -

- ) : null} -
-
+import { BrowserRouter, Routes, Route } from "react-router-dom" +import Navbar from "./components/Navbar" +import HomePage from "./app/HomePage/page" +import CheckoutPage from "./app/CheckoutPage/page" +import EmailConfirmPage from "./app/EmailConfirmPage/page" + +export default function App() { + return ( + + + + } /> + } /> + } /> + + ) } - -export default App + \ No newline at end of file diff --git a/web/src/app/HomePage/page.tsx b/web/src/app/HomePage/page.tsx new file mode 100644 index 0000000..7069544 --- /dev/null +++ b/web/src/app/HomePage/page.tsx @@ -0,0 +1,118 @@ + import { useEffect, useState } from "react"; + import ProductCard from "../../components/ProductCard"; + import ProductCardSkeleton from "../../components/ProductCardSkeleton"; + + type Category = { + id: number; + name: string; + }; + + type Product = { + id: number; + name: string; + category?: Category; + price: number; + }; + + export default function Home() { + const [products, setProducts] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const [search, setSearch] = useState(""); + + useEffect(() => { + async function fetchProducts() { + try { + setLoading(true); + + await new Promise((resolve) => setTimeout(resolve, 3000)); + + const res = await fetch("http://localhost:8080/products"); + + if (!res.ok) throw new Error("Erro ao buscar produtos"); + + const data: Product[] = await res.json(); + setProducts(data); + } catch (err: unknown) { + if (err instanceof Error) { + setError(err.message); + } else { + setError("Erro desconhecido"); + } + } finally { + setLoading(false); + } + } + + fetchProducts(); + }, []); + + const filtered = products.filter((p) => + p.name.toLowerCase().includes(search.toLowerCase()), + ); + + return ( +
+ {loading ? ( +
+ {/* Busca */} +
+ + {/* Título */} +
+ + {/* Quantidade */} +
+ + {/* Cards */} +
+ {Array.from({ length: 4 }).map((_, index) => ( + + ))} +
+
+ ) : ( + <> + {/* Busca */} + setSearch(e.target.value)} + /> + + {/* Título */} +

Produtos

+ + {/* Quantidade */} +

+ {filtered.length} itens +

+ + {/* ERRO */} + {error && ( +

{error}

+ )} + + {/* Nenhum produto */} + {filtered.length === 0 && !error && ( +

Nenhum produto encontrado.

+ )} + + {/* LISTA */} + {!error && filtered.length > 0 && ( +
+ {filtered.map((product) => ( + + ))} +
+ )} + + )} +
+ ); + } \ No newline at end of file diff --git a/web/src/components/Navbar/index.tsx b/web/src/components/Navbar/index.tsx new file mode 100644 index 0000000..1abdd2d --- /dev/null +++ b/web/src/components/Navbar/index.tsx @@ -0,0 +1,53 @@ +import { useState } from "react"; + +export default function Navbar() { + // Controla se o menu mobile está aberto ou fechado + const [isOpen, setIsOpen] = useState(false); + + return ( +
+ ); +} \ No newline at end of file diff --git a/web/src/components/ProductCard/index.tsx b/web/src/components/ProductCard/index.tsx new file mode 100644 index 0000000..77aea29 --- /dev/null +++ b/web/src/components/ProductCard/index.tsx @@ -0,0 +1,46 @@ + type Category = { + id: number; + name: string; +}; + + type Product = { + id: number; + name: string; + category?: Category; + price: number; + }; + + type Props = { + product: Product; + }; + + export default function ProductCard({ product }: Props) { + return ( +
+ {/* Imagem */} + {product.name} + + {/* Categoria */} + {product.category && ( + + {product.category.name} + + )} + + {/* Nome */} +

{product.name}

+ + {/* Preço */} +

R$ {product.price.toFixed(2)}

+ + {/* Botão */} + +
+ ); + } \ No newline at end of file diff --git a/web/src/components/ProductCardSkeleton/index.tsx b/web/src/components/ProductCardSkeleton/index.tsx new file mode 100644 index 0000000..abe41d6 --- /dev/null +++ b/web/src/components/ProductCardSkeleton/index.tsx @@ -0,0 +1,20 @@ +export default function ProductCardSkeleton() { + return ( +
+ {/* Imagem */} +
+ + {/* Categoria */} +
+ + {/* Nome */} +
+ + {/* Preço */} +
+ + {/* Botão */} +
+
+ ); +} \ No newline at end of file