setOpenLists(!isOpenLists)}>
@@ -72,7 +73,7 @@ export default function CheckableTaskList({ selectedTasks, handleTaskCheck} : Pr
{list}
))
- :
No lists to show
+ :
{t('tidier.noLists')}
)}
diff --git a/client/src/components/Login/GoogleCalendarLogin.tsx b/client/src/components/Login/GoogleCalendarLogin.tsx
index 10f2c3a..99900f3 100644
--- a/client/src/components/Login/GoogleCalendarLogin.tsx
+++ b/client/src/components/Login/GoogleCalendarLogin.tsx
@@ -2,8 +2,10 @@ import { useEffect, useState } from "react";
import { Icon } from "../Icon/Icon";
import { useGoogleContext } from "../Context/GoogleContext";
import { useGoogleHandler } from "@/pages/api/google";
+import { useTranslation } from "react-i18next";
export default function LoginGoogleCalendar() {
+ const { t } = useTranslation();
const {loggedIn, authUrl} = useGoogleContext();
const { handleLogin, handleLogout} = useGoogleHandler();
@@ -33,12 +35,12 @@ export default function LoginGoogleCalendar() {
?
:
}
>
diff --git a/client/src/components/Login/InruptLogin.scss b/client/src/components/Login/InruptLogin.scss
index 47d84b9..2cc062c 100644
--- a/client/src/components/Login/InruptLogin.scss
+++ b/client/src/components/Login/InruptLogin.scss
@@ -116,3 +116,16 @@
}
}
}
+
+body.dark-mode .inrupt-container {
+ background-color: $background-dark-4;
+
+ .inrupt-login {
+ background-color: $primary-grey;
+ color: $black;
+
+ .inrupt-logo svg {
+ color: $white;
+ }
+ }
+}
diff --git a/client/src/components/Login/InruptLogin.tsx b/client/src/components/Login/InruptLogin.tsx
index 1fca3d3..330b279 100644
--- a/client/src/components/Login/InruptLogin.tsx
+++ b/client/src/components/Login/InruptLogin.tsx
@@ -1,7 +1,10 @@
import { useInruptHandler } from "@/pages/api/inrupt";
import { useSessionContext } from "../Context/SolidContext";
-export default function Inrupt() {
+import { useTranslation } from "react-i18next";
+
+export default function Inrupt() {
+ const { t } = useTranslation();
const { loginInrupt } = useInruptHandler();
const { solidSession } = useSessionContext();
@@ -14,13 +17,13 @@ export default function Inrupt() {
- Welcome to TidyTime!
- TidyTime works with Solid PODs, ensuring your data is only controlled by you. Log in to your Inrupt Solid POD and start using the app!
-
+ {t('loginPage.title')}
+ {t('loginPage.desc')}
+
diff --git a/client/src/components/Login/LogoutInruptBtn.tsx b/client/src/components/Login/LogoutInruptBtn.tsx
index 0f3dbd7..a4f27eb 100644
--- a/client/src/components/Login/LogoutInruptBtn.tsx
+++ b/client/src/components/Login/LogoutInruptBtn.tsx
@@ -1,14 +1,14 @@
import { useInruptHandler } from "@/pages/api/inrupt";
-import { Icon } from "../Icon/Icon";
+import { useTranslation } from 'react-i18next';
export default function LogoutInrupt() {
-
+ const { t } = useTranslation();
const { logoutInrupt } = useInruptHandler();
return (
)
}
\ No newline at end of file
diff --git a/client/src/components/Menu/CalendarMenu.scss b/client/src/components/Menu/CalendarMenu.scss
index c304b0e..d9e675a 100644
--- a/client/src/components/Menu/CalendarMenu.scss
+++ b/client/src/components/Menu/CalendarMenu.scss
@@ -1,5 +1,9 @@
@use "./../../../styles/variables" as *;
+body.dark-mode .calendar-menu-popup {
+ background-color: $background-dark-1;
+ color: $white;
+}
.calendar-menu-popup {
font-size: .9rem;
diff --git a/client/src/components/Menu/CalendarMenu.tsx b/client/src/components/Menu/CalendarMenu.tsx
index 20e3795..8e61e82 100644
--- a/client/src/components/Menu/CalendarMenu.tsx
+++ b/client/src/components/Menu/CalendarMenu.tsx
@@ -12,9 +12,11 @@ import { IoMenu } from "react-icons/io5";
import { useGoogleHandler } from "@/pages/api/google";
import { useInruptHandler } from "@/pages/api/inrupt";
import { Event } from "@/src/model/Scheme";
+import { useTranslation } from "react-i18next";
export default function CalendarMenu() {
// event context utils
+ const { t } = useTranslation();
const { setEvents } = useEventContext()
const { calendars } = useGoogleContext();
const { getCalendars } = useGoogleHandler();
@@ -24,6 +26,7 @@ export default function CalendarMenu() {
const [selectedColor, setSelectedColor] = useState("");
const [menuOpened, setMenuOpened] = useState(false);
const [importingCalendars, setImportingCalendars] = useState(false);
+ const [creatingEvent, setCreatingEvent] = useState(false);
const titleRef = useRef(null);
const infoRef = useRef(null);
@@ -64,9 +67,11 @@ export default function CalendarMenu() {
* Handler for the creation of the event. If the event is correctly created, the creation of the event modal is closed
*/
const handleCreateEvent = async () => {
+ setCreatingEvent(true);
if (await constructEvent()) {
setMenuOpened(false);
}
+ setCreatingEvent(false);
}
/**
@@ -114,22 +119,26 @@ export default function CalendarMenu() {
// @ts-ignore
{
menuOpened &&
// @ts-ignore
- Add event
+ {t('calendar.calendarMenu.addEvent.title')}
- Create a new event and add it to your calendar.
+ {t('calendar.calendarMenu.addEvent.desc')}
- { createNewEvent && }
+ { createNewEvent && }
{
createNewEvent &&
@@ -145,14 +154,14 @@ export default function CalendarMenu() {
- Google Calendar
+ {t('calendar.calendarMenu.googleCalendar.title')}
- Import your calendars and sync them to import their events.
+ {t('calendar.calendarMenu.googleCalendar.desc')}
{
diff --git a/client/src/components/Menu/Menu.scss b/client/src/components/Menu/Menu.scss
index 9545afd..b6b65f6 100644
--- a/client/src/components/Menu/Menu.scss
+++ b/client/src/components/Menu/Menu.scss
@@ -24,6 +24,17 @@
}
}
+.dark-light-mode {
+ background-color: transparent;
+ border: none;
+ color: var.$white;
+}
+
+body.dark-mode .sidebar-menu {
+ background-color: var.$background-dark-1;
+ color: var.$white;
+}
+
.sidebar-menu-features {
display: flex;
flex-direction: column;
@@ -65,9 +76,10 @@
display: flex;
flex-direction: column;
align-items: center;
+ justify-content: center;
width: 100%;
- gap: .8em;
grid-row: 3/-1;
+ margin-bottom: 3rem;
@media screen and (min-width: 320px) and (max-width: 768px) {
display: none;
@@ -84,6 +96,10 @@
.menu-title {
@media screen and (min-width: 320px) and (max-width: 768px) {
+ display: grid;
+ grid-template-columns: 1fr 1fr 1fr;
+ width: 100%;
+ justify-content: center;
grid-column: 1/-1;
grid-row: 1/1;
}
@@ -119,11 +135,50 @@
@media screen and (min-width: 320px) and (max-width: 768px) {
width: fit-content;
+ grid-column: 2/2;
+ justify-self: center;
}
}
+body.dark-mode .menu-button {
+ background-color: var.$background-dark-1;
+ color: var.$white;
+}
+
.menu-button-text {
@media screen and (min-width: 320px) and (max-width: 768px) {
display: none;
}
+}
+
+.switch-language, .dark-light-mode, .switch-language-mobile, .dark-light-mode-mobile {
+ display: flex;
+ gap: .3rem;
+ font-size: .9rem;
+}
+
+.dark-light-mode-mobile {
+ @media screen and (min-width: 320px) and (max-width: 768px) {
+ grid-column: 3/-1;
+ grid-row: 1/1;
+ margin-right: 1rem;
+ margin-left: auto;
+ }
+
+ @media screen and (min-width: 769px) {
+ display: none;
+ }
+}
+
+.switch-language-mobile {
+ @media screen and (min-width: 320px) and (max-width: 768px) {
+ grid-column: 1/1;
+ grid-row: 1/1;
+ margin-right: auto;
+ margin-left: 1rem;
+ }
+
+ @media screen and (min-width: 769px) {
+ display: none;
+ }
}
\ No newline at end of file
diff --git a/client/src/components/Menu/Menu.tsx b/client/src/components/Menu/Menu.tsx
index 861ecdf..5489be3 100644
--- a/client/src/components/Menu/Menu.tsx
+++ b/client/src/components/Menu/Menu.tsx
@@ -1,11 +1,39 @@
import { useRouter } from "next/router";
import { Icon } from "../Icon/Icon";
import { useSessionContext } from "../Context/SolidContext";
+import { useTranslation } from 'react-i18next';
+import { useEffect, useState } from "react";
+import { MdOutlineDarkMode, MdOutlineLightMode } from "react-icons/md";
+import { SlGlobe } from "react-icons/sl";
export function MenuSideBar() {
-
+ const { t, i18n } = useTranslation();
const { solidSession } = useSessionContext();
const router = useRouter();
+ const [isDark, setIsDark] = useState(false);
+ const [showLanguagePanel, setShowLanguagePanel] = useState(false);
+
+ const languages: Record = {
+ "en": "English",
+ "es": "Español"
+ }
+
+ const changeLanguage = (lng:any) => {
+ i18n.changeLanguage(lng);
+ setShowLanguagePanel(false);
+ };
+
+ const handleChangeLanguage = () => {
+ if (i18n.language == "es") {
+ changeLanguage("en");
+ } else {
+ changeLanguage("es");
+ }
+ }
+
+ useEffect(() => {
+ document.body.classList.toggle('dark-mode', isDark);
+ }, [isDark]);
return (
@@ -13,6 +41,18 @@ export function MenuSideBar() {
+
+
+
+
diff --git a/client/src/components/Modal/AbsoluteModal/AbsoluteModal.scss b/client/src/components/Modal/AbsoluteModal/AbsoluteModal.scss
index 3e37e42..203aab1 100644
--- a/client/src/components/Modal/AbsoluteModal/AbsoluteModal.scss
+++ b/client/src/components/Modal/AbsoluteModal/AbsoluteModal.scss
@@ -25,6 +25,10 @@
}
}
+body.dark-mode .move-task {
+ color: $black;
+}
+
.move-button {
display: flex;
align-items: center;
diff --git a/client/src/components/Modal/AbsoluteModal/AbsoluteModal.tsx b/client/src/components/Modal/AbsoluteModal/AbsoluteModal.tsx
index ee861f8..4bd43c9 100644
--- a/client/src/components/Modal/AbsoluteModal/AbsoluteModal.tsx
+++ b/client/src/components/Modal/AbsoluteModal/AbsoluteModal.tsx
@@ -1,5 +1,6 @@
import { useClickAway } from "@uidotdev/usehooks";
-import { MutableRefObject, useState } from "react";
+import { useState } from "react";
+import { useTranslation } from "react-i18next";
import { TbArrowMoveRight } from "react-icons/tb";
import { uuid } from "uuidv4";
@@ -13,9 +14,10 @@ export interface Props {
export default function MoveModal({options, onClick, columnIndex, cardIndex, onClose} : Props) {
-
+ const { t } = useTranslation();
const [iconClass, setIconClass] = useState("move-icon-not-visible");
const [buttonHovered, setButtonHovered] = useState(-1);
+ const [loading, setLoading] = useState(-1);
const ref = useClickAway(() => {
onClose();
@@ -31,19 +33,27 @@ export default function MoveModal({options, onClick, columnIndex, cardIndex, onC
setButtonHovered(-1);
}
+ const moving = async (index:number) => {
+ setLoading(index);
+ await onClick(index);
+ setLoading(-1);
+ }
+
return (
// @ts-ignore
- {options.length > 1 ?
Move task to:
:
Add columns to move your tasks!
}
+ {options.length > 1 ?
{t('board.movePanel')}
:
{t('board.emptyBoard')}
}
{
options.map((option, index) => {
if (index !== columnIndex) {
return (
diff --git a/client/src/components/Modal/EditModal/EditEventModal.scss b/client/src/components/Modal/EditModal/EditEventModal.scss
index 21cd1e6..e28c4d7 100644
--- a/client/src/components/Modal/EditModal/EditEventModal.scss
+++ b/client/src/components/Modal/EditModal/EditEventModal.scss
@@ -8,7 +8,7 @@
box-sizing: border-box;
z-index: 10;
top: 50%;
- left: 30%;
+ left: 50%;
bottom: 50%;
padding: 1rem 2rem;
width: 35rem;
@@ -20,6 +20,12 @@
grid-template-rows: auto auto repeat(3, 1fr);
font-size: .8rem;
+ @media screen and (min-width: 320px) and (max-width: 768px) {
+ width: 80%;
+ grid-template-rows: auto auto repeat(3, 1fr) 2fr;
+ height: 60vh;
+ }
+
.edit-event-header {
grid-row: 1/1;
grid-column: 1/-1;
@@ -68,6 +74,10 @@
text-decoration: underline;
text-decoration-color: $primary-green;
}
+
+ @media screen and (min-width: 320px) and (max-width: 768px) {
+ grid-column: 1/-1;
+ }
}
.edit-event-info {
@@ -84,6 +94,12 @@
border: none;
outline: none;
}
+
+ @media screen and (min-width: 320px) and (max-width: 768px) {
+ grid-row: 6/-1;
+ grid-column: 1/-1;
+
+ }
}
.edit-event-start-date {
@@ -101,11 +117,18 @@
height: fit-content;
padding-inline: .4rem;
justify-self: self-start;
- width: 60%;
+
+ @media screen and (min-width: 320px) and (max-width: 768px) {
+ grid-column: 1/-1;
+ }
}
.edit-event-color {
grid-row: 5/-1;
grid-column: 1/1;
+
+ @media screen and (min-width: 320px) and (max-width: 768px) {
+ grid-column: 1/-1;
+ }
}
}
\ No newline at end of file
diff --git a/client/src/components/Modal/EditModal/EditEventModal.tsx b/client/src/components/Modal/EditModal/EditEventModal.tsx
index 5a39914..51a4450 100644
--- a/client/src/components/Modal/EditModal/EditEventModal.tsx
+++ b/client/src/components/Modal/EditModal/EditEventModal.tsx
@@ -1,6 +1,5 @@
import { useEffect, useState } from "react";
import { RiDeleteBin6Line } from "react-icons/ri";
-import { GrShareOption } from "react-icons/gr";
import ComboBox from "@/src/components/ComboBox/ComboBox";
import { IoMdClose } from "react-icons/io";
import { MdOutlineDone } from "react-icons/md";
@@ -14,12 +13,15 @@ import ShareModal from "../ShareModal/ShareEventModal";
import PromptModal from "../PromptModal/PromptModal";
import { useGoogleHandler } from "@/pages/api/google";
import { useInruptHandler } from "@/pages/api/inrupt";
+import { useTranslation } from "react-i18next";
+
export interface Props {
onClose: (arg?:any) => void | any;
}
export default function EditEventModal({onClose} : Props) {
+ const { t } = useTranslation();
// event context utils
const {setEvents, events, selectedEventId } = useEventContext();
const { updateEvent, deleteEvent } = useInruptHandler();
@@ -36,6 +38,8 @@ export default function EditEventModal({onClose} : Props) {
const [googleHtml, setGoogleHtml] = useState("");
const [isConfirmationDeleteModalOpen, setConfirmationDeleteModalOpen] = useState(false);
+ const [isSavingEvent, setIsSavingEvent] = useState(false);
+ const [deletingEvent, setDeletingEvent] = useState(false);
const ref = useClickAway(() => {
onClose();
@@ -145,6 +149,7 @@ export default function EditEventModal({onClose} : Props) {
* Save changes made to the event.
*/
const onSave = async () => {
+ setIsSavingEvent(true);
// get event to be saved
let updatedEvents = [...events];
const eventToUpdate = events[selectedIndex];
@@ -165,9 +170,9 @@ export default function EditEventModal({onClose} : Props) {
if (needToUpdateGoogle(changedValues)) {
const promise = updateEventOnGoogle(eventToUpdate);
toast.promise(promise, {
- loading: "Updating event...",
- success: "Google is updated!",
- error: (err) => "Google says '" + err + "'"
+ loading: t('toast.updating'),
+ success: t('toast.updated'),
+ error: (err) => t('toast.googleSays') + err + "'"
}, {
position: "bottom-center",
error: {
@@ -179,6 +184,7 @@ export default function EditEventModal({onClose} : Props) {
}
// close edit modal
onClose();
+ setIsSavingEvent(false);
}
/**
@@ -193,6 +199,7 @@ export default function EditEventModal({onClose} : Props) {
* Deletes the event from the set of events.
*/
const onDelete = async () => {
+ setDeletingEvent(true);
const eventToUpdate = events[selectedIndex];
await deleteEvent(eventToUpdate);
setEvents((prevEvents) => {
@@ -200,13 +207,7 @@ export default function EditEventModal({onClose} : Props) {
});
// close edit modal
onClose();
- }
-
- /**
- * Exports event to Google Calendar.
- */
- const onShare = () => {
-
+ setDeletingEvent(false);
}
return (
@@ -214,14 +215,15 @@ export default function EditEventModal({onClose} : Props) {
{isGoogleEvent &&
-
+
}
-
setNewTitle(e.target.value)}
/>