diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 97ced17..0000000 --- a/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -*.txt -*.png -*.log -*.pdf -*.dat diff --git a/README.md b/README.md deleted file mode 100644 index 8da27b4..0000000 --- a/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# DEURDU website - -This space is meant to contain code, data and configuration related to a multilingual blog/website. diff --git a/deurdu/client/.gitignore b/client/.gitignore similarity index 92% rename from deurdu/client/.gitignore rename to client/.gitignore index a547bf3..af5db47 100644 --- a/deurdu/client/.gitignore +++ b/client/.gitignore @@ -10,6 +10,9 @@ lerna-debug.log* node_modules dist dist-ssr + +package-lock.json + *.local # Editor directories and files diff --git a/client/README.md b/client/README.md new file mode 100644 index 0000000..b480d7c --- /dev/null +++ b/client/README.md @@ -0,0 +1,89 @@ +# React + Vite + +This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. + +Currently, two official plugins are available: + +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh + +# 🛠️ **Setup Instructions for New Contributors** + +Welcome to the project! 🎉 Follow these simple steps to set up your local development environment. This will ensure you avoid any errors related to missing configurations or dependencies. + +--- + +### 1️⃣ **Clone the Repository** + +Start by cloning the repository to your local machine: + +```bash +git clone https://github.com/yourusername/your-repository.git +cd your-repository +``` + +--- + +### 2️⃣ **Install Project Dependencies** + +Ensure that you have Node.js installed (version 14 or above is recommended). Then, install the required dependencies using npm or Yarn: + +- Using npm +```bash +npm install +``` +- Or if you want to use Yarn +```bash +yarn install +``` + +### 3️⃣ **Generate Missing Configuration Files** + +As some configuration files were deleted, you'll need to regenerate or create them manually. Here are the necessary steps for each file: +## **For TailwindCSS:** +Regenerate the ```tailwind.config.js``` file: +```bash +npx tailwindcss init +``` +This will generate the ```tailwind.config.js``` file. + +## **For Vite:** +Regenerate the ```vite.config.js``` file by creating a new one in the root of your project and adding the following content: +```JavaScript +// vite.config.js +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [react()], +}) +``` + +## **For PostCSS** +If the ```postcss.config.js``` is missing, you can either create it manually or reinstall PostCSS: +```bash +npm install postcss +``` +Then, manually create the ```postcss.config.js``` file with the necessary PostCSS configuration. + +## **For ESLint:** +Regenerate your **ESLint** configuration by running: +```bash +npm install eslint --save-dev +npx eslint --init +``` +Follow the prompts to generate the ```**eslint.config.js**``` file. + +### 4️⃣ **Run the development server** +Once everything is set up, you can run the development server with one of the following commands: +- Using npm: +```bash +npm run dev +``` +- Or using Yarn: +```bash +yarn dev +``` + +**🎉 Your development environment should now be up and running! Happy coding! 🎉** diff --git a/deurdu/client/index.html b/client/index.html similarity index 100% rename from deurdu/client/index.html rename to client/index.html diff --git a/deurdu/client/package.json b/client/package.json similarity index 94% rename from deurdu/client/package.json rename to client/package.json index 4e7ead5..4693de7 100644 --- a/deurdu/client/package.json +++ b/client/package.json @@ -11,9 +11,11 @@ }, "dependencies": { "@auth0/auth0-react": "^2.2.4", + "@clerk/clerk-react": "^5.22.10", "@fortawesome/free-solid-svg-icons": "^6.6.0", "@fortawesome/react-fontawesome": "^0.2.2", "axios": "^1.7.7", + "dotenv": "^16.4.7", "i18next": "^23.15.1", "i18next-browser-languagedetector": "^8.0.0", "i18next-http-backend": "^1.4.5", diff --git a/deurdu/client/postcss.config.js b/client/postcss.config.js similarity index 98% rename from deurdu/client/postcss.config.js rename to client/postcss.config.js index 2e7af2b..b4a6220 100644 --- a/deurdu/client/postcss.config.js +++ b/client/postcss.config.js @@ -4,3 +4,4 @@ export default { autoprefixer: {}, }, } + diff --git a/deurdu/client/public/locales/de/translation.json b/client/public/locales/de/translation.json similarity index 100% rename from deurdu/client/public/locales/de/translation.json rename to client/public/locales/de/translation.json diff --git a/deurdu/client/public/locales/en/translation.json b/client/public/locales/en/translation.json similarity index 100% rename from deurdu/client/public/locales/en/translation.json rename to client/public/locales/en/translation.json diff --git a/deurdu/client/public/locales/ur/translation.json b/client/public/locales/ur/translation.json similarity index 100% rename from deurdu/client/public/locales/ur/translation.json rename to client/public/locales/ur/translation.json diff --git a/client/src/.env b/client/src/.env new file mode 100644 index 0000000..b1a434f --- /dev/null +++ b/client/src/.env @@ -0,0 +1,3 @@ +# VITE_AUTH0_DOMAIN_ID = dev-2mlojserqc0ytr8b.us.auth0.com +# VITE_AUTH0_CLIENT_ID = NyjTsxGX6tAxOxKL6JkjzR6hY7MKhHn4 +VITE_CLERK_PUBLISHABLE_KEY=pk_test_c2VjdXJlLXR1cnRsZS02Mi5jbGVyay5hY2NvdW50cy5kZXYk \ No newline at end of file diff --git a/client/src/App.jsx b/client/src/App.jsx new file mode 100644 index 0000000..1438899 --- /dev/null +++ b/client/src/App.jsx @@ -0,0 +1,35 @@ +import { BrowserRouter, Route, Routes, Navigate } from 'react-router-dom'; +import Home from "./Pages/Home"; +import PostBlog from "./Pages/PostBlog"; +import SearchBlog from "./Pages/SearchBlog"; +import BlogCategoryPage from "./Pages/blogCategoryPage"; +import i18 from 'i18next'; // Ensure i18next is imported correctly +import Login from "./Pages/Login"; +import Register from "./Pages/Register"; +import ContactUs from "./Pages/ContactUs"; +import AboutUs from "./Pages/AboutUs"; + +function App() { + // Fallback route to redirect to default language 'en' if i18.language is not set + const language = i18.language || 'en'; // Default to 'en' if i18.language is not available + + return ( + + + {/* Fallback route to redirect to default language if it's not set */} + } /> + + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + + + ); +} + +export default App; diff --git a/deurdu/client/src/Components/Cards.jsx b/client/src/Components/Cards.jsx similarity index 93% rename from deurdu/client/src/Components/Cards.jsx rename to client/src/Components/Cards.jsx index 7cb22fe..4822ead 100644 --- a/deurdu/client/src/Components/Cards.jsx +++ b/client/src/Components/Cards.jsx @@ -1,6 +1,7 @@ import React from 'react' const Cards = ({img,title,description}) => { + // https://tailwindcss.com/docs/display return (
Placeholder Image diff --git a/deurdu/client/src/Components/Footer.jsx b/client/src/Components/Footer.jsx similarity index 100% rename from deurdu/client/src/Components/Footer.jsx rename to client/src/Components/Footer.jsx diff --git a/deurdu/client/src/Components/ImagesSlidebar.jsx b/client/src/Components/ImagesSlidebar.jsx similarity index 89% rename from deurdu/client/src/Components/ImagesSlidebar.jsx rename to client/src/Components/ImagesSlidebar.jsx index 7895d19..0e57de5 100644 --- a/deurdu/client/src/Components/ImagesSlidebar.jsx +++ b/client/src/Components/ImagesSlidebar.jsx @@ -21,6 +21,7 @@ const ImagesSlidebar = () => { return (
+ {/* https://www.digitalocean.com/community/tutorials/4-uses-of-javascripts-arraymap-you-should-know - usage of .map() */} {fadeImages.map((fadeImage, index) => (
{`Slide diff --git a/deurdu/client/src/Components/InputBar.jsx b/client/src/Components/InputBar.jsx similarity index 100% rename from deurdu/client/src/Components/InputBar.jsx rename to client/src/Components/InputBar.jsx diff --git a/client/src/Components/Navbar.jsx b/client/src/Components/Navbar.jsx new file mode 100644 index 0000000..79abce9 --- /dev/null +++ b/client/src/Components/Navbar.jsx @@ -0,0 +1,167 @@ +import React, { useState,useEffect } from 'react'; +import { Link, useNavigate } from 'react-router-dom'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faGlobe } from '@fortawesome/free-solid-svg-icons'; +import i18n from 'i18next'; +import i18 from '../i18.js'; +import { useTranslation } from 'react-i18next'; + +const Navbar = () => { + const [dropdownVisible, setDropdownVisible] = useState(false); + const { t } = useTranslation(); + const navigate = useNavigate(); + + const { ul1, ul2, ul3, ul4, ul5 } = t('ulTranslation'); + const { login, register, logOut } = t('logRegTranslation'); + const { Technology, Music, Literature, Politics, Culture } = t('blogTopics'); + + useEffect(() => { + const savedLang = localStorage.getItem('i18nextLng') || 'en'; + i18.changeLanguage(savedLang); + }, []); + + + const handleLanguageChange = (lang) => { + const currentPath = window.location.pathname; + const newPath = currentPath.replace(`/${i18n.language}`, `/${lang}`); + + i18n.changeLanguage(lang); // Change the language + localStorage.setItem('i18nextLng', lang); // Store the language + navigate(newPath); // Navigate to the new path + setDropdownVisible(false); // Hide the dropdown after changing language + }; + + return ( +
+ {/* First Navbar */} +
+
+
+ +

DeUrdu

+ + +

ڈردو

+ +
+
+ {/* Globe button to toggle language dropdown */} + + + {/* Language Dropdown */} + {dropdownVisible && ( +
+ {i18n.language !== 'en' && ( + + )} + {i18n.language !== 'de' && ( + + )} + {i18n.language !== 'ur' && ( + + )} +
+ )} +
+ + + +
+
+
+
+ + {/* Second Navbar */} +
+
+ + + + + + + + + +
+
+ + + + + + +
+
+ + {/* Third Navbar */} +
+ + + + + + + + + + + + + + + +
+
+
+ ); +}; + +export default Navbar; diff --git a/client/src/Pages/AboutUs.jsx b/client/src/Pages/AboutUs.jsx new file mode 100644 index 0000000..36332e0 --- /dev/null +++ b/client/src/Pages/AboutUs.jsx @@ -0,0 +1,13 @@ +import React from 'react' +import Navbar from '../Components/Navbar' + +const AboutUs = () => { + return ( +
+ + AboutUs +
+ ) +} + +export default AboutUs \ No newline at end of file diff --git a/client/src/Pages/ContactUs.jsx b/client/src/Pages/ContactUs.jsx new file mode 100644 index 0000000..bd8c2be --- /dev/null +++ b/client/src/Pages/ContactUs.jsx @@ -0,0 +1,13 @@ +import React from 'react' +import Navbar from '../Components/Navbar' + +const ContactUs = () => { + return ( +
+ + ContactUs +
+ ) +} + +export default ContactUs \ No newline at end of file diff --git a/deurdu/client/src/Pages/Home.jsx b/client/src/Pages/Home.jsx similarity index 93% rename from deurdu/client/src/Pages/Home.jsx rename to client/src/Pages/Home.jsx index afd6e67..b5273e1 100644 --- a/deurdu/client/src/Pages/Home.jsx +++ b/client/src/Pages/Home.jsx @@ -12,16 +12,18 @@ const Home = () => { fetchBlogs(); }, []); + // Function to fetch recent blogs (limit 20) // Function to fetch recent blogs (limit 20) const fetchBlogs = () => { axios.get(`http://localhost:3030/blogs?limit=20`) .then(response => { - console.log('Blogs received on frontend: ', response.data.result); - setBlogs(response.data.result || []); + console.log('Blogs received on frontend: ', response.data.blogs); // Updated here + setBlogs(response.data.blogs || []); }) .catch(error => console.error('Error fetching blogs: ', error)); }; + return ( <> @@ -55,4 +57,4 @@ const Home = () => { ); }; -export default Home; +export default Home; \ No newline at end of file diff --git a/client/src/Pages/Login.jsx b/client/src/Pages/Login.jsx new file mode 100644 index 0000000..43b3795 --- /dev/null +++ b/client/src/Pages/Login.jsx @@ -0,0 +1,16 @@ +import React from 'react'; +import Navbar from '../Components/Navbar'; +import { SignIn } from '@clerk/clerk-react'; + +const Login = () => { + return ( +
+ +
+ +
+
+ ); +}; + +export default Login; \ No newline at end of file diff --git a/deurdu/client/src/Pages/PostBlog.jsx b/client/src/Pages/PostBlog.jsx similarity index 99% rename from deurdu/client/src/Pages/PostBlog.jsx rename to client/src/Pages/PostBlog.jsx index d80283b..5fdf7ed 100644 --- a/deurdu/client/src/Pages/PostBlog.jsx +++ b/client/src/Pages/PostBlog.jsx @@ -1,7 +1,6 @@ import React, { useState } from 'react'; import Navbar from '../Components/Navbar'; import Footer from '../Components/Footer'; -// import InputBar from '../Components/InputBar'; import { useTranslation } from 'react-i18next'; import axios from 'axios'; import { useAuth0 } from '@auth0/auth0-react'; diff --git a/deurdu/client/src/Pages/Register.jsx b/client/src/Pages/Register.jsx similarity index 100% rename from deurdu/client/src/Pages/Register.jsx rename to client/src/Pages/Register.jsx diff --git a/client/src/Pages/SearchBlog.jsx b/client/src/Pages/SearchBlog.jsx new file mode 100644 index 0000000..55d72a3 --- /dev/null +++ b/client/src/Pages/SearchBlog.jsx @@ -0,0 +1,170 @@ +import React, { useEffect, useState } from 'react'; +import Navbar from '../Components/Navbar'; +import Footer from '../Components/Footer'; +import axios from 'axios'; + +const SearchBlog = () => { + const [blogs, setBlogs] = useState([]); + const [authorName, setAuthorName] = useState(''); + const [category, setCategory] = useState(''); + const [language, setLanguage] = useState(''); + const [topAuthors, setTopAuthors] = useState([]); + const [selectedAuthor, setSelectedAuthor] = useState(""); + + // Fetch all blogs initially + useEffect(() => { + fetchBlogs(); + }, []); + + // Fetch top authors when the component loads + useEffect(() => { + axios.get("http://localhost:3030/blogs/top-authors") + .then(response => setTopAuthors(response.data)) + .catch(error => console.error("Error fetching authors:", error)); + }, []); + + // Function to fetch blogs based on filters + const fetchBlogs = (filterCategory = '', filterAuthor = '', filterLanguage = '') => { + axios + .get(`http://localhost:3030/blogs/search`, { + params: { + category: filterCategory, + author_name: filterAuthor, + language: filterLanguage, + limit: 20, + }, + }) + .then((response) => { + console.log('Blogs received on frontend: ', response.data.blogs); + setBlogs(response.data.blogs || []); + }) + .catch((error) => console.error('Error fetching blogs: ', error)); + }; + + // Handle Category change + const handleCategoryChange = (event) => { + const selectedCategory = event.target.value; + setCategory(selectedCategory); + fetchBlogs(selectedCategory, authorName, language); + }; + + // Handle Author name change from text input + const handleAuthorChange = (event) => { + const name = event.target.value; + setAuthorName(name); + fetchBlogs(category, name, language); + }; + + // Handle Language change + const handleLanguageChange = (event) => { + const selectedLanguage = event.target.value; + setLanguage(selectedLanguage); + fetchBlogs(category, authorName, selectedLanguage); + }; + + // Handle Top Author selection from dropdown + const handleSelectedAuthorChange = (event) => { + const author = event.target.value; + setSelectedAuthor(author); + fetchBlogs(category, author, language); // Fetch blogs of the selected top author + }; + + return ( + <> + + + {/* Input fields for filtering */} +
+ {/* Author Name Input */} +
+ + +
+ +
+ + +
+ + {/* Blog Category Dropdown */} +
+ + +
+ + {/* Blog Language Dropdown */} +
+ + +
+
+ + {/* Displaying the Blogs */} +
+ {blogs.length > 0 ? ( + blogs.map((blog) => ( +
+ Blog Image +
+

{blog.blog_title}

+
+
{blog.blog_content}
+
+

+ Blog language: {blog.blog_language} +

+

+ Author Name: {blog.author_name} +

+

+ Blog Category: {blog.blog_category} +

+ +
+ )) + ) : ( +

No blogs available

+ )} +
+ +