diff --git a/.env b/.env index 046c07c..7602b9d 100644 --- a/.env +++ b/.env @@ -1 +1 @@ -DATABASE_URL="mysql://hp_user:oxU9ExgAHXktIhQZ@79.143.186.18:3306/Superpipo" \ No newline at end of file +DATABASE_URL="mysql://root:@localhost:3306/superpipo" \ No newline at end of file diff --git a/components/AuthRequired.tsx b/components/AuthRequired.tsx new file mode 100644 index 0000000..b27318e --- /dev/null +++ b/components/AuthRequired.tsx @@ -0,0 +1,20 @@ +import { useSession } from 'next-auth/react' +import { useRouter } from 'next/router'; + +export default function Auth({ children }) { + const router = useRouter(); + const { status } = useSession({ + required: true, + onUnauthenticated: () => router.push(`/signin?info=${encodeURI('Vous devez être connecté pour accéder à cette page')}`) + }); + + if (status === 'loading') { + return ( +
+

Chargement de la session en cours

+
+ ); + } + + return children; +} \ No newline at end of file diff --git a/components/Categories/Categories.tsx b/components/Categories/Categories.tsx index d8ea1e4..25f19ab 100644 --- a/components/Categories/Categories.tsx +++ b/components/Categories/Categories.tsx @@ -1,6 +1,11 @@ +import LinkTag from 'next/link'; + import styles from '../../styles/home/categories.module.scss'; import { Category } from '../../types'; +import EditSVG from '../../public/icons/edit.svg'; +import RemoveSVG from '../../public/icons/remove.svg'; + interface CategoriesProps { categories: Category[]; categoryActive: Category; @@ -35,7 +40,28 @@ function CategoryItem({ category, categoryActive, handleSelectCategory }: Catego return (
  • - {category.name} — {category.links.length} +
    + {category.name} + — {category.links.length} +
    +
  • ) +} + +function MenuOptions({ id }: { id: number; }): JSX.Element { + return ( +
    + + + + + + + + + + +
    + ) } \ No newline at end of file diff --git a/components/Categories/SideMenu.tsx b/components/Categories/SideMenu.tsx index 58bb1ab..174bb05 100644 --- a/components/Categories/SideMenu.tsx +++ b/components/Categories/SideMenu.tsx @@ -1,12 +1,13 @@ import { Session } from 'next-auth'; import LinkTag from 'next/link'; -import styles from '../../styles/home/categories.module.scss'; -import { Category, Link } from '../../types'; import Categories from './Categories'; import Favorites from './Favorites'; import UserCard from './UserCard'; +import styles from '../../styles/home/categories.module.scss'; +import { Category, Link } from '../../types'; + interface SideMenuProps { categories: Category[]; favorites: Link[]; diff --git a/components/Categories/UserCard.tsx b/components/Categories/UserCard.tsx index 1a0a7bf..df7242d 100644 --- a/components/Categories/UserCard.tsx +++ b/components/Categories/UserCard.tsx @@ -1,6 +1,7 @@ import { Session } from 'next-auth'; import { signOut } from 'next-auth/react'; import Image from 'next/image'; + import styles from '../../styles/home/categories.module.scss'; export default function UserCard({ session }: { session: Session; }) { @@ -15,7 +16,7 @@ export default function UserCard({ session }: { session: Session; }) { /> {session.user.name} - diff --git a/components/Checkbox.tsx b/components/Checkbox.tsx new file mode 100644 index 0000000..be00fd5 --- /dev/null +++ b/components/Checkbox.tsx @@ -0,0 +1,57 @@ +import { MutableRefObject, useState } from 'react'; + +interface SelectorProps { + name: string; + label?: string; + labelComponent?: JSX.Element; + disabled?: boolean; + innerRef?: MutableRefObject; + placeholder?: string; + fieldClass?: string; + isChecked?: boolean; + onChangeCallback?: (value, { target }) => void; +} + +export default function Selector({ + name, + label, + labelComponent, + disabled = false, + innerRef = null, + fieldClass = '', + placeholder = 'Type something...', + isChecked, + onChangeCallback +}: SelectorProps): JSX.Element { + const [checkboxValue, setCheckboxValue] = useState(isChecked); + + function onChange({ target }) { + setCheckboxValue(!checkboxValue); + if (onChangeCallback) { + onChangeCallback(!checkboxValue, { target }); + } + } + + return (
    + {label && ( + + )} + {labelComponent && ( + + )} + +
    ); +} \ No newline at end of file diff --git a/components/FormLayout.tsx b/components/FormLayout.tsx new file mode 100644 index 0000000..b8e0a45 --- /dev/null +++ b/components/FormLayout.tsx @@ -0,0 +1,55 @@ +import Head from 'next/head'; +import Link from 'next/link'; + +import MessageManager from './MessageManager'; + +import styles from '../styles/create.module.scss'; + +interface FormProps { + title: string; + errorMessage?: string; + successMessage?: string; + infoMessage?: string; + + canSubmit: boolean; + handleSubmit: (event) => void; + + textBtnConfirm?: string; + classBtnConfirm?: string; + + children: any; +} +export default function Form({ + title, + errorMessage, + successMessage, + infoMessage, + canSubmit, + handleSubmit, + textBtnConfirm = 'Valider', + classBtnConfirm = '', + children +}: FormProps) { + return (<> + + Superpipo — {title} + +
    +

    {title}

    +
    + {children} + +
    + + ← Revenir à l'accueil + + +
    + ) +} \ No newline at end of file diff --git a/components/Links/Links.tsx b/components/Links/Links.tsx index 28d88c6..0340b71 100644 --- a/components/Links/Links.tsx +++ b/components/Links/Links.tsx @@ -1,8 +1,12 @@ import LinkTag from 'next/link'; -import styles from '../../styles/home/links.module.scss'; import { Category, Link } from '../../types'; +import EditSVG from '../../public/icons/edit.svg'; +import RemoveSVG from '../../public/icons/remove.svg'; + +import styles from '../../styles/home/links.module.scss'; + export default function Links({ category }: { category: Category; }) { if (category === null) { return (
    @@ -25,7 +29,7 @@ export default function Links({ category }: { category: Category; }) { return (

    {name} — {links.length}

    -
      +
        {links.map((link, key) => ( ))} @@ -34,42 +38,58 @@ export default function Links({ category }: { category: Category; }) { } function LinkItem({ link }: { link: Link; }) { - const { name, url, category } = link; - const { origin, pathname, search } = new URL(url); - + const { id, name, url, category } = link; return ( -
      • +
      • {name} — {category.name} - + +
        + + + + + + + + + + +
      • ); } -function LinkItemURL({ origin, pathname, search }) { - let text = ''; +function LinkItemURL({ url }: { url: string; }) { + try { + const { origin, pathname, search } = new URL(url); + let text = ''; - if (pathname !== '/') { - text += pathname; - } - - if (search !== '') { - if (text === '') { - text += '/'; + if (pathname !== '/') { + text += pathname; } - text += search; - } - return ( - - {origin}{text} - - ) + if (search !== '') { + if (text === '') { + text += '/'; + } + text += search; + } + + return ( + + {origin}{text} + + ) + } catch (error) { + console.error('error', error); + return ( + + {url} + + ) + } } \ No newline at end of file diff --git a/components/MessageManager.tsx b/components/MessageManager.tsx new file mode 100644 index 0000000..9e86ada --- /dev/null +++ b/components/MessageManager.tsx @@ -0,0 +1,14 @@ +import styles from '../styles/components/message-manager.module.scss'; + +interface MessageManagerProps { + error?: string; + success?: string; + info?: string; +} +export default function MessageManager({ error, success, info }: MessageManagerProps) { + return (<> + {info && (
        {info}
        )} + {error && (
        {error}
        )} + {success && (
        {success}
        )} + ); +} \ No newline at end of file diff --git a/components/input.tsx b/components/TextBox.tsx similarity index 79% rename from components/input.tsx rename to components/TextBox.tsx index 9c4614b..d21e4ba 100644 --- a/components/input.tsx +++ b/components/TextBox.tsx @@ -1,22 +1,24 @@ -import { MutableRefObject, useState } from "react"; +import { MutableRefObject, useState } from 'react'; interface InputProps { name: string; label?: string; labelComponent?: JSX.Element; + disabled?: boolean; type?: string; multiple?: boolean; innerRef?: MutableRefObject; placeholder?: string; fieldClass?: string; value?: string; - onChangeCallback: ({ target }, value) => void; + onChangeCallback?: (value) => void; } -export default function Input({ +export default function TextBox({ name, label, labelComponent, + disabled = false, type = 'text', multiple = false, innerRef = null, @@ -29,7 +31,9 @@ export default function Input({ function onChange({ target }) { setInputValue(target.value); - onChangeCallback({ target }, target.value); + if (onChangeCallback) { + onChangeCallback(target.value); + } } return (
        @@ -38,7 +42,7 @@ export default function Input({ {label} )} - {!!labelComponent && ( + {labelComponent && ( @@ -52,6 +56,7 @@ export default function Input({ multiple={multiple} placeholder={placeholder} ref={innerRef} + disabled={disabled} />
        ); } \ No newline at end of file diff --git a/components/selector.tsx b/components/selector.tsx index b52f1f4..77e4dcd 100644 --- a/components/selector.tsx +++ b/components/selector.tsx @@ -1,4 +1,7 @@ -import { MutableRefObject, useState } from "react"; +import { MutableRefObject, useEffect, useState } from 'react'; +import Select, { OptionsOrGroups, GroupBase } from 'react-select'; + +type Option = { label: string | number; value: string | number; } interface SelectorProps { name: string; @@ -6,9 +9,12 @@ interface SelectorProps { labelComponent?: JSX.Element; innerRef?: MutableRefObject; fieldClass?: string; - value?: string | number; - onChangeCallback: ({ target }, value) => void; - children?: any; + + options: OptionsOrGroups>; + value?: number | string; + onChangeCallback?: (value: number | string) => void; + + disabled?: boolean; } export default function Selector({ @@ -18,14 +24,26 @@ export default function Selector({ innerRef = null, fieldClass = '', value, + options = [], onChangeCallback, - children + disabled = false }: SelectorProps): JSX.Element { - const [inputValue, setInputValue] = useState(value); + const [selectorValue, setSelectorValue] = useState