├── .github ├── pull_request_template.md └── workflows │ └── eslint.yml ├── .gitignore ├── .vscode ├── extensions.json └── launch.json ├── LICENSE ├── README.md ├── SECURITY.md ├── astro.config.mjs ├── package-lock.json ├── package.json ├── public ├── browser.png ├── favicon.svg └── icon.png ├── src ├── assets │ ├── discord-logo.png │ ├── github-mark.svg │ ├── github-white.svg │ ├── icon.png │ ├── logo.png │ ├── midufest.svg │ ├── teach.svg │ ├── ticket-frame.svg │ ├── twitch.svg │ ├── twitter-logo.svg │ ├── virtual.svg │ └── yt-logo.svg ├── components │ ├── About │ │ └── index.astro │ ├── Card │ │ ├── index.tsx │ │ └── types.ts │ ├── Event │ │ ├── components │ │ │ ├── Schedule.tsx │ │ │ ├── Speakers.tsx │ │ │ ├── Sponsors.tsx │ │ │ └── index.tsx │ │ └── index.tsx │ ├── EventNavBar │ │ └── index.jsx │ ├── Footer │ │ └── index.astro │ ├── GetTicket │ │ └── index.tsx │ ├── GithubAuth │ │ └── index.tsx │ ├── Home │ │ └── Home.astro │ ├── How │ │ └── index.astro │ ├── NavBar │ │ ├── index.tsx │ │ └── items.ts │ ├── PrimaryButton │ │ └── index.tsx │ ├── Profile │ │ └── index.tsx │ ├── ScheduleItem │ │ └── index.tsx │ ├── SingUp │ │ └── index.tsx │ ├── SocialModal │ │ └── index.tsx │ ├── Speaker │ │ └── index.astro │ ├── Sponsors │ │ └── index.tsx │ ├── Ticket │ │ └── index.tsx │ ├── Tickets │ │ ├── components │ │ │ ├── NotSession │ │ │ │ └── index.tsx │ │ │ ├── Ticket │ │ │ │ └── index.tsx │ │ │ └── index.tsx │ │ ├── index.css │ │ └── index.tsx │ ├── TimerCountdown │ │ └── index.tsx │ └── index.tsx ├── env.d.ts ├── hooks │ ├── useUser.tsx │ └── useUserSubscriber.ts ├── interfaces │ ├── index.ts │ ├── schedule.ts │ ├── speaker.ts │ ├── sponsor.ts │ ├── tickets.ts │ └── user.ts ├── layouts │ ├── EventFrame.astro │ └── Layout.astro ├── pages │ ├── code-of-conduct │ │ └── index.astro │ ├── events │ │ ├── [id].astro │ │ └── index.astro │ ├── index-conf.astro │ ├── index.astro │ ├── profile │ │ └── index.astro │ ├── speakers │ │ ├── [name].astro │ │ └── index.astro │ └── tickets │ │ └── index.astro └── services │ ├── auth.ts │ ├── config.ts │ ├── schedule.ts │ └── sentry.ts ├── tailwind.config.cjs └── tsconfig.json /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | THIS PROJECT IS IN MAINTENANCE MODE. We accept pull requests for Bug, Fixes, Featured, Docs, Refactor, etc... 2 | 3 | 4 | 5 | ## Description 6 | 7 | 8 | ## Related Issue 9 | 10 | 11 | 12 | 13 | 14 | ## Motivation and Context 15 | 16 | 17 | 18 | ## How Has This Been Tested? 19 | 20 | 21 | 22 | 23 | ## Screenshots (if appropriate): 24 | 25 | -------------------------------------------------------------------------------- /.github/workflows/eslint.yml: -------------------------------------------------------------------------------- 1 | # This workflow uses actions that are not certified by GitHub. 2 | # They are provided by a third-party and are governed by 3 | # separate terms of service, privacy policy, and support 4 | # documentation. 5 | # ESLint is a tool for identifying and reporting on patterns 6 | # found in ECMAScript/JavaScript code. 7 | # More details at https://github.com/eslint/eslint 8 | # and https://eslint.org 9 | 10 | name: ESLint 11 | 12 | on: 13 | push: 14 | branches: [ "main" ] 15 | pull_request: 16 | # The branches below must be a subset of the branches above 17 | branches: [ "main" ] 18 | schedule: 19 | - cron: '24 5 * * 5' 20 | 21 | jobs: 22 | eslint: 23 | name: Run eslint scanning 24 | runs-on: ubuntu-latest 25 | permissions: 26 | contents: read 27 | security-events: write 28 | actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status 29 | steps: 30 | - name: Checkout code 31 | uses: actions/checkout@v3 32 | 33 | - name: Install ESLint 34 | run: | 35 | npm install eslint@8.10.0 36 | npm install @microsoft/eslint-formatter-sarif@2.1.7 37 | 38 | - name: Run ESLint 39 | run: npx eslint . 40 | --config .eslintrc.js 41 | --ext .js,.jsx,.ts,.tsx 42 | --format @microsoft/eslint-formatter-sarif 43 | --output-file eslint-results.sarif 44 | continue-on-error: true 45 | 46 | - name: Upload analysis results to GitHub 47 | uses: github/codeql-action/upload-sarif@v2 48 | with: 49 | sarif_file: eslint-results.sarif 50 | wait-for-processing: true 51 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # build output 2 | dist/ 3 | 4 | # generated types 5 | .astro/ 6 | 7 | # dependencies 8 | node_modules/ 9 | 10 | # logs 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | pnpm-debug.log* 15 | 16 | # environment variables 17 | .env 18 | .env.production 19 | 20 | # macOS-specific files 21 | .DS_Store 22 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["astro-build.astro-vscode"], 3 | "unwantedRecommendations": [] 4 | } 5 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "command": "./node_modules/.bin/astro dev", 6 | "name": "Development server", 7 | "request": "launch", 8 | "type": "node-terminal" 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | # Licencia MIT 2 | 3 | Copyright (c) 2023 Tech Conf - Peru 🇵🇪 - Linder Hassinger 4 | 5 | Se concede permiso, de forma gratuita, a cualquier persona que obtenga una copia de este software y de los archivos de documentación asociados (el "Software"), para utilizar el Software sin restricción, incluyendo, sin limitación, los derechos para usar, copiar, modificar, fusionar, publicar, distribuir, sublicenciar y/o vender copias del Software, y para permitir a las personas a las que se les proporcione el Software que lo hagan, de acuerdo con las siguientes condiciones: 6 | 7 | El aviso de derecho de autor anterior y este aviso de permiso se incluirán en todas las copias o partes sustanciales del Software. 8 | 9 | EL SOFTWARE SE PROPORCIONA "COMO ESTÁ", SIN GARANTÍA DE NINGÚN TIPO, EXPRESA O IMPLÍCITA, INCLUYENDO PERO NO LIMITADO A GARANTÍAS DE COMERCIALIZACIÓN, IDONEIDAD PARA UN PROPÓSITO PARTICULAR Y NO INFRACCIÓN. EN NINGÚN CASO LOS AUTORES O TITULARES DE LOS DERECHOS DE AUTOR SERÁN RESPONSABLES DE CUALQUIER RECLAMO, DAÑO U OTRA RESPONSABILIDAD, YA SEA EN UNA ACCIÓN CONTRACTUAL, AGRAVIO O CUALQUIER OTRA ACCIÓN, QUE SURJA DE, O EN RELACIÓN CON EL SOFTWARE O EL USO U OTROS TRATOS EN EL SOFTWARE. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tech Conf - Peru 🇵🇪 2 | 3 | Buscamos compartir conocimiento y experiencias de la comunidad de software en Perú. 4 | 5 | Inicialmente, este proyecto fue creado para la conferencia [TechConf](https://www.techconf.lat/), pero ahora es un proyecto de código abierto para que cualquier conferencia de software en Perú pueda usarlo. 6 | 7 | Planeamos empezar a hacer charlas en Lima, Perú 🇵🇪, pero buscamos expandirnos 8 | y poder compartir esto en todo el país. 9 | 10 | ## ❤️ Comunidad 11 | 12 | Si quieres unirte a la comunidad de software en Perú, puedes unirte a nuestro [Discord](https://discord.gg/mAxZPpxq9e) y así poder compartir conocimiento y experiencias. 13 | 14 | ## 🖇️ Colabora 15 | 16 | Si quieres colaborar con este proyecto, puedes hacerlo de las siguientes maneras: 17 | 18 | - [ ] Crea un issue si quieres hacer alguna mejora 19 | - [ ] Procedere a crear una tarea para que puedas hacer un PR 20 | - [ ] Crear un PR con la mejora que quieras hacer 21 | - [ ] Asigname con reviewer @linder3hs 22 | 23 | * Recuerda usar el PR Template que tiene el repositorio 24 | 25 | ## 🪙 Sponsors 26 | 27 | Si quieres ser un sponsor de la conferencia, puedes completar el siguiente formulario: 28 | 29 | [Formulario de Sponsors](https://linderhassinger00.typeform.com/to/F64EamRc) 30 | 31 | Actualmente tenemos los siguientes sponsors: 32 | 33 | | Sponsor | Logo | Link | 34 | | :--- | :--- | :--- | 35 | | Tecsup | ![Tecsup](https://www.tecsup.edu.pe/themes/tecsup/logo.svg) | [Tecsup](https://www.tecsup.edu.pe/) | 36 | | Codigo power by Tecsup | ![Codigo Tecsup](https://assets.website-files.com/624b2bd5b7be89e20392d489/624e1be85a96e3ac3e45f7fb_logo-color-go.svg) | [Codigo Tecsup](https://codigo.edu.pe/) | 37 | 38 | ## 👨🏻‍💻 Tech Stack 39 | 40 | ### Astro 🚀 41 | 42 | Este proyecto esta construido con [Astro](https://astro.build/), un generador de sitios estáticos moderno y rápido, así como también usamos React con typescript para ciertas partes de la web. 43 | 44 | ### 💾 Supabase 45 | 46 | Usamos [Supabase](https://supabase.io/) para almacenar los diferentes datos de los speakers, sponsors, etc. 47 | 48 | ### 🧞 Commands 49 | 50 | All commands are run from the root of the project, from a terminal: 51 | 52 | | Command | Action | 53 | | :--------------------- | :----------------------------------------------- | 54 | | `npm install` | Installs dependencies | 55 | | `npm run dev` | Starts local dev server at `localhost:3000` | 56 | | `npm run build` | Build your production site to `./dist/` | 57 | | `npm run preview` | Preview your build locally, before deploying | 58 | | `npm run astro ...` | Run CLI commands like `astro add`, `astro check` | 59 | | `npm run astro --help` | Get help using the Astro CLI | 60 | 61 | ## 👀 Ayudanos a encontrar errores 62 | 63 | Si encuentras algún error en la web, puedes crear un issue en el repositorio y así poder ayudar a mejorar la web. También puedes ayudarnos a mejorar la web haciendo un PR. 64 | 65 | ## 📝 Licencia 66 | 67 | Este proyecto esta bajo la licencia MIT, puedes ver el archivo [LICENSE](LICENSE) para más información. 68 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Código de conducta en la Tech Conf 2 | Nosotros, los organizadores de la Tech Conf, nos comprometemos a crear un espacio seguro y respetuoso para todas las personas que asistan. Este código de conducta establece las normas de comportamiento que esperamos de todos los asistentes y nos comprometemos a hacer cumplir estas normas durante todo el evento. 3 | 4 | ## 1. Respeto 5 | Todos los asistentes deben tratar a los demás con respeto. No se permitirá ningún tipo de discriminación, acoso, intimidación o violencia hacia otra persona en el evento. Esto incluye, pero no se limita a: la discriminación por motivos de género, orientación sexual, edad, raza, religión, discapacidad o cualquier otra característica personal. 6 | 7 | ## 2. Comportamiento apropiado 8 | Se espera que todos los asistentes se comporten de manera apropiada y profesional durante todo el evento. Esto incluye, pero no se limita a: mantener un lenguaje y comportamiento apropiados en las charlas y los espacios públicos, abstenerse de consumir alcohol o drogas en exceso durante el evento, y seguir las reglas establecidas por los organizadores. 9 | 10 | ## 3. Seguridad 11 | Los organizadores del evento harán todo lo posible para garantizar la seguridad de todos los asistentes. Se espera que los asistentes también tomen medidas para garantizar su propia seguridad. Esto incluye, pero no se limita a: mantener un comportamiento responsable durante el evento, informar cualquier comportamiento inapropiado o peligroso a los organizadores, y respetar las normas de seguridad del lugar. 12 | 13 | ## 4. Consecuencias 14 | Cualquier persona que viole este código de conducta será expulsada del evento y se tomarán las medidas necesarias para garantizar la seguridad de todos los asistentes. Si alguien experimenta o presencia cualquier comportamiento inapropiado, debe informar inmediatamente a los organizadores del evento. 15 | -------------------------------------------------------------------------------- /astro.config.mjs: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "astro/config"; 2 | import react from "@astrojs/react"; 3 | import tailwind from "@astrojs/tailwind"; 4 | 5 | export default defineConfig({ 6 | integrations: [ 7 | tailwind({ 8 | config: { applyBaseStyles: true }, 9 | }), 10 | react(), 11 | ], 12 | }); 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@example/basics", 3 | "type": "module", 4 | "version": "0.0.1", 5 | "private": true, 6 | "scripts": { 7 | "dev": "astro dev", 8 | "start": "astro dev", 9 | "build": "astro build", 10 | "preview": "astro preview", 11 | "astro": "astro" 12 | }, 13 | "dependencies": { 14 | "@astrojs/react": "2.0.2", 15 | "@astrojs/tailwind": "3.0.1", 16 | "@fontsource/ibm-plex-mono": "4.5.13", 17 | "@headlessui/react": "1.7.11", 18 | "@heroicons/react": "2.0.16", 19 | "@sentry/browser": "7.37.1", 20 | "@sentry/tracing": "7.37.1", 21 | "@supabase/auth-ui-react": "0.2.7", 22 | "@supabase/supabase-js": "2.7.1", 23 | "@types/react": "18.0.27", 24 | "@types/react-dom": "18.0.10", 25 | "astro": "2.0.6", 26 | "js-confetti": "0.11.0", 27 | "react": "18.2.0", 28 | "react-countdown": "2.3.5", 29 | "react-dom": "18.2.0", 30 | "sweetalert2": "11.7.1", 31 | "tailwindcss": "3.2.4" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /public/browser.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linder3hs/Tech-Conf/2fb1e1b96fc7501c42272c2634b7876a39ca0d7c/public/browser.png -------------------------------------------------------------------------------- /public/favicon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linder3hs/Tech-Conf/2fb1e1b96fc7501c42272c2634b7876a39ca0d7c/public/icon.png -------------------------------------------------------------------------------- /src/assets/discord-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linder3hs/Tech-Conf/2fb1e1b96fc7501c42272c2634b7876a39ca0d7c/src/assets/discord-logo.png -------------------------------------------------------------------------------- /src/assets/github-mark.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/github-white.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linder3hs/Tech-Conf/2fb1e1b96fc7501c42272c2634b7876a39ca0d7c/src/assets/icon.png -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/linder3hs/Tech-Conf/2fb1e1b96fc7501c42272c2634b7876a39ca0d7c/src/assets/logo.png -------------------------------------------------------------------------------- /src/assets/midufest.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/assets/teach.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/ticket-frame.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/assets/twitch.svg: -------------------------------------------------------------------------------- 1 | Asset 3 -------------------------------------------------------------------------------- /src/assets/twitter-logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/virtual.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/yt-logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/About/index.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import PrimaryButton from "../PrimaryButton"; 3 | --- 4 | 5 |
6 |
7 |

8 | ¿Para qué hacemos esto? 🤔 9 |

10 |

11 | Dar conferencias de software en Perú será una oportunidad única para 12 | compartir los conocimientos y experiencias en el campo de la tecnología. 13 | Además, el mercado de tecnología en Perú está en constante crecimiento y 14 | hay una gran demanda por parte de profesionales y estudiantes por aprender 15 | y estar al tanto de las últimas tendencias y desarrollos en el mundo de la 16 | tecnología. 17 |

18 |
19 | 20 |
21 |
22 |
23 | -------------------------------------------------------------------------------- /src/components/Card/index.tsx: -------------------------------------------------------------------------------- 1 | import type { Props } from "./types"; 2 | 3 | export default function Card(props: Props) { 4 | const { title, description, date, id } = props; 5 | 6 | return ( 7 |
8 |
9 |
10 |
11 |
12 |

13 | {title ?? ""} 14 |

15 |

{description}

16 |

{date}

17 | 18 |
19 | 23 | Ver detalle 24 | 25 |
26 |
27 |
28 |
29 |
30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /src/components/Card/types.ts: -------------------------------------------------------------------------------- 1 | export interface Props { 2 | title: string; 3 | description: string; 4 | date: string; 5 | capacity: number; 6 | id: string; 7 | } 8 | -------------------------------------------------------------------------------- /src/components/Event/components/Schedule.tsx: -------------------------------------------------------------------------------- 1 | import { ScheduleItem } from "@componentsReact"; 2 | import type { ISchedule } from "@interfaces/schedule"; 3 | 4 | export default function Schedule({ schedule }: { schedule: ISchedule[] }) { 5 | return ( 6 |
7 |

Calendario

8 |
9 | {schedule.map(({ title, time, speaker }) => ( 10 | 16 | ))} 17 |
18 |
19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /src/components/Event/components/Speakers.tsx: -------------------------------------------------------------------------------- 1 | import type { ISpeaker } from "@interfaces/speaker"; 2 | 3 | export default function Speakers({ speakers }: { speakers: ISpeaker[] }) { 4 | return ( 5 | <> 6 |
7 |

Speakers

8 |
9 |
10 | {speakers.map((speaker: ISpeaker) => ( 11 | <> 12 | {speaker.id !== 4 && ( 13 |
14 | {speaker.name} 20 |

21 | 22 | {speaker.name} 23 | 24 |

25 |
26 | )} 27 | 28 | ))} 29 |
30 | 31 | ); 32 | } 33 | -------------------------------------------------------------------------------- /src/components/Event/components/Sponsors.tsx: -------------------------------------------------------------------------------- 1 | import { PrimaryButton } from "@componentsReact"; 2 | import type { ISponsor } from "@interfaces/sponsor"; 3 | 4 | export default function Sponsors({ sponsors }: { sponsors: ISponsor[] }) { 5 | return ( 6 | <> 7 |
8 |

Sponsors

9 |
10 |
11 | {sponsors.map((sponsor: ISponsor) => ( 12 | 13 |
14 | {sponsor.alt} 20 |
21 |
22 | ))} 23 |
24 |
25 | 29 |
30 | 31 | ); 32 | } 33 | -------------------------------------------------------------------------------- /src/components/Event/components/index.tsx: -------------------------------------------------------------------------------- 1 | export { default as Speakers } from "./Speakers"; 2 | 3 | export { default as Sponsors } from "./Sponsors"; 4 | 5 | export { default as Schedule } from "./Schedule"; 6 | -------------------------------------------------------------------------------- /src/components/Event/index.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import { Ticket, TimerCountdown } from "@componentsReact"; 3 | import useUserSubscriber from "@hooks/useUserSubscriber"; 4 | import { getUser } from "@services/auth"; 5 | import { insertDataIntoTable } from "@services/config"; 6 | import type { IEvent, ISpeaker, ISponsor, ISchedule } from "../../interfaces"; 7 | import { Speakers, Schedule, Sponsors } from "./components"; 8 | 9 | interface Props { 10 | event: IEvent; 11 | schedule: ISchedule[]; 12 | speakers: ISpeaker[]; 13 | sponsors: ISponsor[]; 14 | } 15 | 16 | export default function Event(props: Props) { 17 | const { event, schedule, speakers, sponsors } = props; 18 | 19 | const { handleIsSubscribed } = useUserSubscriber(); 20 | 21 | const handleURL = async () => { 22 | const eventId = window.location.pathname.split("/")[2]; 23 | const params = window.location.search.replace("?", "").split("&"); 24 | 25 | if (!params[0]) return; 26 | 27 | const [_, value] = params[0].split("="); 28 | 29 | if (value !== "true") return; 30 | 31 | const user = await getUser(); 32 | 33 | if (!user) return; 34 | 35 | const isSubscribed = await handleIsSubscribed(eventId, user.id); 36 | 37 | if (isSubscribed) return; 38 | 39 | await insertDataIntoTable("tickets", { 40 | user_id: user.id, 41 | event_id: eventId, 42 | }); 43 | 44 | window.location.href = `${window.origin}/events/1`; 45 | }; 46 | 47 | useEffect(() => { 48 | handleURL(); 49 | }, []); 50 | 51 | return ( 52 | <> 53 |
54 |
55 |
56 |

57 | ⭐️ {event.title} 58 |

59 |

60 | {event.description} 61 |

62 |

63 | {event.date} 64 |

65 | 77 | 78 |
79 | 80 |
81 |
82 | 83 | 84 | 85 |
86 |
87 | 99 | 100 | ); 101 | } 102 | -------------------------------------------------------------------------------- /src/components/EventNavBar/index.jsx: -------------------------------------------------------------------------------- 1 | import { Disclosure } from "@headlessui/react"; 2 | import { Bars3Icon, XMarkIcon } from "@heroicons/react/24/outline"; 3 | import icon from "@assets/icon.png"; 4 | 5 | const navigation = [ 6 | { name: "Schedule", href: "#schedule", current: false }, 7 | { name: "Speakers", href: "#speakers", current: false }, 8 | { name: "Sponsors", href: "#sponsors", current: false }, 9 | { name: "Experiencia", href: "#experience", current: false }, 10 | ]; 11 | 12 | function classNames(...classes) { 13 | return classes.filter(Boolean).join(" "); 14 | } 15 | 16 | export default function NavBar() { 17 | return ( 18 | 19 | {({ open }) => ( 20 | <> 21 |
22 |
23 |
24 | 25 | Open main menu 26 | {open ? ( 27 | 32 |
33 |
34 |
35 | 36 | Tech Conf 41 | 42 | 43 | Tech Conf 48 | 49 |
50 |
51 |
52 | {navigation.map((item) => ( 53 | 64 | {item.name} 65 | 66 | ))} 67 |
68 |
69 |
70 |
71 |
72 | 73 | 74 |
75 | {navigation.map((item) => ( 76 | 88 | {item.name} 89 | 90 | ))} 91 |
92 |
93 | 94 | )} 95 |
96 | ); 97 | } 98 | -------------------------------------------------------------------------------- /src/components/Footer/index.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import github from "@assets/github-white.svg"; 3 | import twitch from "@assets/twitch.svg" 4 | --- 5 | 6 |
9 |
10 |

11 | Tech Conf - {new Date().getFullYear()} 12 |

13 |
14 |
15 | Contáctanos 20 | 27 | 28 | 29 | 36 | 37 | 38 |
39 |
40 | -------------------------------------------------------------------------------- /src/components/GetTicket/index.tsx: -------------------------------------------------------------------------------- 1 | import { Fragment, useRef, useState } from "react"; 2 | import { Dialog, Transition } from "@headlessui/react"; 3 | import { signInWithSocialMedia } from "@services/auth"; 4 | import { insertDataIntoTable } from "@services/config"; 5 | import useUserSubscriber from "@hooks/useUserSubscriber"; 6 | import useUser from "@hooks/useUser"; 7 | import Swal from "sweetalert2"; 8 | import type { Provider } from "@supabase/supabase-js"; 9 | 10 | interface Props { 11 | id: number; 12 | } 13 | 14 | export default function GetTicket(props: Props) { 15 | const { id } = props; 16 | 17 | const { handleIsSubscribed } = useUserSubscriber(); 18 | 19 | const [open, setOpen] = useState(false); 20 | 21 | const cancelButtonRef = useRef(null); 22 | 23 | const { user } = useUser(); 24 | 25 | const handleSubscribe = async (provider: Provider = "github") => { 26 | if (!user) { 27 | await signInWithSocialMedia(provider); 28 | return; 29 | } 30 | 31 | const isSubscribed = await handleIsSubscribed(String(id), user.id); 32 | 33 | if (isSubscribed) { 34 | Swal.fire({ 35 | icon: "warning", 36 | title: "Oops...", 37 | text: "Ya te has inscrito a este evento", 38 | }); 39 | return; 40 | } 41 | 42 | const response = await insertDataIntoTable("tickets", { 43 | user_id: user.id, 44 | event_id: id, 45 | }); 46 | 47 | if (!response) { 48 | Swal.fire({ 49 | icon: "success", 50 | title: "Oops...", 51 | text: "Algo salió mal, intentalo de nuevo", 52 | }); 53 | 54 | return; 55 | } 56 | 57 | window.location.reload(); 58 | }; 59 | 60 | return ( 61 | <> 62 | 68 |

Es gratis!

69 | 70 | 76 | 85 |
86 | 87 | 88 |
89 |
90 | 99 | 100 |
101 |
102 |
103 | 107 | Inscribite al evento 108 | 109 |
110 |

111 | Ven y entérate de las ultimas novedades sobre el 112 | desarrollo de software. 113 |

114 |
115 |
116 |
117 |
118 | 124 | 130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 | 139 | ); 140 | } 141 | -------------------------------------------------------------------------------- /src/components/GithubAuth/index.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react"; 2 | import { signInWithSocialMedia, getUser } from "../../services/auth"; 3 | import github from "../../assets/github-white.svg"; 4 | import type { User } from "../../interfaces/user"; 5 | 6 | export default function GithubAuth() { 7 | const fetchLogin = async () => await signInWithSocialMedia(); 8 | 9 | const [user, setUser] = useState(null); 10 | 11 | const fetchUser = async () => { 12 | const user = (await getUser()) as User | null; 13 | setUser(user); 14 | }; 15 | 16 | useEffect(() => { 17 | fetchUser(); 18 | }, []); 19 | 20 | return ( 21 |
22 | {user ? ( 23 | 24 | 29 | 30 | ) : ( 31 | 39 | )} 40 |
41 | ); 42 | } 43 | -------------------------------------------------------------------------------- /src/components/Home/Home.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import discord from "@assets/discord-logo.png"; 3 | --- 4 | 5 |
6 |
7 |

TECH CONF

8 |

9 | Únete a nuestra comunidad y 10 |

11 |

12 | se parte de las mejores charlas de Tech. 13 |

14 |
15 | Ver eventos 20 | 26 | Discord 27 | 28 |
29 |
30 |
31 | -------------------------------------------------------------------------------- /src/components/How/index.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import teach from "@assets/teach.svg"; 3 | import virtual from "@assets/virtual.svg"; 4 | --- 5 | 6 |
7 |
8 |

9 | ¿Cómo se podrá ver el evento? 👀 🎥 10 |

11 |
12 |
15 |
16 |

Virtual

17 |

18 | . Desde la comodidad de tu casa, oficina o cualquier lugar que te 19 | encuentres. 20 |

21 |

22 | . Podrás ver el evento en vivo desde tu computadora, tablet o celular. 23 |

24 |

25 | . Podrás interactuar con los ponentes y con los demás asistentes. 26 |

27 |
28 | teach 29 |
30 |
31 |
32 |

Presencial

33 |

34 | . Podrás asistir al evento en vivo en el lugar indicado. 35 |

36 |

37 | . Podrás interactuar con los ponentes y con los demás asistentes. 38 |

39 |

40 | . Accede a los sorteos y premios que se realizarán durante el evento. 41 |

42 |
43 | virtual 44 |
45 |
46 |
47 |
48 | -------------------------------------------------------------------------------- /src/components/NavBar/index.tsx: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from "react"; 2 | import { Disclosure } from "@headlessui/react"; 3 | import { Bars3Icon, XMarkIcon } from "@heroicons/react/24/outline"; 4 | import GithubAuth from "../GithubAuth"; 5 | import { navigation, classNames } from "./items"; 6 | 7 | export default function Layout() { 8 | const [current, setCurrent] = useState(navigation[0].name); 9 | 10 | const handleCurrent = () => { 11 | const currentPath = window.location.pathname; 12 | const currentName = navigation.find((item) => item.href === currentPath); 13 | if (currentName) setCurrent(currentName.name); 14 | }; 15 | 16 | useEffect(() => handleCurrent(), []); 17 | 18 | return ( 19 | <> 20 | 21 | {({ open }) => ( 22 | <> 23 |
24 |
25 |
26 | 27 | Open main menu 28 | {open ? ( 29 | 34 |
35 |
36 |
37 |
38 | {navigation.map((item) => ( 39 | 49 | {item.name} 50 | 51 | ))} 52 |
53 |
54 |
55 | 56 |
57 |
58 |
59 |
60 |
61 | 62 |
63 | {navigation.map((item) => ( 64 | 76 | {item.name} 77 | 78 | ))} 79 |
80 |
81 | 82 | )} 83 |
84 | 85 | ); 86 | } 87 | -------------------------------------------------------------------------------- /src/components/NavBar/items.ts: -------------------------------------------------------------------------------- 1 | export const navigation = [ 2 | { 3 | name: "Home", 4 | href: "/", 5 | current: true, 6 | }, 7 | { 8 | name: "Eventos", 9 | href: "/events/1", 10 | current: false, 11 | }, 12 | { 13 | name: "Speakers", 14 | href: "/speakers", 15 | current: false, 16 | }, 17 | { 18 | name: "Código de conducta", 19 | href: "/code-of-conduct", 20 | current: false, 21 | }, 22 | { 23 | name: "Sponsors", 24 | href: "/#sponsors", 25 | current: false, 26 | }, 27 | { 28 | name: "Mis Tickets", 29 | href: "/tickets", 30 | current: false, 31 | }, 32 | ]; 33 | 34 | export function classNames(...classes: any) { 35 | return classes.filter(Boolean).join(" "); 36 | } 37 | -------------------------------------------------------------------------------- /src/components/PrimaryButton/index.tsx: -------------------------------------------------------------------------------- 1 | interface Props { 2 | text: string; 3 | url: string; 4 | } 5 | 6 | export default function PrimaryButton(props: Props) { 7 | const { text, url } = props; 8 | 9 | return ( 10 | 15 | {text} 16 | 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /src/components/Profile/index.tsx: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from "react"; 2 | import { getUser, signout } from "@services/auth"; 3 | import type { User } from "@interfaces/user"; 4 | 5 | export default function Profile() { 6 | const [user, setUser] = useState(null); 7 | 8 | const fetchUser = async () => { 9 | const user = await getUser() as User | null; 10 | 11 | if (!user) return; 12 | 13 | setUser(user); 14 | }; 15 | 16 | useEffect(() => { 17 | fetchUser(); 18 | }, []); 19 | 20 | return ( 21 |
22 | {user ? ( 23 |
24 |
25 | {user.user_metadata.preferred_username} 31 |

{user.user_metadata.name}

32 | 36 |

@{user.user_metadata.preferred_username}

37 |
38 | 44 |
45 |
46 | ) : ( 47 |
48 |

Aún no inicias sesión

49 |
50 | )} 51 |
52 | ); 53 | } 54 | -------------------------------------------------------------------------------- /src/components/ScheduleItem/index.tsx: -------------------------------------------------------------------------------- 1 | interface Props { 2 | title: string; 3 | speaker: string; 4 | time: string; 5 | } 6 | 7 | export default function ScheduleItem({ title, speaker, time }: Props) { 8 | return ( 9 |
10 |
11 |
{time}
12 |
13 |
14 |

{title}

15 | 21 | {speaker} 22 | 23 |
24 |
25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /src/components/SingUp/index.tsx: -------------------------------------------------------------------------------- 1 | import { signInWithSocialMedia } from "@services/auth"; 2 | 3 | export default function SignUp() { 4 | return ( 5 | 11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /src/components/SocialModal/index.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | import { Dialog } from "@headlessui/react"; 3 | 4 | export default function SocialModal() { 5 | const [isOpen, setIsOpen] = useState(false); 6 | 7 | return ( 8 | <> 9 | 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /src/components/Speaker/index.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import github_logo from "@assets/github-white.svg"; 3 | import twitter_logo from "@assets/twitter-logo.svg"; 4 | 5 | export interface Props { 6 | image: string; 7 | name: string; 8 | github: string; 9 | youtube: string; 10 | twitter: string; 11 | } 12 | 13 | const { image, name, github, youtube, twitter } = Astro.props; 14 | --- 15 | 16 |
19 | 20 | {name} 21 | 22 |

{name}

23 |
24 |
25 | 26 | {github} 27 | 28 |
29 |
30 | 31 | 32 | 33 |
34 |
35 |
36 | -------------------------------------------------------------------------------- /src/components/Sponsors/index.tsx: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from "react"; 2 | import { getDataFromTable } from "@services/config"; 3 | import type { ISponsor } from "@interfaces/sponsor"; 4 | import PrimaryButton from "../PrimaryButton"; 5 | 6 | export default function Sponsors() { 7 | const [sponsors, setSponsors] = useState([]); 8 | 9 | const fetchSponsors = async () => { 10 | const data = await getDataFromTable("sponsors"); 11 | setSponsors(data); 12 | }; 13 | 14 | useEffect(() => { 15 | fetchSponsors(); 16 | }, []); 17 | 18 | return ( 19 |
20 |
21 |

22 | Nuestros Sponsors 🤝 23 |

24 |
25 |
26 | {sponsors.map((sponsor: ISponsor) => ( 27 |
28 | {sponsor.alt} 29 |
30 | ))} 31 |
32 | 36 |
37 | ); 38 | } 39 | -------------------------------------------------------------------------------- /src/components/Ticket/index.tsx: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from "react"; 2 | import type { User } from "@interfaces/user"; 3 | import { getDataFromTable } from "@services/config"; 4 | import { getUser } from "@services/auth"; 5 | import { GetTicket } from ".."; 6 | 7 | export default function Ticket({ id }: { id: number }) { 8 | const [isRegistered, setIsRegistered] = useState(false); 9 | 10 | const [countTickets, setCountTickets] = useState(0); 11 | 12 | const fetchUser = async () => { 13 | const user = (await getUser()) as User | null; 14 | 15 | const tickets = await getDataFromTable( 16 | "tickets", 17 | { 18 | key: "user_id", 19 | value: String(user?.id), 20 | }, 21 | `*, events(*)` 22 | ); 23 | 24 | setIsRegistered(tickets.length > 0); 25 | }; 26 | 27 | const fetctTickets = async () => { 28 | const tickets = await getDataFromTable("tickets"); 29 | setCountTickets(tickets.length); 30 | }; 31 | 32 | useEffect(() => { 33 | fetchUser(); 34 | }, []); 35 | 36 | useEffect(() => { 37 | fetctTickets(); 38 | }, []); 39 | 40 | return ( 41 | <> 42 | {isRegistered ? ( 43 |
44 |
45 |

#{String(countTickets).padStart(5, "0")}

46 |

47 | tickets registrados 48 |

49 |
50 |
51 | ) : ( 52 | 53 | )} 54 | 55 | ); 56 | } 57 | -------------------------------------------------------------------------------- /src/components/Tickets/components/NotSession/index.tsx: -------------------------------------------------------------------------------- 1 | import github from "@assets/github-white.svg"; 2 | import { signInWithSocialMedia } from "@services/auth"; 3 | 4 | export default function NoSession() { 5 | const fetchLogin = async () => await signInWithSocialMedia(); 6 | 7 | return ( 8 |
9 |

10 | Para poder ver tus tickets, debes iniciar sesión usando tu cuenta de 11 | Github. 12 |

13 | 22 |
23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /src/components/Tickets/components/Ticket/index.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import type { User } from "../../../../interfaces/user"; 3 | import type { ITickets } from "../../../../interfaces/tickets"; 4 | import github from "../../../../assets/github-mark.svg"; 5 | import JSConfetti from "js-confetti"; 6 | 7 | interface Props { 8 | ticket: ITickets; 9 | user: User; 10 | } 11 | 12 | export default function Ticket(props: Props) { 13 | const { ticket, user } = props; 14 | 15 | useEffect(() => { 16 | const jsConfetti = new JSConfetti(); 17 | 18 | jsConfetti.addConfetti(); 19 | }, []); 20 | 21 | return ( 22 |
23 |
24 |
25 |
26 | 33 |
34 |
35 |

{user.user_metadata.name}

36 |

37 | 38 | {user.user_metadata.user_name} 39 |

40 |
41 |
42 |

43 | ⭐️ {ticket.events.title} 44 |

45 |
46 |
47 |

{ticket.events.date}

48 |
49 |
50 |
51 |
52 | ); 53 | } 54 | -------------------------------------------------------------------------------- /src/components/Tickets/components/index.tsx: -------------------------------------------------------------------------------- 1 | export { default as Ticket } from "./Ticket"; 2 | 3 | export { default as NotSession } from "./NotSession"; 4 | -------------------------------------------------------------------------------- /src/components/Tickets/index.css: -------------------------------------------------------------------------------- 1 | .bg-ticket { 2 | background-image: url("../../assets/ticket-frame.svg"); 3 | background-repeat: no-repeat; 4 | background-position: center; 5 | margin: auto; 6 | margin-top: 20px; 7 | transition: box-shadow 0.1s, transform 0.1s; 8 | } 9 | 10 | .bg-miduticket { 11 | background-image: url("../../assets/midufest.svg"); 12 | background-repeat: no-repeat; 13 | background-position: center; 14 | transition: box-shadow 0.1s, transform 0.1s; 15 | } 16 | 17 | .ticket-content { 18 | width: 500px; 19 | margin: auto; 20 | padding: 20px; 21 | } 22 | 23 | .ticket-midufest { 24 | margin: auto; 25 | padding: 90px; 26 | transform: rotate(-10deg); 27 | } 28 | 29 | .image-white { 30 | filter: brightness(10000%); 31 | } 32 | 33 | /* media query mobile */ 34 | @media screen and (max-width: 480px) { 35 | .bg-ticket { 36 | width: 100%; 37 | } 38 | .ticket-content { 39 | width: 100%; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/components/Tickets/index.tsx: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from "react"; 2 | import { getUser } from "../../services/auth"; 3 | import { getDataFromTable } from "../../services/config"; 4 | import type { User } from "../../interfaces/user"; 5 | import type { ITickets } from "../../interfaces/tickets"; 6 | import { Ticket, NotSession } from "./components"; 7 | import "./index.css"; 8 | 9 | export default function Tickets() { 10 | const [user, setUser] = useState(null); 11 | 12 | const [tickets, setTickets] = useState([]); 13 | 14 | const fetchUser = async (): Promise => { 15 | const user = (await getUser()) as User | null; 16 | 17 | if (!user) return null; 18 | 19 | setUser(user); 20 | 21 | return user; 22 | }; 23 | 24 | const fetchTickets = async (user: User | null) => { 25 | if (!user) return; 26 | 27 | const tickets = await getDataFromTable( 28 | "tickets", 29 | { 30 | key: "user_id", 31 | value: user.id, 32 | }, 33 | `*, events(*)` 34 | ); 35 | 36 | setTickets(tickets); 37 | }; 38 | 39 | useEffect(() => { 40 | fetchUser().then((user: User | null) => fetchTickets(user)); 41 | }, []); 42 | 43 | return ( 44 | <> 45 | {user ? ( 46 | tickets.map((ticket: ITickets) => ( 47 | 48 | )) 49 | ) : ( 50 | 51 | )} 52 | 53 | ); 54 | } 55 | -------------------------------------------------------------------------------- /src/components/TimerCountdown/index.tsx: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from "react"; 2 | 3 | interface TimeUnits { 4 | days: number; 5 | hours: number; 6 | minutes: number; 7 | seconds: number; 8 | } 9 | 10 | export default function Countdown() { 11 | const [timeRemaining, setTimeRemaining] = useState({ 12 | days: 0, 13 | hours: 0, 14 | minutes: 0, 15 | seconds: 0, 16 | }); 17 | 18 | useEffect(() => { 19 | const intervalId = setInterval(() => { 20 | const second = 1000; 21 | const minute = second * 60; 22 | const hour = minute * 60; 23 | const day = hour * 24; 24 | 25 | const today = new Date(); 26 | 27 | const targetDate = new Date(2023, 4, 12, 11, 30, 0); 28 | let distance = targetDate.getTime() - today.getTime(); 29 | 30 | if (distance < 0) { 31 | clearInterval(intervalId); 32 | return; 33 | } 34 | 35 | setTimeRemaining({ 36 | days: Math.floor(distance / day), 37 | hours: Math.floor((distance % day) / hour), 38 | minutes: Math.floor((distance % hour) / minute), 39 | seconds: Math.floor((distance % minute) / second), 40 | }); 41 | }, 1000); 42 | 43 | return () => clearInterval(intervalId); 44 | }, []); 45 | 46 | return ( 47 |
48 |
49 |

{timeRemaining.days}

50 |

Días

51 |
52 |
53 |

{timeRemaining.hours}

54 |

Horas

55 |
56 |
57 |

{timeRemaining.minutes}

58 |

Minutos

59 |
60 |
61 |

{timeRemaining.seconds}

62 |

Segundos

63 |
64 |
65 | ); 66 | } 67 | -------------------------------------------------------------------------------- /src/components/index.tsx: -------------------------------------------------------------------------------- 1 | export { default as Card } from "./Card"; 2 | 3 | export { default as GetTicket } from "./GetTicket"; 4 | 5 | export { default as GithubAuth } from "./GithubAuth"; 6 | 7 | export { default as NavBar } from "./NavBar"; 8 | 9 | export { default as PrimaryButton } from "./PrimaryButton"; 10 | 11 | export { default as Profile } from "./Profile"; 12 | 13 | export { default as SignUp } from "./SingUp"; 14 | 15 | export { default as Sponsors } from "./Sponsors"; 16 | 17 | export { default as Ticket } from "./Ticket"; 18 | 19 | export { default as Tickets } from "./Tickets"; 20 | 21 | export { default as TimerCountdown } from "./TimerCountdown"; 22 | 23 | export { default as ScheduleItem } from "./ScheduleItem"; 24 | 25 | export { default as Event } from "./Event"; 26 | -------------------------------------------------------------------------------- /src/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /src/hooks/useUser.tsx: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from "react"; 2 | import { getUser } from "@services/auth"; 3 | 4 | import type { User } from "@interfaces/user"; 5 | 6 | export default function useUser() { 7 | const [user, setUser] = useState(null); 8 | 9 | const handleGetUser = async () => { 10 | const user = (await getUser()) as User | null; 11 | setUser(user); 12 | }; 13 | 14 | useEffect(() => { 15 | handleGetUser(); 16 | }, []); 17 | 18 | return { user }; 19 | } 20 | -------------------------------------------------------------------------------- /src/hooks/useUserSubscriber.ts: -------------------------------------------------------------------------------- 1 | import { getTicketRecord } from "@services/config"; 2 | 3 | export default function useUserSubscriber() { 4 | const handleIsSubscribed = async ( 5 | event_id: string, 6 | user_id: string 7 | ): Promise => { 8 | const { length } = await getTicketRecord(event_id, user_id); 9 | return length > 0; 10 | }; 11 | 12 | return { handleIsSubscribed }; 13 | } 14 | -------------------------------------------------------------------------------- /src/interfaces/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./schedule"; 2 | export * from "./speaker"; 3 | export * from "./user"; 4 | export * from "./sponsor"; 5 | export * from "./tickets"; 6 | -------------------------------------------------------------------------------- /src/interfaces/schedule.ts: -------------------------------------------------------------------------------- 1 | export interface ISchedule { 2 | title: string; 3 | time: string; 4 | speaker: string; 5 | } 6 | -------------------------------------------------------------------------------- /src/interfaces/speaker.ts: -------------------------------------------------------------------------------- 1 | export interface ISpeaker { 2 | id: number; 3 | name: string; 4 | position: string; 5 | company: string; 6 | image: string; 7 | github: string; 8 | twitter: string; 9 | youtube: string; 10 | about: string; 11 | web: string; 12 | created_at: string; 13 | } 14 | -------------------------------------------------------------------------------- /src/interfaces/sponsor.ts: -------------------------------------------------------------------------------- 1 | export interface ISponsor { 2 | id: number; 3 | src: string; 4 | alt: string; 5 | link: string; 6 | created_at: string; 7 | } 8 | -------------------------------------------------------------------------------- /src/interfaces/tickets.ts: -------------------------------------------------------------------------------- 1 | export interface IEvent { 2 | id: number; 3 | title: string; 4 | description: string; 5 | date: string; 6 | capacity: number; 7 | created_at: string; 8 | } 9 | 10 | export interface ITickets { 11 | id: number; 12 | user_id: string; 13 | created_at: string; 14 | events: IEvent; 15 | } 16 | -------------------------------------------------------------------------------- /src/interfaces/user.ts: -------------------------------------------------------------------------------- 1 | export interface AppMetadata { 2 | provider: string; 3 | providers: string[]; 4 | } 5 | 6 | export interface UserMetadata { 7 | avatar_url: string; 8 | email: string; 9 | email_verified: boolean; 10 | full_name: string; 11 | iss: string; 12 | name: string; 13 | preferred_username: string; 14 | provider_id: string; 15 | sub: string; 16 | user_name: string; 17 | } 18 | 19 | export interface IdentityData { 20 | avatar_url: string; 21 | email: string; 22 | email_verified: boolean; 23 | full_name: string; 24 | iss: string; 25 | name: string; 26 | preferred_username: string; 27 | provider_id: string; 28 | sub: string; 29 | user_name: string; 30 | } 31 | 32 | export interface Identity { 33 | id: string; 34 | user_id: string; 35 | identity_data: IdentityData; 36 | provider: string; 37 | last_sign_in_at: Date; 38 | created_at: Date; 39 | updated_at: Date; 40 | } 41 | 42 | export interface User { 43 | id: string; 44 | aud: string; 45 | role: string; 46 | email: string; 47 | email_confirmed_at: Date; 48 | phone: string; 49 | confirmed_at: Date; 50 | last_sign_in_at: Date; 51 | app_metadata: AppMetadata; 52 | user_metadata: UserMetadata; 53 | identities: Identity[]; 54 | created_at: Date; 55 | updated_at: Date; 56 | } 57 | -------------------------------------------------------------------------------- /src/layouts/EventFrame.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import EventNavBar from "../components/EventNavBar"; 3 | 4 | export interface Props { 5 | title: string; 6 | } 7 | 8 | const { title } = Astro.props; 9 | --- 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | {title} 18 | 19 | 20 | 21 | 22 | 23 | 27 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 43 | 47 | 48 | 49 | 50 | 54 | 59 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 106 | -------------------------------------------------------------------------------- /src/layouts/Layout.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import { NavBar } from "@componentsReact"; 3 | import Footer from "@components/Footer/index.astro"; 4 | 5 | export interface Props { 6 | title: string; 7 | } 8 | 9 | const { title } = Astro.props; 10 | --- 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 23 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 39 | 43 | 44 | 45 | 46 | 50 | {title} 51 | 52 | 53 | 54 | 55 |
56 |
57 |
58 | 59 | 60 | 139 | -------------------------------------------------------------------------------- /src/pages/code-of-conduct/index.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import Layout from "@layouts/Layout.astro"; 3 | --- 4 | 5 | 6 |
7 |
8 |

Código de conducta en la Tech Conf

9 |

10 | Nosotros, los organizadores de la Tech Conf, nos 11 | comprometemos a crear un espacio seguro y respetuoso para todas 12 | las personas que asistan. Este código de conducta establece las normas 13 | de comportamiento que esperamos de todos los asistentes y nos 14 | comprometemos a hacer cumplir estas normas durante todo el evento. 15 |

16 |

1. Respeto

17 |

18 | Todos los asistentes deben tratar a los demás con respeto. No se 19 | permitirá ningún tipo de discriminación, acoso, intimidación o violencia 20 | hacia otra persona en el evento. Esto incluye, pero no se limita a: la 21 | discriminación por motivos de género, orientación sexual, edad, raza, 22 | religión, discapacidad o cualquier otra característica personal. 23 |

24 |

2. Comportamiento apropiado

25 |

26 | Se espera que todos los asistentes se comporten de manera apropiada y 27 | profesional durante todo el evento. Esto incluye, pero no se limita a: 28 | mantener un lenguaje y comportamiento apropiados en las charlas y los 29 | espacios públicos, abstenerse de consumir alcohol o drogas en exceso 30 | durante el evento, y seguir las reglas establecidas por los 31 | organizadores. 32 |

33 |

3. Seguridad

34 |

35 | Los organizadores del evento harán todo lo posible para garantizar la 36 | seguridad de todos los asistentes. Se espera que los asistentes también 37 | tomen medidas para garantizar su propia seguridad. Esto incluye, pero no 38 | se limita a: mantener un comportamiento responsable durante el evento, 39 | informar cualquier comportamiento inapropiado o peligroso a los 40 | organizadores, y respetar las normas de seguridad del lugar. 41 |

42 |

4. Consecuencias

43 |

44 | Cualquier persona que viole este código de conducta será expulsada del 45 | evento y se tomarán las medidas necesarias para garantizar la seguridad 46 | de todos los asistentes. Si alguien experimenta o presencia cualquier 47 | comportamiento inapropiado, debe informar inmediatamente a los 48 | organizadores del evento. 49 |

50 |
51 |
52 |
53 | -------------------------------------------------------------------------------- /src/pages/events/[id].astro: -------------------------------------------------------------------------------- 1 | --- 2 | import EventFrame from "@layouts/EventFrame.astro"; 3 | import { getDataFromTable } from "@services/config"; 4 | import type { IEvent } from "@interfaces/tickets"; 5 | import type { ISpeaker } from "@interfaces/speaker"; 6 | import { Event } from "@componentsReact"; 7 | import { schedule } from "../../services/schedule"; 8 | 9 | export async function getStaticPaths() { 10 | const events = await getDataFromTable("events"); 11 | 12 | return events.map((event: IEvent) => ({ 13 | params: { id: event.id }, 14 | })); 15 | } 16 | 17 | const { id } = Astro.params; 18 | 19 | const [event] = await getDataFromTable("events", { 20 | key: "id", 21 | value: Number(id), 22 | }); 23 | 24 | const speakers = await getDataFromTable("speakers"); 25 | const sponsors = await getDataFromTable("sponsors"); 26 | --- 27 | 28 | 29 | 36 | 37 | -------------------------------------------------------------------------------- /src/pages/events/index.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import Layout from "@layouts/Layout.astro"; 3 | import Card from "@components/Card/index"; 4 | import { getDataFromTable } from "@services/config"; 5 | import type { IEvent } from "@interfaces/tickets"; 6 | 7 | const events = (await getDataFromTable("events")) as IEvent[]; 8 | --- 9 | 10 | 11 |
12 |

⭐️ Eventos

13 |
14 | { 15 | events && 16 | events.map((event: IEvent) => ( 17 | 25 | )) 26 | } 27 |
28 |
29 |
30 | -------------------------------------------------------------------------------- /src/pages/index-conf.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import EventFrame from "@layouts/EventFrame.astro"; 3 | import { getDataFromTable } from "@services/config"; 4 | import type { IEvent } from "@interfaces/tickets"; 5 | import type { ISpeaker } from "@interfaces/speaker"; 6 | import { Event } from "@componentsReact"; 7 | import { schedule } from "../services/schedule"; 8 | 9 | export async function getStaticPaths() { 10 | const events = await getDataFromTable("events"); 11 | 12 | return events.map((event: IEvent) => ({ 13 | params: { id: event.id }, 14 | })); 15 | } 16 | 17 | const { id = 1 } = Astro.params; 18 | 19 | const [event] = await getDataFromTable("events", { 20 | key: "id", 21 | value: Number(id), 22 | }); 23 | 24 | const speakers = await getDataFromTable("speakers"); 25 | const sponsors = await getDataFromTable("sponsors"); 26 | --- 27 | 28 | 29 | 36 |
37 |

38 | ¡Si ya te inscribiste, ahora registra tus datos aquí! 42 |

43 |
44 |
45 | -------------------------------------------------------------------------------- /src/pages/index.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import Layout from "@layouts/Layout.astro"; 3 | import { Sponsors } from "@componentsReact"; 4 | import Home from "@components/Home/Home.astro"; 5 | import About from "@components/About/index.astro"; 6 | import How from "@components/How/index.astro"; 7 | import Speaker from "@components/Speaker/index.astro"; 8 | import type { ISpeaker } from "@interfaces/speaker"; 9 | import { getDataFromTable } from "@services/config"; 10 | 11 | const speakers = await getDataFromTable("speakers"); 12 | --- 13 | 14 | 15 |
16 | 17 |
18 |
19 |
20 | 21 |
22 |
23 |

24 | Speakers 🎙️ 25 |

26 |
29 | { 30 | speakers?.length > 0 && 31 | speakers.map((speaker: ISpeaker) => ( 32 | 39 | )) 40 | } 41 |
42 |
43 |
44 | 45 |
46 |
47 | 48 |
49 |
50 |
51 | -------------------------------------------------------------------------------- /src/pages/profile/index.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import Layout from "@layouts/Layout.astro"; 3 | import Profile from "@components/Profile"; 4 | --- 5 | 6 | 7 |
8 | 9 |
10 |
11 | -------------------------------------------------------------------------------- /src/pages/speakers/[name].astro: -------------------------------------------------------------------------------- 1 | --- 2 | import Layout from "@layouts/Layout.astro"; 3 | import { getDataFromTable } from "@services/config"; 4 | import type { ISpeaker } from "@interfaces/speaker"; 5 | 6 | export async function getStaticPaths() { 7 | const speakers = await getDataFromTable("speakers"); 8 | 9 | return speakers.map((speaker: ISpeaker) => ({ 10 | params: { name: speaker.name.replaceAll(" ", "-") }, 11 | })); 12 | } 13 | 14 | const { name } = Astro.params; 15 | 16 | const speaker = await getDataFromTable("speakers", { 17 | key: "name", 18 | value: name?.replaceAll("-", " ") ?? "", 19 | }); 20 | 21 | const { 22 | name: fullname, 23 | image, 24 | github, 25 | company, 26 | position, 27 | web, 28 | } = speaker[0] as ISpeaker; 29 | --- 30 | 31 | 32 |
35 |
36 |

37 | 38 | {fullname} 39 | 40 |

41 |

{company} - {position}

42 |

43 | @{github} 44 |

45 |
46 |
47 | 48 |
49 |
50 |
51 | -------------------------------------------------------------------------------- /src/pages/speakers/index.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import Layout from "@layouts/Layout.astro"; 3 | import Speaker from "@components/Speaker/index.astro"; 4 | import { getDataFromTable } from "@services/config"; 5 | import type { ISpeaker } from "@interfaces/speaker"; 6 | 7 | const speakers = await getDataFromTable("speakers"); 8 | --- 9 | 10 | 11 |
12 |

13 | Speakers 🎙️ 14 |

15 |
18 | { 19 | speakers?.length > 0 && 20 | speakers.map((speaker: ISpeaker) => ( 21 | 28 | )) 29 | } 30 |
31 |
32 |
33 | -------------------------------------------------------------------------------- /src/pages/tickets/index.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import Layout from "@layouts/Layout.astro"; 3 | import Tickets from "@components/Tickets"; 4 | --- 5 | 6 | 7 |
8 |
9 |
10 |

Mis Tickets

11 |
12 | 13 |
14 |
15 |
16 | -------------------------------------------------------------------------------- /src/services/auth.ts: -------------------------------------------------------------------------------- 1 | import type { Provider } from "@supabase/supabase-js"; 2 | import supabase from "./config"; 3 | import * as Sentry from "@sentry/browser"; 4 | 5 | export async function signInWithSocialMedia(provider: Provider = "github") { 6 | const { data, error } = await supabase.auth.signInWithOAuth({ 7 | provider, 8 | }); 9 | 10 | if (error) { 11 | Sentry.captureException(error); 12 | return false; 13 | } 14 | 15 | return data; 16 | } 17 | 18 | export async function signout() { 19 | const { error } = await supabase.auth.signOut(); 20 | 21 | if (error) { 22 | Sentry.captureException(error); 23 | return false; 24 | } 25 | 26 | window.location.href = "/"; 27 | } 28 | 29 | export async function getUser() { 30 | const { data, error } = await supabase.auth.getUser(); 31 | 32 | if (error) { 33 | Sentry.captureException(error); 34 | return null; 35 | } 36 | 37 | return data.user; 38 | } 39 | -------------------------------------------------------------------------------- /src/services/config.ts: -------------------------------------------------------------------------------- 1 | import { createClient } from "@supabase/supabase-js"; 2 | import * as Sentry from "@sentry/browser"; 3 | 4 | interface ICondition { 5 | value: string | number | boolean; 6 | key: string; 7 | } 8 | 9 | const supabase = createClient( 10 | import.meta.env.ASTRO_SUPABASE_URL ?? 11 | "https://nvpaknjxdglnrkhahnaq.supabase.co", 12 | import.meta.env.ASTRO_SUPABASE_PUBLIC_KEY ?? 13 | "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Im52cGFrbmp4ZGdsbnJraGFobmFxIiwicm9sZSI6ImFub24iLCJpYXQiOjE2NzU5MzQ4MjMsImV4cCI6MTk5MTUxMDgyM30.yaixrMiLozraxdYQAKDkA5ta8H-fKn6NTcFAX25cXHU" 14 | ); 15 | 16 | export async function getDataFromTable( 17 | table: string, 18 | condition: ICondition | null = null, 19 | select: string = "*" 20 | ): Promise { 21 | const { data, error } = await supabase 22 | .from(table) 23 | .select(select) 24 | .order("id", { ascending: true }); 25 | 26 | if (error) { 27 | Sentry.captureException(error); 28 | return null; 29 | } 30 | 31 | if (condition) { 32 | return data.filter((item: any) => item[condition.key] === condition.value); 33 | } 34 | 35 | return data; 36 | } 37 | 38 | export async function insertDataIntoTable(table: string, data: any) { 39 | const { error } = await supabase.from(table).insert(data); 40 | 41 | if (error) { 42 | Sentry.captureException(error); 43 | return false; 44 | } 45 | 46 | return true; 47 | } 48 | 49 | export async function getTicketRecord(event_id: string, user_id: string) { 50 | const { data, error } = await supabase 51 | .from("tickets") 52 | .select("*") 53 | .eq("event_id", event_id) 54 | .eq("user_id", user_id); 55 | 56 | if (error) { 57 | Sentry.captureException(error); 58 | return []; 59 | } 60 | 61 | return data; 62 | } 63 | 64 | export default supabase; 65 | -------------------------------------------------------------------------------- /src/services/schedule.ts: -------------------------------------------------------------------------------- 1 | export const schedule = [ 2 | { 3 | title: "Hola mundo!, Bienvenida a la Tech Conf!", 4 | time: "11:30 - 12:00", 5 | speaker: "Linder Hassinger - Victor Saico", 6 | }, 7 | { 8 | title: "Aplicaciones Cloud en soluciones a medida - AWS | Azure", 9 | time: "12:10 - 12:40", 10 | speaker: "Luis Cruz", 11 | }, 12 | { 13 | title: "Almuerzo", 14 | time: "12:40 - 13:40", 15 | speaker: "", 16 | }, 17 | { 18 | title: "ITIL aplicado a las empresas Tech - Tecsup", 19 | time: "13:40 - 14:10", 20 | speaker: "Jaime Gomez", 21 | }, 22 | { 23 | title: "Features de Node JS 20...", 24 | time: "14:10 - 14:40", 25 | speaker: "Linder Hassinger", 26 | }, 27 | { 28 | title: "Marco de trabajo aplicado al código fuente (Git-Flow)", 29 | time: "14:40 - 15:10", 30 | speaker: "Victor Saico", 31 | }, 32 | { 33 | title: 34 | "Introducción a BTP y los servicios brindados en Cloud Foundry y sus aplicaciones en el mundo laboral", 35 | time: "15:10 - 15:40", 36 | speaker: "Guillermo Narvaez", 37 | }, 38 | { 39 | title: "La educación a través de los bootcamp - CodiGO", 40 | time: "15:40 - 16:10", 41 | speaker: "Daniel de la Cruz", 42 | }, 43 | { 44 | title: "Espacio de preguntas - Sorteos", 45 | time: "16:10 - 16:40", 46 | speaker: "", 47 | }, 48 | { 49 | title: "Desarrolladores en tiempos de constante cambio", 50 | time: "16:50 - 17:20", 51 | speaker: "Armando Picón", 52 | }, 53 | { 54 | title: "Cierre", 55 | time: "17:20 - 17:30", 56 | speaker: "Linder Hassinger - Victor Saico", 57 | }, 58 | ]; 59 | -------------------------------------------------------------------------------- /src/services/sentry.ts: -------------------------------------------------------------------------------- 1 | import * as Sentry from "@sentry/browser"; 2 | import { BrowserTracing } from "@sentry/tracing"; 3 | 4 | Sentry.init({ 5 | dsn: "https://0fc7ef2b2d2f4b00b09fb67987ea1269@o773433.ingest.sentry.io/4504657622663168", 6 | integrations: [new BrowserTracing()], 7 | tracesSampleRate: 1.0, 8 | }); 9 | -------------------------------------------------------------------------------- /tailwind.config.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: ["./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}"], 4 | theme: { 5 | extend: { 6 | fontFamily: { 7 | sans: ["IBM Plex Mono", "sans-serif"], 8 | }, 9 | }, 10 | }, 11 | plugins: [], 12 | }; 13 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "astro/tsconfigs/strict", 3 | "compilerOptions": { 4 | "jsx": "react-jsx", 5 | "jsxImportSource": "react", 6 | "baseUrl": ".", 7 | "paths": { 8 | "@components/*": [ 9 | "src/components/*" 10 | ], 11 | "@componentsReact": [ 12 | "src/components/index.tsx" 13 | ], 14 | "@assets/*": [ 15 | "src/assets/*" 16 | ], 17 | "@layouts/*": [ 18 | "src/layouts/*" 19 | ], 20 | "@pages/*": [ 21 | "src/pages/*" 22 | ], 23 | "@interfaces/*": [ 24 | "src/interfaces/*" 25 | ], 26 | "@services/*": [ 27 | "src/services/*" 28 | ], 29 | "@hooks/*": [ 30 | "src/hooks/*" 31 | ], 32 | } 33 | } 34 | } 35 | --------------------------------------------------------------------------------