diff --git a/next.config.mjs b/next.config.mjs
index 4678774..21a6b26 100644
--- a/next.config.mjs
+++ b/next.config.mjs
@@ -1,4 +1,12 @@
/** @type {import('next').NextConfig} */
-const nextConfig = {};
+const nextConfig = {
+ images: {
+ remotePatterns: [
+ {
+ hostname: "images.unsplash.com",
+ },
+ ],
+ },
+};
export default nextConfig;
diff --git a/package-lock.json b/package-lock.json
index 9d86004..d743d44 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -16,11 +16,15 @@
"@radix-ui/react-popover": "^1.1.1",
"@radix-ui/react-slot": "^1.1.0",
"@radix-ui/react-switch": "^1.1.0",
+ "@radix-ui/react-tabs": "^1.1.1",
"@radix-ui/react-tooltip": "^1.1.2",
- "@requestnetwork/payment-widget": "^0.3.1",
+ "@requestnetwork/payment-widget": "^0.3.2",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
"cmdk": "^1.0.0",
+ "date-fns": "^4.1.0",
+ "embla-carousel-autoplay": "^8.3.1",
+ "embla-carousel-react": "^8.3.1",
"framer-motion": "^11.3.28",
"lucide-react": "^0.428.0",
"next": "14.2.5",
@@ -30,7 +34,8 @@
"tailwind-merge": "^2.5.2",
"tailwindcss-animate": "^1.0.7",
"validator": "^13.12.0",
- "zod": "^3.23.8"
+ "zod": "^3.23.8",
+ "zustand": "^5.0.1"
},
"devDependencies": {
"@types/node": "^20",
@@ -2091,6 +2096,7 @@
},
"node_modules/@parcel/watcher-wasm/node_modules/napi-wasm": {
"version": "1.1.0",
+ "extraneous": true,
"inBundle": true,
"license": "MIT"
},
@@ -2591,6 +2597,36 @@
}
}
},
+ "node_modules/@radix-ui/react-roving-focus": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.0.tgz",
+ "integrity": "sha512-EA6AMGeq9AEeQDeSH0aZgG198qkfHSbvWTf1HvoDmOB5bBG/qTxjYMWUKMnYiV6J/iP/J8MEFSuB2zRU2n7ODA==",
+ "dependencies": {
+ "@radix-ui/primitive": "1.1.0",
+ "@radix-ui/react-collection": "1.1.0",
+ "@radix-ui/react-compose-refs": "1.1.0",
+ "@radix-ui/react-context": "1.1.0",
+ "@radix-ui/react-direction": "1.1.0",
+ "@radix-ui/react-id": "1.1.0",
+ "@radix-ui/react-primitive": "2.0.0",
+ "@radix-ui/react-use-callback-ref": "1.1.0",
+ "@radix-ui/react-use-controllable-state": "1.1.0"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@radix-ui/react-slot": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz",
@@ -2636,6 +2672,72 @@
}
}
},
+ "node_modules/@radix-ui/react-tabs": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.1.tgz",
+ "integrity": "sha512-3GBUDmP2DvzmtYLMsHmpA1GtR46ZDZ+OreXM/N+kkQJOPIgytFWWTfDQmBQKBvaFS0Vno0FktdbVzN28KGrMdw==",
+ "dependencies": {
+ "@radix-ui/primitive": "1.1.0",
+ "@radix-ui/react-context": "1.1.1",
+ "@radix-ui/react-direction": "1.1.0",
+ "@radix-ui/react-id": "1.1.0",
+ "@radix-ui/react-presence": "1.1.1",
+ "@radix-ui/react-primitive": "2.0.0",
+ "@radix-ui/react-roving-focus": "1.1.0",
+ "@radix-ui/react-use-controllable-state": "1.1.0"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-context": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.1.tgz",
+ "integrity": "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==",
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-presence": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.1.tgz",
+ "integrity": "sha512-IeFXVi4YS1K0wVZzXNrbaaUvIJ3qdY+/Ih4eHFhWA9SwGR9UDX7Ck8abvL57C4cv3wwMvUE0OG69Qc3NCcTe/A==",
+ "dependencies": {
+ "@radix-ui/react-compose-refs": "1.1.0",
+ "@radix-ui/react-use-layout-effect": "1.1.0"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@radix-ui/react-tooltip": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.1.2.tgz",
@@ -2807,13 +2909,13 @@
"integrity": "sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg=="
},
"node_modules/@requestnetwork/advanced-logic": {
- "version": "0.44.1-next.2088",
- "resolved": "https://registry.npmjs.org/@requestnetwork/advanced-logic/-/advanced-logic-0.44.1-next.2088.tgz",
- "integrity": "sha512-scwdrhtsqJEVv5cwzf2swI1gWyu1piA9X8aJtBrWp9UeVoJq68v5pP/Ya5j0bWtys98x1Vg15rXIyUkZAtBCFQ==",
+ "version": "0.45.0",
+ "resolved": "https://registry.npmjs.org/@requestnetwork/advanced-logic/-/advanced-logic-0.45.0.tgz",
+ "integrity": "sha512-CgEahYeYlRHU5OJZh4OC/INBulQERN1d/hA7XIKut0iBZ5APl4bMypGPkoBzp7450KlfSH5q8WVJl0Vn5a7poA==",
"dependencies": {
- "@requestnetwork/currency": "0.18.1-next.2088+9af0ebdc",
- "@requestnetwork/types": "0.45.1-next.2088+9af0ebdc",
- "@requestnetwork/utils": "0.45.1-next.2088+9af0ebdc",
+ "@requestnetwork/currency": "0.19.0",
+ "@requestnetwork/types": "0.45.1",
+ "@requestnetwork/utils": "0.45.1",
"tslib": "2.5.0"
},
"engines": {
@@ -2821,13 +2923,13 @@
}
},
"node_modules/@requestnetwork/advanced-logic/node_modules/@requestnetwork/currency": {
- "version": "0.18.1-next.2088",
- "resolved": "https://registry.npmjs.org/@requestnetwork/currency/-/currency-0.18.1-next.2088.tgz",
- "integrity": "sha512-cUBAKY5UIqpye5BHbWBb+VE16USmkm2BFT18wn/ymGPNKrfYuGFPMGdJuCNDbOOjNIV4NaO9QoAyRzDLlnukYg==",
+ "version": "0.19.0",
+ "resolved": "https://registry.npmjs.org/@requestnetwork/currency/-/currency-0.19.0.tgz",
+ "integrity": "sha512-TAgV/045bX8YN6GejbEArPjNdSCjwaK3MvuoHidla2gViJup8oqLb5YOicVQ87NUfNYh5FQcIXTgW6m0M/bP/Q==",
"dependencies": {
"@metamask/contract-metadata": "1.31.0",
- "@requestnetwork/types": "0.45.1-next.2088+9af0ebdc",
- "@requestnetwork/utils": "0.45.1-next.2088+9af0ebdc",
+ "@requestnetwork/types": "0.45.1",
+ "@requestnetwork/utils": "0.45.1",
"multicoin-address-validator": "0.5.15",
"node-dijkstra": "2.5.0",
"tslib": "2.5.0"
@@ -2837,9 +2939,9 @@
}
},
"node_modules/@requestnetwork/advanced-logic/node_modules/@requestnetwork/types": {
- "version": "0.45.1-next.2088",
- "resolved": "https://registry.npmjs.org/@requestnetwork/types/-/types-0.45.1-next.2088.tgz",
- "integrity": "sha512-Ydc0Vbu7mePv+ZbK1gNE0x2pKBwxZbPUGYMZeEgqzTYojfRQPQdFfR+VZJ762HiI9E3ampIe1yxjjpyTCFXHcg==",
+ "version": "0.45.1",
+ "resolved": "https://registry.npmjs.org/@requestnetwork/types/-/types-0.45.1.tgz",
+ "integrity": "sha512-cAaZPF2va29/Pjcucx3GqZ7yIlR0iRjmFxC4T5e0/ISONWxclH2nPY+MTYXpFjtNEwnOdnAJ65I8Dzb2FwNEKQ==",
"dependencies": {
"ethers": "5.7.2"
},
@@ -2848,11 +2950,11 @@
}
},
"node_modules/@requestnetwork/advanced-logic/node_modules/@requestnetwork/utils": {
- "version": "0.45.1-next.2088",
- "resolved": "https://registry.npmjs.org/@requestnetwork/utils/-/utils-0.45.1-next.2088.tgz",
- "integrity": "sha512-xTG362M7WaAWFiWpIGJpZnLWhfvSy5Z0jwp0jfK4/sGB14n0oEgbuItYDl/iev3j3e/galm5DiCoKg4N5JhbqQ==",
+ "version": "0.45.1",
+ "resolved": "https://registry.npmjs.org/@requestnetwork/utils/-/utils-0.45.1.tgz",
+ "integrity": "sha512-qr4K6sPkwMFbQpMtMRv1Fsuzc3xXYyVmGzVdTBW/6NcFhEKfEq0Cz5kptxCeu3IZ83+FrZVsNi/5gyu428Bffw==",
"dependencies": {
- "@requestnetwork/types": "0.45.1-next.2088+9af0ebdc",
+ "@requestnetwork/types": "0.45.1",
"@toruslabs/eccrypto": "4.0.0",
"ethers": "5.7.2",
"secp256k1": "4.0.4",
@@ -2898,13 +3000,13 @@
}
},
"node_modules/@requestnetwork/data-access": {
- "version": "0.36.1-next.2088",
- "resolved": "https://registry.npmjs.org/@requestnetwork/data-access/-/data-access-0.36.1-next.2088.tgz",
- "integrity": "sha512-GwsuMiKRkAYrSSh3POEirLBeAwItvRIHt1F6/BEa/kg4kavGyl3tSWpzYHO/AzcvRhoX5kWIOtslypPtbsxNig==",
+ "version": "0.36.1",
+ "resolved": "https://registry.npmjs.org/@requestnetwork/data-access/-/data-access-0.36.1.tgz",
+ "integrity": "sha512-SBe/ttquaxaAuvgzDdFnttMCJem/zWHMre8iaeY4UQmkOWR3YwpK25BIQXfr6/H7UqrTBE0/4BGeq35em6eDRQ==",
"dependencies": {
- "@requestnetwork/multi-format": "0.19.1-next.2088+9af0ebdc",
- "@requestnetwork/types": "0.45.1-next.2088+9af0ebdc",
- "@requestnetwork/utils": "0.45.1-next.2088+9af0ebdc",
+ "@requestnetwork/multi-format": "0.19.1",
+ "@requestnetwork/types": "0.45.1",
+ "@requestnetwork/utils": "0.45.1",
"tslib": "2.5.0"
},
"engines": {
@@ -2912,9 +3014,9 @@
}
},
"node_modules/@requestnetwork/data-access/node_modules/@requestnetwork/types": {
- "version": "0.45.1-next.2088",
- "resolved": "https://registry.npmjs.org/@requestnetwork/types/-/types-0.45.1-next.2088.tgz",
- "integrity": "sha512-Ydc0Vbu7mePv+ZbK1gNE0x2pKBwxZbPUGYMZeEgqzTYojfRQPQdFfR+VZJ762HiI9E3ampIe1yxjjpyTCFXHcg==",
+ "version": "0.45.1",
+ "resolved": "https://registry.npmjs.org/@requestnetwork/types/-/types-0.45.1.tgz",
+ "integrity": "sha512-cAaZPF2va29/Pjcucx3GqZ7yIlR0iRjmFxC4T5e0/ISONWxclH2nPY+MTYXpFjtNEwnOdnAJ65I8Dzb2FwNEKQ==",
"dependencies": {
"ethers": "5.7.2"
},
@@ -2923,11 +3025,11 @@
}
},
"node_modules/@requestnetwork/data-access/node_modules/@requestnetwork/utils": {
- "version": "0.45.1-next.2088",
- "resolved": "https://registry.npmjs.org/@requestnetwork/utils/-/utils-0.45.1-next.2088.tgz",
- "integrity": "sha512-xTG362M7WaAWFiWpIGJpZnLWhfvSy5Z0jwp0jfK4/sGB14n0oEgbuItYDl/iev3j3e/galm5DiCoKg4N5JhbqQ==",
+ "version": "0.45.1",
+ "resolved": "https://registry.npmjs.org/@requestnetwork/utils/-/utils-0.45.1.tgz",
+ "integrity": "sha512-qr4K6sPkwMFbQpMtMRv1Fsuzc3xXYyVmGzVdTBW/6NcFhEKfEq0Cz5kptxCeu3IZ83+FrZVsNi/5gyu428Bffw==",
"dependencies": {
- "@requestnetwork/types": "0.45.1-next.2088+9af0ebdc",
+ "@requestnetwork/types": "0.45.1",
"@toruslabs/eccrypto": "4.0.0",
"ethers": "5.7.2",
"secp256k1": "4.0.4",
@@ -2957,9 +3059,9 @@
}
},
"node_modules/@requestnetwork/data-format": {
- "version": "0.19.1-next.2088",
- "resolved": "https://registry.npmjs.org/@requestnetwork/data-format/-/data-format-0.19.1-next.2088.tgz",
- "integrity": "sha512-l53AtAqqTf65I/qgHWEXnjJ+u0sUKELT0s16yKTvzKrHi+OFiMrLBLkT9iO6ylLT7kStwqhV5+JZfLkdyFr8CA==",
+ "version": "0.19.1",
+ "resolved": "https://registry.npmjs.org/@requestnetwork/data-format/-/data-format-0.19.1.tgz",
+ "integrity": "sha512-jI4w55cOmSgBmG1fU89yrFRym/C+YE2OYQ8yXieWNjaYU6d6ukP96EE82Un6g+Jc/edkuy/7PD0nfK0ag0OSKQ==",
"dependencies": {
"ajv": "6.12.4",
"ethers": "5.7.2",
@@ -2970,12 +3072,12 @@
}
},
"node_modules/@requestnetwork/epk-signature": {
- "version": "0.9.1-next.2088",
- "resolved": "https://registry.npmjs.org/@requestnetwork/epk-signature/-/epk-signature-0.9.1-next.2088.tgz",
- "integrity": "sha512-d37PFle840G2Lx/Tbq0UHoDsxubj8Yg1khKi+lxVDgG7BPJSP114M2M0yBQUHLq6jt1O8l/Am1xybyOU4HoVzg==",
+ "version": "0.9.1",
+ "resolved": "https://registry.npmjs.org/@requestnetwork/epk-signature/-/epk-signature-0.9.1.tgz",
+ "integrity": "sha512-vxuPb4ALCAtoh9wzn6mmP6lEa174TcK05InuDwheSfuVy8xCttGRqXJnH1ybuTDOW1nQaUuz/EsGb4KhUL1uXw==",
"dependencies": {
- "@requestnetwork/types": "0.45.1-next.2088+9af0ebdc",
- "@requestnetwork/utils": "0.45.1-next.2088+9af0ebdc",
+ "@requestnetwork/types": "0.45.1",
+ "@requestnetwork/utils": "0.45.1",
"tslib": "2.5.0"
},
"engines": {
@@ -2983,9 +3085,9 @@
}
},
"node_modules/@requestnetwork/epk-signature/node_modules/@requestnetwork/types": {
- "version": "0.45.1-next.2088",
- "resolved": "https://registry.npmjs.org/@requestnetwork/types/-/types-0.45.1-next.2088.tgz",
- "integrity": "sha512-Ydc0Vbu7mePv+ZbK1gNE0x2pKBwxZbPUGYMZeEgqzTYojfRQPQdFfR+VZJ762HiI9E3ampIe1yxjjpyTCFXHcg==",
+ "version": "0.45.1",
+ "resolved": "https://registry.npmjs.org/@requestnetwork/types/-/types-0.45.1.tgz",
+ "integrity": "sha512-cAaZPF2va29/Pjcucx3GqZ7yIlR0iRjmFxC4T5e0/ISONWxclH2nPY+MTYXpFjtNEwnOdnAJ65I8Dzb2FwNEKQ==",
"dependencies": {
"ethers": "5.7.2"
},
@@ -2994,11 +3096,11 @@
}
},
"node_modules/@requestnetwork/epk-signature/node_modules/@requestnetwork/utils": {
- "version": "0.45.1-next.2088",
- "resolved": "https://registry.npmjs.org/@requestnetwork/utils/-/utils-0.45.1-next.2088.tgz",
- "integrity": "sha512-xTG362M7WaAWFiWpIGJpZnLWhfvSy5Z0jwp0jfK4/sGB14n0oEgbuItYDl/iev3j3e/galm5DiCoKg4N5JhbqQ==",
+ "version": "0.45.1",
+ "resolved": "https://registry.npmjs.org/@requestnetwork/utils/-/utils-0.45.1.tgz",
+ "integrity": "sha512-qr4K6sPkwMFbQpMtMRv1Fsuzc3xXYyVmGzVdTBW/6NcFhEKfEq0Cz5kptxCeu3IZ83+FrZVsNi/5gyu428Bffw==",
"dependencies": {
- "@requestnetwork/types": "0.45.1-next.2088+9af0ebdc",
+ "@requestnetwork/types": "0.45.1",
"@toruslabs/eccrypto": "4.0.0",
"ethers": "5.7.2",
"secp256k1": "4.0.4",
@@ -3028,11 +3130,11 @@
}
},
"node_modules/@requestnetwork/multi-format": {
- "version": "0.19.1-next.2088",
- "resolved": "https://registry.npmjs.org/@requestnetwork/multi-format/-/multi-format-0.19.1-next.2088.tgz",
- "integrity": "sha512-iWWlEM0F3KvL0vJ9l1jMHIhKs+0oJ8q9gwVm79weuhefYWkD3DpCYRc/yxB98U0w3Ab8KDAPA9+8rNbPzDsPfw==",
+ "version": "0.19.1",
+ "resolved": "https://registry.npmjs.org/@requestnetwork/multi-format/-/multi-format-0.19.1.tgz",
+ "integrity": "sha512-XRwpXSY3Lv7gKP1mi8UutpNzMCZGhS391CYGoKBcj7x7qsXOrJv5MxgEphTyCJXHbVEM79t3mtFtxUgfikb4IA==",
"dependencies": {
- "@requestnetwork/types": "0.45.1-next.2088+9af0ebdc",
+ "@requestnetwork/types": "0.45.1",
"tslib": "2.5.0"
},
"engines": {
@@ -3040,9 +3142,9 @@
}
},
"node_modules/@requestnetwork/multi-format/node_modules/@requestnetwork/types": {
- "version": "0.45.1-next.2088",
- "resolved": "https://registry.npmjs.org/@requestnetwork/types/-/types-0.45.1-next.2088.tgz",
- "integrity": "sha512-Ydc0Vbu7mePv+ZbK1gNE0x2pKBwxZbPUGYMZeEgqzTYojfRQPQdFfR+VZJ762HiI9E3ampIe1yxjjpyTCFXHcg==",
+ "version": "0.45.1",
+ "resolved": "https://registry.npmjs.org/@requestnetwork/types/-/types-0.45.1.tgz",
+ "integrity": "sha512-cAaZPF2va29/Pjcucx3GqZ7yIlR0iRjmFxC4T5e0/ISONWxclH2nPY+MTYXpFjtNEwnOdnAJ65I8Dzb2FwNEKQ==",
"dependencies": {
"ethers": "5.7.2"
},
@@ -4609,12 +4711,12 @@
}
},
"node_modules/@requestnetwork/payment-widget": {
- "version": "0.3.1",
- "resolved": "https://registry.npmjs.org/@requestnetwork/payment-widget/-/payment-widget-0.3.1.tgz",
- "integrity": "sha512-xLbmuArxCPQ92OF6EKmBoxrkcQyvY7btCew2n0YaoN15bi9/pRBUyBv5D8tvhiJReK3dJK9XVqKI028H86+8WA==",
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/@requestnetwork/payment-widget/-/payment-widget-0.3.2.tgz",
+ "integrity": "sha512-XPgL0KRmapndKmGG/ZIke5q5KMtHwbsSDziVrct0Cov/Wdu0mhGwA3skAX8j0Oxm+FyS+7QrjdGPP38o/lGyNw==",
"dependencies": {
"@requestnetwork/payment-processor": "^0.47.0",
- "@requestnetwork/request-client.js": "^0.49.1-next.2088",
+ "@requestnetwork/request-client.js": "^0.50.0",
"@requestnetwork/web3-signature": "^0.8.0",
"@web3modal/ethers5": "^5.0.11",
"ethers": "^5.7.2",
@@ -4626,22 +4728,22 @@
}
},
"node_modules/@requestnetwork/request-client.js": {
- "version": "0.49.1-next.2088",
- "resolved": "https://registry.npmjs.org/@requestnetwork/request-client.js/-/request-client.js-0.49.1-next.2088.tgz",
- "integrity": "sha512-RuXat8wbXLAMME1h2fljEYqq4WXd2vk88U3Utjj06am810CkGoyCtvDaAWrvmGJky+oW9B71d8WZ1B0mjGlVrQ==",
- "dependencies": {
- "@requestnetwork/advanced-logic": "0.44.1-next.2088+9af0ebdc",
- "@requestnetwork/currency": "0.18.1-next.2088+9af0ebdc",
- "@requestnetwork/data-access": "0.36.1-next.2088+9af0ebdc",
- "@requestnetwork/data-format": "0.19.1-next.2088+9af0ebdc",
- "@requestnetwork/epk-signature": "0.9.1-next.2088+9af0ebdc",
- "@requestnetwork/multi-format": "0.19.1-next.2088+9af0ebdc",
- "@requestnetwork/payment-detection": "0.45.1-next.2088+9af0ebdc",
- "@requestnetwork/request-logic": "0.35.1-next.2088+9af0ebdc",
- "@requestnetwork/smart-contracts": "0.38.1-next.2088+9af0ebdc",
- "@requestnetwork/transaction-manager": "0.36.1-next.2088+9af0ebdc",
- "@requestnetwork/types": "0.45.1-next.2088+9af0ebdc",
- "@requestnetwork/utils": "0.45.1-next.2088+9af0ebdc",
+ "version": "0.50.0",
+ "resolved": "https://registry.npmjs.org/@requestnetwork/request-client.js/-/request-client.js-0.50.0.tgz",
+ "integrity": "sha512-jOxRhUUzXD+ffgzt5LXCzVQeYW0ow3P6Vf+ZVPAh9Z/4iaEjIMfWkqoLZTe9mXQnx+0BfKUwyDYzyk5z9BDkyQ==",
+ "dependencies": {
+ "@requestnetwork/advanced-logic": "0.45.0",
+ "@requestnetwork/currency": "0.19.0",
+ "@requestnetwork/data-access": "0.36.1",
+ "@requestnetwork/data-format": "0.19.1",
+ "@requestnetwork/epk-signature": "0.9.1",
+ "@requestnetwork/multi-format": "0.19.1",
+ "@requestnetwork/payment-detection": "0.45.1",
+ "@requestnetwork/request-logic": "0.35.1",
+ "@requestnetwork/smart-contracts": "0.39.0",
+ "@requestnetwork/transaction-manager": "0.36.1",
+ "@requestnetwork/types": "0.45.1",
+ "@requestnetwork/utils": "0.45.1",
"ethers": "5.7.2",
"qs": "6.11.2",
"tslib": "2.5.0"
@@ -4651,13 +4753,13 @@
}
},
"node_modules/@requestnetwork/request-client.js/node_modules/@requestnetwork/currency": {
- "version": "0.18.1-next.2088",
- "resolved": "https://registry.npmjs.org/@requestnetwork/currency/-/currency-0.18.1-next.2088.tgz",
- "integrity": "sha512-cUBAKY5UIqpye5BHbWBb+VE16USmkm2BFT18wn/ymGPNKrfYuGFPMGdJuCNDbOOjNIV4NaO9QoAyRzDLlnukYg==",
+ "version": "0.19.0",
+ "resolved": "https://registry.npmjs.org/@requestnetwork/currency/-/currency-0.19.0.tgz",
+ "integrity": "sha512-TAgV/045bX8YN6GejbEArPjNdSCjwaK3MvuoHidla2gViJup8oqLb5YOicVQ87NUfNYh5FQcIXTgW6m0M/bP/Q==",
"dependencies": {
"@metamask/contract-metadata": "1.31.0",
- "@requestnetwork/types": "0.45.1-next.2088+9af0ebdc",
- "@requestnetwork/utils": "0.45.1-next.2088+9af0ebdc",
+ "@requestnetwork/types": "0.45.1",
+ "@requestnetwork/utils": "0.45.1",
"multicoin-address-validator": "0.5.15",
"node-dijkstra": "2.5.0",
"tslib": "2.5.0"
@@ -4667,14 +4769,14 @@
}
},
"node_modules/@requestnetwork/request-client.js/node_modules/@requestnetwork/payment-detection": {
- "version": "0.45.1-next.2088",
- "resolved": "https://registry.npmjs.org/@requestnetwork/payment-detection/-/payment-detection-0.45.1-next.2088.tgz",
- "integrity": "sha512-dJhziM21zwmf5MIG06wdtpYDTdnOqmTCKrXNEgQ3Sjrh4R8PNuGU8tQ7zqVno9mzVU8WZQe0sfplLIlrMgmqgA==",
- "dependencies": {
- "@requestnetwork/currency": "0.18.1-next.2088+9af0ebdc",
- "@requestnetwork/smart-contracts": "0.38.1-next.2088+9af0ebdc",
- "@requestnetwork/types": "0.45.1-next.2088+9af0ebdc",
- "@requestnetwork/utils": "0.45.1-next.2088+9af0ebdc",
+ "version": "0.45.1",
+ "resolved": "https://registry.npmjs.org/@requestnetwork/payment-detection/-/payment-detection-0.45.1.tgz",
+ "integrity": "sha512-6t9uNJXBP8cUJ+8vVJ6cBlUuWjxqkiAyFhZ/Jg0KTqSFyoP1nj9Wn+SVK+hIvQfTwxybSn95gHIAXCIYIvIxnQ==",
+ "dependencies": {
+ "@requestnetwork/currency": "0.19.0",
+ "@requestnetwork/smart-contracts": "0.39.0",
+ "@requestnetwork/types": "0.45.1",
+ "@requestnetwork/utils": "0.45.1",
"ethers": "5.7.2",
"graphql": "16.8.1",
"graphql-request": "6.1.0",
@@ -4687,9 +4789,9 @@
}
},
"node_modules/@requestnetwork/request-client.js/node_modules/@requestnetwork/smart-contracts": {
- "version": "0.38.1-next.2088",
- "resolved": "https://registry.npmjs.org/@requestnetwork/smart-contracts/-/smart-contracts-0.38.1-next.2088.tgz",
- "integrity": "sha512-Re2PDVLnOQzlpXrgddV/GXpeqtQxYhL2t27V0oejH56r7a1m/LuZ36DmQHqx4ZLw3lw6vx93TiEZmxb0iHq1fA==",
+ "version": "0.39.0",
+ "resolved": "https://registry.npmjs.org/@requestnetwork/smart-contracts/-/smart-contracts-0.39.0.tgz",
+ "integrity": "sha512-wxZ38EjjdTig9F2MBTAh5nE1wa50xBGLMiy4EYx6wpTxAQdiwc7s5RWmgULxrAY13huGSBjo6zt8qzIQLuydhA==",
"dependencies": {
"tslib": "2.5.0"
},
@@ -4698,9 +4800,9 @@
}
},
"node_modules/@requestnetwork/request-client.js/node_modules/@requestnetwork/types": {
- "version": "0.45.1-next.2088",
- "resolved": "https://registry.npmjs.org/@requestnetwork/types/-/types-0.45.1-next.2088.tgz",
- "integrity": "sha512-Ydc0Vbu7mePv+ZbK1gNE0x2pKBwxZbPUGYMZeEgqzTYojfRQPQdFfR+VZJ762HiI9E3ampIe1yxjjpyTCFXHcg==",
+ "version": "0.45.1",
+ "resolved": "https://registry.npmjs.org/@requestnetwork/types/-/types-0.45.1.tgz",
+ "integrity": "sha512-cAaZPF2va29/Pjcucx3GqZ7yIlR0iRjmFxC4T5e0/ISONWxclH2nPY+MTYXpFjtNEwnOdnAJ65I8Dzb2FwNEKQ==",
"dependencies": {
"ethers": "5.7.2"
},
@@ -4709,11 +4811,11 @@
}
},
"node_modules/@requestnetwork/request-client.js/node_modules/@requestnetwork/utils": {
- "version": "0.45.1-next.2088",
- "resolved": "https://registry.npmjs.org/@requestnetwork/utils/-/utils-0.45.1-next.2088.tgz",
- "integrity": "sha512-xTG362M7WaAWFiWpIGJpZnLWhfvSy5Z0jwp0jfK4/sGB14n0oEgbuItYDl/iev3j3e/galm5DiCoKg4N5JhbqQ==",
+ "version": "0.45.1",
+ "resolved": "https://registry.npmjs.org/@requestnetwork/utils/-/utils-0.45.1.tgz",
+ "integrity": "sha512-qr4K6sPkwMFbQpMtMRv1Fsuzc3xXYyVmGzVdTBW/6NcFhEKfEq0Cz5kptxCeu3IZ83+FrZVsNi/5gyu428Bffw==",
"dependencies": {
- "@requestnetwork/types": "0.45.1-next.2088+9af0ebdc",
+ "@requestnetwork/types": "0.45.1",
"@toruslabs/eccrypto": "4.0.0",
"ethers": "5.7.2",
"secp256k1": "4.0.4",
@@ -4751,14 +4853,14 @@
}
},
"node_modules/@requestnetwork/request-logic": {
- "version": "0.35.1-next.2088",
- "resolved": "https://registry.npmjs.org/@requestnetwork/request-logic/-/request-logic-0.35.1-next.2088.tgz",
- "integrity": "sha512-FP1dKlQHn/8HHIVmN3KgDOSy7ltobAbhkiB5GY6djIPCU9NR8voUpvNJVLxcRsW+e1/9oQduKHeF22X8F2QQvw==",
- "dependencies": {
- "@requestnetwork/advanced-logic": "0.44.1-next.2088+9af0ebdc",
- "@requestnetwork/multi-format": "0.19.1-next.2088+9af0ebdc",
- "@requestnetwork/types": "0.45.1-next.2088+9af0ebdc",
- "@requestnetwork/utils": "0.45.1-next.2088+9af0ebdc",
+ "version": "0.35.1",
+ "resolved": "https://registry.npmjs.org/@requestnetwork/request-logic/-/request-logic-0.35.1.tgz",
+ "integrity": "sha512-6UGyMO7C8voT6rIrdwroKF6AG4/MZN35AKvCCX3DX/KIP7oOjRkLXlyCgWqxJoAikVHXNU28dt5ZWwDFHdQALQ==",
+ "dependencies": {
+ "@requestnetwork/advanced-logic": "0.45.0",
+ "@requestnetwork/multi-format": "0.19.1",
+ "@requestnetwork/types": "0.45.1",
+ "@requestnetwork/utils": "0.45.1",
"semver": "7.5.4",
"tslib": "2.5.0"
},
@@ -4767,9 +4869,9 @@
}
},
"node_modules/@requestnetwork/request-logic/node_modules/@requestnetwork/types": {
- "version": "0.45.1-next.2088",
- "resolved": "https://registry.npmjs.org/@requestnetwork/types/-/types-0.45.1-next.2088.tgz",
- "integrity": "sha512-Ydc0Vbu7mePv+ZbK1gNE0x2pKBwxZbPUGYMZeEgqzTYojfRQPQdFfR+VZJ762HiI9E3ampIe1yxjjpyTCFXHcg==",
+ "version": "0.45.1",
+ "resolved": "https://registry.npmjs.org/@requestnetwork/types/-/types-0.45.1.tgz",
+ "integrity": "sha512-cAaZPF2va29/Pjcucx3GqZ7yIlR0iRjmFxC4T5e0/ISONWxclH2nPY+MTYXpFjtNEwnOdnAJ65I8Dzb2FwNEKQ==",
"dependencies": {
"ethers": "5.7.2"
},
@@ -4778,11 +4880,11 @@
}
},
"node_modules/@requestnetwork/request-logic/node_modules/@requestnetwork/utils": {
- "version": "0.45.1-next.2088",
- "resolved": "https://registry.npmjs.org/@requestnetwork/utils/-/utils-0.45.1-next.2088.tgz",
- "integrity": "sha512-xTG362M7WaAWFiWpIGJpZnLWhfvSy5Z0jwp0jfK4/sGB14n0oEgbuItYDl/iev3j3e/galm5DiCoKg4N5JhbqQ==",
+ "version": "0.45.1",
+ "resolved": "https://registry.npmjs.org/@requestnetwork/utils/-/utils-0.45.1.tgz",
+ "integrity": "sha512-qr4K6sPkwMFbQpMtMRv1Fsuzc3xXYyVmGzVdTBW/6NcFhEKfEq0Cz5kptxCeu3IZ83+FrZVsNi/5gyu428Bffw==",
"dependencies": {
- "@requestnetwork/types": "0.45.1-next.2088+9af0ebdc",
+ "@requestnetwork/types": "0.45.1",
"@toruslabs/eccrypto": "4.0.0",
"ethers": "5.7.2",
"secp256k1": "4.0.4",
@@ -4823,13 +4925,13 @@
}
},
"node_modules/@requestnetwork/transaction-manager": {
- "version": "0.36.1-next.2088",
- "resolved": "https://registry.npmjs.org/@requestnetwork/transaction-manager/-/transaction-manager-0.36.1-next.2088.tgz",
- "integrity": "sha512-+A+l0vgD+lcZFDZYqTnjMKNhL+jXLX7Iw+tBBXL+PrOPesM1WtY8zsgqgcp0uEVDRTJW0pB81CigvdKITogegg==",
+ "version": "0.36.1",
+ "resolved": "https://registry.npmjs.org/@requestnetwork/transaction-manager/-/transaction-manager-0.36.1.tgz",
+ "integrity": "sha512-s3gt1qM2BudbX1IjZkzUBzFL9oNEpSH6/zmw/ZjGVtkevtfu28lfUScpm3ltJesTKsjFWSW6LOWb68ClLidcTg==",
"dependencies": {
- "@requestnetwork/multi-format": "0.19.1-next.2088+9af0ebdc",
- "@requestnetwork/types": "0.45.1-next.2088+9af0ebdc",
- "@requestnetwork/utils": "0.45.1-next.2088+9af0ebdc",
+ "@requestnetwork/multi-format": "0.19.1",
+ "@requestnetwork/types": "0.45.1",
+ "@requestnetwork/utils": "0.45.1",
"tslib": "2.5.0"
},
"engines": {
@@ -4837,9 +4939,9 @@
}
},
"node_modules/@requestnetwork/transaction-manager/node_modules/@requestnetwork/types": {
- "version": "0.45.1-next.2088",
- "resolved": "https://registry.npmjs.org/@requestnetwork/types/-/types-0.45.1-next.2088.tgz",
- "integrity": "sha512-Ydc0Vbu7mePv+ZbK1gNE0x2pKBwxZbPUGYMZeEgqzTYojfRQPQdFfR+VZJ762HiI9E3ampIe1yxjjpyTCFXHcg==",
+ "version": "0.45.1",
+ "resolved": "https://registry.npmjs.org/@requestnetwork/types/-/types-0.45.1.tgz",
+ "integrity": "sha512-cAaZPF2va29/Pjcucx3GqZ7yIlR0iRjmFxC4T5e0/ISONWxclH2nPY+MTYXpFjtNEwnOdnAJ65I8Dzb2FwNEKQ==",
"dependencies": {
"ethers": "5.7.2"
},
@@ -4848,11 +4950,11 @@
}
},
"node_modules/@requestnetwork/transaction-manager/node_modules/@requestnetwork/utils": {
- "version": "0.45.1-next.2088",
- "resolved": "https://registry.npmjs.org/@requestnetwork/utils/-/utils-0.45.1-next.2088.tgz",
- "integrity": "sha512-xTG362M7WaAWFiWpIGJpZnLWhfvSy5Z0jwp0jfK4/sGB14n0oEgbuItYDl/iev3j3e/galm5DiCoKg4N5JhbqQ==",
+ "version": "0.45.1",
+ "resolved": "https://registry.npmjs.org/@requestnetwork/utils/-/utils-0.45.1.tgz",
+ "integrity": "sha512-qr4K6sPkwMFbQpMtMRv1Fsuzc3xXYyVmGzVdTBW/6NcFhEKfEq0Cz5kptxCeu3IZ83+FrZVsNi/5gyu428Bffw==",
"dependencies": {
- "@requestnetwork/types": "0.45.1-next.2088+9af0ebdc",
+ "@requestnetwork/types": "0.45.1",
"@toruslabs/eccrypto": "4.0.0",
"ethers": "5.7.2",
"secp256k1": "4.0.4",
@@ -9681,6 +9783,15 @@
"resolved": "https://registry.npmjs.org/dash-ast/-/dash-ast-1.0.0.tgz",
"integrity": "sha512-Vy4dx7gquTeMcQR/hDkYLGUnwVil6vk4FOOct+djUnHOUWt+zJPJAaRIXaAFkPXtJjvlY7o3rfRu0/3hpnwoUA=="
},
+ "node_modules/date-fns": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz",
+ "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/kossnocorp"
+ }
+ },
"node_modules/dayjs": {
"version": "1.11.10",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz",
@@ -9948,6 +10059,39 @@
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
"integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA=="
},
+ "node_modules/embla-carousel": {
+ "version": "8.3.1",
+ "resolved": "https://registry.npmjs.org/embla-carousel/-/embla-carousel-8.3.1.tgz",
+ "integrity": "sha512-DutFjtEO586XptDn4cwvBJwsR/8fMa4jUk5Jk2g+/elKgu8mdn0Z2sx33g4JskvbLc1/6P8Xg4QlfELGJFcP5A=="
+ },
+ "node_modules/embla-carousel-autoplay": {
+ "version": "8.3.1",
+ "resolved": "https://registry.npmjs.org/embla-carousel-autoplay/-/embla-carousel-autoplay-8.3.1.tgz",
+ "integrity": "sha512-L8THF1AJJSQtlNa1wZ6lEKh/CiZssE3TsVFtabQNsS+pc1O1O/YTIYCC3khdQAztGMOBf3WfVDIY/4AIfQj3JQ==",
+ "peerDependencies": {
+ "embla-carousel": "8.3.1"
+ }
+ },
+ "node_modules/embla-carousel-react": {
+ "version": "8.3.1",
+ "resolved": "https://registry.npmjs.org/embla-carousel-react/-/embla-carousel-react-8.3.1.tgz",
+ "integrity": "sha512-gBY0zM+2ASvKFwRpTIOn2SLifFqOKKap9R/y0iCpJWS3bc8OHVEn2gAThGYl2uq0N+hu9aBiswffL++OYZOmDQ==",
+ "dependencies": {
+ "embla-carousel": "8.3.1",
+ "embla-carousel-reactive-utils": "8.3.1"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.1 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
+ }
+ },
+ "node_modules/embla-carousel-reactive-utils": {
+ "version": "8.3.1",
+ "resolved": "https://registry.npmjs.org/embla-carousel-reactive-utils/-/embla-carousel-reactive-utils-8.3.1.tgz",
+ "integrity": "sha512-Js6rTTINNGnUGPu7l5kTcheoSbEnP5Ak2iX0G9uOoI8okTNLMzuWlEIpYFd1WP0Sq82FFcLkKM2oiO6jcElZ/Q==",
+ "peerDependencies": {
+ "embla-carousel": "8.3.1"
+ }
+ },
"node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
@@ -14383,6 +14527,34 @@
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
+ },
+ "node_modules/zustand": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.1.tgz",
+ "integrity": "sha512-pRET7Lao2z+n5R/HduXMio35TncTlSW68WsYBq2Lg1ASspsNGjpwLAsij3RpouyV6+kHMwwwzP0bZPD70/Jx/w==",
+ "engines": {
+ "node": ">=12.20.0"
+ },
+ "peerDependencies": {
+ "@types/react": ">=18.0.0",
+ "immer": ">=9.0.6",
+ "react": ">=18.0.0",
+ "use-sync-external-store": ">=1.2.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "immer": {
+ "optional": true
+ },
+ "react": {
+ "optional": true
+ },
+ "use-sync-external-store": {
+ "optional": true
+ }
+ }
}
}
}
diff --git a/package.json b/package.json
index f030ce7..dcca268 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "rn-checkout",
- "version": "0.2.0",
+ "version": "0.3.0",
"private": true,
"scripts": {
"dev": "next dev",
@@ -17,11 +17,15 @@
"@radix-ui/react-popover": "^1.1.1",
"@radix-ui/react-slot": "^1.1.0",
"@radix-ui/react-switch": "^1.1.0",
+ "@radix-ui/react-tabs": "^1.1.1",
"@radix-ui/react-tooltip": "^1.1.2",
- "@requestnetwork/payment-widget": "^0.3.1",
+ "@requestnetwork/payment-widget": "^0.3.2",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
"cmdk": "^1.0.0",
+ "date-fns": "^4.1.0",
+ "embla-carousel-autoplay": "^8.3.1",
+ "embla-carousel-react": "^8.3.1",
"framer-motion": "^11.3.28",
"lucide-react": "^0.428.0",
"next": "14.2.5",
@@ -31,7 +35,8 @@
"tailwind-merge": "^2.5.2",
"tailwindcss-animate": "^1.0.7",
"validator": "^13.12.0",
- "zod": "^3.23.8"
+ "zod": "^3.23.8",
+ "zustand": "^5.0.1"
},
"devDependencies": {
"@types/node": "^20",
diff --git a/src/app/(demo)/checkout/page.tsx b/src/app/(demo)/checkout/page.tsx
new file mode 100644
index 0000000..4538e59
--- /dev/null
+++ b/src/app/(demo)/checkout/page.tsx
@@ -0,0 +1,10 @@
+import { CheckoutStepper } from "@/components/CheckoutStepper";
+
+export default function CheckoutPage() {
+ return (
+
+
Checkout
+
+
+ );
+}
diff --git a/src/app/(demo)/event/[id]/page.tsx b/src/app/(demo)/event/[id]/page.tsx
new file mode 100644
index 0000000..392aedf
--- /dev/null
+++ b/src/app/(demo)/event/[id]/page.tsx
@@ -0,0 +1,112 @@
+import { notFound } from "next/navigation";
+import Image from "next/image";
+import { CalendarIcon, MapPinIcon, Clock } from "lucide-react";
+import { format } from "date-fns";
+import { TicketSelector } from "@/components/TicketSelector";
+import eventsData from "@/const/data.json";
+
+async function getEventById(id: string) {
+ // In a real app, this would be a DB or API call
+ const event = eventsData.events.find((event) => event.id === id);
+ if (!event) return null;
+ return event;
+}
+
+export default async function EventDetailsPage({
+ params,
+}: {
+ params: { id: string };
+}) {
+ const event = await getEventById(params.id);
+
+ if (!event) {
+ notFound();
+ }
+
+ return (
+
+ {/* Hero Section */}
+
+
+
+
+
+ {event.type}
+
+
{event.name}
+
+
+
+ {/* Content Section */}
+
+
+ {/* Main Content */}
+
+ {/* Event Details */}
+
+ Event Details
+
+
+
+
+ {format(new Date(event.dateTime), "MMMM d, yyyy")}
+
+
+
+
+
+ {format(new Date(event.dateTime), "h:mm a")} -{" "}
+ {format(new Date(event.endDateTime), "h:mm a")}
+
+
+
+
+
+ {event.location.venue}, {event.location.city},{" "}
+ {event.location.country}
+
+
+
+
+
+ {/* About Section */}
+
+ About the Event
+ {event.organizer.description}
+
+
+ {/* Organizer Section */}
+
+ Organizer
+
+
+
+
+
+
{event.organizer.name}
+
Event Organizer
+
+
+
+
+
+ {/* Ticket Selection Sidebar */}
+
+
+
+
+
+
+ );
+}
diff --git a/src/app/(demo)/page.tsx b/src/app/(demo)/page.tsx
new file mode 100644
index 0000000..e45f842
--- /dev/null
+++ b/src/app/(demo)/page.tsx
@@ -0,0 +1,66 @@
+import { EventShowcase } from "@/components/EventShowcase";
+import {
+ Carousel,
+ CarouselContent,
+ CarouselItem,
+} from "@/components/ui/carousel";
+import eventData from "@/const/data.json";
+import Image from "next/image";
+import Link from "next/link";
+
+export const metadata = {
+ title: "Request Checkout Demo",
+ description:
+ "This is a demo of the Request Checkout widget. It is a pre-built component that abstracts all the complexities of blockchain transactions using Request Network, making it simple for businesses to handle crypto-to-crypto payments without deep technical knowledge",
+};
+
+export default function DemoPage() {
+ const events = eventData.events;
+ const featuredEvents = events.filter((event) => event.featured);
+
+ return (
+ <>
+
+ {/* Featured Events Carousel */}
+
+
+
+ {featuredEvents.map((event) => (
+
+
+
+
+
+
+
+
+ {event.type}
+
+
+
{event.name}
+
Featured Event
+
+
+
+
+ ))}
+
+
+
+
+
+ >
+ );
+}
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index c3dbf59..a76c4be 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -49,7 +49,9 @@ export default function RootLayout({
- {children}
+
+ {children}
+
diff --git a/src/app/page.tsx b/src/app/playground/page.tsx
similarity index 82%
rename from src/app/page.tsx
rename to src/app/playground/page.tsx
index 5c34afe..a0f4c23 100644
--- a/src/app/page.tsx
+++ b/src/app/playground/page.tsx
@@ -1,9 +1,15 @@
import { Playground } from "@/components/Playground";
-export default function Home() {
+export const metadata = {
+ title: "Request Checkout Playground",
+ description:
+ "A playground for the Request Checkout widget. You can experiment with the widget's properties, such as seller information, product details, and supported currencies.",
+};
+
+export default function PlaygroundPage() {
return (
-
- Request Checkout
+ <>
+ Request Checkout Playground
@@ -46,6 +52,6 @@ export default function Home() {
-
+ >
);
}
diff --git a/src/components/CartReview.tsx b/src/components/CartReview.tsx
new file mode 100644
index 0000000..b55c9a8
--- /dev/null
+++ b/src/components/CartReview.tsx
@@ -0,0 +1,107 @@
+"use client";
+
+import { useTicketStore } from "@/store/ticketStore";
+import { useEffect, useState } from "react";
+
+export function CartReview() {
+ const { tickets, incrementQuantity, decrementQuantity, clearTickets } =
+ useTicketStore();
+ const [total, setTotal] = useState(0);
+
+ useEffect(() => {
+ const newTotal = Object.values(tickets).reduce(
+ (sum, ticket) => sum + ticket.price * ticket.quantity,
+ 0
+ );
+ setTotal(newTotal);
+ }, [tickets]);
+
+ const groupedTickets = Object.entries(tickets).map(([key, ticket]) => ({
+ eventId: key.split("-")[0],
+ ...ticket,
+ }));
+
+ return (
+
+
+
Cart Review
+ {groupedTickets.length > 0 && (
+
+ Clear Cart
+
+ )}
+
+
+ {groupedTickets.length === 0 ? (
+
Your cart is empty
+ ) : (
+
+
+ {groupedTickets.map((ticket) => (
+
+
+
+
{ticket.name}
+
+ ${ticket.price.toFixed(2)}
+
+
+
+ decrementQuantity(ticket.id)}
+ className="w-7 h-7 rounded-lg border border-gray-200 flex items-center justify-center text-gray-600 hover:border-[#099C77] hover:text-[#099C77] transition-colors"
+ aria-label={`Decrease quantity for ${ticket.name}`}
+ >
+ -
+
+
+ {ticket.quantity}
+
+
+ incrementQuantity(ticket.id, {
+ id: ticket.id,
+ name: ticket.name,
+ price: ticket.price,
+ description: "",
+ available: 0,
+ })
+ }
+ className="w-7 h-7 rounded-lg border border-gray-200 flex items-center justify-center text-gray-600 hover:border-[#099C77] hover:text-[#099C77] transition-colors"
+ aria-label={`Increase quantity for ${ticket.name}`}
+ >
+ +
+
+
+
+
+ ))}
+
+
+
+
+ Total:
+
+ ${total > 0 ? total.toFixed(2) : "0.00"}
+
+
+
+
+ )}
+
+ );
+}
diff --git a/src/components/CheckoutStepper.tsx b/src/components/CheckoutStepper.tsx
new file mode 100644
index 0000000..4b1f16a
--- /dev/null
+++ b/src/components/CheckoutStepper.tsx
@@ -0,0 +1,89 @@
+"use client";
+
+import { useState } from "react";
+import { CartReview } from "./CartReview";
+import { PaymentStep } from "./PaymentStep";
+import { useTicketStore } from "@/store/ticketStore";
+
+const steps = [
+ { id: "cart", title: "Cart Review" },
+ { id: "payment", title: "Payment" },
+];
+
+export function CheckoutStepper() {
+ const [currentStep, setCurrentStep] = useState("cart");
+ const { tickets } = useTicketStore();
+
+ // Check if cart has items
+ const hasItems = Object.keys(tickets).length > 0;
+
+ return (
+
+ {/* Stepper UI */}
+
+ {steps.map((step, index) => (
+
+
+ {index + 1}
+
+
+ {step.title}
+
+ {index < steps.length - 1 && (
+
+ )}
+
+ ))}
+
+
+ {/* Step Content */}
+
+ {currentStep === "cart" ? (
+
+
+
+ setCurrentStep("payment")}
+ disabled={!hasItems}
+ className={`px-6 py-2 rounded-lg transition-colors ${
+ hasItems
+ ? "bg-[#099C77] text-white hover:bg-[#099C77]/90"
+ : "bg-gray-100 text-gray-400 cursor-not-allowed"
+ }`}
+ aria-label={hasItems ? "Proceed to payment" : "Cart is empty"}
+ >
+ Proceed to Payment
+
+
+
+ ) : (
+
+
+
+ setCurrentStep("cart")}
+ className="px-6 py-2 border-2 border-gray-200 rounded-lg hover:border-gray-300 transition-colors"
+ >
+ Back to Cart
+
+
+
+ )}
+
+
+ );
+}
diff --git a/src/components/EventShowcase.tsx b/src/components/EventShowcase.tsx
new file mode 100644
index 0000000..1a9a5d2
--- /dev/null
+++ b/src/components/EventShowcase.tsx
@@ -0,0 +1,127 @@
+"use client";
+
+import Image from "next/image";
+import Link from "next/link";
+import { CalendarIcon, MapPinIcon } from "lucide-react";
+import { format } from "date-fns";
+import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs";
+import { useState, useMemo } from "react";
+
+interface Event {
+ id: string;
+ name: string;
+ type: string;
+ image: string;
+ dateTime: string;
+ location: {
+ city: string;
+ country: string;
+ };
+ startingPrice: number;
+}
+
+interface EventShowcaseProps {
+ events: Event[];
+}
+
+export const EventShowcase = ({ events }: EventShowcaseProps) => {
+ const [activeTab, setActiveTab] = useState("all");
+
+ // Get unique event types and add "All" option
+ const eventTypes = useMemo(() => {
+ const types = ["All", ...new Set(events.map((event) => event.type))];
+ return types;
+ }, [events]);
+
+ // Filter events based on active tab
+ const filteredEvents = useMemo(() => {
+ if (activeTab.toLowerCase() === "all") return events;
+ return events.filter(
+ (event) => event.type.toLowerCase() === activeTab.toLowerCase()
+ );
+ }, [events, activeTab]);
+
+ return (
+
+
+ {eventTypes.map((type) => (
+
+ {type}
+
+ ))}
+
+
+ {eventTypes.map((type) => (
+
+
+ {filteredEvents.map((event) => (
+
+
+
+
+
+ {event.type}
+
+
+
+
+
+
+
+ {event.name}
+
+
+
+
+
+
+ {format(
+ new Date(event.dateTime),
+ "MMM d, yyyy • h:mm a"
+ )}
+
+
+
+
+ {`${event.location.city}, ${event.location.country}`}
+
+
+
+
+
+
+ From
+
+ ${event.startingPrice}
+
+
+
+ View Details
+
+
+
+
+ ))}
+
+
+ ))}
+
+ );
+};
diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx
index 296eaf1..e091826 100644
--- a/src/components/Navbar.tsx
+++ b/src/components/Navbar.tsx
@@ -1,6 +1,12 @@
+"use client";
+
import Image from "next/image";
+import Link from "next/link";
+import { usePathname } from "next/navigation";
export const Navbar = () => {
+ const pathname = usePathname();
+
return (
@@ -11,6 +17,35 @@ export const Navbar = () => {
width={100}
height={200}
/>
+
+
+
+
+ Demo
+
+
+
+
+ Playground
+
+
+
diff --git a/src/components/PaymentStep.tsx b/src/components/PaymentStep.tsx
new file mode 100644
index 0000000..c0a5868
--- /dev/null
+++ b/src/components/PaymentStep.tsx
@@ -0,0 +1,80 @@
+"use client";
+
+import { useTicketStore } from "@/store/ticketStore";
+import PaymentWidget from "@requestnetwork/payment-widget/react";
+import { useEffect, useState } from "react";
+import { useRouter } from "next/navigation";
+
+export function PaymentStep() {
+ const { tickets, clearTickets } = useTicketStore();
+ const [total, setTotal] = useState(0);
+ const router = useRouter();
+
+ useEffect(() => {
+ const newTotal = Object.values(tickets).reduce(
+ (sum, ticket) => sum + ticket.price * ticket.quantity,
+ 0
+ );
+ setTotal(newTotal);
+ }, [tickets]);
+
+ return (
+
+ {/* Order Summary */}
+
+
Order Summary
+
+ {Object.values(tickets).map((ticket) => (
+
+
+
{ticket.name}
+
+ Quantity: {ticket.quantity}
+
+
+
+ ${(ticket.price * ticket.quantity).toFixed(2)}
+
+
+ ))}
+
+ Total:
+
+ ${total.toFixed(2)}
+
+
+
+
+
+ {/* Payment Widget */}
+
+
Payment
+
{
+ clearTickets();
+
+ setTimeout(() => {
+ router.push("/");
+ }, 5000);
+ }}
+ hideTotalAmount
+ />
+
+
+ );
+}
diff --git a/src/components/Playground.tsx b/src/components/Playground.tsx
index f112054..f20ce51 100644
--- a/src/components/Playground.tsx
+++ b/src/components/Playground.tsx
@@ -17,7 +17,7 @@ import { Textarea } from "./ui/textarea";
import { ZERO_ADDRESS } from "@/lib/constants";
import { cn } from "@/lib/utils";
import { SectionHeader } from "./ui/section-header";
-import { Tabs } from "./ui/tabs";
+import { Tabs } from "./ui/custom-tabs";
export const Playground = () => {
// Tabs
diff --git a/src/components/TicketSelector.tsx b/src/components/TicketSelector.tsx
new file mode 100644
index 0000000..e31fa3d
--- /dev/null
+++ b/src/components/TicketSelector.tsx
@@ -0,0 +1,143 @@
+"use client";
+
+import { useState, useEffect } from "react";
+import { useTicketStore } from "@/store/ticketStore";
+import Link from "next/link";
+
+interface Location {
+ venue: string;
+ address: string;
+ city: string;
+ country: string;
+ coordinates: {
+ lat: number;
+ lng: number;
+ };
+}
+
+interface Organizer {
+ name: string;
+ logo: string;
+ description: string;
+}
+
+interface TicketTier {
+ id: string;
+ name: string;
+ price: number;
+ description: string;
+ available: number;
+}
+
+interface Event {
+ id: string;
+ name: string;
+ type: string;
+ featured: boolean;
+ image: string;
+ headerImage: string;
+ dateTime: string;
+ endDateTime: string;
+ location: Location;
+ startingPrice: number;
+ organizer: Organizer;
+ ticketTiers: TicketTier[];
+}
+
+interface TicketSelectorProps {
+ event: Event;
+}
+
+export function TicketSelector({ event }: TicketSelectorProps) {
+ const { tickets, incrementQuantity, decrementQuantity } = useTicketStore();
+ const [total, setTotal] = useState(0);
+
+ useEffect(() => {
+ let newTotal = 0;
+ Object.entries(tickets).forEach(([key, ticket]) => {
+ if (key.startsWith(event.id)) {
+ newTotal += Number(ticket.price) * Number(ticket.quantity);
+ }
+ });
+ setTotal(newTotal);
+ }, [tickets, event.id]);
+
+ const getTicketQuantity = (ticketId: string) => {
+ const key = `${event.id}-${ticketId}`;
+ return tickets[key]?.quantity || 0;
+ };
+
+ return (
+
+
Select Tickets
+
+
+ {event.ticketTiers.map((tier) => (
+
+
+
+
{tier.name}
+
+ ${tier.price}
+
+
+
+ decrementQuantity(`${event.id}-${tier.id}`)}
+ className="w-8 h-8 rounded-full border border-gray-300 flex items-center justify-center text-gray-600 hover:border-[#099C77] hover:text-[#099C77] transition-colors"
+ disabled={getTicketQuantity(tier.id) === 0}
+ aria-label={`Decrease quantity for ${tier.name}`}
+ >
+ -
+
+
+ {getTicketQuantity(tier.id)}
+
+
+ incrementQuantity(`${event.id}-${tier.id}`, tier)
+ }
+ className="w-8 h-8 rounded-full border border-gray-300 flex items-center justify-center text-gray-600 hover:border-[#099C77] hover:text-[#099C77] transition-colors"
+ disabled={getTicketQuantity(tier.id) === tier.available}
+ aria-label={`Increase quantity for ${tier.name}`}
+ >
+ +
+
+
+
+
{tier.description}
+
+ ))}
+
+
+
+
+ Total:
+
+ ${total > 0 ? total.toFixed(2) : "0.00"}
+
+
+
+
+
+ Checkout
+
+
+
+
+ );
+}
diff --git a/src/components/ui/carousel.tsx b/src/components/ui/carousel.tsx
new file mode 100644
index 0000000..86a38a3
--- /dev/null
+++ b/src/components/ui/carousel.tsx
@@ -0,0 +1,280 @@
+"use client";
+
+import * as React from "react";
+import useEmblaCarousel, {
+ type UseEmblaCarouselType,
+} from "embla-carousel-react";
+import { ArrowLeft, ArrowRight } from "lucide-react";
+
+import { cn } from "@/lib/utils";
+import { Button } from "@/components/ui/button";
+
+type CarouselApi = UseEmblaCarouselType[1];
+type UseCarouselParameters = Parameters;
+type CarouselOptions = UseCarouselParameters[0];
+type CarouselPlugin = UseCarouselParameters[1];
+
+type CarouselProps = {
+ opts?: CarouselOptions;
+ plugins?: CarouselPlugin;
+ orientation?: "horizontal" | "vertical";
+ setApi?: (api: CarouselApi) => void;
+ autoplay?: boolean;
+ interval?: number;
+};
+
+type CarouselContextProps = {
+ carouselRef: ReturnType[0];
+ api: ReturnType[1];
+ scrollPrev: () => void;
+ scrollNext: () => void;
+ canScrollPrev: boolean;
+ canScrollNext: boolean;
+} & CarouselProps;
+
+const CarouselContext = React.createContext(null);
+
+function useCarousel() {
+ const context = React.useContext(CarouselContext);
+
+ if (!context) {
+ throw new Error("useCarousel must be used within a ");
+ }
+
+ return context;
+}
+
+const Carousel = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes & CarouselProps
+>(
+ (
+ {
+ orientation = "horizontal",
+ opts,
+ setApi,
+ plugins,
+ className,
+ children,
+ autoplay = false,
+ interval = 5000,
+ ...props
+ },
+ ref
+ ) => {
+ const [carouselRef, api] = useEmblaCarousel(
+ {
+ ...opts,
+ axis: orientation === "horizontal" ? "x" : "y",
+ },
+ plugins
+ );
+ const [canScrollPrev, setCanScrollPrev] = React.useState(false);
+ const [canScrollNext, setCanScrollNext] = React.useState(false);
+
+ const onSelect = React.useCallback((api: CarouselApi) => {
+ if (!api) {
+ return;
+ }
+
+ setCanScrollPrev(api.canScrollPrev());
+ setCanScrollNext(api.canScrollNext());
+ }, []);
+
+ const scrollPrev = React.useCallback(() => {
+ api?.scrollPrev();
+ }, [api]);
+
+ const scrollNext = React.useCallback(() => {
+ api?.scrollNext();
+ }, [api]);
+
+ const handleKeyDown = React.useCallback(
+ (event: React.KeyboardEvent) => {
+ if (event.key === "ArrowLeft") {
+ event.preventDefault();
+ scrollPrev();
+ } else if (event.key === "ArrowRight") {
+ event.preventDefault();
+ scrollNext();
+ }
+ },
+ [scrollPrev, scrollNext]
+ );
+
+ React.useEffect(() => {
+ if (!api || !setApi) {
+ return;
+ }
+
+ setApi(api);
+ }, [api, setApi]);
+
+ React.useEffect(() => {
+ if (!api) {
+ return;
+ }
+
+ onSelect(api);
+ api.on("reInit", onSelect);
+ api.on("select", onSelect);
+
+ return () => {
+ api?.off("select", onSelect);
+ };
+ }, [api, onSelect]);
+
+ React.useEffect(() => {
+ if (!api || !autoplay) return;
+
+ const autoplayInterval = setInterval(() => {
+ if (api.canScrollNext()) {
+ api.scrollNext();
+ } else {
+ api.scrollTo(0); // Reset to first slide
+ }
+ }, interval);
+
+ return () => clearInterval(autoplayInterval);
+ }, [api, autoplay, interval]);
+
+ return (
+
+
+ {children}
+
+
+ );
+ }
+);
+Carousel.displayName = "Carousel";
+
+const CarouselContent = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => {
+ const { carouselRef, orientation } = useCarousel();
+
+ return (
+
+ );
+});
+CarouselContent.displayName = "CarouselContent";
+
+const CarouselItem = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => {
+ const { orientation } = useCarousel();
+
+ return (
+
+ );
+});
+CarouselItem.displayName = "CarouselItem";
+
+const CarouselPrevious = React.forwardRef<
+ HTMLButtonElement,
+ React.ComponentProps
+>(({ className, variant = "outline", size = "icon", ...props }, ref) => {
+ const { orientation, scrollPrev, canScrollPrev } = useCarousel();
+
+ return (
+
+
+ Previous slide
+
+ );
+});
+CarouselPrevious.displayName = "CarouselPrevious";
+
+const CarouselNext = React.forwardRef<
+ HTMLButtonElement,
+ React.ComponentProps
+>(({ className, variant = "outline", size = "icon", ...props }, ref) => {
+ const { orientation, scrollNext, canScrollNext } = useCarousel();
+
+ return (
+
+
+ Next slide
+
+ );
+});
+CarouselNext.displayName = "CarouselNext";
+
+export {
+ type CarouselApi,
+ Carousel,
+ CarouselContent,
+ CarouselItem,
+ CarouselPrevious,
+ CarouselNext,
+};
diff --git a/src/components/ui/custom-tabs.tsx b/src/components/ui/custom-tabs.tsx
new file mode 100644
index 0000000..454b50f
--- /dev/null
+++ b/src/components/ui/custom-tabs.tsx
@@ -0,0 +1,98 @@
+"use client";
+
+import * as React from "react";
+import { cn } from "@/lib/utils";
+
+interface TabsContextType {
+ activeTab: string;
+ setActiveTab: (value: string) => void;
+}
+
+const TabsContext = React.createContext(undefined);
+
+interface TabsProps {
+ defaultValue: string;
+ onChange?: (value: string) => void;
+ children: React.ReactNode;
+ className?: string;
+}
+
+interface TabListProps {
+ tabs: {
+ label: string;
+ value: string;
+ }[];
+ className?: string;
+}
+
+interface TabSectionProps {
+ value: string;
+ children: React.ReactNode;
+}
+
+export const Tabs = ({
+ defaultValue,
+ onChange,
+ children,
+ className,
+}: TabsProps) => {
+ const [activeTab, setActiveTab] = React.useState(defaultValue);
+
+ const handleTabChange = (value: string) => {
+ setActiveTab(value);
+ onChange?.(value);
+ };
+
+ return (
+
+ {children}
+
+ );
+};
+
+const TabList = ({ tabs, className }: TabListProps) => {
+ const context = React.useContext(TabsContext);
+ if (!context) throw new Error("TabList must be used within Tabs");
+
+ const { activeTab, setActiveTab } = context;
+
+ return (
+
+
+ {tabs.map((tab) => (
+
setActiveTab(tab.value)}
+ className={cn(
+ "pb-4 relative font-semibold transition-colors flex-1",
+ "hover:text-gray-900",
+ activeTab === tab.value
+ ? "text-gray-900"
+ : "text-gray-500 hover:text-gray-700"
+ )}
+ >
+ {tab.label}
+ {activeTab === tab.value && (
+
+ )}
+
+ ))}
+
+
+ );
+};
+
+const TabSection = ({ value, children }: TabSectionProps) => {
+ const context = React.useContext(TabsContext);
+ if (!context) throw new Error("TabSection must be used within Tabs");
+
+ const { activeTab } = context;
+
+ if (activeTab !== value) return null;
+
+ return {children}
;
+};
+
+// Attach components to Tabs
+Tabs.List = TabList;
+Tabs.Section = TabSection;
diff --git a/src/components/ui/tabs.tsx b/src/components/ui/tabs.tsx
index 454b50f..4d030b1 100644
--- a/src/components/ui/tabs.tsx
+++ b/src/components/ui/tabs.tsx
@@ -1,98 +1,55 @@
-"use client";
-
-import * as React from "react";
-import { cn } from "@/lib/utils";
-
-interface TabsContextType {
- activeTab: string;
- setActiveTab: (value: string) => void;
-}
-
-const TabsContext = React.createContext(undefined);
-
-interface TabsProps {
- defaultValue: string;
- onChange?: (value: string) => void;
- children: React.ReactNode;
- className?: string;
-}
-
-interface TabListProps {
- tabs: {
- label: string;
- value: string;
- }[];
- className?: string;
-}
-
-interface TabSectionProps {
- value: string;
- children: React.ReactNode;
-}
-
-export const Tabs = ({
- defaultValue,
- onChange,
- children,
- className,
-}: TabsProps) => {
- const [activeTab, setActiveTab] = React.useState(defaultValue);
-
- const handleTabChange = (value: string) => {
- setActiveTab(value);
- onChange?.(value);
- };
-
- return (
-
- {children}
-
- );
-};
-
-const TabList = ({ tabs, className }: TabListProps) => {
- const context = React.useContext(TabsContext);
- if (!context) throw new Error("TabList must be used within Tabs");
-
- const { activeTab, setActiveTab } = context;
-
- return (
-
-
- {tabs.map((tab) => (
-
setActiveTab(tab.value)}
- className={cn(
- "pb-4 relative font-semibold transition-colors flex-1",
- "hover:text-gray-900",
- activeTab === tab.value
- ? "text-gray-900"
- : "text-gray-500 hover:text-gray-700"
- )}
- >
- {tab.label}
- {activeTab === tab.value && (
-
- )}
-
- ))}
-
-
- );
-};
-
-const TabSection = ({ value, children }: TabSectionProps) => {
- const context = React.useContext(TabsContext);
- if (!context) throw new Error("TabSection must be used within Tabs");
-
- const { activeTab } = context;
-
- if (activeTab !== value) return null;
-
- return {children}
;
-};
-
-// Attach components to Tabs
-Tabs.List = TabList;
-Tabs.Section = TabSection;
+"use client"
+
+import * as React from "react"
+import * as TabsPrimitive from "@radix-ui/react-tabs"
+
+import { cn } from "@/lib/utils"
+
+const Tabs = TabsPrimitive.Root
+
+const TabsList = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+TabsList.displayName = TabsPrimitive.List.displayName
+
+const TabsTrigger = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+TabsTrigger.displayName = TabsPrimitive.Trigger.displayName
+
+const TabsContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+TabsContent.displayName = TabsPrimitive.Content.displayName
+
+export { Tabs, TabsList, TabsTrigger, TabsContent }
diff --git a/src/const/data.json b/src/const/data.json
new file mode 100644
index 0000000..566f556
--- /dev/null
+++ b/src/const/data.json
@@ -0,0 +1,452 @@
+{
+ "events": [
+ {
+ "id": "tech-conf-2024",
+ "name": "Future of Tech Summit 2024",
+ "type": "Conference",
+ "featured": true,
+ "image": "https://images.unsplash.com/photo-1540575467063-178a50c2df87",
+ "headerImage": "https://images.unsplash.com/photo-1515187029135-18ee286d815b",
+ "dateTime": "2024-09-15T09:00:00",
+ "endDateTime": "2024-09-16T18:00:00",
+ "location": {
+ "venue": "Digital Innovation Center",
+ "address": "123 Tech Boulevard",
+ "city": "San Francisco",
+ "country": "USA",
+ "coordinates": {
+ "lat": 37.7749,
+ "lng": -122.4194
+ }
+ },
+ "startingPrice": 299,
+ "organizer": {
+ "name": "TechEvents Global",
+ "logo": "https://images.unsplash.com/photo-1599305445671-ac291c95aaa9",
+ "description": "Leading technology conference organizer since 2010"
+ },
+ "ticketTiers": [
+ {
+ "id": "regular",
+ "name": "Regular Access",
+ "price": 299,
+ "description": "Full conference access, lunch included",
+ "available": 200
+ },
+ {
+ "id": "vip",
+ "name": "VIP Experience",
+ "price": 599,
+ "description": "Premium seating, exclusive networking event, speaker meet & greet",
+ "available": 50
+ },
+ {
+ "id": "student",
+ "name": "Student Pass",
+ "price": 149,
+ "description": "Valid student ID required, full conference access",
+ "available": 100
+ }
+ ]
+ },
+ {
+ "id": "rock-festival-2024",
+ "name": "Summer Rock Festival",
+ "type": "Concert",
+ "featured": true,
+ "image": "https://images.unsplash.com/photo-1470229722913-7c0e2dbbafd3",
+ "headerImage": "https://images.unsplash.com/photo-1429962714451-bb934ecdc4ec",
+ "dateTime": "2024-07-20T16:00:00",
+ "endDateTime": "2024-07-20T23:00:00",
+ "location": {
+ "venue": "Sunset Arena",
+ "address": "789 Beach Road",
+ "city": "Miami",
+ "country": "USA",
+ "coordinates": {
+ "lat": 25.7617,
+ "lng": -80.1918
+ }
+ },
+ "startingPrice": 79,
+ "organizer": {
+ "name": "Rock Nation Events",
+ "logo": "https://images.unsplash.com/photo-1511671782779-c97d3d27a1d4",
+ "description": "Premier music festival organizer"
+ },
+ "ticketTiers": [
+ {
+ "id": "general",
+ "name": "General Admission",
+ "price": 79,
+ "description": "Standing area access",
+ "available": 1000
+ },
+ {
+ "id": "vip-concert",
+ "name": "VIP Zone",
+ "price": 199,
+ "description": "Premium viewing area, complimentary drinks",
+ "available": 200
+ },
+ {
+ "id": "backstage",
+ "name": "Backstage Experience",
+ "price": 499,
+ "description": "Meet the artists, exclusive merchandise",
+ "available": 50
+ }
+ ]
+ },
+ {
+ "id": "design-workshop",
+ "name": "UX Design Masterclass",
+ "type": "Workshop",
+ "featured": true,
+ "image": "https://images.unsplash.com/photo-1531403009284-440f080d1e12",
+ "headerImage": "https://images.unsplash.com/photo-1558403194-611308249627",
+ "dateTime": "2024-08-05T10:00:00",
+ "endDateTime": "2024-08-05T17:00:00",
+ "location": {
+ "venue": "Creative Hub",
+ "address": "456 Design Street",
+ "city": "London",
+ "country": "UK",
+ "coordinates": {
+ "lat": 51.5074,
+ "lng": -0.1278
+ }
+ },
+ "startingPrice": 199,
+ "organizer": {
+ "name": "Design Masters",
+ "logo": "https://images.unsplash.com/photo-1557821552-17105176677c",
+ "description": "Expert-led design workshops and courses"
+ },
+ "ticketTiers": [
+ {
+ "id": "workshop-basic",
+ "name": "Workshop Access",
+ "price": 199,
+ "description": "Full day workshop, materials included",
+ "available": 30
+ },
+ {
+ "id": "workshop-plus",
+ "name": "Workshop + Mentoring",
+ "price": 299,
+ "description": "Workshop access plus 1-hour personal mentoring session",
+ "available": 15
+ }
+ ]
+ },
+ {
+ "id": "startup-conference",
+ "name": "Startup Growth Summit",
+ "type": "Conference",
+ "featured": false,
+ "image": "https://images.unsplash.com/photo-1475721027785-f74eccf877e2",
+ "headerImage": "https://images.unsplash.com/photo-1559223607-a43c990c692c",
+ "dateTime": "2024-10-10T08:30:00",
+ "endDateTime": "2024-10-11T17:00:00",
+ "location": {
+ "venue": "Innovation Hub",
+ "address": "321 Startup Avenue",
+ "city": "Berlin",
+ "country": "Germany",
+ "coordinates": {
+ "lat": 52.5200,
+ "lng": 13.4050
+ }
+ },
+ "startingPrice": 249,
+ "organizer": {
+ "name": "Startup Network EU",
+ "logo": "https://images.unsplash.com/photo-1559223607-a43c990c692c",
+ "description": "Europe's leading startup community"
+ },
+ "ticketTiers": [
+ {
+ "id": "startup-regular",
+ "name": "Regular Access",
+ "price": 249,
+ "description": "Full conference access, networking lunch",
+ "available": 300
+ },
+ {
+ "id": "startup-premium",
+ "name": "Premium Access",
+ "price": 449,
+ "description": "VIP seating, investor meeting opportunities",
+ "available": 100
+ },
+ {
+ "id": "startup-founder",
+ "name": "Founder Package",
+ "price": 699,
+ "description": "Premium access, pitch session slot, mentor matching",
+ "available": 50
+ }
+ ]
+ },
+ {
+ "id": "jazz-night",
+ "name": "Evening of Jazz",
+ "type": "Concert",
+ "featured": false,
+ "image": "https://images.unsplash.com/photo-1415201364774-f6f0bb35f28f",
+ "headerImage": "https://images.unsplash.com/photo-1511192336575-5a79af67a629",
+ "dateTime": "2024-06-30T19:00:00",
+ "endDateTime": "2024-06-30T23:00:00",
+ "location": {
+ "venue": "Blue Note Club",
+ "address": "567 Jazz Street",
+ "city": "New Orleans",
+ "country": "USA",
+ "coordinates": {
+ "lat": 29.9511,
+ "lng": -90.0715
+ }
+ },
+ "startingPrice": 59,
+ "organizer": {
+ "name": "Jazz & Soul Productions",
+ "logo": "https://images.unsplash.com/photo-1415201364774-f6f0bb35f28f",
+ "description": "Authentic jazz experiences since 1990"
+ },
+ "ticketTiers": [
+ {
+ "id": "jazz-standard",
+ "name": "Standard Seating",
+ "price": 59,
+ "description": "General admission seating",
+ "available": 100
+ },
+ {
+ "id": "jazz-premium",
+ "name": "Premium Table",
+ "price": 129,
+ "description": "Front row table service, complimentary drink",
+ "available": 40
+ },
+ {
+ "id": "jazz-dinner",
+ "name": "Dinner Package",
+ "price": 189,
+ "description": "Premium seating with 3-course dinner",
+ "available": 30
+ }
+ ]
+ },
+ {
+ "id": "eth-global-2025",
+ "name": "ETHGlobal Summit",
+ "type": "Conference",
+ "featured": true,
+ "image": "https://images.unsplash.com/photo-1639762681485-074b7f938ba0",
+ "headerImage": "https://images.unsplash.com/photo-1642006953663-06f0387f5652",
+ "dateTime": "2025-03-15T09:00:00",
+ "endDateTime": "2025-03-17T18:00:00",
+ "location": {
+ "venue": "Crypto Convention Center",
+ "address": "888 Blockchain Boulevard",
+ "city": "Singapore",
+ "country": "Singapore",
+ "coordinates": {
+ "lat": 1.3521,
+ "lng": 103.8198
+ }
+ },
+ "startingPrice": 399,
+ "organizer": {
+ "name": "ETHGlobal",
+ "logo": "https://images.unsplash.com/photo-1622630998477-20aa696ecb05",
+ "description": "Leading Ethereum ecosystem event organizer"
+ },
+ "ticketTiers": [
+ {
+ "id": "builder",
+ "name": "Builder Pass",
+ "price": 399,
+ "description": "Full conference access, hackathon participation, workshops",
+ "available": 1000
+ },
+ {
+ "id": "investor",
+ "name": "Investor Connect",
+ "price": 1499,
+ "description": "VIP access, private networking lounge, pitch sessions",
+ "available": 100
+ }
+ ]
+ },
+ {
+ "id": "nft-art-festival",
+ "name": "Digital Renaissance: NFT Art Festival",
+ "type": "Exhibition",
+ "featured": false,
+ "image": "https://images.unsplash.com/photo-1620641788421-7a1c342ea42e",
+ "headerImage": "https://images.unsplash.com/photo-1638913662380-9799def8ffb1",
+ "dateTime": "2025-05-20T10:00:00",
+ "endDateTime": "2025-05-23T20:00:00",
+ "location": {
+ "venue": "Modern Art Gallery",
+ "address": "456 Digital Avenue",
+ "city": "Tokyo",
+ "country": "Japan",
+ "coordinates": {
+ "lat": 35.6762,
+ "lng": 139.6503
+ }
+ },
+ "startingPrice": 89,
+ "organizer": {
+ "name": "CryptoArt Collective",
+ "logo": "https://images.unsplash.com/photo-1620641788421-7a1c342ea42e",
+ "description": "Bridging traditional and digital art"
+ },
+ "ticketTiers": [
+ {
+ "id": "gallery-pass",
+ "name": "Gallery Access",
+ "price": 89,
+ "description": "Exhibition access, digital catalog",
+ "available": 500
+ },
+ {
+ "id": "collector",
+ "name": "Collector's Pass",
+ "price": 299,
+ "description": "VIP preview, artist meetups, exclusive NFT drop",
+ "available": 100
+ }
+ ]
+ },
+ {
+ "id": "food-wine-fest",
+ "name": "Mediterranean Food & Wine Festival",
+ "type": "Festival",
+ "featured": false,
+ "image": "https://images.unsplash.com/photo-1555396273-367ea4eb4db5",
+ "headerImage": "https://images.unsplash.com/photo-1507434965515-61832950c743",
+ "dateTime": "2025-06-10T12:00:00",
+ "endDateTime": "2025-06-12T22:00:00",
+ "location": {
+ "venue": "Coastal Gardens",
+ "address": "123 Seaside Promenade",
+ "city": "Barcelona",
+ "country": "Spain",
+ "coordinates": {
+ "lat": 41.3851,
+ "lng": 2.1734
+ }
+ },
+ "startingPrice": 129,
+ "organizer": {
+ "name": "Mediterranean Culinary Arts",
+ "logo": "https://images.unsplash.com/photo-1466637574441-749b8f19452f",
+ "description": "Celebrating Mediterranean cuisine and culture"
+ },
+ "ticketTiers": [
+ {
+ "id": "tasting-pass",
+ "name": "Tasting Pass",
+ "price": 129,
+ "description": "All tastings, standard wine pairings",
+ "available": 1000
+ },
+ {
+ "id": "chef-experience",
+ "name": "Chef's Table Experience",
+ "price": 399,
+ "description": "Premium tastings, cooking workshops, exclusive wine tastings",
+ "available": 150
+ }
+ ]
+ },
+ {
+ "id": "defi-summit-2025",
+ "name": "DeFi Innovation Summit",
+ "type": "Conference",
+ "featured": false,
+ "image": "https://images.unsplash.com/photo-1639762681485-074b7f938ba0",
+ "headerImage": "https://images.unsplash.com/photo-1642006953663-06f0387f5652",
+ "dateTime": "2025-09-05T09:00:00",
+ "endDateTime": "2025-09-07T18:00:00",
+ "location": {
+ "venue": "Dubai Financial Center",
+ "address": "Sheikh Zayed Road",
+ "city": "Dubai",
+ "country": "UAE",
+ "coordinates": {
+ "lat": 25.2048,
+ "lng": 55.2708
+ }
+ },
+ "startingPrice": 599,
+ "organizer": {
+ "name": "DeFi Alliance",
+ "logo": "https://images.unsplash.com/photo-1622630998477-20aa696ecb05",
+ "description": "Advancing decentralized finance innovation"
+ },
+ "ticketTiers": [
+ {
+ "id": "defi-standard",
+ "name": "Standard Access",
+ "price": 599,
+ "description": "Full conference access, networking events",
+ "available": 500
+ },
+ {
+ "id": "defi-whale",
+ "name": "Whale Pass",
+ "price": 2499,
+ "description": "VIP access, private investment sessions, founder dinner",
+ "available": 50
+ }
+ ]
+ },
+ {
+ "id": "wayne-charity-gala",
+ "name": "Wayne Enterprises Annual Charity Gala",
+ "type": "Gala",
+ "featured": true,
+ "image": "https://images.unsplash.com/photo-1519167758481-83f550bb49b3",
+ "headerImage": "https://images.unsplash.com/photo-1492684223066-81342ee5ff30",
+ "dateTime": "2025-10-31T19:00:00",
+ "endDateTime": "2025-11-01T02:00:00",
+ "location": {
+ "venue": "Wayne Manor",
+ "address": "1007 Mountain Drive",
+ "city": "Gotham",
+ "country": "USA",
+ "coordinates": {
+ "lat": 40.7128,
+ "lng": -74.0060
+ }
+ },
+ "startingPrice": 1000,
+ "organizer": {
+ "name": "Wayne Foundation",
+ "logo": "https://images.unsplash.com/photo-1481819613568-3701cbc70156",
+ "description": "Serving Gotham's community since 1939"
+ },
+ "ticketTiers": [
+ {
+ "id": "gala-standard",
+ "name": "Standard Admission",
+ "price": 1000,
+ "description": "Evening gala access, dinner included (mask required)",
+ "available": 300
+ },
+ {
+ "id": "gala-vip",
+ "name": "VIP Experience",
+ "price": 5000,
+ "description": "Private lounge access, butler service, potential Batman sighting not guaranteed",
+ "available": 50
+ }
+ ]
+ }
+ ]
+}
diff --git a/src/store/ticketStore.ts b/src/store/ticketStore.ts
new file mode 100644
index 0000000..6a0b012
--- /dev/null
+++ b/src/store/ticketStore.ts
@@ -0,0 +1,69 @@
+import { create } from "zustand";
+import { persist } from "zustand/middleware";
+
+interface TicketTier {
+ id: string;
+ name: string;
+ price: number;
+ description: string;
+ available: number;
+}
+
+interface StoredTicket {
+ id: string;
+ name: string;
+ price: number;
+ quantity: number;
+}
+
+interface TicketStore {
+ tickets: Record;
+ incrementQuantity: (key: string, ticket: TicketTier) => void;
+ decrementQuantity: (key: string) => void;
+ clearTickets: () => void;
+}
+
+export const useTicketStore = create()(
+ persist(
+ (set) => ({
+ tickets: {},
+ incrementQuantity: (key: string, ticket: TicketTier) =>
+ set((state) => {
+ const ticketExists = state.tickets[key];
+
+ return {
+ tickets: {
+ ...state.tickets,
+ [key]: {
+ id: key,
+ quantity: ticketExists ? ticketExists.quantity + 1 : 1,
+ price: ticket.price,
+ name: ticket.name,
+ },
+ },
+ };
+ }),
+ decrementQuantity: (key: string) =>
+ set((state) => {
+ if (state.tickets[key].quantity === 1) {
+ const { [key]: _, ...remainingTickets } = state.tickets;
+ return { tickets: remainingTickets };
+ }
+
+ return {
+ tickets: {
+ ...state.tickets,
+ [key]: {
+ ...state.tickets[key],
+ quantity: state.tickets[key].quantity - 1,
+ },
+ },
+ };
+ }),
+ clearTickets: () => set({ tickets: {} }),
+ }),
+ {
+ name: "ticket-storage",
+ }
+ )
+);
diff --git a/tsconfig.json b/tsconfig.json
index 7b28589..151d648 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -11,16 +11,17 @@
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
+ "target": "es2015",
"incremental": true,
"plugins": [
{
- "name": "next"
- }
+ "name": "next",
+ },
],
"paths": {
- "@/*": ["./src/*"]
- }
+ "@/*": ["./src/*"],
+ },
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
- "exclude": ["node_modules"]
+ "exclude": ["node_modules"],
}