diff --git a/public/logo.jpg b/public/logo.jpg new file mode 100644 index 0000000..8751ed9 Binary files /dev/null and b/public/logo.jpg differ diff --git a/src/actions/auth.tsx b/src/actions/auth.tsx deleted file mode 100644 index 13459ed..0000000 --- a/src/actions/auth.tsx +++ /dev/null @@ -1,58 +0,0 @@ -"use server"; -import { cookies } from "next/headers"; - -import axiosInstance from "../utils/axiosInstance"; - -export const loginIn = async (username: string, password: string) => { - try { - const response = await axiosInstance.post("/api/accounts/token/", { - username, - password - }); - console.log("Login response:", response.data); - - const cookieStore = await cookies(); - - cookieStore.set("accessToken", response.data.access, { - httpOnly: true, - maxAge: 60 * 10, - secure: process.env.NODE_ENV === "production", - sameSite: "lax" - }); - - cookieStore.set("refreshToken", response.data.refresh, { - httpOnly: true, - maxAge: 60 * 60 * 24 * 7, // 7 days - secure: process.env.NODE_ENV === "production", - sameSite: "lax" - }); - - return { success: true, data: response.data }; - } catch (error) { - console.error("Login error:", error); - throw error; - } -}; - - -export const refreshAuth = async () => { - try { - const response = await axiosInstance.post("/api/accounts/token/refresh/", { - withCredentials: true - }); - - const cookieStore = await cookies(); - - cookieStore.set("accessToken", response.data.access, { - httpOnly: true, - secure: process.env.NODE_ENV === "production", - maxAge: 60 * 10, - sameSite: "lax" - }); - - return { success: true }; - } catch (error) { - console.error("Refresh auth error:", error); - throw error; - } -}; diff --git a/src/app/auth/forgot-password/page.tsx b/src/app/auth/forgot-password/page.tsx deleted file mode 100644 index e51cd9e..0000000 --- a/src/app/auth/forgot-password/page.tsx +++ /dev/null @@ -1,137 +0,0 @@ -"use client" - -import type React from "react" -import { useState } from "react" -import { ArrowLeft, Mail } from "lucide-react" -import Link from "next/link" -import { Button } from "@/src/components/ui/button" -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/src/components/ui/card" -import { Input } from "@/src/components/ui/input" -import { Label } from "@/src/components/ui/label" -import { Database } from "lucide-react" -import toast from "react-hot-toast" - - -export default function ForgotPasswordPage() { - const [email, setEmail] = useState("") - const [isSubmitted, setIsSubmitted] = useState(false) - const [isLoading, setIsLoading] = useState(false) - - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault() - if (!email) return - - setIsLoading(true) - - // Simulate API call - setTimeout(() => { - setIsLoading(false) - setIsSubmitted(true) - }, 1500) - } - - return ( -
-
-
- - - VetDataHub - -

Reset your password

-

- {isSubmitted - ? "Check your email for reset instructions" - : "Enter your email address and we'll send you a link to reset your password"} -

-
- - - - - - {isSubmitted ? "Email Sent" : "Forgot Password"} - - - {isSubmitted - ? "We've sent password reset instructions to your email" - : "We'll send you a secure link to reset your password"} - - - - {!isSubmitted ? ( -
-
- - setEmail(e.target.value)} - required - /> -
- - -
- ) : ( -
- {toast.success("Email sent successfully!. We've sent password reset instructions to email")} - -
-

If you don't see the email in your inbox:

-
    -
  • Check your spam or junk folder
  • -
  • Make sure you entered the correct email address
  • -
  • Wait a few minutes for the email to arrive
  • -
-
- - -
- )} - -
- - Remember your password?{" "} - - Sign in - - -
- -
- -
-
-
- -
-

- Need help? Contact our{" "} - - support team - -

-
-
-
- ) -} diff --git a/src/app/auth/login/page.tsx b/src/app/auth/login/page.tsx deleted file mode 100644 index 682b77a..0000000 --- a/src/app/auth/login/page.tsx +++ /dev/null @@ -1,151 +0,0 @@ -"use client" - -import { useState } from "react" -import { Eye, EyeOff } from 'lucide-react' -import Link from "next/link" -import { Button } from "../../../components/ui/button" -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../../../components/ui/card" -import { Input } from "../../../components/ui/input" -import { Label } from "../../../components/ui/label" -import toast from "react-hot-toast" -import { useRouter } from "next/navigation" -import { loginUserSchema } from "../../../schemas/auth" -import { loginIn } from "@/src/actions/auth" -import z from "zod" - -export default function LoginPage() { - const [showPassword, setShowPassword] = useState(false) - const [formData, setFormData] = useState({ - username: "", - password: "", - }) - const router = useRouter(); - - const handleInputChange = (field: string, value: string | boolean) => { - setFormData(prev => ({ ...prev, [field]: value })) - } - - const handleSubmit = async () => { - // Handle form submission logic here - console.log("Form submitted with data:", formData) - try { - loginUserSchema.parse(formData) - await loginIn(formData.username, formData.password) - toast.success('User logged in sucessfully') - router.push('/') - } catch (error) { - if (error instanceof z.ZodError) { - error.issues.forEach(issue => { - toast.error(`Field: ${issue.path.join('.')} - ${issue.message}`) - }) - } else { - toast.error( 'Login failed. Please try again.') - } - } - - } - - return ( -
-
-
-

Welcome back

-

Sign in to your account to continue

-
- - - - Sign In - - Enter your credentials to access your account - - - -
- - handleInputChange('username', e.target.value)} - /> -
- -
- -
- handleInputChange('password', e.target.value)} - /> - -
-
- -
- - Forgot password - -
- - - -
- - Don't have an account?{" "} - - Sign up - - -
- - {/*
-
- -
-
- Or continue with -
-
*/} - {/* -
- - -
*/} -
-
-
-
- ) -} diff --git a/src/app/auth/register/page.tsx b/src/app/auth/register/page.tsx deleted file mode 100644 index 9bcbd27..0000000 --- a/src/app/auth/register/page.tsx +++ /dev/null @@ -1,263 +0,0 @@ -"use client" - -import { useState } from "react" -import { Eye, EyeOff, CheckCircle } from 'lucide-react' -import Link from "next/link" -import { Button } from "@/src/components/ui/button" -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/src/components/ui/card" -import { Input } from "@/src/components/ui/input" -import { Label } from "@/src/components/ui/label" -import { Checkbox } from "@/src/components/ui/checkbox" -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/src/components/ui/select" -import { registerUserSchema } from "@/src/schemas/auth" -import { z } from 'zod' -import toast from "react-hot-toast" -import axiosInstance from "@/src/utils/axiosInstance" -import { useRouter } from "next/navigation" - -export default function RegisterPage() { - const [showPassword, setShowPassword] = useState(false) - const [showConfirmPassword, setShowConfirmPassword] = useState(false) - const [formData, setFormData] = useState({ - first_name: "", - last_name: "", - username: "", - email: "", - affiliation: "", - role: "", - password: "", - confirmPassword: "", - subscribeNewsletter: false - }) - const [agreeToTerms, setAgreeToTerms] = useState(false) - const router = useRouter() - - const handleInputChange = (field: string, value: string | boolean) => { - setFormData(prev => ({ ...prev, [field]: value })) - } - - const handleSubmit = async () => { - - try { - registerUserSchema.parse(formData) - // Handle form submission logic here - - - await axiosInstance.post('api/auth/register', formData) - - toast.success("Registration successful!.") - router.push('/auth/login') - } catch (error) { - - if (error instanceof z.ZodError) { - error.issues.forEach(issue => { - toast.error(`Field: ${issue.path.join('.')} - ${issue.message}`) - }) - } else { - toast.error("Registration failed. Please try again.") - } - } - } - - return ( -
-
-
-

Join VetDataHub

-

Create your account to start sharing and discovering veterinary data

-
- - - - Create Account - - Fill in your information to get started - - - -
-
- - handleInputChange('first_name', e.target.value)} - /> -
-
- - handleInputChange('last_name', e.target.value)} - /> -
-
-
- - handleInputChange('username', e.target.value)} - /> -
- -
- - handleInputChange('email', e.target.value)} - /> -
- -
-
- - handleInputChange('affiliation', e.target.value)} - /> -
-
- - -
-
- -
-
- -
- handleInputChange('password', e.target.value)} - /> - -
-
-
- -
- handleInputChange('confirmPassword', e.target.value)} - /> - -
-
-
- -
-
- setAgreeToTerms(!agreeToTerms)} - /> - -
-
- handleInputChange('subscribeNewsletter', checked as boolean)} - /> - -
-
- - - -
- - Already have an account?{" "} - - Sign in - - -
-
-
- -
-
- -
-

What you get with VetDataHub:

-
    -
  • • Upload and share your research datasets
  • -
  • • Access thousands of veterinary datasets
  • -
  • • Connect with researchers worldwide
  • -
  • • Track citations and impact of your work
  • -
  • • API access for automated workflows
  • -
-
-
-
-
-
- ) -} diff --git a/src/app/auth/reset-password/[token]/page.tsx b/src/app/auth/reset-password/[token]/page.tsx deleted file mode 100644 index d15aeb3..0000000 --- a/src/app/auth/reset-password/[token]/page.tsx +++ /dev/null @@ -1,288 +0,0 @@ -"use client" - -import type React from "react" - -import { useState } from "react" -import { ArrowLeft, Eye, EyeOff, CheckCircle, AlertCircle } from "lucide-react" -import Link from "next/link" -import { Button } from "@/src/components/ui/button" -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/src/components/ui/card" -import { Input } from "@/src/components/ui/input" -import { Label } from "@/src/components/ui/label" -import { Alert, AlertDescription } from "@/src/components/ui/alert" -import { Database } from "lucide-react" -import { useSearchParams } from "next/navigation" - -export default function ResetPasswordPage() { - const searchParams = useSearchParams() - const token = searchParams.get("token") - const email = searchParams.get("email") - - const [formData, setFormData] = useState({ - password: "", - confirmPassword: "", - }) - const [showPassword, setShowPassword] = useState(false) - const [showConfirmPassword, setShowConfirmPassword] = useState(false) - const [isLoading, setIsLoading] = useState(false) - const [isSuccess, setIsSuccess] = useState(false) - const [error, setError] = useState("") - - const handleInputChange = (field: string, value: string) => { - setFormData((prev) => ({ ...prev, [field]: value })) - setError("") // Clear error when user types - } - - const validatePassword = (password: string) => { - if (password.length < 8) return "Password must be at least 8 characters long" - if (!/(?=.*[a-z])/.test(password)) return "Password must contain at least one lowercase letter" - if (!/(?=.*[A-Z])/.test(password)) return "Password must contain at least one uppercase letter" - if (!/(?=.*\d)/.test(password)) return "Password must contain at least one number" - return null - } - - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault() - - // Validate passwords - const passwordError = validatePassword(formData.password) - if (passwordError) { - setError(passwordError) - return - } - - if (formData.password !== formData.confirmPassword) { - setError("Passwords do not match") - return - } - - if (!token) { - setError("Invalid or expired reset link") - return - } - - setIsLoading(true) - setError("") - - // Simulate API call - setTimeout(() => { - setIsLoading(false) - setIsSuccess(true) - }, 1500) - } - - // Invalid token state - if (!token) { - return ( -
-
-
- - - VetDataHub - -
- - - - - - Invalid Reset Link - - - - - - - This password reset link is invalid or has expired. Please request a new one. - - - -
- - -
-
-
-
-
- ) - } - - // Success state - if (isSuccess) { - return ( -
-
-
- - - VetDataHub - -

Password Reset Successful

-

Your password has been successfully updated

-
- - - - - - Success! - - You can now sign in with your new password - - - - - - Your password has been successfully reset. You can now sign in to your account with your new password. - - - - - - -
-
- ) - } - - return ( -
-
-
- - - VetDataHub - -

Set New Password

-

{email ? `Resetting password for ${email}` : "Enter your new password below"}

-
- - - - Reset Password - Choose a strong password for your account - - -
- {error && ( - - - {error} - - )} - -
- -
- handleInputChange("password", e.target.value)} - required - /> - -
-
-

Password must contain:

-
    -
  • = 8 ? "text-green-600" : ""}>At least 8 characters
  • -
  • - One lowercase letter -
  • -
  • - One uppercase letter -
  • -
  • One number
  • -
-
-
- -
- -
- handleInputChange("confirmPassword", e.target.value)} - required - /> - -
- {formData.confirmPassword && formData.password !== formData.confirmPassword && ( -

Passwords do not match

- )} -
- - -
- -
- -
-
-
- -
-

- Having trouble? Contact our{" "} - - support team - -

-
-
-
- ) -} diff --git a/src/app/axios-interceptor-wrapper.tsx b/src/app/axios-interceptor-wrapper.tsx deleted file mode 100644 index 5db0180..0000000 --- a/src/app/axios-interceptor-wrapper.tsx +++ /dev/null @@ -1,48 +0,0 @@ -"use client" -import axiosInstance from "../utils/axiosInstance"; -import React, { useEffect} from "react"; - -import { refreshAuth } from "../actions/auth"; - -export default function AxiosInterceptorWrapper({ - children -}: { - children: React.ReactNode; -}) { - - - - // Setup axios interceptor for automatic token refresh - useEffect(() => { - - const responseIntercept = axiosInstance.interceptors.response.use( - (response) => response, - async (error) => { - const originalRequest = error.config; - - // If error is 401 and we haven't tried to refresh token yet - if (error.response?.status === 401 && !originalRequest._retry) { - originalRequest._retry = true; - - try { - await refreshAuth(); - return axiosInstance(originalRequest, { withCredentials: true }); - } catch (refreshError) { - // If refresh fails, logout user - console.error("Token refresh failed:", refreshError); - throw refreshError; - } - } - return Promise.reject(error); - } - ); - - return () => { - axiosInstance.interceptors.response.eject(responseIntercept); - }; - }, []); - - return ( - <>{children} - ); -} \ No newline at end of file diff --git a/src/app/datasets/[id]/new-version/page.tsx b/src/app/datasets/[id]/new-version/page.tsx deleted file mode 100644 index fe839c7..0000000 --- a/src/app/datasets/[id]/new-version/page.tsx +++ /dev/null @@ -1,551 +0,0 @@ -"use client" - -import { useState } from "react" -import { ArrowLeft, Upload, FileText, AlertCircle, CheckCircle, GitBranch, History } from 'lucide-react' -import Link from "next/link" -import { Button } from "@/src/components/ui/button" -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/src/components/ui/card" -import { Input } from "@/src/components/ui/input" -import { Label } from "@/src/components/ui/label" -import { Textarea } from "@/src/components/ui/textarea" -import { RadioGroup, RadioGroupItem } from "@/src/components/ui/radio-group" -import { Progress } from "@/src/components/ui/progress" -import { Alert, AlertDescription } from "@/src/components/ui/alert" -import { Badge } from "@/src/components/ui/badge" -import { Separator } from "@/src/components/ui/separator" - -const mockCurrentDataset = { - id: "1", - title: "Canine Orthopedic Conditions Survey", - currentVersion: "1.2.0", - description: "Comprehensive dataset of 15,000 cases covering common orthopedic conditions in dogs across North America.", - species: "Dog", - category: "Clinical Trials", - size: "2.3 GB", - format: "CSV", - lastUpdated: "2024-01-15" -} - -export default function NewVersionPage() { - const [currentStep, setCurrentStep] = useState(1) - const [uploadProgress, setUploadProgress] = useState(0) - const [isUploading, setIsUploading] = useState(false) - const [versionType, setVersionType] = useState("minor") - const [formData, setFormData] = useState({ - changesSummary: "", - detailedChanges: "", - breakingChanges: false, - newFeatures: "", - bugFixes: "", - dataAdditions: "", - methodologyChanges: "" - }) - - const handleInputChange = (field: string, value: string) => { - setFormData(prev => ({ ...prev, [field]: value })) - } - - const handleFileUpload = () => { - setIsUploading(true) - const interval = setInterval(() => { - setUploadProgress(prev => { - if (prev >= 100) { - clearInterval(interval) - setIsUploading(false) - setCurrentStep(4) - return 100 - } - return prev + 10 - }) - }, 200) - } - - const getNextVersion = () => { - const [major, minor, patch] = mockCurrentDataset.currentVersion.split('.').map(Number) - switch (versionType) { - case "major": - return `${major + 1}.0.0` - case "minor": - return `${major}.${minor + 1}.0` - case "patch": - return `${major}.${minor}.${patch + 1}` - default: - return `${major}.${minor + 1}.0` - } - } - - const steps = [ - { number: 1, title: "Version Type", description: "Choose the type of version update" }, - { number: 2, title: "File Upload", description: "Upload the new dataset version" }, - { number: 3, title: "Changes Documentation", description: "Document what has changed" }, - { number: 4, title: "Review & Publish", description: "Review and publish the new version" } - ] - - return ( -
- {/* Header */} -
-
-
-
- - - VetDataHub - -
-
-
-
- -
-
-
-
- -

Upload New Version

-
-

Add a new version to: {mockCurrentDataset.title}

-
- Current: v{mockCurrentDataset.currentVersion} - Next: v{getNextVersion()} -
-
- - {/* Progress Steps */} -
-
- {steps.map((step, index) => ( -
-
= step.number - ? 'bg-green-600 border-green-600 text-white' - : 'border-gray-300 text-gray-500' - }`}> - {currentStep > step.number ? ( - - ) : ( - step.number - )} -
- {index < steps.length - 1 && ( -
step.number ? 'bg-green-600' : 'bg-gray-300' - }`} /> - )} -
- ))} -
-
- {steps.map((step) => ( -
-
{step.title}
-
{step.description}
-
- ))} -
-
- - {/* Step 1: Version Type */} - {currentStep === 1 && ( - - - Choose Version Type - Select the appropriate version increment based on your changes - - - - - - VetDataHub uses semantic versioning (MAJOR.MINOR.PATCH) to track dataset changes. - - - - -
-
- -
- -

- Breaking changes that make the dataset incompatible with previous versions. -

-
- Examples: Column renames, file format changes, schema modifications -
-
-
- -
- -
- -

- Backward-compatible additions and improvements. -

-
- Examples: New columns, additional data, new metadata fields -
-
-
- -
- -
- -

- Bug fixes and corrections without functional changes. -

-
- Examples: Data corrections, metadata fixes, typo corrections -
-
-
-
-
- -
-

Selected Version: v{getNextVersion()}

-

- This will create a new {versionType} version of your dataset. -

-
- -
- -
-
-
- )} - - {/* Step 2: File Upload */} - {currentStep === 2 && ( - - - Upload New Dataset Version - Upload the updated dataset files - - - {/* Current Version Info */} -
-

Current Version (v{mockCurrentDataset.currentVersion})

-
-
- Size: {mockCurrentDataset.size} -
-
- Format: {mockCurrentDataset.format} -
-
- Last Updated: {mockCurrentDataset.lastUpdated} -
-
-
- - - - - Upload the new version of your dataset. The system will automatically detect changes and calculate checksums. - - - - {!isUploading && uploadProgress === 0 && ( -
- -

Upload New Version

-

Drag and drop the updated files here, or click to browse

- -
- )} - - {isUploading && ( -
-
- -
-
canine_orthopedic_data_v{getNextVersion()}.csv
-
2.8 GB (+500 MB from previous version)
-
-
- -
- Uploading and analyzing changes... {uploadProgress}% -
-
- )} - - {uploadProgress === 100 && !isUploading && ( -
- - - - File uploaded successfully! Changes detected and verified. - - - -
- - - File Comparison - - -
- Previous Size: - 2.3 GB -
-
- New Size: - 2.8 GB -
-
- Size Change: - +500 MB (+21.7%) -
- -
- Checksum: - Verified -
-
-
- - - - Detected Changes - - -
-
- +2,500 new records added -
-
-
- 3 new columns detected -
-
-
- 15 data corrections applied -
-
-
- Schema: Compatible -
-
-
-
-
- )} - -
- - -
-
-
- )} - - {/* Step 3: Changes Documentation */} - {currentStep === 3 && ( - - - Document Changes - Provide detailed information about what has changed in this version - - -
- - handleInputChange('changesSummary', e.target.value)} - /> -

This will appear in the version history

-
- -
- -