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

@ -46,9 +46,11 @@ body:
### Checkout & Profile ### 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). 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. 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 ```sh
POST /api/checkouts POST /api/checkouts
@ -62,7 +64,8 @@ body:
} }
``` ```
Endpoint para obtener el perfil del usuario autenticado. Necesitas enviar el token JWT en el header. Endpoint para obtener el perfil del usuario autenticado. Necesitas enviar el
token JWT en el header.
```sh ```sh
GET /api/auth/me GET /api/auth/me
@ -70,7 +73,8 @@ GET /api/auth/me
## JWT ## 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. 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: El token JWT se debe enviar en el header `Authorization` de la siguiente manera:

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-task task:
mise r {{task}} mise r {{ task }}
setup: setup:
mise x -- aube i mise x -- aube i
cd backend && npm i cd backend && npm i
start-backend: start-backend:
cd backend && npm start cd backend && npm start
start-frontend: start-frontend:
mise r dev mise r dev
start:
@mprocs

View file

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

View file

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

View file

@ -5,12 +5,23 @@ settings:
excludeLinksFromLockfile: false excludeLinksFromLockfile: false
time: 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 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: importers:
.: .:
dependencies: dependencies:
'@tanstack/react-form':
specifier: ^1.32.0
version: 1.32.0(react@19.2.4)
'@types/node': '@types/node':
specifier: ^20.19.0 || >=22.12.0 specifier: ^20.19.0 || >=22.12.0
version: 25.6.0 version: 25.6.0
@ -29,6 +40,9 @@ importers:
react-router-dom: react-router-dom:
specifier: ^7.14.2 specifier: ^7.14.2
version: 7.14.2(react@19.2.4)(react-dom@19.2.4(react@19.2.4)) 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: devDependencies:
'@commitlint/cli': '@commitlint/cli':
specifier: ^20.5.0 specifier: ^20.5.0
@ -299,34 +313,6 @@ packages:
'@jridgewell/trace-mapping@0.3.31': '@jridgewell/trace-mapping@0.3.31':
resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} 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': '@oxc-parser/binding-linux-x64-gnu@0.115.0':
resolution: {integrity: sha512-9iVX789DoC3SaOOG+X6NcF/tVChgLp2vcHffzOC2/Z1JTPlz6bMG2ogvcW6/9s0BG2qvhNQImd+gbWYeQbOwVw==} resolution: {integrity: sha512-9iVX789DoC3SaOOG+X6NcF/tVChgLp2vcHffzOC2/Z1JTPlz6bMG2ogvcW6/9s0BG2qvhNQImd+gbWYeQbOwVw==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
@ -337,32 +323,6 @@ packages:
libc: libc:
- glibc - 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': '@oxc-project/types@0.115.0':
resolution: {integrity: sha512-4n91DKnebUS4yjUHl2g3/b2T+IUdCfmoZGhmwsovZCDaJSs+QkVAM+0AqqTxHSsHfeiMuueT75cZaZcT/m0pSw==} resolution: {integrity: sha512-4n91DKnebUS4yjUHl2g3/b2T+IUdCfmoZGhmwsovZCDaJSs+QkVAM+0AqqTxHSsHfeiMuueT75cZaZcT/m0pSw==}
@ -375,34 +335,6 @@ packages:
'@quansync/fs@1.0.0': '@quansync/fs@1.0.0':
resolution: {integrity: sha512-4TJ3DFtlf1L5LDMaM6CanJ/0lckGNtJcMjQ1NAV6zDmA0tEHKZtxNKin8EgPaVX1YzljbxckyT2tJrpQKAtngQ==} 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': '@rolldown/binding-linux-x64-gnu@1.0.0-rc.11':
resolution: {integrity: sha512-qXBQQO9OvkjjQPLdUVr7Nr2t3QTZI7s4KZtfw7HzBgjbmAPSFwSv4rmET9lLSgq3rH/ndA3ngv3Qb8l2njoPNA==} resolution: {integrity: sha512-qXBQQO9OvkjjQPLdUVr7Nr2t3QTZI7s4KZtfw7HzBgjbmAPSFwSv4rmET9lLSgq3rH/ndA3ngv3Qb8l2njoPNA==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
@ -413,32 +345,6 @@ packages:
libc: libc:
- glibc - 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': '@rolldown/pluginutils@1.0.0-rc.11':
resolution: {integrity: sha512-xQO9vbwBecJRv9EUcQ/y0dzSTJgA7Q6UVN7xp6B81+tBGSLVAK03yJ9NkJaUA7JFD91kbjxRSC/mDnmvXzbHoQ==} resolution: {integrity: sha512-xQO9vbwBecJRv9EUcQ/y0dzSTJgA7Q6UVN7xp6B81+tBGSLVAK03yJ9NkJaUA7JFD91kbjxRSC/mDnmvXzbHoQ==}
@ -453,6 +359,35 @@ packages:
resolution: {integrity: sha512-KxXvfapcixpz6rVEB6HPjOUZT22yN6v0vI0urQSk1L8MlEWPDFCZkhw2xmkyoTGYeFw7tWTZd7e3lVzRZRN/EA==} resolution: {integrity: sha512-KxXvfapcixpz6rVEB6HPjOUZT22yN6v0vI0urQSk1L8MlEWPDFCZkhw2xmkyoTGYeFw7tWTZd7e3lVzRZRN/EA==}
engines: {node: '>=18'} 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': '@types/estree@1.0.8':
resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
@ -837,12 +772,6 @@ packages:
flatted@3.4.2: flatted@3.4.2:
resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==} 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: gensync@1.0.0-beta.2:
resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
@ -974,34 +903,6 @@ packages:
resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
engines: {node: '>= 0.8.0'} 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: lightningcss-linux-x64-gnu@1.32.0:
resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==}
engines: {node: '>= 12.0.0'} engines: {node: '>= 12.0.0'}
@ -1012,32 +913,6 @@ packages:
libc: libc:
- glibc - 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: lightningcss@1.32.0:
resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==}
engines: {node: '>= 12.0.0'} engines: {node: '>= 12.0.0'}
@ -1364,6 +1239,11 @@ packages:
uri-js@4.4.1: uri-js@4.4.1:
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} 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: vite@8.0.2:
resolution: {integrity: sha512-1gFhNi+bHhRE/qKZOJXACm6tX4bA3Isy9KuKF15AgSRuRazNBOJfdDemPBU16/mpMxApDPrWvZ08DcLPEoRnuA==} resolution: {integrity: sha512-1gFhNi+bHhRE/qKZOJXACm6tX4bA3Isy9KuKF15AgSRuRazNBOJfdDemPBU16/mpMxApDPrWvZ08DcLPEoRnuA==}
engines: {node: ^20.19.0 || >=22.12.0} engines: {node: ^20.19.0 || >=22.12.0}
@ -1451,6 +1331,9 @@ packages:
zod@4.3.6: zod@4.3.6:
resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==} resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==}
zod@4.4.3:
resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==}
snapshots: snapshots:
'@antfu/install-pkg@1.1.0': '@antfu/install-pkg@1.1.0':
@ -1764,27 +1647,9 @@ snapshots:
'@jridgewell/resolve-uri': 3.1.2 '@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.5.5 '@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': '@oxc-parser/binding-linux-x64-gnu@0.115.0':
optional: true 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.115.0': {}
'@oxc-project/types@0.122.0': {} '@oxc-project/types@0.122.0': {}
@ -1795,27 +1660,9 @@ snapshots:
dependencies: dependencies:
quansync: 1.0.0 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': '@rolldown/binding-linux-x64-gnu@1.0.0-rc.11':
optional: true 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.11': {}
'@rolldown/pluginutils@1.0.0-rc.7': {} '@rolldown/pluginutils@1.0.0-rc.7': {}
@ -1826,6 +1673,31 @@ snapshots:
'@simple-libs/stream-utils@1.2.0': {} '@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/estree@1.0.8': {}
'@types/json-schema@7.0.15': {} '@types/json-schema@7.0.15': {}
@ -2259,9 +2131,6 @@ snapshots:
flatted@3.4.2: {} flatted@3.4.2: {}
fsevents@2.3.3:
optional: true
gensync@1.0.0-beta.2: {} gensync@1.0.0-beta.2: {}
get-caller-file@2.0.5: {} get-caller-file@2.0.5: {}
@ -2358,38 +2227,14 @@ snapshots:
prelude-ls: 1.2.1 prelude-ls: 1.2.1
type-check: 0.4.0 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: lightningcss-linux-x64-gnu@1.32.0:
optional: true 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: lightningcss@1.32.0:
dependencies: dependencies:
detect-libc: 2.1.2 detect-libc: 2.1.2
optionalDependencies: 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-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: {} lines-and-columns@1.2.4: {}
@ -2477,13 +2322,7 @@ snapshots:
dependencies: dependencies:
'@oxc-project/types': 0.115.0 '@oxc-project/types': 0.115.0
optionalDependencies: 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-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): oxc-walker@0.7.0(oxc-parser@0.115.0):
dependencies: dependencies:
@ -2581,13 +2420,7 @@ snapshots:
'@oxc-project/types': 0.122.0 '@oxc-project/types': 0.122.0
'@rolldown/pluginutils': 1.0.0-rc.11 '@rolldown/pluginutils': 1.0.0-rc.11
optionalDependencies: 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-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: {} scheduler@0.27.0: {}
@ -2705,6 +2538,10 @@ snapshots:
dependencies: dependencies:
punycode: 2.3.1 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): vite@8.0.2(@types/node@25.6.0)(jiti@2.6.1):
dependencies: dependencies:
lightningcss: 1.32.0 lightningcss: 1.32.0
@ -2714,7 +2551,6 @@ snapshots:
tinyglobby: 0.2.15 tinyglobby: 0.2.15
optionalDependencies: optionalDependencies:
'@types/node': 25.6.0 '@types/node': 25.6.0
fsevents: 2.3.3
jiti: 2.6.1 jiti: 2.6.1
webpack-virtual-modules@0.6.2: {} webpack-virtual-modules@0.6.2: {}
@ -2754,3 +2590,5 @@ snapshots:
zod: 4.3.6 zod: 4.3.6
zod@4.3.6: {} zod@4.3.6: {}
zod@4.4.3: {}

View file

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

View file

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