feat: agregar checkout y unos tests de API

This commit is contained in:
Sofía Maturana 2026-05-18 12:38:56 -04:00
parent 6a9b719226
commit 617c0e82e1
12 changed files with 329 additions and 660 deletions

10
.gram/debug.jsonc Normal file
View file

@ -0,0 +1,10 @@
[
{
"adapter": "JavaScript",
"type": "chrome",
"request": "attach",
"port": 9222,
"label": "Conectar a navegador",
"webRoot": "${GRAM_WORKTREE_ROOT}/src",
},
]

3
.gram/settings.jsonc Normal file
View file

@ -0,0 +1,3 @@
{
"language_servers": ["...", "!biome"],
}

4
.rumdl.toml Normal file
View file

@ -0,0 +1,4 @@
[global]
flavor = "gfm"
[MD013]
reflow = true

12
api-tests/profile.hurl Normal file
View file

@ -0,0 +1,12 @@
POST http://localhost:5000/api/auth/login
{
"email": "test@test.com",
"password": "123123"
}
HTTP 200
[Captures]
token: jsonpath "$.token"
GET http://localhost:5000/api/auth/me
Authorization: Bearer {{token}}
HTTP 200

View file

@ -1,94 +1,98 @@
# Simple API JWT
API para consumir un servicio de Auth con JWT.
## Instalación
```sh
npm install
```
## Uso
```sh
npm run dev
```
## Endpoints
### Pizzas
```sh
GET /api/pizzas
```
### Pizza (única)
```sh
GET /api/pizzas/:id
```
### Auth
```sh
POST /api/auth/login
POST /api/auth/register
```
body:
```json
{
"email": "test@example.com",
"password": "123123"
}
```
### Checkout & Profile
Esta ruta requiere un token JWT en el header, el token se obtiene en el endpoint de Auth explicado en el item siguiente (JWT).
Además puedes enviar un carrito con los productos a comprar, esto es solo una simulación, no se guarda en la base de datos.
```sh
POST /api/checkouts
```
body:
```json
{
"cart": [...]
}
```
Endpoint para obtener el perfil del usuario autenticado. Necesitas enviar el token JWT en el header.
```sh
GET /api/auth/me
```
## JWT
Para obtener el token JWT, se debe hacer una petición a `/api/auth/login` o a `/api/auth/register` con el body correspondiente.
El token JWT se debe enviar en el header `Authorization` de la siguiente manera:
```sh
Authorization Bearer token_jwt
```
Ejemplo con fetch:
```js
await fetch("http://localhost:5000/api/checkout", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer token_jwt`,
},
body: JSON.stringify({
cart: carrito,
}),
});
```
# Simple API JWT
API para consumir un servicio de Auth con JWT.
## Instalación
```sh
npm install
```
## Uso
```sh
npm run dev
```
## Endpoints
### Pizzas
```sh
GET /api/pizzas
```
### Pizza (única)
```sh
GET /api/pizzas/:id
```
### Auth
```sh
POST /api/auth/login
POST /api/auth/register
```
body:
```json
{
"email": "test@example.com",
"password": "123123"
}
```
### Checkout & Profile
Esta ruta requiere un token JWT en el header, el token se obtiene en el endpoint
de Auth explicado en el item siguiente (JWT).
Además puedes enviar un carrito con los productos a comprar, esto es solo una
simulación, no se guarda en la base de datos.
```sh
POST /api/checkouts
```
body:
```json
{
"cart": [...]
}
```
Endpoint para obtener el perfil del usuario autenticado. Necesitas enviar el
token JWT en el header.
```sh
GET /api/auth/me
```
## JWT
Para obtener el token JWT, se debe hacer una petición a `/api/auth/login` o a
`/api/auth/register` con el body correspondiente.
El token JWT se debe enviar en el header `Authorization` de la siguiente manera:
```sh
Authorization Bearer token_jwt
```
Ejemplo con fetch:
```js
await fetch("http://localhost:5000/api/checkout", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer token_jwt`,
},
body: JSON.stringify({
cart: carrito,
}),
});
```

View file

@ -1,231 +0,0 @@
{
"$schema": "https://biomejs.dev/schemas/2.4.8/schema.json",
"vcs": { "enabled": true, "clientKind": "git", "useIgnoreFile": true },
"files": { "ignoreUnknown": false },
"formatter": { "enabled": true, "indentStyle": "tab" },
"linter": {
"enabled": true,
"rules": { "recommended": false },
"includes": ["**", "!dist"]
},
"javascript": { "formatter": { "quoteStyle": "double" } },
"overrides": [
{
"includes": ["**/*.{js,jsx}"],
"linter": {
"rules": {
"complexity": {
"noAdjacentSpacesInRegex": "error",
"noExtraBooleanCast": "error",
"noUselessCatch": "error",
"noUselessEscapeInRegex": "error"
},
"correctness": {
"noConstAssign": "error",
"noConstantCondition": "error",
"noEmptyCharacterClassInRegex": "error",
"noEmptyPattern": "error",
"noGlobalObjectCalls": "error",
"noInvalidBuiltinInstantiation": "error",
"noInvalidConstructorSuper": "error",
"noNonoctalDecimalEscape": "error",
"noPrecisionLoss": "error",
"noSelfAssign": "error",
"noSetterReturn": "error",
"noSwitchDeclarations": "error",
"noUndeclaredVariables": "error",
"noUnreachable": "error",
"noUnreachableSuper": "error",
"noUnsafeFinally": "error",
"noUnsafeOptionalChaining": "error",
"noUnusedLabels": "error",
"noUnusedPrivateClassMembers": "error",
"noUnusedVariables": "error",
"useIsNan": "error",
"useValidForDirection": "error",
"useValidTypeof": "error",
"useYield": "error"
},
"suspicious": {
"noAsyncPromiseExecutor": "error",
"noCatchAssign": "error",
"noClassAssign": "error",
"noCompareNegZero": "error",
"noConstantBinaryExpressions": "error",
"noControlCharactersInRegex": "error",
"noDebugger": "error",
"noDuplicateCase": "error",
"noDuplicateClassMembers": "error",
"noDuplicateElseIf": "error",
"noDuplicateObjectKeys": "error",
"noDuplicateParameters": "error",
"noEmptyBlockStatements": "error",
"noFallthroughSwitchClause": "error",
"noFunctionAssign": "error",
"noGlobalAssign": "error",
"noImportAssign": "error",
"noIrregularWhitespace": "error",
"noMisleadingCharacterClass": "error",
"noPrototypeBuiltins": "error",
"noRedeclare": "error",
"noShadowRestrictedNames": "error",
"noSparseArray": "error",
"noUnsafeNegation": "error",
"noUselessRegexBackrefs": "error",
"noWith": "error",
"useGetterReturn": "error"
}
}
}
},
{
"includes": ["**/*.{js,jsx}"],
"linter": {
"rules": {
"correctness": {
"useExhaustiveDependencies": "warn",
"useHookAtTopLevel": "error"
}
}
}
},
{ "includes": ["**/*.{js,jsx}"], "linter": { "rules": {} } },
{
"includes": ["**/*.{js,jsx}"],
"javascript": {
"globals": [
"onanimationend",
"ongamepadconnected",
"onlostpointercapture",
"onanimationiteration",
"onkeyup",
"onmousedown",
"onanimationstart",
"onslotchange",
"onprogress",
"ontransitionstart",
"onpause",
"onended",
"onpointerover",
"onscrollend",
"onformdata",
"ontransitionrun",
"onanimationcancel",
"ondrag",
"onchange",
"onbeforeinstallprompt",
"onbeforexrselect",
"onmessage",
"ontransitioncancel",
"onpointerdown",
"onabort",
"onpointerout",
"oncuechange",
"ongotpointercapture",
"onscrollsnapchanging",
"onsearch",
"onsubmit",
"onstalled",
"onsuspend",
"onreset",
"onerror",
"onmouseenter",
"ongamepaddisconnected",
"onresize",
"ondragover",
"onbeforetoggle",
"onmouseover",
"onpagehide",
"onmousemove",
"onratechange",
"oncommand",
"onmessageerror",
"onwheel",
"ondevicemotion",
"onauxclick",
"ontransitionend",
"onpaste",
"onpageswap",
"ononline",
"ondeviceorientationabsolute",
"onkeydown",
"onclose",
"onselect",
"onpageshow",
"onpointercancel",
"onbeforematch",
"onpointerrawupdate",
"ondragleave",
"onscrollsnapchange",
"onseeked",
"onwaiting",
"onbeforeunload",
"onplaying",
"onvolumechange",
"ondragend",
"onstorage",
"onloadeddata",
"onfocus",
"onoffline",
"onplay",
"onafterprint",
"onclick",
"oncut",
"onmouseout",
"ondblclick",
"oncanplay",
"onloadstart",
"onappinstalled",
"onpointermove",
"ontoggle",
"oncontextmenu",
"onblur",
"oncancel",
"onbeforeprint",
"oncontextrestored",
"onloadedmetadata",
"onpointerup",
"onlanguagechange",
"oncopy",
"onselectstart",
"onscroll",
"onload",
"ondragstart",
"onbeforeinput",
"oncanplaythrough",
"oninput",
"oninvalid",
"ontimeupdate",
"ondurationchange",
"onselectionchange",
"onmouseup",
"location",
"onkeypress",
"onpointerleave",
"oncontextlost",
"ondrop",
"onsecuritypolicyviolation",
"oncontentvisibilityautostatechange",
"ondeviceorientation",
"onseeking",
"onrejectionhandled",
"onunload",
"onmouseleave",
"onhashchange",
"onpointerenter",
"onmousewheel",
"onunhandledrejection",
"ondragenter",
"onpopstate",
"onpagereveal",
"onemptied"
]
},
"linter": { "rules": { "correctness": { "noUnusedVariables": "error" } } }
}
],
"assist": {
"enabled": true,
"actions": { "source": { "organizeImports": "on" } }
}
}

View file

@ -1,12 +1,15 @@
mise-task task:
mise r {{task}}
mise r {{ task }}
setup:
mise x -- aube i
cd backend && npm i
mise x -- aube i
cd backend && npm i
start-backend:
cd backend && npm start
cd backend && npm start
start-frontend:
mise r dev
mise r dev
start:
@mprocs

View file

@ -2,10 +2,10 @@
aube = "latest"
mprocs = "latest"
oxlint = "latest"
pitchfork = "latest"
pnpm = "latest"
prek = "latest"
oxfmt = "latest"
node = "24"
[tasks.dev]
description = "Arranca el servidor dev"

View file

@ -1,33 +1,35 @@
{
"name": "desafio",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"lint": "biome check",
"preview": "vite preview"
},
"dependencies": {
"radashi": "^12.7.2",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"react-router-dom": "^7.14.2"
},
"devDependencies": {
"@commitlint/cli": "^20.5.0",
"@commitlint/config-conventional": "^20.5.0",
"@eslint/js": "^9.39.4",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"@unocss/reset": "^66.6.7",
"@vitejs/plugin-react": "^6.0.1",
"eslint": "^9.39.4",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.5.2",
"globals": "^17.4.0",
"unocss": "^66.6.7",
"vite": "^8.0.1"
}
"name": "desafio",
"version": "0.0.0",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"lint": "biome check",
"preview": "vite preview"
},
"dependencies": {
"@tanstack/react-form": "^1.32.0",
"radashi": "^12.7.2",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"react-router-dom": "^7.14.2",
"zod": "^4.4.3"
},
"devDependencies": {
"@commitlint/cli": "^20.5.0",
"@commitlint/config-conventional": "^20.5.0",
"@eslint/js": "^9.39.4",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"@unocss/reset": "^66.6.7",
"@vitejs/plugin-react": "^6.0.1",
"eslint": "^9.39.4",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.5.2",
"globals": "^17.4.0",
"unocss": "^66.6.7",
"vite": "^8.0.1"
}
}

View file

@ -5,12 +5,23 @@ settings:
excludeLinksFromLockfile: false
time:
'@tanstack/devtools-event-client@0.4.3': 2026-03-11T13:42:37.747Z
'@tanstack/form-core@1.32.0': 2026-05-10T22:35:01.878Z
'@tanstack/pacer-lite@0.1.1': 2025-12-06T05:29:43.362Z
'@tanstack/react-form@1.32.0': 2026-05-10T22:35:03.027Z
'@tanstack/react-store@0.9.3': 2026-03-25T17:43:08.336Z
'@tanstack/store@0.9.3': 2026-03-25T17:43:08.346Z
radashi@12.7.2: 2026-02-24T20:32:30.146Z
use-sync-external-store@1.6.0: 2025-10-01T21:39:12.499Z
zod@4.4.3: 2026-05-04T07:06:40.819Z
importers:
.:
dependencies:
'@tanstack/react-form':
specifier: ^1.32.0
version: 1.32.0(react@19.2.4)
'@types/node':
specifier: ^20.19.0 || >=22.12.0
version: 25.6.0
@ -29,6 +40,9 @@ importers:
react-router-dom:
specifier: ^7.14.2
version: 7.14.2(react@19.2.4)(react-dom@19.2.4(react@19.2.4))
zod:
specifier: ^4.4.3
version: 4.4.3
devDependencies:
'@commitlint/cli':
specifier: ^20.5.0
@ -299,34 +313,6 @@ packages:
'@jridgewell/trace-mapping@0.3.31':
resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
'@oxc-parser/binding-darwin-arm64@0.115.0':
resolution: {integrity: sha512-ii/oOZjfGY1aszXTy29Z5DRyCEnBOrAXDVCvfdfXFQsOZlbbOa7NMHD7D+06YFe5qdxfmbWAYv4yn6QJi/0d2g==}
engines: {node: ^20.19.0 || >=22.12.0}
os:
- darwin
cpu:
- arm64
'@oxc-parser/binding-linux-arm64-gnu@0.115.0':
resolution: {integrity: sha512-1ej/MjuTY9tJEunU/hUPIFmgH5PqgMQoRjNOvOkibtJ3Zqlw/+Lc+HGHDNET8sjbgIkWzdhX+p4J96A5CPdbag==}
engines: {node: ^20.19.0 || >=22.12.0}
os:
- linux
cpu:
- arm64
libc:
- glibc
'@oxc-parser/binding-linux-arm64-musl@0.115.0':
resolution: {integrity: sha512-HjsZbJPH9mMd4swJRywVMsDZsJX0hyKb1iNHo5ijRl5yhtbO3lj7ImSrrL1oZ1VEg0te4iKmDGGz/6YPLd1G8w==}
engines: {node: ^20.19.0 || >=22.12.0}
os:
- linux
cpu:
- arm64
libc:
- musl
'@oxc-parser/binding-linux-x64-gnu@0.115.0':
resolution: {integrity: sha512-9iVX789DoC3SaOOG+X6NcF/tVChgLp2vcHffzOC2/Z1JTPlz6bMG2ogvcW6/9s0BG2qvhNQImd+gbWYeQbOwVw==}
engines: {node: ^20.19.0 || >=22.12.0}
@ -337,32 +323,6 @@ packages:
libc:
- glibc
'@oxc-parser/binding-linux-x64-musl@0.115.0':
resolution: {integrity: sha512-RmQmk+mjCB0nMNfEYhaCxwofLo1Z95ebHw1AGvRiWGCd4zhCNOyskgCbMogIcQzSB3SuEKWgkssyaiQYVAA4hQ==}
engines: {node: ^20.19.0 || >=22.12.0}
os:
- linux
cpu:
- x64
libc:
- musl
'@oxc-parser/binding-win32-arm64-msvc@0.115.0':
resolution: {integrity: sha512-/ym+Absk/TLFvbhh3se9XYuI1D7BrUVHw4RaG/2dmWKgBenrZHaJsgnRb7NJtaOyjEOLIPtULx1wDdVL0SX2eg==}
engines: {node: ^20.19.0 || >=22.12.0}
os:
- win32
cpu:
- arm64
'@oxc-parser/binding-win32-x64-msvc@0.115.0':
resolution: {integrity: sha512-oxUl82N+fIO9jIaXPph8SPPHQXrA08BHokBBJW8ct9F/x6o6bZE6eUAhUtWajbtvFhL8UYcCWRMba+kww6MBlA==}
engines: {node: ^20.19.0 || >=22.12.0}
os:
- win32
cpu:
- x64
'@oxc-project/types@0.115.0':
resolution: {integrity: sha512-4n91DKnebUS4yjUHl2g3/b2T+IUdCfmoZGhmwsovZCDaJSs+QkVAM+0AqqTxHSsHfeiMuueT75cZaZcT/m0pSw==}
@ -375,34 +335,6 @@ packages:
'@quansync/fs@1.0.0':
resolution: {integrity: sha512-4TJ3DFtlf1L5LDMaM6CanJ/0lckGNtJcMjQ1NAV6zDmA0tEHKZtxNKin8EgPaVX1YzljbxckyT2tJrpQKAtngQ==}
'@rolldown/binding-darwin-arm64@1.0.0-rc.11':
resolution: {integrity: sha512-7WQgR8SfOPwmDZGFkThUvsmd/nwAWv91oCO4I5LS7RKrssPZmOt7jONN0cW17ydGC1n/+puol1IpoieKqQidmg==}
engines: {node: ^20.19.0 || >=22.12.0}
os:
- darwin
cpu:
- arm64
'@rolldown/binding-linux-arm64-gnu@1.0.0-rc.11':
resolution: {integrity: sha512-WMW1yE6IOnehTcFE9eipFkm3XN63zypWlrJQ2iF7NrQ9b2LDRjumFoOGJE8RJJTJCTBAdmLMnJ8uVitACUUo1Q==}
engines: {node: ^20.19.0 || >=22.12.0}
os:
- linux
cpu:
- arm64
libc:
- glibc
'@rolldown/binding-linux-arm64-musl@1.0.0-rc.11':
resolution: {integrity: sha512-jfndI9tsfm4APzjNt6QdBkYwre5lRPUgHeDHoI7ydKUuJvz3lZeCfMsI56BZj+7BYqiKsJm7cfd/6KYV7ubrBg==}
engines: {node: ^20.19.0 || >=22.12.0}
os:
- linux
cpu:
- arm64
libc:
- musl
'@rolldown/binding-linux-x64-gnu@1.0.0-rc.11':
resolution: {integrity: sha512-qXBQQO9OvkjjQPLdUVr7Nr2t3QTZI7s4KZtfw7HzBgjbmAPSFwSv4rmET9lLSgq3rH/ndA3ngv3Qb8l2njoPNA==}
engines: {node: ^20.19.0 || >=22.12.0}
@ -413,32 +345,6 @@ packages:
libc:
- glibc
'@rolldown/binding-linux-x64-musl@1.0.0-rc.11':
resolution: {integrity: sha512-/tpFfoSTzUkH9LPY+cYbqZBDyyX62w5fICq9qzsHLL8uTI6BHip3Q9Uzft0wylk/i8OOwKik8OxW+QAhDmzwmg==}
engines: {node: ^20.19.0 || >=22.12.0}
os:
- linux
cpu:
- x64
libc:
- musl
'@rolldown/binding-win32-arm64-msvc@1.0.0-rc.11':
resolution: {integrity: sha512-dDwf5otnx0XgRY1yqxOC4ITizcdzS/8cQ3goOWv3jFAo4F+xQYni+hnMuO6+LssHHdJW7+OCVL3CoU4ycnh35Q==}
engines: {node: ^20.19.0 || >=22.12.0}
os:
- win32
cpu:
- arm64
'@rolldown/binding-win32-x64-msvc@1.0.0-rc.11':
resolution: {integrity: sha512-LN4/skhSggybX71ews7dAj6r2geaMJfm3kMbK2KhFMg9B10AZXnKoLCVVgzhMHL0S+aKtr4p8QbAW8k+w95bAA==}
engines: {node: ^20.19.0 || >=22.12.0}
os:
- win32
cpu:
- x64
'@rolldown/pluginutils@1.0.0-rc.11':
resolution: {integrity: sha512-xQO9vbwBecJRv9EUcQ/y0dzSTJgA7Q6UVN7xp6B81+tBGSLVAK03yJ9NkJaUA7JFD91kbjxRSC/mDnmvXzbHoQ==}
@ -453,6 +359,35 @@ packages:
resolution: {integrity: sha512-KxXvfapcixpz6rVEB6HPjOUZT22yN6v0vI0urQSk1L8MlEWPDFCZkhw2xmkyoTGYeFw7tWTZd7e3lVzRZRN/EA==}
engines: {node: '>=18'}
'@tanstack/devtools-event-client@0.4.3':
resolution: {integrity: sha512-OZI6QyULw0FI0wjgmeYzCIfbgPsOEzwJtCpa69XrfLMtNXLGnz3d/dIabk7frg0TmHo+Ah49w5I4KC7Tufwsvw==}
engines: {node: '>=18'}
hasBin: true
'@tanstack/form-core@1.32.0':
resolution: {integrity: sha512-Tn5VRDSjyqjmaet2tJMuEWDRFyrCaon03vxXPlSSaiSs6C/N7lCIwGCXJbZXEUq1kTj8jYN9qyXHbsz4LQHcow==}
'@tanstack/pacer-lite@0.1.1':
resolution: {integrity: sha512-y/xtNPNt/YeyoVxE/JCx+T7yjEzpezmbb+toK8DDD1P4m7Kzs5YR956+7OKexG3f8aXgC3rLZl7b1V+yNUSy5w==}
engines: {node: '>=18'}
'@tanstack/react-form@1.32.0':
resolution: {integrity: sha512-6WP5SQTA6/H9crCpvpq3ZppYWqtrdE5NjOy6ebABi6uAQPqhfTzrdjS9t40mCZCFtGI5585OhJV6zBP/KN2zcw==}
peerDependencies:
react: ^17.0.0 || ^18.0.0 || ^19.0.0
peerDependenciesMeta:
'@tanstack/react-start':
optional: true
'@tanstack/react-store@0.9.3':
resolution: {integrity: sha512-y2iHd/N9OkoQbFJLUX1T9vbc2O9tjH0pQRgTcx1/Nz4IlwLvkgpuglXUx+mXt0g5ZDFrEeDnONPqkbfxXJKwRg==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
'@tanstack/store@0.9.3':
resolution: {integrity: sha512-8reSzl/qGWGGVKhBoxXPMWzATSbZLZFWhwBAFO9NAyp0TxzfBP0mIrGb8CP8KrQTmvzXlR/vFPPUrHTLBGyFyw==}
'@types/estree@1.0.8':
resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
@ -837,12 +772,6 @@ packages:
flatted@3.4.2:
resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==}
fsevents@2.3.3:
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
os:
- darwin
gensync@1.0.0-beta.2:
resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
engines: {node: '>=6.9.0'}
@ -974,34 +903,6 @@ packages:
resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
engines: {node: '>= 0.8.0'}
lightningcss-darwin-arm64@1.32.0:
resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==}
engines: {node: '>= 12.0.0'}
os:
- darwin
cpu:
- arm64
lightningcss-linux-arm64-gnu@1.32.0:
resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==}
engines: {node: '>= 12.0.0'}
os:
- linux
cpu:
- arm64
libc:
- glibc
lightningcss-linux-arm64-musl@1.32.0:
resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==}
engines: {node: '>= 12.0.0'}
os:
- linux
cpu:
- arm64
libc:
- musl
lightningcss-linux-x64-gnu@1.32.0:
resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==}
engines: {node: '>= 12.0.0'}
@ -1012,32 +913,6 @@ packages:
libc:
- glibc
lightningcss-linux-x64-musl@1.32.0:
resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==}
engines: {node: '>= 12.0.0'}
os:
- linux
cpu:
- x64
libc:
- musl
lightningcss-win32-arm64-msvc@1.32.0:
resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==}
engines: {node: '>= 12.0.0'}
os:
- win32
cpu:
- arm64
lightningcss-win32-x64-msvc@1.32.0:
resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==}
engines: {node: '>= 12.0.0'}
os:
- win32
cpu:
- x64
lightningcss@1.32.0:
resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==}
engines: {node: '>= 12.0.0'}
@ -1364,6 +1239,11 @@ packages:
uri-js@4.4.1:
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
use-sync-external-store@1.6.0:
resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
vite@8.0.2:
resolution: {integrity: sha512-1gFhNi+bHhRE/qKZOJXACm6tX4bA3Isy9KuKF15AgSRuRazNBOJfdDemPBU16/mpMxApDPrWvZ08DcLPEoRnuA==}
engines: {node: ^20.19.0 || >=22.12.0}
@ -1451,6 +1331,9 @@ packages:
zod@4.3.6:
resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==}
zod@4.4.3:
resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==}
snapshots:
'@antfu/install-pkg@1.1.0':
@ -1764,27 +1647,9 @@ snapshots:
'@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.5.5
'@oxc-parser/binding-darwin-arm64@0.115.0':
optional: true
'@oxc-parser/binding-linux-arm64-gnu@0.115.0':
optional: true
'@oxc-parser/binding-linux-arm64-musl@0.115.0':
optional: true
'@oxc-parser/binding-linux-x64-gnu@0.115.0':
optional: true
'@oxc-parser/binding-linux-x64-musl@0.115.0':
optional: true
'@oxc-parser/binding-win32-arm64-msvc@0.115.0':
optional: true
'@oxc-parser/binding-win32-x64-msvc@0.115.0':
optional: true
'@oxc-project/types@0.115.0': {}
'@oxc-project/types@0.122.0': {}
@ -1795,27 +1660,9 @@ snapshots:
dependencies:
quansync: 1.0.0
'@rolldown/binding-darwin-arm64@1.0.0-rc.11':
optional: true
'@rolldown/binding-linux-arm64-gnu@1.0.0-rc.11':
optional: true
'@rolldown/binding-linux-arm64-musl@1.0.0-rc.11':
optional: true
'@rolldown/binding-linux-x64-gnu@1.0.0-rc.11':
optional: true
'@rolldown/binding-linux-x64-musl@1.0.0-rc.11':
optional: true
'@rolldown/binding-win32-arm64-msvc@1.0.0-rc.11':
optional: true
'@rolldown/binding-win32-x64-msvc@1.0.0-rc.11':
optional: true
'@rolldown/pluginutils@1.0.0-rc.11': {}
'@rolldown/pluginutils@1.0.0-rc.7': {}
@ -1826,6 +1673,31 @@ snapshots:
'@simple-libs/stream-utils@1.2.0': {}
'@tanstack/devtools-event-client@0.4.3': {}
'@tanstack/form-core@1.32.0':
dependencies:
'@tanstack/devtools-event-client': 0.4.3
'@tanstack/pacer-lite': 0.1.1
'@tanstack/store': 0.9.3
'@tanstack/pacer-lite@0.1.1': {}
'@tanstack/react-form@1.32.0(react@19.2.4)':
dependencies:
'@tanstack/form-core': 1.32.0
'@tanstack/react-store': 0.9.3(react@19.2.4)(react-dom@19.2.4(react@19.2.4))
react: 19.2.4
'@tanstack/react-store@0.9.3(react@19.2.4)(react-dom@19.2.4(react@19.2.4))':
dependencies:
'@tanstack/store': 0.9.3
react: 19.2.4
react-dom: 19.2.4(react@19.2.4)
use-sync-external-store: 1.6.0(react@19.2.4)
'@tanstack/store@0.9.3': {}
'@types/estree@1.0.8': {}
'@types/json-schema@7.0.15': {}
@ -2259,9 +2131,6 @@ snapshots:
flatted@3.4.2: {}
fsevents@2.3.3:
optional: true
gensync@1.0.0-beta.2: {}
get-caller-file@2.0.5: {}
@ -2358,38 +2227,14 @@ snapshots:
prelude-ls: 1.2.1
type-check: 0.4.0
lightningcss-darwin-arm64@1.32.0:
optional: true
lightningcss-linux-arm64-gnu@1.32.0:
optional: true
lightningcss-linux-arm64-musl@1.32.0:
optional: true
lightningcss-linux-x64-gnu@1.32.0:
optional: true
lightningcss-linux-x64-musl@1.32.0:
optional: true
lightningcss-win32-arm64-msvc@1.32.0:
optional: true
lightningcss-win32-x64-msvc@1.32.0:
optional: true
lightningcss@1.32.0:
dependencies:
detect-libc: 2.1.2
optionalDependencies:
lightningcss-darwin-arm64: 1.32.0
lightningcss-linux-arm64-gnu: 1.32.0
lightningcss-linux-arm64-musl: 1.32.0
lightningcss-linux-x64-gnu: 1.32.0
lightningcss-linux-x64-musl: 1.32.0
lightningcss-win32-arm64-msvc: 1.32.0
lightningcss-win32-x64-msvc: 1.32.0
lines-and-columns@1.2.4: {}
@ -2477,13 +2322,7 @@ snapshots:
dependencies:
'@oxc-project/types': 0.115.0
optionalDependencies:
'@oxc-parser/binding-darwin-arm64': 0.115.0
'@oxc-parser/binding-linux-arm64-gnu': 0.115.0
'@oxc-parser/binding-linux-arm64-musl': 0.115.0
'@oxc-parser/binding-linux-x64-gnu': 0.115.0
'@oxc-parser/binding-linux-x64-musl': 0.115.0
'@oxc-parser/binding-win32-arm64-msvc': 0.115.0
'@oxc-parser/binding-win32-x64-msvc': 0.115.0
oxc-walker@0.7.0(oxc-parser@0.115.0):
dependencies:
@ -2581,13 +2420,7 @@ snapshots:
'@oxc-project/types': 0.122.0
'@rolldown/pluginutils': 1.0.0-rc.11
optionalDependencies:
'@rolldown/binding-darwin-arm64': 1.0.0-rc.11
'@rolldown/binding-linux-arm64-gnu': 1.0.0-rc.11
'@rolldown/binding-linux-arm64-musl': 1.0.0-rc.11
'@rolldown/binding-linux-x64-gnu': 1.0.0-rc.11
'@rolldown/binding-linux-x64-musl': 1.0.0-rc.11
'@rolldown/binding-win32-arm64-msvc': 1.0.0-rc.11
'@rolldown/binding-win32-x64-msvc': 1.0.0-rc.11
scheduler@0.27.0: {}
@ -2705,6 +2538,10 @@ snapshots:
dependencies:
punycode: 2.3.1
use-sync-external-store@1.6.0(react@19.2.4):
dependencies:
react: 19.2.4
vite@8.0.2(@types/node@25.6.0)(jiti@2.6.1):
dependencies:
lightningcss: 1.32.0
@ -2714,7 +2551,6 @@ snapshots:
tinyglobby: 0.2.15
optionalDependencies:
'@types/node': 25.6.0
fsevents: 2.3.3
jiti: 2.6.1
webpack-virtual-modules@0.6.2: {}
@ -2754,3 +2590,5 @@ snapshots:
zod: 4.3.6
zod@4.3.6: {}
zod@4.4.3: {}

View file

@ -1,7 +1,7 @@
[[repos]]
hooks = [
{ id = "commitlint", name = "commitlint", language = "system", entry = "mise x -- aubx commitlint -e", stages = [
"commit-msg",
] },
{ id = "commitlint", name = "commitlint", language = "system", entry = "mise x -- aubx commitlint -e", stages = [
"commit-msg",
] },
]
repo = "local"

View file

@ -1,65 +1,89 @@
import { useContext, useState } from "react";
import { CartContext } from "../context/CartContext";
import { pizzaCart } from "../pizzas";
import { UserContext } from "../context/UserContext";
const Cart = () => {
const { token } = useContext(UserContext);
const { cart, setCart } = useContext(CartContext);
const { cart, setCart, getTotal } = useContext(CartContext);
const [success, setSuccess] = useState(false);
return (
<>
<h1 className="text-4xl font-bold">Carrito</h1>
<div id="cartContainer" className="flex gap-4 flex-col">
{cart.map((pizza) => (
<div
className="border-3 border-lime-300 rounded-md flex gap-4 overflow-hidden"
key={pizza.id}
>
<img src={pizza.img} className="w-32 rounded-r-sm" />
<div>
<h2>Pizza {pizza.name}</h2>
<div className="flex gap-4">
<button
onClick={() => {
setCart((cart) =>
cart.map((p) => (p.id === pizza.id ? { ...p, count: p.count + 1 } : p)),
);
}}
className="bg-blue-500 px-4 text-white rounded-md"
>
+
</button>
<p className="text-grey-500">{pizza.count}</p>
<button
onClick={() => {
setCart((cart) =>
cart
.map((p) => (p.id === pizza.id ? { ...p, count: p.count - 1 } : p))
.filter((p) => p.count > 0),
);
}}
className="bg-red-500 px-4 text-white rounded-md"
>
-
</button>
{!success ? (
<>
<div id="cartContainer" className="flex gap-4 flex-col">
{cart.map((pizza) => (
<div
className="border-3 border-lime-300 rounded-md flex gap-4 overflow-hidden"
key={pizza.id}
>
<img src={pizza.img} className="w-32 rounded-r-sm" />
<div>
<h2>Pizza {pizza.name}</h2>
<div className="flex gap-4">
<button
onClick={() => {
setCart((cart) =>
cart.map((p) =>
p.id === pizza.id
? { ...p, count: p.count + 1 }
: p,
),
);
}}
className="bg-blue-500 px-4 text-white rounded-md"
>
+
</button>
<p className="text-grey-500">{pizza.count}</p>
<button
onClick={() => {
setCart((cart) =>
cart
.map((p) =>
p.id === pizza.id
? { ...p, count: p.count - 1 }
: p,
)
.filter((p) => p.count > 0),
);
}}
className="bg-red-500 px-4 text-white rounded-md"
>
-
</button>
</div>
</div>
</div>
</div>
))}
<p className="text-3xl">
Total:
<strong>{` $${getTotal().toLocaleString("es-CL")}`}</strong>
</p>
</div>
))}
<p className="text-3xl">
Total:
<strong>
{` $${cart.reduce((acc, it) => acc + it.price * it.count, 0).toLocaleString("es-CL")}`}
</strong>
</p>
</div>
<button
type="submit"
disabled={!token}
className="bg-black text-white rounded-md p-2 text-lg hover:bg-gray-500 disabled:bg-gray-200"
>
Pagar
</button>
<button
type="button"
disabled={!token}
className="bg-black text-white rounded-md p-2 text-lg hover:bg-gray-500 disabled:bg-gray-200"
onClick={async () => {
const res = await fetch("http://localhost:5000/api/checkouts", {
method: "POST",
headers: { Authorization: `Bearer ${token}` },
body: JSON.stringify({ cart }),
});
if (!res.ok) {
console.error(res);
return;
}
setSuccess(true);
}}
>
Pagar
</button>
</>
) : (
<p class="text-4xl text-teal-500">¡Compra hecha con éxito!</p>
)}
</>
);
};