From 1077400260c26108380bf263c787638897f33269 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Un=20=C3=89picier?= Date: Fri, 11 Oct 2024 19:05:06 +0200 Subject: [PATCH] :sparkles: Display & Delete in love list --- app/auth/login/{page.jsx => page.tsx} | 55 +++---- app/auth/login/styles.scss | 73 +++------ app/globals.scss | 2 +- app/hooks/useData.ts | 65 ++++---- app/my-loves/hooks/useActions.ts | 54 +++++++ app/my-loves/hooks/useData.ts | 45 ++++++ app/my-loves/page.jsx | 35 ----- app/my-loves/page.tsx | 90 +++++++++++ app/my-loves/styles.scss | 76 ++++++++-- app/request.ts | 17 +++ app/settings/hooks/useActions.ts | 12 +- app/settings/page.tsx | 142 +++++++----------- app/settings/styles.scss | 141 +++++------------ .../hooks/{useData.js => useData.ts} | 3 + app/statistics/page.tsx | 8 +- app/statistics/styles.scss | 29 ++-- assets/ArrowRightIcon.tsx | 2 +- assets/PenIcon.tsx | 16 ++ components/ConfirmPopup/ConfirmPopup.tsx | 68 +++++++++ components/ConfirmPopup/styles.scss | 88 +++++++++++ components/InlineInput/InlineInput.tsx | 80 ---------- components/InlineInput/styles.scss | 50 ------ components/Input/styles.scss | 16 +- 23 files changed, 644 insertions(+), 523 deletions(-) rename app/auth/login/{page.jsx => page.tsx} (75%) create mode 100644 app/my-loves/hooks/useActions.ts create mode 100644 app/my-loves/hooks/useData.ts delete mode 100644 app/my-loves/page.jsx create mode 100644 app/my-loves/page.tsx create mode 100644 app/request.ts rename app/statistics/hooks/{useData.js => useData.ts} (87%) create mode 100644 assets/PenIcon.tsx create mode 100644 components/ConfirmPopup/ConfirmPopup.tsx create mode 100644 components/ConfirmPopup/styles.scss delete mode 100644 components/InlineInput/InlineInput.tsx delete mode 100644 components/InlineInput/styles.scss diff --git a/app/auth/login/page.jsx b/app/auth/login/page.tsx similarity index 75% rename from app/auth/login/page.jsx rename to app/auth/login/page.tsx index 2cde739..8bf4c9d 100644 --- a/app/auth/login/page.jsx +++ b/app/auth/login/page.tsx @@ -19,11 +19,13 @@ import { useActions } from './hooks/useActions'; // ------------------------------------------------- Assets & Styles --------------------------------------------------- import Loader from '@/assets/Loader'; import './styles.scss'; +import Input from '@/components/Input/Input'; // --------------------------------------------------------------------------------------------------------------------- const Login = () => { - const [error, setError] = useState(null); + const [error, setError] = useState(null); const [isLoading, setIsLoading] = useState(false); + const { handleSubmit } = useActions({ setError, setIsLoading }); return ( @@ -37,36 +39,29 @@ const Login = () => { priority /> -
+

Connexion

-
+ {error &&

{error}

} -
- - -
-
- - -
-
-
+ + + -
- + +
); }; diff --git a/app/auth/login/styles.scss b/app/auth/login/styles.scss index 6908a00..79c360e 100644 --- a/app/auth/login/styles.scss +++ b/app/auth/login/styles.scss @@ -20,69 +20,31 @@ flex-direction: column; align-items: stretch; gap: 10px; - padding: 20px 30px; width: 95svw; max-width: 450px; - border: 2px solid #fefefa; - border-radius: 7px; - background-color: #d94253; & > .formTitle { - margin-bottom: 20px; font-size: 2rem; font-weight: 300; text-align: center; - color: #fefefa; + color: #d94253; } & > .form { display: flex; flex-direction: column; align-items: stretch; - gap: 20px; + gap: 15px; + padding: 20px; + border: 1px solid #d94253; + border-radius: 10px; + background-color: #f9e2e5; & > .error { text-align: center; color: #fefefa; } - & > .inputContainer { - position: relative; - display: grid; - - & > .inputLabel { - position: absolute; - top: 50%; - left: 15px; - transform: translateY(-50%); - padding: 5px; - background-color: #d94253; - color: #fefefa; - user-select: none; - pointer-events: none; - transition: top 0.3s ease, left 0.3s ease, - font-size 0.3s ease, transform 0.3s ease; - } - - & > .input { - padding: 10px; - border: 1px solid #fefefa; - border-radius: 5px; - background-color: #d94253; - outline: none; - font-size: 1em; - color: #fefefa; - - &:focus + .inputLabel, - &:not(:placeholder-shown) + .inputLabel { - top: 0; - left: 10px; - transform: translateY(-50%); - font-size: 0.9em; - } - } - } - & > .forgetLink { position: relative; width: max-content; @@ -106,28 +68,29 @@ width: 100%; } } - } - - & > .buttonsContainer { - display: flex; - justify-content: center; - align-items: center; - gap: 30px; - margin-top: 15px; & > .submitButton { + align-self: center; position: relative; display: flex; justify-content: center; align-items: center; gap: 10px; padding: 7px 42px; - border: 1px solid #fefefa; + border: 1px solid #e1e1e1; border-radius: 5px; - background-color: #fefefa; - font-size: 0.9rem; + background-color: #ffffff; + font-size: 1rem; color: #d94253; cursor: pointer; + transition: border-color 0.3s ease, background-color 0.3s ease, + color 0.3s ease; + + &:hover { + border-color: #d94253; + background-color: #d94253; + color: #ffffff; + } & > svg { position: absolute; diff --git a/app/globals.scss b/app/globals.scss index eccc074..9fc834d 100644 --- a/app/globals.scss +++ b/app/globals.scss @@ -18,7 +18,7 @@ body { flex-direction: column; width: 100svw; height: 100svh; - background-color: #ffffff; + background-color: #fbeced; overflow: hidden; } diff --git a/app/hooks/useData.ts b/app/hooks/useData.ts index 8e8e826..da123d6 100644 --- a/app/hooks/useData.ts +++ b/app/hooks/useData.ts @@ -1,12 +1,25 @@ +// --------------------------------------------------------------------------------------------------------------------- +//! Imports +// --------------------------------------------------------------------------------------------------------------------- + +// -------------------------------------------------- React & Next ----------------------------------------------------- import { useEffect, useRef, useState } from 'react'; +// --------------------------------------------------------------------------------------------------------------------- + +// --------------------------------------------------- Components ------------------------------------------------------ import { IconLayer, ScatterplotLayer } from 'deck.gl'; +// --------------------------------------------------------------------------------------------------------------------- + +// ------------------------------------------------- Assets & Styles --------------------------------------------------- import { StringifiedPinIcon } from '@/assets/PinIcon'; +import { getDataset } from '../request'; +// --------------------------------------------------------------------------------------------------------------------- export const useData = () => { const deckRef = useRef(null); - const [canUseGeolocation, setCanUseGeolocation] = useState(false); - const [geoError, setGeoError] = useState(null); + const [canUseGeolocation, setCanUseGeolocation] = useState(false); + const [geoError, setGeoError] = useState(null); const [userLocation, setUserLocation] = useState<{ [key: string]: number }>( {} ); @@ -14,9 +27,9 @@ export const useData = () => { const [source, setSource] = useState([]); const [layers, setLayers] = useState([]); - const [isAddLoveOpened, setIsAddLoveOpened] = useState(false); - const [isLoading, setIsLoading] = useState(false); - const [addLoveMessage, setAddLoveMessage] = useState(null); + const [isAddLoveOpened, setIsAddLoveOpened] = useState(false); + const [isLoading, setIsLoading] = useState(false); + const [addLoveMessage, setAddLoveMessage] = useState(null); const [viewState, setViewState] = useState({ longitude: 2.209666999999996, @@ -61,7 +74,18 @@ export const useData = () => { (async () => { const dataset = await getDataset(); - setSource(dataset); + const formattedDataset = dataset.map((point) => { + const properties = { ...point }; + delete properties.geopoint; + + return { + type: 'Feature', + geometry: point.geopoint, + properties: properties, + }; + }); + + setSource(formattedDataset); })(); }, []); @@ -127,32 +151,3 @@ export const useData = () => { setAddLoveMessage, }; }; - -const getDataset = async () => { - const token = localStorage.getItem('token'); - const request = await fetch('/api/point', { - method: 'GET', - headers: { - Authorization: token, - }, - }); - - const response = await request.json(); - - if (response) { - const formattedData = response.map((point) => { - const properties = { ...point }; - delete properties.geopoint; - - return { - type: 'Feature', - geometry: point.geopoint, - properties: properties, - }; - }); - - return formattedData; - } - - return []; -}; diff --git a/app/my-loves/hooks/useActions.ts b/app/my-loves/hooks/useActions.ts new file mode 100644 index 0000000..eaa1987 --- /dev/null +++ b/app/my-loves/hooks/useActions.ts @@ -0,0 +1,54 @@ +export const useActions = ({ + loveToDelete, + setLoveToDelete, + setDeleteMessage, + refreshDataset, +}) => { + const handleEdit = (id: string) => { + alert('Fonctionnalité en cours de développement'); + }; + + const openPopup = (id: string) => { + setDeleteMessage(null); + setLoveToDelete(id); + }; + + const closePopup = () => { + setDeleteMessage(null); + setLoveToDelete(null); + }; + + const deleteLove = async () => { + const token = localStorage.getItem('token'); + + try { + const response = await fetch('/api/point', { + method: 'DELETE', + headers: { + Authorization: token, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + pointId: loveToDelete, + }), + }); + + if (response.ok) { + await refreshDataset(); + setLoveToDelete(null); + return; + } + } catch (error) { + console.error(error); + } + + setDeleteMessage("Une erreur s'est produite. Veuillez réessayer"); + }; + + return { + handleEdit, + openPopup, + closePopup, + deleteLove, + }; +}; diff --git a/app/my-loves/hooks/useData.ts b/app/my-loves/hooks/useData.ts new file mode 100644 index 0000000..7a43db8 --- /dev/null +++ b/app/my-loves/hooks/useData.ts @@ -0,0 +1,45 @@ +// --------------------------------------------------------------------------------------------------------------------- +//! Imports +// --------------------------------------------------------------------------------------------------------------------- + +// -------------------------------------------------- React & Next ----------------------------------------------------- +import { useEffect, useState } from 'react'; +// --------------------------------------------------------------------------------------------------------------------- + +// -------------------------------------------------- Hooks & Utils ---------------------------------------------------- +import { getDataset } from '@/app/request'; +// --------------------------------------------------------------------------------------------------------------------- + +export const useData = () => { + const [dataset, setDataset] = useState([]); + const [loveToDelete, setLoveToDelete] = useState(null); + const [deleteMessage, setDeleteMessage] = useState(null); + + useEffect(() => { + refreshDataset(); + }, []); + + const refreshDataset = async () => { + const data = await getDataset(); + + const formattedDataset = data.map((d) => { + const formattedDate = new Date(d.createdat).toLocaleString('fr-FR'); + + return { + ...d, + createdat: formattedDate, + }; + }); + + setDataset(formattedDataset); + }; + + return { + dataset, + loveToDelete, + setLoveToDelete, + deleteMessage, + setDeleteMessage, + refreshDataset, + }; +}; diff --git a/app/my-loves/page.jsx b/app/my-loves/page.jsx deleted file mode 100644 index f525711..0000000 --- a/app/my-loves/page.jsx +++ /dev/null @@ -1,35 +0,0 @@ -// --------------------------------------------------------------------------------------------------------------------- -//! Imports -// --------------------------------------------------------------------------------------------------------------------- - -// ------------------------------------------------- Assets & Styles --------------------------------------------------- -import './styles.scss'; -// --------------------------------------------------------------------------------------------------------------------- - -const MyLoves = () => { - const data = []; - - return ( -
-

Mes loves

- -
- {data.map((love, index) => ( -
-

- Le: {love.createdAt || '20/09/2024'} -

-

- {love.description || 'Description'} -

-
- ))} -
-
- ); -}; - -export default MyLoves; diff --git a/app/my-loves/page.tsx b/app/my-loves/page.tsx new file mode 100644 index 0000000..e4e1bb0 --- /dev/null +++ b/app/my-loves/page.tsx @@ -0,0 +1,90 @@ +'use client'; + +import TrashIcon from '@/assets/TrashIcon'; +// --------------------------------------------------------------------------------------------------------------------- +//! Imports +// --------------------------------------------------------------------------------------------------------------------- + +// --------------------------------------------------- Components ------------------------------------------------------ +import ConfirmPopup from '@/components/ConfirmPopup/ConfirmPopup'; +// --------------------------------------------------------------------------------------------------------------------- + +// -------------------------------------------------- Hooks & Utils ---------------------------------------------------- +import { useData } from './hooks/useData'; +import { useActions } from './hooks/useActions'; +// --------------------------------------------------------------------------------------------------------------------- + +// ------------------------------------------------- Assets & Styles --------------------------------------------------- +import PenIcon from '@/assets/PenIcon'; +import './styles.scss'; +// --------------------------------------------------------------------------------------------------------------------- + +const MyLoves = () => { + const { + dataset, + loveToDelete, + setLoveToDelete, + deleteMessage, + setDeleteMessage, + refreshDataset, + } = useData(); + + const { handleEdit, openPopup, closePopup, deleteLove } = useActions({ + loveToDelete, + setLoveToDelete, + setDeleteMessage, + refreshDataset, + }); + + return ( +
+
+

Mes loves

+
+ +
+ {dataset.map((love) => ( +
+
+

{love.location}

+

{love.comment || '-'}

+

Le: {love.createdat}

+
+ +
+ + +
+
+ ))} +
+ + {loveToDelete && ( + + )} +
+ ); +}; + +export default MyLoves; diff --git a/app/my-loves/styles.scss b/app/my-loves/styles.scss index 138f0ba..3353559 100644 --- a/app/my-loves/styles.scss +++ b/app/my-loves/styles.scss @@ -2,8 +2,21 @@ flex-grow: 1; display: flex; flex-direction: column; - gap: 10px; - padding: 10px; + + & > .header { + display: flex; + flex-wrap: nowrap; + align-items: center; + gap: 20px; + padding: 20px; + width: 100%; + background-color: #d94253; + color: #ffffff; + + & > .title { + font-size: 1.2rem; + } + } & > .title { font-size: 1.5rem; @@ -16,24 +29,57 @@ flex-direction: column; & > .loveItem { - display: flex; - flex-direction: column; - gap: 5px; + $buttonSize: 30px; + display: grid; + grid-template-columns: 1fr $buttonSize; padding: 10px; - background-color: #d94253; + border-bottom: 1px solid #d94253; + background-color: #f9e2e5; - &:not(:last-child) { - border-bottom: 2px solid #d80135; + & > .infos { + display: flex; + flex-direction: column; + gap: 5px; + + & > .location { + font-size: 1.1rem; + color: #d94253; + } + + & > .comment { + color: #d94253; + } + + & > .date { + font-size: 0.9rem; + font-weight: 300; + color: #d94253; + } } - & > .date { - font-size: 1.1rem; - color: #ffffff; - } + & > .actionButtons { + display: flex; + flex-direction: column; + gap: 10px; - & > .description { - font-weight: 300; - color: #ffffff; + & > .actionButton { + display: flex; + justify-content: center; + align-items: center; + padding: 5px; + width: $buttonSize; + height: $buttonSize; + border: none; + border-radius: 50%; + background-color: transparent; + color: #d94253; + transition: background-color 0.3s ease, color 0.3s ease; + + &:hover { + background-color: #d94253; + color: #ffffff; + } + } } } } diff --git a/app/request.ts b/app/request.ts new file mode 100644 index 0000000..816d33e --- /dev/null +++ b/app/request.ts @@ -0,0 +1,17 @@ +export const getDataset = async () => { + const token = localStorage.getItem('token'); + const request = await fetch('/api/point', { + method: 'GET', + headers: { + Authorization: token, + }, + }); + + const response = await request.json(); + + if (response) { + return response; + } + + return []; +}; diff --git a/app/settings/hooks/useActions.ts b/app/settings/hooks/useActions.ts index ad182df..f9e4d3b 100644 --- a/app/settings/hooks/useActions.ts +++ b/app/settings/hooks/useActions.ts @@ -20,10 +20,18 @@ export const useActions = ({ setPasswordMessage(null); }; - const updateUsername = async (value: string) => { + const updateUsername = async (ev: FormEvent) => { resetMessage(); - if (value == userData.username) { + if (!ev.currentTarget.reportValidity()) { + return; + } + + const formData = new FormData(ev.currentTarget); + + const value = formData.get('username').toString(); + + if (value == userData.username || value.length <= 1) { return; } diff --git a/app/settings/page.tsx b/app/settings/page.tsx index cd015fd..8de6dac 100644 --- a/app/settings/page.tsx +++ b/app/settings/page.tsx @@ -7,7 +7,7 @@ // --------------------------------------------------- Components ------------------------------------------------------ import Link from 'next/link'; import Input from '@/components/Input/Input'; -import InlineInput from '@/components/InlineInput/InlineInput'; +import ConfirmPopup from '@/components/ConfirmPopup/ConfirmPopup'; // --------------------------------------------------------------------------------------------------------------------- // -------------------------------------------------- Hooks & Utils ---------------------------------------------------- @@ -18,8 +18,6 @@ import { useActions } from './hooks/useActions'; // ------------------------------------------------- Assets & Styles --------------------------------------------------- import ArrowRightIcon from '@/assets/ArrowRightIcon'; import TrashIcon from '@/assets/TrashIcon'; -import CloseIcon from '@/assets/CloseIcon'; -import CheckIcon from '@/assets/CheckIcon'; import './styles.scss'; // --------------------------------------------------------------------------------------------------------------------- @@ -72,44 +70,18 @@ const Settings = () => {

{usernameMessage}

)} - - -
-
- {passwordMessage && ( -

{passwordMessage}

- )} +

Nom d'utilisateur

+ - - @@ -121,7 +93,47 @@ const Settings = () => {
-
+
+

Modifier le mot de passe

+ + {passwordMessage && ( +

{passwordMessage}

+ )} + +
+ + + +
+ + +
{isPopupDisplayed && ( -
-
ev.stopPropagation()} - > -
-

- Supprimer le compte ? -

- -
-
-

- Êtes-vous sûr de vouloir supprimer votre - compte ? Cette action est irréversible. -

-
-
- - - -
-
-
+ )}
diff --git a/app/settings/styles.scss b/app/settings/styles.scss index ebc37cb..470d2a3 100644 --- a/app/settings/styles.scss +++ b/app/settings/styles.scss @@ -9,11 +9,11 @@ display: grid; grid-template-columns: 25px 1fr; align-items: center; - gap: 15px; + gap: 10px; padding: 15px; + width: 100%; background-color: #d94253; color: #ffffff; - box-shadow: 0 0 6px rgba(255, 255, 255, 0.2); & > .backButton { display: flex; @@ -27,8 +27,6 @@ } & > .settingsContainer { - flex-grow: 1; - position: relative; display: flex; flex-direction: column; gap: 20px; @@ -43,34 +41,43 @@ color: #ffffff; } - & > .separator { - display: block; - margin: 20px auto; - width: 90%; - height: 1px; - background-color: #d94253; - background: linear-gradient( - 90deg, - rgba(#d94253, 0) 0%, - rgba(#d94253, 1) 10%, - rgba(#d94253, 1) 90%, - rgba(#d94253, 0) 100% - ); - } - - & > .passwordForm { + & > .card { display: flex; flex-direction: column; - gap: 10px; + gap: 5px; + padding: 10px; + border: 1px solid #d94253; + border-radius: 10px; + background-color: #f9e2e5; + + & > .cardTitle { + font-size: 1rem; + color: #d94253; + } + + & > .inputsContainer { + display: flex; + flex-direction: column; + gap: 5px; + padding: 15px 0; + } & > .submitButton { padding: 7px 21px; - border: none; + border: 1px solid #e1e1e1; border-radius: 5px; - background-color: #d94253; + background-color: #ffffff; font-size: 1rem; - color: #ffffff; + color: #d94253; cursor: pointer; + transition: border-color 0.3s ease, background-color 0.3s ease, + color 0.3s ease; + + &:hover { + border-color: #d94253; + background-color: #d94253; + color: #ffffff; + } } } @@ -81,92 +88,12 @@ align-items: center; gap: 10px; padding: 7px 21px; - border: 1px solid #d94253; + border: none; border-radius: 5px; - background-color: #d94253; + background-color: #f9e2e5; font-size: 1rem; - color: #ffffff; + color: #d94253; cursor: pointer; } - - & > .popupOverlay { - position: absolute; - top: 0; - left: 0; - display: flex; - justify-content: center; - align-items: center; - width: 100%; - height: 100%; - background-color: rgba(#ffffff, 0.5); - backdrop-filter: blur(5px); - - & > .popup { - z-index: 1; - position: relative; - display: flex; - flex-direction: column; - margin: 10px; - background-color: #ffffff; - border: 1px solid #d94253; - border-radius: 5px; - - & > .popupHeader { - display: grid; - grid-template-columns: 1fr 20px; - align-items: center; - gap: 20px; - padding: 10px; - - & > .popupTitle { - font-size: 1.2rem; - color: #d94253; - } - - & > .popupCloseButton { - display: flex; - justify-content: center; - align-items: center; - border: none; - border-radius: 5px; - background-color: transparent; - color: #d94253; - cursor: pointer; - transition: background-color 0.3s ease; - - &:hover { - background-color: rgba(#d94253, 0.2); - } - } - } - - & > .popupContent { - padding: 10px; - color: #d94253; - } - - & > .popupButtonsContainer { - display: flex; - flex-wrap: nowrap; - justify-content: space-evenly; - align-items: center; - padding: 10px; - - & > .popupButton { - display: grid; - grid-template-columns: 20px 1fr; - align-items: center; - gap: 5px; - padding: 5px 15px; - border: none; - border-radius: 5px; - background-color: #d94253; - font-size: 1rem; - color: #ffffff; - cursor: pointer; - } - } - } - } } } diff --git a/app/statistics/hooks/useData.js b/app/statistics/hooks/useData.ts similarity index 87% rename from app/statistics/hooks/useData.js rename to app/statistics/hooks/useData.ts index da26de8..b38b044 100644 --- a/app/statistics/hooks/useData.js +++ b/app/statistics/hooks/useData.ts @@ -1,6 +1,7 @@ import { useEffect, useState } from 'react'; export const useData = () => { + const [isLoading, setIsLoading] = useState(true); const [userData, setUserData] = useState(null); useEffect(() => { @@ -23,11 +24,13 @@ export const useData = () => { ), }; + setIsLoading(false); setUserData(formattedResponse); })(); }, []); return { + isLoading, userData, }; }; diff --git a/app/statistics/page.tsx b/app/statistics/page.tsx index cfcfc73..2907fd2 100644 --- a/app/statistics/page.tsx +++ b/app/statistics/page.tsx @@ -22,7 +22,7 @@ import './styles.scss'; const Statistics = () => { const router = useRouter(); - const { userData } = useData(); + const { isLoading, userData } = useData(); if (!userData) { return; @@ -33,6 +33,10 @@ const Statistics = () => { router.push('/auth/login'); }; + if (isLoading) { + return <>; + } + return (
@@ -57,7 +61,7 @@ const Statistics = () => {
-
+
.infosContainer { display: flex; @@ -21,13 +22,13 @@ & > .username { font-size: 1.1rem; - color: #d94253; + color: #ffffff; } & > .sinceDate { font-size: 0.9rem; font-weight: 300; - color: #d94253; + color: #e1e1e1; } } @@ -43,33 +44,17 @@ height: $size; border: none; background-color: transparent; - color: #d94253; + color: #ffffff; cursor: pointer; } } } - & > .separator { - display: block; - width: 90%; - height: 1px; - background-color: #d94253; - background: linear-gradient( - 90deg, - rgba(#d94253, 0) 0%, - rgba(#d94253, 1) 10%, - rgba(#d94253, 1) 90%, - rgba(#d94253, 0) 100% - ); - } - & > .statistics { display: grid; grid-template-columns: repeat(2, 1fr); grid-template-rows: repeat(2, 1fr); - gap: 20px; - justify-items: center; - align-items: center; + gap: 15px; padding: 40px 20px; width: 100%; @@ -77,6 +62,10 @@ display: flex; flex-direction: column; align-items: center; + padding: 20px 10px; + border: 1px solid #d94253; + border-radius: 10px; + background-color: #f9e2e5; font-weight: 300; color: #d94253; diff --git a/assets/ArrowRightIcon.tsx b/assets/ArrowRightIcon.tsx index a153108..e6a44e0 100644 --- a/assets/ArrowRightIcon.tsx +++ b/assets/ArrowRightIcon.tsx @@ -12,7 +12,7 @@ const ArrowRightIcon = (props?: SVGProps) => { stroke='currentColor' strokeLinecap='round' strokeLinejoin='round' - strokeWidth='48' + strokeWidth='36' d='M184 112l144 144-144 144' /> diff --git a/assets/PenIcon.tsx b/assets/PenIcon.tsx new file mode 100644 index 0000000..c5a6ca3 --- /dev/null +++ b/assets/PenIcon.tsx @@ -0,0 +1,16 @@ +import { SVGProps } from 'react'; + +const PenIcon = (props: SVGProps) => { + return ( + + + + ); +}; + +export default PenIcon; diff --git a/components/ConfirmPopup/ConfirmPopup.tsx b/components/ConfirmPopup/ConfirmPopup.tsx new file mode 100644 index 0000000..aca3280 --- /dev/null +++ b/components/ConfirmPopup/ConfirmPopup.tsx @@ -0,0 +1,68 @@ +// --------------------------------------------------------------------------------------------------------------------- +//! Imports +// --------------------------------------------------------------------------------------------------------------------- + +// ------------------------------------------------- Assets & Styles --------------------------------------------------- +import CheckIcon from '@/assets/CheckIcon'; +import CloseIcon from '@/assets/CloseIcon'; +import './styles.scss'; +// --------------------------------------------------------------------------------------------------------------------- + +type Props = { + title: string; + content: string | JSX.Element; + message?: string; + onClose: (ev: React.MouseEvent) => void; + onAccept?: (ev: React.MouseEvent) => void; + onDeny?: (ev: React.MouseEvent) => void; +}; + +const ConfirmPopup = ({ + title, + content, + message, + onClose, + onAccept, + onDeny = onClose, +}: Props) => { + return ( +
+
ev.stopPropagation()} + > +
+

{title}

+ +
+
{message || content}
+
+ + + +
+
+
+ ); +}; + +export default ConfirmPopup; diff --git a/components/ConfirmPopup/styles.scss b/components/ConfirmPopup/styles.scss new file mode 100644 index 0000000..0643e83 --- /dev/null +++ b/components/ConfirmPopup/styles.scss @@ -0,0 +1,88 @@ +.popupOverlay { + position: absolute; + top: 0; + left: 0; + display: flex; + justify-content: center; + align-items: center; + width: 100%; + height: 100%; + background-color: rgba(#ffffff, 0.2); + backdrop-filter: blur(2px); + + & > .popup { + z-index: 1; + position: relative; + display: flex; + flex-direction: column; + margin: 10px; + border: 1px solid #e1e1e1; + border-radius: 10px; + background-color: #ffffff; + overflow: hidden; + + & > .popupHeader { + display: grid; + grid-template-columns: 1fr 20px; + align-items: center; + gap: 20px; + padding: 10px; + border-bottom: 1px solid #e1e1e1; + background-color: #f9e2e5; + + & > .popupTitle { + font-size: 1.1rem; + color: #d94253; + } + + & > .popupCloseButton { + display: flex; + justify-content: center; + align-items: center; + border: none; + border-radius: 5px; + background-color: transparent; + color: #d94253; + cursor: pointer; + } + } + + & > .popupContent { + padding: 10px; + font-size: 0.95rem; + font-weight: 300; + color: #d94253; + } + + & > .popupButtonsContainer { + display: flex; + flex-wrap: nowrap; + justify-content: space-evenly; + align-items: center; + padding: 10px; + + & > .popupButton { + display: grid; + grid-template-columns: 20px 1fr; + align-items: center; + gap: 5px; + padding: 5px 15px; + border: 1px solid #a8a5a6; + border-radius: 5px; + background-color: #ffffff; + font-size: 1rem; + color: #d94253; + cursor: pointer; + transition: background-color 0.3s ease; + + &:first-child { + border: none; + } + + &:hover { + background-color: #f9e2e5; + } + } + } + } +} diff --git a/components/InlineInput/InlineInput.tsx b/components/InlineInput/InlineInput.tsx deleted file mode 100644 index 02810a6..0000000 --- a/components/InlineInput/InlineInput.tsx +++ /dev/null @@ -1,80 +0,0 @@ -'use client'; - -// --------------------------------------------------------------------------------------------------------------------- -//! Imports -// --------------------------------------------------------------------------------------------------------------------- - -// ------------------------------------------------------ React -------------------------------------------------------- -import { FormEvent } from 'react'; -// --------------------------------------------------------------------------------------------------------------------- - -// ------------------------------------------------- Assets & Styles --------------------------------------------------- -import CheckIcon from '@/assets/CheckIcon'; -import CloseIcon from '@/assets/CloseIcon'; -import './styles.scss'; -// --------------------------------------------------------------------------------------------------------------------- - -const InlineInput = ({ - label = '', - name = '', - type = 'text', - placeholder = '', - defaultValue = '', - autoComplete = 'off', - onSubmit = (value?: string) => {}, -}) => { - const handleSubmit = (ev: FormEvent) => { - ev.preventDefault(); - - if (!ev.currentTarget.reportValidity()) { - return; - } - - const formData = new FormData(ev.currentTarget); - - const value = formData.get(name); - - onSubmit(value ? value.toString() : null); - }; - - return ( -
- - -
- - - -
-
- ); -}; - -export default InlineInput; diff --git a/components/InlineInput/styles.scss b/components/InlineInput/styles.scss deleted file mode 100644 index f213c33..0000000 --- a/components/InlineInput/styles.scss +++ /dev/null @@ -1,50 +0,0 @@ -.inlineInputContainer { - display: flex; - flex-direction: column; - gap: 5px; - - & > .inlineInputLabel { - margin-left: 5px; - font-size: 1.1rem; - color: #d94253; - } - - & > .inlineInputRow { - display: grid; - grid-template-columns: 1fr repeat(2, calc(1rem + 14px)); - align-items: center; - gap: 5px; - - & > .inlineInput { - padding: 7px 14px; - border: 1px solid #d94253; - border-radius: 5px; - background-color: #f5cfd3; - outline: none; - font-size: 0.9rem; - color: #d94253; - outline: none; - - &::placeholder { - color: #d94253; - } - } - - & > .inlineInputButton { - display: flex; - justify-content: center; - align-items: center; - padding: 5px; - border: none; - border-radius: 5px; - background-color: transparent; - color: #d94253; - cursor: pointer; - transition: background-color 0.3s ease; - - &:hover { - background-color: rgba(#d94253, 0.5); - } - } - } -} diff --git a/components/Input/styles.scss b/components/Input/styles.scss index e172972..5084665 100644 --- a/components/Input/styles.scss +++ b/components/Input/styles.scss @@ -5,15 +5,21 @@ & > .input { padding: 7px 14px; width: 100%; - border: 1px solid #d94253; + border: 1px solid #e1e1e1; border-radius: 5px; - background-color: #f5cfd3; - font-size: 0.9rem; + background-color: #ffffff; + font-size: 0.95rem; + font-weight: 300; color: #d94253; outline: none; + transition: border-color 0.3s ease; &::placeholder { - color: #d94253; + color: #eca0a9; + } + + &:focus { + border-color: #d94253; } } @@ -29,6 +35,6 @@ height: 100%; border: none; background-color: transparent; - color: #d94253; + color: #eca0a9; } }