diff --git a/.eslintrc.js b/.eslintrc.js index c80d021..1d1c29d 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,7 +1,7 @@ /** * MIT License * - * Copyright (c) 2024, Brion Mario + * Copyright (c) 2024, JavaScript Colombo * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -22,6 +22,35 @@ * SOFTWARE. */ +const LICENSE_HEADER_DEFAULT_PATTERN = [ + '*', + ' * MIT License', + ' *', + { + pattern: ' Copyright \\(c\\) \\d{4}, JavaScript Colombo', + template: ` * Copyright (c) ${new Date().getFullYear()}, JavaScript Colombo`, + }, + ' *', + ' * Permission is hereby granted, free of charge, to any person obtaining a copy', + ' * of this software and associated documentation files (the "Software"), to deal', + ' * in the Software without restriction, including without limitation the rights', + ' * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell', + ' * copies of the Software, and to permit persons to whom the Software is', + ' * furnished to do so, subject to the following conditions:', + ' *', + ' * The above copyright notice and this permission notice shall be included in all', + ' * copies or substantial portions of the Software.', + ' *', + ' * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR', + ' * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,', + ' * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE', + ' * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER', + ' * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,', + ' * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE', + ' * SOFTWARE.', + ' ', +]; + module.exports = { env: { es6: true, @@ -29,4 +58,9 @@ module.exports = { }, extends: ['turbo', 'plugin:@brionmario/internal', 'plugin:@brionmario/prettier'], plugins: ['@brionmario'], + rules: { + // Enforce JavaScript Colombo's license header. + // https://github.com/Stuk/eslint-plugin-header + 'header/header': ['warn', 'block', LICENSE_HEADER_DEFAULT_PATTERN, 2], + }, }; diff --git a/.husky/commit-msg b/.husky/commit-msg deleted file mode 100644 index a78cc75..0000000 --- a/.husky/commit-msg +++ /dev/null @@ -1 +0,0 @@ -npx commitlint --edit $1 diff --git a/.husky/pre-commit b/.husky/pre-commit deleted file mode 100644 index e69de29..0000000 diff --git a/.husky/prepare-commit-msg b/.husky/prepare-commit-msg deleted file mode 100644 index 17e2764..0000000 --- a/.husky/prepare-commit-msg +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh -. "$(dirname "$0")/_/husky.sh" - -exec < /dev/tty && npx cz --hook || true diff --git a/apps/www/.eslintrc.cjs b/apps/www/.eslintrc.cjs index 0ec0501..589ac19 100644 --- a/apps/www/.eslintrc.cjs +++ b/apps/www/.eslintrc.cjs @@ -35,6 +35,7 @@ module.exports = { 'plugin:@brionmario/prettier', 'plugin:@brionmario/next', 'plugin:react/jsx-runtime', + '../../.eslintrc.js' ], parserOptions: { project: [path.resolve(__dirname, 'tsconfig.json')], diff --git a/apps/www/app/layout.tsx b/apps/www/app/layout.tsx index 1a3c00e..f874600 100644 --- a/apps/www/app/layout.tsx +++ b/apps/www/app/layout.tsx @@ -25,9 +25,10 @@ import type {Metadata} from 'next'; import {ReactElement} from 'react'; import ThemeProvider from '@/components/ThemeProvider'; -import './globals.scss'; -import './custom.scss'; import {inter, spaceGrotesk} from './fonts'; +import useMeetupConfig from '@/hooks/useMeetupConfig'; +import './custom.scss'; +import './globals.scss'; export const metadata: Metadata = { title: 'JavaScript Colombo', @@ -39,14 +40,23 @@ const RootLayout = ({ children, }: Readonly<{ children: React.ReactNode; -}>): ReactElement => ( - - - - {children} - - - -); +}>): ReactElement => { + const {config} = useMeetupConfig(); + + return ( + + + + {children} + + + + ); +}; export default RootLayout; diff --git a/apps/www/app/page.tsx b/apps/www/app/page.tsx index 54af9c8..1fe866a 100644 --- a/apps/www/app/page.tsx +++ b/apps/www/app/page.tsx @@ -50,13 +50,13 @@ export default function Home(): ReactElement {
-
+
+
-
); } diff --git a/apps/www/components/FlipWords.tsx b/apps/www/components/FlipWords.tsx index 1e4ea4e..b38b925 100644 --- a/apps/www/components/FlipWords.tsx +++ b/apps/www/components/FlipWords.tsx @@ -24,15 +24,20 @@ 'use client'; -import React, {useCallback, useEffect, useRef, useState} from 'react'; +import React, {PropsWithChildren, useCallback, useEffect, useRef, useState} from 'react'; import {AnimatePresence, motion, LayoutGroup} from 'framer-motion'; import {cn} from '@/lib/utils'; -const FlipWords = ({words, duration = 3000, className}: {className?: string; duration?: number; words: string[]}) => { +export interface FlipWordsProps { + className?: string; + duration?: number; + words: string[]; +} + +const FlipWords = ({words, duration = 3000, className, children}: PropsWithChildren) => { const [currentWord, setCurrentWord] = useState(words[0]); const [isAnimating, setIsAnimating] = useState(false); - // thanks for the fix Julian - https://github.com/Julian-AT const startAnimation = useCallback(() => { const word = words[words.indexOf(currentWord) + 1] || words[0]; setCurrentWord(word); @@ -47,67 +52,69 @@ const FlipWords = ({words, duration = 3000, className}: {className?: string; dur }, [isAnimating, duration, startAnimation]); return ( - { - setIsAnimating(false); - }} - > - + { + setIsAnimating(false); }} - className={cn('z-10 inline-block relative text-left px-2', className)} - key={currentWord} > - {/* edit suggested by Sajal: https://x.com/DewanganSajal */} - {currentWord.split(' ').map((word, wordIndex) => ( - - {word.split('').map((letter, letterIndex) => ( - - {letter} - - ))} -   - - ))} - - + + {currentWord.split(' ').map((word, wordIndex) => ( + + {word.split('').map((letter, letterIndex) => ( + + {letter} + + ))} +   + + ))} + + {children} + + ); }; diff --git a/apps/www/components/Footer.tsx b/apps/www/components/Footer.tsx index a9ee0e4..ac15700 100644 --- a/apps/www/components/Footer.tsx +++ b/apps/www/components/Footer.tsx @@ -27,39 +27,45 @@ import {cn} from '@/lib/utils'; import {TestableComponent} from '@/types/dom'; import {ExternalLink, GithubIcon, Heart} from 'lucide-react'; import WSO2 from '@/icons/WSO2'; +import useMeetupConfig from '@/hooks/useMeetupConfig'; export type FooterProps = HTMLAttributes & TestableComponent; const Footer: ForwardRefExoticComponent> = forwardRef< HTMLDivElement, FooterProps ->(({className, ...rest}: FooterProps, ref: ForwardedRef) => ( - -)); + + ); +}); export default Footer; diff --git a/apps/www/components/Hero.tsx b/apps/www/components/Hero.tsx index c1f2374..f84851e 100644 --- a/apps/www/components/Hero.tsx +++ b/apps/www/components/Hero.tsx @@ -31,11 +31,11 @@ import {AppRouterInstance} from 'next/dist/shared/lib/app-router-context.shared- import {cn} from '@/lib/utils'; import {TestableComponent} from '@/types/dom'; import NelumKuluna from '@/icons/NelumKuluna'; -import CoffeeBeans from '@/icons/CoffeeBeans'; import RegisterButton from './RegisterButton'; import FlipWords from './FlipWords'; import Meetup from '@/icons/Meetup'; import {goodBrush} from '@/app/fonts'; +import useMeetupConfig from '@/hooks/useMeetupConfig'; export type HeroProps = HTMLAttributes & TestableComponent; @@ -43,15 +43,11 @@ const Hero: ForwardRefExoticComponent> HTMLDivElement, HeroProps >(({className, ...rest}: HeroProps, ref: ForwardedRef) => { + const {config} = useMeetupConfig(); const router: AppRouterInstance = useRouter(); const handleRegisterClick = (): void => { - const orgName: string | undefined = process.env.NEXT_PUBLIC_ASGARDEO_CLIENT_APP_ORG || ''; - const clientId: string | undefined = process.env.NEXT_PUBLIC_ASGARDEO_CLIENT_APP_ID || ''; - const appName: string | undefined = process.env.NEXT_PUBLIC_ASGARDEO_CLIENT_APP_NAME || ''; - - const url: string = `https://accounts.asgardeo.io/t/${orgName}/accountrecoveryendpoint/register.do?client_id=${clientId}&sp=${appName}`; - router.push(url); + router.push(config.links.meetup.url); }; return ( @@ -66,51 +62,36 @@ const Hero: ForwardRefExoticComponent>

- JavaScript Colombo + {config.hero.title}

- Let's meetup in Colombo, grab a coffee - and talk{' '} - - ` - JavaScript - ` - + {config.hero.tagline}

-
-

🔔 Next session

-

- - Nov 6th 2024 - - ,{' '} - - - 6:00 PM Eastern - {' '} - at{' '} - - - WSO2, Colombo 4, Sri Lanka - -

-
+ {config.hero.showNextMeetupSummary && ( +
+

🔔 Next session

+

+ + {config.meetups.next.date} + + ,{' '} + + + {config.meetups.next.time} + {' '} + at{' '} + + + {config.meetups.next.location} + +

+
+ )}
-
-
- - check us out on{' '} - - - - - -
-
diff --git a/apps/www/components/NavBar.tsx b/apps/www/components/NavBar.tsx index e3d8ad3..7cf57a8 100644 --- a/apps/www/components/NavBar.tsx +++ b/apps/www/components/NavBar.tsx @@ -34,6 +34,7 @@ import {useReducedMotion, AnimatePresence, motion, Transition} from 'framer-moti import {AppRouterInstance} from 'next/dist/shared/lib/app-router-context.shared-runtime'; import Logo from './Logo'; import NavLink, {MobileNavLink} from './NavLink'; +import useMeetupConfig from '@/hooks/useMeetupConfig'; /** * The `NavBarProps` interface represents the props accepted by the `NavBar` component. @@ -58,6 +59,7 @@ export interface NavBarItem extends LinkProps { } const NavBar: FC = ({items}: NavBarProps): ReactElement => { + const {config} = useMeetupConfig(); const pathname = usePathname(); const shouldReduceMotion = useReducedMotion(); @@ -126,9 +128,11 @@ const NavBar: FC = ({items}: NavBarProps): ReactElement => { ))}
-
- -
+ {config.theme.showThemeSwitcher && ( +
+ +
+ )}
{/* Mobile menu button */}