From 42b3331641b3a70aa6264aad52e363b5a35bfe24 Mon Sep 17 00:00:00 2001 From: itziarZG Date: Tue, 3 Feb 2026 23:27:25 +0100 Subject: [PATCH] feat: add astro agenda component --- astro.config.mjs | 5 +- package.json | 5 + pnpm-lock.yaml | 428 ++++++++++++++++++++++++++++ src/components/agenda.tsx | 508 ++++++++++++++++++++++++++++++++++ src/pages/[lang]/agenda.astro | 17 ++ tsconfig.json | 12 +- 6 files changed, 972 insertions(+), 3 deletions(-) create mode 100644 src/components/agenda.tsx create mode 100644 src/pages/[lang]/agenda.astro diff --git a/astro.config.mjs b/astro.config.mjs index 6f86018..c1d9979 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -1,18 +1,21 @@ import { defineConfig } from 'astro/config' import tailwindcss from '@tailwindcss/vite' +import react from '@astrojs/react' export default defineConfig({ site: 'https://2026.es.pycon.org', base: '/', + integrations: [react()], vite: { plugins: [tailwindcss()], }, + output: 'static', i18n: { defaultLocale: 'es', locales: ['es', 'en', 'ca'], routing: { prefixDefaultLocale: true, - redirectToDefaultLocale: false + redirectToDefaultLocale: false, }, }, }) diff --git a/package.json b/package.json index d556089..d6f5ea4 100644 --- a/package.json +++ b/package.json @@ -15,10 +15,15 @@ "license": "ISC", "packageManager": "pnpm@10.10.0", "dependencies": { + "@astrojs/react": "^4.4.2", "@fontsource-variable/jetbrains-mono": "^5.2.8", "@tailwindcss/vite": "^4.1.18", + "@types/react": "^19.2.10", + "@types/react-dom": "^19.2.3", "animejs": "^4.2.2", "astro": "^5.16.8", + "react": "^19.2.4", + "react-dom": "^19.2.4", "tailwindcss": "^4.1.18" }, "devDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3051be6..feef988 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,18 +8,33 @@ importers: .: dependencies: + '@astrojs/react': + specifier: ^4.4.2 + version: 4.4.2(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(jiti@2.6.1)(lightningcss@1.30.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(yaml@2.8.2) '@fontsource-variable/jetbrains-mono': specifier: ^5.2.8 version: 5.2.8 '@tailwindcss/vite': specifier: ^4.1.18 version: 4.1.18(vite@6.4.1(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2)) + '@types/react': + specifier: ^19.2.10 + version: 19.2.10 + '@types/react-dom': + specifier: ^19.2.3 + version: 19.2.3(@types/react@19.2.10) animejs: specifier: ^4.2.2 version: 4.2.2 astro: specifier: ^5.16.8 version: 5.16.8(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.55.1)(typescript@5.9.3)(yaml@2.8.2) + react: + specifier: ^19.2.4 + version: 19.2.4 + react-dom: + specifier: ^19.2.4 + version: 19.2.4(react@19.2.4) tailwindcss: specifier: ^4.1.18 version: 4.1.18 @@ -70,6 +85,15 @@ packages: resolution: {integrity: sha512-q8VwfU/fDZNoDOf+r7jUnMC2//H2l0TuQ6FkGJL8vD8nw/q5KiL3DS1KKBI3QhI9UQhpJ5dc7AtqfbXWuOgLCQ==} engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0} + '@astrojs/react@4.4.2': + resolution: {integrity: sha512-1tl95bpGfuaDMDn8O3x/5Dxii1HPvzjvpL2YTuqOOrQehs60I2DKiDgh1jrKc7G8lv+LQT5H15V6QONQ+9waeQ==} + engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0} + peerDependencies: + '@types/react': ^17.0.50 || ^18.0.21 || ^19.0.0 + '@types/react-dom': ^17.0.17 || ^18.0.6 || ^19.0.0 + react: ^17.0.2 || ^18.0.0 || ^19.0.0 + react-dom: ^17.0.2 || ^18.0.0 || ^19.0.0 + '@astrojs/telemetry@3.3.0': resolution: {integrity: sha512-UFBgfeldP06qu6khs/yY+q1cDAaArM2/7AEIqQ9Cuvf7B1hNLq0xDrZkct+QoIGyjq56y8IaE2I3CTvG99mlhQ==} engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0} @@ -77,6 +101,44 @@ packages: '@astrojs/yaml2ts@0.2.2': resolution: {integrity: sha512-GOfvSr5Nqy2z5XiwqTouBBpy5FyI6DEe+/g/Mk5am9SjILN1S5fOEvYK0GuWHg98yS/dobP4m8qyqw/URW35fQ==} + '@babel/code-frame@7.29.0': + resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.29.0': + resolution: {integrity: sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.29.0': + resolution: {integrity: sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.29.0': + resolution: {integrity: sha512-vSH118/wwM/pLR38g/Sgk05sNtro6TlTJKuiMXDaZqPUfjTFcudpCOt00IhOfj+1BFAX+UFAlzCU+6WXr3GLFQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.28.6': + resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-globals@7.28.0': + resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.28.6': + resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.28.6': + resolution: {integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-plugin-utils@7.28.6': + resolution: {integrity: sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==} + engines: {node: '>=6.9.0'} + '@babel/helper-string-parser@7.27.1': resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} engines: {node: '>=6.9.0'} @@ -85,15 +147,52 @@ packages: resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} engines: {node: '>=6.9.0'} + '@babel/helper-validator-option@7.27.1': + resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.28.6': + resolution: {integrity: sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==} + engines: {node: '>=6.9.0'} + '@babel/parser@7.28.5': resolution: {integrity: sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==} engines: {node: '>=6.0.0'} hasBin: true + '@babel/parser@7.29.0': + resolution: {integrity: sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-transform-react-jsx-self@7.27.1': + resolution: {integrity: sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-source@7.27.1': + resolution: {integrity: sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/template@7.28.6': + resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.29.0': + resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==} + engines: {node: '>=6.9.0'} + '@babel/types@7.28.5': resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} engines: {node: '>=6.9.0'} + '@babel/types@7.29.0': + resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} + engines: {node: '>=6.9.0'} + '@capsizecss/unpack@4.0.0': resolution: {integrity: sha512-VERIM64vtTP1C4mxQ5thVT9fK0apjPFobqybMtA1UdUujWka24ERHbRHFGmpbbhp73MhV+KSsHQH9C6uOTdEQA==} engines: {node: '>=18'} @@ -437,6 +536,9 @@ packages: '@oslojs/encoding@1.1.0': resolution: {integrity: sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==} + '@rolldown/pluginutils@1.0.0-beta.27': + resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==} + '@rollup/pluginutils@5.3.0': resolution: {integrity: sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==} engines: {node: '>=14.0.0'} @@ -682,6 +784,18 @@ packages: peerDependencies: vite: ^5.2.0 || ^6 || ^7 + '@types/babel__core@7.20.5': + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + + '@types/babel__generator@7.27.0': + resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} + + '@types/babel__template@7.4.4': + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} + + '@types/babel__traverse@7.28.0': + resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} + '@types/debug@4.1.12': resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} @@ -700,12 +814,26 @@ packages: '@types/nlcst@2.0.3': resolution: {integrity: sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA==} + '@types/react-dom@19.2.3': + resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} + peerDependencies: + '@types/react': ^19.2.0 + + '@types/react@19.2.10': + resolution: {integrity: sha512-WPigyYuGhgZ/cTPRXB2EwUw+XvsRA3GqHlsP4qteqrnnjDrApbS7MxcGr/hke5iUoeB7E/gQtrs9I37zAJ0Vjw==} + '@types/unist@3.0.3': resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} '@ungap/structured-clone@1.3.0': resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + '@vitejs/plugin-react@4.7.0': + resolution: {integrity: sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + '@volar/kit@2.4.27': resolution: {integrity: sha512-ilZoQDMLzqmSsImJRWx4YiZ4FcvvPrPnFVmL6hSsIWB6Bn3qc7k88J9yP32dagrs5Y8EXIlvvD/mAFaiuEOACQ==} peerDependencies: @@ -799,6 +927,10 @@ packages: base-64@1.0.0: resolution: {integrity: sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==} + baseline-browser-mapping@2.9.19: + resolution: {integrity: sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==} + hasBin: true + boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} @@ -806,10 +938,18 @@ packages: resolution: {integrity: sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw==} engines: {node: '>=18'} + browserslist@4.28.1: + resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + camelcase@8.0.0: resolution: {integrity: sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==} engines: {node: '>=16'} + caniuse-lite@1.0.30001767: + resolution: {integrity: sha512-34+zUAMhSH+r+9eKmYG+k2Rpt8XttfE4yXAjoZvkAPs15xcYQhyBYdalJ65BzivAvGRMViEjy6oKr/S91loekQ==} + ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} @@ -863,6 +1003,9 @@ packages: common-ancestor-path@1.0.1: resolution: {integrity: sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w==} + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + cookie-es@1.2.2: resolution: {integrity: sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg==} @@ -897,6 +1040,9 @@ packages: resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + debug@4.4.3: resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} engines: {node: '>=6.0'} @@ -957,6 +1103,9 @@ packages: resolution: {integrity: sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==} engines: {node: '>=4'} + electron-to-chromium@1.5.286: + resolution: {integrity: sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A==} + emmet@2.4.11: resolution: {integrity: sha512-23QPJB3moh/U9sT4rQzGgeyyGIrcM+GH5uVYg2C6wZIxAIJq7Ng3QLT79tl8FUwDXhyq9SusfknOrofAKqvgyQ==} @@ -1037,6 +1186,10 @@ packages: engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + get-caller-file@2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} @@ -1125,13 +1278,26 @@ packages: resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + js-yaml@4.1.1: resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} hasBin: true + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + json-schema-traverse@1.0.0: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + jsonc-parser@2.3.1: resolution: {integrity: sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg==} @@ -1225,6 +1391,9 @@ packages: lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} @@ -1391,6 +1560,9 @@ packages: node-mock-http@1.0.4: resolution: {integrity: sha512-8DY+kFsDkNXy1sJglUfuODx1/opAGJGyrTuFqEoN90oRc2Vk0ZbD4K2qmKXBBEhZQzdKHIVfEJpDU8Ak2NJEvQ==} + node-releases@2.0.27: + resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} + normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} @@ -1475,6 +1647,19 @@ packages: radix3@1.1.2: resolution: {integrity: sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==} + react-dom@19.2.4: + resolution: {integrity: sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==} + peerDependencies: + react: ^19.2.4 + + react-refresh@0.17.0: + resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==} + engines: {node: '>=0.10.0'} + + react@19.2.4: + resolution: {integrity: sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==} + engines: {node: '>=0.10.0'} + readdirp@4.1.2: resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} engines: {node: '>= 14.18.0'} @@ -1557,6 +1742,13 @@ packages: resolution: {integrity: sha512-1n3r/tGXO6b6VXMdFT54SHzT9ytu9yr7TaELowdYpMqY/Ao7EnlQGmAQ1+RatX7Tkkdm6hONI2owqNx2aZj5Sw==} engines: {node: '>=11.0.0'} + scheduler@0.27.0: + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + semver@7.7.3: resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} engines: {node: '>=10'} @@ -1766,6 +1958,12 @@ packages: uploadthing: optional: true + update-browserslist-db@1.2.3: + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + vfile-location@5.0.3: resolution: {integrity: sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==} @@ -1941,6 +2139,9 @@ packages: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + yaml-language-server@1.19.2: resolution: {integrity: sha512-9F3myNmJzUN/679jycdMxqtydPSDRAarSj3wPiF7pchEPnO9Dg07Oc+gIYLqXR4L+g+FSEVXXv2+mr54StLFOg==} hasBin: true @@ -2065,6 +2266,29 @@ snapshots: dependencies: prismjs: 1.30.0 + '@astrojs/react@4.4.2(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(jiti@2.6.1)(lightningcss@1.30.2)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(yaml@2.8.2)': + dependencies: + '@types/react': 19.2.10 + '@types/react-dom': 19.2.3(@types/react@19.2.10) + '@vitejs/plugin-react': 4.7.0(vite@6.4.1(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2)) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + ultrahtml: 1.6.0 + vite: 6.4.1(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2) + transitivePeerDependencies: + - '@types/node' + - jiti + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + '@astrojs/telemetry@3.3.0': dependencies: ci-info: 4.3.1 @@ -2081,19 +2305,127 @@ snapshots: dependencies: yaml: 2.8.2 + '@babel/code-frame@7.29.0': + dependencies: + '@babel/helper-validator-identifier': 7.28.5 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.29.0': {} + + '@babel/core@7.29.0': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.0 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) + '@babel/helpers': 7.28.6 + '@babel/parser': 7.29.0 + '@babel/template': 7.28.6 + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.29.0': + dependencies: + '@babel/parser': 7.29.0 + '@babel/types': 7.29.0 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-compilation-targets@7.28.6': + dependencies: + '@babel/compat-data': 7.29.0 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.28.1 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-globals@7.28.0': {} + + '@babel/helper-module-imports@7.28.6': + dependencies: + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-module-imports': 7.28.6 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-plugin-utils@7.28.6': {} + '@babel/helper-string-parser@7.27.1': {} '@babel/helper-validator-identifier@7.28.5': {} + '@babel/helper-validator-option@7.27.1': {} + + '@babel/helpers@7.28.6': + dependencies: + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + '@babel/parser@7.28.5': dependencies: '@babel/types': 7.28.5 + '@babel/parser@7.29.0': + dependencies: + '@babel/types': 7.29.0 + + '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/template@7.28.6': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/parser': 7.29.0 + '@babel/types': 7.29.0 + + '@babel/traverse@7.29.0': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.0 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.29.0 + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + '@babel/types@7.28.5': dependencies: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 + '@babel/types@7.29.0': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + '@capsizecss/unpack@4.0.0': dependencies: fontkitten: 1.0.0 @@ -2324,6 +2656,8 @@ snapshots: '@oslojs/encoding@1.1.0': {} + '@rolldown/pluginutils@1.0.0-beta.27': {} + '@rollup/pluginutils@5.3.0(rollup@4.55.1)': dependencies: '@types/estree': 1.0.8 @@ -2508,6 +2842,27 @@ snapshots: tailwindcss: 4.1.18 vite: 6.4.1(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2) + '@types/babel__core@7.20.5': + dependencies: + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 + '@types/babel__generator': 7.27.0 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.28.0 + + '@types/babel__generator@7.27.0': + dependencies: + '@babel/types': 7.28.5 + + '@types/babel__template@7.4.4': + dependencies: + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 + + '@types/babel__traverse@7.28.0': + dependencies: + '@babel/types': 7.28.5 + '@types/debug@4.1.12': dependencies: '@types/ms': 2.1.0 @@ -2528,10 +2883,30 @@ snapshots: dependencies: '@types/unist': 3.0.3 + '@types/react-dom@19.2.3(@types/react@19.2.10)': + dependencies: + '@types/react': 19.2.10 + + '@types/react@19.2.10': + dependencies: + csstype: 3.2.3 + '@types/unist@3.0.3': {} '@ungap/structured-clone@1.3.0': {} + '@vitejs/plugin-react@4.7.0(vite@6.4.1(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2))': + dependencies: + '@babel/core': 7.29.0 + '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.29.0) + '@rolldown/pluginutils': 1.0.0-beta.27 + '@types/babel__core': 7.20.5 + react-refresh: 0.17.0 + vite: 6.4.1(jiti@2.6.1)(lightningcss@1.30.2)(yaml@2.8.2) + transitivePeerDependencies: + - supports-color + '@volar/kit@2.4.27(typescript@5.9.3)': dependencies: '@volar/language-service': 2.4.27 @@ -2730,6 +3105,8 @@ snapshots: base-64@1.0.0: {} + baseline-browser-mapping@2.9.19: {} + boolbase@1.0.0: {} boxen@8.0.1: @@ -2743,8 +3120,18 @@ snapshots: widest-line: 5.0.0 wrap-ansi: 9.0.2 + browserslist@4.28.1: + dependencies: + baseline-browser-mapping: 2.9.19 + caniuse-lite: 1.0.30001767 + electron-to-chromium: 1.5.286 + node-releases: 2.0.27 + update-browserslist-db: 1.2.3(browserslist@4.28.1) + camelcase@8.0.0: {} + caniuse-lite@1.0.30001767: {} + ccount@2.0.1: {} chalk@5.6.2: {} @@ -2783,6 +3170,8 @@ snapshots: common-ancestor-path@1.0.1: {} + convert-source-map@2.0.0: {} + cookie-es@1.2.2: {} cookie@1.1.1: {} @@ -2817,6 +3206,8 @@ snapshots: dependencies: css-tree: 2.2.1 + csstype@3.2.3: {} + debug@4.4.3: dependencies: ms: 2.1.3 @@ -2867,6 +3258,8 @@ snapshots: dset@3.1.4: {} + electron-to-chromium@1.5.286: {} + emmet@2.4.11: dependencies: '@emmetio/abbreviation': 2.3.3 @@ -2951,6 +3344,8 @@ snapshots: fsevents@2.3.3: optional: true + gensync@1.0.0-beta.2: {} + get-caller-file@2.0.5: {} get-east-asian-width@1.4.0: {} @@ -3084,12 +3479,18 @@ snapshots: jiti@2.6.1: {} + js-tokens@4.0.0: {} + js-yaml@4.1.1: dependencies: argparse: 2.0.1 + jsesc@3.1.0: {} + json-schema-traverse@1.0.0: {} + json5@2.2.3: {} + jsonc-parser@2.3.1: {} jsonc-parser@3.3.1: {} @@ -3153,6 +3554,10 @@ snapshots: lru-cache@10.4.3: {} + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + magic-string@0.30.21: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -3498,6 +3903,8 @@ snapshots: node-mock-http@1.0.4: {} + node-releases@2.0.27: {} + normalize-path@3.0.0: {} nth-check@2.1.1: @@ -3581,6 +3988,15 @@ snapshots: radix3@1.1.2: {} + react-dom@19.2.4(react@19.2.4): + dependencies: + react: 19.2.4 + scheduler: 0.27.0 + + react-refresh@0.17.0: {} + + react@19.2.4: {} + readdirp@4.1.2: {} regex-recursion@6.0.2: @@ -3731,6 +4147,10 @@ snapshots: sax@1.4.4: {} + scheduler@0.27.0: {} + + semver@6.3.1: {} + semver@7.7.3: {} sharp@0.34.5: @@ -3932,6 +4352,12 @@ snapshots: ofetch: 1.5.1 ufo: 1.6.2 + update-browserslist-db@1.2.3(browserslist@4.28.1): + dependencies: + browserslist: 4.28.1 + escalade: 3.2.0 + picocolors: 1.1.1 + vfile-location@5.0.3: dependencies: '@types/unist': 3.0.3 @@ -4086,6 +4512,8 @@ snapshots: y18n@5.0.8: {} + yallist@3.1.1: {} + yaml-language-server@1.19.2: dependencies: '@vscode/l10n': 0.0.18 diff --git a/src/components/agenda.tsx b/src/components/agenda.tsx new file mode 100644 index 0000000..711e2b6 --- /dev/null +++ b/src/components/agenda.tsx @@ -0,0 +1,508 @@ +import { useEffect, useState, useMemo } from 'react' + +interface LocalizedString { + es: string + en?: string + [key: string]: string | undefined +} + +interface Speaker { + code: string + name: string + avatar: string +} + +interface Room { + id: number + name: LocalizedString +} + +interface Track { + id: number + color: string + name: LocalizedString +} + +interface Talk { + id: number + title: string | LocalizedString + start: string + end: string + room: number + track: number | null + speakers: string[] + slot_type: 'break' | 'talk' | 'workshop' | string + duration: number + language?: string +} + +interface ScheduleData { + talks: Talk[] + tracks: Track[] + rooms: Room[] + speakers: Speaker[] +} + +interface ScheduleProps { + lang?: string + scheduleData: ScheduleData +} + +// Utility to get localized text +const getLocalizedText = (obj: string | LocalizedString | undefined, lang: string): string => { + if (!obj) return '' + if (typeof obj === 'string') return obj + return obj[lang] || obj.es || '' +} + +export default function Schedule({ lang = 'es', scheduleData }: ScheduleProps) { + // If no data is passed (fallback or error), use empty structure + const data = scheduleData || { + talks: [], + tracks: [], + rooms: [], + speakers: [], + } + + const [selectedTracks, setSelectedTracks] = useState([]) // Array of track IDs + const [selectedLangs, setSelectedLangs] = useState([]) // Array of lang codes + const [searchQuery, setSearchQuery] = useState('') + const [activeDay, setActiveDay] = useState('2025-10-18') + const [favs, setFavs] = useState([]) + const [isFilterOpen, setIsFilterOpen] = useState(false) + + // Load favorites from local storage + useEffect(() => { + try { + const saved = JSON.parse(localStorage.getItem('pycones_favs') || '[]') + setFavs(saved) + } catch (e) { + console.error('Failed to load favorites', e) + } + }, []) + + const toggleFav = (id: number) => { + const newFavs = favs.includes(id) ? favs.filter((f) => f !== id) : [...favs, id] + setFavs(newFavs) + localStorage.setItem('pycones_favs', JSON.stringify(newFavs)) + } + + // Derived state: Filtered Talks + const filteredTalks = useMemo(() => { + return data.talks.filter((talk) => { + // 1. Day + const isSameDay = talk.start.startsWith(activeDay) + + // 2. Tracks + let isSameTrack = true + if (selectedTracks.length > 0) { + // If talk has no track (e.g. break), should we hide it if tracks are selected? + // Usually filters are exclusive. If I select "Python", I want "Python" talks. + // Breaks usually don't have tracks. Let's assume if track is null it doesn't match if tracks are selected. + // Or maybe we want to ALWAYS show breaks? + // The original code was: isSameTrack = selectedTracks.includes(talk.track) + // This implies if talk.track is null/undefined, it returns false. + isSameTrack = talk.track !== null && selectedTracks.includes(talk.track) + } + + // 3. Language + let isSameLang = true + if (selectedLangs.length > 0) { + if (talk.language) { + isSameLang = selectedLangs.includes(talk.language) + } else { + // If no language specified (e.g. breaks), keep it? + // Original code: isSameLang = true + isSameLang = true + } + } + + // 4. Search + let isMatchSearch = true + if (searchQuery.trim()) { + const q = searchQuery.toLowerCase() + const title = getLocalizedText(talk.title, lang).toLowerCase() + const speakers = (talk.speakers || []) + .map((code) => { + const s = data.speakers.find((sp) => sp.code === code) + return s ? s.name.toLowerCase() : '' + }) + .join(' ') + + isMatchSearch = title.includes(q) || speakers.includes(q) + } + + return isSameDay && isSameTrack && isSameLang && isMatchSearch + }) + }, [data.talks, data.speakers, activeDay, selectedTracks, selectedLangs, searchQuery, lang]) + + // Filter handlers + const toggleTrack = (id: number) => { + if (selectedTracks.includes(id)) { + setSelectedTracks(selectedTracks.filter((t) => t !== id)) + } else { + setSelectedTracks([...selectedTracks, id]) + } + } + + const toggleLang = (code: string) => { + if (selectedLangs.includes(code)) { + setSelectedLangs(selectedLangs.filter((l) => l !== code)) + } else { + setSelectedLangs([...selectedLangs, code]) + } + } + + const clearFilters = () => { + setSelectedTracks([]) + setSelectedLangs([]) + setSearchQuery('') + } + + // Unique days found in data could be computed, but hardcoded for now as per original + const DAYS = ['2025-10-17', '2025-10-18', '2025-10-19'] + + return ( +
+ {/* Date Navigation & Filter Toggle */} + + + {/* Main Grid */} +
+ {data.rooms.map((room) => { + const roomTalks = filteredTalks + .filter((t) => t.room === room.id) + .sort((a, b) => a.start.localeCompare(b.start)) + + if (roomTalks.length === 0) return null + + return ( +
+

+ {getLocalizedText(room.name, lang)} +

+ +
+ {roomTalks.map((session) => ( + toggleFav(session.id)} + tracks={data.tracks} + speakers={data.speakers} + /> + ))} +
+
+ ) + })} +
+ + {/* Filter Modal */} + setIsFilterOpen(false)} + tracks={data.tracks} + selectedTracks={selectedTracks} + toggleTrack={toggleTrack} + selectedLangs={selectedLangs} + toggleLang={toggleLang} + searchQuery={searchQuery} + setSearchQuery={setSearchQuery} + clearFilters={clearFilters} + resultCount={filteredTalks.length} + lang={lang} + /> +
+ ) +} + +function SessionCard({ + session, + lang, + isFav, + toggleFav, + tracks, + speakers, +}: { + session: Talk + lang: string + isFav: boolean + toggleFav: () => void + tracks: Track[] + speakers: Speaker[] +}) { + const isBreak = session.slot_type === 'break' + const track = tracks.find((t) => t.id === session.track) + const sessionSpeakers = (session.speakers || []) + .map((code) => speakers.find((s) => s.code === code)) + .filter((s): s is Speaker => !!s) + + return ( +
+
+ + {new Date(session.start).toLocaleTimeString([], { + hour: '2-digit', + minute: '2-digit', + })} + {' - '} + {new Date(new Date(session.start).getTime() + session.duration * 60000).toLocaleTimeString([], { + hour: '2-digit', + minute: '2-digit', + })} + + {!isBreak && ( + + )} +
+ +
+

+ {getLocalizedText(session.title, lang)} +

+ + {!isBreak && ( + <> + {sessionSpeakers.length > 0 && ( +
+ {sessionSpeakers.map((s) => ( +
+ {s.avatar && } + {s.name} +
+ ))} +
+ )} + + {track && ( +
+ + {getLocalizedText(track.name, lang)} + +
+ )} + + )} +
+
+ ) +} + +function FilterModal({ + isOpen, + onClose, + tracks, + selectedTracks, + toggleTrack, + selectedLangs, + toggleLang, + searchQuery, + setSearchQuery, + clearFilters, + resultCount, + lang, +}: { + isOpen: boolean + onClose: () => void + tracks: Track[] + selectedTracks: number[] + toggleTrack: (id: number) => void + selectedLangs: string[] + toggleLang: (code: string) => void + searchQuery: string + setSearchQuery: (q: string) => void + clearFilters: () => void + resultCount: number + lang: string +}) { + if (!isOpen) return null + + return ( +
+ +
+

Filters

+ +
+ +
+ {/* Tracks */} +
+ +
+ {tracks.map((track) => { + const active = selectedTracks.includes(track.id) + return ( + + ) + })} +
+
+ + {/* Languages */} +
+ +
+ {[ + { code: 'en', label: 'English' }, + { code: 'es', label: 'Español' }, + { code: 'ca', label: 'Català' }, + ].map((langItem) => { + const active = selectedLangs.includes(langItem.code) + return ( + + ) + })} +
+
+ + {/* Search */} +
+ +
+ + setSearchQuery(e.target.value)} + placeholder="Search titles, speakers..." + className="w-full bg-[#181818] border border-[#333] text-white rounded-lg py-2.5 pl-10 pr-4 focus:outline-none focus:border-green-500 focus:ring-1 focus:ring-green-500 transition-all placeholder:text-gray-600" + /> +
+
+
+ +
+ + +
+
+
+ ) +} + +function FilterIcon({ className }: { className?: string }) { + return ( + + + + ) +} + +function SearchIcon({ className }: { className?: string }) { + return ( + + + + + ) +} diff --git a/src/pages/[lang]/agenda.astro b/src/pages/[lang]/agenda.astro new file mode 100644 index 0000000..16ca4fb --- /dev/null +++ b/src/pages/[lang]/agenda.astro @@ -0,0 +1,17 @@ +--- +import Layout from '@/layouts/Layout.astro'; +import Schedule from '@/components/agenda.tsx'; + +export function getStaticPaths() { + return [{ params: { lang: 'es' } }, { params: { lang: 'en' } }, { params: { lang: 'ca' } }] +} + +const { lang } = Astro.params; + +const response = await fetch('https://pretalx.com/pycones-2025/schedule/widgets/schedule.json') +const scheduleData = await response.json() +--- + + + + \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 29ac3b3..2d2272b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,5 +2,13 @@ "extends": "astro/tsconfigs/base", "include": [ "src/**/*" - ] -} + ], + "compilerOptions": { + "baseUrl": "./src", + "paths": { + "@/*": ["./*"] + }, + "jsx": "react-jsx", + "jsxImportSource": "react" + } +} \ No newline at end of file