Merged frontend-multilingual branch. This project is now multilingual!
Currently supports the following languages: - English - Hindi - Marathi - French
This commit is contained in:
@@ -13,10 +13,12 @@
|
||||
"@reduxjs/toolkit": "^2.6.0",
|
||||
"@tailwindcss/vite": "^4.0.9",
|
||||
"axios": "^1.8.4",
|
||||
"i18next": "^25.2.1",
|
||||
"lucide-react": "^0.476.0",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"react-hot-toast": "^2.5.2",
|
||||
"react-i18next": "^15.5.3",
|
||||
"react-icons": "^5.5.0",
|
||||
"react-redux": "^9.2.0",
|
||||
"react-router-dom": "^7.2.0"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import "./App.css";
|
||||
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
|
||||
import LanguageSwitcher from './components/LanguageSwitcher'; // Language switcher dropdown menu
|
||||
import Login from "./pages/Authentication/Login";
|
||||
import SignUp from "./pages/Authentication/SignUp";
|
||||
import DrivethruLandingPage from "./pages/UserPages/DrivethruLandingPage";
|
||||
@@ -9,6 +10,7 @@ import NotFoundPage from "./pages/UserPages/NotFoundPage";
|
||||
function App() {
|
||||
return (
|
||||
<Router>
|
||||
<LanguageSwitcher />
|
||||
<Routes>
|
||||
<Route path="/" element={<DrivethruLandingPage />} />
|
||||
<Route path="/login" element={<Login />} />
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
# TRANSLATION
|
||||
|
||||
This is a comprehensive guide for translation for those who wish to contribute in any language.
|
||||
|
||||
---
|
||||
|
||||
## 1. Add Your Language JSON File
|
||||
|
||||
- Go to the `Frontend/src/locales/` directory.
|
||||
- Copy an existing language file (e.g., `en.json`) and rename it to your language code (e.g., `es.json` for Spanish, `de.json` for German).
|
||||
- Translate all the key-value pairs in your new file.
|
||||
|
||||
**Example:**
|
||||
|
||||
```shell
|
||||
cp Frontend/src/locales/en.json Frontend/src/locales/es.json
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"skycrate": "Skycrate",
|
||||
"hero_subtitle": "Store, Access & Share Your Files — Anytime, Anywhere!",
|
||||
}
|
||||
```
|
||||
|
||||
## 2. Register the Language in `Frontend/src/i18n.js`
|
||||
|
||||
- Open `Frontend/src/i18n.js`.
|
||||
- Import your new JSON file:
|
||||
|
||||
```js
|
||||
import en from './locales/en.json';
|
||||
import fr from './locales/fr.json';
|
||||
// import more languages as needed
|
||||
import es from './locales/es.json'; // <-- Add this line
|
||||
|
||||
const resources = {
|
||||
en: { translation: en },
|
||||
fr: { translation: fr },
|
||||
// add other languages here
|
||||
es: { translation: es }, // <-- Add this line
|
||||
};
|
||||
```
|
||||
|
||||
## 3. Update the Language Switcher
|
||||
|
||||
- Open `Frontend/src/components/LanguageSwitcher.jsx`.
|
||||
- Add your language to the `languages` array:
|
||||
|
||||
|
||||
```js
|
||||
const languages = [
|
||||
{ code: 'en', label: 'English' },
|
||||
{ code: 'fr', label: 'Français' },
|
||||
// Add more languages as needed
|
||||
{ code: 'es', label: 'Spanish' }, // <-- Add this line
|
||||
];
|
||||
```
|
||||
|
||||
## 4. Test Your Translation
|
||||
|
||||
- Start the app.
|
||||
- Use the language switcher to select your new language.
|
||||
- Check all pages for missing or untranslated keys.
|
||||
- If you see a key instead of a translation, add it to your JSON file.
|
||||
|
||||
## 5. Submit Your Contribution
|
||||
|
||||
- Double-check your translations for accuracy and completeness.
|
||||
- Commit your changes to:
|
||||
- `Frontend/src/locales/<your_language>.json`
|
||||
- `Frontend/src/i18n.js`
|
||||
- `Frontend/src/components/LanguageSwitcher.jsx`
|
||||
- Open a pull request with a description of your contribution.
|
||||
|
||||
---
|
||||
|
||||
## Thank you for making Skycrate accessible to more people!
|
||||
@@ -8,15 +8,17 @@ import {
|
||||
Phone,
|
||||
MapPin,
|
||||
} from "lucide-react";
|
||||
import { useTranslation } from "react-i18next"; // for multilinguality
|
||||
|
||||
const Footer = () => {
|
||||
const { t } = useTranslation(); // for multilinguality
|
||||
const [email, setEmail] = useState("");
|
||||
|
||||
//Currently storing user email in localstorage
|
||||
const handleSubscribe = () => {
|
||||
if (email.trim() !== "") {
|
||||
localStorage.setItem("subscribedEmail", email);
|
||||
alert("You have successfully subscribed!");
|
||||
alert(t("subscribe_success"));
|
||||
setEmail("");
|
||||
}
|
||||
};
|
||||
@@ -42,10 +44,10 @@ const Footer = () => {
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<h3 className="text-2xl font-bold text-white">Skycrate</h3>
|
||||
<h3 className="text-2xl font-bold text-white">{t("footer_brand")}</h3>
|
||||
</div>
|
||||
<p className="text-white/90">
|
||||
Your secure cloud storage solution for all your digital needs.
|
||||
{t("footer_tagline")}
|
||||
</p>
|
||||
<div className="flex space-x-4">
|
||||
<a
|
||||
@@ -81,14 +83,14 @@ const Footer = () => {
|
||||
|
||||
{/* Quick Links */}
|
||||
<div>
|
||||
<h4 className="font-semibold text-white mb-4">Quick Links</h4>
|
||||
<h4 className="font-semibold text-white mb-4">{t("footer_quick_links")}</h4>
|
||||
<ul className="space-y-2">
|
||||
<li>
|
||||
<a
|
||||
href="#about"
|
||||
className="text-white/90 hover:text-white transition-all duration-200 hover:translate-x-1 inline-block"
|
||||
>
|
||||
About Us
|
||||
{t("footer_about_us")}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
@@ -96,7 +98,7 @@ const Footer = () => {
|
||||
href="#features"
|
||||
className="text-white/90 hover:text-white transition-all duration-200 hover:translate-x-1 inline-block"
|
||||
>
|
||||
Features
|
||||
{t("footer_features")}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
@@ -104,7 +106,7 @@ const Footer = () => {
|
||||
href="#howItWorks"
|
||||
className="text-white/90 hover:text-white transition-all duration-200 hover:translate-x-1 inline-block"
|
||||
>
|
||||
How It Works
|
||||
{t("footer_how_it_works")}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -112,43 +114,42 @@ const Footer = () => {
|
||||
|
||||
{/* Contact Info */}
|
||||
<div>
|
||||
<h4 className="font-semibold text-white mb-4">Contact</h4>
|
||||
<h4 className="font-semibold text-white mb-4">{t("footer_contact")}</h4>
|
||||
<ul className="space-y-2">
|
||||
<li className="flex items-center text-white/90 hover:text-white group transition-colors duration-200">
|
||||
<Mail className="w-4 h-4 mr-2 group-hover:text-cyan-200" />
|
||||
support@drivethru.com
|
||||
{t("footer_email")}
|
||||
</li>
|
||||
<li className="flex items-center text-white/90 hover:text-white group transition-colors duration-200">
|
||||
<Phone className="w-4 h-4 mr-2 group-hover:text-cyan-200" />
|
||||
+91 3628206234
|
||||
{t("footer_phone")}
|
||||
</li>
|
||||
<li className="flex items-center text-white/90 hover:text-white group transition-colors duration-200">
|
||||
<MapPin className="w-4 h-4 mr-2 group-hover:text-cyan-200" />
|
||||
123 Cloud Street, Digital City
|
||||
{t("footer_address")}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* Newsletter */}
|
||||
<div>
|
||||
<h4 className="font-semibold text-white mb-4">Stay Updated</h4>
|
||||
<h4 className="font-semibold text-white mb-4">{t("footer_newsletter_title")}</h4>
|
||||
<p className="text-white/90 mb-4">
|
||||
Get exclusive tips, updates on new features, and special offers
|
||||
directly in your inbox.
|
||||
{t("footer_newsletter_desc")}
|
||||
</p>
|
||||
<div className="space-y-4">
|
||||
<input
|
||||
type="email"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
placeholder="Enter your email"
|
||||
placeholder={t("footer_newsletter_placeholder")}
|
||||
className="w-full px-4 py-2 rounded-md bg-white/10 border border-white/20 text-white placeholder:text-white/50 focus:bg-white/20 transition-all duration-200 outline-none focus:ring-2 focus:ring-white/30"
|
||||
/>
|
||||
<button
|
||||
onClick={handleSubscribe}
|
||||
className="w-full px-4 py-2 rounded-md bg-white text-blue-600 font-medium hover:bg-opacity-90 transition-all duration-200 transform hover:scale-105"
|
||||
>
|
||||
Subscribe to Newsletter
|
||||
{t("footer_newsletter_button")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -158,25 +159,25 @@ const Footer = () => {
|
||||
|
||||
{/* Bottom Section */}
|
||||
<div className="flex flex-col md:flex-row justify-between items-center text-white/90 text-sm">
|
||||
<p>© {new Date().getFullYear()} Skycrate. All rights reserved.</p>
|
||||
<p>© {new Date().getFullYear()} {t("footer_brand")}. {t("footer_rights")}</p>
|
||||
<div className="flex gap-4 mt-4 md:mt-0">
|
||||
<a
|
||||
href="#"
|
||||
className="hover:text-white transition-all duration-200 hover:translate-x-1 inline-block"
|
||||
>
|
||||
Privacy Policy
|
||||
{t("footer_privacy_policy")}
|
||||
</a>
|
||||
<a
|
||||
href="#"
|
||||
className="hover:text-white transition-all duration-200 hover:translate-x-1 inline-block"
|
||||
>
|
||||
Terms of Service
|
||||
{t("footer_terms_of_service")}
|
||||
</a>
|
||||
<a
|
||||
href="#"
|
||||
className="hover:text-white transition-all duration-200 hover:translate-x-1 inline-block"
|
||||
>
|
||||
Cookie Policy
|
||||
{t("footer_cookie_policy")}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const languages = [
|
||||
{ code: 'en', label: 'English' },
|
||||
{ code: 'hi', label: 'Hindi (हिंदी)' },
|
||||
{ code: 'mr', label: 'Marathi (मराठी)' },
|
||||
{ code: 'fr', label: 'French (Français)' },
|
||||
// Add more languages as needed
|
||||
];
|
||||
|
||||
function LanguageSwitcher() {
|
||||
const { i18n } = useTranslation();
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: '1rem',
|
||||
right: '1rem',
|
||||
zIndex: 1000,
|
||||
background: 'rgba(255,255,255,0.95)',
|
||||
borderRadius: '4px',
|
||||
padding: '0.25em 0.5em',
|
||||
boxShadow: '0 2px 8px rgba(0,0,0,0.05)'
|
||||
}}
|
||||
>
|
||||
<select
|
||||
value={i18n.language}
|
||||
onChange={e => i18n.changeLanguage(e.target.value)}
|
||||
style={{
|
||||
border: '1px solid #ccc',
|
||||
borderRadius: '4px',
|
||||
padding: '0.25em 2em 0.25em 0.5em', // More right padding for arrow
|
||||
minWidth: '100px',
|
||||
appearance: 'auto', // Use browser default arrow
|
||||
background: '#fff',
|
||||
}}
|
||||
|
||||
aria-label="Select language"
|
||||
>
|
||||
{languages.map(lang => (
|
||||
<option key={lang.code} value={lang.code}>
|
||||
{lang.label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default LanguageSwitcher;
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
import { useState, useEffect, useRef } from "react";
|
||||
import { Link, useNavigate } from "react-router-dom";
|
||||
import { toast } from "react-hot-toast";
|
||||
import { useTranslation } from "react-i18next"; // for multilinguality
|
||||
|
||||
const Sidebar = () => {
|
||||
const { t } = useTranslation(); // for multilinguality
|
||||
const navigate = useNavigate(); // Hook for programmatic navigation
|
||||
const [userMenuOpen, setUserMenuOpen] = useState(false);
|
||||
const menuRef = useRef();
|
||||
|
||||
// Show loading toast and perform logout
|
||||
const handleLogout = () => {
|
||||
const loadingToast = toast.loading("Logging out...");
|
||||
const loadingToast = toast.loading(t("sidebar_logging_out"));
|
||||
|
||||
// Simulate a delay (for example, network request)
|
||||
setTimeout(() => {
|
||||
@@ -23,7 +25,7 @@ const Sidebar = () => {
|
||||
|
||||
// Show success toast after logout
|
||||
toast.update(loadingToast, {
|
||||
render: "Logged out successfully!",
|
||||
render: t("sidebar_logged_out"),
|
||||
type: "success",
|
||||
isLoading: false,
|
||||
autoClose: 2000,
|
||||
@@ -55,7 +57,7 @@ const Sidebar = () => {
|
||||
type="button"
|
||||
className="inline-flex items-center p-2 text-lg text-white rounded-lg sm:hidden hover:bg-[#37A0EA] focus:outline-none"
|
||||
>
|
||||
<span className="sr-only">Open sidebar</span>
|
||||
<span className="sr-only">{t("sidebar_open_sidebar")}</span>
|
||||
<svg className="w-6 h-6" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path
|
||||
clipRule="evenodd"
|
||||
@@ -67,7 +69,7 @@ const Sidebar = () => {
|
||||
<Link to="/" className="flex ms-2 md:me-24">
|
||||
<img src="./image.png" className="h-8 me-3" alt="Skycrate Logo" />
|
||||
<span className="self-center text-xl font-semibold sm:text-2xl whitespace-nowrap">
|
||||
Skycrate
|
||||
{t("sidebar_brand")}
|
||||
</span>
|
||||
</Link>
|
||||
</div>
|
||||
@@ -84,11 +86,11 @@ const Sidebar = () => {
|
||||
onClick={() => setUserMenuOpen((o) => !o)}
|
||||
className="flex text-lg bg-gray-800 rounded-full focus:ring-4 focus:ring-gray-300"
|
||||
>
|
||||
<span className="sr-only">Open user menu</span>
|
||||
<span className="sr-only">{t("sidebar_open_user_menu")}</span>
|
||||
<img
|
||||
className="w-8 h-8 rounded-full"
|
||||
src="https://flowbite.com/docs/images/people/profile-picture-5.jpg"
|
||||
alt="User Photo"
|
||||
alt={t("sidebar_user_photo")}
|
||||
/>
|
||||
</button>
|
||||
|
||||
@@ -109,7 +111,7 @@ const Sidebar = () => {
|
||||
className="w-full text-left px-4 py-2 text-lg text-white hover:bg-[#37A0EA]"
|
||||
role="menuitem"
|
||||
>
|
||||
Log out
|
||||
{t("sidebar_logout")}
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -139,7 +141,7 @@ const Sidebar = () => {
|
||||
>
|
||||
<path d="M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.62L12 2 9.19 8.62 2 9.24l5.46 4.73L5.82 21z" />
|
||||
</svg>
|
||||
<span className="ms-3">Starred</span>
|
||||
<span className="ms-3">{t("sidebar_starred")}</span>
|
||||
</Link>
|
||||
</li>
|
||||
{/* ...additional sidebar items... */}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import i18n from 'i18next';
|
||||
import { initReactI18next } from 'react-i18next';
|
||||
|
||||
import en from './locales/en.json';
|
||||
import hi from './locales/hi.json';
|
||||
import mr from './locales/mr.json';
|
||||
import fr from './locales/fr.json';
|
||||
// import more languages as needed
|
||||
|
||||
const resources = {
|
||||
en: { translation: en },
|
||||
hi: { translation: hi },
|
||||
mr: { translation: mr },
|
||||
fr: { translation: fr },
|
||||
// add other languages here
|
||||
};
|
||||
|
||||
i18n
|
||||
.use(initReactI18next)
|
||||
.init({
|
||||
resources,
|
||||
lng: 'en', // default language
|
||||
fallbackLng: 'en',
|
||||
//interpolation: {
|
||||
// escapeValue: false, // not needed for React
|
||||
//},
|
||||
});
|
||||
|
||||
export default i18n;
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
{
|
||||
"dashboard": "Dashboard",
|
||||
"failed_to_load_files": "Failed to load files. Please try again later.",
|
||||
|
||||
"skycrate": "Skycrate",
|
||||
"hero_subtitle": "Store, Access & Share Your Files — Anytime, Anywhere!",
|
||||
"hero_desc": "A simple, secure, and fast cloud storage solution for all your files. Upload, organize, and access with ease.",
|
||||
"get_started": "Get Started",
|
||||
"login": "Login",
|
||||
"key_features": "Key Features",
|
||||
"feature_easy_upload_title": "Easy Upload & Access",
|
||||
"feature_easy_upload_desc": "Drag & drop, instant access.",
|
||||
"feature_secure_title": "Secure & Private",
|
||||
"feature_secure_desc": "End-to-end encryption.",
|
||||
"feature_sharing_title": "Seamless Sharing",
|
||||
"feature_sharing_desc": "Share files with one click.",
|
||||
"feature_access_anywhere_title": "Access Anywhere",
|
||||
"feature_access_anywhere_desc": "Works on all devices.",
|
||||
"how_it_works": "How It Works",
|
||||
"how_create_account_title": "Create an account",
|
||||
"how_create_account_desc": "Sign up in seconds.",
|
||||
"how_upload_files_title": "Upload files",
|
||||
"how_upload_files_desc": "Drag & drop or select from your device.",
|
||||
"how_manage_files_title": "Manage files",
|
||||
"how_manage_files_desc": "Rename, move, or delete easily.",
|
||||
"how_access_anytime_title": "Access anytime",
|
||||
"how_access_anytime_desc": "Open files from any device.",
|
||||
|
||||
"not_found_title": "Page Not Found",
|
||||
"not_found_description": "Sorry, we couldn't find the page you were looking for. It may have been moved or deleted.",
|
||||
"go_home": "Go Home",
|
||||
|
||||
"login_title": "Log in",
|
||||
"email_placeholder": "Enter your email",
|
||||
"password_placeholder": "Enter your password",
|
||||
"forgot_password": "Forgot password?",
|
||||
"logging_in": "Logging In...",
|
||||
"login": "Login",
|
||||
"dont_have_account": "Don’t have an account?",
|
||||
"sign_up": "Sign up",
|
||||
"login_successful": "Login successful!",
|
||||
"login_failed": "Login failed.",
|
||||
"an_error_occurred": "An error occurred. Please try again.",
|
||||
"logging_in_toast": "Logging in...",
|
||||
|
||||
"signup_title": "Sign Up",
|
||||
"first_name": "First Name",
|
||||
"last_name": "Last Name",
|
||||
"email_placeholder": "Enter your email",
|
||||
"password_placeholder": "Enter your password",
|
||||
"confirm_password_placeholder": "Confirm your password",
|
||||
"signing_up": "Signing Up...",
|
||||
"sign_up": "Sign Up",
|
||||
"already_have_account": "Already have an account?",
|
||||
"login": "Login",
|
||||
"passwords_do_not_match": "Passwords do not match.",
|
||||
"registering": "Registering...",
|
||||
"signup_failed": "Signup failed.",
|
||||
"folder_creation_failed": "Failed to create user folder.",
|
||||
"signup_success": "Successfully registered and folder created!",
|
||||
"an_error_occurred": "An error occurred. Please try again.",
|
||||
|
||||
"footer_brand": "Skycrate",
|
||||
"footer_tagline": "Your secure cloud storage solution for all your digital needs.",
|
||||
"footer_quick_links": "Quick Links",
|
||||
"footer_about_us": "About Us",
|
||||
"footer_features": "Features",
|
||||
"footer_how_it_works": "How It Works",
|
||||
"footer_contact": "Contact",
|
||||
"footer_email": "support@drivethru.com",
|
||||
"footer_phone": "+91 3628206234",
|
||||
"footer_address": "123 Cloud Street, Digital City",
|
||||
"footer_newsletter_title": "Stay Updated",
|
||||
"footer_newsletter_desc": "Get exclusive tips, updates on new features, and special offers directly in your inbox.",
|
||||
"footer_newsletter_placeholder": "Enter your email",
|
||||
"footer_newsletter_button": "Subscribe to Newsletter",
|
||||
"subscribe_success": "You have successfully subscribed!",
|
||||
"footer_rights": "All rights reserved.",
|
||||
"footer_privacy_policy": "Privacy Policy",
|
||||
"footer_terms_of_service": "Terms of Service",
|
||||
"footer_cookie_policy": "Cookie Policy",
|
||||
|
||||
"sidebar_logging_out": "Logging out...",
|
||||
"sidebar_logged_out": "Logged out successfully!",
|
||||
"sidebar_open_sidebar": "Open sidebar",
|
||||
"sidebar_brand": "Skycrate",
|
||||
"sidebar_open_user_menu": "Open user menu",
|
||||
"sidebar_user_photo": "User Photo",
|
||||
"sidebar_logout": "Log out",
|
||||
"sidebar_starred": "Starred"
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
{
|
||||
"dashboard": "Tableau de bord",
|
||||
"failed_to_load_files": "Échec du chargement des fichiers. Veuillez réessayer plus tard.",
|
||||
|
||||
"skycrate": "Skycrate",
|
||||
"hero_subtitle": "Stockez, accédez et partagez vos fichiers — à tout moment, partout !",
|
||||
"hero_desc": "Une solution de stockage cloud simple, sécurisée et rapide pour tous vos fichiers. Téléchargez, organisez et accédez facilement.",
|
||||
"get_started": "Commencer",
|
||||
"login": "Connexion",
|
||||
"key_features": "Fonctionnalités clés",
|
||||
"feature_easy_upload_title": "Téléversement et accès faciles",
|
||||
"feature_easy_upload_desc": "Glissez-déposez, accès instantané.",
|
||||
"feature_secure_title": "Sécurisé et privé",
|
||||
"feature_secure_desc": "Chiffrement de bout en bout.",
|
||||
"feature_sharing_title": "Partage sans effort",
|
||||
"feature_sharing_desc": "Partagez des fichiers en un clic.",
|
||||
"feature_access_anywhere_title": "Accès partout",
|
||||
"feature_access_anywhere_desc": "Fonctionne sur tous les appareils.",
|
||||
"how_it_works": "Comment ça marche",
|
||||
"how_create_account_title": "Créer un compte",
|
||||
"how_create_account_desc": "Inscrivez-vous en quelques secondes.",
|
||||
"how_upload_files_title": "Téléverser des fichiers",
|
||||
"how_upload_files_desc": "Glissez-déposez ou sélectionnez depuis votre appareil.",
|
||||
"how_manage_files_title": "Gérer les fichiers",
|
||||
"how_manage_files_desc": "Renommez, déplacez ou supprimez facilement.",
|
||||
"how_access_anytime_title": "Accès à tout moment",
|
||||
"how_access_anytime_desc": "Ouvrez des fichiers depuis n'importe quel appareil.",
|
||||
|
||||
"not_found_title": "Page non trouvée",
|
||||
"not_found_description": "Désolé, nous n'avons pas pu trouver la page que vous cherchiez. Elle a peut-être été déplacée ou supprimée.",
|
||||
"go_home": "Accueil",
|
||||
|
||||
"login_title": "Connexion",
|
||||
"email_placeholder": "Entrez votre e-mail",
|
||||
"password_placeholder": "Entrez votre mot de passe",
|
||||
"forgot_password": "Mot de passe oublié ?",
|
||||
"logging_in": "Connexion...",
|
||||
"login": "Connexion",
|
||||
"dont_have_account": "Vous n'avez pas de compte ?",
|
||||
"sign_up": "S'inscrire",
|
||||
"login_successful": "Connexion réussie !",
|
||||
"login_failed": "Échec de la connexion.",
|
||||
"an_error_occurred": "Une erreur s'est produite. Veuillez réessayer.",
|
||||
"logging_in_toast": "Connexion en cours...",
|
||||
|
||||
"sign_up": "S'inscrire",
|
||||
"first_name": "Prénom",
|
||||
"last_name": "Nom de famille",
|
||||
"email_placeholder": "Entrez votre e-mail",
|
||||
"password_placeholder": "Entrez votre mot de passe",
|
||||
"confirm_password_placeholder": "Confirmez votre mot de passe",
|
||||
"already_have_account": "Vous avez déjà un compte ?",
|
||||
"login": "Connexion",
|
||||
"signing_up": "Inscription...",
|
||||
"passwords_do_not_match": "Les mots de passe ne correspondent pas.",
|
||||
"registering": "Enregistrement...",
|
||||
"signup_failed": "Échec de l'inscription.",
|
||||
"failed_create_folder": "Échec de la création du dossier utilisateur.",
|
||||
"signup_success": "Inscription réussie et dossier créé !",
|
||||
"an_error_occurred": "Une erreur s'est produite. Veuillez réessayer.",
|
||||
|
||||
"footer_brand": "Skycrate",
|
||||
"footer_tagline": "Votre solution de stockage cloud sécurisée pour tous vos besoins numériques.",
|
||||
"footer_quick_links": "Liens rapides",
|
||||
"footer_about_us": "À propos",
|
||||
"footer_features": "Fonctionnalités",
|
||||
"footer_how_it_works": "Comment ça marche",
|
||||
"footer_contact": "Contact",
|
||||
"footer_email": "support@drivethru.com",
|
||||
"footer_phone": "+91 3628206234",
|
||||
"footer_address": "123 Rue du Cloud, Ville Digitale",
|
||||
"footer_newsletter_title": "Restez informé",
|
||||
"footer_newsletter_desc": "Recevez des conseils exclusifs, des mises à jour sur les nouvelles fonctionnalités et des offres spéciales directement dans votre boîte de réception.",
|
||||
"footer_newsletter_placeholder": "Entrez votre e-mail",
|
||||
"footer_newsletter_button": "S'abonner à la newsletter",
|
||||
"subscribe_success": "Vous vous êtes abonné avec succès !",
|
||||
"footer_rights": "Tous droits réservés.",
|
||||
"footer_privacy_policy": "Politique de confidentialité",
|
||||
"footer_terms_of_service": "Conditions d'utilisation",
|
||||
"footer_cookie_policy": "Politique relative aux cookies",
|
||||
|
||||
"sidebar_logging_out": "Déconnexion...",
|
||||
"sidebar_logged_out": "Déconnecté avec succès !",
|
||||
"sidebar_open_sidebar": "Ouvrir la barre latérale",
|
||||
"sidebar_brand": "Skycrate",
|
||||
"sidebar_open_user_menu": "Ouvrir le menu utilisateur",
|
||||
"sidebar_user_photo": "Photo de l'utilisateur",
|
||||
"sidebar_logout": "Se déconnecter",
|
||||
"sidebar_starred": "Favoris"
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
{
|
||||
"dashboard": "डैशबोर्ड",
|
||||
"failed_to_load_files": "फ़ाइलें लोड करने में विफल। कृपया बाद में पुनः प्रयास करें।",
|
||||
|
||||
"skycrate": "Skycrate",
|
||||
"hero_subtitle": "अपनी फ़ाइलें संग्रहित करें, एक्सेस करें और साझा करें — कभी भी, कहीं भी!",
|
||||
"hero_desc": "आपकी सभी फ़ाइलों के लिए एक सरल, सुरक्षित और तेज़ क्लाउड स्टोरेज समाधान। अपलोड करें, व्यवस्थित करें और आसानी से एक्सेस करें।",
|
||||
"get_started": "शुरू करें",
|
||||
"login": "लॉगिन",
|
||||
"key_features": "मुख्य विशेषताएं",
|
||||
"feature_easy_upload_title": "सरल अपलोड और एक्सेस",
|
||||
"feature_easy_upload_desc": "ड्रैग और ड्रॉप करें, त्वरित एक्सेस पाएं।",
|
||||
"feature_secure_title": "सुरक्षित और निजी",
|
||||
"feature_secure_desc": "एंड-टू-एंड एन्क्रिप्शन।",
|
||||
"feature_sharing_title": "बिना रुकावट साझाकरण",
|
||||
"feature_sharing_desc": "एक क्लिक में फ़ाइलें साझा करें।",
|
||||
"feature_access_anywhere_title": "कहीं से भी एक्सेस करें",
|
||||
"feature_access_anywhere_desc": "सभी डिवाइस पर कार्य करता है।",
|
||||
|
||||
"how_it_works": "यह कैसे कार्य करता है",
|
||||
"how_create_account_title": "खाता बनाएं",
|
||||
"how_create_account_desc": "कुछ ही सेकंड में साइन अप करें।",
|
||||
"how_upload_files_title": "फ़ाइलें अपलोड करें",
|
||||
"how_upload_files_desc": "ड्रैग और ड्रॉप करें या डिवाइस से चुनें।",
|
||||
"how_manage_files_title": "फ़ाइलें प्रबंधित करें",
|
||||
"how_manage_files_desc": "आसानी से नाम बदलें, स्थानांतरित करें या हटाएं।",
|
||||
"how_access_anytime_title": "कभी भी एक्सेस करें",
|
||||
"how_access_anytime_desc": "किसी भी डिवाइस से फ़ाइलें खोलें।",
|
||||
|
||||
"not_found_title": "पृष्ठ नहीं मिला",
|
||||
"not_found_description": "क्षमा करें, हम वह पृष्ठ नहीं ढूंढ सके जिसे आप खोज रहे थे। यह हटाया गया हो सकता है या स्थानांतरित कर दिया गया हो।",
|
||||
"go_home": "मुख्य पृष्ठ पर जाएं",
|
||||
|
||||
"login_title": "लॉग इन करें",
|
||||
"email_placeholder": "अपना ईमेल दर्ज करें",
|
||||
"password_placeholder": "अपना पासवर्ड दर्ज करें",
|
||||
"forgot_password": "पासवर्ड भूल गए?",
|
||||
"logging_in": "लॉग इन किया जा रहा है...",
|
||||
"login": "लॉगिन",
|
||||
"dont_have_account": "कोई खाता नहीं है?",
|
||||
"sign_up": "साइन अप करें",
|
||||
"login_successful": "सफलतापूर्वक लॉगिन हुआ!",
|
||||
"login_failed": "लॉगिन विफल रहा।",
|
||||
"an_error_occurred": "एक त्रुटि हुई। कृपया पुनः प्रयास करें।",
|
||||
"logging_in_toast": "लॉग इन किया जा रहा है...",
|
||||
|
||||
"signup_title": "साइन अप करें",
|
||||
"first_name": "पहला नाम",
|
||||
"last_name": "अंतिम नाम",
|
||||
"email_placeholder": "अपना ईमेल दर्ज करें",
|
||||
"password_placeholder": "अपना पासवर्ड दर्ज करें",
|
||||
"confirm_password_placeholder": "अपना पासवर्ड पुष्टि करें",
|
||||
"signing_up": "साइन अप किया जा रहा है...",
|
||||
"sign_up": "साइन अप करें",
|
||||
"already_have_account": "पहले से ही खाता है?",
|
||||
"login": "लॉगिन",
|
||||
"passwords_do_not_match": "पासवर्ड मेल नहीं खा रहे हैं।",
|
||||
"registering": "पंजीकरण किया जा रहा है...",
|
||||
"signup_failed": "साइन अप विफल रहा।",
|
||||
"folder_creation_failed": "यूज़र फ़ोल्डर बनाने में विफल।",
|
||||
"signup_success": "सफलतापूर्वक पंजीकरण हुआ और फ़ोल्डर बनाया गया!",
|
||||
"an_error_occurred": "एक त्रुटि हुई। कृपया पुनः प्रयास करें।",
|
||||
|
||||
"footer_brand": "Skycrate",
|
||||
"footer_tagline": "आपकी सभी डिजिटल आवश्यकताओं के लिए सुरक्षित क्लाउड स्टोरेज समाधान।",
|
||||
"footer_quick_links": "त्वरित लिंक",
|
||||
"footer_about_us": "हमारे बारे में",
|
||||
"footer_features": "विशेषताएं",
|
||||
"footer_how_it_works": "यह कैसे कार्य करता है",
|
||||
"footer_contact": "संपर्क करें",
|
||||
"footer_email": "support@drivethru.com",
|
||||
"footer_phone": "+91 3628206234",
|
||||
"footer_address": "123 क्लाउड स्ट्रीट, डिजिटल सिटी",
|
||||
"footer_newsletter_title": "अपडेट प्राप्त करें",
|
||||
"footer_newsletter_desc": "विशेष सुझाव, नई सुविधाओं के अपडेट और ऑफ़र सीधे अपने इनबॉक्स में पाएं।",
|
||||
"footer_newsletter_placeholder": "अपना ईमेल दर्ज करें",
|
||||
"footer_newsletter_button": "न्यूज़लेटर की सदस्यता लें",
|
||||
"subscribe_success": "आपने सफलतापूर्वक सदस्यता ली है!",
|
||||
"footer_rights": "सभी अधिकार सुरक्षित।",
|
||||
"footer_privacy_policy": "गोपनीयता नीति",
|
||||
"footer_terms_of_service": "सेवा की शर्तें",
|
||||
"footer_cookie_policy": "कुकी नीति",
|
||||
|
||||
"sidebar_logging_out": "लॉग आउट किया जा रहा है...",
|
||||
"sidebar_logged_out": "सफलतापूर्वक लॉग आउट हुआ!",
|
||||
"sidebar_open_sidebar": "साइडबार खोलें",
|
||||
"sidebar_brand": "Skycrate",
|
||||
"sidebar_open_user_menu": "उपयोगकर्ता मेनू खोलें",
|
||||
"sidebar_user_photo": "उपयोगकर्ता फोटो",
|
||||
"sidebar_logout": "लॉग आउट",
|
||||
"sidebar_starred": "चिह्नित"
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
{
|
||||
"dashboard": "डॅशबोर्ड",
|
||||
"failed_to_load_files": "फायली लोड करण्यात अयशस्वी. कृपया नंतर पुन्हा प्रयत्न करा.",
|
||||
|
||||
"skycrate": "Skycrate",
|
||||
"hero_subtitle": "तुमच्या फायली साठवा, प्रवेश मिळवा आणि शेअर करा — कधीही, कुठेही!",
|
||||
"hero_desc": "सर्व फायलींसाठी एक साधे, सुरक्षित आणि जलद क्लाऊड स्टोरेज सोल्यूशन. अपलोड करा, व्यवस्थापित करा आणि सहजपणे वापरा.",
|
||||
"get_started": "सुरुवात करा",
|
||||
"login": "लॉगिन",
|
||||
"key_features": "मुख्य वैशिष्ट्ये",
|
||||
"feature_easy_upload_title": "सोपे अपलोड आणि प्रवेश",
|
||||
"feature_easy_upload_desc": "ड्रॅग आणि ड्रॉप, त्वरित प्रवेश.",
|
||||
"feature_secure_title": "सुरक्षित आणि खाजगी",
|
||||
"feature_secure_desc": "संपूर्ण एन्क्रिप्शन.",
|
||||
"feature_sharing_title": "सुलभ शेअरिंग",
|
||||
"feature_sharing_desc": "एक क्लिकमध्ये फायली शेअर करा.",
|
||||
"feature_access_anywhere_title": "कोठूनही प्रवेश",
|
||||
"feature_access_anywhere_desc": "सर्व डिव्हाइसेसवर कार्यरत.",
|
||||
|
||||
"how_it_works": "हे कसे कार्य करते",
|
||||
"how_create_account_title": "खाते तयार करा",
|
||||
"how_create_account_desc": "काही सेकंदांत साइन अप करा.",
|
||||
"how_upload_files_title": "फायली अपलोड करा",
|
||||
"how_upload_files_desc": "ड्रॅग आणि ड्रॉप करा किंवा तुमच्या डिव्हाइसमधून निवडा.",
|
||||
"how_manage_files_title": "फायली व्यवस्थापित करा",
|
||||
"how_manage_files_desc": "नाव बदला, हलवा किंवा हटवा.",
|
||||
"how_access_anytime_title": "कधीही प्रवेश करा",
|
||||
"how_access_anytime_desc": "कोणत्याही डिव्हाइसवरून फायली उघडा.",
|
||||
|
||||
"not_found_title": "पृष्ठ सापडले नाही",
|
||||
"not_found_description": "क्षमस्व, तुम्ही शोधत असलेले पृष्ठ आम्हाला सापडले नाही. कदाचित ते हलवले गेले असेल किंवा हटवले गेले असेल.",
|
||||
"go_home": "मुख्य पृष्ठावर जा",
|
||||
|
||||
"login_title": "लॉग इन करा",
|
||||
"email_placeholder": "तुमचा ईमेल टाका",
|
||||
"password_placeholder": "तुमचा पासवर्ड टाका",
|
||||
"forgot_password": "पासवर्ड विसरलात?",
|
||||
"logging_in": "लॉग इन करत आहे...",
|
||||
"login": "लॉग इन",
|
||||
"dont_have_account": "अजून खाते नाही?",
|
||||
"sign_up": "साइन अप",
|
||||
"login_successful": "यशस्वीरित्या लॉग इन झाले!",
|
||||
"login_failed": "लॉग इन अयशस्वी.",
|
||||
"an_error_occurred": "त्रुटी आली. कृपया पुन्हा प्रयत्न करा.",
|
||||
"logging_in_toast": "लॉग इन होत आहे...",
|
||||
|
||||
"signup_title": "साइन अप करा",
|
||||
"first_name": "पहिले नाव",
|
||||
"last_name": "आडनाव",
|
||||
"email_placeholder": "तुमचा ईमेल टाका",
|
||||
"password_placeholder": "तुमचा पासवर्ड टाका",
|
||||
"confirm_password_placeholder": "तुमचा पासवर्ड पुन्हा टाका",
|
||||
"signing_up": "साइन अप करत आहे...",
|
||||
"sign_up": "साइन अप",
|
||||
"already_have_account": "आधीच खाते आहे?",
|
||||
"login": "लॉग इन",
|
||||
"passwords_do_not_match": "पासवर्ड जुळत नाहीत.",
|
||||
"registering": "नोंदणी करत आहे...",
|
||||
"signup_failed": "साइन अप अयशस्वी.",
|
||||
"folder_creation_failed": "वापरकर्त्याची फोल्डर तयार करण्यात अयशस्वी.",
|
||||
"signup_success": "यशस्वीरित्या नोंदणी झाली आणि फोल्डर तयार झाला!",
|
||||
"an_error_occurred": "त्रुटी आली. कृपया पुन्हा प्रयत्न करा.",
|
||||
|
||||
"footer_brand": "Skycrate",
|
||||
"footer_tagline": "तुमच्या सर्व डिजिटल गरजांसाठी सुरक्षित क्लाऊड स्टोरेज सोल्यूशन.",
|
||||
"footer_quick_links": "त्वरित दुवे",
|
||||
"footer_about_us": "आमच्याबद्दल",
|
||||
"footer_features": "वैशिष्ट्ये",
|
||||
"footer_how_it_works": "हे कसे कार्य करते",
|
||||
"footer_contact": "संपर्क",
|
||||
"footer_email": "support@drivethru.com",
|
||||
"footer_phone": "+९१ ३६२८२०६२३४",
|
||||
"footer_address": "१२३ क्लाऊड स्ट्रीट, डिजिटल सिटी",
|
||||
"footer_newsletter_title": "अपडेट मिळवा",
|
||||
"footer_newsletter_desc": "विशेष टिप्स, नवीन वैशिष्ट्यांवरील अपडेट्स आणि खास ऑफर्स तुमच्या इनबॉक्समध्ये मिळवा.",
|
||||
"footer_newsletter_placeholder": "तुमचा ईमेल टाका",
|
||||
"footer_newsletter_button": "न्यूजलेटरची सदस्यता घ्या",
|
||||
"subscribe_success": "तुमची सदस्यता यशस्वीरित्या घेतली गेली आहे!",
|
||||
"footer_rights": "सर्व हक्क राखीव.",
|
||||
"footer_privacy_policy": "गोपनीयता धोरण",
|
||||
"footer_terms_of_service": "सेवेच्या अटी",
|
||||
"footer_cookie_policy": "कुकी धोरण",
|
||||
|
||||
"sidebar_logging_out": "लॉग आउट करत आहे...",
|
||||
"sidebar_logged_out": "यशस्वीरित्या लॉग आउट झाले!",
|
||||
"sidebar_open_sidebar": "साइडबार उघडा",
|
||||
"sidebar_brand": "Skycrate",
|
||||
"sidebar_open_user_menu": "वापरकर्ता मेनू उघडा",
|
||||
"sidebar_user_photo": "वापरकर्त्याचा फोटो",
|
||||
"sidebar_logout": "लॉग आउट",
|
||||
"sidebar_starred": "आवडते"
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
import './i18n'; // for multilingual functionality
|
||||
|
||||
import { StrictMode } from "react";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import "./index.css";
|
||||
|
||||
@@ -2,10 +2,12 @@ import { useState, useEffect } from "react";
|
||||
import { FiEye, FiEyeOff } from "react-icons/fi";
|
||||
import { Link, useNavigate } from "react-router-dom";
|
||||
import toast from "react-hot-toast"; // Import React Hot Toast
|
||||
import { useTranslation } from "react-i18next"; // for multilinguality
|
||||
|
||||
const API_URL = import.meta.env.VITE_API_URL; // Using .env variable
|
||||
|
||||
const Login = () => {
|
||||
const { t } = useTranslation(); // for multilinguality
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
const [email, setEmail] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
@@ -28,7 +30,7 @@ const Login = () => {
|
||||
setLoading(true);
|
||||
|
||||
// Show loading toast
|
||||
const toastId = toast.loading("Logging in...");
|
||||
const toastId = toast.loading(t("logging_in_toast"));
|
||||
|
||||
try {
|
||||
const response = await fetch(`${API_URL}/api/login`, {
|
||||
@@ -61,18 +63,17 @@ const Login = () => {
|
||||
});
|
||||
|
||||
// Show success toast
|
||||
toast.success("Login successful!");
|
||||
|
||||
toast.success(t("login_successful"));
|
||||
// Redirect to Dashboard
|
||||
navigate("/dashboard");
|
||||
} else {
|
||||
// Show error toast if login fails
|
||||
toast.error(data.message || "Login failed.");
|
||||
toast.error(data.message || t("login_failed"));
|
||||
}
|
||||
} catch (error) {
|
||||
// Dismiss the loading toast and show error
|
||||
toast.dismiss(toastId);
|
||||
toast.error("An error occurred. Please try again.", error);
|
||||
toast.error(t("an_error_occurred"));
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -82,7 +83,7 @@ const Login = () => {
|
||||
<div className="min-h-screen bg-gray-100 flex items-center justify-center p-4">
|
||||
<div className="w-full max-w-md bg-white rounded-4xl shadow-lg p-8">
|
||||
<h1 className="text-2xl font-bold mb-6 text-gray-900 text-center">
|
||||
Log in
|
||||
{t("login_title")}
|
||||
</h1>
|
||||
|
||||
<form onSubmit={handleSubmit}>
|
||||
@@ -91,7 +92,7 @@ const Login = () => {
|
||||
<input
|
||||
type="email"
|
||||
id="email"
|
||||
placeholder="Enter your email"
|
||||
placeholder={t("email_placeholder")}
|
||||
className="w-full border border-gray-300 rounded-l-lg px-4 py-4 focus:outline-none focus:border-blue-500"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
@@ -104,7 +105,7 @@ const Login = () => {
|
||||
<input
|
||||
type={showPassword ? "text" : "password"}
|
||||
id="password"
|
||||
placeholder="Enter your password"
|
||||
placeholder={t("password_placeholder")}
|
||||
className="w-full border border-gray-300 rounded-lg px-4 py-4 focus:outline-none focus:border-blue-500 pr-10"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
@@ -124,7 +125,7 @@ const Login = () => {
|
||||
to="#!"
|
||||
className="text-sm text-blue-600 hover:underline inline-block"
|
||||
>
|
||||
Forgot password?
|
||||
{t("forgot_password")}
|
||||
</Link>
|
||||
</div>
|
||||
<button
|
||||
@@ -132,17 +133,17 @@ const Login = () => {
|
||||
disabled={loading}
|
||||
className="w-full py-3 bg-gradient-to-r from-[#1877F2] to-[#0E458C] hover:from-[#0E458C] hover:to-[#1877F2] text-white font-semibold rounded-full shadow-md transition duration-300"
|
||||
>
|
||||
{loading ? "Logging In..." : "Login"}
|
||||
{loading ? t("logging_in") : t("login")}
|
||||
</button>
|
||||
</form>
|
||||
<div className="text-center mt-6">
|
||||
<p className="text-gray-700">
|
||||
Don’t have an account?{" "}
|
||||
{t("dont_have_account")}{" "}
|
||||
<Link
|
||||
to="/signup"
|
||||
className="text-emerald-500 hover:underline font-medium"
|
||||
>
|
||||
Sign up
|
||||
{t("sign_up")}
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -2,10 +2,12 @@ import { useState } from "react";
|
||||
import { FiEye, FiEyeOff } from "react-icons/fi";
|
||||
import { Link, useNavigate } from "react-router-dom";
|
||||
import toast, { Toaster } from "react-hot-toast";
|
||||
import { useTranslation } from "react-i18next"; // for multilinguality
|
||||
|
||||
const API_URL = import.meta.env.VITE_API_URL;
|
||||
|
||||
const SignUp = () => {
|
||||
const { t } = useTranslation(); // for multilinguality
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [formData, setFormData] = useState({
|
||||
@@ -30,12 +32,12 @@ const SignUp = () => {
|
||||
e.preventDefault();
|
||||
|
||||
if (formData.password !== formData.confirmPassword) {
|
||||
toast.error("Passwords do not match.");
|
||||
toast.error(t("passwords_do_not_match"));
|
||||
return;
|
||||
}
|
||||
|
||||
setLoading(true);
|
||||
const toastId = toast.loading("Registering...");
|
||||
const toastId = toast.loading(t("registering"));
|
||||
|
||||
try {
|
||||
// 1️⃣ Sign up the user
|
||||
@@ -52,7 +54,7 @@ const SignUp = () => {
|
||||
const signupData = await signupRes.json();
|
||||
|
||||
if (!signupRes.ok) {
|
||||
toast.error(signupData.message || "Signup failed.", { id: toastId });
|
||||
toast.error(signupData.message || t("signup_failed"), { id: toastId });
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -66,11 +68,9 @@ const SignUp = () => {
|
||||
|
||||
if (!folderRes.ok) {
|
||||
// you might choose to roll back user creation or just notify
|
||||
toast.error("Failed to create user folder.", { id: toastId });
|
||||
toast.error(t("failed_create_folder"), { id: toastId });
|
||||
} else {
|
||||
toast.success("Successfully registered and folder created!", {
|
||||
id: toastId,
|
||||
});
|
||||
toast.success(t("signup_success"), { id: toastId });
|
||||
}
|
||||
|
||||
// 3️⃣ Redirect to login after a short delay
|
||||
@@ -79,7 +79,7 @@ const SignUp = () => {
|
||||
}, 1500);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
toast.error("An error occurred. Please try again.", { id: toastId });
|
||||
toast.error(t("an_error_occurred"), { id: toastId });
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -89,12 +89,12 @@ const SignUp = () => {
|
||||
<div className="min-h-screen bg-gray-100 flex items-center justify-center p-6">
|
||||
<Toaster position="top-right" />
|
||||
<div className="w-full max-w-md bg-white rounded-2xl shadow-lg p-8">
|
||||
<h1 className="text-2xl font-bold text-gray-900 mb-6">Sign Up</h1>
|
||||
<h1 className="text-2xl font-bold text-gray-900 mb-6">{t("sign_up")}</h1>
|
||||
<form className="space-y-4" onSubmit={handleSubmit}>
|
||||
<input
|
||||
type="text"
|
||||
name="firstname"
|
||||
placeholder="First Name"
|
||||
placeholder={t("first_name")}
|
||||
value={formData.firstname}
|
||||
onChange={handleChange}
|
||||
className="w-full border border-gray-300 rounded-lg px-4 py-4 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
@@ -103,7 +103,7 @@ const SignUp = () => {
|
||||
<input
|
||||
type="text"
|
||||
name="lastname"
|
||||
placeholder="Last Name"
|
||||
placeholder={t("last_name")}
|
||||
value={formData.lastname}
|
||||
onChange={handleChange}
|
||||
className="w-full border border-gray-300 rounded-lg px-4 py-4 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
@@ -112,7 +112,7 @@ const SignUp = () => {
|
||||
<input
|
||||
type="email"
|
||||
name="email"
|
||||
placeholder="Enter your email"
|
||||
placeholder={t("email_placeholder")}
|
||||
value={formData.email}
|
||||
onChange={handleChange}
|
||||
className="w-full border border-gray-300 rounded-lg px-4 py-4 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
@@ -124,7 +124,7 @@ const SignUp = () => {
|
||||
<input
|
||||
type={showPassword ? "text" : "password"}
|
||||
name="password"
|
||||
placeholder="Enter your password"
|
||||
placeholder={t("password_placeholder")}
|
||||
value={formData.password}
|
||||
onChange={handleChange}
|
||||
className="w-full border border-gray-300 rounded-lg px-4 py-4 focus:outline-none focus:ring-2 focus:ring-blue-500 pr-10"
|
||||
@@ -144,7 +144,7 @@ const SignUp = () => {
|
||||
<input
|
||||
type={showConfirmPassword ? "text" : "password"}
|
||||
name="confirmPassword"
|
||||
placeholder="Confirm your password"
|
||||
placeholder={t("confirm_password_placeholder")}
|
||||
value={formData.confirmPassword}
|
||||
onChange={handleChange}
|
||||
className="w-full border border-gray-300 rounded-lg px-4 py-4 focus:outline-none focus:ring-2 focus:ring-blue-500 pr-10"
|
||||
@@ -169,18 +169,18 @@ const SignUp = () => {
|
||||
: "bg-gradient-to-r from-[#10B981] to-[#07533A] hover:from-[#0E458C] hover:to-[#1877F2]"
|
||||
} text-white font-semibold rounded-lg shadow-md transition duration-300`}
|
||||
>
|
||||
{loading ? "Signing Up..." : "Sign Up"}
|
||||
{loading ? t("signing_up") : t("sign_up")}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
{/* Redirect to Login */}
|
||||
<p className="text-center mt-4 text-gray-700">
|
||||
Already have an account?{" "}
|
||||
{t("already_have_account")}{" "}
|
||||
<Link
|
||||
to="/login"
|
||||
className="text-blue-500 hover:underline font-medium"
|
||||
>
|
||||
Login
|
||||
{t("login")}
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useTranslation } from "react-i18next"; // for multilinguality
|
||||
import Sidebar from "../../components/Sidebar";
|
||||
import FileList from "../../components/FileList";
|
||||
import FileUploadModal from "../../components/FileUploadModal";
|
||||
import { FiPlus } from "react-icons/fi";
|
||||
|
||||
const Dashboard = () => {
|
||||
const { t } = useTranslation(); // for multilinguality
|
||||
const [files, setFiles] = useState([]);
|
||||
const [isUploadModalOpen, setIsUploadModalOpen] = useState(false);
|
||||
const [error, setError] = useState("");
|
||||
const navigate = useNavigate();
|
||||
const API_URL = import.meta.env.VITE_API_URL;
|
||||
|
||||
const isUserLoggedIn = () => {
|
||||
const token = localStorage.getItem("token");
|
||||
const username = localStorage.getItem("username");
|
||||
@@ -36,7 +39,7 @@ const Dashboard = () => {
|
||||
setFiles(data);
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch files:", error);
|
||||
setError("Failed to load files. Please try again later.");
|
||||
setError(t("failed_to_load_files"));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -46,6 +49,7 @@ const Dashboard = () => {
|
||||
} else {
|
||||
fetchFiles();
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
}, [navigate]);
|
||||
|
||||
return (
|
||||
@@ -54,7 +58,7 @@ const Dashboard = () => {
|
||||
<div className="p-4 sm:ml-64">
|
||||
<div className="p-4 border-2 border-gray-200 border-dashed rounded-lg mt-14">
|
||||
<div className="w-full flex justify-between items-center">
|
||||
<h1 className="text-2xl font-bold mb-4">Dashboard</h1>
|
||||
<h1 className="text-2xl font-bold mb-4">{t("dashboard")}</h1>
|
||||
<button
|
||||
onClick={() => setIsUploadModalOpen(true)}
|
||||
className="block text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-3 py-2 text-center"
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import Footer from "../../components/Footer";
|
||||
import React from "react";
|
||||
import { useState, useEffect } from "react";
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { useTranslation } from "react-i18next"; // for multilinguality
|
||||
|
||||
const DrivethruLandingPage = () => {
|
||||
const { t } = useTranslation(); // for multilinguality
|
||||
|
||||
const features = [
|
||||
{
|
||||
title: "Easy Upload & Access",
|
||||
description: "Drag & drop, instant access.",
|
||||
title: t("feature_easy_upload_title"),
|
||||
description: t("feature_easy_upload_desc"),
|
||||
icon: (
|
||||
<svg
|
||||
className="w-6 h-6"
|
||||
@@ -25,8 +27,8 @@ const DrivethruLandingPage = () => {
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Secure & Private",
|
||||
description: "End-to-end encryption.",
|
||||
title: t("feature_secure_title"),
|
||||
description: t("feature_secure_desc"),
|
||||
icon: (
|
||||
<svg
|
||||
className="w-6 h-6"
|
||||
@@ -44,8 +46,8 @@ const DrivethruLandingPage = () => {
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Seamless Sharing",
|
||||
description: "Share files with one click.",
|
||||
title: t("feature_sharing_title"),
|
||||
description: t("feature_sharing_desc"),
|
||||
icon: (
|
||||
<svg
|
||||
className="w-6 h-6"
|
||||
@@ -63,8 +65,8 @@ const DrivethruLandingPage = () => {
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Access Anywhere",
|
||||
description: "Works on all devices.",
|
||||
title: t("feature_access_anywhere_title"),
|
||||
description: t("feature_access_anywhere_desc"),
|
||||
icon: (
|
||||
<svg
|
||||
className="w-6 h-6"
|
||||
@@ -85,8 +87,8 @@ const DrivethruLandingPage = () => {
|
||||
|
||||
const howItWorks = [
|
||||
{
|
||||
title: "Create an account",
|
||||
description: "Sign up in seconds.",
|
||||
title: t("how_create_account_title"),
|
||||
description: t("how_create_account_desc"),
|
||||
icon: (
|
||||
<svg
|
||||
className="w-6 h-6"
|
||||
@@ -104,8 +106,8 @@ const DrivethruLandingPage = () => {
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Upload files",
|
||||
description: "Drag & drop or select from your device.",
|
||||
title: t("how_upload_files_title"),
|
||||
description: t("how_upload_files_desc"),
|
||||
icon: (
|
||||
<svg
|
||||
className="w-6 h-6"
|
||||
@@ -123,8 +125,8 @@ const DrivethruLandingPage = () => {
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Manage files",
|
||||
description: "Rename, move, or delete easily.",
|
||||
title: t("how_manage_files_title"),
|
||||
description: t("how_manage_files_desc"),
|
||||
icon: (
|
||||
<svg
|
||||
className="w-6 h-6"
|
||||
@@ -148,8 +150,8 @@ const DrivethruLandingPage = () => {
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Access anytime",
|
||||
description: "Open files from any device.",
|
||||
title: t("how_access_anytime_title"),
|
||||
description: t("how_access_anytime_desc"),
|
||||
icon: (
|
||||
<svg
|
||||
className="w-6 h-6"
|
||||
@@ -168,6 +170,7 @@ const DrivethruLandingPage = () => {
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
// UseEffect and handle....click function to handle set and handle the animation of features..
|
||||
const [activeIndex, setActiveIndex] = useState(0);
|
||||
const [isPaused, setIsPaused] = useState(false);
|
||||
@@ -234,17 +237,16 @@ const DrivethruLandingPage = () => {
|
||||
</svg>
|
||||
</div>
|
||||
<h1 className="text-4xl md:text-5xl font-bold text-black">
|
||||
Skycrate
|
||||
{t("skycrate")}
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<h2 className="text-xl md:text-2xl font-bold mb-4 md:mb-6 text-black">
|
||||
Store, Access & Share Your Files — Anytime, Anywhere!
|
||||
{t("hero_subtitle")}
|
||||
</h2>
|
||||
|
||||
<p className="text-gray-800 mb-6 md:mb-10 text-base md:text-lg">
|
||||
A simple, secure, and fast cloud storage solution for all your
|
||||
files. Upload, organize, and access with ease.
|
||||
{t("hero_desc")}
|
||||
</p>
|
||||
|
||||
{/* Buttons */}
|
||||
@@ -253,13 +255,13 @@ const DrivethruLandingPage = () => {
|
||||
to="/signup"
|
||||
className="bg-emerald-500 hover:bg-emerald-600 text-white font-medium rounded-full px-6 py-4 md:px-8 md:py-6 transform hover:scale-105 transition-all duration-300 shadow-lg hover:shadow-xl"
|
||||
>
|
||||
Get Started
|
||||
{t("get_started")}
|
||||
</Link>
|
||||
<Link
|
||||
to="/login"
|
||||
className="bg-blue-600 hover:bg-blue-700 text-white font-medium rounded-full px-6 py-4 md:px-8 md:py-6 transform hover:scale-105 transition-all duration-300 shadow-lg hover:shadow-xl"
|
||||
>
|
||||
Login
|
||||
{t("login")}
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
@@ -283,7 +285,7 @@ const DrivethruLandingPage = () => {
|
||||
id="features"
|
||||
className="w-full max-w-5xl mx-auto p-6 sm:p-8 bg-gray-100 rounded-lg shadow-lg"
|
||||
>
|
||||
<h2 className="text-3xl font-bold text-center mb-8">Key Features</h2>
|
||||
<h2 className="text-3xl font-bold text-center mb-8">{t("key_features")}</h2>
|
||||
<div className="flex flex-col-reverse md:flex-row items-center gap-8 lg:gap-12">
|
||||
{/* Left Side - Image */}
|
||||
<div className="w-full md:w-1/2 flex justify-center">
|
||||
@@ -328,7 +330,7 @@ const DrivethruLandingPage = () => {
|
||||
id="howItWorks"
|
||||
className="w-full max-w-5xl mx-auto p-6 sm:p-8 bg-gray-100 rounded-lg shadow-lg"
|
||||
>
|
||||
<h2 className="text-3xl font-bold text-center mb-8">How It Works</h2>
|
||||
<h2 className="text-3xl font-bold text-center mb-8">{t("how_it_works")}</h2>
|
||||
<div className="flex flex-col md:flex-row items-center gap-8 lg:gap-12">
|
||||
{/* Left Side - Feature List */}
|
||||
<div className="w-full md:w-1/2">
|
||||
|
||||
@@ -1,29 +1,25 @@
|
||||
import { Link } from "react-router-dom";
|
||||
import { useTranslation } from "react-i18next"; // for multilinguality
|
||||
|
||||
const NotFoundPage = () => {
|
||||
const { t } = useTranslation(); // for multilinguality
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center h-screen bg-gray-100 p-4">
|
||||
{/* Placeholder SVG - Replace this with your SVG */}
|
||||
<img
|
||||
src="/404.png"
|
||||
style={{ width: "30%", height: "auto" }}
|
||||
alt="404 Not Found"
|
||||
></img>
|
||||
{/* Page number and title */}
|
||||
<h2 className="text-2xl font-bold mb-4 mt-4">Page Not Found</h2>
|
||||
|
||||
{/* Description text */}
|
||||
/>
|
||||
<h2 className="text-2xl font-bold mb-4 mt-4">{t("not_found_title")}</h2>
|
||||
<p className="text-center text-gray-700 mb-6">
|
||||
Sorry, we couldn't find the page you were looking for. It may have
|
||||
been moved or deleted.
|
||||
{t("not_found_description")}
|
||||
</p>
|
||||
|
||||
{/* Call-to-action button */}
|
||||
<Link
|
||||
to="/"
|
||||
className="px-6 py-2 bg-[#1877F2] text-white rounded hover:bg-blue-600 transition duration-200"
|
||||
>
|
||||
Go Home
|
||||
{t("go_home")}
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user