diff --git a/README-photos/devakirawal.jpeg b/README-photos/devakirawal.jpeg index 78ad01e..1863282 100644 Binary files a/README-photos/devakirawal.jpeg and b/README-photos/devakirawal.jpeg differ diff --git a/README.md b/README.md index faa3838..a2f4760 100644 --- a/README.md +++ b/README.md @@ -116,10 +116,10 @@ The contributors of this project are participants of Incubate Nepal.
- Image of Aditya Bikram Thakur + Image of Devaki Rawal - Devaki Rawal is in Kathmandu, living in Bhaktapur, and currently in her gap year. She knows frontend development and UI/UX design from her experience working on a Buddhist lipi to English translation app for tourists. In her free time she enjoys watching Shark Tank, listening to music, and singing. + Devaki Rawal is a high school graduate and a tech enthusiast, always eager to spread digital literacy programs. In her free time, she loves teaching children. She challenges herself every day to become one step better than yesterday. Devaki enjoys traveling, watching Shark Tank, and teaching. She is always open to learning and exploring new things every day.
diff --git a/nepalingo-web/package.json b/nepalingo-web/package.json index 638707a..d2a0d2f 100644 --- a/nepalingo-web/package.json +++ b/nepalingo-web/package.json @@ -10,6 +10,7 @@ "preview": "vite preview" }, "dependencies": { + "@fortawesome/fontawesome-svg-core": "^6.5.2", "@fortawesome/free-solid-svg-icons": "^6.5.2", "@fortawesome/react-fontawesome": "^0.2.2", "@supabase/supabase-js": "^2.44.2", diff --git a/nepalingo-web/pnpm-lock.yaml b/nepalingo-web/pnpm-lock.yaml index cd6a031..736b27d 100644 --- a/nepalingo-web/pnpm-lock.yaml +++ b/nepalingo-web/pnpm-lock.yaml @@ -8,6 +8,9 @@ importers: .: dependencies: + '@fortawesome/fontawesome-svg-core': + specifier: ^6.5.2 + version: 6.5.2 '@fortawesome/free-solid-svg-icons': specifier: ^6.5.2 version: 6.5.2 diff --git a/nepalingo-web/public/dictionaries/Tajpuriya grammer - Sheet1.csv b/nepalingo-web/public/dictionaries/Tajpuriya grammer - Sheet1.csv new file mode 100644 index 0000000..c632494 --- /dev/null +++ b/nepalingo-web/public/dictionaries/Tajpuriya grammer - Sheet1.csv @@ -0,0 +1,41 @@ +English,Tajpuriya +Go,जा +Come,ओस +Sit,बोठ +Rice,खराक +play,खेल्लुवा +I,मुई +You,तोर +My,मोर +We,हमा +Our,हमार +All,गोट्टेला +yesterday,कल +Today,आज +Tomorrow,कालीगे +where,कुना +mummy,आई +Baba,बाउ +Grandfather,दादो +Gradndmother,दादी +Brother,भाई +Sister,बहिन +When, +How,किनङ्गे +Why,काए +Way,रस्ता +Here,हितीना +What,काए +Water,जल +Look,देख +And,आर +People,लोग +Over,उपर +Know,पत्ता +Like this,हिनङ्गे +This,ईला +Did,करेसोक +House,घरत +Infront,आगुबिती +Behind,पाछुबिती +His/Her,वाँर \ No newline at end of file diff --git a/nepalingo-web/public/quotes/newari.csv b/nepalingo-web/public/quotes/newari.csv index 857b18b..aeffc84 100644 --- a/nepalingo-web/public/quotes/newari.csv +++ b/nepalingo-web/public/quotes/newari.csv @@ -1,6 +1,6 @@ -nepalbhasa, english -"𑐮𑐸𑑃 𑐮𑐣𑐾𑐐𑐸 𑐟𑑅𑐎𑑅, 𑐩𑐣𑐹 𑐮𑐣𑐾𑐐𑐸 𑐕𑐎𑑅 𑑋", "Gold may be weighed many times, a man weighed but once.(www.nepal-lipi.com)" -"𑐟𑑅𑐡𑐾𑐰𑐵𑐫𑐵 𑐎𑑂𑐰𑐫𑑂 𑐩𑐟𑑃 𑐩𑐏𑑃 𑑋", "Dark under the lamp.(www.nepal-lipi.com)" -"𑐘𑐶𑐩𑑂𑐴 𑐟𑐶𑐥𑐹 𑐮𑑂𑐰𑐵𑐂, 𑐩𑐵𑐎𑐮𑑃 𑐦𑑃𑐳𑐶 𑐏𑐵𑐂 𑑋", "While the couple quarrels the monkey picks up the fruit.(www.nepal-lipi.com)" -"𑐮𑐸𑐦𑐶𑑃 𑐴𑐵𑐫𑐾𑐰𑑃 𑐩𑐵𑑃 𑐮𑐸𑐩𑑃 𑑋", "When you stumble, you remember your mother.(www.nepal-lipi.com)" -"𑐩𑐶𑐳𑐵𑐫𑐵 𑐟𑐶𑐳𑐵, 𑐧𑐬𑐾𑐖𑐸𑐫𑐵 𑐣𑐳𑐵 𑑋", "A woman’s ornament is food for the goldsmith. (www.nepal-lipi.com)" +nepalbhasa, english +"𑐮𑐸𑑃 𑐮𑐣𑐾𑐐𑐸 𑐟𑑅𑐎𑑅, 𑐩𑐣𑐹 𑐮𑐣𑐾𑐐𑐸 𑐕𑐎𑑅 𑑋", "Gold may be weighed many times, a man weighed but once.(www.nepal-lipi.com)" +"𑐟𑑅𑐡𑐾𑐰𑐵𑐫𑐵 𑐎𑑂𑐰𑐫𑑂 𑐩𑐟𑑃 𑐩𑐏𑑃 𑑋", "Dark under the lamp.(www.nepal-lipi.com)" +"𑐘𑐶𑐩𑑂𑐴 𑐟𑐶𑐥𑐹 𑐮𑑂𑐰𑐵𑐂, 𑐩𑐵𑐎𑐮𑑃 𑐦𑑃𑐳𑐶 𑐏𑐵𑐂 𑑋", "While the couple quarrels the monkey picks up the fruit.(www.nepal-lipi.com)" +"𑐮𑐸𑐦𑐶𑑃 𑐴𑐵𑐫𑐾𑐰𑑃 𑐩𑐵𑑃 𑐮𑐸𑐩𑑃 𑑋", "When you stumble, you remember your mother.(www.nepal-lipi.com)" +"𑐩𑐶𑐳𑐵𑐫𑐵 𑐟𑐶𑐳𑐵, 𑐧𑐬𑐾𑐖𑐸𑐫𑐵 𑐣𑐳𑐵 𑑋", "A woman’s ornament is food for the goldsmith. (www.nepal-lipi.com)" diff --git a/nepalingo-web/src/App.tsx b/nepalingo-web/src/App.tsx index 37bc721..732b19c 100644 --- a/nepalingo-web/src/App.tsx +++ b/nepalingo-web/src/App.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React from "react"; import { BrowserRouter as Router, Route, @@ -7,7 +7,8 @@ import { } from "react-router-dom"; import User_auth from "./components/userAuth/UserAuth"; import Home from "./pages/Home/Home"; -import FlashcardPage from "./pages/FlashcardPage" +import SearchBarPage from "./pages/SearchBarPage"; +import FlashcardPage from "./pages/FlashcardPage"; import { useAuth } from "./components/userAuth/AuthContext"; import ReactGA from "react-ga4"; @@ -22,11 +23,16 @@ const App: React.FC = () => { } /> - } /> {/* Protect the / route, redirect to /login if not authenticated */} : } /> - {/* Default route redirects to /login */} - } /> + : } + /> + : } + /> ); diff --git a/nepalingo-web/src/components/DailyQuiz.tsx b/nepalingo-web/src/components/DailyQuiz.tsx new file mode 100644 index 0000000..f7f68ae --- /dev/null +++ b/nepalingo-web/src/components/DailyQuiz.tsx @@ -0,0 +1,33 @@ +import React from 'react'; +import { useNavigate } from 'react-router-dom'; // Import useNavigate +import Button from './Button'; +const DailyQuiz: React.FC = () => { + const backgroundImageUrl = 'https://t3.ftcdn.net/jpg/00/73/08/22/360_F_73082224_ay4Tus31QNHNmGSIty53ZE6mBrBc47cV.jpg'; // Set your image URL + const navigate = useNavigate(); // Get the navigate function + + const handleStartQuizClick = () => { + // Redirect to /flashcard when the button is clicked + navigate('/flashcard'); + }; + + return ( +
+

Nepalingo

+

QUIZ YOURSELF

+

+ Taking Quiz is a better and fun way for learning +

+ +
+ ); +}; + +export default DailyQuiz; diff --git a/nepalingo-web/src/components/Flashcard.tsx b/nepalingo-web/src/components/Flashcard.tsx index b9475ce..028150f 100644 --- a/nepalingo-web/src/components/Flashcard.tsx +++ b/nepalingo-web/src/components/Flashcard.tsx @@ -1,61 +1,75 @@ -import React, { useState } from 'react'; -import Card from './Card'; -import Button from './Button'; -import useDictionary from '../hooks/useDictionary'; +import React, { useState } from "react"; +import Card from "./Card"; +import Button from "./Button"; +import useDictionary from "../hooks/useDictionary"; import { generate } from "random-words"; +import ReactGA from "react-ga4"; const Flashcard: React.FC = () => { + ReactGA.event({ + category: "flash cards", + action: "Click", + value: 99, // optional, must be a number + nonInteraction: true, // optional, true/false + transport: "xhr", // optional, beacon/xhr/image + }); + const [word, setWord] = useState("salt"); + const [isFlipped, setIsFlipped] = useState(false); + const { data, isLoading, error } = useDictionary({ + language: "newari", + word, + }); + console.log(error); + const handleFlip = () => { + setIsFlipped(!isFlipped); + }; - const [word, setWord] = useState('salt'); - const [isFlipped, setIsFlipped] = useState(false); - const { data, isLoading, error } = useDictionary({ language: 'newari', word }); - console.log(error) + const handleNextWord = () => { + setWord(generate() as string); + setIsFlipped(false); + }; + if (isLoading) return
Loading
; + if (error?.response?.length) handleNextWord(); + const meaning = data && data.meanings[0]; - const handleFlip = () => { - setIsFlipped(!isFlipped); - }; + return ( +
+
+ {error ? ( +
Error: {error.message}
+ ) : ( + + )} - const handleNextWord = () => { - setWord(generate() as string) - setIsFlipped(false); - }; - - - - if (isLoading) return
Loading
- if (error?.response?.length) handleNextWord() - const meaning = data && data.meanings[0] - - - return ( -
-
- { - error ?
Error: {error.message}
: - } - - -
- - -
-
+
+ +
- ); +
+
+ ); }; export default Flashcard; diff --git a/nepalingo-web/src/components/Header.tsx b/nepalingo-web/src/components/Header.tsx deleted file mode 100644 index d23f8df..0000000 --- a/nepalingo-web/src/components/Header.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import React from "react"; -import logo from "../assets/NepaLingoLogoWhiteBg.jpg"; - -const Header: React.FC = () => { - return ( - - ); -}; - -export default Header; diff --git a/nepalingo-web/src/components/SearchBar/SearchBar.tsx b/nepalingo-web/src/components/SearchBar/SearchBar.tsx new file mode 100644 index 0000000..50353c4 --- /dev/null +++ b/nepalingo-web/src/components/SearchBar/SearchBar.tsx @@ -0,0 +1,75 @@ +import React, { useState } from 'react'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faSearch } from '@fortawesome/free-solid-svg-icons'; +import useDictionary, { DictionaryProps} from '../../hooks/useDictionary'; + +interface DictionarySearchBarProps { + language: DictionaryProps['language']; // Define language as a prop +} + +const DictionarySearchBar: React.FC = ({ language }) => { + const [searchTerm, setSearchTerm] = useState(''); + const { data, isLoading, error } = useDictionary({ language, word: searchTerm }); + const handleChange = (e: React.ChangeEvent) => { + setSearchTerm(e.target.value); + }; + + const handleSearchClick = () => { + // Trigger re-fetch (not necessary for useSWR as it auto-updates) + }; + + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === 'Enter') { + handleSearchClick(); + } + }; + + return ( +
+
+ + + + +
+ + {error &&

{error.message}

} +
    + {isLoading &&

    Loading...

    } + {data && data.meanings ? ( + data.meanings.length > 0 ? ( + data.meanings.map((meaning) => ( +
  • +

    {data.word}

    +

    {meaning.meaningEn}

    + {meaning.meaningNp &&

    {meaning.meaningNp}

    } + {meaning.meaningOriginal &&

    {meaning.meaningOriginal}

    } + {meaning.audio && } + {meaning.image && {data.word}} +
  • + )) + ) : ( +

    No results found

    + ) + ) : ( +

    No results available

    + )} +
+ + + +
+ ); +}; + +export default DictionarySearchBar; diff --git a/nepalingo-web/src/components/StreakContext.tsx b/nepalingo-web/src/components/StreakContext.tsx new file mode 100644 index 0000000..b41c149 --- /dev/null +++ b/nepalingo-web/src/components/StreakContext.tsx @@ -0,0 +1,112 @@ +import React, { + createContext, + useContext, + useState, + useEffect, + ReactNode, +} from "react"; +import supabase from "./userAuth/supabaseClient"; +import { useAuth } from "./userAuth/AuthContext"; + +interface StreakContextProps { + currentStreak: number; + longestStreak: number; + updateStreak: () => void; +} + +export const StreakContext = createContext({ + currentStreak: 0, + longestStreak: 0, + updateStreak: () => {}, +}); + +export const StreakProvider = ({ children }: { children: ReactNode }) => { + const { user } = useAuth(); + const [currentStreak, setCurrentStreak] = useState(0); + const [longestStreak, setLongestStreak] = useState(0); + const [streakEndDate, setStreakEndDate] = useState(null); + + const fetchStreakData = async () => { + if (user) { + const { data, error } = await supabase + .from("user_daily_streaks") + .select("*") + .eq("user_id", user.id) + .single(); + + if (error && error.code !== "PGRST116") { + console.error("Error fetching streak data:", error); + } + + if (data) { + setCurrentStreak(data.current_streak); + setLongestStreak(data.longest_streak); + setStreakEndDate(data.streak_end_date); + } + } + }; + + useEffect(() => { + fetchStreakData(); + }, [user]); + + const updateStreak = async () => { + const currentDate = new Date().toISOString().split("T")[0]; // Get current date + + if (user) { + let newStreak = 1; + let newLongestStreak = 1; + + if (streakEndDate) { + const lastDate = new Date(streakEndDate).toISOString().split("T")[0]; + if (lastDate === currentDate) { + return; // No update needed if the user has already visited today + } else if ( + new Date(currentDate).getTime() - new Date(lastDate).getTime() === + 86400000 + ) { + newStreak = currentStreak + 1; + newLongestStreak = Math.max(newStreak, longestStreak); + } + } + + // Update state + setCurrentStreak(newStreak); + setLongestStreak(newLongestStreak); + setStreakEndDate(currentDate); + + // Upsert database + const { error } = await supabase + .from("user_daily_streaks") + .upsert({ + user_id: user.id, + streak_start_date: newStreak === 1 ? currentDate : undefined, + streak_end_date: currentDate, // Update streak_end_date with currentDate + current_streak: newStreak, + longest_streak: newLongestStreak, + created_at: new Date().toISOString(), + }) + .select(); + + if (error) { + console.error("Error updating streak data:", error); + } + } + }; + + return ( + + {children} + + ); +}; + +export const useStreak = () => { + const context = useContext(StreakContext); + if (context === undefined) { + throw new Error("useStreak must be used within a StreakProvider"); + } + return context; +}; diff --git a/nepalingo-web/src/components/UserAvatar.tsx b/nepalingo-web/src/components/UserAvatar.tsx new file mode 100644 index 0000000..70ed5d6 --- /dev/null +++ b/nepalingo-web/src/components/UserAvatar.tsx @@ -0,0 +1,18 @@ +import React from "react"; +import { useAuth } from "./userAuth/AuthContext"; // Adjust the import path based on your project structure + +const UserAvatar: React.FC = () => { + const { user } = useAuth(); + const username = user?.user_metadata?.username; + const avatarUrl = `https://robohash.org/${username}.png`; + + return ( + User Avatar + ); +}; + +export default UserAvatar; diff --git a/nepalingo-web/src/components/header/ChangeLanguage.tsx b/nepalingo-web/src/components/header/ChangeLanguage.tsx new file mode 100644 index 0000000..b11c2e6 --- /dev/null +++ b/nepalingo-web/src/components/header/ChangeLanguage.tsx @@ -0,0 +1,61 @@ +import React, { useEffect, useRef, useState } from "react"; +import Menu from "./Menu"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faChevronDown } from "@fortawesome/free-solid-svg-icons"; +import { LanguageKey, Languages, useLanguage } from "../../hooks/Langauge"; + +const ChangeLanguage: React.FC = () => { + const [isOpen, setIsOpen] = useState(false); + const { selectedLanguage, switchLanguage } = useLanguage(); + const dropdownRef = useRef(null); + useEffect(() => { + const handleEscape = (event: KeyboardEvent) => { + if (event.key === "Escape") { + setIsOpen(false); + } + }; + const handleOutsideClick = (event: MouseEvent) => { + if (!dropdownRef.current?.contains(event.target as Node)) { + setIsOpen(false); + } + } + if (isOpen) { + document.addEventListener("click", handleOutsideClick); + } else { + document.removeEventListener("click", handleOutsideClick); + } + + window.addEventListener("keydown", handleEscape); + + return () => { + window.removeEventListener("keydown", handleEscape); + document.removeEventListener("click", handleOutsideClick); + }; + }, [open]); + + const toggleMenu = () => { + setIsOpen(!isOpen); + }; + + const handleSelect = (option: LanguageKey) => { + switchLanguage(option); + setIsOpen(false); + }; + const options = Object.keys(Languages).map((key) => ({ label: key, value: Languages[key as LanguageKey] })) + console.log(options) + return ( +
+ + +
+ ); +}; + +export default ChangeLanguage; diff --git a/nepalingo-web/src/components/header/Header.tsx b/nepalingo-web/src/components/header/Header.tsx new file mode 100644 index 0000000..26bd7a9 --- /dev/null +++ b/nepalingo-web/src/components/header/Header.tsx @@ -0,0 +1,24 @@ +import React from "react"; +import logo from "../../assets/NepaLingoLogoWhiteBg.jpg"; +import UserProfile from "./UserProfile"; +import ChangeLanguage from "./ChangeLanguage"; + +const Header: React.FC = () => { + return ( + + ); +}; + +export default Header; diff --git a/nepalingo-web/src/components/header/Menu.tsx b/nepalingo-web/src/components/header/Menu.tsx new file mode 100644 index 0000000..31c096f --- /dev/null +++ b/nepalingo-web/src/components/header/Menu.tsx @@ -0,0 +1,44 @@ +import React from "react"; +import { LanguageKey } from "../../hooks/Langauge"; + +interface MenuProps { + isOpen: boolean; + onSelect: (option: LanguageKey) => void; + options: { label: string; value: string }[]; +} + +const Menu: React.FC = ({ isOpen, onSelect, options }) => { + if (!isOpen) return null; + + return ( +
+
+ {options.map((option) => ( + + ))} +
+
+ ); +}; + +export default Menu; diff --git a/nepalingo-web/src/components/header/UserProfile.tsx b/nepalingo-web/src/components/header/UserProfile.tsx new file mode 100644 index 0000000..687ff85 --- /dev/null +++ b/nepalingo-web/src/components/header/UserProfile.tsx @@ -0,0 +1,59 @@ +import React, { useState, useContext } from "react"; +import UserAvatar from "../UserAvatar"; +import Menu from "./Menu"; +import { useAuth } from "../userAuth/AuthContext"; +import { StreakContext } from "../StreakContext"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faFire } from "@fortawesome/free-solid-svg-icons"; + +const UserProfile: React.FC = () => { + const [isOpen, setIsOpen] = useState(false); + const { user, signOut } = useAuth(); + const { currentStreak, longestStreak } = useContext(StreakContext); // Get streak data from context + + const toggleMenu = () => { + setIsOpen(!isOpen); + }; + + const handleSelect = (option: string) => { + if (option === "signOut") { + signOut(); + } + setIsOpen(false); + }; + + const options = [ + { label: `Username: ${user?.user_metadata?.username}`, value: "username" }, + { label: `Current Streak: ${currentStreak} days`, value: "currentStreak" }, + { label: `Longest Streak: ${longestStreak} days`, value: "longestStreak" }, + { label: "Log out", value: "signOut" }, + ]; + + return ( +
+ + +
+ ); +}; + +export default UserProfile; diff --git a/nepalingo-web/src/components/userAuth/AuthContext.tsx b/nepalingo-web/src/components/userAuth/AuthContext.tsx index ac09917..5030bce 100644 --- a/nepalingo-web/src/components/userAuth/AuthContext.tsx +++ b/nepalingo-web/src/components/userAuth/AuthContext.tsx @@ -6,13 +6,22 @@ import React, { useEffect, } from "react"; import supabase from "./supabaseClient"; -import { AuthError, AuthResponse, AuthTokenResponsePassword, SignInWithPasswordCredentials, SignUpWithPasswordCredentials, User } from "@supabase/supabase-js"; +import { + AuthError, + AuthResponse, + AuthTokenResponsePassword, + SignInWithPasswordCredentials, + SignUpWithPasswordCredentials, + User, +} from "@supabase/supabase-js"; interface AuthContextProps { user: User | null; signUp: (data: SignUpWithPasswordCredentials) => Promise; - signIn: (data: SignInWithPasswordCredentials) => Promise; - signOut: () => Promise<{ error: AuthError | null }> + signIn: ( + data: SignInWithPasswordCredentials + ) => Promise; + signOut: () => Promise<{ error: AuthError | null }>; } const AuthContext = createContext(undefined); diff --git a/nepalingo-web/src/hooks/Langauge.tsx b/nepalingo-web/src/hooks/Langauge.tsx new file mode 100644 index 0000000..284bd47 --- /dev/null +++ b/nepalingo-web/src/hooks/Langauge.tsx @@ -0,0 +1,51 @@ +import React from 'react'; +import { useContext, useState, useEffect, createContext } from 'react'; + +type ValueOf = T[keyof T]; +export type Language = ValueOf + +export type LanguageKey = keyof typeof Languages; + +export const Languages = { + "Newari": "newari", + "Tajpuriya": "tajpuriya", + "Maithili": "coming soon", +} as const + + +type LanguageContextProps = { + selectedLanguage: string | null | undefined; + switchLanguage: (value: LanguageKey) => void; +} +const LanguageContext = createContext({ + selectedLanguage: '', + switchLanguage: (value) => console.log(value) +}); + +export const LanguageProvider = ({ children }: { children: React.ReactNode }) => { + const [language, setLanguage] = useState("Newari") + + const switchLanguage = (value: LanguageKey) => { + setLanguage(value) + localStorage.setItem('language', value) + } + + + useEffect(() => { + localStorage.getItem('language') && setLanguage(localStorage.getItem('language') as LanguageKey) + }, []) + + return ( + + {children} + + ); +}; + +export const useLanguage = () => { + return useContext(LanguageContext); +}; diff --git a/nepalingo-web/src/hooks/getTajpuriya.tsx b/nepalingo-web/src/hooks/getTajpuriya.tsx new file mode 100644 index 0000000..5c08dac --- /dev/null +++ b/nepalingo-web/src/hooks/getTajpuriya.tsx @@ -0,0 +1,58 @@ +import { useState, useEffect } from "react"; + +const language = "tajpuriya" as const; + +export type dictResponse = { + language: string; + word: { + text: string; + translation: string; + }; +}; + +const getTajpuriya = () => { + const [word, setWord] = useState(null); + const [wordText, setwordText] = useState(""); + + useEffect(() => { + const sourceFile = "./dictionaries/Tajpuriya grammer - Sheet1.csv"; + + if (sourceFile) { + fetch(sourceFile) + .then((r) => r.text()) + .then((text) => { + setwordText(text); + }) + .catch((error) => { + console.error("Error fetching quotes:", error); + }); + } + }, []); + + useEffect(() => { + if (wordText) { + const loadDict = () => { + const dictArray = wordText.split("\n").map((line: string) => { + const [text, translation] = line.split(","); + return { + text: text ? text.trim().replace(/(^"|"$)/g, "") : "", + translation: translation + ? translation.trim().replace(/(^"|"$)/g, "") + : "", + }; + }); + + const randomIndex = Math.floor(Math.random() * dictArray.length); + const randomWord = dictArray[randomIndex]; + + setWord({ language, word: randomWord }); + }; + + loadDict(); + } + }, [wordText]); + + return word; +}; + +export default getTajpuriya; diff --git a/nepalingo-web/src/hooks/useDictionary.tsx b/nepalingo-web/src/hooks/useDictionary.tsx index 8624edb..ced8e94 100644 --- a/nepalingo-web/src/hooks/useDictionary.tsx +++ b/nepalingo-web/src/hooks/useDictionary.tsx @@ -1,13 +1,8 @@ +import { Language } from './Langauge'; import useNewari from './useNewari' -const Languages = [ - 'newari', - 'tajpuriya', - 'maithili' -] - export type DictionaryProps = { - language: typeof Languages[number], + language: Language, word: string } @@ -15,8 +10,8 @@ export type DictionaryResponse = { language: string; word: string; meanings: [{ - audio?: { uri: string}, - image?: {uri:string}, + audio?: { uri: string }, + image?: { uri: string }, language: string, meaningOriginal?: string, meaningNp?: string, @@ -33,18 +28,18 @@ const useDictionary = ({ language, ...otherProps }: DictionaryProps) => { return useNewari(otherProps) // case 'tajpuriya': // return ({ - // error: { message: "Sorry the language doesnot exists" }, + // error: { message: "Sorry the language does not exist" }, // data: null, // isLoading: false, // }) default: return ({ - error: { message: "Sorry the language doesnot exists" }, + error: { message: "Sorry the language does not exist" }, data: undefined, isLoading: false, }) } } - export default useDictionary + diff --git a/nepalingo-web/src/hooks/useNewari.tsx b/nepalingo-web/src/hooks/useNewari.tsx index 51150e0..8a8050d 100644 --- a/nepalingo-web/src/hooks/useNewari.tsx +++ b/nepalingo-web/src/hooks/useNewari.tsx @@ -38,4 +38,4 @@ const useNewari = (props: Omit) => { return { data: response, error:customError, isLoading } } -export default useNewari; +export default useNewari; \ No newline at end of file diff --git a/nepalingo-web/src/index.css b/nepalingo-web/src/index.css index d544329..68826d1 100644 --- a/nepalingo-web/src/index.css +++ b/nepalingo-web/src/index.css @@ -1,3 +1,5 @@ +@import url('https://fonts.googleapis.com/css2?family=Nunito:ital,wght@0,200..1000;1,200..1000&family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap'); + @tailwind base; @tailwind components; @tailwind utilities; @@ -16,4 +18,4 @@ } .rotateY-0 { transform: rotateY(0deg); -} \ No newline at end of file +} diff --git a/nepalingo-web/src/main.tsx b/nepalingo-web/src/main.tsx index 6762a56..9c75839 100644 --- a/nepalingo-web/src/main.tsx +++ b/nepalingo-web/src/main.tsx @@ -3,11 +3,16 @@ import ReactDOM from "react-dom/client"; import App from "./App"; import "./index.css"; import { AuthProvider } from "./components/userAuth/AuthContext"; - +import { StreakProvider } from "./components/StreakContext"; +import { LanguageProvider } from "./hooks/Langauge"; ReactDOM.createRoot(document.getElementById("root")!).render( - - - - - + + + + + + + + + ); diff --git a/nepalingo-web/src/pages/FlashcardPage.tsx b/nepalingo-web/src/pages/FlashcardPage.tsx index de893af..8e88f61 100644 --- a/nepalingo-web/src/pages/FlashcardPage.tsx +++ b/nepalingo-web/src/pages/FlashcardPage.tsx @@ -1,13 +1,27 @@ -import Flashcard from "../components/Flashcard" -import Header from "../components/Header" -import React from 'react'; +import Flashcard from "../components/Flashcard"; +import Header from "../components/header/Header"; +import React, { useEffect } from "react"; +import { useStreak } from "../components/StreakContext"; +import ReactGA from "react-ga4"; const FlashcardPage: React.FC = () => { + const { updateStreak } = useStreak(); + + ReactGA.send({ + hitType: "pageview", + page: window.location.pathname, + title: "flashcard page", + }); + + useEffect(() => { + updateStreak(); // Trigger streak update on flashcard page load + }, []); + return ( <>
- ) -} -export default FlashcardPage + ); +}; +export default FlashcardPage; diff --git a/nepalingo-web/src/pages/Home/Home.tsx b/nepalingo-web/src/pages/Home/Home.tsx index e87a99c..38d021f 100644 --- a/nepalingo-web/src/pages/Home/Home.tsx +++ b/nepalingo-web/src/pages/Home/Home.tsx @@ -1,53 +1,61 @@ import React from "react"; import { Link } from "react-router-dom"; import logo from "../../assets/logo.png"; -import ReactGA from 'react-ga4'; +import Header from "../../components/header/Header"; +import ReactGA from "react-ga4"; import { useAuth } from "../../components/userAuth/AuthContext"; import GreetingCard from "../../components/GreetingCard"; +import DailyQuiz from "../../components/DailyQuiz"; const Home: React.FC = () => { - ReactGA.send({ hitType: "pageview", page: window.location.pathname, title: "home" }); + ReactGA.send({ + hitType: "pageview", + page: window.location.pathname, + title: "home", + }); const { user } = useAuth(); - return ( -
-
-
- -
-
-

- Welcome to Nepalingo! -

+
+
+
+
+
+ +
-
-
-
- Nepalingo Logo +
+
+ Nepalingo Logo +
+
+

Nepalingo

+
+
+

+ Learn indigenous languages of Nepal for free +

+
-
-

Nepalingo

+ +
+
-
-

- Learn indigenous languages of Nepal for free -

+ +
+ + Search Bar +
-
- - Learn Words - -
); }; diff --git a/nepalingo-web/src/pages/SearchBarPage.tsx b/nepalingo-web/src/pages/SearchBarPage.tsx new file mode 100644 index 0000000..35822cd --- /dev/null +++ b/nepalingo-web/src/pages/SearchBarPage.tsx @@ -0,0 +1,15 @@ +import React from 'react'; +import Header from '../components/header/Header'; +import DictionarySearchBar from '../components/SearchBar/SearchBar'; + +const SearchBarPage: React.FC = () => { + return ( + <> +
+

Search Dictionary

+ + + ); +} + +export default SearchBarPage; diff --git a/nepalingo-web/tailwind.config.ts b/nepalingo-web/tailwind.config.ts index ea3c42c..be7c8e2 100644 --- a/nepalingo-web/tailwind.config.ts +++ b/nepalingo-web/tailwind.config.ts @@ -1,14 +1,29 @@ import { Config } from 'tailwindcss'; const config: Config = { - content: [ - './index.html', - './src/**/*.{js,ts,jsx,tsx}', - ], - theme: { - extend: {}, - }, - plugins: [], + content: [ + './index.html', + './src/**/*.{js,ts,jsx,tsx}', + ], + theme: { + extend: { + colors: { + primary: '#D03641', + black: '#1A1A1A', + white: '#F8F4F4', + grayDark: '#2B2B2B', + grayLight: '#C8C8C8' + }, + fontFamily: { + primary: ['Nunito', 'sans serif'], + secondary: 'Roboto' + } + + }, + + }, + plugins: [], }; export default config; +