diff --git a/next.config.js b/next.config.js index 3af45901..062a0ad0 100644 --- a/next.config.js +++ b/next.config.js @@ -24,5 +24,4 @@ module.exports = { }, experimental: {}, swcMinify: true, - transpilePackages: ['next-sanity-client'], }; diff --git a/package.json b/package.json index 64948211..1a7e6857 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,6 @@ "fs": false }, "dependencies": { - "@emailjs/browser": "^3.12.1", "@fortawesome/fontawesome-svg-core": "^6.6.0", "@fortawesome/free-brands-svg-icons": "^6.6.0", "@fortawesome/free-solid-svg-icons": "^6.6.0", @@ -33,7 +32,6 @@ "next": "14.2.5", "next-auth": "^4.24.7", "prettier-plugin-tailwindcss": "^0.3.0", - "query-string": "^8.2.0", "react": "^18.3.1", "react-cookie-consent": "^6.4.1", "react-dom": "^18.3.1", @@ -79,7 +77,6 @@ "eslint-plugin-prettier": "^3.4.1", "knip": "^5.27.0", "next-sanity": "^9.4.4", - "next-sanity-client": "1.0.8", "postcss": "^8.4.41", "postcss-preset-env": "^7.8.3", "prettier": "^2.8.8", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3103f87f..c10ade5f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,9 +8,6 @@ importers: .: dependencies: - '@emailjs/browser': - specifier: ^3.12.1 - version: 3.12.1 '@fortawesome/fontawesome-svg-core': specifier: ^6.6.0 version: 6.6.0 @@ -62,9 +59,6 @@ importers: prettier-plugin-tailwindcss: specifier: ^0.3.0 version: 0.3.0(prettier@2.8.8) - query-string: - specifier: ^8.2.0 - version: 8.2.0 react: specifier: ^18.3.1 version: 18.3.1 @@ -195,9 +189,6 @@ importers: next-sanity: specifier: ^9.4.4 version: 9.4.4(@sanity/client@6.21.1)(@sanity/icons@3.3.1(react@18.3.1))(@sanity/types@3.53.0)(@sanity/ui@2.8.8(react-dom@18.3.1(react@18.3.1))(react-is@18.3.1)(react@18.3.1)(styled-components@6.1.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1)))(next@14.2.5(@babel/core@7.25.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sanity@3.53.0(@types/node@20.14.14)(@types/react@18.3.3)(bufferutil@4.0.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(terser@5.27.0)(utf-8-validate@5.0.10))(styled-components@6.1.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(svelte@4.2.9) - next-sanity-client: - specifier: 1.0.8 - version: 1.0.8(next@14.2.5(@babel/core@7.25.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) postcss: specifier: ^8.4.41 version: 8.4.41 @@ -1116,10 +1107,6 @@ packages: peerDependencies: react: '>=16.8.0' - '@emailjs/browser@3.12.1': - resolution: {integrity: sha512-C5nK07CgSCFx3onsuRt/ZaaMvIi0T3SHHanM7fKozjSvbZu+OjHHP9W608fYpic0OavF7yIlfy4+lRDO33JdbA==} - engines: {node: '>=14.0.0'} - '@emnapi/runtime@1.2.0': resolution: {integrity: sha512-bV21/9LQmcQeCPEg3BDFtvwL6cwiTMksYNWQQ4KOxCZikEGalWtenoZ0wCiukJINlGCIi2KXx01g4FoH/LxpzQ==} @@ -4011,10 +3998,6 @@ packages: decimal.js@10.4.3: resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==} - decode-uri-component@0.4.1: - resolution: {integrity: sha512-+8VxcR21HhTy8nOt6jf20w0c9CADrw1O8d+VZ/YzzCt4bJ3uBjw+D1q2osAB8RnpwwaeYBxy0HyKQxD5JBMuuQ==} - engines: {node: '>=14.16'} - decompress-response@7.0.0: resolution: {integrity: sha512-6IvPrADQyyPGLpMnUh6kfKiqy7SrbXbjoUuZ90WMBJKErzv2pCiwlGEXjRX9/54OnTq+XFVnkOnOMzclLI5aEA==} engines: {node: '>=10'} @@ -4590,10 +4573,6 @@ packages: resolution: {integrity: sha512-lO3ttPjHZRfjMcxWKb1j1eDhTFsu4meeR3lnMcnBFhk6RuLhvEiuALu2TlfL310ph4lCYYwgF/ElIjdP739tdg==} engines: {node: '>=8'} - filter-obj@5.1.0: - resolution: {integrity: sha512-qWeTREPoT7I0bifpPUXtxkZJ1XJzxWtfoWWkdVGqa+eCr3SHW/Ocp89o8vLvbUuQnadybJpjOKu4V+RwO6sGng==} - engines: {node: '>=14.16'} - finalhandler@1.2.0: resolution: {integrity: sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==} engines: {node: '>= 0.8'} @@ -5983,11 +5962,6 @@ packages: nodemailer: optional: true - next-sanity-client@1.0.8: - resolution: {integrity: sha512-vpJMIsEN9WTuciLWjaGfSfEZChivMf2522sS+tjl6GtbNI8reU+5R29Zi5FGP0/pfieGlaiS3kPF6hIrFCrhCQ==} - peerDependencies: - next: latest - next-sanity@9.4.4: resolution: {integrity: sha512-3AkFx+gi5cumZ0KzW+WlRL156qzCkyRBTWpWinPv2SHdbf2i+hssnoHmL3T/gMFoNICIYwO6TaILPf3LCnjXBg==} engines: {node: '>=18.17'} @@ -6871,10 +6845,6 @@ packages: resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==} engines: {node: '>=0.6'} - query-string@8.2.0: - resolution: {integrity: sha512-tUZIw8J0CawM5wyGBiDOAp7ObdRQh4uBor/fUR9ZjmbZVvw95OD9If4w3MQxr99rg0DJZ/9CIORcpEqU5hQG7g==} - engines: {node: '>=14.16'} - querystring-es3@0.2.1: resolution: {integrity: sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==} engines: {node: '>=0.4.x'} @@ -7527,10 +7497,6 @@ packages: speedometer@1.0.0: resolution: {integrity: sha512-lgxErLl/7A5+vgIIXsh9MbeukOaCb2axgQ+bKCdIE+ibNT4XNYGNCR1qFEGq6F+YDASXK3Fh/c5FgtZchFolxw==} - split-on-first@3.0.0: - resolution: {integrity: sha512-qxQJTx2ryR0Dw0ITYyekNQWpz6f8dGd7vffGNflQQ3Iqj9NJ6qiZ7ELpZsJ/QBhIVAiDfXdag3+Gp8RvWa62AA==} - engines: {node: '>=12'} - split2@4.2.0: resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} engines: {node: '>= 10.x'} @@ -9643,8 +9609,6 @@ snapshots: react: 18.3.1 tslib: 2.6.3 - '@emailjs/browser@3.12.1': {} - '@emnapi/runtime@1.2.0': dependencies: tslib: 2.6.3 @@ -12991,8 +12955,6 @@ snapshots: decimal.js@10.4.3: {} - decode-uri-component@0.4.1: {} - decompress-response@7.0.0: dependencies: mimic-response: 3.1.0 @@ -13806,8 +13768,6 @@ snapshots: filter-obj@2.0.2: {} - filter-obj@5.1.0: {} - finalhandler@1.2.0: dependencies: debug: 2.6.9 @@ -15317,10 +15277,6 @@ snapshots: react-dom: 18.3.1(react@18.3.1) uuid: 8.3.2 - next-sanity-client@1.0.8(next@14.2.5(@babel/core@7.25.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)): - dependencies: - next: 14.2.5(@babel/core@7.25.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - next-sanity@9.4.4(@sanity/client@6.21.1)(@sanity/icons@3.3.1(react@18.3.1))(@sanity/types@3.53.0)(@sanity/ui@2.8.8(react-dom@18.3.1(react@18.3.1))(react-is@18.3.1)(react@18.3.1)(styled-components@6.1.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1)))(next@14.2.5(@babel/core@7.25.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sanity@3.53.0(@types/node@20.14.14)(@types/react@18.3.3)(bufferutil@4.0.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(terser@5.27.0)(utf-8-validate@5.0.10))(styled-components@6.1.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(svelte@4.2.9): dependencies: '@portabletext/react': 3.1.0(react@18.3.1) @@ -16221,12 +16177,6 @@ snapshots: dependencies: side-channel: 1.0.6 - query-string@8.2.0: - dependencies: - decode-uri-component: 0.4.1 - filter-obj: 5.1.0 - split-on-first: 3.0.0 - querystring-es3@0.2.1: {} querystringify@2.2.0: {} @@ -17160,8 +17110,6 @@ snapshots: speedometer@1.0.0: {} - split-on-first@3.0.0: {} - split2@4.2.0: {} stackframe@1.3.4: {} diff --git a/src/app/(website)/[...slug]/page.tsx b/src/app/(website)/[...slug]/page.tsx index 5c80483e..8b6e0031 100644 --- a/src/app/(website)/[...slug]/page.tsx +++ b/src/app/(website)/[...slug]/page.tsx @@ -1,23 +1,29 @@ import { notFound } from 'next/navigation'; -import type { AppPage } from '@/lib/types'; -import { getPageByPath } from '@/lib/api.server'; import { PageComponents } from '@/components/Page/Matcher'; +import { getPageByPath } from '@/lib/sanity/page/getPageByPath'; +import type { AppPage } from '@/lib/types'; export const generateStaticParams = () => { return []; }; // FIXME: https://github.com/vercel/next.js/issues/49489 -const dynamicParams = true; -export { dynamicParams }; +export const dynamicParams = true; const CustomPage: AppPage<{ slug: string[] }> = async ({ params }) => { const [base, ...rest] = params.slug; const path = [`/${base}`].concat(rest).join('/'); - const page = await getPageByPath({ path, next: { revalidate: 120 } }); + const page = await getPageByPath( + { path }, + { + next: { + revalidate: 120, + }, + }, + ); if (!page) return notFound(); diff --git a/src/app/(website)/api/profiles/search/route.ts b/src/app/(website)/api/profiles/search/route.ts deleted file mode 100644 index 8c079dc3..00000000 --- a/src/app/(website)/api/profiles/search/route.ts +++ /dev/null @@ -1,115 +0,0 @@ -import { type NextRequest, NextResponse } from 'next/server'; -import type { Profile, ProfileFilters } from '@/lib/types'; -import { profilesProjections } from '@/lib/queries'; -import { client } from '@/lib/api.server'; - -export const runtime = 'edge'; - -const sanityKeys: Record< - keyof ProfileFilters, - { type: string; value: string } -> = { - roleId: { - type: 'string', - value: 'role._ref', - }, - active: { - type: 'string', - value: 'isActive', - }, - available: { - type: 'bool', - value: 'isAvailable', - }, - description: { - type: 'match', - value: 'description', - }, - location: { - type: 'match', - value: 'location', - }, - seniorityId: { - type: 'string', - value: 'seniority._ref', - }, - technologies: { - type: 'match', - value: 'technologies[]._ref', - }, -}; - -function getParsedValue( - key: { type: string; value: string }, - value: string | string[] | boolean, -): string | boolean { - if (key.type === 'string') return `"${value}"`; - - if (key.type === 'bool') return value as boolean; - - if (key.type === 'match' && Array.isArray(value)) - return `[${value.map((v: string) => `"${v}"`).join(',')}]`; - - if (key.type === 'match') { - return `"${value}"`; - } - - return value as string; -} - -const getType = (type: string) => - ['string', 'bool'].includes(type) ? '==' : type; - -function makeQuery(filters: ProfileFilters): string { - const queryFilters = Object.entries(filters) - .reduce( - (result, entry) => { - const [key, value] = entry as [keyof ProfileFilters, any]; - - if ( - !value || - (Array.isArray(value) && value.length === 0) || - !sanityKeys[key] - ) - return result; - - const type = getType(sanityKeys[key].type); - - const sKey = sanityKeys[key].value; - - const newValue = getParsedValue(sanityKeys[key], value); - - return [...result, `${sKey} ${type} ${newValue}`]; - }, - [''], - ) - .join(' && '); - - return `*[_type =='profile' && isActive == true ${ - queryFilters.length > 0 ? '&&' : '' - } ${queryFilters}] { - ${profilesProjections} - }`; -} - -export async function GET(req: NextRequest) { - const url = new URL(req.url); - - const filters = Object.fromEntries(url.searchParams.entries()); - - const technologies = url.searchParams.getAll('technologies') as any; - - const profiles = await client.fetch({ - query: makeQuery({ - ...filters, - technologies, - available: Boolean(filters.available), - }), - config: { - cache: 'force-cache', - next: { revalidate: 120 }, - }, - }); - - return NextResponse.json(profiles); -} diff --git a/src/app/(website)/cmyk/layout.tsx b/src/app/(website)/cmyk/layout.tsx index 9ac9bc5a..5d696774 100644 --- a/src/app/(website)/cmyk/layout.tsx +++ b/src/app/(website)/cmyk/layout.tsx @@ -1,17 +1,15 @@ import SectionHero from '@/components/SectionHero'; -import { getPageByName } from '@/lib/api.server'; +import { getPageByName } from '@/lib/sanity/page/getPageByName'; export default async function CMYKLayout({ children, }: { children: React.ReactNode; }) { - const page = await getPageByName({ - name: 'CMYK', - next: { - revalidate: 60, - }, - }); + const page = await getPageByName( + { name: 'CMYK' }, + { next: { revalidate: 60 } }, + ); return (
diff --git a/src/app/(website)/cmyk/page.tsx b/src/app/(website)/cmyk/page.tsx index 916f455d..1ec2c596 100644 --- a/src/app/(website)/cmyk/page.tsx +++ b/src/app/(website)/cmyk/page.tsx @@ -1,7 +1,7 @@ import CMYKEdition from '@/components/CMYKEditions'; import CMYKEditionsSkeleton from '@/components/CMYKEditions/CMYKEditionsSkeleton'; import { cmykVersions } from '@/components/CMYKEditions/cmykVersions'; -import { getAllCMYKVersionsOrderedFromLatest } from '@/lib/api.server'; +import { getAllCMYKProjects } from '@/lib/sanity/cmyk/getAllCMYKProjects'; import { getPageMetadata } from '@/lib/seo'; import type { AppPage } from '@/lib/types'; import { notFound } from 'next/navigation'; @@ -12,10 +12,14 @@ export const generateMetadata = () => { }; const CMYKPage: AppPage = async () => { - const versions = await getAllCMYKVersionsOrderedFromLatest({ - next: { revalidate: 3600 }, + const projects = await getAllCMYKProjects({ + next: { + revalidate: 3600, + }, }); + const versions = projects.map((project) => project.cmykVersion); + const current = cmykVersions.find( (cmykVersion) => cmykVersion.version === versions[0], ); diff --git a/src/app/(website)/docs/[slug]/page.tsx b/src/app/(website)/docs/[slug]/page.tsx index e8c13a36..920fc9f7 100644 --- a/src/app/(website)/docs/[slug]/page.tsx +++ b/src/app/(website)/docs/[slug]/page.tsx @@ -1,5 +1,6 @@ import DocsRichText from '@/components/DocsRichText'; -import { getAllDocs, getDocBySlug } from '@/lib/api.server'; +import { getAllDocs } from '@/lib/sanity/docs/getAllDocs'; +import { getDocBySlug } from '@/lib/sanity/docs/getDocBySlug'; import { getMetadata } from '@/lib/seo'; import { AppPage } from '@/lib/types'; import { notFound } from 'next/navigation'; @@ -23,10 +24,12 @@ export const generateStaticParams = async () => { }; const DocPage: AppPage<{ slug: string }> = async ({ params }) => { - const doc = await getDocBySlug({ - slug: params.slug, - next: { revalidate: 60 }, - }); + const doc = await getDocBySlug( + { slug: params.slug }, + { + next: { revalidate: 60 }, + }, + ); if (!doc) return notFound(); diff --git a/src/app/(website)/docs/page.tsx b/src/app/(website)/docs/page.tsx index 9480d417..a7ad7615 100644 --- a/src/app/(website)/docs/page.tsx +++ b/src/app/(website)/docs/page.tsx @@ -1,6 +1,6 @@ import Link from 'next/link'; -import { getAllDocs } from '@/lib/api.server'; +import { getAllDocs } from '@/lib/sanity/docs/getAllDocs'; import { getMetadata } from '@/lib/seo'; export const generateMetadata = () => @@ -13,7 +13,9 @@ export const generateMetadata = () => export default async function DocsPage() { const docs = await getAllDocs({ cache: 'force-cache', - next: { revalidate: 60 }, + next: { + revalidate: 60, + }, }); return ( diff --git a/src/app/(website)/equipo/page.tsx b/src/app/(website)/equipo/page.tsx deleted file mode 100644 index 444aea08..00000000 --- a/src/app/(website)/equipo/page.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import SectionHero from '@/components/SectionHero'; -import StaffCard from '@/components/StaffCard'; -import { getFecTeam, getPageByName } from '@/lib/api.server'; -import { getPageMetadata } from '@/lib/seo'; - -export const generateMetadata = () => getPageMetadata('Equipo'); - -export default async function TeamPage() { - const [page, profiles] = await Promise.all([ - getPageByName({ name: 'Equipo' }), - getFecTeam({ next: { revalidate: 60 } }), - ]); - - return ( - -
    - {profiles?.map((profile, i) => ( - - ))} -
-
- ); -} diff --git a/src/app/(website)/eventos/page.tsx b/src/app/(website)/eventos/page.tsx index ed0fd06b..d60be737 100644 --- a/src/app/(website)/eventos/page.tsx +++ b/src/app/(website)/eventos/page.tsx @@ -1,6 +1,6 @@ import EventList from '@/components/EventList'; import SectionHero from '@/components/SectionHero'; -import { getPageByName } from '@/lib/api.server'; +import { getPageByName } from '@/lib/sanity/page/getPageByName'; import { getPageMetadata } from '@/lib/seo'; export const generateMetadata = () => getPageMetadata('Eventos'); diff --git a/src/app/(website)/inscripcion-cmyk/page.tsx b/src/app/(website)/inscripcion-cmyk/page.tsx index dd695847..1ac0cbc5 100644 --- a/src/app/(website)/inscripcion-cmyk/page.tsx +++ b/src/app/(website)/inscripcion-cmyk/page.tsx @@ -1,6 +1,6 @@ import CMYKForm from '@/components/CMYKForm'; import SectionHero from '@/components/SectionHero'; -import { getSettings } from '@/lib/api.server'; +import { getSettings } from '@/lib/sanity/settings/getSettings'; import { getMetadata } from '@/lib/seo'; import Image from 'next/image'; import Link from 'next/link'; diff --git a/src/app/(website)/layout.tsx b/src/app/(website)/layout.tsx index efb0827f..7c5ffb1d 100644 --- a/src/app/(website)/layout.tsx +++ b/src/app/(website)/layout.tsx @@ -7,8 +7,8 @@ import Footer from '@/components/Footer'; import Header from '@/components/Header'; import PreviewBanner from '@/components/PreviewBanner'; import Providers from '@/components/Providers'; -import { getSettings } from '@/lib/api.server'; import fontVariables from '@/lib/font-variables'; +import { getSettings } from '@/lib/sanity/settings/getSettings'; import { getMetadata } from '@/lib/seo'; import clsx from 'clsx'; import { draftMode } from 'next/headers'; @@ -26,7 +26,11 @@ export default async function RootLayout({ }: { children: React.ReactNode; }) { - const settings = await getSettings({ next: { revalidate: 120 } }); + const settings = await getSettings({ + next: { + revalidate: 120, + }, + }); const { isEnabled } = draftMode(); diff --git a/src/app/(website)/mentorias/(mentorships)/layout.tsx b/src/app/(website)/mentorias/(mentorships)/layout.tsx index f94f9d6f..4ec13149 100644 --- a/src/app/(website)/mentorias/(mentorships)/layout.tsx +++ b/src/app/(website)/mentorias/(mentorships)/layout.tsx @@ -1,6 +1,6 @@ import { PageComponents } from '@/components/Page/Matcher'; import SectionHero from '@/components/SectionHero'; -import { getPageByName } from '@/lib/api.server'; +import { getPageByName } from '@/lib/sanity/page/getPageByName'; async function MentorshipsLayout({ children }: { children: React.ReactNode }) { const page = await getPageByName({ name: 'Mentorías' }); diff --git a/src/app/(website)/mentorias/(mentorships)/page.tsx b/src/app/(website)/mentorias/(mentorships)/page.tsx index 55e95cfa..ff0d0ec0 100644 --- a/src/app/(website)/mentorias/(mentorships)/page.tsx +++ b/src/app/(website)/mentorias/(mentorships)/page.tsx @@ -1,7 +1,8 @@ import MentorList from '@/components/MentorList'; import MentorListSkeleton from '@/components/MentorList/MentorListSkeleton'; -import { getAllMentors, getMentoringTopics } from '@/lib/api.server'; import { getAllDiscordEvents } from '@/lib/discord'; +import { getAllMentors } from '@/lib/sanity/mentor/getAllMentors'; +import { getMentoringTopics } from '@/lib/sanity/topics/getMentoringTopics'; import { getPageMetadata } from '@/lib/seo'; import { shuffle } from '@/lib/shuffle'; import { Suspense } from 'react'; diff --git a/src/app/(website)/mentorias/perfil/[id]/PhotoUpload.tsx b/src/app/(website)/mentorias/perfil/[id]/PhotoUpload.tsx index be6dec86..eced8124 100644 --- a/src/app/(website)/mentorias/perfil/[id]/PhotoUpload.tsx +++ b/src/app/(website)/mentorias/perfil/[id]/PhotoUpload.tsx @@ -1,7 +1,7 @@ 'use client'; import { imgUrlFrom } from '@/lib/sanity'; -import type { Mentor } from '@/lib/types'; +import { Mentor } from '@/lib/sanity/mentor/getMentor'; import { faEdit } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import Image from 'next/image'; diff --git a/src/app/(website)/mentorias/perfil/[id]/mentor-actions.ts b/src/app/(website)/mentorias/perfil/[id]/mentor-actions.ts index 6b596394..d714752e 100644 --- a/src/app/(website)/mentorias/perfil/[id]/mentor-actions.ts +++ b/src/app/(website)/mentorias/perfil/[id]/mentor-actions.ts @@ -1,9 +1,8 @@ 'use server'; import { authOptions } from '@/app/(website)/api/auth/[...nextauth]/authOptions'; -import { getMentor } from '@/lib/api.server'; import { postClient } from '@/lib/sanity'; -import type { Mentor } from '@/lib/types'; +import { getMentor, Mentor } from '@/lib/sanity/mentor/getMentor'; import { getServerSession } from 'next-auth'; import { revalidatePath } from 'next/cache'; @@ -42,7 +41,7 @@ export async function photoUploadAction( export async function mentorFormAction(id: string, formData: FormData) { const session = getServerSession(authOptions); - const mentor = await getMentor({ id }); + const mentor = await getMentor(id); if (!session || !mentor) return; const topicsRefs = formData diff --git a/src/app/(website)/mentorias/perfil/[id]/page.tsx b/src/app/(website)/mentorias/perfil/[id]/page.tsx index 2aeaca75..db4c3bf1 100644 --- a/src/app/(website)/mentorias/perfil/[id]/page.tsx +++ b/src/app/(website)/mentorias/perfil/[id]/page.tsx @@ -2,9 +2,9 @@ import { authOptions } from '@/app/(website)/api/auth/[...nextauth]/authOptions' import Combobox from '@/components/Combobox'; import Input from '@/components/Input'; import SectionHero from '@/components/SectionHero'; -import { getMentor } from '@/lib/api.server'; import client from '@/lib/sanity'; -import type { Topic } from '@/lib/types'; +import { getMentor } from '@/lib/sanity/mentor/getMentor'; +import { Topic } from '@/lib/sanity/topics/getMentoringTopics'; import { faUserEdit } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import groq from 'groq'; @@ -25,7 +25,7 @@ async function Page({ params: { id } }: { params: { id: string } }) { } const [mentor, allTopics] = await Promise.all([ - getMentor({ id }), + getMentor(id), client.fetch( groq`*[_type == 'topic'] { _id, title, description }`, ), diff --git a/src/app/(website)/page.tsx b/src/app/(website)/page.tsx index c0ab4c40..ea45f399 100644 --- a/src/app/(website)/page.tsx +++ b/src/app/(website)/page.tsx @@ -1,14 +1,23 @@ import FeaturedCardList from '@/components/FeaturedCardList'; import Hero from '@/components/Hero'; -import { getAllFeaturedCards, getSettings } from '@/lib/api.server'; +import { getAllFeaturedCards } from '@/lib/sanity/featuredCards/getAllFeaturedCards'; +import { getSettings } from '@/lib/sanity/settings/getSettings'; import { getMetadata } from '@/lib/seo'; export const generateMetadata = () => getMetadata({ title: 'Home' }); export default async function HomePage() { const [settings, cards] = await Promise.all([ - getSettings({ next: { revalidate: 120 } }), - getAllFeaturedCards({ next: { revalidate: 360 } }), + getSettings({ + next: { + revalidate: 120, + }, + }), + getAllFeaturedCards({ + next: { + revalidate: 360, + }, + }), ]); const { diff --git a/src/app/(website)/perfiles/[id]/page.tsx b/src/app/(website)/perfiles/[id]/page.tsx deleted file mode 100644 index 7dba2fe1..00000000 --- a/src/app/(website)/perfiles/[id]/page.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import { notFound } from 'next/navigation'; -import { getProfile } from '@/lib/api.server'; -import type { AppPage } from '@/lib/types'; -import { getMetadata } from '@/lib/seo'; - -export const generateStaticParams = () => []; - -export const dynamicParams = true; - -export const generateMetadata = async ({ - params, -}: { - params: { id: string }; -}) => { - const profile = await getProfile({ id: params.id }); - - return await getMetadata({ - title: `Perfil: ${profile?.person?.firstName}`, - description: 'Compartí tu profile FEC a las redes', - }); -}; - -const ProfilePage: AppPage<{ id: string }> = async ({ params }) => { - const profile = await getProfile({ id: params.id }); - - if (!profile?.person?.discord) return notFound(); - - return ( -
-
- {profile.person.firstName} -
- - Usuario: - {profile.person.firstName} ({profile.person.discord}) - - {profile.person.github && ( - - GitHub: - {profile.person.github} - - )} - {profile.person.portfolio && ( - - Portfolio: - {profile.person.portfolio} - - )} -
-
-
- ); -}; - -export default ProfilePage; diff --git a/src/app/(website)/talentos/nuevo/page.tsx b/src/app/(website)/talentos/nuevo/page.tsx deleted file mode 100644 index fbe05091..00000000 --- a/src/app/(website)/talentos/nuevo/page.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import { - getAllRoles, - getAllSeniorities, - getAllTechnologies, - getPageByName, -} from '@/lib/api.server'; -import SectionHero from '@/components/SectionHero'; -import NewTalentForm from '@/components/NewTalentForm'; -import { getPageMetadata } from '@/lib/seo'; - -export const generateMetadata = () => getPageMetadata('Tu perfil'); - -export default async function NewTalentPage() { - const [page, technologies, roles, seniorities] = await Promise.all([ - getPageByName({ name: 'Tu perfil', next: { revalidate: 60 } }), - getAllTechnologies(), - getAllRoles(), - getAllSeniorities(), - ]); - - const formattedTechnologies = technologies.map((tech) => ({ - ...tech, - label: tech.name, - value: tech._id, - })); - - return ( - <> - - - - - ); -} diff --git a/src/app/(website)/talentos/page.tsx b/src/app/(website)/talentos/page.tsx deleted file mode 100644 index 11faeb06..00000000 --- a/src/app/(website)/talentos/page.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import Profiles from '@/components/Profiles'; -import SectionHero from '@/components/SectionHero'; -import { - getAllProfiles, - getAllRoles, - getAllSeniorities, - getAllTechnologies, - getPageByName, -} from '@/lib/api.server'; -import { shuffle } from '@/lib/shuffle'; -import { getPageMetadata } from '@/lib/seo'; - -function sortResponse(array: T[]) { - return [ - ...array.sort((a, b) => { - if (a.name < b.name) { - return -1; - } - if (a.name > b.name) { - return 1; - } - return 0; - }), - ]; -} - -export const generateMetadata = () => getPageMetadata('Talentos'); - -export default async function TalentsPage() { - const [page, technologies, seniorities, profiles, roles] = await Promise.all([ - getPageByName({ name: 'Talentos' }), - getAllTechnologies(), - getAllSeniorities(), - getAllProfiles({ next: { revalidate: 120 } }), - getAllRoles(), - ]); - - const formattedTechnologies = technologies.map((tech) => ({ - ...tech, - label: tech.name, - value: tech._id, - })); - - sortResponse(technologies); - sortResponse(seniorities); - sortResponse(roles); - - shuffle(profiles); - - return ( - <> - - - - ); -} diff --git a/src/components/CMYKEditions/index.tsx b/src/components/CMYKEditions/index.tsx index d4c4b0cd..93429a44 100644 --- a/src/components/CMYKEditions/index.tsx +++ b/src/components/CMYKEditions/index.tsx @@ -1,4 +1,4 @@ -import { getAllCMYKProjects } from '@/lib/api.server'; +import { getAllCMYKProjects } from '@/lib/sanity/cmyk/getAllCMYKProjects'; import Link from 'next/link'; import { notFound } from 'next/navigation'; import CMYKItemCard from '../CMYKItemCard'; diff --git a/src/components/CMYKItemCard/index.tsx b/src/components/CMYKItemCard/index.tsx index d5ea9f7a..573b901b 100644 --- a/src/components/CMYKItemCard/index.tsx +++ b/src/components/CMYKItemCard/index.tsx @@ -1,7 +1,6 @@ +import { CMYK } from '@/lib/sanity/cmyk/getAllCMYKProjects'; import { Card } from '../Card'; -import { CMYK } from '../../lib/types'; - type CMYKItemProps = { project: CMYK; index: number; diff --git a/src/components/Combobox/index.stories.tsx b/src/components/Combobox/index.stories.tsx index 3ab26036..b78a621b 100644 --- a/src/components/Combobox/index.stories.tsx +++ b/src/components/Combobox/index.stories.tsx @@ -1,4 +1,4 @@ -import { Topic } from '@/lib/types'; +import { Topic } from '@/lib/sanity/topics/getMentoringTopics'; import { Meta, StoryObj } from '@storybook/react'; import { userEvent, within } from '@storybook/test'; import Combobox from '.'; diff --git a/src/components/EventList/index.tsx b/src/components/EventList/index.tsx index 65a37d0a..e9dfce78 100644 --- a/src/components/EventList/index.tsx +++ b/src/components/EventList/index.tsx @@ -1,7 +1,6 @@ import UpcomingEvents from '@/app/(website)/eventos/components/UpcomingEvents'; -import { getAllEvents } from '@/lib/api.server'; import { getAllDiscordEvents } from '@/lib/discord'; -import type { Event } from '@/lib/types'; +import { Event, getAllEvents } from '@/lib/sanity/event/getAllEvents'; import { isPast } from 'date-fns'; import EventPreview from '../EventPreview'; @@ -10,7 +9,9 @@ const pastEvents = (events: Event[]) => export default async function EventList() { const events = await getAllEvents({ - next: { revalidate: 60 }, + next: { + revalidate: 60, + }, }); const upcomingEvents = await getAllDiscordEvents(); diff --git a/src/components/EventPreview/index.tsx b/src/components/EventPreview/index.tsx index 73db6e7e..08366058 100644 --- a/src/components/EventPreview/index.tsx +++ b/src/components/EventPreview/index.tsx @@ -1,6 +1,6 @@ import Timezones from '@/lib/completeTimezones.json'; import { imageBuilder } from '@/lib/sanity'; -import type { Event } from '@/lib/types'; +import { Event } from '@/lib/sanity/event/getAllEvents'; import { format } from 'date-fns'; import { es } from 'date-fns/locale'; import { useMemo } from 'react'; diff --git a/src/components/FeaturedCard/index.tsx b/src/components/FeaturedCard/index.tsx index 528a2645..83dd05f3 100644 --- a/src/components/FeaturedCard/index.tsx +++ b/src/components/FeaturedCard/index.tsx @@ -1,14 +1,19 @@ +import { FeaturedCard as FeaturedCardType } from '@/lib/sanity/featuredCards/getAllFeaturedCards'; import { faArrowRight } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import Link from 'next/link'; -import { FeaturedCards } from '../../lib/types'; -const FeaturedCard = ({ btnText, description, title, link }: FeaturedCards) => { +const FeaturedCard = ({ + btnText, + description, + title, + link, +}: FeaturedCardType) => { return (

{title}

-

{description}

+

{description}

{link && ( = ({ diff --git a/src/components/Footer/index.tsx b/src/components/Footer/index.tsx index 826ea328..2224bf67 100644 --- a/src/components/Footer/index.tsx +++ b/src/components/Footer/index.tsx @@ -1,4 +1,4 @@ -import { getSettings } from '@/lib/api.server'; +import { getSettings } from '@/lib/sanity/settings/getSettings'; import Image from 'next/image'; import Link from 'next/link'; import logo_vercel from '../../../public/img/powered-by-vercel.svg'; @@ -6,8 +6,11 @@ import SocialMediaLinks from '../SocialMediaLinks'; const Footer = async () => { const { socialnetworks, footerNavItems } = await getSettings({ - next: { revalidate: 120 }, + next: { + revalidate: 120, + }, }); + const currentYear = new Date().getFullYear(); return ( @@ -32,7 +35,7 @@ const Footer = async () => {
-
+

© FrontendCafé {currentYear}

= ({ preview, logo, navItems }) => { diff --git a/src/components/MentorCard/index.tsx b/src/components/MentorCard/index.tsx index 03a2b812..6ec40fef 100644 --- a/src/components/MentorCard/index.tsx +++ b/src/components/MentorCard/index.tsx @@ -1,5 +1,7 @@ import { FrontendCafeId } from '@/lib/constants'; -import type { DiscordEvent, Mentor, Topic } from '@/lib/types'; +import { Mentor } from '@/lib/sanity/mentor/getMentor'; +import { Topic } from '@/lib/sanity/topics/getMentoringTopics'; +import type { DiscordEvent } from '@/lib/types'; import { faChain } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { motion } from 'framer-motion'; diff --git a/src/components/MentorList/index.tsx b/src/components/MentorList/index.tsx index 8fbc81fe..9180f3cb 100644 --- a/src/components/MentorList/index.tsx +++ b/src/components/MentorList/index.tsx @@ -1,6 +1,8 @@ 'use client'; -import type { DiscordEvent, Mentor, Topic } from '@/lib/types'; +import { Mentor } from '@/lib/sanity/mentor/getMentor'; +import { Topic } from '@/lib/sanity/topics/getMentoringTopics'; +import type { DiscordEvent } from '@/lib/types'; import { faDiscord } from '@fortawesome/free-brands-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { signIn, useSession } from 'next-auth/react'; diff --git a/src/components/Navbar/index.tsx b/src/components/Navbar/index.tsx index 86ffd2eb..deeefccd 100644 --- a/src/components/Navbar/index.tsx +++ b/src/components/Navbar/index.tsx @@ -1,12 +1,12 @@ +import { Settings } from '@/lib/sanity/settings/getSettings'; import Image from 'next/image'; import Link from 'next/link'; -import { NavItemData } from '../../lib/types'; import MenuBtn from './MenuBtn'; import NavItem from './NavItem'; import UserSettings from './UserSettings'; interface NavbarProps { - navItems: NavItemData[]; + navItems: Settings['navItems']; logoImg: string; isOpen: boolean; toggle: () => void; diff --git a/src/components/NewTalentForm/index.tsx b/src/components/NewTalentForm/index.tsx deleted file mode 100644 index d5987971..00000000 --- a/src/components/NewTalentForm/index.tsx +++ /dev/null @@ -1,482 +0,0 @@ -'use client'; - -import type { Profile, Role, Seniority, Technology } from '@/lib/types'; -import emailjs from '@emailjs/browser'; -import { signIn, useSession } from 'next-auth/react'; -import Link from 'next/link'; -import React, { useEffect, useState } from 'react'; -import { - useForm, - type FieldValues, - type SubmitErrorHandler, - type SubmitHandler, -} from 'react-hook-form'; -import Resizer from 'react-image-file-resizer'; -import Select, { type MultiValue } from 'react-select'; - -type Technologies = Technology[]; - -type NewTalentFormProps = { - technologies: Technologies; - roles: Role[]; - seniorities: Seniority[]; -}; - -const resizeFile = (newFile: File) => - new Promise((resolve) => { - Resizer.imageFileResizer( - newFile, - 150, - 150, - 'WEBP', - 60, - 0, - (uri) => { - return resolve(uri); - }, - 'base64', - ); - }); - -interface LabelProps { - children: React.ReactNode; -} - -const Label: React.FC = ({ children }) => { - return ( - - ); -}; - -interface DescriptionProps { - children: React.ReactNode; -} - -const Description: React.FC = ({ children }) => { - return

{children}

; -}; - -interface InputContainerProps { - children: React.ReactNode; -} -const InputContainer: React.FC = ({ children }) => { - return
{children}
; -}; - -const Required = () => { - return *; -}; - -const NewTalentForm: React.FC = ({ - technologies, - roles, - seniorities, -}) => { - const { data: session, status } = useSession(); - const loading = status === 'loading'; - - const { - register, - handleSubmit, - setValue, - formState: { errors }, - } = useForm(); - const [selectedTechnologies, setSelectedTechnologies] = useState< - MultiValue - >([]); - const [photo, setPhoto] = useState(''); - const [userId, setUserId] = useState(''); - const [loadingProfile, setLoadingProfile] = useState(true); - const [message, setMessage] = useState({ error: false, text: '' }); - const [loadingForm, setLoadingForm] = useState(false); - - const handleTechnologies = (techSelected: MultiValue) => { - setSelectedTechnologies(techSelected); - }; - - useEffect(() => { - const fetchUser = async (uId: string) => { - setLoadingProfile(true); - const response = await fetch(`/api/profiles/${uId}`); - - if (response.status === 200) { - const user: Profile = await response.json(); - - setSelectedTechnologies(user.technologies); - setPhoto(user.person.photo); - setUserId(user._id); - setValue('email', user.person.email); - setValue('name', user.person.firstName); - setValue('location', user.location); - setValue('twitter', user.person.twitter); - setValue('linkedin', user.person.linkedin); - setValue('github', user.person.github); - setValue('portfolio', user.person.portfolio); - setValue('roleId', user.role._id); - setValue('seniorityId', user.seniority._id); - setValue('description', user.description); - setValue('available', user.isAvailable); - } - - setLoadingProfile(false); - }; - if (session && !loading) { - fetchUser(session.user.id); - } - }, [session, setValue, loading]); - - const handleFile = async (event: React.ChangeEvent) => { - const { files } = event.target; - if (!files || files.length <= 0) return; - const file = files[0]; - if (file) { - const image = await resizeFile(file); - setPhoto(image as string); - } - }; - - const onSubmit: SubmitHandler = async (data) => { - setLoadingForm(true); - try { - const body = { - ...data, - technologies: selectedTechnologies, - photo, - discordId: session?.user.id, - id: userId, - }; - await fetch('/api/profiles', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(body), - }); - - setMessage({ - error: false, - text: `${ - !userId - ? 'Tu perfil ha sido creado con éxito. Será publicado en nuestro portal en los próximos días.' - : 'Tu perfil ha sido actualizado con éxito.' - }`, - }); - } catch (error) { - setMessage({ - error: true, - text: 'Tu perfil no ha podido ser guardado, por favor vuelva a intentarlo nuevamente', - }); - } - emailjs.send( - 'fec_gmail', - 'talentos_ingreso', - { - user: session?.user.name, - id: userId, - }, - process.env.NEXT_PUBLIC_EMAILJS_USER_ID, - ); - - setLoadingForm(false); - setTimeout(() => setMessage({ error: false, text: '' }), 5000); - }; - - const onError: SubmitErrorHandler | undefined = (errorsLog, e) => - console.log(errorsLog, e); - - if (loading && loadingProfile) { - return ( -
- Cargando sesión... -
- ); - } - - return ( -
- {message.text && ( -
- {message.text} -
- )} - {session ? ( - loadingForm ? ( -
Enviando formulario...
- ) : ( -
-
- - - - - - - - - - - - - - - - - - - Incluye enlace completo de tu perfil. -
- -
-
- - - - Incluye enlace completo de tu perfil. - - - - - Incluye enlace completo de tu perfil. - - - - - - Incluye enlace completo de tu web personal. - -
- -
-
- - - - - - - - -
- -
-
-
- - -
- -
-
- - - Selecciona un máximo de 5 tecnologías. - - - - - - - - - - -
-
- -
-
-
- ) - ) : ( -
-

- Para poder registrar tu perfil es necesario que inicies sesión con - Discord. -

- - - - -
- )} -
- ); -}; - -export default NewTalentForm; diff --git a/src/components/Page/Components.tsx b/src/components/Page/Components.tsx index 47ff3fee..abaa534c 100644 --- a/src/components/Page/Components.tsx +++ b/src/components/Page/Components.tsx @@ -1,10 +1,10 @@ -import { Card } from '../Card'; -import type { Component, FeaturedCards, Step } from '@/lib/types'; -import Matcher from './Matcher'; -import clsx from 'clsx'; -import StepsComponent from '../Steps'; import { imageBuilder } from '@/lib/sanity'; +import type { Component, Step } from '@/lib/types'; +import clsx from 'clsx'; +import { Card } from '../Card'; import FeaturedCard from '../FeaturedCard'; +import StepsComponent from '../Steps'; +import Matcher from './Matcher'; type CardComponentProps = { title?: string; @@ -58,7 +58,7 @@ export const Grid: React.FC = ({
= ({ steps }) => ( ); -export const FeatureCard: React.FC = (props) => ( - -); +export const FeatureCard = FeaturedCard; export const Spacing: React.FC<{ width?: number; height: number }> = ({ width, diff --git a/src/components/ProfileCard/index.stories.ts b/src/components/ProfileCard/index.stories.ts deleted file mode 100644 index 15d3803b..00000000 --- a/src/components/ProfileCard/index.stories.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { Meta, StoryObj } from '@storybook/react'; -import ProfileCard from '.'; - -const meta = { - title: 'Components/Cards/Profile Card', - component: ProfileCard, - tags: ['autodocs'], -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - args: { - profile: { - _id: '0', - description: - 'Lorem ipsum dolor sit amet consectetur adipisicing elit. Error vitae repellat dolores iste, maxime neque delectus, debitis ipsa omnis architecto a earum eaque, reprehenderit sequi atque eius voluptate expedita laboriosam!', - isAvailable: false, - location: 'location', - person: { - _id: '1', - discord: 'discord', - email: 'email', - firstName: 'firstName', - github: 'github', - lastName: 'lastName', - linkedin: 'linkedin', - photo: 'https://placehold.co/112x112', - portfolio: 'portfolio', - twitter: 'twitter', - username: 'username', - }, - role: { - _id: '0', - name: 'role', - }, - seniority: { - _id: '1', - name: 'senionity', - }, - technologies: [ - { - _id: '1', - name: 'Frontend', - }, - { - _id: '1', - name: 'UI/UX', - }, - ], - }, - }, -}; diff --git a/src/components/ProfileCard/index.tsx b/src/components/ProfileCard/index.tsx deleted file mode 100644 index d3b6201d..00000000 --- a/src/components/ProfileCard/index.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import { Profile } from '@/lib/types'; -import Image from 'next/image'; -import SocialMediaLinks from '../SocialMediaLinks'; - -type Props = { - profile: Profile; -}; - -const ProfileCard: React.FC = ({ profile }) => { - // TODO: Check why "email", "portfolio" aren't rendering - // const socialMediaList = { - // ...(profile.person.email && { email: 'mailto:' + profile.person.email }), - // ...(profile.person.linkedin && { linkedin: profile.person.linkedin }), - // ...(profile.person.github && { github: profile.person.github }), - // ...(profile.person.twitter && { twitter: profile.person.twitter }), - // ...(profile.person.portfolio && { portfolio: profile.person.portfolio }), - // }; - - return ( -
  • -
    - {profile.person.firstName} -
    -
    -

    {profile.person.firstName}

    -

    - {profile.role.name} - | {profile.seniority.name} -

    - - {profile.location && ( -

    {profile.location}

    - )} -
    - -

    - {profile.description} -

    - {profile.technologies?.length > 0 && ( -
      - {profile.technologies?.map((tech) => ( -
    • - {tech.name} -
    • - ))} -
    - )} - {profile.isAvailable && ( -
    - En búsqueda activa -
    - )} -
  • - ); -}; - -export default ProfileCard; diff --git a/src/components/ProfileList/index.stories.ts b/src/components/ProfileList/index.stories.ts deleted file mode 100644 index 5cbb02e6..00000000 --- a/src/components/ProfileList/index.stories.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { Meta, StoryObj } from '@storybook/react'; -import ProfileList from '.'; - -const meta = { - title: 'ProfileList', - component: ProfileList, - args: {}, - tags: ['autodocs'], -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - args: { - isError: false, - isLoading: false, - profiles: Array(6).fill({ - _id: '0', - description: - 'Lorem ipsum dolor sit amet consectetur adipisicing elit. Odit est, similique voluptatem doloribus laudantium cumque deleniti asperiores quae tempore beatae?', - isAvailable: false, - location: 'Location', - person: { - _id: '1', - discord: '/#', - email: '/#', - firstName: 'First Name', - github: '/#', - lastName: 'Lastname', - linkedin: '/#', - photo: 'https://placehold.co/400x400', - portfolio: 'portfolio', - twitter: '/#', - username: 'User Name', - }, - role: { - _id: '0', - name: 'Role', - }, - seniority: { - _id: '1', - name: 'Senionity', - }, - technologies: [ - { - _id: '1', - name: 'Frontend', - }, - { - _id: '1', - name: 'UI/UX', - }, - ], - }), - }, -}; diff --git a/src/components/ProfileList/index.tsx b/src/components/ProfileList/index.tsx deleted file mode 100644 index f6d8bfb5..00000000 --- a/src/components/ProfileList/index.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import ProfileCard from '@/components/ProfileCard'; -import { Profile } from '@/lib/types'; - -interface ProfilesProps { - isLoading: boolean; - isError: boolean; - profiles: Profile[]; -} - -const Profiles: React.FC = ({ - isLoading, - isError, - profiles, -}) => { - if (isLoading) return
    Cargando...
    ; - - if (isError) - return ( -
    - Hubo un error al cargar los perfiles, intente de nuevo -
    - ); - - if (profiles.length === 0) { - return ( -
    - No se han encontrado perfiles con los filtros aplicados. -
    - ); - } - - return ( -
      - {profiles.map((profile) => ( - - ))} -
    - ); -}; - -export default Profiles; diff --git a/src/components/Profiles/PaginationBar.tsx b/src/components/Profiles/PaginationBar.tsx deleted file mode 100644 index 4f49fcdd..00000000 --- a/src/components/Profiles/PaginationBar.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import { faArrowLeft, faArrowRight } from '@fortawesome/free-solid-svg-icons'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import React, { Dispatch, SetStateAction } from 'react'; - -interface PaginationBarProps { - page: number; - pagesCount: number; - setPage: Dispatch>; - totalProfiles: number; -} - -const PaginationBar: React.FC = ({ - page, - pagesCount, - setPage, - totalProfiles, -}) => { - return ( -
    -
    - - - Página {page} de {pagesCount} - - -
    - - Total de {totalProfiles} perfiles - -
    - ); -}; - -export default PaginationBar; diff --git a/src/components/Profiles/filterReducer.ts b/src/components/Profiles/filterReducer.ts deleted file mode 100644 index e97eb203..00000000 --- a/src/components/Profiles/filterReducer.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { ProfileFilters, Technology } from '@/lib/types'; - -interface FilterStringAction { - type: 'ADD_ROLE' | 'ADD_LOCATION' | 'ADD_SENIORITY' | 'ADD_DESCRIPTION'; - payload: string; -} - -interface FilterTechnologyAction { - type: 'ADD_TECHNOLOGIES'; - payload: readonly Technology[]; -} - -interface FilterBooleanAction { - type: 'SET_AVAILABLE'; - payload: boolean; -} - -export type FilterProfileAction = - | FilterStringAction - | FilterTechnologyAction - | FilterBooleanAction; - -export function filterReducer( - state: ProfileFilters, - action: FilterProfileAction, -): ProfileFilters { - switch (action.type) { - case 'ADD_ROLE': - return { ...state, roleId: action.payload }; - case 'ADD_LOCATION': - return { ...state, location: action.payload }; - case 'ADD_SENIORITY': - return { ...state, seniorityId: action.payload }; - case 'ADD_DESCRIPTION': - return { ...state, description: action.payload }; - case 'ADD_TECHNOLOGIES': - return { ...state, technologies: [...action.payload] }; - case 'SET_AVAILABLE': - return { ...state, available: action.payload }; - default: - throw new Error('Invalid action'); - } -} diff --git a/src/components/Profiles/index.stories.ts b/src/components/Profiles/index.stories.ts deleted file mode 100644 index 622558d8..00000000 --- a/src/components/Profiles/index.stories.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { Meta, StoryObj } from '@storybook/react'; -import Profiles from '.'; - -const meta = { - title: 'Profiles', - component: Profiles, - args: {}, - tags: ['autodocs'], -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - args: { - technologies: [ - { _id: '0', name: 'Technology' }, - { _id: '1', name: 'Technology' }, - ], - roles: [ - { _id: '0', name: 'Role' }, - { _id: '1', name: 'Role' }, - ], - seniorities: [ - { _id: '0', name: 'Seniority' }, - { _id: '1', name: 'Seniority' }, - ], - profiles: Array(30).fill({ - _id: '0', - description: - 'Lorem ipsum dolor sit amet consectetur adipisicing elit. Odit est, similique voluptatem doloribus laudantium cumque deleniti asperiores quae tempore beatae?', - isAvailable: false, - location: 'Location', - person: { - _id: '1', - discord: '/#', - email: '/#', - firstName: 'First Name', - github: '/#', - lastName: 'Lastname', - linkedin: '/#', - photo: 'https://placehold.co/400x400', - portfolio: 'portfolio', - twitter: '/#', - username: 'User Name', - }, - role: { - _id: '0', - name: 'Role', - }, - seniority: { - _id: '1', - name: 'Senionity', - }, - technologies: [ - { - _id: '1', - name: 'Frontend', - }, - { - _id: '1', - name: 'UI/UX', - }, - ], - }), - }, -}; diff --git a/src/components/Profiles/index.tsx b/src/components/Profiles/index.tsx deleted file mode 100644 index bf515fcc..00000000 --- a/src/components/Profiles/index.tsx +++ /dev/null @@ -1,84 +0,0 @@ -'use client'; - -import ProfileList from '@/components/ProfileList'; -import FilterForm from '@/components/ProfilesFilterForm'; -import { Profile, Role, Seniority, Technology } from '@/lib/types'; -import React, { - SetStateAction, - Suspense, - useEffect, - useRef, - useState, -} from 'react'; -import PaginationBar from './PaginationBar'; -import { useProfiles } from './useProfiles'; - -interface PostsPageProps { - profiles: Profile[]; - technologies: Technology[]; - roles: Role[]; - seniorities: Seniority[]; -} - -const Profiles: React.FC = ({ - profiles, - seniorities, - roles, - technologies, -}) => { - const { - filters, - dispatchFilter, - isLoading, - isError, - page, - setPage, - pagesCount, - pageProfiles, - totalProfiles, - } = useProfiles(profiles); - const [hasToScroll, setHasToScroll] = useState(false); - - const profileListRef = useRef(null); - - useEffect(() => { - if (hasToScroll) { - profileListRef.current?.scrollIntoView({ behavior: 'smooth' }); - setHasToScroll(false); - } - }, [hasToScroll]); - - const moveToPage = (page: SetStateAction) => { - setHasToScroll(true); - setPage(page); - }; - - return ( -
    -
    - - - -
    - - -
    - ); -}; - -export default Profiles; diff --git a/src/components/Profiles/useProfiles.ts b/src/components/Profiles/useProfiles.ts deleted file mode 100644 index 76e2f158..00000000 --- a/src/components/Profiles/useProfiles.ts +++ /dev/null @@ -1,134 +0,0 @@ -import { - useState, - useEffect, - useReducer, - Dispatch, - SetStateAction, - useRef, -} from 'react'; -import { Profile, ProfileFilters } from '@/lib/types'; -import { filterReducer, FilterProfileAction } from './filterReducer'; -import { useDebounce } from '@/lib/useDebounce'; -import { shuffle } from '@/lib/shuffle'; -import queryString from 'query-string'; - -function searchProfiles(filters: ProfileFilters) { - const query = queryString.stringify({ - ...filters, - technologies: filters.technologies?.map((t) => t._id), - }); - - return fetch(`/api/profiles/search?${query}`); -} - -interface FilteredProfilesState { - isLoading: boolean; - isError: boolean; - profiles?: Profile[]; -} - -const initialProfileFilters: ProfileFilters = { - roleId: '', - location: '', - seniorityId: '', - description: '', - technologies: [], - available: false, -}; - -// https://www.telerik.com/blogs/debouncing-and-throttling-in-javascript -/** - * Minimum time between requests to database (in ms) - */ -const DEBOUNCE_TIME = 300; - -/** - * Number of profiles per page - */ -const ITEMS_PER_PAGE = 18; - -interface UseProfilesResult { - filters: ProfileFilters; - dispatchFilter: Dispatch; - isLoading: boolean; - isError: boolean; - pageProfiles: Profile[]; - page: number; - pagesCount: number; - totalProfiles: number; - setPage: Dispatch>; -} - -export function useProfiles(profiles: Profile[]): UseProfilesResult { - const initialLoad = useRef(true); - - const [filters, dispatchFilter] = useReducer( - filterReducer, - initialProfileFilters, - ); - - const [page, setPage] = useState(1); - const [pagesCount, setPageCount] = useState( - Math.ceil(profiles.length / ITEMS_PER_PAGE), - ); - - const [filteredProfiles, setFilteredProfiles] = - useState({ - isLoading: false, - isError: false, - profiles, - }); - - const debouncedFilters = useDebounce(filters, DEBOUNCE_TIME); - - useEffect(() => { - if (!initialLoad.current) { - setFilteredProfiles({ isLoading: true, isError: false, profiles: [] }); - } - }, [filters]); - - useEffect(() => { - const fn = async () => { - const response = await searchProfiles(debouncedFilters); - - let profiles = []; - try { - profiles = await response.json(); - } catch (error) { - setFilteredProfiles({ isLoading: false, isError: true, profiles: [] }); - } - - // randomizes profiles in place - shuffle(profiles); - - // go back to page 1 in new search - setPage(1); - - setPageCount(Math.ceil(profiles.length / ITEMS_PER_PAGE)); - - setFilteredProfiles({ isLoading: false, isError: false, profiles }); - }; - - if (initialLoad.current) { - initialLoad.current = false; - } else { - fn(); - } - }, [debouncedFilters]); - - const offset = (page - 1) * ITEMS_PER_PAGE; - const pageProfiles = - filteredProfiles.profiles?.slice(offset, offset + ITEMS_PER_PAGE) ?? []; - - return { - filters, - dispatchFilter, - pageProfiles: pageProfiles, - isLoading: filteredProfiles.isLoading, - isError: filteredProfiles.isError, - page, - pagesCount, - totalProfiles: profiles.length, - setPage, - }; -} diff --git a/src/components/ProfilesFilterForm/index.stories.ts b/src/components/ProfilesFilterForm/index.stories.ts deleted file mode 100644 index 45d84cfc..00000000 --- a/src/components/ProfilesFilterForm/index.stories.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { Meta, StoryObj } from '@storybook/react'; -import { ComponentProps } from 'react'; -import ProfilesFilterForm from '.'; - -const args = { - technologies: [ - { _id: '0', name: 'Technology' }, - { _id: '1', name: 'Technology' }, - ], - roles: [ - { _id: '0', name: 'Role' }, - { _id: '1', name: 'Role' }, - ], - seniorities: [ - { _id: '0', name: 'Seniority' }, - { _id: '1', name: 'Seniority' }, - ], - filters: { - active: false, - available: false, - description: 'Description', - location: 'Location', - roleId: 'roleId', - seniorityId: 'seniorityId', - technologies: [ - { _id: '0', name: 'technologies' }, - { _id: '1', name: 'technologies' }, - ], - }, - dispatch: () => { - console.log('dispatch'); - }, -} satisfies ComponentProps; - -const meta = { - title: 'ProfilesFilterForm', - component: ProfilesFilterForm, - args, - tags: ['autodocs'], -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - args, -}; diff --git a/src/components/ProfilesFilterForm/index.tsx b/src/components/ProfilesFilterForm/index.tsx deleted file mode 100644 index 8520e2af..00000000 --- a/src/components/ProfilesFilterForm/index.tsx +++ /dev/null @@ -1,166 +0,0 @@ -import { FilterProfileAction } from '@/components/Profiles/filterReducer'; -import { ProfileFilters, Role, Seniority, Technology } from '@/lib/types'; -import { useRouter, useSearchParams } from 'next/navigation'; -import React, { Dispatch } from 'react'; -import Select from 'react-select'; - -interface FormProps { - filters: ProfileFilters; - dispatch: Dispatch; - technologies: Technology[]; - roles: Role[]; - seniorities: Seniority[]; -} - -const maxTechnologies = 5; - -const FilterForm: React.FC = ({ - filters, - dispatch, - technologies, - roles, - seniorities, -}) => { - const router = useRouter(); - - const searchParams = useSearchParams(); - const activesQuery = searchParams?.get('activos'); - - const onSubmit: React.FormEventHandler = (e) => { - e.preventDefault(); - }; - - const isRoleSelected = filters.roleId !== ''; - const isSenioritySelected = filters.seniorityId !== ''; - - React.useEffect(() => { - if (activesQuery) - dispatch({ - type: 'SET_AVAILABLE', - payload: true, - }); - }, [activesQuery, dispatch]); - - return ( -
    -
    - - - - dispatch({ type: 'ADD_LOCATION', payload: event.target.value }) - } - /> - - dispatch({ type: 'ADD_DESCRIPTION', payload: event.target.value }) - } - /> -
    -
    - { - dispatch({ - type: 'SET_AVAILABLE', - payload: event.target.checked, - }); - activesQuery && router.push('/talentos'); - }} - className={`border-zinc form-checkbox absolute block h-6 w-6 cursor-pointer rounded-full border-4 outline-none ring-0 transition-transform focus:outline-none focus:ring-0 focus:ring-offset-0 ${ - filters.available - ? ' translate-x-4 text-green-400' - : 'border-zinc-500' - }`} - /> - -
    -
    -
    - - ); -}; - -export default FilterForm; diff --git a/src/components/SocialMediaLinks/index.tsx b/src/components/SocialMediaLinks/index.tsx index fb6d3a5f..a3905229 100644 --- a/src/components/SocialMediaLinks/index.tsx +++ b/src/components/SocialMediaLinks/index.tsx @@ -1,4 +1,5 @@ -import type { Mentor, Profile, SocialNetworks } from '@/lib/types'; +import { Mentor } from '@/lib/sanity/mentor/getMentor'; +import { SocialNetworks } from '@/lib/sanity/settings/getSettings'; import { faGithub, faInstagram, @@ -24,7 +25,7 @@ const iconCollection = { interface Props extends React.ComponentProps<'ul'> { background?: 'solid' | 'transparent'; - socialMedia: SocialMedia | Profile | Mentor | SocialNetworks; + socialMedia: SocialMedia | Mentor | SocialNetworks; } type IconCollection = typeof iconCollection; type SocialMedia = { diff --git a/src/components/StaffCard/index.stories.ts b/src/components/StaffCard/index.stories.ts deleted file mode 100644 index 6ab79e55..00000000 --- a/src/components/StaffCard/index.stories.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { Meta, StoryObj } from '@storybook/react'; -import StaffCard from '.'; - -const meta = { - title: 'StaffCard', - component: StaffCard, - tags: ['autodocs'], -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - args: { - index: 0, - profile: { - _id: '0', - discordID: 'discordID', - photo: '', // TODO: Add fallback placeholder? - username: { current: 'Username' }, - cmykParticipant: [ - { - aboutParticipant: 'About Participant', - cmykVersion: '0', - discordUser: { _ref: '', _type: '_type' }, - experience: 'Experience', - isChix: false, - otherQuestions: 'Other Questions', - previousKnowledge: 'Previous Knowladge', - projects: 'Projects', - stackWanted: 'Stack Wanted', - status: 'Status', - timeAvailability: 'Time Availability', - workExperience: 'Work Experience', - _id: '_id', - participationLevel: 'Participation Level', - participationType: 'Participation Type', - }, - ], - email: 'email', - fecTeam: false, - firstName: 'First Name', - github: '/#', - lastName: 'Last Name', - linkedin: '/#', - portfolio: 'Portfolio', - timezone: 'Time Zone', - twitter: '/#', - }, - }, -}; diff --git a/src/components/StaffCard/index.tsx b/src/components/StaffCard/index.tsx deleted file mode 100644 index 2f64497e..00000000 --- a/src/components/StaffCard/index.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { imageBuilder } from '@/lib/sanity'; -import { Person } from '@/lib/types'; -import Image from 'next/image'; -import Link from 'next/link'; - -interface ProfileProp { - profile: Person; - index: number; -} - -const StaffCard: React.FC = ({ profile, index }) => { - return ( -
  • - {profile.photo && ( - {`Foto - )} - -

    - {profile.firstName} {profile.lastName} -

    - -
  • - ); -}; - -export default StaffCard; diff --git a/src/lib/api.server.ts b/src/lib/api.server.ts deleted file mode 100644 index f2dd26d7..00000000 --- a/src/lib/api.server.ts +++ /dev/null @@ -1,84 +0,0 @@ -import * as queries from './queries'; - -import type { - CMYK, - Doc, - Event, - FeaturedCards, - Mentor, - Page, - Person, - Profile, - Role, - Seniority, - Settings, - Technology, - Topic, -} from './types'; - -import SanityClient from 'next-sanity-client'; -import { config } from './sanity'; - -export const client = new SanityClient({ - ...config, - queries, -}); - -export const getSettings = client.createApiUtil('settingsQuery'); - -export const getPageByPath = client.createApiUtil( - 'pageByPathQuery', -); - -export const getAllFeaturedCards = - client.createApiUtil('featuredCardsQuery'); - -export const getAllDocs = client.createApiUtil('docsQuery'); - -export const getDocBySlug = client.createApiUtil< - Doc | undefined, - { slug: string } ->('docQuery'); - -export const getPageByName = client.createApiUtil( - 'pageQueryByName', - { - next: { - revalidate: 120, - }, - }, -); - -export const getAllEvents = client.createApiUtil('eventsQuery'); - -export const getFecTeam = client.createApiUtil('staffQuery'); - -export const getAllCMYKProjects = client.createApiUtil('cmykQuery'); - -export const getAllCMYKVersionsOrderedFromLatest = client.createApiUtil( - 'cmykVersionsOrderedFromLatestQuery', -); - -export const getProfile = client.createApiUtil( - 'profileQuery', -); -// FIXME: Mentor>topics type -type TMentor = Omit & { topics: Topic[] }; -export const getMentor = client.createApiUtil( - 'mentorQuery', -); - -export const getMentoringTopics = - client.createApiUtil('mentorsTopicsQuery'); - -export const getAllMentors = client.createApiUtil('mentorsQuery'); - -export const getAllProfiles = client.createApiUtil('profilesQuery'); - -export const getAllSeniorities = - client.createApiUtil('senioritiesQuery'); - -export const getAllTechnologies = - client.createApiUtil('technologiesQuery'); - -export const getAllRoles = client.createApiUtil('rolesQuery'); diff --git a/src/lib/api.ts b/src/lib/api.ts index 5581a2c1..1453cd01 100644 --- a/src/lib/api.ts +++ b/src/lib/api.ts @@ -1,7 +1,9 @@ import client, { postClient, previewClient } from './sanity'; -import { CMYKParticipant, Event, Person, Profile, SanityEvent } from './types'; +import { SanityEvent } from './types'; -import { personQuery, personQueryByDiscordID } from './queries'; +import { personQueryByDiscordID } from './queries'; +import { Event } from './sanity/event/getAllEvents'; +import { CMYKParticipant, Person } from './sanity/person/types'; const eventFields = ` title, @@ -82,10 +84,3 @@ export async function getPersonByDiscordID( const result = await getClient(preview).fetch(personQueryByDiscordID, { id }); return result.length > 0 && result[0]; } - -export async function getPerson( - id: string, - preview = false, -): Promise { - return await getClient(preview).fetch(personQuery, { id }); -} diff --git a/src/lib/queries.ts b/src/lib/queries.ts index 5dd21e5e..314408a4 100644 --- a/src/lib/queries.ts +++ b/src/lib/queries.ts @@ -73,25 +73,6 @@ export const eventsQuery = groq` } `; -export const eventsQueryByType = groq` - *[_type == "event" && category->name == $categoryFilter && dateTime(now()) < dateTime(date)] | order(date asc) { - title, - 'slug': slug.current, - 'category': { - 'name': category->name, - }, - 'cover': { - 'alt': cover.alt, - 'src': cover.asset->url - }, - date, - endDate, - tags, - recording, - description - } -`; - export const futureEventsDiscordIdQuery = groq` *[_type == "event" && dateTime(now()) <= dateTime(date) && discordId != null] { discordId, @@ -154,7 +135,7 @@ export const mentorsQuery = groq` github, twitter, linkedin, - topics + topics[]-> } `; @@ -177,7 +158,7 @@ export const docQuery = groq` `; export const cmykQuery = groq` - *[_type == "cmyk"] { + *[_type == "cmyk"] | order(cmykVersion desc) { _id, name, description, @@ -191,10 +172,6 @@ export const cmykQuery = groq` } `; -export const cmykVersionsOrderedFromLatestQuery = groq` - *[_type == "cmyk"] | order(cmykVersion desc) .cmykVersion -`; - export const personQueryByDiscordID = groq` *[_type == "person" && discordID.current == $id]{ _id, @@ -204,17 +181,6 @@ export const personQueryByDiscordID = groq` } `; -export const staffQuery = groq` - *[_type == "person" && fecTeam] | order(_id desc){ - _id, - username, - firstName, - lastName, - linkedin, - photo, - } -`; - export const featuredCardsQuery = groq` *[_type == 'featuredCards'] { _id, @@ -227,88 +193,6 @@ export const featuredCardsQuery = groq` } `; -export const technologiesQuery = groq` - *[_type == 'technology'] { - _id, - name - } -`; - -export const senioritiesQuery = groq` - *[_type == 'seniority'] { - _id, - name - } -`; - -export const rolesQuery = groq` - *[_type == 'role'] { - _id, - name - } -`; - -export const profilesProjections = ` - _id, - description, - location, - isAvailable, - role-> { - _id, - name - }, - person-> { - "discord": discordID.current, - email, - firstName, - lastName, - github, - linkedin, - "photo": photo.asset->url, - portfolio, - twitter, - username, - }, - technologies []-> { - _id, - name - }, - seniority-> { - _id, - name - } -`; - -export const profilesQuery = ` - *[_type == "profile" && isActive == true] { - ${profilesProjections} - } -`; - -export const profileQuery = ` - *[_type == "profile" && isActive == true && person->discordID.current == $id][0] { - ${profilesProjections} - } -`; - -export const personQuery = groq` - *[_type == "person" && discordID.current == $id][0] { - _id, - email, - firstName, - github, - linkedin, - photo, - portfolio, - twitter, - username - } -`; - -export const pagesPathsQuery = groq` - *[_type == "page" && defined(path)].path.current -`; - export const pageByPathQuery = groq` *[_type == "page" && path.current == $path][0]{ hero, diff --git a/src/lib/sanity/cmyk/getAllCMYKProjects.ts b/src/lib/sanity/cmyk/getAllCMYKProjects.ts new file mode 100644 index 00000000..562854fe --- /dev/null +++ b/src/lib/sanity/cmyk/getAllCMYKProjects.ts @@ -0,0 +1,22 @@ +import { cmykQuery } from '@/lib/queries'; +import { FilteredResponseQueryOptions } from 'next-sanity'; +import client from '..'; + +export interface CMYK { + _id: string; + name: string; + description: string; + color: string; + image: { + src: string; + }; + github: string; + demo: string; + cmykVersion: string; +} + +export async function getAllCMYKProjects( + options: FilteredResponseQueryOptions, +) { + return client.fetch(cmykQuery, {}, options); +} diff --git a/src/lib/sanity/docs/getAllDocs.ts b/src/lib/sanity/docs/getAllDocs.ts new file mode 100644 index 00000000..a0b2fab3 --- /dev/null +++ b/src/lib/sanity/docs/getAllDocs.ts @@ -0,0 +1,8 @@ +import { FilteredResponseQueryOptions } from 'next-sanity'; +import client from '..'; +import { docsQuery } from '../../queries'; +import { Doc } from './getDocBySlug'; + +export async function getAllDocs(options?: FilteredResponseQueryOptions) { + return client.fetch(docsQuery, {}, options); +} diff --git a/src/lib/sanity/docs/getDocBySlug.ts b/src/lib/sanity/docs/getDocBySlug.ts new file mode 100644 index 00000000..b078c83c --- /dev/null +++ b/src/lib/sanity/docs/getDocBySlug.ts @@ -0,0 +1,21 @@ +import { FilteredResponseQueryOptions } from 'next-sanity'; +import client from '..'; +import { docQuery } from '../../queries'; + +export interface Doc { + slug: string; + title: string; + body: any; + content?: string; +} + +interface GetDocBySlugProps { + slug: string; +} + +export async function getDocBySlug( + { slug }: GetDocBySlugProps, + options?: FilteredResponseQueryOptions, +) { + return client.fetch(docQuery, { slug }, options); +} diff --git a/src/lib/sanity/event/getAllEvents.ts b/src/lib/sanity/event/getAllEvents.ts new file mode 100644 index 00000000..5b4eb277 --- /dev/null +++ b/src/lib/sanity/event/getAllEvents.ts @@ -0,0 +1,24 @@ +import { eventsQuery } from '@/lib/queries'; +import { FilteredResponseQueryOptions } from 'next-sanity'; +import client from '..'; + +export interface Event { + title: string; + slug: string; + category: { + name: string; + }; + cover: { + src: string; + alt?: string; + }; + date: string; + endDate?: string; + description: string; + recording?: string; + discordId?: string; +} + +export async function getAllEvents(options: FilteredResponseQueryOptions) { + return client.fetch(eventsQuery, {}, options); +} diff --git a/src/lib/sanity/featuredCards/getAllFeaturedCards.ts b/src/lib/sanity/featuredCards/getAllFeaturedCards.ts new file mode 100644 index 00000000..2ba9279b --- /dev/null +++ b/src/lib/sanity/featuredCards/getAllFeaturedCards.ts @@ -0,0 +1,18 @@ +import { FilteredResponseQueryOptions } from 'next-sanity'; +import client from '..'; +import { featuredCardsQuery } from '../../queries'; + +export interface FeaturedCard { + icon: string; + title: string; + description: string; + color: string; + btnText: string; + link?: string; +} + +export async function getAllFeaturedCards( + options?: FilteredResponseQueryOptions, +) { + return client.fetch(featuredCardsQuery, {}, options); +} diff --git a/src/lib/sanity.ts b/src/lib/sanity/index.ts similarity index 71% rename from src/lib/sanity.ts rename to src/lib/sanity/index.ts index 565fde29..dc8dd492 100644 --- a/src/lib/sanity.ts +++ b/src/lib/sanity/index.ts @@ -1,39 +1,45 @@ -import { createClient } from '@sanity/client'; import sanityImage from '@sanity/image-url'; import type { SanityImageSource } from '@sanity/image-url/lib/types/types'; -import { dataset, projectId } from '../../sanity.env'; +import { ClientConfig, createClient } from 'next-sanity'; +import { dataset, projectId } from '../../../sanity.env'; -export const config = { +const config = { dataset, projectId, apiVersion: '2022-04-30', useCdn: process.env.NODE_ENV === 'production', -}; +} satisfies ClientConfig; const client = createClient(config); export const postClient = createClient({ ...config, - useCdn: false, token: process.env.SANITY_TOKEN, }); export const imageBuilder = sanityImage(client); -const urlFor = (source: SanityImageSource) => imageBuilder.image(source); -export const imgUrlFrom = ( + +function urlFor(source: SanityImageSource) { + return imageBuilder.image(source); +} + +export function imgUrlFrom( image: SanityImageSource | null | undefined, { size, width = 512, height = 512, }: { size?: number; width?: number; height?: number }, -) => { - if (!image) return null; +) { + if (!image) { + return null; + } + return urlFor(image) .width(size || width) .height(size || height) .url(); -}; +} export const previewClient = createClient({ ...config, diff --git a/src/lib/sanity/mentor/getAllMentors.ts b/src/lib/sanity/mentor/getAllMentors.ts new file mode 100644 index 00000000..b731252b --- /dev/null +++ b/src/lib/sanity/mentor/getAllMentors.ts @@ -0,0 +1,8 @@ +import { FilteredResponseQueryOptions } from 'next-sanity'; +import client from '..'; +import { mentorsQuery } from '../../queries'; +import { Mentor } from './getMentor'; + +export async function getAllMentors(options?: FilteredResponseQueryOptions) { + return client.fetch(mentorsQuery, {}, options); +} diff --git a/src/lib/sanity/mentor/getMentor.ts b/src/lib/sanity/mentor/getMentor.ts new file mode 100644 index 00000000..6f7c9fe0 --- /dev/null +++ b/src/lib/sanity/mentor/getMentor.ts @@ -0,0 +1,41 @@ +import { FilteredResponseQueryOptions } from 'next-sanity'; +import client from '..'; +import { mentorQuery } from '../../queries'; +import { Topic } from '../topics/getMentoringTopics'; + +type MentorStatus = 'ACTIVE' | 'NOT_AVAILABLE' | 'INACTIVE' | 'OUT'; + +export interface Mentor { + _id: string; + name: string; + description: string; + photo: { + src: string; + alt?: string; + }; + status: MentorStatus; + web: string; + calendly: string; + linkedin: string; + github: string; + twitter?: string; + // FIXME: Topics are not allways like this + topics: [ + { + _key: string; + _ref: string; + }, + ]; +} + +/** + * TODO: unify the Topic types + */ +type TMentor = Omit & { topics: Topic[] }; + +export async function getMentor( + id: string, + options?: FilteredResponseQueryOptions, +) { + return client.fetch(mentorQuery, { id }, options); +} diff --git a/src/lib/sanity/page/getPageByName.ts b/src/lib/sanity/page/getPageByName.ts new file mode 100644 index 00000000..23158ed6 --- /dev/null +++ b/src/lib/sanity/page/getPageByName.ts @@ -0,0 +1,25 @@ +import { FilteredResponseQueryOptions } from 'next-sanity'; +import client from '..'; +import { pageQueryByName } from '../../queries'; +import { Page } from '../../types'; + +interface GetPageByNameProps { + name: string; +} + +export async function getPageByName( + { name }: GetPageByNameProps, + options?: FilteredResponseQueryOptions, +) { + return client.fetch( + pageQueryByName, + { name }, + { + ...options, + next: { + ...options?.next, + revalidate: options?.next?.revalidate || 120, + }, + }, + ); +} diff --git a/src/lib/sanity/page/getPageByPath.ts b/src/lib/sanity/page/getPageByPath.ts new file mode 100644 index 00000000..3411043f --- /dev/null +++ b/src/lib/sanity/page/getPageByPath.ts @@ -0,0 +1,19 @@ +import { FilteredResponseQueryOptions } from 'next-sanity'; +import client from '..'; +import { pageByPathQuery } from '../../queries'; +import { Page } from '../../types'; + +interface GetPageByPathProps { + path: string; +} + +export async function getPageByPath( + { path }: GetPageByPathProps, + options?: FilteredResponseQueryOptions, +) { + return client.fetch( + pageByPathQuery, + { path }, + options, + ); +} diff --git a/src/lib/sanity/person/types.ts b/src/lib/sanity/person/types.ts new file mode 100644 index 00000000..fac08bac --- /dev/null +++ b/src/lib/sanity/person/types.ts @@ -0,0 +1,46 @@ +import { SanityImageSource } from '@sanity/asset-utils'; + +export interface CMYKParticipant { + _id?: string; + discordUser: { + _type: string; + _ref: string; + }; + participationLevel?: string; + participationType?: string; + aboutParticipant: string; + previousKnowledge: string; + isChix: boolean; + workExperience: string; + stackWanted: string; + timeAvailability: string; + projects: string; + experience: string; + otherQuestions: string; + cmykVersion: string; + status: string; +} + +export interface Person { + _id: string; + username: { + current: string; + }; + discordID: string; + photo: + | { + src: string; + alt?: string; + } + | SanityImageSource; + firstName?: string; + lastName?: string; + email?: string; + linkedin?: string; + twitter?: string; + portfolio?: string; + github?: string; + fecTeam?: boolean; + timezone?: string; + cmykParticipant?: CMYKParticipant[]; +} diff --git a/src/lib/sanity/settings/getSettings.ts b/src/lib/sanity/settings/getSettings.ts new file mode 100644 index 00000000..14c389e4 --- /dev/null +++ b/src/lib/sanity/settings/getSettings.ts @@ -0,0 +1,44 @@ +import { FilteredResponseQueryOptions } from 'next-sanity'; +import client from '..'; +import { settingsQuery } from '../../queries'; +import { Image } from '../../types'; + +export interface SocialNetworks { + github: string; + linkedin: string; + twitch: string; + twitter: string; + youtube: string; + instagram: string; +} + +export interface Settings { + description: string; + heroBackground: Image; + heroWords: string[]; + heroSubtitle: string; + heroDescription?: string; + discordButtonLabel: string; + iniciativasButtonText: string; + logo: Image; + navItems: { + title: string; + link: string; + }[]; + socialnetworks: SocialNetworks; + title: string; + cmykSettings: { + cmykInscription: boolean; + cmykInscriptionChix: boolean; + }; + footerNavItems: { + title: string; + link: { + value: string; + }; + }[]; +} + +export async function getSettings(options?: FilteredResponseQueryOptions) { + return client.fetch(settingsQuery, {}, options); +} diff --git a/src/lib/sanity/topics/getMentoringTopics.ts b/src/lib/sanity/topics/getMentoringTopics.ts new file mode 100644 index 00000000..06ae32d0 --- /dev/null +++ b/src/lib/sanity/topics/getMentoringTopics.ts @@ -0,0 +1,15 @@ +import { FilteredResponseQueryOptions } from 'next-sanity'; +import client from '..'; +import { mentorsTopicsQuery } from '../../queries'; + +export interface Topic { + _id: string; + title: string; + description?: string; +} + +export async function getMentoringTopics( + options?: FilteredResponseQueryOptions, +) { + return client.fetch(mentorsTopicsQuery, {}, options); +} diff --git a/src/lib/seo.ts b/src/lib/seo.ts index 6b711433..c821ba69 100644 --- a/src/lib/seo.ts +++ b/src/lib/seo.ts @@ -1,5 +1,5 @@ -import { getPageByName } from '@/lib/api.server'; -import { getSettings } from './api.server'; +import { getPageByName } from './sanity/page/getPageByName'; +import { getSettings } from './sanity/settings/getSettings'; const DEFAULT_DESCRIPTION = 'Somos una comunidad de personas interesadas en tecnología y ciencias informáticas en donde charlamos sobre lenguajes de programación, diseño web, infraestructura, compartimos dudas, preguntamos y respondemos.'; diff --git a/src/lib/types.ts b/src/lib/types.ts index 7e579bdd..1adb4c29 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -1,51 +1,6 @@ import { TypedObject } from '@sanity/block-tools'; -import { SanityImageSource } from '@sanity/image-url/lib/types/types'; import { PortableTextTextBlock } from 'sanity'; -export interface Settings { - description: string; - heroBackground: Image; - heroWords: string[]; - heroSubtitle: string; - heroDescription?: string; - discordButtonLabel: string; - iniciativasButtonText: string; - logo: Image; - navItems: NavItemData[]; - socialnetworks: SocialNetworks; - title: string; - cmykSettings: CMYKSettings; - footerNavItems: LinkItemData[]; -} - -export interface NavItemData { - title: string; - link: string; -} - -interface LinkData { - value: string; -} - -interface LinkItemData { - title: string; - link: LinkData; -} - -export interface SocialNetworks { - github: string; - linkedin: string; - twitch: string; - twitter: string; - youtube: string; - instagram: string; -} - -interface CMYKSettings { - cmykInscription: boolean; - cmykInscriptionChix: boolean; -} - export interface Image { _type: string; asset: Asset; @@ -56,23 +11,6 @@ interface Asset { _type: string; } -export interface Event { - title: string; - slug: string; - category: { - name: string; - }; - cover: { - src: string; - alt?: string; - }; - date: string; - endDate?: string; - description: string; - recording?: string; - discordId?: string; -} - export interface SanityEvent { _id?: string; discordId: string; @@ -100,109 +38,6 @@ export interface EventsSettings { sendEmailsOnMigration: boolean; } -export interface Mentor { - _id: string; - name: string; - description: string; - photo: { - src: string; - alt?: string; - }; - status: 'ACTIVE' | 'NOT_AVAILABLE' | 'INACTIVE' | 'OUT'; - web: string; - calendly: string; - linkedin: string; - github: string; - twitter?: string; - // FIXME: Topics are not allways like this - topics: [ - { - _key: string; - _ref: string; - }, - ]; -} - -export interface CMYK { - _id: string; - name: string; - description: string; - color: string; - image: { - src: string; - }; - github: string; - demo: string; - cmykVersion: string; -} - -export interface CMYKParticipant { - _id?: string; - discordUser: { - _type: string; - _ref: string; - }; - participationLevel?: string; - participationType?: string; - aboutParticipant: string; - previousKnowledge: string; - isChix: boolean; - workExperience: string; - stackWanted: string; - timeAvailability: string; - projects: string; - experience: string; - otherQuestions: string; - cmykVersion: string; - status: string; -} - -export interface Topic { - _id: string; - title: string; - description?: string; -} - -export interface Doc { - title: string; - slug: string; - body: any[]; - content?: string; -} - -export interface FeaturedCards { - icon: string; - title: string; - description: string; - color: string; - btnText: string; - link?: string; -} - -export interface Person { - _id: string; - username: { - current: string; - }; - discordID: string; - photo: - | { - src: string; - alt?: string; - } - | SanityImageSource; - firstName?: string; - lastName?: string; - email?: string; - linkedin?: string; - twitter?: string; - portfolio?: string; - github?: string; - fecTeam?: boolean; - timezone?: string; - cmykParticipant?: CMYKParticipant[]; -} - export interface EmbeddedTweet { media: { height: number; @@ -261,54 +96,6 @@ export interface EmbeddedTweet { }; } -export interface Role { - _id: string; - name: string; -} - -export interface Seniority { - _id: string; - name: string; -} - -export interface Technology { - _id: string; - name: string; -} - -export interface ProfileFilters { - roleId?: string; - location?: string; - seniorityId?: string; - description?: string; - technologies?: Technology[]; - available?: boolean; - active?: boolean; -} - -export interface Profile { - _id: string; - description: string; - isAvailable: boolean; - location: string; - technologies: Technology[]; - person: { - _id: string; - discord: string; - email: string; - firstName: string; - lastName: string; - github: string; - linkedin: string; - photo: string; - portfolio: string; - twitter: string; - username: string; - }; - role: Role; - seniority: Role; -} - export type Component = { _type: string; _key: string; diff --git a/src/lib/useDebounce.ts b/src/lib/useDebounce.ts deleted file mode 100644 index 8fbe169a..00000000 --- a/src/lib/useDebounce.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { useState, useEffect } from 'react'; - -export function useDebounce(value: T, delay: number): T { - const [debouncedValue, setDebouncedValue] = useState(value); - - useEffect( - () => { - // Update debounced value after delay - const handler = setTimeout(() => { - setDebouncedValue(value); - }, delay); - - // Cancel the timeout if value changes (also on delay change or unmount) - // This is how we prevent debounced value from updating if value is changed ... - // .. within the delay period. Timeout gets cleared and restarted. - return () => { - clearTimeout(handler); - }; - }, - [value, delay], // Only re-call effect if value or delay changes - ); - - return debouncedValue; -} diff --git a/src/pages/api/add-cmyk-participant.ts b/src/pages/api/add-cmyk-participant.ts index c1c4dfed..7a1ce2a0 100644 --- a/src/pages/api/add-cmyk-participant.ts +++ b/src/pages/api/add-cmyk-participant.ts @@ -1,9 +1,9 @@ import { NextApiRequest, NextApiResponse } from 'next'; import { - createPerson, createCMYKParticipant, - updatePerson, + createPerson, getPersonByDiscordID, + updatePerson, } from '../../lib/api'; // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types diff --git a/src/pages/api/profiles/[uid].ts b/src/pages/api/profiles/[uid].ts deleted file mode 100644 index 5a34c7d3..00000000 --- a/src/pages/api/profiles/[uid].ts +++ /dev/null @@ -1,25 +0,0 @@ -import { getProfile } from '@/lib/api.server'; -import type { NextApiRequest, NextApiResponse } from 'next'; - -export default async function handler( - req: NextApiRequest, - res: NextApiResponse, -): Promise { - const { uid } = req.query; - - let profile; - try { - profile = await getProfile({ id: uid as string }); - } catch (error) { - res.status(500).send({ - statusCode: 500, - message: `Can't retrieve profile, error ${error}`, - }); - } - - if (!profile?._id) { - res.status(404).send({ statusCode: 404, message: 'Profile not found' }); - } - - res.status(200).json(profile); -} diff --git a/src/pages/api/profiles/index.ts b/src/pages/api/profiles/index.ts deleted file mode 100644 index 24a4c740..00000000 --- a/src/pages/api/profiles/index.ts +++ /dev/null @@ -1,199 +0,0 @@ -import type { NextApiRequest, NextApiResponse } from 'next'; -import { postClient } from '@/lib/sanity'; -import { getPerson } from '@/lib/api'; -import { profileQuery } from '@/lib/queries'; -import { Profile } from '@/lib/types'; - -function isValidHttpUrl(string: string) { - let url: URL; - - try { - url = new URL(string); - } catch (_) { - return false; - } - - return url.protocol === 'http:' || url.protocol === 'https:'; -} - -export default async function handle( - req: NextApiRequest, - res: NextApiResponse, -): Promise { - if (req.method === 'POST') { - const { - name, - email, - discord, - discordId, - github, - linkedin, - portfolio, - twitter, - location, - photo, - seniorityId, - technologies: selectedTechnologies, - roleId, - available, - description, - } = req.body; - - // const tech: Technology[] = technologies.map((t) => ({ - // id: t.value, - // })); - - const person = await getPerson(discordId); - const profile = await postClient.fetch(profileQuery, { - id: discordId, - }); - - const technologies = selectedTechnologies.map((tech: { _id: string }) => ({ - _type: 'reference', - _ref: tech._id, - _key: tech._id, - })); - - let photoObj; - - if (photo && !isValidHttpUrl(photo)) { - const photoBlob = Buffer.from(photo.split(';base64,')[1], 'base64'); - - const asset = await postClient.assets.upload('image', photoBlob); - - photoObj = { - _type: 'image', - asset: { - _type: 'reference', - _ref: asset._id, - }, - }; - } else photoObj = undefined; - - if (profile._id) { - const transaction = postClient.transaction(); - - transaction.patch(person._id, { - set: { - firstName: name, - email, - twitter, - portfolio, - github, - linkedin, - username: discord, - photo: photoObj, - }, - }); - - transaction.patch(profile._id, { - set: { - isAvailable: available, - location, - isActive: true, - description, - seniority: { - _type: 'reference', - _ref: seniorityId, - }, - role: { - _type: 'reference', - _ref: roleId, - }, - technologies, - }, - }); - - await transaction.commit({ autoGenerateArrayKeys: true }); - - res.json({}); - - return; - } - - if (person._id) { - const transaction = postClient.transaction(); - - transaction.patch(person._id, { - set: { - firstName: name, - email, - twitter, - portfolio, - github, - linkedin, - username: discord, - photo: photoObj, - }, - }); - - transaction.create({ - _type: 'profile', - isAvailable: available, - location, - isActive: true, - description, - seniority: { - _type: 'reference', - _ref: seniorityId, - }, - role: { - _type: 'reference', - _ref: roleId, - }, - person: { - _type: 'reference', - _ref: person._id, - }, - technologies, - }); - - await transaction.commit({ autoGenerateArrayKeys: true }); - res.json({}); - - return; - } - - const newPerson = await postClient.create({ - _type: 'person', - firstName: name, - email, - twitter, - portfolio, - username: discord, - discordID: { - _type: 'slug', - current: discordId, - }, - github, - linkedin, - photo: photoObj, - }); - - await postClient.create( - { - _type: 'profile', - isAvailable: available, - location, - isActive: true, - description, - seniority: { - _type: 'reference', - _ref: seniorityId, - }, - role: { - _type: 'reference', - _ref: roleId, - }, - person: { - _type: 'reference', - _ref: newPerson._id, - }, - technologies, - }, - { autoGenerateArrayKeys: true }, - ); - - res.json({}); - } -} diff --git a/tsconfig.json b/tsconfig.json index 3d301edd..fa150e47 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -9,7 +9,7 @@ "noEmit": true, "esModuleInterop": true, "module": "esnext", - "moduleResolution": "node", + "moduleResolution": "Bundler", "resolveJsonModule": true, "isolatedModules": true, "jsx": "preserve",